Skip to content

docs(spec): AnchorBindingContext's boot half names the stack's capability DECLARATIONS, not the not-yet-seeded sys_capability rows - #18767

Merged
os-bill merged 2 commits into
mainfrom
claude/issue-18603-anchor-binding-capability-source
Sep 17, 2026
Merged

os-bill merged 2 commits into
mainfrom
claude/issue-18603-anchor-binding-capability-source

Conversation

@os-bill

@os-bill os-bill commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Clause-②: no

Fixes #18603

Comment text only, in one file: the AnchorBindingContext docblock in packages/spec/src/security/high-privilege.ts. No predicate, type, export or accept set moves.

What the sentence said, and why a literal follower is refused

The docblock named two sources for declaredCapabilities: at boot 「the sys_capability rows carrying managed_by: 'package' provenance」, at authoring time the stack's own capabilities array. The boot half carried an ordering precondition the sentence never stated. The ADR-0090 D5 anchor binding runs BEFORE the seeder that writes those rows, so on a first boot the table is empty at exactly the moment the docblock said to read it — and this docblock's own 「omission refuses」 property then turns that emptiness into a silent refusal of every declared token: the app's own isDefault set unbindable at the everyone anchor, which is the defect #17811 introduced the input to remove, reproduced one layer in.

The boot half now names the DECLARATIONS, read through the seeder's own two-step — the ObjectQL registry first, the metadata service as the fallback — which is exactly what readDeclaredCapabilityContext (@objectstack/plugin-security, PR #18602) already implements, so the contract text and its one runtime consumer corroborate each other instead of contradicting. The sys_capability rows stay a valid source, qualified: only once the seeder has written them.

LIT — the ordering was READ, by symbol, on this branch's base

The card's line numbers were taken on PR #18602's head and were carried forward unverified. They were re-derived here by SYMBOL on origin/main 95b21b33be (this branch's merge base), packages/plugins/plugin-security/src/security-plugin.ts:

symbol line inside
const runBootstrap :3655 the boot sweep itself
await seedCatalogBuiltins(...) :3866 runBootstrap — reaches bootstrapBuiltinRoles at :3572 (defined in seedCatalogBuiltins, :3570), which seeds the everyone anchor
await bindBaselineToEveryone(...) :3888 runBootstrap — the ADR-0090 D5 bind; defined at :3583, consults describeHighPrivilegeBits at :3595
await reconcileAudienceBindingSuggestions(...) :3905 runBootstrap
await bootstrapDeclaredCapabilities(...) :3927 runBootstrap — the seeder that WRITES the managed_by: 'package' rows

:3888 and :3927 sit in one straight-line try body of one function with no branch between them, so the bind precedes the seeder. The card's conclusion holds. Three line attributions in the card's table are worth correcting for the next reader, and none of them moves the conclusion:

  • :3572 is bootstrapBuiltinRoles's call site inside the helper seedCatalogBuiltins (:3570), not a line of runBootstrap; runBootstrap reaches it at :3866.
  • :3639 is a SECOND bindBaselineToEveryone call, inside seedCatalogForOrganization (:3635) — the organization-creation hook, not the boot sweep. Only :3888 is runBootstrap's.
  • :3742 is reconcileAudienceBindingSuggestions inside the publish-materializer callback runBootstrap registers — a runtime publish path, not a boot step. The boot step is :3905.

DARK — a reading that must be ZERO, with a control proving it fires

Predicate: take git diff -U0 over packages/spec/src/security/high-privilege.ts, keep the +/- lines that are not the +++/--- headers, and drop every one that is blank or begins with *, // or /*. What remains is CODE.

leg input reading
this change git diff -U0 95b21b33be HEAD -- packages/spec/src/security/high-privilege.ts NON_COMMENT_CHANGED_LINES=0
control the same file's own d5c91dd681 (#17811), same predicate, same input shape NON_COMMENT_CHANGED_LINES=33 — it names the added import, the export interface AnchorBindingContext, its member and the whole of appDeclaredCapabilityNames

The zero is a measurement, not an absence of input: the same instrument reads 33 on a real code change to the same file. git diff --stat for this change is 17 insertions / 2 deletions, all of them comment.

Changeset — measured, not assumed

skip-changeset would be wrong: published content moves.

  • packages/spec/src/security/high-privilege.ts is NOT shipped as source. @objectstack/spec's files[] takes src/**/*.zod.ts and this file is not one — npm pack --dry-run --json lists 2021 shipped paths and does not include it, with the sibling src/security/permission.zod.ts present in the same listing as the lit control.
  • Its published reach is the EMITTED declarations, and they move. After pnpm --filter @objectstack/spec build, the new clause is present in dist/security/index.d.ts and dist/security/index.d.mts — both in that same shipped listing — the superseded spelling is absent from every built declaration file (0 files), and the docblock's unchanged neighbouring sentence (「Never synthesize this from the set under test」) is present in the same two files as the lit control.

Hence .changeset/18603-anchor-binding-declared-capabilities.md, @objectstack/spec: patch.

Verification, at 2387ad9a5c

  • pnpm --filter @objectstack/spec build — green.
  • pnpm --filter @objectstack/spec test — 486 test files, 14017 tests, all passed.
  • pnpm --filter @objectstack/spec typecheck — green.
  • pnpm --filter @objectstack/spec check:generated — all 15 generated artifacts up to date; nothing needed regenerating.
  • pnpm build — 73/73 tasks successful.
  • pnpm lint (eslint . --no-inline-config, the repo-wide population) — green, exit 0.
  • The gate families derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: all 75 run, all exit 0, reconciled with --ran (75 derived / 75 run / 0 NOT-MEASURED, derived from recorded exit codes). Three of them (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure) first answered exit 3 PREREQUISITE NOT MET on an unbuilt tree, which is not a finding; they were re-run green after pnpm build.

Acceptance notes

Nothing filable was found alongside this change. The three line-attribution corrections above are reported here rather than filed: they are a nuance in a card's evidence table, not a defect in the code, and the ordering they describe is correct.

Landing is the owning seat's — left as a draft, auto-merge not armed.


Generated by Claude Code

…lity DECLARATIONS, not the not-yet-seeded sys_capability rows

The docblock named two sources for `declaredCapabilities`: at boot the
`sys_capability` rows carrying `managed_by: 'package'` provenance, at authoring
time the stack's own `capabilities` array. The boot half has an ordering
precondition the sentence never stated. The ADR-0090 D5 anchor binding
(`bindBaselineToEveryone`) runs BEFORE `bootstrapDeclaredCapabilities`, the
seeder that writes those rows, so on a first boot the table is empty at exactly
the moment the docblock said to read it — and this docblock's own "omission
refuses" property then turns that emptiness into a silent refusal of every
declared token, the defect #18535 removed, reproduced one layer in.

The boot half now names the DECLARATIONS, read through the seeder's own two-step
(the ObjectQL registry first, the metadata service as the fallback) — the same
source the authoring-time half already named, and the read
`readDeclaredCapabilityContext` in `@objectstack/plugin-security` already
implements. The `sys_capability` rows stay a valid source, qualified: only once
the seeder has written them.

Comment text only. No predicate, type, export or behaviour changes.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
…t boot-source clause

The docblock's published reach is the emitted declarations, not the source:
`src/security/high-privilege.ts` is not a `.zod.ts`, so `files[]` does not ship
it, but `dist/security/index.d.ts` and `dist/security/index.d.mts` are shipped
and carry the TSDoc — measured with `npm pack --dry-run`. Published content
moves, so this is a `patch`, not `skip-changeset`.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@os-bill os-bill added domain:spec priority:p2 Medium: important, M3 labels Sep 17, 2026 — with Claude
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/security/high-privilege.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/security/high-privilege.ts) — pages documenting those are invisible to this run
  • 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 — 136 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 74327d3f054dde7d9d59fd19a88ae57690dbc266packageMentionDocs.

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 domain:spec priority:p2 Medium: important, M3 size/s tooling

Projects

None yet

2 participants