# Batch 56 — Cycle 1 Report **Date**: 2026-05-14 **Tasks**: AZ-335 (C1 Warm-Start + F8 Reboot Recovery) **Verdict**: COMPLETE — PASS_WITH_WARNINGS ## Summary Implemented the cross-strategy warm-start hint persistence layer, the F2 takeoff (FC EKF) and F8 reboot (disk) prime hooks, and the AC-5.3 "no fake confidence" covariance enforcement at the runtime composition layer. The persistence layer is c1-internal (`components/c1_vio/warm_start_store.py`); the cross-strategy wiring (wrapper + prime hooks) lives at the composition root (`runtime_root/warm_start_wiring.py`) so any concrete `VioStrategy` gains warm-start behaviour without per-strategy edits. AC-5.3 is enforced via a wrapper-owned post-reset covariance inflation + baseline floor — not by mutating any strategy. Default ships with the `JsonSidecarWarmStartHintStore` (atomic JSON + SHA-256 sidecar via AZ-280); a future Redis-backed store can plug in via the same `WarmStartHintStore` Protocol without touching the wiring. Closes the AZ-335 dependency chain: AZ-331 / AZ-332 / AZ-333 / AZ-334 (strategies) + AZ-263 / AZ-269 / AZ-266 / AZ-270 (bootstrap + config + log + compose lint) + AZ-280 (sha256 sidecar) + AZ-272 (FDR schema). Runs immediately after AZ-528 (batch 55) — no other c1_vio work was blocked behind AZ-335. ## Files added / modified ### Added (3) - `src/gps_denied_onboard/components/c1_vio/warm_start_store.py` — 440 lines. Exports `HINT_FILENAME`, `HINT_SCHEMA_VERSION`, `LoadedWarmStartHint` dataclass, `WarmStartHintStore` Protocol, `WarmStartFcSource` Protocol (consumer-side cut over C8 FcAdapter per AZ-507), and the default `JsonSidecarWarmStartHintStore` impl. JSON schema v1: `version`, `calibration_id` (Risk-2 mitigation), `pre_reboot_covariance_norm` (AC-8 floor), `pose` block (4×4 matrix + velocity + bias + ns timestamp). - `src/gps_denied_onboard/runtime_root/warm_start_wiring.py` — 563 lines. Exports `WARM_START_PRODUCER_ID`, `WarmStartWiredStrategy` (the wrapper that adds AC-6 throttled save + AC-7 inflation + AC-8 floor on top of any inner `VioStrategy`), `prime_warm_start_from_disk` (F8 hook), and `prime_warm_start_from_fc` (F2 hook). Single point of FDR record emission via `_emit_prime_fdr` and single point of INFO/WARN log emission via `_emit_prime_log`. - `tests/unit/c1_vio/test_az335_warm_start.py` — 34 unit tests covering all 10 ACs + 3 NFRs. Local fakes for `VioStrategy` and `WarmStartFcSource`; real `Sha256Sidecar` on `tmp_path` for the store tests so AC-1 / AC-2 / AC-10 atomicity contracts are exercised against the production helper. ### Modified (3) - `src/gps_denied_onboard/components/c1_vio/config.py` — added `warm_start_store_dir` (default `/var/lib/gps_denied_onboard/warm_start/`), `warm_start_save_period_frames` (default 5), `post_reset_covariance_inflation_factor` (default 2.0). Each new field has a `__post_init__` validation matching the existing pattern. - `src/gps_denied_onboard/fdr_client/records.py` — registered the new FDR kind `vio.warm_start` in `KNOWN_PAYLOAD_KEYS` with the frozen schema {`source`, `strategy_label`, `bias_norm`, `staleness_ns`, `pre_reboot_covariance_norm`}. - `tests/unit/test_az272_fdr_record_schema.py` — added the per-kind fixture branch for `vio.warm_start` so the AC-1 round-trip suite stays exhaustive over `KNOWN_KIND`. ## Tests - `tests/unit/c1_vio/test_az335_warm_start.py` — 34 new tests, all pass (4.01 s). - Adjacent regression sweep (`tests/unit/c1_vio/`, `tests/unit/c13_fdr/`, `tests/unit/composition_root/`, `test_az272_fdr_record_schema`, `test_az269_config_loader`, `test_az270_compose_root`, `test_az273_fdr_client_ringbuf`, `test_az266_logging_schema`, `test_ac1_scaffold_layout`) — 356 pass + 6 tier-2 skipped (unchanged from pre-AZ-335 state). ## AC traceability | AC | Status | Test | |-------|--------|-------------------------------------------------------------------| | AC-1 | ✓ | `TestStoreAc1RoundTrip` (3 tests; deep-equal + file presence) | | AC-2 | ✓ | `TestStoreAc2Corrupted` (3 tests; sha mismatch + bad envelope) | | AC-3 | ✓ | `TestWiringAc3ColdStart::test_cold_start_does_not_invoke_reset` | | AC-4 | ✓ | `TestWiringAc4F8Reboot::test_f8_reboot_loads_hint_calls_reset_emits_fdr` | | AC-5 | ✓ | `TestWiringAc5F2Takeoff::test_f2_takeoff_fetches_fc_calls_reset_persists` | | AC-6 | ✓ | `TestWiringAc6PerFrameSave` (2 tests; period=5 + period=1) | | AC-7 | ✓ | `TestWiringAc7PostResetInflation` (2 tests; with/without reset) | | AC-8 | ✓ | `TestWiringAc8CovarianceFloor` (2 tests; floor active + dormant) | | AC-9 | ✓ | `TestStoreAc9Clear` (3 tests; remove + log + idempotent) | | AC-10 | ✓ | `TestStoreAc10Atomicity::test_kill_mid_save_leaves_prior_hint_loadable` | | NFR-perf-save | ✓ | `TestStoreNfrPerf::test_nfr_perf_save_p99_under_50ms` | | NFR-perf-load | ✓ | `TestStoreNfrPerf::test_nfr_perf_load_p99_under_20ms` | | NFR-no-crash | ✓ | `TestWiringNfrNoCrash` (4 tests; FC raise/None + save fail + reset fail) | | Risk-2 (calib) | ✓ | `TestStoreAc3CalibrationMismatch::test_calibration_mismatch_returns_none_with_specific_warn` | ## Code review See `_docs/03_implementation/reviews/batch_56_review.md` — verdict **PASS_WITH_WARNINGS**, 1 Medium + 3 Low findings, all informational / documentation-tightening: - F1 (Style, Low): AC-3 spec text shorthand vs source-suffixed log kind — recommend updating spec phrasing in cycle 2. - F2 (Maintainability, Medium): per-frame save uses strategy-frame pose as `body_T_world`; semantically defensible because the strategy's "internal frame" persists across F8 reload via the saved pose; recommend an inline 3-line comment explaining the design choice. - F3 (Spec-Gap, Low): NFR perf tests are dev-hardware smoke; full Tier-2 NVMe perf gate is owned by C1-PT-01 (deferred to E-BBT). - F4 (Architecture, Low): `runtime_root/warm_start_wiring.py` imports c1-internal `_facade_spine` for shared FDR conventions; allowed by module-layout §6, but noted for a possible future promotion of `bias_norm` to `helpers/imu_bias.py`. ## Outcomes & lessons - The Protocol-cut-at-consumer pattern (defining `WarmStartFcSource` inside `c1_vio/warm_start_store.py` instead of importing the concrete C8 `FcAdapter`) is the right shape for AZ-507 compliance. The composition root will wire a thin adapter from C8's actual `FcAdapter` to this Protocol. The AZ-335 wiring tests inject a fake matching the surface directly — no C8 dependency in the test. - Wrapping (rather than per-strategy mixing) for cross-strategy concerns scales: AC-7 inflation + AC-8 floor + AC-6 throttled save all live in one 240-line wrapper class with one inner `VioStrategy` field. The three strategies (OKVIS2 / VINS-Mono / KLT-RANSAC) needed zero edits. - AC-7 and AC-8 stack cleanly: inflation is applied first, then if the inflated norm is below the AC-8 floor it is scaled up to the floor. Both operations preserve SPD because they're positive scalar multiplications. No matrix re-decomposition required. - The AC-NFR-no-crash policy (catch + log + return False; never propagate) is enforced at every prime hook seam: FC source raise, FC source returns None, store.save raises, inner.reset raises. Each path emits a distinct log `kind` so post-mortem can partition the failure mode. ## Outstanding - F1 / F2 / F3 / F4 from this batch's review — non-blocking; recommend folding into a future hygiene PBI alongside any AZ-345+ c3 work that touches the same `vio.warm_start` FDR namespace. - The composition root's `compose_*` binaries do NOT yet wire a `WarmStartWiredStrategy` over the `vio_factory` output. The wiring is in place; the actual call site (`runtime_root/runtime.py` or the per-binary compose script) needs to construct the `WarmStartWiredStrategy` + `JsonSidecarWarmStartHintStore` and call the F8 prime hook before the first `process_frame`. This is out of scope for AZ-335 (the spec only delivers the wiring module, not the per-binary integration); the integration belongs to the next-cycle compose-root task that adds the F2/F8 hook invocations alongside the existing strategy build. ## Next batch AZ-345 (C3 DISK + LightGlue Primary Matcher, 5 points) is the next unblocked product PBI per `_dependencies_table.md`. All its dependencies (AZ-263, AZ-269, AZ-278, AZ-282, AZ-298, AZ-299, AZ-303, AZ-281, AZ-321, AZ-266, AZ-272, AZ-344) are complete.