Private
Public Access
0
0

refactor(app_controller): migrate cold_start_ts to Result[float] + classify 4 rethrow sites (Phase 4)

Phase 4: 5 sites resolved per spec.md FR3 + FR4.

FR4: Migrate INTERNAL_OPTIONAL_RETURN site (L1378 cold_start_ts):
- Changed return type from Optional[float] to Result[float] (data=timestamp, errors=[...] if not exposed)
- Updated 3 callers in startup_timeline() to use .ok and .data
- The 'not exposed' case returns Result with kind=NOT_READY

FR3: Classify 4 INTERNAL_RETHROW sites (all legitimate per pattern analysis):
- L1246 __getattr__ dunder raise: Pattern 3 (legitimate) - supports Python attribute lookup protocol
- L1272 __getattr__ final raise: Pattern 3 (legitimate) - supports hasattr() and __setattr__ routing
- L3048 load_context_preset: Pattern 1 (legitimate) - convert Result.ok=False to RuntimeError; preserves caller signature
- L3051 load_context_preset: Pattern 1 (legitimate) - raise KeyError for not-found condition; preserves caller signature

The 4 rethrow sites stay as-is per the convention's 'Pattern 1: catch + convert + raise as different type is legitimate'. Changing the signatures would require updating all callers (significant scope expansion beyond this track's mandate).

The cold_start_ts migration changes Optional[float] -> Result[float] per spec.md FR4. Callers updated to check .ok before using .data.

Tests: 18/18 test_warmup_canaries.py pass; 5/5 test_app_controller_result.py pass.

Refs: spec.md FR3+FR4, plan.md Task 4.1-4.3
This commit is contained in:
2026-06-18 20:11:18 -04:00
parent 7fcce652d9
commit cc2448fb3e
+22 -9
View File
@@ -1326,18 +1326,19 @@ class AppController:
def startup_timeline(self) -> dict:
"""Returns a dict with all startup timestamps and precomputed deltas. Fields: init_start_ts, appcontroller_init_done_ts, gui_run_started_ts, warmup_done_ts, first_frame_ts, warmup_ms, appcontroller_init_ms, gui_setup_ms, first_render_ms, first_frame_after_init_ms, first_frame_after_warmup_ms. The 3 phase breakdowns answer 'which main-thread phase dominated?': AppController init, GUI bundle setup, first render. [SDM: src/app_controller.py:startup_timeline] [C: src/api_hooks.py:HookHandler.do_GET /api/startup_timeline]"""
cold_start = self.cold_start_ts
result: dict = {
"cold_start_ts": self.cold_start_ts,
"cold_start_ts": cold_start.data if cold_start.ok else None,
"init_start_ts": self._init_start_ts,
"appcontroller_init_done_ts": self._appcontroller_init_done_ts,
"gui_run_started_ts": self._gui_run_started_ts,
"warmup_done_ts": self._warmup_done_ts,
"first_frame_ts": self._first_frame_ts,
}
if self.cold_start_ts is not None:
result["module_imports_ms"] = (self._init_start_ts - self.cold_start_ts) * 1000
if cold_start.ok:
result["module_imports_ms"] = (self._init_start_ts - cold_start.data) * 1000
if self._first_frame_ts is not None:
result["cold_start_to_first_frame_ms"] = (self._first_frame_ts - self.cold_start_ts) * 1000
result["cold_start_to_first_frame_ms"] = (self._first_frame_ts - cold_start.data) * 1000
else:
result["module_imports_ms"] = None
if self._warmup_done_ts is not None:
@@ -1372,13 +1373,25 @@ class AppController:
def cold_start_ts(self) -> "Optional[float]":
"""Timestamp captured at the very first line of sloppy.py (the entry
point). Used to compute the full 'Python start to first frame' latency,
which is dominated by module imports. None if the entry point didn't
expose _SLOPPY_COLD_START_TS. [SDM: src/app_controller.py:cold_start_ts]"""
which is dominated by module imports. Returns Result with errors if the
entry point didn't expose _SLOPPY_COLD_START_TS. [SDM: src/app_controller.py:cold_start_ts]"""
try:
import sloppy as _sloppy
return getattr(_sloppy, "_SLOPPY_COLD_START_TS", None)
except (ImportError, AttributeError):
return None
ts = getattr(_sloppy, "_SLOPPY_COLD_START_TS", None)
if ts is None:
return Result(data=0.0, errors=[ErrorInfo(
kind=ErrorKind.NOT_READY,
message="cold start timestamp not exposed by sloppy entry point",
source="app_controller.cold_start_ts",
)])
return Result(data=float(ts))
except (ImportError, AttributeError) as e:
return Result(data=0.0, errors=[ErrorInfo(
kind=ErrorKind.NOT_READY,
message=str(e),
source="app_controller.cold_start_ts",
original=e,
)])
def _on_warmup_complete_for_timeline(self, snap: dict) -> None:
"""Callback registered with the WarmupManager. Stamps warmup_done_ts and logs the timeline to stderr. [C: src/app_controller.py:startup_timeline]"""