mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 23:41: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:
|
sub_step:
|
||||||
phase: 7
|
phase: 7
|
||||||
name: batch-loop
|
name: batch-loop
|
||||||
detail: "batch 47 — AZ-337 (C2 UltraVPR primary backbone)"
|
detail: "batch 47 complete — selecting batch 48"
|
||||||
retry_count: 0
|
retry_count: 0
|
||||||
cycle: 1
|
cycle: 1
|
||||||
tracker: jira
|
tracker: jira
|
||||||
last_completed_batch: 46
|
last_completed_batch: 47
|
||||||
last_cumulative_review: batches_43-45
|
last_cumulative_review: batches_43-45
|
||||||
|
|||||||
Reference in New Issue
Block a user