mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 22:01:14 +00:00
chore: WIP pre-implement
Bundled hygiene commit before cycle-3 /implement (AZ-776, AZ-777). Mixes two concerns by user choice (autodev option B): - Cycle-3 autodev artifacts not yet committed by Step 9 (new-task): task specs for AZ-776 / AZ-777 under _docs/02_tasks/todo/ and the updated _docs/02_tasks/_dependencies_table.md. - Accumulated skill / rule tooling maintenance under .cursor/ (skills: autodev, code-review, decompose, deploy, implement, new-task, plan, refactor, retrospective, test-spec; rules: coderule, cursor-meta, meta-rule, testing; new release skill scaffolding). - Autodev bootstrap state: _docs/_autodev_state.md (step 10 in_progress) and _docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md (replay timestamp refreshed; gtsam 4.2 still numpy<2-only). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Dependencies Table
|
||||
|
||||
**Date**: 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path)
|
||||
**Total Tasks**: 163 (122 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19
|
||||
**Total Complexity Points**: 535 (402 product + 133 blackbox-test) — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt
|
||||
**Date**: 2026-05-21 (cycle-3 Step 9 New Task — added AZ-776 (3pt open-loop ESKF composition profile via `c4_pose.enabled` flag, no deps, epic AZ-602) + AZ-777 (5pt Derkachi C6 reference tile cache + FAISS descriptor index from OSM/CARTO basemap, depends on AZ-776, epic AZ-602). Both unblock the 7 currently-`@xfail`-masked Derkachi e2e tests on Jetson; AZ-776 unblocks 5 (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap), AZ-777 unblocks the remaining 2 (AC-3 + AZ-699 real-flight verdict). Earlier 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path)
|
||||
**Total Tasks**: 165 (124 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19
|
||||
**Total Complexity Points**: 543 (410 product + 133 blackbox-test) — +3pt AZ-776 + 5pt AZ-777 added 2026-05-21 — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt
|
||||
|
||||
Dependencies columns list only the tracker-ID portion (descriptive tail
|
||||
text in each task spec is omitted here for table-readability). The
|
||||
@@ -183,6 +183,8 @@ are all declared and documented below under **Cycle Check**.
|
||||
| AZ-700 | T4: Replay map visualization (estimated vs ground-truth tracks) | 3 | AZ-699 | AZ-696 |
|
||||
| AZ-701 | T5: HTTP Replay API service (POST tlog+video, return GPS fixes + map) | 5 | AZ-699, AZ-700 | AZ-696 |
|
||||
| AZ-702 | T6: Topotek KHP20S30 camera calibration (factory-sheet approximation) | 1 | None | AZ-696 |
|
||||
| AZ-776 | Open-loop ESKF composition profile (c4_pose.enabled flag) | 3 | None | AZ-602 |
|
||||
| AZ-777 | Derkachi C6 reference tile cache + FAISS descriptor index (OSM/CARTO) | 5 | AZ-776 | AZ-602 |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# Open-loop ESKF composition profile for the airborne binary
|
||||
|
||||
**Task**: AZ-776_eskf_open_loop_composition_profile
|
||||
**Name**: Make the c5_state=eskf path runnable end-to-end by allowing c4_pose to be excluded from the airborne composition
|
||||
**Description**: Introduce an explicit `c4_pose.enabled: bool` config flag (default True). When False, the airborne bootstrap skips C4 wiring entirely, the C5 ESKF estimator composes alongside C1 (and any other configured components) with no iSAM2 graph handle, and `gps-denied-replay` exits 0 on the Derkachi fixture with one EstimatorOutput per video frame. Unblocks five of the seven `@xfail`-masked Derkachi e2e tests on Jetson.
|
||||
**Complexity**: 3 points
|
||||
**Dependencies**: None
|
||||
**Component**: runtime_root / c4_pose / c5_state
|
||||
**Tracker**: AZ-776
|
||||
**Epic**: AZ-602
|
||||
|
||||
## Problem
|
||||
|
||||
The c5_state strategy `eskf` is registered (`_STRATEGY_REGISTRY`),
|
||||
documented as the IT-12 mandatory simple baseline (`eskf_baseline.py`
|
||||
line 30), and has unit tests
|
||||
(`tests/unit/c5_state/test_az386_eskf_baseline.py`). It has NEVER been
|
||||
composition-tested end-to-end. When the airborne binary is configured
|
||||
with `config.components.c5_state.strategy = eskf`, the bootstrap fails
|
||||
at compose time:
|
||||
|
||||
```
|
||||
PoseEstimatorConfigError: build_pose_estimator: isam2_graph_handle does
|
||||
not satisfy the C4 ISam2GraphHandle Protocol (missing get_pose_key /
|
||||
update / compute_marginals / last_anchor_age_ms?)
|
||||
```
|
||||
|
||||
Root cause: `EskfStateEstimator._build_eskf_state_estimator` returns
|
||||
`(estimator, handle=None)` by design — the ESKF has no GTSAM graph.
|
||||
`airborne_bootstrap._build_c5_state_estimator_pair` (line 779) seeds
|
||||
`pre_constructed['c5_isam2_graph_handle'] = None`.
|
||||
`_c4_pose_wrapper` (line 371) extracts the None.
|
||||
`pose_factory.build_pose_estimator` (line 127) rejects it.
|
||||
|
||||
The IT-12 "ESKF is the mandatory simple baseline" mandate has no
|
||||
executable production path today — the baseline exists at the component
|
||||
layer but not at the system layer. Every Derkachi e2e attempt with
|
||||
`c5_state=eskf` exits non-zero at compose time, producing 0 JSONL rows
|
||||
and masking every downstream behaviour the heavy ACs are supposed to
|
||||
exercise.
|
||||
|
||||
## Outcome
|
||||
|
||||
- The airborne bootstrap can compose a binary with `c1_vio` + `c5_state(strategy=eskf)` end-to-end against a real `gps-denied-replay` invocation without composing `c4_pose` and without a stub or no-op pose estimator masking the absence.
|
||||
- The composition-mode selection is explicit in YAML — operator sets `config.components.c4_pose.enabled = False` (or omits the block entirely) to opt into the open-loop profile.
|
||||
- `_run_replay_loop` in `runtime_root/__init__.py` is exercised end-to-end on Jetson by a non-`xfail` integration test (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap in `tests/e2e/replay/test_derkachi_1min.py` un-xfail and pass).
|
||||
- The replay protocol document records the new open-loop ESKF variant alongside the existing full-GTSAM path so future readers do not have to reverse-engineer it from the code.
|
||||
- AZ-602 (E2E Tier-1 harness rehabilitation epic) advances by 5/7 of the Derkachi xfails removed; the remaining 2 (AC-3 and AZ-699) are AZ-777 territory.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Add an `enabled: bool = True` field to `C4PoseConfig` (and the corresponding YAML schema in `config/schema.py`).
|
||||
- In `airborne_bootstrap.build_pre_constructed`: when `_replay_omits_component_block(config, "c4_pose")` OR `config.components["c4_pose"].enabled is False`, skip `_build_c5_state_estimator_pair`'s iSAM2-handle requirement; the C5 ESKF estimator still builds, but the handle slot is absent from the returned dict.
|
||||
- In `airborne_bootstrap._AIRBORNE_REGISTRATIONS` (or wherever `compose_root` walks the component slugs): exclude `c4_pose` from the topological walk when the config flag says so. The downstream edge `_C5_STATE_DEPENDS_ON = ("c1_vio", "c4_pose")` becomes `("c1_vio",)` for the open-loop profile (or the dependency-walker treats a deselected slug as a no-op edge).
|
||||
- Validation in `compose_root`: when `c4_pose.enabled is False`, refuse `c5_state.strategy = gtsam_isam2` with a clear `CompositionError` — the gtsam_isam2 estimator needs a real handle, so the open-loop profile is only valid against ESKF. Symmetric refusal when `c4_pose.enabled is True` AND `c5_state.strategy = eskf` (the ESKF still returns `handle=None`, so c4 would still reject).
|
||||
- `tests/unit/runtime_root/`: composition test that exercises the open-loop profile end-to-end with `compose_root`, asserting (i) the returned components dict contains `c1_vio` and `c5_state` but NOT `c4_pose`; (ii) `gps-denied-replay --config <open-loop yaml>` exits 0 against a minimal fixture and emits one EstimatorOutput per video frame.
|
||||
- `tests/unit/runtime_root/`: regression test that the live (full-GTSAM) path still composes identically after the flag is added — `c4_pose.enabled` unset defaults to True and the existing topological walk is unchanged.
|
||||
- `_docs/02_document/contracts/replay/replay_protocol.md`: add an `## Open-loop ESKF variant` section documenting the per-frame loop pseudocode (mirrors the existing GTSAM one minus C2–C4 stages) plus the YAML shape that selects it.
|
||||
- `_docs/02_document/adr/`: create the ADR folder if absent, then write `ADR-012_open_loop_eskf_composition.md` recording the `c4_pose.enabled` flag, the strategy-pairing rules, and why the per-component flag was chosen over a top-level `composition_profile` knob.
|
||||
- `tests/e2e/replay/test_derkachi_1min.py`: remove the `@pytest.mark.xfail` decorators on AC-1 (line 61), AC-2 (line 138), AC-5 (line 413), AC-6 realtime (line 453), AC-6 asap (line 479). Update the test conftest or fixture to drive the open-loop YAML profile.
|
||||
- `_docs/02_document/components/06_c4_pose.md`: amend the component doc to document the `enabled` flag.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Building the Derkachi C6 reference tile cache + descriptor index (AZ-777 territory).
|
||||
- Implementing C2/C3/C4 anchoring against Derkachi imagery (depends on AZ-777).
|
||||
- Un-xfailing `test_ac3_within_100m_80pct_of_ticks` (`test_derkachi_1min.py` line 174) — that test needs satellite anchoring, requires AZ-777.
|
||||
- Un-xfailing `test_az699_real_flight_validation_emits_verdict_and_report` (`test_derkachi_real_tlog.py` line 174) — also requires AZ-777 for accuracy gate.
|
||||
- Changing the production default — this task makes ESKF *runnable*, not *the default*.
|
||||
- Renaming any existing config field, YAML block, or factory function (per AZ-618 umbrella's "MUST NOT touch any per-component factory signature" constraint).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Open-loop profile composes**
|
||||
Given a YAML config with `config.components.c4_pose.enabled = False` and `config.components.c5_state.strategy = eskf`
|
||||
When `compose_root(config, pre_constructed=build_pre_constructed(config))` runs
|
||||
Then it returns a components dict containing `c1_vio` and `c5_state` but NOT `c4_pose`, and no `PoseEstimatorConfigError` is raised
|
||||
|
||||
**AC-2: Full GTSAM profile unchanged**
|
||||
Given a YAML config with `config.components.c4_pose` absent or `enabled = True` and `config.components.c5_state.strategy = gtsam_isam2`
|
||||
When `compose_root` runs
|
||||
Then the returned components dict contains `c1_vio`, `c4_pose`, `c5_state` exactly as before this task
|
||||
|
||||
**AC-3: Invalid pairing rejected**
|
||||
Given a YAML config that pairs `c4_pose.enabled = True` with `c5_state.strategy = eskf`, OR `c4_pose.enabled = False` with `c5_state.strategy = gtsam_isam2`
|
||||
When `compose_root` runs
|
||||
Then it raises `CompositionError` naming both the conflicting fields and the valid pairings
|
||||
|
||||
**AC-4: Replay binary exits 0 on Derkachi open-loop**
|
||||
Given the Derkachi 1-min fixture and a YAML config selecting the open-loop ESKF profile
|
||||
When `gps-denied-replay --config <open-loop.yaml> --video <derkachi> --tlog <derkachi> --output <out.jsonl>` runs on Jetson
|
||||
Then it exits with code 0 and `<out.jsonl>` contains one EstimatorOutput line per video frame (±10 % to allow for VIO INIT-state skips on the first few frames)
|
||||
|
||||
**AC-5: Replay protocol documents the variant**
|
||||
Given the updated `_docs/02_document/contracts/replay/replay_protocol.md`
|
||||
When a reader looks for the open-loop ESKF composition
|
||||
Then there is a dedicated `## Open-loop ESKF variant` section with per-frame loop pseudocode and the YAML shape that selects it
|
||||
|
||||
**AC-6: ADR records the design choice**
|
||||
Given the new `_docs/02_document/adr/ADR-012_open_loop_eskf_composition.md`
|
||||
When a reader looks for why the per-component flag was chosen
|
||||
Then ADR-012 records the alternatives considered (per-component flag vs. top-level profile vs. implicit rule) and the rationale for the per-component flag
|
||||
|
||||
**AC-7: Five Derkachi e2e xfails removed**
|
||||
Given `tests/e2e/replay/test_derkachi_1min.py` after this task
|
||||
When the file is read
|
||||
Then the `@pytest.mark.xfail` decorators on AC-1 (line 61), AC-2 (line 138), AC-5 (line 413), AC-6 realtime (line 453), AC-6 asap (line 479) are removed and the underlying tests pass on the Jetson harness
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Performance**
|
||||
- No measurable latency regression on the full-GTSAM path (the new flag check is O(1) at compose time, zero overhead per-frame).
|
||||
- Open-loop profile per-frame latency budget remains the same 400 ms p95 (the path is strictly shorter — fewer components — so this should improve, not regress).
|
||||
|
||||
**Compatibility**
|
||||
- Default `c4_pose.enabled = True` so every existing config file behaves identically without modification.
|
||||
- No changes to `compose_root` public signature, `build_pre_constructed` public signature, or any per-component factory signature (per AZ-618 umbrella constraint).
|
||||
|
||||
**Reliability**
|
||||
- Invalid YAML pairings (open-loop + gtsam_isam2 OR full-GTSAM + eskf) MUST fail loud at compose time, never silently fall back. An operator who misconfigures the profile sees a clear error referencing both conflicting fields, not a downstream `KeyError` or stack trace.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
| AC Ref | What to Test | Required Outcome |
|
||||
|--------|--------------|------------------|
|
||||
| AC-1 | `compose_root` with open-loop YAML → returned components dict | Contains `c1_vio`, `c5_state`; no `c4_pose` key |
|
||||
| AC-1 | `build_pre_constructed` with open-loop YAML → no `c5_isam2_graph_handle` key in result | Key absent from dict |
|
||||
| AC-2 | `compose_root` with default full-GTSAM YAML → returned components dict | Contains `c1_vio`, `c4_pose`, `c5_state` (regression guard) |
|
||||
| AC-3 | `compose_root` with `c4_pose.enabled=False` + `c5_state.strategy=gtsam_isam2` | `CompositionError` raised naming both fields |
|
||||
| AC-3 | `compose_root` with `c4_pose.enabled=True` + `c5_state.strategy=eskf` | `CompositionError` raised naming both fields |
|
||||
| C4PoseConfig | `enabled` field defaults to `True` when unset in YAML | Round-trips through `load_config` |
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|------------------------|--------------|-------------------|----------------|
|
||||
| AC-4 | Derkachi 1-min fixture + open-loop YAML profile | `gps-denied-replay` invoked on Jetson via `scripts/run-tests-jetson.sh` with `RUN_REPLAY_E2E=1 GPS_DENIED_TIER=2` | Exit 0; one EstimatorOutput line per video frame ±10 % | Perf, Compat |
|
||||
| AC-7 | `test_derkachi_1min.py` AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap | Tests run on Jetson after this task | All five pass (xfail decorators removed) | Reliability |
|
||||
|
||||
## Constraints
|
||||
|
||||
- The flag MUST be `c4_pose.enabled: bool`, not a top-level `composition_profile` knob and not an implicit ADR-only rule. User chose this shape during AZ-776 scoping (cycle-3 Step 9, 2026-05-21). Rationale recorded in ADR-012.
|
||||
- The flag MUST NOT change the C4 estimator's own runtime behaviour — it ONLY affects whether the component is wired into the composition graph. The C4 estimator itself remains a fully functional component.
|
||||
- The `_run_replay_loop` warning at `runtime_root/__init__.py` (`replay_loop.satellite_anchoring_not_wired`, emitted every 25 frames) is the existing honest signal for the open-loop path. Do NOT remove it; AZ-777 will replace the wiring it warns about. The warning remains visible in the JSONL output during AZ-776 → AZ-777 transition.
|
||||
- ADR file naming follows the existing pattern (`ADR-NNN_<slug>.md`). Increment past the highest current ADR number; if `_docs/02_document/adr/` is empty (as today), start at ADR-012 to leave room for the ADR-001..ADR-011 references already in code+docs.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: Topological-walk regression on the live path**
|
||||
- *Risk*: Modifying `_AIRBORNE_REGISTRATIONS` or the walker logic could silently break the live GTSAM composition.
|
||||
- *Mitigation*: AC-2 is a regression guard. Run the full `tests/unit/runtime_root/` suite before declaring done.
|
||||
|
||||
**Risk 2: ESKF estimator surfaces a latent bug under the open-loop wiring**
|
||||
- *Risk*: Until this task lands, `EskfStateEstimator` has never been driven by a real composition root + replay binary. Latent bugs (state initialization, IMU preintegration handoff, FDR write paths) may surface only at AC-4.
|
||||
- *Mitigation*: Reproduce on Tier-1 Colima first (no GPU needed for ESKF). Use the Tier-2 Jetson run only as the AC-4 confirmation, not as the debug environment. Any non-trivial ESKF fix surfaced this way becomes a sibling ticket — do not in-scope creep.
|
||||
|
||||
**Risk 3: Replay protocol doc inconsistency**
|
||||
- *Risk*: The new `## Open-loop ESKF variant` section may contradict the existing "Wire C1–C5 + C6 + C7 + C13 exactly as in the live composition" line at line 188.
|
||||
- *Mitigation*: Amend line 188 to say "Wire C1–C5 + C6 + C7 + C13 exactly as in the live composition **for the full-GTSAM profile**; see `## Open-loop ESKF variant` for the open-loop case". One sentence; no ambiguity.
|
||||
|
||||
### ADR Impact
|
||||
|
||||
> Affects ADR-001 (composition root is single registration site): unchanged — still one registration site. The new flag selects *what* is registered, not *where*.
|
||||
> Affects ADR-002 (build-flag gate is the lazy-loading boundary): unchanged — `BUILD_*` flags still gate physical linkage; `c4_pose.enabled` is a *runtime* selection on top of a built strategy.
|
||||
> Affects ADR-009 (interface-first DI): unchanged — the open-loop path still resolves through interfaces, just with the C4 interface unbound.
|
||||
> Affects ADR-011 (replay is a configuration, not a separate composition root): unchanged — open-loop is a configuration of the same airborne composition root; the new flag is orthogonal to the live/replay split.
|
||||
@@ -0,0 +1,193 @@
|
||||
# Derkachi C6 reference tile cache + descriptor index (OSM/CARTO basemap)
|
||||
|
||||
**Task**: AZ-777_derkachi_c6_reference_fixture
|
||||
**Name**: Build the C6 reference tile cache + FAISS descriptor index for the Derkachi flight bbox so the full-protocol C1+C2+C3+C4+C5 pipeline can produce satellite anchors during e2e replay
|
||||
**Description**: Add a reproducible build script that downloads OSM/CARTO basemap tiles for the Derkachi flight bbox (approx 50.05–50.15 lat, 36.05–36.15 lon), pre-computes feature descriptors via the same C7 backbone the airborne binary uses (DINOv2 or the configured VPR backbone), populates the C6 tile store + FAISS HNSW index, and integrates them into the e2e replay harness. Unblocks the two remaining `@xfail`-masked Derkachi tests on Jetson (`test_ac3_within_100m_80pct_of_ticks` and `test_az699_real_flight_validation_emits_verdict_and_report`) and produces the first honest AZ-699 accuracy verdict.
|
||||
**Complexity**: 5 points
|
||||
**Dependencies**: AZ-776_eskf_open_loop_composition_profile
|
||||
**Component**: c6_tile_cache / e2e fixtures / input_data
|
||||
**Tracker**: AZ-777
|
||||
**Epic**: AZ-602
|
||||
|
||||
## Problem
|
||||
|
||||
The Derkachi e2e fixture
|
||||
(`_docs/00_problem/input_data/flight_derkachi/`) ships the real
|
||||
flight inputs (video, tlog, IMU, camera calibration) but DOES NOT
|
||||
ship the C6 tile-cache artifacts that the replay protocol requires
|
||||
the operator's pre-flight C10 stage to produce:
|
||||
|
||||
- `c6_tile_store` — persistent JPEG tiles covering the flight area at the chosen zoom levels
|
||||
- `c6_descriptor_index` — FAISS index of VPR-backbone descriptors over those tiles
|
||||
|
||||
Without these artifacts:
|
||||
|
||||
- C2 VPR has no haystack to look up against — `c2_vpr.lookup` returns empty.
|
||||
- C3 matcher has nothing to match against (depends on C2 candidates).
|
||||
- C4 pose has no anchors — cannot estimate satellite-frame pose.
|
||||
- C5 state has no anchors to fuse — runs open-loop on VIO only.
|
||||
|
||||
When `c5_state.strategy = gtsam_isam2` (the default that AZ-699's e2e
|
||||
exercises), the composition reaches the per-frame loop but
|
||||
`iSAM2.update` crashes at frame 1 with:
|
||||
|
||||
```
|
||||
EstimatorFatalError: compute_marginals failed: Attempting to at the
|
||||
key 'x2', which does not exist in the Values.
|
||||
```
|
||||
|
||||
— because no C4 anchor was ever inserted (C2/C3/C4 have nothing to
|
||||
match against).
|
||||
|
||||
AZ-776 (sibling, prerequisite) makes the open-loop C1+C5(ESKF)
|
||||
composition runnable, but that path skips C2–C4 entirely and accepts
|
||||
unbounded drift. To validate the FULL protocol-compliant pipeline
|
||||
against Derkachi — i.e. AC-3 (`≤100 m for 80 % of ticks`) and the
|
||||
AZ-699 horizontal-error verdict — we need real C6 fixtures.
|
||||
|
||||
The replay protocol (`replay_protocol.md` line 214) explicitly states
|
||||
"`BUILD_FAISS_INDEX` is ON in the airborne binary (live and replay
|
||||
alike). C2 in replay queries the **real** C6 `FaissDescriptorIndex`,
|
||||
populated by the pre-flight C10 build. This is the architectural
|
||||
change vs. v1.0.0 of this contract." We have no such build for
|
||||
Derkachi.
|
||||
|
||||
## Outcome
|
||||
|
||||
- A reproducible build script under `scripts/` produces the C6 artifacts (`tile_store` + `descriptor_index`) given the Derkachi bbox + zoom levels + camera calibration, deterministically on a clean checkout, in under 30 minutes on a developer workstation.
|
||||
- Reference imagery source is OSM-tile-server-distributed basemap (CARTO Voyager or equivalent CC-BY-licensed source). Each tile carries the source URL + license attribution in its metadata sidecar.
|
||||
- The Derkachi fixture directory documents the build invocation; tiles + index are EITHER committed to the repo (if total size ≤ 100 MB) OR built on-demand from the script (if larger) — decision recorded in the fixture README.
|
||||
- `tests/e2e/replay/conftest.py`'s `operator_pre_flight_setup` fixture is replaced (or extended) to mount the prebuilt artifacts into the e2e-runner container. The mock-suite-sat-service stub is retired for the C6-served paths (it remains for the C12 operator-workflow AC-8).
|
||||
- After this task ships (with AZ-776), un-xfail `test_ac3_within_100m_80pct_of_ticks` (`test_derkachi_1min.py` line 174) AND `test_az699_real_flight_validation_emits_verdict_and_report` (`test_derkachi_real_tlog.py` line 174); both pass on the Jetson harness.
|
||||
- The first honest AZ-699 verdict lands at `_docs/06_metrics/real_flight_validation_<YYYY-MM-DD>.md` with the full horizontal-error distribution. Whether the verdict is PASS or FAIL is the honest finding — this task's success is that the verdict is *produced* against the real pipeline, not that it is necessarily green.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- `scripts/build_derkachi_c6_fixture.py` (or equivalent module under `e2e/fixtures/derkachi_c6/`): reproducible build pipeline that:
|
||||
- Reads the Derkachi bbox + zoom levels from a small YAML config (`tests/fixtures/derkachi_c6/bbox.yaml`).
|
||||
- Downloads OSM/CARTO basemap tiles into `<output>/tiles/{zoom}/{x}/{y}.jpg` mirroring `satellite-provider`'s on-disk layout (per architecture principle #5).
|
||||
- Computes per-tile descriptors via the same C7 backbone the airborne binary uses (configurable; defaults to whatever `config.components.c2_vpr.strategy`'s feature dimension is — e.g. UltraVPR or NetVLAD).
|
||||
- Builds a FAISS HNSW index over the descriptors, writes via `faiss.write_index` + atomicwrites + SHA-256 content-hash gate (per D-C10-3).
|
||||
- Emits a manifest JSON recording tile count, bbox, zoom levels, backbone, descriptor dimension, FAISS index parameters, source URL template, license, and the SHA-256 of every artifact.
|
||||
- `tests/fixtures/derkachi_c6/bbox.yaml`: the bbox + zoom + backbone config consumed by the build script. Committed.
|
||||
- `tests/fixtures/derkachi_c6/README.md`: how to rebuild + license attribution + estimated artifact size.
|
||||
- Build the artifacts once, decide commit vs on-demand:
|
||||
- If total size ≤ 100 MB → commit to `_docs/00_problem/input_data/flight_derkachi/c6_cache/` (under LFS).
|
||||
- If > 100 MB → keep build-on-demand only, document the build invocation in the fixture README, and add a `scripts/run-tests-jetson.sh` pre-step that builds if absent.
|
||||
- `tests/e2e/replay/conftest.py`: replace `operator_pre_flight_setup`'s mock with a real fixture that mounts the prebuilt artifacts into the e2e-runner container at the expected paths (`/opt/tiles/`, `/opt/descriptor_index.index`).
|
||||
- `docker-compose.test.yml` + `docker-compose.test.jetson.yml`: mount the artifacts into the `e2e-runner` service (bind mount or named volume), set `c6_tile_store.path` + `c6_descriptor_index.path` env vars.
|
||||
- `tests/e2e/replay/test_derkachi_1min.py`: remove the `@pytest.mark.xfail` decorator on AC-3 (line 174).
|
||||
- `tests/e2e/replay/test_derkachi_real_tlog.py`: remove the `@pytest.mark.xfail` decorator on AZ-699 (line 174).
|
||||
- `_docs/00_problem/input_data/flight_derkachi/README.md`: document the new C6 artifacts + build invocation + license attribution.
|
||||
- `_docs/02_document/contracts/c6_tile_cache/`: if a contract file exists for the descriptor-index format, append a Consumer entry naming this fixture; if not, no new contract needed.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Multi-flight fixtures — just Derkachi. (Other flights would each need their own C6 build invocation.)
|
||||
- Online tile download at test time — the e2e harness MUST remain offline (per replay protocol Invariant 5 / RESTRICT-SAT-1 / NFT-SEC-02; the docker compose `internal: true` network). The build script downloads tiles AT BUILD TIME from the developer workstation; the e2e harness only sees the prebuilt artifacts.
|
||||
- Replacing the mock-suite-sat-service stub for the C12 operator-workflow `test_ac8_operator_workflow` test — that test exercises the D-PROJ-2 ingest contract which is parent-suite work, not in scope here.
|
||||
- Building tiles for any backbone other than the airborne-default. If the operator wants a different backbone, they re-run the script with a different `--backbone` flag; this task only commits the default-backbone artifacts.
|
||||
- Switching the airborne C6 backend from Postgres-mirroring to anything else — the build script writes the same on-disk layout the production C6 expects.
|
||||
- AZ-776 (sibling): this task does NOT introduce the `c4_pose.enabled` flag or the open-loop composition profile. AZ-776 must land first to unblock the open-loop xfails (AC-1, AC-2, AC-5, AC-6); this task targets the full-GTSAM xfails (AC-3, AZ-699).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Reproducible build**
|
||||
Given a clean checkout
|
||||
When `python scripts/build_derkachi_c6_fixture.py --output tests/fixtures/derkachi_c6/out --bbox tests/fixtures/derkachi_c6/bbox.yaml` runs
|
||||
Then it produces a `tiles/` directory in the documented `{zoom}/{x}/{y}.jpg` layout, a FAISS `.index` file with a SHA-256-verified content hash, and a `manifest.json` recording tile count, bbox, backbone, descriptor dimension, FAISS parameters, source URL template, license, and per-artifact SHA-256, in under 30 minutes on a developer workstation
|
||||
|
||||
**AC-2: License attribution**
|
||||
Given the produced artifacts
|
||||
When the manifest is inspected
|
||||
Then it records the tile source URL template, the license name (CC-BY-3.0 or CC-BY-4.0 as applicable), and the attribution string the operator must surface in any derived publication
|
||||
|
||||
**AC-3: Offline e2e harness**
|
||||
Given the prebuilt C6 artifacts mounted into the e2e-runner container
|
||||
When `scripts/run-tests-jetson.sh` runs on Jetson with `RUN_REPLAY_E2E=1 GPS_DENIED_TIER=2` and the Docker compose network is `internal: true`
|
||||
Then the test harness never reaches out to any external host; all C6 queries are served from the mounted artifacts
|
||||
|
||||
**AC-4: Full-protocol e2e passes**
|
||||
Given AZ-776 has landed AND the C6 artifacts are mounted AND the YAML config selects `c5_state.strategy = gtsam_isam2` with `c4_pose.enabled = True`
|
||||
When `gps-denied-replay` runs the Derkachi 1-min fixture on Jetson
|
||||
Then it exits with code 0, emits one EstimatorOutput per video frame, `test_ac3_within_100m_80pct_of_ticks` un-xfails and passes (≥80 % of ticks within 100 m of ground truth), and the per-frame loop emits `replay.satellite_anchor_inserted` log lines (not the existing `satellite_anchoring_not_wired` warning)
|
||||
|
||||
**AC-5: AZ-699 produces an honest verdict**
|
||||
Given AZ-776 has landed AND the C6 artifacts are mounted AND the real flight video + factory calibration are present (already are)
|
||||
When `test_az699_real_flight_validation_emits_verdict_and_report` runs on Jetson
|
||||
Then it un-xfails, the test runs to completion within the 15-min NFR budget, and `_docs/06_metrics/real_flight_validation_<YYYY-MM-DD>.md` records the horizontal-error distribution with the honest PASS/FAIL verdict against the ≥80 % within 100 m gate
|
||||
|
||||
**AC-6: Fixture README documents rebuild**
|
||||
Given the updated `_docs/00_problem/input_data/flight_derkachi/README.md`
|
||||
When a new contributor reads it
|
||||
Then it documents (i) what C6 artifacts now exist, (ii) the exact `python scripts/build_derkachi_c6_fixture.py …` invocation to rebuild, (iii) the license attribution operators must propagate, (iv) the size-on-disk decision (committed vs. build-on-demand)
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Performance**
|
||||
- Build script completes in ≤ 30 minutes on a developer workstation (Apple Silicon or x86 Linux, no GPU required for OSM tile download + descriptor pre-compute via the CPU-fallback path of the backbone).
|
||||
- Built artifacts do not regress the airborne C2 lookup latency budget — the FAISS HNSW parameters MUST match what production C6 expects (M, efConstruction, efSearch); the index is built once and never rebuilt at runtime.
|
||||
|
||||
**Compatibility**
|
||||
- Tile on-disk layout `{zoom}/{x}/{y}.jpg` MUST be byte-equivalent to `satellite-provider`'s layout (architecture principle #5) so a future post-landing upload would be byte-identical.
|
||||
- FAISS index format MUST be loadable by the airborne `c6_descriptor_index.FaissDescriptorIndex` impl without code changes.
|
||||
- Descriptor dimension MUST match the configured C7 backbone's output dimension — the build script asserts this at start.
|
||||
|
||||
**Reliability**
|
||||
- Build script MUST fail loud on partial downloads (network error, HTTP 429/500, malformed tile) rather than silently producing an incomplete tile store. Resume-from-partial is allowed but each resumed run re-verifies SHA-256 of every committed tile.
|
||||
- The SHA-256 content-hash gate on the FAISS index (per D-C10-3) MUST be enforced — operator can verify a downloaded fixture matches what was built.
|
||||
|
||||
**Security**
|
||||
- Reference imagery URLs MUST be HTTPS. Tile metadata MUST record the exact source URL so license auditors can verify attribution.
|
||||
- No API keys committed to the repo — if the chosen tile source requires registration, the build script reads the key from an env var and documents the env var name in the fixture README.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
| AC Ref | What to Test | Required Outcome |
|
||||
|--------|--------------|------------------|
|
||||
| AC-1 | Build script produces `tiles/`, `descriptor_index.index`, `manifest.json` on a small mock bbox | All three artifacts exist, manifest fields populated |
|
||||
| AC-1 | SHA-256 of `descriptor_index.index` recorded in manifest matches actual file hash | Hashes match |
|
||||
| AC-2 | Manifest records source URL template + license + attribution | All three fields non-empty |
|
||||
| AC-2 | License field matches the source's documented license | Round-trips against an enum |
|
||||
| AC-6 | Fixture README documents the build invocation | Invocation string greps cleanly |
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|------------------------|--------------|-------------------|----------------|
|
||||
| AC-3 | Prebuilt C6 artifacts + e2e-runner with `internal: true` network | Run `scripts/run-tests-jetson.sh` end-to-end | No outbound network calls observed by Docker network logs; all C6 queries return from local index | Security, Reliability |
|
||||
| AC-4 | AZ-776 landed + C6 artifacts mounted + full-GTSAM YAML | `test_ac3_within_100m_80pct_of_ticks` un-xfailed | Test passes (≥80 % of ticks within 100 m); `satellite_anchor_inserted` log lines visible | Perf, Compat |
|
||||
| AC-5 | AZ-776 landed + C6 artifacts mounted + real flight video + factory calibration | `test_az699_real_flight_validation_emits_verdict_and_report` un-xfailed | Test runs to completion ≤ 15 min, verdict report written to `_docs/06_metrics/` | Perf |
|
||||
|
||||
## Constraints
|
||||
|
||||
- Reference imagery source MUST be OSM/CARTO basemap (CC-BY-licensed). Operator chose this during AZ-777 scoping (cycle-3 Step 9, 2026-05-21) over Maxar Open Data (license uncertainty for in-repo redistribution) and video-self-orthorectification (self-referential, makes AC-3 a smoke test rather than a real accuracy gate). The trade-off — lower-resolution reference imagery may produce a higher residual on the AC-3 horizontal-error metric than satellite imagery would — is an HONEST finding the AZ-699 verdict will surface.
|
||||
- The build script MUST NOT depend on `satellite-provider` running. The script's only network dependency is the chosen OSM/CARTO tile server (HTTPS, public, no auth).
|
||||
- The committed artifact size budget (if AC-6 chooses commit-to-repo) is 100 MB total across `tiles/` + `descriptor_index.index`. Over budget → switch to build-on-demand, document in README.
|
||||
- The `mock-suite-sat-service` stub stays in place for `test_ac8_operator_workflow` — that test exercises the D-PROJ-2 contract which this task does not address.
|
||||
- Per replay protocol Invariant 5: ZERO outbound network from the e2e-runner. The build script runs on the developer workstation; the harness only sees prebuilt artifacts.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: OSM basemap residual is too coarse for the AC-3 threshold**
|
||||
- *Risk*: AC-3's `≤100 m for 80 %` gate may be physically unmeetable when the reference imagery is OSM rasterized basemap (street-level features, not satellite features) — the visual descriptors may not lock against the aerial nav-camera frames at all.
|
||||
- *Mitigation*: This is an honest discovery. If AC-3 still fails after this task lands, the failure mode shifts from "no anchors at all" (current) to "anchors exist but VPR similarity is too low to produce ≥80 % within 100 m". The AZ-699 verdict report will surface the actual horizontal-error distribution; if it lands at e.g. p50 = 250 m, that becomes evidence for a follow-up ticket to switch to satellite imagery. The xfail is removed in either case because the test now exercises the real pipeline — the verdict, not the xfail, becomes the honest signal.
|
||||
|
||||
**Risk 2: Tile source rate-limits or goes offline mid-build**
|
||||
- *Risk*: Public OSM/CARTO tile servers may rate-limit or temporarily go down, breaking reproducibility on a re-build.
|
||||
- *Mitigation*: Build script implements exponential backoff + resume-from-partial. Document the chosen tile-server URL in the fixture README so an operator can swap to a mirror if needed. If commit-to-repo is chosen for the artifacts, future re-builds are unnecessary — the committed artifacts are the source of truth.
|
||||
|
||||
**Risk 3: Repo size pressure if artifacts are committed**
|
||||
- *Risk*: Tile store + FAISS index could exceed 100 MB depending on bbox + zoom levels; committing them under LFS still costs LFS storage and bandwidth.
|
||||
- *Mitigation*: First build run measures the size. If under 100 MB → commit. If over → build-on-demand documented in README + `scripts/run-tests-jetson.sh` pre-step. Either choice is acceptable per AC-6.
|
||||
|
||||
**Risk 4: Backbone descriptor dimension mismatch**
|
||||
- *Risk*: If the operator changes the airborne C2 backbone (UltraVPR → NetVLAD, etc.) without rebuilding the index, the FAISS load will fail at runtime with a dimension mismatch.
|
||||
- *Mitigation*: Manifest records the descriptor dimension. C6 loader asserts the manifest's dimension matches the configured backbone's output dimension at compose time; mismatch surfaces as an `AirborneBootstrapError` naming both numbers + the rebuild invocation.
|
||||
|
||||
### ADR Impact
|
||||
|
||||
> Affects ADR-001 (composition root is single registration site): unchanged — C6 is built outside the composition root by the operator-side build script; the airborne binary still just loads what's on disk.
|
||||
> Implements architecture principle #4 (no in-air network I/O) and principle #5 (all persistent imagery in `satellite-provider` on-disk layout) — this is the FIRST executable artifact that demonstrates both principles end-to-end against a real flight.
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
## Current Step
|
||||
flow: existing-code
|
||||
step: 12
|
||||
name: Test-Spec Sync
|
||||
status: not_started
|
||||
step: 10
|
||||
name: Implement
|
||||
status: in_progress
|
||||
sub_step:
|
||||
phase: 0
|
||||
name: awaiting-invocation
|
||||
detail: ""
|
||||
detail: "cycle 3 implement: AZ-776 first (no deps), then AZ-777 (depends on AZ-776)"
|
||||
retry_count: 0
|
||||
cycle: 2
|
||||
cycle: 3
|
||||
tracker: jira
|
||||
last_completed_batch: 102
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# D-CROSS-CVE-1 opencv-python pin deferred — gtsam/numpy ABI block
|
||||
|
||||
**Recorded**: 2026-05-11T02:55+03:00 (Europe/Kyiv)
|
||||
**Last replay attempt**: 2026-05-21T12:14+03:00 (Europe/Kyiv) — replay re-checked
|
||||
at start of next `/autodev` invocation (~19h after prior check at 2026-05-20
|
||||
17:34). PyPI re-queried via `pip index versions gtsam`: only `gtsam 4.2`
|
||||
is published. Replay condition (numpy>=2 stable wheels) still NOT met.
|
||||
Leftover remains open.
|
||||
**Last replay attempt**: 2026-05-21T13:10+03:00 (Europe/Kyiv) — replay re-checked
|
||||
at start of next `/autodev` invocation (~56 min after prior check at
|
||||
2026-05-21 12:14). PyPI re-queried via `python3 -m pip index versions
|
||||
gtsam`: only `gtsam 4.2` is published. Replay condition (numpy>=2 stable
|
||||
wheels) still NOT met. Leftover remains open.
|
||||
**Status**: deferred-non-user (replay when upstream gtsam wheels target numpy>=2)
|
||||
|
||||
## What is blocked
|
||||
|
||||
Reference in New Issue
Block a user