Private
Public Access
0
0

TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 10: refactor(gui_2): migrate L1693 render_main_interface autosave to Result[T] (Phase 10 site 9)

Extracted _autosave_flush_result(app) -> Result[None] helper above the
call site in render_main_interface.
ANTI-SLIMING: full Result[T] propagation (NO except+pass with comment).
The helper returns Result(data=None) on success or Result(data=None,
errors=[ErrorInfo]) on exception (logging NOT a drain per the user's
principle 2026-06-17). The 'don't disrupt the GUI loop' intent is
preserved via the data plane (app._last_request_errors) rather than
silent swallow.

The legacy render_main_interface code preserves its behavior, calls the
helper, and drains errors to app._last_request_errors.

Tests: 2 new tests verify both paths (success and OSError).

Audit: L1693 reclassified from INTERNAL_SILENT_SWALLOW (5 sites remaining,
was 6). New helper L1693 is INTERNAL_COMPLIANT.
This commit is contained in:
2026-06-20 00:56:58 -04:00
parent 9188e548ff
commit 1e5a742813
2 changed files with 73 additions and 6 deletions
+30 -6
View File
@@ -1690,12 +1690,36 @@ def _focus_response_window_result() -> Result[None]:
now = time.time()
if now - app._last_autosave >= app._autosave_interval:
app._last_autosave = now
try:
app._flush_to_project()
app._flush_to_config()
app.save_config()
except Exception:
pass # silent — don't disrupt the GUI loop
autosave_result = _autosave_flush_result(app)
if not autosave_result.ok:
if not hasattr(app, '_last_request_errors'): app._last_request_errors = []
app._last_request_errors.append(("render_main_interface.autosave", autosave_result.errors[0]))
def _autosave_flush_result(app: "App") -> Result[None]:
"""Drain-aware variant of render_main_interface auto-save try block (L1693 INTERNAL_SILENT_SWALLOW).
Extracts the auto-save flush_to_project + flush_to_config + save_config
try/except from render_main_interface into a Result-returning helper. On
exception (disk full, JSON parse error), converts to ErrorInfo (logging
NOT a drain per the user's principle 2026-06-17). The caller drains to
app._last_request_errors and continues with the GUI loop, preserving
the original "don't disrupt the GUI loop" intent via the data plane
rather than silent swallow.
[C: src/gui_2.py:render_main_interface (L1693 legacy wrapper)]
"""
try:
app._flush_to_project()
app._flush_to_config()
app.save_config()
return Result(data=None)
except Exception as e:
return Result(data=None, errors=[ErrorInfo(
kind=ErrorKind.INTERNAL,
message=f"autosave flush failed: {e}",
source="gui_2._autosave_flush_result",
original=e,
)])
# Sync pending comms
with app._pending_comms_lock:
+43
View File
@@ -2068,4 +2068,47 @@ def test_phase_10_l1647_focus_response_window_result_failure():
assert "IM_ASSERT" in err.message
def test_phase_10_l1693_autosave_flush_result_success():
"""
L1693 _autosave_flush_result returns Result(data=None) on success.
The helper extracts the auto-save flush_to_project + flush_to_config +
save_config try/except from render_main_interface into a Result-returning
helper. On success, returns Result(data=None). The legacy wrapper
continues with the GUI loop.
"""
from unittest.mock import MagicMock
import src.gui_2 as gui2_mod
app = MagicMock()
result = gui2_mod._autosave_flush_result(app)
assert result.ok, f"Expected ok=True on success, got errors: {result.errors}"
assert result.data is None
app._flush_to_project.assert_called_once()
app._flush_to_config.assert_called_once()
app.save_config.assert_called_once()
def test_phase_10_l1693_autosave_flush_result_failure():
"""
L1693 _autosave_flush_result returns Result(data=None, errors=[ErrorInfo]) on failure.
When any of _flush_to_project/_flush_to_config/save_config raises
(disk full, JSON parse error), the helper converts to ErrorInfo. The
caller (render_main_interface) drains to self._last_request_errors and
continues with the GUI loop (preserving the original "don't disrupt the
GUI loop" intent via the data plane rather than silent swallow).
"""
from unittest.mock import MagicMock
import src.gui_2 as gui2_mod
app = MagicMock()
app._flush_to_project.side_effect = OSError("disk full")
result = gui2_mod._autosave_flush_result(app)
assert not result.ok, f"Expected ok=False on failure, got data: {result.data}"
assert result.data is None
assert result.errors, "Expected at least one error on failure"
err = result.errors[0]
assert err.source == "gui_2._autosave_flush_result"
assert "disk full" in err.message