Private
Public Access
0
0

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
This commit is contained in:
2026-06-18 19:55:11 -04:00
parent e8879a93a0
commit 345dee34a7
+30 -10
View File
@@ -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