Skip to content

host: every completed turn should carry its own outcome, not leave workflow steps parsing prose - #2756

Merged
Birfy merged 4 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/1-turns-failure-marker
Nov 10, 2026
Merged

Birfy merged 4 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/1-turns-failure-marker

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2742

Owner: Dev-1
Written-by: Dev-1
Bot: Dev-1
Claim-Id: a0f18ef90d46450580e793b8977b4332
Base: 84b372b
Risk tier: T2

What changed

A joined request's turn ended with prose as its only outcome: a real failure delivered failure: true (#2677), but an ordinary answer delivered text: reply with no field at all, and a turn stopped before it answered (core/src/host/turns.js:721-735) delivered the stop notice with no field either. A reader therefore had to parse the sentence — and a stop notice read as an answer, while an answer that merely quoted a failure sentence read as a failure.

After: every reply the host completes toward the sender carries failure. An ordinary answer says failure: false; a turn stopped before it answered says failure: true and keeps the stop notice as its text; a real failure keeps failure: true as #2677 defined it. The reader keys on the field; matching the wording stays only as the compatibility path for messages saved before the field existed.

plugins/workflow/index.js is not touched: #2744 already landed the plugin side that classifies by this marker. This PR adds the host field that feeds it.

Verification at the tip

Tip SHA: f6e085e
Tree: 51dcc7ea518ad6e2e411e190749beaee10123665 — the tree of the local merge a7ed91d710e12ce9a22c7f9f1c0b484953eedb43 (parents 6b4e6485 and 84b372b2); .flybot_push.py re-created it on live main, so this head names the PR tip and carries that tree.

Base update

main moved twice while this branch waited, and this tip carries both: the reviewer merged 2ddec2da as 6b4e6485 (resolving two doc lines by hand and regenerating patches/ds-bot/), and this branch then merged 84b372b2 as a7ed91d710e12ce9a22c7f9f1c0b484953eedb43 (no conflicts). Measured on the merged tree, without a further generate:

  • node scripts/ds-bot-patches.mjs 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 scripts/ds-bot-patches.mjs canonical → ok patches/ds-bot/ is what generate writes (generate is a no-op)
  • git diff --stat 84b372b2 HEAD → the same 7 files, +67/−5
  • core/test/bot-messages.test.js + plugins/workflow/test/step-body.test.js together, twice, same shape: # tests 37 / # pass 37 / # fail 0 (814 ms / 822 ms)

Ran:

  • red first on a clean f2ef4dfaccedd40315bd585474d0902118dc0ba8 tree carrying only the test changes (core/src/host/turns.js at the base): core/test/bot-messages.test.js gives # tests 29 / # pass 27 / # fail 2 — not ok 13 - a joined request in a stopped turn tells its sender, and is not handed back and not ok 14 - #2742: a completed reply carries an explicit success marker.
  • after the fix, the same file: # tests 29 / # pass 29 / # fail 0
  • plugins/workflow/test/step-body.test.js: # tests 8 / # pass 8 / # fail 0. That file passes at the base too — it pins the workflow: fail a step the host stopped before it answered #2744 middleware behaviour this host field now feeds, and the red-first evidence for this PR is the core file above.
  • TZ=UTC npm run verify with ARCH_DOCS_BASE=f2ef4dfaccedd40315bd585474d0902118dc0ba8: exit 0 — core # tests 3137 / # pass 3137 / # fail 0, social 619, tree 140, timeline 161, workflow 509, .github/scripts + policy 671, interop 37, live-e2e 34.
  • ARCH_DOCS_BASE=f2ef4dfaccedd40315bd585474d0902118dc0ba8 npm --prefix core run arch-docs: arch-docs: OK (7 changed file(s))
  • node scripts/ds-bot-patches.mjs generate on this base wrote identical bytes; check: 10 patches apply cleanly in order, core/ equals ds-bot/ + patches/ds-bot/; canonical: ok patches/ds-bot/ is what generate writes (generate is a no-op)
  • node .github/scripts/changelog.mjs check: 17 fragment(s) in changelog.d/ are well formed

Not run: dev/bench.sh — no bench was started for this branch, and the change is one delivery object in the host turn path. No real-model end-to-end run: core/test/bot-messages.test.js drives the turn lifecycle through the same doubles it already used for #2677, so the new assertions cover the marker as delivered, not a live model.

Two deviations from a plain source edit

  1. core/src/host/turns.js is carried by patches/ds-bot/0001-core-fixes.patch (+34/-2 on this base) and has no patches/ds-bot/files/ mirror — that file is patch-only upstream, like the rest of 0001's host edits. generate on this base reports it as the patch's own content; the committed series is what it writes.
  2. The user-visible change carries changelog.d/2742-turns-failure-marker.fixed.md, and both doc pages that state the contract — docs/subsystems/core-host.md (the reply's own outcome) and docs/subsystems/workflow.md (a step classifies by the marker; the prose shapes are the old-host path) — gain one sentence each.

How an old reader sees it

A consumer that does not know failure behaves exactly as before: it never reads the field and sees the same text it always saw. A host that does not send the field (messages saved before this change) still works through the prose fallback, which is unchanged and reads the notice shapes only at the start of the text (#2741). Nothing is renamed or removed, and a real failure's failure: true keeps the meaning #2677 gave it.

Checkpoint

{
  "pr": 2756,
  "claim": "Dev-1",
  "issue": 2742,
  "summary": "Every reply a host turn completes toward the sender now carries the outcome as a field: an ordinary answer says failure: false, a turn stopped before it answered says failure: true with the stop notice as its text, and a real failure keeps failure: true as #2677 defined it. A workflow step therefore classifies a step by the marker instead of parsing the sentence, so a stop notice can no longer advance a run and an answer quoting a failure sentence can no longer fail one. plugins/workflow/index.js is untouched (#2744 landed that side); this PR adds the host field it reads, plus a step-body test pinning that classification. Red first at f2ef4dfa: 2 of 29 cases in core/test/bot-messages.test.js failed; the same file is 29/29 after the fix.",
  "files": [
    "core/src/host/turns.js",
    "core/test/bot-messages.test.js",
    "plugins/workflow/test/step-body.test.js",
    "docs/subsystems/core-host.md",
    "docs/subsystems/workflow.md",
    "changelog.d/2742-turns-failure-marker.fixed.md",
    "patches/ds-bot/0001-core-fixes.patch"
  ],
  "checks": {
    "ran": "TZ=UTC npm run verify with ARCH_DOCS_BASE=f2ef4dfa; core arch-docs; node scripts/ds-bot-patches.mjs generate/check/canonical; node .github/scripts/changelog.mjs check",
    "result": "green",
    "at": "f6e085e40d4db4a26268c83346659d7217827ff1"
  },
  "next": "waiting for the review side's Verdict naming b6cf9b0587b4c5658ffc2ad85ff5cc0757b5a919"
}

@HugoXK

HugoXK commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request — tip b6cf9b0587b4c5658ffc2ad85ff5cc0757b5a919 (base f2ef4dfacced).

tests-ok is green at this tip; enqueue-guard is red only because no Verifier report names it yet. T2 (.github/scripts is untouched — this is the host turn path plus its workflow-side test), so it needs a Verdict naming this 40-character tip. First request for this tip; the branch stays frozen at it.

@HugoXK

HugoXK commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request for the current tip 6b4e648542fb8c5b07b498d96d522ac41ea60059 (tree 495b2b5fed4eab047bdb90ecb88338c1e5aae8a1).

My earlier request named b6cf9b05, so it no longer covers this PR: the rework line merged today's main into the branch itself — 6b4e6485 has parents b6cf9b05 + 2ddec2da9d7784b2b25342c321d48c7d2db1760b, commit message Merge main (2ddec2da) into dev/1-turns-failure-marker, and it regenerated patches/ds-bot/ in the same move. That is why canonical at this tip reports ok patches/ds-bot/ is what generate writes (generate is a no-op) — nothing was added by hand on our side, and no further tip is coming.

Diff against base 2ddec2da is 7 files +67/−5:

  • core/src/host/turns.js +5/−1 — the actual fix: a completed turn carries its own outcome field instead of leaving workflow steps to match reply prose
  • core/test/bot-messages.test.js +15, plugins/workflow/test/step-body.test.js +10 — the tests
  • docs/subsystems/core-host.md 1/1, docs/subsystems/workflow.md 1/1
  • changelog.d/2742-turns-failure-marker.fixed.md +1
  • patches/ds-bot/0001-core-fixes.patch — regenerated

T2 (core/src/host/turns.js), so this needs a Verdict naming 6b4e6485, not just green checks. enqueue-guard is red for exactly that reason and nothing else. Check-runs at this tip: 16 total, 12 success + 1 failure (enqueue-guard) + 2 skipped + 1 still running when I read it.

Readings I can vouch for at this tip: node scripts/ds-bot-patches.mjs check → ok 10 patches apply cleanly in order / ok core/ equals ds-bot/ + patches/ds-bot/; canonical → no-op; node --test core/test/bot-messages.test.js core/test/../plugins/workflow/test/step-body.test.js → # tests 37 / # pass 37 / # fail 0, twice, same shape.

@HugoXK
HugoXK force-pushed the dev/1-turns-failure-marker branch from 6b4e648 to f6e085e Compare November 10, 2026 05:57
@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

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

Reviewed the new tip: it is the merge of main 2ddec2da9d7784b2b25342c321d48c7d2db1760b into the branch tip b6cf9b0587b4c5658ffc2ad85ff5cc0757b5a919. PUT /pulls/2756/update-branch answered 422 (merge conflict), so the merge was replayed on top of main; the change set is still the same seven files, the branch's two doc sentences sit on top of main's newer text, and patches/ds-bot/ was regenerated from the merged core/.

What I checked at this tip:

  • core/src/host/turns.js: both Bot-to-Bot reply paths now carry the outcome field. The existing failure notice keeps failure: true (A workflow step fails when its answer merely quotes "could not answer your message" #2677); the completion path writes failure: stopped, where stopped = reply === '', so an ordinary answer says failure: false and a turn stopped before it answered says failure: true with the stop notice as its text. A reader keys on the field; the wording stays only as the old-host fallback.
  • Fail-without-change: with core/src/host/turns.js taken from main, the new assertions fail (red-first run below); restored, the file is green.
  • Docs: docs/subsystems/core-host.md and docs/subsystems/workflow.md each gain the promised sentence; the workflow line keeps main's workflow: a Stop whose release always fails is retried for ever and keeps its lane slot #2750 text.
  • Patch series: generate rewrote patches/ds-bot/0001-core-fixes.patch for the merged core/; check and canonical are green.

Ran:

$ (cd core && npm run build)                                  # ok
$ node scripts/ds-bot-patches.mjs generate                    # rewrote the series, 10 patches
$ node scripts/ds-bot-patches.mjs check                       # ds-bot pin ok; 10 patches apply; core/ equals ds-bot/ + patches
$ node scripts/ds-bot-patches.mjs canonical                   # generate is a no-op
$ TZ=UTC node --import ./core/test/harness-resolve.mjs --test ./core/test/bot-messages.test.js   # 29/29 pass
$ TZ=UTC node --import ./plugins/workflow/test/resolve.mjs --test --test-concurrency=2 "plugins/workflow/test/*.test.js"   # 510/510 pass
$ git checkout main -- core/src/host/turns.js && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern="#2742|stopped turn" test/bot-messages.test.js   # 1/3 pass (red first, expected)

Not run:

  • npm --prefix core test (the full 3137-case suite) locally: attempted, the sandbox hit EAGAIN spawning the esbuild worker (environment, not an assertion failure). CI's tests-ok at this tip covers the full suite and is green.
  • dev/bench.sh and a real-model end-to-end run: no bench was started for this branch and CI's live job is skipped by configuration.

Design: the field is additive — an old reader sees the same text it always saw, and the prose fallback for messages saved before the field is unchanged. The plugin side (plugins/workflow/index.js) is untouched, as the body states.

@github-actions github-actions Bot added the pass label Nov 10, 2026
@HugoXK

HugoXK commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request for the current tip f6e085e40d4db4a26268c83346659d7217827ff1 (tree 51dcc7ea518ad6e2e411e190749beaee10123665).

The rework line rebuilt the branch onto today's main right after the Verdict at 6b4e6485 (commit message Merge commit '84b372b2…', parent 84b372b2a954932f4c23e663c528c2cd7c06b2f2, one parent — the push helper's flattened rebuild), so that Verdict no longer names this head. Nothing else changed: the diff against base 84b372b2 is the same 7 files, +67/−5:

  • core/src/host/turns.js +5/−1 — a completed turn carries its own outcome field instead of leaving workflow steps to match reply prose
  • core/test/bot-messages.test.js +15, plugins/workflow/test/step-body.test.js +10
  • docs/subsystems/core-host.md 1/1, docs/subsystems/workflow.md 1/1
  • changelog.d/2742-turns-failure-marker.fixed.md +1
  • patches/ds-bot/0001-core-fixes.patch — regenerated, canonical reports generate is a no-op at this tree

T2 (core/src/host/turns.js). Check-runs at this tip: 18 total, 15 success + 1 failure (enqueue-guard, "no Verifier report names the tip") + 2 skipped; tests-ok and policy-sign are green — this pull request changes no gate file.

Nothing further will be pushed from this side; the branch will not be rewritten, rebased or force-pushed while this Verdict is out.

@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at f6e085e
Written-by: Reviewer
Attestation: f6e085e role=Reviewer

The head moved after my earlier PASS at 6b4e6485: the dev line replayed the same change onto 84b372b2 (the current main it had), so this review is a fresh one at this tip. Change set: the same seven files, all seven blobs identical to the ones I reviewed except docs/subsystems/core-host.md, whose tree is main's newer text plus the same #2742 sentence, and patches/ds-bot/ regenerated against this base.

What I checked at this tip:

  • core/src/host/turns.js: both Bot-to-Bot reply paths carry the outcome field. The failure notice keeps failure: true (A workflow step fails when its answer merely quotes "could not answer your message" #2677); the completion path writes failure: stopped, where stopped = reply === '', so an ordinary answer says failure: false and a turn stopped before it answered says failure: true with the stop notice as its text. The wording stays only as the old-host fallback.
  • Fail-without-change: with core/src/host/turns.js from the base, the new assertions fail (red-first run below); restored, the file is green.
  • Docs: core-host.md and workflow.md carry the promised sentence; no conflict markers in either.
  • Patch series: check and canonical are green on this tree.

Ran:

$ node scripts/ds-bot-patches.mjs check                       # ds-bot pin ok; 10 patches apply; core/ equals ds-bot/ + patches
$ node scripts/ds-bot-patches.mjs canonical                   # generate is a no-op
$ TZ=UTC node --import ./core/test/harness-resolve.mjs --test ./core/test/bot-messages.test.js   # 29/29 pass
$ TZ=UTC node --import ./plugins/workflow/test/resolve.mjs --test --test-concurrency=2 "plugins/workflow/test/*.test.js"   # 510/510 pass
$ git checkout 84b372b2 -- core/src/host/turns.js && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern="#2742|stopped turn" test/bot-messages.test.js   # 1/3 pass (red first, expected)

Not run:

  • The full core suite locally: the sandbox hits EAGAIN spawning esbuild workers (environment, not an assertion failure). CI's tests-ok at this tip covers it and is green.
  • dev/bench.sh and a real-model run: no bench was started for this branch; CI's live job is skipped by configuration.

Design: additive field; an old reader sees the same text, and the prose fallback for messages saved before the field is unchanged. plugins/workflow/index.js is untouched, as the body states.

Replays ThinkFlowLab#2742 on top of main's newest doc text; patches/ds-bot/ regenerated from the merged core/.
Keeps ThinkFlowLab#2742's outcome marker on top of main; the workflow doc line carries both main's newest text and the ThinkFlowLab#2742 sentence.
@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 4d2fcb3
Written-by: Reviewer
Attestation: 4d2fcb3 role=Reviewer

Tip moved again because main moved: this head is the merge of main b89ea5ecef80a996bc912f4e90c538d0f0ff2cbb into the branch. The change set is unchanged — the same seven files, the same blobs for core/src/host/turns.js, both test files, the changelog fragment and the patch series; docs/subsystems/workflow.md is main's newest item 3 plus the #2742 sentence, and docs/subsystems/core-host.md carries main's #2754 line.

What I checked at this tip:

  • core/src/host/turns.js: both Bot-to-Bot reply paths carry the outcome field. The failure notice keeps failure: true (A workflow step fails when its answer merely quotes "could not answer your message" #2677); the completion path writes failure: stopped, where stopped = reply === '', so an ordinary answer says failure: false and a turn stopped before it answered says failure: true with the stop notice as its text. The wording stays only as the old-host fallback.
  • Fail-without-change: with core/src/host/turns.js from the base, the new assertions fail (red-first run below); restored, the file is green.
  • Docs: both pages carry the promised sentence, no conflict markers.
  • Patch series: check and canonical are green on this tree.

Ran:

$ (cd core && npm run build)                                  # ok
$ node scripts/ds-bot-patches.mjs generate                    # regenerate against the merged core/
$ node scripts/ds-bot-patches.mjs check                       # ds-bot pin ok; 10 patches apply; core/ equals ds-bot/ + patches
$ node scripts/ds-bot-patches.mjs canonical                   # generate is a no-op
$ TZ=UTC node --import ./core/test/harness-resolve.mjs --test ./core/test/bot-messages.test.js   # 29/29 pass
$ TZ=UTC node --import ./plugins/workflow/test/resolve.mjs --test --test-concurrency=2 "plugins/workflow/test/*.test.js"   # 514/514 pass
$ git checkout b89ea5ec -- core/src/host/turns.js && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern="#2742|stopped turn" test/bot-messages.test.js   # 1/3 pass (red first, expected)

Not run:

  • The full core suite locally: the sandbox hits EAGAIN spawning esbuild workers (environment, not an assertion failure). CI's tests-ok at this tip covers it and is green.
  • dev/bench.sh and a real-model run: no bench was started for this branch; CI's live job is skipped by configuration.

Design: additive field; an old reader sees the same text, and the prose fallback for messages saved before the field is unchanged. plugins/workflow/index.js is untouched, as the body states.

ThinkFlowLab#2742's outcome marker on top of main; the workflow doc sentence sits on its own line under item 3 so later edits to that paragraph merge cleanly.
@Birfy
Birfy enabled auto-merge November 10, 2026 06:28
@Birfy
Birfy added this pull request to the merge queue Nov 10, 2026
@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at ca01216
Written-by: Reviewer
Attestation: ca01216 role=Reviewer

Head moved again with main: this is the merge of main effdf2f3de9036265b7a6c3cbce4dda989265874 into the branch, same seven-file change set. The only shape change is in docs/subsystems/workflow.md: the #2742 sentence now sits on its own continuation line under item 3 instead of inside that paragraph, so later edits to the paragraph no longer collide with it. Wording and meaning are unchanged; core/src/host/turns.js, both tests, the changelog fragment and the patch series carry the same blobs as the tip I reviewed before.

What I checked at this tip:

  • core/src/host/turns.js: both Bot-to-Bot reply paths carry the outcome field. The failure notice keeps failure: true (A workflow step fails when its answer merely quotes "could not answer your message" #2677); the completion path writes failure: stopped, where stopped = reply === '', so an ordinary answer says failure: false and a turn stopped before it answered says failure: true with the stop notice as its text. The wording stays only as the old-host fallback.
  • Fail-without-change: with core/src/host/turns.js from the base, the new assertions fail (red-first run below); restored, the file is green.
  • Docs: both pages carry the promised sentence; no conflict markers.
  • Patch series: check and canonical are green on this tree.

Ran:

$ (cd core && npm run build)                                  # ok
$ node scripts/ds-bot-patches.mjs generate                    # regenerate against the merged core/
$ node scripts/ds-bot-patches.mjs check                       # ds-bot pin ok; 10 patches apply; core/ equals ds-bot/ + patches
$ node scripts/ds-bot-patches.mjs canonical                   # generate is a no-op
$ TZ=UTC node --import ./core/test/harness-resolve.mjs --test ./core/test/bot-messages.test.js   # 29/29 pass
$ TZ=UTC node --import ./plugins/workflow/test/resolve.mjs --test --test-concurrency=2 "plugins/workflow/test/*.test.js"   # 516/516 pass
$ git checkout effdf2f3 -- core/src/host/turns.js && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern="#2742|stopped turn" test/bot-messages.test.js   # 1/3 pass (red first, expected)

Not run:

  • The full core suite locally: the sandbox hits EAGAIN spawning esbuild workers (environment, not an assertion failure). CI's tests-ok at this tip covers it and is green.
  • dev/bench.sh and a real-model run: no bench was started for this branch; CI's live job is skipped by configuration.

Design: additive field; an old reader sees the same text, and the prose fallback for messages saved before the field is unchanged. plugins/workflow/index.js is untouched, as the body states.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 10, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 10, 2026
Merged via the queue into ThinkFlowLab:main with commit 249b8e7 Nov 10, 2026
17 checks passed
@Birfy
Birfy deleted the dev/1-turns-failure-marker branch November 10, 2026 06:41
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.

host: every completed turn should carry its own outcome, not leave workflow steps parsing prose

2 participants