# 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
