Files
gps-denied-onboard/_docs/02_tasks/done/AZ-596_fc_proxy_runtime.md
T
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.5 KiB

fc_proxy_runtime driver (FDR-replay mode) for FT-N-04

Task: AZ-596_fc_proxy_runtime Name: Implement runtime driver wrapping BlackoutSpoofProxy + replace FT-N-04 stub Description: Add runner/helpers/fc_proxy_runtime.py with drive_fc_proxy(schedule_path, *, now_ms_provider=None) that loads the blackout-spoof schedule via the existing fixtures/injectors/fc_proxy.BlackoutSpoofProxy, returns a ProxyDriveReport, and (when E2E_SITL_REPLAY_DIR is set) writes proxy_drive_report.json for downstream evaluators. Replace the local _drive_fc_proxy NotImplementedError stub in FT-N-04 with the new helper. FDR-replay mode only — live MAVLink router wiring is out of scope. Complexity: 2 points Dependencies: AZ-406 (fixtures/injectors/fc_proxy.BlackoutSpoofProxy), AZ-595 (sitl_observer.replay_dir) Component: Blackbox Tests / Test Infrastructure (epic AZ-262) Tracker: AZ-596 Epic: AZ-262 (E-BBT)

Problem

FT-N-04 (test_ft_n_04_blackout_spoof) calls a local stub:

def _drive_fc_proxy(schedule_path: Path) -> None:
    raise NotImplementedError(
        "FC-inbound spoof proxy driver is owned by AZ-441 / runner.helpers.fc_proxy_runtime"
    )

The BlackoutSpoofProxy itself was implemented under AZ-406's injector module — fully unit-tested, accepts a now_ms_provider, exposes activate(...) + process_inbound_message(...). What's missing is the runtime driver that the scenario calls to wrap the proxy: load the schedule, activate it, optionally record what happened so downstream evaluators (sitl_observer.read_gps_health_samples / read_consistency_check_events) can correlate.

In FDR-replay mode (the AZ-595 strategy), the actual FC inbound transport is not real — the SITL replay fixture builder pre-bakes the expected spoofed-GPS-rejected events into the FDR JSON files. So the runtime driver doesn't need to plumb into a live MAVLink router. It just needs to (a) validate the schedule loads correctly, (b) optionally align with the harness clock, and (c) write a small audit report into ${E2E_SITL_REPLAY_DIR} so evaluators can verify the schedule actually ran.

Live-mode driving (real MAVLink router + actual FC inbound) is out of scope for this ticket and is a separate live-mode infrastructure task.

Surfaces

  • drive_fc_proxy(schedule_path: Path, *, now_ms_provider: NowMsProvider | None = None, replay_dir: Path | None = None) -> ProxyDriveReport
    • Loads the schedule via BlackoutSpoofProxy.from_schedule_file(schedule_path).
    • If now_ms_provider is supplied: activates the proxy and reads alignment_err_ms from the activation report.
    • If now_ms_provider is None: emits ProxyDriveReport with alignment_err_ms=0 and was_replay_mode=True.
    • If replay_dir is supplied (or resolved from E2E_SITL_REPLAY_DIR): writes proxy_drive_report.json into that directory.
  • ProxyDriveReport (frozen dataclass): schedule_path: Path, window_start_ms: int, window_end_ms: int, spoof_frame_count: int, alignment_err_ms: int, was_replay_mode: bool.

Acceptance Criteria

AC-1: drive_fc_proxy(schedule_path) loads the schedule via BlackoutSpoofProxy.from_schedule_file. Missing schedule_pathFileNotFoundError (inherited from BlackoutSpoofProxy). Malformed JSON → ValueError with the file path.

AC-2: When now_ms_provider is supplied, the driver activates the proxy and records alignment_err_ms on the report. When unsupplied, the report fills alignment_err_ms=0 and was_replay_mode=True.

AC-3: When replay_dir is supplied (or E2E_SITL_REPLAY_DIR env var is set), the driver writes proxy_drive_report.json into that directory. When neither is supplied, no file is written.

AC-4: ≥5 unit tests covering: happy path, missing schedule path, malformed schedule JSON, replay-mode JSON write, no-write when env var unset, alignment-error path with injected clock.

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

Out of Scope

  • Live MAVLink router wiring + docker-compose orchestration.
  • 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 gets its own follow-up ticket.

Files Touched

  • e2e/runner/helpers/fc_proxy_runtime.py (new)
  • e2e/_unit_tests/helpers/test_fc_proxy_runtime.py (new)
  • e2e/tests/negative/test_ft_n_04_blackout_spoof.py (replace local stub)
  • e2e/_unit_tests/test_directory_layout.py (register new module)