# FT-P-11 — Cold-start initialization from operator origin (primary) OR FC EKF (secondary)

**Task**: AZ-419_ft_p_11_cold_start_init
**Name**: Cold-start initialization — operator-origin-from-Manifest primary; FC EKF GPS secondary (ADR-010 + AC-5.1)
**Description**: Implement FT-P-11 — exercise both cold-start paths defined by ADR-010. **Primary path (AZ-490)**: pre-bake a `takeoff_origin` into the C10 Manifest, start SUT cold, push first nav-camera frame, assert the first outbound estimate's lat/lon falls within ±50 m of the operator origin even when the SITL FC EKF reports NO valid GPS. **Secondary path (legacy AC-5.1)**: clear the Manifest's `takeoff_origin`, load a `cold-boot-fixture` snapshot into SITL, start SUT cold, push first nav-camera frame, assert the first outbound estimate's lat/lon falls within ±50 m of the FC EKF snapshot. The two paths share a single test module parameterised on `(origin_source ∈ {operator_manifest, fc_ekf})`. The test also exercises the bounded-delta gate (Principle #11 amended): set Manifest origin to A and SITL FC EKF to a position B with `|A − B| > 200 m`; assert the operator origin wins and the FC GPS is logged as suspect.
**Complexity**: 3 points
**Dependencies**: AZ-406, AZ-407 (cold-boot-fixture), AZ-323 / AZ-325 (Manifest with takeoff_origin), AZ-490 (set_takeoff_origin), AZ-489 (FlightsApiClient — used by the test fixture builder to fabricate Manifests with a known origin)
**Component**: Blackbox Tests / Positive / Startup (epic AZ-262)
**Tracker**: AZ-419
**Epic**: AZ-262 (E-BBT)

## Problem

Cold-start initialization is a critical path. The original assumption that the FC EKF's last valid GPS fix is always available at takeoff (AC-5.1) does not hold under realistic EW conditions — a UAV can be jammed at the launch site before takeoff, leaving the FC EKF with no valid GPS. ADR-010 introduces the operator-planned mission as the **primary** cold-start trust anchor: the operator authors the route in the Mission Planner UI, C12 fetches the `Flight`, derives `takeoff_origin` from `waypoints[0]`, and bakes it into the C10 Manifest. The airborne C5's `set_takeoff_origin` (AZ-490) consumes it before any sensor sample. The FC EKF GPS becomes the **secondary** path used only when the Manifest carries no origin (back-compat). Both paths must be measured end-to-end, and the bounded-delta gate (the third clause of the spoof-promotion gate, Principle #11) must be exercised so an inconsistent FC GPS at takeoff does not silently override the operator origin.

## Outcome

- pytest scenario at `e2e/tests/positive/test_ft_p_11_cold_start_init.py`.
- Parameterised on `origin_source ∈ {operator_manifest, fc_ekf, bounded_delta_conflict}`:
  - **`operator_manifest`** (primary path, AZ-490): the test fixture builder writes a Manifest with `flight.takeoff_origin = A` (a known `LatLonAlt`); SITL starts with NO valid GPS (`GPS_TYPE = 0` or simulated denial); SUT cold-starts; the test asserts the first outbound estimate's lat/lon is within ±50 m of `A`.
  - **`fc_ekf`** (secondary path, legacy AC-5.1): Manifest has no `flight.takeoff_origin`; `cold-boot-fixture` JSON pose loaded into SITL (parameter-load path); SUT cold-starts; the test asserts the first outbound estimate's lat/lon is within ±50 m of the FC-EKF snapshot pose.
  - **`bounded_delta_conflict`** (ADR-010 Principle #11 amended): Manifest carries `takeoff_origin = A`; SITL FC EKF reports `B` with `vincenty(A, B) > 200 m`; the test asserts the first outbound estimate falls within ±50 m of `A` (operator origin wins), the source label on the first estimate is NOT `SATELLITE_ANCHORED` (no immediate spoof-promotion), and the FDR carries a `c5.gps_bounded_delta.reject` record naming both A and B.
- Starts SUT (cold — no prior FDR, no in-memory state). Pushes a single first nav-camera frame. Reads the first outbound estimate; computes Vincenty distance to the expected origin.

## Scope

### Included
- Cold-boot SITL parameter load (secondary path).
- Test fixture builder that produces a C10 Manifest with a known `flight.takeoff_origin` (primary path); reuses AZ-323's canonical JSON serialization.
- SUT cold start (`docker compose up gps-denied-onboard` from clean state; OR `systemctl start` on Tier-2).
- First-frame push and first-emission read.
- Distance comparison.
- FDR record assertions for the bounded-delta conflict scenario.

### Excluded
- Cold-start TTFF latency — owned by NFT-PERF-03 (AZ-430).
- Companion mid-flight reboot — owned by NFT-RES-02 (AZ-433).
- Mid-flight bounded-delta gate (only the takeoff slice is covered here; mid-flight is part of AZ-385 follow-up).

## Acceptance Criteria

**AC-1: Primary path (operator origin) — SUT cold-starts even when FC EKF has no GPS (ADR-010)**
Given a C10 Manifest with `flight.takeoff_origin = LatLonAlt(50.0, 36.2, 200.0)` AND SITL configured with no valid GPS
When SUT cold-starts and the first nav-camera frame is pushed
Then the SUT emits its first outbound message within ≤30 s; `vincenty(estimate.position, manifest.takeoff_origin) ≤ 50 m`; the FDR carries a `c5.cold_start_origin.set` record with `source = "manifest"`

**AC-2: Secondary path (FC EKF) — Manifest has no origin (back-compat)**
Given a C10 Manifest with no `flight.takeoff_origin` AND `cold-boot-fixture` JSON loaded into SITL
When SUT cold-starts
Then the first outbound estimate is within ±50 m of the FC EKF snapshot; the FDR carries a `c5.cold_start_origin.set` record with `source = "fc_ekf"`

**AC-3: No origin available — SUT refuses takeoff**
Given a C10 Manifest with no `flight.takeoff_origin` AND SITL with no valid GPS
When SUT cold-starts
Then NO outbound `EmittedExternalPosition` is produced within the AC-NEW-1 30 s budget; the SUT logs `c5.cold_start_origin.unavailable` to FDR + GCS STATUSTEXT; the test asserts the FT-P-11 takeoff-abort policy fires

**AC-4: Bounded-delta conflict — operator origin wins (ADR-010 Principle #11 amended)**
Given Manifest `takeoff_origin = A` AND SITL FC EKF reports `B` with `vincenty(A, B) > 200 m`
When SUT cold-starts and the first nav-camera frame is pushed
Then the first outbound estimate is within ±50 m of `A`; the source label is NOT `SATELLITE_ANCHORED` (no immediate spoof-promotion); the FDR carries a `c5.gps_bounded_delta.reject` record naming both A and B and the computed distance

**AC-5: parameterization across FC adapters + VIO strategies**
Given conftest parameterization on `(fc_adapter, vio_strategy, origin_source)`
Then each combination listed in the test matrix runs the appropriate ACs (AC-1 / AC-2 / AC-3 / AC-4 per `origin_source`)

## System Under Test Boundary

End-to-end through public boundaries.

- **Allowed**: SITL parameter load (a public SITL feature), SITL state read, outbound message read.
- **Forbidden**: pre-seeding SUT internal C5 state to bypass cold-start.

## Constraints

- The cold-boot-fixture JSON is loaded via the SITL's standard parameter-load path (`param load <file>` in mavproxy or equivalent for iNav); it is NOT loaded into the SUT.
- "Cold" means no prior FDR, no in-memory state — `fdr-output` volume is freshly created before this test.

## Document Dependencies

- `_docs/02_document/tests/blackbox-tests.md` § FT-P-11
- `_docs/02_document/tests/test-data.md` § FC contract & startup (FT-P-11 row)
