Skip to content

Commit c27f29f

Browse files
fix(devx): check:nul-bytes 把「已写入但尚未 git add」的文件纳入扫描面 (#6984) (#7104)
`scan()` 原本只枚举 `git ls-files`,也就是索引。一个刚写出来、还没 `git add` 的文件根本不在扫描集里,门禁照样 exit 0 并打印它惯常的成功 行 —— 读起来是「这棵树没有裸控制字节」,而不是「你刚写的那个文件没被 看过」。 这个洞的形状恰好等于本门禁自己的事故源:每一例裸字节都来自编辑工具在 作者「正在写关于这个字节的内容」时把转义落成真字节(#4763 / #4890 / PR #5140 / #5460 的 issue 正文),而落点必然是一个**新文件**。于是 agent 指令要求的那次「push 前跑一遍」,正是最可能在看一个索引枚举够不 到的文件的那次运行。#6984 实测过:一个未跟踪文件里躺着裸 0x1b, AGENTS.md 的 self-scan 找得到,本门禁绿,唯一的差别是索引。 - 扫描集改为「索引 + `--others --exclude-standard`」,即 git 既不跟踪 也不忽略的工作区文件。 - 成功行改为同时报出两半:`scanned N text file(s) -- N tracked, M untracked-not-ignored`。M 为 0 时**照样打印**,否则 CI 的绿和 #6984 之前那种绿会渲染成同一句话,而后者正是本单要消掉的假信心。 - 未跟踪的命中在报错行上标 `[untracked]`——「还没 add」是作者拿到 一个被拒文件后第一个需要知道的事实。 - `--list` 逐条列出未跟踪的那一半路径。 三项实测,不是断言: 1. `--exclude-standard` 让本地垃圾留在外面。本仓库在完整 `pnpm install` 之后,未跟踪路径 76122 条,过 `--exclude-standard` 之后 0 条;它是剪 枝而不是遍历,16ms vs 索引枚举的 5ms。EXCLUDED 仍叠加在上面,所以有 人把 node_modules 取消忽略也点不红。 2. CI 里是 no-op:workflow 检出的是一个 commit,全树受跟踪,未跟踪那一 半恒为空。`--self-test` 用「把样本 stage 掉再扫一遍」证明,而不是在 注释里声称。 3. 反向:回退 `enumerate()` 的未跟踪那一半,6 条新断言立刻转红。 `--self-test` 断言数 48 → 75。临时仓库额外把 `core.excludesFile` 指向 一个空文件,否则开发者的全局 ignore 会让「EXCLUDED 仍然生效」那条样本 因为另一个原因通过。 工具链改动,不涉及任何 package,不发版 —— 走 `skip-changeset`,与近期 scripts/ + workflow-only PR 的做法一致(#7048 / #7008 / #6983 / #6917)。 Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 9f7a7c2 commit c27f29f

2 files changed

Lines changed: 313 additions & 27 deletions

File tree

.github/workflows/lint.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,13 @@ jobs:
117117
- name: "@objectstack/verify stand-in erasure guard"
118118
run: pnpm check:verify-stand-in
119119

120-
# Raw control-byte guard (#3127 / #4890 / #5157 / #5460). Scans every
121-
# tracked TEXT file for a raw ASCII control byte and fails on any hit.
120+
# Raw control-byte guard (#3127 / #4890 / #5157 / #5460 / #6984). Scans
121+
# every TEXT file git knows about for a raw ASCII control byte and fails on
122+
# any hit. Since #6984 the scan set is the index PLUS untracked-but-not-
123+
# ignored working-tree files, so a locally-authored file is covered before
124+
# it is staged; here that widening is a no-op, because a workflow checks out
125+
# a commit and has no untracked files at all (the step's summary line says
126+
# so — it names both halves, and the untracked one reads 0 in CI).
122127
# WHICH bytes are in the set and WHY each is rejected are stated and argued
123128
# once, in the gate script's header — `scripts/check-nul-bytes.mjs`. That
124129
# header is authoritative and this comment cites it rather than restating it

0 commit comments

Comments
 (0)