[AZ-329] [AZ-330] [AZ-523] [AZ-524] Batch 44 atomic refactor

Implements two new C12 services and rebalances the C11/C12 boundary
in one atomic commit:

* AZ-329 PostLandingUploadOrchestrator — gates C11 upload on the
  `flight_footer` FDR record's `clean_shutdown` field; 4 refusal
  modes; new FdrFooterReader Protocol + LocalFdrFooterReader.
* AZ-330 OperatorReLocService — AC-3.4 visual-loss re-localization
  hint; reuses shared LatLonAlt; OperatorCommandTransport Protocol
  cut (E-C8 owns the future pymavlink concrete); new FDR record
  kind `c12.reloc.requested`; log redaction (lat/lon 5 decimals,
  reason 200 chars).
* AZ-523 C11 internal flight-state gate removed (SRP refactor):
  `confirm_flight_state` / `FlightStateSignal` use /
  `FlightStateNotOnGroundError` deleted from C11; TileUploader
  contract bumped to v2.0.0 (frozen) with migration note; AZ-317
  superseded.
* AZ-524 Package rename `c12_operator_tooling` →
  `c12_operator_orchestrator` across source, tests, pyproject,
  CMake, Dockerfile, compose, CI, runtime-root services class
  (`OperatorOrchestratorServices`) + factory function
  (`build_operator_orchestrator`), logger namespaces, config slug,
  docs, and the E-C12 epic title.

Tests: 1543 passed, 80 skipped (all environment gates). Targeted
AC suite (AZ-329 + AZ-330 + FdrFooterReader): 37 passed. Cold-start
NFR-perf still ≤ 500 ms p99.

Tracker: AZ-317 → Done (superseded); AZ-319 v2.0.0 contract bump
comment; AZ-329/AZ-330 → In Testing; AZ-253 epic renamed; AZ-523
+ AZ-524 created and closed as audit-trail tickets.

See `_docs/03_implementation/batch_44_cycle1_report.md`.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-13 19:42:46 +03:00
parent 2d88d3d674
commit 5fe67023b2
112 changed files with 3409 additions and 1311 deletions
+3 -3
View File
@@ -37,7 +37,7 @@ See `architecture.md` for the full ADR set (ADR-001..ADR-009), 12 architectural
| 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 12 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, gated `flight_state == ON_GROUND`); excluded from airborne image | C6, E-CC-CONF, E-CC-LOG | AZ-251 |
| 13 | C12 Operator Pre-flight Tooling | CLI subcommands (`download`, `build-cache`, `upload-pending`, `reloc-confirm`); sector classification UI hook; FDR retrieval helpers | C10, C11, E-CC-LOG | AZ-253 |
| 13 | C12 Operator Pre-flight Orchestrator | CLI subcommands (`download`, `build-cache`, `upload-pending`, `reloc-confirm`); 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).
@@ -103,7 +103,7 @@ The test suite is organised as scenario specs (no source code yet). Per-componen
| 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_tooling/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/`)
@@ -142,7 +142,7 @@ Both the inclusive reading (PARTIAL = covered) and the strict reading clear the
| 7 | AZ-250: E-C6 — Tile Cache + Spatial Index | C6 | M | 1321 | E-BOOT, E-CC-LOG, E-CC-CONF |
| 8 | AZ-251: E-C11 — Tile Manager | C11 | M | 1321 | E-C6, E-CC-CONF, E-CC-LOG |
| 9 | AZ-252: E-C10 — Pre-flight Cache Provisioning | C10 | M | 1321 | E-C6, E-C7, E-CC-LOG |
| 10 | AZ-253: E-C12 — Operator Pre-flight Tooling | C12 | M | 1321 | E-C10, E-C11, E-CC-LOG |
| 10 | AZ-253: E-C12 — Operator Pre-flight Orchestrator | C12 | M | 1321 | E-C10, E-C11, E-CC-LOG |
| 11 | AZ-254: E-C1 — Visual / Visual-Inertial Odometry | C1 | XL | 3455 | E-BOOT, E-CC-FDR-CLIENT, E-C7 |
| 12 | AZ-255: E-C2 — Visual Place Recognition | C2 | L | 2134 | E-C6, E-C7, E-CC-FDR-CLIENT |
| 13 | AZ-256: E-C2.5 — Inlier-based Re-rank | C2.5 | S | 58 | E-C2, E-C7, E-C6 (shared LightGlue helper) |
+6 -6
View File
@@ -139,9 +139,9 @@ The system is a **Jetson Orin Nano Super-hosted onboard companion** that deliver
**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 Tile Manager + C12 Operator Pre-flight Tooling) 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 Orchestrator) 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.
- **Container scope**: Tier-1 uses Docker (`docker compose` for the developer setup including a `mock-suite-sat-service` container, the operator-orchestrator 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").
**Environment-specific configuration**:
@@ -170,7 +170,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 + e2e-test mock-suite-sat-service compose for offline integration testing)
│ └─ operator-orchestrator tarball (C11 + C12 + e2e-test mock-suite-sat-service compose for offline integration testing)
└─→ deploy paths:
├─ Jetson operational deploy: JetPack image flash (deployment-binary)
@@ -321,7 +321,7 @@ The onboard side of D-PROJ-2 is fully specified in `_docs/_process_leftovers/202
| Companion ↔ GCS (AP profile) | MAVLink 2.0 signing inherited from the FC channel |
| Operator workstation ↔ `satellite-provider` (pre-flight) | TLS + service-internal API key (workstation only; never on the airborne companion) |
| Companion ↔ `satellite-provider` (post-landing upload, **D-PROJ-2 planned**) | Per-flight onboard signing key carried with each uploaded tile; the planned ingest endpoint verifies the key |
| Operator workstation pre-flight stage | OS-level (operator login + workstation hardening — operator-tooling concern, C12) |
| Operator workstation pre-flight stage | OS-level (operator login + workstation hardening — operator-orchestrator concern, C12) |
**Authorization**:
@@ -424,7 +424,7 @@ This decision is made on **technical grounds only**. Component licenses (BSD/Apa
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.
2. Hardware-level switch (e.g., physical write-enable jumper) — rejected: adds operations cost; software-image-isolation gives equivalent assurance for this threat model.
**Consequences**: Two binaries to maintain (companion image + operator-tooling image). CI builds and tests both. The operator workflow has an explicit post-landing step ("run the upload tool") which is itself a feature, not a bug.
**Consequences**: Two binaries to maintain (companion image + operator-orchestrator image). CI builds and tests both. The operator workflow has an explicit post-landing step ("run the upload tool") which is itself a feature, not a bug.
### ADR-005 — Two execution tiers (Tier-1 / Tier-2) are first-class architectural concerns (F6)
@@ -462,7 +462,7 @@ This decision is made on **technical grounds only**. Component licenses (BSD/Apa
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 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.
**Consequences**: The mock continues to ship in the operator-orchestrator 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)
@@ -145,7 +145,7 @@ would break AC-6.
**Potential race conditions**:
- Concurrent `build_cache_artifacts` invocations on the same cache root would corrupt state. Single-process operator-tool wraps with a filesystem lockfile (the same lockfile C11 honours); if a second invocation tries to start, fail with explicit error.
- Concurrent `build_cache_artifacts` invocations on the same cache root would corrupt state. Single-process operator-orchestrator wraps with a filesystem lockfile (the same lockfile C11 honours); if a second invocation tries to start, fail with explicit error.
**Performance bottlenecks**:
@@ -5,7 +5,7 @@
**Purpose**: own the operator-side network I/O against `satellite-provider` for the onboard tile corpus, in **both directions**:
- **Download** (pre-flight, F1): fetch tiles from `satellite-provider` for the operational area, apply AC-NEW-6 freshness gating, and write into C6 (`TileStore` + `TileMetadataStore`). C11 is the **only** path that crosses the workstation/companion enclave to the parent suite for tile pixels — C10 reads from the populated C6 store and never touches `satellite-provider` itself.
- **Upload** (post-landing, F10): when `flight_state == ON_GROUND` is confirmed, read pending mid-flight tiles from C6 and POST to `satellite-provider`'s ingest endpoint (D-PROJ-2 contract sketch).
- **Upload** (post-landing, F10): read pending mid-flight tiles from C6 and POST to `satellite-provider`'s ingest endpoint (D-PROJ-2 contract sketch). C11 itself does NOT gate on flight state — it is a dumb pipe; the post-landing safety gate is owned by C12's `PostLandingUploadOrchestrator` (AZ-329 / Batch 44), which checks the C13 `flight_footer` FDR record for `clean_shutdown=True` before invoking `TileUploader.upload_pending_tiles`.
C11 is a **separate operator-side binary / image**. The airborne companion image's CMake target deliberately excludes the entire `c11_tilemanager/` source tree so the airborne process cannot accidentally execute either the download path or the upload path even via reflection or config error (ADR-004 process-level isolation, AC-8.4). Both directions of tile I/O are operator-driven on the operator workstation; the companion only consumes the populated C6 store while airborne.
@@ -36,10 +36,11 @@ C11 is a **separate operator-side binary / image**. The airborne companion image
| Method | Input | Output | Async | Error Types |
|--------|-------|--------|-------|-------------|
| `confirm_flight_state` | `()` | `FlightStateSignal` (must be ON_GROUND) | No | `FlightStateNotOnGroundError` |
| `enumerate_pending_tiles` | `flight_id: uuid (optional)` | `list[TileMetadata]` | No | `TileMetadataError` |
| `upload_pending_tiles` | `UploadRequest` | `UploadBatchReport` | No | `SatelliteProviderError`, `RateLimitedError`, `SignatureRejectedError` |
C11 no longer exposes `confirm_flight_state` — the post-landing flight-state gate moved to C12 (`PostLandingUploadOrchestrator`, AZ-329) per Batch 44. `FlightStateNotOnGroundError` is retired from C11; the corresponding refusal now lives at the C12 boundary as `FlightStateNotConfirmedError`.
**Input/Output DTOs**:
```
@@ -65,8 +66,6 @@ UploadRequest:
batch_size: int
satellite_provider_url: URL
FlightStateSignal: see C8 — must be ON_GROUND for any upload to proceed
UploadBatchReport:
batch_uuid: uuid (assigned by satellite-provider per D-PROJ-2 contract)
per_tile_status: list[(tile_id, status: enum {queued, rejected, duplicate, superseded})]
@@ -154,9 +153,10 @@ C11 reads from / writes to C6 (the local store) and reads from / writes to `sate
- `RateLimitedError` (429): obey `Retry-After`; the operator can also re-invoke later. Same handling either direction.
- `FreshnessRejectionError` / `ResolutionRejectionError`: download-side only. Per AC-NEW-6 / RESTRICT-SAT-4 — never silently downgrade fresh-required tiles in `active_conflict` sectors. Surface counts in the `DownloadBatchReport`.
- `CacheBudgetExceededError`: download-side only. Pre-flight free-space check against AC-8.3 (≤ 10 GB). Fail fast with explicit budget delta; no partial write.
- `FlightStateNotOnGroundError`: upload-side only. Refuse to start; log + show explicit reason. ADR-004 process-level isolation means C11 should never run when the FC believes it's airborne — this error is a defense-in-depth, not the primary control.
- `SignatureRejectedError`: upload-side only. Per-flight signing key was rejected by `satellite-provider`. This is a security-critical event — do NOT silently drop; surface to operator + log to FDR.
Post-landing safety: C11's upload path no longer gates on flight state internally. The check now lives in C12's `PostLandingUploadOrchestrator` (AZ-329 / Batch 44), which refuses to invoke `TileUploader.upload_pending_tiles` unless the C13 `flight_footer` FDR record records `clean_shutdown=True` for the target flight. ADR-004 process-level isolation remains the primary control — C11 should never run on the companion at all.
## 6. Extensions and Helpers
| Helper | Purpose | Used By |
@@ -192,7 +192,7 @@ C11 reads from / writes to C6 (the local store) and reads from / writes to `sate
| Log Level | When | Example |
|-----------|------|---------|
| ERROR | `FlightStateNotOnGroundError`, `SignatureRejectedError`, persistent `SatelliteProviderError`, `CacheBudgetExceededError` | `C11 refused to start: flight_state=IN_AIR; safeguard active` |
| ERROR | `SignatureRejectedError`, persistent `SatelliteProviderError`, `CacheBudgetExceededError` | `C11 upload failure: signature rejected by satellite-provider` |
| WARN | one-off network failure, scheduled retry, freshness-driven rejections (counts) | `C11 batch upload retry: batch_uuid=…; next_retry_in_s=30` |
| INFO | session start/end; per-batch report (download + upload) | `C11 download complete: 87654 tiles, 12 stale-rejected; bbox=…` |
| DEBUG | per-tile request/response | `C11 tile uploaded: tile_id=(z=18,lat=…,lon=…); status=queued` |
@@ -66,19 +66,13 @@ Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C11 wa
---
### C11-IT-04: TileUploader gates on `flight_state == ON_GROUND`
### C11-IT-04: post-landing safety gate lives in C12 (cross-reference)
**Summary**: `TileUploader.upload_pending` refuses to run if `FlightStateSignal != ON_GROUND` (defense-in-depth atop ADR-004 process isolation).
**Summary**: post-landing safety is owned by C12, not C11. The gate that historically lived in `TileUploader.upload_pending_tiles` was removed in Batch 44 (supersedes AZ-317); the equivalent check now lives in C12's `PostLandingUploadOrchestrator` (AZ-329) and refuses to invoke `TileUploader.upload_pending_tiles` unless the C13 `flight_footer` FDR record records `clean_shutdown=True` for the target flight.
**Traces to**: AC-8.4 (defensive — ADR-004's secondary guard)
**Traces to**: see `_docs/02_document/components/13_c12_operator_orchestrator/tests.md` → C12-IT-03 for the post-landing safety test.
**Description**: call `upload_pending` with `FlightStateSignal == IN_FLIGHT`; assert `UploadGateBlockedError`. Same with `UNKNOWN`. Set `ON_GROUND` and assert upload proceeds.
**Input data**: scripted FlightStateSignal source.
**Expected result**: upload blocked except in `ON_GROUND`.
**Max execution time**: 30 s.
**Status**: cross-reference only. C11's `TileUploader` no longer exposes `confirm_flight_state` or raises `FlightStateNotOnGroundError`.
---
@@ -193,10 +187,10 @@ Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C11 wa
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | `operator-tool download --area derkachi.geojson --since 2026-01` | `DownloadBatchReport` printed; tiles in C6 |
| 2 | `operator-tool build-cache` | C10 builds engines + descriptors + Manifest |
| 1 | `operator-orchestrator download --area derkachi.geojson --since 2026-01` | `DownloadBatchReport` printed; tiles in C6 |
| 2 | `operator-orchestrator build-cache` | C10 builds engines + descriptors + Manifest |
| 3 | (simulate flight) | (covered by other tests) |
| 4 | `operator-tool upload-pending` | Pending-upload tiles POSTed; report printed |
| 4 | `operator-orchestrator upload-pending` | Pending-upload tiles POSTed; report printed |
---
@@ -1,4 +1,4 @@
# C12 — Operator Pre-flight Tooling
# C12 — Operator Pre-flight Orchestrator
## 1. High-Level Overview
@@ -26,7 +26,7 @@
| Method | Input | Output | Async | Error Types |
|--------|-------|--------|-------|-------------|
| `build_cache` | `flight_id` (online) OR `flight_file: Path` (offline), `sector_class`, `calibration_path`, `satellite_provider_url`, `api_key` | `CacheBuildReport` (wraps `FlightResolveReport` + C11 `DownloadBatchReport` + C10 `BuildReport`) | No (operator-facing; minutes) | `CacheBuildError` (wraps `FlightNotFoundError`, `FlightsApiUnreachableError`, `SatelliteProviderError`, `EngineBuildError`, etc.) |
| `trigger_post_landing_upload` | `flight_id` | C11 `UploadBatchReport` | No (operator-facing; minutes) | `CacheBuildError` wrapper around `FlightStateNotOnGroundError`, `SignatureRejectedError`, etc. |
| `trigger_post_landing_upload` | `PostLandingUploadRequest` (`flight_id`, `satellite_provider_url`, `api_key`, `batch_size`) | C11 `UploadBatchReport` (re-exposed as `UploadBatchReportCut`) | No (operator-facing; minutes) | `FlightStateNotConfirmedError` (footer missing / unclean / fdr-unreadable / flight-id not found), `SatelliteProviderError`, `SignatureRejectedError` (passthrough from C11) |
| `verify_companion_ready` | `companion_address` | `ReadinessReport` | No | `CompanionUnreachableError`, `ContentHashMismatchError` |
| `set_sector_classification` | `area, sector_class` | `None` | No | — |
| `apply_freshness_threshold` | `sector_class` | `int (months)` | No | — |
@@ -1,4 +1,4 @@
# Test Specification — C12 Operator Pre-flight Tooling
# Test Specification — C12 Operator Pre-flight Orchestrator
Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C12 sequences the F1 (C11 download → C10 build) and F10 (C11 upload trigger) operator-side flows.
@@ -47,17 +47,17 @@ Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C12 se
---
### C12-IT-03: trigger_post_landing_upload invokes C11 TileUploader on confirmed ON_GROUND
### C12-IT-03: trigger_post_landing_upload invokes C11 TileUploader on confirmed clean-shutdown footer
**Summary**: `trigger_post_landing_upload` reads the most recent `FlightStateSignal` from the post-flight FDR; if `ON_GROUND` is confirmed for ≥ a configurable safety threshold (default 30 s), it invokes `C11.TileUploader.upload_pending`. If `ON_GROUND` is not confirmed, it refuses and returns a clear error.
**Summary**: `trigger_post_landing_upload` reads the post-flight FDR newest-segment-first looking for a `flight_footer` record (kind registered by C13 in AZ-292; emitted exactly once per flight on `close_flight()`); if found with `payload["clean_shutdown"] == True`, it invokes `C11.TileUploader.upload_pending_tiles(UploadRequest(flight_id=..., ...))`. If the footer is absent (truncation / crash) or carries `clean_shutdown == False`, it refuses with `FlightStateNotConfirmedError`.
**Traces to**: AC-8.4
**Description**: stage two flight FDR fixtures — one ending with confirmed ON_GROUND for 60 s, one ending with `IN_FLIGHT` (incomplete log). Call `trigger_post_landing_upload`; assert (a) first case invokes upload, (b) second case refuses with `FlightStateNotConfirmedError`.
**Description**: stage two flight FDR fixtures produced by C13's `FileFdrWriter` — one with a clean-shutdown footer (the writer's standard `close_flight()` path, which always sets `clean_shutdown=True` in the current AZ-292 implementation), one truncated (writer terminated before `close_flight()` ran, so no footer record). Call `trigger_post_landing_upload`; assert (a) first case invokes upload via the `TileUploaderCut` and returns the recorded `UploadBatchReport`, (b) second case refuses with `FlightStateNotConfirmedError(not_confirmed_reason="footer_missing")`.
**Input data**: 2 scripted FDR fixtures.
**Input data**: 2 FDR fixtures generated by `FileFdrWriter` (one closed cleanly; one with the close skipped).
**Expected result**: per assertion.
**Expected result**: per assertion. No 30-second ON_GROUND threshold is consulted — the footer's existence + `clean_shutdown` flag is the sole signal.
**Max execution time**: 60 s.
@@ -99,7 +99,7 @@ Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C12 se
### C12-ST-01: CLI rejects writes to airborne images
**Summary**: the operator-tool CLI has no command path that writes into the airborne `production-binary` image (defends against operator-side mistakes that would defeat ADR-004).
**Summary**: the operator-orchestrator CLI has no command path that writes into the airborne `production-binary` image (defends against operator-side mistakes that would defeat ADR-004).
**Traces to**: ADR-004 R02 enforcement (C12 side)
@@ -135,7 +135,7 @@ Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`. C12 se
| Data Set | Source | Size |
|----------|--------|------|
| Operator-tooling tarball | CI build artifact | varies |
| FDR fixtures (ON_GROUND-confirmed and IN_FLIGHT) | scripted | <100 MB each |
| FDR fixtures (clean-shutdown footer present / footer absent) | generated by C13 `FileFdrWriter` | <100 MB each |
| Small Derkachi sub-area for C12-IT-02 | scripted | <500 MB |
**Setup**: extract operator-tooling tarball; bring up Docker compose.
@@ -90,7 +90,7 @@ Not applicable.
|---------|---------|---------|
| orjson / msgpack | per project pin | Record serialisation (serialised format choice during decompose phase) |
| atomicwrites | latest | Segment file rotation (atomic open of new segment + close of previous) |
| filelock | per project pin | Cross-process safety for the FDR root (operator-tool reads while companion writes — companion-only access during flight) |
| filelock | per project pin | Cross-process safety for the FDR root (operator-orchestrator reads while companion writes — companion-only access during flight) |
**Error Handling Strategy**:
- `FdrOpenError` at takeoff: refuse takeoff (per AC-NEW-3 every payload class must be present from t=0).
@@ -1,17 +1,23 @@
# Contract: tile_uploader
**Component**: c11_tilemanager
**Producer task**: AZ-319_c11_tile_uploader
**Consumer tasks**: AZ-253 (E-C12 Operator Pre-flight Tooling — TBD at C12 decompose time)
**Version**: 1.0.0
**Status**: draft
**Last Updated**: 2026-05-10
**Producer task**: AZ-319_c11_tile_uploader (initial), Batch 44 C11-SRP-revert (v2.0.0 gate removal)
**Consumer tasks**: AZ-329 (C12 `PostLandingUploadOrchestrator`) — see `_docs/02_document/contracts/c12_operator_orchestrator/` for the C12 surface that owns the post-landing safety gate.
**Version**: 2.0.0
**Status**: frozen
**Last Updated**: 2026-05-13
## Migration note — v1.0.0 → v2.0.0
Batch 44 removed C11's internal post-landing safety gate per SRP. v1.0.0 exposed `confirm_flight_state(): FlightStateSignal` and raised `FlightStateNotOnGroundError` from `upload_pending_tiles`. v2.0.0 drops both — the equivalent check moved to C12's `PostLandingUploadOrchestrator` (AZ-329), which inspects the C13 `flight_footer` FDR record and refuses to invoke `upload_pending_tiles` unless `clean_shutdown=True` is recorded. C11 is now a dumb pipe.
Consumers that still call `confirm_flight_state` or catch `FlightStateNotOnGroundError` MUST migrate to consuming C12's `FlightStateNotConfirmedError` family instead. ADR-004 process-level isolation remains the primary control — C11 never runs on the companion at all.
## Purpose
The `TileUploader` Protocol is C11's operator-side post-landing upload interface. C12 invokes it during F10 (post-landing) to read mid-flight tiles flagged pending-upload from C6 (`source = onboard_ingest`, `voting_status = pending`), package them per the D-PROJ-2 ingest contract sketch, sign each tile payload with the per-flight ephemeral key (AZ-318), and POST to `satellite-provider`'s `/api/satellite/tiles/ingest` endpoint. Acknowledged tiles are marked uploaded in C6.
The `TileUploader` Protocol is C11's operator-side post-landing upload interface. C12's `PostLandingUploadOrchestrator` (AZ-329) invokes it during F10 (post-landing) AFTER it has confirmed `clean_shutdown=True` from the C13 `flight_footer` FDR record. C11 then reads mid-flight tiles flagged pending-upload from C6 (`source = onboard_ingest`, `voting_status = pending`), packages them per the D-PROJ-2 ingest contract sketch, signs each tile payload with the per-flight ephemeral key (AZ-318), and POSTs to `satellite-provider`'s `/api/satellite/tiles/ingest` endpoint. Acknowledged tiles are marked uploaded in C6.
The uploader gates on `flight_state == ON_GROUND` (AZ-317) before any network egress. C11 is operator-side ONLY; ADR-004 forbids the airborne companion image from importing this module.
C11 is operator-side ONLY; ADR-004 forbids the airborne companion image from importing this module.
## Shape
@@ -24,14 +30,12 @@ from typing import Protocol, runtime_checkable
class TileUploader(Protocol):
def upload_pending_tiles(self, request: UploadRequest) -> UploadBatchReport: ...
def enumerate_pending_tiles(self, flight_id: uuid.UUID | None = None) -> list[TileMetadata]: ...
def confirm_flight_state(self) -> FlightStateSignal: ...
```
| Name | Signature | Throws / Errors | Blocking? |
|------|-----------|-----------------|-----------|
| `upload_pending_tiles` | `(request: UploadRequest) -> UploadBatchReport` | `FlightStateNotOnGroundError`, `SatelliteProviderError`, `RateLimitedError`, `SignatureRejectedError`, `TileMetadataError` | sync (post-landing; minutes) |
| `upload_pending_tiles` | `(request: UploadRequest) -> UploadBatchReport` | `SatelliteProviderError`, `RateLimitedError`, `SignatureRejectedError`, `TileMetadataError` | sync (post-landing; minutes) |
| `enumerate_pending_tiles` | `(flight_id: uuid.UUID \| None) -> list[TileMetadata]` | `TileMetadataError` | sync (seconds) |
| `confirm_flight_state` | `() -> FlightStateSignal` | `FlightStateNotOnGroundError` | sync (≤ 1 ms) |
### Data DTOs
@@ -70,7 +74,7 @@ class PerTileStatus:
## Invariants
- I-1: `confirm_flight_state` is called by `upload_pending_tiles` BEFORE any C6 read or network egress; if `FlightStateNotOnGroundError` is raised, NO tiles are read, NO POSTs are issued, NO C6 mutation occurs. The gate is closed by default.
- I-1 (v2.0.0): C11 itself does NOT gate on flight state. The pre-call gate is C12's `PostLandingUploadOrchestrator` (AZ-329), which inspects the C13 `flight_footer` FDR record for `clean_shutdown=True` BEFORE invoking `upload_pending_tiles`. C11 is a dumb pipe — once called, it proceeds to read C6 + POST to the satellite-provider with no internal short-circuit. ADR-004 process-level isolation remains the primary defence (C11 never runs on the companion).
- I-2: Every uploaded tile carries a signature produced by the AZ-318 per-flight key manager's `sign(payload)`. The parent suite verifies against the public key it received via the safety officer's pre-flight enrolment OR the `kind="c11.upload.session.key.public"` FDR record.
- I-3: A tile acknowledged as `queued`, `duplicate`, or `superseded` by the parent suite is marked `uploaded` in C6 (`mark_uploaded(tile_id)`); a tile acknowledged as `rejected` is NOT marked uploaded — it remains `pending` for human review.
- I-4: The per-flight signing key is zeroised at the end of `upload_pending_tiles` regardless of success or failure (try/finally in the caller; AZ-318's `end_session()`).
@@ -98,8 +102,8 @@ class PerTileStatus:
| Case | Input | Expected | Notes |
|------|-------|----------|-------|
| upload-happy-path | 50 pending tiles, ON_GROUND, parent-suite returns 202 with all `queued` | `UploadBatchReport.outcome = success`; all 50 marked `uploaded` in C6; signature verifies on each | C11-IT-03 |
| flight-state-blocks | `FlightStateSource` returns `IN_FLIGHT` | `FlightStateNotOnGroundError`; zero C6 reads; zero POSTs | C11-IT-04 |
| upload-happy-path | 50 pending tiles, parent-suite returns 202 with all `queued` | `UploadBatchReport.outcome = success`; all 50 marked `uploaded` in C6; signature verifies on each | C11-IT-03 |
| post-landing-gate-in-c12 | C12 `PostLandingUploadOrchestrator` invocation flow | The flight-state gate lives in C12 (`FlightStateNotConfirmedError`), not C11. v2.0.0 removed the C11 internal gate. | See `c12_operator_orchestrator` contract + AZ-329 spec |
| signature-rejected | Parent suite returns `rejected` for 1 tile with reason `"invalid signature"` | `PerTileStatus.status = rejected`; `outcome = partial`; FDR `c11.upload.signature_rejected` emitted; the tile NOT marked uploaded | I-5 |
| duplicate-acknowledged | Parent suite returns `duplicate` for 5 tiles (already ingested in a prior batch) | All 5 marked `uploaded`; `outcome = success` | I-3 |
| signing-key-zeroised | Run a successful upload, then assert the AZ-318 manager's `_private_key is None` | Always zeroised; FDR `c11.upload.session.key.zeroised` recorded | I-4 |
@@ -112,3 +116,4 @@ class PerTileStatus:
| Version | Date | Change | Author |
|---------|------|--------|--------|
| 1.0.0 | 2026-05-10 | Initial contract — produced by AZ-319 (E-C11 decomposition) | autodev |
| 2.0.0 | 2026-05-13 | Batch 44: remove C11 internal flight-state gate per SRP. `confirm_flight_state` method dropped; `FlightStateNotOnGroundError` retired; post-landing safety gate now owned by C12's `PostLandingUploadOrchestrator` (AZ-329). Breaking — consumers MUST migrate to C12's `FlightStateNotConfirmedError`. | autodev (Batch 44) |
@@ -1,6 +1,6 @@
# Contract: flights_api_client
**Component**: c12_operator_tooling
**Component**: c12_operator_orchestrator
**Producer task**: AZ-489 — `_docs/02_tasks/todo/AZ-489_c12_flights_api_client.md`
**Consumer tasks**: AZ-326 (CLI app — wires `--flight-id` / `--flight-file` flags), AZ-328 (build-cache orchestrator — calls `fetch_flight` / `load_flight_file`, then `bbox_from_waypoints` + `takeoff_origin_from_flight`)
**Version**: 1.0.0
@@ -1,6 +1,6 @@
# Contract: operator_command_transport
**Component**: c12_operator_tooling
**Component**: c12_operator_orchestrator
**Producer task**: AZ-330 — `_docs/02_tasks/todo/AZ-330_c12_operator_reloc_service.md`
**Consumer tasks**: TBD — a future E-C8 (AZ-261) task implements `MavlinkOperatorCommandTransport` against pymavlink
**Version**: 1.0.0
@@ -9,7 +9,7 @@
## Purpose
Defines the operator-workstation ↔ companion command channel for AC-3.4 operator-relocalization. C12 owns the Protocol shape; E-C8 (AZ-261) ships the pymavlink-backed concrete implementation that encodes the hint into a MAVLink message and transmits it over the GCS link to the airborne companion. Decoupling the two sides through this Protocol prevents C12 from having to know MAVLink details, and prevents E-C8 from having to know operator-tool internals — they meet at this contract.
Defines the operator-workstation ↔ companion command channel for AC-3.4 operator-relocalization. C12 owns the Protocol shape; E-C8 (AZ-261) ships the pymavlink-backed concrete implementation that encodes the hint into a MAVLink message and transmits it over the GCS link to the airborne companion. Decoupling the two sides through this Protocol prevents C12 from having to know MAVLink details, and prevents E-C8 from having to know operator-orchestrator internals — they meet at this contract.
## Shape
+6 -6
View File
@@ -73,7 +73,7 @@ The tile is the single most important persistent entity. The schema deliberately
- B-tree on `(zoom_level, tile_x, tile_y)` — primary spatial lookup path for VPR retrieval and pre-flight cache hydration.
- B-tree on `(latitude, longitude)` — bounding-box queries for sector classification and spatial-coverage reports.
- B-tree on `voting_status` partial WHERE `source = 'onboard_ingest'` — operator-tooling queries for "which mid-flight tiles are still pending promotion?".
- B-tree on `voting_status` partial WHERE `source = 'onboard_ingest'` — operator-orchestrator queries for "which mid-flight tiles are still pending promotion?".
- B-tree on `flight_id` — FDR cross-reference; post-landing upload batching.
- B-tree on `created_at` — pruning / rollover queries.
@@ -141,7 +141,7 @@ A lightweight tracking row per flight, used by the FDR's manifest, the Tile Mana
### 2.3 `sector_classifications` (PostgreSQL — operator-set, onboard-side cache)
Mirrors operator-tooling C12's authoritative sector classification onto the companion so the freshness gate (AC-8.2 / AC-NEW-6) can be evaluated locally without a network call.
Mirrors operator-orchestrator C12's authoritative sector classification onto the companion so the freshness gate (AC-8.2 / AC-NEW-6) can be evaluated locally without a network call.
| Column | Type | Constraints | Description |
|---|---|---|---|
@@ -318,13 +318,13 @@ record_crc32 u32
**Backward compatibility**: new record types are appended; readers MUST skip records they don't recognise (the `record_header` length is enough to advance the cursor). No record type is ever renumbered or removed; deprecation is by ceasing to emit.
**Retention**: per-flight ring; on `IN_AIR → ON_GROUND` transition, the ring is sealed and the operator-tooling FDR-retrieval workflow (C12) copies it off the companion. The companion auto-prunes flights older than the configured retention window (default: 30 days) — the prune log itself is its own FDR record on the next flight.
**Retention**: per-flight ring; on `IN_AIR → ON_GROUND` transition, the ring is sealed and the operator-orchestrator FDR-retrieval workflow (C12) copies it off the companion. The companion auto-prunes flights older than the configured retention window (default: 30 days) — the prune log itself is its own FDR record on the next flight.
---
### 2.9 Tile JPEG bodies (filesystem)
JPEG bodies live at `./tiles/{zoomLevel}/{x}/{y}.jpg`. A sidecar `./tiles/{zoomLevel}/{x}/{y}.json` carries the full row content for upload-time payload assembly. Both files are atomic-written (via `atomicwrites`); both are removed only after the corresponding `tiles` row's lifecycle says it is safe (see § 2.1.2). Filesystem and PostgreSQL drift is treated as a defect: the operator-tooling C12 has a periodic `consistency_audit` that reports any orphan files / missing files.
JPEG bodies live at `./tiles/{zoomLevel}/{x}/{y}.jpg`. A sidecar `./tiles/{zoomLevel}/{x}/{y}.json` carries the full row content for upload-time payload assembly. Both files are atomic-written (via `atomicwrites`); both are removed only after the corresponding `tiles` row's lifecycle says it is safe (see § 2.1.2). Filesystem and PostgreSQL drift is treated as a defect: the operator-orchestrator C12 has a periodic `consistency_audit` that reports any orphan files / missing files.
---
@@ -533,7 +533,7 @@ Schema-version bumps are tracked in `_docs/02_document/schemas/` (a new `tiles_q
### 6.5 FDR file-format compatibility
The FDR `record_header` is fixed at version 1. Every FDR reader (operator-tooling, replay tools) MUST:
The FDR `record_header` is fixed at version 1. Every FDR reader (operator-orchestrator, replay tools) MUST:
- Validate `magic == 0x47464452` and skip a corrupt segment.
- Read the `version` field; on `version != 1`, refuse to interpret the body and emit a "unknown FDR version" diagnostic.
@@ -568,4 +568,4 @@ The following DTOs flow through the per-frame pipeline in memory and are **NOT**
- **D-PROJ-2 #1 ingest-endpoint contract**: the `signature` column's exact algorithm (Ed25519 vs ECDSA) and the per-flight key distribution is a parent-suite design decision; onboard side is contract-flexible and treats `signature` as opaque `bytea`.
- **D-PROJ-2 #2 voting-layer schema**: parent-suite-side; this onboard data model writes `voting_status='pending'` and reads `'trusted'` only — the actual promotion table lives in `satellite-provider`'s schema and is out of scope here.
- **GeoJSON polygon precision** (`sector_classifications.polygon_geojson`): GeoJSON is precision-bounded by JSON number representation; if AC-NEW-7 cache-poisoning safety needs sub-metre polygon edges, a future migration can switch to PostGIS `geography(Polygon, 4326)`. Captured as carryforward (currently no AC requirement to do so).
- **FDR retention policy default**: 30 days post-landing is a reasonable default but is not pinned in any AC; carryforward to the operator-tooling spec (C12) for confirmation.
- **FDR retention policy default**: 30 days post-landing is a reasonable default but is not pinned in any AC; carryforward to the operator-orchestrator spec (C12) for confirmation.
@@ -19,7 +19,7 @@ The pipeline has **two execution tiers** (architecture.md ADR-005), reflected in
| Build (Tier-2 deployment binary) | PR merge to `dev`, `stage`, `main` | Tier-2 (self-hosted Jetson) | Native build on Jetson green; deployment binary SBOM matches Tier-1 deployment SBOM |
| AC-bound NFTs (Tier-2) | PR merge to `dev`, `stage`, `main`; manual on PR | Tier-2 | NFT-PERF-* (AC-4.1, AC-NEW-1, AC-NEW-2), NFT-LIM-* (AC-4.2, AC-NEW-3), NFT-RES-* (AC-NEW-4, AC-NEW-7), IT-12 (comparative study) all pass thresholds in `tests/traceability-matrix.md` |
| JetPack image build | Tag on `main` | Tier-2 | JetPack 6.2 image built with deployment binary preinstalled, signed, and attested |
| Operator tooling tarball | Tag on `main` | Tier-1 | Tarball contains C11 Tile Manager (both `TileDownloader` and `TileUploader`) + C12 Operator Pre-flight Tooling + mock-sat-service compose + verification script |
| Operator tooling tarball | Tag on `main` | Tier-1 | Tarball contains C11 Tile Manager (both `TileDownloader` and `TileUploader`) + C12 Operator Pre-flight Orchestrator + mock-sat-service compose + verification script |
Tier-2 jobs are the **only** AC-bound jobs. Everything else runs on Tier-1.
@@ -146,7 +146,7 @@ Runs on tag push to `main`. Produces `gps-denied-jetpack-<semver>-<sha>.img` (th
### Operator tooling tarball (release-only)
Bundles `operator-tooling` Docker image + `mock-suite-sat-service` Docker image + their compose file + a verification script + the documentation under `_docs/02_document/`. The tarball is uploaded to the release bucket alongside the JetPack image.
Bundles `operator-orchestrator` Docker image + `mock-suite-sat-service` Docker image + their compose file + a verification script + the documentation under `_docs/02_document/`. The tarball is uploaded to the release bucket alongside the JetPack image.
## Caching Strategy
@@ -9,7 +9,7 @@ This project has **asymmetric containerization** by design (architecture.md § 3
- **Tier-1** (workstation): Docker is the universal runtime. Dev, lint, unit, most integration, and `mock-suite-sat-service` all run in Docker compose.
- **Tier-2 (Jetson)**: **NO Docker**. The deployed JetPack image runs the deployment binary natively. TensorRT INT8 calibration caches and `jetson-stats` thermal telemetry are most reliable without a container layer (D-C7-9 + D-C10-6). The "image" is a JetPack 6.2 system image with the deployment binary preinstalled.
- **Operator workstation**: Docker is used for the local `satellite-provider` mirror, the `mock-suite-sat-service` (when offline), and the operator-tooling stack (C11 Tile Manager + C12 Operator Pre-flight Tooling).
- **Operator workstation**: Docker is used for the local `satellite-provider` mirror, the `mock-suite-sat-service` (when offline), and the operator-orchestrator stack (C11 Tile Manager + C12 Operator Pre-flight Orchestrator).
Three Dockerfiles are maintained; the airborne companion uses **none of them** in production.
@@ -43,9 +43,9 @@ e2e-test fixture only — implements the planned D-PROJ-2 ingest contract (`POST
| Health check | HTTP `GET /healthz` (returns 200 if listening + storage backend mounted). 10 s interval. |
| Exposed ports | `5100/tcp` (matches `satellite-provider`'s port so the same client config works) |
| Key build args | `MOCK_FAILURE_PROFILE` (default `none`; used by NFT-SEC-01 to inject latency / 5xx / partial responses) |
| Notes | The mock is a release artifact (operator-tooling tarball includes its compose file). When the real `satellite-provider` D-PROJ-2 endpoint ships, the mock is retired. |
| Notes | The mock is a release artifact (operator-orchestrator tarball includes its compose file). When the real `satellite-provider` D-PROJ-2 endpoint ships, the mock is retired. |
### `operator-tooling` (Operator workstation Tile Manager + pre-flight UI, C11 + C12)
### `operator-orchestrator` (Operator workstation Tile Manager + pre-flight UI, C11 + C12)
| Property | Value |
|----------|-------|
@@ -53,7 +53,7 @@ e2e-test fixture only — implements the planned D-PROJ-2 ingest contract (`POST
| Build image | `python:3.10-slim` (no native deps; pure Python plus `httpx` for both download and upload, `psycopg` for read/write of C6 mirror, `cryptography` for upload signing) |
| Stages | `python-deps``runtime` |
| User | `operator` (non-root) |
| Health check | `python -m operator_tooling.healthcheck` (validates `satellite-provider` reachable). 30 s interval. |
| Health check | `python -m operator_orchestrator.healthcheck` (validates `satellite-provider` reachable). 30 s interval. |
| Exposed ports | `8080/tcp` (operator pre-flight UI, C12); no inbound network for C11 Tile Manager (it's a CLI / one-shot tool, both directions) |
| Key build args | `INCLUDE_PRE_FLIGHT_UI=true` (default; can be turned off for headless CLI-only deployments) |
| Notes | **C11 Tile Manager (both `TileDownloader` and `TileUploader`) is in this image, NEVER in `gps-denied-companion-tier1`** (ADR-004 process-level isolation). The airborne deployment binary on Tier-2 also does not contain C11. |
@@ -120,11 +120,11 @@ services:
interval: 5s
networks: [ gps-denied-net ]
operator-tooling:
operator-orchestrator:
build:
context: .
dockerfile: docker/operator-tooling.Dockerfile
image: gps-denied/operator-tooling:dev
dockerfile: docker/operator-orchestrator.Dockerfile
image: gps-denied/operator-orchestrator:dev
environment:
- SATELLITE_PROVIDER_URL=http://mock-sat:5100
- COMPANION_DB_URL=postgresql://gps_denied:dev@db:5432/gps_denied
@@ -207,7 +207,7 @@ Tier-2 CI runs the same deployment binary directly on the self-hosted Jetson run
| CI build (deployment binary) | `<registry>/gps-denied/companion-tier1:deployment-<git-sha>` | `ghcr.io/azaion/gps-denied/companion-tier1:deployment-a1b2c3d` |
| CI build (research binary) | `<registry>/gps-denied/companion-tier1:research-<git-sha>` | `ghcr.io/azaion/gps-denied/companion-tier1:research-a1b2c3d` |
| Mock sat service | `<registry>/gps-denied/mock-suite-sat-service:<git-sha>` | `ghcr.io/azaion/gps-denied/mock-suite-sat-service:a1b2c3d` |
| Operator tooling | `<registry>/gps-denied/operator-tooling:<git-sha>` | `ghcr.io/azaion/gps-denied/operator-tooling:a1b2c3d` |
| Operator tooling | `<registry>/gps-denied/operator-orchestrator:<git-sha>` | `ghcr.io/azaion/gps-denied/operator-orchestrator:a1b2c3d` |
| Release | `<registry>/gps-denied/<image>:<semver>` | `ghcr.io/azaion/gps-denied/companion-tier1:deployment-1.2.0` |
| Local dev | `gps-denied/<image>:dev` | `gps-denied/companion-tier1:dev` |
| JetPack image (Tier-2) | `gps-denied-jetpack-<semver>-<sha>.img` | `gps-denied-jetpack-1.2.0-a1b2c3d.img` (file artifact, not a container tag) |
@@ -5,12 +5,12 @@
## Deployment scope and model
This project does **not** ship a service; it ships an **embedded edge image** plus an **operator-tooling bundle**. The "deployment" patterns from the standard template (blue-green / rolling / canary) are not applicable. Deployment for this project means:
This project does **not** ship a service; it ships an **embedded edge image** plus an **operator-orchestrator bundle**. The "deployment" patterns from the standard template (blue-green / rolling / canary) are not applicable. Deployment for this project means:
| Artifact | Target | Deployment mechanism |
|---|---|---|
| **JetPack image** (`gps-denied-jetpack-<semver>-<sha>.img`) | Production Jetson Orin Nano Super on a UAV | Operator flashes the image onto the Jetson via NVIDIA `sdkmanager` or `Etcher`-style `dd` from the operator workstation |
| **Operator tooling tarball** | Operator workstation | Operator extracts; `docker compose up -d` brings up `mock-suite-sat-service` (when offline) + `operator-tooling` |
| **Operator tooling tarball** | Operator workstation | Operator extracts; `docker compose up -d` brings up `mock-suite-sat-service` (when offline) + `operator-orchestrator` |
| **Tier-1 dev compose** | Developer workstation | Developer runs `docker compose up` from repo root |
**Zero-downtime is not a goal**: a UAV is not in service while it is being re-flashed. The deployment cadence is per-airframe maintenance, not per-request availability.
@@ -25,9 +25,9 @@ Performed once per release on Tier-1 + Tier-2 CI; produces signed artifacts stor
2. **Tier-1 produces**:
- `companion-tier1:deployment-<sha>` and `companion-tier1:research-<sha>` Docker images (pushed to registry).
- `mock-suite-sat-service:<sha>` Docker image.
- `operator-tooling:<sha>` Docker image.
- `operator-orchestrator:<sha>` Docker image.
- SBOM artifacts for both binaries (deployment and research).
- `operator-tooling-<semver>-<sha>.tar.gz` containing the operator-tooling image + mock-sat image + their compose file + verification script + relevant docs.
- `operator-orchestrator-<semver>-<sha>.tar.gz` containing the operator-orchestrator image + mock-sat image + their compose file + verification script + relevant docs.
3. **Tier-2 produces**:
- Native deployment-binary build on the self-hosted Jetson runner.
- SBOM verification: byte-equal (after canonicalization) to Tier-1's deployment-binary SBOM. Mismatch fails the release.
@@ -35,7 +35,7 @@ Performed once per release on Tier-1 + Tier-2 CI; produces signed artifacts stor
4. **Signing** (Tier-1):
- Both Docker image manifests are signed with the project's release key.
- The JetPack image is signed; checksum is published as a separate signed file (`gps-denied-jetpack-<semver>-<sha>.img.sha256.sig`).
- The operator-tooling tarball is signed.
- The operator-orchestrator tarball is signed.
5. **Release bucket**: artifacts uploaded; release notes published; the previous release's artifacts retained for at least 90 days for rollback support.
A release fails if any step above fails — including any AC-bound NFT failure on Tier-2 (`ci_cd_pipeline.md` § AC-bound NFTs).
@@ -85,19 +85,19 @@ cosign verify-blob \
sha256sum -c gps-denied-jetpack-<semver>-<sha>.img.sha256
# Verify the operator-tooling tarball.
# Verify the operator-orchestrator tarball.
cosign verify-blob \
--signature operator-tooling-<semver>-<sha>.tar.gz.sig \
--signature operator-orchestrator-<semver>-<sha>.tar.gz.sig \
--key gps-denied-release-key.pub \
operator-tooling-<semver>-<sha>.tar.gz
operator-orchestrator-<semver>-<sha>.tar.gz
```
### 3. Pre-flight cache build (operator-tooling C12)
### 3. Pre-flight cache build (operator-orchestrator C12)
Performed on the operator workstation, with `satellite-provider` reachable (locally mirrored or via lab VPN).
```sh
docker compose -f operator-tooling-compose.yml up -d
docker compose -f operator-orchestrator-compose.yml up -d
# Operator opens http://127.0.0.1:8080
```
@@ -164,7 +164,7 @@ The first flight on a freshly-deployed airframe is a **commissioning flight**, n
Post first commissioning flight:
- [ ] FDR retrieved and visualized on operator workstation (operator-tooling C12 dashboard, observability.md § 5.1).
- [ ] FDR retrieved and visualized on operator workstation (operator-orchestrator C12 dashboard, observability.md § 5.1).
- [ ] AC-NEW-4 statistics for the commissioning flight reviewed; outliers investigated.
- [ ] No FDR segment drops; no `ContentHashGateFail` events.
- [ ] Mid-flight tile generation working (post-landing upload — handle that separately).
@@ -172,12 +172,12 @@ Post first commissioning flight:
## Post-landing tile upload (per-flight, ADR-004)
Per AC-8.4 + ADR-004, mid-flight tile upload to `satellite-provider` is **post-landing only**, and uses the operator-tooling's C11 Tile Manager (`TileUploader` interface; a separate binary, never linked into the airborne image).
Per AC-8.4 + ADR-004, mid-flight tile upload to `satellite-provider` is **post-landing only**, and uses the operator-orchestrator's C11 Tile Manager (`TileUploader` interface; a separate binary, never linked into the airborne image).
```sh
# Operator plugs the companion's NVM into the workstation OR ssh's into the powered-off-then-re-booted Jetson.
docker compose run operator-tooling \
python -m operator_tooling.tilemanager upload \
docker compose run operator-orchestrator \
python -m operator_orchestrator.tilemanager upload \
--flight-id <uuid> \
--satellite-provider $SATELLITE_PROVIDER_URL \
--signing-pubkey-fingerprint <fingerprint>
@@ -210,7 +210,7 @@ When the parent-suite voting layer (D-PROJ-2 design task #2) ships, this flow do
### Rollback steps (per-airframe)
1. **Re-flash** the previous release's JetPack image onto the affected Jetson (same procedure as § 4 with the previous artifact).
2. **Re-stage** the previous release's pre-flight bundle (the operator workstation retains it in the operator-tooling cache for ≥ 30 days).
2. **Re-stage** the previous release's pre-flight bundle (the operator workstation retains it in the operator-orchestrator cache for ≥ 30 days).
3. **Re-run** the pre-takeoff readiness gate.
4. **Confirm** AC-5.2 fallback is still functional (it is FC firmware behavior; rolling back the companion image cannot break it, but verify on the GCS).
5. **Document** the rollback in the post-mortem template; include FDR snapshots from the offending flight (if any) plus the rollback artifacts versions.
@@ -141,7 +141,7 @@ This means the threat surface on a captured companion reduces to "what is in the
|---|---|---|
| Per-flight MAVLink signing key | Every flight (per-flight ephemeral) | Automated at takeoff load |
| Per-flight onboard tile-signing key | Every flight (per-flight ephemeral) | Automated at takeoff load |
| `SATELLITE_PROVIDER_API_KEY` | Operator-managed; rotated when an operator workstation is reissued or compromised is suspected | Operator workstation hardening procedure (out of scope of this document; operator-tooling C12 owns it) |
| `SATELLITE_PROVIDER_API_KEY` | Operator-managed; rotated when an operator workstation is reissued or compromised is suspected | Operator workstation hardening procedure (out of scope of this document; operator-orchestrator C12 owns it) |
| Production binary signing key | Per release cycle or on suspected compromise | Release engineer rotates; new key fingerprint is published in release notes; verification scripts on the operator workstation pull the latest fingerprint |
| JetPack image signing key | Same as production binary signing key | Same |
@@ -12,7 +12,7 @@ Observability therefore splits into three regimes:
| Regime | Where | Live or post-flight | Primary mechanism |
|---|---|---|---|
| **In-flight onboard** | Production Jetson, in flight | Live (to FDR ring) + best-effort live (to GCS) | FDR binary record stream + GCS STATUSTEXT / NAMED_VALUE_FLOAT |
| **Post-flight onboard** | Operator workstation after pulling the FDR | Post-flight | FDR replay + visualization in operator-tooling C12 |
| **Post-flight onboard** | Operator workstation after pulling the FDR | Post-flight | FDR replay + visualization in operator-orchestrator C12 |
| **CI / dev (Tier-1, Tier-2)** | Workstation Docker / Jetson CI runner | Live | Standard structured logging + Prometheus metrics endpoint where applicable |
The sections below are organized by regime.
@@ -85,7 +85,7 @@ There is no Prometheus endpoint on the production airborne companion. The justif
When the operator plugs the companion in post-landing:
1. **FDR retrieval** (operator tooling C12 — feature, not in scope of this document's structure but observability-impacting): operator-tooling reads the FDR ring, copies it to the workstation, and seals the in-flight ring. The companion's per-flight ephemeral keys are deleted at this step (environment_strategy.md § Per-flight key lifecycle).
1. **FDR retrieval** (operator tooling C12 — feature, not in scope of this document's structure but observability-impacting): operator-orchestrator reads the FDR ring, copies it to the workstation, and seals the in-flight ring. The companion's per-flight ephemeral keys are deleted at this step (environment_strategy.md § Per-flight key lifecycle).
2. **Visualization** (operator tooling C12): the workstation renders:
- Time-series of `horiz_accuracy`, `vert_accuracy`, `last_anchor_age_ms`, source label timeline, thermal-throttle hybrid switches, and CPU / GPU / temp.
- Map view: emitted positions vs. (when available) FC `GLOBAL_POSITION_INT` ground truth.
@@ -173,7 +173,7 @@ Collection interval: 15 s (typical Prometheus default; Tier-2 NFT runs may use 1
The runtime is a single in-process Python program with no cross-service hops in flight (architecture.md § 5 internal communication is all in-process). Distributed tracing is therefore not applicable to the production runtime.
The Tier-1 integration setup DOES involve cross-container hops (companion ↔ mock-sat ↔ db ↔ e2e-runner), but those are exercised by the e2e test framework's own log + status capture; OpenTelemetry is not provisioned for this project. If a future cycle introduces a multi-process companion (which ADR-004 explicitly rejected for the airborne profile but might appear on the operator workstation for C11 Tile Manager + C12 Operator Pre-flight Tooling), tracing can be reconsidered then.
The Tier-1 integration setup DOES involve cross-container hops (companion ↔ mock-sat ↔ db ↔ e2e-runner), but those are exercised by the e2e test framework's own log + status capture; OpenTelemetry is not provisioned for this project. If a future cycle introduces a multi-process companion (which ADR-004 explicitly rejected for the airborne profile but might appear on the operator workstation for C11 Tile Manager + C12 Operator Pre-flight Orchestrator), tracing can be reconsidered then.
## 4. Alerting (post-flight, not in-flight)
@@ -201,7 +201,7 @@ There is no PagerDuty / on-call rotation for this project; in-flight failures ar
### 5.1 Operator workstation post-flight dashboard
Built into operator-tooling C12. Per flight:
Built into operator-orchestrator C12. Per flight:
- Time series: source label, `horiz_accuracy`, `last_anchor_age_ms`, CPU%, GPU%, temp.
- Event markers: VISUAL_BLACKOUT entries, spoofing events, signing key rotations, thermal hybrid switches.
@@ -227,6 +227,6 @@ Out of scope by design. The GCS is the only live operator surface; all other ins
## 6. Open Items / Plan-Phase Carryforward
- **Long-term FDR archive** (multi-flight statistical headroom): D-PROJ-3 (multi-flight fixture acquisition for AC-NEW-4 / AC-NEW-7) is not pursued this cycle. If pursued in a future cycle, post-flight FDR archives become a corpus contribution path; the operator-tooling FDR-retrieval step would need an explicit "contribute to corpus" toggle.
- **Long-term FDR archive** (multi-flight statistical headroom): D-PROJ-3 (multi-flight fixture acquisition for AC-NEW-4 / AC-NEW-7) is not pursued this cycle. If pursued in a future cycle, post-flight FDR archives become a corpus contribution path; the operator-orchestrator FDR-retrieval step would need an explicit "contribute to corpus" toggle.
- **Telemetry-link encryption** beyond MAVLink-2.0 signing: out of scope; addressed by physical link assumptions in the threat model (architecture.md § 7).
- **iNav signing**: still has no equivalent to MAVLink-2.0 signing (Mode B Source #129). Carryforward Plan-phase action: file a feature request upstream; meanwhile observability for iNav-profile flights is the same as AP-profile minus the `MavlinkSigningKeyRotated` records (which are NULL on iNav flights per data_model.md § 2.2).
+10 -10
View File
@@ -27,7 +27,7 @@ Row 20 (E-CC-HELPERS / AZ-264) was added during Decompose Step 2 to comply with
| 7 | E-C6 | C6 Tile Cache + Spatial Index | component | AZ-250 | M | 1321 | E-BOOT, E-CC-LOG, E-CC-CONF |
| 8 | E-C11 | C11 Tile Manager (TileDownloader + TileUploader) | component | AZ-251 | M | 1321 | E-C6, E-CC-CONF, E-CC-LOG |
| 9 | E-C10 | C10 Pre-flight Cache Provisioning | component | AZ-252 | M | 1321 | E-C6, E-C7, E-CC-LOG |
| 10 | E-C12 | C12 Operator Pre-flight Tooling | component | AZ-253 | M | 1321 | E-C10, E-C11, E-CC-LOG |
| 10 | E-C12 | C12 Operator Pre-flight Orchestrator | component | AZ-253 | M | 1321 | E-C10, E-C11, E-CC-LOG |
| 11 | E-C1 | C1 Visual / Visual-Inertial Odometry | component | AZ-254 | XL | 3455 | E-BOOT, E-CC-FDR-CLIENT, E-C7 |
| 12 | E-C2 | C2 Visual Place Recognition | component | AZ-255 | L | 2134 | E-C6, E-C7, E-CC-FDR-CLIENT |
| 13 | E-C2.5 | C2.5 Inlier-based Re-rank | component | AZ-256 | S | 58 | E-C2, E-C7, E-C6 (LightGlue helper shared with C3) |
@@ -127,7 +127,7 @@ flowchart LR
### Problem / Context
No source layout exists yet. Every downstream epic assumes a defined repo skeleton: `src/components/<id>_<name>/`, `src/shared/<concern>/`, `tests/`, `tests/fixtures/`, plus the Tier-1 Docker compose, the Tier-2 CI job, the Postgres init scripts that match `data_model.md`, and the operator-tooling tarball build path. Until this exists, no other epic can start.
No source layout exists yet. Every downstream epic assumes a defined repo skeleton: `src/components/<id>_<name>/`, `src/shared/<concern>/`, `tests/`, `tests/fixtures/`, plus the Tier-1 Docker compose, the Tier-2 CI job, the Postgres init scripts that match `data_model.md`, and the operator-orchestrator tarball build path. Until this exists, no other epic can start.
### Scope
@@ -1047,7 +1047,7 @@ Per `components/11_c10_provisioning/tests.md`.
---
## E-C12 — C12 Operator Pre-flight Tooling
## E-C12 — C12 Operator Pre-flight Orchestrator
**Tracker**: AZ-253 | **Type**: component | **T-shirt**: M | **Story points**: 1321
@@ -1055,7 +1055,7 @@ Per `components/11_c10_provisioning/tests.md`.
```mermaid
flowchart LR
CLI[operator-tool CLI]
CLI[operator-orchestrator CLI]
CLI --> C11D[C11 TileDownloader]
CLI --> C10[C10 CacheProvisioner]
CLI --> C11U[C11 TileUploader]
@@ -1065,7 +1065,7 @@ flowchart LR
### Problem / Context
Operator-facing CLI that sequences pre-flight (C11 download → C10 build) and post-landing (C11 upload), surfaces actionable failures, and handles the AC-3.4 re-localization workflow. Delivered as part of the operator-tooling tarball.
Operator-facing CLI that sequences pre-flight (C11 download → C10 build) and post-landing (C11 upload), surfaces actionable failures, and handles the AC-3.4 re-localization workflow. Delivered as part of the operator-orchestrator tarball.
### Scope
@@ -1075,7 +1075,7 @@ Operator-facing CLI that sequences pre-flight (C11 download → C10 build) and p
### Architecture notes
- File: `components/13_c12_operator_tooling/description.md`.
- File: `components/13_c12_operator_orchestrator/description.md`.
- Strict process boundary: C12 is operator-side only, in the same image as C11, but never airborne.
### Interface specification
@@ -1144,7 +1144,7 @@ T-shirt M; 1321 points.
### Testing strategy
Per `components/13_c12_operator_tooling/tests.md`.
Per `components/13_c12_operator_orchestrator/tests.md`.
---
@@ -1616,7 +1616,7 @@ sequenceDiagram
### Risks & mitigations
- **R10** (latency under throttle) — threshold tunable via operator-tooling pre-flight.
- **R10** (latency under throttle) — threshold tunable via operator-orchestrator pre-flight.
### Effort
@@ -2124,7 +2124,7 @@ ROS as the input transport was considered and rejected: the system is MAVLink-na
### Architecture notes
- ADR-001 / ADR-002 / ADR-009 all apply unchanged.
- New `BUILD_*` flags: `BUILD_VIDEO_FILE_FRAME_SOURCE`, `BUILD_TLOG_REPLAY_ADAPTER`, `BUILD_REPLAY_SINK_JSONL`. Default ON for the new replay-cli binary; OFF for airborne, research, and operator-tooling.
- New `BUILD_*` flags: `BUILD_VIDEO_FILE_FRAME_SOURCE`, `BUILD_TLOG_REPLAY_ADAPTER`, `BUILD_REPLAY_SINK_JSONL`. Default ON for the new replay-cli binary; OFF for airborne, research, and operator-orchestrator.
- New cross-cutting `FrameSource` interface lives at `src/gps_denied_onboard/frame_source/` (Layer 1 Foundation per `module-layout.md` § layering).
- `compose_replay` lives in `runtime_root.py` alongside `compose_root` and `compose_operator`.
@@ -2209,7 +2209,7 @@ T-shirt M; 2732 points across 8 child tasks.
- ADR-001 / ADR-002 / ADR-009.
- C1C5 components MUST remain mode-agnostic; replay-aware logic lives only in the composition root, the new strategies, and the CLI.
- No HTTP server in any companion binary (airborne or replay); HTTP wrapper, if added later, lives in operator-tooling per `module-layout.md` Layer-4 placement.
- No HTTP server in any companion binary (airborne or replay); HTTP wrapper, if added later, lives in operator-orchestrator per `module-layout.md` Layer-4 placement.
### Testing strategy
+1 -1
View File
@@ -76,7 +76,7 @@ Terms are alphabetical. Each entry: one-line definition + parenthetical source.
**Satellite anchored** — Source label `satellite_anchored`: estimate produced by matching the current nav frame against pre-cached satellite tiles. Highest confidence among the three labels. (source: AC-1.4)
**Sector classification** — Pre-flight operator decision: active-conflict (6-month tile-freshness threshold) vs stable rear (12-month threshold). Drives the freshness gate at ingest and during runtime tile use. (source: AC-8.2, AC-NEW-6, `solution.md` operator-tooling section)
**Sector classification** — Pre-flight operator decision: active-conflict (6-month tile-freshness threshold) vs stable rear (12-month threshold). Drives the freshness gate at ingest and during runtime tile use. (source: AC-8.2, AC-NEW-6, `solution.md` operator-orchestrator section)
**Source label** — Provenance tag carried with every emitted estimate: `{satellite_anchored | visual_propagated | dead_reckoned}`. (source: AC-1.4)
+15 -15
View File
@@ -221,7 +221,7 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- Composition root: `runtime_root/c10_factory.py` (`build_engine_compiler`, `build_backbone_specs`, `build_manifest_builder`, `build_manifest_verifier`, `build_descriptor_batcher` + the C6→C10 adapters `c6_tile_metadata_store_to_tiles_batch_query`, `c6_tile_store_to_pixel_opener`, `c6_descriptor_index_to_rebuilder`)
- **Owns**: `src/gps_denied_onboard/components/c10_provisioning/**`, `tests/unit/c10_provisioning/**`
- **Imports from**: `_types` (cross-component DTOs `EngineCacheEntry`, `BuildConfig`, `PrecisionMode`, `OptimizationProfile`, `HostCapabilities`, `TileMetadata`, etc.), `_types.inference_errors` (AZ-507 typed-error envelope for `EngineBuildError` + `CalibrationCacheError`), `helpers.sha256_sidecar`, `helpers.engine_filename_schema`, `helpers.wgs_converter`, `config`, `logging`, `fdr_client`. The `InferenceRuntime.compile_engine` surface (c7) and the `TileMetadataStore.query_by_bbox` surface (c6) are obtained via constructor-injected consumer-side structural Protocol cuts (the `CompileEngineCallable` cut already lives in `engine_compiler.py`; AZ-323 / AZ-324 will define analogous `query_by_bbox` cuts inside `c10_provisioning/`). NEVER `from gps_denied_onboard.components.c6_tile_cache import ...` or `from gps_denied_onboard.components.c7_inference import ...` inside `c10_provisioning/*.py`.
- **Consumed by**: `c12_operator_tooling`, `runtime_root` (operator binary only — excluded from airborne via `BUILD_C10_PROVISIONING=OFF` for airborne build per ADR-002)
- **Consumed by**: `c12_operator_orchestrator`, `runtime_root` (operator binary only — excluded from airborne via `BUILD_C10_PROVISIONING=OFF` for airborne build per ADR-002)
### Component: c11_tile_manager
@@ -235,12 +235,12 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- `satellite_provider_uploader.py` (post-landing batch upload, D-PROJ-2 ingest contract)
- **Owns**: `src/gps_denied_onboard/components/c11_tile_manager/**`, `tests/unit/c11_tile_manager/**`
- **Imports from**: `_types`, `helpers.sha256_sidecar`, `helpers.wgs_converter`, `config`, `logging`, `fdr_client`. The c6 storage surface (`TileStore`, `TileMetadataStore`) is obtained via constructor-injected consumer-side structural Protocol cuts (see AZ-507 cross-component rule below); composition root wires the concrete c6 strategy in. NEVER `from gps_denied_onboard.components.c6_tile_cache import ...` inside `c11_tile_manager/*.py`.
- **Consumed by**: `c12_operator_tooling`, `runtime_root` (operator binary only — `BUILD_C11_TILE_MANAGER=OFF` for airborne)
- **Consumed by**: `c12_operator_orchestrator`, `runtime_root` (operator binary only — `BUILD_C11_TILE_MANAGER=OFF` for airborne)
### Component: c12_operator_tooling
### Component: c12_operator_orchestrator
- **Epic**: AZ-253 (E-C12 Operator Pre-flight Tooling)
- **Directory**: `src/gps_denied_onboard/components/c12_operator_tooling/`
- **Epic**: AZ-253 (E-C12 Operator Pre-flight Orchestrator)
- **Directory**: `src/gps_denied_onboard/components/c12_operator_orchestrator/`
- **Public API**:
- `__init__.py` (re-exports `CacheBuildWorkflow`, `OperatorReLocService`)
- `interface.py`
@@ -248,9 +248,9 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- `cache_build_workflow.py` (CLI orchestrator)
- `operator_reloc_service.py` (CLI; GUI deferred per epic)
- `sector_classifier.py` (operator sets `SectorClassification` → C6)
- **Owns**: `src/gps_denied_onboard/components/c12_operator_tooling/**`, `tests/unit/c12_operator_tooling/**`
- **Imports from**: `_types`, `helpers.wgs_converter`, `config`, `logging`, `fdr_client`. The c6 / c10 / c11 surfaces (`TileStore`, `TileMetadataStore`, `CacheProvisioner`, `TileDownloader`, `TileUploader`) are obtained via constructor-injected consumer-side structural Protocol cuts (see AZ-507 cross-component rule below); composition root wires the concrete c6/c10/c11 strategies in. NEVER `from gps_denied_onboard.components.c6_tile_cache import ...`, `from gps_denied_onboard.components.c10_provisioning import ...`, or `from gps_denied_onboard.components.c11_tile_manager import ...` inside `c12_operator_tooling/*.py`.
- **Consumed by**: `runtime_root` (operator binary only — `BUILD_C12_OPERATOR_TOOLING=OFF` for airborne)
- **Owns**: `src/gps_denied_onboard/components/c12_operator_orchestrator/**`, `tests/unit/c12_operator_orchestrator/**`
- **Imports from**: `_types`, `helpers.wgs_converter`, `config`, `logging`, `fdr_client`. The c6 / c10 / c11 surfaces (`TileStore`, `TileMetadataStore`, `CacheProvisioner`, `TileDownloader`, `TileUploader`) are obtained via constructor-injected consumer-side structural Protocol cuts (see AZ-507 cross-component rule below); composition root wires the concrete c6/c10/c11 strategies in. NEVER `from gps_denied_onboard.components.c6_tile_cache import ...`, `from gps_denied_onboard.components.c10_provisioning import ...`, or `from gps_denied_onboard.components.c11_tile_manager import ...` inside `c12_operator_orchestrator/*.py`.
- **Consumed by**: `runtime_root` (operator binary only — `BUILD_C12_OPERATOR_ORCHESTRATOR=OFF` for airborne)
### Component: c13_fdr
@@ -330,7 +330,7 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **Directory**: `src/gps_denied_onboard/helpers/wgs_converter.py`
- **Purpose**: WGS84 ↔ local-tangent-plane conversion utilities (`04_helper_wgs_converter.md`).
- **Owned by**: AZ-264.
- **Consumed by**: c4_pose, c5_state, c6_tile_cache, c8_fc_adapter, c10_provisioning, c11_tile_manager, c12_operator_tooling.
- **Consumed by**: c4_pose, c5_state, c6_tile_cache, c8_fc_adapter, c10_provisioning, c11_tile_manager, c12_operator_orchestrator.
### shared/helpers/sha256_sidecar
@@ -379,7 +379,7 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **File**: `src/gps_denied_onboard/runtime_root.py`
- **Purpose**: Composition root — config → strategy resolution → graph wiring (ADR-009). The ONLY place that may import concrete strategy classes across components. Per-binary CMake `BUILD_*` flags + composition root validator enforce ADR-002 build-time exclusion. Hosts `compose_root(config)` (airborne), `compose_operator(config)` (operator), and `compose_replay(config)` (replay-cli).
- **Owned by**: AZ-263 (Bootstrap stub); per-component additions that wire a new strategy are owned jointly by the bootstrap epic and the consuming component task (touching `runtime_root.py` is allowed only via the explicit "wire-in" task in each component's epic). The `compose_replay` extension is owned by AZ-265 child task #4.
- **Consumed by**: the airborne binary entrypoint + the operator-tooling binary entrypoint + the research/comparative binary entrypoint + the replay-cli binary entrypoint.
- **Consumed by**: the airborne binary entrypoint + the operator-orchestrator binary entrypoint + the research/comparative binary entrypoint + the replay-cli binary entrypoint.
### shared/cli/replay
@@ -393,7 +393,7 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **File**: `src/gps_denied_onboard/healthcheck.py`
- **Purpose**: Importable healthcheck callable used by Dockerfile `HEALTHCHECK CMD` and CI smoke.
- **Owned by**: AZ-263.
- **Consumed by**: companion-tier1 Dockerfile, operator-tooling Dockerfile, CI smoke job.
- **Consumed by**: companion-tier1 Dockerfile, operator-orchestrator Dockerfile, CI smoke job.
## Allowed Dependencies (Layering)
@@ -402,7 +402,7 @@ Read top-to-bottom; an upper layer may import from a lower layer but NEVER the r
| Layer | Components / Modules | May import from |
|-------|---------------------|-----------------|
| 5. Entry / Composition | `runtime_root`, `cli/replay`, `healthcheck` | 1, 2, 3, 4 |
| 4. Adapters | c8_fc_adapter (incl. `tlog_replay_adapter` + `replay_sink`), c11_tile_manager, c10_provisioning, c12_operator_tooling, `frame_source/VideoFileFrameSource` + `frame_source/LiveCameraFrameSource` | 1, 2, 3 (limited — see notes) |
| 4. Adapters | c8_fc_adapter (incl. `tlog_replay_adapter` + `replay_sink`), c11_tile_manager, c10_provisioning, c12_operator_orchestrator, `frame_source/VideoFileFrameSource` + `frame_source/LiveCameraFrameSource` | 1, 2, 3 (limited — see notes) |
| 3. Domain (runtime path) | c1_vio, c2_vpr, c2_5_rerank, c3_matcher, c3_5_adhop, c4_pose, c5_state, c13_fdr | 1, 2 |
| 2. Infrastructure | c6_tile_cache, c7_inference | 1 |
| 1. Foundation (shared) | `_types`, `config`, `logging`, `fdr_client`, `helpers/*`, `frame_source` (interface only), `clock` | (none) |
@@ -415,7 +415,7 @@ Read top-to-bottom; an upper layer may import from a lower layer but NEVER the r
## Build-Time Exclusion Map (ADR-002)
Four binaries are built from this codebase: **airborne** (Tier-1 + Tier-2 production), **research** (IT-12 comparative-study, links every strategy), **operator-tooling** (pre-flight workflows on operator workstation), **replay-cli** (offline `gps-denied-replay` against video + tlog; AZ-265).
Four binaries are built from this codebase: **airborne** (Tier-1 + Tier-2 production), **research** (IT-12 comparative-study, links every strategy), **operator-orchestrator** (pre-flight workflows on operator workstation), **replay-cli** (offline `gps-denied-replay` against video + tlog; AZ-265).
| CMake flag | Components / native libs gated | Airborne | Research | Operator-tooling | Replay-cli |
|-----------|-------------------------------|----------|----------|------------------|------------|
@@ -427,7 +427,7 @@ Four binaries are built from this codebase: **airborne** (Tier-1 + Tier-2 produc
| `BUILD_PYTORCH_RUNTIME` | c7_inference/pytorch_fp16_runtime | OFF | ON | OFF | OFF |
| `BUILD_C10_PROVISIONING` | c10_provisioning | OFF | OFF | ON | OFF |
| `BUILD_C11_TILE_MANAGER` | c11_tile_manager | OFF | OFF | ON | OFF |
| `BUILD_C12_OPERATOR_TOOLING` | c12_operator_tooling | OFF | OFF | ON | OFF |
| `BUILD_C12_OPERATOR_ORCHESTRATOR` | c12_operator_orchestrator | OFF | OFF | ON | OFF |
| `BUILD_GTSAM_BINDINGS` | cpp/gtsam_bindings (used by c4_pose + c5_state) | ON | ON | OFF | ON |
| `BUILD_FAISS_INDEX` | c6_tile_cache `FaissDescriptorIndex` (faiss-cpu wheel; runtime gate at `runtime_root.storage_factory` — no native target) | ON | ON | ON | OFF (replay reads pre-built cache only) |
| `BUILD_VIDEO_FILE_FRAME_SOURCE` | `frame_source/VideoFileFrameSource` (AZ-265) | OFF | OFF | OFF | ON |
@@ -456,7 +456,7 @@ Build-time exclusion is enforced by:
## Self-Verification Checklist
- [x] Every component in `_docs/02_document/components/` has a Per-Component Mapping entry (14 components: c1_vio, c2_vpr, c2_5_rerank, c3_matcher, c3_5_adhop, c4_pose, c5_state, c6_tile_cache, c7_inference, c8_fc_adapter, c10_provisioning, c11_tile_manager, c12_operator_tooling, c13_fdr).
- [x] Every component in `_docs/02_document/components/` has a Per-Component Mapping entry (14 components: c1_vio, c2_vpr, c2_5_rerank, c3_matcher, c3_5_adhop, c4_pose, c5_state, c6_tile_cache, c7_inference, c8_fc_adapter, c10_provisioning, c11_tile_manager, c12_operator_orchestrator, c13_fdr).
- [x] Every shared / cross-cutting concern has a Shared section entry (_types, config, logging, fdr_client, frame_source, clock, helpers/* × 8, runtime_root, cli/replay, healthcheck).
- [x] Layering table covers every component; foundation at Layer 1.
- [x] No component's `Imports from` list points at a component in a higher layer (back-channel exception for C8 → C1/C5 documented as interface-at-producer pattern).
+1 -1
View File
@@ -153,7 +153,7 @@ flowchart TD
| 3 | `satellite-provider` | C11 | Paged tile blobs + metadata rows | JPEG + JSON metadata |
| 4 | C11 | C6 filesystem (over USB/Eth) | Tile JPEG bodies | `./tiles/{zoomLevel}/{x}/{y}.jpg` |
| 5 | C11 | C6 PostgreSQL | Tile metadata rows (`source='googlemaps'`) | SQL INSERT (mirror of `satellite-provider`'s `tiles` table) |
| 6 | C12 | C10 `CacheProvisioner` | `BuildRequest(bbox, zoom_levels, sector_class, calibration_path, takeoff_origin, flight_id)` | in-process call (operator-tool side); RPC over USB/Eth to companion runner |
| 6 | C12 | C10 `CacheProvisioner` | `BuildRequest(bbox, zoom_levels, sector_class, calibration_path, takeoff_origin, flight_id)` | in-process call (operator-orchestrator side); RPC over USB/Eth to companion runner |
| 7 | C10 → C7 | TRT engine cache | TRT engines | `.engine` files keyed by `(SM, JP, TRT, precision)` (D-C10-7) |
| 8 | C2 backbone (driven by C10) | C6 FAISS index | Descriptor matrix | `.index` (FAISS HNSW), atomicwrites, SHA-256 sidecar |
| 9 | C10 | filesystem | Manifest (carries `takeoff_origin` + hashes) | YAML or JSON |