Skip to content

fix(workflow): an early-ended failed run logs neither the early end nor why, and a silent reply claims it stopped there - #1615

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev2/fix/p105-early-end-narrate
Oct 29, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev2/fix/p105-early-end-narrate

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1577
Owner: Dev2
Reviewer: Reviewer
Written-by: Dev-2

Base: bde09d1 (live main when this branch was cut)
Tip: 5fc75fb (the fork head; tree 588d5fec…)
Tree: 588d5feca2eb7e0dd939b52c9500775bf58536ed

What was wrong

#1572 made a failure able to end the run early (needs-human, already-claimed, tip-unchanged, open-pr): failNode (plugins/workflow/src/graph.js:721-735) marks the node done, returns finished: true with earlyEnd, and service.js:448 finishes the run with run.earlyEnd. linesFor receives earlyEnd at service.js:497, but neither fail branch in narrate.js read it — only the complete branch checks finished.

So the log of a run that ended early said neither that it ended early nor why: with an empty reply it printed Verify 这一步失败了,停在本节点。 while the run was done and every node done, and the ⏹ 提前结束 · <reason> line that #1572 earned never appeared. A reader could not tell "the Bot failed and the run stopped" from "the pass produced no new tip, so the run was closed on purpose".

What changed

plugins/workflow/src/narrate.js only — both fail branches (live :102, non-live :154) ask one helper first:

  • earlyEndLines(workflow, event) returns null unless event.earlyEnd != null. When it is set, it emits the Bot reply if event.text has one, then a kind: 'system' frame: key ⏹ Ended early · {reason}, params { reason }, text ⏹ 提前结束 · <reason>. No tone, so it never renders in the failure colour.
  • With no reply there is no bot line at all, rather than a line that says the step failed: an empty bubble would be noise, and the sentence it used to carry was false.
  • The complete branch is untouched: a complete step that ends the run early still closes on ✓ 完成 · … and does not add a ⏹ line, so the two paths never double up.
  • Plain failures still say 停在本节点, and recovered still says ↺ 失败恢复 → …; the regression tests pin both on this tip.

graph.js and service.js are untouched: nodeStatus, finished, doneCount and run.earlyEnd keep exactly the semantics #1572 landed. The new line follows this file's existing frame contract (English key + params + stored Chinese text, #1489), so the shell can translate it and older rows keep the fallback.

Red → green, honestly

Red first on the live base bde09d1c, with only plugins/workflow/test/narrate-early-end.test.js added and narrate.js at main: TZ=UTC node --import ./test/resolve.mjs --test test/narrate-early-end.test.js — 7 tests, 3 pass, 4 fail, exactly the four early-end tests:

  • live fail with earlyEnd and no reply says early end, never failure
  • live fail with earlyEnd keeps the Bot reply and adds the early-end line
  • non-live fail with earlyEnd says early end, never failure
  • service: a Verify reply that marks tip-unchanged narrates the early end

The three that pass on the base are the regression locks (complete path still ✓ 完成 with no ⏹, plain failure still 停在本节点, recovered still ↺ 失败恢复).

At this tip: the same file 7/7 pass, and TZ=UTC npm --prefix plugins/workflow test — 221 tests, 221 pass, 0 fail.

Files

3 files changed, 119 insertions(+)
 crucially unchanged: plugins/workflow/src/graph.js, plugins/workflow/src/service.js
 plugins/workflow/src/narrate.js                 |  15 ++++
 plugins/workflow/test/narrate-early-end.test.js | 103 ++++++++++++++++++++++++
 changelog.d/1577.fixed.md                       |   1 +

Checks

TZ=UTC ARCH_DOCS_BASE=bde09d1c npm run verify at tree 588d5fec… — exit 0: core 1903, social 528, tree 125, timeline 129, workflow 221, .github 549, interop 37, live-e2e 24; oxlint 0 errors (only the repository's pre-existing warnings); build-client --check clean; arch-docs: OK (3 changed file(s)); ds-bot-patches check: ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5), ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/. node .github/scripts/changelog.mjs check — fragments well formed, exit 0.

Not run: no live instance (no instance access), no browser; CI was not waited on.

Docs-not-needed: narration copy only — graph.js and service.js keep the state semantics docs/subsystems/workflow.md already describes, so the page needs no new rule.

Checkpoint

{
  "pr": 1615,
  "claim": "Dev2",
  "issue": 1577,
  "summary": "A run that ended early (needs-human, already-claimed, tip-unchanged, open-pr) was narrated as a failed step: the log said the node failed and stopped there, and the reason never appeared. Both fail branches in narrate.js now ask one helper first and emit ⏹ 提前结束 · <reason>, keeping the Bot reply when there is one and emitting no bot line when there is none. The complete path still closes on ✓ 完成 with no second line, and plain failures and recoveries keep their wording. graph.js and service.js are untouched.",
  "files": [
    "plugins/workflow/src/narrate.js",
    "plugins/workflow/test/narrate-early-end.test.js",
    "changelog.d/1577.fixed.md"
  ],
  "checks": {
    "ran": "TZ=UTC ARCH_DOCS_BASE=bde09d1c npm run verify, exit 0 (core 1903, social 528, tree 125, timeline 129, workflow 221, .github 549, interop 37, live-e2e 24, oxlint 0 errors); narrate-early-end.test.js red first on bde09d1c 3/7, green 7/7 here",
    "result": "green",
    "at": "5fc75fb29893f7fc5fae83f3de2bd6d361427583"
  },
  "next": "none — ready for review"
}

@HugoXK
HugoXK marked this pull request as ready for review October 29, 2026 01:01
@HugoXK

HugoXK commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request — PR #1615 at 5fc75fb29893f7fc5fae83f3de2bd6d361427583.

#1577 (S, T2 — plugins/workflow): a run that ended early (needs-human, already-claimed, tip-unchanged, open-pr) was narrated as a failed step — the log said the node failed and the run stopped there, and the ⏹ 提前结束 · <reason> line never appeared, so "the Bot failed" and "the pass produced no new tip, so the run closed on purpose" looked identical. Both fail branches in narrate.js now ask one helper first: it emits the Bot reply when there is one, then a kind: 'system' frame ⏹ 提前结束 · <reason>; with no reply there is no bot line at all (the old sentence was false). The complete path still closes on ✓ 完成 with no second line, and plain failures / recoveries keep their wording. graph.js and service.js are untouched, so the state semantics #1572 landed do not move.

As of my read: 3 files +119/−0 (narrate.js +15, narrate-early-end.test.js +103, one changelog fragment), 1 commit, base bde09d1c (live main). At the tip: tests-ok, package, plugins (tree|social|workflow|timeline), judge, merge-gate, agent-audience, reviewed tip (report only), ubuntu-latest · Node 24 · DSH 0.2.0-rc.2 all success; enqueue-guard red only for "no Verifier report names the tip".

A fail step that ends the run early (needs-human, already-claimed,
tip-unchanged, open-pr) was narrated with the failure copy, so the log said
the run stopped on the node and never named the reason. Both fail branches now
emit the early-end frame and keep a Bot reply when there is one.

Docs-not-needed: narration copy only; graph and run state semantics unchanged.

Fixes ThinkFlowLab#1577
@Birfy

Birfy commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 5fc75fb
Written-by: Reviewer
Attestation: 5fc75fb role=Reviewer

Ran at this tip:

$ cd plugins/workflow && TZ=UTC npm test
# tests 221  # pass 221  # fail 0
$ cd plugins/workflow && cp <base bde09d1c>/plugins/workflow/src/narrate.js src/narrate.js
$ TZ=UTC node --import ./test/resolve.mjs --test test/narrate-early-end.test.js
# tests 7  # pass 3  # fail 4
$ cd plugins/workflow && TZ=UTC npm test   (merged tree: main a6755fa + this tip's 3 files)
# tests 224  # pass 224  # fail 0

Scope: compare bde09d1c...5fc75fb = ahead 1, exactly 3 files — plugins/workflow/src/narrate.js (+15), plugins/workflow/test/narrate-early-end.test.js (new, 103), changelog.d/1577.fixed.md. graph.js and service.js are unchanged, so #1572's state semantics stay as main has them; tip tree 588d5fec… matches the body.

Red first: with narrate.js put back to the base blob the new file is 7 tests, 3 pass, 4 fail, and the four failures are exactly the early-end cases; the three locks (complete path, plain failure, recovered) still pass. Green on the tip is 221/221, matching the PR's number.

Merge tree: main moved to a6755fa (#1614) and that range touches plugins/workflow/src/service.js + adds test/history-row-id.test.js, neither file in this diff — merged and re-run: 224/224, so the early-end narration does not collide with #1611's service change.

Gate facts at the tip: risk-gate.mjs job log says tier T2; tests-ok, merge-gate, plugins (workflow), reviewed tip all success; the only failure in the check list is the 01:01 enqueue-guard run from before any conclusion existed, and its 01:13 re-run is success. Fork PR, so the agent-pr line is the fork exemption; label added anyway. Checkpoint block is 1177 chars, summary 511, files 3 all in this diff, checks.at this tip. Docs-not-needed: is in both the branch commit and the body.

Not run: no live instance or browser (narration copy only), and no npm run verify at the fork — the workflow suite is the affected one; core/social/tree/timeline are untouched by this diff.

@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 29, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 29, 2026
@github-actions github-actions Bot added the pass label Oct 29, 2026
Merged via the queue into ThinkFlowLab:main with commit 0dbcd79 Oct 29, 2026
18 of 19 checks passed
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(workflow): an early-ended failed run logs neither the early end nor why, and a silent reply claims it stopped there

2 participants