fix(standard): §6.5 gate reads the index, not the working tree — v2.02 - #35
Conversation
v1.51 required CI to fail when "a tracked text file contains a CR byte" and left the implementation to the reader. The reference implementation grepped the working tree (`git grep -Il $'\r'`), with a note claiming git grep honors .gitattributes so a pinned exception is invisible to it. That claim is false, and it breaks the exception §6.5 itself grants. A file pinned `*.bat text eol=crlf` is stored LF and checked out CRLF by design — that is the point of the pin. git grep reads the working tree, finds the CR the pin deliberately put there, and fails the build. The rule granted an exception in one row and punished it in the next; the rows only appeared to agree because no repo had yet needed a pin. achernar's gradlew.bat is the case that found it. The gate now reads what is stored. `git ls-files --eol` reports the index line ending as i/lf, i/crlf or i/mixed; any i/crlf or i/mixed is CRLF in the repository, which is what §6.5 was always about. A pinned eol=crlf file reports i/lf and passes; a binary reports i/-text; a file committed with CRLF before .gitattributes existed reports i/crlf and fails, which is the case the section exists to catch. Also recorded: .gitattributes does not retroactively normalize blobs already committed with CRLF. bravais has carried `* text=auto eol=lf` for months and still reports two i/crlf files. Adoption in a repo that already carries CRLF is two acts — the config files, and a renormalization commit — not one. This repository's own CI step is corrected to match. No other §6.5 rule changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XGgUmYtDJxo8EMKyB5LdoV
A path pinned `-text` is declared binary; git does no eol conversion on it, so its blob keeps CRLF and ls-files reports i/crlf even though the file is not a text file at all. The gate flagged it, which would fail a saved MIME archive or a binary git cannot auto-detect — found on a PDF with no NUL bytes in its first 8KB, which git classifies as text. The gate now skips attr/-text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XGgUmYtDJxo8EMKyB5LdoV
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c4d7de721a
ℹ️ 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".
| CR=$(printf '\r') | ||
| if git grep -Il "$CR" -- .; then | ||
| echo "::error::CR byte in a tracked text file — §6.5 requires LF line endings" | ||
| bad=$(git ls-files --eol | awk -F'\t' '$1 ~ /i\/(crlf|mixed)/ && $1 !~ /-text/ {print $2}') |
There was a problem hiding this comment.
Reject lone carriage returns in the index
With the repository's * text=auto eol=lf attribute—and even for a path explicitly marked text—Git 2.43 reports an indexed file containing a lone CR as i/-text, not i/mixed. This predicate therefore leaves bad empty and CI passes, although §6.5 still explicitly prohibits lone CR characters in source, configuration, scripts, documentation, and CI definitions; the previous CR-byte grep detected this case. Add an index-blob check for lone CR bytes while continuing to exempt paths explicitly pinned -text.
Useful? React with 👍 / 👎.
The Standard side of v2.02. Construct's half already merged as Construct#59, so the pair is currently half-landed: the skill masthead reads v2.02 while the published Standard is still v2.01. This closes that gap.
What changed
§6.5's CI gate said "CI MUST fail when a tracked text file contains a CR byte" and left the implementation to the reader. The reference implementation grepped the working tree — and that is not a correct implementation of the rule.
A file pinned
*.bat text eol=crlfis stored LF in the repository and checked out CRLF by design — that is the entire point of the pin. A working-tree grep finds the CR the pin deliberately put there and fails the build. The section granted an exception in one row and shipped a gate that punished it in the next.The gate now reads what is stored:
Any
i/crlfori/mixedis the violation — CRLF in the repository, which is what §6.5 was always about. A pinnedeol=crlffile reportsi/lfand correctly passes. A path pinned-textis declared binary, is not a text file at all, and is skipped with its blob untouched.The second commit extends that skip to
-textpins.Why it was found
achernarcarries agradlew.bat, which a Windows batch interpreter requires in CRLF. The two §6.5 rows only appeared to agree until a repository actually needed the exception the section grants.Version
@set VERSION 2.02,@set UPDATED 2026-09-12— matching the date and version of the already-merged Construct entry, per the two-repo rule.The_Steelbore_Standard.mdis regenerated..htmland.infoare untracked build artifacts and are not carried in the repository.Note on authorship
These are two pre-existing local commits, pushed unmodified. I did not author or alter them — only opened the PR so the pair stops being half-landed. One observation worth flagging: both carry
+0300committer offsets, which Standard §14.2 forbids (Z/+0000only). Harmless to the content, and fixing it means rewriting the commits — say the word if you want that before merge.🤖 Generated with Claude Code