Skip to content

Commit 95a8435

Browse files
feat(plugin-security): a package's declared capabilities are served by the registry alone — delete the declared capability seeder and capability-name-collision (ADR-0131 D3, #15204 stage 6b-1c) (#22711)
Part of #15204 Clause-②: no Stage 6b-1c of the ADR-0131 D3 cutover: the declared capability seeder and its collision module are deleted. This PR deletes code and changes no surviving seeder. It lands before stage 5 (C9) under the maintainer's ruling 6104498446 on #15204, after stage 6b-1b (#22709, merged as `7aa8d51c`); this branch merged `main` twice, with no rebase. ## What is deleted - `plugin-security/src/bootstrap-declared-capabilities.ts` (547 lines) and its test (935). - `plugin-security/src/capability-name-collision.ts` (224) and its test (202). - Their call and import in `security-plugin.ts`, and the five `capability-name-collision` exports in `index.ts`: `CAPABILITY_NAME_COLLISION`, `capabilityNameCollisionDiagnostic`, `formatCapabilityNameCollisionDiagnostic`, `reportCapabilityNameCollisions`, `CapabilityNameCollisionDiagnostic`. Outside `plugin-security` the only references are CHANGELOGs, so no consumer exists. - Stage 6b-1a's transitional view in `builtin-capabilities.ts` (`withoutPlatformCapabilityDeclarations`, `withoutPlatformCapabilityItems`, `isPlatformCapabilityDeclaration`) and its filter in `declared-capability-context.ts`. The 6b-1a ACCEPT on the card records that these go with this seeder. `registerBuiltinCapabilities` stays. - The declared-capability sections of `bootstrap-seed-round-trips.test.ts` (it imported the deleted module). ## FROM → TO - FROM: a package's declared capability got a `managed_by: 'package'` `sys_capability` row at boot. TO: the registry serves the declaration, and no row is written. The security catalog read, `GET /api/v1/meta/capability` and the anchor predicates already read it from the registry. - FROM: a name collision reported by the runtime diagnostic. TO: the registry's one-holder rule (`objectql`, `SecurityCatalogNameConflictError`, `422`) refuses it at boot. It already refused it before the diagnostic could run. ## Measurements - **The one-holder refusal still holds.** `runtime/src/standalone-stack-security-catalog-one-holder.test.ts` passes on this tree (with the closure rebuilt). It covers two packages of one artifact sharing a capability name (`capability/regional.export`, `422`, holder `com.test.first`), and a door-less second stack declaring a name the first holds. Deleting the diagnostic loses no refusal. - **Dropping the filter in `declared-capability-context.ts` does not change the anchor verdict.** The context now carries the nine curated declarations beside the package ones. The spec's platform floor (`appDeclaredCapabilityNames` in `high-privilege.ts`) skips every name in `PLATFORM_CAPABILITY_NAMES`, so the curated declarations excuse nothing. A registry that holds only those nine gives the same verdict as no context at all. `builtin-capabilities.test.ts` pins both halves. - **No row for a declared capability.** The plugin's real boot (`builtin-capabilities.boot.test.ts`) failed 4 of 4 census cases before the pin changed: it expected the `field.export` package row, and the row was gone. The pin is no `sys_capability` row at all (6b-1b removed the curated ones), with zero writes on a fresh and on a seeded database, in both postures. The runtime pin in `standalone-stack-seeder-declaration-copy.test.ts` now asserts no row for `probe.export`. ## Outside the landing zone, and why - `packages/spec/liveness/capability.json`: `check:liveness` went red because five rows cited the deleted file. They are re-anchored to the readers that remain. `name`: the one-holder rule, the anchor context and the lint. `packageId`: `metadata-manager.ts#unregisterPackage` / `#collectPackageMembers`. `label` / `description` / `scope`: objectui's metadata-admin list and item form at the pinned `.objectui-sha` (display). - `docs/qa/platform-checklist/areas/access-security.json`: the capability item cited the deleted file (`ANCHOR FILE NOT FOUND`) and told the runner to read the package row. It now reads the metadata door and expects no row, and it anchors the shadow refusal on `SecurityCatalogNameConflictError`. - `scripts/engine-double-contract.baseline.json`: the deleted test's entry, which the gate reports as `RECONCILED … Delete the entry`. - Comments that would otherwise be false, reworded only: `objectql/src/engine.ts`, `runtime/src/app-plugin.ts`, `lint/src/validate-capability-references.ts`, `examples/app-showcase/src/security/capabilities.ts`, and five comments in `packages/spec/src` (`kernel/metadata-plugin.zod.ts`, `kernel/metadata-type-schemas.ts`, `meta-spelling/manifest-collection-spelling.ts`, `security/capabilities.ts`, `security/high-privilege.ts`). None changes a `.describe()`, schema, export or generated page; contract record 6105283656 judged each. - Docs made true after the deletion: `content/docs/permissions/authorization.mdx`, `capabilities.mdx`, and `permission-sets.mdx:150` (a declared capability is no longer seeded as a row). ## Acceptance notes - In `security-plugin.ts`, the `materializedCapabilityNames` stub and 6b-1b's removed call are both gone (landing round, after #22709 merged). No reference to either seeder remains. - With the filter gone, `readDeclaredCapabilityContext`'s metadata-service fallback is no longer reached once `SecurityPlugin.start` has registered the curated declarations. Every composition registers a stack's collections through the engine, so the registry already holds what that fallback would read. The fallback goes with `declared-capability-context.ts`'s later move (6b-2), not here (only delete). - Not edited, as retired or dying modules (batch #310 item 4): `seed-refusal-diagnostics.ts`, `seed-refusal-sink.ts` and `seed-name-lookup.ts` name the deleted seeder only in comments. `bootstrap-system-capabilities.ts` and `sys-capability.object.ts` (stage 8) are not touched either. - `check:platform-checklist` still reports 7 problems that are not this PR's: absent symbols in `metadata-protocol/src/protocol.ts` and `service-storage/src/attachment-access-hooks.ts`, which are red on `main` too (watchdog #22758 on `149294c02c`). ## Gates Build first: the closures of `plugin-security`, `runtime` and `dogfood`. The results below are against the head named in the report on #15204. - `plugin-security`: typecheck green; full suite 196 files, 4003 passed / 45 skipped (landing head `b3c8d0da`). - `runtime`: typecheck green; `standalone-stack-seeder-declaration-copy` and `standalone-stack-security-catalog-one-holder` 19 / 19. - Typecheck green for `objectql`, `lint` and `example-showcase` (comment edits). - Dogfood: `security-catalog-showcase`, `audit-log-audit-capability`, `showcase-permission-seeding` 47 / 47. - `check:liveness`, `check:engine-double-contract` and `check:adr-0087-registration` green. The derived `dispatch-gates --commands` list ran and was reconciled with `--ran`: 130 derived, 130 run on the landing head (report 6105207325). - Changed lines: 2,632 (+186 / -2,446, 29 files) against `7aa8d51c`, under 3,000. --- _Generated by [Claude Code](https://claude.ai/code/session_01RtYXEFNnKGHihJTBNxGX71)_ _Landing update by the epic seat (`session_01Rerax7QTjKMPCUZxQUtPFR`): ordering, size, gates and the out-of-zone list updated to the landing head; contract record 6105283656 (PASS)._ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a18c514 commit 95a8435

29 files changed

Lines changed: 186 additions & 2446 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
'@objectstack/plugin-security': minor
3+
'@objectstack/spec': patch
4+
'@objectstack/objectql': patch
5+
'@objectstack/runtime': patch
6+
'@objectstack/lint': patch
7+
---
8+
9+
feat(plugin-security): a package's declared capabilities are served by the registry alone — the declared-capability seeder and its collision diagnostic are deleted
10+
11+
Clause-②: no
12+
13+
`SecurityPlugin` no longer seeds a `sys_capability` row for a capability a package declares (`defineCapability` / a stack's `capabilities`). The registry is that capability's one home (ADR-0131 D3): the security catalog read (`createSecurityCatalogReader`), `GET /api/v1/meta/capability` and the anchor predicates' declared-capability context read the declaration there, as they already did. This change does not alter how the platform's curated capabilities are served.
14+
15+
A capability name declared by two packages, or a package declaring a curated platform capability name, is refused at boot by the registry's one-holder rule (`SecurityCatalogNameConflictError`, `422`), which already refused it before the deleted runtime diagnostic could run.
16+
17+
In `@objectstack/spec`, `@objectstack/objectql`, `@objectstack/runtime` and `@objectstack/lint`, the shipped comments and the capability liveness evidence (`liveness/capability.json`) now name the registry instead of the retired declared-capability seeder. Their behaviour is unchanged.
18+
19+
Removed from `@objectstack/plugin-security`'s public exports, with no replacement:
20+
21+
- `CAPABILITY_NAME_COLLISION`
22+
- `capabilityNameCollisionDiagnostic`
23+
- `formatCapabilityNameCollisionDiagnostic`
24+
- `reportCapabilityNameCollisions`
25+
- `CapabilityNameCollisionDiagnostic` (type)
26+
27+
FROM → TO:
28+
29+
- FROM a package's declared capability materialized as a `managed_by: 'package'` `sys_capability` row at boot → TO the declaration served by the registry, with no row written. Read it with `GET /api/v1/meta/capability/:name` or the security catalog read, not from `sys_capability`.
30+
- FROM a name collision reported at boot through `reportCapabilityNameCollisions` → TO the same collision refused by the registry's one-holder rule. Delete any import of the five names above; there is nothing to call in their place.

‎content/docs/permissions/authorization.mdx‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,19 @@ export default defineStack({
195195
});
196196
```
197197

198-
At boot `bootstrapDeclaredCapabilities`
199-
(`packages/plugins/plugin-security/src/bootstrap-declared-capabilities.ts`) seeds
200-
each declaration into `sys_capability` with **`managed_by: 'package'` + `package_id`**
201-
provenance — idempotent, re-seeded on upgrade, and it **never clobbers**
202-
admin-authored rows, refuses to hijack a curated platform capability, and refuses
203-
to write into another package's capability.
198+
At boot each declaration becomes a registry item owned by its package — the
199+
capability's one home (ADR-0131 D3). No `sys_capability` row is written for it.
200+
The registry's one-holder rule refuses a second holder of a name at boot with a
201+
`422`, so a package can neither take a curated platform capability nor take
202+
over another package's.
204203

205204
This **replaces the implicit back-door** where a capability existed only as an
206205
untitled placeholder derived from whatever a permission set happened to reference
207206
in `systemPermissions[]`. That derivation is retired: a reference with no
208207
declaration still matches by name at runtime but gets no `sys_capability` row.
209-
An explicit `defineCapability` takes precedence and — for a derived placeholder
210-
an older database still holds — **claims** it, upgrading the row to package
211-
provenance with the authored label/description/scope. This is the ADR-0066 D1
208+
An explicit `defineCapability` is the attributable form: its label,
209+
description, scope and owning package live on the registry item, and it gets
210+
no row either. This is the ADR-0066 D1
212211
direction: retire the implicit `managed_by`-guessing back-doors in favour of
213212
explicit, attributable declarations.
214213

@@ -557,8 +556,8 @@ The complete, prioritized gap map lives in issue **#2561** (the production
557556
`sys_capability` row.
558557
A **package** now DEFINES its own capabilities explicitly via
559558
[`defineCapability` / `stack.capabilities`](#package-capability-declaration-adr-0066-d1),
560-
seeded with `managed_by:'package'` + `package_id` provenance by
561-
`bootstrap-declared-capabilities.ts` — replacing the implicit
559+
served by the registry with owning-package provenance (no row is written for
560+
it) — replacing the implicit
562561
derive-from-`systemPermissions` back-door (now retired).
563562
The authoring lint (ADR-0066 ⑨) is also **landed**: `validateCapabilityReferences`
564563
(`@objectstack/lint`) warns at author time (`os validate` / `os lint`) when a

‎content/docs/permissions/capabilities.mdx‎

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ capability name travels through five stations:
4949
② REGISTER AppPlugin → metadata.registerInMemory('capability', name)
5050
│ (also: **/*.capability.ts files)
5151
▼
52-
③ SEED bootstrapDeclaredCapabilities → sys_capability row
53-
│ managed_by: 'package' + package_id provenance
52+
③ SERVE registry item (capability kind) → catalog read, /meta door
53+
│ owning-package provenance, one holder per name
5454
▼
5555
④ GRANT permission set: systemPermissions: ['export_data']
5656
│ assigned to positions / users
@@ -62,12 +62,13 @@ capability name travels through five stations:
6262
1. **Declare** — `defineCapability(...)` on the stack's `capabilities` array,
6363
or a `*.capability.ts` / `*.capability.yml` file the filesystem loader globs.
6464
2. **Register** — at boot, `AppPlugin` puts each stack-declared capability into
65-
the metadata registry under the `capability` kind, so boot seeders and
66-
runtime resolvers can list it.
67-
3. **Seed** — `bootstrapDeclaredCapabilities` (in `@objectstack/plugin-security`)
68-
reads them back and upserts each one into `sys_capability` with
69-
`managed_by: 'package'` and `package_id` provenance. This is what makes the
70-
capability *attributable* and package uninstall well-defined.
65+
the metadata registry under the `capability` kind, so the security catalog
66+
and runtime resolvers can list it.
67+
3. **Serve** — the registry is the capability's one home. It records the
68+
owning package as provenance, which is what makes the capability
69+
*attributable*. The security catalog read and
70+
`GET /api/v1/meta/capability/:name` answer its definition. No
71+
`sys_capability` row is written for it.
7172
4. **Grant** — an administrator (or a permission set your package ships) lists
7273
the name in [`systemPermissions`](/docs/permissions/permission-metadata#system-permissions).
7374
5. **Check** — a resource lists the name in `requiredPermissions`, and the
@@ -157,7 +158,8 @@ plain names. Three consequences worth internalising before you pick one:
157158

158159
You cannot take a platform name. A declaration whose `name` collides with a
159160
curated platform capability (`manage_users`, `manage_metadata`,
160-
`setup.access`, …) is refused loudly at boot — those are platform-owned.
161+
`setup.access`, …) is refused at boot by the registry's one-holder rule
162+
(`422`) — those are platform-owned, and the platform declares them itself.
161163

162164
## "Or" is one capability, granted twice
163165

@@ -193,36 +195,29 @@ the same way; a user who may edit the object still reads all of its list views,
193195
so a save of what they loaded keeps them.
194196
</Callout>
195197

196-
## What the seeder does, and what it refuses
198+
## One holder per name
197199

198-
`bootstrapDeclaredCapabilities` runs on `kernel:ready` and is idempotent: it
199-
re-seeds on every boot, so the row always reflects the shipped declaration. Four
200-
outcomes are refusals rather than writes, and each one leaves the capability
201-
behaving differently:
200+
Each capability name has exactly one holder in the registry. When a package
201+
registers a declaration, the registry records that package as its owner.
202+
Package uninstall then removes it with the rest of the package. Two cases are
203+
refused at boot, with a `422` that names the name and both holders:
202204

203205
| Situation | Outcome |
204206
|:--|:--|
205-
| No resolvable owning package (`_packageId` and `packageId` both absent) | **Refused — no row is written.** The declaration is inert |
206-
| Name collides with a curated platform capability | Refused; the platform keeps its own definition |
207-
| A row already exists owned by a **different** package | Skipped loudly; a package never writes into a foreign record |
208-
| A row was authored by an administrator (`managed_by: 'admin'`) | Never clobbered |
209-
210-
A pre-existing `managed_by: 'platform'` row for a *non-curated* name is a
211-
different case: that is the untitled placeholder the old implicit back-door
212-
derived from whatever a permission set happened to reference, and an explicit
213-
declaration **claims** it — upgrading the row to package provenance with your
214-
authored label, description and scope. The provenance and composition rules
215-
behind that behaviour are set out in
216-
[Package capability declaration](/docs/permissions/authorization#package-capability-declaration-adr-0066-d1).
207+
| The name is a curated platform capability | Refused; the platform holds its own definition |
208+
| Another installed package already declares the name | Refused; a package never takes over another package's capability |
209+
210+
Declaring a capability writes no `sys_capability` row. Its label, description,
211+
scope and provenance live on the registry item, and that is what the catalog
212+
read and the metadata door serve.
217213

218214
<Callout type="info">
219-
**The `sys_capability` row is a catalogue, not a gate.** It carries the label,
220-
the scope and the provenance that make a capability reviewable, attributable and
221-
uninstallable — but no authorization check reads it. Permission-set grants and
222-
resource `requiredPermissions` match capability names **as strings**; the only
223-
production reader of the table is the declared-capability boot seeder. The row's `active`
224-
flag is a catalogue/visibility flag with no authorization effect: clearing it
225-
revokes nothing. Withdraw a capability by withdrawing the **grant**.
215+
**No authorization check reads a `sys_capability` row.** Permission-set grants
216+
and resource `requiredPermissions` match capability names **as strings**. The
217+
boot writes no row for a capability, declared or curated: both are served by
218+
the registry. A row an older database still holds is a catalogue only, and
219+
clearing its `active` flag revokes nothing. Withdraw a capability by
220+
withdrawing the **grant**.
226221
</Callout>
227222

228223
## `capability` is code-only
@@ -267,7 +262,7 @@ to store arbitrary JSON on the authorization surface.
267262
- [ ] `label` and `description` are written for the administrator who will grant it in Setup
268263
- [ ] `scope` is `org` unless the privilege really is platform-wide
269264
- [ ] The declaration is reachable from the stack (`capabilities: [...]`) or a `*.capability.ts` file
270-
- [ ] The package resolves an owning package id — otherwise the seeder writes no row
265+
- [ ] No other installed package declares the same name — the registry refuses a second holder at boot
271266
- [ ] Something actually **requires** the name (`requiredPermissions`), and something **grants** it (`systemPermissions`)
272267

273268
## See also

‎content/docs/permissions/permission-sets.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ the anchor decides *which* unit `unit*` means.
150150
- **Capabilities** are named privileges such as `manage_users`,
151151
`manage_metadata`, `setup.access`, `studio.access`. The platform's curated
152152
ones are declared `capability` metadata served by the registry
153-
(`GET /api/v1/meta/capability`) and have no `sys_capability` row; a
154-
capability a package declares with `defineCapability` is also seeded as a
155-
`sys_capability` row.
153+
(`GET /api/v1/meta/capability`); a capability a package declares with
154+
`defineCapability` is served the same way, under its owning package. Neither
155+
has a `sys_capability` row.
156156
Grant them via `systemPermissions`; declare them as prerequisites via an
157157
object/field/app `requiredPermissions` (an **AND**-gate checked before any
158158
CRUD grant).

‎docs/qa/platform-checklist/areas/access-security.json‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,15 +2149,15 @@
21492149
]
21502150
},
21512151
"variants": [
2152-
"DEFINE — capability metadata authored via defineCapability, bootstrapped to a sys_capability row",
2152+
"DEFINE — capability metadata authored via defineCapability, served by the registry (no sys_capability row is written for it — ADR-0131 D3)",
21532153
"GRANT — a permission set's systemPermissions carries the capability name",
21542154
"REQUIRE — an action's requiredPermissions lists it; invocation on the platform action path is denied unless a granted set carries it (ADR-0066 D4 actionPermissionError)",
21552155
"REGRANT — the grant is withdrawn from the permission set and restored; the gate must follow both edits, not cache the first answer",
2156-
"shadow-refusal — a declaration whose name collides with PLATFORM_CAPABILITY_NAMES is refused loudly"
2156+
"shadow-refusal — a declaration whose name collides with PLATFORM_CAPABILITY_NAMES, or with a name another package declares, is refused loudly by the registry's one-holder rule"
21572157
],
21582158
"steps": [
2159-
"boot showcase isolated; confirm the declared capability bootstrapped: GET the sys_capability row for showcase.export_data (bootstrap-declared-capabilities.ts upsertPackageCapability writes name/label/description/scope)",
2160-
"read its fields — label falls back to humanize(name), description to 'Capability <name>.' when absent (capabilityRowFields)",
2159+
"boot showcase isolated; confirm the declared capability is served: GET /api/v1/meta/capability/showcase.export_data answers its declared name/label/description/scope, and no sys_capability row names it",
2160+
"read its fields — they are the declaration's own; nothing is transcribed into a row",
21612161
"identify which platform action-invoke surface this build exposes (dispatcher /actions domain · MCP run_action · in-process ql.executeAction/invokeBusinessAction) and record it; do NOT probe /api/v1/actions/... or /api/v1/data/:object/actions/... — @objectstack/rest mounts no action-invoke route by design (#7680), so a 404 there scores nothing",
21622162
"on that surface, INVOKE showcase_zoo_perm_held (requires showcase.export_data) as the GRANTED member holding OpsPermissionSet, then as a NON-granted member: granted executes, non-granted is refused with the actionPermissionError text naming the action and the missing capability",
21632163
"REGRANT leg: withdraw showcase.export_data from OpsPermissionSet's systemPermissions, re-invoke as the (formerly) granted member → now refused; restore the grant, re-invoke → executes again",
@@ -2167,10 +2167,10 @@
21672167
],
21682168
"acceptance": [
21692169
{
2170-
"clause": "the package-declared capability materializes as a sys_capability row at boot with its authored identity (name is the upsert key; label/description apply their documented fallbacks)",
2170+
"clause": "the package-declared capability is served by the registry with its authored identity, and no sys_capability row is written for it (ADR-0131 D3)",
21712171
"oracle": "api",
2172-
"verify": "the sys_capability read for showcase.export_data matches the declaration; label/description fallbacks hold when omitted",
2173-
"evidence": "the row read + the declaration source"
2172+
"verify": "GET /api/v1/meta/capability/showcase.export_data matches the declaration; the sys_capability read finds no row for it",
2173+
"evidence": "the metadata read + the empty row read + the declaration source"
21742174
},
21752175
{
21762176
"clause": "the three-way resolves by NAME on the surface that actually enforces it — the PLATFORM action path (ADR-0066 D4 actionPermissionError, reached via the dispatcher /actions domain, the MCP run_action bridge, or ql.executeAction): invoking an action that REQUIREs the capability is refused for a member whose granted sets do not carry it and executes for one that does, the refusal follows a grant WITHDRAWN from the permission set, and execution returns when it is re-granted — grant / deny / regrant, all three legs",
@@ -2181,7 +2181,7 @@
21812181
{
21822182
"clause": "a declaration whose name shadows a curated PLATFORM_CAPABILITY_NAME is refused LOUDLY — a package cannot hijack a platform-owned capability name",
21832183
"oracle": "build",
2184-
"verify": "the shadow declaration is rejected at authoring/boot with a located error naming the collision (bootstrap-declared-capabilities.ts guard)",
2184+
"verify": "the shadow declaration is rejected at authoring/boot with a located error naming the collision (the registry's one-holder refusal, SecurityCatalogNameConflictError)",
21852185
"evidence": "the refusal text"
21862186
},
21872187
{
@@ -2205,7 +2205,7 @@
22052205
],
22062206
"source": [
22072207
"packages/spec/liveness/capability.json (ADR-0066 D1, #5961 — the kind's own liveness ledger)",
2208-
"packages/plugins/plugin-security/src/bootstrap-declared-capabilities.ts#upsertPackageCapability (upsertPackageCapability, capabilityRowFields, PLATFORM_CAPABILITY_NAMES guard)",
2208+
"packages/objectql/src/registry.ts#SecurityCatalogNameConflictError (the one-holder refusal: a second holder of a capability name, a curated platform name included)",
22092209
"packages/lint/src/validate-capability-references.ts#known (authoring lint known-name set)",
22102210
"examples/app-showcase/src/security/{capabilities.ts,permission-sets.ts} (DEFINE + GRANT fixtures)",
22112211
"packages/runtime/src/action-execution.ts#actionPermissionError (actionPermissionError — ADR-0066 D4, the single-sourced REQUIRE-side gate every invoke surface calls)",

‎examples/app-showcase/src/security/capabilities.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ import { defineCapability } from '@objectstack/spec';
2929
* ['showcase.export_data']`.
3030
*
3131
* `packageId` is the ADR-0086 D3 AUTHOR-DECLARED provenance — the documented
32-
* fallback the seeder reads when the registry has not stamped `_packageId`
33-
* (`cap._packageId ?? cap.packageId` in `bootstrapDeclaredCapabilities`).
34-
* Without an owning package the declaration is NOT materialized into
32+
* fallback the declared-capability seeder read when the registry had not
33+
* stamped `_packageId` (`cap._packageId ?? cap.packageId`; that seeder is
34+
* retired by ADR-0131 D3, and no row is written for a declared capability).
35+
* Without an owning package the declaration was NOT materialized into
3536
* `sys_capability` (a `managed_by:'package'` row with no `package_id` would
3637
* make uninstall undefined), which left `OpsPermissionSet` granting a
3738
* capability that never existed — one boot `warn` and an inert security

‎packages/lint/src/validate-capability-references.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* 1. the built-in platform capabilities (`PLATFORM_CAPABILITY_NAMES`),
1818
* 2. every capability the stack DECLARES via `defineCapability`
1919
* (`stack.capabilities`) — the explicit, package-provenanced declaration
20-
* (ADR-0066 D1), materialized at boot by `bootstrapDeclaredCapabilities`,
20+
* (ADR-0066 D1), served at runtime by the registry (ADR-0131 D3),
2121
* 3. every capability a permission set in this stack GRANTS via
2222
* `systemPermissions` (granting a capability also declares it), and
2323
* 4. any `sys_capability` row shipped as seed data.

‎packages/objectql/src/engine.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3429,8 +3429,8 @@ const METADATA_ARRAY_KEYS = [
34293429
// `permissions` (#5870, #4967 Part 2): the ONLY seam that stamps
34303430
// ADR-0010 provenance is `registerItem` → `applyProtection`, so a
34313431
// collection missing from this list reaches no registry with a
3432-
// `_packageId`. `bootstrapDeclaredCapabilities` resolves the owning
3433-
// package as `cap._packageId ?? cap.packageId`; while `capabilities`
3432+
// `_packageId`. The declared-capability seeder (retired by ADR-0131 D3)
3433+
// resolved the owning package as `cap._packageId ?? cap.packageId`; while `capabilities`
34343434
// sat outside this list the first half could never be satisfied and
34353435
// `readDeclared(ql, 'capability')` returned nothing, which made the
34363436
// author-side `packageId` — documented as the FALLBACK — mandatory,

0 commit comments

Comments
 (0)