# Batch 45 — Code Review **Tasks**: AZ-341 (C2 FAISS HNSW Retrieve Wiring) **Cycle**: 1 **Reviewer**: autodev **Verdict**: **PASS_WITH_WARNINGS** ## Scope reviewed Production code: - `src/gps_denied_onboard/components/c2_vpr/_faiss_bridge.py` (NEW — `FaissBridge` class with constructor validation, `retrieve(...)` method, INV-4 defensive check, WARN-threshold + DEBUG-flag log emission, FDR record + latency capture). - `src/gps_denied_onboard/components/c2_vpr/descriptor_index_cut.py` (NEW — `DescriptorIndexCut` Protocol mirroring c6 `DescriptorIndex.search_topk`; AZ-507 consumer-side structural cut so c2_vpr remains c6-import-free; defines the local `TileIdTuple` alias matching `VprCandidate.tile_id`'s composite identity). - `src/gps_denied_onboard/components/c2_vpr/config.py` (added `warn_top1_threshold: float = 0.30` and `debug_per_frame_distances: bool = False` config knobs + their validators in `__post_init__`). - `src/gps_denied_onboard/components/c2_vpr/__init__.py` (re-exports `DescriptorIndexCut` and `TileIdTuple` for the composition root's c6 adapter; deliberately does NOT re-export `FaissBridge` per the internal/`_underscore` convention — each strategy injects the bridge via its own `create(...)` factory). - `src/gps_denied_onboard/fdr_client/records.py` (registered `vpr.retrieve_topk` in `KNOWN_PAYLOAD_KEYS` with payload field set `{frame_id, backbone_label, top10_distances, latency_us}`). Tests: - `tests/unit/c2_vpr/test_faiss_bridge.py` (NEW — 22 tests covering AC-1..AC-11 + NFR-perf microbench + constructor validation + retrieve-argument validation). - `tests/unit/test_az272_fdr_record_schema.py` (added a fixture payload for `vpr.retrieve_topk` so the schema roundtrip suite covers the new record kind alongside `c12.reloc.requested`). ## Phase 1 — Context loading Inputs read: - Task spec: `_docs/02_tasks/todo/AZ-341_c2_faiss_retrieve_wiring.md` - Contracts: `_docs/02_document/contracts/c2_vpr/vpr_strategy_protocol.md`, `_docs/02_document/contracts/c6_tile_cache/descriptor_index.md`, `_docs/02_document/contracts/c6_tile_cache/tile_metadata_store.md`. - Module layout: `_docs/02_document/module-layout.md` (c2_vpr Per-Component Mapping; AZ-507 cross-component rule). - Existing surfaces: `c2_vpr/{interface,config,errors,__init__}.py`, `c6_tile_cache/{interface,errors,_types}.py`, `_types/vpr.py`, `fdr_client/{client,records}.py`, `clock/interface.py`, `c12_operator_orchestrator/operator_reloc_service.py` (pattern reference for FdrClient + Clock + logger composition). ## Phase 2 — Spec compliance AC coverage map (every AC has at least one covering test): | AC | Test | |----|------| | AC-1 happy-path retrieve | `test_retrieve_happy_path_returns_vpr_result_and_emits_fdr` | | AC-2 undersized → IndexUnavailableError | `test_retrieve_undersized_corpus_raises_index_unavailable_error` | | AC-3 unordered → IndexUnavailableError | `test_retrieve_unordered_distances_raises_index_unavailable_error` | | AC-4 c6 error propagates unchanged | `test_retrieve_propagates_index_unavailable_error_unchanged` | | AC-5 WARN threshold trigger | `test_retrieve_emits_warn_log_when_top1_above_threshold` | | AC-6 WARN not triggered when below | `test_retrieve_does_not_emit_warn_when_top1_below_threshold` | | AC-7 DEBUG on → log | `test_retrieve_emits_debug_log_when_per_frame_distances_on` | | AC-8 DEBUG off → no log | `test_retrieve_does_not_emit_debug_log_when_per_frame_distances_off` | | AC-9 FDR record fields | `test_retrieve_fdr_record_fields_are_populated_with_positive_latency` | | AC-10 retrieve_topk body is one line | `test_every_concrete_strategy_retrieve_topk_body_is_one_return_statement` | | AC-11 per-strategy descriptor_dim | `test_descriptor_dim_carried_through_to_each_candidate` | | NFR-perf p95 ≤ 0.5 ms | `test_bridge_retrieve_overhead_p95_under_500us` | Contract verification: - `vpr_strategy_protocol.md` — the bridge returns ``VprResult`` with exactly ``k`` candidates sorted ascending (INV-4), populates ``backbone_label`` non-empty (INV-5), and propagates ``IndexUnavailableError`` rather than returning stale candidates (C2-ST-01). All satisfied. - `descriptor_index.md` — the c6 surface returns ``≤ k`` results (Invariant I-2) and ``IndexUnavailableError`` on stale handle / dim mismatch. The bridge enforces a stricter operational invariant (== k) on top, which is the task-spec's explicit defended-in-depth intent (see Constraints § "The defensive INV-4 check is mandatory"). The operational/structural distinction is documented in `_faiss_bridge.py`'s module docstring. ## Phase 3 — Code quality - **SRP**: `FaissBridge` has exactly one job — share the retrieve plumbing across strategies. `DescriptorIndexCut` has one job — cut c6 imports per AZ-507. - **Constructor validation**: every argument is type- and range-checked at construction; `ValueError` for value violations, `TypeError` for type violations. - **Error handling**: no bare `except`; only catches nothing (lets c6 errors propagate per AC-4); raises `IndexUnavailableError` from c2_vpr's family for the bridge's own INV-4 violations. - **Naming**: `_verify_invariants` / `_log_invariant_violation` / `_emit_fdr_record` read clearly from the call site. - **Complexity**: `retrieve(...)` is ~30 lines including blank lines; `_verify_invariants` is ~22 lines. All under 50. - **Test quality**: every test follows Arrange / Act / Assert with explicit comments; assertions check the actual record kind, the WARN log's structured `kv`, and the FDR payload by key — not just "no exception thrown". - **Dead code**: none. - **No verbose debug logging by default**: DEBUG per-frame distances is OFF by default (AC-8) — only ON when explicitly enabled by config; the WARN threshold is config-driven, not hard-coded. ## Phase 4 — Security quick-scan - No SQL, no `subprocess`, no `eval` / `exec`. - No hardcoded secrets. - No user input is interpolated into log messages or FDR records beyond integer `frame_id` and string `backbone_label` already validated upstream. - Sensitive data: the FDR record carries `top10_distances` (raw floats) — not PII, not signing material. Safe to persist. ## Phase 5 — Performance scan - `retrieve` is O(k) for the INV-4 sorted check + O(k) for candidate construction; k=10 typical. - No N+1 / unbounded fetch / blocking I/O / async-context issues. - FDR enqueue is non-blocking (SPSC ring, drop-oldest on overrun delegated to the AZ-274 overrun policy). - Microbench `test_bridge_retrieve_overhead_p95_under_500us` exercises 1000 calls with a wall-clock-backed `Clock` and asserts p95 ≤ 500 µs — passes. ## Phase 6 — Cross-task consistency Single-task batch (AZ-341 only). N/A. ## Phase 7 — Architecture compliance - **Layer direction**: c2_vpr (Layer 3) imports only from `_types.vpr` (L1), `clock` (L1), `config` (L1), `logging` (L1), `fdr_client` (L1), and its own component module (`descriptor_index_cut`). No upward imports. ✓ - **Public API respect**: ZERO direct imports of `gps_denied_onboard.components.c6_tile_cache.*` in any c2_vpr source file — the bridge consumes `DescriptorIndexCut` per the AZ-507 rule. - **New cyclic dependencies**: none introduced. - **Duplicate symbols**: `IndexUnavailableError` already exists in both `c2_vpr.errors` and `c6_tile_cache.errors` (pre-existing, documented in both modules' docstrings — intentional namespace duplication for the closed-envelope rewrap pattern). - **Cross-cutting concerns**: `_iso_ts_from_clock` is duplicated across `c2_vpr/_faiss_bridge.py`, `c5_state`, `c11_tile_manager`, and `c12_operator_orchestrator/operator_reloc_service.py`. See Finding F1 below. ## Findings | # | Severity | Category | File:Line | Title | |---|----------|----------------|------------------------------------------------------|-------| | 1 | Low | Maintainability | `c2_vpr/_faiss_bridge.py`:`_iso_ts_from_clock` | Duplicated `_iso_ts_from_clock` helper across c2/c5/c11/c12 | | 2 | Low | Spec-Gap | `_docs/02_tasks/todo/AZ-341_c2_faiss_retrieve_wiring.md` | Task spec's constructor lists `normaliser: DescriptorNormaliser` but body never calls it; bridge correctly omits the parameter | ### Finding Details **F1: Duplicated `_iso_ts_from_clock` helper** (Low / Maintainability) - Location: `c2_vpr/_faiss_bridge.py` `_iso_ts_from_clock` - Description: This six-line helper converts `clock.time_ns()` to an RFC 3339 UTC ISO string with nanosecond precision. The same body appears inline in `c12_operator_orchestrator/operator_reloc_service.py`, `c11_tile_manager`, and `c5_state`. Factoring it into a shared helper (e.g. `helpers.iso_ts_from_clock`) would let every FDR producer share one definition. - Suggestion: defer to a cross-cutting hygiene PBI (AZ-508 "ISO timestamps consolidation" is already in `todo/` for exactly this concern). No action this batch. - Task: AZ-341 **F2: Spec lists unused `normaliser` constructor parameter** (Low / Spec-Gap) - Location: `_docs/02_tasks/todo/AZ-341_c2_faiss_retrieve_wiring.md` § Outcome (Constructor) - Description: The task-spec constructor signature names a `normaliser: DescriptorNormaliser` parameter, but the `retrieve(...)` body uses neither L2 nor intra-cluster normalisation — the embedding arrives already L2-normalised because every concrete `VprStrategy.embed_query` normalises before returning the `VprQuery` (per `VprStrategy` INV-3). Including the parameter would be dead weight on every strategy's `create(...)` factory with no behavioural effect. - Suggestion: surface to the user; if the spec genuinely intended defensive re-normalisation in the bridge, add it explicitly and document why (it conflicts with INV-3 of the producing strategy). Otherwise update the spec to drop the parameter. The implementation matches the literal AC list either way. - Task: AZ-341 ## Verdict PASS_WITH_WARNINGS — two Low-severity findings, both pre-existing or stylistic. No Critical, no High, no Medium. The implementation satisfies every AC, every NFR, the AZ-507 cross-component rule, and the existing C2 contract surface. The implement skill's Auto-Fix gate accepts this verdict without intervention; both findings carry over to the cumulative review (batches 43-45) where they may be batched with similar items.