# 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.
