Description
When the engine completes all checklist items via checklist_write, the main chat transcript correctly shows "Todo list updated (X items, 100% complete)", but the sidebar "Work" panel shows stale data or "Work state updating..." even after the turn has finished.
The root cause is a synchronization mismatch between the two sidebar panels:
- Tasks panel: reads directly from
App struct fields (app.task_panel, app.runtime_turn_id, etc.) — all in the same thread, no lock needed. ✅ Always fresh.
- Work panel: reads checklist data from
app.todos which is an Arc<tokio::sync::Mutex<TodoList>> shared with the engine's tokio task. Uses try_lock() — a non-blocking attempt that silently fails when the lock is held, returning state_updating = true and discarding all cached data. ❌ Fragile.
Relevant code
crates/tui/src/tui/sidebar.rs line 242: match app.todos.try_lock()
crates/tui/src/tools/todo.rs line 4: use tokio::sync::Mutex;
Proposed fix
Cache the last successful SidebarWorkSummary in App. When try_lock() succeeds, update the cache. When it fails, return the cached snapshot instead of resetting to "Work state updating...". This way a transient lock miss won't wipe the panel.
Description
When the engine completes all checklist items via
checklist_write, the main chat transcript correctly shows "Todo list updated (X items, 100% complete)", but the sidebar "Work" panel shows stale data or "Work state updating..." even after the turn has finished.The root cause is a synchronization mismatch between the two sidebar panels:
Appstruct fields (app.task_panel,app.runtime_turn_id, etc.) — all in the same thread, no lock needed. ✅ Always fresh.app.todoswhich is anArc<tokio::sync::Mutex<TodoList>>shared with the engine's tokio task. Usestry_lock()— a non-blocking attempt that silently fails when the lock is held, returningstate_updating = trueand discarding all cached data. ❌ Fragile.Relevant code
crates/tui/src/tui/sidebar.rsline 242:match app.todos.try_lock()crates/tui/src/tools/todo.rsline 4:use tokio::sync::Mutex;Proposed fix
Cache the last successful
SidebarWorkSummaryinApp. Whentry_lock()succeeds, update the cache. When it fails, return the cached snapshot instead of resetting to "Work state updating...". This way a transient lock miss won't wipe the panel.