# AZ-619 — Phase A: build_pre_constructed seeds c13_fdr + clock

**Task**: AZ-619_pre_constructed_phase_a_c13_fdr_clock
**Name**: AZ-618 Phase A: build_pre_constructed seeds c13_fdr + clock
**Description**: Foundational subtask of AZ-618. Introduces `airborne_bootstrap.build_pre_constructed(config) -> dict[str, Any]` and seeds the two simplest entries: `c13_fdr` (per-binary single FdrClient via `make_fdr_client("airborne_main", config)`) and `clock` (`WallClock()`). The function is additive — phases B..F (AZ-620..AZ-624) extend it without breaking this contract.
**Complexity**: 2 points
**Dependencies**: AZ-591 (registry seam), AZ-273 (FdrClient), AZ-398 (WallClock). All in `done/`.
**Component**: runtime_root (cross-cutting)
**Tracker**: AZ-619
**Epic**: AZ-602 (parent: AZ-618 umbrella)

## Outcome

- `src/gps_denied_onboard/runtime_root/airborne_bootstrap.py` exports a public `build_pre_constructed(config: Config) -> dict[str, Any]`.
- The returned dict contains at least `c13_fdr` (FdrClient) and `clock` (WallClock).
- Re-invocation in the same process returns dict where `c13_fdr` is the same FdrClient (per `make_fdr_client` cache); `clock` may be a fresh WallClock each call.
- Unit tests under `tests/unit/runtime_root/test_az619_pre_constructed_phase_a.py` cover both ACs.

## Scope

### Included

- New module symbol: `airborne_bootstrap.build_pre_constructed(config)`.
- Internal builders: `_build_c13_fdr(config) -> FdrClient` and `_build_clock(config) -> WallClock`. Replay-mode clock override is NOT done here — `compose_root` already merges `replay_components` over `pre_constructed` so `WallClock` gets replaced by `TlogDerivedClock` in replay mode. The contract is "live default; replay overrides".
- `__all__` updated to export `build_pre_constructed`.
- New unit test file: `tests/unit/runtime_root/test_az619_pre_constructed_phase_a.py` (AC-619.1, AC-619.2).

### Excluded

- All other 10 keys in `AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS` — AZ-620..AZ-623.
- `runtime_root.main()` integration — AZ-624.
- Full AC-1..AC-5 of the AZ-618 umbrella — AZ-624.
- Jetson tier-2 verification — AZ-624 (per `_docs/02_document/tests/tier2-jetson-testing.md` Mandatory-Tier-2 Scope, this subtask is in scope but the actual Jetson run is consolidated at AZ-624 to avoid 6 separate Jetson runs for one feature).

## Acceptance Criteria

**AC-619.1: `build_pre_constructed(default_config)` returns required minimum keys**
Given a default airborne `Config`
When `build_pre_constructed(config)` is called
Then the returned dict has key `c13_fdr` mapping to an `FdrClient` instance
And key `clock` mapping to a `WallClock` instance
And no other keys are required at this phase (additional keys are allowed for forward-compat).

**AC-619.2: c13_fdr is cached across calls**
Given `build_pre_constructed(config)` has been called once and returned `dict_a`
When `build_pre_constructed(config)` is called a second time, returning `dict_b`
Then `dict_a["c13_fdr"] is dict_b["c13_fdr"]` (same instance via `make_fdr_client` cache).

**AC-619.3: unit tests exist**
Given the changes above are landed
When `pytest tests/unit/runtime_root/test_az619_pre_constructed_phase_a.py` is invoked
Then both AC-619.1 and AC-619.2 are exercised by named tests.

## Non-Functional Requirements

- The construction is single-threaded and synchronous; no GPU, no I/O beyond what `make_fdr_client` already does (FdrClient logger init).
- Builder must complete in <100 ms on Mac dev host for the default config.

## Dependencies

- AZ-591 (registry seam): DONE — `airborne_bootstrap.py` already exists with the required keys map.
- AZ-273 (FdrClient): DONE — `make_fdr_client` is the public entry-point.
- AZ-398 (WallClock): DONE — stateless, no-arg constructor.

## Constraints

- MUST NOT modify any per-component factory signature.
- MUST NOT introduce new `BUILD_*` env flags.
- MUST be additive: the function shape `build_pre_constructed(config) -> dict[str, Any]` survives unchanged through AZ-620..AZ-624.

## Implementation Notes

- The producer ID for `make_fdr_client` is "airborne_main" (per the bootstrap convention; consumer-side components call `make_fdr_client(<their_slug>, config)` separately and get their own per-component FdrClient via the per-producer cache). The single FdrClient passed via `pre_constructed["c13_fdr"]` is the one the wrapper factories pass into `build_<...>_strategy(config, fdr_client=...)`. Whether wrappers should use the shared "airborne_main" client or fetch their own per-component client is a design question that does NOT need to be resolved in this subtask — the wrappers already accept whichever client we pass. Defer the design clarification to the AZ-624 integration AC if needed.

## Evidence

- Umbrella spec: `_docs/02_tasks/todo/AZ-618_airborne_bootstrap_pre_constructed.md`
- Existing bootstrap module: `src/gps_denied_onboard/runtime_root/airborne_bootstrap.py:92` (AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS)
- FdrClient factory: `src/gps_denied_onboard/fdr_client/client.py:192` (`make_fdr_client`)
- WallClock: `src/gps_denied_onboard/clock/wall_clock.py`
