fix(agents): recover genuine terminal child result on lost-context sweep (#90299)#92791
fix(agents): recover genuine terminal child result on lost-context sweep (#90299)#92791leorivastech wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 13, 2026, 7:58 PM ET / 23:58 UTC. Summary PR surface: Source +100, Tests +320. Total +420 across 4 files. Reproducibility: yes. Source inspection shows current main can fall from unresolved session-store reconciliation into the lost-context error while the announce path can still read child output; the PR body also includes a transcript-harness reproduction over real on-disk transcripts. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused recovery after exact-head checks and maintainer acceptance, preserving the strict terminal predicate and unchanged display helper semantics. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main can fall from unresolved session-store reconciliation into the lost-context error while the announce path can still read child output; the PR body also includes a transcript-harness reproduction over real on-disk transcripts. Is this the best way to solve the issue? Yes. This is the narrowest maintainable fix I found: keep session-store terminal status authoritative, then recover only strict terminal transcript text before falling back to the existing lost-context error; the broader alternatives in #90492 and #91400 did not cover the same predicate boundaries. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against c2754150c963. Label changesLabel justifications:
Evidence reviewedPR surface: Source +100, Tests +320. Total +420 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
The registry sweeper labeled a stale, context-lost subagent run as "failed: subagent run lost active execution context" even when the child had delivered a genuine final result, so a parent orchestrating with sessions_spawn / sessions_yield saw successful work as a plain failure. Before emitting the lost-context error, reconcile the child transcript with a strict terminal-result predicate that reuses the existing sessions_yield -aware snapshot: recover as ok only when there is final visible assistant text and the child is not parked waiting. Tool-call-only histories and yield waits keep the lost-context error, and a transcript-read failure is caught so the sweep still settles the run. Fixes openclaw#90299.
88dc6b6 to
633a04a
Compare
|
Thanks for the review — good catch on the
Added regression coverage: unit tests for a mixed text+ @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
closing this because transcript text alone is not a safe success predicate for lost-context lifecycle recovery. Fresh autoreview found two blocking cases:
The canonical fix must reconcile the current run's private transcript/status boundary and only accept an explicitly successful terminal turn. This patch is too risky to narrow safely without redesigning that recovery boundary. |
Summary
Problem. Agent Teams could deliver a subagent completion event whose status was
failed: subagent run lost active execution contextwhile the same event carried anon-empty child result (the report shows a full
# ARCHITECTURE.md). The lifecyclestatus and the delivered result disagree, so a parent orchestrating with
sessions_spawn/sessions_yieldtreats successful work as a plain failure and canretry, stall, or leave the workflow inconsistent.
Root cause. When the registry sweeper finds a stale active run whose in-memory
context is gone and whose session store has not settled a terminal status,
resolveCompletionFromSessionEntry()returnsnulland the sweep falls through to ahardcoded lost-context error (
subagent-registry.ts, thesweeper-lost-contextbranch). It never consults the child transcript, even though the announce path has
already captured a real final result from it.
Fix. Before emitting the lost-context error, the sweeper now reconciles the child
transcript and, only when the child produced a genuine terminal result, settles the
run as
okso the lifecycle status matches the delivered result. When there is nogenuine result it keeps the existing lost-context error.
The success predicate is the key. Rather than reusing the broad display helper
readSubagentOutput(which also surfaces a synthetic"N tool call(s) made without visible output."summary) or a fresh "any visible text" check (which ignores atrailing
sessions_yieldwait), this adds a strict predicate that reuses the existingsessions_yield-aware snapshot (summarizeSubagentOutputHistory) and returns a resultonly when there is final visible assistant text and the child is not parked waiting.
This is exactly the boundary the two prior attempts (#90492, #91400) got wrong. The
predicate also rejects a
toolUseassistant turn that carries visible pre-tool textplus a tool call (the model expected to continue after tool results), and any trailing
tool-only turn after a text turn — in both cases the child is still working, not finished.
Changes
src/agents/subagent-announce-output.tsloadSubagentHistoryMessages()(shared history source).resolveTerminalChildResultText(messages)— strict terminal-result predicate.Returns the final visible assistant text only;
undefinedforsessions_yieldwaits,
toolUseturns (visible pre-tool text plus a tool call), trailing tool-onlyturns, tool-call-only histories, silent markers, and empty histories. A new
lifecycle-only
latestAssistantTurnTerminalsnapshot flag tracks this; the displayselector (
selectSubagentOutputText/readSubagentOutput) ignores it, so displaybehavior is unchanged.
readTerminalChildResult(sessionKey, options)— reads a child session andapplies the strict predicate (reuses the same source as
readSubagentOutput).src/agents/subagent-registry.tssweeper-lost-contextbranch, attemptreadTerminalChildResultfirst; on agenuine result complete as
ok(sweeper-lost-context-recovered). A read failureis caught and falls through to the existing lost-context error so the run is still
settled (the sweep is never aborted).
What did NOT change (scope)
readSubagentOutputkeeps its existing display semantics — only its internals werefactored into a shared loader.
genuine terminal result still complete with the existing lost-context error.
Reproduction
sessions_spawnto create a subagent andsessions_yieldtowait for completion.
cleared before the session store settles a terminal status (the sweeper then enters
the lost-context branch).
status: "error"/"subagent run lost active execution context"even though the same event carries thechild's result.
status: "ok"with the result; tool-call-only and yield-wait children still settleas lost-context errors.
Real behavior proof
Behavior or issue addressed: The lost-context sweeper branch labeling a stale child
run as
failed: subagent run lost active execution contextwhile a real child resultwas delivered, and the inverse risk of recovering a non-terminal child (tool-call-only
or
sessions_yieldwait) as a successful completion (#90299).Real environment tested: Linux (Ubuntu 24.04, Node v22.22). A standalone harness
writes three real child transcripts as on-disk
.jsonlfiles in OpenClaw's transcriptformat, then drives the shipping reconciliation and output code against them: the real
resolveCompletionFromSessionEntry, the real transcript reader(
readSessionMessagesAsync), and the real output predicates. The child's in-memory runcontext is genuinely absent and the session entry status genuinely lags as
running, soresolveCompletionFromSessionEntryreturnsnulland the lost-context branch isexercised exactly as in the report. Each predicate decision is computed by the real
shipping code over the real parsed transcript; the prior approaches (#90492's
readSubagentOutputand #91400'shasVisibleAssistantOutput) are run side by side overthe same files for comparison.
Exact steps or command run after this patch:
child-terminal.jsonl(final assistantresult),
child-tool-only.jsonl(tool call, no visible result),child-yield.jsonl(visible progress text followed by a
sessions_yieldwait), andchild-pretool.jsonl(visible pre-tool text plus a tool call,
stopReason: "toolUse").node_modules/.bin/tsx oc-proof-90299.ts.Evidence after fix:
Observed result after fix: On the genuine-terminal-result transcript the run settles
as
okwith the captured result instead of the contradictoryfailed: lost active execution context, resolving the reported disagreement. On the tool-call-only,text-then-
sessions_yield, and pre-tool-toolUsetranscripts the run correctly stays onthe lost-context error path. Across the four real transcripts this is the only predicate
correct on all of them: the original main reports
failedeven with a delivered result;readSubagentOutputfalsely recovers the tool-call-only and pre-tool-toolUseruns;hasVisibleAssistantOutputfalsely recovers the yield-wait and pre-tool-toolUseruns.What was not tested: A full live Gateway socket round-trip with a real LLM-backed
subagent process that loses context mid-run was not driven. The harness exercises the real
reconciliation, transcript parsing, and predicate code over real on-disk transcripts and a
real lagging session entry, but it synthesizes the lost-context window rather than killing
a live gateway process, and it does not depend on any external model/provider.
Notes
AI-assisted change. Automated coverage added and passing: unit tests for
resolveTerminalChildResultText/readTerminalChildResult(terminal result, tool-only,sessions_yieldwait, text-after-yield,toolUsepre-tool text, trailing tool-only,whitespace, empty) and three sweeper integration tests in
subagent-registry.test.ts(recovers
okwith a genuine result; keeps the lost-context error for tool-call-only andfor a
toolUsepre-tool-text turn). Core and test typechecks are clean.Fixes #90299. Supersedes the inactive #90492 (and the closed #91400), addressing the
predicate-breadth findings ClawSweeper raised on both: tool-call-only histories,
sessions_yieldwaits, andtoolUseturns with visible pre-tool text are not recoveredas success, and a transcript-read failure preserves the lost-context error instead of
aborting the sweep.