You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
dispatch-gates classifies check:type-check-coverage as SILENT (known-irrelevant) for a card editing check-test-typecheck.mts, though the coupling is real and the path literal is in the gate's own source — it sits inside a prose sentence, so extractWatchHints discards it #8551
Filed by the dev seat implementing #8538 (PR #8549). Unassigned and unlabeled for triage. Measured, not reasoned — a near-miss I hit on that card and worked around by hand.
⚠️No gate is asked to be weakened, and dispatch-gates.mjs is accurate to its design. It derives families from each check's own source, and this coupling genuinely is not expressed there. The defect is that a real coupling exists and the derivation's output cannot express it, so silence reads as "no gates apply" when it means "no gate names this path".
The near-miss
#8538 changes exactly one file, scripts/check-test-typecheck.mts. The derivation says nothing applies:
$ node scripts/pm/dispatch-gates.mjs scripts/check-test-typecheck.mts
dispatch-gates: 93 check famil(ies) discovered across 25 workflow file(s) — derived at runtime, nothing listed in this script.
No check family names the given paths in its own source.
Repo-wide / undetermined (no path literals discoverable — not known irrelevant): 34 famil(ies).
Convention-scoped gates match by what the change IS, not where it lives. …
But check:type-check-coverage is genuinely coupled to that file, and I added it to my local run on my own reading. It went green only because I designed the change around the coupling. A dev who trusted the derivation would have skipped the one gate most likely to redden this diff.
Why the coupling is real — the 29-of-80 accounting
scripts/check-type-check-coverage.mjs holds a root entry @objectstack/spec-monorepo recorded at 80 errors, and its own note says:
> scripts/check-test-typecheck.mts alone accounts for 29 of the 80, and the analytics-reconcile tree for 32.
The note also records that 80 was re-measured exactly, with no bootstrap margin. Under the root tsconfig that program has no types:["node"], so in that file every console. reference costs +1 TS2584 and every process. reference +1 TS2591. Editing check-test-typecheck.mts therefore moves a number a different gate ratchets on, and ordinary additions move it: a self-test block that logged its own failures would have added several errors and turned check:type-check-coverage red.
Measured on the #8538 branch, before and after: 29 and 29, and check:type-check-coverage green. That is a designed-around result, not a free one.
The mechanism — SILENT, not undetermined
Sharper than "invisible", and worth stating precisely because it changes which remedy makes sense. Measured on main at 427344c using the script's own exported functions:
REAL exported extractWatchHints() on check-type-check-coverage.mjs
hints extracted: 36
sample: ["scripts/check-type-check-coverage.mjs","@objectstack/cloud-connection","@objectstack/core", …]
any hint naming check-test-typecheck? NO
classifyEntry verdict for this card: silent
Two consequences:
The path literal IS in the gate's own module body — scripts/check-type-check-coverage.mjs:427 contains the characters scripts/check-test-typecheck.mts. extractWatchHints still discards it, because it accepts a quoted literal only when the whole string is path-shaped (/^[\w.@][\w.@/*-]*$/). Here the path sits inside a prose sentence — 'scripts/check-test-typecheck.mts alone accounts for 29 of the 80, and the analytics-reconcile ' — so the spaces disqualify the entire literal and the path never becomes a hint. This is not the comment/self-test masking from [finding] dispatch-gates watch hints are read from self-test fixtures and comments, so gates are printed as MATCHED for paths they never read #8478; the string is a live module-body constant.
The family lands in the silent bucket, not undetermined.classifyEntry returns undetermined only when hints.length === 0; this file yields 36 hints (mostly package names), so the verdict is silent — neither matched nor undetermined, and therefore printed nowhere at all. The gate is actively judged known-irrelevant and dropped. That is why re-reading the "undetermined 34" would not have rescued it either.
So this is the mirror image of #8478, which recorded gates printed as MATCHED for paths they never read: this is a gate suppressed as irrelevant for a path it demonstrably does read.
B. Express the coupling in check-type-check-coverage.mjs's own source, in a shape the existing extractor already finds — a module-body constant holding the bare path, referenced by the note rather than spelled inside it. Cost to weigh: it is per-coupling manual upkeep, and a coupling nobody remembers to declare stays invisible, which is the same class of gap one level up.
⛔ Neither is attempted here, and this card does not ask for the gate or the derivation to be loosened.
Filed by the dev seat implementing #8538 (PR #8549). Unassigned and unlabeled for triage. Measured, not reasoned — a near-miss I hit on that card and worked around by hand.
dispatch-gates.mjsis accurate to its design. It derives families from each check's own source, and this coupling genuinely is not expressed there. The defect is that a real coupling exists and the derivation's output cannot express it, so silence reads as "no gates apply" when it means "no gate names this path".The near-miss
#8538 changes exactly one file,
scripts/check-test-typecheck.mts. The derivation says nothing applies:But
check:type-check-coverageis genuinely coupled to that file, and I added it to my local run on my own reading. It went green only because I designed the change around the coupling. A dev who trusted the derivation would have skipped the one gate most likely to redden this diff.Why the coupling is real — the 29-of-80 accounting
scripts/check-type-check-coverage.mjsholds a root entry@objectstack/spec-monoreporecorded at 80 errors, and its own note says:> scripts/check-test-typecheck.mts alone accounts for 29 of the 80, and the analytics-reconcile tree for 32.
The note also records that 80 was re-measured exactly, with no bootstrap margin. Under the root tsconfig that program has no
types:["node"], so in that file everyconsole.reference costs +1 TS2584 and everyprocess.reference +1 TS2591. Editingcheck-test-typecheck.mtstherefore moves a number a different gate ratchets on, and ordinary additions move it: a self-test block that logged its own failures would have added several errors and turnedcheck:type-check-coveragered.Measured on the #8538 branch, before and after: 29 and 29, and
check:type-check-coveragegreen. That is a designed-around result, not a free one.The mechanism — SILENT, not undetermined
Sharper than "invisible", and worth stating precisely because it changes which remedy makes sense. Measured on
mainat 427344c using the script's own exported functions:Two consequences:
scripts/check-type-check-coverage.mjs:427contains the charactersscripts/check-test-typecheck.mts.extractWatchHintsstill discards it, because it accepts a quoted literal only when the whole string is path-shaped (/^[\w.@][\w.@/*-]*$/). Here the path sits inside a prose sentence —'scripts/check-test-typecheck.mts alone accounts for 29 of the 80, and the analytics-reconcile '— so the spaces disqualify the entire literal and the path never becomes a hint. This is not the comment/self-test masking from [finding] dispatch-gates watch hints are read from self-test fixtures and comments, so gates are printed as MATCHED for paths they never read #8478; the string is a live module-body constant.silentbucket, notundetermined.classifyEntryreturnsundeterminedonly whenhints.length === 0; this file yields 36 hints (mostly package names), so the verdict issilent— neither matched nor undetermined, and therefore printed nowhere at all. The gate is actively judged known-irrelevant and dropped. That is why re-reading the "undetermined 34" would not have rescued it either.So this is the mirror image of #8478, which recorded gates printed as MATCHED for paths they never read: this is a gate suppressed as irrelevant for a path it demonstrably does read.
Not a duplicate of the neighbouring cards
Two candidate remedies, deliberately not chosen and not built
Recording both so triage can rule rather than inherit a decision:
check-type-check-coverage.mjs's own source, in a shape the existing extractor already finds — a module-body constant holding the bare path, referenced by the note rather than spelled inside it. Cost to weigh: it is per-coupling manual upkeep, and a coupling nobody remembers to declare stays invisible, which is the same class of gap one level up.⛔ Neither is attempted here, and this card does not ask for the gate or the derivation to be loosened.
Related
scripts/*.mjs#8538 / PR fix(devx): mark the ledger-expanding remedy maintainer-only in check-test-typecheck (#8538) #8549 — where this was hit; that PR's body carries the 29-before / 29-after measurement.Generated by Claude Code