From 0dacbfce623da1d2ccf4deece0e102cf479ab6ef Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 19 Jun 2026 22:22:21 -0400 Subject: [PATCH] refactor(gui_2): migrate L4848 render_warmup_status_indicator to Result[T] (Phase 3) TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 3. Adds _render_warmup_status_indicator_result(app) -> Result[dict] helper that wraps the controller.warmup_status() try/except in render_warmup_status_indicator. The data field carries the status dict so the legacy wrapper can use it for rendering without an additional instance attribute. render_warmup_status_indicator becomes a thin wrapper that drains errors to app.controller._worker_errors under the controller's lock (worker error plane; thread-safe per app_controller pattern). Audit: BROAD_CATCH count 18 -> 17, COMPLIANT count 19 -> 20. Migration target count drops from 42 to 34 (8 sites migrated). Tests: 2/2 pass. --- src/gui_2.py | 35 +++++++++++++++++++++++++++++++--- tests/test_gui_2_result.py | 39 +++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index 0d847ec3..faa32b35 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -4793,9 +4793,13 @@ def render_warmup_status_indicator(app: App) -> None: controller = getattr(app, "controller", None) if controller is None: return if not hasattr(controller, "warmup_status"): return - try: - status = controller.warmup_status() - except Exception: return + result = _render_warmup_status_indicator_result(app) + if not result.ok: + if not hasattr(controller, "_worker_errors"): controller._worker_errors = [] + with controller._worker_errors_lock: + controller._worker_errors.append(("render_warmup_status_indicator", result.errors[0])) + return + status = result.data pending = status.get("pending", []) completed = status.get("completed", []) failed = status.get("failed", []) @@ -7537,6 +7541,31 @@ def _show_menus_is_max_result(app: "App", hwnd) -> Result[bool]: original=e, )]) +def _render_warmup_status_indicator_result(app: "App") -> Result[dict]: + """Drain-aware variant of L4848 render_warmup_status_indicator warmup_status try/except. + + Extracts the controller.warmup_status() try/except from + render_warmup_status_indicator into a Result-returning helper. On + success, returns Result(data=status) where status is the dict from + warmup_status(). On failure, returns Result(data={}, errors=[ErrorInfo]). + + The data field is the status dict so the legacy wrapper can use it + for rendering without an additional instance attribute. Errors drain + to app.controller._worker_errors (worker error plane, lock-protected). + + [C: src/gui_2.py:render_warmup_status_indicator (L4848 legacy wrapper)] + """ + try: + status = app.controller.warmup_status() + return Result(data=status) + except Exception as e: + return Result(data={}, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"warmup_status failed: {e}", + source="gui_2._render_warmup_status_indicator_result", + original=e, + )]) + def _handle_history_logic_result(app: "App") -> Result[bool]: """Drain-aware variant of L1284 _handle_history_logic snapshot try/except. diff --git a/tests/test_gui_2_result.py b/tests/test_gui_2_result.py index 16d47743..bdda79e7 100644 --- a/tests/test_gui_2_result.py +++ b/tests/test_gui_2_result.py @@ -508,4 +508,41 @@ def test_phase_3_l1284_handle_history_logic_result_failure(): assert result.errors, "Expected at least one error on failure" err = result.errors[0] assert err.source == "gui_2._handle_history_logic_result" - assert "snapshot failed" in err.message \ No newline at end of file + assert "snapshot failed" in err.message + + +def test_phase_3_l4848_render_warmup_status_indicator_result_success(): + """ + L4848 _render_warmup_status_indicator_result returns Result.ok=True with status dict. + + The helper wraps the controller.warmup_status() try/except in + render_warmup_status_indicator. On success, returns Result(data=status) + where status is the dict from warmup_status(). + """ + from src import gui_2 + from unittest.mock import MagicMock + app = MagicMock() + mock_status = {"pending": [], "completed": ["a"], "failed": []} + app.controller.warmup_status.return_value = mock_status + result = gui_2._render_warmup_status_indicator_result(app) + assert result.ok, f"Expected ok=True on success, got errors: {result.errors}" + assert result.data is mock_status + + +def test_phase_3_l4848_render_warmup_status_indicator_result_failure(): + """ + L4848 _render_warmup_status_indicator_result returns Result.ok=False on failure. + + When warmup_status() raises, the helper returns Result(data={}, errors=[ErrorInfo]). + The legacy wrapper should drain to app.controller._worker_errors (worker error plane). + """ + from src import gui_2 + from unittest.mock import MagicMock + app = MagicMock() + app.controller.warmup_status.side_effect = RuntimeError("warmup backend down") + result = gui_2._render_warmup_status_indicator_result(app) + assert not result.ok, f"Expected ok=False on failure, got data: {result.data}" + assert result.errors, "Expected at least one error on failure" + err = result.errors[0] + assert err.source == "gui_2._render_warmup_status_indicator_result" + assert "warmup backend down" in err.message \ No newline at end of file