Skip to content

bug(workflow): a node is never told which OUTPUT its outgoing edges read #2669

Closed

Description

@Birfy

Written-by: Chief

现象

一个 workflow 节点该打印哪些 OUTPUT,只来自它自己声明的 nodes[].outputs。但出边上的条件也读 OUTPUT(edges[].when.output),这部分节点完全看不到。于是「节点没声明 outputs + 出边却按某个 output 分流」的图,天生不可能满足自己的闸门:节点不知道要打那一行,每一步都会落进 Missing OUTPUT <name>。

issue 快线 wf-1cdf6ec5 正是这种图:triage 节点没有 outputs,出边是 {from:"triage",to:"claim",when:{output:"state",eq:"ready"}}。结果最近 10 个 run 里 7 个死在 Missing OUTPUT state(i2643/i2645/i2646/i2652/i2654/i2656/i2660),PR 都开好了、合入了,run 还是 failed。#2666 修的是「已经交付的步不要判死」这个症状;根因是节点拿不到它该打的 OUTPUT 名单,不修的话,任何新快线都会重新踩。

链路(tip 3b8d4e8c)

  1. plugins/workflow/src/service.js:725-742 stepBody() 组这一步交给 Bot 的内容,其中 outputs: node.outputs ?? [] —— 只有声明过的端口,没有出边读的那些。

  2. plugins/workflow/index.js:90-91 把名单写成一句提示:

    const outputs = (step.outputs ?? []).map(port => port.name).filter(Boolean)
    if (outputs.length > 0) lines.push(`When you finish, add one line per output: OUTPUT <name>: <value> (${outputs.join(', ')}).`)

    outputs 为空时这句根本不出现,节点于是从来不知道自己该打 state。

  3. missingVerdicts() 按出边读的 output 判缺,commitAdvance 把步判 fail(service.js:1218-1239)。两边对同一件事的认知不一致。

修法

  1. stepBody()(service.js:725)把「出边读的 OUTPUT」并进 outputs:新增一个纯函数(放 graph.js,与 missingVerdicts 相邻)edgeOutputs(item, nodeId),返回 [{ name, values: [...] }]:遍历 item.edges 里 from === nodeId 且 when?.output 的边,按 output 名聚合 when.eq 的取值(else 不贡献)。声明过的端口优先,同名合并且补上 values。
  2. index.js:90-91 的提示带上取值域:名字有 values 时写成 OUTPUT state: <one of: ready, escalate>,没有就维持 OUTPUT <name>: <value>。这样一个没读过图的节点也能知道该打哪几个值。
  3. 不改 missingVerdicts 的判据,也不放松 fail:它仍然是「出边要的没打就失败」,只是现在节点能知道要打什么。

红先测试(plugins/workflow/test/step-body.test.js 或现有 assign.test.js):

  1. 造图:节点 triage 无 outputs,边 triage→claim when {output:'state',eq:'ready'}、triage→escalate else;断言 stepBody(...).outputs 里有 state,且 values 含 ready(改前为空 → 红);
  2. dispatch 收到的文本必须含 OUTPUT state(用假 fb.bot.send 抓文本;改前不含 → 红);
  3. 声明了 outputs 的节点行为不变(按声明那组输出,不重复、不丢);
  4. 一条端到端的:wf-1cdf6ec5 这种形状的 run 里,节点按提示打 state: ready 后 triage=done 且 claim 起跑(可复用 assign.test.js 的夹具)。

验收命令:

cd plugins/workflow && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/step-body.test.js test/graph.test.js test/assign.test.js

新用例改前红、改后全绿;node .github/scripts/changelog.mjs check 通过。

范围外

  • 定义图时对「出边读了没声明的 output」报警/拒绝(是另一个决策,另开单);
  • #2666 已经做的「已交付步站住」兜底;
  • 重写 template.js 里 issue 快线的 prompt 文案(那是权宜,不是根因)。

风险面

plugins/workflow/** 不是 T0/T1,T2,PR 要 tip 上的 PASS。改的是每个节点收到的提示文本,所有 workflow 都会受影响,所以既有 assign/queue/graph 套件要一起跑。

Activity

  1. self-assigned this
    on Nov 8, 2026
  2. added
    bugSomething isn't working
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:automationScheduled tasks and automation
    on Nov 8, 2026
  3. Birfy commented on Nov 8, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    已派实现线 wf-67229151:taskId i2669-impl(build → ship → report),05:14Z 起跑。改动面按本单正文:graph.js 新增 edgeOutputs、service.js 的 stepBody 并集、index.js:90-91 的提示带取值域,附红先用例与 docs/subsystems/workflow.md 一句。出 PR 走 PR 快线拿 PASS(T2)。

  4. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 8, 2026
  5. 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
  6. added 2 commits that reference this issue on Nov 9, 2026
    656fada
    af9a30b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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