From 5b341038a7e632e494ebcceb93e0662f86d54379 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 19 Jun 2026 23:54:02 -0400 Subject: [PATCH] TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 5: refactor(gui_2): migrate L3163 render_tool_preset_manager_content bias_save to Result[T] (Phase 5) Extract _render_tool_preset_bias_save_result helper from the BiasProfile save try/except in render_tool_preset_manager_content. The imgui.button callback drains errors to app._last_request_errors per FR-BC-4 event-handler pattern. [pre-audit] L3163 INTERNAL_BROAD_CATCH [post-audit] V count: 9 -> 8 (L3163 removed) --- src/gui_2.py | 44 ++++++++++++++++++++++++++++++--- tests/test_gui_2_result.py | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index d1f6c4cc..4505dede 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -3116,10 +3116,10 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N imgui.end_child() if imgui.button("Save Profile", imgui.ImVec2(-1, 0)): - try: - p = models.BiasProfile(name=app._editing_bias_profile_name, tool_weights=app._editing_bias_profile_tool_weights, category_multipliers=app._editing_bias_profile_category_multipliers) - app.controller._cb_save_bias_profile(p, app._editing_tool_preset_scope); app.ai_status = f"Saved: {p.name}" - except Exception as e: app.ai_status = f"Error: {e}" + bias_save_result = _render_tool_preset_bias_save_result(app) + if not bias_save_result.ok: + if not hasattr(app, '_last_request_errors'): app._last_request_errors = [] + app._last_request_errors.append(("_render_tool_preset_bias_save", bias_save_result.errors[0])) imgui.end_child() imgui.end_table() imgui.end_child() @@ -7895,6 +7895,42 @@ def request_patch_from_tier4_result(app: "App", error: str, file_context: str) - original=e, )]) + +def _render_tool_preset_bias_save_result(app: "App") -> Result[bool]: + """Drain-aware variant of L3163 render_tool_preset_manager_content Save Profile button. + + Extracts the BiasProfile save try/except from + render_tool_preset_manager_content into a Result-returning helper. On + success (BiasProfile construction + _cb_save_bias_profile), sets + app.ai_status to "Saved: " and returns Result(data=True). On + failure (BiasProfile construction or _cb_save_bias_profile raises), sets + app.ai_status to an error message and returns + Result(data=False, errors=[ErrorInfo]). + + The legacy wrapper (the imgui.button callback) drains errors to + app._last_request_errors (per FR-BC-4 event-handler drain pattern; + data plane attribute). + + [C: src/gui_2.py:render_tool_preset_manager_content (L3163 legacy wrapper)] + """ + try: + p = models.BiasProfile( + name=app._editing_bias_profile_name, + tool_weights=app._editing_bias_profile_tool_weights, + category_multipliers=app._editing_bias_profile_category_multipliers, + ) + app.controller._cb_save_bias_profile(p, app._editing_tool_preset_scope) + app.ai_status = f"Saved: {p.name}" + return Result(data=True) + except Exception as e: + app.ai_status = f"Error: {e}" + return Result(data=False, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"Bias profile save failed: {e}", + source="gui_2._render_tool_preset_bias_save_result", + original=e, + )]) + #endregion: Phase 5 Event Handler Result Helpers #endregion: MMA diff --git a/tests/test_gui_2_result.py b/tests/test_gui_2_result.py index 17f98d4f..a73abd33 100644 --- a/tests/test_gui_2_result.py +++ b/tests/test_gui_2_result.py @@ -1018,3 +1018,53 @@ def test_phase_5_l1428_request_patch_from_tier4_result_failure(): err = result.errors[0] assert err.source == "gui_2.request_patch_from_tier4_result" assert "tier4 backend down" in err.message + + +def test_phase_5_l3163_render_tool_preset_bias_save_result_success(): + """ + L3163 _render_tool_preset_bias_save_result returns Result.ok=True on success. + + The helper wraps the BiasProfile save try/except in + render_tool_preset_manager_content. On success (BiasProfile construction + + _cb_save_bias_profile), sets app.ai_status to "Saved: " and returns + Result(data=True). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + app._editing_bias_profile_name = "test_bias" + app._editing_bias_profile_tool_weights = {"foo": 2} + app._editing_bias_profile_category_multipliers = {"bar": 1.5} + app._editing_tool_preset_scope = "project" + mock_profile = MagicMock() + mock_profile.name = "test_bias" + with patch.object(gui_2.models, "BiasProfile", return_value=mock_profile): + result = gui_2._render_tool_preset_bias_save_result(app) + assert result.ok, f"Expected ok=True on success, got errors: {result.errors}" + assert result.data is True + assert "Saved: test_bias" in app.ai_status + + +def test_phase_5_l3163_render_tool_preset_bias_save_result_failure(): + """ + L3163 _render_tool_preset_bias_save_result returns Result.ok=False on failure. + + When BiasProfile construction or _cb_save_bias_profile raises, the helper + sets app.ai_status to an error message and returns + Result(data=False, errors=[ErrorInfo]). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + app._editing_bias_profile_name = "test_bias" + app._editing_bias_profile_tool_weights = {"foo": 2} + app._editing_bias_profile_category_multipliers = {"bar": 1.5} + app._editing_tool_preset_scope = "project" + with patch.object(gui_2.models, "BiasProfile", side_effect=RuntimeError("bias validation failed")): + result = gui_2._render_tool_preset_bias_save_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_tool_preset_bias_save_result" + assert "bias validation failed" in err.message + assert "Error:" in app.ai_status