# Mission Pull + Schema Validation

**Task**: AZ-644_mission_client_pull_and_schema
**Name**: HTTPS mission fetch + schema validation
**Description**: HTTPS REST client to the external `missions` API, mission fetch by `mission_id` on startup, validate the response against the shared `mission-schema`, bounded retry on transient connection loss.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure
**Component**: mission_client
**Tracker**: AZ-644
**Epic**: AZ-638

## Problem

`autopilot` does not own the missions database — it fetches the mission by ID from the external `missions` API at startup. The response must validate against the shared `mission-schema`; on schema-invalid the mission MUST be rejected (no silent downcast). On transient connectivity failure, fetch is retried with bounded exponential backoff; on exceeding the cap, the mission start is refused and health flips to red.

## Outcome

- `MissionClient::fetch(mission_id) -> Result<Mission, FetchError>` performs an HTTPS GET against the configured `missions` endpoint, validates the response against the bundled `mission-schema` (schema version recorded), and returns a typed `Mission` (`{ waypoints, geofences, return_point, mission_id, schema_version }`).
- Transient failures (timeout, 5xx, DNS) are retried with bounded exponential backoff; max attempts configurable (default 5).
- On schema mismatch the call returns `Err(SchemaInvalid)` with a size-capped sample of the raw response for offline analysis.
- Health surface includes `last_fetch_ts`, `fetch_errors_total`, `schema_version`, `connection_state`.

## Scope

### Included
- HTTPS client (`reqwest` or `hyper` — pick the one already pinned in `shared`).
- Auth header plumb-through (concrete scheme deferred to `../_docs/02_missions.md`; passed as opaque `Authorization` header).
- Schema validation against `mission-schema` (bundled in `shared/contracts/`).
- Bounded exponential backoff.

### Excluded
- Middle-waypoint POST (task 06).
- MapObjects pre-flight pull (task 07).
- MapObjects post-flight push and durable queue (task 08).

## Acceptance Criteria

**AC-1: Happy path fetch**
Given a fixture `missions` API that returns a schema-valid mission JSON for `mission_id = M1`
When `MissionClient::fetch("M1")` is called
Then it returns `Ok(Mission { ... })` and `health()` reports `last_fetch_ts` updated, `connection_state = "ok"`.

**AC-2: Schema-invalid is rejected**
Given a fixture `missions` API that returns a valid HTTP 200 but the JSON body has a missing required field
When `MissionClient::fetch("M1")` is called
Then it returns `Err(SchemaInvalid)` and `health()` records the failure; the raw response excerpt is logged size-capped.

**AC-3: Transient failure retries within budget**
Given the missions API returns `503` for the first two attempts and `200` on the third
When `MissionClient::fetch("M1")` is called
Then it returns `Ok` after the third attempt; backoff is observed between attempts.

**AC-4: Cap exhaustion refuses start**
Given the missions API is unreachable for all 5 default attempts
When `MissionClient::fetch("M1")` is called
Then it returns `Err(MaxRetriesExceeded)` and `health()` is red.

## Non-Functional Requirements

**Performance**
- Startup fetch completes within ≤5 s on healthy connectivity.

**Reliability**
- No silent downcast on schema mismatch.
- No infinite retry — bounded backoff cap is configurable.

## Constraints

- Mission schema is shared with the external `missions` repo; the schema file lives in `shared/contracts/mission-schema.json` (bundled at build time).

## Contract

- `mission-schema.json` is the authoritative wire contract. Owner: `../_docs/02_missions.md`. Bundled copy in `shared/contracts/mission-schema.json`.
- Canonical typed model: `data_model.md §MissionItem`, `§MissionWaypoint`, `§Geofence`.

## Runtime Completeness

- **Named capability**: HTTPS REST to the external `missions` API + JSON Schema validation.
- **Production code that must exist**: real HTTPS request; real JSON Schema validator (e.g. `jsonschema` crate).
- **Allowed external stubs**: in tests, the missions API can be a local `wiremock`/`mockito` server.
- **Unacceptable substitutes**: skipping schema validation in production "for speed" is not acceptable; validation is a safety boundary.
