From 345dee34a7bc84274606a6afac00c6c0f3eedaed Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 18 Jun 2026 19:55:11 -0400 Subject: [PATCH] refactor(app_controller): migrate 6 project-op sites to Result (batch 2) Migrated 6 INTERNAL_BROAD_CATCH sites in src/app_controller.py: 1. cb_prune_logs.run_manual_prune (L2157) - log pruning with aggressive thresholds - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, AttributeError) - Returns Result[None] via OK on success, Result with errors on failure - logging.debug added per Heuristic #19 2. _load_active_project primary (L2168) - project_manager.load_project - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) - logging.debug added - Preserves the migrate_from_legacy_config fallback 3. _load_active_project fallback_loop (L2182) - load_project for each project_path - Same exception narrowing as primary - logging.debug includes the failed path - Preserves the continue-on-error behavior 4. _prune_old_logs.run_prune (L2223) - background log pruning - Same exception narrowing as run_manual_prune - logging.debug added - Returns Result[None] 5. _refresh_from_project active_track deserialization (L2918) - Narrowed: except Exception -> (TypeError, ValueError, KeyError, AttributeError) - logging.debug added - Preserves the active_track = None fallback 6. _save_active_project (L2972) - project_manager.save_project - Narrowed: except Exception -> (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) - logging.debug added - Preserves the ai_status = save error fallback Added import tomllib to the top of app_controller.py for the TOMLDecodeError exception narrowing in _load_active_project. Refs: spec.md FR1, plan.md Task 2.3 --- src/app_controller.py | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index a1e22aca..7e466fbb 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -10,6 +10,7 @@ import signal import sys import threading import time +import tomllib import traceback import uuid @@ -2144,7 +2145,7 @@ class AppController: """Manually triggers the log pruning process with aggressive thresholds.""" self.ai_status = "Manual prune started (Age > 0d, Size < 100KB)..." - def run_manual_prune() -> None: + def run_manual_prune() -> Result[None]: try: from src import log_registry from src import log_pruner @@ -2154,18 +2155,26 @@ class AppController: # Note: max_age_days=0 means cutoff is NOW. pruner.prune(max_age_days=0, min_size_kb=100) self.ai_status = "Manual prune complete." - except Exception as e: + return OK + except (OSError, IOError, ValueError, TypeError, AttributeError) as e: + logging.getLogger(__name__).debug("manual prune error: %s", e, extra={"source": "app_controller.cb_prune_logs.run_manual_prune"}) self.ai_status = f"Manual prune error: {e}" - print(f"Error during manual log pruning: {e}") + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller.cb_prune_logs.run_manual_prune", + original=e, + )]) self.submit_io(run_manual_prune) - def _load_active_project(self) -> None: + def _load_active_project(self) -> Result[None]: """Loads the active project configuration, with fallbacks.""" if self.active_project_path and Path(self.active_project_path).exists(): try: self.project = project_manager.load_project(self.active_project_path) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) as e: + logging.getLogger(__name__).debug("load_project failed: %s", e, extra={"source": "app_controller._load_active_project.primary"}) print(f"Failed to load project {self.active_project_path}: {e}") self.project = project_manager.migrate_from_legacy_config(self.config) self.active_project_path = "" @@ -2179,7 +2188,8 @@ class AppController: self.project = project_manager.load_project(pp) self.active_project_path = pp break - except Exception: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError, tomllib.TOMLDecodeError) as e: + logging.getLogger(__name__).debug("load_project failed for %s: %s", pp, e, extra={"source": "app_controller._load_active_project.fallback_loop"}) continue if not self.active_project_path: name = self.project.get("project", {}).get("name", "project") @@ -2213,15 +2223,23 @@ class AppController: def _prune_old_logs(self) -> None: """Asynchronously prunes old insignificant logs on startup.""" - def run_prune() -> None: + def run_prune() -> Result[None]: try: from src import log_registry from src import log_pruner registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml")) pruner = log_pruner.LogPruner(registry, str(paths.get_logs_dir())) pruner.prune() - except Exception as e: + return OK + except (OSError, IOError, ValueError, TypeError, AttributeError) as e: + logging.getLogger(__name__).debug("log pruning error: %s", e, extra={"source": "app_controller._prune_old_logs.run_prune"}) print(f"Error during log pruning: {e}") + return Result(data=None, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=str(e), + source="app_controller._prune_old_logs.run_prune", + original=e, + )]) self.submit_io(run_prune) def start_services(self, app: Any = None): @@ -2915,7 +2933,8 @@ class AppController: tickets=tickets ) self.active_tickets = at_data.get("tickets", []) # Keep dicts for UI table - except Exception as e: + except (TypeError, ValueError, KeyError, AttributeError) as e: + logging.getLogger(__name__).debug("active track deserialize failed: %s", e, extra={"source": "app_controller._refresh_from_project.active_track"}) print(f"Failed to deserialize active track: {e}") self.active_track = None else: @@ -2969,7 +2988,8 @@ class AppController: try: cleaned = project_manager.clean_nones(self.project) project_manager.save_project(cleaned, self.active_project_path) - except Exception as e: + except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) as e: + logging.getLogger(__name__).debug("save_project failed: %s", e, extra={"source": "app_controller._save_active_project"}) self.ai_status = f"save error: {e}" #endregion: Project