Commit Graph
3 Commits
Author SHA1 Message Date
ed 7298fbd62b refactor(src): narrow exception types in startup_profiler + project_manager (6 sites)
Migrates the 6 try/except sites by narrowing the exception types
from broad 'except Exception' to specific stdlib/known exceptions.
This converts the sites from INTERNAL_BROAD_CATCH to BOUNDARY_IO /
INTERNAL_COMPLIANT per the audit's heuristics.

1. src/startup_profiler.py:40 (1 site) - sys.stderr.write/flush
   except Exception -> except OSError

2. src/project_manager.py:32 (1 site) - datetime.strptime
   except Exception -> except (ValueError, TypeError)

3. src/project_manager.py:98 (1 site) - subprocess.run for git command
   except Exception -> except (OSError, subprocess.SubprocessError,
                               subprocess.TimeoutExpired)

4. src/project_manager.py:363 (1 site) - state.from_dict in get_all_tracks
   except Exception -> except (OSError, AttributeError, KeyError, TypeError)

5. src/project_manager.py:375 (1 site) - metadata.json read
   except Exception -> except (OSError, json.JSONDecodeError, UnicodeDecodeError)

6. src/project_manager.py:390 (1 site) - plan.md read
   except Exception -> except (OSError, UnicodeDecodeError, re.error)

This is a 'narrowing migration' rather than a Result[T] migration
because the public API (Optional[datetime], str, list[dict]) is
preserved and no callers need updating. The behavior is unchanged.

Tests verified:
- tests/test_project_manager_tracks.py (4 tests) PASS
- tests/test_project_manager_modes.py (2 tests) PASS
2026-06-17 19:11:35 -04:00
ed 77873c21f3 feat(startup_profiler): add module-level singleton + live stderr logging
- startup_profiler: StartupProfiler = StartupProfiler() at module bottom
  so sloppy.py can import it without circular imports.
- phase() context manager now writes a [startup] <name>: <ms>ms line to
  stderr in its finally block. Live visibility of every measured phase.
2026-06-06 23:57:19 -04:00
ed 5a85653654 feat(startup_profiler): add StartupProfiler for per-phase init timing
Lightweight, in-memory profiler for AppController init phases. Used by
the startup_speedup_20260606 track to measure where the time goes
during boot (config hydration, hook server start, subsystem init, etc.).

The profiler is exposed via /api/startup_profile (Phase 8 work) and
the Diagnostics panel so the user can see the exact per-phase cost.

Public API:
  StartupProfiler() - create
  .phase(name) - context manager
  .snapshot() - {phases: {name: {start_ts, duration_ms}}, total_ms, count}
  .reset() - clear recorded phases
  .enable() / .disable() - toggle recording

Implementation:
  - dataclass with list of _Phase(name, start_ts, end_ts)
  - @contextmanager records wall-clock via time.perf_counter
  - records duration even if the body raises (try/finally)
  - snapshot is a copy, so consumers can't mutate the live state

TDD: 5 tests in tests/test_startup_profiler.py cover: basic
recording, total math, snapshot isolation, exception safety, empty
state.
2026-06-06 13:57:26 -04:00