Skip to content

[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

Description

@hotlong

Found while wiring #8162 (PR #8477), measured rather than reasoned. Observation class, precision papercut — the tool's verdicts are unaffected, only its leads. Unassigned; grading is the triage seat's call.

The observation

extractWatchHints in scripts/pm/dispatch-gates.mjs scans a check script's source for quoted path-ish spans and treats each as a path that gate operates on. The scan does not distinguish

  • a literal the gate really reads at runtime, from
  • a self-test fixture string, from
  • a path named in a comment — the regex accepts backticks, so every backticked path in a header is a hint too.

A gate whose source contains a fixture path is then printed in the Local gates for this card block — the MATCHED column, which the script's own header reserves for high-signal answers ("22 leads is the same as none") — for cards that have nothing to do with it.

Measured on main at 03b5f81, today

Coverage-capable hints per script (i.e. after hintCovers drops the too-generic ones):

script hints notable entries
scripts/pm/dispatch-gates.mjs 45 2 real, 43 self-test fixturespackages/spec/src, packages/objectql, packages/plugins, packages/drivers, .claude/agents
scripts/check-adr-0087-registration.mjs 33 packages/runtime, packages/spec — from changeset fixtures
scripts/check-empty-changeset.mjs 35 skills/demo/SKILL.md, src/app.ts, a dozen .changeset/*.md fixture names
scripts/pm/check-skill-id-lint.mjs 2 both real (this is what a clean one looks like)

End to end, with dispatch-gates.mjs itself wired as a gate (the naive wiring PR #8477 measured and then rejected):

- pnpm check:pm-dispatch-gates   [lint.yml]   matched via packages/spec/src/data/filter.zod.ts ⇢ 'packages/spec/src'

The comment half is measured too: the first draft of scripts/pm/check-dispatch-gates.mjs explained the pollution with each path in backticks, and that header alone produced ten hints — reproducing, from the file documenting the problem, the exact false leads it was written to avoid.

Why it is worth a card and not a shrug

The error is one-directional in the expensive direction. A missing lead costs one CI round on one card; a fabricated lead is pasted into every dispatch prompt whose file surface brushes a fixture path, and the dev who runs it cannot tell it from a real one. The precision of the matched column is the whole product here — the "repo-wide / undetermined" bucket already exists for gates the derivation cannot place, and it is the honest home for these.

PR #8477 does not fix this. It sidesteps it for one gate by giving check:pm-dispatch-gates its own thin gate file whose only literal is the tool it runs, which is a local shape, not a remedy for the population above.

Directions (not a recommendation — grading is triage's)

  1. Stop scanning at the self-test boundary — most of these scripts put their self-test last; hints taken only from the module body would drop nearly all the false ones. Needs a boundary that is mechanically detectable, not a comment convention.
  2. Skip comment spans (and backticks inside them). Cheap, partial — it fixes the header half and none of the fixture half.
  3. Rank rather than filter — keep fixture-derived hints but print them in a weaker bucket than a literal the gate reads. Preserves recall, admits the tool cannot tell the two apart.
  4. Do nothing. The output is advisory and the PM's judgment call already sits on top of it.

Whichever direction is taken, the change is to extractWatchHints and its self-test only, and it should be measured by re-deriving the four scripts above rather than by reasoning about the regex.

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