# Batch 54 — Cycle 1 Report **Date**: 2026-05-14 **Tasks**: AZ-334 (C1 KLT/RANSAC Strategy) **Verdict**: COMPLETE — PASS_WITH_WARNINGS ## Summary Implemented `KltRansacStrategy`, the ADR-002 engine-rule mandatory simple-baseline VIO for C1. Pure-Python facade over OpenCV's `cv2.goodFeaturesToTrack` / `cv2.calcOpticalFlowPyrLK` / `cv2.findEssentialMat` / `cv2.recoverPose` pipeline — no C++/pybind11 binding by design so a Tier-0 workstation can run the strategy with `pip install opencv-python` and the AZ-331 factory's `BUILD_KLT_RANSAC=ON` gate. Mirrors the AZ-332 OKVIS2 + AZ-333 VINS-Mono facade pattern on the orchestration spine so the AZ-331 factory + IT-12 comparative harness treat all three strategies as drop-in substitutable. ## Files added / modified ### Added (3) - `src/gps_denied_onboard/components/c1_vio/klt_ransac.py` — Python facade, ~770 lines. - `tests/unit/c1_vio/test_klt_ransac_strategy.py` — AC-1..AC-11 + NFR-perf + KltRansacConfig validation tests, ~990 lines, 25 tests (23 pass + 2 tier-2 skipped on dev/CI runners). - `_docs/03_implementation/reviews/batch_54_review.md` — code review report. ### Modified (4) - `src/gps_denied_onboard/components/c1_vio/config.py` — add `KltRansacConfig` dataclass + `klt_ransac` field on `C1VioConfig`; `__all__` updated. (Pre-existing `KNOWN_STRATEGIES` already had `klt_ransac` from AZ-331.) - `src/gps_denied_onboard/components/c1_vio/__init__.py` — export `KltRansacConfig`. - `cpp/klt_ransac/CMakeLists.txt` — replace AZ-263 placeholder with a deliberate "pure-Python; no native target" STATUS message so the build graph stays symmetric with `cpp/okvis2/` and `cpp/vins_mono/` while the `BUILD_KLT_RANSAC=ON` flag only gates the Python module import at the AZ-331 composition-root factory. - `tests/unit/c1_vio/test_protocol_conformance.py` — introduce the `_STRATEGIES_WITHOUT_NATIVE_BINDING` category and route `klt_ransac` through it inside `test_ac5_build_vio_strategy_flag_on_but_module_missing` so the parametrised factory test correctly handles the pure-Python shape (no native binding to fail on; the construction should succeed and return a `VioStrategy` instance instead). ## AC coverage (AC-1..AC-11 + NFR-perf) All 11 ACs + NFR-perf mapped to passing tests (see `batch_54_review.md` Phase 2 table). AC-9 + NFR-perf are tier-2-tagged per the task spec; they skip on macOS dev + GitHub Actions Linux runner with `Tier-2-only test; set GPS_DENIED_TIER=2 to run`. AC-3 / AC-6 / AC-9 / AC-10 / AC-11 / NFR-perf monkeypatch `cv2.findEssentialMat` + `cv2.recoverPose` + `RansacFilter.filter_correspondences` to deterministic values so the unit suite exercises the FACADE's state machine without depending on real OpenCV geometry on synthetic correspondences; real-geometry validation lives in C1-IT-12 (Jetson Tier-2 fixture). ## Test results ### Focused suite — `tests/unit/c1_vio/` ``` 95 passed, 6 skipped in 1.67s ``` The 6 skips are the 2 OKVIS2 tier-2 tests + the 2 VINS-Mono tier-2 tests + the 2 new KLT/RANSAC tier-2 tests (`test_ac9_*` and `test_nfr_perf_*`). ### Adjacent regression — config / compose-root ``` 17 passed in 1.96s ``` (`test_az269_config_loader.py`, `test_az270_compose_root.py` — all green; the new `KltRansacConfig` registration did not break the schema-loader or compose-root layered-import guards.) ### Tier-2 verification ``` GPS_DENIED_TIER=2 pytest tests/unit/c1_vio/test_klt_ransac_strategy.py → 25 passed in 0.43s ``` All 25 tests pass under the tier-2 gate (including AC-9 honest- covariance monotonicity over 48 synthetic frames + NFR-perf p95 record). ### Full suite Deferred per the implement skill's Test-Run Cadence — the full unit-suite gate runs exactly once at Step 16 (end of implementation phase), not per-batch. Focused tests + cumulative review (every K batches) catch cross-batch regressions before then. ## Architectural decisions - **No native binding by design**: KLT/RANSAC is pure Python over OpenCV's Python bindings. The `cpp/klt_ransac/CMakeLists.txt` placeholder is preserved (with an explanatory STATUS message) for build-graph symmetry with `cpp/okvis2/` and `cpp/vins_mono/`; the `BUILD_KLT_RANSAC=ON` flag only gates the Python module import at the AZ-331 composition-root factory. - **Constructor shape matches factory**: `KltRansacStrategy(config, *, fdr_client, clock=None)` mirrors `Okvis2Strategy` + `VinsMonoStrategy` so the AZ-331 factory invokes all three via the same call shape. The task spec's illustrative constructor (with explicit injection of `CameraCalibration` / `ImuPreintegrator` / `RansacFilter` / `Logger`) was deliberately not adopted because it would diverge from the existing factory contract; the same dependencies are resolved internally instead — `RansacFilter` is static (AZ-282 stateless helper) and `ImuPreintegrator` is constructed lazily on the first `process_frame` call (it needs the per-call `CameraCalibration` which is not available at construction time). - **Honest covariance, AC-9 compliant**: per-frame covariance = `np.eye(6) * (sigma_sq + inlier_penalty) / max(inlier_count - 5, 1)` where `sigma_sq = median_residual_px**2` and `inlier_penalty = threshold_px / max(inlier_count, 1)`. No client-side floor or smoother; the formula grows monotonically as inliers drop or residuals scatter. Tier-2 test walks 48 scripted-inlier frames through the DEGRADED window and verifies monotonicity. - **Camera agnostic, AC-11 enforced**: no `adti20` / `adti26` literals in executable source (docstring mentions are excluded from the CI grep via AST-aware stripping in the test). The per-call `CameraCalibration` argument carries intrinsics; the same code path produces sensible `VioOutput` for two distinct calibrations (different f, cx, cy). - **State machine mirrors OKVIS2 / VINS-Mono**: `INIT` until `warm_start_max_frames` is exhausted, then `TRACKING` if inlier count ≥ `min_features_for_pose`, else `DEGRADED`; sustained pose-recovery failure for `lost_frame_threshold` consecutive frames raises `VioFatalError` with state == `LOST`. Exactly one `vio.health` FDR record per transition (AC-10). - **Error envelope closed**: every OpenCV `cv2.error` is caught at each call site and rewrapped into `VioFatalError` with `__cause__` chaining (AC-4). `RansacFilterError` + `ImuPreintegrationError` are also caught and rewrapped. ## Out of scope / deferred - Real geometry validation against Derkachi fixtures — Tier-2 follow-up; C1-IT-12 binds KLT/RANSAC alongside OKVIS2. - Warm-start hint persistence (AZ-335) — separate batch. - Strategy-facade consolidation hygiene PBI — now formally in scope for the cumulative review covering batches 52-54 (next trigger). All three strategy shapes (OKVIS2, VINS-Mono, KLT/RANSAC) are now visible so the right factoring (template-method base class for the orchestration spine + per-strategy geometry hook) can be scoped without over-fitting. ## Honest-covariance numeric envelope (AC-9 evidence) Tier-2 test walks the following inlier-count sequence through the DEGRADED gate (`min_features_for_pose=50`): | Frame range | Inlier count | State | Cov Frobenius (approx) | |-------------|--------------|----------|------------------------| | 1 | first-frame | INIT | 24.49 (10·√6) | | 2..29 | 80 | TRACKING | ≈ 0.0056 | | 30 | 40 | DEGRADED | ≈ 0.013 | | 31..43 | 35..12 | DEGRADED | strictly increasing | | 44..49 | 10 | DEGRADED | ≈ 0.127 | The Frobenius norm grows monotonically across the entire DEGRADED window — the AC-9 honest-covariance invariant is upheld by the formula's structure, not by a floor clamp.