# GPS-Denied Onboard Pose Estimation — Planning Report

> Generated 2026-05-09 at the close of greenfield Plan (Step 3) — Final / Quality Checklist phase.
> Sources: `architecture.md`, `glossary.md`, `system-flows.md`, `data_model.md`, `risk_mitigations.md`, `epics.md`, `tests/traceability-matrix.md`, `tests/*.md`, `components/**/*.md`, `common-helpers/*.md`, `deployment/*.md`.

## Executive Summary

A Jetson Orin Nano Super-hosted Python+C++ companion replaces real GPS for fixed-wing UAVs in GPS-denied/spoofed environments by fusing pre-flight-cached `satellite-provider` tiles, live nav-camera frames (3 Hz), and FC IMU/attitude (100–200 Hz) through a hierarchical pipeline `VIO → VPR → re-rank → matcher → AdHoP-conditional refinement → pose → state` and emits a WGS84 fix with honest 6×6 covariance + provenance label to the FC at 5 Hz, all inside a 400 ms p95 latency budget. Plan decomposes the system into **14 components + 8 common helpers**, **22 numbered system-level Mermaid flows across F1–F10**, **39 acceptance criteria + 20 restrictions** with **92.4 % inclusive / 89.8 % strict test coverage**, and **19 Jira epics (AZ-244..AZ-262)** with a total estimated effort of **~295–477 story points** (range across t-shirt sizes).

## Problem Statement

Fixed-wing UAVs operating in eastern/southern Ukraine must continue to navigate when the FC's GPS is denied or actively spoofed. The companion provides a drop-in replacement WGS84 fix derived from pre-cached `satellite-provider` imagery + onboard vision + FC IMU, with honest covariance and a `{satellite_anchored | visual_propagated | dead_reckoned}` provenance label, while remaining read-only against `satellite-provider` in flight (post-landing tile uploads run from the operator workstation only). Mission profile: 8 h flights, ~60 km/h cruise, ≤1 km AGL, ≤400 km² total cached area, 25 W TDP, -20 °C to +50 °C envelope.

## Architecture Overview

Single Python-with-C++-extensions monolithic process per binary track on the Jetson Orin Nano Super (8 GB shared LPDDR5, 25 W, JetPack 6.2). Cross-component coupling routes through a shared GTSAM substrate so posterior covariance is recovered natively (`Marginals.marginalCovariance`, D-C5-5 = (c)). Interface-first components live under `src/components/<component>/` with constructor-injected dependencies; per-binary CMake `BUILD_*` flags select which `Strategy` implementations are linked into each artifact (`production-binary` = OKVIS2 + KltRansac + UltraVPR + …; `research-binary` = all candidate strategies for the IT-12 comparative study). The operator-side Tile Manager (C11) is a separate binary excluded from the airborne CMake target — process-level isolation is the primary defence for ADR-004 (no in-flight `satellite-provider` I/O in either direction).

**Technology stack**: Python 3.10 + C++17, TensorRT 10.3 (JetPack 6.2 pin) / ONNX Runtime + TRT EP / pure PyTorch FP16 baseline; OpenCV ≥ 4.12.0; GTSAM iSAM2 + `IncrementalFixedLagSmoother`; FAISS HNSW; PostgreSQL 16 (`tiles` schema mirrored from `satellite-provider`); pymavlink + MAVLink 2.0 signing for ArduPilot, YAMSPy + INAV-Toolkit MSP2 for iNav.

**Deployment**: two execution tiers — Tier-1 workstation Docker (fast/cheap, AC-bound jobs that do not require Jetson hardware) and Tier-2 self-hosted Jetson runner (AC-NEW-1 / NFT-PERF / NFT-LIM); pre-flight + post-landing operator tooling runs on the operator workstation; companion image is bare JetPack 6.2 (no Docker on Tier-2 to keep INT8 calibration cache trustworthy per D-C10-6).

See `architecture.md` for the full ADR set (ADR-001..ADR-009), 12 architectural principles, and per-component intent statements.

## Component Summary

| # | Component | Purpose | Dependencies | Epic |
|---|-----------|---------|-------------|------|
| 01 | C1 VIO | Pluggable `VioStrategy` (OKVIS2 default, VINS-Mono research, KltRansac mandatory simple-baseline) producing per-frame `VioOutput` | C7, helpers (IMU preintegrator, SE3 utils) | AZ-254 |
| 02 | C2 VPR | Pre-cached satellite-tile retrieval (UltraVPR primary, MegaLoc/MixVPR/SelaVPR/EigenPlaces/NetVLAD/SALAD candidates) behind `VprStrategy` | C6, C7 | AZ-255 |
| 03 | C2.5 Re-rank | Single-pair LightGlue inlier-count rerank K=10 → N=3 | C2, C7, helper LightGlue runtime | AZ-256 |
| 04 | C3 Matcher | DISK+LightGlue cross-domain matching + RANSAC + reprojection residual filter | C2.5, C7, helper RANSAC filter | AZ-257 |
| 05 | C3.5 AdHoP | Conditional refinement when initial reprojection residual exceeds threshold | C3, C7 | AZ-258 |
| 06 | C4 Pose | OpenCV `solvePnPRansac` (IPPE) wrapped in GTSAM `Marginals` for native 6×6 covariance | C3.5, C5 (shared GTSAM substrate) | AZ-259 |
| 07 | C5 State | GTSAM iSAM2 + `IncrementalFixedLagSmoother` (K=10–20); spoof-promotion gate; AC-4.5 internal smoothing | C1, C4, C8 inbound, C13 | AZ-260 |
| 08 | C6 Tile Cache | PostgreSQL btree spatial index + filesystem `./tiles/{zoom}/{x}/{y}.jpg` mirroring `satellite-provider`; FAISS HNSW VPR descriptor index | E-BOOT, E-CC-LOG, E-CC-CONF | AZ-250 |
| 09 | C7 Inference Runtime | TensorRT 10.3 engines (Polygraphy / trtexec / IBuilderConfig hybrid); ORT+TRT EP fallback; PyTorch FP16 baseline | E-BOOT, E-CC-CONF, E-CC-FDR-CLIENT | AZ-249 |
| 10 | C8 FC + GCS Adapter | `pymavlink` `GPS_INPUT` for ArduPilot (signed) + `MSP2_SENSOR_GPS` for iNav (unsigned, accepted residual risk); honest 6×6 → 2×2 covariance projection; GCS 1–2 Hz downsampled telemetry | C5, E-CC-CONF, E-CC-LOG | AZ-261 |
| 11 | C10 Pre-flight Cache Provisioning | Builds model-derived cache (descriptors, engines, manifest, content hashes); F2 takeoff verifier; does NOT touch `satellite-provider` (network I/O lives in C11) | C6, C7, E-CC-LOG | AZ-252 |
| 12 | C11 Tile Manager | Operator-side `TileDownloader` (pre-flight) + `TileUploader` (post-landing — no internal flight-state gate after Batch 44; gating lives in C12); excluded from airborne image | C6, E-CC-CONF, E-CC-LOG | AZ-251 |
| 13 | C12 Operator Pre-flight Orchestrator | CLI subcommands (`download`, `build-cache`, `upload-pending`, `reloc-confirm`); `PostLandingUploadOrchestrator` (gates on `flight_footer.clean_shutdown`); `OperatorReLocService` (AC-3.4 hint via `OperatorCommandTransport`); sector classification UI hook; FDR retrieval helpers | C10, C11, E-CC-LOG | AZ-253 |
| 14 | C13 Flight Data Recorder | Per-flight ≤64 GB NVM ring (estimates + IMU + emitted MAVLink + health + mid-flight tiles + ≤0.1 Hz failed-tile thumbnails); raw nav/AI-cam frames excluded | E-BOOT, E-CC-LOG, E-CC-CONF, E-CC-FDR-CLIENT | AZ-248 |

**Cross-cutting epics** (not components, but shared concerns): E-BOOT (AZ-244), E-CC-LOG (AZ-245), E-CC-CONF (AZ-246), E-CC-FDR-CLIENT (AZ-247).
**System-level test epic**: E-BBT (AZ-262) parents the FT/NFT scenarios across all components.

**Implementation order** (from dependency graph in `epics.md`):

1. **Phase 1 (foundation, parallel)**: E-BOOT (AZ-244) → E-CC-LOG (AZ-245), E-CC-CONF (AZ-246), E-CC-FDR-CLIENT (AZ-247).
2. **Phase 2 (data + runtime + recorder)**: E-C13 (AZ-248), E-C7 (AZ-249), E-C6 (AZ-250).
3. **Phase 3 (operator tooling chain)**: E-C11 (AZ-251) → E-C10 (AZ-252) → E-C12 (AZ-253).
4. **Phase 4 (perception pipeline)**: E-C1 (AZ-254), E-C2 (AZ-255) → E-C2.5 (AZ-256) → E-C3 (AZ-257) → E-C3.5 (AZ-258) → E-C4 (AZ-259).
5. **Phase 5 (state + emission)**: E-C5 (AZ-260) → E-C8 (AZ-261).
6. **Phase 6 (system tests)**: E-BBT (AZ-262) — hardens behind every other epic.

## System Flows

| Flow | Description | Key Components |
|------|-------------|---------------|
| F1 | Pre-flight cache provisioning | C11 (TileDownloader), C6, C10, C12 |
| F2 | Takeoff load (cold-start TTFF, AC-NEW-1) | C10, C7, C6, C8, C5 |
| F3 | Steady-state per-frame estimation (the F3 hot path) | C1, C2, C2.5, C3, C3.5, C4, C5, C8, C13 |
| F4 | Mid-flight tile generation + local cache write | C5 (orthorectifier subpath), C6, C13 |
| F5 | Visual blackout + spoofed-GPS failsafe (AC-NEW-8) | C1, C5, C8 (STATUSTEXT) |
| F6 | Sharp-turn / disconnected-segment re-localization | C2, C2.5, C3, C5 |
| F7 | Spoofing-promotion via EKF source-set switch (AC-NEW-2) | C5 (SourceLabelStateMachine), C8 (AP D-C8-2) |
| F8 | Companion reboot recovery | C13 (FDR replay), C5, C8 |
| F9 | GCS telemetry stream (AC-6.1) | C8 (GcsAdapter) |
| F10 | Post-landing tile upload (D-PROJ-2 contract) | C11 (TileUploader), C6, C12 |

Reference `system-flows.md` for the per-flow Mermaid sequence diagrams + flowcharts; `_docs/02_document/diagrams/components.drawio` is the visual companion for component boundaries.

## Risk Summary

| Level | Count | Key Risks |
|-------|-------|-----------|
| Critical | 0 | — |
| High | 3 | R01 (D-PROJ-2 ingest endpoint not yet shipped), R03 (MAVLink-2.0 signing handshake no precedent — IT-3 gated), R11 (AC-NEW-4/AC-NEW-7 multi-flight statistical headroom — D-PROJ-3 deferred) |
| Medium | 8 | R02 (ADR-004 process-isolation regression), R04 (TRT engine SM/JP/TRT mismatch), R05 (iSAM2 silent factor-add failure), R06 (VPR top-1 false positive), R07 (premature spoof re-promotion), R08 (tile freshness drift in active-conflict sectors), R10 (`Marginals` latency under thermal throttle), R12 (single deployment camera) |
| Low | 3 | R09 (per-flight signing key compromise), R13 (FDR queue overrun), R14 (C2.5 ↔ C3 LightGlue circular dependency — resolved via shared helper) |

**Iterations completed**: 1 (initial Step 4 risk pass; R02 enforcement scope extended, R14 resolved via helper ownership).
**All Critical/High risks mitigated**: Yes — R01 has e2e mock-suite-sat-service fixture + leftover tracking; R03 is gated by IT-3 with documented D-C8-2-FALLBACK options (ADR-008); R11 is mitigated by AC-text relaxation 2026-05-09 (Monte-Carlo-with-CI) plus D-PROJ-3 carryforward.

Reference `risk_mitigations.md` for the full register, per-risk trigger conditions, and detailed mitigations.

## Test Coverage

The test suite is organised as scenario specs (no source code yet). Per-component tests live under each component's `tests.md`; cross-component / system-level scenarios live under `_docs/02_document/tests/`.

### Per-component scenario counts (from each `components/*/tests.md`)

| Component | Component-internal tests file |
|-----------|------------------------------|
| C1 | `components/01_c1_vio/tests.md` |
| C2 | `components/02_c2_vpr/tests.md` |
| C2.5 | `components/03_c2_5_rerank/tests.md` |
| C3 | `components/04_c3_matcher/tests.md` |
| C3.5 | `components/05_c3_5_adhop/tests.md` |
| C4 | `components/06_c4_pose/tests.md` |
| C5 | `components/07_c5_state/tests.md` |
| C6 | `components/08_c6_tile_cache/tests.md` |
| C7 | `components/09_c7_inference/tests.md` |
| C8 | `components/10_c8_fc_adapter/tests.md` |
| C10 | `components/11_c10_provisioning/tests.md` |
| C11 | `components/12_c11_tilemanager/tests.md` |
| C12 | `components/13_c12_operator_orchestrator/tests.md` |
| C13 | `components/14_c13_fdr/tests.md` |

### System-level scenario suites (`_docs/02_document/tests/`)

| File | Scenario family |
|------|-----------------|
| `tests/blackbox-tests.md` | FT-P-* (positive functional) + FT-N-* (negative functional) |
| `tests/performance-tests.md` | NFT-PERF-* (Tier-2) |
| `tests/resource-limit-tests.md` | NFT-LIM-* |
| `tests/security-tests.md` | NFT-SEC-* |
| `tests/resilience-tests.md` | NFT-RES-* |
| `tests/test-data.md` | Fixture inventory (Derkachi flight + AerialVL S03 + e2e mock-suite-sat-service) |
| `tests/environment.md` | Test environment + Tier-1/Tier-2 split |
| `tests/traceability-matrix.md` | Single source of truth for AC ↔ scenario coverage |

### Acceptance Criteria + Restrictions coverage (from `tests/traceability-matrix.md`, revised 2026-05-09)

| Category | Total | Covered | PARTIAL | Not Covered | Coverage % (PARTIAL counted half) |
|----------|-------|---------|---------|-------------|------------------------------------|
| Acceptance Criteria | 39 | 35 | 2 | 2 | 92.3 % |
| Restrictions | 20 | 18 | 1 | 1 | 92.5 % |
| **Total** | **59** | **53** | **3** | **3** | **92.4 %** (strict 89.8 %) |

Both the inclusive reading (PARTIAL = covered) and the strict reading clear the 75 % gate with margin. Remaining PARTIAL / NOT COVERED rows: AC-8.6 scene-change subset (labeled change-pair dataset not available), AC-NEW-5 hot-soak chamber (physical hardware), AC-7.1 / AC-7.2 + RESTRICT-CAM-2 (no AI-camera fixture), RESTRICT-HW-2 chamber portion (paired with AC-NEW-5).

## Epic Roadmap

| Order | Epic | Component | T-shirt | Story points | Dependencies |
|-------|------|-----------|---------|--------------|-------------|
| 1 | AZ-244: E-BOOT — Bootstrap & Initial Structure | repo scaffolding | M | 13–21 | — |
| 2 | AZ-245: E-CC-LOG — Structured JSON Logging | cross-cutting | S | 5–8 | E-BOOT |
| 3 | AZ-246: E-CC-CONF — Configuration & Composition Root | cross-cutting | S | 5–8 | E-BOOT |
| 4 | AZ-247: E-CC-FDR-CLIENT — FDR Producer Client | cross-cutting | M | 8–13 | E-BOOT, E-CC-LOG |
| 5 | AZ-248: E-C13 — Flight Data Recorder | C13 | L | 21–34 | E-BOOT, E-CC-LOG, E-CC-CONF, E-CC-FDR-CLIENT |
| 6 | AZ-249: E-C7 — On-Jetson Inference Runtime | C7 | L | 21–34 | E-BOOT, E-CC-CONF, E-CC-FDR-CLIENT |
| 7 | AZ-250: E-C6 — Tile Cache + Spatial Index | C6 | M | 13–21 | E-BOOT, E-CC-LOG, E-CC-CONF |
| 8 | AZ-251: E-C11 — Tile Manager | C11 | M | 13–21 | E-C6, E-CC-CONF, E-CC-LOG |
| 9 | AZ-252: E-C10 — Pre-flight Cache Provisioning | C10 | M | 13–21 | E-C6, E-C7, E-CC-LOG |
| 10 | AZ-253: E-C12 — Operator Pre-flight Orchestrator | C12 | M | 13–21 | E-C10, E-C11, E-CC-LOG |
| 11 | AZ-254: E-C1 — Visual / Visual-Inertial Odometry | C1 | XL | 34–55 | E-BOOT, E-CC-FDR-CLIENT, E-C7 |
| 12 | AZ-255: E-C2 — Visual Place Recognition | C2 | L | 21–34 | E-C6, E-C7, E-CC-FDR-CLIENT |
| 13 | AZ-256: E-C2.5 — Inlier-based Re-rank | C2.5 | S | 5–8 | E-C2, E-C7, E-C6 (shared LightGlue helper) |
| 14 | AZ-257: E-C3 — Cross-Domain Matcher | C3 | L | 21–34 | E-C2.5, E-C7 |
| 15 | AZ-258: E-C3.5 — AdHoP-Conditional Refinement | C3.5 | M | 8–13 | E-C3, E-C7 |
| 16 | AZ-259: E-C4 — Pose Estimator | C4 | M | 13–21 | E-C3.5, E-C5 (shared GTSAM substrate) |
| 17 | AZ-260: E-C5 — State Estimator | C5 | XL | 34–55 | E-C1, E-C4 (shared graph), E-CC-FDR-CLIENT |
| 18 | AZ-261: E-C8 — FC + GCS Adapter | C8 | L | 21–34 | E-C5, E-CC-CONF, E-CC-LOG |
| 19 | AZ-262: E-BBT — Blackbox Tests (FT/NFT scenarios) | system-level tests | M | 13–21 | every component epic |

**Total estimated effort**: 295–477 story points across 19 epics (sum of t-shirt-band lower / upper bounds from `epics.md`). The two XL epics (E-C1 = AZ-254 and E-C5 = AZ-260) carry ~22 % of the upper-bound estimate alone.

## Key Decisions Made

| # | Decision | Rationale | Alternatives Rejected |
|---|----------|-----------|----------------------|
| 1 | Single Python+C++ monolith per binary track (ADR-001) | Latency budget (400 ms p95) + GTSAM substrate sharing + Jetson 8 GB shared memory | Multi-process (D-C1-1-SUB-A=(b)) — too much IPC overhead; ROS — adds runtime weight without flight-controller benefit |
| 2 | Build-time exclusion of unused `Strategy` implementations via per-binary CMake `BUILD_*` flags (ADR-002) | Binary size on 8 GB shared Jetson, AC-NEW-1 30 s boot budget, accidental-selection risk | Runtime-only selection — bigger binary, larger attack surface, harder cold-start; component licenses do not drive this decision |
| 3 | Shared GTSAM substrate between C4 (pose) and C5 (state) (ADR-003, D-C5-5 = (c)) | Native posterior covariance via `Marginals.marginalCovariance`; one numerical foundation | Independent EKF in C5 + Jacobian-only covariance — cannot deliver honest 6×6 |
| 4 | Process-level isolation of operator-side Tile Manager (ADR-004) — C11 not linked into airborne image | AC-8.4 in-flight no-write; defeats reflection / config-error attack vectors | Runtime gate alone — reflection / DI bug could re-introduce code path |
| 5 | Two execution tiers (ADR-005): Tier-1 workstation Docker + Tier-2 self-hosted Jetson | Cost (Jetson runner saturated) + AC-NEW-1 must run on real hardware | Single-tier Jetson-only — too slow / expensive for pre-merge CI |
| 6 | D-CROSS-LATENCY-1 hybrid (ADR-006): K=3 baseline auto-degrades to K=2 + Jacobian covariance under thermal throttle | Preserves AC-4.1 at +50 °C ambient at the cost of ~5–10 % accuracy | Static K=2 — wastes covariance precision in nominal conditions; static K=3 — blows the budget under throttle |
| 7 | Spoof-promotion gate (ADR-008): re-promote only after ≥10 s `gps_health == STABLE_NON_SPOOFED` AND visual-consistency check passes | AC-NEW-2 / AC-NEW-8 floor; defends against attacker turning spoof off briefly | Time-only gate (≥30 s) — slower mission recovery, still fool-able by transient honest GPS during attack |
| 8 | Interface-first components with constructor injection (ADR-009) | Multiple interchangeable strategies on the same interface (C1 has 3, C2 has 6+, C8 has 2) — selection via composition root only | Service-locator / global registry — couples runtime to import order, breaks tests, breaks build-time exclusion |
| 9 | OpenCV pin **temporarily relaxed** to `>=4.11.0.86,<4.12` (was `>=4.12.0` per original Mode B Fact #112) | `gtsam==4.2.1` (only published wheel) is built against numpy 1.x ABI; `opencv-python>=4.12` requires numpy>=2; D-CROSS-CVE-1 follow-up tracked in `_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md` and replays the `>=4.12.0` pin once gtsam ships numpy-2 wheels (or an alternative SE(3) backend is adopted) | Keep numpy>=2 — `gtsam.Pose3` SEGFAULTs; force `opencv-python>=4.12` — uninstallable; drop gtsam — loses ADR-003 honest 6×6 covariance |
| 10 | DISK + LightGlue replaces SuperPoint+SuperGlue for cross-domain matching (D-C3-1 = (a)) | License — SP+SG is Magic Leap noncommercial canonical; DISK+LightGlue is BSD-3-Clause | SuperPoint+SuperGlue — license incompatibility; XFeat — promising but unproven cross-domain |
| 11 | AC-NEW-4 / AC-NEW-7 text relaxed 2026-05-09 to Monte-Carlo-over-current-data with stated 95 % CI | D-PROJ-3 multi-flight fixture acquisition is out of scope this cycle; literal "≥100 flights" wording cannot be met | Block planning on D-PROJ-3 — cycle would not close; relaxed wording is documented residual risk in R11 |

## Open Questions

| # | Question | Impact | Assigned To |
|---|----------|--------|-------------|
| 1 | D-PROJ-2: parent-suite `satellite-provider` ingest endpoint + multi-flight voting layer not yet implemented service-side | F10 post-landing upload depends on this; R01; e2e mock-suite-sat-service fixture stands in for tests | Parent suite (cross-workspace; tracked in `_docs/_process_leftovers/2026-05-09_satellite-provider-design-tasks.md`) |
| 2 | D-PROJ-3: multi-flight fixture acquisition (Maxar Open Data Ukraine + AerialVL S03 + own multi-flight data) | AC-NEW-4 / AC-NEW-7 statistical headroom; R11 carryforward to next Plan cycle | Project lead |
| 3 | D-C8-2 runtime gate: companion-driven `MAV_CMD_SET_EKF_SOURCE_SET` switch lacks deployed precedent | R03 — IT-3 ArduPilot SITL validation is the lock gate; D-C8-2-FALLBACK options recorded in ADR-008 | Onboard team (gated by IT-3) |
| 4 | D-C2-12: DINOv2-feature-based matcher evaluation as a possible C3 backbone alternative | Could close D-C3-1 retrain cost concern; carryforward to next Plan cycle | Onboard team |
| 5 | AC-NEW-5 hot-soak chamber test (25 W @ +50 °C, 8 h, no throttle) | Physical hardware required; release-tag-blocking gate; out of scope for data-acquisition this cycle | Project lead |
| 6 | AC-7.1 / AC-7.2 / RESTRICT-CAM-2: AI-camera object localization pipeline | Out of scope for this cycle; deferred to a follow-up Plan cycle scoped to AI-camera integration | Future cycle (next Plan) |
| 7 | AC-8.6 scene-change subset: needs labeled change-pair dataset | Stale-tile match in active-conflict sectors — scene-change recall unmeasured (scale-ratio half is covered) | Future cycle |

## Artifact Index

| File | Description |
|------|-------------|
| `architecture.md` | System architecture, ADR set (ADR-001..ADR-009), 12 architectural principles, technology stack |
| `glossary.md` | Canonical project terminology (locked Phase 2a.0) |
| `system-flows.md` | F1–F10 Mermaid sequence diagrams + flowcharts + per-flow data flow tables |
| `data_model.md` | PostgreSQL `tiles` (mirrored from `satellite-provider`) + flight + manifest schema; FAISS / TRT artifact layout; FDR record schema |
| `risk_mitigations.md` | 14-row risk register (R01..R14) with per-risk mitigation + contingency |
| `epics.md` | Local plan E-* IDs, t-shirt sizes, story-point ranges, dependencies, child-issue breakdowns; canonical `E-*` ↔ `AZ-NN` mapping |
| `components/01_c1_vio/description.md` … `components/14_c13_fdr/description.md` | Per-component spec (interface, implementations, dependencies, ACs, NFRs, risks) |
| `components/01_c1_vio/tests.md` … `components/14_c13_fdr/tests.md` | Per-component test scenarios |
| `common-helpers/01_helper_imu_preintegrator.md` … `common-helpers/08_helper_descriptor_normaliser.md` | Shared helpers (IMU preintegrator, SE3 utils, LightGlue runtime, WGS converter, SHA-256 sidecar, engine filename schema, RANSAC filter, descriptor normaliser) |
| `tests/traceability-matrix.md` | AC ↔ scenario coverage; restriction ↔ scenario coverage |
| `tests/blackbox-tests.md`, `tests/performance-tests.md`, `tests/security-tests.md`, `tests/resource-limit-tests.md`, `tests/resilience-tests.md` | System-level FT / NFT scenario specs |
| `tests/test-data.md`, `tests/environment.md` | Fixture inventory + Tier-1/Tier-2 environment definition |
| `deployment/containerization.md`, `deployment/ci_cd_pipeline.md`, `deployment/environment_strategy.md`, `deployment/observability.md`, `deployment/deployment_procedures.md` | Deployment plan |
| `diagrams/components.drawio` | Component-level diagram (visual companion to `components/`) |
| `diagrams/flows/00_index.md` | Per-flow index pointing into `system-flows.md` |

## Cycle 1 Implementation Status

> Appended 2026-05-19 as part of greenfield Step 13 (Update Docs, task mode). Captures the as-built deltas from this planning report after 97 implementation batches + Step 11 Run Tests + Step 12 Test-Spec Sync. Sources: `_docs/03_implementation/implementation_completeness_cycle1_report.md`, `_docs/03_implementation/run_tests_step11_report.md`, `_docs/02_tasks/done/` (165 task specs), `_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md`.

### Composition-root architecture additions (not in the original Plan)

The Plan-era `architecture.md` § ADR-009 (interface-first, constructor injection) and module-layout.md's "Composition root is `runtime_root/`" rule remain correct, but cycle 1 added two cross-cutting Tier-1 mechanisms inside `runtime_root/` that the Plan did not anticipate. Both are operational prerequisites for `compose_root()` reaching takeoff:

- **`_STRATEGY_REGISTRY` + `register_strategy(...)` API (AZ-591)** — a module-level `dict[(component_slug, strategy_name)] → factory` populated per-binary. `runtime_root.airborne_bootstrap.register_airborne_strategies()` fills 7 airborne slots (`c1_vio`, `c2_vpr`, `c2_5_rerank`, `c3_matcher`, `c3_5_adhop`, `c4_pose`, `c5_state`) with `tier="airborne"`. Without this, `compose_root()` raises `StrategyNotLinkedError` on the first config-driven strategy lookup. The registry is the runtime-side complement to ADR-002 build-time exclusion: the build chooses which strategies are even available to register, the registry chooses which one this binary serves.
- **`pre_constructed` kwarg + `build_pre_constructed(config)` (AZ-618 umbrella → subtasks AZ-619..AZ-624)** — `compose_root(config, pre_constructed=...)` now requires a 12-key dict of infrastructure objects (`c13_fdr`, `c6_descriptor_index`, `c6_tile_store`, `c7_inference`, `c3_lightglue_runtime`, `c3_feature_extractor`, `c282_ransac_filter`, `c5_wgs_converter`, `c5_se3_utils`, `c5_isam2_graph_handle`, `c5_imu_preintegrator`, `clock`). The airborne entrypoint builds these in 6 dependency-ordered phases (A=c13/clock → F=wire_main); GPU-touching builders gate on the corresponding `BUILD_*` env flag. Missing keys raise `AirborneBootstrapError` with the missing-key name; tests stub by passing the same `pre_constructed=...` kwarg.

These additions sit inside `runtime_root/`; no component crosses the import boundary AZ-507 enforces. Both still need to be folded into `architecture.md` (ADR-009 sibling notes) and `module-layout.md` § "Composition Root" — deferred to a follow-up `/document` task pass.

### BLOCKED tasks with parked Tier-2 follow-ups

Per the implement skill § 15 "PASS-with-BLOCKED" allowable terminal classification (see `implementation_completeness_cycle1_report.md`):

| Task | Status | Reason | Parked Tier-2 follow-up |
|------|--------|--------|-------------------------|
| AZ-332 — C1 OKVIS2 production-default `VioStrategy` | BLOCKED | Tier-2 prerequisites: CI build env + Jetson hardware + DBoW2 vocab artifact. Ships a Python facade + pybind11 binding skeleton; first `add_frame` raises until upstream `okvis::ThreadedSlam` wiring lands. | **AZ-592** (`_docs/02_tasks/backlog/`) |
| AZ-333 — C1 VINS-Mono research-only `VioStrategy` | BLOCKED | Tier-2 prerequisites + upstream vendoring decision (HKUST + ROS-strip vs. community fork). Same skeleton-only state as AZ-332. | **AZ-593** (`_docs/02_tasks/backlog/`) |

**Operational consequence**: the production-default airborne `VioStrategy` for cycle-1 release is **`KltRansac`** (the engine-rule-mandatory simple baseline, AZ-334), NOT OKVIS2 as `architecture.md` ADR-001 nominally implies. ADR-001 / ADR-002 remain architecturally correct (the seam exists; the build-flag gating works); the production *default selection* shifts until Tier-2 lands. The `_STRATEGY_REGISTRY` still registers the OKVIS2 + VINS-Mono slots so the registry seam stays correct — selecting them via config raises `StrategyNotAvailableError` from `vio_factory.py` until their `BUILD_*` flag is ON.

**Closed Won't-Fix during this cycle**: AZ-589 + AZ-590 (the original remediation tasks for AZ-332 + AZ-333). Both targeted upstream APIs that don't exist in the actually-checked-in OKVIS2 submodule + a non-existent VINS-Mono submodule. The post-mortem details are in `implementation_completeness_cycle1_report.md` § "Verdict — Revised 2026-05-16".

### Run Tests (Step 11) results

| Surface | Result |
|---------|--------|
| Local Tier-1 pytest suite | **3343 passed / 88 skipped / 0 failed** (12 logical chunks; full details in `run_tests_step11_report.md`) |
| Docker Tier-1 SUT Reality Gate | **NOT MET** — both harnesses (`scripts/run-tests.sh` + `e2e/docker/run-tier1.sh`) have pre-existing drift unrelated to Step 10 work (missing `ardupilot/*` + `inavflight/*` images on Docker Hub; broken Dockerfile entrypoints; unseeded tile-cache volume). Rehabilitation epic **AZ-602** owns this. |
| Skip classification | 14 Tier-2-only (Jetson) — legitimate; 8 CUDA/GPU absent on macOS dev host — legitimate; 6 TensorRT-on-Tier-2-only — legitimate; 57 Docker-compose-dependent — borderline, becomes covered once any harness runs end-to-end; 3 console-scripts-not-on-PATH — env-conditional; remainder legitimate |

### Dependency pin drift since Plan

- **opencv-python**: relaxed from `>=4.12.0` to `>=4.11.0.86,<4.12` (D-CROSS-CVE-1 deferred per `_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md`; root cause is the gtsam==4.2.1 / numpy<2.0 ABI lock). See revised Decision 9 above. The original CVE-2025-53644 intent stands and will replay once gtsam ships numpy-2 wheels.

### Documentation reconciliation still owed (deferred to a follow-up `/document` task pass)

This Step-13 session updated the highest-leverage system-level deltas only. The following surfaces still carry Plan-era assertions that do not match cycle-1 as-built behaviour and should be refreshed in a follow-up session:

- `architecture.md` — add ADR-009 sibling notes for `_STRATEGY_REGISTRY` + `pre_constructed`; revise OpenCV mentions (§ Technology stack, § Risks); reflect KltRansac-as-production-default in § C1 ADR commentary.
- `module-layout.md` — extend § "Composition Root" with `airborne_bootstrap` + `build_pre_constructed` ownership; AZ-618 / AZ-591 ownership rows in the runtime_root section.
- `components/01_c1_vio/description.md` — note KltRansac is the operational default while AZ-592 / AZ-593 are parked.
- `components/<NN>_<cN>/description.md` for the other 13 components — task-by-task reconciliation against the 80 product tasks in `done/`.
- `common-helpers/*.md` — task-by-task reconciliation against the 8 helper tasks in `done/`.
- `tests/*.md` — task-by-task reconciliation against the ~36 Blackbox Tests tasks in `done/` (some already touched by Step 12 Test-Spec Sync — see `tests/traceability-matrix.md` and `tests/resilience-tests.md` diffs).

## Quality Checklist Verification

All 8 checklist sections from `.cursor/skills/plan/steps/07_quality-checklist.md` pass for this cycle:

- **Blackbox Tests**: every AC + restriction in `tests/traceability-matrix.md`; restrictions verified by ≥1 scenario each (RESTRICT-CAM-2 deferred with documented mitigation); positive (FT-P) + negative (FT-N) balanced; Tier-1 Docker + Tier-2 Jetson defined in `tests/environment.md`; consumer treats system as black box (FC + GCS contracts only); CI integration in `deployment/ci_cd_pipeline.md`.
- **Architecture**: covers all `solution.md` capabilities; technology choices justified in § 2 of `architecture.md` + ADR-001..ADR-009; deployment model in `deployment/`; blackbox findings F6–F10 reflected (Tier split, mock-suite-sat-service, two-binary CI emit).
- **Data Model**: every persistent entity from `architecture.md` § 4 defined in `data_model.md`; relationships have explicit FK cardinality; migration strategy is additive-only with ADR-recorded deprecation requirement; seed data = `satellite-provider` mirror of `tiles`; backward compatibility = `tiles` schema frozen on canonical columns.
- **Deployment**: containerization covers Tier-1 workstation Docker; CI/CD pipeline includes lint, test, security (SBOM diff, ASan), build, deploy stages; environment strategy covers dev / staging / production (Tier-1 + Tier-2 + Jetson production image); observability covers structured JSON logging + FDR + GCS STATUSTEXT; deployment procedures include rollback (per-flight key zeroisation, FDR rollover) and health checks (F2 takeoff verifier).
- **Components**: 14 components each follow SRP; no circular dependencies (R14 resolved via shared LightGlue helper); inter-component interfaces defined as `Protocol`/`ABC` per ADR-009; no orphan components — every component appears in at least one F1–F10 flow; every blackbox scenario traceable through the component dependency graph.
- **Risks**: all High risks have mitigations + contingency (R01, R03, R11); mitigations reflected in `architecture.md` (ADR-004 enforcement scope, ADR-008 D-C8-2-FALLBACK), `tests/security-tests.md` (NFT-SEC-02 egress test), and `tests/traceability-matrix.md` (AC-text relaxation).
- **Tests**: every AC + restriction covered by ≥1 test (with PARTIAL/NOT-COVERED items having documented mitigation); 4 test types per component represented where applicable (unit/contract inside per-component `tests.md` + integration/performance/security/resilience at system level); test data management defined in `tests/test-data.md`.
- **Epics**: E-BOOT (AZ-244) "Bootstrap & Initial Structure" present; E-BBT (AZ-262) "Blackbox Tests" present; every component maps to a component epic (C1..C8, C10..C13 → AZ-254, AZ-255, AZ-256, AZ-257, AZ-258, AZ-259, AZ-260, AZ-261, AZ-252, AZ-251, AZ-253, AZ-248); dependency order matches `epics.md` "Implementation order"; acceptance criteria are measurable (per-epic IT/PT/ST IDs trace back to `traceability-matrix.md`).
