Skip to content

[finding] dispatch-gates residue: after #12514 the extensionless-target reason is only reachable for a BARE hint, where it names the wrong cause — latent wrong-reason row, zero instances today #12797

Description

@claude

Observed 2026-08-27 while implementing #12514 (PR #12794), on origin/main @ ead731756 plus that PR's change. Filed unassigned, recording only. ⛔ Not fixed in #12794 — out of that card's scope, which is the matcher.

What changed under this branch

unreachableReason checks target FIRST, and it is the strongest sentence the residue can print:

if (target) {
return `'${hint}' — the tree HAS ${target}; the literal is that file's
extensionless module spelling, which no whole-segment comparison reaches`;
}

That was true when it was written: hintCovers could not reach a dropped extension, so "no whole-segment comparison reaches" was a statement about the rule. #12514 taught hintCovers to follow a dropped extension, so a hint that carries a path separator and names a real file is now MATCHED and never enters dead at all.

The residue path that survives, and prints the wrong cause

One population still reaches the target branch: a hint the matcher refuses for a different reason — the bare-single-segment rule (if (!hint.includes('/') && !plain.startsWith('.')) return false). For a hint spelled conversions with a tracked root file conversions.ts:

  • extensionlessModuleTarget('conversions', files, prefixes) returns conversions.ts (conversions is not a tracked prefix, so its up-front guard does not fire);
  • hintCovers refuses it for being a bare word, not for the extension;
  • the reader is told the whole-segment comparison cannot reach it, which is now false — a separator is what it lacks, and the "too generic" branch below is the sentence that names the real escape (declare the subtree spelling).

Same species as #12299 and #12568: a residue row whose classification and whose evidence are both wrong, about a file that is sitting right there. Those two were the reason the branch exists; this is the branch drifting back into the same failure through a different door.

Reachability today: LATENT, measured not assumed

Zero on this tree. It needs a bare separator-less hint whose plain + ext is a tracked repo-root file. Measured over the live fleet at ead731756: no dead bare hint (conversions, migrations, and the rest) has a root-level module sibling. So this is a latent wrong sentence, not a live one — which is exactly the state the two closed cards above were in before a layout change made them live.

Why the #12794 pin does not cover it

That PR adds a coherence case asserting no hint that carries a separator is both dead to the matcher and extensionless-resolvable. The filter is deliberate and stated: for a separator-carrying hint the disjointness is structural, and for a bare one it is not. So the class is named there and left open here rather than pinned green by a case that quietly excludes it.

⛔ Not proposing a shape

Whether the right repair is to order the bare-word branch ahead of target, to teach the target sentence which refusal actually fired, or to decide the branch is now residue that should be retired, is triage — and it sits on scripts/pm/dispatch-gates.mjs, which has a serial queue on it (#12470, #12411, #12410, #12500, #12749). Whoever holds that file next is the cheapest place for it.

Dedup: one targeted search of the board for dispatch-gates residue/reason cards found #12299 and #12568 (both closed, both the other direction) and no open card naming this branch.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions