What
plugins/ca/tools/farm.ts contains a single raw NUL byte at offset ~48294, inside what is meant to be a string separator:
return createHash("sha256").update(parts.join("<NUL>")).digest("hex");
It is a literal U+0000 in the source rather than the escape "\0" or "\u0000". TypeScript accepts it and the behaviour is correct — the separator genuinely is a NUL, which is the right choice for a hash separator — but it is written in a form no reader can see.
Why it matters
ripgrep refuses to search the file. rg treats any file containing a NUL as binary, so the repo's largest source file (154 KB, ~3100 lines) silently returns "binary file matches" instead of results:
binary file matches (found "\0" byte around offset 48294)
Every content search across the codebase — including the ones agents run to orient themselves — skips farm.ts unless the caller knows to pass --text. That is a silent gap in exactly the file most likely to be searched.
git diff is unaffected: git's binary sniff only reads the first 8000 bytes and the NUL sits well past that, so diffs and blame render normally. Verified. So this is a search/tooling problem, not a version-control one.
Fix
Replace the raw byte with the escape "\0" (or "\u0000"). Byte-for-byte identical at runtime — the emitted string is unchanged, so farm.js rebuilds identically and the hash values are unaffected.
Worth pairing with a guard, since the next paste can reintroduce it invisibly: a check that no tracked text source contains a raw NUL. test_ci_impact.py or the farm surface suite is the natural home.
Acceptance criteria
- AC-1:
plugins/ca/tools/farm.ts contains no raw NUL byte, and rg returns normal text results for it.
- AC-2: the separator's runtime value is unchanged — the fingerprint helper still joins on U+0000, proven by a test asserting the hash of a known input is unchanged across the edit.
- AC-3: a guard fails if any tracked source file under
plugins/** or .github/scripts/** reintroduces a raw NUL.
Not in scope
Found while investigating #515, when a content search over farm.ts returned nothing and the reason was not the query.
What
plugins/ca/tools/farm.tscontains a single raw NUL byte at offset ~48294, inside what is meant to be a string separator:It is a literal U+0000 in the source rather than the escape
"\0"or"\u0000". TypeScript accepts it and the behaviour is correct — the separator genuinely is a NUL, which is the right choice for a hash separator — but it is written in a form no reader can see.Why it matters
ripgrep refuses to search the file.
rgtreats any file containing a NUL as binary, so the repo's largest source file (154 KB, ~3100 lines) silently returns "binary file matches" instead of results:Every content search across the codebase — including the ones agents run to orient themselves — skips
farm.tsunless the caller knows to pass--text. That is a silent gap in exactly the file most likely to be searched.git diffis unaffected: git's binary sniff only reads the first 8000 bytes and the NUL sits well past that, so diffs and blame render normally. Verified. So this is a search/tooling problem, not a version-control one.Fix
Replace the raw byte with the escape
"\0"(or"\u0000"). Byte-for-byte identical at runtime — the emitted string is unchanged, sofarm.jsrebuilds identically and the hash values are unaffected.Worth pairing with a guard, since the next paste can reintroduce it invisibly: a check that no tracked text source contains a raw NUL.
test_ci_impact.pyor the farm surface suite is the natural home.Acceptance criteria
plugins/ca/tools/farm.tscontains no raw NUL byte, andrgreturns normal text results for it.plugins/**or.github/scripts/**reintroduces a raw NUL.Not in scope
Found while investigating #515, when a content search over
farm.tsreturned nothing and the reason was not the query.