# Batch 101 — Cycle 2 — AZ-700 **Date**: 2026-05-20 **Tasks**: AZ-700 (replay map visualization). **Story points**: 3. **Jira status**: AZ-700 → `In Testing`. ## What shipped A new operator-side console-script `gps-denied-render-map` that renders a self-contained HTML map (folium / Leaflet) of the estimator's track vs the tlog ground-truth track, with start/end markers, 100 m + 50 m scale circles, optional summary banner from AZ-699, and an `--offline-tiles` mode for Jetsons without internet access. folium is gated behind a new `[operator-tools]` optional-dependency group so the airborne binary never pays for it. ## Files changed Production (2): - `src/gps_denied_onboard/cli/render_map.py` (new) - `pyproject.toml` (new optional-deps group + console script) Tests (1): - `tests/unit/test_az700_render_map.py` (14 tests, all PASS local) Docs: - `_docs/02_document/tests/environment.md` — refined the 2026-05-20 "Jetson-only" policy to: unit tests local-OK, e2e Jetson-only. - `.cursor/rules/testing.mdc` — added the refined policy as an always-applied agent rule. - `_docs/02_tasks/done/AZ-700_replay_map_visualization.md` — Implementation Notes appended; moved from `todo/`. ## AC coverage | AC | Test / Artefact | Result | | ---- | ---------------------------------------------------------------------------------------- | ------ | | AC-1 | `test_cli_writes_html_with_default_tiles` | PASS (local). | | AC-2 | `test_render_map_html_emits_two_polylines`, `…emits_four_markers_and_two_circles` | PASS | | AC-3 | `test_render_map_html_emits_two_polylines`, `…emits_four_markers_and_two_circles` | PASS — exactly 2 polylines + 4 markers + 2 scale circles. | | AC-4 | Visual smoke on Tier-2 Jetson with operator-opened `map.html` | DEFERRED to Jetson (correctly per refined test-env policy). | | AC-5 | `test_render_map_html_offline_tiles_omits_openstreetmap`, `…_template_uses_local_url` | PASS | ## Test run ``` tests/unit/test_az700_render_map.py 14 PASS in 2.5 s Wider regression slice 107 PASS 1 SKIP ``` The 1 skipped test is the pre-existing AZ-698 AC-5 e2e smoke (needs the real video in `_docs/00_problem/input_data/flight_derkachi/`). ## Strict typing ``` mypy --strict src/gps_denied_onboard/cli/render_map.py → Success: no issues found in 1 source file. ``` The lazy folium import uses `# type: ignore[import-untyped, import-not-found, unused-ignore]` so strict passes cleanly whether or not `[operator-tools]` is installed. ## Refined test-environment policy Mid-batch the user clarified the existing "Jetson-only across all tiers" policy: **unit tests may run locally, e2e tests stay Jetson-only.** Rationale: the unit suite is fully synthetic, so a local PASS = Jetson PASS for that tier; the e2e suite is bound to Jetson hardware / latency / SITL and a local run is meaningless. Captured in: - `_docs/02_document/tests/environment.md` — banner + new "Where each tier runs (active policy)" table + Test Execution section rewritten. - `.cursor/rules/testing.mdc` — appended "Test environment (this project)" section so future agent sessions cannot drift back to running e2e locally. ## Next batch Batch 102 — **AZ-701** (HTTP replay API service). Depends on AZ-697 (truth source) and AZ-699 (report writer). Last task in cycle 2.