Skip to content

CI 聚合门禁把合并队列重建的 aggregate result: abandoned 判成红 —— 在队 PR 零测试失败被踢出(ci.yml 两处白名单缺 abandoned) #6082

Closed

Description

@baozhoutao

发现于合并队列管家第 14 轮巡检(#5810 简报)对 PR #6010 被踢的分诊;管家会话无 search API 无法查重立单,由 identity 车道 PM 代立(session_01JwwiU9bjhwy2SWj13ho8uv)。本单只登记,不随代码 PR 携带。

现象(2026-08-06,run 31120902911)

合并队列在平台抖动期反复重建,分片 job 被整片丢弃后,两个聚合门禁读到 abandoned 并判红:

job 结束 终报错
Test Core 17:32:13Z test matrix aggregate result: abandoned (filter job: success) → ::error::Test Core shards did not pass (aggregate result: abandoned)
Dogfood Regression Gate 17:33:02Z dogfood matrix aggregate result: abandoned → 判红

判红 31 秒后 #6010 被移出队列(timeline removed_from_merge_queue 17:32:44Z)。该 run 内零测试失败:Test Core (2/3) success,(1/3)/(3/3) 17:16/17:24Z 被 cancelled,dogfood 分片同形。

根因

.github/workflows/ci.yml 两处聚合门禁的白名单是 success|skipped|cancelled(Verify test shard results 约 :346-349;Verify dogfood shard results 约 :799-812 同形),abandoned 落进 *) 兜底 ⇒ 假红 ⇒ 踢出。

第二处门禁正上方的注释(引 #3668)论证的恰恰是同一条推理的 cancelled 版:cancelled is a run-lifecycle state, not a shard verdict … the old *) fallthrough painted a false red。abandoned 是同类生命周期状态(队列重建丢弃分片),只是当时没被写进白名单。

为什么它不只是一次误踢

停滞期的动力学(管家第 13/14 轮实测):队列重建 churn → 分片被丢弃 → abandoned 假红 → 在队 PR 被踢 → 重投加深队列 → 更多重建。门禁缺口把重建从「浪费算力」放大成「每次重建踢掉一个无辜 PR」,与停滞互相喂养。今日 15:14Z 起 main 零落地 3h+,本缺口是已确认的放大器。

建议修法(带验证义务,不是无脑加词)

把 abandoned 补进两处白名单,但必须先补齐 #3668 式的语义验证再合入:

  1. 确认 abandoned 只在「该队列条目的 run 已被废弃/重建」时出现——即它的判决对合并决策已 moot(队列会为重建后的条目跑新 run),绿过它不会让任何条目在零测试运行上落地;
  2. 确认真实分片失败支配聚合读数(CI: Dogfood Regression Gate 把 cancelled 当失败 —— 每次连续推送都产生一条假红 #3668 对 cancelled 做过实验:fail-fast 矩阵里一真败 + 一 cancelled,聚合读 failure 而非 cancelled)——同样的支配性对 abandoned 成立才允许放行;
  3. 若 1/2 任一不成立,回到本单重议(例如改为「abandoned + 后续存在同条目新 run」的窄放行)。

#4928 的 filter-skipped 守卫(skipped 只在 filter success 时放行)不受影响,不要动。

查重与路由

参考

Activity

  1. self-assigned this
    on Aug 6, 2026
  2. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    认领:PM 循环第 4 轮(devx 车道,止血单)
    会话:session_01BDmDsu2575gDxeMCxXhDE3
    分支:claude/issue-6082-aggregate-abandoned-whitelist
    Worktree:objectstack-issue-6082
    域:domain:devx
    文件面:.github/workflows/ci.yml(仅两处聚合门禁白名单及其注释;⛔ 不动 #4928 的 filter-skipped 守卫)(越界即停,报告说明)

    晋级与认领依据(审计,维护者可否决):本单由立卡方显式路由 devx(「本单不自派」+ 60 分钟接手窗,19:14Z 起,本认领在窗内);finding → pm:queue 的晋级按 SKILL step 8 restore-invariant 类执行 —— 门禁自身 #3668 注释已写明不变量(生命周期态非判决、*) 兜底会假红),abandoned 缺席即不变量破损,且该缺口正实时放大全仓队列停滞(管家第 14/15 轮实测:今日同签名三例 #6010/#6012/#6013,main 15:14Z 起零落地 4h+)。「queue it, dispatch it, report it」条款适用,不占分诊轮周期。修法严格按本单三条验证义务执行,任一不成立则窄放行或回报。


    Generated by Claude Code

  3. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    验证义务 1 与 2 双双不成立 —— 按本单义务 3 退回重议,⛔ 未提交任何代码

    接手实测(os-dev,session_01BDmDsu2575gDxeMCxXhDE3,分支 claude/issue-6082-aggregate-abandoned-whitelist 已建但零改动、无 PR)。本单的修法带了两条前置验证义务,我逐条取证,两条都被实测反证。按本单自陈的义务 3 与派单口径(「⛔ 无法满足义务 2 时不要硬加词表」),此处停手交维护者裁决。

    一、前提复核:结构面成立,因果面不成立

    结构面成立(origin/main@9e3709a4):

    • .github/workflows/ci.yml:347(test-gate / Verify test shard results)= success|skipped|cancelled)
    • .github/workflows/ci.yml:838(dogfood-gate / Verify dogfood shard results)= success|cancelled)
    • 全仓 ci.yml 内 abandoned 零出现,两处确实落进 *) 兜底。

    因果面不成立。本单把 abandoned 归因为「合并队列重建把分片整片丢弃」,并据此论证判决 moot。实测两者都不是。

    二、义务 1 反证:abandoned 出现时,队列条目仍然活着,判决并不 moot

    实证 run 31120902911(条目 pr-6010-9e3709a4…)逐 job 核过:

    1. 该 run 并未被废弃 —— 分片开始死亡之后,它还在继续调度并跑完普通 job(非 always()):Dogfood Regression Gate (1/3) 完整跑完步骤表(Boot example apps and exercise real user flows success)于 17:02:51Z success;Build Core 17:00:03Z success;Temporal Conformance 17:00:20Z success。一个被废弃/重建的 run 不会继续把普通 job 派到 runner 上跑完。
    2. 分片死亡时刻是散开的,不是一次性解散:17:09:14Z / 17:09:30Z / 17:16:26Z / 17:24:48Z / 17:24:48Z。这几个 job 的 runner_id 全为 0、started_at == created_at、steps 数组根本不存在 ⇒ 它们从未被派到 runner,执行了零个测试。这是 runner 饥饿下的逐 job 丢弃,不是 run 级解散。
    3. 队列当时仍在实时消费本 run 的检查结论:Test Core 17:32:13Z 判红,31 秒后(17:32:44Z)fix(plugin-auth): /sso/register 门禁改用唯一那把管理员等级尺 (#5942) #6010 被移出队列(见 fix(plugin-auth): /sso/register 门禁改用唯一那把管理员等级尺 (#5942) #6010 的管家评论 fix(plugin-auth): /sso/register 门禁改用唯一那把管理员等级尺 (#5942) #6010 (comment) )。若该组早已解散,条目会在解散那一刻离队,而不是在门禁转红 31 秒后离队。判决是承重的,不是 moot 的。

    ⇒ 此处 abandoned 的含义是「这个 job 从未运行、已被丢弃」(平台抖动下的逐 job 生命周期态),不是「该条目的 run 已废弃」。把它放绿 = 在一个仍在队、仍在被队列读取的条目上,对零测试执行发布 Test Core: success —— 正是白名单上方两行注释里 #4928 那句 published a green Test Core over zero test runs 要挡的那件事。

    与 #3668 的 cancelled 有结构性不对称:cancelled 的 run 已被新 push 取代,没有任何东西依赖它;abandoned 的 job 待在一个仍然活着、仍在把关合并的 run 里。

    三、义务 2 反证:真实分片失败不支配聚合读数(反例实测)

    义务 2 要求复现 #3668 对 cancelled 的支配性(run 30271824408:一真败 + 一 cancelled ⇒ 聚合读 failure)。对 abandoned 实测得到相反结果:

    run 31114735713(条目 pr-6010-f2ee1b7f…,head_sha e915a0e9,run 结论 failure):

    事实 取证
    分片 Test Core (3/3)(job 92661255470)"conclusion": "failure" runner 已分配(runner_id: 1000157579),Set up job 于 15:19:06Z 报 ##[error]Service Unavailable / Failed to resolve action download info.
    同 run 聚合门禁 Test Core(job 92664660973)15:25:13Z 读到 test matrix aggregate result: abandoned (filter job: success)

    ⇒ 一个结论为 failure 的分片在场,needs.test.result 仍读 abandoned,failure 被聚合读数吞掉了。 支配性对 abandoned 不成立,与 cancelled 恰好相反。

    这条反例的杀伤半径:该 run failed_jobs: 2 / 14,唯二的红就是那个分片和它的聚合门禁。按 #3622 的设计,分片 context 不是必需检查,必需的只有裸 Test Core 这一个名字。所以若当时词表里有 abandoned,该 run 唯一的必需检查红会翻绿,条目 pr-6010-f2ee1b7f… 会在 3/3 分片零测试执行的情况下直接落地。

    四、附带取证结论(供下一位读者省一轮)

    • 无文档支撑:abandoned 不在 needs 结果的四个文档化取值(success / failure / cancelled / skipped)之列,是运行器内部 TaskResult 枚举漏出的未文档化值;docs.github.com 本环境代理返回 403,拿不到官方定义。所以「加词表」这一步在文档面也没有依据,只能靠对语义的推断 —— 而实测把这个推断证伪了。
    • pnpm check:workflow-status-functions 不覆盖这两处:该脚本头部明确把 needs.*.result / needs.*.conclusion 划在范围外(「Those ARE status reads … This gate is about the silent compression of status into data」)。故本单即便实施也无语料需要同步。
    • filter job 一旦失败,Test Core / Build Core / Dogfood 会全部 skipped 而分支保护判为通过 —— 隐式 success() 今天已第三次咬人 #4928 的 filter-skipped 守卫全程未触碰。

    五、给维护者的备选(义务 3)

    • A. 照本单原样加词表 —— ⛔ 建议否决,已被上面两条反例实测击穿。
    • B. 窄放行(abandoned + 存在同条目后续新 run) —— 本单自陈的备选,但实测在观测到的时序里根本不会触发:门禁 17:32:13Z 就要出结论,条目 17:32:44Z 才离队,重建后的新 run 此刻尚不存在 ⇒ 依然判红,解决不了本单要解决的症状;且要给纯 shell、contents: read 的门禁加 actions: read 与 API 调用。
    • C. 门禁改读逐分片显式判决(推荐的长期解) —— 本次调查真正挖出的缺陷比 abandoned 更宽:只要有兄弟分片被 abandoned,聚合读数就可能吞掉任意红分片。让每个分片显式发布「我通过了」的凭据、门禁数够 N 个正面凭据才放行(declared = enforced,数正面而不是「没看到负面」),既消除这个吞噬面,又让「分片没跑」自然判红。量级 M。
    • D. 治驱逐而不是治判决(推荐的即时止血) —— 「分片没跑 ⇒ 判红」本身是正确的真负例;真正的用户可见伤害是「在队 PR 被驱逐 + 队列 churn」。对应的杠杆是驱逐后的按签名自动重投(队列管家 Routine(三仓总管):合并队列健康专责化 —— flaky 签名分诊 / 原样重投 / 新签名拦截 / 跨仓 pin 链停滞观测(座位 Routine 化第二例,维护者 2026-08-06 拍板) #5810 台账已在人工做这件事)与让分片别再被丢弃(action 解析重试 / 抖动期降分片数),这条不削弱合并门禁。

    我的建议:D 即时止血 + C 长期修,明确否决 A;B 仅在维护者想留痕时才做,但它对本单症状无效。


    Generated by Claude Code

  4. removed their assignment
    on Aug 6, 2026
  5. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    PM 复核与流转(devx 车道,会话 session_01BDmDsu2575gDxeMCxXhDE3):dev 的 needs_decision 成立,本单转决策箱(摘 pm:queue/pm:dispatched → needs-user-decision,认领解除 —— 派发工作已终结,零代码即正确交付)。

    背景:原修法(两处白名单补 abandoned)带三条验证义务派发,dev 实测证伪了前两条(完整证据见上方 comment 5208599210):

    1. abandoned 非 moot —— 被丢弃分片 runner_id=0、无 steps(零执行),而队列实时消费该 run 的判决(判红 31 秒后驱逐);放绿 = 对零测试执行发布 success,正是 filter job 一旦失败,Test Core / Build Core / Dogfood 会全部 skipped 而分支保护判为通过 —— 隐式 success() 今天已第三次咬人 #4928 要挡的形状;
    2. 真败不支配聚合 —— run 31114735713 反例:分片 job 级 conclusion=failure,同 run 聚合读 abandoned,failure 被吞;且按 chore(ci): cut PR wall-clock ~9.5min → ~5-6min — dedupe dogfood, shard it, cache lint's build #3622 分片 context 非必需检查 ⇒ 白名单若含 abandoned,该 run 会在带真失败的情况下落地。

    具体问题:abandoned 假红与其放大的队列驱逐环如何治(义务 3 重议)。

    可选方案(dev 三轴分析在报告,此处摘要):

    建议:D 即批(= 批准管家的台账提请)+ C 立项长期,否决 A。 三轴:实测痛点是「PR 被驱逐、main 零落地」而非「读数难看」,D 直接命中;A 的语义无任何业务场景需要;C 把 #3622/#4928 两条不变量结构化钉死,是唯一同时保真红与消吞噬的解。「聚合吞真败」这一更宽缺陷按 attach-don't-scatter 留在本单(即 C 的完成范围),不拆孪生单。


    Generated by Claude Code

  6. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    ContributorAuthor

    裁决落档(维护者 2026-08-07 批准「按建议执行」于 identity PM 会话;执行人 session_01JwwiU9bjhwy2SWj13ho8uv。裁决范围严格限于会话内呈报的三轴建议,未涉其他待裁项)


    Generated by Claude Code

  7. self-assigned this
    on Aug 7, 2026
  8. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    ⛔ 认领(claim,C 实现轮)

    派 1 名 os-dev(M 量级)。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions