mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 16:21:12 +00:00
[AZ-489] [AZ-490] ADR-010 design pass: operator-mission as cold-start anchor
Architecture, contracts, and task amendments for the flight-route-driven preflight + cold-start origin feature (ADR-010). No source code touched in this commit; the implementation commits for AZ-489 / AZ-490 / AZ-419 land separately. * architecture.md: ADR-010, new Principle #14, amended Principle #11, external systems gain flights service + Mission Planner UI, data model gains Flight / Waypoint / TakeoffOrigin. * system-flows.md: F1 gains phase 0 (Flight resolve), F2 gains cold-start ladder, F7 gains mid-flight bounded-delta GPS gate. * glossary.md: Flight, Flights API, Mid-flight bounded-delta GPS gate, Mission Planner UI, Takeoff origin, Waypoint. * C10: description + cache_provisioner + manifest_verifier bumped to v1.1 carrying takeoff_origin + flight_id in the manifest hash. * C12: description updated + new flights_api_client.md contract v1.0. * C5: description + state_estimator_protocol bumped to v1.1 with set_takeoff_origin + 3-clause spoof-promotion gate. * AZ-323/324/325/326/328/419 amended in place. AZ-490 spec created (C5 set_takeoff_origin entrypoint). * Dependencies table: 142 tasks / 478 pts / 15 forward edges (2 new tasks, 2 backward deps, 2 forward deps from AZ-419). * Leftovers cleared: 2026-05-11 Jira transition entries for AZ-355 and AZ-386 are deleted (Jira reconnected; both already transitioned in their respective implementation commits). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -66,6 +66,8 @@ class BuildRequest:
|
||||
calibration_path: Path
|
||||
cache_root: Path
|
||||
key_path: Path # operator signing key per C10-ST-01
|
||||
takeoff_origin: LatLonAlt | None = None # ADR-010 + AZ-489: planned takeoff position from Flight.waypoints[0]; baked into Manifest body + build-identity hash
|
||||
flight_id: UUID | None = None # ADR-010: pass-through provenance of which Flight produced the build
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -105,6 +107,8 @@ class BuildReport:
|
||||
| CP-INV-5 | `cache_root` must already exist; `build_cache_artifacts` does NOT create the directory tree (operator workflow places it). | Avoids accidental builds in unintended paths. |
|
||||
| CP-INV-6 | No network calls (no `satellite-provider`, no Postgres TLS to a remote DB beyond the local instance, no metric push). | Epic § Architecture notes: C10 is workstation-local. |
|
||||
| CP-INV-7 | The operator key file at `request.key_path` is opened exactly once (via AZ-323's signer) and zeroized when out of scope; this contract does NOT cache the key in memory across calls. | Operator key hygiene. |
|
||||
| CP-INV-8 | `takeoff_origin` is treated as one more identity field by the build-identity hash. If the prior Manifest carries `takeoff_origin=A` and a new request carries `takeoff_origin=B != A` (with all other fields equal), the build is NOT idempotent and proceeds; the verifier (AZ-324) at boot then refuses any cache whose manifest origin disagrees with the manifest-on-disk's origin. | ADR-010: cache identity must include the origin or boot-time consistency breaks. |
|
||||
| CP-INV-9 | When `takeoff_origin` is None, the prior cold-start ladder (FC-EKF-GPS via AZ-419) remains the only origin source. C10 does not invent a default origin from the bbox; that decision is for C12. | Single-responsibility — C10 records, C12 decides. |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
@@ -125,6 +129,7 @@ class BuildReport:
|
||||
| Version | Date | Notes | Author |
|
||||
|---------|------|-------|--------|
|
||||
| 1.0.0 | 2026-05-10 | Initial contract — produced by AZ-325 (E-C10 decomposition) | autodev |
|
||||
| 1.1.0 | 2026-05-11 | Additive: `BuildRequest.takeoff_origin` + `BuildRequest.flight_id` (defaults `None` for back-compat); CP-INV-8 + CP-INV-9. Consumer requires the Manifest hash to include `takeoff_origin` when set. ADR-010 + AZ-489. | autodev |
|
||||
|
||||
## Test Cases (consumer side)
|
||||
|
||||
@@ -143,3 +148,6 @@ class BuildReport:
|
||||
| CP-TC-11 | `compile_engines_for_corpus` directly callable for re-compile-only flows | Returns `tuple[EngineCacheEntry, ...]`; no descriptor / Manifest work |
|
||||
| CP-TC-12 | Cold build wall-clock benchmark on Tier-1 dev workstation, 1k tiles, 3 backbones | ≤ 12 min (NFR C10-PT-01) |
|
||||
| CP-TC-13 | Warm idempotent re-run benchmark | ≤ 1 min (NFR C10-PT-01) |
|
||||
| CP-TC-14 | Build with `takeoff_origin=A` → second build with same request + `takeoff_origin=A` | `outcome=IDEMPOTENT_NO_OP` |
|
||||
| CP-TC-15 | Build with `takeoff_origin=A` → second build with same request + `takeoff_origin=B (B != A)` | `outcome=SUCCESS` (re-build); new Manifest hash differs from prior |
|
||||
| CP-TC-16 | `BuildRequest.takeoff_origin=None` with no prior Manifest | `outcome=SUCCESS`; Manifest written without `takeoff_origin` field |
|
||||
|
||||
@@ -62,6 +62,8 @@ class VerifyFailReason(Enum):
|
||||
ARTIFACT_HASH_MISMATCH = "artifact_hash_mismatch"
|
||||
TILES_COVERAGE_MISMATCH = "tiles_coverage_mismatch"
|
||||
MANIFEST_SELF_HASH_MISMATCH = "manifest_self_hash_mismatch"
|
||||
TAKEOFF_ORIGIN_INVALID = "takeoff_origin_invalid" # ADR-010: schema check on the LatLonAlt block
|
||||
TAKEOFF_ORIGIN_OUT_OF_BBOX = "takeoff_origin_out_of_bbox" # ADR-010: origin must lie inside the cache bbox
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -79,6 +81,8 @@ class VerificationResult:
|
||||
fail_details: tuple[str, ...] # human-readable diagnostic per reason
|
||||
signing_public_key_fingerprint: str | None # populated when signature parses, even if untrusted
|
||||
per_artifact_checks: tuple[ArtifactCheck, ...]
|
||||
takeoff_origin: LatLonAlt | None # ADR-010 + AZ-490: passed through from Manifest body; None when Manifest carries no origin
|
||||
flight_id: UUID | None # ADR-010: provenance of which Flight produced the build
|
||||
elapsed_ms: int
|
||||
```
|
||||
|
||||
@@ -93,6 +97,8 @@ class VerificationResult:
|
||||
| MV-INV-5 | `tiles_coverage` mismatch is reported separately from `ARTIFACT_HASH_MISMATCH` because tiles are hashed in aggregate (per AZ-323). The verifier re-derives the aggregate hash from a `TileMetadataStore` query if available, OR (in airborne F2 mode) treats the recorded `tiles_coverage_sha256` as authoritative and only verifies the Manifest signature + non-tile artifacts. | Airborne C5 may not load 100k per-tile rows just to arm; the trust chain is signature → manifest_hash → tiles_coverage_sha256. C12 / operator mode does the full re-derivation. |
|
||||
| MV-INV-6 | The verifier never writes to disk, never opens network sockets, never calls C13. Telemetry is the caller's responsibility. | Read-only contract — composable in airborne C5 + operator C12 contexts without side-effect surprise. |
|
||||
| MV-INV-7 | `elapsed_ms` is recorded for every call (pass or fail) so operators and C5 can observe drift in verify cost on slow disks. | NFR for C10-PT-01's takeoff load budget. |
|
||||
| MV-INV-8 | When the Manifest body carries a `takeoff_origin`, the verifier checks: (a) the LatLonAlt block is well-formed (`-90 ≤ lat ≤ 90`, `-180 ≤ lon ≤ 180`, `alt` finite) → `TAKEOFF_ORIGIN_INVALID` otherwise, (b) the lat/lon falls inside the Manifest's `bbox` → `TAKEOFF_ORIGIN_OUT_OF_BBOX` otherwise. When the Manifest body carries no `takeoff_origin`, the field is absent from `VerificationResult` (None) and no origin check runs. | ADR-010: garbage / out-of-bbox origin must not silently propagate to `C5.set_takeoff_origin`. |
|
||||
| MV-INV-9 | `takeoff_origin` is surfaced on `VerificationResult` even on `FAIL` outcomes when the Manifest body parsed (so caller can inspect what was attempted), but the takeoff-arming gate only consumes it on `PASS`. | Diagnostics — operators can see "your origin was X and that's why we rejected it". |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
@@ -112,6 +118,7 @@ class VerificationResult:
|
||||
| Version | Date | Notes | Author |
|
||||
|---------|------|-------|--------|
|
||||
| 1.0.0 | 2026-05-10 | Initial contract — produced by AZ-324 (E-C10 decomposition) | autodev |
|
||||
| 1.1.0 | 2026-05-11 | Additive: `VerificationResult.takeoff_origin` + `flight_id`; new `VerifyFailReason.TAKEOFF_ORIGIN_INVALID` + `TAKEOFF_ORIGIN_OUT_OF_BBOX`; MV-INV-8 + MV-INV-9. ADR-010 + AZ-490. | autodev |
|
||||
|
||||
## Test Cases (consumer side)
|
||||
|
||||
@@ -132,3 +139,7 @@ class VerificationResult:
|
||||
| MV-TC-13 | Tier-2 Tile-coverage check (operator mode with TileMetadataStore) | If recomputed `tiles_coverage_sha256` differs → `TILES_COVERAGE_MISMATCH`; if matches → that part passes |
|
||||
| MV-TC-14 | Empty `trusted_public_keys` | `outcome=FAIL`, `fail_reasons=(UNTRUSTED_PUBLIC_KEY,)` (every key is untrusted by definition) |
|
||||
| MV-TC-15 | Pristine Manifest verified inside 100 ms on Tier-2 (excludes per-tile re-walk) | `elapsed_ms ≤ 100` for the signature + non-tile artifact path |
|
||||
| MV-TC-16 | Manifest body carries no `takeoff_origin` | `outcome=PASS`; `VerificationResult.takeoff_origin is None` |
|
||||
| MV-TC-17 | Manifest body carries a well-formed `takeoff_origin` inside bbox | `outcome=PASS`; `VerificationResult.takeoff_origin` populated as `LatLonAlt` |
|
||||
| MV-TC-18 | Manifest body carries a malformed `takeoff_origin` (lat = 200) | `outcome=FAIL`, `fail_reasons` contains `TAKEOFF_ORIGIN_INVALID`; `takeoff_origin` field is populated for diagnostics |
|
||||
| MV-TC-19 | Manifest body carries `takeoff_origin` outside the recorded `bbox` | `outcome=FAIL`, `fail_reasons` contains `TAKEOFF_ORIGIN_OUT_OF_BBOX` |
|
||||
|
||||
Reference in New Issue
Block a user