[AZ-622] Phase D: build_pre_constructed seeds c3 GPU runtimes

build_pre_constructed now populates c3_lightglue_runtime
(LightGlueRuntime) + c3_feature_extractor (FeatureExtractor) on top
of AZ-619/620/621. Strategy-specific BUILD_MATCHER_* flag mismatch
raises AirborneBootstrapError naming the missing flag and the c3_matcher
consumer; the c7 InferenceRuntime built earlier in the bootstrap is
reused as the engine source so no double-build at this layer.

C3MatcherConfig gains optional lightglue_weights_path: Path | None
for the operator's deployment config; production main() (AZ-624)
populates it. Real LightGlue inference correctness is verified by
AZ-624's Jetson AC-5 run per the AZ-622 Tier-2 Note.

Phase tests for AZ-619/620/621 gain an autouse _stub_c3_matcher_builders
fixture so additivity assertions remain valid as the bootstrap grows.

Code review: PASS_WITH_WARNINGS (3 Low: signature drift from spec,
_is_build_flag_on duplication across 3 runtime_root modules, and
BuildConfig literal mirrored with per-strategy build configs). All
deferred to future hygiene PBIs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 08:56:04 +03:00
parent eaf2f47f69
commit 5c4d129f80
10 changed files with 856 additions and 19 deletions
@@ -80,6 +80,24 @@ def _stub_c7_inference_builder(monkeypatch: pytest.MonkeyPatch) -> None:
)
@pytest.fixture(autouse=True)
def _stub_c3_matcher_builders(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-622 Phase D C3 matcher builders so the AZ-619
# tests stay focused on Phase A. Without this the bare Config() below
# would hit the BUILD_MATCHER_DISK_LIGHTGLUE flag check (typically
# unset in the test env) and raise AirborneBootstrapError before the
# AC-619 keys could be asserted. Sentinels are opaque on purpose —
# AZ-619 assertions never inspect them.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c3_lightglue_runtime",
lambda _config, *, inference_runtime: object(),
)
monkeypatch.setattr(
airborne_bootstrap, "_build_c3_feature_extractor", lambda _config: object()
)
def test_ac_619_1_default_config_seeds_c13_fdr_and_clock() -> None:
# Arrange
config = Config()
@@ -74,6 +74,27 @@ def _stub_c7_inference_builder(monkeypatch: pytest.MonkeyPatch) -> None:
)
@pytest.fixture(autouse=True)
def _stub_c3_matcher_builders(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-622 Phase D C3 matcher builders so AZ-620
# tests stay focused on the Phase B contract. Without this the
# configs used below would hit the BUILD_MATCHER_DISK_LIGHTGLUE flag
# check (typically unset in the test env) and raise
# AirborneBootstrapError before the AC-620 keys could be asserted.
# Sentinels are opaque on purpose — AZ-620 assertions never inspect
# them.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c3_lightglue_runtime",
lambda _config, *, inference_runtime: MagicMock(name="LightGlueRuntime"),
)
monkeypatch.setattr(
airborne_bootstrap,
"_build_c3_feature_extractor",
lambda _config: MagicMock(name="FeatureExtractor"),
)
def test_ac_620_1_adds_c6_descriptor_index_and_c6_tile_store(
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -77,6 +77,27 @@ def _stub_c6_builders(monkeypatch: pytest.MonkeyPatch) -> None:
)
@pytest.fixture(autouse=True)
def _stub_c3_matcher_builders(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-622 Phase D C3 matcher builders so AZ-621
# tests stay focused on the Phase C contract. Without this the
# configs used below would hit the BUILD_MATCHER_DISK_LIGHTGLUE flag
# check (typically unset in the test env) and raise
# AirborneBootstrapError before the AC-621 keys could be asserted.
# Sentinels are opaque on purpose — AZ-621 assertions never inspect
# them.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c3_lightglue_runtime",
lambda _config, *, inference_runtime: MagicMock(name="LightGlueRuntime"),
)
monkeypatch.setattr(
airborne_bootstrap,
"_build_c3_feature_extractor",
lambda _config: MagicMock(name="FeatureExtractor"),
)
def test_ac_621_1_adds_c7_inference(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub build_inference_runtime to a sentinel so we can
# assert wiring without standing up real GPU/TensorRT/PyTorch.
@@ -0,0 +1,296 @@
"""AZ-622 — Phase D of AZ-618: ``build_pre_constructed`` seeds c3_lightglue_runtime + c3_feature_extractor.
Verifies the contract at
``_docs/02_tasks/todo/AZ-622_pre_constructed_phase_d_c3_runtimes.md``:
* AC-622.1: with a default airborne ``Config`` (and the
``BUILD_MATCHER_DISK_LIGHTGLUE`` flag ON in spirit — here stubbed at
the airborne_bootstrap module boundary), ``build_pre_constructed(config)``
additionally contains ``c3_lightglue_runtime`` (a
:class:`LightGlueRuntime` instance) AND ``c3_feature_extractor`` (a
:class:`FeatureExtractor` instance) on top of AZ-619 + AZ-620 + AZ-621.
* AC-622.2: when ``BUILD_MATCHER_DISK_LIGHTGLUE=OFF`` AND a config
selects ``c3_matcher.strategy="disk_lightglue"``, ``build_pre_constructed``
raises :class:`AirborneBootstrapError` whose message names
``c3_lightglue_runtime`` (the missing key), ``BUILD_MATCHER_DISK_LIGHTGLUE``
(the gating flag), and ``c3_matcher`` (the consuming component slug).
AC-622.3 (this file exists with the above tests) is satisfied by the
existence of this module.
The tests stub the heavy ``_load_lightglue_engine_handle`` seam (AZ-622)
plus the upstream ``_build_c6_*`` (AZ-620) and ``_build_c7_inference``
(AZ-621) factories at the airborne_bootstrap module boundary — exactly
mirroring the prior phase pattern (see
:mod:`tests.unit.runtime_root.test_az621_pre_constructed_phase_c`). Real
LightGlue inference correctness is verified by AZ-624's Jetson AC-5 run
per the AZ-622 ``Tier-2 Note``.
"""
from __future__ import annotations
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from gps_denied_onboard.config import Config
from gps_denied_onboard.fdr_client import client as fdr_client_module
from gps_denied_onboard.helpers.feature_extractor import (
FeatureExtractor,
OpenCvOrbExtractor,
)
from gps_denied_onboard.helpers.lightglue_runtime import LightGlueRuntime
from gps_denied_onboard.runtime_root import airborne_bootstrap
from gps_denied_onboard.runtime_root.airborne_bootstrap import (
C3_MATCHER_BUILD_FLAGS,
AirborneBootstrapError,
build_pre_constructed,
)
@dataclass(frozen=True)
class _C3MatcherBlock:
"""Minimal c3_matcher config block — only the fields the tests need."""
strategy: str = "disk_lightglue"
lightglue_weights_path: Path | None = None
def _make_engine_handle_mock(*, descriptor_dim: int = 128) -> MagicMock:
"""Build a mock that satisfies the LightGlueRuntime engine_handle contract.
The runtime checks ``engine_handle.descriptor_dim`` (must be ``int``
and ``>= 1``) and stores the handle for later ``forward`` calls.
AZ-622 only exercises construction; tests do not invoke ``forward``.
"""
handle = MagicMock(name="LightGlueEngineHandle")
handle.descriptor_dim = descriptor_dim
return handle
@pytest.fixture(autouse=True)
def _isolated_fdr_cache() -> Iterator[None]:
# Arrange: every test starts with an empty FdrClient cache so the
# bootstrap's make_fdr_client call doesn't accidentally pick up a
# stale instance from a prior test in the same process.
fdr_client_module._reset_for_tests()
yield
fdr_client_module._reset_for_tests()
@pytest.fixture(autouse=True)
def _stub_c6_and_c7_builders(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange: stub the AZ-620 (Phase B) C6 builders and the AZ-621
# (Phase C) C7 inference builder so AZ-622 stays focused on the
# Phase D contract. Sentinels are opaque on purpose — AZ-622
# assertions never inspect them.
monkeypatch.setattr(
airborne_bootstrap,
"_build_c6_descriptor_index",
lambda _config: MagicMock(name="DescriptorIndex"),
)
monkeypatch.setattr(
airborne_bootstrap,
"_build_c6_tile_store",
lambda _config: MagicMock(name="TileStore"),
)
monkeypatch.setattr(
airborne_bootstrap,
"_build_c7_inference",
lambda _config: MagicMock(name="InferenceRuntime"),
)
def test_ac_622_1_adds_c3_lightglue_runtime_and_c3_feature_extractor(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Arrange: make BUILD_MATCHER_DISK_LIGHTGLUE ON and stub the heavy
# LightGlue engine load with a sentinel handle. Default Config()
# carries no c3_matcher block, so the bootstrap defaults to
# "disk_lightglue" (per _resolve_c3_matcher_strategy).
monkeypatch.setenv("BUILD_MATCHER_DISK_LIGHTGLUE", "ON")
engine_handle = _make_engine_handle_mock(descriptor_dim=128)
monkeypatch.setattr(
airborne_bootstrap,
"_load_lightglue_engine_handle",
lambda _config, _inference_runtime: engine_handle,
)
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert: AZ-622 keys are present and correctly typed; the
# LightGlueRuntime wraps the stubbed engine handle (identity
# check); the feature extractor satisfies the FeatureExtractor
# Protocol. Prior phase keys are still present (additivity contract).
assert "c3_lightglue_runtime" in pre_constructed
assert "c3_feature_extractor" in pre_constructed
assert isinstance(pre_constructed["c3_lightglue_runtime"], LightGlueRuntime)
assert (
pre_constructed["c3_lightglue_runtime"].descriptor_dim()
== engine_handle.descriptor_dim
)
assert isinstance(pre_constructed["c3_feature_extractor"], FeatureExtractor)
assert isinstance(pre_constructed["c3_feature_extractor"], OpenCvOrbExtractor)
assert {
"c13_fdr",
"clock",
"c6_descriptor_index",
"c6_tile_store",
"c7_inference",
}.issubset(pre_constructed.keys())
def test_ac_622_2_build_flag_off_with_configured_strategy_raises_named_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Arrange: BUILD_MATCHER_DISK_LIGHTGLUE OFF (delete to defend against
# a leftover ON from a different test in the same session) AND a
# config that selects strategy="disk_lightglue". The flag check must
# fire BEFORE _load_lightglue_engine_handle is consulted, so we don't
# need to stub the loader for this branch.
monkeypatch.delenv("BUILD_MATCHER_DISK_LIGHTGLUE", raising=False)
config = Config.with_blocks(
c3_matcher=_C3MatcherBlock(strategy="disk_lightglue")
)
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
# The missing key, the gating flag, and the consuming component
# slug must ALL appear in the operator-facing message (AZ-622
# AC-622.2 + AZ-618 NFR "operator-facing error contract").
assert "c3_lightglue_runtime" in message
expected_flag = C3_MATCHER_BUILD_FLAGS["disk_lightglue"]
assert expected_flag in message, (
f"{expected_flag!r} missing from error: {message!r}"
)
assert "c3_matcher" in message
# The flag-OFF branch raises directly — there is no upstream cause to
# preserve (cause-chain preservation is exercised in
# test_ac_622_2_lightglue_engine_load_failure_wraps_runtime_error).
assert excinfo.value.__cause__ is None
def test_ac_622_2_build_flag_off_with_aliked_strategy_names_aliked_flag(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Per-strategy flag specificity: aliked_lightglue surfaces ALIKED's flag."""
# Arrange: ALIKED strategy with its own gating flag OFF.
monkeypatch.delenv("BUILD_MATCHER_ALIKED_LIGHTGLUE", raising=False)
config = Config.with_blocks(
c3_matcher=_C3MatcherBlock(strategy="aliked_lightglue")
)
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
assert "c3_lightglue_runtime" in message
assert C3_MATCHER_BUILD_FLAGS["aliked_lightglue"] in message
assert "c3_matcher" in message
# The DISK flag must NOT appear — the message is strategy-specific.
assert C3_MATCHER_BUILD_FLAGS["disk_lightglue"] not in message
def test_ac_622_2_default_config_no_c3_matcher_block_still_raises(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Defence-in-depth: even with no c3_matcher block configured, AZ-622
still fails loudly when the default-strategy flag is OFF.
Mirrors the AZ-620 / AZ-621 defence-in-depth tests. Silently returning
a dict without ``c3_lightglue_runtime`` would let a downstream caller
misread the state.
"""
# Arrange
monkeypatch.delenv("BUILD_MATCHER_DISK_LIGHTGLUE", raising=False)
config = Config() # empty components, default strategy resolves to disk_lightglue
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
assert "c3_lightglue_runtime" in message
assert C3_MATCHER_BUILD_FLAGS["disk_lightglue"] in message
assert "c3_matcher" in message
def test_ac_622_2_lightglue_engine_load_failure_wraps_runtime_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""When the BUILD flag is ON but the engine load itself fails,
``RuntimeNotAvailableError`` is wrapped into ``AirborneBootstrapError``
with the cause chain preserved (mirrors AZ-621's wrapping pattern)."""
# Arrange
from gps_denied_onboard.runtime_root.errors import RuntimeNotAvailableError
monkeypatch.setenv("BUILD_MATCHER_DISK_LIGHTGLUE", "ON")
def _raise_engine_load_failure(_config: Config, _inference_runtime: object) -> None:
raise RuntimeNotAvailableError(
"InferenceRuntime.deserialize_engine: simulated GPU bind failure"
)
monkeypatch.setattr(
airborne_bootstrap,
"_load_lightglue_engine_handle",
_raise_engine_load_failure,
)
config = Config.with_blocks(
c3_matcher=_C3MatcherBlock(strategy="disk_lightglue")
)
# Act + Assert
with pytest.raises(AirborneBootstrapError) as excinfo:
build_pre_constructed(config)
message = str(excinfo.value)
assert "c3_lightglue_runtime" in message
assert "disk_lightglue" in message
assert "c3_matcher" in message
assert isinstance(excinfo.value.__cause__, RuntimeNotAvailableError)
def test_lightglue_runtime_uses_c7_inference_from_pre_constructed(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The c7 InferenceRuntime built earlier in build_pre_constructed
must be the SAME instance passed into ``_load_lightglue_engine_handle``
— no double-build of the inference runtime at this layer."""
# Arrange
monkeypatch.setenv("BUILD_MATCHER_DISK_LIGHTGLUE", "ON")
inference_runtime_sentinel = MagicMock(name="InferenceRuntime")
monkeypatch.setattr(
airborne_bootstrap,
"_build_c7_inference",
lambda _config: inference_runtime_sentinel,
)
captured: dict[str, object] = {}
def _capture_loader(config: Config, inference_runtime: object) -> object:
captured["config"] = config
captured["inference_runtime"] = inference_runtime
return _make_engine_handle_mock(descriptor_dim=128)
monkeypatch.setattr(
airborne_bootstrap, "_load_lightglue_engine_handle", _capture_loader
)
config = Config()
# Act
pre_constructed = build_pre_constructed(config)
# Assert: the loader received the EXACT same InferenceRuntime instance
# placed under pre_constructed['c7_inference'] (identity-share — the
# same single-build-per-bootstrap discipline AZ-621 established).
assert captured["inference_runtime"] is inference_runtime_sentinel
assert pre_constructed["c7_inference"] is inference_runtime_sentinel