# Code Review Report

**Batch**: 74 — AZ-594 (harness stubs: core three)
**Date**: 2026-05-17
**Verdict**: PASS

## Findings

(none)

## Findings Sweep

### Phase 1 — Context Loading

Read the AZ-594 task spec, then the three stub files in their
pre-implementation state (`fdr_reader.py`, `frame_source_replay.py`,
`imu_replay.py`). Cross-checked the SUT-side `FdrRecord` wire schema
in `src/gps_denied_onboard/fdr_client/records.py` to confirm field
names (`schema_version`, `ts`, `producer_id`, `kind`, `payload`,
`extra`) and the rename strategy the runner-side `FdrRecord`
duplicates (`kind → record_type`, `ts → monotonic_ms`). Re-read the
existing scenario `_harness_helpers_implemented` probes in the batch
71-73 scenario files to confirm the post-fix probe semantics.

### Phase 2 — Spec Compliance

| AC | Coverage | Status |
|----|----------|--------|
| AC-1 (`fdr_reader.iter_records` parses every `*.jsonl` under root, yields `FdrRecord` ordered by `monotonic_ms`; raises FileNotFoundError on missing root) | `test_missing_root_raises_file_not_found`, `test_empty_root_yields_nothing`, `test_single_file_round_trip`, `test_multiple_files_are_merged_and_sorted`, `test_subdirectory_files_included`, `test_payload_passed_through`; envelope-validation tests cover the wire-schema gate (`test_missing_envelope_key_raises`, `test_non_object_line_raises`, `test_malformed_json_raises`, `test_empty_producer_id_raises`, `test_ts_iso_without_z_parses`, `test_blank_lines_are_skipped`); `archive_size_bytes` covered by `test_archive_size_bytes_sums_all_files` and `test_archive_size_bytes_missing_root_returns_zero` | Covered |
| AC-2 (`frame_source_replay.replay_video` accepts `.mp4` OR directory; decodes via OpenCV; emits via sink at cadence; raises FileNotFoundError on missing input) | `test_video_missing_path_raises_file_not_found`, `test_video_dir_delegates_to_image_directory`, `test_video_mp4_round_trip`; image-dir mode covered by `test_image_dir_missing_raises_file_not_found`, `test_image_dir_empty_returns_zero`, `test_image_dir_emits_sorted_by_name`, `test_image_dir_non_jpeg_reencoded`, `test_image_dir_skips_non_image_files`, `test_image_dir_non_realtime_does_not_sleep`, `test_image_dir_realtime_sleeps_per_frame` | Covered |
| AC-3 (`imu_replay.ImuReplayer.replay` parses CSV, constructs `ImuSample`s, calls emitter; tolerates scientific-notation floats) | `test_happy_path_emits_all_rows`, `test_scientific_notation_parses`, `test_attitude_radians_converted`, `test_realtime_sleeps_per_sample`, `test_non_realtime_does_not_sleep`, `test_empty_csv_emits_nothing`; error paths: `test_missing_csv_raises_file_not_found`, `test_rate_hz_must_be_positive`, `test_missing_required_columns_raises`, `test_row_with_unparseable_value_raises` | Covered |
| AC-4 (≥5 unit tests per body — happy + 2 error + 1 boundary) | fdr_reader: 14 tests; frame_source_replay: 10 tests; imu_replay: 10 tests | Covered (exceeds floor) |
| AC-5 (full suite passes) | 558 passed (+31 from 527 baseline) | Covered |

### Phase 3 — Code Quality

* **Single responsibility**:
  * `fdr_reader` owns wire-envelope parsing + projection to the runner-side
    dataclass. It does NOT validate per-kind payload keys — that's left to
    the consuming evaluators, matching the AZ-272/273 contract that
    documents `payload` as opaque on the runner side.
  * `frame_source_replay.FrameSourceReplayer` owns frame decode + sink
    emission. Cadence pacing is parameterised via `sleep_fn` for testability;
    OpenCV is the only third-party import.
  * `imu_replay.ImuReplayer` owns CSV parse + emitter invocation. Cadence
    pacing same pattern as frame_source_replay. Row parsing is a pure
    function `_parse_row` so missing/malformed columns surface as a
    `ValueError` with a file + line pointer.
* **No suppressed errors**:
  * `fdr_reader._parse_envelope` raises `ValueError` (with file + line)
    on every malformed envelope branch; `json.JSONDecodeError` propagates
    naturally for unrecoverable JSON corruption.
  * `frame_source_replay._decode_and_emit_video` raises explicit
    `ValueError` if OpenCV cannot open the file or fails to encode a
    frame — never silently skips a frame.
  * `imu_replay._parse_row` re-raises `KeyError`/`ValueError` wrapped
    as a contextual `ValueError`; the original `__cause__` is preserved
    via `raise ... from exc`.
  * No `except Exception: pass`, `2>/dev/null`, or empty `except` blocks.
* **AAA comment discipline**: every new test uses `# Arrange / # Act /
  # Assert`; sections omitted when not needed.
* **No code comments narrating what code does** — only the module-level
  docstrings explain the wire-vs-runner schema rename, the OpenCV
  realtime/non-realtime distinction, and the scientific-notation
  tolerance rationale.
* **Public boundary**: confirmed all three modules import only
  stdlib (`csv`, `json`, `math`, `time`, `pathlib`, `datetime`) +
  `cv2`. No `from gps_denied_onboard ...` anywhere.

### Phase 4 — Security

* **No new credentials, secrets, or network surface**. All three
  helpers are deterministic file-I/O over caller-supplied paths.
* **Wire-schema gate** in `fdr_reader._parse_envelope` is the safety
  invariant — if the SUT FDR schema drifts, the runner blows up at
  parse time with a file+line pointer rather than silently producing
  records with default-zero fields. This is the explicit
  drift-detection rationale documented in the `FdrRecord` docstring.
* **No `eval`, `exec`, `pickle`, or `subprocess`** in any of the three
  modules.

### Phase 5 — Performance

* All three implementations are O(N) over their input streams
  (JSONL lines, frames, CSV rows). Sorting in `iter_records` is the
  one O(N log N) step — necessary for the multi-file merge-sort
  guarantee, and acceptable since the typical archive is bounded by
  the AZ-441 50 GB / 8 h budget.
* No file I/O at module-import time.
* `FrameSourceReplayer._decode_and_emit_video` always re-encodes
  frames to JPEG (lossy double-encode if source was MP4). The
  intent is so the sink always receives JPEG bytes regardless of
  source format — matches the SUT's `ONBOARD_FRAME_SOURCE_PATH`
  expectation of file-system-readable JPEG frames.

### Phase 6 — Cross-Task Consistency

* **Sleep injection pattern**: `frame_source_replay.FrameSourceReplayer`
  and `imu_replay.ImuReplayer` both accept `sleep_fn` as a keyword
  argument defaulting to `time.sleep`. Tests pass `lambda _: None`
  or a recording stub. Single pattern across the two replay surfaces.
* **`realtime` vs `non-realtime` flag**: both replayers default to
  `realtime=True` and skip the sleep when `False`. Consistent semantics
  so that future scenarios can choose between wall-clock replay
  (for live-FC tests) and fast replay (for FDR/evidence-only tests).
* **FileNotFoundError**: all three new bodies surface missing input
  paths via `FileNotFoundError`, consistent with how the existing
  helpers `accuracy_evaluator`, `multi_segment_evaluator`,
  `mavproxy_tlog_reader`, `cold_start_evaluator` handle missing
  inputs.
* **Scenario probe interaction**: the existing
  `_harness_helpers_implemented` probes in batches 71-73 (FT-P-07,
  FT-P-08, FT-P-10, FT-P-11, FT-N-01, FT-N-02, FT-N-03, FT-N-04)
  pass `/tmp/non-existent` to each helper. Previously the inner
  `except NotImplementedError: return False` fired; now the outer
  `except Exception: return False` catches the new `FileNotFoundError`
  and still returns False. The scenarios continue to skip — which is
  correct because the OTHER probe-gated helpers (`sitl_observer`,
  `mavproxy_tlog_reader` for some scenarios, `fc_proxy_runtime`) are
  still pending. Probe cleanup is explicitly excluded from AZ-594
  scope and will happen once the remaining harness stubs land.

### Phase 7 — Architecture Compliance

* **Module placement unchanged**: existing files at
  `e2e/runner/helpers/{fdr_reader,frame_source_replay,imu_replay}.py`
  were edited in place (bodies replace `NotImplementedError`); no
  layout changes. New unit-test files in
  `e2e/_unit_tests/helpers/`. Layout invariant test still passes —
  these helpers were already listed.
* **No `src/gps_denied_onboard` imports** anywhere. Verified.
* **Existing scenario surface preserved**: `FdrRecord` dataclass
  field names and types are unchanged from the AZ-406 contract.
  `ImuSample` likewise. `ReplayCadence` and `FrameSink` Protocol
  unchanged. Consumers in batches 71-73 stay valid.

## Test Results

* New unit tests: 14 (fdr_reader) + 10 (frame_source_replay) + 10
  (imu_replay) = **34 new tests**.
* Full `e2e/_unit_tests` suite: **558 passed in 139 s** (previous
  cumulative: 527 → +31 net).
* No new linter errors (`ReadLints` on all six new/modified files
  reported clean).
