Skip to content

The @ref hygiene gate dies on a mid-rename working tree instead of reporting (#942) - #947

Merged
philcunliffe merged 2 commits into
masterfrom
fix/issue-942
Aug 19, 2026
Merged

The @ref hygiene gate dies on a mid-rename working tree instead of reporting (#942)#947
philcunliffe merged 2 commits into
masterfrom
fix/issue-942

Conversation

@philcunliffe

Copy link
Copy Markdown
Contributor

Clears the one deferred finding from the PR #933 triage.

The defect

trackedFiles() in test/core/llp-ref-hygiene.test.js enumerated the scan set with git ls-files -z and fed it straight into fs.readFileSync at module scope. git ls-files reports the index, not the working tree, so a path it names can have no file behind it: mid-rebase, mid-rename, or any other moment where the two have not converged. One such path threw ENOENT before a single test ran and took the whole test file with it, so a developer on a transiently inconsistent checkout got a stack trace where a hygiene result belonged.

Reproduced directly in a worktree by deleting a tracked file (README.md) and running the gate:

Error: ENOENT: no such file or directory, open '.../README.md'
    at Object.readFileSync (node:fs:440:20)
    at .../test/core/llp-ref-hygiene.test.js:328:89

With the fix applied and README.md still absent, the same run is # tests 11 / # pass 11 / # fail 0.

The fix

The listing is extracted into test/helpers/tracked_files.js and filtered to paths that are present on disk, so it is a shared, testable unit rather than a private detail of one gate, and every caller of it gets the guarantee that what it returns can be read. test/core/llp-ref-hygiene.test.js now imports it at both call sites (the module-scope REFS scan and the suppression-marker gate).

This is the same guard the sibling listing in PR #933 carries, factored so there is one copy of the reasoning rather than two.

Regression test

New: test/core/tracked-files.test.js (4 tests).

  • a tracked path with no file behind it is left out of the listing
  • every listed path can be read, which is what the gates do next
  • the extension filter and the presence filter both apply
  • the real repository listing is non-empty and entirely readable

The first three build a throwaway git repo, stage files, then delete some from the working tree.

Fail -> pass evidence, with only the existsSync filter removed from the helper:

# tests 4 / # pass 1 / # fail 3

Restored:

# tests 4 / # pass 4 / # fail 0

Checks

  • npm test: # tests 4533 / # pass 4532 / # fail 0 / # skipped 1
  • npm run typecheck: clean

Not a production defect, and no LLP change: this is test-harness robustness only.

Fixes #942

@philcunliffe

Copy link
Copy Markdown
Contributor Author

⚠️ Duplicate fix — this PR and #948 fix the same defect

Issues #940 and #942 are follow-ups from two different PRs (#929 and #933), but both
enumerated the same deferred finding: trackedFiles() in
test/core/llp-ref-hygiene.test.js feeds git ls-files -z output into
fs.readFileSync with no presence filter. Two workers fixed it independently.

Both branch off master and both modify test/core/llp-ref-hygiene.test.js, so
whichever merges second will conflict.

This needs a human call: pick one approach (or merge one and rebase the other onto
it). neutral is holding both and will not merge either. The regression tests differ in
kind and are not redundant, so the surviving PR may be worth taking the other's test
with it.

test and others added 2 commits August 19, 2026 12:37
…porting (#942)

`git ls-files` reports the index, not the working tree, so a path it names can
have no file behind it: mid-rebase, mid-rename, or any other moment where the
two have not converged. `trackedFiles()` in the `@ref` hygiene gate fed that
list straight into `fs.readFileSync` at module scope, so one such path threw
ENOENT before a single test ran and took the whole file with it. A developer on
a transiently inconsistent checkout got a stack trace where a hygiene result
belonged.

Extract the listing into `test/helpers/tracked_files.js` and filter it to paths
that are present on disk, so every caller of the shared gate listing gets the
guarantee that what it returns can be read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@philcunliffe
philcunliffe marked this pull request as ready for review August 19, 2026 19:44
@philcunliffe
philcunliffe merged commit ff895aa into master Aug 19, 2026
10 checks passed
@philcunliffe
philcunliffe deleted the fix/issue-942 branch August 19, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: deferred review findings from PR #933

1 participant