Skip to content

AGENTS.md — SailEver WatchApp for Garmin (apps/garmin-watchapp)

How to build, test, debug and find code. What the app does — every screen, value, colour, menu and upload rule — is specified in ../../docs/specs/watchapp/. Read the workspace ../../AGENTS.md first (Definition of Done, glossary, spec rules).

Sibling ports implement the same spec: Wear OS in ../../apps/android/ (its §3 maps every Garmin file to its Kotlin counterpart), Apple Watch planned (../../docs/plans/apple_watch_port_plan.md). A change here that isn't Garmin-specific goes to Wear OS too (/port-change), or into parity.md.

Build and run

mise garmin:watchapp [device]     # build + simulator + run; no device: the one used last; --track N plays a track; --list
mise garmin:check                  # the Definition of Done builds (fenix5plus, fenix8pro47mm) and unit tests

By hand (what the tasks run):

# Java and the Connect IQ SDK come from mise (root mise.toml, AGENTS.md §3.0).

monkeyc -f monkey.jungle -o /tmp/sailever.prg -y ~/.config/sailever/developer_key -d fenix5plus -w
connectiq &                                  # simulator
monkeydo /tmp/sailever.prg fenix5plus

# Release build with size report
monkeyc -f monkey.jungle -o /tmp/sailever.iq -y ~/.config/sailever/developer_key -r -O 2 --build-stats 0
# Unit tests (SLTB golden vectors, paused track)
monkeyc -f monkey.jungle -o /tmp/test.prg -y ~/.config/sailever/developer_key -d fenix6 --unit-test && monkeydo /tmp/test.prg fenix6 -t

Use a device id from manifest.xml (103 round products; plain fenix5 is not one). Store releases: /sailever-garmin-release skill.

In the simulator, drive the data from Simulation → Activity Data and the position from Simulation → GPS; without them SOG stays 0.0 and the Track view shows no fix. Simulated sailing tracks: mise tracksim play N --target garmin (../../tools/tracksim/README.md).

Debugging

  • VS Code (.vscode/launch.json, Monkey C extension): F5 debugs with breakpoints, Ctrl+F5 runs without. Configurations: Run App: fenix 8 Pro 47mm, Run App: pick device, Run Tests, E2E: arm autopilot.
  • Debug builds: Config.mc (:debug) points at http://127.0.0.1:3000/api/v1, [sync] trace lines are on (source/sync/SyncLog.mc), and "last known" GPS fixes are accepted (Config.MIN_GPS_QUALITY), which is all a manually set simulator position gives.
  • E2E smoke run: run E2E: arm autopilot (valid 2 min; Run Tests arms it too), then Run App: fenix 8 Pro 47mm. The app starts a trip, feeds a synthetic 6 kn course if there is no fix, and stops after 40 s through the save view. Expect [sync] POST seq 0 … response 201, then seq 1. source/sync/E2EAutopilot.mc is compiled out of release builds.
  • Rails side: bin/dev in ../../apps/server, then Attach to bin/dev from its .vscode/launch.json (rdbg extension) or bundle exec rdbg -A.
  • Real watch: task Sideload: debug build to USB watch copies bin/SailEver.prg to GARMIN/APPS/ and creates GARMIN/APPS/LOGS/SAILEVER.TXT for System.println output. Requests go through the phone, so expose local Rails over HTTPS (cloudflared tunnel --url http://localhost:3000) and set the (:debug) API_BASE_URL to it for that session. No breakpoints on the device.
  • Environment layout check: debug builds print the measured rows (EnvironmentView.reportLayout): monkeydo /tmp/p.prg fenix5plus | grep -E "OVERFLOWS|COLLIDES|OFF SCREEN"

Fonts

Custom bitmap fonts (BMFont .fnt + PNG sheet) work on every target, including fenix 5; vector fonts don't. tools/genfont.py generates the sheets for the 9 screen families into resources-round-<w>x<h>/. Face, sizes and glyph sets: visual-system.md. Adding a glyph (e.g. a letter to the numerals font) means regenerating all families.

Source map

Path (source/) Role Spec
SailEverApp.mc Entry point, GPS on, crash recovery, onStop lifecycle
StartMenu.mc Start menu lifecycle
TripDelegate.mc Button / touch dispatch on the instrument screens input
ActionMenu.mc, VesselStateMenu.mc START menu, vessel state picker, discard confirm menus
TripContext.mc, DataModel.mc 1 Hz tick, totals, timers, state periods, plot buffer, overlay slot recording
TripRecorder.mc FIT session + developer fields recording
TripStore.mc, SettingsStore.mc Application.Storage: trip, paused track, settings (cfg_*) recording, alerts
VesselState.mc The 8 states, labels, accents vessel-state
AlertManager.mc, settings/SettingsMenu.mc Alerts, auto-stats settings alerts
CogCalculator.mc, BaroIndicator.mc, SunCalc.mc, MoonCalc.mc, Chart.mc Calculations calculations
Theme.mc Palette, fonts, proportional sizes visual-system
views/DashboardView.mc, TrackView.mc, EnvironmentView.mc Instrument screens 1–3 dashboard, track-view, environment
views/PauseView.mc, StatsSummaryView.mc, SplashView.mc Pause, stats, splash pause-view, stats
views/EphemeralView.mc, AlertView.mc, AutoStatsView.mc 6 s overlays alerts
views/SyncView.mc, ConsentView.mc, PairingView.mc, DeviceRegistration.mc, TideService.mc Save / upload views, privacy consent, pairing, registration, tides sync-and-pairing
sync/SltbEncoder.mc, TrackSession.mc, TrackQueue.mc, SyncEngine.mc SLTB encoder, segments, queue, uploader
sync/Net.mc The only caller of Communications.makeWebRequest: blocks every request without consent sync-and-pairing
Config.mc API URL, debug flags —

Definition of Done (Garmin)

In addition to the workspace rules: builds without warnings for a small (fenix5plus, 240 px) and a large (fenix8pro47mm or similar, 454 px) target; unit tests pass; changed screens checked in the simulator on both sizes; the spec is updated and the Wear OS port follows or a parity row is added.