docs(pilot): 补 .pilot.yml + 规划层七件套(适配层),门禁 0/7 → 7/7 - #46
Conversation
端到端测试 pilot 的产物。跑 `pilot doctor` 发现本地未就绪,`pilot plan` 补齐。
## 为什么是「适配层」而不是规划本身
Brood 的规划事实来源是 `backlog/`:4 个 milestone、49 个带验收标准的 task、2 个 ADR ——
比多数仓库完整。但 `check-docs.sh --strict` 报 **0/7**,`run` 直接 fail-closed 拒跑,
因为它只认 `docs_dir` 下那七个固定文件名。
而 `plan.md` A.3 明写「已有规划 → 不要重复造」。**两条同时遵守是不可能的。**
所以七件套写成**指向 backlog/ 的视图**:每份开头声明「本文件是视图,不是事实来源,
改规划请改 backlog/」,内容是摘要 + 怎么读 + 怎么挑下一个,不复制任务条目。
## 内容不是模板填空
七份都写了本仓库的真实约束,其中几条是今天用出来的教训:
- `architecture.md`:分层强制 ①GitHub 分支保护 ②hook(未建) ③git-guard ④散文,
「越往下越不可信」;任何『这条护栏保证了 X』必须能指到 ① 或 ③ 的具体行
- `architecture.md`:**放权必须先建立证据** —— `--allow-trunk` / `--squash-merged`
都是先要服务端证明,不是「为了让守卫能跑就削弱它」
- `spec.md`:每步失败必须 fail-closed,且「查不了」与「没有」在输出上**必须可区分**
(这条踩过:子 shell 里设的状态返回后丢失,「无法核实」永远打印成「没有可清理的」)
- `spec.md`:解析外部 JSON **只取 stdout** —— 折进 stderr 会让 shell hook 的诊断行污染 JSON
- `acceptance.md`:单列「已知未达标项」,包括三阶段主流程从未端到端跑过、
以及 SKILL.md 自称的首要强制手段(PreToolUse hook)实现为空
## .pilot.yml
`integration_branch: main` —— Brood 是单主干,pilot 默认的 `preview` 对本仓库是错的。
不设 `preflight:`,因为 preflight.sh 已自动发现 `scripts/ci/*.{sh,py}` 与 build,
显式再写一条只会让同一个检查跑两遍。
## 账本新增两条(测试挖出来的 pilot 缺陷)
- FU-5(B):门禁应支持可配置规划源,否则每个用 backlog/issues/Jira 管规划的仓库
都会被判「未就绪」
- FU-6(C):doctor 在单主干仓库会把人往「去建 preview」引,而正确答案是
`integration_branch: main` + `--allow-trunk`;doctor 不知道这两件事是连着的
验证:`check-docs.sh --docs-dir docs/agent --strict` → `ok=7/7`、
`PILOT_DOCS: ready — planning layer complete, safe to run unattended.`、rc=0
Claude-Session: https://claude.ai/code/session_01CmAW1q62bBtjT99inZeyLk
clestons
left a comment
There was a problem hiding this comment.
APPROVE — 头条主张实测成立,且这是少见的「不掺水」的规划层 PR
两条核心断言都实跑验过:
check-docs.sh --docs-dir docs/agent --strict
→ PILOT_DOCS: mode=strict dir=docs/agent min_bytes=120 ok=7/7
→ ready — planning layer complete, safe to run unattended. rc=0
preflight.sh run
→ PREFLIGHT: ok — 4/4 passed. Stamped HEAD d6fdb21 (grade A)
「不设 preflight:」这个删减也复算过是对的 —— 自动发现确实覆盖了 scripts/ci/ 下全部三个脚本(check-docs-gate.sh / check-task-yaml.py / check-version-sync.sh)加 build,显式再写一条只会让同一检查跑两遍。
七件套里可查的断言逐条核过,全部成立:49 tasks / 4 milestones / 2 ADRs;To Do 27 / In Progress 15 / Done 7;按里程碑的 In-Progress 拆分 m-1=5 / m-r=5 / m-3=3 / m-2=2 与 roadmap.md 逐位对得上;TASK-40 确实是 status: To Do,plugin.json 确实没有 hooks 段,所以「PreToolUse hook 实现为空」是准确的;SKILL.md:41 的引述一字不差;13 处引用路径全部 resolve,零悬空引用;task-schema.md 确实是五态;git-guard.sh 的退出码(2=用法错 / 3=die)与 spec.md 的约定一致;七份都以 12-20 倍余量越过 120 字节门槛,不是卡边过。
.pilot.yml 在真解析器下是干净的:protect_patterns: [release, hotfix, cla-signatures] 用的 inline flow 形式被 git-guard.sh:39 正确解析(PROTECTED=…,release,hotfix,cla-signatures),实测 git-guard.sh push origin cla-signatures → BLOCKED。而 .github/workflows/cla.yml:36 推 cla-signatures 走的是服务端 Action、不经 git-guard,所以保护这个名字只拦本地误推、不会打断 CLA 流水线 —— 这个取舍是对的。
已知未达标项 主动写了「三阶段主流程从未端到端跑过」和「SKILL.md 自称的首要强制手段实现为空」;FU-5 / FU-6 指的是根因(固定文件名的门禁 vs 真实规划源)而不是给自己打补丁。这是本 PR 最有价值的部分。
以下全部 非阻塞。
⚠️ 一条需要在合并动作上注意的(不是代码问题)
docs/agent/followups.md:22 —— 和在途的 #45 在同一个 append-only 账本上有真冲突。
#45 把 FU-1~FU-4 改成 [x] … done=PR#45 并在第 14 行后插入「4ee077e。实测三方合并:
git merge-file -p pr46.md base.md pr45.md → rc=1,2 个冲突标记,冲突块覆盖 FU-1..FU-6
gh pr view 45 --json mergeable → MERGEABLE gh pr view 46 --json mergeable → MERGEABLE
GitHub 两边都报 MERGEABLE,因为它只拿每个 PR 和 main 两两比、不比彼此 —— 冲突要等其中一个合进 main 之后才现形。而这个账本的契约是 append-only / 永不删行,用 --ours/--theirs 随手取一边就会静默丢掉四条 done 标记、或者丢掉 FU-5/FU-6。
建议:#45 先合,#46 rebase 时手工解,两边都留。
🟡 建议(非阻塞)
-
[Low]
acceptance.md:37—— 「现在真正兜底的只有 GitHub 分支保护,它管 PR 合并,管不到git add -A和直推」这句话对了一半。实读线上规则:required_approving_review_count=1、enforce_admins=true、allow_force_pushes=false、allow_deletions=false—— 这条规则下 GitHub 会拒绝直推 main。本 PR 自己在architecture.md里立的标准是「任何『护栏保证了 X』必须能指到 ① 或 ③ 的具体行」,反过来的「护栏管不到 X」应该受同一把尺子约束。建议改成「管不到git add -A(本地暂存),直推 main 已被①拦住」。(说反的方向是保守的,所以不阻塞。) -
[Low]
spec.md:12+tasks.md:36——milestone字段有三种形态,而「坑」那一列是空的。 这是我在全量复扫里唯一觉得可能真的误导无人值守运行的一条:实际数据里 26 条用m-N、11 条用带引号的阶段全名('Phase 1: Genesis Launch'、"Phase 3: Ecosystem Maturity")、12 条根本没有milestone:字段。于是tasks.md:36那条自动化选取规则(「优先 M1 的 To Do」)如果按字面过滤milestone: m-1,只能选出 1 条 To Do;而那 12 条无字段的恰好是本仓库自己的 pilot/CI 任务 —— 包括TASK-40,也就是acceptance.md:36/architecture.md:31/research.md:45/tasks.md:45四处都点名为最重要缺口的那一条。文档化的挑活规则在结构上永远够不到它。 这和同一张表里status那行已经写了的Donevs"Done"坑是完全同一类。建议补上:「milestone 有三种形态:m-N、引号包裹的阶段全名、以及缺失(本仓库自己的 pilot/CI 任务全部无 milestone)—— 按m-1过滤会漏掉 23/49」,并给tasks.md一条「什么时候该挑无字段的本仓库任务」的规则。 -
[Low]
progress.md:9本地分支清单(main+cla-signatures+feat/pilot-auto-commit)漏了本提交所在的分支docs/pilot-planning-layer和 #45 的分支,而同文件下一节就写着「在途 PR #45」—— 这份清单在写下的那一刻就不可能为真。 -
[Low]
progress.md:36「两个已知缺口记在followups.md」,但同一个 commit 里的账本有六条,全是- [ ]。就算把 #45 在途的 FU-1/2/4 记在它头上,也还剩三条(FU-3/5/6)。 -
[Low]
.pilot.yml:9-12—— 六个 key 里只有一半是脚本机械读的,但外观上完全一样。base_branch/integration_branch/protect_patterns被git-guard.sh、grade-change.sh读;preflight被preflight.sh读;而remote/docs_dir/allow_remote_cleanup没有任何.sh/.py读(check-docs.sh:22是硬编码docs_dir="docs/agent",从不打开.pilot.yml)。allow_remote_cleanup只活在phases/status.md/phases/run.md/reference/git-safety.md的散文里 —— 正是本 PR 自己architecture.md分层里的 ④「靠模型自觉」,却和 ③ 的机械 key 混排在同一个文件里、没有任何视觉区分。建议在.pilot.yml注释里标出哪几个 key 是脚本读的、哪几个是 skill 读的。 -
[Low, 不在本 diff 内,建议记成 FU-7]
templates/pilot.example.yml:6-8的protect_patterns写法照抄会静默失效,而且比预想的更糟。 官方模板文档的是块状写法(protect_patterns:换行后- release),而git-guard.sh:39的sed … | head -1 | tr -d " \"'[]"对块状形式解析不出任何东西。实跑该 sed 对模板原文的结果是:base_branch → [main#主干,受保护,禁止直推/直合] integration_branch → [preview#PR合并进这里;若该分支不存在则回退到base_branch] protect_patterns → [#额外保护的分支前缀(如release/1.2会被release护住)]照模板抄的仓库会拿到三条垃圾条目 + 零额外保护,且没有任何告警。本 PR 之所以安全,是因为它把注释单独放行、并用了 inline flow —— 是撞对的,不是被保证的。「配置看起来生效了其实没有」正是本仓库连着修掉三个「守卫跑不起来」的同一家族。建议要么让 parser 也认块状形式并剥
#注释,要么把模板改成 inline。
反向验证(这些不是 bug,已排除)
- R1a 唯一那条 finding(「FU-5 应建议可配置 planning_source」)不成立:FU-5 本身就是这条建议,是一条正确归档的账本条目,不是 diff 里的缺陷。
followups.sh的next_id算出来是 6 → 下一个 FU-7,编号无冲突。scripts/ci/check-docs-gate.sh只在mktemp出来的 fixture 目录上跑,不读仓库真实的docs/agent,所以新增七件套不会影响这个必需检查。acceptance.md里三个 CI check 名与verify.yml逐字一致。
2-round(post-R2 全 Low,按闸门跳过 Codex R3)· R1a/R1b=deepseek-v4-flash · R2/R4=opus · head d6fdb21 · 所有实测均在 PR head 的独立 worktree 里跑,未修改任何仓库文件,未执行任何写操作
这是 pilot 端到端测试的第一个产物。 跑
pilot doctor发现本地未就绪,pilot plan补齐。为什么是「适配层」而不是规划本身
Brood 的规划事实来源是
backlog/:4 个 milestone、49 个带验收标准的 task、2 个 ADR ——比多数仓库完整。但
check-docs.sh --strict报 0/7,run直接 fail-closed 拒跑,因为它只认
docs_dir下那七个固定文件名。而
plan.mdA.3 明写「已有规划 → 不要重复造」。两条同时遵守是不可能的。所以七件套写成指向
backlog/的视图:每份开头声明「本文件是视图,不是事实来源,改规划请改
backlog/」,内容是摘要 + 怎么读 + 怎么挑下一个,不复制任务条目。内容不是模板填空
七份都写了本仓库的真实约束,其中几条是今天用出来的教训:
architecture.md— 分层强制 ①GitHub 分支保护 ②hook(未建) ③git-guard ④散文,「越往下越不可信」;任何「这条护栏保证了 X」必须能指到 ① 或 ③ 的具体代码行,
指不到就只是 ④。今天三个「守卫跑不起来」的教训都出在把 ③ 当成 ① 来信。
architecture.md— 放权必须先建立证据:--allow-trunk/--squash-merged都是先要服务端证明,不是「为了让守卫能跑就削弱它」。
spec.md— 每步失败必须 fail-closed,且**「查不了」与「没有」在输出上必须可区分**。这条踩过:子 shell 里设的状态返回后丢失,「无法核实」永远打印成「没有可清理的」。
spec.md— 解析外部 JSON 只取 stdout;折进 stderr 会让 shell hook 的诊断行污染 JSON。acceptance.md— 单列「已知未达标项」,不掩盖:三阶段主流程从未端到端跑过、SKILL.md 自称的首要强制手段(PreToolUse hook / TASK-40)实现为空。
.pilot.ymlintegration_branch: main—— Brood 是单主干,pilot 默认的preview对本仓库是错的。不设
preflight:——preflight.sh已自动发现scripts/ci/*.{sh,py}与 build,显式再写一条只会让同一个检查跑两遍(初稿写了,自查时删掉)。
账本新增两条 —— 测试挖出来的 pilot 缺陷
backlog// issues / Jira 管规划的仓库都会被判「未就绪」doctor在单主干仓库会把人往「去建 preview」引,而正确答案是integration_branch: main+--allow-trunk;doctor 不知道这两件事是连着的这两条读代码都看不出来 —— 和今天那三个守卫一样,只有真跑才现形。
验证
preflight.sh4/4 通过。自审
等级 B(344 净变更行),3 轮:①内容真实性(每条断言都能指到仓库里的具体文件/行,
不写模板套话)②内部一致性(七份之间、与
backlog/之间无矛盾;.pilot.yml的preflight:冗余在这一轮删掉)③门禁真跑(不是读代码判断,是rc=0实测)。