Skip to content

Commit 720b836

Browse files
committed
docs(spec): AnchorBindingContext's boot half names the stack's capability 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>
1 parent 95b21b3 commit 720b836

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

‎packages/spec/src/security/high-privilege.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,23 @@ function coerceRecord(v: unknown): Record<string, unknown> | undefined {
3131
* The predicates are pure and synchronous — they read one permission-set
3232
* definition and nothing else — so the one fact they cannot discover for
3333
* themselves is which capability names this stack DECLARED. The caller holds
34-
* it: at boot from the `sys_capability` rows carrying `managed_by: 'package'`
35-
* provenance, at authoring time from the stack's own `capabilities` array.
34+
* it: at boot from the stack's own `capabilities:` DECLARATIONS — and from
35+
* the `sys_capability` rows carrying `managed_by: 'package'` provenance only
36+
* ONCE THE SEEDER HAS WRITTEN THEM — at authoring time from the stack's own
37+
* `capabilities` array.
38+
*
39+
* ⚠️ [#18603] Why the boot half names the DECLARATIONS and not the rows: the
40+
* ADR-0090 D5 anchor binding (`bindBaselineToEveryone` in
41+
* `@objectstack/plugin-security`) runs BEFORE `bootstrapDeclaredCapabilities`,
42+
* the seeder that WRITES those `managed_by: 'package'` rows, and that order is
43+
* fixed by two other constraints — the binding must follow the seeding of the
44+
* `everyone` anchor it binds to, and precede the audience-binding suggestion
45+
* reconciliation. On a first boot the table is therefore EMPTY at the bind
46+
* moment, and "omission refuses" below turns that emptiness into a silent
47+
* refusal of every declared token. A boot caller reads the declarations
48+
* through the seeder's own two-step — the ObjectQL registry first, the
49+
* metadata service as the fallback — as `readDeclaredCapabilityContext`
50+
* (`@objectstack/plugin-security`) does.
3651
*
3752
* ⛔ Never synthesize this from the set under test. The point of the input is
3853
* that a set cannot vouch for its own tokens; a "declared" list derived from

0 commit comments

Comments
 (0)