From 90b20879d269283f454956e2158bde5c6884cd36 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 19 Jun 2026 16:01:58 -0400 Subject: [PATCH] refactor(app_controller): migrate _cb_run_conductor_setup + _cb_load_track to Result (Phase 6 Groups 6.5+6.7 partial) Migrates the 2 remaining _cb_* sites with proper Result[T] propagation: - _cb_run_conductor_setup: per-file read via _read_conductor_file_result - _cb_load_track: state hydration via _cb_load_track_result New helpers: - _read_conductor_file_result(f) -> Result[int] - _cb_load_track_result(state, track_id) -> Result[None] Audit: INTERNAL_SILENT_SWALLOW for src/app_controller.py: 12 -> 10. --- src/app_controller.py | 98 ++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index a3ceeebe..f709d3b0 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -4749,6 +4749,22 @@ class AppController: engine.engine = ExecutionEngine(engine.dag, auto_queue=engine.engine.auto_queue) self._push_mma_state_update() + def _read_conductor_file_result(self, f: Path) -> "Result[int]": + """Phase 6 Group 6.7: read a conductor file and return line count. + On failure: OSError/IOError/UnicodeDecodeError -> ErrorInfo(original=e). + Caller (`_cb_run_conductor_setup`) records per-file errors and + adds an 'Error reading' line to the summary.""" + try: + with open(f, "r", encoding="utf-8") as fd: + return Result(data=len(fd.readlines())) + except (OSError, IOError, UnicodeDecodeError) as e: + return Result(data=0, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source=f"app_controller._read_conductor_file_result[{f.name}]", + original=e, + )]) + def _cb_run_conductor_setup(self) -> None: """ [C: src/gui_2.py:App._render_mma_conductor_setup, tests/test_gui_phase3.py:test_conductor_setup_scan] @@ -4763,13 +4779,12 @@ class AppController: summary.append(f"Total Files: {len(files)}") total_lines = 0 for f in files: - try: - with open(f, "r", encoding="utf-8") as fd: - lines = len(fd.readlines()) - total_lines += lines - summary.append(f"- {f.relative_to(base)}: {lines} lines") - except (OSError, IOError, UnicodeDecodeError) as e: - logging.getLogger(__name__).debug("conductor file read failed for %s: %s", f, e, extra={"source": "app_controller._cb_run_conductor_setup"}) + result = self._read_conductor_file_result(f) + if result.ok: + total_lines += result.data + summary.append(f"- {f.relative_to(base)}: {result.data} lines") + else: + self._last_request_errors.append((f"conductor_file_read[{f.name}]", result.errors[0])) summary.append(f"- {f.relative_to(base)}: Error reading") summary.append(f"Total Line Count: {total_lines}") tracks_dir = base / "tracks" @@ -4842,34 +4857,49 @@ class AppController: """ state = project_manager.load_track_state(track_id, self.active_project_root) if state: - try: + result = self._cb_load_track_result(state, track_id) + if not result.ok: + err = result.errors[0] + self.ai_status = f"Load track error: {err.message}" + print(f"Error loading track {track_id}: {err.message}") + self._report_worker_error("cb_load_track", result) + + def _cb_load_track_result(self, state, track_id: str) -> "Result[None]": + """Phase 6 Group 6.7: load a track with Result propagation. + On failure: OSError/IOError/ValueError/TypeError/KeyError/AttributeError/tomllib.TOMLDecodeError + -> ErrorInfo(original=e). Caller drains via stderr print + ai_status.""" + try: # Convert list[Ticket] or list[dict] to list[Ticket] for Track object - tickets = [] - for t in state.tasks: - if isinstance(t, dict): - tickets.append(models.Ticket(**t)) - else: - tickets.append(t) - self.active_track = models.Track( - id=state.metadata.id, - description=state.metadata.name, - tickets=tickets - ) - # Keep dicts for UI table - self._load_active_tickets() - # Load track-scoped history - history = project_manager.load_track_history(track_id, self.active_project_root) - with self._disc_entries_lock: - if history: - self.disc_entries[:] = models.parse_history_entries(history, self.disc_roles) - else: - self.disc_entries.clear() - self._recalculate_session_usage() - self.ai_status = f"Loaded track: {state.metadata.name}" - except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) as e: - logging.getLogger(__name__).debug("load track failed: %s", e, extra={"source": "app_controller._cb_load_track"}) - self.ai_status = f"Load track error: {e}" - print(f"Error loading track {track_id}: {e}") + tickets = [] + for t in state.tasks: + if isinstance(t, dict): + tickets.append(models.Ticket(**t)) + else: + tickets.append(t) + self.active_track = models.Track( + id=state.metadata.id, + description=state.metadata.name, + tickets=tickets + ) + # Keep dicts for UI table + self._load_active_tickets() + # Load track-scoped history + history = project_manager.load_track_history(track_id, self.active_project_root) + with self._disc_entries_lock: + if history: + self.disc_entries[:] = models.parse_history_entries(history, self.disc_roles) + else: + self.disc_entries.clear() + self._recalculate_session_usage() + self.ai_status = f"Loaded track: {state.metadata.name}" + return OK + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) as e: + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._cb_load_track_result", + original=e, + )]) def _push_mma_state_update(self) -> None: """