171 Commits

Author SHA1 Message Date
Oleksandr Bezdieniezhnykh 1f634c2604 Update demo replay validation and testing documentation
ci/woodpecker/push/02-build-push Pipeline failed
- Modified the autodev state to reflect the current testing phase and details of the new `jetson-e2e` tests.
- Enhanced the "How to Test" documentation to provide clearer instructions on the demo replay validation process, including video and tlog alignment steps.
- Updated architectural documentation to include the new demo replay operator flow and its dependencies.
- Documented the removal of deprecated auto-sync features and clarified the operator-facing UI for replay validation.
- Added new entries in the dependencies table for upcoming tasks related to the demo replay flow.

These changes improve clarity and usability for operators and developers working with the demo replay system.
2026-06-20 11:24:43 +03:00
Oleksandr Bezdieniezhnykh 201ec7cdd4 [AZ-963] xfail divergent ESKF tests + honest returncode assertion on AC-3 2026-06-09 20:43:15 +03:00
Oleksandr Bezdieniezhnykh 89606ccfdc [AZ-965] [AZ-835] Archive completed task specs to done/ 2026-06-09 14:06:35 +03:00
Oleksandr Bezdieniezhnykh ba70381346 Update NetVLAD checkpoint paths and enhance .gitignore
ci/woodpecker/push/02-build-push Pipeline failed
- Changed paths in documentation and configuration files to reflect the new naming convention for the NetVLAD model, transitioning from `models/netvlad/netvlad.pt` to `models/net_vlad/net_vlad.pt`.
- Updated the `.gitignore` to include additional file types and directories related to input data and locally-generated evidence frames.
- Removed the old NetVLAD checkpoint file as part of the transition to the new naming scheme.

These changes ensure consistency across the project and improve the management of generated files.
2026-05-31 19:27:32 +03:00
Oleksandr Bezdieniezhnykh 97f5f9793c [AZ-965] NetVLAD-VGG16 backbone checkpoint + YAML/compose wiring
AZ-965 ships the NetVLAD .pt checkpoint that clears the AZ-839
empty-c10_provisioning.backbones SKIP gate. Pipeline-integration
scaffold — encoder is real, NetVLAD tail is honestly labelled as
untrained.

Composition:

* Encoder (26 keys, encoder.0..encoder.28): torchvision
  vgg16(weights=IMAGENET1K_V1) features [:-2], BSD-3-Clause.
  Real ImageNet-pretrained VGG16 conv stack.
* NetVLAD pool + PCA tail (5 keys: pool.conv.{weight,bias},
  pool.centroids, pca.{weight,bias}): random-init via
  torch.manual_seed(0). NOT trained for visual place recognition.

Total: 149,002,112 params (568.4 MiB fp32, sha256=745c6f29...).
Round-trip verified locally: torch.load(weights_only=True) +
load_state_dict(strict=True) succeed; forward(1,3,480,480) emits
{'vlad_descriptor': (1, 4096) fp32} — matches NetVladStrategy
contract per net_vlad.py:247-251.

Two material discoveries documented in the AZ-965 spec:

1. The NetVLAD-VGG16 architecture already lives in repo at
   src/gps_denied_onboard/components/c2_vpr/_net_vlad_architecture.py
   — we instantiate it and save a state_dict, NOT externally source.
2. The PyTorch FP16 runtime expects a .pt state_dict (NOT .onnx).
   BackboneConfig.onnx_path is a misnomer for NetVLAD: per AZ-321
   design + c2_vpr description.md §1, NetVLAD runs on PyTorch FP16
   (NOT TRT). compile_engine is a no-op sha256+path wrap;
   deserialize_engine does torch.load(weights_only=True) +
   load_state_dict(strict=True).

User skipped Option A/B/C/D/E question — judgment call = Option B
(IMAGENET1K_V1 + random tail) per "use judgment, don't block":
* Option A (Nanne translation) was 5-8 SP, above the 5 SP budget.
* Option B is 3 SP, fits the budget, honestly labelled.
* Option C (pure random) was borderline-dishonest per Real Results.

Files:

* scripts/mk_netvlad_checkpoint.py — deterministic generator.
* models/netvlad/netvlad.pt — 568 MiB, via git-lfs (.gitattributes
  extended for models/**/*.pt, *.onnx, *.engine).
* configs/operator_replay.yaml — c2_vpr + c10_provisioning blocks
  populated; the field literally named onnx_path actually points
  at the .pt for NetVLAD per the runtime semantics noted above.
* docker-compose.test.jetson.yml — ./models:/opt/models:ro bind
  mount added to e2e-runner.
* _docs/03_ip_attribution/netvlad.md — provenance, licence, how-to-
  reproduce, honest scope statement ("NOT a real-retrieval
  checkpoint; ESKF divergence under garbage retrievals is the
  expected next gate").
* _docs/02_tasks/todo/AZ-965_netvlad_onnx_backbone_provisioning.md
  — rewritten to reflect the .pt-not-.onnx + Option B discoveries.

Tier-2 verification follows in a separate commit after the harness
run confirms the empty-backbones SKIP gate clears.

Out of scope (filed as follow-ups):

* Real-retrieval NetVLAD weights (Nanne Pittsburgh-30k translation
  or internal team checkpoint) — separate ticket.
* AZ-840 orchestrator PASSing end-to-end (depends on retrieval
  quality + ESKF stability).
* AZ-963 60s smoke ESKF divergence (independent chain).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 18:03:32 +03:00
Oleksandr Bezdieniezhnykh 288aae881d [AZ-964] FAISS index bootstrap for AZ-839 fixture + build flag
AZ-964 SHIPPED — AZ-840 orchestrator test moves past FAISS gate.

Changes:
* tests/e2e/replay/_faiss_seed.py — extracts the empty HNSW32
  seeding logic from scripts/mk_test_faiss_fixture.py into a
  reusable test-infra module: seed_empty_faiss_index(root_dir,
  *, descriptor_dim=512, backbone_label="ultra_vpr") -> Path.
* scripts/mk_test_faiss_fixture.py rewritten as a thin CLI shim
  importing the same helper. compose `tile-init` contract is
  preserved.
* tests/e2e/replay/conftest.py::_build_operator_pre_flight_cache
  now calls seed_empty_faiss_index(cache_root) immediately before
  build_descriptor_index(config), so the factory's _load() finds
  a valid .index + .sha256 + .meta.json triplet at the fixture's
  override root_dir. populate_c6_from_route later in the fixture
  rebuilds the real index once route tiles are downloaded.
* docker-compose.test.jetson.yml: BUILD_PYTORCH_FP16_RUNTIME: "ON"
  added to e2e-runner.environment. Scope creep documented honestly
  in the spec — Tier-2 surfaced this third config gap on the same
  fixture chain while validating AZ-964 (RuntimeNotAvailableError:
  ... the flag is OFF). One-line wiring; the dustynv/l4t-pytorch
  base image bakes the Tegra-tuned PyTorch wheel and
  pytorch_fp16_runtime.py exists, so flag flip is sufficient.

Tier-2 verdict (4F / 48P / 3S / 1XF / 1XP in 86.07s, 0 errors —
was 2 errors before this commit): AZ-840 orchestrator test moves
from ERROR at FAISS gate to SKIP at empty-backbones gate — exactly
the AZ-965 gate AZ-964 AC-3 promised. test_operator_pre_flight_
integration SKIPs cleanly too. The 4 derkachi_1min ESKF-divergence
FAILs are constant across all three runs today (AZ-963 path,
independent of orchestrator chain).

Three Tier-2 runs today on the orchestrator chain:
  i.   pre-AZ-962: SKIP at env-var gate
  ii.  post-AZ-962: ERROR at FAISS gate
  iii. post-AZ-964: SKIP at backbones gate (AZ-965)

Cycle-4 e2e gate still NOT GREEN. Orchestrator chain remaining =
AZ-965 (NetVLAD backbone provisioning); 60s smoke chain remaining
= AZ-963 (ESKF divergence). OKVIS2 deferral directive unchanged.

Pre-existing yamllint false positive on docker-compose.test.jetson
.yml:185 (sibling `volumes:` keys flagged as duplicates without
respecting parent-key scope) — PyYAML parses cleanly with no
duplicates and docker-compose accepts the file at runtime.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 17:02:49 +03:00
Oleksandr Bezdieniezhnykh 763d8b21ad [AZ-962] [AZ-964] [AZ-965] operator_replay.yaml + Tier-2 wiring
AZ-962 SHIPPED — Tier-2 Jetson AZ-840 orchestrator test no longer
SKIPs at the env-var gate. configs/operator_replay.yaml registers
c6/c7/c10/c11 with sane defaults (backbones intentionally empty,
see AZ-965); docker-compose.test.jetson.yml exports
GPS_DENIED_OPERATOR_CONFIG_PATH=/opt/configs/operator_replay.yaml
and bind-mounts ./configs:/opt/configs:ro. ENV_KEY_MAP gains
SATELLITE_PROVIDER_URL → c11_tile_manager.satellite_provider_url
and SATELLITE_PROVIDER_API_KEY → c11_tile_manager.service_api_key
so secrets flow from .env.test and never sit in YAML. README drops
the manual export step. 97/97 c11 + config unit tests stay green.

Tier-2 re-run (4 failed / 48 passed / 1 skipped / 1 xfailed /
1 xpassed / 2 errors in 84.99s vs baseline 3 skipped — i.e. -2
skipped, +2 errors): AZ-840 orchestrator test moves from SKIP to
ERROR with a deeper, real gate — IndexUnavailableError on
FaissDescriptorIndex against a fresh c6_tile_cache.root_dir.

AZ-964 (3 SP, todo/) filed for FAISS index bootstrap in the AZ-839
C3 fixture. AZ-965 (3 SP, todo/, blocked by AZ-964) filed for
NetVLAD ONNX backbone provisioning — the next gate the orchestrator
test will hit once FAISS clears.

Cycle-4 e2e gate remains NOT GREEN: AZ-840 chain is now AZ-964 →
AZ-965 → PASS; 60s smoke chain is AZ-963 → PASS. OKVIS2 deferral
directive (2026-05-29) unchanged — still gated behind Derkachi
e2e green, still NOT MET.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:42:55 +03:00
Oleksandr Bezdieniezhnykh 92ba7997a9 [AZ-962] [AZ-963] Cycle-4 Tier-2 e2e validation NOT GREEN; file 2 follow-up tickets
Ran the Tier-2 Jetson e2e harness on Jetson AGX Orin:
  JETSON_SSH_ALIAS=jetson bash scripts/run-tests-jetson.sh
Result: 4 failed, 48 passed, 3 skipped, 1 xfailed, 1 xpassed in 90.59s.

Two distinct cycle-4 blockers surfaced:

Blocker 1 (AZ-962, 3 SP):
The AZ-840 orchestrator test that should prove the full 7-step
cycle-4 pipeline works was SKIPPED, not PASSed.
docker-compose.test.jetson.yml does not export
GPS_DENIED_OPERATOR_CONFIG_PATH and the operator_replay.yaml the
README references does not exist anywhere in the repo. Epic AZ-835's
'Done' status across its children was validated by doc-content
presence only, never by actual test execution.

Blocker 2 (AZ-963, 3 SP):
4 tests in test_derkachi_1min.py (AZ-265/AZ-404 60s smoke) regress
with EstimatorFatalError('eskf filter divergence: mahalanobis²=212.31
> 100.0') at frame 233. AZ-895 made the CSV-driven path primary; the
CSV path runs open-loop on the Derkachi fixture (no reference C6 tile
cache -> no satellite anchoring -> ESKF integrates open-loop ->
diverges in ~10s). Before AZ-895 the tlog path was primary and
exited cleanly. test_ac3_within_100m_80pct_of_ticks also XPASSed -
third silent-failure surface flagged for investigation in AZ-963.

Filed both as separate Jira Tasks (see local specs in
_docs/02_tasks/todo/AZ-962_*.md and AZ-963_*.md for full payload,
ACs, options, run-log evidence).

OKVIS2 chain (AZ-943/951/952) stays deferred per user 2026-05-29
directive — Derkachi e2e is not green, directive unchanged.

AZ-842 caveat: the AZ-840/AZ-842 'Done' tracker state I set earlier
today (commits 10c2a1e / 2cc992d) is contingent on whether
convention (A) 'In Testing = shipped' or (B) 'Done = shipped+tested'
applies; user-skipped convention question, the leftover at
_docs/_process_leftovers/2026-05-29_jira_status_drift_audit.md holds
the walk-back payload if (A).

No production code changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 13:43:52 +03:00
Oleksandr Bezdieniezhnykh a3dc8e2636 [AZ-961] accuracy_report: rename tlog_path -> ground_truth_path
ReportContext.tlog_path was widened in-place by AZ-959 to mean
"ground-truth source path" without renaming, leaving the rendered
report's "- Tlog: <csv_path>" line cosmetically wrong for CSV
runs. This rename + label fix completes the cleanup.

- helpers/accuracy_report.py: field rename + docstring update +
  rendered line now reads "- Ground truth: <path>" for both
  inputs.
- replay_api/app.py: kwarg updated, AZ-959 inline comment about
  the overload removed (field name now carries the intent).
- tests/unit/test_az699_report_writer.py: fixture updated, two
  new symmetric tests assert the canonical label for tlog AND
  csv inputs (AC-2).
- tests/e2e/replay/_e2e_orchestrator.py +
  test_derkachi_real_tlog.py: kwarg updated.

Tests: 62/62 green across test_az699_report_writer.py,
test_az700_render_map.py, test_az701_replay_api.py.

CSV-replay-input chain (AZ-959 + AZ-960 + AZ-961) is now coherent:
- API accepts (video, csv) with XOR validation
- /static/example-csv serves the AZ-896 reference doc
- Runner dispatches --imu vs --tlog argv
- Report renders with source-agnostic "Ground truth:" label
- Map renders from CSV truth via gps-denied-render-map dispatch

Bookkeeping: AZ-961 spec moved todo/ → done/, dep-table preamble
eighth bump documents the rename + summarises the cycle-4 CSV
chain, state.md records batch 7 complete.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:55:57 +03:00
Oleksandr Bezdieniezhnykh 7f590582cc [AZ-960] render-map: dispatch --truth loader on extension (CSV+tlog)
load_ground_truth_track now dispatches on truth_path.suffix:
- .csv → load_csv_ground_truth (AZ-894)
- else (.tlog, .bin, no ext) → load_tlog_ground_truth (AZ-697)

Removes the AZ-959 short-circuit in SubprocessReplayRunner.
_maybe_render_map so CSV-path replay jobs ship with the same
map.html artefact as tlog jobs. Both ground-truth DTOs expose
row-aligned (lat_deg, lon_deg) records so the renderer needs no
other changes.

Touches:
- src/gps_denied_onboard/cli/render_map.py: dispatch +
  source-agnostic tooltip + --truth CLI help expanded
- src/gps_denied_onboard/replay_api/app.py: workaround removed,
  truth_path resolution picks whichever input was uploaded

Tests: 44/44 green across test_az700_render_map.py +
test_az701_replay_api.py:
- 17 pre-existing render-map tests pass unchanged (AC-2)
- New test_load_ground_truth_track_dispatches_to_csv_loader (AC-1)
- New test_load_ground_truth_track_csv_propagates_schema_error
  (AC-4: malformed CSV raises ReplayInputAdapterError)
- New test_cli_renders_map_with_csv_truth (AC-1 end-to-end)
- AZ-959 test_post_replay_csv_path_returns_200... extended to
  assert map_html_url is now present (AC-3)

Bookkeeping: AZ-960 spec moved todo/ → done/, dep-table preamble
seventh bump documents the landing + AC coverage, state.md records
batch 6 complete with AZ-961 as next.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:53:17 +03:00
Oleksandr Bezdieniezhnykh 363c235264 [AZ-960] [AZ-961] File AZ-959 follow-up tickets as cycle-4 todo/ items
Per user 2026-05-29 directive ("File AZ-960 + AZ-961 and continue
with one of them next"), the two deferred items surfaced during
AZ-959 implementation are now tracked:

- AZ-960 (2pt, todo/): render-map --truth dispatch on extension so
  CSV-path replay jobs ship with a map link. Removes the AZ-959
  short-circuit in _maybe_render_map. Deps: AZ-700 + AZ-894 + AZ-959.
- AZ-961 (1pt, todo/): ReportContext.tlog_path → ground_truth_path
  rename + label fix in rendered report so CSV runs stop saying
  "Tlog: <csv_path>". Deps: AZ-699 + AZ-959.

Sequencing: AZ-960 next (closes the UX gap), AZ-961 after to avoid
re-conflict on _maybe_render_report kwargs.

Touches: 2 local spec files in todo/, dep-table preamble sixth bump
narrative, state.md batch detail update.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:50:45 +03:00
Oleksandr Bezdieniezhnykh 1d18e25cf4 [AZ-959] replay_api: POST /replay (video,csv) + /static/example-csv
Extend the AZ-701 replay_api POST /replay endpoint so AZ-897 (now
in ../ui repo) can drive the AZ-894 CSV-replay path. The endpoint
keeps full back-compat for tlog clients and adds:

- (video, tlog) OR (video, csv) multipart with strict XOR enforced
  at the API boundary (AC-2 / AC-3 → 400 multipart_missing_field)
- validate_csv_kind: rejects malformed CSV schema at boundary by
  scanning the header line for AZ-896 required tokens; messages
  point at csv_replay_format.md (AC-4)
- ReplayInputs DTO: tlog_path / csv_path are now Path | None with
  XOR re-enforced in __post_init__ for internal callers
- JobStorage reserves both input.tlog and input.csv paths; handler
  writes exactly one
- SubprocessReplayRunner.run dispatches --imu vs --tlog argv (AC-1)
- _maybe_render_report dispatches load_csv_ground_truth vs
  load_tlog_ground_truth; CsvGpsFix and TlogGpsFix have
  field-compatible shapes for the GroundTruthRow adapter (AC-6)
- GET /static/example-csv serves the AZ-896 reference CSV; honours
  REPLAY_API_EXAMPLE_CSV_PATH env, falls back to source-checkout
  layout, returns 503 with example_csv_unavailable when neither
  resolves to a readable file. No auth required (AC-5)

Tests: 27/27 unit tests green:
- 18 pre-existing tlog-path tests unchanged (AC-7)
- 9 new tests covering ACs 1-6 + validate_csv_kind isolation

Deferred (NOT silently fixed; reported to user as end-of-turn
notes for scope discipline):

- gps-denied-render-map only consumes binary tlog truth today, so
  CSV-path jobs return map_html_url=None. Extending render-map to
  dispatch on truth-file extension is AZ-700 follow-up territory.
- ReportContext.tlog_path field is now overloaded as the
  "ground-truth source path"; the rendered report still labels
  the line "Tlog: <csv_path>" which is cosmetically misleading
  for CSV runs. Field rename + label fix is AZ-699 follow-up.

Bookkeeping: AZ-959 spec moved todo/ → done/, dep-table preamble
fifth bump documents what landed + what's deferred, state.md
records batch 5 complete and what comes next.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:45:25 +03:00
Oleksandr Bezdieniezhnykh 05fcacffa3 [AZ-943] [AZ-951] [AZ-952] Move OKVIS2 chain back to todo/ as next phase
Per user 2026-05-29 directive: "OKVIS2-related tasks needed to be
implemented after full e2e derkachi flight test would be finished
successfully. So maybe put it back to todo?"

Reasoning accepted. OKVIS2 chain is the planned NEXT phase after
the cycle-4 Derkachi demo lands, not a cycle-5+ deferral. The
2026-05-27 production-default pivot directive remains in force;
today's earlier "deferred to cycle-5+" framing was over-correction
after the AZ-943 spec-reality gap.

- AZ-943 stays HARD-BLOCKED on AZ-951 + AZ-952 (PAUSED preamble
  preserved). Cannot be worked on until both blockers land. Moving
  to todo/ signals "queued, next-after-blockers", not "actionable
  now".
- AZ-951 + AZ-952 are themselves NOT blocked. They ship the
  upstream patches that unblock AZ-943.

Implementation sequence (unchanged): finish cycle-4 demo (AZ-959
+ remaining CSV-replay path) → AZ-951 → AZ-952 → AZ-943 → AZ-944
→ AZ-945. Current implement-batch target stays AZ-959; this
commit is bookkeeping only, does not change what's next on deck.

Touches: 3 file moves (backlog/ → todo/), dep-table preamble
fourth bump narrative documenting the placement reversal.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:34:32 +03:00
Oleksandr Bezdieniezhnykh 5ae352c68d [AZ-897] [AZ-959] Relocate AZ-897 to ../ui, file AZ-959 backend slice
AZ-897 ("Build the first operator-facing UI for the GPS-denied
onboard system") was wrong-shop: the spec named React + Tailwind
but assumed it would land in gps-denied-onboard. The Azaion suite
already has a single React 19 SPA front-end at ../ui per
ui/README.md; spinning up a second React toolchain inside this
repo would have been parallel-pipeline duplication forbidden by
coderule.mdc.

Per user 2026-05-29 directive:

- Jira AZ-897 summary + description rewritten to UI-only scope in
  ../ui (adapted to take CSV + nadir-camera video uploads aligned
  with the AZ-894 CSV path). Full audit comment attached.
- Local AZ-897 spec deleted from this repo's todo/ and re-authored
  into ../ui/_docs/02_tasks/todo/AZ-897_replay_ui_web_form.md
  (left uncommitted there — ../ui repo's next autodev cycle picks
  it up).
- Filed AZ-959 (3 SP, todo/) to extend replay_api POST /replay to
  accept (video, csv) multipart + add GET /static/example-csv.
  Without this endpoint the relocated AZ-897 UI cannot drive the
  CSV-replay path.
- Linked AZ-959 'is blocked by' against AZ-897 Jira-side (verified
  via read-back: AZ-897 issuelinks now includes AZ-959 as blocker
  alongside the existing AZ-894 + AZ-896 dependency links).

Cycle-4 in-repo effort: −5 SP (AZ-897) + 3 SP (AZ-959) = −2 SP
net. AZ-897 itself remains open and active; its 5 SP now belong
to ../ui's cycle (the Jira ticket stays AZ-897 — no renumbering,
no duplicate, no Won't-Fix; just a scope + repo-home correction).

Touches: _docs/02_tasks/_dependencies_table.md (preamble third
bump narrative + AZ-959 row + totals to 184 / 584), _autodev
state pivots to AZ-959 as next implement-batch target. The
../ui-side spec write is intentionally uncommitted in that repo;
surface flagged in the chat summary.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 12:31:39 +03:00
Oleksandr Bezdieniezhnykh e8caa29da6 [AZ-943] [AZ-951] [AZ-952] Pause AZ-943 on OKVIS2 telemetry gap
AZ-943 implementation attempt confirmed the C++ binding cannot satisfy
AC-4 without upstream OKVIS2 patches. The spec's "approach (a)
in-binding subclass workaround" is structurally impossible:
- ThreadedSlam::estimator_ is `private` (not `protected`)
- ViSlamBackend has no public covariance / counts / parallax / MRE
  accessor in the v2 upstream headers
- TrackingState carries only id / isKeyframe / TrackingQuality enum /
  recognisedPlace / isFullGraphOptimising / currentKeyframeId — none
  of the five tracking-stats fields the binding needs

Filed the spec-documented "approach (b)" fallback as two sibling
tickets, both linked Jira-side as `is blocked by` against AZ-943:

- AZ-951 (3 SP): upstream patch — expose 6x6 pose covariance accessor
  (+ ADR-XXX for the AZ-332 Plan-phase pin deviation)
- AZ-952 (3 SP): upstream patch — expose tracking-stats accessor
  (feature counts + parallax + MRE)

AZ-943 transitioned In Progress -> To Do in Jira, full audit comment
attached. Local AZ-943 spec moved todo/ -> backlog/ with PAUSED
preamble; original AC list preserved for the post-unblock turn.

Per user 2026-05-29 confirmation: cycle-4 Derkachi demo target stays
KLT/RANSAC (tests/e2e/replay/conftest.py line 159
c1_vio: strategy: klt_ransac), so AZ-951 + AZ-952 + AZ-943 chain is
correctly deferred. Pivoting next batch to AZ-897 (replay UI form).

Touches: _docs/02_tasks/_dependencies_table.md (preamble + table
rows for AZ-943 paused / AZ-951 / AZ-952 added; totals bumped to
142 product + 41 blackbox-test = 183, 448 product + 133 blackbox
= 581), _docs/_autodev_state.md (sub_step pivot to AZ-897).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 11:48:09 +03:00
Oleksandr Bezdieniezhnykh 42b1db6ace [AZ-842] Batch 04 cycle 4: AZ-835 docs + cycle-4 redesign narrative
Closes AZ-835 Epic C6 (docs) and folds the cycle-4 replay-input
redesign narrative (AZ-894 CSV adapter / AZ-895 auto-sync deprecation
/ AZ-896 format spec / AZ-897 UI follow-up) into the three
authoritative documents.

Modified:
- _docs/02_document/contracts/replay/replay_protocol.md: extend
  Invariant 12 with sub-invariants 12.c (route-driven supersedes
  bbox; ~100x tile efficiency + did-fly-vs-might-fly honesty) and
  12.d (fixture failure-handling: validation/terminal re-raise;
  transient -> C11 backoff x3). Add Invariant 14 with sub-
  invariants 14.a-14.d covering the single canonical clock model,
  the CSV-driven path, the tlog adapter's audit-only role, the
  auto-sync deprecation, and the AZ-897 UI follow-up pointer.
- _docs/02_document/architecture.md: add the AZ-777 Phase 3+
  superseded-by-Epic-AZ-835 supersession block + new "Replay input
  redesign (cycle 4)" sub-section with the cycle-4 ticket table.
- tests/e2e/replay/README.md: top section restructured for two
  distinct entry points (AZ-265/AZ-404 vs. AZ-835/AZ-840); add
  full AZ-835 orchestrator-test section (env vars, skip gates,
  expected runtime, verdict report path); add Imagery (c) Google
  attribution + dev-only caveat; add Epic AZ-835 ticket map.

Spec deviation: AC-1b says "new Invariant 13" but Invariant 13 is
already taken (C4<->C5 pairing, AZ-776 / ADR-012), and is referenced
by number in architecture.md, c4_pose description.md, and ADR-012
prose. Cycle-4 content shipped as Invariant 14 to preserve those
cross-references; renumbering would have cascaded to 3 files outside
AZ-842's ownership envelope. Documented in batch report.

Out-of-scope hygiene gap (NOT fixed in this batch):
BUILD_CSV_REPLAY_ADAPTER flag is not yet enumerated in
_docs/02_document/module-layout.md's Build-Time Exclusion Map.
Inherited from cycle-4 AZ-894. Suggested as a cycle-5+ hygiene PBI.

AZ-835 epic file stays in todo/ until AZ-841 (backlog) is resolved.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 11:13:33 +03:00
Oleksandr Bezdieniezhnykh e367b07e3b [AZ-943] Import OKVIS2 binding spec + dep table; autodev state trim
Imports AZ-943 (OKVIS2 binding: real ThreadedSlam wiring; AZ-592 split
1/3, 5pt) from Jira into a local task spec at
_docs/02_tasks/todo/AZ-943_okvis2_threadedslam_binding.md so the
implement skill batch loop has the input it needs.

Dependency table: +AZ-943 row, +preamble entry, totals 180→181 tasks /
570→575 SP. AZ-944 + AZ-945 stay Jira-only this session per the
AZ-943→AZ-944→AZ-945 Blocks chain (their local specs land when their
Implement turns come up).

State file trimmed from 52 lines to schema-compliant 13 lines per
.cursor/skills/autodev/state.md (sub_step.detail must be a one-line
pointer, not a logbook). Resume context lives in the new task spec +
git log of 94d2358 (AZ-918..AZ-922 baseline fixes).

Per AZ-942 + AZ-923 are parked (state file's "Open Items At Pause" is
recorded in git log via this commit's body; not retained in state file
going forward).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 10:50:18 +03:00
Oleksandr Bezdieniezhnykh 007aa36fbf [AZ-895] Deprecate replay auto-sync surface; file AZ-908 follow-up
Option A (minimum-deprecation, 2 SP) per user complexity-budget
decision. Auto-sync stays importable as a raising stub for one cycle
so external callers see a clean ReplayInputAdapterError instead of an
ImportError. Full physical removal is filed as AZ-908 (cycle-5+ backlog).

Production:
- auto_sync.py: 700+ LOC -> 56-line no-op stub raising
  "auto-sync removed; supply --imu CSV instead"
- tlog_video_adapter.py: 700+ LOC -> 105-line deprecated stub;
  ReplayInputAdapter.open() raises immediately, close() is a no-op
- _replay_branch.py: dropped legacy auto-sync branch +
  _build_auto_sync_config; _validate_replay_paths now requires
  imu_csv_path; replay_input_adapter_factory parameter removed
- cli/replay.py: --time-offset-ms / --skip-auto-sync / --auto-trim
  emit DeprecationWarning + stderr line; values ignored
- tlog_replay_adapter.py + tlog_ground_truth.py docstrings: AUDIT-ONLY

Tests:
- DELETED test_az405_auto_sync, test_az405_replay_input_adapter,
  test_az698_window_alignment (covered code no longer runs)
- ADDED test_az895_auto_sync_deprecated_stub (5 parametrised, pins AC-1)
- test_az402_replay_cli: deprecation warnings + ignored-value asserts
- test_az401_compose_root_replay: new imu_csv_path-required gate;
  deleted the calibration-loading test that relied on the removed
  replay_input_adapter_factory injection point
- test_derkachi_real_tlog: xfail reason refreshed to AZ-848 + AZ-883
  (AC-4 "AZ-848-scoped reason")

Docs:
- module-layout.md: replay_input file list flags deprecated modules,
  adds csv_ground_truth.py
- _dependencies_table.md: +AZ-908 row, preamble + totals updated
  (179 -> 180 tasks, 567 -> 570 SP)
- AZ-908 backlog spec added; AZ-895 spec moved todo -> done
- batch_03_cycle4_report.md written

Touched-module tests green (111 passed, 1 skipped). Full unit suite
green: 2287 passed, 85 skipped, 1 deselected (pre-existing flaky perf
test, unrelated).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 22:09:59 +03:00
Oleksandr Bezdieniezhnykh 6be207cef3 [AZ-894] [AZ-896] Add CSV-driven replay adapter + format docs
Replaces the tlog two-clock replay surface with a single-clock path
driven by the Derkachi-schema CSV. --imu is the new required CLI arg;
--tlog stays as a deprecated alias (warned + ignored when --imu set)
until AZ-895 deletes it.

* csv_ground_truth.py parses the 15-column schema, fails fast at
  startup on every documented schema fault (AC-5).
* CsvReplayFcAdapter slots into ReplayInputBundle.fc_adapter alongside
  the tlog sibling; mirrors Invariant-5 outbound wiring; inbound bus is
  intentionally a no-op since the loop reads CSV directly.
* _run_replay_loop branches on imu_csv_path, stamps
  VioOutput.emitted_at_ns from the CSV-derived frame_end_ns (AC-4),
  closing the AZ-848 two-clock surface for the new path.
* AZ-896 ships the operator-facing format spec at
  _docs/02_document/contracts/replay/csv_replay_format.md plus a
  20-row example CSV (AC-3 regression-locked).

Tests: 11 + 12 new unit tests, plus updates to AZ-401 import-boundary
and AZ-402 CLI suites. Full unit suite 2,327 passed / 86 skipped.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 18:40:29 +03:00
Oleksandr Bezdieniezhnykh aa8b9f2ee9 [AZ-899] [AZ-900] [AZ-901] Baseline doc + retro gate + EVIDENCE_OUT fix
AZ-899: create _docs/02_document/architecture_compliance_baseline.md
seeded with 0 violations and the 2026-05-20 structural snapshot facts
(15 inventory entries, 0 import cycles, 5 contract files). Documents
the append-on-violation / mark-resolved-on-fix / snapshot-refresh
protocol so cumulative reviews can emit Baseline Delta sections.
Closes cycle-1 retro Top-3 #3 (third attempt).

AZ-900: codify LESSONS 2026-05-26 [process] in
.cursor/skills/autodev/flows/existing-code.md - Re-Entry After
Completion now hosts a Previous-Cycle Retro Existence Gate that
BLOCKS the cycle increment if no _docs/06_metrics/retro_*.md file
dated within [cycle_start, cycle_end] exists. Skipped on
state.cycle == 1. Presents Choose A (author retro) / B (stub +
leftover) / C (abort). state.md - Session Boundaries gains a
cross-reference bullet.

AZ-901: fix e2e/runner/conftest.py:56 EVIDENCE_OUT default - host
pytest now resolves <repo_root>/e2e-results/evidence/ instead of
/e2e-results/evidence (container-only path; crashed on macOS / non-
root Linux). Docker + Jetson harnesses unaffected (they pass
--evidence-out explicitly). Verified locally: 24 SKIPPED, exit 0,
evidence written. Closes leftover 2026-05-26_evidence_out_default_path.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 17:18:54 +03:00
Oleksandr Bezdieniezhnykh 940066bee2 chore: WIP pre-implement
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 17:09:13 +03:00
Oleksandr Bezdieniezhnykh fd52cc9b1d [AZ-845][AZ-846][AZ-847] Refactor 02: relocate RouteSpec + widen lint
Cycle-3 refactor run 02-az507 (RouteSpec relocation + module-layout
refresh + AZ-270 lint widening). Single batch of 3 tasks; epic AZ-844.

AZ-845 — Relocate RouteSpec DTO to _types/route.py (rule-9 fix):
  * New canonical home: src/gps_denied_onboard/_types/route.py
    (frozen+slots dataclass; full docstring carried over verbatim).
  * c11_tile_manager/route_client.py imports from _types.route.
  * replay_input/tlog_route.py and replay_input/__init__.py keep
    re-exports for backward-compat (RouteSpec in __all__).
  * 5 test files updated to import from _types.route for symmetry.
  * Identity-preserving re-export verified by new test
    test_az845_routespec_canonical_home_and_reexport_identity.

AZ-846 — Refresh module-layout.md cycle-3 entries:
  * c11_tile_manager Internal list rewritten with all 8 internals
    (alphabetised) — corrects a stale entry that referenced files
    (satellite_provider_*.py) that no longer exist.
  * shared/replay_input file list adds errors.py (cycle-2 carry),
    tlog_ground_truth.py (cycle-2 carry), tlog_route.py (cycle-3 NEW).
  * shared/_types section registers route.py with provenance line.
  * Out-of-scope cycle-2 carry-overs (replay_api/, cli/render_map.py,
    helpers/gps_compare.py, etc.) intentionally untouched.

AZ-847 — Widen test_az270 lint to enforce full rule-9 allow-list:
  * test_ac6_only_compose_root_imports_concrete_strategies now walks
    every components/<X>/*.py ImportFrom/Import and rejects anything
    not in the rule-9 allow-list (own subpackage + _types + helpers
    + config/logging/fdr_client/clock + frame_source interface-only).
  * Strict superset of the original AC-6 narrow check.
  * Reports zero violations on the codebase post-AZ-845.
  * Two principled carve-outs documented in the test docstring:
    - components/<X>/bench/** path skip (measurement code legitimately
      constructs production strategies via runtime_root factories).
    - register_* lazy self-registration imports from
      runtime_root.<X>_factory (central-registry plugin pattern).
  * Both carve-outs surfaced to user via Choose A/B/C/D Risk-1
    protocol; user skipped both — agent proceeded with documented
    defaults. Doc-only follow-up tracked in
    _docs/_process_leftovers/2026-05-24_az847_rule9_wording_followup.md
    for rule-9 wording update in module-layout.md.

Test results: 2287 passed, 90 skipped (environmental — Docker / CUDA
/ TensorRT / Jetson hardware / fixtures), 0 failed. Focused subset
(replay_input/ + c11_tile_manager/ + test_az270_compose_root.py)
also clean: 169 passed, 1 skipped.

Tracker: AZ-845/846/847 transitioned In Progress -> In Testing.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 10:07:20 +03:00
Oleksandr Bezdieniezhnykh 9dc04cc677 Update autodev state and dependencies table for Phase 2 progress
ci/woodpecker/push/02-build-push Pipeline failed
- Changed autodev state sub_step to reflect new phase and task details: updated phase from 7 to 2, renamed task to 'refactor-analysis-gate', and revised detail to indicate the creation of new tasks AZ-844, AZ-845, AZ-846, and AZ-847, awaiting Phase-2 gate.
- Updated dependencies table with the latest task counts and complexity points, reflecting the addition of new tasks and the closure of AZ-777 in Jira. Total tasks now stand at 173 with 557 complexity points.
2026-05-23 17:11:50 +03:00
Oleksandr Bezdieniezhnykh ade0c86f2b [AZ-840] [AZ-835] e2e orchestrator test (E-AZ-835 C4)
Wraps the AZ-699 verdict-report path with the AZ-839
operator_pre_flight_setup C3 fixture so a single Tier-2 test
takes only (tlog, video, calibration) and runs the full 7-step
pipeline on the Jetson harness without operator hand-curation.

New surface (tests-only, no src/ changes):
- tests/e2e/replay/_e2e_orchestrator.py — orchestrator with
  OrchestratorStep enum, OrchestrationFailure exception (step
  prefix per AC-5), OrchestrationReport dataclass,
  write_effective_replay_config helper, and
  run_e2e_orchestration entry point covering steps 1-2-6-7.
- tests/e2e/replay/test_e2e_orchestrator_unit.py — 17 unit
  tests covering each failure mode + happy path with mocked
  subprocess + ground-truth loader (AC-8).
- tests/e2e/replay/test_az835_e2e_real_flight.py — Tier-2 +
  RUN_REPLAY_E2E gated integration test asserting verdict
  report exists, 15-min budget held (AC-1, AC-2, AC-3, AC-4,
  AC-6).

The effective config write overlays c6_tile_cache.root_dir
onto the static operator YAML at runtime so the airborne
subprocess shares the cache_root the C3 fixture chose. Field-
level merge — every other operator-config block stays
verbatim. The static YAML on disk is never touched.

Test run: tests/e2e/replay 45 passed, 10 skipped (10 skips
were 9 pre-existing + 1 new tier2). No src/ touched, no
AZ-839 driver changes; AC-7 (AZ-699 still passes) holds by
inspection.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 15:27:41 +03:00
Oleksandr Bezdieniezhnykh bfcac2cb9f [AZ-839] [AZ-835] operator_pre_flight_setup real fixture (E-AZ-835 C3)
Replace the placeholder operator_pre_flight_setup pytest fixture (the
mkdir stub at tests/e2e/replay/conftest.py:293-310) with a real driver
that wires C1 (AZ-836 RouteSpec) + C2 (AZ-838 SatelliteProviderRoute
Client) + C11 (AZ-316 HttpTileDownloader) + C10 (AZ-322 Descriptor
Batcher) end-to-end and yields a typed PopulatedC6Cache. AZ-306 FAISS
sidecar triple-consistency is verified post-rebuild via a caller-
supplied descriptor_index_factory; partial sidecars are cleaned up on
failure (AC-7) while pre-existing warm-cache files are preserved.
Algorithm lives in tests/e2e/replay/_operator_pre_flight.py with
pure dependency injection so the AC-8 unit suite (11 tests covering
happy / transient-retry / terminal-failure / validation-error /
tamper-detection / cleanup-on-failure) runs against stubs and the
AC-9 Tier-2 integration test runs the same algorithm against the
real Jetson harness. The conftest fixture skip-gates on RUN_REPLAY
_E2E + SATELLITE_PROVIDER_URL/API_KEY + BUILD_FAISS_INDEX +
GPS_DENIED_OPERATOR_CONFIG_PATH and wires deps through the existing
runtime_root factories. Supersedes AZ-777 Phase 3.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 15:08:34 +03:00
Oleksandr Bezdieniezhnykh 0ed1a5d988 [AZ-835] [AZ-777] Decompose Epic into C3-C6 + close AZ-777
AZ-839 (C3, 5pt) operator_pre_flight_setup real fixture: wire
C1+C2+C11+C10, supersedes AZ-777 Phase 3 (route-driven, not bbox).
AZ-840 (C4, 3pt) E2E orchestrator test ingesting raw
(tlog, video, calibration), runs steps 1-7 end-to-end on Jetson.
AZ-841 (C5, 1pt) Un-xfail AZ-777 AC-4 + AC-5 once C3 + C4 land.
AZ-842 (C6, 2pt) Docs: replay_protocol Invariant 12 + architecture
+ orchestrator-test README.

AZ-777 transitioned to Done in Jira (Phases 1+2 shipped batches
104-106; Phases 3-5 superseded per 2026-05-22 route-driven
directive). Closure comment 11177 added with phase-by-phase status.
Local spec moved todo/ -> done/ with a status banner at the top.

Dependencies table preamble bumped to 173 tasks / 557 SP and a
2026-05-23 entry prepended. Autodev state sub_step.detail set to
"batch 108 next; AZ-839 C3".

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 14:02:53 +03:00
Oleksandr Bezdieniezhnykh c3a1ebc754 [AZ-838] SatelliteProviderRouteClient + seed_route.py CLI (E-AZ-835 C2)
ci/woodpecker/push/02-build-push Pipeline failed
Operator-side HTTP client + CLI that takes a RouteSpec from AZ-836
and onboards it via satellite-provider's POST /api/satellite/route:
pre-emptive AZ-809 validation, request submission, polling until
mapsReady, and POST /api/satellite/tiles/inventory verify.

Lives in c11_tile_manager (shared parent-suite HTTP/JWT plumbing,
shared BUILD_C11_TILE_MANAGER gate); error hierarchy split off
SatelliteProviderRouteError to keep the tile path and route path
independent. 30 unit tests + 1 RUN_E2E-gated integration test.

Pre-emptive validator tracks the actual AZ-809 server bounds
(points [2,500], zoom [0,22]) instead of the AZ-838 spec's narrower
client-only bounds; flagged as F1 in batch_107_cycle3_report.md
for user decision (accept-and-update-spec / revert-to-spec).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 13:29:45 +03:00
Oleksandr Bezdieniezhnykh 5e52779056 [AZ-836] TlogRouteExtractor: tlog -> RouteSpec for Epic AZ-835 C1
First building block of Epic AZ-835. Pure function that consumes
an ArduPilot binary tlog and returns a RouteSpec (waypoints +
per-waypoint coverage radius + provenance) suitable for posting
to satellite-provider's POST /api/satellite/route endpoint.

Pipeline:
- Load GPS fixes via existing load_tlog_ground_truth (AZ-697).
- Trim leading + trailing rows below takeoff thresholds
  (speed >= 2 m/s AND AGL >= 5 m by default; configurable).
- Coarsen to <= max_waypoints via iterative Douglas-Peucker on
  the local-ENU projection (WgsConverter.latlonalt_to_local_enu,
  AZ-279). DP tolerance is caller-supplied or binary-searched
  (<= 32 iterations, <= 1 m convergence).

Public surface (re-exported from replay_input/__init__.py):
- RouteSpec (frozen, slots, with provenance fields).
- RouteExtractionError (subclass of ReplayInputAdapterError).
- extract_route_from_tlog().

Tests: 14 unit tests cover AC-1..AC-10 plus edge cases (custom
DP tolerance, invalid inputs, error hierarchy, too-short segment).
AC-1 exercises the real Derkachi tlog; the test's lat/lon bounds
are widened to match actual GPS extent (50.0800..50.0840 /
36.1070..36.1145) — the AZ-836 spec's tighter IMU-derived bounds
(50.0808..50.0832 / 36.1070..36.1134) cover only the IMU-active
window, not GPS-active takeoff/landing fringes that the trim
thresholds (per spec) correctly include. See
_docs/03_implementation/batch_106_cycle3_report.md "Spec drift
surfaced" for the full note.

Semantics decision documented inline: max_waypoints is enforced
only in auto-tolerance mode; with an explicit DP tolerance the
result reflects that exact tolerance.

AZ-836 moved to done/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 13:09:38 +03:00
Oleksandr Bezdieniezhnykh 63c0217e3d [AZ-835] Epic split (C1/C2) + workspace-boundary rule expansion
AZ-835 Epic (E2E real-flight validation pipeline, ~17 SP across
6 children C1-C6) supersedes AZ-777 Phase 3+ (bbox-based static
seed). Children C3-C6 deliberately not yet filed — will be
re-estimated after C1+C2 land from real RouteSpec shape and
Route API client ergonomics.

- AZ-836 (C1, 3 SP): TlogRouteExtractor — pure function over
  .tlog binary returning RouteSpec (waypoints + suggested
  region size). Deps: AZ-697 (load_tlog_ground_truth, done),
  AZ-279 (WGS converter, done).
- AZ-838 (C2, 3 SP): SatelliteProviderRouteClient + seed_route.py
  CLI mirror of seed_region.py. Hard-depends on AZ-836's
  RouteSpec dataclass.
- _dependencies_table.md updated with the three new rows.

Workspace-boundary rule expansion: codifies the sibling-repo
task-spec exception (the only permitted write into a sibling
repo) and the "External Systems Are Black Boxes" rule
(contract-only consumption of producer repos like
satellite-provider).

Bookkeeping: _autodev_state.md condensed to <30 lines per the
state.md conciseness rule; opencv-pin leftover replay
re-checked 2026-05-22 (gtsam still only 4.2, replay condition
unchanged).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 17:39:38 +03:00
Oleksandr Bezdieniezhnykh b15454b9a9 [AZ-777] Phase 1 hotfix (z/x/y) + Phase 2 Derkachi seed + ops
Phase 1 hotfix:
- C11 HttpTileDownloader adapted to satellite-provider v2.0.0
  z/x/y inventory contract (bulk POST keyed by slippy-map coords).
- Unit tests rewritten to exercise the new inventory schema.
- E2E smoke test updated to match the v2.0.0 wire.

Phase 2 (Derkachi seed + smoke-validated on Jetson):
- tests/fixtures/derkachi_c6/{README,bbox.yaml,seed_region.py}
  drives POST /api/satellite/region against satellite-provider
  with Google Maps as the imagery source. Smoke run produced
  4 regions, 175 tiles, inventory 32/32.
- scripts/mint_dev_jwt.py + run-tests-jetson.sh auto-mint and
  export SATELLITE_PROVIDER_API_KEY using JWT_SECRET / JWT_ISSUER
  / JWT_AUDIENCE env vars (no host port mappings; e2e-runner
  reaches SP via internal docker network only).

Spec amendment: AZ-777 todo spec updated to record the
Google Maps imagery source decision and STOP-gate state.

AZ-777 Phase 3+ work is superseded by Epic AZ-835 (see next
commit).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 17:39:21 +03:00
Oleksandr Bezdieniezhnykh 544b37fdc9 [AZ-777] Refresh spec to match codebase reality (cycle-3 batch 104)
Cycle-3 /autodev session discovered material drift between the prior
session's rewritten AZ-777 spec and current codebase reality. Refreshed
the spec, re-synced Jira (description + summary updated, status
unchanged at In Progress), appended an addendum to the 2026-05-21
decision log capturing the findings, and slimmed the state file to
the conciseness rule.

Findings reconciled:
- Tier-1 (docker-compose.test.yml) is deprecated per 2026-05-20 env
  policy; original Phase 1 mods there are out of scope.
- Jetson compose ALREADY has satellite-provider + satellite-provider
  -postgres services (lineage AZ-688 / AZ-691 / AZ-692). No new
  service definitions needed; only e2e-runner env block.
- Port / protocol: 8080 HTTPS (self-signed dev cert), not 5101 HTTP.
- C11 contract drift: _LIST_PATH/_GET_PATH constants in
  tile_downloader.py don't match the real /api/satellite/tiles
  /inventory + /tiles/{z}/{x}/{y} endpoints. Phase 1 now includes
  C11 contract adaptation (the largest single sub-deliverable).
- arm64 manifest of mcr.microsoft.com/dotnet/aspnet:10.0 verified;
  Risk 3 closed.
- mock-sat retired from Jetson + D-PROJ-2 /api/satellite/upload
  shipped on parent; mock-sat retention closed.

8-pt complexity unchanged. Single-ticket containment preserved.
Phase boundaries (STOP gates) preserved. No code changed yet —
this commit is spec / state / decision-log only; next /autodev
session executes Phase 1.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 14:17:03 +03:00
Oleksandr Bezdieniezhnykh 1198890b74 [AZ-777] Rewrite spec: real satellite-provider + production C10/C11
Original spec called for direct OSM/CARTO downloads, contradicting
architecture (C11 owns tile network I/O against parent-suite
satellite-provider .NET 8 service; C10 batches descriptors over the
populated C6, never touches the upstream). Rewritten spec drives the
production C10/C11 pipeline against the real satellite-provider
running in docker-compose.test.yml, replacing the mock-suite-sat-
service GET stub. Complexity 5 -> 8 pts (single-ticket override).
Decision log: _docs/_process_leftovers/2026-05-21_az777_complexity_
override.md. Jira AZ-777 description + summary synced. Autodev state
pauses for next session to pick up Phase 1 (satellite-provider
stand-up + smoke test).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 13:57:01 +03:00
Oleksandr Bezdieniezhnykh 2b53168142 [AZ-776] Archive task spec to done/ after In Testing transition
ci/woodpecker/push/02-build-push Pipeline failed
Closes batch 103 cycle3.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 13:40:48 +03:00
Oleksandr Bezdieniezhnykh 6044a33197 chore: WIP pre-implement
Bundled hygiene commit before cycle-3 /implement (AZ-776, AZ-777). Mixes
two concerns by user choice (autodev option B):

- Cycle-3 autodev artifacts not yet committed by Step 9 (new-task):
  task specs for AZ-776 / AZ-777 under _docs/02_tasks/todo/ and the
  updated _docs/02_tasks/_dependencies_table.md.
- Accumulated skill / rule tooling maintenance under .cursor/ (skills:
  autodev, code-review, decompose, deploy, implement, new-task, plan,
  refactor, retrospective, test-spec; rules: coderule, cursor-meta,
  meta-rule, testing; new release skill scaffolding).
- Autodev bootstrap state: _docs/_autodev_state.md (step 10 in_progress)
  and _docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md
  (replay timestamp refreshed; gtsam 4.2 still numpy<2-only).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 13:14:11 +03:00
Oleksandr Bezdieniezhnykh 7d53cef0cf [AZ-701] HTTP replay API service (FastAPI + magic-byte upload validation)
ci/woodpecker/push/02-build-push Pipeline failed
New replay_api component: FastAPI service wrapping the offline
gps-denied-replay pipeline. POST tlog+video (multipart) → either
sync 200 with result/map/report URLs, or async 202 + job id with
/jobs/{id} polling. Magic-byte validation, bearer auth, in-memory
JobRegistry with concurrency + queue caps (429 on overflow).

Helper accuracy_report.py promoted from tests/ to src/ because the
API needs the Markdown report writer at runtime; all AZ-699 imports
re-pointed. OpenAPI spec exported to docs.

18/18 unit tests pass (AC-1 sync, AC-2 async, AC-3 state machine,
AC-5 auth, AC-6 health, AC-8 concurrency, AC-9 magic-byte). Full
unit suite: 2251 pass, 86 skip, 1 pre-existing C12 cold-start flake
(unchanged). mypy --strict clean on the new surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 17:30:26 +03:00
Oleksandr Bezdieniezhnykh b66b68ff76 [AZ-700] gps-denied-render-map: HTML map of estimated vs truth tracks
New operator-side console-script renders a self-contained HTML map
(folium / Leaflet) comparing the estimator's JSONL track against
the tlog ground-truth track. Pinned visual style: red truth + blue
estimated polylines, start/end markers per track, 100 m + 50 m
scale circles, optional AZ-699 accuracy-summary banner, and an
--offline-tiles mode (with optional local tile-URL template) for
Jetsons without internet.

folium is gated behind a new [operator-tools] optional-dep so the
airborne binary's cold-start NFR is unaffected (C12 binary doesn't
import the new module). 14 new unit tests pin polyline count,
marker count, scale-circle radii, summary embedding, offline-tile
behaviour, and full CLI smoke. Zero mypy --strict errors.

Refines the 2026-05-20 Jetson-only test policy: unit tests may run
locally, e2e/perf/resilience/security stay Jetson-only. Documented
in _docs/02_document/tests/environment.md (Where each tier runs)
and .cursor/rules/testing.mdc (Test environment for this project).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 17:04:01 +03:00
Oleksandr Bezdieniezhnykh dcde602f61 [AZ-699] Real-flight validation runner + Markdown accuracy report
New e2e test runs gps-denied-replay --auto-trim against the real
derkachi.tlog + flight video + AZ-702 calibration, computes the
horizontal-error distribution (mean/p50/p95/p99 + 10/25/50/100 m
threshold-hit share), writes _docs/06_metrics/real_flight_
validation_{date}.md, and asserts honest PASS/FAIL with no @xfail
mask. AZ-404's 1-min test is untouched (sibling, not replacement).

Extends gps_compare.py with HorizontalErrorDistribution +
percentile_sorted (numpy-equivalent linear interpolation). New
test helper _report_writer.py renders the canonical Markdown
schema documented as FT-P-20 in blackbox-tests.md.

16 new unit tests pin distribution arithmetic, verdict gate,
failure-message templating (references calibration acquisition
method per AC-3), and report layout. 129 passed in focused
regression, 3 skipped (real video / Tier-2 prerequisites).
Zero new mypy --strict errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:53:48 +03:00
Oleksandr Bezdieniezhnykh 87fe98858f [AZ-698] Tlog trim + mid-flight alignment for replay
Adds find_aligned_window cross-correlation (NCC, per-window unit norm)
between IMU energy and video optical-flow magnitude. Returns
AlignedWindow{tlog_start_ns, tlog_end_ns, offset_ms, confidence,
used_fallback}, with fallback to head-takeoff on low confidence to
preserve AZ-405 behavior. TlogReplayFcAdapter honors tlog_start_ns and
skips pre-window messages. New --auto-trim CLI flag, mutex with
--time-offset-ms. AC-1..AC-4 covered by unit tests; AC-5 skipped (no
real flight_derkachi.mp4 in repo). 106 tests pass in regression slice.
Zero new mypy --strict errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:29:59 +03:00
Oleksandr Bezdieniezhnykh 64d961f60c [AZ-697] [AZ-702] tlog GPS truth + KHP20S30 factory calibration
Batch 98 (cycle 2) — first two PBIs of epic AZ-696 (real-flight
validation harness):

AZ-697: direct binary-tlog GPS-truth extractor

- New src/gps_denied_onboard/replay_input/tlog_ground_truth.py reads
  GLOBAL_POSITION_INT (with GPS_RAW_INT fallback) from a binary
  ArduPilot tlog via pymavlink.mavutil and returns a frozen+slotted
  TlogGroundTruth DTO with per-record ts_ns / lat_deg / lon_deg / alt_m
  / hdg_deg / vx_m_s / vy_m_s / vz_m_s.
- Promoted l2_horizontal_m + match_percentage + GroundTruthRow from
  tests/e2e/replay/_helpers.py into the new production module
  src/gps_denied_onboard/helpers/gps_compare.py. The e2e helper now
  re-exports the same objects (identity, not copies) so existing test
  imports continue working untouched.
- tests/e2e/replay/conftest.py prefers the real derkachi.tlog when
  present, falls back to the CSV synth path otherwise.
- 22 new unit tests cover AC-1..AC-5 (mypy --strict subprocess test
  included). All passing.

AZ-702: Topotek KHP20S30 factory-sheet camera calibration

- New _docs/00_problem/input_data/flight_derkachi/khp20s30_factory.json:
  fx = fy = 4644.444, cx = 960, cy = 540, HFOV ~ 23.3 deg, VFOV ~ 13.2
  deg, computed from the published 8.5 mm focal length + 1/2.8" sensor
  + 1920x1080 capture at lowest zoom step. Distortion zeroed,
  body_to_camera_se3 = identity with nadir convention. Acquisition
  method explicitly recorded as factory_sheet so downstream code can
  expect higher residual error than a lab calibration.
- _docs/00_problem/input_data/flight_derkachi/camera_info.md updated
  to document the assumptions, expected residual error window, and
  conftest pick-up rule.
- tests/e2e/replay/conftest.py::_calibration_path() prefers
  khp20s30_factory.json when present, falls back to adti26.json.
- 9 new unit tests cover AC-1..AC-4 (schema, intrinsics traceback,
  doc reference, conftest pick-up). All passing.

Test run: 45 new tests, all passing. Full-suite gate deferred to
Step 16 (after the last batch in cycle 2 per the implement skill).

Adjacent note (not fixed in this batch, recorded in the batch report):
auto_sync.py has the same redundant pymavlink type:ignore + a few
numpy/cv2 mypy --strict issues. None on this batch's path.

Refs: _docs/03_implementation/batch_98_cycle2_report.md
Refs: _docs/02_tasks/done/AZ-697_tlog_ground_truth_extractor.md
Refs: _docs/02_tasks/done/AZ-702_khp20s30_calibration.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:09:03 +03:00
Oleksandr Bezdieniezhnykh a12638dd92 [AZ-696] chore: cycle-2 bootstrap — gitignore tlog inputs, Step 9 PBIs
Pre-implement chore commit to land orchestration artifacts produced by
autodev cycle-2 Step 9 (New Task), so that Step 10 (Implement) starts
against a clean working tree.

What's included:

- .gitignore: exclude _docs/00_problem/input_data/**/*.{tlog,mp4,h264}
  (derkachi.tlog is a 5.8 MB binary input and stays out-of-band).
- _docs/02_tasks/todo/AZ-697..AZ-702: 6 new PBI specs under epic AZ-696
  (tlog ground-truth extractor, mid-flight trim+align, real-flight
  validation runner, replay map viz, HTTP replay API, KHP20S30 calib).
- _docs/02_tasks/_dependencies_table.md: dep edges for the 6 PBIs.
- _docs/_autodev_state.md: status -> in_progress, step 10 cycle 2.
- _docs/_process_leftovers/...opencv_pin_deferred.md: replay-attempt
  timestamp refreshed (gtsam-numpy-2 wheels still not published;
  leftover remains open).

No source code is modified by this commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 15:50:50 +03:00
Oleksandr Bezdieniezhnykh 9bdc868dfd [AZ-687] Guard build_pre_constructed seeds in replay mode
Replay CLI synthesizes a minimal Config whose `components` mapping
omits the strategy-component blocks (`c6_tile_cache`, `c7_inference`,
`c5_state`) the airborne bootstrap historically read unconditionally.
Add `_replay_omits_component_block` and gate the c6 seeds, the c7 +
c3_lightglue_runtime pair, and the c5 (estimator, handle) eager build
on `config.mode == "replay" AND block absent`. Live mode and any
replay config that DOES populate the blocks remain unchanged — the
guard is conditional, not blanket.

The skip is safe because compose_root's per-component wrappers only
run for slugs in `config.components`; absent blocks mean absent
wrappers, so the seeded slots would never be read. Fix lives at the
BUILD-PRE-CONSTRUCTED layer per the spec's explicit "no silent fallback
in `_c6_config`" constraint.

Covers AC-687-1 / AC-687-2 / AC-687-4. AC-687-3 (Jetson Tier-2 e2e
replay) requires an out-of-band hardware re-run; evidence destination
documented in autodev state.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 12:22:03 +03:00
Oleksandr Bezdieniezhnykh 2be1b5101e [AZ-687] [autodev] File replay-mode guard task + Tier-2 evidence
Jetson Tier-2 e2e on 2026-05-19 11:27 surfaced a NEW gap one phase
deeper than where Rerun 3 died: build_pre_constructed seeds
c6_descriptor_index unconditionally, which reads
config.components["c6_tile_cache"] via storage_factory._c6_config.
The replay CLI synthesizes a Config that has no c6_tile_cache
block, so AC-1/2/5/6 fail with KeyError 'c6_tile_cache'.

Bootstrap (no source code changes):
- AZ-687 (Story, To Do, 2pt, Epic AZ-602; blocks AZ-618)
- Task spec in _docs/02_tasks/todo/
- _dependencies_table.md row + header narrative
- _docs/_autodev_state.md detail repointed at AZ-687
- _docs/03_implementation/jetson_runs/ Tier-2 evidence

The fix itself lives in batch 97 (next session): guard the c6/c7
seeds at the BUILD-PRE-CONSTRUCTED layer when config.mode ==
"replay". Per existing storage_factory._c6_config docstring the
silent-fallback path is explicitly rejected — the bootstrap layer
is the right seam.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 11:53:14 +03:00
Oleksandr Bezdieniezhnykh c3639a5d1c [AZ-624] [AZ-618] Phase F: wire build_pre_constructed into main()
Wire register_airborne_strategies + build_pre_constructed +
compose_root(config, pre_constructed=...) into runtime_root.main(). The
existing exception block now catches AirborneBootstrapError distinctly
before the broader (ConfigurationError, StrategyNotLinkedError,
RuntimeError) clause so the operator-facing "airborne_bootstrap:"
prefix carried by every bootstrap error reaches stderr cleanly with
EXIT_GENERIC_FAILURE rather than getting absorbed into a generic
backtrace.

This closes the AZ-618 umbrella: AZ-619..AZ-623 + AZ-625 had built
each pre_constructed key; this batch lands the integration that the
production main() actually invokes them. Both the live
gps-denied-onboard and replay gps-denied-replay binaries dispatch
through this main() per ADR-011, so both reach takeoff with
pre_constructed populated end-to-end.

Tests: tests/unit/runtime_root/test_az618_pre_constructed.py adds 6
tests covering AC-618-1..AC-618-4 + AZ-624 local handler-ordering
regression guard. The strategy factories are stubbed at the
airborne_bootstrap module boundary so the test exercises the
integration seam without standing up gtsam / FAISS / TensorRT /
PyTorch / OpenCV at unit-test scope.

AC-618-5 (Jetson tier-2 e2e) is BLOCKED on operator-supplied hardware
evidence: scripts/run-tests-jetson.sh
tests/e2e/replay/test_derkachi_1min.py must run on Jetson Orin Nano
(JetPack 6.2.2+b24) and the terminal log path + JetPack version + run
timestamp captured per _docs/02_document/tests/tier2-jetson-testing.md.

Quality gates: ruff format clean, ruff lint clean, 6/6 new umbrella
tests pass, 261/261 runtime_root + c5_state regression suite passes,
25/25 test_az401_compose_root_replay regression passes, full Tier-1
unit suite 2150/2151 passes (1 unrelated pre-existing failure:
c12_operator_orchestrator subprocess cold-start NFR fails on Mac dev
host's Python startup ~700 ms; not regressed by AZ-624). Code review
verdict PASS (1 Low finding; full report in
_docs/03_implementation/reviews/batch_96_review.md).

Archives AZ-624 task spec + AZ-618 umbrella reference to done/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 10:28:43 +03:00
Oleksandr Bezdieniezhnykh 2b8ef52f66 [AZ-625] Phase E.5: airborne_bootstrap c5_isam2_graph_handle ordering
Wire the airborne bootstrap to seed pre_constructed['c5_isam2_graph_handle']
so c4_pose's compose-time lookup is satisfied (c4_pose runs before c5_state in
topological order; the iSAM2 graph handle is built INSIDE the C5 estimator's
constructor and so must be produced eagerly at bootstrap time).

build_pre_constructed now invokes a new internal _build_c5_state_estimator_pair
helper that calls state_factory.build_state_estimator once, captures the
(estimator, handle) tuple, and seeds two slots: 'c5_isam2_graph_handle' for
C4's lookup, and an internal '_c5_prebuilt_estimator' look-aside key for the
C5 wrapper's short-circuit. _c5_state_wrapper checks the look-aside key first
and returns the prebuilt instance as-is — the SAME object the handle was
extracted from, so c4_pose._isam2_handle and c5_state._isam2_handle reference
ONE object across the C4 / C5 seam (AC-625.3 cross-seam identity invariant).

C5_STATE_BUILD_FLAGS mirrors state_factory._STATE_BUILD_FLAGS so the bootstrap
can name the gating BUILD_STATE_* flag in operator errors before the lower
level StateEstimatorConfigError fires (AC-625.2). When the factory itself
rejects the configuration with the flag ON, the error wraps into
AirborneBootstrapError with __cause__ preserved (matches AZ-621 / AZ-622
patterns).

Constraints respected per AZ-618 umbrella: no per-component factory signature
changed; additive on top of AZ-619..AZ-623; no edits under state_factory,
pose_factory, or c5_state internals.

Tests: tests/unit/runtime_root/test_az625_c5_isam2_graph_handle_ordering.py
adds 8 tests covering AC-625.1..3 (presence + Protocol conformance, internal
key invariant, BUILD-flag-OFF error, unknown-strategy error, factory error
wrapping, cross-seam identity, wrapper short-circuit, wrapper fallback).
Autouse stubs added to test_az619/620/621/622/623 so prior phase tests stay
isolated from the new builder.

Quality gates: ruff format clean, ruff lint clean, 32/32 phase tests pass,
255/255 runtime_root + c5_state regression suite passes. Code review verdict
PASS (2 Low findings; full report in
_docs/03_implementation/reviews/batch_95_review.md).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 09:38:13 +03:00
Oleksandr Bezdieniezhnykh 02208c577e [AZ-623] [AZ-625] Phase E: c282_ransac + c5 helpers; split handle work
Wire 4 stateless / cached helpers into airborne_bootstrap.build_pre_constructed:
c282_ransac_filter, c5_imu_preintegrator (cached on calibration path),
c5_se3_utils (helpers.se3_utils module as namespace handle), c5_wgs_converter.

The original AZ-623 5th deliverable (c5_isam2_graph_handle) hit an
unresolvable construction-order conflict between c4_pose (consumes the handle)
and c5_state (creates it inside build_state_estimator's tuple return) under
the umbrella's "MUST NOT touch any per-component factory signature" constraint.
Per AZ-623 spec's escalation gate, scope was split: AZ-625 captures the handle
ordering work; AZ-624 dependency edge updated to require both.

Tests: tests/unit/runtime_root/test_az623_pre_constructed_phase_e.py adds 7
tests covering AC-623.1..3 (4 new keys + correct types, IMU preintegrator
caching, operator-actionable error messages for empty / unreadable / malformed
calibration paths). Autouse stubs added to test_az619/620/621/622 so prior
phase tests remain isolated from new builders.

Quality gates: ruff format clean, ruff lint clean, 24/24 phase tests pass,
247/247 runtime_root + c5_state regression suite passes. Code review verdict
PASS_WITH_WARNINGS (3 Low findings; full report in
_docs/03_implementation/reviews/batch_94_review.md).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 09:20:28 +03:00
Oleksandr Bezdieniezhnykh 5c4d129f80 [AZ-622] Phase D: build_pre_constructed seeds c3 GPU runtimes
build_pre_constructed now populates c3_lightglue_runtime
(LightGlueRuntime) + c3_feature_extractor (FeatureExtractor) on top
of AZ-619/620/621. Strategy-specific BUILD_MATCHER_* flag mismatch
raises AirborneBootstrapError naming the missing flag and the c3_matcher
consumer; the c7 InferenceRuntime built earlier in the bootstrap is
reused as the engine source so no double-build at this layer.

C3MatcherConfig gains optional lightglue_weights_path: Path | None
for the operator's deployment config; production main() (AZ-624)
populates it. Real LightGlue inference correctness is verified by
AZ-624's Jetson AC-5 run per the AZ-622 Tier-2 Note.

Phase tests for AZ-619/620/621 gain an autouse _stub_c3_matcher_builders
fixture so additivity assertions remain valid as the bootstrap grows.

Code review: PASS_WITH_WARNINGS (3 Low: signature drift from spec,
_is_build_flag_on duplication across 3 runtime_root modules, and
BuildConfig literal mirrored with per-strategy build configs). All
deferred to future hygiene PBIs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 08:56:04 +03:00
Oleksandr Bezdieniezhnykh 680ba29ae6 [AZ-621] Phase C: build_pre_constructed seeds c7_inference
Third subtask of AZ-618. Extends airborne_bootstrap.build_pre_constructed
additively with c7_inference (GPU InferenceRuntime). Wraps the existing
inference_factory.build_inference_runtime so a BUILD_TENSORRT_RUNTIME /
BUILD_PYTORCH_FP16_RUNTIME mismatch surfaces a clear operator-facing
AirborneBootstrapError naming BOTH airborne C7 flags plus the consuming
component slug, rather than bubbling up RuntimeNotAvailableError with no
context.

New public const C7_AIRBORNE_BUILD_FLAGS pairs each airborne runtime
with its gating env flag (onnx_trt_ep deliberately omitted — research
only). Tests stub at the factory boundary; real GPU/TensorRT load
remains Tier-2 only (consolidated at AZ-624). AZ-619 and AZ-620 test
files extended with a _stub_c7_inference_builder autouse fixture
mirroring the AZ-620 pattern for _build_c6_*.

18/18 runtime_root unit tests pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 06:47:05 +03:00
Oleksandr Bezdieniezhnykh dbae0cad5b [autodev] Backfill batch_90_cycle1_report.md for AZ-619
Prior session committed AZ-619 (Phase A of AZ-618) as 8abfb02,
transitioned the tracker, and archived the spec, but did not write
the batch report. Content reconstructed from git show + the AZ-619
task spec + the prior _docs/_autodev_state.md sub_step.detail.

No code change. Pure audit-trail housekeeping.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 06:35:47 +03:00
Oleksandr Bezdieniezhnykh 8abfb020fe [AZ-619] Phase A: build_pre_constructed seeds c13_fdr + clock
Adds airborne_bootstrap.build_pre_constructed(config) returning a
dict with the two foundational keys: a per-binary shared FdrClient
under "c13_fdr" (via make_fdr_client with the new
AIRBORNE_MAIN_PRODUCER_ID constant) and a fresh WallClock under
"clock". Phases B..F (AZ-620..AZ-624) extend this function
additively without breaking the AZ-619 contract.

The c13_fdr instance is identity-stable across calls (per the
make_fdr_client per-producer cache) so callers can call
build_pre_constructed twice and get the same FdrClient back -
AC-619.2.

Replay-mode override is unchanged: compose_root merges
replay_components over pre_constructed so the WallClock here is
replaced by TlogDerivedClock in replay binaries (existing
contract documented in compose_root's docstring).

Tests: 5 new unit tests under tests/unit/runtime_root/
test_az619_pre_constructed_phase_a.py, all passing. AZ-591 not
regressed (12/12 in the combined run).

Spec moved to _docs/02_tasks/done/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 06:23:15 +03:00
Oleksandr Bezdieniezhnykh 8cee532516 [AZ-618] [AZ-619] [AZ-620] [AZ-621] [AZ-622] [AZ-623] [AZ-624] Split AZ-618 into 6 subtasks per spec sizing-note
The AZ-618 spec author flagged "likely a true 8" with a recommended
6-subtask split; combined with the user-rule cap on PBI complexity
(create at 2-3pt, max 5pt) the right move was to split before any
implementation began. Subtasks created in Jira as children of AZ-618:

  AZ-619 (Phase A) c13_fdr + clock                       2pt
  AZ-620 (Phase B) c6_descriptor_index + c6_tile_store   3pt
  AZ-621 (Phase C) c7_inference engine                   3pt
  AZ-622 (Phase D) c3_lightglue_runtime + c3_feature_extractor 3pt
  AZ-623 (Phase E) c282_ransac_filter + c5 helpers       3pt
  AZ-624 (Phase F) wire main() + AC-1..AC-5 + Jetson     2pt

Aggregate: 16pt actionable work (vs. AZ-618's original 5pt filing,
which the author had already qualified as understated). AZ-618 stays
In Progress in Jira as the umbrella tracker; its task spec file is
now an umbrella reference pointing to the 6 phase-specific spec files.

Deps table updated: AZ-618 row reduced to 0pt with subtask deps; six
new rows added; header counts refreshed (156 -> 162 tasks, 522 -> 533
points). Autodev state set to phase=1 (parse) for the next batch =
AZ-619 (Phase A) only.

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