Files
Oleksandr Bezdieniezhnykh 17a0d074af [AZ-401] [AZ-400] Replay — compose_root replay-mode branch + transport seam
Wires the airborne composition root for replay-as-configuration (ADR-011):

- compose_root(config) branches on config.mode in {"live", "replay"}.
  Live behaviour is unchanged; replay builds ReplayInputAdapter,
  attaches JsonlReplaySink, and injects NoopMavlinkTransport.
- New private module runtime_root/_replay_branch.py holds the
  replay-only strategy graph + build-flag gate + calibration loader.
- Config gains Config.mode (Literal["live","replay"]) plus
  Config.replay sub-block with nested ReplayAutoSyncConfig that mirrors
  the AZ-405 AutoSyncConfig DTO; YAML loader + ENV map updated.

Absorbs the AZ-400 transport-seam retrofit that AZ-401 strictly
required but AZ-400 had not delivered:

- New MavlinkTransport Protocol (write/bytes_written/close).
- NoopMavlinkTransport (replay; build-flag gated, idempotent close,
  thread-safe byte counter).
- SerialMavlinkTransport (live, no-op restructure of existing pymavlink
  byte path; encoder retrofit to actually USE it is the AZ-558
  follow-up).

AZ-401 AC-9 (NoopMavlinkTransport.bytes_written > 0 after C8 encoders
run) is BLOCKED on AZ-558 — the encoder routing retrofit is out of
the AZ-401 task envelope (FORBIDDEN files: pymavlink_ardupilot_adapter,
msp2_inav_adapter). AZ-558 spec, batch_61_review.md, and the test's
@pytest.mark.skip rationale all carry the deferral reason.

Tests: 22 compose_root replay-branch tests + 17 transport tests.
Full regression: 2063 passed, 86 environment-skips, 1 documented
skip (AC-9 / AZ-558), 1 pre-existing flaky perf test deselected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 11:55:33 +03:00

42 lines
1.0 KiB
Python

"""C8 FC Adapter smoke test — AC-9 (legacy) + AZ-390 public-API gate.
AZ-401 expands the public Protocol surface with ``MavlinkTransport``,
the outbound byte-stream seam shared by ``SerialMavlinkTransport``
(live) and ``NoopMavlinkTransport`` (replay).
"""
def test_interface_importable() -> None:
# Assert
from gps_denied_onboard.components.c8_fc_adapter import (
EmittedExternalPosition,
FcAdapter,
GcsAdapter,
MavlinkTransport,
ReplaySink,
)
for sym in (
FcAdapter,
GcsAdapter,
ReplaySink,
EmittedExternalPosition,
MavlinkTransport,
):
assert sym is not None
def test_internal_modules_not_in_public_all() -> None:
"""AZ-390 AC-8: only the contract symbols appear in ``__all__``."""
# Arrange
from gps_denied_onboard.components import c8_fc_adapter
# Assert
assert set(c8_fc_adapter.__all__) == {
"EmittedExternalPosition",
"FcAdapter",
"GcsAdapter",
"MavlinkTransport",
"ReplaySink",
}