Skip to content

dispatch-gates.mjs names check:type-check-coverage for a rationale only check:type-check-debt satisfies — the ratchet half never runs locally #8545

Description

@os-zhuang

Filed by the dev seat on #8442 (PR #8533), where this cost a red CI lap. Unassigned and unlabeled for triage. Surface: PM dispatch tooling / gate derivation.

⚠️No gate is asked to be weakened, and no gate is wrong.check-type-check-coverage.mjs behaved correctly — it caught four real new type errors. This is about the name the derivation hands a dev seat, and the fact that running that name cannot reproduce the failure.

The mismatch

scripts/pm/dispatch-gates.mjs:396 emits, as a convention-triggered gate for any change that adds a test file:

- pnpm check:type-check-coverage — TEST_DEBT ratchets a package's test-layer
type errors, so a new test file that does not typecheck cleanly moves it

The rationale is the ratchet. The named command does not run the ratchet. From package.json:

check:type-check-coverage = check-type-check-coverage.mjs --self-test && check-type-check-coverage.mjs
check:type-check-debt = check-type-check-coverage.mjs --self-test && check-type-check-coverage.mjs --re-measure

Same script file, two invocations, two different questions. --re-measure is the half that re-runs tsc per ledgered package and fails when an entry drifts upward. Plain invocation checks coverage/wiring and never re-measures. lint.yml runs bothcheck:type-check-coverage at line 950 and check:type-check-debt at line 1357 — and it is the second that goes red on a new type error.

Why the usual defences did not catch it

This is the part worth carrying forward. The lane's standing rule is to derive gates from the workflow and re-derive with dispatch-gates.mjs, then run the union. That rule could not help here: both lists name the same script, and what differed was the flag. A union of two lists of script names is blind to an invocation split inside one script. On #8442 the union-green report therefore listed check:type-check-coverage in good faith while the ratchet had never been run locally at all; CI found four new errors (2 in metadata-protocol, 1 each in objectql and runtime).

The failure is also asymmetric in the expensive direction: --re-measure refuses to run at all without a fully built workspace closure (it throws rather than measure a different world, per #6376), so it is precisely the gate a dev seat is least likely to have run incidentally.

Suggested shape (not decided here)

Emit the package script CI actually runs for the ratchet rationale — check:type-check-debt — either replacing the current entry or listed beside it, since the two answer different questions and a change adding a test file genuinely moves both. If both are emitted, the ratchet line should carry the build-closure prerequisite, because a seat that runs it cold gets a throw rather than a verdict and may read that as "not applicable to me".

Worth considering as a general rule for the derivation: where one script has multiple CI invocations, the entry should name the invocation, not the script. A quick check for other instances of that shape in CHANGE_KIND_GATES would tell whether this is a one-off or a class.

Related

#8442 / PR #8533 (where it surfaced) · #5278 (the ratchet's shrink-only rule) · #6376 (why --re-measure demands a built closure). #8538 is a different finding about the same script family — it concerns whether a ledger-expanding remedy carries an authority marker, not which invocation gets derived; it is not addressed here and remains open.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions