fix(tui): prevent orphan log panes and thread-unsafe reviews reads#27
Conversation
The log pane removal in _poll_log popped keys from _log_panes before attempting DOM removal, with the removal wrapped in a bare try/except: pass. If anything threw, the key was already gone from the tracking dict but the widget stayed in the DOM — an invisible orphan that could never be reconciled. Over time this caused more log panels than active reviews. Fix: only delete from _log_panes after the DOM removal succeeds; on failure, fall back to a full container rebuild so orphans are cleared. Also add keys_to_add.discard(key) in the creation loop as a guard against duplicate pane creation for the same key. Additionally, _poll_server now snapshots server.reviews once (via list copy) and passes it to _refresh_reviews_table, _update_title, and _poll_log, preventing the TUI thread from seeing inconsistent state when the server thread mutates the list mid-cycle. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Gate (error) — review failed: Command 'gh' returned non-zero exit status 1.. Auto-approving. |
Co-authored-by: Cursor <cursoragent@cursor.com>
Gate Review ✅Approved with notes — The PR correctly fixes two related TUI bugs: orphan log panes accumulating due to premature Warnings
Notes
Build Results
1 warning, 1 note across 6 stages (547s, confidence: high) |
Replaces logger.debug with logger.exception in the orphan log pane recovery handler so tracebacks are preserved in activity.log when the fallback fires, matching the project Error Handling standard.
Gate Auto-Fix AppliedFixed 1/1 findings in 1 iteration(s) (7898b40). Fixed:
|
Gate Review ✅Approved — Re-review of fix/tui-orphan-log-panes. The single actionable prior warning (0168a408a5 — Notes
Resolved since last review
Build Results
1 note across 4 stages (194s, confidence: high) |
Summary
_poll_logpopped keys from_log_panesbefore attempting DOM removal, wrapped in a baretry/except: pass. If removal threw, the widget stayed in the DOM as an invisible orphan — never reconciled, accumulating over time. This caused more log panels than active reviews (e.g. 4 panels with 2 reviews). Now the key is only deleted after successful removal, and on failure a full container rebuild clears all orphans._poll_servernow copiesserver.reviewsonce and passes the snapshot to_refresh_reviews_table,_update_title, and_poll_log, preventing the TUI thread from seeing inconsistent state when the server thread mutates the list between calls.keys_to_add.discard(key)in the pane creation loop so the same key can never produce two mounts in a single pass.Test plan
pytest tests/test_tui.py tests/test_tui_unit.py— 75 tests passMade with Cursor