Uh oh!
There was an error while loading. Please reload this page.
fix(pm): stop the residue calling an extensionless module specifier a layout move - #12780
Merged
Conversation
… as a layout move `hintCovers` compares whole segments and an ESM/TS relative import drops its extension, so a resolved hint like `packages/spec/scripts/lib/dist-freshness` misses the tracked-prefix set by exactly `.ts`. `deepestTrackedPrefix` then stops one segment short and `unreachableClass` filed the family under "THE LAYOUT MOVED ... a real miss, worth triaging" -- about a directory nothing moved out of, for a file sitting right there. Residue-side only: `extensionlessModuleTarget` names the file the tree really has, `unreachableReason` says so instead, and such a hint no longer votes for the layout-moved class. No verdict moves; `hintCovers` is untouched. Measured on 1246b4c: unreachable families 11 (unchanged), "layout moved" 9 -> 1, and the survivor keeps hints that genuinely name nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
…nsion narrowing The measured reason the extension list is explicit rather than "any suffix": over the live fleet both rules select the same 38 hints, but the loose one names a `.test.ts` sibling for 4 of them -- a new false sentence in place of the old one. Pinned as a cost-free narrowing plus the live disagreement that justifies it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
os-zhuang
marked this pull request as ready for review
August 27, 2026 19:26
os-zhuang
enabled auto-merge
August 27, 2026 19:26
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#12299
Option B of the #12371 fork, sized to what the recount found rather than to what
the card described.
The measurement came first, and it disagrees with the dispatch — in the card's favour
Recounted on
origin/main@1246b4cf2(176 families, 7125 tracked files, 11unreachable), using the tool's own exported functions rather than a second
implementation of them.
On the question as asked, the dispatch's relayed number is exactly right and
the answer is zero. Families still printing "never was a repo path": 2 of
11 —
check:driver-memory-censusandscripts/check-test-completeness.mjs.Every one of their dead hints is a bare package specifier (
@objectstack/types,@objectstack/driver-memory, ...). Zero of them resolve to anything. That isthe honest class (c), and by the dispatch's own stated criterion the card was
done.
It is not done, because the false statement moved instead of leaving. Ruling
A resolved the module-relative literal against its writing script — correctly,
and it stays. What it also did was carry those nine families out of one wrong
sentence into another, and out of the inert bucket into the actionable one:
The mechanism is one missing extension.
hintCoverscompares whole segments andan ESM/TypeScript relative import drops its extension, so the resolved hint
packages/spec/scripts/lib/dist-freshnessmisses the tracked-prefix set byexactly
.tswhile the tree holdspackages/spec/scripts/lib/dist-freshness.ts.deepestTrackedPrefixthereforestops one segment short, at
packages/spec/scripts/lib, andunreachableClassreads a short prefix as a move. All nine families were being advertised under
"THE LAYOUT MOVED under a gate that still spells the old path — a real miss,
worth triaging" about directories nothing moved out of.
globInNonFinalSegment's docblock already calls that row "the worst row thisoutput can print", and #12329 landed partly to take
layout movedfrom 1 to 0.It came back at 9.
How it landed green. Ruling A shipped this pin:
It asserts only that the hint LEFT the "never" branch. Leaving that branch is
precisely what puts a hint into the "layout moved" one, so the pin was green for
an arrival it never looked at. A departure pin cannot see an arrival. It is
widened here to assert both ends on the same live specimen.
What this changes
Residue-side only. No verdict moves: the hints stay dead, the families stay
unreachable,
hintCoversis untouched, and the MATCHED column is byte-identical.extensionlessModuleTarget— names the tracked file a dead hint means when thehint is a module specifier with its extension dropped. It refuses a hint the
tree already HAS as a path, so the "too generic" case keeps its own, more
useful message (measured: the two cases never overlap on this tree).
unreachableReason— a fourth case, checked first because it is the strongeststatement available: the sweep knows the actual file, while every other branch
reasons from a prefix and for this shape every one of them lands on something
false.
unreachableClass— such a hint no longer votes forlayout moved.Result on this tree:
layout moved9 to 1, and the survivor(
check:entry-nameability) is honest — it keepspackages/node_modules/typescript/bin/tscandpackages/spec/scripts/dist/other.d.ts, hints that genuinely name nothingtracked. The narrowing is exactly as narrow as it claims.
The extension list is priced, not guessed
MODULE_SPECIFIER_EXTENSIONSis compared against the obvious alternative — "anysuffix in the same directory" — over the live fleet (829 distinct hints, 385
inert). Both rules select the same 38 hints, so the narrowing costs no lead.
They name a different file for 4 of them, because a test sibling sorts first:
(likewise
metadata-type-schemas,react-blocks,manifest-collection-spelling.) Reporting a gate's test sibling as "the filethis specifier means" would be a new false sentence in place of the old one —
the exact failure this repair exists to undo. Both halves are pinned, and so is
the live disagreement that justifies the narrowing, with a re-point instruction
rather than a delete instruction if those test files ever go.
Verification — all on the final commit
dea0c190dGate union re-derived for the ACTUAL diff with
node scripts/pm/dispatch-gates.mjs(no paths, merge-base semantics); it returned exactly the dispatch's list, no
additions. No STALE TREE warning. Every exit code captured before any pipe.
Verdict lines quoted from the gates themselves:
dispatch-gates self-test: 736 cases pass.— 2 of my new pins failed on thefirst run and both were real findings: one caught the
.test.tssibling above,the other a fixture-count pin my new sweep family moved.
check-self-test-wired: every one of the 140 script(s) CI runs that ship a --self-test has that self-test run by CI.bare-root-worklist OK self-test: 46 live row(s), 39 unreachable as spelled, 39 recorded verdict(s) — none stale, none missing, none contradicted.check-nul-bytes: OK (scanned 7118 text file(s) ... no raw ASCII control bytes).Repo-wide
pnpm lintis narrowed, and the narrowing is declared rather thanskipped. (1) The population is read from eslint's own config, which states —
measured there with a positive control — that this repo runs one
eslint.config.mjsthat never enables type-aware linting for ANY file. (2)--format jsonreports 1 file linted, 0 errors, 0 warnings. (3) With notype-aware linting and a one-file diff, this change cannot move the verdict on
any untouched file. CI runs the full farm regardless.
skip-changeset: internal PM tooling, in no published package; this publishesnothing.
Generated by Claude Code
Generated by Claude Code