From ae62a3f5d1e39600d7eff3e6b00e4e9c5712f56f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 18 Jun 2026 19:58:06 -0400 Subject: [PATCH] refactor(app_controller): migrate 7 conductor/track sites to Result (batch 3) Migrated 7 INTERNAL_BROAD_CATCH sites in src/app_controller.py: 1. _do_project_switch load (L2813) - project_manager.load_project - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) - Returns Result[None] with errors on failure - Preserves the _project_switch_error state 2. _do_project_switch managers (L2825) - manager initialization - Same exception narrowing - Returns Result[None] with errors - Preserves the _project_switch_error state 3. _start_track_logic (L4304) - track creation + engine spawn - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, RuntimeError) - logging.debug added - Preserves the ai_status = Track start error 4. _cb_run_conductor_setup file read (L4416) - file iteration - Narrowed: except Exception -> (OSError, IOError, UnicodeDecodeError) - logging.debug with file path - Preserves the Error reading fallback 5. _cb_load_track (L4513) - project_manager.load_track_state - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) - logging.debug added - Preserves the Load track error fallback 6. _push_mma_state_update (L4542) - project_manager.save_track_state - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) - logging.debug added - Preserves the print to stderr fallback 7. _load_active_tickets beads (L4571) - bclient.list_beads - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) - logging.debug added - Preserves the Error loading beads fallback Refs: spec.md FR1, plan.md Task 2.4 --- src/app_controller.py | 66 ++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index 7e466fbb..5aebc557 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -2804,16 +2804,22 @@ class AppController: self.save_config() self.ai_status = "config saved" - def _do_project_switch(self, path: str) -> None: + def _do_project_switch(self, path: str) -> Result[None]: self._project_switch_error = None try: self._flush_to_project() try: new_project = project_manager.load_project(path) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) as e: + logging.getLogger(__name__).debug("project load failed: %s", e, extra={"source": "app_controller._do_project_switch.load"}) self.ai_status = f"failed to load project: {e}" self._project_switch_error = f"load failed: {e}" - return + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._do_project_switch.load", + original=e, + )]) try: self.project = new_project self.active_project_path = path @@ -2822,14 +2828,21 @@ class AppController: self.tool_preset_manager = tool_presets.ToolPresetManager(new_root) from src.personas import PersonaManager self.persona_manager = PersonaManager(new_root) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) as e: + logging.getLogger(__name__).debug("manager init failed: %s", e, extra={"source": "app_controller._do_project_switch.managers"}) self.ai_status = f"failed to init managers: {e}" self._project_switch_error = f"manager init failed: {e}" - return + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._do_project_switch.managers", + original=e, + )]) self._refresh_from_project() file_items_as_dicts = [{"path": f.path if hasattr(f, "path") else str(f)} for f in self.files] mcp_client.configure(file_items_as_dicts, [str(new_root)]) self.ai_status = f"switched to: {Path(path).stem}" + return OK finally: with self._project_switch_lock: pending = self._project_switch_pending_path @@ -3079,7 +3092,7 @@ class AppController: if not self.rag_config or not self.rag_config.enabled or not self.rag_engine: return - def _run(): + def _run() -> Result[None]: try: self._set_rag_status("indexing...") import concurrent.futures @@ -3102,8 +3115,16 @@ class AppController: self.rag_engine.delete_documents_by_path(stale_paths) self._set_rag_status("ready") - except Exception as e: + return OK + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, RuntimeError) as e: + logging.getLogger(__name__).debug("RAG indexing error: %s", e, extra={"source": "app_controller._do_rag_sync._run"}) self._set_rag_status(f"error: {e}") + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._do_rag_sync._run", + original=e, + )]) self.submit_io(_run) @@ -3124,12 +3145,13 @@ class AppController: return self.ai_status = "fetching models..." - def do_fetch() -> None: + def do_fetch() -> Result[None]: try: for p in ai_client.PROVIDERS: try: self.all_available_models[p] = ai_client.list_models(p) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, RuntimeError) as e: + logging.getLogger(__name__).debug("list_models failed for %s: %s", p, e, extra={"source": "app_controller._fetch_models.do_fetch.per_provider"}) self.all_available_models[p] = [] models_list = self.all_available_models.get(provider, []) @@ -3139,9 +3161,17 @@ class AppController: ai_client.set_provider(self._current_provider, self.current_model) if self.ai_status == "fetching models...": self.ai_status = f"models loaded: {len(models_list)}" - except Exception as e: + return OK + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, RuntimeError) as e: + logging.getLogger(__name__).debug("model fetch error: %s", e, extra={"source": "app_controller._fetch_models.do_fetch"}) if self.ai_status == "fetching models...": self.ai_status = f"model fetch error: {e}" + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._fetch_models.do_fetch", + original=e, + )]) self.submit_io(do_fetch) def _apply_preset(self, name: str, scope: str) -> None: @@ -4284,7 +4314,8 @@ class AppController: self.submit_io(engine.run, md_content=full_md) sys.stderr.write(f"[DEBUG] _start_track_logic: Engine thread spawned for {track_id}.\n") sys.stderr.flush() - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, RuntimeError) as e: + logging.getLogger(__name__).debug("track start error: %s", e, extra={"source": "app_controller._start_track_logic"}) self.ai_status = f"Track start error: {e}" print(f"ERROR in _start_track_logic: {e}") @@ -4396,7 +4427,8 @@ class AppController: lines = len(fd.readlines()) total_lines += lines summary.append(f"- {f.relative_to(base)}: {lines} lines") - except Exception: + 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"}) summary.append(f"- {f.relative_to(base)}: Error reading") summary.append(f"Total Line Count: {total_lines}") tracks_dir = base / "tracks" @@ -4493,7 +4525,8 @@ class AppController: self.disc_entries.clear() self._recalculate_session_usage() self.ai_status = f"Loaded track: {state.metadata.name}" - except Exception as e: + 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}") @@ -4522,8 +4555,8 @@ class AppController: track.tickets = new_tickets state = models.TrackState(metadata=track, tasks=list(new_tickets)) project_manager.save_track_state(track.id, state, self.active_project_root) - except Exception as e: - import sys + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) as e: + logging.getLogger(__name__).debug("push MMA state failed: %s", e, extra={"source": "app_controller._push_mma_state_update"}) print(f"Error pushing MMA state: {e}", file=sys.stderr) def _load_active_tickets(self) -> None: @@ -4551,7 +4584,8 @@ class AppController: "status": bead.status, "depends_on": [], }) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) as e: + logging.getLogger(__name__).debug("load beads failed: %s", e, extra={"source": "app_controller._load_active_tickets.beads"}) print(f"Error loading beads: {e}") #region: --- Config I/O (single source of truth) ---