fix(tooling): check-doc-authoring 的死 ROOT 改为点名硬报错 (#4916) - #4934
Merged
xuyushun441-sys merged 1 commit intoAug 3, 2026
Merged
Conversation
…s dead (#4916) `collectFiles()` walked each root inside `try { walk(r, files); } catch {}`. Rename, move or delete any one of `.claude` / `skills` / `content` and its ENOENT was swallowed in place: the scan finished the remaining roots and printed `✓ doc authoring guard: N files clean`, exit 0. Measured on this tree with `.claude/` renamed away, the old code reports 215 files clean, exit 0, where the honest answer is 219 — "all three roots are clean" and "one root was never opened" are the same green line with a smaller N, and nobody reads N. `assertRootsResolvable()` now runs before any walking and throws a `DeadRootError` naming every root that is missing, unreadable, or not a directory; `main()` renders that as a red gate pointing at the dead root. No whitelist and no `optional` flag: all three roots are git-tracked directories with tracked files, so no checkout that can run this gate at the repo root is legitimately missing one, and an optional marker would be a supported way to silence the failure instead of following the rename — the empty catch, spelled politely. The inner try is gone too: an error during the walk also means the corpus was only partly read. The proof is bidirectional and permanent. `--self-test` (#4913) already walked a real temporary tree with the real walker; it now renames one root away mid-run and requires red naming that root and not the survivors, replaces another root with a file and requires the `not a directory` verdict, then restores both and requires green again. This closes what #4913's self-test could not: it stayed green with the repo's real `.claude/` renamed away, because it asserts over its own temp tree. Same discipline as #4690 / #4804 / #4835 / #4851 / #4868 / #4890. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
xuyushun441-sys
marked this pull request as ready for review
August 3, 2026 16:35
xuyushun441-sys
enabled auto-merge
August 3, 2026 16:35
This was referenced Aug 3, 2026
xuyushun441-sys
deleted the
claude/issue-4916-doc-authoring-dead-root-hard-error
branch
August 3, 2026 16:55
This was referenced Aug 3, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4916
scripts/check-doc-authoring.mjs的collectFiles()把每条 root 的遍历包在try { walk(r, files); } catch {}里。.claude/skills/content任何一条被改名、移动或删除,ENOENT 就地被吞,门禁跑完剩下的 root 然后打印✓ doc authoring guard: N files clean,exit 0。从外部看,"三个 root 都干净"和"其中一个压根没被打开"是同一行绿字,只是 N 小了 —— 而没人读 N。缺陷实测(旧代码,当前这棵树)
219 → 215,exit 仍是 0。第三段是本单相对 #4915 的增量所在:#4913 加的
--self-test在自己的临时树上断言,所以仓库里真实的.claude/被改名时它照样绿。选的是硬报错,不是白名单 —— 理由
没有白名单,也没有
optional: true开关,这是刻意的:.claude、skills、content三条都是 git 跟踪的目录、里面有跟踪的文件,所以任何一个能在仓库根跑pnpm check:doc-authoring的检出都不会合法地缺其中之一 —— 议题里假设的"某个 root 在某些检出形态下合法缺席"经核查不存在,白名单没有要装的东西。而"先留着以防万一"是有代价的:一个
optional标记等于给下一个作者一条受支持的路去让这个失败闭嘴,而不是去跟进改名 —— 那就是空catch {},只是说得客气些。真有 root 变成合法缺席的那天,那是一个需要连同条件和测试一起记录的决定,不是把检查放松。顺带把 walk 内层的容错也去掉了:遍历途中出错同样意味着语料只读了一半,那也不该打印成一次干净的扫描。
新增
assertRootsResolvable()在任何遍历之前跑,对每条 root 抛DeadRootError并带上名字(缺失 / 读不到 / 存在但不是目录三种判据分开)。双向证明
红 —— 真实仓库树,
.claude/改名走开:红 —— 同时两条死根,两条都点名(存活的 root 不背锅):
绿 —— 两条都恢复之后:
这条红-绿证明已经折进
--self-test,常驻每一次 CI,不是只活在这段 PR 描述里:自检现在会在运行中途把一条 root 改名走开、要求判红且点名那条 root、且不提及存活的两条,再把另一条 root 换成同名文件、要求exists but is not a directory判据,然后两条都恢复、要求重新判绿。只观察到绿,对一个"失败表现为少扫一点"的门禁来说什么也证明不了 —— 所以自检每次都先观察红。对称方向的调查结论(#4851 的经验)
#4851(PR #4921)在隔壁脚本上揭示的不只有 16 条死条目,还有 48 份从未被列过的文档。所以本单也查了反方向:有没有真实存在、应当被这条规则约束、却从来不在
ROOTS里的语料目录?有。 全仓 md/mdx 逐个走 ts 围栏块统计后:
docs/——docs/notes/crm-development-standards.mdx(16 个 ts 块 / 3 处defineX()、docs/adr/0010、0015、0017、0057、docs/design/permission-model.md都在 ts 围栏里教 metadata 编写。这是 agent 会读的手写语料(Prime Directive [WIP] Add Chinese version of the documentation #13 要求改 ADR 治下行为前先 grep ADR),和 check:doc-authoring 的 ROOTS 不含 .claude —— agent 语料里的裸 metadata 字面量无人拦 #4913 把.claude纳入的理由是同一条。.codex/(check:doc-authoring 的 ROOTS 不含 .claude —— agent 语料里的裸 metadata 字面量无人拦 #4913 的 dev 提过的同类目录)本检出里不存在,且.gitignore:123就是.codex/—— 纯本地不入库,CI 永远看不到,不该进 ROOTS(进了反而会让本 PR 的硬报错在没有.codex/的机器上误红)。这条查过了,是"没有"。examples/、apps/、packages/**/README.md、根目录 md,零命中。本 PR 刻意没有改
ROOTS:纳入哪些docs/子目录(docs/audits//docs/handoff/这类一次性过程记录纳入等于让历史快照永久受当前 lint 约束)是个范围决定,单独立单 #4929 交维护者定。顺带扫到的同形状(未在本 PR 修)
三条
check:*脚本是同一缺陷的更彻底版本 —— 扫描根读不到时 walker 返回空数组,于是收集到 0 个文件、循环一次不进、exit 0:check-single-authz-resolver.mjs:38、check-startup-registry-verdict.mjs:217、check-driver-conformance.mjs:147。单独立单 #4930,顺手全改会让这条 PR 失焦。判定为不同形状、不该一起改的(glob 展开中途的跳过、已记账的跳过、已声明的 exit 0)在该单里逐条写明。验证
范围
只动了
scripts/check-doc-authoring.mjs一个文件,外加一份空 frontmatter 的 changeset(tooling 改动,不发版)。没有改根package.json,没有改.github/workflows/——check:doc-authoring已经在package.json:36里是--self-test && 实跑的组合,lint.yml:95已经调它,新的自检断言自动生效,无需碰这两个本仓最热的冲突点。Generated by Claude Code