Skip to content

Web UI: todos panel stuck on stale snapshot after mid-turn reconnect #68

@alex-fedotyev

Description

@alex-fedotyev

Symptom

The todos panel in the chat UI freezes on a stale snapshot when the browser reconnects in the middle of an active turn (page refresh, WS drop, tab backgrounding for long enough to trigger a reconnect). New TodoWrite calls keep landing in the WS buffer and on the persisted message history, but the panel keeps showing the count from the last TodoWrite that was persisted before the reconnect.

I hit this on a session that ran roughly 10 TodoWrite calls across two turns. The panel showed 4/9 with one task "in progress" even though the latest persisted TodoWrite was 13/13. The mismatch persisted across the rest of the session.

Root cause

handleSessionStatus in web/src/stores/handlers/sessionHandlers.ts rebuilds streamingBlocks from msg.buffered_events via applyStreamEvent, but it never derives currentTodos from those events. The only paths that update currentTodos are:

  1. The live handleToolUse handler (works while the WS is live, not during replay).
  2. extractTodosFromMessages(hydrated) on switchSession (only sees persisted DB messages).

So if a TodoWrite arrives in the WS buffer for an in-flight turn that hasn't been persisted yet, the buffer replay populates streamingBlocks correctly but currentTodos is left as whatever the last persisted history had.

Reproduction

  1. Start a session that issues many TodoWrite calls in a single turn.
  2. Refresh the browser tab while the turn is still streaming.
  3. After reconnect, the todos panel sticks at the last persisted TodoWrite snapshot. Live updates resume eventually but the buffered window between persistence and reconnect is lost from the panel.

Proposed fix

Add an extractTodosFromBuffer(events) helper next to extractTodosFromMessages and call it from handleSessionStatus after rebuilding blocks. Return null when no top-level TodoWrite is in the buffer so the caller preserves the existing value from persisted history. Do not skip the all-completed case (unlike extractTodosFromMessages); the panel's existing 5s auto-hide handles that animation.

Patch coming as a PR linked to this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions