[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>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-29 12:53:17 +03:00
parent 363c235264
commit 7f590582cc
7 changed files with 128 additions and 20 deletions
@@ -0,0 +1,54 @@
# render_map: dispatch --truth loader on extension to unblock CSV-path map render
**Task**: AZ-960_render_map_csv_truth_dispatch
**Name**: Extend `gps-denied-render-map` so `--truth` accepts AZ-896 CSV in addition to binary tlog; remove the AZ-959 workaround
**Description**: AZ-959 landed CSV-path support in the `replay_api` `POST /replay` endpoint but `gps-denied-render-map` still only consumes binary tlog as ground truth. As a workaround AZ-959 made `SubprocessReplayRunner._maybe_render_map` short-circuit to `None` for CSV-path jobs — that means the AZ-897 UI (in `../ui`) currently shows no map for CSV uploads. This ticket closes the gap by dispatching on the `--truth` file extension and removing the workaround.
**Complexity**: 2 SP
**Dependencies**: AZ-700 (existing render-map CLI, done), AZ-894 (CSV ground-truth loader, done), AZ-959 (replay_api CSV path that carries the current workaround, done)
**Blocks**: (none — UX completeness, not a hard blocker)
**Component**: cli/render_map + replay_api/app (workaround removal)
**Tracker**: AZ-960 (https://denyspopov.atlassian.net/browse/AZ-960)
**Parent Epic**: (none — cycle-4 replay UX follow-up to AZ-959)
Jira AZ-960 is the authoritative spec; this file is the in-workspace mirror.
## Goal
Make `gps-denied-render-map` source-agnostic for the `--truth` argument: tlog OR CSV. Both already produce row-aligned `(lat_deg, lon_deg)` series via `load_tlog_ground_truth` / `load_csv_ground_truth`, so the rest of the renderer is unchanged. After this lands, the AZ-959 short-circuit in `_maybe_render_map` goes away and CSV-path jobs ship with a map link.
## Scope
1. **`src/gps_denied_onboard/cli/render_map.py`** — `load_ground_truth_track(path)`:
- Dispatch on extension. `.csv``load_csv_ground_truth(path)`; otherwise (`.tlog`, `.bin`, no ext) → `load_tlog_ground_truth(path)`.
- Both return DTOs with row-aligned `records` carrying `lat_deg` / `lon_deg`; the existing list comprehension survives unchanged.
- Update `--truth` CLI help to call out CSV support.
- Update the renderer's `tooltip="Ground truth (tlog)"``tooltip="Ground truth"` (cosmetic; the dispatch hides the source).
2. **`src/gps_denied_onboard/replay_api/app.py`** — `SubprocessReplayRunner._maybe_render_map`:
- Drop the `if inputs.tlog_path is None: return None` short-circuit added by AZ-959.
- Pass whichever of `tlog_path` / `csv_path` is set as `--truth`.
3. **`tests/unit/test_az700_render_map.py`**:
- Add focused test: build a tiny CSV via the AZ-896 schema, call `load_ground_truth_track`, assert the returned `list[tuple[float, float]]` matches what `load_csv_ground_truth` would return.
- Add an integration test: run `main()` against a CSV `--truth` and assert the produced HTML contains a polyline.
4. **`tests/unit/replay_api/test_az701_replay_api.py`**:
- Extend the AZ-959 CSV happy-path test (`test_post_replay_csv_path_returns_200_and_dispatches_imu_flag`) to also assert `map_html_url` is present in the response (no longer `None`).
## Acceptance Criteria
- **AC-1**: `gps-denied-render-map --truth foo.csv --estimated bar.jsonl --output baz.html` succeeds when `foo.csv` is a valid AZ-896 schema CSV.
- **AC-2**: `gps-denied-render-map --truth foo.tlog ...` still works unchanged (no tlog regression).
- **AC-3**: The replay_api `POST /replay` CSV path response now includes `map_html_url`; the corresponding `/jobs/{job_id}/map` returns 200 + valid HTML.
- **AC-4**: A CSV with a malformed schema (missing required column) raises `ReplayInputAdapterError` from `load_csv_ground_truth` and the CLI exits non-zero; the renderer never sees a half-baked DTO.
## Out of scope
- Renaming `RenderInputs.truth_track` or the internal `_TRUTH_LINE_COLOR` constant — naming stays.
- Schema validation specifics — those live in `csv_ground_truth.py` and are owned by AZ-896.
- The cosmetic `ReportContext` field rename — that's AZ-961.
## Notes
- The `load_csv_ground_truth` loader already strict-validates the AZ-896 schema at entry; the CLI inherits that fail-fast behaviour for free.
- After this lands, the existing AZ-959 `_maybe_render_map` log line ("skipping map render — CSV-path runs do not yet support ...") is dead code and goes with the short-circuit.