# Batch Report — Cycle 1 · Batch 2 **Tasks shipped**: AZ-266, AZ-269, AZ-277, AZ-280 **Date**: 2026-05-11 **Branch**: dev **Review verdict**: PASS_WITH_WARNINGS — see `reviews/batch_02_review.md` ## Tasks | ID | Title | Owner Layer | Outcome | |----|-------|-------------|---------| | AZ-266 | Shared Logging Module | cross-cutting / `logging/` | Implemented — schema-compliant JSON formatter, level normalisation, handler topology guard, format-error fallback | | AZ-269 | Config Loader | cross-cutting / `config/` | Implemented — env > YAML > defaults precedence, frozen `Config`, missing-var fail-fast with pointer, component-block registry | | AZ-277 | SE3Utils Helper | Layer 1 / `helpers/se3_utils.py` | Implemented — GTSAM-backed `matrix_to_se3` / `se3_to_matrix` / `exp_map` / `log_map` / `adjoint` with strict orthogonality + dtype contract | | AZ-280 | Sha256Sidecar Helper | Layer 1 / `helpers/sha256_sidecar.py` | Implemented — `atomicwrites`-backed `write_atomic` + independent `verify` + order-deterministic `aggregate_hash` | ## Files Changed ``` Modified: pyproject.toml (+4 lines : gtsam, atomicwrites pins) src/gps_denied_onboard/config/__init__.py (re-exports) src/gps_denied_onboard/config/loader.py (load_config impl) src/gps_denied_onboard/config/schema.py (frozen dataclasses + component registry) src/gps_denied_onboard/helpers/__init__.py (re-exports) src/gps_denied_onboard/helpers/se3_utils.py (SE3 primitives) src/gps_denied_onboard/helpers/sha256_sidecar.py (atomic write + sidecar verify) src/gps_denied_onboard/logging/__init__.py (re-exports) src/gps_denied_onboard/logging/structured.py (schema-compliant formatter) tests/unit/test_logging_smoke.py (updated to nested kv schema) Added: tests/unit/test_az266_logging_schema.py tests/unit/test_az269_config_loader.py tests/unit/test_az277_se3_utils.py tests/unit/test_az280_sha256_sidecar.py _docs/03_implementation/reviews/batch_02_review.md ``` ## Test Results ``` $ pytest tests/unit -q --timeout=30 139 passed, 2 skipped in 5.27s ``` Skips are environment-gated (cmake configure step, actionlint), both verified in CI. ### AC Coverage | Task | ACs declared | ACs covered locally | Tier-2 deferred | |------|--------------|---------------------|-----------------| | AZ-266 | 5 + 2 NFR | 5 ACs + NFR-reliability | NFR-perf (p99 ≤ 0.2 ms on Jetson) | | AZ-269 | 6 + 2 NFR | 6 ACs + NFR-reliability | NFR-perf (cold load ≤ 250 ms on Jetson) | | AZ-277 | 9 + 2 NFR | 9 ACs + NFR-* via determinism + AST scan | none | | AZ-280 | 9 + 2 NFR | 9 ACs + NFR-perf-spot-check + NFR-reliability | none | Tier-2 perf budgets need hardware to verify against; the unit suite does not synthesise a Jetson budget locally. AZ-428..AZ-431 own the Tier-2 perf scenarios; the batch-2 modules are wired so those tasks plug in without further code changes here. ## Dependency Pins This batch amended `pyproject.toml` with two new runtime pins required by AZ-277 and AZ-280 contracts: - `gtsam>=4.2,<5.0` — SE(3) backend per AZ-277 contract - `atomicwrites>=1.4,<2.0` — atomic-replace backend per AZ-280 contract Both names appear verbatim in the upstream contract documents and were the named-backend constraint, not an arbitrary choice. AZ-263 intentionally left these unpinned; the pins are added by the first batch that needs them. ## Architecture Compliance - `helpers/se3_utils.py` and `helpers/sha256_sidecar.py` import only stdlib + named externals (numpy / gtsam / atomicwrites). No `gps_denied_onboard.components.*` imports — enforced by AST scan in both helper test files. - `logging/structured.py` imports stdlib only (optional `systemd-python` inside the Tier-2 handler factory, lazy). - `config/loader.py` imports stdlib + `pyyaml`. The `register_component_block` function is the only authorised path for a component to contribute a config block, satisfying AZ-269 § Risks Mitigation Risk 1. No new circular imports. Verified by running the test suite (any cycle would fail collection). ## Review Findings Summary Verdict: **PASS_WITH_WARNINGS**. Four Low-severity findings: 1. AC-2 test pivots on `fdr.queue_size` rather than `log.level` — clean test design choice given `LOG_LEVEL` is required-env. 2. NFR-perf microbenchmarks deferred to Tier-2 perf suite. 3. Tier-2 journald handler unverified on macOS dev; runs in Jetson CI. 4. `pyproject.toml` dep amendment — by the consumer batch as designed. Full details in `reviews/batch_02_review.md`. ## Tracker Transitions - AZ-266: To Do → In Progress → (batch close) In Testing - AZ-269: To Do → In Progress → (batch close) In Testing - AZ-277: To Do → In Progress → (batch close) In Testing - AZ-280: To Do → In Progress → (batch close) In Testing