TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 10: refactor(gui_2): migrate L7271 render_task_dag_panel cycle_check to Result[T] (Phase 10 site 12)

Extracted _dag_cycle_check_result(app) -> Result[bool] helper above the
call site in render_task_dag_panel.
ANTI-SLIMING: full Result[T] propagation (NO except+pass). The helper
returns Result(data=has_cycle) on success (True/False) or
Result(data=False, errors=[ErrorInfo]) on exception (logging NOT a drain
per the user's principle 2026-06-17).

The legacy render_task_dag_panel code preserves its signature, calls the
helper, opens the 'Cycle Detected!' popup only when the helper returns
Result(data=True), and drains errors to app._last_request_errors.

Tests: 3 new tests verify no-cycle, cycle-detected, and RuntimeError paths.

Audit: L7271 reclassified from INTERNAL_SILENT_SWALLOW (1 site remaining,
was 2). New helper L7271 is INTERNAL_COMPLIANT.
This commit is contained in:
ed
2026-06-20 01:01:40 -04:00
parent e2d2105b16
commit b4a6ebc101
2 changed files with 97 additions and 9 deletions
+65
View File
@@ -2202,4 +2202,69 @@ def test_phase_10_l6908_tier_stream_scroll_sync_result_failure():
assert "missing key" in err.message
def test_phase_10_l7271_dag_cycle_check_result_no_cycle():
"""
L7271 _dag_cycle_check_result returns Result(data=False) when no cycle is found.
The helper extracts the TrackDAG() cycle check try/except from
render_task_dag_panel into a Result-returning helper. On a valid DAG
(no cycle), returns Result(data=False). The caller continues without
opening the "Cycle Detected!" popup.
"""
from unittest.mock import MagicMock, patch
import src.gui_2 as gui2_mod
app = MagicMock()
app.active_tickets = [{"id": "T-001", "depends_on": []}]
mock_dag = MagicMock()
mock_dag.has_cycle.return_value = False
with patch("src.dag_engine.TrackDAG", return_value=mock_dag):
result = gui2_mod._dag_cycle_check_result(app)
assert result.ok, f"Expected ok=True on success, got errors: {result.errors}"
assert result.data is False
def test_phase_10_l7271_dag_cycle_check_result_cycle_detected():
"""
L7271 _dag_cycle_check_result returns Result(data=True) when a cycle IS found.
The helper detects cycles via TrackDAG.has_cycle(). On a cyclic DAG,
returns Result(data=True). The caller opens the "Cycle Detected!" popup.
"""
from unittest.mock import MagicMock, patch
import src.gui_2 as gui2_mod
app = MagicMock()
app.active_tickets = [
{"id": "T-001", "depends_on": ["T-002"]},
{"id": "T-002", "depends_on": ["T-001"]},
]
mock_dag = MagicMock()
mock_dag.has_cycle.return_value = True
with patch("src.dag_engine.TrackDAG", return_value=mock_dag):
result = gui2_mod._dag_cycle_check_result(app)
assert result.ok, f"Expected ok=True on success, got errors: {result.errors}"
assert result.data is True
def test_phase_10_l7271_dag_cycle_check_result_failure():
"""
L7271 _dag_cycle_check_result returns Result(data=False, errors=[ErrorInfo]) on failure.
When TrackDAG() construction or has_cycle() raises (bad ticket dict,
dag engine error), the helper converts to ErrorInfo. The caller
drains to app._last_request_errors.
"""
from unittest.mock import MagicMock, patch
import src.gui_2 as gui2_mod
app = MagicMock()
app.active_tickets = []
with patch("src.dag_engine.TrackDAG", side_effect=RuntimeError("dag engine failure")):
result = gui2_mod._dag_cycle_check_result(app)
assert not result.ok, f"Expected ok=False on failure, got data: {result.data}"
assert result.data is False
assert result.errors, "Expected at least one error on failure"
err = result.errors[0]
assert err.source == "gui_2._dag_cycle_check_result"
assert "dag engine failure" in err.message