dash/scripts: the last two literal NUL bytes become escapes - #316
Merged
Conversation
Two tracked sources carried a literal NUL inside a sentinel string, and grep answers a query against such a file by saying nothing at all - not "no match", just silence, because one control byte makes it treat the whole file as binary: dash/src/filter.ts:147 matchKey's "(Blanks)" sentinel scripts/test-clipboard.ts:314 the colour-agreement rig's fallback marker Both are un-collidable prefixes, the same idea as the U+001F separator in the CRDT elKey, so the intent was right and only the encoding was wrong. Written as \u0000 they are the byte-identical string - which is already the repo's idiom for \u001f in crdt.ts and fields.ts. Nothing moves: dash tsc -b is clean, test-dash-filter 86/86, test-clipboard 73/73. filter.ts was hidden twice over. Git calls a diff binary when it finds a NUL in the first 8000 bytes, and that one sat at 6911 - so `git grep` answered "Binary file ... matches" with no line and no content, and every diff of the file came back as "Binary files differ", which reads as a generated blob nobody should be reading. test-clipboard.ts's NUL was at 17979, past that window, so only grep lost it and review still worked. These were the last two. DECISIONS.md carried one until a61b211, and the entry that repair points at already says to write these as an escape - this applies that to the two remaining instances. A sweep of every tracked file now finds NULs only in genuine binaries: fonts, images, media, the gradle wrapper.
nyblnet
added a commit
that referenced
this pull request
Aug 30, 2026
Two things in one push, because splitting them turns `main` red in between. FILTER.TS, AGAINST #316. Git reported `UU` with ZERO conflict markers — a BINARY conflict, because the file carried a literal NUL byte and git had been treating it as binary, which is the very defect #316 landed to fix. So there was nothing to read: the sides had to be compared by hand. #316's whole change to this file is ONE LINE — a literal NUL inside the blank sentinel replaced by its escape. Verified by forcing the diff through `cat -v`, and verified as the ONLY commit touching filter.ts on main since this branch diverged. This branch had already made the identical change: the filter work hit the same binary-diff problem from the other side and fixed it the same way. Line 147 is byte-identical on both sides and neither carries a literal NUL any more. Ours is a strict superset — the same fix plus 73 lines of filter work — so it is resolved to ours on that evidence rather than on "ours usually wins", and the file is UTF-8 text again instead of binary. NOT_RUN IS EMPTY. #399 (48945fb) registered all three exempted rigs, so all three reasons expired at once and the rig failed by design: 288/291, exactly those three, with failure text naming the fix. Deleted together with the comment blocks that justified them, because a reason for an entry that no longer exists is the same rot the list warns about. 288/288 now. Verified after: tsc clean, 64/64 dash rigs, build:single builds, shell-gate passes, and test-doc-index.mjs — which this branch briefly and wrongly repaired inside ops' zone — passes on main's version of it. test-spaces.mjs needs slides/node_modules, which CI has and this checkout does not. I did not rebase. This branch is a PR head with 100+ commits and merge commits; replaying it would rewrite published history and force-push under an open PR to avoid one three-line resolution. Merged instead. Say if the integrator wants a true rebase and I will do it deliberately rather than as a side effect.
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.
Two tracked sources carried a literal NUL byte inside a sentinel string.
grepanswers a query against such a file by saying nothing at all — not "no match",
just silence — because one control byte makes it treat the whole file as binary.
dash/src/filter.ts:147matchKey's "(Blanks)" keyscripts/test-clipboard.ts:314Both are un-collidable prefixes — the same idea as the U+001F separator in the
CRDT elKey — so the intent was right and only the encoding was wrong. Written as
\u0000they are the byte-identical string, which is already this repo's idiomfor
\u001finkernel/src/sync/crdt.tsandspaces/src/fields.ts.filter.tswas hidden twice overGit calls a diff binary when it finds a NUL in the first 8000 bytes, and that
one sat at 6911. So
git grepansweredBinary file HEAD:dash/src/filter.ts matches— no line, no content — and every diff of the file came back asBinary files differ, which reads as a generated blob nobody should be reading.test-clipboard.ts's NUL was at 17979, past that window, so only grep lost itand code review still worked. Both now grep and diff normally.
That is also why the
filter.tshunk shows asBin 21884 -> 21889 bytesin thisPR's diffstat: the old blob is the binary side. Diffs from here on are text.
Verification
grep -c ""gives 535 / 343 instead of silence;grep -n "blank" dash/src/filter.tsfinds line 147 along with 20 other hits that were invisible.in genuine binaries (fonts, images, media, the gradle wrapper) — these were the
last two in the repo.
source: both begin at codepoint 0.
tsc -bclean indash/;test-dash-filter86/86;test-clipboard73/73(bundled with esbuild exactly as CI does — it is not standalone, it imports six
slides/srcmodules).No behaviour change and no test moved.
Notes
Scoped to its own PR per
docs/PARALLEL-WORK.md:dash/is an app zone andscripts/is ops.This class of bug is already documented in
docs/DECISIONS.md— DECISIONS.mditself carried one until a61b211, and the entry that repair points at says to
write these as an escape. This applies that to the two remaining instances.
Not done here, as it would touch a third zone: a CI gate rejecting a NUL byte in
any tracked text file would stop this recurring, rather than fixing instances
three and four.