Filed by the domain:engine execution seat. These are the three advisory findings an isolated contract reviewer returned on PR #14738 (card #14474) alongside its PASS. None of them downgraded the verdict, and they were deliberately not held against that PR: it is a p2 refusal-envelope fix whose measured behaviour is correct, and stalling it behind three text corrections would have been the wrong trade. Recording only — ⛔ no severity asserted, no routing; grading is triage's.
All three live in files PR #14738 introduced or edited, so the line numbers below are against that PR's head 966dd3897 and will need re-deriving once it lands.
(1) The why string generalises a reason that belongs to only one of the rows it covers
packages/runtime/src/dispatcher-error-vocabulary.ts:591-594. The new NAMESPACE_CONFLICT row's why says its reachability is what:
separates it from the three ADR-0130 install-time rows below, whose door: none turns on needing an artifact install SCOPE that no HTTP caller builds
That reasoning is true of the DUPLICATE_ARTIFACT_OBJECT_NAME row (:783) and only that one. The three packages/core/src/artifact-packages.ts rows (:721, :741, :762) are door: none for a different reason: they are boot refusals, because the artifact loader runs inside plugin init — there is no HTTP boundary yet, scope or no scope.
The row's operative claim is unaffected and correct: this gate needs no scope, an ordinary one-package install reaches it, and that was measured on a booted stack rather than inferred from the call graph. What is wrong is the comparison it draws to justify itself, in a runtime string operators read.
(2) The row asserts a reachability it does not pin
dispatcher-error-vocabulary.ts:587-588 — door: 'dispatcher', verdict: 'pending-registration'.
The only thing that ever drove POST /api/v1/packages to the 422 was the predecessor seat's temporary probe (test/zz-measure-14474.tmp.test.ts), which is not in the PR. So the row's door claim rests on a measurement no longer in the tree.
Not a defect against any declared rule: pin is mandatory only for runtime-pinned rows, and the triage scope for #14474 did not ask for one. But the sibling owd_widening_forbidden row does cite a door-driving test, so the convention exists and this row is the exception to it.
⚠️ Same shape as #14573 (rest: the approvals FORBIDDEN → 403 wire-code row has no live-emission pin) — different row, different door, same class. Whoever grades this should decide whether the two are one card.
(3) The changeset's summary line reads against the bullets above it
.changeset/registry-namespace-conflict-refusal-envelope.md:17 says:
Not narrowed, not widened: no accept-set changes, no export changes, and no ledger registration.
Triage's own note on #14474 called the change one that "widens the public error surface", and the bullets immediately above this line disclose the wire moving 500 → 422. The sentence is defensible — it is scoped to accept-set / exports / ledger, which is the scope semver turns on — but a reader arriving at the compiled release notes meets the denial before the disclosure. Sharper, per the reviewer: "no accept-set, export or schema change; the door's answer to this refusal changes 500→422".
Why one card rather than three
All three are one PR's residue, in two files, found in one review pass. Splitting them would triple the triage cost for text corrections that a single dispatch fixes together. If triage disagrees, (2) is the one that separates cleanly — it is a test to write, not a sentence to fix.
Dedup
search_issues "dispatcher error vocabulary why string over-generalises ADR-0130 install-time rows door none artifact scope boot refusal pending-registration reachability no permanent pin NAMESPACE_CONFLICT" → 95 results, top 10 read. Nearest is #14573 (the missing-live-emission-pin shape, cited above as related, not duplicate); #14474 itself ranks first, which is the firing control. #14723 and #14725 are the neighbouring wire-code-vocabulary cards and name different disagreements. Nothing names any of these three residues.
Re-check
git grep -n "turns on needing an artifact install SCOPE" origin/main -- packages/runtime/src/dispatcher-error-vocabulary.ts
git grep -n "Not narrowed, not widened" origin/main -- .changeset/
Refs: PR #14738 / card #14474 (the fix these are residues of; the review comment carrying all three is #14738 (comment)) · #14573 (sibling of (2)) · #14367, #14309 (the door-family precedents).
Filed by the
domain:engineexecution seat. These are the three advisory findings an isolated contract reviewer returned on PR #14738 (card #14474) alongside its PASS. None of them downgraded the verdict, and they were deliberately not held against that PR: it is ap2refusal-envelope fix whose measured behaviour is correct, and stalling it behind three text corrections would have been the wrong trade. Recording only — ⛔ no severity asserted, no routing; grading is triage's.All three live in files PR #14738 introduced or edited, so the line numbers below are against that PR's head
966dd3897and will need re-deriving once it lands.(1) The
whystring generalises a reason that belongs to only one of the rows it coverspackages/runtime/src/dispatcher-error-vocabulary.ts:591-594. The newNAMESPACE_CONFLICTrow'swhysays its reachability is what:That reasoning is true of the
DUPLICATE_ARTIFACT_OBJECT_NAMErow (:783) and only that one. The threepackages/core/src/artifact-packages.tsrows (:721,:741,:762) aredoor: nonefor a different reason: they are boot refusals, because the artifact loader runs inside plugin init — there is no HTTP boundary yet, scope or no scope.The row's operative claim is unaffected and correct: this gate needs no scope, an ordinary one-package install reaches it, and that was measured on a booted stack rather than inferred from the call graph. What is wrong is the comparison it draws to justify itself, in a runtime string operators read.
(2) The row asserts a reachability it does not pin
dispatcher-error-vocabulary.ts:587-588—door: 'dispatcher',verdict: 'pending-registration'.The only thing that ever drove
POST /api/v1/packagesto the 422 was the predecessor seat's temporary probe (test/zz-measure-14474.tmp.test.ts), which is not in the PR. So the row'sdoorclaim rests on a measurement no longer in the tree.Not a defect against any declared rule:
pinis mandatory only forruntime-pinnedrows, and the triage scope for #14474 did not ask for one. But the siblingowd_widening_forbiddenrow does cite a door-driving test, so the convention exists and this row is the exception to it.rest: the approvalsFORBIDDEN → 403wire-code row has no live-emission pin) — different row, different door, same class. Whoever grades this should decide whether the two are one card.(3) The changeset's summary line reads against the bullets above it
.changeset/registry-namespace-conflict-refusal-envelope.md:17says:Triage's own note on #14474 called the change one that "widens the public error surface", and the bullets immediately above this line disclose the wire moving
500→422. The sentence is defensible — it is scoped to accept-set / exports / ledger, which is the scope semver turns on — but a reader arriving at the compiled release notes meets the denial before the disclosure. Sharper, per the reviewer: "no accept-set, export or schema change; the door's answer to this refusal changes 500→422".Why one card rather than three
All three are one PR's residue, in two files, found in one review pass. Splitting them would triple the triage cost for text corrections that a single dispatch fixes together. If triage disagrees, (2) is the one that separates cleanly — it is a test to write, not a sentence to fix.
Dedup
search_issues"dispatcher error vocabulary why string over-generalises ADR-0130 install-time rows door none artifact scope boot refusal pending-registration reachability no permanent pin NAMESPACE_CONFLICT" → 95 results, top 10 read. Nearest is #14573 (the missing-live-emission-pin shape, cited above as related, not duplicate); #14474 itself ranks first, which is the firing control. #14723 and #14725 are the neighbouring wire-code-vocabulary cards and name different disagreements. Nothing names any of these three residues.Re-check
Refs: PR #14738 / card #14474 (the fix these are residues of; the review comment carrying all three is #14738 (comment)) · #14573 (sibling of (2)) · #14367, #14309 (the door-family precedents).