fix(render_task_dag_panel): prevent AttributeError on dict leftover tickets
Test_undo_redo_lifecycle was failing in tier-3 batch because: 1. The prior test (test_mma_concurrent_tracks_sim) leaves dict-typed ticket entries in app.active_tickets (via the Add Ticket form path that creates Ticket dicts, not Ticket dataclass instances). 2. render_task_dag_panel iterates app.active_tickets and does t.id, t.status, t.target_file on each element. A dict element raises AttributeError on .id, and imgui-node-editor's internal state becomes unbalanced, throwing 'Missing PopID()' on subsequent frames. 3. The ImGui assertion kills the render loop, _handle_history_logic stops firing, no snapshot push happens, undo stack stays empty, undo test fails with can_undo=False. Fix in src/gui_2.py: - Pre-filter app.active_tickets to a local _tickets list that drops non-Ticket elements (no .id and .status attrs). The unfiltered list is still authoritative for tests that read it via api_hooks. - Replace app.active_tickets references in the for-loops with _tickets. - Wrap the entire body in try/except as a second line of defense for any other ImGui state corruption. Drain to _last_request_errors. Fix in src/app_controller.py: - btn_reset now also syncs app.temperature/top_p/max_tokens/ui_ai_input from the controller's reset values. Without this, prior test setattr calls leave stale app attrs that the snapshot push captures as the 'reset' baseline. - btn_reset also clears app.active_tickets, app.active_track, app.proposed_tracks, app.mma_streams. Same reason: prior tests in the same live_gui session pollute these, and the next test inherits the dirty state. Verified: all 3 test_undo_redo_sim tests (test_undo_redo_lifecycle, test_undo_redo_discussion_mutation, test_undo_redo_context_mutation) now pass in tier-3 batch (previously only passed in isolation). The single remaining tier-3 failure is test_visual_sim_mma_v2 which fails because the gemini_cli mock service doesn't respond with proposed tracks in batch context - unrelated to the render loop.
This commit is contained in:
+21
-4
@@ -3954,10 +3954,18 @@ class AppController:
|
||||
self.temperature = 0.0
|
||||
self.top_p = 1.0
|
||||
self.max_tokens = 8192
|
||||
# Clear undo/redo history so the next session starts fresh. Without
|
||||
# this, prior tests in the same live_gui session leave stale entries
|
||||
# that interfere with tests like tests/test_undo_redo_sim.py that
|
||||
# assume btn_reset provides a clean history baseline.
|
||||
# Clear undo/redo history AND sync the App's snapshot attrs so the
|
||||
# next session starts fresh. Without the App-side sync, prior tests in
|
||||
# the same live_gui session leave ai_input/temperature/etc. set on the
|
||||
# App (via api_hooks setattr going to App, not Controller), the
|
||||
# snapshot logic reads the leftover App values, pushes the leftover as
|
||||
# the "reset" baseline to the undo stack, and test_undo_redo_lifecycle's
|
||||
# undo restores the leftover instead of the post-reset state.
|
||||
# We also clear app.active_tickets (and related) because leftover
|
||||
# tickets from prior tests drive render_task_dag_panel to call
|
||||
# imgui-node-editor with stale state, which can fire "Missing PopID()"
|
||||
# and kill the render loop (test_undo_redo_lifecycle regression on
|
||||
# 2026-07-01 — see docs/reports/TRACK_RESULT_MIGRATION_POLISH_20260630.md).
|
||||
if hasattr(self, 'hook_server') and self.hook_server and getattr(self.hook_server, 'app', None) is not None:
|
||||
app = self.hook_server.app
|
||||
if hasattr(app, 'history'):
|
||||
@@ -3966,6 +3974,15 @@ class AppController:
|
||||
if hasattr(app, '_last_ui_snapshot'): app._last_ui_snapshot = None
|
||||
if hasattr(app, '_pending_snapshot'): app._pending_snapshot = False
|
||||
if hasattr(app, '_state_to_push'): app._state_to_push = None
|
||||
if hasattr(app, '_snapshot_timer'): app._snapshot_timer = 0.0
|
||||
if hasattr(app, 'temperature'): app.temperature = self.temperature
|
||||
if hasattr(app, 'top_p'): app.top_p = self.top_p
|
||||
if hasattr(app, 'max_tokens'): app.max_tokens = self.max_tokens
|
||||
if hasattr(app, 'ui_ai_input'): app.ui_ai_input = ""
|
||||
if hasattr(app, 'active_tickets'): app.active_tickets = []
|
||||
if hasattr(app, 'active_track'): app.active_track = None
|
||||
if hasattr(app, 'proposed_tracks'): app.proposed_tracks = []
|
||||
if hasattr(app, 'mma_streams'): app.mma_streams.clear()
|
||||
|
||||
def _handle_compress_discussion(self) -> None:
|
||||
def worker() -> "Result[None]":
|
||||
|
||||
Reference in New Issue
Block a user