You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 95a8435
Browse filesBrowse the repository at this point in the historyBrowse 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>
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.
Copy file name to clipboardExpand all lines: docs/qa/platform-checklist/areas/access-security.json
+9-9Lines changed: 9 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -2149,15 +2149,15 @@
2149
2149
]
2150
2150
},
2151
2151
"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)",
2153
2153
"GRANT — a permission set's systemPermissions carries the capability name",
2154
2154
"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)",
2155
2155
"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_NAMESis 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"
2157
2157
],
2158
2158
"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",
2161
2161
"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",
2162
2162
"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",
2163
2163
"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 @@
2167
2167
],
2168
2168
"acceptance": [
2169
2169
{
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)",
2171
2171
"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"
2174
2174
},
2175
2175
{
2176
2176
"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 @@
2181
2181
{
2182
2182
"clause": "a declaration whose name shadows a curated PLATFORM_CAPABILITY_NAME is refused LOUDLY — a package cannot hijack a platform-owned capability name",
2183
2183
"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)",
2185
2185
"evidence": "the refusal text"
2186
2186
},
2187
2187
{
@@ -2205,7 +2205,7 @@
2205
2205
],
2206
2206
"source": [
2207
2207
"packages/spec/liveness/capability.json (ADR-0066 D1, #5961 — the kind's own liveness ledger)",
"packages/objectql/src/registry.ts#SecurityCatalogNameConflictError (the one-holder refusal: a second holder of a capability name, a curated platform name included)",
0 commit comments