# Batch 47 / Cycle 1 — Per-Batch Code Review **Date**: 2026-05-13 **Tasks**: AZ-337 — C2 UltraVPR Primary Backbone (5pt) **Reviewer**: autodev orchestrator (inline review) **Verdict**: `PASS_WITH_WARNINGS` — three Low-severity findings, none blocking. ## Scope - `src/gps_denied_onboard/components/c2_vpr/ultra_vpr.py` (new, 372 lines) - `src/gps_denied_onboard/components/c2_vpr/_preprocessor_ultra_vpr.py` (new, 188 lines) - `tests/unit/c2_vpr/test_ultra_vpr.py` (new, 581 lines, 29 tests) - `_docs/_autodev_state.md` (state bump) No modifications to existing production files. The composition-root helper `_register_strategy_architecture` already no-ops cleanly for strategies that do not expose `MODEL_NAME` / `architecture_factory` — exactly the design path AZ-337 takes. ## Acceptance-Criteria Coverage Verification 12/12 ACs have at least one covering test: | AC | Test(s) | |----|---------| | AC-1 (Protocol conformance) | `test_ac1_protocol_conformance` | | AC-2 (L2-norm FP16 (512,)) | `test_ac2_embed_query_returns_unit_norm_fp16_512`, `test_ac2_embedding_is_single_stage_l2_no_intra_cluster_path` | | AC-3 (deterministic) | `test_ac3_embed_query_deterministic_for_same_frame` | | AC-4 (`retrieve_topk == k`, sorted, label) | `test_ac4_retrieve_topk_returns_exactly_k_with_ultra_vpr_label` | | AC-5 (`descriptor_dim()` stable returns 512) | `test_ac5_descriptor_dim_stable_returns_512` | | AC-6 (engine output shape mismatch → `ConfigError`) | `test_ac6_create_rejects_engine_output_shape_mismatch`, `test_ac6_create_rejects_engine_with_missing_embedding_key` | | AC-7 (`VprBackboneError` on forward fail + ERROR log + FDR) | `test_ac7_runtime_error_yields_vpr_backbone_error`, `test_ac7_missing_embedding_key_yields_vpr_backbone_error`, `test_ac7_wrong_forward_output_shape_yields_vpr_backbone_error` | | AC-8 (`VprPreprocessError` on corrupt image + ERROR log + FDR) | `test_ac8_corrupt_image_yields_vpr_preprocess_error`, `test_ac8_wrong_dtype_image_yields_vpr_preprocess_error` | | AC-9 (calibration absent → geometric centre + WARN) | `test_ac9_identity_calibration_falls_back_to_geometric_centre`, `test_ac9_principal_point_offset_changes_crop_window` | | AC-10 (`IndexUnavailableError` re-raised unchanged) | `test_ac10_index_unavailable_propagates_unchanged` | | AC-11 (composition-root wiring + BUILD-flag gate) | `test_ac11_create_emits_strategy_ready_info_log`, `test_ac11_non_trt_runtime_rejected_at_create`, `test_ac11_onnx_trt_ep_runtime_accepted_at_create` | | AC-12 (top-1 > threshold → WARN via FaissBridge) | `test_ac12_top1_above_threshold_emits_warn_via_faiss_bridge` | ## Tests - `tests/unit/c2_vpr/test_ultra_vpr.py`: **29 / 29 PASS** in 2.5s. - Full unit suite: **1637 passed / 80 skipped / 0 failed** in ~66s. Up from 1608 at the close of Batch 46 (+29 new tests). - `ruff check` on all new + modified files: clean. ## Architectural Review ### F1 — `_iso_ts_from_clock` is now the 7th copy (Low / Maintainability, carried) `_iso_ts_from_clock` appears verbatim in `ultra_vpr.py` lines 296-303, matching the same helper in `net_vlad.py`, `_faiss_bridge.py`, `c11_*`, `c12_*`, `c6_tile_cache.postgres_filesystem_store`, and `c6_tile_cache.freshness_gate`. This is the 7th identical copy. Already tracked by **AZ-508** ("ISO timestamp consolidation, 2pt"). Recommend prioritising AZ-508 before the remaining C2 strategies (AZ-339, AZ-340) add copies #8 and #9. ### F2 — Spec→implementation drift on C7 API names (Low / Spec-Hygiene) The AZ-337 spec § Outcome uses outdated C7 API names: - Spec: `runtime.forward(engine_id, {...})["embedding"]` - Live: `runtime.infer(handle, {...})` returning `dict[str, ndarray]` - Spec: `runtime.load_engine(weights_path)` - Live: `runtime.compile_engine(model_path, build_config)` → `EngineCacheEntry`, then `deserialize_engine(entry)` → `EngineHandle` Same drift was flagged in Batch 46 (AZ-338) review as F2. The implementation aligns with the live v1.0.0 Protocol (AZ-297); spec text is stale. **Recommendation**: a spec-hygiene PBI for AZ-339 / AZ-340 / AZ-358 / AZ-349 to refresh references to the v1.0.0 C7 Protocol BEFORE those tasks are picked up — otherwise each batch repeats the same "spec said X, code does Y" review note. ### F3 — Principal-point fallback heuristic relies on identity-matrix detection (Low / Test-Robustness) `UltraVprBackbonePreprocessor._extract_principal_point` treats `(cx, cy) == (0, 0)` as "no calibration data" because the test fixture uses `np.eye(3)` for "missing" calibration. A real camera calibration with a genuine principal point near the top-left (unusual but legal for cropped sensors) would also be skipped to geometric-centre fallback. The heuristic is correct for production (intrinsics zeroed → no calibration) but the test fixture trick would benefit from a `None` sentinel or a flag rather than relying on the zero-equality check. **Risk is bounded**: no real camera has `cx == 0 and cy == 0`; the worst-case is a one-frame mis-crop with a graceful WARN log. Not blocking. **Recommendation**: when a real `intrinsics_3x3 == None` path lands (currently the dataclass field is `Any` not `Optional`), tighten the type annotation and remove the zero-detection branch. ### Architecture Notes (Strengths) 1. **AZ-507 layering clean**. UltraVPR consumes `InferenceRuntimeCut` + `DescriptorIndexCut` (both C2-owned structural cuts), never `components.c7_inference` or `components.c6_tile_cache` directly. Architecture lint test `test_ac6_only_compose_root_imports_concrete_strategies` PASS. 2. **No PyTorch architecture registration**. UltraVPR is the first strategy that does NOT register a NN architecture (TRT-engine-only, no PyTorch fallback). Verified by `test_create_does_not_register_pytorch_architecture`. The composition-root `_register_strategy_architecture` helper no-ops cleanly for this case — no code change needed there. 3. **Engine load + output-shape assertion at `create` time**. Failure surfaces at composition time, NOT at first frame. Matches Constraint § 5 of the task spec. 4. **Single-stage L2 normalisation**. Explicitly verified to NOT call `intra_cluster_normalise` (NetVLAD's two-stage path). This is a regression-blocking spy in `test_ac2_embedding_is_single_stage_l2_no_intra_cluster_path` — if a future refactor accidentally adds the intra-cluster step, recall would silently degrade on the Derkachi corpus. 5. **Constructor-injection only**. No `import` of `gps_denied_onboard.config` inside `ultra_vpr.py`; config is consumed exclusively through the `create()` factory parameter. 6. **Pattern parity with NetVLAD**. `embed_query` / `retrieve_topk` / `_emit_*` shape mirrors `NetVladStrategy` line-for-line where semantics permit; UltraVPR-specific paths (single-stage L2, `"embedding"` output key, TRT runtime, no architecture registry) are clearly localised. 7. **AC-12 delegation**. `FaissBridge` already owns the top-1-distance WARN log; UltraVPR inherits this for free via the same delegation that NetVLAD uses — one production touchpoint, two strategies. Confirmed by direct test. 8. **Calibration consumption matches spec**. The principal-point crop is the documented UltraVPR preprocessing; the geometric- centre fallback path with WARN log satisfies AC-9 exactly. ## Performance Performance NFR (C2-PT-01 `embed_query` p95 ≤ 60 ms on Tier-1 Jetson Orin) is deferred to E-BBT per task spec § NFRs. Macbook dev tier has no TRT 10.3 + Jetson Orin to benchmark against. **Carry-over to the next cumulative review**: F3 from the 43-45 cumulative report already tracks "Tier-1 perf microbenchmarks deferred"; AZ-337 adds to that backlog. ## Comparison vs Batch 46 (AZ-338) | Aspect | NetVLAD (B46) | UltraVPR (B47) | |--------|---------------|----------------| | Runtime label | `pytorch_fp16` | `tensorrt` / `onnx_trt_ep` | | Engine input | `.pth` state dict | `.trt` engine file | | Architecture registry | binds factory | no-op | | Descriptor dim | 4096 (configurable PCA) | 512 (fixed) | | Normalisation | intra-cluster THEN L2 | L2 only | | Output key | `vlad_descriptor` | `embedding` | | Input shape | `(480, 480)` | `(384, 384)` | | Calibration use | ignored | principal-point crop | | Test count | 31 | 29 | ## Verdict `PASS_WITH_WARNINGS`. Three Low-severity findings, none blocking: - **F1** (carried from B46 / cumulative 43-45): `_iso_ts_from_clock` is the 7th copy; AZ-508 will consolidate. - **F2** (carried from B46): spec→implementation drift on C7 API names; affects future C2 strategies AZ-339 / AZ-340. - **F3** (new): principal-point fallback heuristic uses zero-detection for "no calibration"; safe for production but could be tightened when calibration becomes `Optional`. No Critical, High, or Medium findings. AZ-337 may transition to **In Testing**.