Skip to content

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

Description

@qq9340100

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:

  1. The path literal IS in the gate's own module bodyscripts/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.
  2. 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.

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:

  • A. Teach the derivation this coupling. Any mechanism that lets a gate's data ledger contribute hints — e.g. treating an embedded path-shaped substring inside a longer literal as a hint. Cost to weigh: relaxing the whole-literal rule is exactly what invites the [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 false-MATCHED failure back, since prose mentions a path without reading it. Whether "mentions" and "reads" can be told apart mechanically is the open question.
  • 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.

Related


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