refactor(spec,plugin-sharing,runtime): one canonical predicate for publicSharing.enabled - #16029
refactor(spec,plugin-sharing,runtime): one canonical predicate for publicSharing.enabled#16029claude[bot] wants to merge 4 commits into
Conversation
…nabled` predicate `isPublicSharingEnabled` is now exported from `@objectstack/spec/data`, declared beside the `publicSharing` block in `src/data/object.zod.ts` (the same shape as the neighbouring `isTenancyDisabled`). `plugin-sharing`'s service and route probe consume it, and `packages/runtime`'s documented private mirror in `src/domains/share-links.ts` is deleted. The mirror's stated justification — importing the plugin would invert the dependency direction, it being a dev dependency of the runtime — held only for that home: both packages already depend on `@objectstack/spec`, so the shared home needed no new edge. Behaviour unchanged, fail-closed included: an absent block, an absent schema and an engine that cannot answer `getSchema` remain one answer, `false`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
📓 Docs Drift CheckThis PR changes 3 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 135 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 214ae5f5fd1fed64a1ecfe62196a5fc7344d5d66 && git checkout 214ae5f5fd1fed64a1ecfe62196a5fc7344d5d66
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 53cbad9f75572600ef43bb2a18071633fd6c0f68 05fc7cfff31f82616e672a03e7321160e97e1848 && git checkout -B drift-repro 53cbad9f75572600ef43bb2a18071633fd6c0f68 && git merge --no-ff 05fc7cfff31f82616e672a03e7321160e97e1848
node scripts/docs-audit/affected-docs.mjs --json 53cbad9f75572600ef43bb2a18071633fd6c0f68
|
…ne shift
`check-system-context-census` red on `1f294f94a`: this PR's edit to
`plugin-sharing/src/share-link-service.ts` is +10 above line 45 and -20 above
line 108, so every line below shifts by exactly -10, and row 37 of
`content/docs/permissions/system-context.mdx` cites five of them by number.
All five are PURE LINE ROT, checked per anchor with the file open rather than
assumed: the text now at 459 / 513 / 517 / 590 / 620 is byte-identical to the
text that was at 469 / 523 / 527 / 600 / 630 at the merge base, and the diff's
two hunks both end above line 108 — no line at or below the first anchor was
touched at all. Row 37's claim ("share-link policy `enabled` check bypassed;
system callers re-enter under a system context") is unchanged and still
described by the same five `context.isSystem` reads, so no ledger row with a
needle is owed here.
Repaired with `node scripts/check-system-context-census.mjs --fix`, which
rewrote exactly those five numbers and no prose.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…blicsharing-canonical-predicate
`content/docs/permissions/system-context.mdx` is an os-regen artifact and both sides moved it since `f7db8f4fd`: this branch re-anchored row 37 after its own -10 line shift, and `origin/main` carried the census rows that #15996's share-link admission seam moved. The driver merges such a path with exit 0 while silently keeping ONE side, so it is regenerated from the merged tree rather than resolved by picking a side or by hand. `pnpm gen:system-context-census` re-derives BOTH sides at once, which is visible in the result: row 37 keeps this branch's anchors (`share-link-service.ts:459`, `:513`, `:517`, `:590`, `:620`) while main's shifts land beside them (`sharing-plugin.ts:1088` becomes `:1189`, `rest-server.ts:1553`/`:1582`/`:1585` become `:1565`/`:1594`/`:1597`, `domains/packages.ts:422` becomes `:535`, and row 50's five `rest-server.ts` anchors move). No prose changed — only anchors. Landed through `scripts/pm/os-regen-merge.sh`: merge first, then regenerate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
|
Contract review (clause ②) PASS at head
Implemented-by: Generated by Claude Code |
Contract review (clause ②) — PASS extended to head
|
| commit | what | reading | verdict |
|---|---|---|---|
89372b7c |
Merge remote-tracking branch 'origin/main' (parents 4620deb7, 53cbad9f) |
Merge commit, ⛔ no rebase — as instructed. The PR's own file list is unchanged (9 files, identical to the reviewed set); the 84 files in the merge commit are all origin/main content. |
merge-only |
05fc7cff |
chore: regenerate the system-context census from the merged tree |
One file, content/docs/permissions/system-context.mdx, 6/6 lines: line-number re-anchoring only (rest-server.ts:1553→1565, :1582→1594, :1585→1597, sharing-plugin.ts:1088→1189, domains/packages.ts:422→535, row 50's five rest-server.ts anchors). merge=os-regen path, regenerated with the repo's tool on the merged tree — exactly what PASS 5555213275 asked for; not a hand edit. Lint & Repo Gates (regen-pending check) success on this head. |
tool regen |
Verdict on 4620deb7 stands unchanged for 05fc7cff. Semver reading unchanged (spec minor / plugin-sharing patch / runtime patch, one fixed group).
Evidence and landing
- Checks on
05fc7cff: 33 success / 7 skipped / 0 red (Test Core (1/6)completed 23:00:31Z success). check-governed-merges --teston the head file list: 0 of 9 paths — ordinary queue landing.mergeable: true,mergeable_state: blocked(draft only).- Gate label: the push re-hung
needs:contract-review(22:30:46Z); the maintainer removed it by hand at 22:34:39Z. This extension is the review of record behind that removal.
Landing now from this seat: draft:false + auto-merge SQUASH (merge queue). Card #14935 closes on merge via Fixes.
Generated by Claude Code
Fixes #14935
publicSharing.enabledhad one policy read and two spellings. This gives it one canonical predicate, exported from the package that declares the key, and deletes thepackages/runtimemirror.What lands
packages/spec/src/data/object.zod.ts— new exportisPublicSharingEnabled(schema), declared beside thepublicSharingblock itself and shaped exactly like its neighbourisTenancyDisabled(an object posture the spec owns precisely because more than one package must not re-derive it). Reachable as@objectstack/spec/data.packages/plugins/plugin-sharing—share-link-service.tsno longer defines the predicate, it imports it;share-link-routes.tsimports it from the spec rather than from the service module, so the route probe and the redemption gate insideresolveTokenread one definition instead of one definition and one re-export of it.packages/runtime/src/domains/share-links.ts— the documented private MIRROR is deleted; the/share-linksdispatcher domain imports the same predicate.The mirror's stated justification — that importing
@objectstack/plugin-sharingwould invert the dependency direction, it being a dev dependency ofpackages/runtime— is true of that one home and not of the question. Both packages already list@objectstack/specindependencies, so the shared home existed all along and this de-duplication adds no dependency edge.check:undeclared-dep-importsis green on both new imports.Behaviour is unchanged, fail-closed included: an absent
publicSharingblock, an absent schema, and an engine that cannot answergetSchemaat all remain one answer,false, and only the booleantrueenables.Clause-② is YES — this widens a published contract face, so the PR is PARKED
Measured, not assumed:
check:api-surfacesaid it in its own words before regeneration:@objectstack/spec public API changed: 0 breaking (removed/narrowed), 1 added.One member added to@objectstack/spec/data, nothing removed or narrowed.export-origins/data.jsongains the matching row. Both artifacts were regenerated withcheck:generated --fix(only the 2 it proved stale) and both gates are green at the final commit.Consequences taken here:
needs:contract-reviewon this PR and on the card, the PR stays a draft with no ready flip and no auto-merge arming, and the changeset is minor for@objectstack/spec— the act (additive widening of a published surface) sets the bump, not therefactor(...)-shaped title.The removal side is not a narrowing — and the check that says so was NARROWED, declared here
"Retire the mirror" is a removal, so the opposite question had to be asked: was anything removed from a published surface? Answer: no, and here is the measurement rather than an assumption.
packages/runtime— at the merge base the predicate is declaredfunction isPublicSharingEnabled(schema: unknown): booleanwith noexportkeyword (git show f7db8f4fd:packages/runtime/src/domains/share-links.ts, line 61). A module-local function cannot be re-exported and cannot appear in any entry declaration file. Positive control on the same file and the same grep:createShareLinksDomainandhandleShareLinksRequestin that file do carryexport, so the scan distinguishes the two cases.packages/runtime/src/index.tsnamesshare-linkszero times.packages/plugins/plugin-sharing— the predicate was exported fromshare-link-service.ts, but the package publishes exactly one entry (exportshas only".",files: ["dist"],types: dist/index.d.ts), and at the merge basesrc/index.tsre-exports onlyShareLinkServiceandShareLinkServiceOptionsfrom that module: the symbol's name occurs 0 times in the entry file (positive control on the same command:ShareLinkServiceoccurs there). A symbol unreachable from the entry is not indist/index.d.ts.dist/index.d.ts, restore and prove the restore — was written and attempted, and it never ran: three consecutive attempts on the shared verify lock returnedVERDICT queue-timeout (exit 99), which is NOT MEASURED and is recorded as such rather than as a pass. No mutation was in flight at any point, and the worktree is clean at the final commit. What is above is entry-point reachability, which answers the same question one level up from the artifact. CI builds both packages regardless.The pins that had to stay green
Both are unchanged by this PR, which is the point: they assert the same observable answer on both surfaces rather than trusting either copy, so they are exactly the instrument that proves a de-duplication did not move behaviour.
packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts— 43 passedpackages/runtime/src/domains/share-links-enforcement-context.test.ts— 18 passedNew: the predicate's own contract, which those two can only observe indirectly, is pinned directly in
packages/spec/src/data/object.test.ts— that only the booleantrueenables (a truthy'true'or1does not publish records), that the block-absent / schema-absent / unreadable cases collapse to one answer, and that the predicate agrees with the schema default the same file parses.Verification — every command below ran at HEAD 1f294f9
Exit codes were captured before any pipe. Two further test batches (
share-link-service.test.ts,share-link-envelope.conformance.test.ts,route-ledger.conformance.test.ts,domain-handler-registry.test.ts,standalone-stack.test.ts) queued out on the shared verify lock atexit 99and are recorded as NOT MEASURED, not as green; CI runs them.Known CI flake — not this PR
If
Lint & Repo Gatesfails oncheck:merge-driverwithself-test failed -- 1 failure(s) (cases and floor), that is thecheck-regen-pending.mjs --self-testCorepack issue tracked on #15990 / #15992 with the fix in PR #16002. Nothing was skipped, disabled or quarantined over it.Second commit — census anchors repaired, decided per anchor rather than renumbered blind
check-system-context-censuswent red on1f294f94a, and it was mine: the edit toshare-link-service.tsis+10above line 45 and-20above line 108, so everything below shifts by exactly-10, and row 37 ofcontent/docs/permissions/system-context.mdxcites five of those lines by number. The gate names two situations and only one is--fix-able, so each of the five was judged with the file open before anything was rewritten::469:459if (!policy.enabled && !this.permissive && !context.isSystem) {:523:513context: context.isSystem ? SYSTEM_CTX : context,:527:517throw context.isSystem:600:590let permitted = context.isSystem === true || row.created_by === context.userId;:630:620context: context.isSystem ? SYSTEM_CTX : context,All five are pure line rot; none needed a ledger row with a needle. The structural proof is stronger than the five line comparisons on their own:
git diff -U0on this file yields exactly two hunks,@@ -34,0 +35,10 @@and@@ -88,20 +97,0 @@, both ending above line 108 — no line at or below the first anchor was touched at all, so the citation cannot be pointing at different code. Row 37's claim is unchanged and still described by the same fivecontext.isSystemreads.Repaired with
node scripts/check-system-context-census.mjs --fix, which rewrote those five numbers and no prose (the row's diff is one line,-/+, differing only in the digits). Green at the new head:check-system-context-census: OK — 105 elevation read sites in 19 packages across 44 files, all anchored; 140 anchors resolve, self-test green beside it.Docs drift — read per file, verdict is a measured null
affected-docs.mjs --json f7db8f4fdmaps this diff to exactly one hand-written page,content/docs/concepts/metadata-driven.mdx, via the symbolObjectSchemaBase. That page's only use of the anchor is one line: "Type Safety: Compile-time type checking via thez.inputoftypeof ObjectSchemaBase" (spelled without the angle brackets here on purpose — the platform rewrites those bytes in a body). This change does not falsify it. The whole edit inside that const is two comment lines (@seein thepublicSharingTSDoc); the new function lands at line 2328, after the const closes at 2324 — so no key, no type and no parse behaviour moved, whichcheck:authorable-surfaceindependently confirms by staying green. The page says nothing about share links or the predicate:publicSharing0,isPublicSharingEnabled0,share-link0 — with positive controls firing on the same command and scope,ObjectSchemaBase1 andsharingModel6.Widened past the mapper's floor, since it flags 3 files that yielded no anchor (
share-link-routes.tsand the two generated JSONs): acrosscontent/**anddocs/**markdown,isPublicSharingEnabledappears in 0 files and the mirror's prose (deliberate MIRROR) in 0 — positive controls on the identical command and scope:publicSharing11 files,ObjectSchemaBase2 files (metadata-driven.mdxand ADR-0122, which governs schema type alias naming and so does not reach a new function). No docs page documents the retired copy, so retiring it leaves no page stating something untrue. Nothing undercontent/docs/releases/**was touched.The
TypeScript Type Checkred on the first head was a cancellation — measured, and the cause is not what it looks likeRun
33990004034on1f294f94ahas run-levelconclusion: cancelled. Its jobs:Lint & Repo Gatesfailed at 20:25:02 (the census, above),Type Check · source gatessucceeded at 20:27:09, and the other three lanes ran on until 20:30:51–20:30:52 — five and a half minutes past the census failure — so the census did not fail-fast them. They were cancelled within a second of the push of4620deb7d, whose run was created at 20:30:35:lint.ymldeclaresconcurrency: cancel-in-progress: truekeyed on the PR, so the new head cancelled the old head's run. The aggregate then started at 20:30:54 and reported failure two seconds later because its verdict step errors on anyneeds.*.resultthat is notsuccessand printsconcluded 'cancelled' -- expected 'success'; it emits no TypeScript diagnostics of its own.cancelledmeans the lane never ran. Ruled out by measurement rather than assumed: alabeledevent cannot be the trigger —lint.yml'spull_request:declares notypes:, so it fires only on opened / synchronize / reopened.On the new head the lanes are actually running: at the time of writing,
Type Check · source gates,Type Check · consumer gatesandType Check · debt ledgerarecompleted / success, whileType Check · workspaceandLint & Repo Gatesare stillin_progress— read as status, not as a zero-failure count.Generated by Claude Code