Skip to content

test: stop autocrlf from poisoning the embedded templates - #657

Merged
apotema merged 2 commits into
mainfrom
fix/goldens-crlf
Aug 7, 2026
Merged

apotema merged 2 commits into
mainfrom
fix/goldens-crlf

Conversation

@apotema

@apotema apotema commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

On a Windows clone with core.autocrlf=true (the default), all 49 golden tests fail on a clean checkout. After this: zero. The 33 remaining suite failures are E2E tests needing external toolchains (crystal, cargo, quickjs runners) and fail identically on main — verified by diffing the failure sets.

The mechanism — and why the goldens were innocent

src/templates/*.txt are @embedFile'd into the assembler. autocrlf checks them out with CRLF (build_zig.txt: 424 CRs on disk against 0 in the blob), so generated output becomes a mix — template-derived lines ending \r\n, writer-emitted lines ending \n — byte-compared against LF goldens.

Diagnosing it was its own lesson, recorded in the commit: anything that normalizes newlines while displaying the test output (a text-mode subprocess capture, most terminals) shows "expected" and "found" as character-identical, because the \r is invisible exactly where it matters. I chased line endings through the goldens twice before measuring the templates' raw bytes.

The fix

* -text — no translation, repo-wide. The committed blobs are already LF, so nothing rewrites and non-Windows checkouts are untouched; Windows checkouts stop manufacturing platform-dependent bytes in files whose bytes are load-bearing.

Repo-wide rather than scoped to templates/ + goldens/ deliberately: this repo's committed examples/**/.labelle trees are also byte-compared (regeneration no-op checks), and any future embedded fixture would silently rejoin the trap.

A red that reproduces for one developer and nobody else is worse than a real failure — it teaches that red is normal. That's how 49 phantom failures were sitting on top of 33 real (environmental) ones without anyone able to tell them apart.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Chores
    • Standardized repository file handling to preserve LF line endings across checkouts.

On a Windows clone with core.autocrlf=true, all 49 golden tests fail on a
clean checkout. The goldens were innocent: src/templates/*.txt are checked out
with CRLF, @embedfile bakes those bytes into the assembler, and the generated
output becomes a mix -- template-derived lines ending \r\n, writer-emitted
lines ending \n -- compared byte-for-byte against LF goldens.

Diagnosing this was its own lesson: piping the test output through anything
that normalizes newlines (a text-mode subprocess capture, most terminals)
shows "expected" and "found" as character-identical, because the \r is
invisible precisely where it matters. The templates' byte counts were the
tell: build_zig.txt carries 424 CRs on disk against 0 in the blob.

`* -text` disables translation in both directions for the whole repo. The
committed blobs are already LF, so nothing rewrites; checkouts stop
manufacturing platform-dependent bytes in files whose bytes are load-bearing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pbwN1ytAa8tctZMaTMjAk
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds a .gitattributes rule that disables Git line-ending translation for all files and documents the LF requirement.

Changes

Line-ending preservation

Layer / File(s) Summary
Configure line-ending policy
.gitattributes
Documents the LF requirement and adds * -text to preserve committed line endings during checkout.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Poem

A rabbit hops through files so bright,
Keeping every line ending right.
LF stays steady, clean, and true,
Across each checkout, old and new.
“No conversions!” the rabbit sings,
While tidy code grows sturdy wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: preventing autocrlf from altering embedded template line endings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/goldens-crlf

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.gitattributes:
- Line 10: Update the .gitattributes rule from "* -text" to enforce LF
normalization with "* text eol=lf", adding explicit binary exceptions for files
that must retain raw bytes. Alternatively, add a CI validation that rejects CRLF
bytes in fixtures and golden files.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fe949ba-8fb5-46c3-917b-7cbf5f8b282a

📥 Commits

Reviewing files that changed from the base of the PR and between 3bce493 and 3be1ee0.

📒 Files selected for processing (1)
  • .gitattributes

Comment thread .gitattributes
# on a CLEAN checkout -- 49 of them -- for one platform and nobody else. A
# red that reproduces only for one developer teaches everyone that red is
# normal, which is worse than a real failure.
* -text

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

if git grep -Il $'\r' HEAD -- .; then
  echo "Tracked text files contain CRLF bytes."
  exit 1
fi

Repository: labelle-toolkit/labelle-assembler

Length of output: 171


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- .gitattributes ---'
cat -n .gitattributes

printf '%s\n' '--- related policy references ---'
rg -n -i --hidden --glob '!.git' 'CRLF|LF|line.?ending|golden|fixture|template' .gitattributes README* .github 2>/dev/null || true

printf '%s\n' '--- effective attributes for representative paths ---'
git check-attr -a -- \
  .gitattributes \
  README.md \
  2>/dev/null || true

printf '%s\n' '--- tracked files containing CR bytes ---'
git grep -Il $'\r' HEAD -- . || true

Repository: labelle-toolkit/labelle-assembler

Length of output: 3448


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
cd "$tmp"

git init -q
git config user.name verifier
git config user.email verifier@example.invalid

printf '* -text\n' > .gitattributes
printf 'one\r\ntwo\r\n' > fixture.txt
git add .gitattributes fixture.txt

printf '%s\n' '--- -text staged blob bytes ---'
git cat-file blob :fixture.txt | od -An -tx1
printf '%s\n' '--- -text effective attributes ---'
git check-attr -a -- fixture.txt

git commit -qm initial
rm fixture.txt
git checkout -q -- fixture.txt
printf '%s\n' '--- -text checked-out bytes ---'
od -An -tx1 fixture.txt

printf '%s\n' '--- LF-normalizing policy ---'
printf '* text eol=lf\n' > .gitattributes
git add .gitattributes
git commit -qm policy
printf 'one\r\ntwo\r\n' > fixture.txt
git add fixture.txt
git cat-file blob :fixture.txt | od -An -tx1
git check-attr -a -- fixture.txt

Repository: labelle-toolkit/labelle-assembler

Length of output: 244


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
cd "$tmp"

git init -q
git config user.name verifier
git config user.email verifier@example.invalid

show_bytes() {
  python3 - "$1" <<'PY'
import sys
print(sys.argv[1], open(sys.argv[1], "rb").read().hex(" "))
PY
}

printf '* -text\n' > .gitattributes
printf 'one\r\ntwo\r\n' > fixture.txt
git add .gitattributes fixture.txt

printf '%s\n' '--- -text staged blob bytes ---'
git cat-file blob :fixture.txt > blob.bin
show_bytes blob.bin
printf '%s\n' '--- -text effective attributes ---'
git check-attr -a -- fixture.txt

git commit -qm initial
rm fixture.txt
git checkout -q -- fixture.txt
printf '%s\n' '--- -text checked-out bytes ---'
show_bytes fixture.txt

printf '%s\n' '--- LF-normalizing policy ---'
printf '* text eol=lf\n' > .gitattributes
git add .gitattributes
git commit -qm policy
printf 'one\r\ntwo\r\n' > fixture.txt
git add fixture.txt
git cat-file blob :fixture.txt > blob.bin
show_bytes blob.bin
git check-attr -a -- fixture.txt

Repository: labelle-toolkit/labelle-assembler

Length of output: 587


Enforce LF for future fixtures.

* -text preserves CRLF bytes in the index and working tree. It does not enforce LF. A CRLF template or golden can therefore reintroduce clean-checkout test failures. Use * text eol=lf with explicit binary exceptions, or add a CI check that rejects CRLF bytes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitattributes at line 10, Update the .gitattributes rule from "* -text" to
enforce LF normalization with "* text eol=lf", adding explicit binary exceptions
for files that must retain raw bytes. Alternatively, add a CI validation that
rejects CRLF bytes in fixtures and golden files.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3be1ee0dd2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .gitattributes
# on a CLEAN checkout -- 49 of them -- for one platform and nobody else. A
# red that reproduces only for one developer teaches everyone that red is
# normal, which is worse than a real failure.
* -text

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve LF normalization when adding text

When a Windows contributor edits a template with an editor that saves CRLF, this rule also disables clean-time normalization, so Git stores those CR bytes in the blob rather than merely preventing checkout conversion. A subsequent edit to src/templates/*.txt therefore makes @embedFile consume CRLF on every platform and recreates the mixed-ending golden failures this file is intended to eliminate. Git documents unset text as disabling conversion “upon checkin or checkout”; use an LF-enforcing rule such as * text=auto eol=lf so checkouts remain LF without accepting new CRLF blobs. Git gitattributes documentation

Useful? React with 👍 / 👎.

Comment thread .gitattributes
# on a CLEAN checkout -- 49 of them -- for one platform and nobody else. A
# red that reproduces only for one developer teaches everyone that red is
# normal, which is worse than a real failure.
* -text

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh files in existing Windows checkouts

In an existing checkout already populated with core.autocrlf=true, pulling this attributes-only commit does not rewrite unchanged tracked files. Reproducing a checkout from the parent to this change left all 424 CR bytes in src/templates/build_zig.txt, so the same golden failures persist; once Git refreshes the index, the newly unset text attribute also makes the CRLF copies of hundreds of tracked files appear modified. Include a one-time migration that safely forces affected working trees to re-check out the LF blobs rather than relying on the attribute alone.

Useful? React with 👍 / 👎.

@apotema
apotema merged commit 1e43237 into main Aug 7, 2026
6 checks passed
@apotema
apotema deleted the fix/goldens-crlf branch August 7, 2026 23:34
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.

1 participant