mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 05:41:13 +00:00
[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>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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`
|
||||
@@ -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).
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user