mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 08:31:13 +00:00
[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 <cursoragent@cursor.com>
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user