Skip to content

[finding] A package's whole declared permission set is dropped on a set-name collision with no diagnostic and no build gate — the comment says "refuse loudly", the code warns through a doubly-optional logger #17516

Description

@os-bill

Maintainer-action: rule how a domain:services card obtains the CONTRACT_REVIEW_TIER clause-② verdict its carrier needs — route it to the spec seat (naming how), or rule that this seat's default-judgment-tier review plus the gates suffices — done when node scripts/pm/check-clause2-carriers.mjs --pair 18022 exits 0 with the carrier cleared on both carriers

A package's entire declared permission set is dropped on a set-name collision, and the drop is invisible to the author who caused it.

packages/plugins/plugin-security/src/bootstrap-declared-permissions.ts, the else arm at the skippedForeign branch (anchor by content — out.skippedForeign += 1), on origin/main d9e15877aa read 2026-09-10T18:25Z:

} else {
  // Package-namespaced object api names make set-name collisions a
  // packaging bug, not a merge case — refuse loudly (ADR-0086 D4:
  // a package never writes into a foreign record).
  out.skippedForeign += 1;
  logger?.warn?.('[security] permission set name owned by another package — skipped', {
    name: ps.name, declaredBy: packageId, ownedBy: existing.package_id,
  });
}

Why this is a finding and not a design choice

The comment says "refuse loudly". The code does not refuse loudly. ⚠️ Sharper than it looks: logger?.warn?. is optionally-chained twice, so a caller that passes no logger produces no output at all — the set vanishes with only an internal counter incremented. Nothing is written back where an author can read it: no diagnostic record, no os build check, no os validate check.

Measured absence, with a lit control so the zero means something:

skippedForeign referenced in packages/cli/src or packages/spec/src : 0 files
LIT CONTROL   'permission' in packages/cli/src                     : 27 files

Skipping the set is arguably the correct runtime behaviour (ADR-0086 D4: a package never writes into a foreign record). ⛔ This card does not propose changing that. The defect is that the refusal never reaches the author.

This is a class the repo has already ruled on — one layer over

This is the same failure shape #14553 just inverted for navigationContributions: present, plausible-looking, and silently different from what the author wrote. That fix gave nav a nav_contribution_group_missing diagnostic raised at BOTH doors — runtime (recordNavGroupMiss) and compile (os build / os validate via checkNavContributionGroups) — behind one shared predicate so the two cannot drift. The authorization surface never got the same treatment.

An AI author colliding on a set name is exactly how this fires, which puts it in class (c): metadata that will be stored and re-authored by someone other than its writer, silently dropped at the door.

Second, smaller defect in the same three lines

The comment's stated premise — "Package-namespaced object api names make set-name collisions a packaging bug, not a merge case" — is falsified by ADR-0130 D1, which lets N packages co-own one namespace. ADR-0130 already records this under its "What was NOT decided" section, so ⛔ no separate card is warranted; it belongs in the same change as the fix above.

Dedup

domain:spec open + closed, newest 100 each (⚠️ both at the API page cap, so bounded, not exhaustive). One near miss, not this: #16870 (readScope beside viewAllRecords). The round that surfaced this also searched via MCP and found only closed #9952 and #15298, neither of them this, with a live control hit.

Provenance

Surfaced by the #14488 proposal round, which was dispatched write-restricted (⛔ no card filing) and correctly handed it to the seat rather than filing it or dropping it. Re-verified independently by the seat before filing — including the double optional-chaining, which the round did not report.

⛔ Ungraded — triage sets type and priority. Successor: whoever builds #14488 edits this exact branch.


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

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions