Make the asset-version guard line-ending independent #28

Open
solvreven wants to merge 1 commit from fix/asset-guard-line-endings into main
Owner

Outcome

test_asset_version.py hashed the four versioned assets byte-for-byte. They are text files, the repo has no .gitattributes, and CI checks them out LF on debian-trixie-python while a Windows clone with core.autocrlf=true holds CRLF. The digest therefore differed by platform, and the guard was green only for whoever recorded it last.

Both sides have been re-recording their own platform's value in turn, each fix correct locally and each breaking the other:

commit recorded LF (CI) CRLF (Windows) CI Windows
1c94db7 v135 d20d1fa11d2d9a94 a932c205c61efb9b d20d1fa11d2d9a94 RED green
bbf3c01 v136 (PR #27) a932c205c61efb9b a932c205c61efb9b d20d1fa11d2d9a94 green RED
9c2faca v137 d9ca43b746e10252 350840c64b1a1aa0 d9ca43b746e10252 RED green

So main has been red in CI since 9c2faca, and PR #27's asset commit was not wrong — it was right for Linux.

Why this shape of fix

The repo had already solved this, one digest over. projection_digest in pipeline.py normalises \r\n to \n and says why, in terms that describe this bug exactly: "git stash/checkout rewrite tracked files between LF and CRLF under core.autocrlf=true, and on Windows that flipped this digest with zero code difference: same source, two different hashes, one commit apart." It is covered by test_crlf_and_lf_produce_the_same_digest.

The asset digest is the one place that remedy was never applied — "fixing a defect in one location is not fixing the defect", with the second location in a different file.

Chosen over a .gitattributes entry deliberately: that would leave the guard's correctness depending on every contributor's checkout config, which is the disconnected-mechanism shape this repo keeps paying for. Normalising in the digest makes the platform difference unrepresentable instead.

A CRLF/LF flip is not an asset change a browser needs a new ?v= for, so it must not move the digest. EXPECTED_VERSION stays 137; EXPECTED_DIGEST becomes the normalised value, identical under both conventions.

Verification

Proved RED on both the property fixed and the property that must not break — driving the real test module against controlled inputs:

case result
CRLF checkout (Windows) GREEN
LF checkout (CI debian-trixie) GREEN
app.js changed, ?v= not bumped RED
tokens.css changed, ?v= not bumped RED

tests/web/test_asset_version.py: 3 passed. Nothing else in the tree references that module or its _digest, so the change is self-contained.

Note for #27

bbf3c01 becomes unnecessary rather than conflicting once this lands. Asked there for a rebase, so neither side has to concede a digest that was correct where it was measured.

🤖 Generated with Claude Code

https://claude.ai/code/session_014adnmRztkdAPwinWXnNQ7N

## Outcome `test_asset_version.py` hashed the four versioned assets byte-for-byte. They are text files, the repo has no `.gitattributes`, and CI checks them out LF on `debian-trixie-python` while a Windows clone with `core.autocrlf=true` holds CRLF. The digest therefore differed by platform, and the guard was green only for whoever recorded it last. Both sides have been re-recording their own platform's value in turn, each fix correct locally and each breaking the other: | commit | recorded | LF (CI) | CRLF (Windows) | CI | Windows | |---|---|---|---|---|---| | `1c94db7` v135 | `d20d1fa11d2d9a94` | `a932c205c61efb9b` | `d20d1fa11d2d9a94` | **RED** | green | | `bbf3c01` v136 (PR #27) | `a932c205c61efb9b` | `a932c205c61efb9b` | `d20d1fa11d2d9a94` | green | **RED** | | `9c2faca` v137 | `d9ca43b746e10252` | `350840c64b1a1aa0` | `d9ca43b746e10252` | **RED** | green | So **`main` has been red in CI since `9c2faca`**, and PR #27's asset commit was not wrong — it was right for Linux. ## Why this shape of fix **The repo had already solved this, one digest over.** `projection_digest` in `pipeline.py` normalises `\r\n` to `\n` and says why, in terms that describe this bug exactly: *"`git stash`/`checkout` rewrite tracked files between LF and CRLF under `core.autocrlf=true`, and on Windows that flipped this digest with zero code difference: same source, two different hashes, one commit apart."* It is covered by `test_crlf_and_lf_produce_the_same_digest`. The asset digest is the one place that remedy was never applied — "fixing a defect in one location is not fixing the defect", with the second location in a different file. Chosen over a `.gitattributes` entry deliberately: that would leave the guard's correctness depending on every contributor's checkout config, which is the disconnected-mechanism shape this repo keeps paying for. Normalising in the digest makes the platform difference unrepresentable instead. A CRLF/LF flip is not an asset change a browser needs a new `?v=` for, so it must not move the digest. `EXPECTED_VERSION` stays 137; `EXPECTED_DIGEST` becomes the normalised value, identical under both conventions. ## Verification Proved RED on both the property fixed and the property that must not break — driving the real test module against controlled inputs: | case | result | |---|---| | CRLF checkout (Windows) | GREEN | | LF checkout (CI debian-trixie) | GREEN | | `app.js` changed, `?v=` not bumped | **RED** | | `tokens.css` changed, `?v=` not bumped | **RED** | `tests/web/test_asset_version.py`: 3 passed. Nothing else in the tree references that module or its `_digest`, so the change is self-contained. ## Note for #27 `bbf3c01` becomes unnecessary rather than conflicting once this lands. Asked there for a rebase, so neither side has to concede a digest that was correct where it was measured. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014adnmRztkdAPwinWXnNQ7N
fix: the asset guard hashed raw bytes, so it could only be green on one platform
All checks were successful
CI / syntax (pull_request) Successful in 8s
CI / tests (pull_request) Successful in 1m7s
CI / wheel (pull_request) Successful in 29s
CI / browser (pull_request) Successful in 16m33s
83111ce2e0
test_asset_version.py hashed the four assets byte-for-byte. These are text
files and the repo has no .gitattributes, so CI checks them out LF on
debian-trixie while a Windows clone with core.autocrlf=true holds CRLF.
The digest therefore differed by platform, and the guard was green only
for whoever recorded it last.

Both sides then re-recorded their own value in turn, each fix correct
locally and each breaking the other:

  1c94db7  135  d20d1fa11d2d9a94  CRLF   green here, RED in CI
  bbf3c01  136  a932c205c61efb9b  LF     green in CI, RED here   (PR #27)
  9c2faca  137  d9ca43b746e10252  CRLF   green here, RED in CI

So main has been red in CI since 9c2faca, and PR #27's asset commit was
not wrong -- it was right for Linux. Neither side could see the other's
failure, because each measured the assets through its own checkout.

A CRLF/LF difference is not an asset change a browser needs a new ?v=
for, so it must not move the digest. Normalising before hashing makes the
platform difference unrepresentable rather than documented; recorded
digest is now the normalised value, identical under both conventions.

Chosen over a .gitattributes entry deliberately: that would leave the
guard's correctness depending on every contributor's checkout config,
which is the disconnected-mechanism shape this repo keeps paying for.

Proved RED on both properties -- the one fixed and the one that must not
break:

  CRLF checkout (Windows)              GREEN
  LF checkout (CI debian-trixie)       GREEN
  app.js changed, ?v= not bumped       RED
  tokens.css changed, ?v= not bumped   RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014adnmRztkdAPwinWXnNQ7N
All checks were successful
CI / syntax (pull_request) Successful in 8s
CI / tests (pull_request) Successful in 1m7s
CI / wheel (pull_request) Successful in 29s
CI / browser (pull_request) Successful in 16m33s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/asset-guard-line-endings:fix/asset-guard-line-endings
git switch fix/asset-guard-line-endings

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff fix/asset-guard-line-endings
git switch fix/asset-guard-line-endings
git rebase main
git switch main
git merge --ff-only fix/asset-guard-line-endings
git switch fix/asset-guard-line-endings
git rebase main
git switch main
git merge --no-ff fix/asset-guard-line-endings
git switch main
git merge --squash fix/asset-guard-line-endings
git switch main
git merge --ff-only fix/asset-guard-line-endings
git switch main
git merge fix/asset-guard-line-endings
git push origin main
Sign in to join this conversation.
No description provided.