Update autodev state, architecture documentation, and glossary terms

Transitioned the autodev state to phase 21, reflecting the completion of Step 5 and the drafting of Step 6 epics. Revised the architecture documentation to clarify the roles of the Tile Manager and its components, ensuring accurate representation of the system's operational flow. Updated glossary entries for Flight State and Operator to incorporate recent changes and enhance clarity on component interactions and responsibilities.
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-10 00:21:34 +03:00
parent 723f574b14
commit 64542d32fc
52 changed files with 8789 additions and 88 deletions
+44 -38
View File
@@ -7,7 +7,7 @@
> User-confirmed in Plan Phase 2a.0 (2026-05-09). This section is the spine of the document; nothing below it may contradict it without a recorded ADR.
The system is a **Jetson Orin Nano Super-hosted onboard companion** that delivers a GPS-equivalent WGS84 position (with honest 6×6 covariance and provenance label `{satellite_anchored | visual_propagated | dead_reckoned}`) to a fixed-wing UAV's flight controller in GPS-denied or GPS-spoofed environments. It runs as a **single Python-with-C++-extensions monolithic process per binary track** on the companion PC, fusing pre-flight-cached satellite tiles served by the parent-suite `satellite-provider` with live nav-camera frames (3 Hz) and FC-supplied IMU/attitude (100200 Hz). A canonical hierarchical pipeline `VIO → retrieval → re-rank → matching → AdHoP-conditional refinement → pose → fusion` drives the per-frame loop within a 400 ms p95 latency budget. Cross-component coupling routes through a shared GTSAM substrate so posterior covariance is recovered natively (D-C5-5 = (c)). The companion is **read-only against `satellite-provider` while airborne**; mid-flight tiles are generated locally and uploaded post-landing only via a separate operator-side tool, with the airborne companion image not loading the upload code path at all (process-level isolation, AC-8.4).
The system is a **Jetson Orin Nano Super-hosted onboard companion** that delivers a GPS-equivalent WGS84 position (with honest 6×6 covariance and provenance label `{satellite_anchored | visual_propagated | dead_reckoned}`) to a fixed-wing UAV's flight controller in GPS-denied or GPS-spoofed environments. It runs as a **single Python-with-C++-extensions monolithic process per binary track** on the companion PC, fusing pre-flight-cached satellite tiles served by the parent-suite `satellite-provider` with live nav-camera frames (3 Hz) and FC-supplied IMU/attitude (100200 Hz). A canonical hierarchical pipeline `VIO → retrieval → re-rank → matching → AdHoP-conditional refinement → pose → fusion` drives the per-frame loop within a 400 ms p95 latency budget. Cross-component coupling routes through a shared GTSAM substrate so posterior covariance is recovered natively (D-C5-5 = (c)). The companion is **read-only against `satellite-provider` while airborne** — both the pre-flight tile download and the post-landing tile upload run from the operator-side `Tile Manager` (C11), a separate binary that is excluded from the airborne CMake target so the companion image cannot load either code path even via reflection or config error (process-level isolation, AC-8.4).
### Components — intent-level (formal decomposition belongs to Step 3)
@@ -21,11 +21,10 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
- **C6 — Tile cache + spatial index**: PostgreSQL btree spatial index over filesystem `./tiles/{zoomLevel}/{x}/{y}.jpg` mirroring `satellite-provider`'s on-disk layout, plus FAISS HNSW index for VPR descriptors (`.index` written via `faiss.write_index` + atomicwrites + SHA-256 content-hash gate, D-C10-3).
- **C7 — On-Jetson inference runtime**: TensorRT 10.3 engines (Polygraphy / trtexec / IBuilderConfig hybrid orchestration), JetPack 6.2, SM 87; ONNX Runtime + TRT EP fallback; pure PyTorch FP16 baseline.
- **C8 — Flight-Controller adapter**: `pymavlink` `GPS_INPUT` for ArduPilot Plane (MAVLink 2.0 message signing on the companion ↔ AP wired channel, D-C8-9 = (d)) and `YAMSPy` / INAV-Toolkit `MSP2_SENSOR_GPS` for iNav (signing-gap accepted residual risk).
- **C10 — Pre-flight cache provisioning**: orchestrates download-from-`satellite-provider` → tile cache; descriptor generation; engine compilation; calibration loading; manifest+content-hash verification (D-C10-1, D-C10-3, D-C10-6, D-C10-7).
- **C11 — Post-landing tile upload tool** (operator-side, distinct binary/image): only loaded when `flight_state == ON_GROUND`; pushes locally-saved mid-flight tiles to `satellite-provider`'s ingest endpoint (D-PROJ-2 contract; not yet implemented service-side).
- **C10 — Pre-flight cache provisioning**: builds the **model-derived** cache artifacts (descriptor generation, engine compilation, manifest + content-hash) on top of an already-populated tile store; F2 takeoff verifier (D-C10-1, D-C10-3, D-C10-6, D-C10-7). C10 does NOT touch `satellite-provider` — tile network I/O lives in C11.
- **C11 — Tile Manager** (operator-side, distinct binary/image, ADR-004 process-isolated): owns operator-side network I/O against `satellite-provider` in **both directions**. `TileDownloader` interface fetches tiles into C6 during F1 (TLS + service-internal API key); `TileUploader` interface, gated on `flight_state == ON_GROUND`, pushes mid-flight tiles to `satellite-provider`'s ingest endpoint (D-PROJ-2 contract; not yet implemented service-side). The component bundles both interfaces because they share auth, HTTP client, deployment unit, and the airborne-exclusion property.
- **C12 — Operator pre-flight tooling** (Plan-phase carryforward, deferred from research): cache provisioning UI, sector classification (active-conflict vs stable rear), freshness pipeline workflow.
- **C13 — Flight Data Recorder (FDR)**: per-flight ≤64 GB NVM record of estimates + IMU traces + emitted MAVLink + system health + mid-flight tiles + ≤0.1 Hz failed-tile thumbnails; raw nav/AI-cam frames excluded (AC-8.5, AC-NEW-3).
- **`mock-suite-sat-service`** (testing-time component boundary, F7): a process that publishes the `satellite-provider` ingest contract during NFT-SEC-01 / FT-P-17 / IT runs; not a fixture but a real component boundary.
- **External: `satellite-provider`** (parent-suite .NET 8 service): tile producer pre-flight; tile sink post-landing (D-PROJ-2). Treated as a planned external dependency on the upload + voting paths.
### Architectural principles / non-negotiables
@@ -33,7 +32,7 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
1. **Camera-specific math enters only via a `Camera calibration artifact` JSON** (intrinsics + distortion + body-to-camera extrinsics + acquisition method `factory_sheet | checkerboard_refined | hybrid`). No hard-coded camera math anywhere; test fixtures (`adti26`) and production deployments (`adti20`) load different artifacts on the same code path.
2. **VioStrategy is selected at startup via config; not hot-swappable mid-flight.**
3. **Build-time exclusion of unused `Strategy` implementations.** A given binary links only the implementations it actually uses at runtime. The default deployment binary links the production-default strategies (e.g. OKVIS2 on C1) plus the engine-rule-mandatory simple-baseline (KltRansac on C1); the IT-12 comparative-study binary links all C1 implementations side-by-side. The mechanism is per-component CMake `BUILD_*` flags (`BUILD_VINS_MONO`, `BUILD_SALAD`, …) plus the per-binary composition root choosing among the linked implementations at startup. **Justification is technical** — binary size on the 8 GB shared Jetson, boot/load time inside the AC-NEW-1 30 s budget, deployed dependency / attack surface, and accidental-selection risk reduction (a binary with only OKVIS2 + KltRansac linked cannot be misconfigured into running VINS-Mono). **Component licenses do not drive this decision** — see ADR-002. CI emits both the deployment binary and the research binary on every PR.
4. **In-air outbound writes to `satellite-provider` are forbidden.** Enforced primarily by **process-level isolation** — the upload daemon (C11) is not loaded in the airborne companion image. Software guard on `flight_state == ON_GROUND` is a defense-in-depth check, not the primary control.
4. **In-air network I/O against `satellite-provider` is forbidden — in BOTH directions.** Enforced primarily by **process-level isolation** — the Tile Manager (C11), which carries both the `TileDownloader` and the `TileUploader` interfaces, is not loaded in the airborne companion image. Software guard on `flight_state == ON_GROUND` (upload) is a defense-in-depth check, not the primary control. The companion is read-only against C6 in flight; both pre-flight tile fetching and post-landing tile upload happen on the operator workstation.
5. **All persistent imagery is in `satellite-provider`'s on-disk tile format** (`./tiles/{zoomLevel}/{x}/{y}.jpg` + matching metadata) so post-landing upload is byte-identical. No raw frames on disk except the AC-8.5 forensic ≤0.1 Hz failed-tile thumbnail log inside FDR.
6. **Honest 6×6 posterior covariance via GTSAM `Marginals`** is the safety floor for AC-NEW-4 and AC-NEW-7. Under-reported `horiz_accuracy` is a defect, not a tuning knob.
7. **MAVLink 2.0 message signing on the companion ↔ ArduPilot wired channel**, with per-flight key rotation (D-C8-9 = (d)). iNav has no signing equivalent — accepted residual risk, Plan-phase carryforward proposes an iNav firmware feature request.
@@ -47,7 +46,7 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
### Open architectural items (tracked, NOT blocking Phase 2a)
- **D-PROJ-1** (camera calibration acquisition): CLOSED in this Plan cycle as hybrid factory + checkerboard refinement (~1 day per deployed unit). No physical hardware available this cycle, so production calibration is documented as instructions only; runtime path uses test-fixture calibration for `adti26` images.
- **D-PROJ-2** (parent-suite `satellite-provider` ingest endpoint + multi-flight voting layer): open, parent-suite work, tracked in `_docs/_process_leftovers/2026-05-09_satellite-provider-design-tasks.md`. Onboard-side proceeds with `mock-suite-sat-service` standing in for the contract.
- **D-PROJ-2** (parent-suite `satellite-provider` ingest endpoint + multi-flight voting layer): open, parent-suite work, tracked in `_docs/_process_leftovers/2026-05-09_satellite-provider-design-tasks.md`. Onboard-side proceeds against the real `satellite-provider` — and uses an e2e-test-only `mock-suite-sat-service` fixture (under `tests/fixtures/`) to stand in for the not-yet-shipped POST contract during integration tests.
- **D-PROJ-3** (multi-flight fixture acquisition for AC-NEW-4 / AC-NEW-7 statistical headroom): not pursued this cycle; AC-text was relaxed 2026-05-09 to Monte-Carlo-over-current-data with stated 95 % CI; multi-flight statistical headroom is residual risk in the Step 4 risk register.
- **D-C8-2 runtime gate** (companion-driven `MAV_CMD_SET_EKF_SOURCE_SET` switch): pattern is firmware-supported but not deployed-precedent. ArduPilot Plane SITL validation (IT-3) is the lock gate; D-C8-2-FALLBACK options recorded.
- **D-C2-12** (DINOv2-feature-based matcher evaluation): carryforward research item; potentially closes D-C3-1 retrain cost.
@@ -64,12 +63,11 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
|---|---|
| Companion PC runtime (Jetson Orin Nano Super, JetPack 6.2) | Flight controller firmware (ArduPilot Plane, iNav) |
| All onboard pose-estimation logic (C1C8, C13) | Parent-suite `satellite-provider` (.NET 8 REST microservice) |
| Pre-flight cache provisioning (C10) | GCS (QGroundControl) |
| Operator-side post-landing upload tool (C11) | Nav camera hardware (`adti20`); AI-camera hardware |
| Pre-flight cache artifact build (C10 — engines + descriptors + manifest) | GCS (QGroundControl) |
| Operator-side Tile Manager (C11 — pre-flight download + post-landing upload) | Nav camera hardware (`adti20`); AI-camera hardware |
| Operator pre-flight tooling (C12) | UAV airframe / FC IMU / sensors |
| FDR writer (C13) | Operator's workstation OS / authentication |
| Camera calibration artifact format + loader | The act of calibration itself (operator runs checkerboard rig) |
| `mock-suite-sat-service` (testing-time, F7) | Cellular / satellite GCS link bandwidth |
**External systems**:
@@ -103,7 +101,7 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
| VIO (production) | OKVIS2 (BSD-3-Clause) | upstream HEAD pinned per Plan-phase | D-C1-1-SUB-A = (a) production-default |
| VIO (research / IT-12) | VINS-Mono | upstream HEAD pinned per Plan-phase | Research binary only (`BUILD_VINS_MONO=ON`) for IT-12 comparative study; build-time exclusion from deployment binary per ADR-002 |
| VIO (mandatory baseline) | KLT+RANSAC over OpenCV | OpenCV ≥ 4.12.0 | Engine-rule-required mandatory simple-baseline |
| Tile cache backend | PostgreSQL + filesystem | PostgreSQL 16 (mirror of `satellite-provider`) | C6 mirrors `satellite-provider`'s on-disk and table layout for byte-identical post-landing upload |
| Tile cache backend | PostgreSQL + filesystem | PostgreSQL 16 (mirror of `satellite-provider`) | C6 mirrors `satellite-provider`'s on-disk and table layout so C11 `TileUploader`'s post-landing payload is byte-identical to what the parent suite already serves |
| Container runtime | Docker (Tier-1) + bare JetPack (Tier-2) | Docker 27.x; JetPack 6.2 | Tier-1 workstation Docker; Tier-2 Jetson native (no Docker — direct JetPack to keep INT8 calibration cache trustworthy per D-C10-6) |
| Build system | CMake + Python `pyproject.toml` | CMake ≥ 3.27 | CMake `option(BUILD_VINS_MONO ...)` D-C1-1-SUB-A; Python wheels built per Jetson via cibuildwheel-equivalent recipe |
| CI/CD | GitHub Actions (Tier-1) + self-hosted Jetson runner (Tier-2) | latest pinned action versions | Two-binary emit on every PR (production + research); Tier-2 runs are AC-bound jobs only |
@@ -132,11 +130,11 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
| `staging-tier1` | CI runs that don't require Jetson hardware | GitHub-hosted runner (x86_64); Docker |
| `staging-tier2` | CI runs that require Jetson (AC-bound jobs only) | Self-hosted Jetson runner; bare JetPack (no Docker) |
| `production` | Deployed companion image on a UAV | Jetson Orin Nano Super (pinned); bare JetPack; no inbound network listening (defense-in-depth, NFT-SEC-05) |
| `production-operator-workstation` | Pre-flight cache build + post-landing upload + FDR retrieval | Operator's Linux workstation; Docker for `satellite-provider` mirror |
| `production-operator-workstation` | Pre-flight tile download + cache artifact build (C10) + post-landing tile upload (C11) + FDR retrieval | Operator's Linux workstation; Docker for `satellite-provider` mirror |
**Infrastructure**:
- **No cloud orchestration**. The companion is an embedded edge device; the operator's workstation is a single host that runs the operator tooling (C11, C12) and a local `satellite-provider` mirror or VPN-reaches the lab `satellite-provider`.
- **No cloud orchestration**. The companion is an embedded edge device; the operator's workstation is a single host that runs the operator tooling (C11 Tile Manager + C12 Operator Pre-flight Tooling) and a local `satellite-provider` mirror or VPN-reaches the lab `satellite-provider`.
- **Two binaries shipped on every PR** (ADR-002): `deployment-binary` (links the production-default strategy on each component + the mandatory simple-baseline; CMake `BUILD_VINS_MONO=OFF`, `BUILD_SALAD=OFF`, …) and `research-binary` (links every available strategy on every component; all `BUILD_*` flags `ON`, used for the IT-12 comparative study). The deployment binary is what installs onto an operational Jetson; the research binary runs on dev/lab Jetson hardware for the comparative-study report. The same code base produces both — ADR-002 mechanism scales to additional binary variants later if packaging strategy requires it.
- **Container scope**: Tier-1 uses Docker (`docker compose` for the developer setup including a `mock-suite-sat-service` container, the operator-tool container, and a Postgres for C6). **Tier-2 (Jetson) does NOT use Docker** — TensorRT INT8 calibration caches and `jetson-stats` thermal telemetry are most reliable without a container layer, per D-C7-9 + D-C10-6. The deployed image on the Jetson is a JetPack-based system image with the deployment binary preinstalled.
- **Scaling**: not applicable (per-UAV, single companion). Failover is per-airframe (the FC's IMU-only fallback at AC-5.2 is the system's "scale-out").
@@ -145,7 +143,7 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
| Config | dev-tier1 | staging-tier2 | production |
|---|---|---|---|
| `satellite-provider` host | local Docker (`satellite-provider:5100`) | mock-suite-sat-service or test fixture | operator workstation (pre-flight only) |
| `satellite-provider` host | local Docker (`satellite-provider:5100`) | real `satellite-provider` Docker (download path; existing) + e2e-test `mock-suite-sat-service` fixture (POST/upload only, until D-PROJ-2 lands) | operator workstation (pre-flight only) |
| Camera calibration source | test-fixture artifact (`adti26.json`) | test-fixture artifact | `adti20.json` (D-PROJ-1 hybrid output) |
| Logging sink | console (DEBUG) | journald + FDR | FDR (per-flight, ≤ 64 GB rolling) |
| MAVLink signing key | dev key (committed to test fixtures) | per-flight key from test config | per-flight key generated at takeoff load, rotated per flight |
@@ -167,7 +165,7 @@ source repo
│ ├─ deployment-binary tarball (production-default strategies + mandatory baselines, ADR-002)
│ ├─ research-binary tarball (all strategies linked; for IT-12 comparative study)
│ ├─ JetPack image (deployment-binary preinstalled)
│ └─ operator-tooling tarball (C11 + C12 + mock-suite-sat-service compose)
│ └─ operator-tooling tarball (C11 + C12 + e2e-test mock-suite-sat-service compose for offline integration testing)
└─→ deploy paths:
├─ Jetson operational deploy: JetPack image flash (deployment-binary)
@@ -215,13 +213,13 @@ source repo
**Data flow summary** (one-line each; full sequences in `system-flows.md`):
- Pre-flight: `satellite-provider` → C10`Tile` cache + `EngineCacheEntry` + `Manifest` + descriptor `.index` (atomic write + content-hash gate).
- Pre-flight: `satellite-provider` → C11 `TileDownloader``Tile` cache (C6) → C10 → `EngineCacheEntry` + `Manifest` + descriptor `.index` (atomic write + content-hash gate).
- Takeoff load: `Manifest` content-hash verify + FAISS mmap + TRT deserialize + MAVLink signing handshake → ready.
- Per-frame runtime: `NavCameraFrame` + `ImuWindow` → C1 (`VioOutput`) → C2 → C2.5 → C3 → C3.5 → C4 → C5 → C8 → `EmittedExternalPosition` to FC.
- Mid-flight tile gen: `NavCameraFrame` + `PoseEstimate` → orthorectify → dedup → write to local C6 (no upload).
- GCS telemetry: C5 → C8 → 12 Hz downsampled summary to QGroundControl.
- FDR: every emitted/received stream → C13 ring with per-flight ≤ 64 GB cap.
- Post-landing: operator triggers C11 → reads C6 → uploads to `satellite-provider` ingest endpoint (D-PROJ-2 contract).
- Post-landing: operator triggers C11 `TileUploader` → reads C6 → uploads to `satellite-provider` ingest endpoint (D-PROJ-2 contract).
---
@@ -252,7 +250,7 @@ source repo
| ArduPilot Plane FC | MAVLink 2.0 (`GPS_INPUT` 5 Hz; `MAV_CMD_SET_EKF_SOURCE_SET`; `STATUSTEXT` / `NAMED_VALUE_FLOAT`) over UART/USB | MAVLink 2.0 message signing, per-flight key (D-C8-9 = (d)) | 5 Hz periodic emit; signing handshake at takeoff load (≤ 5 s, AC-NEW-1) | Signing handshake fail → companion refuses takeoff; mid-flight signing key compromise → FC ignores unsigned messages, AC-5.2 takes over |
| iNav FC | MSP2 `MSP2_SENSOR_GPS` over UART; MAVLink outbound for telemetry | None (iNav has no signing) — accepted residual risk per Mode B Source #129 | 5 Hz periodic emit | Mid-flight bad-frame → iNav `mspGPSReceiveNewData()` receives only the latest frame; honest `hPosAccuracy` is the only safety net |
| QGroundControl (GCS) | MAVLink 2.0 (`STATUSTEXT`, `NAMED_VALUE_FLOAT`, `GPS_RAW_INT`) | Same MAVLink 2.0 signing as the AP path (AP profile); no signing on iNav profile | 12 Hz downsampled (AC-6.1); operator commands are best-effort | GCS link drop → companion continues; no mid-flight reconfiguration is required from GCS |
| `satellite-provider` (pre-flight) | REST over HTTP, OpenAPI at `/swagger`; filesystem access if co-located | TLS + service-internal API key (operator workstation only); the companion never reaches `satellite-provider` directly while airborne | Off-line pre-flight; not time-critical | Cache miss → C10 fails fast pre-flight; takeoff blocked |
| `satellite-provider` (pre-flight) | REST over HTTP, OpenAPI at `/swagger`; filesystem access if co-located | TLS + service-internal API key (operator workstation only); the companion never reaches `satellite-provider` directly while airborne | Off-line pre-flight; not time-critical | Cache miss → C11 `TileDownloader` fails fast pre-flight; C10 build is blocked downstream; takeoff blocked |
| `satellite-provider` (post-landing ingest, D-PROJ-2, **planned**) | REST `POST /api/satellite/tiles/ingest` (multipart) | Per-flight onboard signing key (carried with each tile); rate-limited | Bursty post-landing | Endpoint not yet implemented service-side → C11 keeps batches queued locally; never blocks the pre-flight cycle |
| Operator workstation (pre-flight stage) | Filesystem (USB / Ethernet) | OS-level (operator login) | Not time-critical | Bad-stage detection via Manifest content-hash gate (D-C10-3) |
| Nav camera | USB / MIPI-CSI / GigE (lens-module dependent) | n/a | 3 Hz | Frame drop / hardware fault → "VISUAL_BLACKOUT" path (AC-3.5, AC-NEW-8) |
@@ -264,7 +262,7 @@ The onboard side of D-PROJ-2 is fully specified in `_docs/_process_leftovers/202
- **`Tile` writes are append-only and idempotent** (the same `(zoomLevel, lat, lon, capture_timestamp, companion_id, flight_id)` tuple is the dedup key).
- **Quality metadata is mandatory on every uploaded tile** so the planned voting layer can promote `pending → trusted` without re-deriving statistics on the service side.
- **Onboard tiles never claim the `trusted` status**; they are uploaded as `pending` and the parent-suite voting layer (D-PROJ-2 design task #2) decides promotion.
- **Mock substitute**: `mock-suite-sat-service` (testing-time component, F7) implements the contract for NFT-SEC-01 / FT-P-17 / IT runs — it is treated as a real component boundary, not a test fixture.
- **Test substitute**: `mock-suite-sat-service` is an e2e-test-only fixture (under `tests/fixtures/mock-suite-sat-service/`) that implements the upload contract for NFT-SEC-01 / FT-P-17 / IT runs until D-PROJ-2 lands service-side. It is **not a component** in the architectural sense — the production architectural counterparty for both download and upload is the real `satellite-provider`. The fixture is retired the moment the real ingest endpoint ships.
---
@@ -319,7 +317,7 @@ The onboard side of D-PROJ-2 is fully specified in `_docs/_process_leftovers/202
**Authorization**:
- **Onboard runtime**: a single principal (the runtime process); no in-process privilege boundaries. The post-landing upload tool (C11) runs as a different principal with write access to `satellite-provider` and only loads when `flight_state == ON_GROUND`. The airborne image does not contain the C11 binary at all.
- **Onboard runtime**: a single principal (the runtime process); no in-process privilege boundaries. The Tile Manager (C11) runs as a different principal on the operator workstation, holding the only credentials that reach `satellite-provider` (TLS API key for download; per-flight onboard signing key for post-landing upload). The airborne image does not contain the C11 binary at all.
- **GCS**: operator commands (`AC-6.2`) are best-effort hints; the operator cannot promote a pose, override covariance, or reach the `satellite-provider` write path. Operator re-loc requests trigger the satellite re-localization flow (F6) but do not bypass any safety gate.
**Data protection**:
@@ -405,9 +403,14 @@ This decision is made on **technical grounds only**. Component licenses (BSD/Apa
### ADR-004 — Process-level isolation for in-air upload prevention (AC-8.4 enforcement)
**Context**: AC-8.4 forbids in-air outbound writes to `satellite-provider` for drone-security reasons. A software guard checking `flight_state == ON_GROUND` can be bypassed by code injection if the upload code path is ever loaded.
**Context**: AC-8.4 forbids in-air outbound writes to `satellite-provider` for drone-security reasons. The companion is also read-only against `satellite-provider` while airborne — there is no operational reason to fetch tiles in flight either, since the pre-flight cache is the contract. A software guard checking `flight_state == ON_GROUND` can be bypassed by code injection if the network I/O code path is ever loaded.
**Decision**: The post-landing upload tool (C11) is a **separate binary / image** that runs only on the operator's workstation post-landing; the airborne companion image does not contain the C11 binary at all. The `flight_state == ON_GROUND` software guard remains as defense-in-depth. The local mid-flight tile format is byte-identical to `satellite-provider`'s on-disk layout so no transformation is needed at upload time.
**Decision**: The Tile Manager (C11) is a **separate binary / image** that runs only on the operator's workstation; the airborne companion image does not contain the C11 binary at all — neither the `TileDownloader` (pre-flight) nor the `TileUploader` (post-landing) code paths can be reached from the airborne process. The `flight_state == ON_GROUND` software guard inside the `TileUploader` remains as defense-in-depth for the upload direction. The local mid-flight tile format is byte-identical to `satellite-provider`'s on-disk layout so no transformation is needed at upload time.
**Enforcement gates (per R02 risk register)**:
1. **CI SBOM diff**: the build pipeline fails the airborne `production-binary` artifact if any symbol from `c11_tilemanager/` (or any module that transitively imports `c11_tilemanager`) appears in the linked image. This is an extension of the per-implementation SBOM enforcement already in ADR-002.
2. **Runtime self-check in `runtime_root.py`**: at startup, before opening the FC adapter, the airborne composition root attempts `importlib.util.find_spec("c11_tilemanager")` and panics if the spec resolves to anything other than `None`. Cost: one import lookup at startup; benefit: catches a build-system regression even if SBOM diff was bypassed.
3. **Network egress test (NFT-SEC-02)**: the airborne process is run inside a network namespace with no route to `satellite-provider`'s host; any attempted outbound TCP connection to it is a release-blocking test failure.
**Alternatives considered**:
1. Single binary with software-only guard — rejected on principle: a runtime guard cannot be the primary control for an "is the system airborne?" safety property.
@@ -439,17 +442,19 @@ This decision is made on **technical grounds only**. Component licenses (BSD/Apa
**Consequences**: ~510 % accuracy loss at the upper thermal envelope (still inside AC-NEW-4). The hybrid is part of the runtime, not a config knob; the threshold is.
### ADR-007 — `mock-suite-sat-service` is a real component boundary, not a fixture (F7)
### ADR-007 — `mock-suite-sat-service` is an e2e-test fixture, not a first-class component (REVERSED 2026-05-09)
**Context**: D-PROJ-2 (parent-suite ingest endpoint + voting layer) is not yet implemented. NFT-SEC-01 / FT-P-17 / IT runs need a counterparty for the post-landing upload contract. Treating the counterparty as a "fixture" buried inside C8 hides the actual contract.
**Context**: D-PROJ-2 (parent-suite ingest endpoint + voting layer) is not yet implemented. NFT-SEC-01 / FT-P-17 / IT runs need a counterparty for the post-landing upload contract. An earlier iteration of this ADR promoted the mock to a first-class component boundary peer of `satellite-provider`, with its own description under `components/` and its own deployable image — to make the contract auditable.
**Decision**: `mock-suite-sat-service` is documented as a testing-time component boundary with its own description in `components/`. Component decomposition (Step 3) treats it as a peer of `satellite-provider`, not a sub-implementation of C8. The mock implements the contract sketched in `_docs/_process_leftovers/2026-05-09_satellite-provider-design-tasks.md`.
**Decision (current)**: the mock is **an e2e-test fixture only**, scoped under `tests/fixtures/mock-suite-sat-service/`. The architectural counterparty for both the existing download path and the planned D-PROJ-2 upload path is the **real** `satellite-provider`. The contract sketch lives in `_docs/_process_leftovers/2026-05-09_satellite-provider-design-tasks.md` (the source of truth for the parent-suite work) and is mirrored in C11 Tile Manager's external API section (the onboard consumer's view). The mock implements that contract in tests; production never reaches it.
**Why reversed**: promoting an e2e-test fixture to a component boundary inflated the architectural surface and risked the test fixture drifting away from the real contract once D-PROJ-2 lands. The contract sketch in the leftover file is sufficient as the auditable source of truth without a separate component spec.
**Alternatives considered**:
1. Embed mock inside C8 test fixtures — rejected: hides the contract.
2. Wait for D-PROJ-2 service-side implementation — rejected: blocks the onboard cycle.
1. Keep ADR-007 as originally written — rejected: see "Why reversed".
2. Wait for D-PROJ-2 service-side implementation before any tests — rejected: blocks the onboard cycle.
**Consequences**: The mock is a release artifact (not a test-only fixture); it is the source of truth for the onboard side of the D-PROJ-2 contract until the parent suite catches up. When `satellite-provider` ships the real endpoint, the mock is retired and the contract is replayed against the real service.
**Consequences**: The mock continues to ship in the operator-tooling tarball's compose file as a test-time service, but it is no longer documented under `_docs/02_document/components/`. Test specs and CI references treat it as a fixture. When `satellite-provider` ships the real endpoint, the fixture is replaced by pointing tests at the real service; no architectural changes flow from that switch.
### ADR-008 — D-C8-2 source-set switch is `Selected with runtime gate` (Mode B Fact #111)
@@ -533,22 +538,23 @@ This decision is made on **technical grounds only**. Component licenses (BSD/Apa
msp2_inav_adapter.py
qgc_telemetry_adapter.py
tests/
c10_cache_provisioning/
interface.py # CacheProvisioner, ManifestVerifier
provisioner.py
tests/
c11_post_landing_upload/ # SEPARATE BINARY — never linked into airborne image
interface.py
uploader.py
tests/
c10_cache_provisioning/
interface.py # CacheProvisioner, ManifestVerifier
provisioner.py
tests/
c11_tilemanager/ # SEPARATE BINARY — never linked into airborne image
interface.py # TileDownloader, TileUploader (two interfaces in one component)
http_tile_downloader.py
http_tile_uploader.py
tests/
c13_fdr/
interface.py # FdrWriter
file_fdr_writer.py
tests/
composition/
runtime_root.py # composition root: config -> concrete graph
upload_tool_root.py # composition root for the C11 operator-side tool
research_root.py # composition root for the research/dev binary
runtime_root.py # composition root: config -> concrete graph
tilemanager_root.py # composition root for the C11 operator-side tool (download + upload)
research_root.py # composition root for the research/dev binary
```
3. **Constructor injection only.** Every component class declares its collaborators as **typed `__init__` arguments**, against the sibling's interface (not the concrete class). Example sketch: