Commit Graph

197 Commits

Author SHA1 Message Date
Oleksandr Bezdieniezhnykh 811ddc8aa7 chore: bump opencv-pin leftover replay timestamp
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-16 05:47:21 +03:00
Oleksandr Bezdieniezhnykh 2b19b8b90b [AZ-558] Route C8 outbound encoder bytes through MavlinkTransport seam
All FC adapter outbound MAVLink bytes now go through the AZ-401
MavlinkTransport seam (NoopMavlinkTransport in replay,
SerialMavlinkTransport in live). New helpers in
_outbound_mavlink_payloads.py extract encode/pack/seq-bump so the four
AP _send sites and the iNav statustext _send site become
encode -> pack -> transport.write. TlogReplayFcAdapter emits real
AP-shape MAVLink bytes through the injected NoopMavlinkTransport,
satisfying replay protocol Invariant 5 and unblocking AZ-401 AC-9.

Closes AZ-558. Also unskips AZ-401 AC-9 and AZ-404 AC-4b. Live wire
output remains byte-identical (proven via two-instance MAVLink
byte-equivalence tests). AST scan asserts no .mav.<name>_send( calls
remain in the retrofit set (AP / iNav / tlog adapters).

Out of scope (logged in review): GCS adapter retrofit; airborne live
strategy registration that would activate the SerialMavlinkTransport
factory injection path.

Tests: 2110 passed, 92 environmental skips, 1 unrelated pre-existing
macOS cold-start flake deselected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-16 05:33:56 +03:00
Oleksandr Bezdieniezhnykh d7e6b0959e [AZ-404] [AZ-389] [AZ-559] E2E replay test (Derkachi 60s) + AZ-389 cleanup
Batch 63 of /autodev replay slice. Adds the AZ-404 E2E test harness
against the Derkachi fixture and resolves the AZ-389 dependency
phantom (closing AZ-559 Won't Fix).

E2E test (AZ-404)
- tests/e2e/replay/_tlog_synth.py: deterministic CSV->tlog generator
  (the original Derkachi tlog is not in repo; data_imu.csv is its
  export, so we round-trip the CSV through pymavlink). Verified:
  SCALED_IMU2 + ATTITUDE + GPS_RAW_INT + HEARTBEAT round-trip cleanly
  through mavutil.mavlink_connection.
- tests/e2e/replay/_helpers.py: parse_jsonl, l2_horizontal_m
  (haversine), match_percentage, CapturingMavlinkTransport (ready
  for AZ-558 unblock), GroundTruthRow + load_ground_truth_csv.
- tests/e2e/replay/conftest.py: derkachi_replay_inputs (session
  scope), replay_runner (subprocess fixture per AZ-402 CLI),
  operator_pre_flight_setup placeholder.
- tests/e2e/replay/test_derkachi_1min.py: 9 tests covering AC-1..AC-8
  with AC-7 skip-gate self-check + AC-4a mode-agnosticism AST scan
  (passes unconditionally, confirms ADR-011 holding).
- tests/e2e/replay/test_helpers.py: 14 unit tests covering AC-9
  helper L2 correctness + match_percentage + parse_jsonl +
  CapturingMavlinkTransport (all unconditional).
- tests/e2e/replay/README.md: AC matrix, fixture state, runtime
  budget, failure cookbook (AC-10).

AC matrix
- AC-1, AC-2, AC-5, AC-6 implemented and Tier-1 gated on
  RUN_REPLAY_E2E=1.
- AC-3 (<=100m for 80%) xfail until real Topotek KHP20S30
  calibration ships (camera_info.md states intrinsics are unknown).
- AC-4a (mode-agnosticism AST scan) PASSES unconditionally.
- AC-4b (encoder byte-equality) skip until AZ-558 routes C8 bytes
  through MavlinkTransport.
- AC-7 (skip-gate self-check) PASSES unconditionally.
- AC-8 (operator workflow rehearsal) skip until D-PROJ-2
  mock-suite-sat-service implements tile-fetch + index-build
  endpoints.
- AC-9 (helper L2 correctness) 14 PASSES unconditionally.

AZ-389 housekeeping
- AZ-559 closed Won't Fix: investigation against
  c6_tile_cache/_types.py confirmed TileSource.ONBOARD_INGEST +
  TileMetadata.quality_metadata + write_tile's FreshnessRejectionError
  already cover the mid-flight ingest semantic. The "missing API"
  was a spec-vs-impl naming mismatch.
- AZ-389 spec rewritten to consume the existing write_tile API +
  catch FreshnessRejectionError per AC-NEW-3 opportunistic emission.
- _dependencies_table.md reverted: AZ-389 deps -> AZ-303 (was
  AZ-559 in the previous commit on this branch); total 150 / 497
  pts.

Tests
- Full regression: 2099 passed (+14 new e2e/replay), 94 skipped
  (incl. 8 e2e/replay heavy-tier + documented blocker skips), 3
  perf-microbench flakes deselected (test_cli_cold_start_under_2s,
  test_cold_start_under_500ms_p99, test_nfr_perf_sign_microbench;
  all pass in isolation - pre-existing under-load flakes on dev
  macOS).

Reviews
- _docs/03_implementation/reviews/batch_63_review.md: code review
  PASS_WITH_WARNINGS (3 documented spec-gap deferrals: AC-3, AC-4b,
  AC-8).
- _docs/03_implementation/cumulative_review_batches_61-63_cycle1_report.md:
  cumulative review PASS_WITH_WARNINGS. Action items: prioritise
  AZ-558 (closes AZ-401 AC-9 + AZ-404 AC-4b); consider 2pt hygiene
  PBI for Protocol-completeness AST scan to catch the AZ-389 /
  AZ-559 phantom-API pattern at task-prep time.

Architecture invariants observably holding
- ADR-011 (replay-as-configuration): AC-4a's AST scan over
  src/gps_denied_onboard/components/**/*.py finds zero violations -
  components branch on neither config.mode nor any synonym.
- Single composition root (replay protocol Invariant 11): AZ-402
  CLI dispatches to runtime_root.main(config); does not call
  compose_root directly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 21:41:39 +03:00
Oleksandr Bezdieniezhnykh 4f10fd230f [AZ-559] [AZ-389] docs: defer AZ-389 to AZ-559 (C6 mid-flight tile gap)
AZ-389's task spec assumed the existence of `tile_store.put_mid_flight_
candidate(MidFlightTileCandidate)` (in Excluded: "owned by AZ-303 / E-C6"),
but the current TileStore Protocol has only the four-method baseline
shipped under AZ-303 — there is no put_mid_flight_candidate, no
MidFlightTileCandidate DTO, and no MID_FLIGHT_INGEST TileSource enum value.

Filed AZ-559 as a 5pt task to close the C6 storage gap (Protocol method
+ DTO + enum + persistence + freshness/LRU integration + contract
update). Updated AZ-389 spec to depend on AZ-559 (replacing the stale
AZ-303 dep) with a Status: BLOCKED note. Updated the dependencies
table totals: 151 tasks / 502 complexity points.

This is the same dep-gap pattern surfaced for AZ-401 in batch 61
(missing AZ-400 transport-seam retrofit) — the autodev replay-track
sequence is exposing under-spec deliveries upstream. Tracker remains
the source of truth via the new AZ-559 issue + Blocks link.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 20:14:47 +03:00
Oleksandr Bezdieniezhnykh 2c31cc094f [AZ-402] Replay — gps-denied-replay console-script + shared main(config)
Implements the replay-mode CLI dispatcher per ADR-011 (replay-as-
configuration):

- src/gps_denied_onboard/cli/replay.py: argparse with all 6 required
  args (--video, --tlog, --output, --camera-calibration, --config,
  --mavlink-signing-key) plus --pace and --time-offset-ms; path
  validation, calibration JSON schema-validation, config mutation
  (mode='replay' + replay sub-block + signing-key hex on dev_static
  field), dispatch into runtime_root.main(config).
- runtime_root.main() now accepts an optional Config (additive,
  backward-compat). Adds dedicated catch for ReplayInputAdapterError
  mapping to EXIT_FDR_OPEN_FAILURE (2) so the CLI's exit-code matrix
  holds end-to-end (AC-9 + epic AZ-265 AC-8).
- Signing-key contents stored as hex; redacted in startup banner.
- Top-level except logs full traceback via logger.exception + stderr
  print and exits 1.

The CLI does NOT call compose_root directly — it builds a Config and
hands it to the shared airborne main, which calls compose_root, which
branches on config.mode (AZ-401 / replay protocol Invariant 11).

Tests: 22 unit tests covering AC-1..AC-10 + extras (signing-key
redaction, file-not-dir validation, dev_static propagation, unhandled
exception traceback). Full regression: 2085 passed (+22) green; no
new flaky tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 20:04:37 +03:00
Oleksandr Bezdieniezhnykh 17a0d074af [AZ-401] [AZ-400] Replay — compose_root replay-mode branch + transport seam
Wires the airborne composition root for replay-as-configuration (ADR-011):

- compose_root(config) branches on config.mode in {"live", "replay"}.
  Live behaviour is unchanged; replay builds ReplayInputAdapter,
  attaches JsonlReplaySink, and injects NoopMavlinkTransport.
- New private module runtime_root/_replay_branch.py holds the
  replay-only strategy graph + build-flag gate + calibration loader.
- Config gains Config.mode (Literal["live","replay"]) plus
  Config.replay sub-block with nested ReplayAutoSyncConfig that mirrors
  the AZ-405 AutoSyncConfig DTO; YAML loader + ENV map updated.

Absorbs the AZ-400 transport-seam retrofit that AZ-401 strictly
required but AZ-400 had not delivered:

- New MavlinkTransport Protocol (write/bytes_written/close).
- NoopMavlinkTransport (replay; build-flag gated, idempotent close,
  thread-safe byte counter).
- SerialMavlinkTransport (live, no-op restructure of existing pymavlink
  byte path; encoder retrofit to actually USE it is the AZ-558
  follow-up).

AZ-401 AC-9 (NoopMavlinkTransport.bytes_written > 0 after C8 encoders
run) is BLOCKED on AZ-558 — the encoder routing retrofit is out of
the AZ-401 task envelope (FORBIDDEN files: pymavlink_ardupilot_adapter,
msp2_inav_adapter). AZ-558 spec, batch_61_review.md, and the test's
@pytest.mark.skip rationale all carry the deferral reason.

Tests: 22 compose_root replay-branch tests + 17 transport tests.
Full regression: 2063 passed, 86 environment-skips, 1 documented
skip (AC-9 / AZ-558), 1 pre-existing flaky perf test deselected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 11:55:33 +03:00
Oleksandr Bezdieniezhnykh 8149083cac [AZ-405] Replay — replay_input/ coordinator + IMU take-off auto-sync
Adds the Layer-4 cross-cutting `replay_input/` module per ADR-011:
ReplayInputAdapter converges (video, tlog) into the standard
FrameSource + FcAdapter + Clock surfaces the airborne composition
root consumes. Owns time-alignment between video frames and tlog
IMU/attitude ticks (manual via --time-offset-ms or auto via the
AZ-405 IMU-take-off detector + Farneback motion-onset detector).

Auto-sync algorithm (auto_sync.py):
- Tlog take-off detector: sustained vertical-accel excess > 0.5 g for
  >= 0.5 s + sustained attitude-rate magnitude > 1 rad/s.
- Video motion-onset detector: dense Farneback flow magnitude > 1.5 px
  sustained >= 0.5 s (deterministic per AC-10).
- compute_offset combines the two; confidence = min(tlog, video).
- validate_offset_or_fail implements the AC-9 95 % frame-window match
  validator with configurable threshold + window.

ReplayInputAdapter.open() ordering (AC-13):
1. Load tlog samples + fail-fast on missing RAW_IMU/SCALED_IMU2 or
   ATTITUDE BEFORE any video read.
2. Resolve offset (auto-sync OR manual override; manual bypasses the
   detectors entirely per AC-8).
3. Run AC-9 validator on resolved offset; raise auto-sync hard-fail
   for AC-7 (CLI exit 2 mapping).
4. Build single Clock instance per pace (TlogDerived/ASAP, Wall/REAL).
5. Construct VideoFileFrameSource and TlogReplayFcAdapter with the
   resolved offset baked in (replay protocol Invariant 8).

Structured log + FDR records on auto-sync detected / low-confidence /
AC-8 hard-fail kinds. Idempotent close (AC-12).

Tests: 25 unit tests across tests/unit/replay_input/ covering all 13
ACs (kernel-level synthetic fixtures for AC-1..AC-10; coordinator-
level OpenCV synthetic videos + faked pymavlink for AC-6..AC-13).

Contract update: replay_protocol.md v2.0.0 added fdr_client to the
ReplayInputAdapter __init__ signature (was missing in the prose; the
task spec already listed it in the allowed-imports section).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 09:50:51 +03:00
Oleksandr Bezdieniezhnykh f9b4241d3a [AZ-403] Remove process leftover after Jira cancellation replay
Replayed deferred tracker write: AZ-403 transitioned to Done with
cancellation comment per ADR-011 (replay-as-configuration).
Resolution auto-set to Done by AZ workflow (no Cancelled status
exposed in this Jira instance; resolution edit rejected by API).
Cancellation reason recorded in the Jira comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 09:12:59 +03:00
Oleksandr Bezdieniezhnykh 5adf3dd04f [AZ-265] Replay as configuration of airborne binary (ADR-011)
Re-design replay mode per user direction: replay is no longer a fourth
Docker image with a reduced component set, but a `config.mode = "replay"`
branch of the single airborne binary. The pre-flight workflow (route in
suite UI -> C12 tile download via real satellite-provider -> C10
manifest+engines build) is identical between live and replay; only three
strategies swap at compose time:

  FrameSource:      Live <-> Video
  FcAdapter:        Pymavlink/MSP2 <-> TlogReplay
  MavlinkTransport: Serial <-> Noop

The C8 outbound MAVLink encoders run unchanged in both modes; their
bytes hit `NoopMavlinkTransport` in replay and disappear. A new
`JsonlReplaySink` taps C5's `EstimatorOutput` stream so the parent-suite
UI sees per-tick coordinates by tailing `results.jsonl`. MAVLink 2.0
signing key remains mandatory (operator supplies a dummy file).

A new `replay_input/` Layer-4 cross-cutting coordinator owns
`(video, tlog) -> (FrameSource, FcAdapter, Clock)` convergence; the
composition root sees only standard interfaces past `.open()`.

Docs:
- architecture.md: new ADR-011 with full rationale; ADR-002 binary
  narrative updated.
- contracts/replay/replay_protocol.md: bumped to v2.0.0; 12 invariants
  (notably mode-agnosticism + encoder byte-equality + signing key
  mandatory + real C6 cache in replay).
- module-layout.md: Build-Time Exclusion Map dropped from 4 to 3 binary
  columns; replay-mode `BUILD_*` flags default ON in airborne;
  `shared/replay_input` cross-cutting entry added.
- epics.md: E-DEMO-REPLAY scope reframed; story points 27-32 -> 19-24.

Task respecs:
- AZ-401: shrunk 3 -> 2 pts; `compose_root` mode branch + JSONL sink +
  NoopMavlinkTransport wiring; legacy `compose_replay` export deleted.
- AZ-402: console-script wrapper that mutates `config.mode = "replay"`
  and dispatches into the shared airborne main; `--mavlink-signing-key`
  mandatory.
- AZ-403: CANCELLED. Moved to done/ with banner; Jira transition deferred
  via `_docs/_process_leftovers/2026-05-14_az_403_cancellation_pending_tracker.md`.
- AZ-404: AC-4 reworded as mode-agnosticism AST scan + encoder
  byte-equality test; new AC-8 operator-workflow rehearsal.
- AZ-405: also owns the `replay_input/` module + `ReplayInputAdapter`.

_dependencies_table.md updated: AZ-401 gains AZ-405 dep; AZ-404 drops
AZ-403 dep; AZ-403 row marked CANCELLED.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 09:01:04 +03:00
Oleksandr Bezdieniezhnykh fa3742d582 [AZ-399] [AZ-400] C8 TlogReplayFcAdapter + ReplaySink + JsonlReplaySink
Opens E-DEMO-REPLAY (AZ-265): the two C8 strategies that let the
upcoming compose_replay (AZ-401) and gps-denied-replay CLI (AZ-402)
run the production C1-C5 pipeline against a recorded (.tlog, video)
pair without touching live FC I/O.

AZ-400 lands the contract ReplaySink Protocol (emit + close per
replay_protocol.md v1.0.0) and JsonlReplaySink: orjson-serialised
JSONL, fsync-on-close, build-flag gated (BUILD_REPLAY_SINK_JSONL),
double-close idempotent, FDR mirror on open/close. The drifted
AZ-390 stub in interface.py is removed; the canonical Protocol now
lives in replay_sink.py per module-layout.md and is re-exported via
__init__.py. AZ-390 conformance test widened.

AZ-399 lands TlogReplayFcAdapter: full FcAdapter Protocol surface,
build-flag gated (BUILD_TLOG_REPLAY_ADAPTER), pymavlink stream-parse
with bounded pre-scan + fail-fast on missing required messages
(R-DEMO-3), dedicated decode thread feeding the existing AZ-391
SubscriptionBus. Outbound surface raises FcEmitError per Invariant 5;
request_source_set_switch raises SourceSetSwitchNotSupportedError.
Pacing honours Invariant 6 via Clock.sleep_until_ns. time_offset_ms
shifts every emitted received_at per Invariant 8. Non-monotonic
timestamps raise FcOpenError.

Test coverage: 188 c8_fc_adapter tests pass; 1 skipped (AZ-399 AC-1
500 MB tlog RSS bound, deferred to AZ-404 e2e behind RUN_REPLAY_E2E).
Code review: PASS_WITH_WARNINGS — 1 Medium (mapping logic duplicates
AZ-391 live decoder; intentional today, four behavioural deltas
documented), 2 Low.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 05:33:20 +03:00
Oleksandr Bezdieniezhnykh 4eac24f37a [AZ-358] [AZ-361] C4 OpenCVGtsamPoseEstimator + Jacobian thermal hybrid
Implement the single production-default C4 PoseEstimator strategy.

AZ-358 — Marginals path: OpenCV solvePnPRansac (SOLVEPNP_IPPE) on
best-candidate inliers, PriorFactorPose3 with Jacobian-derived initial
covariance, flushed into C5's iSAM2 graph via the widened
ISam2GraphHandle.update(graph, values, None) (Option B). Posterior
covariance from compute_marginals().marginalCovariance(pose_key) with
SPD-defensive Cholesky check. Tile pixel -> ENU world conversion via
the shared WgsConverter + a configurable tile_size_px. Two spec
deviations now documented in the AZ-358 task file: PriorFactorPose3
over GenericProjectionFactorCal3DS2 (avoids unbounded landmark
variables; same Fisher information on the pose marginal) and explicit
(graph, values, timestamps) update args (aligns with C5's impl).

AZ-361 — Jacobian + thermal hybrid: per-frame dispatch on
thermal_state.thermal_throttle_active selects the cv2.projectPoints-
derived 6x6 information matrix (with ridge regularisation) as the
emitted covariance. Skips the iSAM2 factor add under throttle
(Invariant 12). Emits CovarianceDegradedWarning via warnings.warn
(never raised); paired WARN log + FDR record rate-limited per
covariance_degraded_warn_window_ns (default 60 s) via an injected
monotonic Clock. Supersedes the AZ-358 NotImplementedError stub.

Widens ISam2GraphHandle from get_pose_key only to all five C4-facing
methods (add_factor, update, compute_marginals, last_anchor_age_ms);
C5's existing ISam2GraphHandleImpl already satisfies the superset, so
no C5 source change this batch. Threads fdr_client + clock through
pose_factory composition.

Registers two new FDR payload kinds: pose.frame_done (per-call
telemetry; both success and PnpFailureError paths) and
pose.covariance_degraded (per-window throttle exposure).

Tests: 21 new (AZ-358 AC-1..11 + AZ-361 AC-1..10/12/13; AZ-361 AC-11
RMSE-ratio informational per spec, not asserted). Updates 2 existing
test files for Protocol widening and the FDR-schema round trip.

Code review verdict: PASS_WITH_WARNINGS (5 findings: Medium x2,
Low x3; none blocking). Full suite: 1958 passed, 1 unrelated
host-dependent perf failure (c12 CLI cold-start, pre-existing).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 05:01:14 +03:00
Oleksandr Bezdieniezhnykh 360aece7a6 [AZ-528] [AZ-335] [AZ-345..AZ-347] [AZ-349] Cumulative review 55-57
Cumulative code review for the C3 / C3.5 cross-domain matching
pipeline going live (B55 facade-spine consolidation, B56 warm-start
+ F8 reboot recovery, B57 three concrete matchers + AdHoP refiner).
Verdict PASS_WITH_WARNINGS — three Low findings, no Critical / High
/ Architecture issues. Cumulative-52-54 Medium F1 (c1_vio
facade-spine duplication) closed by AZ-528 with regression guards.

State: last_completed_batch=57, last_cumulative_review=batches_55-57,
current_batch=58.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 04:12:47 +03:00
Oleksandr Bezdieniezhnykh abe8c5cd2c [AZ-345] [AZ-346] [AZ-347] [AZ-349] Archive batch 57 task specs
Move completed task specs from _docs/02_tasks/todo/ to
_docs/02_tasks/done/ now that the four tickets are In Testing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 04:10:34 +03:00
Oleksandr Bezdieniezhnykh a1185d0a28 [AZ-345] [AZ-346] [AZ-347] [AZ-349] C3 matchers + C3.5 AdHoP refiner
Implement the three concrete C3 CrossDomainMatcher strategies plus the
C3.5 production-default AdHoPRefiner.

C3 (AZ-345/346/347):
- DiskLightGlueMatcher + AlikedLightGlueMatcher share a single shared
  _pipeline.run_lightglue_pipeline orchestrator (decode -> query
  extract -> per-candidate loop -> RANSAC sort -> health update ->
  FDR emit) so the only per-backbone delta is the keypoint+descriptor
  extractor closure. ALIKED adds a create-time engine output-schema
  probe (AC-special-1).
- XFeatMatcher owns its own per-candidate loop (single forward fuses
  extraction + matching); it re-uses the shared FDR emission helpers
  to keep telemetry byte-identical across strategies. lightglue_runtime
  parameter accepted by factory but discarded (AC-special-1).
- All three consume the shared LightGlueRuntime / RansacFilter /
  RollingHealthWindow helpers; no helper forks. InferenceRuntimeCut
  consumer-side Protocol added per AZ-507.

C3.5 (AZ-349):
- AdHoPRefiner implements the <= conditional gate, runs the OrthoLoC
  AdHoP TRT engine over best-candidate correspondences, re-runs RANSAC
  on the perspective-preconditioned set, and emits an enriched
  MatchResult with refinement_label="adhop".
- Invariant 4 passthrough fall-through: any RefinerBackboneError (TRT
  failure, OOM, NaN, bad shape) is caught, logged ERROR, FDR-emitted
  with error: true, and converted to passthrough that still counts
  against the rolling invocation-rate window. MemoryError and other
  non-listed exceptions propagate by design (AC-5 closed-set
  semantics).
- Rolling 60-s invocation-rate window + rate-limited WARN log
  (configurable via ratelimited_warn_window_ns; default 60 s).

Shared changes:
- C3MatcherConfig + C3_5RefinerConfig extended with the new
  weights/threshold/window fields.
- matcher_factory + refiner_factory optionally forward clock +
  fdr_client to the strategy's create(); backward-compatible.
- fdr_client.records registers five new kinds: matcher.frame_done,
  matcher.backbone_error, matcher.insufficient_inliers,
  matcher.all_failed, refiner.frame_done.

Tests: 66 new (43 C3 parametrised + 23 AdHoP) covering 47/47 ACs;
focused suite green; full project test suite green except for one
pre-existing flaky CLI cold-start timing test unrelated to this batch.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 04:09:22 +03:00
Oleksandr Bezdieniezhnykh 06f655d8fb [AZ-335] C1 warm-start hint persistence + F8 reboot recovery wiring
Adds JsonSidecarWarmStartHintStore (atomic JSON + SHA-256 sidecar via
AZ-280) inside c1_vio, plus the cross-strategy WarmStartWiredStrategy
wrapper + prime_warm_start_from_disk / prime_warm_start_from_fc hooks
at runtime_root. AC-7 post-reset covariance inflation and AC-8 "no
fake confidence" baseline floor are enforced at the wiring layer so
no strategy module needed edits. Adds three c1_vio config fields
(warm_start_store_dir, warm_start_save_period_frames,
post_reset_covariance_inflation_factor) and registers the new FDR
kind vio.warm_start. 34 unit tests cover all 10 ACs + 3 NFRs.

Verdict PASS_WITH_WARNINGS — see
_docs/03_implementation/reviews/batch_56_review.md for the four
non-blocking documentation findings (F1 cold-start log kind shorthand,
F2 strategy-frame pose semantics, F3 dev-hardware perf smoke, F4
runtime_root importing c1-internal _facade_spine for shared FDR
conventions).

Closes AZ-335; depends on AZ-528 (batch 55).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 03:30:46 +03:00
Oleksandr Bezdieniezhnykh f12789ebf0 [AZ-528] Consolidate c1_vio strategy facade orchestration spine
Replace 3-way byte-equivalent orchestration-spine duplication across
okvis2.py / vins_mono.py / klt_ransac.py with a single c1-internal
helper at components/c1_vio/_facade_spine.py. Closes cumulative
review batches 52-54 Finding F1. No behaviour change — all existing
AZ-332 / AZ-333 / AZ-334 AC tests pass unmodified (114 c1_vio tests
green, 237 with adjacent regression suite).

The helper exposes 5 stateless free functions (now_iso, bias_norm,
se3_from_4x4, frame_ts_ns, frame_image) and a FacadeSpine mixin
class providing _classify_state / _tick_lost / _emit_transition.
Concrete strategies inherit the mixin and set spine-required
instance attributes in __init__. Mirrors the AZ-527 precedent for
c2_vpr-side _assert_engine_output_dim consolidation.

New test file test_az528_facade_spine.py covers AC-1..AC-8 with 19
tests, including an AST regression guard that prevents future
re-introduction of the consolidated free functions in any strategy
module, plus a Risk-1 static check that every strategy's __init__
assigns every spine-required attribute.

Archive AZ-528 task spec to done/, bump autodev state to batch 56.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 03:03:16 +03:00
Oleksandr Bezdieniezhnykh ac3e288dbd [AZ-528] Add AZ-528 task spec + register in dependencies table
Follow-up to cumulative review batches 52-54 Finding F1. Creates the
local task-spec file under _docs/02_tasks/todo/ and adds the row to
_dependencies_table.md so Batch 55's implement-loop can pick AZ-528
up. Mirrors the AZ-527 precedent from the c2_vpr-side cumulative
review (49-51): cumulative review opens the Jira ticket + raises the
finding, the prep commit adds the spec, the next batch implements.

Sized at 3 points (1 helper module + 3 strategy edits + 1 test file
with AST-walk + import-grep regression guards). Marginally larger
than AZ-527's 2-point c2 consolidation because the c1 spine has both
module-level free functions AND mixin-shaped instance methods.

Jira: https://denyspopov.atlassian.net/browse/AZ-528
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 02:49:31 +03:00
Oleksandr Bezdieniezhnykh 21cef8bdce [AZ-528] [AZ-527] [AZ-333] [AZ-334] Cumulative review batches 52-54
Verdict: PASS_WITH_WARNINGS — auto-chain allowed per implement skill
Step 14.5. AZ-528 created as the formal hygiene PBI for the c1_vio
strategy facade orchestration-spine 3-way duplication (Medium /
Maintainability) — the deferred F1 finding from B53 + B54 per-batch
reviews. AZ-527 closes the parallel c2_vpr-side helper duplication
finding (carried over from cumulative-49-51 F1).

Carry-overs: F2 (B52-54 test-fake / _patch_pose_recovery sharing) +
cumulative-49-51 F2 (AC-10 spec wording drift across c2_vpr specs)
remain informational; no code defect, no active drift.

Next cumulative review trigger fires after Batch 57 (every K=3).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 02:45:28 +03:00
Oleksandr Bezdieniezhnykh ceb24b5a62 [AZ-334] C1 KLT/RANSAC strategy — engine-rule simple-baseline VIO
Implement KltRansacStrategy, the ADR-002 engine-rule mandatory
simple-baseline VioStrategy for E-C1. Pure-Python facade over
OpenCV's cv2.goodFeaturesToTrack / calcOpticalFlowPyrLK /
findEssentialMat / recoverPose pipeline — no C++/pybind11 binding
by design so a Tier-0 workstation runs the strategy with
`pip install opencv-python` and the BUILD_KLT_RANSAC=ON gate alone.
Constructor + state machine + FDR transition spine mirror
Okvis2Strategy + VinsMonoStrategy so the AZ-331 factory + IT-12
comparative harness treat all three as drop-in substitutable; the
duplication is the consolidation target now formally in scope for
the next cumulative review (batches 52-54).

AC coverage: AC-1..AC-11 + NFR-perf mapped to passing tests
(25 tests, 23 pass + 2 tier-2 skipped on dev/CI runners; all 25
pass under GPS_DENIED_TIER=2). Honest-covariance invariant (AC-9)
implemented as residual-scatter / (N_inliers - 5) with an inlier-
count penalty — no client-side floor or smoother; cov Frobenius
grows monotonically across DEGRADED. Camera-agnostic source
(AC-11) enforced by CI-grep gate that excludes docstring text.

Test-Run Cadence: focused suite tests/unit/c1_vio/ green (95 passed,
6 skipped); config-loader + compose-root suites green; full-suite
gate deferred to Step 16 per implement skill.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 02:40:01 +03:00
Oleksandr Bezdieniezhnykh 4815dd6aa1 chore: bump D-CROSS-CVE-1 leftover replay timestamp
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 02:15:37 +03:00
Oleksandr Bezdieniezhnykh 6a5954bdae [AZ-333] C1 VINS-Mono strategy — research-only comparative VIO
VinsMonoStrategy: Python facade conforming to AZ-331 Protocol; mirrors
the AZ-332 OKVIS2 facade so the AZ-331 factory + IT-12 comparative
harness can treat both as drop-in substitutable. Native binding is a
pybind11 skeleton compiled behind BUILD_VINS_MONO=ON (default OFF for
airborne / operator-tooling / replay-cli per module-layout.md
Build-Time Exclusion Map). Real vins_estimator wiring is the Tier-2
follow-up.

VinsMonoConfig added to c1_vio/config.py with sliding-window /
feature-tracker / marginalisation / opt-iteration knobs plus
__post_init__ validation; exported through the package __init__.

cpp/vins_mono/CMakeLists.txt replaces the AZ-263 placeholder with full
pybind11 wiring: Risk-1 mitigation forces VINS_MONO_USE_ROS=OFF;
Risk-2 mitigation links Eigen from the same cpp/_third_party/eigen pin
as OKVIS2; Risk-3 mitigation enforces BUILD_VINS_MONO=OFF in
deployment binaries via the gate at the top of the file.

Tests: 17 new in test_vins_mono_strategy.py (15 pass + 2 tier2 skip);
fake_vins_mono_binding fixture added to conftest.py mirroring the
fake_okvis2_binding pattern; test_protocol_conformance updated to drop
vins_mono from _STRATEGIES_WITHOUT_PY_MODULE so the existing
parametrised factory tests route through the new strategy.

Focused c1_vio suite: 72 passed, 4 skipped. Full suite: 1788 passed,
1 unrelated pre-existing flake (c12 cold-start perf, env-bound).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 01:11:09 +03:00
Oleksandr Bezdieniezhnykh 2ce300ddb1 [AZ-527] Archive AZ-527 + batch 52 report + state bump
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 00:51:19 +03:00
Oleksandr Bezdieniezhnykh 235eb4549e [AZ-527] Consolidate _assert_engine_output_dim into c2-internal helper
Closes cumulative review batches 49-51 Finding F1 (Medium /
Maintainability) -- the 7-way duplication of _assert_engine_output_dim
across c2_vpr secondary VPR strategy modules.

Add c2-internal helper assert_engine_output_dim(inference_runtime,
handle, preprocessor, descriptor_dim, *, output_key='embedding',
input_key='input') in src/gps_denied_onboard/components/c2_vpr/
_engine_dim_assertion.py. The helper runs a zero-init dry-run
inference at preprocessor.input_shape() and asserts the engine output
dict carries (1, descriptor_dim) under output_key. Raises
gps_denied_onboard.config.schema.ConfigError on mismatch (preserving
the prior error envelope and message wording byte-identically).

Migrate 7 strategy modules (ultra_vpr, net_vlad, mega_loc, mix_vpr,
sela_vpr, eigen_places, salad) to import the helper and delete the
local _assert_engine_output_dim definitions + their inline
'AZ-527 (planned)' comments. NetVLAD is the only call site that
overrides output_key='vlad_descriptor'; the other 6 explicitly pass
output_key=_OUTPUT_KEY + input_key=_ENGINE_INPUT_KEY (matching helper
defaults but documenting strategy contract at the call site).

Add tests/unit/c2_vpr/test_az527_engine_dim_assertion.py (14 tests,
AAA pattern, Protocol-conforming fakes) covering AC-1..AC-4: helper
signature; wrong shape raises ConfigError naming both dims; missing
output key raises ConfigError naming the missing key; AST-walk
regression guard for stray definitions outside the helper module
(modeled on AZ-526's test_ac4_az526_no_module_level_iso_ts_from_clock_outside_helper);
import-grep regression guard verifying all 7 strategy modules import
the helper.

AC-5 (existing AZ-337/338/339/340 AC-6 sub-tests pass unmodified) is
exercised transitively: c2_vpr/ full directory 230/230 PASS, no test
file modified outside the new test_az527_*. AC-6 (AZ-270 + AZ-507
layer lints) verified by tests/unit/test_az270_compose_root.py
8/8 PASS.

Code-review verdict: PASS (zero findings). Ruff clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 00:50:17 +03:00
Oleksandr Bezdieniezhnykh f6a180e5df [AZ-340] [AZ-527] Archive AZ-340 + batch 51 report + cumulative review 49-51
Bookkeeping for batch 51 close:

- Archive AZ-340 spec todo/ -> done/
- Add _docs/03_implementation/batch_51_cycle1_report.md
- Add _docs/03_implementation/cumulative_review_batches_49-51_cycle1_report.md
  Verdict: PASS_WITH_WARNINGS. F1 (Medium) escalates the 2-way
  _assert_engine_output_dim near-duplicate from cumulative-46-48 to a
  7-way duplication after AZ-339 + AZ-340; new hygiene PBI AZ-527
  formally created. F2 (Low) carries the AC-10 ConfigError vs literal
  ConfigurationError spec drift (documentation only).
- File AZ-527 hygiene PBI (Hygiene -- consolidate
  _assert_engine_output_dim into a c2-internal helper, 2pt, AZ-255
  E-C2). Add the spec stub at _docs/02_tasks/todo/AZ-527_*.md.
- Refresh _docs/02_tasks/_dependencies_table.md: +AZ-527 row, totals
  bumped to 148 tasks / 491 points.
- Bump _docs/_autodev_state.md: last_completed_batch=51,
  last_cumulative_review=batches_49-51.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 00:39:29 +03:00
Oleksandr Bezdieniezhnykh 87909cce9f [AZ-340] C2 SelaVPR + EigenPlaces + SALAD secondary VPR backbones
Three new VprStrategy implementations for IT-12 comparative-study
(research binary only, gated OFF for airborne / operator-tooling per
ADR-002). All run via the C7 TensorRT runtime (or ONNX-RT fallback)
with their own concrete BackbonePreprocessor, single-stage L2
normalisation, and FaissBridge-delegated retrieval — same pattern as
AZ-339 (MegaLoc + MixVPR), parametrised in tests for compactness.

  * SelaVprStrategy   — D=512,  input 224x224
  * EigenPlacesStrategy — D=2048, input 480x480
  * SaladStrategy     — D=8448, input 322x322 (DINOv2-Large backbone;
                        heaviest in the C2 family — NFR-perf budget
                        relaxed to 120 ms p95 / 1200 MB GPU per task
                        spec)

The composition-root factory tables and KNOWN_STRATEGIES set were
already pre-wired at AZ-336 land time; module-layout.md already names
all three Internal entries and BUILD_VPR_* rows. No CMake change
required (env-flag gating).

54 unit tests (3 strategies * 18 cases) cover AC-1..AC-11 plus extras
(single-stage L2, NCHW FP16, constructor validation, FDR emission).
All pass; sibling c2_vpr suite + composition-root regression + AZ-526
iso-ts regression all green.

Code review verdict: PASS_WITH_WARNINGS. Two Low findings logged in
batch_51_review.md: F1 escalates `_assert_engine_output_dim`
duplication from 4-way to 7-way (already tracked by AZ-527 hygiene
PBI; will surface in cumulative review batches 49-51); F2 mirrors the
AZ-337 / 338 / 339 AC-10 spec-drift precedent (literal
ConfigurationError vs implemented ConfigError / StrategyNotAvailable).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 00:32:38 +03:00
Oleksandr Bezdieniezhnykh e81616a09d [meta] Refresh D-CROSS-CVE-1 leftover replay timestamp
Bootstrap-time replay check confirmed gtsam==4.2.1 still pins
numpy<2.0.0; opencv-python>=4.12 pin remains deferred.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 00:19:06 +03:00
Oleksandr Bezdieniezhnykh 0d65ff4705 [AZ-339] C2 MegaLoc + MixVPR secondary VPR backbones
Adds two research-only VprStrategy implementations for the IT-12
comparative-study matrix. MegaLocStrategy (D=2048, 322x322) and
MixVprStrategy (D=4096, 320x320), both via C7 TensorRT FP16 with
their own concrete BackbonePreprocessor. Single-stage global L2
normalisation; retrieval delegated to FaissBridge; FDR records +
structured logs identical to UltraVPR. BUILD_VPR_MEGALOC and
BUILD_VPR_MIXVPR ON for research/replay-cli only, OFF for airborne
and operator-tooling (fail-fast at composition root via existing
AZ-336 factory). Uses helpers.iso_ts_from_clock from day 1 — no
new timestamp helper duplicates introduced.

36 parametrised AC tests + 25 protocol-conformance + 18 helper
regression tests pass; 1690 / 1690 unit tests pass (excluding 1
pre-existing flaky cold-start subprocess test in c12). Verdict:
PASS_WITH_WARNINGS — one Medium follow-on (AZ-527 to consolidate
4-way _assert_engine_output_dim) + one Low AC wording drift.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 23:52:54 +03:00
Oleksandr Bezdieniezhnykh 5dfd9a577e [AZ-526] Consolidate _iso_ts_from_clock into helpers/iso_timestamps
Closes cumulative review 46-48 F1 (Medium) + F3 (Low). Adds
iso_ts_from_clock(clock) alongside iso_ts_now() in the Layer-1
helper; migrates four duplicate definitions in c2_vpr (net_vlad,
ultra_vpr, _faiss_bridge) and c12_operator_orchestrator
(operator_reloc_service). Output format flipped +00:00 -> Z to
align with iso_ts_now() and the canonical FDR _TS fixture (FDR
schema test passes unmodified).

18 helper AC tests + 186 sibling tests pass; ruff clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 23:37:04 +03:00
Oleksandr Bezdieniezhnykh fbeeab60b3 [AZ-337] [AZ-338] [AZ-508] Cumulative review batches 46-48
Verdict: PASS_WITH_WARNINGS. Per-batch reviews already validated
each task's ACs; this cumulative review focuses on cross-batch
drift and surfaces 1 Medium + 2 Low maintainability findings:

- F1 (Medium): `_iso_ts_from_clock` Clock-injected helper duplicated
  across 4 files (c2_vpr/net_vlad + ultra_vpr + _faiss_bridge,
  c12_operator_orchestrator/operator_reloc_service). B46 + B47
  carry inline comments anticipating AZ-508 would consolidate this,
  but AZ-508 (Batch 48) scoped itself narrower (stdlib-only,
  Excluded the Clock-injected variant). Recommend a 2-point follow-up
  PBI adding `iso_ts_from_clock(clock)` to helpers/iso_timestamps.py
  before AZ-339 / AZ-340 / AZ-358 / AZ-389 add more copies.

- F2 (Low): `_assert_engine_output_dim` near-duplicated between
  NetVLAD and UltraVPR. Defer consolidation until 5 c2_vpr strategies
  are in flight (after AZ-339 / AZ-340).

- F3 (Low): Clock-driven helper outputs `+00:00`; canonical FDR `ts`
  is `Z`. Fold into F1 follow-up PBI.

No Critical or High findings; auto-chain to next batch allowed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 23:26:58 +03:00
Oleksandr Bezdieniezhnykh 5441ea2017 [AZ-508] Consolidate _iso_ts_now into helpers/iso_timestamps
Batch 48 / Cycle 1 (greenfield Step 7). Closes cumulative review
batches 31-33 F2 and 28-30 F3 by replacing the duplicated private
_iso_ts_now() one-liners with a single Layer-1 helper:

  src/gps_denied_onboard/helpers/iso_timestamps.py
  iso_ts_now() -> str

Output format matches the canonical FDR _TS fixture
(YYYY-MM-DDTHH:MM:SS.ffffffZ); no FDR schema change.

Migrated call-sites (3): c7_inference/onnx_trt_ep_runtime,
c7_inference/thermal_publisher, plus the 3 c6_tile_cache callers
that previously imported from the local c6_tile_cache/_timestamp
shim (now deleted, superseded by the Layer-1 helper).

Spec drift resolved (Choose A, user-approved): spec listed 5 call
sites + +00:00 regex; on-disk reality at batch start is 3 sites +
Z-suffix matching every existing helper and the FDR _TS fixture.
Spec preamble + AC-2 regex updated in the task file; documented in
batch_48_cycle1_report.md.

Tests: 9 new AC tests (AC-1..AC-7 + Layer-1 invariant +
public-surface defensive); 216 focused tests pass including the
unmodified AZ-272 FDR schema suite and AZ-270 / AZ-507 layering
lints. Verdict: PASS (no findings).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 23:23:22 +03:00
Oleksandr Bezdieniezhnykh f29897cb3a [meta] Tighten Jira tracker error handling: STOP and ASK on any error
User feedback after a transitionJiraIssue call returned a bare
{"success": true} that I trusted blindly: the rule should require
explicit verification and stop-and-ask on any ambiguous response.

Two targeted clarifications:

- .cursor/rules/tracker.mdc - Tracker Availability Gate now lists
  the full set of failure modes (non-2xx, timeout, empty body,
  opaque success) and bans automatic retries. Adds an explicit
  read-back requirement when the response is minimal, and adds
  "abort" to the user-choice menu.

- .cursor/skills/implement/SKILL.md - Step 5 (In Progress) and
  Step 12 (In Testing) now spell out the STOP-and-ASK rule inline
  instead of just pointing at tracker.mdc. Adds the read-back
  verification step for opaque responses.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 23:06:48 +03:00
Oleksandr Bezdieniezhnykh b64f3a1b93 [AZ-337] Archive task spec + batch 47 report + state bump
- _docs/02_tasks/todo/AZ-337_c2_ultra_vpr.md
  -> _docs/02_tasks/done/AZ-337_c2_ultra_vpr.md
- _docs/03_implementation/batch_47_cycle1_report.md (new)
- _docs/_autodev_state.md: last_completed_batch 46 -> 47;
  sub_step.detail "batch 47 complete - selecting batch 48"

AZ-337 transitioned in Jira: In Progress -> In Testing.

Batches 45/46/47 close the C2 production path (Protocol +
FaissBridge + NetVLAD baseline + UltraVPR primary).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 22:44:22 +03:00
Oleksandr Bezdieniezhnykh 3c4fd272f1 [AZ-337] C2 UltraVPR primary backbone VprStrategy
UltraVPR is the Documentary Lead's PRIMARY backbone per
description.md § 1 and is wired by default
(config.c2_vpr.strategy = "ultra_vpr"). Runs on the C7 TensorRT
runtime (AZ-298) or ONNX-Runtime fallback (AZ-299); explicitly NOT
on the PyTorch FP16 runtime so a TRT engine compile bug can fall
back to NetVLAD without simultaneously breaking both strategies.

Production changes:
- c2_vpr/ultra_vpr.py - UltraVprStrategy + module-level create()
  factory. embed_query pipeline: preprocess -> runtime.infer ->
  single-stage L2 -> VprQuery. retrieve_topk delegates one-line to
  FaissBridge. Engine load + output-shape assertion happen at
  create() time (AC-6) so misconfiguration surfaces at startup,
  not 17 minutes into a flight. UltraVPR has D=512 fixed (NOT a
  config knob; AC-5 / AC-6 / AC-7 all assume 512). Single-stage L2
  (no intra-cluster step like NetVLAD; spy-test enforces this so a
  future refactor cannot silently regress recall).
- c2_vpr/_preprocessor_ultra_vpr.py - centre-crop using the camera
  calibration's principal point (cx, cy from intrinsics_3x3),
  falling back to geometric centre + WARN log when calibration is
  absent (AC-9). Resize -> (384, 384) -> ImageNet mean/std ->
  FP16 NCHW.
- No composition-root changes: UltraVPR consumes a pre-compiled
  .trt engine (no PyTorch nn.Module), so the strategy module does
  NOT expose MODEL_NAME / architecture_factory. The composition-
  root _register_strategy_architecture helper no-ops cleanly for
  this case (verified by test_create_does_not_register_pytorch_architecture).

Tests:
- tests/unit/c2_vpr/test_ultra_vpr.py - 29 tests covering all 12
  ACs + preprocessor contract + constructor validation + FDR
  record emission + single-stage L2 enforcement.

Full unit suite: 1637 passed / 80 env-skipped (+29 new tests).
Per-batch code review (batch_47_review.md): PASS_WITH_WARNINGS
(3 Low-severity findings; no Critical / High / Medium):
- F1: _iso_ts_from_clock is now the 7th copy (AZ-508 will close).
- F2: AZ-337 spec uses outdated C7 API names; affects upcoming
  AZ-339 / AZ-340. Spec-hygiene PBI recommended.
- F3: principal-point fallback uses (0, 0) zero-detection for
  missing calibration; safe but tightens when intrinsics become
  Optional.

Architectural notes:
- AZ-507 layering clean. Imports only InferenceRuntimeCut,
  DescriptorIndexCut, c2_vpr internals, _types, helpers,
  clock, fdr_client. Architecture lint test passes.
- Pattern parity with NetVLAD (B46) where semantics permit;
  UltraVPR-specific paths (single-stage L2, 'embedding' output
  key, TRT runtime, no architecture registry, principal-point
  crop) are clearly localised.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 22:43:17 +03:00
Oleksandr Bezdieniezhnykh 773d589d34 [AZ-338] Archive task spec + batch 46 report + state bump
- _docs/02_tasks/todo/AZ-338_c2_net_vlad.md
  -> _docs/02_tasks/done/AZ-338_c2_net_vlad.md
- _docs/03_implementation/batch_46_cycle1_report.md (new)
- _docs/_autodev_state.md: last_completed_batch 45 -> 46;
  sub_step.detail "batch 46 complete - selecting batch 47"

AZ-338 transitioned in Jira: In Progress -> In Testing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 22:31:56 +03:00
Oleksandr Bezdieniezhnykh af0dbe863a [AZ-338] [AZ-283] C2 NetVLAD mandatory simple-baseline VprStrategy
NetVLAD is the C2 comparative baseline per the engine rule (every
production-default backbone ships with a simple-baseline alongside).
Runs on the C7 PyTorch FP16 runtime (NOT TRT) so a TRT engine compile
bug cannot simultaneously break NetVLAD AND UltraVPR.

Production changes:
- c2_vpr/net_vlad.py — NetVladStrategy + module-level create() factory.
  Constructor wires InferenceRuntimeCut + DescriptorIndexCut +
  NetVladBackbonePreprocessor + DescriptorNormaliser + FaissBridge.
  embed_query pipeline: preprocess -> runtime.infer -> dual-stage
  normalisation (intra-cluster THEN global L2) -> VprQuery.
  retrieve_topk delegates one-line to FaissBridge.
- c2_vpr/_net_vlad_architecture.py — Arandjelovic et al. 2016 NetVLAD
  layer over torchvision VGG16 features + optional Linear PCA
  projection to descriptor_dim (default 4096; published Pittsburgh
  reference uses K*D=64*512=32768 raw + Linear(32768, 4096) PCA).
- c2_vpr/_preprocessor_net_vlad.py — OpenCV-based image preprocessor:
  decode -> centre-crop square -> resize (480, 480) -> ImageNet
  normalisation -> FP16 NCHW. Calibration is not consumed (NetVLAD
  is calibration-agnostic per published preprocessing chain).
- c2_vpr/inference_runtime_cut.py — NEW AZ-507 consumer-side cut
  mirroring C7 InferenceRuntime; lets c2_vpr stay AZ-507-clean.
- c2_vpr/config.py — added netvlad_descriptor_dim: int = 4096 knob.
- helpers/descriptor_normaliser.py — added intra_cluster_normalise
  (DescriptorNormaliser v1.0.0 -> v1.1.0; backward-compatible add).
- runtime_root/vpr_factory.py — added _register_strategy_architecture
  helper that binds (MODEL_NAME, architecture_factory(descriptor_dim))
  to C7's architecture registry before delegating to the strategy's
  create() factory. Keeps the c7 import at L4, preserves AZ-507.
- fdr_client/records.py — registered vpr.embed_query,
  vpr.backbone_error, vpr.preprocess_error record kinds.

Tests:
- tests/unit/c2_vpr/test_net_vlad.py — 31 tests covering all 11 ACs +
  preprocessor contract + architecture factory + constructor
  validation + FDR record emission.
- tests/unit/test_az283_descriptor_normaliser.py — +8 tests for the
  new intra_cluster_normalise.
- tests/unit/test_az272_fdr_record_schema.py — +3 fixture payloads.

Full unit suite: 1608 passed / 80 env-skipped (+43 new tests).
Per-batch code review (batch_46_review.md): PASS_WITH_WARNINGS
(4 Low-severity hygiene findings; no Critical/High/Medium).

Architectural notes:
- The spec implied c2_vpr.net_vlad.create() registers the architecture
  with C7. That violates AZ-507 (no cross-component imports). Resolved
  by exposing MODEL_NAME + architecture_factory(descriptor_dim) on the
  strategy module and having the composition root perform the C7 bind.
- C7 PyTorch runtime API names in the spec (forward, load_engine)
  were outdated; aligned implementation with the live v1.0.0 Protocol
  (infer, compile_engine + deserialize_engine). Spec hygiene flagged
  in review F2.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 22:30:29 +03:00
Oleksandr Bezdieniezhnykh dd2f1cbae6 [AZ-341] [AZ-329] [AZ-330] [AZ-328] Cumulative review batches 43-45
PASS_WITH_WARNINGS verdict covering AZ-328 (BuildCacheOrchestrator),
AZ-329 (PostLandingUploadOrchestrator + FdrFooterReader), AZ-330
(OperatorReLocService), AZ-523/AZ-524 (C11 internal gate removal +
c12_operator_orchestrator rename), and AZ-341 (FaissBridge +
DescriptorIndexCut).

Four Low-severity findings, all hygiene or carry-over: F1 ISO
timestamp helper duplicated across 6 modules (AZ-508 hygiene PBI
exists), F2 IndexUnavailableError namespace duplication c2/c6
flagged for spec/docstring alignment, F3 AZ-341 spec lists unused
normaliser parameter, F4 carry-over cold-start microbench host-load
flake.

Full unit suite 1565 passed / 80 env-skipped at close of window.
No new layer-direction or AZ-507 violations introduced; three new
structural Protocol cuts (TileDownloaderCut, FdrFooterReader,
DescriptorIndexCut) all follow the same shape.

State file updated: last_cumulative_review batches_40-42 ->
batches_43-45.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 21:50:32 +03:00
Oleksandr Bezdieniezhnykh 1682dc354b [AZ-341] Archive AZ-341 + batch 45 report
Batch 45 (AZ-341 C2 FAISS retrieve wiring) post-commit bookkeeping:
- Move AZ-341 task spec to done/ (implement skill step 13).
- Write batch_45_cycle1_report.md (test results, AC coverage,
  architectural decisions, findings carried into cumulative review).
- Bump state.last_completed_batch 44 → 45.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 21:47:07 +03:00
Oleksandr Bezdieniezhnykh 88f6ae6dce [AZ-341] C2 FAISS HNSW retrieve wiring (FaissBridge + AZ-507 cut)
Shared retrieve_topk plumbing for every concrete C2 VprStrategy:
- FaissBridge centralises the c6 search_topk → VprResult pipeline,
  the defended-in-depth INV-4 check (exactly k, distance-ascending),
  the WARN-threshold check on distances[0], optional per-frame DEBUG
  log, and one `vpr.retrieve_topk` FDR record per call with latency
  measurement.
- DescriptorIndexCut Protocol — consumer-side structural cut of c6
  DescriptorIndex.search_topk (AZ-507); keeps c2_vpr c6-import-free.
- C2VprConfig gains warn_top1_threshold + debug_per_frame_distances
  knobs with validators.
- KNOWN_PAYLOAD_KEYS registers vpr.retrieve_topk for the FDR record
  schema with payload {frame_id, backbone_label, top10_distances,
  latency_us}; companion fixture added to the AZ-272 roundtrip suite.
- 22 unit tests cover AC-1..AC-11 + NFR-perf microbench (p95 ≤ 0.5 ms)
  + constructor and retrieve-argument validation.

Verdict: PASS_WITH_WARNINGS (2 Low findings — duplicated ISO-ts
helper across c2/c5/c11/c12, captured in AZ-508 hygiene PBI;
spec-listed but unused `normaliser` parameter dropped — INV-3 makes
the embedding L2-normalised at the strategy's `embed_query`).

Tests: 1565 passed / 80 skipped (was 1543; +22 new tests).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 21:45:40 +03:00
Oleksandr Bezdieniezhnykh 25836925c9 [AZ-329] [AZ-330] Archive Batch 44 task files to done/
Implementation completed in Batch 44 (commit 5fe6702); archive the task
specs per implement skill Step 13.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 21:30:25 +03:00
Oleksandr Bezdieniezhnykh a92e5ee482 [AZ-329] [AZ-330] [AZ-523] [AZ-524] Doc sweep: arch + glossary for Batch 44
Propagate Batch 44 SRP refactor (C11 internal flight-state gate moved to
C12; PostLandingUploadOrchestrator gates on flight_footer.clean_shutdown;
OperatorReLocService dispatches AC-3.4 hints via OperatorCommandTransport)
into the suite-wide architecture documents that the per-component sweep
in Phase F did not yet cover.

Files updated:
- architecture.md: C11/C12 component entries, principle #4 phrasing,
  Data Model table (FlightStateSignal annotation + new
  FlightFooterRecord / PostLandingUploadRequest / ReLocHint rows),
  post-landing + reloc data-flow summaries, ADR-004 "Why the gate
  moved to C12" rationale, deployment + security wording.
- glossary.md: Tile Manager entry — gate-removal note.
- data_model.md: FlightStateSignal row clarified; new rows for
  Batch 44 DTOs.
- system-flows.md: F10 row, dependencies, full F10 prose +
  preconditions + mermaid + error table reworked around the
  footer-based gate.
- epics.md: E-C11 scope/interface/AC/child-issue table (gate
  stripped, AZ-317 superseded); E-C12 scope/interface/AC/child-
  issue table expanded with PostLandingUploadOrchestrator,
  OperatorReLocService, FdrFooterReader, OperatorCommandTransport.
- FINAL_report.md: component table rows 12 + 13.
- components/10_c8_fc_adapter/description.md: removed stale claim
  that C11 TileUploader consumes FlightStateSignal.
- contracts/c6_tile_cache/tile_metadata_store.md: minor C12
  naming fix.

Tests: 1543 passed / 80 skipped — doc-only sweep, no regressions.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 21:28:59 +03:00
Oleksandr Bezdieniezhnykh 9116e304fd [Batch 44] Close batch 44 in autodev state
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 19:43:08 +03:00
Oleksandr Bezdieniezhnykh 5fe67023b2 [AZ-329] [AZ-330] [AZ-523] [AZ-524] Batch 44 atomic refactor
Implements two new C12 services and rebalances the C11/C12 boundary
in one atomic commit:

* AZ-329 PostLandingUploadOrchestrator — gates C11 upload on the
  `flight_footer` FDR record's `clean_shutdown` field; 4 refusal
  modes; new FdrFooterReader Protocol + LocalFdrFooterReader.
* AZ-330 OperatorReLocService — AC-3.4 visual-loss re-localization
  hint; reuses shared LatLonAlt; OperatorCommandTransport Protocol
  cut (E-C8 owns the future pymavlink concrete); new FDR record
  kind `c12.reloc.requested`; log redaction (lat/lon 5 decimals,
  reason 200 chars).
* AZ-523 C11 internal flight-state gate removed (SRP refactor):
  `confirm_flight_state` / `FlightStateSignal` use /
  `FlightStateNotOnGroundError` deleted from C11; TileUploader
  contract bumped to v2.0.0 (frozen) with migration note; AZ-317
  superseded.
* AZ-524 Package rename `c12_operator_tooling` →
  `c12_operator_orchestrator` across source, tests, pyproject,
  CMake, Dockerfile, compose, CI, runtime-root services class
  (`OperatorOrchestratorServices`) + factory function
  (`build_operator_orchestrator`), logger namespaces, config slug,
  docs, and the E-C12 epic title.

Tests: 1543 passed, 80 skipped (all environment gates). Targeted
AC suite (AZ-329 + AZ-330 + FdrFooterReader): 37 passed. Cold-start
NFR-perf still ≤ 500 ms p99.

Tracker: AZ-317 → Done (superseded); AZ-319 v2.0.0 contract bump
comment; AZ-329/AZ-330 → In Testing; AZ-253 epic renamed; AZ-523
+ AZ-524 created and closed as audit-trail tickets.

See `_docs/03_implementation/batch_44_cycle1_report.md`.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 19:42:46 +03:00
Oleksandr Bezdieniezhnykh 2d88d3d674 [Batch 44 prep] Add batch 44 implementation plan
Captures the architectural plan agreed in the prior /autodev session:
C12 package rename (c12_operator_tooling -> c12_operator_orchestrator),
C11 internal flight-state gate removal (SRP fix; supersedes AZ-317),
AZ-329 PostLandingUploadOrchestrator rewrite around flight_footer FDR
record, and AZ-330 OperatorReLocService implementation. Execution starts
in the next /autodev invocation; this commit makes the planning artifact
durable so the batch executes against a fixed plan.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 18:06:02 +03:00
Oleksandr Bezdieniezhnykh 7644b25e8c [AZ-328] C12 BuildCacheOrchestrator + remote C10 invoker (Batch 43)
Implements F1 pre-flight cache build orchestrator on the operator
workstation. Composes C11 TileDownloader (AZ-316), C12 CompanionBringup
(AZ-327), C12 FlightsApiClient (AZ-489), and the new
RemoteCacheProvisionerInvoker into one sequenced flow guarded by a
filelock-backed workstation-side lockfile.

Architectural decisions:
- Phase-0 flight-resolve runs BEFORE the lockfile (ADR-010): a flight
  that cannot be resolved is an operator-input error, not a contended-
  resource error. Enforced by AC-11 + AC-14.
- Consumer-side cuts (AZ-507) for C11 + C10 types: local Protocols /
  mirror DTOs in tile_downloader_cut.py and _types.py; external errors
  matched by name-based whitelisting so unknown exceptions still
  propagate per AC-6. Cross-component type translation lives at the
  composition root (c12_factory).
- Failure surfacing: recognised operational failures (download error,
  companion not ready, build error, flight-resolve error) return as
  CacheBuildReport(outcome=failure, failure_phase=...). Only lockfile
  contention raises (BuildLockHeldError) since no phase ever ran.
- Workstation-side filelock library (project pin); no custom primitive.
- Remote C10 stdout streamed line-by-line as DEBUG with api_key /
  auth_token redacted before logging (defence-in-depth).
- CLI is now a thin adapter; all workflow logic lives in
  build_cache.py. operator-tool build-cache exit codes map per
  CacheBuildReport.failure_phase + failure_exception_type.

Tests: 116 c12 unit tests pass (29 new for AZ-328 covering 15/15 ACs +
NFR-perf-overhead microbench; 7 new for remote_c10_invoker; 3 new for
file_lock; test_cli_build_cache rewritten for new orchestrator
interface). Full repo suite: 1522 passed, 80 skipped.

Also: replays Batch 42's ruff format leftover for c12 flights_api +
test_az489 files (formatter ran over the c12 directory after new
files were added). Pure whitespace; no behaviour change.

Full report: _docs/03_implementation/batch_43_cycle1_report.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 11:03:46 +03:00
Oleksandr Bezdieniezhnykh 099c75c6f8 chore: cumulative review batches 40-42 (PASS_WITH_WARNINGS)
5 findings: F1 (Medium / Maintainability) - _iso_now copies grew to 8
across c11 + c13 + c7, AZ-508 hygiene PBI no longer matches reality;
F2-F5 (Low) - triplicated atomic-write JSON helpers, 4x duplicated
SectorClassification enum (acknowledged by ADR-009), recurring
"outcome=failure" prose vs typed-exception drift across the C11 trio,
and an NFR-perf-cold-start near-miss that prompted PEP 562 lazy-import
discipline in c12. None block the implement loop.

Updated _autodev_state.md last_cumulative_review to batches_40-42.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 09:40:27 +03:00
Oleksandr Bezdieniezhnykh 91ce1c2047 [AZ-326] [AZ-327] C12 operator-tool CLI + companion SSH bringup
AZ-326 (3pt): operator-tool Click CLI shell at
src/gps_denied_onboard/components/c12_operator_tooling/cli.py with six
subcommands (download, build-cache, upload-pending, reloc-confirm,
verify-ready, set-sector); SectorClassificationStore (atomic-write JSON
under ~/.azaion/onboard/sector-classifications.json); freshness-table
lookup driving AC-NEW-6; EXIT_* constants; AZ-266 structured-JSON log
wiring to a rotating ~/.azaion/onboard/c12-tooling.log handler;
operator-tool console-script entry in pyproject.toml.

AZ-327 (3pt): CompanionBringup orchestrator at
src/gps_denied_onboard/components/c12_operator_tooling/companion_bringup.py
that opens an SSH session against the companion (paramiko per project
pin), checks the four pre-flight artifacts (Manifest, expected engines,
sha256 sidecars, calibration), and returns a ReadinessReport per
description.md S2; CompanionUnreachableError + ContentHashMismatchError
with operator-friendly remediation hints; ParamikoSshSessionFactory +
RemoteSidecarVerifier (sha256sum + cat over SSH, no bytes pulled to
the workstation); paramiko>=3.4,<4.0 dep added.

NFR-perf-cold-start fix: PEP 562 lazy __getattr__ in
c12_operator_tooling/__init__.py and flights_api/__init__.py defers
HttpxFlightsApiClient (httpx), ParamikoSshSession[Factory] (paramiko +
cryptography), bbox_from_waypoints / takeoff_origin_from_flight (numpy +
pyproj). cli.py imports from leaf flights_api modules. operator-tool
--help cold start: ~870ms -> <200ms typical, <500ms p99.

Includes 73 unit tests (incl. paramiko-version-drift smoke per AZ-327
Risk 1) + console-script integration test. All 1494 repo-wide unit
tests pass; 80 skips are pre-existing environment gates.

Batch report: _docs/03_implementation/batch_42_cycle1_report.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 09:34:14 +03:00
Oleksandr Bezdieniezhnykh a06b107fc3 [AZ-320] Add C11 IdempotentRetryTileUploader decorator
Wraps HttpTileUploader (AZ-319) with two bounded retry budgets:

- In-call (per-batch) — re-invokes inner on PARTIAL outcome up to
  `max_in_call_retries` times with capped exponential backoff
  (`min(base ** attempt_number, cap)`). On exhaustion: surfaces an
  operator hint via `next_retry_at_s = now + backoff_cap_s`.
- Per-tile (cross-call) — atomically increments c6's
  `tiles.upload_attempts` counter for every rejection; once a tile
  hits `max_per_tile_attempts` it is forward-only transitioned to
  `voting_status = upload_giveup` (excluded from `pending_uploads`).
  Each transition emits FDR `kind="c11.upload.giveup"` plus an
  ERROR log.

C6 contract changes (AZ-303 v1.3.0):
- VotingStatus.UPLOAD_GIVEUP added (forward-only from PENDING/TRUSTED).
- TileMetadataStore.increment_upload_attempts(tile_id) -> int added
  with NotImplementedError default for backwards-compat.
- Migration 0003_c11_upload_attempts: additive column +
  widened ck_tiles_voting_status (preserves IS NULL clause).

C11 wiring:
- C11RetryConfig + disable_retry_decorator on C11Config.
- build_tile_uploader wraps in decorator by default; bypass flag
  returns the bare HttpTileUploader. New `clock` keyword.

Cross-component isolation honoured (AZ-507): the decorator declares
`_RetryMetadataStoreLike` Protocol cut over c6's TileMetadataStore
and references `UPLOAD_GIVEUP` via a local string constant — no c6
imports.

Tests: 13 decorator + 1 conformance + 2 factory bypass + AC-6 enum
update + alembic head bump + AZ-272 schema fixture. 238 passed across
c11/c6/fdr suites; pre-existing perf microbenches unrelated.

Code review: PASS_WITH_WARNINGS (5 Low/Informational findings,
docs-level or downstream-CI-blocked). See
_docs/03_implementation/reviews/batch_41_review.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 08:48:53 +03:00
Oleksandr Bezdieniezhnykh 90f4ac78f4 [AZ-316] Implement C11 HttpTileDownloader (batch 40)
Lands the operator-side pre-flight download path: authenticated
httpx GETs against satellite-provider, RESTRICT-SAT-4 (>= 0.5 m/px)
enforcement at the C11 boundary, c6 writes via consumer-side cuts
(_TileWriterLike, _BudgetEnforcerLike), per-(flight_id, request_hash)
journal under cache_root/.c11/journal/ for idempotent re-runs (AC-8,
AC-12), 429 Retry-After + 5xx exponential backoff handling, fail-fast
on TLS / 401 / 403, and a redacted-bearer auth-header policy.

Architecture:
- AZ-507 cross-component rule held: tile_downloader.py imports zero
  c6 symbols; the composition-root _C6DownloadAdapter in
  runtime_root/c11_factory.py absorbs c6's TileMetadata / TileSource /
  FreshnessLabel / VotingStatus enum assembly.
- Sleep-callable injection (not full Clock) per Batch 39 precedent;
  default routes through WallClock.sleep_until_ns to keep the AZ-398
  invariant intact.
- No FDR records on the download path; spec mandates structured logs
  only (8 log kinds wired: session.start/end, resolution_rejected,
  freshness_rejected_summary, freshness_downgraded, batch.retry,
  provider.failed, budget.exceeded, idempotent_no_op).

Tests: 14 new downloader unit tests covering AC-1..AC-9, AC-11, AC-12
plus throughput NFR + 429 HTTP-date + 429 budget exhaustion; 2 new
TileDownloader Protocol conformance tests (AC-10). Full unit suite:
1420 passed, 80 skipped (env-gated), 0 failed.

Code review: PASS_WITH_WARNINGS (5 Low findings, all documentation
or downstream-blocked). See _docs/03_implementation/reviews/
batch_40_review.md and batch_40_cycle1_report.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 07:01:14 +03:00
Oleksandr Bezdieniezhnykh 3a61a4f5bf chore: cumulative review batches 37-39 (PASS_WITH_WARNINGS)
Captures the C11 operator-side trio landing (AZ-317/318/319) plus the
C10 build-orchestrator close-out (AZ-325) and the AZ-515 canonical-hash
extraction. Three Low findings, all documentation-level drift between
spec text and as-built code; none block Batch 40. Resolves prior F1
(AZ-515 closed the verifier-into-builder private import).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 06:40:09 +03:00
Oleksandr Bezdieniezhnykh 610e8a743c [AZ-319] C11 HttpTileUploader (post-landing upload path)
Lands the production HttpTileUploader composing AZ-317's gate, AZ-318's
per-flight signing, and consumer-side cuts over c6 storage. Implements
the full upload flow: gate ON_GROUND -> start_session -> enumerate
pending -> per-batch multipart POST with Ed25519 signing -> mark_uploaded
on ack -> end_session in finally. Honours Retry-After (RFC 7231 int +
HTTP-date), exponential backoff on 5xx, fail-fast on TLS/401/403.

Adds C11Config block, three FDR kinds (tile.queued, tile.rejected,
batch.complete), and the build_tile_uploader composition-root factory.
Cross-component access to c6 stays Protocol-cut (AZ-507 / AZ-270).

Tests: 17 new unit tests covering AC-1..AC-14 plus throughput NFR; AZ-272
schema fixtures for the three new FDR kinds. Full unit suite: 1404 passed.

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