Sail Tracker

Type: Software Status: In Progress Deadline: TBD Created: 2026-06-02

What is this?

A training and practice tool for sailors. GPS speed, heading, locked-heading deviation (shift indicator), CSV logging, heat maps, and session replay. Used between races and during practice sessions — explicitly not designed for use during a race. Sibling app to race-timer (see race-timer decision 002 for the split rationale).

Motivation

The features that are unsafe under one-design class rules during racing — GPS speed, heading, logging — are genuinely useful for training and post-session debrief. Splitting them into a separate app sidesteps every class-rules issue and produces a cleaner product on both sides. The original single-app scope kept colliding with ILCA Rule 22(d) and the various class-permission gates; this app exists to give those features a clean home.

What does success look like?

  • Sailors reach for it during practice sessions and after races to review the day’s data
  • Post-session debrief surface (heat maps, speed-by-heading, splits, replay) that’s better than RaceQs and the rest of the field
  • Paid App Store listing; cross-promote from race-timer

Technical

  • Stack: SwiftUI, watchOS 10+, iOS 17+. Paired iOS + watch distribution model (decision 010 reverses decision 005’s standalone-watch technical claim — Apple’s tooling no longer accepts standalone watchOS uploads). User-facing the iPhone surface is a 35-line “Open the app on your watch” placeholder; the watch app is the whole product. Sessions reach race-timer’s iPhone Training tab (M-3 home, per decision 005) via shared CloudKit container.
  • Repo: ~/projects/sail-tracker/djt53/sail-tracker (private) on GitHub
  • Build: XcodeGen → SailTracker.xcodeproj; ./build.sh archives the SailTrackerCompanion iPhone scheme (embeds watch via embed: true) → uploads to App Store Connect
  • Team: 8HZAKZWFY8 (Enigma Studio Inc., shared with race-timer)
  • Bundle IDs:
    • iPhone (carrier): com.davidtingle.sailtrackerwatch — the ASC record’s primary bundle ID
    • Watch (embedded): com.davidtingle.sailtrackerwatch.watchkitapp
  • CloudKit container: iCloud.com.davidtingle.sailtracker (linked to watch App ID)
  • ASC API key: V79677KG8A at ~/.appstoreconnect/private_keys/AuthKey_V79677KG8A.p8 (Issuer b72fb376-0438-48ac-bf15-59c4dd7b3928, team-wide, also used by race-timer)
  • Deploy target: App Store (iPhone listing with companion watch app, internally). v0.1 free (parity timer only); paywall arrives in M-2 gating GPS features
  • Intended users: sailors looking to improve — overlap with race-timer demo, different use mode
  • Debrief surface: lives as a Training tab inside race-timer’s iPhone app (M-3), not in sail-tracker’s iPhone carrier

Spec snapshot (v1.0 — see decisions/)

  • Parity baseline (decision 002): feature-equivalent to race-timer’s watch app, with one deliberate omission — no BLE fleet-sync receive. Class-rules guardrail: sail-tracker is never to be used in a real race.
  • Inherited watch features: pre-start countdown (5-4-1-Go default, 10-5-1-Go big-boat, 3-2-1-Go dinghy), sync nudge ±1s + sync-to-nearest-gun, haptic schedule (30/10/5/4/3/2/1/Go), race-state handling (Reset / Postpone / Resume / Abandon), race flag lookup (14 flags), WKExtendedRuntimeSession so timer + haptics survive screen sleep, polished UI (OLED black, SF Pro Compressed Heavy clock, tap-reveal action menu).
  • Visual divergence (decision 004): identical chassis, electric blue accent (~#0099FF-range) replacing race-timer’s electric yellow throughout.
  • Code sharing (decision 003): fork-and-strip from race-timer into new repo. No shared package or monorepo until divergence patterns warrant.
  • App shape (decisions 005 + 010): paired iPhone+watch for distribution (forced by Apple removing standalone watchOS uploads from every tool — see decision 010), but watch-first for product (iPhone surface is a 35-line “Open the app on your watch” placeholder). Debrief surface still lives in race-timer’s iPhone Training tab (M-3), with session sync via CloudKit. Decision 005 marked Superseded by 010.
  • Unique training features (post-parity): GPS speed (live + recorded), heading / COG, locked-heading deviation (shift indicator), magnetic compass, session recording on watch; debrief (heat maps, replay, CSV export) inside race-timer iPhone.

Milestones

IDNameStateNotes
M-1Watch v1.0 — parity with race-timer minus broadcast, electric blueHardware-verified (build 11)Build 11 installed via TestFlight; first interactive hardware test on 2026-06-10 returned “everything worked pretty well :)” across the M-1 timer surfaces (home → countdown → race-state transitions). Granular per-surface enumeration not done; broad-positive confirmation. Haptic pattern (gun = burst vs .failure) deferred for on-water feel test. Remaining M-1 items reduce to listing copy + privacy labels + Apple Feedback Assistant report on the standalone-watchOS upload regression (low priority).
M-2Training features on watchIn progress (heading redesign per decision 011 shipped, build 12 on TestFlight)Foundation + UI shell (sessions 1-3, see prior log entries) shipped Countdown/Speed/Heading paged container, GPS trial gate, paywall, persistence. Session 4 (2026-06-10): heading page redesigned end-to-end per decision 011 after first hardware feedback. CompassDial reversed from north-up + locked-up-rotation to heading-up frame (arrow fixed at top, bezel rotates with COG, GMT-style 30°/10° ticks, brand-anchor blue accent pulled OUT of the rotating layer). Center number turns green (lift) / red (header) signed by current tack, outside a ±3° dead-band. S/P tack chip lower-right (single tap → flipTack() + .click haptic); auto-tack detector (rate 15°/s sustained 3s, delta 70°-170°, 30s cooldown) inside appendTrackPoint. Numbers-only speed glance lower-left replaced the originally-asked mini sparkline. Tack persisted on SessionRecord + each TrackPoint for per-tack debrief splits. 11 new tests (72/72 pass). Build 12 archived + uploaded. Remaining: hardware verify the redesign + battery audit (3 suspects flagged: GPS always-on accuracy, scenePhase/.background ExtendedSession teardown, HKWorkoutSession .ended verification) + heading smoothing EMA + sparkline 3-min window. See decisions 006-011 + spike-findings + battery-audit-001.
M-3Debrief surface as race-timer iPhone Training tabFutureLives in race-timer’s iPhone app, not standalone. Reads sail-tracker watch sessions from shared CloudKit container. Map track, speed-by-heading, heat maps, replay, CSV export. Requires coordination with race-timer M-2.1 CloudKit work.
M-4Interop with race-timerFutureShared identity (Sign in with Apple), cross-launch from race-day → training debrief, race-context inheritance.
M-5Social / fleet comparisonFutureCompare tracks across sailors. Deferred — solo vs social decision still open.

Open questions

  • Solo training tool, or fleet/social training tool (compare with friends)?
  • Interop with race-timer — shared identity? Cross-launch? Or fully independent ecosystems?
  • Monetization model — defer until race-timer paywall (decision 003) reveals what works.