Found while implementing objectstack#5425 (the repo-wide scan it asks for immediately reported these). Filed separately per Prime Directive #10 — objectstack#5425's dispatch scope is useDatasetFields.ts plus the gate, so these are not fixed there.
Blocked-by: objectstack#5425 — its PR adds the KNOWN_OFFENDERS baseline map in scripts/check-control-bytes.mjs that lists these four. Fixing a file here means deleting its baseline entry in the same PR; the script fails on a stale entry, so the two cannot drift apart.
The four files
⚠️ Written as escape descriptions only, never as the byte itself — see the Notes in objectstack#5425. In this issue "the six-character escape" means backslash, lowercase u, four zeroes.
| file |
line |
byte |
used as |
packages/core/src/evaluator/listConditional.ts |
71 |
U+0000 |
composite cache key, (label ?? '') + SEP + source |
packages/core/src/utils/record-title.ts |
45 |
U+0000 |
EMPTY_TOKEN sentinel |
packages/fields/src/widgets/PeoplePicker.tsx |
320 |
U+0000 |
.join(SEP) over person ids |
packages/plugin-dashboard/src/DatasetWidget.tsx |
89 |
U+0001 |
.join(SEP) over row-dimension values |
Reproduce (the scan the gate runs):
git ls-files -z | tr '\0' '\n' | while IFS= read -r f; do
LC_ALL=C grep -qaP '[\x00-\x08\x0b\x0c\x0e-\x1f]' "$f" 2>/dev/null && echo "$f"
done
Two different harms, and the measurement that separates them
The first three carry U+0000 and are invisible to content search today:
grep -n "const" packages/core/src/utils/record-title.ts
# => grep: packages/core/src/utils/record-title.ts: binary file matches
packages/plugin-dashboard/src/DatasetWidget.tsx is different, and worth writing down because objectstack#5425's body predicts otherwise. Measured on GNU grep 3.11 and ripgrep 14, writing one control byte per fixture file and searching for a literal on another line:
- U+0000 is the only byte that triggers binary classification. grep prints
binary file matches; ripgrep prints binary file matches (found NUL byte around offset N).
- U+0001, U+0002, U+0007, U+0008, U+000B, U+000C, U+000E, U+001A, U+001B, U+001F and U+007F all print the matching line normally in both tools.
So DatasetWidget.tsx is not grep-blind — grep -n "rowDims" on it returns line 67 as normal text. Its defect is the other one: an invisible byte sitting in a string literal, which no reviewer can see, no diff renders, and which is indistinguishable from a typo'd neighbour. That is still a real defect (a separator nobody can read or verify), but it is not the code-search outage the other three are, and the fix priority should reflect that.
This also sharpens objectstack#5157's lesson rather than contradicting it: a NUL-only scanner does miss these, and they should not be in the tree — but the reason to reject them is "never intentional, never reviewable", not "grep goes blind". The gate landing in objectstack#5425 covers both classes and says which harm applies to which byte.
Suggested fix
Same shape as objectstack#5425's: replace each separator with a readable spelling that cannot occur in the joined values — a newline, or the six-character escape named above if the runtime value must stay byte-identical — then delete that file's entry from KNOWN_OFFENDERS. Four one-line changes; record-title.ts's EMPTY_TOKEN is a sentinel rather than a separator, so check that nothing compares it against persisted data before changing its value.
⚠️ Writing discipline: any PR or comment text describes the escape and never pastes the byte. Two of the five prior incidents in this family happened while an agent was writing the rule itself (objectstack#4763 / #4890 / #5140 / #5157). This issue's own first revision lost an escape to that exact trap: a backslash-u escape typed into a JSON tool payload was decoded into a real byte before it ever reached GitHub, then stripped — leaving empty backticks. Spell it in words.
Found while implementing objectstack#5425 (the repo-wide scan it asks for immediately reported these). Filed separately per Prime Directive #10 — objectstack#5425's dispatch scope is
useDatasetFields.tsplus the gate, so these are not fixed there.Blocked-by: objectstack#5425 — its PR adds the
KNOWN_OFFENDERSbaseline map inscripts/check-control-bytes.mjsthat lists these four. Fixing a file here means deleting its baseline entry in the same PR; the script fails on a stale entry, so the two cannot drift apart.The four files
packages/core/src/evaluator/listConditional.ts(label ?? '') + SEP + sourcepackages/core/src/utils/record-title.tsEMPTY_TOKENsentinelpackages/fields/src/widgets/PeoplePicker.tsx.join(SEP)over person idspackages/plugin-dashboard/src/DatasetWidget.tsx.join(SEP)over row-dimension valuesReproduce (the scan the gate runs):
Two different harms, and the measurement that separates them
The first three carry U+0000 and are invisible to content search today:
packages/plugin-dashboard/src/DatasetWidget.tsxis different, and worth writing down because objectstack#5425's body predicts otherwise. Measured on GNU grep 3.11 and ripgrep 14, writing one control byte per fixture file and searching for a literal on another line:binary file matches; ripgrep printsbinary file matches (found NUL byte around offset N).So
DatasetWidget.tsxis not grep-blind —grep -n "rowDims"on it returns line 67 as normal text. Its defect is the other one: an invisible byte sitting in a string literal, which no reviewer can see, no diff renders, and which is indistinguishable from a typo'd neighbour. That is still a real defect (a separator nobody can read or verify), but it is not the code-search outage the other three are, and the fix priority should reflect that.This also sharpens objectstack#5157's lesson rather than contradicting it: a NUL-only scanner does miss these, and they should not be in the tree — but the reason to reject them is "never intentional, never reviewable", not "grep goes blind". The gate landing in objectstack#5425 covers both classes and says which harm applies to which byte.
Suggested fix
Same shape as objectstack#5425's: replace each separator with a readable spelling that cannot occur in the joined values — a newline, or the six-character escape named above if the runtime value must stay byte-identical — then delete that file's entry from
KNOWN_OFFENDERS. Four one-line changes;record-title.ts'sEMPTY_TOKENis a sentinel rather than a separator, so check that nothing compares it against persisted data before changing its value.