Skip to content

Issue line marks the run failed after its PR already merged: triage dies waiting on a background job #2658

Closed

Description

@Birfy

Written-by: Chief

现象

issue 快线 wf-1cdf6ec5 的 triage 节点把整个 run 标成 failed,但它的 PR 已经合入 main、issue 也已自动关闭。于是运行历史里出现「失败」而实际工作已经落地,下游的 gate / enqueue / escalate / attest 节点全部停在 queued,run 的收口记录(谁入的队、attestation)永远不写。

今天两条都这样:

run task PR PR 合入 run 标 failed
i2654-impl #2655,tip f8fcc864a6 2026-11-08T01:23:16Z,main b4de72ca9f 2026-11-08T01:15:51Z({"triage":"failed","claim":"queued",…})
i2656-impl #2657,tip f840979143 2026-11-08T02:11:29Z,main e22675313a 2026-11-08T02:12:13Z(同上形状)

对照:巡检线 wf-d79af436 的 verify 节点跑满 177 步、正常收尾(done),说明不是所有长节点都会这样。

共同的形状

两次都在 triage 节点用 bash(..., run_in_background: true) 起了一个「盯 CI」的脚本,然后用 job_output { wait: true, timeout_ms: 600000 } 等它:

  • i2656:session-9aedd362-d3df-46b7-b2d7-316b0af3b8c3 的日志最后一条是 tool/call seq 568, step 71, name: job_output, arguments: {"job_id":"bash-b7276a82a1f94e9b","timeout_ms":600000,"wait":true},之后没有 tool/result、step/end、turn/end,下一个事件就是 run 被标 failed(02:12:13Z,约合入后 44 秒)。
  • i2654 同形(该节点日志同样在等待后台 job 之后截断)。

也就是说:turn 在等一个后台 job 时被外部结束,节点拿不到输出 → 标 failed;而此前它已经把 PR 开好并入了队。

期望

要么 turn 不要被这样中断(等后台 job 的 job_output 应能正常返回),要么节点结束时把「工作已交付」的状态如实写回(例如合入后把 run 结算为 done 或 partial,而不是 failed)。现在这个 failed 让人无法分辨「线挂了」和「线成了但收尾没写」,也让 gate/enqueue/escalate/attest 永远不跑。

复现

在 issue 快线上派任意一条会开 PR 的实现任务(flybot/workflow/assign {workflowId:'wf-1cdf6ec5', taskId:'i<n>-impl', …}),等它开 PR 后 call('flybot/workflow/state') 看该 run:run.status=failed、nodeStatus.triage=failed 而其余节点停在 queued,同时 PR 仍能入队合入。

说明

今天两条都在 T1(enqueue-guard summary:no PASS needed: computed tier T1),所以不是绕过评审的问题,只是状态信号不可信。标 needs-info:需要有人判断是 DSH 的 turn 生命周期问题(等后台 job 时被中断)还是 workflow 线自己的结算问题,再决定修哪一侧。

Activity

  1. added
    bugSomething isn't working
    needs-infoWaiting for the reporter or a maintainer decision
    area:automationScheduled tasks and automation
    on Nov 8, 2026
  2. Birfy commented on Nov 8, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    更正:我 02:19Z 写的诊断是错的,不是「等后台 job 时 turn 被中断」。run 记录里的原文是 Missing OUTPUT state——那是 commitAdvance() 自己把这一步判成 fail 的(plugins/workflow/src/service.js:1218-1239),不是 turn 没跑完。i2656 的 session 日志结尾是 turn/end reason=completed,它正常收尾并打印了 OUTPUT pr: 2657。

    真实现象(可量化,不是偶发)

    issue 快线 wf-1cdf6ec5 最近 10 个 run 里 7 个死在 Missing OUTPUT state:i2643、i2645、i2646、i2652、i2654、i2656、i2660——其中至少 i2654(#2655)、i2656(#2657)、i2660(#2661)、i2649 的 PR 已经合入 main。只有 i2650、i2649 两个 run 是 done。所以这不是「某次运气不好」,而是这条线几乎每个 run 都在开完 PR 之后被自己判死,claim/gate/enqueue/attest 全停在 queued,attestation 永远写不出来。

    根因链(每步有 path:line)

    1. wf-1cdf6ec5 的 triage 节点没有声明 outputs(nodes[].outputs 是 undefined),但它的出边条件读 state({from:"triage",to:"claim",when:{output:"state",eq:"ready"}})。
    2. missingVerdicts() 因此要求 state,而节点的回包只打印了 issue/pr/tip/merged/tier/tests,没有 state → 进 #2583 的「stands」兜底分支。
    3. 兜底用 stepArtifacts(op.text, outputs) 找「这步已经交付」的证据,但它只认正文里的散文形式:PR URL、pull/<n>、或同一行里有 opened/created/raised/submitted/pushed 再加 PR #n。本节点的正文写着「已修复并合入」+ 表格行「PR fix(client): read the Inbox workflow pack through the plugin endpoint (#2656) #2657:Owner: Triage …」,没有出现那个动词,于是 pull=null、claimed=false → stands=false → type:'fail'、text:'Missing OUTPUT state'。
    4. run 结算成 failed,后继节点全部 queued。工作其实已经落地。

    实测(plugins/workflow/src/graph.js 直接 import,喂 i2656 的真实回包文本):

    outputs keys [ 'issue', 'pr', 'tip', 'merged', 'tier', 'tests' ] | pr = "2657 (verified)"
    stepArtifacts -> {"pull":null,"claimed":false}
    body still names the PR? true
    proposed outputs.pr -> 2657
    

    修法(这一步切片)

    stepArtifacts(text, outputs)(plugins/workflow/src/graph.js:108-124)同时读该步自己声明的 OUTPUT 值,作为「已交付」的证据:

    • 名为 pr / pull 的 output 值里出现整数 → 取它当 pull("2657 (verified)" → 2657;值里没有整数就是没有,不要猜);
    • 名为 merged / merge-commit 的 output 值里出现 40 位十六进制 sha → 也算已交付(我们今天所有快线 brief 都会打这两行);
    • 其余名字一律不算,宁可 fail。判据保持 fail-closed:只有这两个名字、且值里确实有对应形状才算。

    红先测试(plugins/workflow/test/graph.test.js 或新 test/step-artifacts.test.js):

    1. 原样喂 i2656 的回包(正文「已修复并合入」+ OUTPUT pr: 2657 (verified),节点无声明 outputs)→ 改前 {pull:null,claimed:false},改后 {pull:2657};
    2. OUTPUT pr: none / 没有 pr 行 → 仍然是 pull:null(不猜);
    3. OUTPUT merged: <40 位 sha> 单独出现也算交付;
    4. 端到端一条:commitAdvance 收到该回包时,run 不再 failed,而是 triage=done + 后继节点起跑(可在 test/assign.test.js 的现有夹具上加)。

    验收命令:cd plugins/workflow && TZ=UTC node --import ./test/resolve.mjs --test test/graph.test.js test/step-artifacts.test.js test/assign.test.js 全绿;把 stepArtifacts 改回 base 后新增用例转红。

    范围外(另单或后续):① 让 runner 把「出边要读的 OUTPUT」提前告诉节点(现在的节点根本看不到自己要打 state,i2650/i2649 只是碰巧打对了);② failed 与「已交付」并存时的 partial 结算。这两条不挡本单。

    风险面:plugins/workflow/** 不在 T0/T1,T2,要 tip 上的 PASS。

  3. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    and removed
    needs-infoWaiting for the reporter or a maintainer decision
    on Nov 8, 2026
  4. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 8, 2026
  5. added 2 commits that reference this issue on Nov 9, 2026
    3b8d4e8
    af9a30b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:automationScheduled tasks and automationbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions