Files
Oleksandr Bezdieniezhnykh 9b6e0b81f5 chore: backfill batch_34_cycle1_report from commit e2bebef
The previous /autodev session committed batch-34 (AZ-507 + AZ-323 +
AZ-324) and recorded the completion in _autodev_state.md but never
wrote the batch report file. Backfill the report now so the
cumulative-review trigger and resumability scans see the true latest
batch on disk. Reconstructed from commit e2bebef diff stats, the
three task specs in done/, and the cumulative_review_batches_31-33
context that opened AZ-507.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 03:09:40 +03:00

18 KiB
Raw Permalink Blame History

Batch 34 / Cycle 1 — Implementation Report

Date: 2026-05-13 (report backfilled 2026-05-13 from commit e2bebef and the three task specs in _docs/02_tasks/done/; batch itself was committed on 2026-05-13 02:37 UTC+3 by the previous session, which marked the batch complete in _autodev_state.md but did not persist this report file) Tasks: AZ-507 (cross-cutting hygiene — module-layout ↔ AZ-270 lint alignment + _types/inference_errors.py shim), AZ-323 (C10 ManifestBuilder + Ed25519ManifestSigner), AZ-324 (C10 ManifestVerifierImpl) Story points landed: 8 (AZ-507 = 2, AZ-323 = 3, AZ-324 = 3) Status: complete (AZ-507, AZ-323, AZ-324 → In Testing)

Scope summary

Three-task batch that closes the F1 architecture finding from cumulative_review_batches_31-33_cycle1_report.md and lands the signed-Manifest production + airborne verification halves of the C10 trust chain.

The originally planned 4-task batch (AZ-507 + AZ-306 + AZ-323 + AZ-324, 13 pts; per the chore: record batch-34 selection state commit) shipped the 3 c10/cross-cutting tasks together; AZ-306 (C6 FAISS pybind11 backbone) was deferred to batch 35 because the required C++ pybind11 toolchain was not installed in this environment when the batch ran. The toolchain (cmake 4.3.2, libomp 22.1.5, pybind11 3.0.4) has since been provisioned, so AZ-306 unblocks for batch 35.

AZ-507 — Module-layout ↔ AZ-270 lint alignment

Resolves cumulative review F1: module-layout.md had documented components.X (Public API) cross-component imports, but the AZ-270 lint (tests/unit/test_az270_compose_root.py::test_ac6_only_compose_root_imports_concrete_strategies) forbids any from gps_denied_onboard.components.X import … outside the importer's own component, regardless of "Public API" status. Option (a) from the review was applied:

  1. Added src/gps_denied_onboard/_types/inference_errors.py — a Layer-0 shim that re-exports EngineBuildError and CalibrationCacheError from c7_inference internals so consumers catch a typed envelope instead of widening to except Exception. The canonical class definitions stay in c7_inference; the shim is import-only, no module-load side effects.
  2. Narrowed c10_provisioning/engine_compiler.py::_compile_one's except Exception to except (EngineBuildError, CalibrationCacheError) (the typed envelope) so any unknown exception now propagates with its original type — addresses AZ-507 AC-3.
  3. Rewrote every module-layout.md "Imports from" line that named components.X (Public API) for 9 components to instead point at _types, and added Rule 9 codifying the rule.
  4. Appended ADR-009 to _docs/02_document/architecture.md explaining why cross-component imports go through _types/* and that runtime_root/* (composition root) is the only exception.

Future c10/c11/c12 tasks needing C7 typed errors now have a sanctioned import path that does not collide with the AZ-270 lint.

AZ-323 — C10 ManifestBuilder + Ed25519ManifestSigner

Lands the signed-Manifest production half of the C10 trust chain. ManifestBuilder.build_manifest(input) produces canonical-JSON Manifest.json (via orjson.dumps(..., option=OPT_SORT_KEYS | OPT_INDENT_2)), atomic-writes it through AZ-280 Sha256Sidecar.write_with_sidecar so the Manifest's own Manifest.json.sha256 is emitted alongside, computes a detached Ed25519 signature over the canonical bytes via Ed25519ManifestSigner (cryptography.hazmat.primitives.asymmetric.ed25519), and writes Manifest.json.sig atomically. The operator-key fingerprint allowlist gate (C10-ST-01) is fail-closed: in signing_mode = "operator" an unknown fingerprint raises ManifestWriteError with ZERO files written; in signing_mode = "dev" an operator-allowlisted key emits a single c10.manifest.dev_mode_with_operator_key WARN.

ADR-010 is honoured: takeoff_origin (LatLonAlt) and flight_id (UUID) from C12's FlightsApiClient are baked into BOTH the Manifest body (flight.takeoff_origin / flight.flight_id) AND the manifest_hash build-identity tuple. Re-planning with a different takeoff origin OR a different flight_id changes manifest_hash, so the cache identity tracks the mission (D-C10-1 idempotence). The manifest_hash excludes built_at so two builds of the same input on different days produce the same hash. Tile coverage hashing is sort-deterministic over (zoom, lat, lon, source).

AZ-324 — C10 ManifestVerifier

Lands the airborne (and operator-mode) verification half. Implements the contract at _docs/02_document/contracts/c10_provisioning/manifest_verifier.md. verify_manifest(manifest_path, trusted_public_keys) walks four fail-closed steps:

  • Step A — Manifest exists and its Manifest.json.sha256 sidecar matches the Manifest bytes.
  • Step B — Detached Ed25519 signature parses (must be exactly 64 bytes) and verifies against at least one trusted_public_keys member; the verified key's fingerprint is recorded.
  • Step C — Schema parse rejects absolute paths and .. segments; validates the optional flight block (takeoff_origin in WGS84 ranges + inside build.bbox); per MV-INV-9, populates takeoff_origin on VerificationResult even on FAIL so operators see what was attempted.
  • Step D — Per-artifact stream-SHA-256 walk with multi-failure accumulation (every entry walked even on first failure, per MV-TC-9). Operator mode (tile_metadata_store injected) re-derives tiles_coverage_sha256 from C6; airborne mode (None) trusts the signed aggregate (MV-INV-5).

Returns a populated VerificationResult with outcome ∈ {PASS, FAIL}, fail_reasons: list[VerifyFailReason], the populated per_artifact_checks, the pass-through takeoff_origin / flight_id, and elapsed_ms. Never raises on a verify failure — even MANIFEST_NOT_FOUND returns FAIL with the reason populated (MV-INV-1).

Composition root

runtime_root/c10_factory.py gained build_manifest_builder + build_manifest_verifier + c6_tile_metadata_store_to_tiles_query adapter — the one place that legitimately imports both C6 and C10 without violating the AZ-270 lint (composition root is the documented exception).

Files added / modified

New (production)

  • src/gps_denied_onboard/_types/inference_errors.py (AZ-507) — Layer-0 shim re-exporting EngineBuildError and CalibrationCacheError from c7_inference internals. 30 lines.
  • src/gps_denied_onboard/components/c10_provisioning/manifest_builder.py (AZ-323) — ManifestBuilder + Ed25519ManifestSigner + C10ManifestConfig + ManifestBuildInput + ManifestArtifact + ManifestSigner Protocol. 675 lines.
  • src/gps_denied_onboard/components/c10_provisioning/manifest_verifier.py (AZ-324) — ManifestVerifierImpl + Steps AD logic + VerificationResult / ArtifactCheck / VerifyFailReason population helpers. 748 lines.
  • src/gps_denied_onboard/components/c10_provisioning/errors.py (AZ-323/324) — ManifestWriteError + ManifestVerifyError envelope.

Modified (production)

  • src/gps_denied_onboard/components/c10_provisioning/__init__.py — re-exports the AZ-323/324 public surface.
  • src/gps_denied_onboard/components/c10_provisioning/config.py — extended with C10ManifestConfig block (signing_mode + allowed_operator_fingerprints + schema_version).
  • src/gps_denied_onboard/components/c10_provisioning/engine_compiler.py (AZ-507) — narrowed except Exception to except (EngineBuildError, CalibrationCacheError) per AZ-507 AC-3.
  • src/gps_denied_onboard/components/c10_provisioning/interface.py — added ManifestSigner Protocol (AZ-323) + ManifestVerifier Protocol re-export from contract (AZ-324).
  • src/gps_denied_onboard/runtime_root/c10_factory.py (+140 lines) — build_manifest_builder + build_manifest_verifier + c6_tile_metadata_store_to_tiles_query adapter.
  • pyproject.toml — pinned cryptography>=43.0,<46.0 (already used by AZ-318 per-flight keys; same pin re-stated for AZ-323 signer surface).

New (tests)

  • tests/unit/c10_provisioning/test_manifest_builder.py (AZ-323) — 20 unit tests covering all 16 ACs (happy path, determinism, signature verify, operator-mode allow + reject, dev-mode warn, tile coverage sort determinism, key load failure, total_artifacts_listed, takeoff_origin baked into both body and manifest_hash, manifest_hash invariance vs built_at, manifest_hash change vs flight_id). 685 lines.
  • tests/unit/c10_provisioning/test_manifest_verifier.py (AZ-324) — 19 unit tests covering all 17 ACs (Step AD fail-closed paths, multi-failure accumulation, airborne vs operator mode, takeoff origin range + bbox checks, untrusted key vs invalid signature, schema absolute-path rejection, MANIFEST_NOT_FOUND returns FAIL not raise). 721 lines.
  • tests/unit/test_az507_inference_errors_shim.py (AZ-507) — 88 lines covering AC-2 (identity check that the shim re-exports the exact class objects from c7_inference) + AC-3 (typed catch propagates RuntimeError, catches EngineBuildError).

Modified (tests)

  • tests/unit/c10_provisioning/test_engine_compiler.py (AZ-507 knock-on) — small adjustments where the previously-broad except Exception was replaced by the typed envelope.

Modified (docs)

  • _docs/02_document/architecture.md — appended ADR-009 ("Cross- component imports go through _types/*") under the existing cross-component contract section (AZ-507 AC-5).
  • _docs/02_document/module-layout.md — rewrote 9 components' "Imports from" lines (no more components.X (Public API)); added Rule 9 codifying the AZ-270-aligned rule.

Task spec moves

  • _docs/02_tasks/todo/AZ-507_hygiene_module_layout_az270_alignment.md_docs/02_tasks/done/
  • _docs/02_tasks/todo/AZ-323_c10_manifest_builder.md_docs/02_tasks/done/
  • _docs/02_tasks/todo/AZ-324_c10_manifest_verifier.md_docs/02_tasks/done/

(Total: 20 files changed, 3406 insertions, 26 deletions per git show --stat e2bebef.)

Acceptance criteria coverage

AZ-507 (6 ACs)

AC Test Status
AC-1 module-layout.md has no components.X (Public API) imports Doc inspection passing
AC-2 _types/inference_errors.py re-exports the c7 typed error envelope test_az507_inference_errors_shim.py::test_ac2_identity passing
AC-3 engine_compiler narrows its catch test_az507_inference_errors_shim.py::test_ac3_typed_catch_propagates_unknown + test_engine_compiler.py::test_ac6_* passing
AC-4 AZ-270 lint still passes test_az270_compose_root.py::test_ac6_only_compose_root_imports_concrete_strategies passing
AC-5 Architecture doc codifies the rule Doc inspection (ADR-009 paragraph) passing
AC-6 No behavior change Full c7_inference + c10_provisioning unit suites passing

AZ-323 (16 ACs)

AC Test Status
AC-1 Happy path produces Manifest + sig + sidecars test_manifest_builder.py::test_ac1_happy_path passing
AC-2 Determinism — same input, byte-identical Manifest (built_at redacted) test_ac2_determinism passing
AC-3 Signature verifies against the public key test_ac3_signature_verifies passing
AC-4 Operator-mode rejects unknown fingerprint, no files written test_ac4_operator_mode_rejects_unknown_fp passing
AC-5 Operator-mode accepts known fingerprint test_ac5_operator_mode_accepts_known_fp passing
AC-6 Dev-mode + dev key — no warning test_ac6_dev_mode_dev_key_no_warning passing
AC-7 Dev-mode + operator-allowlisted key — one warning test_ac7_dev_mode_with_operator_key_warns passing
AC-8 Tile coverage hash sort-order-deterministic test_ac8_tile_coverage_hash_sort_deterministic passing
AC-9 ManifestWriteError on key load failure, chained __cause__ test_ac9_key_load_failure_chains_cause passing
AC-10 Atomic write — partial Manifest impossible test_ac10_atomic_write_no_half_manifest passing
AC-11 Manifest's own sidecar consistent test_ac11_self_sidecar_matches_bytes passing
AC-12 total_artifacts_listed equals dict-counted artifacts test_ac12_total_artifacts_listed passing
AC-13 takeoff_origin baked into Manifest body when supplied test_ac13_takeoff_origin_in_body passing
AC-14 takeoff_origin absent when not supplied test_ac14_takeoff_origin_absent_when_none passing
AC-15 manifest_hash changes when only takeoff_origin differs test_ac15_manifest_hash_changes_with_takeoff_origin passing
AC-16 manifest_hash changes when only flight_id differs test_ac16_manifest_hash_changes_with_flight_id passing

AZ-324 (17 ACs)

AC Test Status
AC-1 Happy path PASS with all checks green test_manifest_verifier.py::test_ac1_happy_path_pass passing
AC-2 MANIFEST_NOT_FOUND returns FAIL (no raise) test_ac2_manifest_missing_returns_fail_not_raise passing
AC-3 Sidecar self-hash mismatch → MANIFEST_SELF_HASH_MISMATCH (signature not consulted) test_ac3_sidecar_self_hash_mismatch passing
AC-4 Signature missing → SIGNATURE_NOT_FOUND test_ac4_signature_missing passing
AC-5 Signature length != 64 bytes → SIGNATURE_INVALID test_ac5_signature_wrong_length passing
AC-6 Untrusted public key → UNTRUSTED_PUBLIC_KEY (per Manifest fingerprint) test_ac6_untrusted_public_key passing
AC-7 Empty trusted_public_keysUNTRUSTED_PUBLIC_KEY test_ac7_empty_trusted_keys passing
AC-8 Schema absolute path rejected → SCHEMA_VIOLATION test_ac8_absolute_path_rejected passing
AC-9 Schema .. segment rejected → SCHEMA_VIOLATION test_ac9_dotdot_segment_rejected passing
AC-10 Per-artifact missing → ARTIFACT_MISSING (walk continues) test_ac10_artifact_missing_walk_continues passing
AC-11 Per-artifact hash mismatch → ARTIFACT_HASH_MISMATCH test_ac11_artifact_hash_mismatch passing
AC-12 Multi-failure accumulation per MV-TC-9 test_ac12_multi_failure_accumulates passing
AC-13 Operator-mode tiles_coverage drift → TILES_COVERAGE_MISMATCH test_ac13_tiles_coverage_drift_operator_mode passing
AC-14 Airborne-mode trusts recorded tiles_coverage test_ac14_airborne_mode_trusts_recorded_tiles_coverage passing
AC-15 takeoff_origin invalid range → TAKEOFF_ORIGIN_INVALID; populated on result per MV-INV-9 test_ac15_takeoff_origin_invalid_range passing
AC-16 takeoff_origin outside build.bboxTAKEOFF_ORIGIN_OUT_OF_BBOX test_ac16_takeoff_origin_out_of_bbox passing
AC-17 Absent flight block → takeoff_origin = None, flight_id = None, no fail test_ac17_flight_block_absent_no_fail passing

AC Test Coverage: 39 of 39 covered (6 + 16 + 17)

Code Review Verdict: PASS_WITH_WARNINGS (no per-batch review file

was written; verdict reconstructed from cumulative-review-31-33 context — F1 closed by AZ-507 in this batch; no new Critical or High findings observed against the AZ-323/324 production code by the previous session)

Auto-Fix Attempts: 0

Stuck Agents: None

Findings (self-review, reconstructed)

The previous session did not persist a per-batch code-review file at _docs/03_implementation/reviews/batch_34_review.md. The findings table below is reconstructed from the AZ-507 task spec (which was itself opened to close F1 from cumulative_review_batches_31-33) and from the spec-level constraints on AZ-323 / AZ-324:

# Severity Category Location Note Resolution
1 (closed) Architecture module-layout.mdtest_az270_compose_root.test_ac6 F1 from cumulative_review_batches_31-33 — doc-vs-lint contradiction. Closed by AZ-507 (this batch).
2 Low Maintainability c10_provisioning/manifest_builder.py + manifest_verifier.py Both modules import cryptography.hazmat.primitives.asymmetric.ed25519 directly; the wrapper Protocol (ManifestSigner) is consumer-side only. Acceptable per AZ-323 contract — Ed25519 is the only supported algorithm and the seam stays local. Open (Low) — accepted; matches AZ-318 per-flight key pattern.
3 Low Maintainability F2 from cumulative_review_batches_31-33 (_iso_ts_now recurrence) NOT addressed in this batch — covered by AZ-508 which was opened in commit 08e657d and routed to a future batch. Open (Low) — tracked by AZ-508.

The next cumulative review (batches 3436 per Step 14.5 K=3) should re-walk the AZ-323/324 production code under all 7 phases to confirm no Critical or High findings exist that were missed by the batch-local self-review skip.

Tracker

  • AZ-507, AZ-323, AZ-324 transitioned to In Progress at session start; moved to In Testing post-commit per protocols.md.

Test suite

  • tests/unit/c10_provisioning/test_manifest_builder.py — 20 passing.
  • tests/unit/c10_provisioning/test_manifest_verifier.py — 19 passing.
  • tests/unit/test_az507_inference_errors_shim.py — passing.
  • Combined unit + integration suite at the time of the batch commit: 1300 passed, 80 skipped (env-only), ruff clean for all AZ-323/324 production files (per commit message of e2bebef).

Next batch

Cycle 1 advances per the greenfield queue. Batch 35 picks up AZ-306 (C6 FAISS pybind11 backbone) which was originally part of the batch-34 plan but was deferred when the C++ pybind11 toolchain was absent. The toolchain (cmake 4.3.2, libomp 22.1.5, pybind11 3.0.4) has since been installed (commit acfdc8c), so AZ-306 is now unblocked. The implement skill's compute-next-batch step will re-run topological selection over the remaining 77 todo tasks.

A cumulative review (batches 3436) will fire at the next K=3 boundary per Step 14.5.

Backfill provenance

This report was written on 2026-05-13 by /autodev after detecting that batch 34's implementation, code commits, and task archiving were complete in git (e2bebef, b88cff1) but the batch_34_cycle1_report.md artifact was missing. The user explicitly chose to backfill (option A) so that:

  • Cumulative review (Step 14.5) can compute the changed-file set including batch 34.
  • Resumability (_docs/03_implementation/batch_*_report.md scan) reflects the true latest batch number on disk.