Skip to content

check-doc-authoring 的对称方向:docs/ 是真实存在、教 metadata 编写的语料,却从来不在 ROOTS 里 #4929

Closed

Description

@xuyushun441-sys

未认领。 实现 #4916(死 ROOT 硬报错)时按 PM 要求查的对称方向,不在那条 PR 的范围内,按 Prime Directive #10 单独归档。

背景:为什么要查对称方向

#4851(PR #4921)修的是隔壁脚本 .claude/workflows/docs-accuracy-audit.js 上的同一形状,而它揭示的比议题写的更多:那份清单不只有 16 条死条目,还有 48 份磁盘上存在却从未被列过的文档。两个方向同时腐烂,且都不出声,但只有第一个方向被人立过单。

#4916 只覆盖了第一个方向(声明了却解析不到的 root)。第二个方向是:有没有一个真实存在的、应当被这条规则约束的语料目录,从来就不在 ROOTS 里?

查的方法与结论

对全仓 git ls-files '*.md' '*.mdx'(不含 .changeset/)逐个走 ts/typescript/tsx 围栏块,统计两件事:(a) 现有的 bare-literal 违规;(b) 块内是否出现 defineX(...) 或 16 个 domain 的类型标注 —— 即"这份文档是否在教 metadata 编写"。

(a) 全仓当前零违规,ROOTS 内外都是。所以下面这些是尚未腐烂的盲区,不是已经烂掉的现场 —— 和 .claude 在 #4913 之前的状态完全一样。

(b) ROOTS 之外确实在教 metadata 编写的文件:

文件 ts 围栏块 defineX( 类型标注
docs/notes/crm-development-standards.mdx 16 3 0
docs/adr/0010-metadata-protection-model.md 9 2 0
docs/adr/0015-external-datasource-federation.md 12 2 0
docs/adr/0057-system-data-lifecycle-and-retention.md 1 3 0
docs/adr/0017-object-has-many-view.md 1 1 0
docs/design/permission-model.md 1 3 0

排除的误报:docs/adr/0024-mcp-connectors.md 命中的是 TS interface 字段 def: Connector;,packages/spec/docs/SYNC_ARCHITECTURE.md 命中的是散文里的 "Field Mapping",都不是 export const 形。

核对过、确认不是语料的: examples/(21 份 md,零命中)、apps/、docker/、根目录 md、packages/**/README.md(155 份,零命中)。

.codex/(#4913 的 dev 提到的同类 agent 目录):本检出里不存在,且 .gitignore:123 就是 .codex/。它是纯本地、不入库的 agent 配置,CI 里永远看不到 —— 不该进 ROOTS(进了反而会让 #4916 的硬报错在没有 .codex/ 的机器上误红)。查过了,这条是"没有"。

所以 docs/ 该不该进 ROOTS

支持的理由,和 #4913 把 .claude 加进来的理由是同一条:docs/ 是 agent 会读的手写语料 —— AGENTS.md Prime Directive #13 明确要求"改动 ADR 治下的行为前先 grep ADR",docs/notes/crm-development-standards.mdx 的标题直接就是《Development Standards》,里面 16 个 ts 块教应用怎么写。一份 ADR 里的 bare literal 会被下一个 agent 原样抄进 app 代码,和 skills/ 里的一份坏样本没有区别。

需要先定的两件事(所以本单是决策单,不是直接实现单):

  1. 范围:整个 docs/(177 份 md),还是只 docs/adr/ + docs/design/ + docs/notes/?docs/audits/、docs/handoff/、docs/plans/ 是一次性的过程记录,不是"AI 抄写的语料",把它们纳入等于让历史快照永久受当前 lint 约束 —— 一份两个月前的 handoff 里写着当时的 bare literal 是史实,改它是伪造记录。倾向:只纳入 docs/adr / docs/design / docs/notes,并把"为什么不是整个 docs/"写进脚本注释,否则下一个人会以为是漏了。
  2. 历史文档怎么办:当前零违规,所以现在纳入是零成本的 —— 这正是纳入的最佳时机,拖到有违规时再纳入就变成一次要么改史料要么加豁免的争论。

与 #4916 的关系

#4916 让"声明了但没了"变红;本单问的是"存在但从没声明过"。两者互补,合起来才是 #4851 那条经验的完整版本。#4916 的 PR 里没有改 ROOTS,刻意留给本单决定。

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 3, 2026
  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    认领(PM 分派)

    范围按本单正文的分析定:纳入 docs/adr、docs/design、docs/notes(教 metadata 编写的活语料);排除 docs/audits、docs/handoff、docs/plans(一次性历史快照,纳入等于让史料永久受当前 lint 约束 —— 改两个月前的 handoff 是伪造记录)。"全仓当前零违规、现在纳入零成本"的窗口正开着,别等它关上。


    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    审核通过 — PR #4978 已取消草稿并挂上 auto-merge(SQUASH)

    算术对得上:docs/ 共 177 个 md/mdx,排除 audits(32)+ plans(3)+ handoff(1)= 36,净增 141,扫描面 219 → 360 精确吻合 —— 零违规不是靠放宽或加 SKIP_FILES 凑出来的。趁"全仓零违规"的窗口纳入,兑现了本单立案时的判断:现在做零成本,拖到有违规再做就是"改史料还是加豁免"的争论。

    一处比我的裁定更宽的判断,我接受并说明理由:我裁的是"纳入三个子目录",dev 把 ROOT 定为整个 docs + 三个历史目录进 SKIP_PATHS。效果是顶层 11 份手写指南(protocol-upgrade-guide 等)也进了扫描,且未来新增的 docs/ 子目录默认在保护内 —— 与 #4913 的教训一致(范围划窄一次,就会再漏一次)。历史豁免用显式 skip 表达,方向正确:活语料默认在内,豁免必须点名。

    双向证明含 mutation 检查:同一个违规体放在 docs/adr(判红)与 docs/handoff(保持绿);从 ROOTS 删掉 docs → self-test 红,删掉三条 SKIP_PATHS → self-test 红。两个方向的断言都是承重的。

    SKIP_PATHS 不做死条目检测的取舍成立:docs/audits 改名后 skip 失配的失败方向是安全的 —— 那些文件会进入扫描并响亮判红,而不是静默漏掉;且 .claude/worktrees 在正常检出里本就不存在,加可解析性断言反而会误红。与 #4916 对 ROOTS 的处理不同向,理由写进了注释,没有为对称而对称。

    落点 2 文件,零共享文件。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

domain:devxDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looptoolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions