# Refactor sitl_replay_builder to strategy pattern

**Task**: AZ-600_refactor_sitl_replay_builder_to_strategy_pattern
**Complexity**: 3 points
**Dependencies**: AZ-598 (b78 FT-P-01 builder), AZ-599 (b79 FT-P-02 builder)
**Component**: Blackbox Tests / Test Infrastructure (epic AZ-262)
**Tracker**: AZ-600

## Problem

b78 and b79 introduced two per-scenario builder modules
(`build_p01_fixtures.py`, `build_p02_fixtures.py`) that share `_common.py`
(gps-denied-replay subprocess wrapper + observer fixture writer) but still
duplicate ~45 lines of MAVLink packers, pymavlink writer factory boilerplate,
and argparse boilerplate. More importantly, the structure forces every future
scenario to copy-paste the orchestration loop. With 6+ Derkachi-based
scenarios still to land (FT-P-04, FT-P-05, FT-P-07, FT-P-08, FT-P-10,
FT-P-11), the duplication compounds.

## Strategy

Introduce three strategy ABCs in a new module
`e2e/fixtures/sitl_replay_builder/builder.py`:

1. **VideoSource** — produces the MP4 the replay CLI consumes
   - `StillImagesSource(image_paths, fps)` — was b78 `encode_stills_to_mp4`
   - `Mp4PassthroughSource(mp4_path)` — was b79 path resolution

2. **TlogSource** — produces the tlog the replay CLI consumes
   - `SyntheticStationaryTlog(duration_s, hz)` — was b78 `generate_stationary_tlog`
   - `ImuCsvTlog(csv_path, column_schema)` — was b79 `convert_imu_csv_to_tlog`

3. **FdrProjection** — translates the FDR JSONL into the scenario fixture shape
   - `RawFdrPassthrough()` — b79 / FT-P-02 / future Derkachi scenarios
   - `OutboundMessagesProjection(fdr_kind, image_ids)` — was b78
     `parse_fdr_for_outbound_estimates` + `write_outbound_messages_fixture`

A single `build_fixtures(cfg: FixtureBuilderConfig)` orchestrator composes
the three strategies plus `run_gps_denied_replay` + `write_observer_fixture`.

`build_p01_fixtures.py` and `build_p02_fixtures.py` shrink to thin
scenario-config factories + argparse CLI wrappers (~60 lines each).

The MAVLink RAW_IMU + ATTITUDE packers consolidate into one parameterized
helper (zero-motion is just one config of real-motion).

## Files Touched

* `e2e/fixtures/sitl_replay_builder/builder.py` (new) — strategy ABCs +
  concrete impls + orchestrator + `FixtureBuilderConfig` dataclass +
  parameterized MAVLink packers.
* `e2e/fixtures/sitl_replay_builder/build_p01_fixtures.py` (edit, shrink) —
  scenario config factory + CLI; ~60 lines.
* `e2e/fixtures/sitl_replay_builder/build_p02_fixtures.py` (edit, shrink) —
  scenario config factory + CLI; ~60 lines.
* `e2e/fixtures/sitl_replay_builder/README.md` (edit) — replace per-builder
  sections with strategy reference + worked example for adding a new scenario.
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_builder.py` (new) —
  strategy class tests (one suite per strategy ABC + orchestrator).
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_p01.py` (edit) —
  scenario-level integration tests for the FT-P-01 builder; drops tests
  that moved to the strategy suite.
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_p02.py` (edit) —
  same for FT-P-02.
* `e2e/_unit_tests/test_directory_layout.py` (edit) — register new module.

## Acceptance Criteria

**AC-1**: `builder.py` exports `VideoSource`, `TlogSource`, `FdrProjection`
ABCs plus the four concrete impls listed above, with `build_fixtures(cfg)`
orchestrator and `FixtureBuilderConfig` dataclass.

**AC-2**: `build_p01_fixtures.py` is ≤80 lines and only contains:
image-glob helper, scenario config factory, CLI wrapper.

**AC-3**: `build_p02_fixtures.py` is ≤80 lines and only contains:
Derkachi-input resolve helper, scenario config factory, CLI wrapper.

**AC-4**: The MAVLink RAW_IMU + ATTITUDE packer is a single helper
parameterized by IMU values + yaw; zero-motion is the
`(0, 0, -9810, 0, 0, 0)` config; real-motion is per-row CSV values.

**AC-5**: Unit tests reorganize as described in *Files Touched*. Same
coverage, same AC verification, no functional regression.

**AC-6**: Full `e2e/_unit_tests` suite passes at ≥ 686 tests (regression
gate). All b78 + b79 acceptance behaviors (AC-1..AC-5 of AZ-598;
AC-1..AC-5 of AZ-599) remain validated.

**AC-7**: A worked example in `README.md` shows how to add a new scenario
(e.g., FT-P-04) by writing only a ~30-line scenario config — no new
strategy code required.

## Out of Scope

* Adding new scenarios (FT-P-04..FT-P-11 stay in todo/; this refactor only
  enables them).
* Changing the `gps-denied-replay` CLI contract.
* Changing the `observer_<fc_kind>_<host>.json` payload schema.
* Live capture pipeline changes.
