Normalize line endings to LF so local and CI fingerprint the same bytes (#26 part 1) - #27
Merged
Merged
Conversation
The publish workflow's first real run reported published=0 skipped=9 stale=9 — every skill warned that its content differs from the 1.0.0 published under it. Nothing had changed. The fingerprint is computed over file bytes, and the bytes differ by platform. Git stores these files correctly: every committed .md blob has zero CR bytes, and `git add --renormalize .` against this commit changes nothing in the index. The divergence is entirely in the working tree. With no .gitattributes, a clone on a machine with core.autocrlf=true checks out CRLF — 9 of 9 skill folders, 27 of 27 .md files. The nine skills were published from such a tree, so the registry holds CRLF content, while the Linux runner checks out LF and fingerprints LF. `* text=auto eol=lf` pins the checkout to LF everywhere, so the next publish from any machine agrees with the next dry-run on the runner. This does not clear the nine warnings on its own — the published artifacts still hold CRLF, and realigning them means republishing, which the script deliberately refuses to do without a version bump. That is issue #26 part 2, decided separately. Refs #26 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fingerprint is over the bytes CI checks out, and .gitattributes is what decides those bytes. A change to it can change what publishes, so it belongs in the trigger paths beside clawhub.yml and clawhub-sync.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of #26. Stops the platform divergence. It does not clear the nine standing warnings — that is part 2, and it is a decision, left out of this PR deliberately so this can merge alone.
What was wrong
Nothing was wrong with the repo's content. Git stores LF: every committed
.mdblob has zero CR bytes, andgit add --renormalize .againstorigin/mainchanges nothing in the index.The divergence is entirely in the working tree. With no
.gitattributes, a clone on a machine withcore.autocrlf=truechecks out CRLF. Measured on a fresh clone ofb749e99:The fingerprint
clawhub skill publishcomputes is over those bytes, so the nine skills — published from such a tree — are stored under their CRLF fingerprint, and the ubuntu runner's LF checkout fingerprints differently. All nine differ:878408a2…17a3d92d…4d8c26d7…3c849fbb…5c321c66…9289f42d…1e367cff…de507672…84ea32d4…42ccc2d8…018ec6ed…951b0b26…7b53cad6…2c92bc24…38758458…e164a31a…54e08e30…a8942926…Confirmed against the live registry with a
--dry-runprobe from each tree. CRLF content:LF content, same commit:
So the registry really does hold the CRLF bytes, and
stale=9is a true statement about the artifacts, not a script bug.The fix, and evidence it takes effect
* text=auto eol=lfpins the checkout to LF on every platform, plus explicitbinaryfor the GIFs and PNG andeol=lffor.sh/.py(a CR after a shebang is a runtime failure, not a diff).git add --renormalize .— no index change, because git's copy was already right:A fresh clone of this branch, on Windows, with
core.autocrlf=true:And the stronger form of the same claim — the working tree is now byte-identical to what git stores, which is by definition what a Linux runner checks out:
The 6 that already matched are the GIFs and the PNG; they are still byte-identical here, so
binaryis doing its job and nothing was corrupted on the way through.python3 scripts/clawhub-sync.py --checkpasses on the normalized tree.One operational note
An existing clone does not renormalize itself.
.gitattributesgoverns checkout, and git skips rewriting files whose stat already matches the index, sogit checkout -- .is a no-op. A working tree that predates this commit keeps its CRLF until it is forced:Worth running once in any existing clone, otherwise a manual publish from there re-introduces exactly what this PR removes.
Second commit
.gitattributesis added to the publish workflow's trigger paths. The fingerprint is over the bytes CI checks out and this file decides those bytes, so editing it can change what publishes — it belongs besideclawhub.ymlandscripts/clawhub-sync.py.What this does not do
CI will still report
stale=9, and now it will do so from every machine rather than only from the runner — before this, a Windows--dry-runcompared CRLF against CRLF and readstale=0, which is why three review passes missed it. The published artifacts still hold CRLF. Realigning them is #26 part 2; the registry behaviour that decides it is now settled and written up in a comment on the issue.Refs #26
🤖 Generated with Claude Code