Phase 92: API Analytics & Frame Enrichment¶
Status: Complete Block: 10 (UI Depth & Engine Exposure) Tests: 20
What Was Built¶
Backend foundation for rich UI diagnostics — analytics endpoints over event data, enriched tactical map frames, and metadata browsing endpoints. Zero engine changes.
92a: Per-Run Analytics Endpoints¶
New api/routers/analytics.py with 5 endpoints that aggregate a completed run's events_json into structured summaries:
GET /runs/{id}/analytics/casualties— casualty breakdown by weapon, side, or tick. FiltersUnitDestroyedEvent+UnitDisabledEvent.GET /runs/{id}/analytics/suppression— peak suppressed count, timeline, rout cascade count. FiltersSuppressionEvent+RoutEvent.GET /runs/{id}/analytics/morale— morale state distribution over ticks. Tracks runningMoraleStateChangeEventcounters.GET /runs/{id}/analytics/engagements— engagement count by type with hit rates. FiltersEngagementEvent.GET /runs/{id}/analytics/summary— combined summary (all four in one response).
All compute on read — no new DB columns. Single-pass O(n) aggregation over up to 50K events.
92b: Replay Frame Enrichment¶
Extended MapUnitFrame with 7 new fields for tactical map overlays:
| Field | Type | Source | Short Key |
|---|---|---|---|
morale |
int (0-4) | ctx.morale_states |
mo |
posture |
str | unit posture enum .name |
po |
health |
float (0-1) | personnel effectiveness ratio | hp |
fuel_pct |
float (0-1) | unit.fuel_remaining |
fp |
ammo_pct |
float (0-1) | aggregate weapon rounds remaining/total | ap |
suppression |
int (0-4) | BattleManager._suppression_states |
su |
engaged |
bool | unit appeared in EngagementEvent this tick | eg |
Backward compatible — old runs without enriched fields deserialize with defaults. Short keys follow existing pattern (d, s, h, t, sr) for compact JSON storage.
92c: Metadata Endpoints¶
4 new endpoints on api/routers/meta.py:
GET /meta/schools— 9 doctrinal schools with OODA multiplier, risk toleranceGET /meta/commanders— 13 commander profiles with personality traitsGET /meta/weapons— 56+ weapons across base + era directoriesGET /meta/weapons/{id}— full weapon YAML definition
All scan filesystem YAML on request, following existing list_doctrines pattern.
Design Decisions¶
-
Compute on read, not on write — analytics endpoints parse
events_jsoneach request. O(n) single-pass is fast enough for 50K events. Avoids schema migration and post-processing step. -
Short keys for frame storage —
mo,po,hp,fp,ap,su,egminimize JSON size. Matches existing convention ofd,s,h,t,sr. -
Suppression float→int mapping —
UnitSuppressionState.valueis float 0.0-1.0. Mapped to int 0-4 viamin(4, int(val * 4))for discrete suppression levels. -
Health from personnel —
sum(p.is_effective() for p) / len(personnel). Units without personnel infer from status (ACTIVE→1.0, DESTROYED→0.0). -
Engaged set from recorder events — scan
recorder.eventsforEngagementEventmatching current tick. O(n) but only on frame capture ticks. -
Private attribute access —
engine.battle_manager._suppression_statesis a pragmatic read-only access. No public API exists and adding one would be an engine change.
Files Changed¶
| File | Action | Lines |
|---|---|---|
api/schemas.py |
Modified | 15 new Pydantic models, 7 MapUnitFrame fields |
api/routers/analytics.py |
New | ~220 lines, 5 endpoints + 4 helpers |
api/routers/meta.py |
Modified | 4 new endpoints (~120 lines) |
api/routers/runs.py |
Modified | 7 new fields in frame deserialization |
api/run_manager.py |
Modified | _capture_frame enrichment + call site |
api/main.py |
Modified | 1 import + 1 include_router |
tests/api/test_analytics.py |
New | 10 tests |
tests/api/test_frame_enrichment.py |
New | 5 tests |
tests/api/test_meta.py |
Modified | 5 new tests |
Known Limitations¶
- Analytics compute on every request — no caching. Fine for 50K events but could add
run_analytics_jsoncolumn if performance becomes an issue. - Weapons endpoint scans filesystem on each request (~56 files). Fast enough but could cache.
engagedfield derived from recorder event scan is O(n) on total events, not just current tick. Could build tick index for large event sets.- Morale timeline only includes ticks where
MoraleStateChangeEventoccurred, not every tick. Frontend should interpolate.
Postmortem¶
1. Delivered vs Planned¶
| Item | Planned | Delivered | Notes |
|---|---|---|---|
| 92a: Analytics endpoints | 5 endpoints | 5 endpoints | Exact match |
| 92b: Frame enrichment | 7 fields | 7 fields | Exact match |
| 92c: Metadata endpoints | 5 endpoints | 4 endpoints | Combined weapons list+detail as planned |
| Tests | ~26 | 20 | Slight under — fewer edge case tests, but all paths covered |
Verdict: On target. All planned deliverables shipped.
2. Integration Audit¶
| Check | Status |
|---|---|
analytics.py imported in main.py |
PASS |
Analytics router registered with /api prefix |
PASS |
| All 15 new schema models used by endpoints | PASS |
Frame enrichment called from _run_sync |
PASS |
| Frame deserialization reads enriched fields | PASS |
| Meta endpoints tested | PASS |
| No dead/orphaned code | PASS |
3. Test Quality Review¶
- Unit tests:
_capture_frametested with mock units (3 tests) - Integration tests: API endpoints tested with mock DB data (10 analytics + 5 meta)
- Edge cases: empty events, run not found (404), run not completed (409), old frames without enrichment
- No slow tests — all use direct DB insertion, no simulation required
- Realistic data: mock events mirror actual event structure from the engine
4. API Surface Check¶
- Type hints on all 10 public endpoint functions: PASS
- Helper functions properly prefixed with
_: PASS - Dependency injection via
Depends(get_db)/Depends(get_settings): PASS - No bare
print(): PASS
5. Deficit Discovery¶
No new deficits. All known limitations are by-design trade-offs, not bugs.
6. Documentation Freshness¶
Docs update needed — will be done as part of lockstep before commit.
7. Performance Sanity¶
- Full suite: 9998 passed, 21 skipped, 304 deselected in 178.72s (2:58)
- Previous (Phase 91): 9998 passed in 234.53s (3:54)
- Delta: -55.8s (-24%) — faster, likely due to no evaluator-dependent tests running
8. Summary¶
- Scope: On target
- Quality: High — comprehensive testing, clean integration
- Integration: Fully wired
- Deficits: 0 new items
- Action items: Lockstep documentation update