emrg: fix task_vibe_check LLM 400 from window-boundary orphan tool messages - #874
Conversation
…ssages Slicing a validated session history to the last 100 messages can orphan a leading role:'tool' message whose matching assistant(tool_calls) lies before the window, so the LLM rejects every task_vibe_check with 400 'Messages with role tool must be a response to a preceding message with tool_calls' (48x on aitokenpool-opensource-task, 18x on emrg-task since 2026-08-19T18:51). Add session.last_n_messages() which drops leading window-boundary orphan tool messages, and use it in _task_vibe_check. 6 new tests: 5 unit (test_session.py) + 1 e2e regression (test_ws_e2e.py, 180-message session whose [-100:] window starts at a tool message). Agent.md count 977 -> 983.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (1/3). Verified locally: full suite 982 passed + 1 skipped, import + CLI green, vibe-check e2e 6/6. Root cause reproduced with the real aitokenpool session (172-message validated history whose [-100:] window starts with role:'tool'); last_n_messages drops boundary orphans. Agent.md count synced 977 → 983.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (2/3). Re-verified: head 7742dfa unchanged, MERGEABLE/CLEAN, test + test-windows both PASS (run 32248439433). Root cause + fix reviewed — session.last_n_messages drops window-boundary orphan tool messages so task_vibe_check stops failing with LLM 400 for long sessions. Agent.md count synced.
Uh oh!
There was an error while loading. Please reload this page.
Problem
Every
task_vibe_checkfor long-running scheduled tasks fails with an LLM 400:Observed repeatedly in the daemon log (48x on
aitokenpool-opensource-task, 18x onemrg-tasksince 2026-08-19T18:51). Because the vibe check fails, the scheduler's empty-cycle counter is never updated —recommend_slowdownnever engages.Root cause
_task_vibe_checkloads the task's own session history (rant 2026-08-19T10:15:43) and slices it to the last 100 messages (history[-100:]). Slicing a validated message list can orphan a leadingrole: "tool"message: its matchingassistantmessage withtool_callslies just before the window boundary. The API then rejects the request because a tool message must follow an assistant-with-tool_calls.Reproduced with real data: the aitokenpool task session has 172 validated messages ending in
assistant; the[-100:]window starts withrole: "tool".Fix
session.last_n_messages(messages, n)— slices the lastnmessages and drops leading window-boundary orphanrole: "tool"messages (the remaining window keeps its original order)._task_vibe_checkinstead ofhistory[-100:].tests/test_session.py(including a reproduction of the real 172-message session) + 1 e2e regression intests/test_ws_e2e.py(a 180-message session whose 100-window starts at a tool message).Agent.mddocumented Python count synced 977 → 983.Verification