Blocked-by: #17516
Filed by the domain:services execution PM seat, routed out of #17516 / PR #18022 — the same pattern as #17909 → domain:spec and #17995 after it. ⛔ Unlabelled on purpose: domain:* and priority are triage's. ⚠️ Routing note: packages/cli is domain:cli (cli.md:7), which is why the delivering lane stopped rather than finishing.
What already landed, and what is left
PR #18022 gives the permission-set name-collision refusal a runtime door: a package declaring a set whose name another package owns is still refused (correct, ADR-0086 D4, unchanged) — but the refusal now reaches the author instead of vanishing through a doubly-optional logger?.warn?. into an internal counter.
⛔ The compile-time door is not done. os build / os validate do not report the collision.
Why that matters — the precedent this is half of
#14553 fixed the identical shape for navigationContributions by raising its diagnostic at both doors, runtime and compile, behind one shared predicate so the two cannot drift. The compile half of that precedent lives in packages/cli/src/utils/nav-contribution-groups.ts.
⭐ The shared derivation is already published for you — consume it, ⛔ do not re-derive it. @objectstack/plugin-security now exports, from its package entry (dist/index.d.ts):
PERMISSION_SET_NAME_COLLISION
permissionSetNameIsForeign
permissionSetNameCollisionDiagnostic
formatPermissionSetNameCollisionDiagnostic
reportPermissionSetNameCollisions
type PermissionSetNameCollisionDiagnostic
type CollisionReportSink
That export is deliberate and is the whole reason PR #18022 graded Clause-②: yes / minor. Its own source comment states the intent: the compile door "must consume these rather than re-deriving either the predicate or the wording — that drift is what this card is about, one layer up."
⇒ ⛔ Do not write a second predicate, and ⛔ do not retype the sentence. Two doors phrasing the same refusal differently is the defect, not the fix.
⭐ A second lane fence that was avoided, worth knowing before you start
The nav precedent's diagnostic constant required a classification row in packages/runtime/src/dispatcher-error-vocabulary.ts — another fenced package. PR #18022 stamped its token as event (following the in-package position_name_fold_grant precedent) rather than as code (the cross-lane nav one), and measured that check:dispatcher-error-vocabulary then passes with no row owed in packages/runtime. ⚠️ If your compile-door work re-stamps it as a code, that fence comes back — check before you change the shape.
⚠️ Bounds
Refs
#17516 / PR #18022 (the runtime door, landed or landing) · #14553 (the nav precedent, both doors, one predicate) · packages/cli/src/utils/nav-contribution-groups.ts · #18023 (the capabilities sibling) · #17909 + #17995 (the precedent for routing a fenced half out of this lane)
Generated by Claude Code
Blocked-by: #17516
Filed by the⚠️ Routing note:
domain:servicesexecution PM seat, routed out of #17516 / PR #18022 — the same pattern as #17909 →domain:specand #17995 after it. ⛔ Unlabelled on purpose:domain:*and priority are triage's.packages/cliisdomain:cli(cli.md:7), which is why the delivering lane stopped rather than finishing.What already landed, and what is left
PR #18022 gives the permission-set name-collision refusal a runtime door: a package declaring a set whose name another package owns is still refused (correct, ADR-0086 D4, unchanged) — but the refusal now reaches the author instead of vanishing through a doubly-optional
logger?.warn?.into an internal counter.⛔ The compile-time door is not done.
os build/os validatedo not report the collision.Why that matters — the precedent this is half of
#14553 fixed the identical shape for
navigationContributionsby raising its diagnostic at both doors, runtime and compile, behind one shared predicate so the two cannot drift. The compile half of that precedent lives inpackages/cli/src/utils/nav-contribution-groups.ts.⭐ The shared derivation is already published for you — consume it, ⛔ do not re-derive it.
@objectstack/plugin-securitynow exports, from its package entry (dist/index.d.ts):That export is deliberate and is the whole reason PR #18022 graded
Clause-②: yes/minor. Its own source comment states the intent: the compile door "must consume these rather than re-deriving either the predicate or the wording — that drift is what this card is about, one layer up."⇒ ⛔ Do not write a second predicate, and ⛔ do not retype the sentence. Two doors phrasing the same refusal differently is the defect, not the fix.
⭐ A second lane fence that was avoided, worth knowing before you start
The nav precedent's diagnostic constant required a classification row in⚠️ If your compile-door work re-stamps it as a
packages/runtime/src/dispatcher-error-vocabulary.ts— another fenced package. PR #18022 stamped its token asevent(following the in-packageposition_name_fold_grantprecedent) rather than ascode(the cross-lane nav one), and measured thatcheck:dispatcher-error-vocabularythen passes with no row owed inpackages/runtime.code, that fence comes back — check before you change the shape.navigationContributions[].groupthat names no group in the target app is silently RELOCATED to the top level — refuse, warn, or leave to the consumer? #14553 chose for nav and follow it unless there is a reason not to.bootstrap-declared-capabilities.tscarries the identical doubly-optional logger on its ownskippedForeignbranch. Different record type, different outcome key; ⛔ not this card.Refs
#17516 / PR #18022 (the runtime door, landed or landing) · #14553 (the nav precedent, both doors, one predicate) ·
packages/cli/src/utils/nav-contribution-groups.ts· #18023 (the capabilities sibling) · #17909 + #17995 (the precedent for routing a fenced half out of this lane)Generated by Claude Code