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 athttp://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, thenseq 1.source/sync/E2EAutopilot.mcis compiled out of release builds. - Rails side:
bin/devin../../apps/server, then Attach to bin/dev from its.vscode/launch.json(rdbg extension) orbundle exec rdbg -A. - Real watch: task Sideload: debug build to USB watch copies
bin/SailEver.prgtoGARMIN/APPS/and createsGARMIN/APPS/LOGS/SAILEVER.TXTforSystem.printlnoutput. Requests go through the phone, so expose local Rails over HTTPS (cloudflared tunnel --url http://localhost:3000) and set the(:debug)API_BASE_URLto 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.