# SESSION_STATE.md ## Backlog - **Pirate mode** — Easter egg / opt-in toggle, not the default voice. Long-press the trip report header flips the narrative into pirate dialect. Seamanlike voice stays default. - **Race mode tack detection** — Tighter windows (T_SETTLE ~10–15 s), wider STAB_MAX (~30°), shorter MIN_GAP_MS (~20 s) for quick successive tacks at race starts and mark roundings. Current mode is CRUISING (conservative). See `TackDetector.kt` constants. - **Persist tracks to Room** — Replace in-memory `_pastTracks` with a Room database. SAF write path is solid; the in-memory list is still the source of truth at runtime. - **AnchorWatchHandler UI** — Re-integrate into `activity_main.xml` and wire to the Safety Dashboard. - **Unit tests for trip generators** — `TripReportGenerator` and `PreTripReportGenerator` have no JVM tests yet. --- ## Recently Completed ### Tack Detection Redesign — 2026-05-26 Full rewrite of `TackDetector.kt` (both `android-app` and `test-runner` mirrors). **Root causes fixed:** - Point-count window (HALF_WIN=2) gave wildly different reliability at 1 Hz vs 0.2 Hz - Only 2 points used for heading estimate — one noisy fix contaminated the result - Single-pair spread check fired on individual noisy fixes - No protection against GPS COG noise at low/zero SOG **New algorithm (CRUISING mode — conservative):** - Time-based 30 s settle windows before/after each candidate apex, with a 30 s guard zone - `circularMAD` stability check: rejects any window where heading spread > 20°. Handles anchor noise (MAD ≈ 90°) without any SOG gate — the stability check is sufficient. - `circularMean` via unit-vector averaging — correct 0°/360° wrap - `MAX_DELTA` raised to 160° (covers deep jibes the old 140° missed) - `START_SKIP_MS` reduced to 60 s - De-duplication uses adjacent-candidate comparison (not group-start), so a long raw-candidate stream from one maneuver stays as one group even if it spans > MIN_GAP_MS - Position pin refined to max instantaneous heading rate within the maneuver zone **Key constants:** `T_SETTLE=30s, T_MANEUVER=30s, STAB_MAX=20°, MIN_DELTA=75°, no MAX_DELTA, MIN_GAP_MS=60s, START_SKIP_MS=60s` **Tests:** 16/16 passing at 1 Hz (FULL mode). Covers: straight course, port↔stbd tack, jibe, 0/360 wrap, below/above delta limits, noisy anchor (rejected), unstable before-window (rejected), gradual course change (rejected), two tacks with sufficient gap, two tacks within MIN_GAP_MS (deduplicated), cold-start suppression, too few points. ### GPX / Storage — completed prior session - `AndroidManifest.xml`: fixed `application/octet-stream` VIEW filter (no `pathSuffix`); added full SEND filter set for all five MIME types - `TrackStorage.kt`: SAF primary path (`saveViaSaf`, `loadViaSaf`, `safState`, `initSafDirectory`) with MediaStore fallback; survives reinstall with one re-auth tap - `SavedTracksFragment.kt` + `fragment_saved_tracks.xml`: "Set up track storage" / "Restore track access" button observing `safState` flow - `MainActivity.kt`: `safPickerLauncher` + `launchSafPicker()` via `ACTION_OPEN_DOCUMENT_TREE` ### Logbook + Trip Report Improvements — 2026-05-26 - **Notes/photos on finished tracks** — `+ Add note` button in `TrackDetailSheet`; dialog with text, camera, gallery. Saved with `timestampMs = track.endMs` so they fall within track window and appear in `buildLogEvents()` automatically. - **Single Nav voice** — Removed `NarrativeStyle` enum and 4-style ChipGroup. `generateNarrative(summary)` emits a concise PASSAGE header (date/time range, stats, conditions, timestamped deck log). - **Speed coloring fixed** — Replaced broken `Expression.get("color")` (MapLibre Android 11.x silently fails for `lineColor` on `LineLayer`) with `Expression.step(Expression.get("speed"), ...)` mapping the numeric `"speed"` property to color literals. --- ## Infrastructure - **`test-runner/`** — standalone Kotlin/JVM Gradle project for logic tests without Android SDK. Run: `cd test-runner && ./gradlew test` - **Branch policy** — all work merges to `main`. - **Build note** — `./gradlew assembleDebug` requires network access to Google plugin repo (unavailable in cloud sandbox). Use `test-runner` for logic tests; build verification requires a local dev environment.