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(卡 #12573dev 看见 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

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions