Skip to content

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

Merged
objectstack-fleet[bot] merged 14 commits into
mainfrom
claude/issue-15204-s6b1c-declared-capability-seeder
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 14 commits into
mainfrom
claude/issue-15204-s6b1c-declared-capability-seeder

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

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 feat(plugin-security): delete the curated capability seeder — the boot writes no sys_capability row for the platform's capabilities (ADR-0131 D3, #15204 stage 6b-1b) #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 Release version 0.4.0 #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 check:platform-checklist is red on main #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

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).

…b1c-declared-capability-seeder

# Conflicts:
#	packages/plugins/plugin-security/src/bootstrap-declared-capabilities.ts
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 10, 2026
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/lint, @objectstack/objectql, @objectstack/plugin-security, @objectstack/runtime, @objectstack/spec, touching 37 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/lint/src/validate-capability-references.ts, packages/plugins/plugin-security/src/index.ts, packages/spec/liveness/capability.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via DEFAULT_METADATA_TYPE_REGISTRY (symbol, a top-level const object))
  • content/docs/data-modeling/objects.mdx (via permissionSets (symbol, a field of interface SeedOptions))
  • content/docs/permissions/authorization.mdx (via permissionSets (symbol, a field of interface SeedOptions), sys_capability (literal, a string literal in bootstrapDeclaredCapabilities; a string literal in upsertPackageCapability), /api/v1/meta/capability (route, a path literal in a comment in start))
  • content/docs/permissions/capabilities.mdx (via permissionSets (symbol, a field of interface SeedOptions), sys_capability (literal, a string literal in bootstrapDeclaredCapabilities; a string literal in upsertPackageCapability), /api/v1/meta/capability (route, a path literal in a comment in start))
  • content/docs/permissions/delegated-administration.mdx (via permissionSets (symbol, a field of interface SeedOptions))
  • content/docs/permissions/explain.mdx (via permissionSets (symbol, a field of interface SeedOptions))
  • content/docs/permissions/permission-sets.mdx (via permissionSets (symbol, a field of interface SeedOptions), sys_capability (literal, a string literal in bootstrapDeclaredCapabilities; a string literal in upsertPackageCapability), /api/v1/meta/capability (route, a path literal in a comment in start))
  • content/docs/permissions/permissions-matrix.mdx (via ownedBy (symbol, a field of interface CapabilityNameCollisionDiagnostic))
  • content/docs/permissions/positions.mdx (via permissionSets (symbol, a field of interface SeedOptions))
  • content/docs/permissions/profiles.mdx (via permissionSets (symbol, a field of interface SeedOptions))
  • content/docs/permissions/sharing-rules.mdx (via ownedBy (symbol, a field of interface CapabilityNameCollisionDiagnostic))
  • content/docs/plugins/adding-a-metadata-type.mdx (via BUILTIN_METADATA_TYPE_SCHEMAS (symbol, a top-level const object), DEFAULT_METADATA_TYPE_REGISTRY (symbol, a top-level const object))
  • content/docs/protocol/objectql/security.mdx (via ownedBy (symbol, a field of interface CapabilityNameCollisionDiagnostic))
  • content/docs/protocol/objectui/concept.mdx (via DEFAULT_METADATA_TYPE_REGISTRY (symbol, a top-level const object))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx (via MetadataTypeSchema (symbol, a top-level const))
  • content/docs/releases/v15.mdx (via sys_capability (literal, a string literal in bootstrapDeclaredCapabilities; a string literal in upsertPackageCapability))
  • content/docs/releases/v17/17-0.mdx (via ownedBy (symbol, a field of interface CapabilityNameCollisionDiagnostic), permissionSets (symbol, a field of interface SeedOptions))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/lint/src/validate-capability-references.ts, packages/plugins/plugin-security/src/index.ts, packages/spec/liveness/capability.json, …) — pages documenting those are invisible to this run
  • 12 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 147 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 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 60d950119f474073f1d3157c1fe88b54ea50e54c — the merge of head b3c8d0daf228a5de710155bdf89841ade7f60f1c into base 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 60d950119f474073f1d3157c1fe88b54ea50e54c && git checkout 60d950119f474073f1d3157c1fe88b54ea50e54c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610 b3c8d0daf228a5de710155bdf89841ade7f60f1c && git checkout -B drift-repro 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610 && git merge --no-ff b3c8d0daf228a5de710155bdf89841ade7f60f1c

node scripts/docs-audit/affected-docs.mjs --json 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 7aa8d51c0ffb2f34f41de9610b90557eb3e0b610 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ys_capability row, and a second holder is refused by the one-holder rule
…idence changed; drop the curated-row sentence 6b-1b makes false
…b1c-declared-capability-seeder

# Conflicts:
#	packages/plugins/plugin-security/src/security-plugin.ts
…ared capability in the registry, where a real boot holds it
…b1c-declared-capability-seeder

# Conflicts:
#	content/docs/permissions/authorization.mdx
#	content/docs/permissions/capabilities.mdx
#	packages/plugins/plugin-security/src/bootstrap-declared-capabilities.test.ts
#	packages/plugins/plugin-security/src/bootstrap-seed-round-trips.test.ts
#	packages/plugins/plugin-security/src/builtin-capabilities.boot.test.ts
#	packages/plugins/plugin-security/src/security-plugin.ts
…e registry is a declared capability's one home
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

os-dev-report

{
  "issue": 15204,
  "stage": "6b-1c",
  "round": "landing round (ACCEPT 6101787808; the maintainer's ruling 6104498446; #22709 merged as 7aa8d51c)",
  "status": "done",
  "branch": "claude/issue-15204-s6b1c-declared-capability-seeder",
  "pr": "https://github.com/objectstack-ai/objectstack/pull/22711",
  "head": "b3c8d0da",
  "session": "session_01RtYXEFNnKGHihJTBNxGX71",
  "size": "2632 changed lines (+186 / -2446, 29 files) against the new merge base 7aa8d51c, under 3,000",
  "summary": "Two merges of origin/main, no rebase and no force-push. First, e4d87817 (base d7b26df5: stage 1 and 2b): the one conflict was the import block in security-plugin.ts, resolved by keeping stage 1's declareEveryoneBaseline import and dropping the transitional-view import. Second, b4791345 (base 7aa8d51c, with 6b-1b and stage 3), six conflicts: (1) security-plugin.ts: the stub `const materializedCapabilityNames` and 6b-1b's removed call are deleted together with the declared seeder call; no live reference to bootstrapSystemCapabilities, bootstrapDeclaredCapabilities, withoutPlatformCapabilityDeclarations or materializedCapabilityNames remains. (2) bootstrap-declared-capabilities.test.ts: modify/delete, kept deleted. (3) bootstrap-seed-round-trips.test.ts: both seeder imports dropped. (4) builtin-capabilities.boot.test.ts: the census is now empty, no row curated or declared, and the pin asserts zero sys_capability writes on a fresh and on a seeded database. (5, 6) capabilities.mdx and authorization.mdx: merged to say that neither a curated nor a declared capability has a row. Also rewrote the permission-sets.mdx:150 clause ('is also seeded as a sys_capability row'): a declared capability is served by the registry under its owning package, and neither kind has a row.",
  "commits_this_round": [
    "e4d87817 merge origin/main (d7b26df5)",
    "a8c5a6fd test(plugin-security): security-plugin.test.ts's anchor-binding boot now holds the stack's declared capability in the registry, where a real boot holds it",
    "b4791345 merge origin/main (7aa8d51c, carries 6b-1b)",
    "b3c8d0da docs(spec): five comments in packages/spec/src that named the declared-capability seeder (comment-only)"
  ],
  "deviations": [
    "a8c5a6fd: once stage 1 gave security-plugin.test.ts's fake engine a real registry, three '#18535' anchor-binding cases failed. The nine curated declarations now sit in that registry, so the context no longer falls back to the metadata service, where those cases served the app's capability. This is exactly the Q2 path the seat ruled 'accept, do not fence'. I changed the test double, not the runtime: the declared capability is registered in the fake registry under 'com.example.app', as registerApp does. The undeclared-token and platform-floor control cases still pass, so the accepting case still means something.",
    "b3c8d0da: comment-only edits in packages/spec/src (meta-spelling/manifest-collection-spelling.ts, kernel/metadata-type-schemas.ts, kernel/metadata-plugin.zod.ts x2, security/capabilities.ts x2 TSDoc, security/high-privilege.ts TSDoc). Each stated that bootstrapDeclaredCapabilities reads or seeds today; they now name the registry, ADR-0131 D3, or the seeder as retired. No code, schema or .describe() changed. spec check:generated: all 14 artifacts up to date.",
    "Left unedited, per the exclusions: sys-capability.object.ts (no change, by amendment 6098440046); the retired seed-refusal-diagnostics.ts and seed-name-lookup.ts (batch #310 item 4); test-file comments in objectql engine-capability-provenance.test.ts and metadata-protocol protocol.capability-write-door.test.ts (narration of history in tests)."
  ],
  "tests": "On b3c8d0da, built first (pnpm build, 72/72). Exit codes were captured from os-verify-lock VERDICT lines, before any pipe. Typecheck: plugin-security, spec, runtime, objectql, lint and example-showcase all exit 0. Suites: plugin-security 196 files / 4003 passed / 45 skipped; spec 642 files / 19204 passed / 1 todo; objectql 397 files passed; lint 136 files / 6351 passed; runtime standalone-stack-seeder-declaration-copy plus standalone-stack-security-catalog-one-holder 19/19. The full runtime suite was not run: its only touched file is a comment in app-plugin.ts, and the edited runtime test ran.",
  "gates": "dispatch-gates --commands (no paths) on b3c8d0da against merge base 7aa8d51c: 130 commands, all run with exit codes recorded; --ran: 130 derived, 130 run, 0 NOT-MEASURED, 0 UNRUN. 128 exit 0. check-engine-split-ratio exits 2: a shallow-clone refusal by design. check:platform-checklist exits 1 with the same 7 problems red on main (absent symbols in metadata-protocol/src/protocol.ts and service-storage attachment-access-hooks.ts); none comes from this diff. spec check:generated: up to date.",
  "pr_body_note": "The PR body is unchanged (os-dev writes it once). Lines that are now stale, for the seat to update: the size (now 2632 against 7aa8d51c); the 'materializedCapabilityNames is a constant empty list' acceptance note (the stub is gone); the line saying curated rows still seed (6b-1b removed them); and 'Outside the landing zone' should add the docs pages, permission-sets.mdx and the five spec comments.",
  "mcp_calls": "0",
  "api_writes": "1 relay write: this PR comment (post-stamped). Plus one git push of b3c8d0da (4 commits)."
}

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b3c8d0daf228a5de710155bdf89841ade7f60f1c
Local-runs: none

Why this review is owed: the PR declares Clause-②: no, and its landing round (b3c8d0da) touches five non-test files under packages/spec/src/**, so the landing-operations contract-surface path limb fires. Read: card #15204 (body; ACCEPT 6101787808, amendment 6098440046, ruling 6104498446, 6b-1b Landed 6104947165), PR #22711 (body, 29-file list, net diff against main, comments 6100775190 and 6105207325), the head's check-runs, and the files the diff cites at this head and at the merge base 7aa8d51c. Not read as inputs: the dispatch order or the seat's own conclusions.

Check-runs on this head: 36 runs, all completed: 34 success, 2 roster skips (Console Pin Gate, Packed-tarball smoke). Lint & Repo Gates, Test Core 1–6, Spec property liveness, Check Changeset, Build Docs, Dogfood 1–3 and Temporal Conformance are green. No red run exists on this head.

① Derived judgments

  1. No sys_capability row for a declared capability — right. security-plugin.ts at this head imports and calls neither bootstrapDeclaredCapabilities nor bootstrapSystemCapabilities, and holds no materializedCapabilityNames (grep of the head file: only readDeclaredCapabilityContext ×3 and registerBuiltinCapabilities ×1 remain). With 6b-1b already on main, the boot writes no sys_capability row at all. builtin-capabilities.boot.test.ts pins that as EXPECTED_CENSUS = [] plus fresh.writes and reseeded.writes both empty, on four posture × declared-source scenarios; the runtime pin standalone-stack-seeder-declaration-copy.test.ts asserts no row for probe.export. That is the card's own acceptance ("zero rows … count pinned") for this table.
  2. What reads the registry instead — right. The three consumers the changeset names exist and read the registry: packages/core/src/security/security-catalog.ts#createSecurityCatalogReader (engine registry first, metadata service second, SECURITY_CATALOG_READ_ORDER); GET /api/v1/meta/capability (the metadata service, fed by AppPlugin's registerInMemory('capability', …) at app-plugin.ts:1087/1174); and the anchor predicates through readDeclaredCapabilityContext (readDeclared(ql,'capability') = ql.registry.listItems('capability')). No authorization decision read a row before this PR (6098440046, 6104947165), so none changes.
  3. The collision diagnostic's refusal is not lost — right. objectql/src/registry.ts#SecurityCatalogNameConflictError (class, :1661) is thrown at :2462 over declaredSecurityCatalogNames(manifest) for a second holder of a name, curated names included (BUILT_IN_SECURITY_CATALOG_NAMES.capability = PLATFORM_CAPABILITY_NAMES). It runs at package registration, before any kernel:ready seeder could. standalone-stack-security-catalog-one-holder.test.ts is in Test Core (green).
  4. The five index.ts exports leave with 0 consumers — right. CAPABILITY_NAME_COLLISION, capabilityNameCollisionDiagnostic, formatCapabilityNameCollisionDiagnostic, reportCapabilityNameCollisions, CapabilityNameCollisionDiagnostic: outside plugin-security the only mentions on main are CHANGELOGs and test-file prose; @objectstack/lint does not import them. The three 6b-1a helpers deleted from builtin-capabilities.ts were never exported from index.ts. Public-surface narrowing, no widening.
  5. Dropping the filter in declared-capability-context.ts changes no anchor verdict — right, with one real consequence. The context now carries the nine curated declarations; appDeclaredCapabilityNames (high-privilege.ts) skips every PLATFORM_CAPABILITY_NAMES member, so they excuse nothing; builtin-capabilities.test.ts pins both halves (describeHighPrivilegeBits on a package token vs a curated token, and curated-only = no context). The consequence: once SecurityPlugin.start has registered the curated nine, the registry is never empty, so the metadata-service fallback in readDeclaredCapabilityContext is unreachable in every real boot. A declaration present only in the metadata service and not in the engine registry is therefore no longer excused by the anchor gate. The PR body names it (Acceptance notes, item 2) and the ACCEPT ruled it Q2 = accept, no fence, dead branch goes with the module's re-home (census row 13, 6b-2). Not a contract widening; a narrowing of a path no composition reaches.
  6. The test-double change in security-plugin.test.ts (dev deviation a8c5a6fd) — a harness fix, not a masked behaviour change. The fake now holds the stack's declared capabilities in the engine registry under com.example.app (the shape registerApp produces) and its metadata service lists nothing. That is the path a real boot takes (item 5). The path the test stops pinning is exactly the unreachable fallback, already ruled. The undeclared-token and platform-floor control cases stay in the suite, and Test Core is green on this head. The behaviour change it tracks is disclosed in the PR body and the report, so nothing is masked.
  7. The 323 lines removed from bootstrap-seed-round-trips.test.ts are the declared-capability sections only. The removed describes are the five #11096 … (declared capabilities) blocks and the bootstrapDeclaredCapabilities import; no surviving seeder's round-trip pin is deleted.
  8. The liveness re-anchor (packages/spec/liveness/capability.json) is truthful. Every new anchor resolves at this head: security-catalog-namespace.ts#declaredSecurityCatalogNames (:182), declared-capability-context.ts#readDeclaredCapabilityContext, validate-capability-references.ts#validateCapabilityReferences, metadata-manager.ts#unregisterPackage (:1779, which reads the authored packageId key itself, not only the _packageId stamp) and #collectPackageMembers (:1826), and the two objectui files exist at the pinned .objectui-sha 20c6d351ad. No property changes status; Spec property liveness is green.
  9. The platform-checklist edit adds one anchor and it resolves. access-security.json now cites packages/objectql/src/registry.ts#SecurityCatalogNameConflictError, a class declaration (not comment-only, which the symbol-anchor rule would read as absent).

② Semver level

  • Changesets match what the diff publishes. @objectstack/plugin-security: minor — five public exports deleted with 0 consumers, each named with FROM → TO as AGENTS.md "Before declaring done" item 3 requires; major is refused by check-changeset-no-major, and the ACCEPT's Q1 answer (minor, no !) stands. @objectstack/spec, @objectstack/objectql, @objectstack/runtime, @objectstack/lint: patch — shipped comment text and the liveness evidence only, no runtime or type change. examples/app-showcase, docs/qa, scripts/, content/docs are not published packages and need none. Check Changeset is green.
  • Clause-②: no is still TRUE. The five packages/spec/src edits, one by one, against what consumes each comment:
    1. kernel/metadata-plugin.zod.ts:169–176 — a // comment inside the MetadataTypeSchema enum array. Enum members, .describe() strings and the registry entries are untouched. gen:docs reads only a .zod.ts module's top-level leading doc block (lib/file-description.ts rules 1–3), never an indented comment inside a literal; the generated content/docs/references/kernel/metadata-plugin.mdx is unchanged in the diff and check:generated is reconciled in Lint & Repo Gates (green). Not a contract change.
    2. kernel/metadata-plugin.zod.ts:1134–1141 — a // comment inside DEFAULT_METADATA_TYPE_REGISTRY's capability entry; the entry's values (allowRuntimeCreate, allowOrgOverride, filePatterns, schema) are untouched. Same generator reasoning. Not a contract change.
    3. kernel/metadata-type-schemas.ts:169–176 — a // comment inside BUILTIN_METADATA_TYPE_SCHEMAS; not a .zod.ts file, no generator reads it, the capability → CapabilityDeclarationSchema binding is untouched. Not a contract change.
    4. meta-spelling/manifest-collection-spelling.ts:76–83 — a // comment inside PLURAL_TO_SINGULAR; gen:meta-url-spelling consumes the mapping's values, which are untouched (capabilities: 'capability'). Not a contract change.
    5. security/capabilities.ts:93–100, 127–134 and security/high-privilege.ts:31–50 — TSDoc blocks on the exported CapabilityDeclarationSchema and AnchorBindingContext. These ship only as .d.ts hover text. api-surface/security.json records names and kinds ("AnchorBindingContext (interface)"), not doc text; declaration-map/ and export-origins/ record names; no content/docs/references page is generated from either file; the schema shape, every .describe(), gen:schema's JSON Schema and the authorable surface are untouched (check:authorable-surface is inside the reconciled check:generated). A shipped doc comment, which is why @objectstack/spec: patch is right, and not a contract change.
      Nothing a contract consumer can observe changes: no .describe(), schema, exported name or value, generated reference page, JSON Schema or authorable key. The amendment's Clause-② rationale ("no packages/spec/src change") is superseded by this item-by-item reading, with the same answer.

③ Boundary flags

  1. Dev deviation a8c5a6fd (test double) — answered in ① item 6: harness fix tracking the ruled Q2 narrowing; carrier for the dead fallback branch is 6b-2 (census row 13). No action for this PR.
  2. Dev deviation b3c8d0da (five spec comments) — answered in ②: Clause-②: no holds. It makes one PR-body sentence false ("No packages/spec/src change.", under "Outside the landing zone"); the seat patches it (item 5 below).
  3. Left unedited per the exclusions (sys-capability.object.ts, seed-refusal-diagnostics.ts, seed-refusal-sink.ts, seed-name-lookup.ts; test-file prose in objectql/src/engine-capability-provenance.test.ts, metadata-protocol/src/protocol.capability-write-door.test.ts, spec/src/kernel/metadata-type-schemas.test.ts:624, spec/src/kernel/capability-metadata-kind.test.ts:18, examples/app-showcase/test/inert-wirings.test.ts:323; docs/adr/0066) — answered: amendment 6098440046 and batch Release version 0.4.0 #310 item 4 exclude the first group; the rest narrate history in tests and an ADR, import nothing deleted (typecheck green), and are not a contract surface. No carrier needed.
  4. check:platform-checklist red is main's, verified. The gate is not in per-PR CI (lint.yml, maintainer decision); its standing caller is the watchdog. Watchdog issue check:platform-checklist is red on main #22758, swept 2026-10-11T03:05Z on main commit 149294c02c (the commit immediately before the merge base 7aa8d51c, which is 6b-1b, plugin-security only), records gate exit 1 with exactly 7 problems: five ABSENT SYMBOL rows in access-security.json for metadata-protocol/src/protocol.ts#anonymousFormIntakeOrgScopeRefusal, #anonymousFormIntakeReopenRefusal ×2, #envWideRawViewRows ×2; one in attachments-storage.json for service-storage/src/attachment-access-hooks.ts#canEdit; and that file's SYMBOL ANCHORS LOST floor (27 vs 28). Read at 7aa8d51c: the three protocol.ts names do not occur in the file, and canEdit occurs only in comments and as canEditCache. None of the seven touches the capability item this PR edits. Triage closed check:platform-checklist is red on main #22758 not_planned at 03:57Z as the same red as check:platform-checklist is red on main #22594. Not this PR's; not a FAIL reason.
  5. Stale PR-body lines, for the seat to patch (the dev's list, plus two it did not name):
    • size: "2623 (+112 / −2511)" → 29 files, +186 / −2446 = 2,632 against 7aa8d51c (≤ 3,000; Check PR Size green);
    • "lands after stage 5 (C9), as order correction 3 … requires" and "Stage 6b-1b … lands first, and this branch merges main after it" → superseded by ruling 6104498446 and done (6b-1b merged as 7aa8d51c; two main merges, no rebase);
    • "No packages/spec/src change." → false: five comment-only edits (② above), not named by the dev's list;
    • Measurements: "The new pin is the curated rows and nothing else" → the pin is no row at all, and zero writes on a fresh database too;
    • Acceptance note 1 (materializedCapabilityNames "is now a constant empty list") → the stub and 6b-1b's call are both gone;
    • "Outside the landing zone" → add content/docs/permissions/authorization.mdx, capabilities.mdx, permission-sets.mdx:150 (the ACCEPT's landing item 3, done) and the five spec comments;
    • Gates: plugin-security "4056 passed" → 4003 passed / 45 skipped on this head; derived gates 114 → 130 run, 130 reconciled; the platform-checklist sentence's reference commit 96469912 → check:platform-checklist is red on main #22758 on 149294c02c.
  6. The ruling's window is one stage short after this PR — escalated as a note for the Landed record, not a defect. 6104498446 and 6104947165 describe the accepted window as "package rows only" (2 rows instead of 11) on Setup's capability object page; after this PR a fresh database's sys_capability holds no row, so that page lists nothing until C9's registry-reading Setup surface lands. The ruling covers both PRs, the card's acceptance is zero rows, and release-cut condition (c) of 6102862135 keeps the window out of every cut; the Landed record should state the 0-row reading.
  7. Docs. The Docs Drift advisory (6100775190) lists 14 hand-written pages; on main the deleted names occur outside content/docs/releases/ only in authorization.mdx and capabilities.mdx, both rewritten here, and permission-sets.mdx:150 is rewritten as the ACCEPT required. No release-owned page is touched. The ADR-0066 "Landed" line naming the seeders is history and not this PR's.
  8. Carried from the ACCEPT, unchanged: check:adr-0087-registration has no disposition for deleting a runtime-only export whose changeset must carry FROM → TO (out-of-scope finding, no carrier).

Implemented-by: session_01RtYXEFNnKGHihJTBNxGX71
Reviewed-by: session_01Rerax7QTjKMPCUZxQUtPFR

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 11, 2026 04:10
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 11, 2026 04:10
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 95a8435 Oct 11, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-15204-s6b1c-declared-capability-seeder branch October 11, 2026 04:49
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 size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants