mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 17:41:13 +00:00
[AZ-624] [AZ-618] Phase F: wire build_pre_constructed into main()
Wire register_airborne_strategies + build_pre_constructed + compose_root(config, pre_constructed=...) into runtime_root.main(). The existing exception block now catches AirborneBootstrapError distinctly before the broader (ConfigurationError, StrategyNotLinkedError, RuntimeError) clause so the operator-facing "airborne_bootstrap:" prefix carried by every bootstrap error reaches stderr cleanly with EXIT_GENERIC_FAILURE rather than getting absorbed into a generic backtrace. This closes the AZ-618 umbrella: AZ-619..AZ-623 + AZ-625 had built each pre_constructed key; this batch lands the integration that the production main() actually invokes them. Both the live gps-denied-onboard and replay gps-denied-replay binaries dispatch through this main() per ADR-011, so both reach takeoff with pre_constructed populated end-to-end. Tests: tests/unit/runtime_root/test_az618_pre_constructed.py adds 6 tests covering AC-618-1..AC-618-4 + AZ-624 local handler-ordering regression guard. The strategy factories are stubbed at the airborne_bootstrap module boundary so the test exercises the integration seam without standing up gtsam / FAISS / TensorRT / PyTorch / OpenCV at unit-test scope. AC-618-5 (Jetson tier-2 e2e) is BLOCKED on operator-supplied hardware evidence: scripts/run-tests-jetson.sh tests/e2e/replay/test_derkachi_1min.py must run on Jetson Orin Nano (JetPack 6.2.2+b24) and the terminal log path + JetPack version + run timestamp captured per _docs/02_document/tests/tier2-jetson-testing.md. Quality gates: ruff format clean, ruff lint clean, 6/6 new umbrella tests pass, 261/261 runtime_root + c5_state regression suite passes, 25/25 test_az401_compose_root_replay regression passes, full Tier-1 unit suite 2150/2151 passes (1 unrelated pre-existing failure: c12_operator_orchestrator subprocess cold-start NFR fails on Mac dev host's Python startup ~700 ms; not regressed by AZ-624). Code review verdict PASS (1 Low finding; full report in _docs/03_implementation/reviews/batch_96_review.md). Archives AZ-624 task spec + AZ-618 umbrella reference to done/. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,268 +0,0 @@
|
||||
# AZ-618 — Airborne main() builds pre_constructed infrastructure for compose_root (UMBRELLA — split into AZ-619..AZ-624)
|
||||
|
||||
> **STATUS — split**: 2026-05-19. Per the spec author's own Sizing-note recommendation and per the user-rule cap (≤5pt per PBI), AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). This file remains as the **umbrella reference** — it carries the shared narrative, the full 12-key map, the architectural rationale, and the canonical AC-1..AC-5 list. Subtask spec files in `_docs/02_tasks/todo/AZ-619..AZ-624_*.md` carry phase-specific scope and ACs. AZ-618 itself is no longer an actionable task; its work is entirely captured in the subtasks.
|
||||
>
|
||||
> **Actionable subtasks** (phase order, must land in dependency order):
|
||||
>
|
||||
> - **AZ-619** (Phase A) — c13_fdr + clock — 2pt — `_docs/02_tasks/todo/AZ-619_pre_constructed_phase_a_c13_fdr_clock.md`
|
||||
> - **AZ-620** (Phase B) — c6_descriptor_index + c6_tile_store — 3pt — `_docs/02_tasks/todo/AZ-620_pre_constructed_phase_b_c6_storage.md`
|
||||
> - **AZ-621** (Phase C) — c7_inference engine — 3pt — `_docs/02_tasks/todo/AZ-621_pre_constructed_phase_c_c7_inference.md`
|
||||
> - **AZ-622** (Phase D) — c3_lightglue_runtime + c3_feature_extractor — 3pt — `_docs/02_tasks/todo/AZ-622_pre_constructed_phase_d_c3_runtimes.md`
|
||||
> - **AZ-623** (Phase E) — c282_ransac_filter + c5 helpers + c5_isam2_graph_handle — 3pt — `_docs/02_tasks/todo/AZ-623_pre_constructed_phase_e_ransac_c5_helpers.md`
|
||||
> - **AZ-624** (Phase F) — wire main() + AC-1..AC-5 verification (incl. Jetson tier-2) — 2pt — `_docs/02_tasks/todo/AZ-624_pre_constructed_phase_f_wire_main.md`
|
||||
>
|
||||
> Aggregate work: 16pt across 6 subtasks (vs. AZ-618's original 5pt filing). The author's "likely a true 8" caveat in the Sizing note below was understated — c5_isam2_graph_handle ordering + GPU builder unknowns drove the honest total higher.
|
||||
|
||||
**Task**: AZ-618_airborne_bootstrap_pre_constructed
|
||||
**Name**: Airborne bootstrap pre_constructed assembly (cross-cutting Tier-1) — UMBRELLA
|
||||
**Description**: Land an `airborne_bootstrap.build_pre_constructed(config) -> dict[str, Any]` function (or equivalent in-`main()` wiring) that constructs every infrastructure object the registered airborne-strategy wrappers require, and call `compose_root(config, pre_constructed=...)` with the result from `runtime_root.main()`. Without this, `compose_root()` raises `AirborneBootstrapError` on the first wrapper lookup (`c1_vio` reaches for `pre_constructed['c13_fdr']` and finds nothing) and the binary cannot reach takeoff.
|
||||
**Complexity**: 0 points (umbrella — actionable work is in AZ-619..AZ-624; original filing was 5 points before split)
|
||||
**Dependencies**: AZ-591 (registry registration is the prerequisite — without it the wrappers do not run at all). Helper / runtime classes consumed by the wrappers are all already in `done/` per their own task IDs (c13_fdr → AZ-273+, c6_descriptor_index → AZ-306, c6_tile_store → AZ-303+, c7_inference → AZ-320+, c3_lightglue_runtime + c3_feature_extractor → AZ-278+, c2_82_ransac_filter → AZ-358, c5_imu_preintegrator → AZ-276, c5_se3_utils → AZ-277, c5_wgs_converter → AZ-284, c5_isam2_graph_handle → AZ-381).
|
||||
**Component**: runtime_root (cross-cutting)
|
||||
**Tracker**: AZ-618
|
||||
**Epic**: AZ-602 (E2E Tier-1 harness rehabilitation — parent set during ticket creation)
|
||||
|
||||
## Problem
|
||||
|
||||
Step 11 (Run Tests) cycle 1 Jetson tier-2 e2e rerun #3 surfaced this gap. With AZ-614 (synth time-base) + AZ-611 (skip-auto-sync) + AZ-602 (compose `BUILD_*` flag completeness) all landed, the Derkachi 1-min replay path now passes every layer up to and including:
|
||||
|
||||
```
|
||||
replay.compose_root.ready: pace=asap resolved_offset_ms=0 auto_sync_used=false
|
||||
```
|
||||
|
||||
…then crashes inside `runtime_root.airborne_bootstrap._require`:
|
||||
|
||||
```
|
||||
runtime_root: airborne_bootstrap: component 'c4_pose' requires
|
||||
pre_constructed['c282_ransac_filter'] to be populated before compose_root() runs;
|
||||
available keys in constructed: ['clock', 'fc_adapter', 'frame_source',
|
||||
'mavlink_transport', 'replay_sink'].
|
||||
Production main() must build infrastructure (c13_fdr, c6_*, c7_inference, etc.)
|
||||
into pre_constructed and pass it to compose_root(config, pre_constructed=...).
|
||||
Tests stub it via the same kwarg.
|
||||
```
|
||||
|
||||
**Cause**: `runtime_root.main()` (`src/gps_denied_onboard/runtime_root/__init__.py:636`) calls `register_airborne_strategies()` (registers the wrapper factories — AZ-591 work) and then `compose_root(config)` with **no** `pre_constructed=`. The wrappers' `_require(constructed, "c13_fdr", "c1_vio")` etc. raise on the first lookup because the dict is empty.
|
||||
|
||||
**Why hidden until now**: every prior Reality-Gate run died at auto-sync (AZ-614 root cause, 2026-05-17) BEFORE the composition graph was walked. AZ-591 was self-described as registering the "registry seam" — it explicitly deferred the `pre_constructed` assembly to a follow-up. That follow-up is this task.
|
||||
|
||||
**Why both binaries are affected**: the live `gps-denied-onboard` binary would crash at the same lookup the moment any component reaches into `pre_constructed`. Existing unit tests for `compose_root` (`tests/unit/test_az401_compose_root_replay.py`, 38 passing) pass only because they inject a stub via the `replay_components_factory` kwarg, bypassing the registry-driven path entirely. There is currently no test that exercises the production assembly.
|
||||
|
||||
## Outcome
|
||||
|
||||
- `src/gps_denied_onboard/runtime_root/airborne_bootstrap.py` exposes a new
|
||||
public `build_pre_constructed(config: Config) -> dict[str, Any]` that returns
|
||||
a dict populated with every key in `AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS`
|
||||
(12 distinct infrastructure objects: `c13_fdr`, `c6_descriptor_index`,
|
||||
`c6_tile_store`, `c7_inference`, `c3_lightglue_runtime`,
|
||||
`c3_feature_extractor`, `c282_ransac_filter`, `c5_wgs_converter`,
|
||||
`c5_se3_utils`, `c5_isam2_graph_handle`, `c5_imu_preintegrator`, `clock`).
|
||||
GPU-touching builders (`c7_inference`, `c3_lightglue_runtime`,
|
||||
`c3_feature_extractor`) are gated by their existing `BUILD_*` env flags;
|
||||
when a flag is OFF, the builder either skips (if the matching component
|
||||
strategy is not selected by config) or raises a clear operator-facing error
|
||||
naming the missing flag.
|
||||
|
||||
- `src/gps_denied_onboard/runtime_root/__init__.py::main()` calls
|
||||
`register_airborne_strategies()` followed by
|
||||
`pre_constructed = build_pre_constructed(config)` and then
|
||||
`compose_root(config, pre_constructed=pre_constructed)`. The
|
||||
`EXIT_FDR_OPEN_FAILURE` path already covers FDR open failures; this task
|
||||
extends the existing `RuntimeError` catch to surface
|
||||
`AirborneBootstrapError` with a clear operator-facing message rather than
|
||||
the current implicit traceback.
|
||||
|
||||
- New unit tests under `tests/unit/runtime_root/test_az618_pre_constructed.py`
|
||||
verify:
|
||||
- AC-1: `build_pre_constructed(config)` returns a dict containing every key
|
||||
in `AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS` flattened (no duplicates).
|
||||
- AC-2: A config that selects every default strategy completes
|
||||
`compose_root(config, pre_constructed=build_pre_constructed(config))`
|
||||
without raising. (Heavy infrastructure objects may be stubbed via the
|
||||
existing `_BUILD_*` env flags — the test asserts the seam, not the
|
||||
runtime.)
|
||||
- AC-3: When a required `BUILD_*` flag is OFF but the matching component
|
||||
strategy IS selected by config, the builder raises a clear error naming
|
||||
both the missing flag and the consuming component slug.
|
||||
- AC-4: `runtime_root.main()` end-to-end on a minimal config returns 0
|
||||
(success) when all `BUILD_*` flags + infra deps resolve; returns
|
||||
`EXIT_GENERIC_FAILURE` with the `AirborneBootstrapError` message in
|
||||
stderr when a required infra dep cannot be constructed.
|
||||
|
||||
- Existing Jetson tier-2 e2e replay tests
|
||||
(`tests/e2e/replay/test_derkachi_1min.py`) cross the
|
||||
`replay.compose_root.ready` log boundary and reach the per-frame inference
|
||||
loop. The 5 currently-failing ACs (AC-1, AC-2, AC-5, AC-6 × 2) advance to
|
||||
exercising C1..C8 end-to-end on the GPU — at which point any remaining
|
||||
failure is a different, deeper class of bug and out of scope for this task.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- New / refactored module: `runtime_root/airborne_bootstrap.py` —
|
||||
`build_pre_constructed(config)` function with one internal builder per
|
||||
required key. Builders reuse existing helper / strategy factories (no new
|
||||
infrastructure logic — only assembly).
|
||||
|
||||
- `runtime_root/__init__.py::main()` modification: insert
|
||||
`build_pre_constructed(config)` call between `register_airborne_strategies()`
|
||||
and `compose_root(config, ...)`. Add `AirborneBootstrapError` to the
|
||||
exception block so it surfaces with `EXIT_GENERIC_FAILURE` and a clear
|
||||
operator-facing message.
|
||||
|
||||
- New unit tests: `tests/unit/runtime_root/test_az618_pre_constructed.py`
|
||||
covering AC-1..AC-4.
|
||||
|
||||
- 6 internal phases — each phase is one source-file delta + matching unit
|
||||
test, and they may be batched but MUST land in dependency order:
|
||||
|
||||
1. **c13_fdr + clock** — foundational. The FDR client + WallClock helper
|
||||
(live) / TlogDerivedClock reuse (replay) — both already exist; the
|
||||
builder is an assembly step.
|
||||
2. **c6_descriptor_index + c6_tile_store** — descriptor faiss index +
|
||||
tile cache storage. AZ-306 + AZ-303 already built the runtime classes.
|
||||
3. **c7_inference engine** — GPU model load. PyTorch FP16 vs. TensorRT
|
||||
selected by config; `BUILD_TENSORRT_RUNTIME` / `BUILD_PYTORCH_FP16_RUNTIME`
|
||||
env flags gate the import path.
|
||||
4. **c3_lightglue_runtime + c3_feature_extractor** — ALIKED / DISK
|
||||
LightGlue. Gated by `BUILD_C3_MATCHER_DISK_LIGHTGLUE` /
|
||||
`BUILD_C3_MATCHER_ALIKED_LIGHTGLUE` env flags.
|
||||
5. **c282_ransac_filter** — small, stateless OpenCV-USAC wrapper.
|
||||
6. **c5 helpers** — `c5_imu_preintegrator`, `c5_se3_utils`,
|
||||
`c5_wgs_converter`, `c5_isam2_graph_handle`. All four are already-done
|
||||
helpers; the builder is pure assembly.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Changing the per-component helper / strategy factory signatures. Each
|
||||
builder consumes the existing factory's documented surface (e.g.
|
||||
`make_fdr_client(...)`, `build_inference_runtime(config, ...)`); no
|
||||
changes to those signatures are in scope.
|
||||
- GPU build-flag matrix expansion. The `BUILD_*` env flag system is already
|
||||
in place per component (`config.components.*.strategy`); this task only
|
||||
consumes the existing flags. New flags are out of scope.
|
||||
- Operator binary (`operator_bootstrap.py`) extensions. AZ-591 deferred the
|
||||
operator-side pre_constructed assembly; this task is airborne-only.
|
||||
Operator binary's current direct-factory path is not affected.
|
||||
- Replay-branch wiring beyond what already exists. Replay continues to
|
||||
supply `frame_source` / `fc_adapter` / `clock` / `mavlink_transport` /
|
||||
`replay_sink` via `build_replay_components`; this task adds the
|
||||
airborne-side keys ABOVE that set in the same `pre_constructed` dict.
|
||||
- Refactor of `airborne_bootstrap.py`'s wrapper-factory layer. The existing
|
||||
`_c1_vio_wrapper`, `_c2_vpr_wrapper`, etc. functions consume `constructed`
|
||||
correctly today; only the dict-population layer is new.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: `build_pre_constructed(config)` populates every required key**
|
||||
Given a process where `register_airborne_strategies()` has run
|
||||
And a `Config` selecting every component's default strategy
|
||||
When `build_pre_constructed(config)` is called
|
||||
Then the returned dict contains exactly the set of keys
|
||||
`set.union(*AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS.values())`
|
||||
And no key maps to `None`.
|
||||
|
||||
**AC-2: `compose_root(config, pre_constructed=...)` reaches takeoff**
|
||||
Given `register_airborne_strategies()` has run
|
||||
And `pre_constructed = build_pre_constructed(config)` for the default config
|
||||
When `compose_root(config, pre_constructed=pre_constructed)` runs
|
||||
Then it returns a `RuntimeRoot` whose `components` dict contains all 7
|
||||
registered slots (c1_vio, c2_vpr, c2_5_rerank, c3_matcher, c3_5_adhop,
|
||||
c4_pose, c5_state) without raising `AirborneBootstrapError`.
|
||||
|
||||
**AC-3: `BUILD_*` flag mismatch surfaces a clear error**
|
||||
Given the config selects `c2_vpr.strategy="net_vlad"` (requires `c7_inference`)
|
||||
And `BUILD_PYTORCH_FP16_RUNTIME=OFF`
|
||||
When `build_pre_constructed(config)` is called
|
||||
Then it raises `AirborneBootstrapError` whose message names both
|
||||
`c7_inference` (the missing infrastructure) and the gating
|
||||
`BUILD_*` flag.
|
||||
|
||||
**AC-4: `runtime_root.main()` end-to-end exit codes**
|
||||
Given a minimal in-process `Config` that selects all-defaults
|
||||
When `main(config)` is called with every `BUILD_*` flag the defaults need
|
||||
Then it returns `0` (success) and the runtime_root constructed log line
|
||||
fires.
|
||||
And when a single required infra dep is forcibly unavailable
|
||||
Then it returns `EXIT_GENERIC_FAILURE` (`1`) and stderr contains the
|
||||
`airborne_bootstrap:` prefix with the missing key and consuming component.
|
||||
|
||||
**AC-5: Jetson tier-2 e2e replay tests cross compose_root.ready**
|
||||
Given the AZ-618 changes are landed
|
||||
And the Jetson tier-2 e2e harness is invoked
|
||||
(`tests/e2e/replay/test_derkachi_1min.py::test_ac1_exits_0_jsonl_count_match`
|
||||
+ AC-2 + AC-5 + AC-6 ×2)
|
||||
Then each test progresses BEYOND `replay.compose_root.ready` (cross-cycle
|
||||
smoke: log appears in stdout AND the per-frame pipeline log
|
||||
`replay.input.frame_emitted` fires at least once per test).
|
||||
This AC verifies the airborne wiring is correct end-to-end; whether
|
||||
the per-frame results pass each AC's substantive threshold (count
|
||||
match, schema match, determinism, pace) is gated by other tasks and
|
||||
not blocking this AC.
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
- **Startup time**: `build_pre_constructed(config)` must complete within
|
||||
60 s on Jetson Orin Nano (JetPack 6.2.2+b24) for the default config.
|
||||
GPU model load + TensorRT engine cache compilation dominate; if the
|
||||
engine cache is cold and exceeds 60 s, log a one-line progress
|
||||
notice at 30 s.
|
||||
- **Memory**: peak resident set after `build_pre_constructed` must be
|
||||
< 2 GB on Jetson (excluding the inference model itself; the model is
|
||||
separately bounded by AZ-320's NFRs).
|
||||
- **Determinism**: invoking `build_pre_constructed(config)` twice in the
|
||||
same process MUST produce equivalent dicts (every key present, every
|
||||
builder callable). Re-invocation is not expected in production but
|
||||
IS expected in tests; the second call must not raise on already-loaded
|
||||
GPU resources.
|
||||
- **Operator-facing error contract**: every `AirborneBootstrapError`
|
||||
message MUST include (a) the consuming component slug, (b) the
|
||||
missing dependency key or `BUILD_*` flag, and (c) one actionable
|
||||
sentence pointing at the fix (e.g. "set `BUILD_C3_MATCHER_DISK_LIGHTGLUE=ON`"
|
||||
or "ensure `c13_fdr.path` is writable").
|
||||
|
||||
## Dependencies
|
||||
|
||||
- AZ-591 (registry registration prerequisite) — DONE
|
||||
- All component runtime classes/factories listed under the **Dependencies**
|
||||
field above — DONE per individual task IDs
|
||||
|
||||
## Constraints
|
||||
|
||||
- This task MUST NOT touch any per-component factory signature. All
|
||||
changes are confined to `runtime_root/airborne_bootstrap.py`,
|
||||
`runtime_root/__init__.py`, and the new test file.
|
||||
- This task MUST NOT introduce new `BUILD_*` env flags. Reuse the
|
||||
existing per-strategy `BUILD_*` matrix already gated by each
|
||||
component's strategy factory.
|
||||
- Do not stub or mock the inference engine in production code. The
|
||||
`c7_inference` builder MUST exercise the real (PyTorch FP16 or
|
||||
TensorRT) runtime when called from `main()`. Tests MAY stub it
|
||||
via `build_pre_constructed` mock seams documented in the new test
|
||||
file.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- 6-phase internal split (see Scope.Included). Phases land in
|
||||
dependency order; AC tests for each phase live with the phase
|
||||
but the full AC-1..AC-4 suite only goes green after phase 6.
|
||||
- The Jetson-only AC-5 cannot be run from the Mac dev host. The
|
||||
task is "done" when AC-1..AC-4 pass locally + AC-5 passes on
|
||||
the operator's Jetson per `scripts/run-tests-jetson.sh`.
|
||||
- AZ-591's task spec called out this exact follow-up (see its
|
||||
"AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS docstring": *"production
|
||||
wiring populates them from the takeoff orchestrator — separate
|
||||
task — AZ-591 follow-up infrastructure-prep"*). This is that task.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Step-11 Cycle-3 addendum: `_docs/03_implementation/run_tests_step11_report.md`
|
||||
(committed `e054a55`)
|
||||
- Jetson tier-2 e2e rerun #3 terminal output:
|
||||
`/Users/obezdienie001/.cursor/projects/Users-obezdienie001-dev-azaion-suite-gps-denied-onboard/terminals/110515.txt`
|
||||
(2026-05-18 06:01 UTC, log lines for `replay.compose_root.ready` +
|
||||
`airborne_bootstrap` raise).
|
||||
- `AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS` definition site:
|
||||
`src/gps_denied_onboard/runtime_root/airborne_bootstrap.py:92`.
|
||||
- Current incomplete `main()`: `src/gps_denied_onboard/runtime_root/__init__.py:636`.
|
||||
@@ -1,81 +0,0 @@
|
||||
# AZ-624 — Phase F: wire build_pre_constructed into runtime_root.main() + AC-1..AC-5 verification
|
||||
|
||||
**Task**: AZ-624_pre_constructed_phase_f_wire_main
|
||||
**Name**: AZ-618 Phase F: wire build_pre_constructed into runtime_root.main() + AC-1..AC-5 verification
|
||||
**Description**: Final / umbrella subtask of AZ-618. Wires `build_pre_constructed` into `runtime_root.main()` and lands the FULL AC suite from the AZ-618 umbrella (AC-1..AC-5), including the Jetson tier-2 verification.
|
||||
**Complexity**: 2 points
|
||||
**Dependencies**: AZ-619, AZ-620, AZ-621, AZ-622, AZ-623, AZ-625 (all phases must be in `done/` before this lands; AZ-625 was added 2026-05-19 when the `c5_isam2_graph_handle` ordering work was split out of AZ-623).
|
||||
**Component**: runtime_root (cross-cutting)
|
||||
**Tracker**: AZ-624
|
||||
**Epic**: AZ-602 (parent: AZ-618 umbrella)
|
||||
|
||||
## Outcome
|
||||
|
||||
- `runtime_root/__init__.py::main()` calls `register_airborne_strategies()` then `pre_constructed = build_pre_constructed(config)` then `compose_root(config, pre_constructed=pre_constructed)`.
|
||||
- Existing exception block extended to surface `AirborneBootstrapError` cleanly with `EXIT_GENERIC_FAILURE` and a clear operator-facing stderr message (rather than the current implicit traceback).
|
||||
- New unit test file `tests/unit/runtime_root/test_az618_pre_constructed.py` lands the FULL AC-1..AC-4 suite from the AZ-618 umbrella spec.
|
||||
- Jetson AC-5 verified via `scripts/run-tests-jetson.sh tests/e2e/replay/test_derkachi_1min.py`.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- 2-line addition in `main()` between `register_airborne_strategies()` and `compose_root(config)`.
|
||||
- Extension of the `except (ConfigurationError, StrategyNotLinkedError, RuntimeError)` block to surface `AirborneBootstrapError` distinctly.
|
||||
- New unit test `tests/unit/runtime_root/test_az618_pre_constructed.py` covering AC-1..AC-4 from the umbrella spec.
|
||||
- Jetson tier-2 run + evidence capture in batch report.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Any new builders (those live in AZ-619..AZ-623).
|
||||
- New `BUILD_*` env flags.
|
||||
- Operator binary's pre_constructed assembly (AZ-591 explicitly deferred that; this remains airborne-only).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
The full AZ-618 umbrella AC suite plus this subtask's verification gates:
|
||||
|
||||
**AC-618-1: `build_pre_constructed(config)` populates every required key**
|
||||
Given `register_airborne_strategies()` has run
|
||||
And a `Config` selecting every component's default strategy
|
||||
When `build_pre_constructed(config)` is called
|
||||
Then the returned dict contains exactly `set.union(*AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS.values())`
|
||||
And no key maps to `None`.
|
||||
|
||||
**AC-618-2: `compose_root(config, pre_constructed=...)` reaches takeoff**
|
||||
Given `register_airborne_strategies()` has run
|
||||
And `pre_constructed = build_pre_constructed(config)` for the default config
|
||||
When `compose_root(config, pre_constructed=pre_constructed)` runs
|
||||
Then it returns a `RuntimeRoot` whose `components` dict contains all 7 registered slots without raising `AirborneBootstrapError`.
|
||||
|
||||
**AC-618-3: `BUILD_*` flag mismatch surfaces a clear error**
|
||||
Given a config selecting a strategy whose `BUILD_*` flag is OFF
|
||||
When `build_pre_constructed(config)` is called
|
||||
Then it raises `AirborneBootstrapError` whose message names both the missing flag and the consuming component.
|
||||
|
||||
**AC-618-4: `runtime_root.main()` end-to-end exit codes**
|
||||
Given a minimal in-process Config selecting all-defaults
|
||||
When `main(config)` is called with every required `BUILD_*` flag ON
|
||||
Then it returns 0 (success).
|
||||
And when a single required infra dep is forcibly unavailable
|
||||
Then it returns `EXIT_GENERIC_FAILURE` (1) and stderr contains the `airborne_bootstrap:` prefix with the missing key + consuming component.
|
||||
|
||||
**AC-618-5 / AC-624.tier2: Jetson tier-2 e2e replay tests cross compose_root.ready**
|
||||
Given the AZ-624 changes are landed and AZ-619..AZ-623 are in `done/`
|
||||
When the Jetson tier-2 e2e harness is invoked (`scripts/run-tests-jetson.sh tests/e2e/replay/test_derkachi_1min.py`)
|
||||
Then each test crosses BOTH `replay.compose_root.ready` AND `replay.input.frame_emitted` log lines.
|
||||
And the batch report's `Tier-2 evidence:` field captures the terminal log path, JetPack version, and run timestamp per `_docs/02_document/tests/tier2-jetson-testing.md`.
|
||||
|
||||
**AC-624.local**: full Tier-1 pytest suite green after the wire-in.
|
||||
|
||||
## Constraints
|
||||
|
||||
- This subtask MUST NOT touch any per-component factory signature.
|
||||
- MUST NOT introduce new `BUILD_*` env flags.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Umbrella spec: `_docs/02_tasks/todo/AZ-618_airborne_bootstrap_pre_constructed.md` (AC-1..AC-5 narrative)
|
||||
- Tier-2 testing policy: `_docs/02_document/tests/tier2-jetson-testing.md`
|
||||
- Current incomplete `main()`: `src/gps_denied_onboard/runtime_root/__init__.py:636`
|
||||
- Existing replay-mode merge in `compose_root`: `src/gps_denied_onboard/runtime_root/__init__.py:419-484`
|
||||
Reference in New Issue
Block a user