summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-06-03 23:43:51 +0000
committerClaude <noreply@anthropic.com>2026-06-03 23:43:51 +0000
commit9d2486eb1001a7b6204e0ba878ac2fdd49262a70 (patch)
treeb693a2abc57b654a42d52b58c06e62943d7bb236
parent0fc17312687adfc21f268a0da70ddafe3682d255 (diff)
Add code-map.md: package topology, key classes, gotchas, testing guide
Comprehensive bootstrap doc for future agent sessions: full package/file table for every domain, architectural invariants (SAF source-of-truth, NavLogger, MOB always-on-top, etc.), testing commands, CI/merge workflow, and a gotchas section covering known tricky spots. Updated config.md directory table and worklog. https://claude.ai/code/session_015p2yh5CaTdjWWJatAz5NBY
-rw-r--r--.agent/code-map.md399
-rw-r--r--.agent/config.md1
-rw-r--r--.agent/worklog.md4
3 files changed, 404 insertions, 0 deletions
diff --git a/.agent/code-map.md b/.agent/code-map.md
new file mode 100644
index 0000000..eb2f564
--- /dev/null
+++ b/.agent/code-map.md
@@ -0,0 +1,399 @@
+# Code Map & Context Tips
+
+**Purpose:** Bootstrap context for any new agent session. Read this before touching code.
+**Last updated:** 2026-06-03
+
+---
+
+## 1. Session Start Checklist
+
+Run these four commands before anything else:
+
+```bash
+git branch --show-current # must be main (or a feature branch off main)
+git checkout main # if not already there
+git config core.hooksPath scripts/git-hooks # activate pre-commit hook
+git pull origin main # sync latest
+```
+
+Then read `.agent/worklog.md` for current focus and recently completed work.
+
+**Merge policy (non-negotiable):** merge to `main` after every complete unit of work, not at session end.
+
+---
+
+## 2. Module Topology
+
+```
+nav/
+├── android-app/ Android application (API 36, Kotlin)
+│ └── app/src/main/kotlin/org/terst/nav/ ← all app source
+├── test-runner/ JVM-only test module (no Android SDK)
+│ └── src/main/kotlin/org/terst/nav/ ← shared portable code
+├── .agent/ Agent config, worklog, design, standards
+├── scripts/
+│ ├── git-hooks/pre-commit blocks pushes to master
+│ └── .claude/ change templates (ct-*.yaml)
+└── .github/workflows/android.yml CI; fires on main pushes only
+```
+
+**Build note:** `./gradlew assembleDebug` requires network access to Google plugin repo — unavailable in the cloud sandbox. Use `test-runner` for logic tests:
+
+```bash
+cd test-runner && ./gradlew test
+```
+
+---
+
+## 3. Source Package Map
+
+All source lives under `org.terst.nav`.
+
+### Root-level (android-app only)
+
+| File | Role |
+|------|------|
+| `MainActivity.kt` | Single-activity host; nav graph, SAF picker launcher |
+| `NavApplication.kt` | Application class; DI/singleton initialization |
+| `MainViewModel.kt` → `ui/MainViewModel.kt` | Top-level state (GPS, NMEA, mode) |
+| `LocationService.kt` | Foreground service; provides GPS to background |
+| `AnchorAlarmManager.kt` | Anchor drag detection; survives screen lock |
+| `PerformanceViewModel.kt` | Real-time sailing metrics (VMG, polar %, wind triangle) |
+| `PerformanceViewModelFactory.kt` | Factory for PerformanceViewModel |
+| `UnitPrefs.kt` | Unit system prefs (knots/m/s, m/ft, °C/°F) |
+| `NmeaConnectionPrefs.kt` | TCP/UDP NMEA connection settings |
+| `PolarData.kt` | Polar table data class + interpolation |
+| `PolarDiagramView.kt` | Custom View: polar diagram canvas renderer |
+| `BarometerSensorManager.kt` | Device barometer reader |
+| `BarometerTrendView.kt` | Pressure trend sparkline view |
+| `BarometerData.kt` | Barometer data class |
+| `MobData.kt` | MOB event data (position, timestamp) |
+| `TidalCurrentData.kt` | Tidal current data model |
+| `MockTidalCurrentGenerator.kt` | Test-only tidal current stub |
+| `PowerMode.kt` | Enum: FULL / ECONOMY / ANCHOR_WATCH |
+| `FeatureFlags.kt` → `settings/FeatureFlags.kt` | Runtime feature toggles |
+
+### `ais/`
+
+| File | Role |
+|------|------|
+| `AisRepository.kt` | In-memory AIS target store (MMSI → AisVessel) |
+| `AisVessel.kt` | AIS target data class |
+| `AisVdmParser.kt` → `nmea/` | Parses !AIVDM sentences |
+| `CpaCalculator.kt` | CPA / TCPA computation |
+| `CpaAlert.kt` | Alert data class for CPA threshold breach |
+| `AisHubApiService.kt` | AISHub internet feed Retrofit service |
+| `AisHubSource.kt` | Polls AISHub; feeds AisRepository |
+
+### `data/`
+
+| Sub-package | Files | Role |
+|-------------|-------|------|
+| `api/` | `ApiClient.kt`, `WeatherApiService.kt`, `MarineApiService.kt` | Retrofit/OkHttp setup; weather + marine endpoints |
+| `model/` | `BoatPolars`, `GribFile`, `GribRegion`, `GribParameter`, `WeatherResponse`, `MarineResponse`, `MarineConditions`, `WindArrow`, `WindForecast`, `ForecastItem`, `TideStation`, `TideConstituent`, `TidePrediction`, `LogbookEntry`, `SensorData`, `SatelliteDownloadRequest` | Pure data classes, no Android deps |
+| `repository/` | `WeatherRepository.kt` | Weather fetch + cache coordination |
+| `storage/` | `GribFileManager.kt` | GRIB2 file lifecycle (download, store, delete) |
+| `weather/` | `GribStalenessChecker.kt`, `SatelliteGribDownloader.kt` | GRIB freshness logic; satellite GRIB fetch |
+
+### `db/`
+
+| File | Role |
+|------|------|
+| `NavDatabase.kt` | Room database; holds `TrackDao` |
+
+### `gps/`
+
+| File | Role |
+|------|------|
+| `GpsProvider.kt` | Interface; abstracts device GPS vs NMEA GPS |
+| `GpsPosition.kt` | Data class: lat, lon, sog, cog, accuracy, timestamp |
+| `DeviceGpsProvider.kt` | FusedLocationProvider implementation |
+
+### `logbook/`
+
+| File | Role |
+|------|------|
+| `LogbookRepository.kt` | Interface |
+| `InMemoryLogbookRepository.kt` | Thread-safe impl (`@Synchronized` on save/getAll/reload) |
+| `LogbookStorage.kt` | JSON persistence to SAF directory |
+| `LogEntry.kt` | Log entry data class (type enum, text, photo, position) |
+| `LogbookFormatter.kt` | Formats log for display |
+| `LogbookPdfExporter.kt` | PDF export |
+| `VoiceLogViewModel.kt` | ViewModel for logbook + trip report tab |
+| `VoiceLogState.kt` | Sealed state for voice log UI |
+
+### `nmea/`
+
+| File | Role |
+|------|------|
+| `NmeaParser.kt` | Parses all NMEA 0183 sentences (GGA, RMC, MWV, HDG, DBT, VHW, XTE…) |
+| `AisVdmParser.kt` | Parses !AIVDM / !AIVDO AIS sentences |
+| `NmeaStreamManager.kt` | TCP/UDP connection manager; streams sentences to NmeaParser |
+
+### `routing/`
+
+| File | Role |
+|------|------|
+| `IsochroneRouter.kt` | Isochrone weather routing engine |
+| `IsochroneResult.kt` | Result data class with optimal route + isochrones |
+| `RoutePoint.kt` | Waypoint data class for routing |
+
+### `safety/`
+
+| File | Role |
+|------|------|
+| `AnchorWatchState.kt` | State sealed class for anchor alarm UI |
+
+### `sensors/`
+
+Data-only classes (no logic): `WindData`, `HeadingData`, `BoatSpeedData`, `DepthData`, `AttitudeData`, `NmeaBaroData`
+
+### `settings/`
+
+| File | Role |
+|------|------|
+| `FeatureFlags.kt` | Runtime boolean flags (e.g. night mode, race mode) |
+
+### `thermal/`
+
+| File | Role |
+|------|------|
+| `ThermalMonitor.kt` | Monitors battery temp via `PowerManager`; fires alert if overheating |
+
+### `tide/`
+
+| File | Role |
+|------|------|
+| `HarmonicTideCalculator.kt` | Offline tidal harmonic prediction; no network required |
+
+### `track/`
+
+| File | Role |
+|------|------|
+| `TrackRepository.kt` | Coordinates GPX + Room storage; source of truth = GPX/SAF |
+| `TrackStorage.kt` | SAF primary + MediaStore fallback for GPX files |
+| `TrackEntity.kt` | Room entity; `toShallowSavedTrack()` for fast list load |
+| `TrackDao.kt` | Room DAO |
+| `TackDetector.kt` | Time-based tack/jibe detection (CRUISING + RACE modes) |
+| `TackEvent.kt` | Detected tack data class |
+| `GpxParser.kt` | GPX file → list of TrackPoints |
+| `GpxSerializer.kt` | TrackPoints → GPX file |
+| `SavedTrack.kt` | In-memory track with points + metadata |
+| `TrackSummary.kt` | Lightweight summary (no points) for list display |
+| `TrackStats.kt` | Computed stats: distance, max speed, duration, tacks |
+| `TrackColors.kt` | Speed-to-color mapping for track rendering |
+| `SavedTracksFragment.kt` | List of saved tracks; SAF setup button |
+| `TrackDetailSheet.kt` | Bottom sheet: track stats, add note/photo |
+| `TrackSummarySheet.kt` | Mini summary bottom sheet |
+
+### `tripreport/`
+
+| File | Role |
+|------|------|
+| `TripReportFragment.kt` | Full trip report view (map + stats + log entries) |
+| `TripReportGenerator.kt` | Generates report from track + log data |
+| `TripReportViewModel.kt` | ViewModel for trip report |
+| `PreTripReportFragment.kt` | Pre-departure checklist / briefing |
+| `PreTripReportGenerator.kt` | Generates pre-trip summary (weather, similar trips, watch items) |
+| `PreTripReportViewModel.kt` | ViewModel for pre-trip report |
+| `PreTripModels.kt` | Data classes for pre-trip report |
+| `BoatProfileRepository.kt` | Stores boat profile (name, dimensions, polars) |
+
+### `vessel/`
+
+| File | Role |
+|------|------|
+| `VesselRepository.kt` | Crew + vessel data; in-memory + persistence |
+
+### `ui/` (top level)
+
+| File | Role |
+|------|------|
+| `MainViewModel.kt` | Top-level app state |
+| `MobHandler.kt` | MOB activation logic; always-on-top button handler |
+| `InstrumentHandler.kt` | Routes NMEA/GPS data to instrument display widgets |
+| `MapHandler.kt` | MapLibre map setup, overlay rendering, track display |
+| `MapLayerManager.kt` | Layer visibility management |
+| `LayerPickerSheet.kt` | Bottom sheet: layer toggle UI |
+| `NavLogger.kt` | In-app debug log (primary debugging tool — prefer over ADB) |
+| `LocationPermissionHandler.kt` | Runtime location permission request flow |
+| `DevLogSheet.kt` | Bottom sheet: shows NavLogger output |
+| `DirectionArrowView.kt` | Custom View: compass/bearing arrow |
+| `InclinometerView.kt` | Custom View: heel angle indicator |
+| `WaveView.kt` | Custom View: wave height visualization |
+| `WeatherActivity.kt` | Standalone weather details screen |
+
+### `ui/map/`
+
+| File | Role |
+|------|------|
+| `MapFragment.kt` | Main chart fragment; hosts MapLibre GL map |
+| `ParticleWindView.kt` | Animated wind particle overlay on chart |
+
+### `ui/safety/`
+
+| File | Role |
+|------|------|
+| `SafetyFragment.kt` | Safety tab: MOB, anchor alarm (depth + rode inputs, radius display) |
+
+### `ui/voicelog/`
+
+| File | Role |
+|------|------|
+| `VoiceLogFragment.kt` | Log tab: logbook entries + trip report; passes both `onSelect` and `onReport` to `SavedTrackAdapter` |
+
+### `ui/forecast/`
+
+| File | Role |
+|------|------|
+| `ForecastFragment.kt` | Weather forecast tab |
+| `ForecastAdapter.kt` | RecyclerView adapter for forecast items |
+
+### `ui/vessel/`
+
+| File | Role |
+|------|------|
+| `VesselRegistryFragment.kt` | Crew registry UI |
+
+### `ui/doc/`
+
+| File | Role |
+|------|------|
+| `DocFragment.kt` | In-app documentation viewer |
+
+### `ui/learn/`
+
+| File | Role |
+|------|------|
+| `LearnFragment.kt` | Sailing knowledge / learning content |
+
+---
+
+## 4. test-runner Module
+
+JVM-only; mirrors a subset of android-app source with no Android SDK dependencies. Run with `cd test-runner && ./gradlew test`.
+
+**Shared sources** (canonical copy in android-app, symlinked or mirrored in test-runner):
+
+| Package | Files |
+|---------|-------|
+| `ais/` | `AisVessel`, `CpaCalculator`, `CpaAlert` |
+| `data/model/` | `GribFile`, `GribRegion` |
+| `data/storage/` | `GribFileManager` |
+| `gps/` | `GpsPosition`, `GpsProvider` |
+| `nmea/` | `NmeaParser`, `AisVdmParser` |
+| `settings/` | `HardwareSource` |
+| `track/` | `TackDetector` ← **symlink** to android-app canonical source |
+| `vessel/` | `Vessel`, `VesselRepository`, `CrewMember` |
+
+**Important:** `TackDetector.kt` in test-runner is a filesystem symlink (`../../../../../../../../android-app/.../TackDetector.kt`). Edit the android-app copy only — test-runner picks it up automatically.
+
+---
+
+## 5. Key Architectural Invariants
+
+1. **Track storage source-of-truth = GPX files in `Documents/Nav/` via SAF.** Room (`NavDatabase`) is a read-cache for fast list loading. GPX/SAF is always authoritative.
+2. **Offline-first.** Every core feature (navigation, tides, safety) works without connectivity. Never gate core functionality on network.
+3. **`getExternalFilesDir()` is never acceptable** for user data. User data must survive uninstall. Use SAF (`Documents/Nav/`) or MediaStore.
+4. **`NavLogger` is the primary debug tool.** Log to `NavLogger`, not just Android `Log`, so in-app debug sheet shows it.
+5. **MOB button is always visible and non-dismissible.** Never place anything above it in the Z-order.
+6. **Logbook `InMemoryLogbookRepository`** has `@Synchronized` on `save()`, `getAll()`, `reload()`. Don't remove synchronization.
+7. **`VoiceLogFragment`** must pass both `onSelect` and `onReport` to `SavedTrackAdapter` — omitting either causes a CI failure.
+8. **MapLibre Android 11.x**: `Expression.get("color")` silently fails for `lineColor` on `LineLayer`. Use `Expression.step(Expression.get("speed"), ...)` to map speed → color literal.
+9. **Speed/temp/depth in UI** must use `UnitPrefs` conversions — never raw SI values.
+10. **NMEA sentences from instruments are magnetic.** Convert to true using WMM variation from `HarmonicTideCalculator`'s companion magnetic model — not a hardcoded offset.
+
+---
+
+## 6. Testing Guide
+
+```bash
+# JVM unit tests (no Android SDK required, works in cloud sandbox)
+cd test-runner && ./gradlew test
+
+# Android unit tests (requires local dev env with Android SDK)
+cd android-app && ./gradlew test
+
+# Android instrumented tests (requires device/emulator)
+cd android-app && ./gradlew connectedAndroidTest
+```
+
+Key test files by domain:
+
+| Domain | Test file |
+|--------|-----------|
+| Tack detection | `test-runner/.../track/TackDetectorTest.kt` (16 tests CRUISING + 2 RACE) |
+| Pre-trip report | `android-app/.../tripreport/PreTripReportGeneratorTest.kt` (18 JVM tests) |
+| Tide calculator | `android-app/.../tide/HarmonicTideCalculatorTest.kt` |
+| AIS / CPA | `test-runner/.../ais/CpaCalculatorTest.kt` |
+| NMEA parsing | `test-runner/.../nmea/NmeaParserTest.kt` |
+| GRIB storage | `test-runner/.../data/storage/GribFileManagerTest.kt` |
+
+---
+
+## 7. CI / Merge Workflow
+
+```bash
+# 1. Work on feature branch
+git checkout -b feature/my-thing
+
+# 2. Commit incrementally
+git add <files> && git commit -m "..."
+
+# 3. Push branch
+git push -u origin feature/my-thing
+
+# 4. Merge to main IMMEDIATELY when done (not at session end)
+git checkout main
+git merge --no-ff feature/my-thing
+git push -u origin main
+
+# 5. Delete feature branch (optional cleanup)
+git branch -d feature/my-thing
+```
+
+CI (`android.yml`) triggers only on pushes to `main`. Firebase App Distribution fires on `main` pushes only.
+
+---
+
+## 8. Context Tips & Gotchas
+
+### Navigation / GPS
+- `DeviceGpsProvider` uses `FusedLocationProviderClient`. External GPS comes via NMEA TCP/UDP through `NmeaStreamManager` → `NmeaParser` → `GpsProvider` interface.
+- GPS fix is stale after 10 s — `MainViewModel` shows red "No GPS" banner.
+
+### NMEA
+- `NmeaStreamManager` handles connection lifecycle (reconnect on failure). Don't manage socket lifecycle elsewhere.
+- All wind data from NMEA is apparent (`AWS`, `AWA`). True wind is computed in `PerformanceViewModel` using the vector triangle.
+
+### Tack Detection
+- `TackDetector` uses time-based windows (not point-count). `T_SETTLE=30s` before/after apex.
+- `circularMAD` stability check handles anchor-swing noise without an SOG gate.
+- RACE mode: `T_SETTLE=12s`, `MIN_GAP=20s`, `STAB_MAX=30°`.
+
+### Anchor Alarm
+- Alarm must survive: app background, screen lock, low battery, OS memory pressure.
+- `AnchorAlarmManager` runs in `LocationService` (foreground service), not in an Activity.
+- UI inputs (depth, rode) embedded in `card_anchor` in `fragment_safety.xml` — there is no separate anchor config screen.
+
+### Trip Report
+- Report auto-generates on tab open; RETRY button appears only on error.
+- Track map uses a full-bleed 260dp MapLibre instance embedded in the report fragment.
+- Pirate mode Easter egg: long-press title in `TripReportFragment`.
+
+### GRIB / Weather
+- GRIB files stored via `GribFileManager`; staleness checked by `GribStalenessChecker`.
+- `addGpsPoint()` matches current UTC hour prefix against `timeIso` for forecast slot lookup.
+- Particle wind animation: `ParticleWindView` is a GL overlay on the map.
+
+### Room / Track Storage
+- Room is a read-cache only. On cold start, if Room is empty, GPX files are backfilled.
+- `TrackEntity.toShallowSavedTrack()` serves the list view without loading track points.
+- `TrackStorage` has SAF primary path with MediaStore fallback; SAF state is a `Flow` observed by `SavedTracksFragment`.
+
+### Logbook
+- `onSafDirectorySelected` must call `logbookRepository.reload()` so entries from the newly-granted directory appear immediately.
+
+### Units
+- `UnitPrefs` is the single source of truth for display units. Always convert through it.
+- Distance: nm / km. Speed: knots / m/s. Depth: m / ft. Temperature: °C / °F.
diff --git a/.agent/config.md b/.agent/config.md
index 52d135d..56d9df5 100644
--- a/.agent/config.md
+++ b/.agent/config.md
@@ -7,6 +7,7 @@ This is the primary source of truth for all AI agents working on **Nav**. These
| File | Purpose |
|------|---------|
| `config.md` | **Main Entry Point** — Rules, workflows, and core mandates. |
+| `code-map.md` | **Code Map** — Package topology, key classes, gotchas, testing guide. Read this to bootstrap context fast. |
| `worklog.md` | **Session State** — Current focus, recently completed, and next steps. |
| `design.md` | **Architecture** — Component design, UI mockups, and roadmap. |
| `coding_standards.md` | **Technical Standards** — Android/Kotlin best practices, offline-first. |
diff --git a/.agent/worklog.md b/.agent/worklog.md
index c023a13..e55bf9f 100644
--- a/.agent/worklog.md
+++ b/.agent/worklog.md
@@ -8,6 +8,10 @@
## Recently Completed
+### Code Map & Context Tips — 2026-06-03
+
+Created `.agent/code-map.md`: full package topology, key class table per domain, architectural invariants, testing guide, CI/merge workflow, and a "gotchas" section covering known tricky spots (MapLibre lineColor expression, TackDetector symlink, Room as read-cache, VoiceLogFragment adapter signature, etc.). Updated `config.md` directory table to reference the new file.
+
### Tech Debt + Trip Report Redesign — 2026-06-03
Six tech debt items closed and trip report rebuilt: