From 5ae352c68dbedb4a23dbf79e471e4a22b2c6dc99 Mon Sep 17 00:00:00 2001 From: Oleksandr Bezdieniezhnykh Date: Fri, 29 May 2026 12:31:39 +0300 Subject: [PATCH] [AZ-897] [AZ-959] Relocate AZ-897 to ../ui, file AZ-959 backend slice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- _docs/02_tasks/_dependencies_table.md | 7 +- .../todo/AZ-897_replay_ui_web_form.md | 45 ------------ .../AZ-959_replay_api_csv_path_endpoint.md | 72 +++++++++++++++++++ _docs/_autodev_state.md | 2 +- 4 files changed, 77 insertions(+), 49 deletions(-) delete mode 100644 _docs/02_tasks/todo/AZ-897_replay_ui_web_form.md create mode 100644 _docs/02_tasks/todo/AZ-959_replay_api_csv_path_endpoint.md diff --git a/_docs/02_tasks/_dependencies_table.md b/_docs/02_tasks/_dependencies_table.md index afae8ea..67db354 100644 --- a/_docs/02_tasks/_dependencies_table.md +++ b/_docs/02_tasks/_dependencies_table.md @@ -1,8 +1,8 @@ # Dependencies Table -**Date**: 2026-05-29 (cycle-4 Step 10 Implement — AZ-943 implementation attempt paused mid-batch on spec-reality gap: OKVIS2 v2 public API does NOT expose 6×6 pose covariance, feature counts, mean parallax, or MRE; the AZ-943 spec's "approach (a) in-binding subclass workaround" is structurally impossible because `ThreadedSlam::estimator_` is `private` and `ViSlamBackend` has no public telemetry accessor. The spec-documented "approach (b) upstream patch" fallback filed as **AZ-951** (3pt, backlog/, OKVIS2 v2 upstream patch: expose 6×6 pose covariance accessor + ADR for pin deviation; deps AZ-332 + AZ-592; epic AZ-254) + **AZ-952** (3pt, backlog/, OKVIS2 v2 upstream patch: expose tracking-stats accessor — feature counts + parallax + MRE; deps AZ-332 + AZ-592 + AZ-951 SOFT; epic AZ-254). Both linked Jira-side as `is blocked by` against AZ-943; AZ-943 transitioned In Progress → To Do with full audit comment. **AZ-943** moved todo/ → backlog/ with PAUSED preamble preserving original AC list for audit. Per user 2026-05-29 confirmation, cycle-4 Derkachi demo target stays KLT/RANSAC (per `tests/e2e/replay/conftest.py` line 159 `c1_vio: strategy: klt_ransac`); OKVIS2 chain (AZ-943 → AZ-944 → AZ-945 + AZ-951/952 blockers) deferred to cycle-5+ alongside AZ-945's Tier-2 `--vio-strategy okvis2` Jetson variant. Pivot to AZ-897 (replay UI web form). Earlier this session: OKVIS2 production-default pivot per user 2026-05-27 directive: AZ-592 placeholder split into 3 properly-sized sub-tickets per PBI rule, all three filed Jira-side then; local-spec import for AZ-943 happens this session before implement batch starts. **AZ-943** (5pt, **NOW backlog/** with PAUSED preamble, c1_vio, OKVIS2 binding wiring; replaces AZ-332 skeleton; deps AZ-332 + AZ-592 + **AZ-951 + AZ-952 (blockers)**; epic AZ-254). Sibling tickets remain Jira-only this session: **AZ-944** (3pt, Linux CI build env + DBoW2 small_voc + Tier-1 EuRoC smoke; Blocks chain AZ-943→AZ-944) and **AZ-945** (3pt, Jetson L4T + Tier-2 Derkachi `--vio-strategy okvis2` e2e; Blocks chain AZ-944→AZ-945). Local specs for AZ-944 + AZ-945 will be authored when their Implement turns come up. Earlier 2026-05-26 (cycle-4 Step 10 Implement — AZ-895 batch 3 user complexity decision: chose Option A "minimum deprecation" path. Filed **AZ-908** (3pt, backlog/, replay: hard removal of deprecated auto-sync surface — AZ-895 follow-up; deps AZ-895 HARD + AZ-842 HARD; no epic) to track the cycle-5+ physical removal that AZ-895's minimum-path explicitly defers. AZ-895 ships the no-op stubs + CLI deprecation warnings; AZ-908 will delete the stub files, drop the DTOs from `replay_input/interface.py`, remove the deprecated CLI flags, and drop the `auto_sync` config block. No SP change to cycle-4 totals (AZ-908 is cycle-5+ backlog, not cycle-4 active scope). Earlier same-day at Step 9 New Task — scope adjustments: (a) AZ-841 (1pt, un-xfail AZ-777 Tier-2 tests) moved from todo/ to backlog/ due to hard conflict with AZ-895 AC-4 (test_derkachi_real_tlog.py stays @xfail in cycle 4 because AZ-848 is backlogged) + partial overlap with AZ-894 AC-3 (CSV-path adapter covers the test_derkachi_1min.py un-xfail target); Jira comment added to AZ-841 documenting the deferral. (b) AZ-842 (2pt → **3pt**, +1 SP rescope) — dropped AZ-841 soft dependency, expanded replay_protocol.md scope to add new Invariant 13 covering single-canonical-clock model + cycle-4 CSV-driven replay narrative (AZ-894 + AZ-895 + AZ-896), plus architecture.md replay-input section updates. New deps: AZ-894 HARD + AZ-895 HARD + AZ-896 SOFT. (c) +**AZ-899** (1pt, product, todo/, land architecture_compliance_baseline.md — cycle-3 retro Top-3 #3 third try; deps None; no epic). (d) +**AZ-900** (1pt, product, todo/, autodev cycle-N+1 Step-9 retro-existence gate — cycle-3 retro Top-3 #2 + 2026-05-26 LESSONS process entry; deps None; no epic). (e) +**AZ-901** (1pt, product, todo/, fix EVIDENCE_OUT default path in e2e/runner/conftest.py:56 — closes 2026-05-26 leftover; deps None; no epic). Cycle-4 active scope: 6 product tickets in todo/ totaling **17 SP** = AZ-842 (3, docs) + AZ-894 (3, CSV adapter) + AZ-895 (2, auto-sync deprecation) + AZ-896 (1, format docs) + AZ-897 (5, replay UI) + AZ-899 (1) + AZ-900 (1) + AZ-901 (1). Dependency order: AZ-894 blocks AZ-895 + AZ-842 + AZ-897; AZ-896 blocks AZ-897 + AZ-842. AZ-899/AZ-900/AZ-901 standalone (no internal blockers). AZ-848 (5) + AZ-883 (2) + AZ-908 (3) remain in backlog/ (cycle-3 retro Top-3 #1 + AZ-895 follow-up deferred to cycle-5+; CSV-bypass strategy supersedes their fixes for the demo path). Earlier 2026-05-23 (cycle-3 Step 10 Implement, refactor run 02-az507-routespec-relocation — added AZ-844 (Epic, run dir `_docs/04_refactoring/02-az507-routespec-relocation/`) + AZ-845 (C01, 2pt relocate `RouteSpec` from `replay_input/tlog_route.py` to `_types/route.py`, deps None, epic AZ-844) + AZ-846 (C02, 2pt refresh `module-layout.md` cycle-3 entries — c11 + replay_input + `_types/route`, deps AZ-845, epic AZ-844) + AZ-847 (C03, 2pt widen `test_az270_compose_root` lint to enforce full rule-9 allow-list, deps AZ-845, epic AZ-844). Resolves cycle-3 cumulative review FAIL verdict (F1 High Architecture, F2 Medium Architecture, F3 Medium Maintainability) per `_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md`. Jira "Blocks" links recorded: AZ-845 → AZ-846, AZ-845 → AZ-847. Earlier same-day at start of Step 10 Implement — Epic AZ-835 decomposed into 4 leaf tasks + AZ-777 closed: AZ-839 (C3, 5pt operator_pre_flight_setup real fixture, deps AZ-836+AZ-838+AZ-777Phase1+AZ-322+AZ-316+AZ-306, epic AZ-835), AZ-840 (C4, 3pt e2e orchestrator test (tlog,video,calibration), deps AZ-839+AZ-836+AZ-838+AZ-699+AZ-405+AZ-702+AZ-696, epic AZ-835), AZ-841 (C5, 1pt un-xfail AZ-777 AC-4+AC-5, deps AZ-839+AZ-840, epic AZ-835), AZ-842 (C6, 2pt docs — replay_protocol.md Invariant 12 + architecture.md + orchestrator README, soft dep AZ-841, epic AZ-835). AZ-777 transitioned to Done in Jira: Phases 1+2 shipped (batch 104 + between batches 104 and 106); Phases 3-5 superseded by Epic AZ-835 children per 2026-05-22 user directive. AZ-777 spec moved to done/. Earlier 2026-05-21 (cycle-3 Step 9 New Task — added AZ-776 (3pt open-loop ESKF composition profile via `c4_pose.enabled` flag, no deps, epic AZ-602) + AZ-777 (5pt Derkachi C6 reference tile cache + FAISS descriptor index from OSM/CARTO basemap, depends on AZ-776, epic AZ-602). Both unblock the 7 currently-`@xfail`-masked Derkachi e2e tests on Jetson; AZ-776 unblocks 5 (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap), AZ-777 unblocks the remaining 2 (AC-3 + AZ-699 real-flight verdict). Earlier 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path) -**Total Tasks**: 183 (142 product + 41 blackbox-test) — 2026-05-29 cycle-4 Step 10 second bump (AZ-943 paused, dependency PBIs filed): +AZ-951 + AZ-952 (2 product tasks, both backlog/, 3pt each). AZ-943 (5pt) moved todo/ → backlog/ (no count change). Prior same-day 2026-05-29 bump (OKVIS2 binding session start): 181 (140 product + 41 blackbox-test) → 182 (141 product) — +AZ-943 (1 product task, originally todo/, 5pt). AZ-944 + AZ-945 remain Jira-only at the time of this update (sibling tickets, local specs deferred to their own Implement turns); their Total-Tasks impact will be reconciled when their specs land. Prior 2026-05-26 cycle-4 Step 10 bump (AZ-895 batch 3 follow-up): 180 (139 product + 41 blackbox-test) — +AZ-908 (1 product task, backlog/, 3pt). Prior 2026-05-26 cycle-4 Step 9 bump: +AZ-899 + AZ-900 + AZ-901 (3 product tasks). AZ-841 moved todo/ → backlog/ (no count change; backlog tickets are still in the table). Prior 2026-05-23 refactor-run bump: 176 (135 product + 41 blackbox-test) — +AZ-844 (Epic, 0pt umbrella for refactor run 02) + AZ-845 + AZ-846 + AZ-847 (3 product tasks). Prior 2026-05-23 bump (Epic AZ-835 decomposition): 173 (132 product + 41 blackbox-test) = +AZ-835 (Epic) + AZ-836 (C1) + AZ-837 (test-stack hardening, not this Epic) + AZ-838 (C2) added 2026-05-22→2026-05-23 prior to that update; +AZ-839 (C3) + AZ-840 (C4) + AZ-841 (C5) + AZ-842 (C6) added in that update. AZ-777 stays in the table (now closed in Jira; spec at `done/AZ-777_derkachi_c6_reference_fixture.md` retains 8pt credit for Phases 1+2 shipped). Earlier counts: 165 (124 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19 -**Total Complexity Points**: 581 (448 product + 133 blackbox-test) — 2026-05-29 cycle-4 Step 10 second bump (AZ-943 paused, dependency PBIs filed): +3pt AZ-951 + 3pt AZ-952 = +6 product pts. AZ-943 stays counted at 5pt (moved todo/ → backlog/, not deleted). Prior same-day 2026-05-29 bump (OKVIS2 binding session start): 580 (447 product + 133 blackbox-test) — +5pt AZ-943. AZ-944 (3pt) + AZ-945 (3pt) sibling tickets are filed Jira-side but not yet imported as local specs; their +6pt will land when AZ-944 / AZ-945 specs are authored. Prior 2026-05-26 cycle-4 Step 10 bump (AZ-895 batch 3 follow-up): 570 (437 product + 133 blackbox-test) — +3pt AZ-908. Prior 2026-05-26 cycle-4 Step 9 bump: +1pt AZ-899 + 1pt AZ-900 + 1pt AZ-901 + 1pt AZ-842 rescope (2→3) = +4 product pts. Prior 2026-05-23 refactor-run bump: 563 (430 product + 133 blackbox-test) — +2pt AZ-845 + 2pt AZ-846 + 2pt AZ-847 = +6 product pts on top of prior reconciled total (AZ-844 epic itself is 0pt umbrella). Prior 2026-05-23 reconciled total: 557 (424 product + 133 blackbox-test) — +5pt AZ-839 + 3pt AZ-840 + 1pt AZ-841 + 2pt AZ-842 = +11 product pts on top of prior reconciled total. AZ-836 (3pt) + AZ-838 (3pt) were added 2026-05-22→2026-05-23 prior to that update; AZ-837 (test-stack hardening, not this Epic) is unaccounted in that delta and should be folded in at the next preamble reconciliation. Earlier baseline: 546 (413 product + 133 blackbox-test) — +3pt AZ-776 + 8pt AZ-777 (5→8 override 2026-05-21 cycle-3 batch 104; see `_docs/_process_leftovers/2026-05-21_az777_complexity_override.md` for rationale + the spec refresh that pulled e2e-runner wiring + C11 contract adapt + Derkachi catalog seed + fixture replacement + un-xfail into one ticket) — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt +**Date**: 2026-05-29 (cycle-4 Step 10 Implement — AZ-897 relocated to `../ui` repo: original framing was wrong-shop. The Azaion suite already has a single React 19 SPA front-end at `../ui` per `ui/README.md`; spinning up a second React toolchain in `gps-denied-onboard` would have been parallel-pipeline duplication forbidden by coderule.mdc. Per user 2026-05-29 directive, AZ-897 description + summary rewritten to UI-only scope in `../ui` (adapted to take CSV + nadir-camera video uploads aligned with the AZ-894 CSV path); local AZ-897 spec deleted from `gps-denied-onboard/_docs/02_tasks/todo/` and re-authored into `../ui/_docs/02_tasks/todo/AZ-897_replay_ui_web_form.md` (no commit in `../ui` — left for that repo's autodev next cycle). Backend dependency filed as **AZ-959** (3pt, todo/, c1 replay_api extension to accept (video, csv) multipart + GET /static/example-csv endpoint; deps AZ-701 + AZ-894 + AZ-896; no epic) — extends the AZ-701 `POST /replay` to dispatch on `--imu` vs `--tlog` based on which upload field was present, with XOR validation. AZ-897 Jira linked `is blocked by` AZ-959. Cycle-4 in-repo effort: −5 SP (AZ-897) + 3 SP (AZ-959) = −2 SP net. Pivoting next implement batch to AZ-959. Earlier same-day — AZ-943 implementation attempt paused mid-batch on spec-reality gap: OKVIS2 v2 public API does NOT expose 6×6 pose covariance, feature counts, mean parallax, or MRE; the AZ-943 spec's "approach (a) in-binding subclass workaround" is structurally impossible because `ThreadedSlam::estimator_` is `private` and `ViSlamBackend` has no public telemetry accessor. The spec-documented "approach (b) upstream patch" fallback filed as **AZ-951** (3pt, backlog/, OKVIS2 v2 upstream patch: expose 6×6 pose covariance accessor + ADR for pin deviation; deps AZ-332 + AZ-592; epic AZ-254) + **AZ-952** (3pt, backlog/, OKVIS2 v2 upstream patch: expose tracking-stats accessor — feature counts + parallax + MRE; deps AZ-332 + AZ-592 + AZ-951 SOFT; epic AZ-254). Both linked Jira-side as `is blocked by` against AZ-943; AZ-943 transitioned In Progress → To Do with full audit comment. **AZ-943** moved todo/ → backlog/ with PAUSED preamble preserving original AC list for audit. Per user 2026-05-29 confirmation, cycle-4 Derkachi demo target stays KLT/RANSAC (per `tests/e2e/replay/conftest.py` line 159 `c1_vio: strategy: klt_ransac`); OKVIS2 chain (AZ-943 → AZ-944 → AZ-945 + AZ-951/952 blockers) deferred to cycle-5+ alongside AZ-945's Tier-2 `--vio-strategy okvis2` Jetson variant. Pivot to AZ-897 (replay UI web form). Earlier this session: OKVIS2 production-default pivot per user 2026-05-27 directive: AZ-592 placeholder split into 3 properly-sized sub-tickets per PBI rule, all three filed Jira-side then; local-spec import for AZ-943 happens this session before implement batch starts. **AZ-943** (5pt, **NOW backlog/** with PAUSED preamble, c1_vio, OKVIS2 binding wiring; replaces AZ-332 skeleton; deps AZ-332 + AZ-592 + **AZ-951 + AZ-952 (blockers)**; epic AZ-254). Sibling tickets remain Jira-only this session: **AZ-944** (3pt, Linux CI build env + DBoW2 small_voc + Tier-1 EuRoC smoke; Blocks chain AZ-943→AZ-944) and **AZ-945** (3pt, Jetson L4T + Tier-2 Derkachi `--vio-strategy okvis2` e2e; Blocks chain AZ-944→AZ-945). Local specs for AZ-944 + AZ-945 will be authored when their Implement turns come up. Earlier 2026-05-26 (cycle-4 Step 10 Implement — AZ-895 batch 3 user complexity decision: chose Option A "minimum deprecation" path. Filed **AZ-908** (3pt, backlog/, replay: hard removal of deprecated auto-sync surface — AZ-895 follow-up; deps AZ-895 HARD + AZ-842 HARD; no epic) to track the cycle-5+ physical removal that AZ-895's minimum-path explicitly defers. AZ-895 ships the no-op stubs + CLI deprecation warnings; AZ-908 will delete the stub files, drop the DTOs from `replay_input/interface.py`, remove the deprecated CLI flags, and drop the `auto_sync` config block. No SP change to cycle-4 totals (AZ-908 is cycle-5+ backlog, not cycle-4 active scope). Earlier same-day at Step 9 New Task — scope adjustments: (a) AZ-841 (1pt, un-xfail AZ-777 Tier-2 tests) moved from todo/ to backlog/ due to hard conflict with AZ-895 AC-4 (test_derkachi_real_tlog.py stays @xfail in cycle 4 because AZ-848 is backlogged) + partial overlap with AZ-894 AC-3 (CSV-path adapter covers the test_derkachi_1min.py un-xfail target); Jira comment added to AZ-841 documenting the deferral. (b) AZ-842 (2pt → **3pt**, +1 SP rescope) — dropped AZ-841 soft dependency, expanded replay_protocol.md scope to add new Invariant 13 covering single-canonical-clock model + cycle-4 CSV-driven replay narrative (AZ-894 + AZ-895 + AZ-896), plus architecture.md replay-input section updates. New deps: AZ-894 HARD + AZ-895 HARD + AZ-896 SOFT. (c) +**AZ-899** (1pt, product, todo/, land architecture_compliance_baseline.md — cycle-3 retro Top-3 #3 third try; deps None; no epic). (d) +**AZ-900** (1pt, product, todo/, autodev cycle-N+1 Step-9 retro-existence gate — cycle-3 retro Top-3 #2 + 2026-05-26 LESSONS process entry; deps None; no epic). (e) +**AZ-901** (1pt, product, todo/, fix EVIDENCE_OUT default path in e2e/runner/conftest.py:56 — closes 2026-05-26 leftover; deps None; no epic). Cycle-4 active scope: 6 product tickets in todo/ totaling **17 SP** = AZ-842 (3, docs) + AZ-894 (3, CSV adapter) + AZ-895 (2, auto-sync deprecation) + AZ-896 (1, format docs) + AZ-897 (5, replay UI) + AZ-899 (1) + AZ-900 (1) + AZ-901 (1). Dependency order: AZ-894 blocks AZ-895 + AZ-842 + AZ-897; AZ-896 blocks AZ-897 + AZ-842. AZ-899/AZ-900/AZ-901 standalone (no internal blockers). AZ-848 (5) + AZ-883 (2) + AZ-908 (3) remain in backlog/ (cycle-3 retro Top-3 #1 + AZ-895 follow-up deferred to cycle-5+; CSV-bypass strategy supersedes their fixes for the demo path). Earlier 2026-05-23 (cycle-3 Step 10 Implement, refactor run 02-az507-routespec-relocation — added AZ-844 (Epic, run dir `_docs/04_refactoring/02-az507-routespec-relocation/`) + AZ-845 (C01, 2pt relocate `RouteSpec` from `replay_input/tlog_route.py` to `_types/route.py`, deps None, epic AZ-844) + AZ-846 (C02, 2pt refresh `module-layout.md` cycle-3 entries — c11 + replay_input + `_types/route`, deps AZ-845, epic AZ-844) + AZ-847 (C03, 2pt widen `test_az270_compose_root` lint to enforce full rule-9 allow-list, deps AZ-845, epic AZ-844). Resolves cycle-3 cumulative review FAIL verdict (F1 High Architecture, F2 Medium Architecture, F3 Medium Maintainability) per `_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md`. Jira "Blocks" links recorded: AZ-845 → AZ-846, AZ-845 → AZ-847. Earlier same-day at start of Step 10 Implement — Epic AZ-835 decomposed into 4 leaf tasks + AZ-777 closed: AZ-839 (C3, 5pt operator_pre_flight_setup real fixture, deps AZ-836+AZ-838+AZ-777Phase1+AZ-322+AZ-316+AZ-306, epic AZ-835), AZ-840 (C4, 3pt e2e orchestrator test (tlog,video,calibration), deps AZ-839+AZ-836+AZ-838+AZ-699+AZ-405+AZ-702+AZ-696, epic AZ-835), AZ-841 (C5, 1pt un-xfail AZ-777 AC-4+AC-5, deps AZ-839+AZ-840, epic AZ-835), AZ-842 (C6, 2pt docs — replay_protocol.md Invariant 12 + architecture.md + orchestrator README, soft dep AZ-841, epic AZ-835). AZ-777 transitioned to Done in Jira: Phases 1+2 shipped (batch 104 + between batches 104 and 106); Phases 3-5 superseded by Epic AZ-835 children per 2026-05-22 user directive. AZ-777 spec moved to done/. Earlier 2026-05-21 (cycle-3 Step 9 New Task — added AZ-776 (3pt open-loop ESKF composition profile via `c4_pose.enabled` flag, no deps, epic AZ-602) + AZ-777 (5pt Derkachi C6 reference tile cache + FAISS descriptor index from OSM/CARTO basemap, depends on AZ-776, epic AZ-602). Both unblock the 7 currently-`@xfail`-masked Derkachi e2e tests on Jetson; AZ-776 unblocks 5 (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap), AZ-777 unblocks the remaining 2 (AC-3 + AZ-699 real-flight verdict). Earlier 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path) +**Total Tasks**: 184 (143 product + 41 blackbox-test) — 2026-05-29 cycle-4 Step 10 third bump (AZ-897 relocation + AZ-959 filing): +AZ-959 (1 product task, todo/, 3pt). AZ-897 was never in this table's row count (pre-existing gap — the cycle-4 AZ-89x specs land in todo/ but were not back-filled into the table rows; not fixing that here, out of scope). Prior same-day 2026-05-29 second bump (AZ-943 paused, dependency PBIs filed): 183 (142 product + 41 blackbox-test) — +AZ-951 + AZ-952 (2 product tasks, both backlog/, 3pt each). AZ-943 (5pt) moved todo/ → backlog/ (no count change). Prior same-day 2026-05-29 bump (OKVIS2 binding session start): 181 (140 product + 41 blackbox-test) → 182 (141 product) — +AZ-943 (1 product task, originally todo/, 5pt). AZ-944 + AZ-945 remain Jira-only at the time of this update (sibling tickets, local specs deferred to their own Implement turns); their Total-Tasks impact will be reconciled when their specs land. Prior 2026-05-26 cycle-4 Step 10 bump (AZ-895 batch 3 follow-up): 180 (139 product + 41 blackbox-test) — +AZ-908 (1 product task, backlog/, 3pt). Prior 2026-05-26 cycle-4 Step 9 bump: +AZ-899 + AZ-900 + AZ-901 (3 product tasks). AZ-841 moved todo/ → backlog/ (no count change; backlog tickets are still in the table). Prior 2026-05-23 refactor-run bump: 176 (135 product + 41 blackbox-test) — +AZ-844 (Epic, 0pt umbrella for refactor run 02) + AZ-845 + AZ-846 + AZ-847 (3 product tasks). Prior 2026-05-23 bump (Epic AZ-835 decomposition): 173 (132 product + 41 blackbox-test) = +AZ-835 (Epic) + AZ-836 (C1) + AZ-837 (test-stack hardening, not this Epic) + AZ-838 (C2) added 2026-05-22→2026-05-23 prior to that update; +AZ-839 (C3) + AZ-840 (C4) + AZ-841 (C5) + AZ-842 (C6) added in that update. AZ-777 stays in the table (now closed in Jira; spec at `done/AZ-777_derkachi_c6_reference_fixture.md` retains 8pt credit for Phases 1+2 shipped). Earlier counts: 165 (124 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19 +**Total Complexity Points**: 584 (451 product + 133 blackbox-test) — 2026-05-29 cycle-4 Step 10 third bump (AZ-897 relocation + AZ-959 filing): +3pt AZ-959. AZ-897 (5pt) was never table-counted here, so no decrement at this layer; the in-repo cycle-4 effort still drops by 5pt at the active-scope layer (AZ-897 work is now executed in `../ui`). Prior same-day 2026-05-29 second bump (AZ-943 paused, dependency PBIs filed): 581 (448 product + 133 blackbox-test) — +3pt AZ-951 + 3pt AZ-952 = +6 product pts. AZ-943 stays counted at 5pt (moved todo/ → backlog/, not deleted). Prior same-day 2026-05-29 bump (OKVIS2 binding session start): 580 (447 product + 133 blackbox-test) — +5pt AZ-943. AZ-944 (3pt) + AZ-945 (3pt) sibling tickets are filed Jira-side but not yet imported as local specs; their +6pt will land when AZ-944 / AZ-945 specs are authored. Prior 2026-05-26 cycle-4 Step 10 bump (AZ-895 batch 3 follow-up): 570 (437 product + 133 blackbox-test) — +3pt AZ-908. Prior 2026-05-26 cycle-4 Step 9 bump: +1pt AZ-899 + 1pt AZ-900 + 1pt AZ-901 + 1pt AZ-842 rescope (2→3) = +4 product pts. Prior 2026-05-23 refactor-run bump: 563 (430 product + 133 blackbox-test) — +2pt AZ-845 + 2pt AZ-846 + 2pt AZ-847 = +6 product pts on top of prior reconciled total (AZ-844 epic itself is 0pt umbrella). Prior 2026-05-23 reconciled total: 557 (424 product + 133 blackbox-test) — +5pt AZ-839 + 3pt AZ-840 + 1pt AZ-841 + 2pt AZ-842 = +11 product pts on top of prior reconciled total. AZ-836 (3pt) + AZ-838 (3pt) were added 2026-05-22→2026-05-23 prior to that update; AZ-837 (test-stack hardening, not this Epic) is unaccounted in that delta and should be folded in at the next preamble reconciliation. Earlier baseline: 546 (413 product + 133 blackbox-test) — +3pt AZ-776 + 8pt AZ-777 (5→8 override 2026-05-21 cycle-3 batch 104; see `_docs/_process_leftovers/2026-05-21_az777_complexity_override.md` for rationale + the spec refresh that pulled e2e-runner wiring + C11 contract adapt + Derkachi catalog seed + fixture replacement + un-xfail into one ticket) — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt Dependencies columns list only the tracker-ID portion (descriptive tail text in each task spec is omitted here for table-readability). The @@ -202,6 +202,7 @@ are all declared and documented below under **Cycle Check**. | AZ-943 | OKVIS2 binding: real ThreadedSlam wiring (AZ-592 split 1/3) — PAUSED, blocked on AZ-951+AZ-952 | 5 | AZ-332; AZ-592; **AZ-951 (blocker)**; **AZ-952 (blocker)** | AZ-254 | | AZ-951 | OKVIS2 v2 upstream patch: expose 6×6 pose covariance accessor (+ ADR for pin deviation) | 3 | AZ-332; AZ-592 | AZ-254 | | AZ-952 | OKVIS2 v2 upstream patch: expose tracking-stats accessor (counts + parallax + MRE) | 3 | AZ-332; AZ-592; AZ-951 (SOFT) | AZ-254 | +| AZ-959 | replay_api: extend POST /replay to accept (video, csv) multipart for AZ-897 UI | 3 | AZ-701; AZ-894; AZ-896 | (none) | ## Notes diff --git a/_docs/02_tasks/todo/AZ-897_replay_ui_web_form.md b/_docs/02_tasks/todo/AZ-897_replay_ui_web_form.md deleted file mode 100644 index cf8e0bb..0000000 --- a/_docs/02_tasks/todo/AZ-897_replay_ui_web_form.md +++ /dev/null @@ -1,45 +0,0 @@ -# Replay UI: web form for paired video + CSV input + example download - -**Task**: AZ-897_replay_ui_web_form -**Name**: Build the first operator-facing UI for the GPS-denied onboard system — a single-page form that uploads a paired (video, CSV) for replay -**Description**: User decision (2026-05-26): the system offers an operator-facing UI for the test/demo replay path. The UI surfaces the hard constraints visually (nadir, airborne, aligned-start) so operators don't fail silently from a misaligned video. This is also the foundation for the deferred operator-tooling work (see `_docs/00_research/00_question_decomposition.md` lines 119, 224). - -Tech stack per `.cursor/rules/techstackrule.mdc`: React + Tailwind CSS. - -**Complexity**: 5 SP -**Dependencies**: AZ-894 (backend CSV adapter), AZ-896 (format docs + example CSV that the UI serves) -**Component**: frontend (new — first piece of operator-facing UI), backend (new HTTP endpoint that fronts `gps-denied-replay`) -**Tracker**: AZ-897 (https://denyspopov.atlassian.net/browse/AZ-897) -**Parent Epic**: (none — cycle-4 replay-input redesign; will likely become the first piece of a future operator-tooling epic) - -## Shape - -A single-page web form, served from a target to be decided during implementation (Jetson? operator workstation? containerised dev mode?). Hosts: - -- **Video file picker**. Accept `.mp4`, `.mov`. Display constraint hint: "Nadir camera; UAV already airborne at frame 0." -- **CSV file picker**. Accept `.csv`. Display constraint hint: "Row 0 timestamp must equal video frame 0; see format docs." -- **"Download example CSV"** link → AZ-896's `example_data_imu.csv`. -- **"View format docs"** link → AZ-896's `replay_input_format.md`. -- **"Start replay"** button → POSTs (video_path, csv_path) to a backend endpoint that invokes `gps-denied-replay --video X --imu Y`. -- **Result panel**: tail the replay subprocess output, display final verdict (PASS/FAIL + accuracy metrics). - -## Acceptance Criteria - -- **AC-1**: Form renders with both pickers, both constraint hints, download/docs links, and the start button. -- **AC-2**: The start button correctly invokes the replay pipeline against the selected files; success path returns a verdict; failure path returns the error reason from the backend. -- **AC-3**: Form rejects mismatched filename pairs only with explicit operator-actionable error messages — no silent failures. -- **AC-4**: Example-CSV download serves the file from AZ-896 with the correct content-type. -- **AC-5**: Tests cover empty submissions, mismatched file types, backend failures, and the happy path. React Testing Library + jest for component tests; an e2e smoke test covers the full flow. - -## Out of scope - -- Multi-flight management / history / library. -- Authentication / user accounts. -- Sector classification, pre-flight cache provisioning, mission planning (those are separate deferred items from C10 / `00_question_decomposition.md`). -- The deploy-target decision (Jetson vs operator workstation) — to be resolved during implementation; default proposal: containerised dev mode for now. - -## References - -- Companion: AZ-894 (CSV adapter), AZ-896 (docs + example CSV) -- Deferred precedent: `_docs/00_research/00_question_decomposition.md` lines 119 ("Mission-planning UX is out of scope"), 224 ("Operator-side CLI/desktop tool design deferred to Plan-phase") -- Tech stack: React + Tailwind CSS per `.cursor/rules/techstackrule.mdc` diff --git a/_docs/02_tasks/todo/AZ-959_replay_api_csv_path_endpoint.md b/_docs/02_tasks/todo/AZ-959_replay_api_csv_path_endpoint.md new file mode 100644 index 0000000..700dcc3 --- /dev/null +++ b/_docs/02_tasks/todo/AZ-959_replay_api_csv_path_endpoint.md @@ -0,0 +1,72 @@ +# replay_api: extend POST /replay to accept (video, csv) multipart for AZ-897 UI + +**Task**: AZ-959_replay_api_csv_path_endpoint +**Name**: Extend `replay_api` `POST /replay` to accept (video, csv) multipart so the AZ-897 UI in `../ui` can drive the CSV-replay path +**Description**: AZ-897 was relocated to the `../ui` repo (the single Azaion suite React 19 front-end). The UI uploads `(video, CSV)` per AZ-894's CSV path, but the existing `replay_api` `POST /replay` endpoint only accepts `(tlog, video, calibration)` — it predates AZ-894. This ticket extends the endpoint to accept either input pair (XOR), validates the CSV against the AZ-896 schema, dispatches the subprocess with the right CLI flag (`--imu` vs `--tlog`), and adds a `GET /static/example-csv` endpoint serving the AZ-896 reference CSV. Existing tlog-path callers continue to work unchanged for the cycle-4 demo + transitional clients; AZ-908 (cycle-5+ backlog) eventually removes the tlog branch. +**Complexity**: 3 SP +**Dependencies**: AZ-701 (existing `replay_api` package, done), AZ-894 (CSV adapter that the CLI consumes, done), AZ-896 (example CSV + format spec, done) +**Blocks**: AZ-897 (UI in `../ui` — HARD BLOCKER; the UI cannot ship until this endpoint exists) +**Component**: replay_api (existing FastAPI app) +**Tracker**: AZ-959 (https://denyspopov.atlassian.net/browse/AZ-959) +**Parent Epic**: (none — cycle-4 replay-input redesign, replacement for the original AZ-897 backend slice after the UI relocation) + +Jira AZ-959 is the authoritative spec; this file is the in-workspace mirror. + +## Goal + +Extend the AZ-701 `replay_api` `POST /replay` endpoint to accept the `(video, csv)` input pair that the AZ-894 CLI introduced. AZ-897 (relocated to `../ui`) calls this endpoint with `(video, csv, calibration)` multipart to drive the CSV-replay path; the UI does not upload pymavlink tlog files. + +## Scope + +1. **`src/gps_denied_onboard/replay_api/app.py`** (`post_replay` handler): + - Add `csv: Annotated[UploadFile | None, File()] = None` parameter alongside the existing `tlog`. + - Make `tlog` optional (currently required). + - Enforce XOR: exactly one of `tlog` or `csv` must be present; both or neither → 400 with clear error pointing at the XOR contract. + - Validate csv bytes via new `validate_csv_kind`. + - Persist via `job_storage.csv_path` when csv route taken. + - Pass through to `SubprocessReplayRunner.run` via the extended `ReplayInputs` shape. + +2. **`src/gps_denied_onboard/replay_api/handlers.py`**: + - New `validate_csv_kind(probe_bytes: bytes) -> None`: checks the CSV header line starts with `timestamp(ms),Time,SCALED_IMU2.xacc,...` matching the AZ-896 csv_replay_format.md schema. Raises `UnsupportedFileKindError` with a message pointing to the docs path. + +3. **`src/gps_denied_onboard/replay_api/interface.py`**: + - `ReplayInputs`: change `tlog_path: Path` to `tlog_path: Path | None` and add `csv_path: Path | None`. Invariant: exactly one is None (raised in `__post_init__` if violated). + +4. **`src/gps_denied_onboard/replay_api/storage.py`**: + - Per-job storage: add `csv_path` property pointing to `{job_dir}/input/data_imu.csv` (mirrors `tlog_path`). + +5. **`SubprocessReplayRunner.run` in `app.py`**: + - When `inputs.csv_path is not None`, shell out with `--imu` flag; when `inputs.tlog_path is not None`, shell out with `--tlog`. + - Ground-truth extraction (`_maybe_render_report`) currently calls `load_tlog_ground_truth(inputs.tlog_path)`. For the CSV path, ground truth must come from the CSV's `GLOBAL_POSITION_INT.*` columns. Default proposal: extend the ground-truth loader to dispatch on file extension via a thin helper next to `load_tlog_ground_truth` (avoids branching inside `_maybe_render_report`). + +6. **New `GET /static/example-csv` endpoint** in `app.py`: + - Serve `_docs/02_document/contracts/replay/example_data_imu.csv` with `Content-Type: text/csv; charset=utf-8`. + - 200 if file exists, 503 if missing (build/packaging issue — file is in the source tree, so a missing file means a deploy-misconfiguration). + - This is what AZ-897 UI's "Download example CSV" links to. + +7. **`tests/unit/replay_api/test_az701_replay_api.py`**: + - Update existing tests to cover the XOR validation (both rejected, neither rejected). + - Add tests: CSV happy path, malformed CSV schema rejected, example-csv endpoint serves correct content + content-type. + +## Acceptance Criteria + +- **AC-1**: `POST /replay` with `(csv, video, calibration)` multipart is accepted; subprocess invocation uses `--imu CSV_PATH`. Same response shape as the tlog-path call. +- **AC-2**: `POST /replay` with both `tlog` AND `csv` returns 400 + clear error pointing at the XOR contract. +- **AC-3**: `POST /replay` with neither `tlog` nor `csv` returns 400 + clear error. +- **AC-4**: `POST /replay` with malformed CSV (missing required column, e.g. no `Time` column) returns 400 + error referencing the AZ-896 format docs. +- **AC-5**: `GET /static/example-csv` returns 200 + `text/csv; charset=utf-8` content-type + exact file bytes from `_docs/02_document/contracts/replay/example_data_imu.csv`. +- **AC-6**: Ground-truth extraction works for the CSV path — accuracy report renders against the CSV's `GLOBAL_POSITION_INT.*` columns when `csv_path` was used. +- **AC-7**: All existing AZ-701 tlog-path tests in `test_az701_replay_api.py` still pass unchanged. + +## Out of scope + +- Calibration handling changes — keep current behaviour (operator uploads `calibration` field; falls back to `REPLAY_API_DEFAULT_CALIBRATION` env if not provided). +- Removing the tlog path — AZ-895 deprecated `--tlog` in the CLI but tlog API support stays for backwards compat for the cycle-4 demo + any existing programmatic clients. AZ-908 (cycle-5+ backlog) will remove tlog from both CLI and API. +- The UI itself — that's AZ-897 in `../ui`. +- Format docs page rendering / serving — keep markdown source in `_docs/02_document/contracts/replay/csv_replay_format.md`; UI links to the docs URL when published. + +## Notes + +- The `--imu` flag was added to the CLI by AZ-894; this ticket exposes that path through the HTTP API. No changes to the CLI itself. +- `validate_csv_kind` should be schema-aware (checks the header line matches the AZ-896 format), not just content-type sniffing. Bad schema must fail fast at the API boundary, not deep in `gps-denied-replay`. +- The `GET /static/example-csv` endpoint should not require auth (the example CSV is a public reference document, not a secret). diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index 761a1e4..59603e9 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -8,7 +8,7 @@ status: in_progress sub_step: phase: 6 name: implement-tasks - detail: "batch 5 of N: AZ-897 replay UI web form (pivoted from AZ-943 — paused, blocked on AZ-951+AZ-952 per 2026-05-29 spec-reality gap)" + detail: "batch 5 of N: AZ-959 replay_api POST /replay CSV-path extension (pivoted from AZ-897 after relocation to ../ui repo per user 2026-05-29; AZ-959 is the backend slice that unblocks the relocated AZ-897 UI)" retry_count: 0 cycle: 4 tracker: jira