# Harness stubs — core three (fdr_reader, frame_source_replay, imu_replay)

**Task**: AZ-594_harness_stubs_core_three
**Name**: Implement the three foundational e2e/runner/helpers/ stubs that AZ-406 reserved
**Description**: Replace `NotImplementedError` stubs in `fdr_reader.iter_records`, `frame_source_replay.replay_video`, and `imu_replay.ImuReplayer.replay` with concrete bodies. Together they unblock the skip-gated scenarios from batches 71-73 once `sitl_observer` lands.
**Complexity**: 4 points
**Dependencies**: AZ-406, AZ-407, AZ-408
**Component**: Blackbox Tests / Test Infrastructure (epic AZ-262)
**Tracker**: AZ-594
**Epic**: AZ-262 (E-BBT)

## Problem

AZ-406 committed to the public surface of the runner-side helpers and
reserved each method body as `NotImplementedError`. AZ-407/408 filled
the fixture-builder side; the runtime replay/read surfaces were
deferred without a dedicated ticket. The skip-gated scenarios in
batches 71-73 (`test_ft_p_07/08/10/11`, `test_ft_n_01..04`) reference
these stubs in their `_harness_helpers_implemented` probe and skip
cleanly until the bodies land.

## Outcome

- `fdr_reader.iter_records` reads `*.jsonl` files under an archive
  root and yields `FdrRecord` envelopes ordered by `monotonic_ms`.
- `frame_source_replay.replay_video` decodes `.mp4` files or
  directories of `.jpg`/`.png` frames via OpenCV and emits to the
  injected `FrameSink` at the requested cadence.
- `imu_replay.ImuReplayer.replay` parses `data_imu.csv` and drives a
  `FcInboundEmitter` at the configured rate.
- Each body has ≥5 unit tests (happy path + ≥2 error paths + ≥1
  boundary case).

## Scope

### Included
- The three method bodies + their unit tests.
- Layout-test entries (already present for the helper files).

### Excluded
- `sitl_observer.get_observer` + `read_*` surfaces — separate ticket.
- `fc_proxy` runtime driver — separate ticket.
- Removing the skip-gates in existing scenarios — happens once
  `sitl_observer` lands.

## Acceptance Criteria

**AC-1**: `fdr_reader.iter_records` opens every `*.jsonl` file under
the archive root, parses each line as JSON, and yields `FdrRecord`
envelopes sorted by `monotonic_ms`. Raises `FileNotFoundError` on
missing root.

**AC-2**: `frame_source_replay.replay_video` accepts an `.mp4` file
OR a directory of `.jpg`/`.png` frames; decodes via OpenCV; emits to
the sink at the cadence (encoded FPS if `realtime`, configured FPS
otherwise). Returns the count emitted. Raises `FileNotFoundError` on
missing input.

**AC-3**: `imu_replay.ImuReplayer.replay` parses the CSV at
`csv_path`, constructs `ImuSample`s with accel/gyro/attitude/baro,
and calls `emitter.emit(sample)` for each row. Returns the count
emitted. Tolerates scientific-notation floats.

**AC-4**: Each new body has ≥5 unit tests in
`e2e/_unit_tests/helpers/` covering the happy path + 2 error paths +
1 boundary case.

**AC-5**: Full e2e unit-test suite passes (regression gate).

## System Under Test Boundary

None — these are runner-side helpers that synthesize the SUT's
inputs and parse the SUT's FDR output. They do NOT import any
`src/gps_denied_onboard` symbol.

## Constraints

- Use `opencv-python-headless` (already pinned in
  `e2e/runner/requirements.txt`).
- `imu_replay.replay` must not block on wall-clock at the requested
  rate when called in tests; a `time.sleep`-based pacing path is
  acceptable for production use but the test must inject a fake
  clock / cadence=0 to keep unit tests fast.
- `fdr_reader.iter_records` uses stdlib `json` (orjson is fine but
  not required at this stage — keep dependencies minimal).

## Document Dependencies

- `_docs/02_document/tests/blackbox-tests.md` § Test infrastructure
- `_docs/02_tasks/done/AZ-406_test_infrastructure.md`
- `_docs/02_tasks/done/AZ-407_fixture_builders_static.md`
