check-widening-tells' T4 cannot tell a new error-code registration (which widens the published union) from a second provenance row for a code already in it (which does not), so a provenance-only diff is forced to a conservative Clause-②: yes and a minor changeset for an accept set that is byte-identical.
Measured
On PR #17924 (card #17909), whose whole spec-side diff is one provenance row for RESUME_FAILED under '@objectstack/plugin-approvals' — a code already registered under '@objectstack/rest':
$ node scripts/pm/check-widening-tells.mjs --declaration no --diff
exit 4 — T4 packages/spec/src/api/error-code-ledger.zod.ts:1084
a new registration in a registry / catalog
$ node scripts/pm/check-widening-tells.mjs --declaration yes --diff
exit 0
⇒ The tell fires on the row, not on the union membership. Nothing an implementation may emit changed: the code was already in the published union before this diff.
Why the shape is legitimate, not a mistake to be stamped out
The ledger's own header states the rule the tell is enforcing:
Registering a code widens this face and is therefore a Clause-② change, door or no door
and, in the same header, the fact that makes T4 over-broad here:
A code emitted by several packages is listed once per emitting package — the union dedupes; the per-package rows are provenance, not identity.
⇒ Multi-owner rows are a declared, intended shape. ⚠️ The precedent already exists in the tree: plugin-security's CONTROLLED_BY_PARENT row sits beside metadata-protocol's for the same code. So this is not a one-off.
⛔ What this card does NOT ask for
⛔ Not a request to relax the gate. The conservative direction is the right default, and the gate's own header records false positives as an accepted cost. ⛔ Not a claim that PR #17924 declared wrongly — it declared yes and the seat kept it, because ⛔ overriding a mechanical judge by hand is the repair the reference explicitly calls wrong.
⇒ The ask is narrow: teach T4 the difference, so a provenance-only row is not charged a contract review and a minor release for a byte-identical accept set. The reference names the matcher as the prescribed place to repair a demonstrated false positive.
⚠️ Explicitly unmeasured: how often this shape occurs. Two instances are known (the one above and CONTROLLED_BY_PARENT); nobody has censused the ledger for the rest. Whoever takes this should count before choosing between "teach the matcher" and "leave it and accept the cost" — a two-instance population may not be worth a matcher change, and that is a legitimate outcome of this card.
Provenance
Surfaced by the #17909 dev round as an open question (A keep / B declare no / C teach T4), recommending A for that PR + C as the durable fix. The seat took A (the declaration stands at yes, both carriers hung) and files C here. ⛔ The round deliberately did not file it — it is a governance-tool judgement, not one of the three fileable classes — and the seat agrees that is the right call for a round to make; the seat files it because the seat owns tool-face follow-ups.
Duplicate check
Title census over the 600 newest issues and PRs, 2026-09-13T05:1xZ: widening-tells → 0 title hits, provenance → 0, while contract-review → 6 and tier → 7 in the same pass. Neighbouring terms fire, so the zeros are readings rather than a dead instrument. ⚠️ The GitHub search API is unusable for this in this session — it returns total: None even for a control term.
⛔ Filed bare and ungraded — no domain:*, no priority:*; both are the triage seat's sole production. Type prefilled only.
Generated by Claude Code
check-widening-tells' T4 cannot tell a new error-code registration (which widens the published union) from a second provenance row for a code already in it (which does not), so a provenance-only diff is forced to a conservativeClause-②: yesand a minor changeset for an accept set that is byte-identical.Measured
On PR #17924 (card #17909), whose whole spec-side diff is one provenance row for
RESUME_FAILEDunder'@objectstack/plugin-approvals'— a code already registered under'@objectstack/rest':⇒ The tell fires on the row, not on the union membership. Nothing an implementation may emit changed: the code was already in the published union before this diff.
Why the shape is legitimate, not a mistake to be stamped out
The ledger's own header states the rule the tell is enforcing:
and, in the same header, the fact that makes T4 over-broad here:
⇒ Multi-owner rows are a declared, intended shape.⚠️ The precedent already exists in the tree:
plugin-security'sCONTROLLED_BY_PARENTrow sits besidemetadata-protocol's for the same code. So this is not a one-off.⛔ What this card does NOT ask for
⛔ Not a request to relax the gate. The conservative direction is the right default, and the gate's own header records false positives as an accepted cost. ⛔ Not a claim that PR #17924 declared wrongly — it declared
yesand the seat kept it, because ⛔ overriding a mechanical judge by hand is the repair the reference explicitly calls wrong.⇒ The ask is narrow: teach T4 the difference, so a provenance-only row is not charged a contract review and a minor release for a byte-identical accept set. The reference names the matcher as the prescribed place to repair a demonstrated false positive.
CONTROLLED_BY_PARENT); nobody has censused the ledger for the rest. Whoever takes this should count before choosing between "teach the matcher" and "leave it and accept the cost" — a two-instance population may not be worth a matcher change, and that is a legitimate outcome of this card.Provenance
Surfaced by the
#17909dev round as an open question (A keep / B declareno/ C teach T4), recommending A for that PR + C as the durable fix. The seat took A (the declaration stands atyes, both carriers hung) and files C here. ⛔ The round deliberately did not file it — it is a governance-tool judgement, not one of the three fileable classes — and the seat agrees that is the right call for a round to make; the seat files it because the seat owns tool-face follow-ups.Duplicate check
Title census over the 600 newest issues and PRs, 2026-09-13T05:1xZ:⚠️ The GitHub search API is unusable for this in this session — it returns
widening-tells→ 0 title hits,provenance→ 0, whilecontract-review→ 6 andtier→ 7 in the same pass. Neighbouring terms fire, so the zeros are readings rather than a dead instrument.total: Noneeven for a control term.⛔ Filed bare and ungraded — no
domain:*, nopriority:*; both are the triage seat's sole production. Type prefilled only.Generated by Claude Code