Files
Oleksandr Bezdieniezhnykh 6554d568f1 [AZ-596] Batch 76: fc_proxy_runtime driver (FDR-replay mode)
Add `runner/helpers/fc_proxy_runtime.py` wrapping the existing
`BlackoutSpoofProxy` (AZ-406) with a scenario-facing `drive_fc_proxy`
entry point. FDR-replay mode only: loads `schedule.json`, optionally
activates the proxy against a caller clock for alignment verification,
and writes a `proxy_drive_report.json` audit record into
`${E2E_SITL_REPLAY_DIR}` for downstream evaluators.

Replaces the local `_drive_fc_proxy` stub in FT-N-04. Adds 3
@property accessors on `BlackoutSpoofProxy` so the wrapper does not
reach into private attributes. +11 unit tests (608 total, up from
596). Live-mode router wiring remains out of scope (future ticket).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-17 09:08:48 +03:00

4.2 KiB

Batch 76 Report — fc_proxy_runtime driver (cycle 1, batch 10 of test phase)

Batch: 76 Date: 2026-05-17 Context: Test implementation (greenfield Step 10 — Implement Tests) Tasks: AZ-596 (2 cp) — 1 task (fc_proxy_runtime driver, FDR-replay mode) Cycle: 1 Verdict: COMPLETE — PASS (self-reviewed; see reviews/batch_76_review.md)

Summary

Final piece of the harness-stubs arc that started in batch 74. The FT-N-04 (test_ft_n_04_blackout_spoof) scenario called a local _drive_fc_proxy stub that raised NotImplementedError("FC-inbound spoof proxy driver is owned by runner.helpers.fc_proxy_runtime"). That module didn't exist. The BlackoutSpoofProxy state machine (load schedule, activate, replace inbound GPS frames inside the window) was already fully implemented under AZ-406 in fixtures/injectors/fc_proxy.py — what was missing was the scenario-facing wrapper.

This batch adds runner/helpers/fc_proxy_runtime.py (one function + one dataclass) using the same FDR-replay strategy as AZ-595: the runtime driver does not plumb into a live MAVLink router. It loads the schedule, optionally activates the proxy against a caller-supplied clock, and writes a small audit JSON (proxy_drive_report.json) into ${E2E_SITL_REPLAY_DIR} so the downstream FDR evaluators can correlate. Live-mode driving (real router + real FC) is explicitly a separate live-mode infrastructure ticket.

AZ-596 — fc_proxy_runtime driver (2 cp)

  • runner/helpers/fc_proxy_runtime.pydrive_fc_proxy(schedule_path, *, now_ms_provider=None, replay_dir=None):
    • Loads the schedule via BlackoutSpoofProxy.from_schedule_file(schedule_path).
    • Wraps json.JSONDecodeError as ValueError with a file pointer (consistent with the rest of e2e/runner/helpers/).
    • When now_ms_provider is supplied, activates the proxy and records the resulting alignment_err_ms. When absent, sets was_replay_mode=True.
    • Resolves the write directory in this order: explicit replay_dir argument > ${E2E_SITL_REPLAY_DIR} env var > no write. The chosen directory is created if missing.
    • Returns ProxyDriveReport (frozen dataclass with schedule_path, window_start_ms, window_end_ms, spoof_frame_count, alignment_err_ms, was_replay_mode).
  • fixtures/injectors/fc_proxy.py — added three additive @property accessors (window_start_ms, window_end_ms, spoof_frame_count) so the runtime wrapper does NOT reach into private attributes. Existing callers unaffected.
  • FT-N-04 scenario — local _drive_fc_proxy stub replaced with from runner.helpers.fc_proxy_runtime import drive_fc_proxy; drive_fc_proxy(schedule_path). The scenario's b75 sitl_replay_ready skip gate continues to govern when this code path actually runs.
  • Directory layout test — registered the new runner/helpers/fc_proxy_runtime.py path.

Out of scope (deferred)

  • Live MAVLink router + FC inbound transport — the runtime driver currently does not wire proxy.process_inbound_message into a real router. A live-mode follow-up ticket will own the docker-compose-bound MAVLink router that plumbs in the per-message replace.
  • Other per-scenario _resolve_* / _drive_* stubs (_resolve_frame_sink, _resolve_fc_inbound_emitter, _resolve_outage_injection_frames, _resolve_gt_per_frame, _drive_imu_replay, _resolve_frame_period_ms) — each will get its own follow-up ticket. They remain NotImplementedError stubs in their respective scenario files; the sitl_replay_ready skip gate ensures they're never reached in unit-test mode.

Test Results

  • New unit tests: 11 (3 schedule load/error, 2 activation, 6 replay-dir write).
  • Full e2e/_unit_tests suite: 608 passed in 124 s (previous cumulative: 596 → +12 net = +11 new fc_proxy_runtime tests + 1 new directory-layout parametrize entry).
  • No new linter errors.

State

  • Spec moved: _docs/02_tasks/todo/AZ-596_fc_proxy_runtime.md_docs/02_tasks/done/.
  • _docs/_autodev_state.md advanced to last_completed_batch: 76.
  • last_cumulative_review remains batches_73-75; next K=3 cumulative review fires at the end of batch 78.