Commit 5100c42
docs(spec): AnchorBindingContext's boot half names the stack's capability DECLARATIONS, not the not-yet-seeded sys_capability rows (#18767)
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 95b21b3 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](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2085be2 commit 5100c42
2 files changed
Lines changed: 64 additions & 2 deletions
File tree
- .changeset
- packages/spec/src/security
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
35 | | - | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
36 | 51 | | |
37 | 52 | | |
38 | 53 | | |
| |||
0 commit comments