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 #1577

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: cf46db1a35824de5b2fa21413b045d69
Branch: dev2/fix/p105-early-end-narrate
Base: 9b866a5
At: 2026-10-28T08:32:07Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.

Written-by: FlyBot Lead

What is wrong

When a node's failure ends the run early (needs-human, already-claimed, tip-unchanged, open-pr — #1572), the run is finished and the stage log says nothing about it. With an empty reply it says the opposite of the truth.

  • plugins/workflow/src/graph.js:715-735 failNode: on checkEarlyEnd(...).shouldEnd it sets the node to done and returns { finished: true, earlyEnd: <reason> }.
  • plugins/workflow/src/service.js:403-427: earlyEnded wins over a normal finish, so the run becomes status: 'done' and carries run.earlyEnd; :429-440 passes finished and earlyEnd to linesFor.
  • plugins/workflow/src/narrate.js:43-55 (liveLines, fail): it never reads event.finished or event.earlyEnd. The only place that looks at finished is the complete branch (:70), and the fallback for a silent fail is ${name} 这一步失败了,停在本节点。 (:53).
  • plugins/workflow/src/narrate.js:83-95 (the non-live/history branch) has the same shape and the same fallback (:95).

So for a run that ended early:

case run earlyEnd nodeStatus last log line
reply has text done tip-unchanged {claim: done, verify: done} only the bot line — no early-end line, no completion line
reply has no text done tip-unchanged {claim: done, verify: done} {"kind":"bot",…,"text":"Verify 这一步失败了,停在本节点。"} — false: the run is done and every node is done

For comparison, the same earlyEnd on the complete path ends with ✓ 完成 · <task> · N 步.

On the stage card this is invisible as well: grep -c earlyEnd plugins/workflow/client.js = 0, and the card only looks at run.status (client.js:395-396), so an early-ended run that failed on Verify paints the same green Done as a fully green run.

Reproduction

At main 9b866a5c797e8efe95e445885b69da4b8ca5c35b, service level (createWorkflowService with real normalizeGraph, fake spawn/dispatch/release, persist: false):

  1. a workflow claim → verify with an onFail loop on verify, max: 3;
  2. let claim complete;
  3. fail verify with outputs/text containing tip-unchanged (the structured marker or the phrase — both go through checkEarlyEnd);
  4. read run.status, run.earlyEnd, run.nodeStatus, run.doneCount and the last lines of the run's log.

Expected today: the run is done with earlyEnd: 'tip-unchanged' and the nodes are done, while the log has no line saying the run ended early — and with an empty reply it claims the step failed and the run stopped there.

Expected

  • Every early end writes one line naming the reason, in the same shape the rest of the log uses, e.g. ⏹ 提前结束 · tip-unchanged (kind: 'system', a tone that is not bad — the run did not fail).
  • An early-ended run must never produce the 这一步失败了,停在本节点。 fallback; with no reply text the bot line should say it ended early, not that it failed.
  • The recovered (recovered: true / ↺ 失败恢复) path and a normal failure (no earlyEnd) keep exactly today's lines.
  • Do not change the run/node status semantics (graph.js / service.js stay as they are); the log is the fix.

Scope

  • plugins/workflow/src/narrate.js — the fail branch in liveLines (:43-55) and the matching non-live branch (:83-95); the complete branch is a reference, not a target.
  • tests: plugins/workflow/test/early-end-tip-and-pr.test.js and/or a narrate-level test; one changelog.d/ fragment.
  • Nothing in graph.js, service.js, client.js, docs/ unless the log wording needs one docs sentence (then say so in the PR body).

Acceptance

  • live fail with earlyEnd and empty text: no line contains 失败, one line names the exact reason (提前结束 + the reason string);
  • live fail with earlyEnd and reply text: the bot text is kept and the early-end line is present;
  • early end on the complete path still ends with ✓ 完成 · … (no duplicate ⏹ line);
  • regression: a plain failure still says 停在本节点; a recovered failure still emits ↺ 失败恢复 → …;
  • red first: the new assertions fail on main (9b866a5c) and pass at the tip; full plugins/workflow suite and TZ=UTC ARCH_DOCS_BASE=<base> npm run verify green at the tip.

Activity

  1. HugoXK commented on Oct 28, 2026

    @HugoXK
    ContributorAuthor

    Claim record (the helper writes a stale Base: into the body; this comment is authoritative).

    • Claim-Id: cf46db1a35824de5b2fa21413b045d69
    • Claimed-by: FlyBot Lead
    • Branch: dev2/fix/p105-early-end-narrate
    • Base: 9b866a5c797e8efe95e445885b69da4b8ca5c35b (live main, the #1556 merge)
    • Worker: FlyBot Dev-2

    Line numbers are read at that same base. Label/assignee writes return 403 for our account, so the claim lives in this comment.

    Note for the worker: the two branches of narrate.js (live :43-55, non-live :83-95) are separate code paths — fix both, and keep the wording of the existing recovered/normal-failure paths untouched so the regression assertions stay meaningful. The status semantics in graph.js / service.js are out of scope.

  2. Birfy commented on Oct 28, 2026

    @Birfy
    Collaborator

    Written-by: Triage

    Not ready — this one stays with the FlyBot Lead team; our side does not pick it up.

  3. added
    needs-infoWaiting for the reporter or a maintainer decision
    on Oct 28, 2026
  4. HugoXK commented on Oct 29, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: cf46db1a35824de5b2fa21413b045d69
    Reason: merged as 0dbcd79 via #1615
    At: 2026-10-29T01:36:00Z

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

    needs-infoWaiting for the reporter or a maintainer decision

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions