Skip to content

docs(os-dev): 「Byte discipline」段的 binary-file 危害点名 NUL,其余字节改引门禁脚本头的三条 (#5579) - #5642

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5579-byte-discipline-harms
Aug 5, 2026
Merged

docs(os-dev): 「Byte discipline」段的 binary-file 危害点名 NUL,其余字节改引门禁脚本头的三条 (#5579)#5642
os-zhuang merged 1 commit into
mainfrom
claude/issue-5579-byte-discipline-harms

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5579

正文用 U+00XX 这种不含反斜杠转义的写法指代字节(#5460 口径)。文件里的散文沿用该文件与门禁脚本头既有的 0x01/0x7f 十六进制写法 —— 同样不含反斜杠转义,不会被编辑工具 materialise 成真字节,且与本 PR 要对齐的那个脚本头一致。

前提复核:仍成立

origin/main(4658e57)的 .claude/agents/os-dev.md「Byte discipline」段仍是 issue 记录的那个表述 —— 该段给出的唯一危害理由是:

One raw control byte makes grep treat the whole file as binary: zero matches, no signal, and the rule you just wrote becomes invisible to every agent that greps for it.

实测:该危害只对 NUL 成立

不引用 issue 的表格,在本分支容器内重测。三个样本各为「一枚控制字节 + 一行可搜索文本」,用 printf 生成(未粘贴任何裸字节),od -tx1 确认字节确实落在文件里;GNU grep 3.11 + ripgrep 14.1.0,均不加 -a:

样本字节 grep -n searchable rg -n searchable
U+0000 binary file matches(不打印行内容) binary file matches (found "\0" byte around offset 4)
U+0001 2:searchable line 2:searchable line
U+007F 2:searchable line 2:searchable line

与门禁脚本头 #5157 段的实测结论一致。即:门禁扫描面里除 NUL 之外的每个字节(含 #5460 纳入门禁、PR #5577 刚补进自扫字符类的 DEL)都不会让文件被当成二进制。

这不只是文字不精确 —— 它给 agent 留了一条自洽的错误推理:写出一枚 U+0001 或 U+007F、按 #5484 修好的字符类自扫命中,再去核对指令给出的唯一判据(「本文件被 grep 当成二进制」),发现不成立,于是把门禁的红读成误报。

改法:搬门禁脚本头已经写好的口径

scripts/check-nul-bytes.mjs 脚本头早就把两侧分开论证好了,os-dev.md 一条都没搬 —— 它搬的恰好是唯一不适用的那条。逐条对齐:

脚本头 改后的 os-dev.md
「Measured, that harm really is NUL-specific: GNU grep 3.11 and ripgrep 14.1 report 'binary file matches' for a file carrying 0x00, and keep matching normally for one carrying 0x01 or 0x03」 「Measured, only a raw NUL makes grep and ripgrep treat the whole file as binary and report zero matches with no signal」+「Every other scanned byte keeps matching line by line」
危害 1「The byte RENDERS AS NOTHING … Code that lies to every reader」 「it renders as nothing, so the code lies to every reader」
危害 2「Nobody can search for it … unfindable in BOTH spellings」 「it is unfindable in both spellings, since the file holds a byte and not the escape text you would search for」
危害 3「The accident source does not pick bytes」 「the accident source does not pick byte values

另补一句直接堵上那条错误推理:「不是 NUL、grep 还能搜到」永远不构成把门禁红或自扫命中读成误报的理由。危害论证指向脚本头「cite it, don't re-derive it」,不在 prompt 里再抄一遍论证细节 —— 该文件每个词都进每次派单的 token 预算。

顺带两处陈旧(issue 正文点名)

边界:未做也未声称做的事

验证

  • node scripts/check-nul-bytes.mjsOK (scanned 5537 tracked text file(s); … no raw ASCII control bytes)
  • 改动文件自扫 grep -naP 完整控制字节类(含 DEL)→ 零命中(exit 1)
  • 新增行 cat -Aod -c 复核 → 除 LF 外无任何控制字节
  • 全程未向任何文件写入裸控制字节;验证样本在 scratchpad 用 printf 生成,与仓库文件面隔离

.claude/ 文档-only、无用户可见变更,故不加 changeset,走 skip-changeset 标签路线(⛔ 不写空 frontmatter changeset)。


Generated by Claude Code

…#5579)

该段给出的唯一理由是「One raw control byte makes grep treat the whole file as
binary: zero matches, no signal」——而这条只对 NUL 成立。在容器内独立复现(样本用
printf 生成,未粘贴裸字节;GNU grep 3.11 + ripgrep 14.1.0):

    U+0000  grep: binary file matches            rg: binary file matches (found "\0" ...)
    U+0001  grep: 2:searchable line              rg: 2:searchable line
    U+007F  grep: 2:searchable line              rg: 2:searchable line

即门禁扫描面里除 NUL 之外的每个字节(含 #5460 纳入门禁、#5577 补进自扫字符类的
DEL)都不会让文件被当成二进制。危害只写这一条的后果不是文字不精确:agent 写出一枚
非 NUL 控制字节、自扫命中后去核对指令,会发现唯一被陈述的判据不成立,从而把门禁的红
判成误报。

`scripts/check-nul-bytes.mjs` 脚本头早就把两侧分开论证好了(#5157 段),本次把散文
口径搬过去对齐:

- binary-file / zero-matches 那条点名 NUL,并标明是实测结论;
- 其余扫描面字节引脚本头写清的三条:渲染为空(代码对每个读者说谎)、两种拼写互不
  命中(文件里是字节,不是你会去搜的转义文本)、事故源不挑字节值;
- 补一句直接堵住上述推理:「不是 NUL、grep 还能搜到」永远不构成把门禁红或自扫命中
  读成误报的理由;
- 危害论证指向脚本头「引用它,不要重新推导」,不在此处再抄一遍论证细节。

顺带修同段两处陈旧:

- 「this repo has paid four times」的硬编码计数改为免计数措辞——该族已多于四例,
  #5624 刚因同样的漂移把台账里的 sibling 计数改成不含数字的表达;
- 「a `0x01` that `check:nul-bytes` does not scan for (#5157)」的现在时已错:#5157
  正是把该字节纳入扫描面的那一单,改为过去时的事实句。

未做(留档而非顺手扩面):单源化——让字符类与危害论证不再手抄多处——是 #5484 正文
留下的方向,本 PR 只修散文口径,不动 `scripts/check-nul-bytes.mjs`、不动 #5577 刚
补的自扫字符类、不动 #5630 刚加的 Toolchain traps 条目。

纪律:全程未向任何文件写入裸控制字节,散文沿用该文件与脚本头既有的 `0x01`/`0x7f`
十六进制写法(不含反斜杠转义,不会被编辑工具 materialise)。
`node scripts/check-nul-bytes.mjs` 绿;改动文件自扫
`grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` 无命中;`cat -A` / `od -c` 复核新增
行无意外字节。

`.claude/` 文档-only,无用户可见变更,走 skip-changeset 标签路线。

Fixes #5579

Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE

Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 9:31pm

Request Review

@github-actions github-actions Bot added the size/s label Aug 5, 2026
@os-zhuang os-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added documentation Improvements or additions to documentation size/s labels Aug 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 21:42
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 952f5c7 Aug 5, 2026
27 of 28 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5579-byte-discipline-harms branch August 5, 2026 21:52
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…tstack-ai#5650)

* docs(os-dev): PR 收尾清单增补 `skip-changeset` 硬步骤,以回读 PR labels 为闭环 (objectstack-ai#5559)

测试-only / 工作流-only / `.claude/`-only 的 PR 不写 changeset,靠 `skip-changeset`
标签豁免 Check Changeset。此前收尾清单对这一步只字未提,dev 在 PR 正文「声明」了
标签却没有真正挂上,objectstack-ai#5533/objectstack-ai#5538 两例各烧一轮门禁红并由 PM 手工兜底 ——
「声明了 ≠ 执行了」正是 declared ≠ enforced 的 agent 版。

新增条目把它写成与「PR 正文以 Fixes 开头」同级的硬步骤,并按实测校正了三处认知:

- 没有任何自动化会替你挂:`.github/labeler.yml` 九条规则里没有 `skip-changeset`,
  2026-08-05 六例(objectstack-ai#5533/objectstack-ai#5538/objectstack-ai#5542/objectstack-ai#5624/objectstack-ai#5642/objectstack-ai#5645)的该标签全部来自 agent,
  `github-actions[bot]` 一次都没挂过 —— 它只负责 `size/*`、`documentation`、
  `tests`、`ci/cd`。
- 只「加一个标签」,不要整集写入:整集写入会把 bot 刚挂的 `size/*` /
  `documentation` / `tests` 抹掉(objectstack-ai#5533/objectstack-ai#5538/objectstack-ai#5642/objectstack-ai#5645 的 agent `unlabeled`
  事件即此),反向也成立 —— objectstack-ai#5533 的标签 15:46:44 落、15:46:45 就被
  `github-actions[bot]` 的写入抹掉,只活了一秒。
- 闭环是回读而不是 POST:Check Changeset 在 PR 打开后数秒就实时读一次标签
  (objectstack-ai#5580),`opened` run 通常早于标签落地 —— objectstack-ai#5542 的 dev 做对了全套,仍留下
  一条红 `opened` run,之后每一次 run 才被标签豁免。所以要在 bot 平息后回读
  标签、并把读到的列表写进报告。

* docs(os-dev): 首 run 颜色按实测改成双向竞态,不再预设为红 (objectstack-ai#5559)

本 PR(objectstack-ai#5650)自己按新硬步骤执行时,实测结果与原措辞相反:标签 22:03:54 落地,
`opened` run(workflow run 31051251795,创建于 22:03:38)的 Check Changeset job
直到 22:04:35 才启动并做实时标签回读 —— 早于回读 41 秒落地的标签被读到,该 run
直接 **success**,并非预设的红。

于是这一句改为如实描述竞态:首 run 的颜色取决于 runner 启动耗时与 POST 落地
的先后,两个方向都有实例(objectstack-ai#5542 红、objectstack-ai#5650 绿)。指导语随之从「预期会红」改为
「尽快落标签,把首 run 的颜色当信息而不是自己的判决」。

预设方向被自己的实测证伪时如实记录,而不是让措辞压着实测走。

---------

Co-authored-by: os-zhuang <hr@objectstack.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant