Skip to content

docs(os-dev): 自扫字符类补上 DEL,与 #5460 扩面后的门禁扫描面对齐 (#5484) - #5577

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5484-osdev-selfscan-del
Aug 5, 2026
Merged

docs(os-dev): 自扫字符类补上 DEL,与 #5460 扩面后的门禁扫描面对齐 (#5484)#5577
os-zhuang merged 1 commit into
mainfrom
claude/issue-5484-osdev-selfscan-del

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5484

问题

.claude/agents/os-dev.md 的 Byte discipline 段要求 dev agent 在改动「提及控制字符」时,除了跑门禁还要逐字节自扫,理由原话是 self-scan beyond the gate —— the gate's blind spots are exactly where these bytes hide。

但它给出的字符类停在 #5157 时代的扫描面(ASCII 控制字符减 tab/LF/CR,且只覆盖 C0 连续区间)。#5460(PR #5479)已把门禁 scripts/check-nul-bytes.mjs 的扫描面扩到含 DEL(U+007F,该字节不属于 C0,独自坐在 ASCII 表末端,正因如此被区间写法漏掉),这条指令没跟 —— 于是它成了门禁的真子集:

  • agent 写下一枚 U+007F,按指令自扫 → 绿;
  • 推上去,CI 的 check:nul-bytes

即这条指令在它唯一被设计来防的场景里给出假绿,把本可本地一秒发现的问题推迟整个 CI 轮次。

改动

一行:该字符类补上 \x7f,与门禁脚本头现行表述一致([\x00-\x08\x0b\x0c\x0e-\x1f\x7f])。周边散文未陈述扫描面范围,无需同步;文件面仅 .claude/agents/os-dev.md,未触 scripts/packages/lint

「同一事实手抄三处(门禁脚本 / 历史 changeset / 本指令)、靠人肉同步」的单源化根治方向,按裁定留在 #5484 正文,不在本单实施。另两处均正确:门禁脚本 #5460 已更新;两份历史 changeset 凝固在各自当时的语义。

验证

本改动无可执行面、无单测面(纯 agent 指令文本),验证由覆盖 .claude/ 的两道门禁 + 逐字节自扫构成。写这条修改本身就是 #4890 一族的事故源现场(该族多次事故全发生在「写关于该字节的内容」时),因此自扫用的是扩面后的字符类。

# 逐字节自扫(扩面后字符类,含 0x7f)—— 改动文件
$ grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' .claude/agents/os-dev.md
exit=1(无匹配)
# 提交后的 blob 同样干净:git show HEAD:.claude/agents/os-dev.md | grep -caP ... -> 0

# 门禁 1
$ node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs
✓ check-nul-bytes --self-test: 48 assertions over a temp git repo (real scan() path)
check-nul-bytes: OK (scanned 5520 tracked text file(s); skipped 5 binary, 1 non-regular;
                     no raw ASCII control bytes).

# 门禁 2
$ node scripts/check-doc-authoring.mjs --self-test && node scripts/check-doc-authoring.mjs
✓ check-doc-authoring self-test: scope wiring (.claude and the live docs/ corpus in, ...)
✓ doc authoring guard: 362 files clean — no bare metadata literals.

字节级确认改动写入的是转义文本而非真字节(od -c 该行):\ x 7 f 四个字符,行内无 177 八进制字节。

反向验证(方向先判后跑)

预判三条腿:同一枚真 DEL 字节的样本上,字符类绿(即 issue 描述的假绿)、字符类红、门禁红。实跑与预判一致:

三条腿合起来即「指令与门禁在该字节类上等价」——issue 描述的假绿窗口关闭。

changeset

.claude/ 内部 agent 指令文本,不释放任何包 → 按 pr-automation.yml 的 route 2 走 skip-changeset 标签(先例 PR #5501),不加空 frontmatter changeset(#4898)。


🤖 Generated with Claude Code

https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE


Generated by Claude Code

`.claude/agents/os-dev.md` 的 Byte discipline 段要求 dev agent 在改动涉及
控制字符时「self-scan beyond the gate」,给出的字符类却停在 #5157 时代的
扫描面(C0 减 tab/LF/CR)。#5460(PR #5479)已把 `check:nul-bytes` 的扫描
面扩到含 DEL(0x7f),该指令未跟 —— 于是它成了门禁的真子集:agent 写下一枚
0x7f,按指令自扫绿,推上去 CI 红,把本可本地一秒发现的问题推迟整个 CI 轮次,
恰好在这条指令唯一被设计来防的场景里给出假绿。

改动一行,字符类与门禁脚本头的 `[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]` 一致。
「同一事实手抄三处」的单源化根治方向留在 #5484 正文,不在本单实施。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
@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 7:36pm

Request Review

@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation labels Aug 5, 2026
@os-zhuang os-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed documentation Improvements or additions to documentation labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

范围外发现(Prime Directive #10,未在本 PR 修):

本 PR 严格只改裁定的那一行字符类,未顺手改散文 —— 该段散文的准确性归 #5579


Generated by Claude Code


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 19:47
@os-zhuang
os-zhuang enabled auto-merge August 5, 2026 19:47
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit e0b2ea7 Aug 5, 2026
29 of 31 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5484-osdev-selfscan-del branch August 5, 2026 19:58
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…ack-ai#5580) (objectstack-ai#5625)

`github.event.pull_request.labels` 是事件触发那一刻的快照。开 PR 后数秒内补
`skip-changeset` 标签,`opened` 事件的 run 看不见它 → 走计数路径 → 无 changeset
→ 红;而 `rerun_failed_jobs` 复用同一份载荷(pm-dispatch Operational notes 5),
于是这个红 run 按构造无法被重跑成绿。一日三例:objectstack-ai#5467(本门禁自己的修复 PR)、
objectstack-ai#5501objectstack-ai#5577,每例都要一个人或 agent 停下来「认签名解释掉」。

job 内新增第一个步骤,用 `gh api repos/$REPO/pulls/$PR` 实时读回标签集,产出
`steps.labels.outputs.skip`;其后每个步骤按它决定是否执行。载荷读法按 issue 建议
保留为 fast-path —— 载荷已有标签就整个 job 跳过,常规路径依旧零 runner 成本。

- **容忍方向朝着执行**:标签读不到(API 报错、无 PR 号)判为 `skip=false`,即
  照常执行守卫。读不到输入的门什么也没验证,据此发豁免正是 objectstack-ai#4690 反模式(静默
  跳过、exit 0、看起来像「无违规」);失败以 `::warning::` 明说,由计数步骤定论。
- **实时读放在 checkout 之前**:标签在位时其后全部步骤跳过,整个 job 只花一次 API
  调用 —— 收敛到实时状态比它替掉的那个 stale 红更便宜。
- **精确整行匹配**(`grep -qxF`,here-string 而非管道):被替换的 `contains(数组,
  'skip-changeset')` 是数组元素精确匹配,子串匹配会让 `skip-changeset-audit`
  这类标签新获豁免;here-string 让 `grep -q` 不进管道,避免 `-q` 首个命中即关闭
  管道、写入端吃 SIGPIPE 在 `pipefail` 下把判定翻成 false。
- 保留 fast-path 留下唯一一个反向 stale 格:标签在开 PR 后被**移除**时本 run 仍
  短路。该格自愈 —— 移除标签必然触发 `unlabeled` 事件,它起的 run 两处都看不到
  标签而照常执行;objectstack-ai#5580 那个方向没有这种救援(`labeled` run 的绿不会清掉
  `opened` run 的红)。文件内注释写明了这笔交换。

⛔ 未动 `BASE_SHA` diff 计数逻辑与 objectstack-ai#5292/PR objectstack-ai#5467 的三段有序失败文案(heredoc
终结符仍在块基缩进);未动其他 job。`allow-major` 步骤的同款载荷读法按边界留在
原样 —— RC pre-mode 期间休眠(`check-changeset-no-major.mjs` 整体让位),已记为
objectstack-ai#5620。

验证:`check:workflow-status-functions` 与 `check:nul-bytes`(含各自 self-test)
全绿;从 YAML 抽出该步骤真实脚本,以 stub `gh` 在 `bash -e` 与 `bash -eo pipefail`
两种方言下跑 7 场景 × 2 = 14 例全通过(载荷 stale/标签实时在位、无标签、空标签、
API 失败、无 PR 号、近似标签名、401 个标签的 pipefail 压力);另建前后决策真值表,
7 格中仅「载荷无标签 + 实时有标签」的首 run 与其重跑两格改变(enforce → exempt),
与事前预测一致。

Fixes objectstack-ai#5580

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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…objectstack-ai#5579) (objectstack-ai#5642)

该段给出的唯一理由是「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 之外的每个字节(含 objectstack-ai#5460 纳入门禁、objectstack-ai#5577 补进自扫字符类的
DEL)都不会让文件被当成二进制。危害只写这一条的后果不是文字不精确:agent 写出一枚
非 NUL 控制字节、自扫命中后去核对指令,会发现唯一被陈述的判据不成立,从而把门禁的红
判成误报。

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

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

顺带修同段两处陈旧:

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

未做(留档而非顺手扩面):单源化——让字符类与危害论证不再手抄多处——是 objectstack-ai#5484 正文
留下的方向,本 PR 只修散文口径,不动 `scripts/check-nul-bytes.mjs`、不动 objectstack-ai#5577 刚
补的自扫字符类、不动 objectstack-ai#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 objectstack-ai#5579

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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…ai#5680)

字符类此前在两处手抄:IS_SCANNED 表(门禁真正扫的东西)与 os-dev.md
自扫命令行。该族漂移当日已付两次(objectstack-ai#5577 自扫类缺 \x7f、objectstack-ai#5579 危害论证
只搬了对 NUL 成立的那条)。

scannedCharClass() 从表派生出 PCRE 字符类,--self-test 断言登记在
CHAR_CLASS_REFERENCES 里的每处引用与之逐字节相等;提取不到也红。
不做散文 codegen:引用文件是手写 prompt,断言只拒绝它们「错」,并在
失败信息里给出该粘贴的确切字符串。

Co-authored-by: Claude <noreply@anthropic.com>
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/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

os-dev.md 的「自扫」正则比门禁本身还窄:#5460 把 DEL 纳入扫描面后,那条指令会给出假绿

2 participants