Skip to content

test: gate the cache-name conflict, and say so where it happens (#706) - #727

Merged
realmarcin merged 1 commit into
mainfrom
fix-706-normaliser-conflict-visibility
Aug 31, 2026
Merged

test: gate the cache-name conflict, and say so where it happens (#706)#727
realmarcin merged 1 commit into
mainfrom
fix-706-normaliser-conflict-visibility

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

Closes #706.

What the issue asked, and what I found instead

#706 asked whether the normaliser's discarded exit code should fail a recipe. Working it, the exit code turns out to be the least of it — and correctly left alone.

The committed tree was only gated on Linux. test_reference_cache_names_are_case_exact.py reads os.listdir(REFERENCES_CACHE) — the filesystem. macOS cannot hold DOI_x.md and doi_x.md at once, so on a developer machine the pair is invisible, and the divergence appears only in CI.

That is #690's lesson stated as a test: a green local run is not a green CI run. The new check reads git's index, which records the name it was told regardless of what the filesystem will store, so the clash is caught everywhere.

Mutation-checked by staging a clashing name straight into the index:

git update-index --add --cacheinfo 100644,<blob>,references_cache/PMID_10049867.TXT
→ both tracked; test RED
→ git update-index --force-remove ...; test green

A state macOS cannot represent on disk — which is precisely why the index is the right thing to read.

The exit code, deliberately unchanged

The recipes' exit $code is the validator's, and that is right: normalising must never turn a failing validation green (#697). So the exit code cannot be the signal. Instead the script prints a trailing banner naming the count and what a human has to decide, so a conflict is not one line lost in a long validation log.

Tested by stubbing rename, because the real conflict needs two files whose names differ only by case — the filesystem, not the code, is what makes it unreachable here. Both directions asserted: a conflict prints it, no conflict does not, and removing the banner reds the first.

just lint       clean
pytest tests/   2912 passed, 23 skipped

Known unrelated failure: test_no_snippet_stops_mid_word, from an untracked local cache on a record this PR does not touch.

Not done

Making just qc carry the conflict, one of the issue's suggested alternatives. It is unnecessary now: the committed tree is gated by a test that runs in validate-strict, which is a blocking lane, and qc-references is explicitly not a CI gate.

The normaliser reports a `[conflict]` when both casings of a cache name exist as
distinct files and refuses to choose -- picking between two fetches of the same
reference is not a script's call. But the recipes discard its exit code, on
purpose: normalising must not turn a failing validation green (#697). So the
condition was one line in the middle of a long validation log and nothing else.

Two changes, and the first matters more than the issue suggested.

**The committed tree is now gated on every machine.**
`test_reference_cache_names_are_case_exact.py` reads
`os.listdir(REFERENCES_CACHE)` -- the filesystem. macOS cannot hold `DOI_x.md`
and `doi_x.md` at once, so on a developer machine the pair is invisible and the
divergence appears only on Linux. That is #690's lesson stated as a test: a
green local run is not a green CI run. The new check reads GIT'S INDEX instead,
which records the name it was told regardless of what the filesystem will store,
so the clash is caught everywhere.

Mutation-checked by staging a clashing name directly into the index with
`git update-index --cacheinfo` -- a state macOS cannot represent on disk, which
is exactly why the index is the right thing to read.

**A conflict is announced rather than buried.** The script prints a trailing
banner naming the count and what a human has to decide. The exit code is
deliberately left as it was: the recipes' `exit $code` is the validator's, and
that is correct.

The banner is tested by stubbing `rename`, because the real conflict needs two
files whose names differ only by case -- the filesystem, not the code, is what
makes it unreachable here. Both directions: a conflict prints it, no conflict
does not, and removing the banner reds the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@realmarcin
realmarcin merged commit 55ad374 into main Aug 31, 2026
4 checks passed
@realmarcin
realmarcin deleted the fix-706-normaliser-conflict-visibility branch August 31, 2026 23:29
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.

The cache normaliser's exit code is discarded, so a conflict cannot fail anything

1 participant