Skip to content

refactor(spec,plugin-sharing,runtime): one canonical predicate for publicSharing.enabled - #16029

Queued
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-14935-publicsharing-canonical-predicate
Queued

refactor(spec,plugin-sharing,runtime): one canonical predicate for publicSharing.enabled#16029
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-14935-publicsharing-canonical-predicate

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #14935

publicSharing.enabled had one policy read and two spellings. This gives it one canonical predicate, exported from the package that declares the key, and deletes the packages/runtime mirror.

What lands

  • packages/spec/src/data/object.zod.ts — new export isPublicSharingEnabled(schema), declared beside the publicSharing block itself and shaped exactly like its neighbour isTenancyDisabled (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-sharingshare-link-service.ts no longer defines the predicate, it imports it; share-link-routes.ts imports it from the spec rather than from the service module, so the route probe and the redemption gate inside resolveToken read 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-links dispatcher domain imports the same predicate.

The mirror's stated justification — that importing @objectstack/plugin-sharing would invert the dependency direction, it being a dev dependency of packages/runtime — is true of that one home and not of the question. Both packages already list @objectstack/spec in dependencies, so the shared home existed all along and this de-duplication adds no dependency edge. check:undeclared-dep-imports is green on both new imports.

Behaviour is unchanged, fail-closed included: an absent publicSharing block, an absent schema, and an engine that cannot answer getSchema at all remain one answer, false, and only the boolean true enables.

Clause-② is YES — this widens a published contract face, so the PR is PARKED

Measured, not assumed:

git diff origin/main...HEAD -- packages/spec/api-surface/
  packages/spec/api-surface/data.json | 1 +
  +    "isPublicSharingEnabled (function)",

check:api-surface said 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.json gains the matching row. Both artifacts were regenerated with check:generated --fix (only the 2 it proved stale) and both gates are green at the final commit.

Consequences taken here: needs:contract-review on 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 the refactor(...)-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 declared function isPublicSharingEnabled(schema: unknown): boolean with no export keyword (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: createShareLinksDomain and handleShareLinksRequest in that file do carry export, so the scan distinguishes the two cases. packages/runtime/src/index.ts names share-links zero times.
  • packages/plugins/plugin-sharing — the predicate was exported from share-link-service.ts, but the package publishes exactly one entry (exports has only ".", files: ["dist"], types: dist/index.d.ts), and at the merge base src/index.ts re-exports only ShareLinkService and ShareLinkServiceOptions from that module: the symbol's name occurs 0 times in the entry file (positive control on the same command: ShareLinkService occurs there). A symbol unreachable from the entry is not in dist/index.d.ts.

⚠️ Declared narrowing. The stronger instrument — build both packages, swap the three changed sources back to the merge base, rebuild, byte-diff the built 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 returned VERDICT 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 passed
  • packages/runtime/src/domains/share-links-enforcement-context.test.ts — 18 passed

New: 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 boolean true enables (a truthy 'true' or 1 does 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

pnpm --filter @objectstack/spec build                                  exit 0
pnpm --filter @objectstack/spec check:generated                        2 of 15 stale: api-surface, export-origins
pnpm --filter @objectstack/spec check:generated --fix                  regenerated exactly those 2
pnpm --filter @objectstack/spec check:api-surface                      exit 0
pnpm --filter @objectstack/spec check:export-origins                   exit 0
pnpm --filter @objectstack/spec vitest run src/data/object.test.ts     191 passed
pnpm --filter @objectstack/plugin-sharing vitest share-link-eligibility 43 passed
pnpm --filter @objectstack/runtime vitest share-links-enforcement-ctx   18 passed
pnpm --filter @objectstack/spec typecheck                              exit 0 (test layer included)
pnpm --filter @objectstack/plugin-sharing typecheck                    exit 0
pnpm --filter @objectstack/runtime typecheck                           exit 0
pnpm check:nul-bytes                                                   exit 0
node scripts/check-undeclared-dep-imports.mjs                          exit 0
node scripts/check-empty-changeset.mjs --base origin/main              exit 0
node scripts/check-changeset-no-major.mjs --base origin/main           exit 0

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 at exit 99 and are recorded as NOT MEASURED, not as green; CI runs them.

Known CI flake — not this PR

If Lint & Repo Gates fails on check:merge-driver with self-test failed -- 1 failure(s) (cases and floor), that is the check-regen-pending.mjs --self-test Corepack 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-census went red on 1f294f94a, and it was mine: the edit to share-link-service.ts is +10 above line 45 and -20 above line 108, so everything below shifts by exactly -10, and row 37 of content/docs/permissions/system-context.mdx cites 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:

page anchor (was) read site (now) line at the new number vs the old one verdict
:469 :459 if (!policy.enabled && !this.permissive && !context.isSystem) { byte-identical — pure rot
:523 :513 context: context.isSystem ? SYSTEM_CTX : context, byte-identical — pure rot
:527 :517 throw context.isSystem byte-identical — pure rot
:600 :590 let permitted = context.isSystem === true || row.created_by === context.userId; byte-identical — pure rot
:630 :620 context: context.isSystem ? SYSTEM_CTX : context, byte-identical — pure rot

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 -U0 on 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 five context.isSystem reads.

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 f7db8f4fd maps this diff to exactly one hand-written page, content/docs/concepts/metadata-driven.mdx, via the symbol ObjectSchemaBase. That page's only use of the anchor is one line: "Type Safety: Compile-time type checking via the z.input of typeof 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 (@see in the publicSharing TSDoc); the new function lands at line 2328, after the const closes at 2324 — so no key, no type and no parse behaviour moved, which check:authorable-surface independently confirms by staying green. The page says nothing about share links or the predicate: publicSharing 0, isPublicSharingEnabled 0, share-link 0 — with positive controls firing on the same command and scope, ObjectSchemaBase 1 and sharingModel 6.

Widened past the mapper's floor, since it flags 3 files that yielded no anchor (share-link-routes.ts and the two generated JSONs): across content/** and docs/** markdown, isPublicSharingEnabled appears in 0 files and the mirror's prose (deliberate MIRROR) in 0 — positive controls on the identical command and scope: publicSharing 11 files, ObjectSchemaBase 2 files (metadata-driven.mdx and 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 under content/docs/releases/** was touched.

The TypeScript Type Check red on the first head was a cancellation — measured, and the cause is not what it looks like

Run 33990004034 on 1f294f94a has run-level conclusion: cancelled. Its jobs: Lint & Repo Gates failed at 20:25:02 (the census, above), Type Check · source gates succeeded 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 of 4620deb7d, whose run was created at 20:30:35: lint.yml declares concurrency: cancel-in-progress: true keyed 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 any needs.*.result that is not success and prints concluded 'cancelled' -- expected 'success'; it emits no TypeScript diagnostics of its own. cancelled means the lane never ran. Ruled out by measurement rather than assumed: a labeled event cannot be the trigger — lint.yml's pull_request: declares no types:, 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 gates and Type Check · debt ledger are completed / success, while Type Check · workspace and Lint & Repo Gates are still in_progress — read as status, not as a zero-failure count.


Generated by Claude Code

…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
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-sharing, @objectstack/runtime, @objectstack/spec, touching 2 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/plugins/plugin-sharing/src/share-link-routes.ts, packages/spec/api-surface/data.json, packages/spec/export-origins/data.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-driven.mdx (via ObjectSchemaBase (symbol, a top-level const object))
What this run could not see
  • 3 changed file(s) yielded no anchor (packages/plugins/plugin-sharing/src/share-link-routes.ts, packages/spec/api-surface/data.json, packages/spec/export-origins/data.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 135 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 53cbad9f75572600ef43bb2a18071633fd6c0f68packageMentionDocs.

Which tree this was computed on

This run read content/docs from 214ae5f5fd1fed64a1ecfe62196a5fc7344d5d66 — the merge of head 05fc7cfff31f82616e672a03e7321160e97e1848 into base 53cbad9f75572600ef43bb2a18071633fd6c0f68, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 53cbad9f75572600ef43bb2a18071633fd6c0f68 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Sep 5, 2026
…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
claude and others added 2 commits September 5, 2026 22:14
`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

Copy link
Copy Markdown
Contributor

Contract review (clause ②) PASS at head 4620deb7 — director seat, session_01TezFG8ZMrNH6n5VTNpPpdH, 2026-09-05T22:4xZ. Verdict with derived judgments (one additive spec export, both copies removed without narrowing any published face, fail-closed preserved, census anchors regenerated), semver and flags: objectstack#14935 comment 5555213275. needs:contract-review removed from this PR and the card in the same stroke.

⚠️ Not flipped ready by this seat: mergeable_state: dirty — the dispatching seat (session_01ARYe3yQTQCUFm5qPYNgKaJ) owes a merge lap (merge commit, no rebase; re-run check-system-context-census --fix on the merged tree if the os-regen driver deferred system-context.mdx). Post the new head on the card; merge-only movement is extended on request, a hand edit re-hangs the gate.

Implemented-by: session_01ARYe3yQTQCUFm5qPYNgKaJ
Reviewed-by: session_01TezFG8ZMrNH6n5VTNpPpdH


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 5, 2026 23:01
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 5, 2026
Any commits made after this event will not be merged.

Copy link
Copy Markdown
Contributor

Contract review (clause ②) — PASS extended to head 05fc7cff

Director seat, summon #15, session_01TezFG8ZMrNH6n5VTNpPpdH (os-zhuang), 2026-09-05T23:02Z. Extends PASS 5555213275 (head 4620deb7) per its own terms: merge-only head movement is re-extended, a hand edit re-hangs the gate.

Implemented-by: session_01ARYe3yQTQCUFm5qPYNgKaJ os-dev round (merge lap)
Reviewed-by: session_01TezFG8ZMrNH6n5VTNpPpdH

Delta read (4620deb7…05fc7cff, 2 commits)

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 --test on 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: give publicSharing.enabled one canonical predicate; retire the packages/runtime mirror (#14637 follow-up)

3 participants