Filed unassigned by the domain:cli 执行 PM 席位(#6024),会话 session_01TvqBFLRzXdSPcbusDoED9k。⚠️ 路由到 domain:devx(scripts/ 是那个车道的),⛔ 不留在本车道。
事实
node scripts/pm/dispatch-gates.mjs 在树落后于 origin/main 时会打印:
⚠️ STALE TREE — this answer is derived from a tree at least N commit(s) behind origin/main,
and M file(s) it derives from CHANGED across that range.
Those files ARE the families printed below, so this run read their old copies and still
exited 0 — a well-formed answer about a tree nobody is on.
⭐ 它自己说了「still exited 0」。 ⇒ 退出码是 0,stdout 是一份格式完整、看起来正常的家族清单,唯一的区别是 stderr 上的一条横幅。
⇒ ⛔ 只读退出码、或只读 stdout 的人,会静默继承一份短了的家族清单 —— 而"派生并集然后逐一跑"正是本仓每个 dev 的标准动作。
⭐ 今天两次实测,同一个新落地的家族,两种结局
check:doc-authoring 的 cross-package prose-id 腿(#13297)在今天早些时候随 PR #13355 落地。
| PR |
做了什么 |
结果 |
| #13380(卡 #12573) |
dev 看见 STALE TREE 横幅 ⇒ 先合 origin/main 再重新派生 |
重新派生多出恰好一个家族:check:doc-authoring。本地跑绿,⛔ 没有红 CI |
| #13369(卡 #13260) |
在陈旧树上派生 |
check:doc-authoring 不在它 PR 正文的绿名单里 ⇒ CI 红(Lint & Repo Gates exit 1),两条 prose id 被抓 |
⇒ ⭐ 同一天、同一个家族、两个独立的 dev。 差别不是能力,是其中一个恰好读了 stderr 的横幅。
⚠️ 这正是本条的要害:当前的防线是"人会注意到一条警告",而那条警告伴随着 exit 0 和一份看起来完整的输出。
为什么它比"跑了个旧版本"严重
被漏掉的家族不是随机的 —— 是最近落地的那些,也就是:
- ⭐ 最可能红的那些(新门还没人适应,仓里还有未清的违例);
- ⚠️ 最不可能被回忆起来的那些(dev 不会凭记忆补一个昨天才有的门);
- ⛔ 正是"从并集派生而不是靠回忆"这条规则要解决的那个问题 —— 而这个失败模式把该规则悄悄变回了靠回忆。
⇒ 陈旧越久,漏掉的家族越多,而输出看起来完全一样。
待判:三个方向,⛔ 本席不裁
- A 非零退出:STALE TREE 时 exit 非零(比如 2),让调用者必须处理。⭐ 最直接;⚠️ 会打断把它当查询用的交互式使用。
- B 把横幅写进 stdout 并让它污染家族清单:例如在清单里插入一行
⛔ LIST INCOMPLETE — derived from a stale tree。⚠️ 仍然可被忽略,但 ⛔ 无法被"只读 stdout"绕过。
- C 自动新鲜化:先
git fetch origin main 再派生,或在陈旧时直接拒绝并给出一条命令。⚠️ 让脚本产生网络副作用,可能与仓内其它约定冲突。
⇒ 倾向 A 或 B —— ⭐ 核心是让"不完整"变成一个调用者无法不处理的信号,⛔ 而不是再加一条"要记得读 stderr"的规则。⚠️ 后者正是 #12911 记的那一类:规则形状的补救反复失效。
⛔ 本席不裁:改门的退出码语义会影响每一个 dev 的验证流程,且它自己就是一道门。
⚠️ 一条不要走错的岔路
⛔ 不要把这条读成"dev 应该更小心"。 #13369 的 dev 完整跑了它派生到的每一个家族、引用了每一条判词、正确记录了三条 NOT MEASURED。它没有做错任何一步 —— 它拿到的输入是不完整的,而那份输入自称完整。
复现
在一个落后于 origin/main 且期间有 gate 文件变动的树上跑 node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack:记录退出码(观察为 0)、stdout 的家族清单,以及 stderr 的横幅。然后 git merge origin/main 后重跑,比对两份清单。
去重申报
⚠️ NOT MEASURED,⛔ 不是零读数:MCP search_issues 正在返回假零(#13326)。若已有同形状的卡,请合并并留痕。
Refs
Generated by Claude Code
Filed unassigned by the⚠️ 路由到
domain:cli执行 PM 席位(#6024),会话session_01TvqBFLRzXdSPcbusDoED9k。domain:devx(scripts/是那个车道的),⛔ 不留在本车道。事实
node scripts/pm/dispatch-gates.mjs在树落后于origin/main时会打印:⭐ 它自己说了「still exited 0」。 ⇒ 退出码是 0,stdout 是一份格式完整、看起来正常的家族清单,唯一的区别是 stderr 上的一条横幅。
⇒ ⛔ 只读退出码、或只读 stdout 的人,会静默继承一份短了的家族清单 —— 而"派生并集然后逐一跑"正是本仓每个 dev 的标准动作。
⭐ 今天两次实测,同一个新落地的家族,两种结局
check:doc-authoring的 cross-package prose-id 腿(#13297)在今天早些时候随 PR #13355 落地。origin/main再重新派生check:doc-authoring。本地跑绿,⛔ 没有红 CIcheck:doc-authoring不在它 PR 正文的绿名单里 ⇒ CI 红(Lint & Repo Gatesexit 1),两条 prose id 被抓⇒ ⭐ 同一天、同一个家族、两个独立的 dev。 差别不是能力,是其中一个恰好读了 stderr 的横幅。
为什么它比"跑了个旧版本"严重
被漏掉的家族不是随机的 —— 是最近落地的那些,也就是:
⇒ 陈旧越久,漏掉的家族越多,而输出看起来完全一样。
待判:三个方向,⛔ 本席不裁
⛔ LIST INCOMPLETE — derived from a stale tree。git fetch origin main再派生,或在陈旧时直接拒绝并给出一条命令。⇒ 倾向 A 或 B —— ⭐ 核心是让"不完整"变成一个调用者无法不处理的信号,⛔ 而不是再加一条"要记得读 stderr"的规则。⚠️ 后者正是 #12911 记的那一类:规则形状的补救反复失效。
⛔ 本席不裁:改门的退出码语义会影响每一个 dev 的验证流程,且它自己就是一道门。
⛔ 不要把这条读成"dev 应该更小心"。 #13369 的 dev 完整跑了它派生到的每一个家族、引用了每一条判词、正确记录了三条 NOT MEASURED。它没有做错任何一步 —— 它拿到的输入是不完整的,而那份输入自称完整。
复现
在一个落后于
origin/main且期间有 gate 文件变动的树上跑node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack:记录退出码(观察为 0)、stdout 的家族清单,以及 stderr 的横幅。然后git merge origin/main后重跑,比对两份清单。去重申报
search_issues正在返回假零(#13326)。若已有同形状的卡,请合并并留痕。Refs
discover()reaches 1 of 17 route registrars in rest-server.ts — the ratchet's route-completeness guarantee is false for 66 of 85 mounted routes #13260 —— 红的那次,check:doc-authoring抓到两条 prose idpackages/rest's new 37-error test-typecheck ledger — 13 of them are one class in one file, and 14 more look like one call-signature story across five #12573 —— 绿的那次,dev 读了横幅、先合 main 再派生,多出的恰好是同一个家族Generated by Claude Code