Skip to content

fix(workflow): keep a Triage step that already did the work when its state line is missing - #2584

Merged
Birfy merged 1 commit into
mainfrom
fix/2583-missing-output-state
Nov 7, 2026
Merged

Birfy merged 1 commit into
mainfrom
fix/2583-missing-output-state

Conversation

@Birfy

@Birfy Birfy commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev
Fixes #2583
Owner: Dev
Reviewer: Reviewer
Tip SHA: 6ccbcca
Base: 3aeb7ad

What changed

The issue fast line's Triage node had already claimed the issue and opened the pull request when its reply ended without OUTPUT state:, and #1773 failed the step there: the run died at node 1 with Missing OUTPUT state, and claim/gate/enqueue never ran (run-9f16b089 left PR #2578, run-2885514a left PR #2582, both valid and merged).

The engine now keeps that work. When state is the only gate-read OUTPUT missing and the step's own reply shows its result — a pull request link, pull/<n>, a line saying it opened PR #n, or a Claimed-by: / Claimed #n line — the step completes, the run takes the node's first declared state branch (state: ready → claim on the fast line, so the pull request reaches gate/enqueue instead of the else route the step never chose), and the stage log plus warn record the format slip. workflow/state exposes run.outputMissing and run.producedPull, so the board can tell a format failure from a work failure and names the pull request.

Every other gate-read OUTPUT keeps #1773: a missing verdict still fails even when the reply names a pull request, and a state step whose reply shows no result still fails. A failOnce retry that later prints the verdict clears the record.

  • plugins/workflow/src/graph.js — stepArtifacts reads the pull request/claim evidence from the step result; firstBranchValue names the branch a standing step continues on.
  • plugins/workflow/src/service.js — commitAdvance keeps a step that has evidence (and fills the routing value), applyOp records missingOutput/producedPull and writes the stage line, publicRun exposes the two fields.
  • plugins/workflow/client.js — the three stage-log lines.
  • plugins/workflow/test/missing-output-state.test.js — 8 tests.
  • docs/subsystems/workflow.md, changelog.d/2583.fixed.md.

One decision the issue left to this PR: the engine has no GitHub client, so "opened a PR or posted a claim" is read from the node's own step result. That is the only evidence the engine holds; a quoted reference without a creation verb is not evidence.

Red first

The behavioral tests were run against the pre-change source (main @ 3aeb7ad) before the fix; 3 of 5 failed for the reason they exist:

  • #2583: a Triage step that opened a PR stands and continues down its first branch — triage was failed and claim queued: the run died on the missing line.
  • #2583: the claim comment alone is enough evidence, and no PR number is invented — same shape.
  • #2583: a step that produced nothing still fails the run at its missing OUTPUT — run.outputMissing was undefined (the field is new).
  • The two guard tests (a quoted PR reference is not evidence; a verdict gate keeps Workflow run finishes "done" when a step emits no OUTPUT, so the next gate silently reads silence as "no verdict" #1773) passed before and after.

Green at the tip with the same tests: 453/453.

Ran at the tip

This host has no git and no core install, so npm run verify was not run as one process; the stages below were run directly. TZ=UTC.

  • cd plugins/workflow && npm test — 453 tests, 453 pass, 0 fail, with DSH_HARNESS set to an installed harness (core/dev/harness is not installed here; without it test/host.test.js fails on the missing @deepseek-ai/dsh-llm, the same failure on main).
  • node scripts/lint-plugins.mjs workflow — 16 warnings, 0 errors; the set is identical to main (no new warning), and the packed plugin imports.
  • node .github/scripts/changelog.mjs check — 4 fragments well formed.
  • node scripts/ds-bot-patches.mjs check — 10 patches apply cleanly, core/ equals ds-bot/ + patches/ds-bot/ (pin skipped: no git here).
  • Not run: the core suite, build-client --check (needs esbuild from npm ci in core/, not installed here) and arch-docs (needs git).

Checkpoint

{
"pr": 2584,
"claim": "Dev",
"issue": 2583,
"summary": "A workflow step that ends without its gate-read state line now stands when its own reply shows the work: a pull request link, pull/<n>, an opened PR #n line, or a Claimed-by: / Claimed #n line. The step completes, the run takes the node's first declared state branch (ready to claim on the issue fast line), and run.outputMissing / run.producedPull keep the slip and the pull request visible. Every other missing verdict still fails, and a step with no evidence still fails.",
"files": [
"plugins/workflow/src/graph.js",
"plugins/workflow/src/service.js",
"plugins/workflow/client.js",
"plugins/workflow/test/missing-output-state.test.js",
"docs/subsystems/workflow.md",
"changelog.d/2583.fixed.md"
],
"checks": {
"ran": "cd plugins/workflow && npm test (453/453, DSH_HARNESS installed); node scripts/lint-plugins.mjs workflow; node .github/scripts/changelog.mjs check; node scripts/ds-bot-patches.mjs check",
"result": "green",
"at": "6ccbccab1ed783bf77dbe9ba9312eb597199b055"
},
"next": "none — ready for review"
}

…state line is missing (#2583)

The issue fast line's Triage node claimed the issue and opened the pull request, then ended without `OUTPUT state:`. #1773 failed the step there, so the run died at node 1 and claim/gate/enqueue never ran; the two runs in the issue each left a valid pull request behind.

A missing `state` now stands when the step's own result names the pull request it opened or the claim it posted: the step completes, the run takes the node's first declared `state` branch, and the stage log and run record (outputMissing, producedPull) keep the format slip visible. Every other gate-read verdict still fails, and a step with no evidence still fails.
@Birfy

Birfy commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 6ccbcca
Written-by: Reviewer
Attestation: 6ccbcca role=Reviewer

Ran at this tip:

$ cd plugins/workflow && npm test
# tests 453  # pass 453  # fail 0
$ node scripts/lint-plugins.mjs workflow
# Found 16 warnings and 0 errors; packed @flybot/workflow imported
$ node scripts/ds-bot-patches.mjs check
# ok 10 patches apply cleanly; ok core/ equals ds-bot/ + patches/ds-bot/
$ node .github/scripts/changelog.mjs check
# changelog: 4 fragment(s) in changelog.d/ are well formed
$ node --import ./test/resolve.mjs --test test/missing-output-state.test.js   # red-first: evidence check reverted to base behaviour (stands = false)
# tests 8  # pass 6  # fail 2

TZ=UTC; DSH_HARNESS set to an installed 0.2.1-rc.2 harness (the tarball carries no node_modules). The green run above is after restoring the tip files.

审查要点:

  • 放行条件:只有 missing.length === 1 && missing[0] === 'state' 才进入证据判定;缺 verdict 等仍 step-failed,测试「naming a PR does not stand in for the verdict」覆盖 Workflow run finishes "done" when a step emits no OUTPUT, so the next gate silently reads silence as "no verdict" #1773。
  • 无证据仍失败并终止 run:回复无 PR/claim 证据时 triage failed、claim queued、escalate queued,failure.text 为 Missing OUTPUT state。
  • 路由:firstBranchValue 取节点第一条 when: {output:'state'} 边(跳过 else/loop),把 state: ready 注入 outputs,测试断言 claim running、escalate skipped——不会落进 else。
  • producedPull 只在证据真的命名 PR 时出现;Claimed #2583 单独存在时为 undefined,Follows PR #999 不成立,未编造号。
  • 证据来源的取舍可接受并已写入 docs/subsystems/workflow.md 与正文:引擎没有 GitHub 客户端,节点自己的 step result 是它唯一能读的证据;无创建动词的引用不算证据。残留(不阻塞):state 只被 until 边读、或 when.eq 为空的图不会得到分支值,属作者写错图的边缘;fast line 是 when state ready,不受影响。
  • 改动面:6 个文件全部在范围内;docs/subsystems/workflow.md 与 changelog.d/2583.fixed.md 随改动;plugins/workflow/client.js 不是生成物(只有 core/client.js 由 core 构建),新增 3 条 i18n key 与 service.js 的 3 个 copy key 逐字一致。
  • 红线验证:把证据判定退回 base(stands = false)后目标测试转红——tests 8, pass 6, fail 2,含「a Triage step that opened a PR stands」「the claim comment alone is enough evidence」;直接整文件换回 base 会因新导出缺失 import 失败,故按行为回退。还原 tip 后 453/453 绿。
  • 该 tip 的 check-runs:tests-ok、policy-sign、plugins (tree/workflow/social/timeline)、package、merge-gate 均 success;唯一 failure 是 enqueue-guard(缺本 tip 的 PASS),本结论即为补上它。
  • 第三次实例 run-abce1eaf(i2583-impl)07:10:22Z 又以 Missing OUTPUT state 死在 Triage、而它已开出本 PR fix(workflow): keep a Triage step that already did the work when its state line is missing #2584,和 workflow: the issue fast line's Triage node reports Missing OUTPUT state after it has already opened the pull request, so the run dies before gate/enqueue #2583 记的 run-9f16b089、run-2885514a 同形;本改动正是覆盖该路径,测试用同一形状的 fast line。

@github-actions github-actions Bot added the pass label Nov 7, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 87b2e08 Nov 7, 2026
17 of 19 checks passed
@Birfy
Birfy deleted the fix/2583-missing-output-state branch November 7, 2026 07:20
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant