# Batch 55 — Cycle 1 Report **Date**: 2026-05-14 **Tasks**: AZ-528 (Hygiene — c1_vio strategy facade orchestration-spine consolidation) **Verdict**: COMPLETE — PASS ## Summary Consolidated the 3-way orchestration-spine duplication across the c1_vio `VioStrategy` modules (`okvis2.py`, `vins_mono.py`, `klt_ransac.py`) into a single c1-internal helper module `_facade_spine.py`. Closes cumulative review batches 52-54 Finding F1 (Medium / Maintainability). Mirrors the AZ-527 precedent for the c2_vpr-side `_assert_engine_output_dim` consolidation. No behaviour change — every existing AZ-332 / AZ-333 / AZ-334 AC test passes unmodified. ## Files added / modified ### Added (2) - `src/gps_denied_onboard/components/c1_vio/_facade_spine.py` — new c1-internal helper, 225 lines. Exports 5 free functions (`now_iso`, `bias_norm`, `se3_from_4x4`, `frame_ts_ns`, `frame_image`) and the `FacadeSpine` mixin class (`_classify_state`, `_tick_lost`, `_emit_transition`). - `tests/unit/c1_vio/test_az528_facade_spine.py` — new test file, 19 tests covering AC-1..AC-8 + a Risk-1 mitigation test (each strategy's `__init__` statically asserts that every spine-required attribute is assigned). All pass. ### Modified (3) - `src/gps_denied_onboard/components/c1_vio/okvis2.py` — inherit from `FacadeSpine`; import the 5 free functions; delete the 5 local module-level definitions, 3 instance methods, and the unused `_BIAS_NORM_FLOOR` constant. Set the 3 spine-required attributes (`_feature_threshold`, `_producer_id`, `_strategy_label`) in `__init__`. - `src/gps_denied_onboard/components/c1_vio/vins_mono.py` — same pattern as `okvis2.py`. - `src/gps_denied_onboard/components/c1_vio/klt_ransac.py` — same pattern; spine threshold reads `self._cfg.min_features_for_pose` (the KLT-side equivalent of OKVIS2's `degraded_feature_threshold`). Deferred-consolidation comments referencing this PBI removed. ## Tests - `tests/unit/c1_vio/test_az528_facade_spine.py` — 19 new tests, all pass. - `tests/unit/c1_vio/` (full focused suite) — 120 collected, 114 pass + 6 tier-2 skipped (unchanged from pre-AZ-528 state). - Adjacent regression suite (`test_az270_compose_root`, `test_az272_fdr_record_schema`, `test_az273_fdr_client_ringbuf`, `test_ac1_scaffold_layout`, `tests/unit/c1_vio/`) — 237 pass + 6 skipped. ## AC traceability | AC | Status | Notes | |-------|--------|---------------------------------------------------------| | AC-1 | ✓ | Helper module exposes documented surface. | | AC-2 | ✓ | `now_iso()` returns aware UTC `+00:00` ISO-8601. | | AC-3 | ✓ | `bias_norm` matches L2 formula (accel + gyro). | | AC-4 | ✓ | `se3_from_4x4` builds `gtsam.Pose3` identity correctly. | | AC-5 | ✓ | `_classify_state` INIT during warmup, T/D thresholds. | | AC-6 | ✓ | `_tick_lost` demotes T→D first call, escalates to LOST. | | AC-7 | ✓ | `_emit_transition` exactly one FDR record per change. | | AC-8 | ✓ | AST guard: 0 module-level free funcs in 3 strategies. | | AC-9 | ✓ | All AZ-332 / AZ-333 / AZ-334 AC tests pass unmodified. | | AC-10 | ✓ | AZ-270 layer-lint regression still passes. | ## Code review See `_docs/03_implementation/reviews/batch_55_review.md` — verdict PASS, no findings. ## Outcomes & lessons - Pattern of an "AST regression guard against duplicated free functions" continues to work well (AZ-508 / AZ-526 / AZ-527 / AZ-528 all use it). It catches re-introduction by future authors who don't know the consolidated home exists. - Mixin-via-instance-attributes (rather than constructor parameters) keeps strategy `__init__` signatures stable — the AZ-331 factory signature `(config, *, fdr_client)` is preserved across all three strategies post-refactor. - A 3-point hygiene PBI was the right granularity: 1 new module, 3 small edits to existing strategies, 1 test file with AST + import regression guards. Same shape and complexity as AZ-527. ## Outstanding None for AZ-528. F2 from cumulative review batches 52-54 (test fake + `_patch_pose_recovery` helper consolidation) remains deferred — sits at a different abstraction layer and will ride a later hygiene pass.