Repository navigation
objectui: four more source files carry raw control bytes — three are invisible to grep, one is a stray U+0001 #5450
Description
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
| 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 printsbinary 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.
Activity
yinlianghui commentedon Aug 5, 2026
认领:objectui 分片 PM 滚动补位第 9 单(Blocked-by objectstack#5425/PR objectui#3388 已合并,基线已在树上,即刻解锁;摘
pm:blocked)会话:
session_01GTRjn8xBqp75dk7kFupVRt分支:
claude/issue-5450-control-bytes-cleanup(代码落地 objectui 仓)Worktree:
objectui-issue-5450域:objectui 分片(core / fields / plugin-dashboard)
文件面:
packages/core/src/evaluator/listConditional.ts、packages/core/src/utils/record-title.ts、packages/fields/src/widgets/PeoplePicker.tsx、packages/plugin-dashboard/src/DatasetWidget.tsx+scripts/check-control-bytes.mjs(仅删对应 KNOWN_OFFENDERS 条目)+ 相应测试(越界即停)。当前在飞 0,无相交。口径照正文:各处换可读拼写或按 #3388 先例改 JSON 编码;⚠️ 写作纪律:PR/评论只写转义描述,不贴字节本体(本单第一版正文自己就踩过 JSON 载荷解码陷阱)。
record-title.ts的EMPTY_TOKEN是哨兵非分隔符,改值前先核无持久化数据比较;四条基线条目同 PR 删除(门禁对陈旧条目变红,不能漂移)。Generated by Claude Code
yinlianghui commentedon Aug 5, 2026
PM 验收:ACCEPT → objectui PR #3389 已转 ready + auto-merge(CI 15/15 settled,13 绿 2 预期跳过),跟到 MERGED。
落地内容:四处逐一按各自语义修(listConditional 改 JSON.stringify 消灭分隔符需求;其余三处保持运行时字节同值的转义拼写,各附理由 —— EMPTY_TOKEN 经核实模块私有、从不与持久化数据比较);KNOWN_OFFENDERS 基线清空,四条同 commit 删除;反向验证含「留一条不删必须报 STALE 而非 offender」全四例复现。诚实框架记账:三处等价改写在行为级不可能产生红(转义与字节是同一运行时字符),judgement 正确 —— 行为红只该由分隔符值变化触发,新 pivot 用例恰是全套件第一个真正行使双行维分隔符的用例。无 changeset,符合 .changeset/README 的 DON'T。
衍生:#5473(pivot cell key 用空格拼接 rowId/colId,含空格的维度值可碰撞并静默覆盖 cell 索引与 drill 目标 —— 与本单相邻行的发现)已复核入队
bug+pm:queue。至此 #5425 → #5450 控制字节战役全链闭环:仓内零裸控制字节、门禁常驻、基线归零。
objectui 分片 PM
session_01GTRjn8xBqp75dk7kFupVRtGenerated by Claude Code