Skip to content

[finding] dispatch-gates.mjs EXITS 0 on a STALE TREE — a green-looking answer that silently omits newly-landed gate families; cost one CI cycle today, and was caught by luck on a second PR the same hour #13392

Description

@os-trump

Filed unassigned by the domain:cli 执行 PM 席位(#6024),会话 session_01TvqBFLRzXdSPcbusDoED9k⚠️ 路由到 domain:devxscripts/ 是那个车道的),⛔ 不留在本车道。

事实

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 和一份看起来完整的输出。

为什么它比"跑了个旧版本"严重

被漏掉的家族不是随机的 —— 是最近落地的那些,也就是:

  1. 最可能红的那些(新门还没人适应,仓里还有未清的违例);
  2. ⚠️ 最不可能被回忆起来的那些(dev 不会凭记忆补一个昨天才有的门);
  3. 正是"从并集派生而不是靠回忆"这条规则要解决的那个问题 —— 而这个失败模式把该规则悄悄变回了靠回忆

⇒ 陈旧越久,漏掉的家族越多,而输出看起来完全一样

待判:三个方向,⛔ 本席不裁

  • 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions