[AZ-619] Phase A: build_pre_constructed seeds c13_fdr + clock

Adds airborne_bootstrap.build_pre_constructed(config) returning a
dict with the two foundational keys: a per-binary shared FdrClient
under "c13_fdr" (via make_fdr_client with the new
AIRBORNE_MAIN_PRODUCER_ID constant) and a fresh WallClock under
"clock". Phases B..F (AZ-620..AZ-624) extend this function
additively without breaking the AZ-619 contract.

The c13_fdr instance is identity-stable across calls (per the
make_fdr_client per-producer cache) so callers can call
build_pre_constructed twice and get the same FdrClient back -
AC-619.2.

Replay-mode override is unchanged: compose_root merges
replay_components over pre_constructed so the WallClock here is
replaced by TlogDerivedClock in replay binaries (existing
contract documented in compose_root's docstring).

Tests: 5 new unit tests under tests/unit/runtime_root/
test_az619_pre_constructed_phase_a.py, all passing. AZ-591 not
regressed (12/12 in the combined run).

Spec moved to _docs/02_tasks/done/.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 06:23:15 +03:00
parent 8cee532516
commit 8abfb020fe
4 changed files with 155 additions and 5 deletions
@@ -0,0 +1,109 @@
"""AZ-619 — Phase A of AZ-618: ``build_pre_constructed`` seeds c13_fdr + clock.
Verifies the contract at
``_docs/02_tasks/todo/AZ-619_pre_constructed_phase_a_c13_fdr_clock.md``:
* AC-619.1: ``build_pre_constructed(default_config)`` returns a dict
containing keys ``c13_fdr`` (FdrClient instance) and ``clock``
(WallClock instance).
* AC-619.2: invoking twice in the same process returns dicts where
``c13_fdr`` is the SAME FdrClient instance (per ``make_fdr_client``
cache); ``clock`` may be a fresh WallClock each call.
AC-619.3 (this file exists with the above tests) is satisfied by the
existence of this module.
"""
from __future__ import annotations
from collections.abc import Iterator
import pytest
from gps_denied_onboard.clock.wall_clock import WallClock
from gps_denied_onboard.config import Config
from gps_denied_onboard.fdr_client import client as fdr_client_module
from gps_denied_onboard.fdr_client.client import FdrClient
from gps_denied_onboard.runtime_root.airborne_bootstrap import (
AIRBORNE_MAIN_PRODUCER_ID,
build_pre_constructed,
)
@pytest.fixture(autouse=True)
def _isolated_fdr_cache() -> Iterator[None]:
# Arrange: every test starts with an empty FdrClient cache so AC-619.2's
# "same instance across calls" assertion is exercised against fresh state
# rather than stale cache from a prior test.
fdr_client_module._reset_for_tests()
yield
fdr_client_module._reset_for_tests()
def test_ac_619_1_default_config_seeds_c13_fdr_and_clock() -> None:
# Arrange
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert
assert "c13_fdr" in pre_constructed
assert "clock" in pre_constructed
assert isinstance(pre_constructed["c13_fdr"], FdrClient)
assert isinstance(pre_constructed["clock"], WallClock)
def test_ac_619_1_c13_fdr_uses_airborne_main_producer_id() -> None:
# Arrange
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert: the FdrClient is keyed under the documented producer ID so
# any per-component caller using the same ID gets the same instance.
assert pre_constructed["c13_fdr"].producer_id == AIRBORNE_MAIN_PRODUCER_ID
def test_ac_619_2_c13_fdr_cached_across_calls() -> None:
# Arrange
config = Config()
# Act
first = build_pre_constructed(config)
second = build_pre_constructed(config)
# Assert: same FdrClient instance via make_fdr_client cache.
assert first["c13_fdr"] is second["c13_fdr"]
def test_ac_619_2_clock_is_independent_instance_per_call() -> None:
# Arrange
config = Config()
# Act
first = build_pre_constructed(config)
second = build_pre_constructed(config)
# Assert: WallClock is stateless; identity-share is not contractual.
# Both must be valid WallClock instances. Identity may or may not match.
assert isinstance(first["clock"], WallClock)
assert isinstance(second["clock"], WallClock)
def test_phase_a_only_seeds_two_keys() -> None:
"""Phase A scope discipline: exactly the two documented keys.
Phases B..F (AZ-620..AZ-624) will add more keys; this test will be
relaxed at that point. For now it pins the AZ-619 contract precisely so
a regression that adds keys here without updating the spec is caught.
"""
# Arrange
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert
assert set(pre_constructed.keys()) == {"c13_fdr", "clock"}