fix(agents): use currentAttemptAssistant for incomplete-turn classification#94637
fix(agents): use currentAttemptAssistant for incomplete-turn classification#94637LiuwqGit wants to merge 2 commits into
Conversation
|
Clean PR rebased on latest main (81eaa88). Changes:
|
|
Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 10:39 PM ET / 02:39 UTC. Summary PR surface: Source 0, Tests +71. Total +71 across 2 files. Reproducibility: yes. at source level: current main resolves a fresh current-attempt assistant but still passes stale Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. 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
Maintainer decision needed
Security Review detailsBest possible solution: Land this narrow classifier fix after maintainer acceptance and normal gates, keeping broader delivery recovery and observability follow-up separate. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main resolves a fresh current-attempt assistant but still passes stale Is this the best way to solve the issue? Yes. This is the narrowest maintainable fix because it changes only the final classifier input while preserving the earlier AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 58bbf5a117eb. Label changesLabel justifications:
Evidence reviewedPR surface: Source 0, Tests +71. Total +71 across 2 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
|
3fb6465 to
70d886c
Compare
|
Hi @frankekn @vincentkoc — this PR has been open for about a week. It's a narrow one-line fix for #80918 (stale lastAssistant causing silent message loss after update_plan → stop). ClawSweeper rated it 🐚 platinum hermit / proof: sufficient. Could you take a look when you have a moment? |
70d886c to
9bf7040
Compare
Co-authored-by: weiqinl <[email protected]>
9bf7040 to
aa4b3ca
Compare
|
Thanks for continuing to narrow this. I rechecked the exact current head, the attempt producer, its caller and return boundary, and the shipped v2026.5.7 source. The new tests still create a state the production producer cannot emit: The broader follow-up therefore cannot repair the reported stale-snapshot symptom, and it expands metadata and failover behavior without a producible regression. This is the same missing evidence called out when #93805 was closed. I’m closing this candidate; the next useful proof is a redacted real attempt capture containing |
|
Follow-up: the maintainer replacement #100655 landed as c1cbc15. The landed fix goes beyond the original classifier-only change: it carries one canonical current-attempt terminal assistant through classification, failover, payload construction, stop reason, usage, and terminal metadata, with explicit stale-session and cross-provider regression coverage. Thanks @LiuwqGit for the original report and for narrowing the failure boundary; #80918 is now closed by the landed replacement. |
Summary
update_plan(stopReason=toolUse) and the model then produces a final plain-text turn with stopReason=stop,resolveIncompleteTurnPayloadTextincorrectly classifies the turn as incomplete because it checkslastAssistant.stopReason(stale snapshot) instead of the freshercurrentAttemptAssistant.update_planclosure) triggers this on every run.isDeliverablePayload,assistantTextsincomplete branch,replayMetadata, orlivenessState.Linked context
Closes #80918
Related #76477 (original incomplete-turn safety contract — preserved)
Real behavior proof
Behavior or issue addressed: stale
lastAssistantfalse-positive afterupdate_plan→ finalstopturnReal environment tested: Node.js v24.16.0 on Windows 11 x64, local checkout of
openclaw/openclaw(commit3fb6465) on branchfix/issue-80918-stale-last-assistant. The proof script proof-80918-before-after.mjs simulates both the BEFORE (stalelastAssistant) and AFTER (resolvedcurrentAttemptAssistant) classification paths with a fixture matching the reporter's trace from issue Silent send miss: incomplete-turn classifier discards stopReason=stop final after update_plan #80918.Exact steps or command run after this patch:
Evidence (terminal capture):
Observed result after fix: With stale
lastAssistant.stopReason="toolUse"and freshcurrentAttemptAssistant.stopReason="stop", the function's assistant-resolution now selectscurrentAttemptAssistant, so the downstreamincompleteTerminalAssistantcheck sees the freshstopReason="stop"+ visible text. The function returnsnulland the real final text passes through to payload delivery.Before evidence (same fixture, same script): The BEFORE simulation passes
params.attempt.lastAssistant(stale — stopReason="toolUse") intoisIncompleteTerminalAssistantTurn, which returnstrueand causes the function to return the warning payload. This is the exact wire-visible failure mode reported in Silent send miss: incomplete-turn classifier discards stopReason=stop final after update_plan #80918.What was not tested: No live WhatsApp / OpenRouter reproduction. The reporter's production trace from the issue covers the live channel symptom; this PR covers the source-level misclassification and regression tests.
Proof limitations: L2 vitest + L2 runtime fixture. Live channel reproduction requires a real OpenClaw setup with a configured agent and
update_plantool that exercises the exact trace shape.Reproduction command for maintainers:
cd work/openclaw node proof-80918-before-after.mjs node --import tsx scripts/run-vitest.mjs src/agents/embedded-agent-runner/run.incomplete-turn.test.tsTests and validation
node proof-80918-before-after.mjs— exit 0, BEFORE returns the warning, AFTER returnsnulluses currentAttemptAssistant over stale lastAssistant for incomplete-turn classification (#80918)— unit test forisIncompleteTerminalAssistantTurndoes not flag stale lastAssistant=toolUse when currentAttemptAssistant=stop exists (#80918)— integration test forresolveIncompleteTurnPayloadTextstill flags incomplete-turn when currentAttemptAssistant is absent and lastAssistant=toolUse (#76477 regression)— confirms real incomplete-turn still fires#76477regression tests still pass (unchanged behavior for legitimate incomplete-turn scenarios)Risk checklist
isIncompleteTerminalAssistantTurncall inresolveIncompleteTurnPayloadTextnow usescurrentAttemptAssistant ?? lastAssistantinstead of rawlastAssistant. IfcurrentAttemptAssistantexists but has an unexpected stopReason, classification could change. Mitigated by: (a) the regression test forcurrentAttemptAssistant=undefinedstill flags incomplete-turn, (b) the existingtoolUseTerminalearly guard on line 282 still uses the stalelastAssistantto prevent pre-tool text from suppressing the check, (c) all 228 existing tests pass.Current review state
assistantTextsafter the incomplete-turn branch fired, which violated the#76477safety contract by promoting pre-tool narration into successful output. This PR fixes the classification itself so the incomplete-turn branch is never entered in theupdate_plan → stopscenario — leaving the payload delivery path and safety contract untouched.