Introduction
On November 9, a release of this website stopped before deployment even though a content comparison had passed. The existing articles still matched their approved records. A separate check was comparing the exact bytes on disk, and the Mac and Windows checkouts did not contain the same line endings.
That distinction became visible during the morning AI News release. It is a useful small case for anyone reviewing work on one machine and building it on another: decide whether a check is protecting meaning, formatting or an exact artifact before changing it.
Two stops, for different reasons
The first attempt failed one of 172 tests. The failing test compared a historical text prefix with the current exported article file. The same test had already compared the parsed article records, including their text and order. Its raw-text assertion also depended on the newline representation.
We changed that text assertion to treat CRLF and LF line endings alike while retaining the exact parsed-record comparison. The next attempt passed all 172 tests, plus type checking and focused lint. The build then stopped at the editorial source digest: the reviewed files and the Windows working files still differed at the byte level.
These were two different contracts. The historical content check needed to protect the earlier articles. The release digest needed to identify the reviewed source files. Making the first comparison portable did not justify relaxing the second.
A newline can change a fingerprint
LF represents a line ending with one byte, hexadecimal 0A. CRLF uses two: 0D followed by 0A. Two text editors can display the same lines while a byte comparison sees different input.
Git can convert line endings as files enter or leave a checkout. Its text and eol attributes, together with repository configuration, determine that behavior. A shared commit therefore needs an explicit checkout policy when a release check fingerprints working files. Git line-ending documentation
We made a small Python example using two versions of the same fictional JSON record. One ends with LF; the other ends with CRLF. Their parsed objects match, while their SHA-256 digests differ. For this deliberately limited text fixture, replacing CRLF with LF makes the byte sequences equal. Changing the record’s value remains a content change and fails that equality.
Download the runnable line-ending example
We executed this example locally with Python. It writes no files and contacts no service. It demonstrates a controlled byte difference; it does not reproduce our entire build or establish that arbitrary files are safe to normalize. Python’s hashing interface accepts bytes, which is the relevant distinction here. Python hashlib documentation
Keep the reviewed bytes as the release boundary
For the actual release, we restored the reviewed source bytes in the Windows checkout and disabled automatic line-ending conversion for that repository. We kept the editorial digest unchanged. We also restored two existing SVG assets byte for byte after a text-normalization attempt had changed their line endings.
That last detail matters. A broad text rewrite can alter an asset that was never part of the intended change. We used per-file hashes to locate differences, then returned those assets to their approved bytes. We did not use a blanket normalization result as approval.
For a fresh project, we would define text-file line endings in repository attributes before review and test the checkout on the build machine. For an existing project, changing that policy is a separate source change: inspect the resulting diff, preserve binary files and signed artifacts, and review the new candidate before recording a new digest. This is a recommendation, not a claim that we retrofitted a repository-wide attributes policy here.
A matching digest is only one check
The morning release subsequently passed all seven release commands, including its 172 tests, source review check, builds and deployment. We then checked the canonical article, feed, sitemap and published image bytes. The 39 earlier editions remained unchanged.
A source digest alone would not establish any of that live behavior. It would not show whether a phone image loaded, a link reached the right article or a private editor stayed unavailable. We reviewed the article in Chrome at desktop and phone widths; this was responsive browser testing, not a physical-device or Safari test.
The practical question for the next release is precise: what is this check supposed to keep equal? Compare parsed records to protect article content. Compare exact bytes to identify a reviewed artifact. Check the live page to learn what readers can actually reach. A failure is useful when its boundary is clear enough to diagnose without quietly changing the rule.



