Repository navigation
把「跟到合并为止 / CI 诊断纪律 / 生成物同步」写进 pm-dispatch 与 spec-property-retirement skill #4892
Description
今天(2026-08-03)一整天的 v17 协议变更派发里,有六处已经付出过代价的经验,现行 skill 里没有,或者只写了半截。逐条给出事故、缺口、和要补的规则。
维护者已确认按此更新 skill。
A. pm-dispatch —— 四条主项
A1. ACCEPT 的落地判据只到「进队列」就断了
SKILL.md 第 7 步 ACCEPT 写的是「once every check is green, mark it ready and add it to the merge queue」,然后就结束了。没有任何一句说怎么确认它真的落地。
事故:PR #4852 的 auto-merge 从 10:15 就挂着,我每轮查「git grep origin/main,判据在不在 main 上」,不在 → 记为「还在队列里排队」。实际上它从来没进过队列 —— CI 是红的,auto-merge 永远不会触发。这样空转了 100 分钟。
根因:「不在 main 上」这一个读数,无法区分「在队列里等」和「压根没进队列」。两者的处置完全相反(前者等,后者要动手)。
要补的规则:落地检查必须是两个读数,不是一个 ——
git ls-remote origin 'refs/heads/gh-readonly-queue/*' # 真的在队列里?
git log --oneline -1 origin/main # 真的落地了?队列分支名里带 PR 号(gh-readonly-queue/main/pr-4878-<base-sha>),而且基 sha 串成链 —— 能同时读出「排第几」。另外:PR 一旦被转回 draft,auto-merge 与队列成员资格都会掉,不会自动恢复。
A2. rerun_failed_jobs 的语义:复用原 run 的合并 ref,不重算
skill 全文没提重跑。但 PM 恰恰是按重跑按钮的那个人。
事故:#4852 的 CI 红在 #4856(testTimeout: 60_000)落地之前。我重跑了,还是红;当时的判断是「队列慢 / flaky」。实际上 rerun_failed_jobs 复用原 run 的提交与合并 ref,不会拿新的 main 重算 —— 重跑一百次都还是 5000ms 的那个基。
要补的规则:当红的原因是「基上缺一个已经合并的修复」时,重跑无效,只有推新提交才能带上新 main(git merge origin/main 后 push)。区分方法:先看失败签名,再看那个修复的合并时间与本 run 的创建时间。
与既有的
.github/workflows/rerun-safety-nightly.yml无关 —— 那个查的是「同一 checkout 里跑两遍是否自洽」的测试污染,不是重跑语义。
A3. 入队前的生成物同步(merge=os-regen 静默吞并)
第 3 步只保证「同一批内 file-disjoint」。它管不到先后两单都碰 packages/spec 生成物的情形 —— 而协议变更几乎必然如此。
.gitattributes 把 spec-changes.json / authorable-surface.json / json-schema.manifest.json / api-surface.json / api-surface-signatures.json 路由到 merge=os-regen。这个驱动会让 merge exit 0、零冲突标记,却静默丢掉一侧的改动。只有重新生成才暴露。
今天在三个 PR 上各复现一次:#4809、#4846、#4841。
要补的规则(PM 侧,写进 ACCEPT 前的动作):任何碰这五个文件的 PR,入队前要求 dev ——
git merge origin/main(⛔ 禁 rebase / force-push,AGENTS.md §3)git checkout origin/main -- <五个生成物>- 整体重新生成,⛔ 绝不做文本合并
- 断言所有兄弟单的条目都还在(今天 refactor(spec)!: 退役 plugin-runtime 家族五个 schema —— 无任何 runtime 实现的「Dynamic Loading」词表 (#4834, ADR-0049) #4878 是四条 step17 条目并存)
驱动本身另有缺陷,已单独立 #4868。
A4. CI 红了怎么诊断 —— 三条读日志的纪律
skill 里没有任何 CI 诊断章节。今天我在这里犯了当天最严重的错误:公开断定四次 CI 红是内核 OOM-killer 杀掉 DTS 构建,并据此开了 PR #4853。被 #4853 自己的 CI 推翻 —— 它挂着 8192 跑,红得一模一样。真因是 #4796 那一族的 5000ms 超时,由 #4856 修掉。完整更正见 #4845。
三个叠加的错误,每一个都该成为一条规则:
check-test-completeness.mjs的「all N accounted for」≠「测试通过」。 它只断言没有 worker 静默死掉。workflow 自己的注释:"A red suite plus a GREEN completeness check means real test failures"。- turbo 并发输出的「相邻」≠「因果」。
test的dependsOn只有["^build"](上游),packages/spec没有pretest,所以--concurrency=4下spec#build(DTS)与spec#test同时跑,GitHub 又给整组打同一个时间戳。DTS Build start紧接着ELIFECYCLE完全可能来自两个无关进程。 - 下结论前拿完整日志归档,不要只看 tail。 那次的 ~10 KB 尾巴被
gen:schema的 1675 行清单吃光,真正的失败行根本不在里面。
B. pm-dispatch —— 两条次项
B1. 同类基础设施修复的撞车,现有门禁盖不住
.github/workflows/duplicate-fix-guard.yml 已经存在,它在两个 PR 声明同一个 Fixes #N 时把后开的那个判红(#4588 的产物)。
但它盖不住今天这一例:PR #4864(我方)和 PR #4856(另一车道)修的是同一个基础设施问题,却挂在不同的 issue 号下 —— 门禁看不到任何重复。而且 #4856 先合了 60s,我的 #4864 若合进去会把它降回 30s,即一次静默回退。
要补的规则:共享基础设施类修复(CI 配置、超时、构建脚本、门禁)在入队前,要按症状而不是 issue 号复查 main:git log --oneline origin/main -- <该文件>,确认在飞期间没有别人已经修掉。并明确点出现有门禁的覆盖边界。
B2. 读数纪律 —— 三条各自产出过一个我信了的错读数
cd X && cmd会短路。 Bash 工具每次调用 cwd 重置;cd /home/user/objectui && git grep ...在路径不存在时cd失败但整条命令继续,于是在当前仓里执行,给出假的「objectui 零消费方」。⛔ 跨仓一律git -C <path> grep。git grep -c … | wc -l数的是文件数,不是命中数。 曾据此得出「分支比 main 命中更多」的荒谬结论。- 裸名 grep 会被幸存家族当子串命中。
system/EmailTemplate命中仍然活着的EmailTemplateDefinition一族;退役核验必须带引号精确名。 - 统一原则:空结果必须用「确定存在的邻近词」反查,证伪「扫描器/路径坏了」这个解释,否则零命中不成立。
C. spec-property-retirement —— 一条
C1. 两种退役形态在 ratchet 上的可见性是相反的
今天各测到一例,可以互为对照:
| 退役形态 | 四张 ratchet | 实例 |
|---|---|---|
| 枚举值收窄 | 字节完全相同(不可见) | #4391 crypto.hash |
| 整 def 删除 | 必须变化 | #4834:api-surface −12 / authorable −23 / manifest −5 |
为什么重要:验收时「ratchet 零变化」到底是正常还是异常,完全取决于走的是哪条退役路线。拿错对照就会把正常判成异常(白折腾),或者把异常判成正常(放过一个没真正删掉的 def)。
这条属于退役形态判据,放 spec-property-retirement(第 2 节「the fork: which removal route」附近),pm-dispatch 的验收清单引用即可,不重复。
验收标准
.claude/skills/pm-dispatch/SKILL.md覆盖 A1–A4、B1–B2,规则写成可执行的命令 / 可核对的判据,不是泛泛的告诫;每条带上事故编号,后来者能回溯。.claude/skills/spec-property-retirement/SKILL.md覆盖 C1。- B1 必须明确写出
duplicate-fix-guard.yml的覆盖边界,不能让读者以为门禁已经管住了。 - ⛔ 不碰
content/docs/releases/。 - 空 frontmatter changeset(本 PR 不发布任何东西)。
Activity
os-zhuang commentedon Aug 3, 2026
认领:维护者直接指派(非 PM 循环轮次)
会话:
session_0176qgxgCXTJCUv4YFLtusP9分支:
claude/issue-4892-pm-skill-landing-disciplineWorktree:
objectstack-skill-4892开这个 issue 时
origin/main跳到e2086fef0,即 PR #4885(issue #4882,另一 PM 会话session_015Br2xsJsczFsTR9bvbh2Ny),标题「增补 2026-08-03 运行沉淀的八条 PM 操作经验」—— 同一天、同一份 skill、同一类工作,挂在不同的 issue 号下。这正是本单 B1 要写的那条规则的现场实例:
duplicate-fix-guard.yml只在两个 PR 声明同一个Fixes #N时开火,跨 issue 号的同类工作它看不见。所以本单先读了 #4885 的全文再动笔,重叠部分一律不重写:
gh-readonly-queue/*auto_merge字段入队后回落为 off)rerun_failed_jobs复用原 refspec-property-retirement顺手修掉的一个悬空引用
spec-property-retirement/SKILL.md第 2 节「nothing parses it」路线的先例指针写的是packages/spec/src/kernel/plugin-runtime.zod.ts:243-248—— 那个文件今天刚被 #4878 整体删除。已改为指向 #3896 / #4834(PR #4878)两个 issue 编号,以及幸存下来的packages/spec/src/kernel/index.ts说明块。指 issue 比指行号耐放。写进去的事实都实跑核过
test的dependsOn只有["^build"]turbo.json实读packages/spec无pretestpackage.json实读merge=os-regen的路径清单.gitattributes—— 是 7 条不是 5 条,我初稿漏了docs/protocol-upgrade-guide.md与content/docs/references/**,已补;并注明权威清单是文件本身.github/workflows/ci.yml:234duplicate-fix-guard.yml的覆盖边界Fixes #NGenerated by Claude Code
docs(skills): 补齐「跟到合并为止 / CI 诊断纪律 / 生成物同步」四类 PM 经验 (objectstack-ai#4892…
docs(pm-dispatch): the draft-conversion reading splits by queue state…