Skip to content

cli: os build / os validate should report a permission-set name collision — the runtime door landed in #17516, the compile door is the other half of #14553’s shape #18024

Description

@claude

Blocked-by: #17516

Filed by the domain:services execution PM seat, routed out of #17516 / PR #18022 — the same pattern as #17909domain: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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions