From b64f3a1b933254a7f5b2d5cddfeab2af4c0c04ac Mon Sep 17 00:00:00 2001 From: Oleksandr Bezdieniezhnykh Date: Wed, 13 May 2026 22:44:22 +0300 Subject: [PATCH] [AZ-337] Archive task spec + batch 47 report + state bump - _docs/02_tasks/todo/AZ-337_c2_ultra_vpr.md -> _docs/02_tasks/done/AZ-337_c2_ultra_vpr.md - _docs/03_implementation/batch_47_cycle1_report.md (new) - _docs/_autodev_state.md: last_completed_batch 46 -> 47; sub_step.detail "batch 47 complete - selecting batch 48" AZ-337 transitioned in Jira: In Progress -> In Testing. Batches 45/46/47 close the C2 production path (Protocol + FaissBridge + NetVLAD baseline + UltraVPR primary). Co-authored-by: Cursor --- .../{todo => done}/AZ-337_c2_ultra_vpr.md | 0 .../batch_47_cycle1_report.md | 199 ++++++++++++++++++ _docs/_autodev_state.md | 4 +- 3 files changed, 201 insertions(+), 2 deletions(-) rename _docs/02_tasks/{todo => done}/AZ-337_c2_ultra_vpr.md (100%) create mode 100644 _docs/03_implementation/batch_47_cycle1_report.md diff --git a/_docs/02_tasks/todo/AZ-337_c2_ultra_vpr.md b/_docs/02_tasks/done/AZ-337_c2_ultra_vpr.md similarity index 100% rename from _docs/02_tasks/todo/AZ-337_c2_ultra_vpr.md rename to _docs/02_tasks/done/AZ-337_c2_ultra_vpr.md diff --git a/_docs/03_implementation/batch_47_cycle1_report.md b/_docs/03_implementation/batch_47_cycle1_report.md new file mode 100644 index 0000000..4b46a08 --- /dev/null +++ b/_docs/03_implementation/batch_47_cycle1_report.md @@ -0,0 +1,199 @@ +# Batch 47 / Cycle 1 — Implementation Report + +**Date**: 2026-05-13 +**Tasks**: AZ-337 — C2 UltraVPR Primary Backbone (5pt) +**Total complexity**: 5 points +**Result**: PASS_WITH_WARNINGS (per-batch code review) +**Jira tracker state**: AZ-337 transitioned To Do → In Progress → In Testing + +## Scope + +UltraVPR is the Documentary Lead's PRIMARY backbone per +`components/02_c2_vpr/description.md` § 1 and is the strategy wired +by default when `config.c2_vpr.strategy == "ultra_vpr"`. Runs on the +C7 TensorRT runtime (AZ-298) or ONNX-Runtime fallback (AZ-299) — +explicitly NOT on the PyTorch FP16 runtime (reserved for the +NetVLAD baseline). This runtime isolation means a TRT engine compile +bug can fall back to NetVLAD without simultaneously breaking both +strategies. This batch closes the C2 default production path that +NetVLAD (B46) and FaissBridge (B45) prepared for. + +## Files Changed + +### Production (new) + +- `src/gps_denied_onboard/components/c2_vpr/ultra_vpr.py` — + `UltraVprStrategy` class implementing the `VprStrategy` Protocol. + Constructor wires `InferenceRuntimeCut` + `DescriptorIndexCut` + + `UltraVprBackbonePreprocessor` + `DescriptorNormaliser` + + `FaissBridge` + `FdrClient` + `Clock`. Module-level + `create(config, descriptor_index, inference_runtime)` factory + consumed by `build_vpr_strategy`. Module-level + `DESCRIPTOR_DIM = 512` constant (NOT a config knob; spec + § Constraints "preprocessing parameters are hard-coded"). + Engine load + output-shape assertion at `create()` time. +- `src/gps_denied_onboard/components/c2_vpr/_preprocessor_ultra_vpr.py` — + `UltraVprBackbonePreprocessor` implementing the C2-internal + `BackbonePreprocessor` Protocol. Decode → centre-crop around the + camera calibration's principal point (cx, cy from `intrinsics_3x3`, + fallback to geometric centre + WARN log when calibration is + absent) → resize (384, 384) → ImageNet mean/std → FP16 NCHW. + +### Production (modified) + +- None. The composition-root `_register_strategy_architecture` + helper (added in B46) already no-ops cleanly for strategies that + do not expose `MODEL_NAME` / `architecture_factory`. UltraVPR + consumes a pre-compiled `.trt` engine (no PyTorch `nn.Module`), + so no registration is needed. + +### Tests (new) + +- `tests/unit/c2_vpr/test_ultra_vpr.py` — 29 tests covering all + 12 ACs (with per-AC variants for AC-2, AC-6, AC-7, AC-8, AC-9, + AC-11) + preprocessor contract (5 tests, including + `test_preprocessor_mean_std_correct_on_grey_image`) + + constructor validation (2) + FDR record emission (1) + + no-architecture-registration verification (1). + +### Docs (new) + +- `_docs/03_implementation/reviews/batch_47_review.md` — + per-batch code review (PASS_WITH_WARNINGS). + +## Acceptance Criteria Coverage + +All 12 ACs of AZ-337 have at least one covering unit test: + +| AC | Description | Status | +|----|-------------|--------| +| AC-1 | Protocol conformance | covered | +| AC-2 | L2-norm == 1.0 ± 1e-3 FP16 (512,) | covered + single-stage L2 enforcement spy | +| AC-3 | Deterministic across 3 calls | covered | +| AC-4 | `retrieve_topk` == k, label="ultra_vpr", sorted | covered | +| AC-5 | `descriptor_dim()` stable returns 512 | covered | +| AC-6 | Engine output shape mismatch → `ConfigError` | covered (shape + missing-key variants) | +| AC-7 | `VprBackboneError` on forward failure + ERROR log + FDR | covered (3 variants) | +| AC-8 | `VprPreprocessError` on corrupt image + ERROR log + FDR | covered (2 variants) | +| AC-9 | Calibration absent → geometric centre + WARN log | covered + offset-changes-crop control test | +| AC-10 | `IndexUnavailableError` propagated unchanged | covered | +| AC-11 | Composition-root wiring + INFO log "c2.vpr.ready" + BUILD-flag gate | covered (TRT accept + ONNX-RT accept + PyTorch reject) | +| AC-12 | Top-1 distance > threshold → WARN log via FaissBridge | covered | + +## Test Results + +- **Full unit suite**: `1637 passed / 80 environment-skipped / 0 failed` + in ~66s. Up from 1608 at the close of Batch 46 (+29 new tests). +- **Focused per-component**: `c2_vpr/test_ultra_vpr.py` 29/29 PASS; + `c2_vpr/test_net_vlad.py` 31/31 PASS (no regression); + `c2_vpr/test_faiss_bridge.py` 22/22 PASS (no regression). +- **Lint**: `ruff check` clean on every new file. +- **AZ-507 layering lint**: `test_ac6_only_compose_root_imports_concrete_strategies` PASS. + +## Architectural Decisions + +1. **No composition-root changes**. UltraVPR uses a pre-compiled + `.trt` engine; there is no PyTorch `nn.Module` to register with + C7's architecture registry. The strategy module therefore does + NOT expose `MODEL_NAME` / `architecture_factory`. The + composition-root `_register_strategy_architecture` helper + (added in B46 for NetVLAD) no-ops cleanly for this case — + no code change needed there. Verified by + `test_create_does_not_register_pytorch_architecture`. + +2. **`DESCRIPTOR_DIM = 512` is a module constant, not a config knob**. + UltraVPR's research code drop ships with a fused embedding head + that produces D=512. Unlike NetVLAD (whose Linear PCA layer + makes D a tunable knob), UltraVPR's D is weights-coupled. + Making it a config knob would let an operator silently break + AC-2.1b recall floor. AC-5 / AC-6 / AC-7 all assume 512. + +3. **Single-stage L2 normalisation, enforced by spy**. UltraVPR is + single-stage L2 (no intra-cluster step like NetVLAD). The test + `test_ac2_embedding_is_single_stage_l2_no_intra_cluster_path` + uses a spy normaliser to assert that + `intra_cluster_normalise` is NEVER called — a regression + guard against a future refactor accidentally introducing the + two-stage path (which would silently degrade recall on the + Derkachi corpus). + +4. **Engine load + output-shape assertion at `create()` time, NOT + first frame**. Misconfiguration surfaces at startup (5 seconds + into the binary) rather than 17 minutes into a flight at the + first VPR query. Matches Constraint § 5 of the task spec. + +5. **Principal-point-aware centre-crop**. The preprocessor extracts + `(cx, cy)` from `intrinsics_3x3` and anchors the square crop + there, falling back to geometric centre + WARN log when + calibration is absent. Matches the upstream UltraVPR contract; + relevant for wide-angle cameras where the principal point is + not at the image centre. + +6. **Pattern parity with NetVLAD where semantics permit**. Method + shapes for `embed_query` / `retrieve_topk` / `_emit_*` mirror + `NetVladStrategy` line-for-line; UltraVPR-specific paths + (single-stage L2, `"embedding"` output key, TRT runtime, no + architecture registry, principal-point crop) are clearly + localised. This makes AZ-339 / AZ-340 (the remaining C2 + strategies) drop into the same skeleton with minimal new + architectural decisions. + +7. **AC-12 delegation to `FaissBridge`**. The top-1 distance WARN + log is owned by `FaissBridge` (B45); UltraVPR inherits this + path for free via the same one-line `retrieve_topk` delegation + that NetVLAD uses — one production touchpoint, two strategies. + +## Carried-over Findings + +- **F1 from cumulative review 43-45 / Batch 46**: `_iso_ts_from_clock` + duplicated across modules — Batch 47 adds the 7th copy + (`ultra_vpr.py` line 296). AZ-508 hygiene PBI exists for + consolidation. Recommend prioritising AZ-508 before AZ-339 / + AZ-340 add copies #8 and #9. +- **F2 from Batch 46**: spec→implementation drift on C7 API names. + AZ-337 spec also exhibits the same drift (`runtime.forward()`, + `runtime.load_engine()` — neither exists in v1.0.0 Protocol). + Affects upcoming AZ-339 / AZ-340 / AZ-358 / AZ-349. Spec-hygiene + PBI recommended. + +## New Findings (per Batch 47 code review) + +- **F3 (Low, Test-Robustness)**: `_extract_principal_point` uses + `(cx, cy) == (0, 0)` zero-detection to identify "no calibration". + Safe in production (no real camera has `cx == 0 and cy == 0`) + but the heuristic exists only because the dataclass field is + typed `Any` instead of `Optional`. Tighten when calibration + becomes properly optional. + +## Jira Tracker + +- AZ-337 transitioned: To Do → In Progress → In Testing. +- Task spec archived: `_docs/02_tasks/todo/AZ-337_c2_ultra_vpr.md` → + `_docs/02_tasks/done/AZ-337_c2_ultra_vpr.md`. + +## Next + +Batches 45 / 46 / 47 close the C2 production path: AZ-336 (Protocol), +AZ-341 (FaissBridge), AZ-338 (NetVLAD baseline), AZ-337 (UltraVPR +primary). The Documentary Lead has both baseline + primary backbones +operational; default-strategy wiring works end-to-end at the +composition root. + +Per the autodev orchestrator loop: select Batch 48. Top candidates: + +- **AZ-508** — ISO-timestamp helper consolidation (2pt). Closes F1 + before AZ-339 / AZ-340 add copies #8 and #9. Strongly recommended. +- **AZ-339** — MegaLoc + MixVPR strategies (5pt). Two strategies in + one task; same skeleton as UltraVPR but different backbones. +- **AZ-340** — SelaVPR + EigenPlaces + SALAD strategies (5pt). + Three strategies in one task; closes the C2 alternative-backbone + buffet. +- **AZ-358** — C4 OpenCV/GTSAM pose estimator (5pt). Opens a new + component (C4); changes pace from C2-heavy streak. +- **AZ-389** — C5 internal orthorectifier (3pt). Different + component (C5); unblocks mid-flight tile generation. +- Spec-hygiene PBI to address F2 from B46 / B47 (refresh + AZ-339 / AZ-340 against the live C7 v1.0.0 Protocol). + +The next cumulative review (batches 46-48) will trigger after Batch +48 lands. diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index c19e6ac..ac13892 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -8,9 +8,9 @@ status: in_progress sub_step: phase: 7 name: batch-loop - detail: "batch 47 — AZ-337 (C2 UltraVPR primary backbone)" + detail: "batch 47 complete — selecting batch 48" retry_count: 0 cycle: 1 tracker: jira -last_completed_batch: 46 +last_completed_batch: 47 last_cumulative_review: batches_43-45