mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 09:11:13 +00:00
[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>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# 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.py`** —
|
||||
`drive_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.
|
||||
Reference in New Issue
Block a user