Skip to content

AGENTS.md — SailEver Workspace Guide

Rules, repository map and pointers for AI agents (Claude, Gemini, Antigravity) working anywhere in the SailEver workspace. One repository; each folder under apps/ has its own AGENTS.md. This file holds no product behaviour: that is in docs/specs/.


0. Definition of Done (read before handing anything back)

Every agent (Claude, Gemini, Antigravity, and every subagent) follows these rules. Commands are mise tasks (§3): mise check runs the gates of what changed. Work is not done, and must not be reported as done, until all of them hold.

  1. Follow all instruction files. This file, the app's own AGENTS.md, and the user's global instructions (~/.claude/CLAUDE.md). In particular: find code with semble (mcp__semble__search / semble search), not grep or find. Use grep only to list every occurrence of a known literal string.
  2. The spec and the code agree. Behaviour changes update the spec in the same piece of work (/spec-update). A WatchApp change that isn't platform-specific is made on every implemented platform (/port-change), or the gap goes into docs/specs/watchapp/parity.md. mise doccheck check reports 0 errors. The architecture stays arc42-shaped (docs/architecture/): a change that touches a building block, a runtime flow, a quality goal or a decision updates its section there (or adds an ADR that names the quality goals it affects), and link, never copy, from specs and ADRs. In Claude Code, a Stop hook (mise doccheck hook) enforces this; for a pure refactor run mise doccheck ack "<reason>".
  3. Rails (apps/server): the quality gate passes. mise server:check (bin/quality --ci exit 0 and bin/rails test 0 failures) on the final tree. Fix new findings; never silence a check, add ignores, or raise thresholds without the user's approval. A Stop/SubagentStop hook (apps/server/script/agent_gate.sh) enforces this.
  4. Garmin (apps/garmin-watchapp, apps/garmin-datafield): builds and tests pass, changed screens checked in the simulator on a small and a large screen. Details in each app's AGENTS.md.
  5. Wear OS (apps/android): build, tests and lint pass. mise android:check (./gradlew :core:test :wear:assembleDebug lintDebug) with 0 lint errors, and UI or recording changes checked on the emulator. Details: apps/android/AGENTS.md §11.
  6. Behaviour is preserved. For refactors, compare before and after: tests, plus rendered HTML for view changes, screenshots for watch screens.
  7. Privacy (GDPR) holds. Everything follows docs/specs/shared/privacy.md:
  8. No watch app (Garmin, Wear OS, Apple Watch, the CIQ SDK) sends anything to SailEver before the user has said Yes on the watch. Every request goes through the consent gate.
  9. Every new table or column holding user data is removed by account deletion. account_erasure_test.rb must stay green.
  10. Every new kind of data or new outside service is added to the privacy page in the same change.
  11. Breaking a rule needs the owner's decision and a new ADR first.
  12. Report honestly. State what was verified and how. List anything skipped, warnings that remain, parity gaps left open, and side effects (for example, rows written to the development database).

1. Products and folders

Product Repo Stack Spec Build guide
WatchApp, Garmin apps/garmin-watchapp/ Monkey C, Connect IQ 3+ docs/specs/watchapp/ AGENTS.md
WatchApp, Wear OS apps/android/ Kotlin 2.4, Compose for Wear OS same spec AGENTS.md
WatchApp, Apple Watch not built Swift, SwiftUI same spec (⏳ sections) docs/plans/apple_watch_port_plan.md
DataField, Garmin apps/garmin-datafield/ Monkey C docs/specs/datafield/ AGENTS.md
Server: website, logbook, watch API apps/server/ Ruby 4.0, Rails 8.1, PostgreSQL, Hotwire docs/specs/server/ AGENTS.md
Phone apps, iOS / Android not built (click dummies in docs/companion_app/) — docs/specs/mobile/ —
Track simulator tools/tracksim/ Python README —

Shared by several products: docs/specs/shared/ — SLTB track sync, device registration and pairing API, vessel states, sailing calculations.

The watch apps are one product. Garmin, Wear OS and (later) Apple Watch implement the same spec. When you work in one watch app, the others are your siblings: read the spec, not just the code in front of you, and carry shared changes across (/port-change). The Wear OS AGENTS.md §3 maps every Garmin file to its Kotlin counterpart.


2. Documentation

Where What Rule
docs/glossary.md Domain terms Use these words in UI, docs, code comments. It is always track (the glossary lists the banned words); device for a paired watch in the signed-in area.
docs/specs/ What every product does, down to each value on each screen Single source of truth. General behaviour first, then ### Garmin / ### Wear OS / ### Apple Watch (or ### iOS / ### Android). One place per fact. Edited in place, no change requests.
docs/architecture/ How the system is built and why, in the 12 arc42 sections The map: links to specs, ADRs and the infra map, never copies them. Each section has Last reviewed:; every ADR is listed in section 9. Diagrams are Mermaid.
docs/adr/ Decisions and their reasons Append-only. Template and the Quality goals affected line in its README.
docs/plans/ Work not built yet Deleted once built (behaviour moves into the spec).
docs/marketing/ Film scripts, pricing Not a spec; must not contradict it.
apps/<app>/AGENTS.md Build, test, debug, code map No behaviour. Shown in the docs viewer as they are (docs/development/, an include: page per file; doccheck fails when one is missing).

Skills (in .agents/skills/, linked into .claude/skills/):

Skill When
/spec-update Before and after any behaviour change: read the spec, then update it
/port-change Carry a WatchApp change to the other platforms
/spec-audit Cross-check a spec against all implementations; mise doccheck drift lists specs whose code changed since they were verified
/sim-track Simulated sailing tracks for any watch app or the server
/sailever-garmin-release Garmin store releases

mise doccheck: check (banned terms, broken links, missing code references, the 12 arc42 sections, a Mermaid diagram in sections 3, 5, 6 and 10, no ⏳ placeholder older than 45 days, every ADR in section 9, a docs page per AGENTS.md), drift, gate (also: a structural change, such as a new job, model, controller or migration, a change to routes.rb, deploy.yml, recurring.yml, queue.yml, infra/ or a workflow, needs a change under docs/architecture/ or docs/adr/), ack "<reason>" (works in a worktree).

Docs viewer. mise docs shows docs/ in the browser (MkDocs, live reload); mise docs:build builds it with --strict (broken links fail). The HTML is generated and never committed (the infra map is the one exception). Cloudflare Pages builds the same site from main, visible to the owner only. Config: tools/docs/.


3. Toolchains & development commands

Every command is a mise task, started from anywhere in the repository (mise <task> is short for mise run <task>; mise tasks lists them all with descriptions). The tasks are files in mise-tasks/ (shared helpers in tools/mise/); a new runner is a new file there. There is no bin/ folder at the root.

3.0 Setup and tool versions (mise)

mise setup          # everything in one command: git hooks, Java, uv, OpenTofu, gitleaks, Ruby and Node, server database, Android SDK path, status of the optional tools
mise.toml pins Java, uv, OpenTofu and gitleaks (checksums in mise.lock); Ruby and Node come from apps/server/.ruby-version / .node-version. It also puts the Connect IQ SDK on PATH. Activate mise in your shell; without activation, prefix commands with mise exec --. mise settings add trusted_config_paths ~/Workspace/sailever (once per machine) makes every worktree trusted. Secrets are never set in mise.toml: they stay in 1Password.

3.1 Tasks

Task What it does
server (server:dev) Rails server. Main checkout: sailever_development; a worktree gets its own database (sailever_<worktree>_dev); --db NAME overrides
server:setup · server:test · server:quality · server:check bundle + db:prepare; bin/rails test; bin/quality --ci; tests then quality gate (§0.3)
server:db-pull <identity> [object] Replace sailever_development with the newest production backup (main checkout only; asks first, --yes skips)
garmin:watchapp [device] · garmin:datafield [device] Build and run in the Connect IQ simulator. No device given: the one used last (first time fenix8pro47mm / fenix7). --track N plays track N, --list lists the products
garmin:check Garmin gates (§0.4): builds with -w for fenix5plus and fenix8pro47mm, unit tests (--skip-tests builds only)
android:watchapp [avd] Wear OS app on the emulator (never the real watch). No AVD given: the one used last, else the first Wear AVD; --list lists them
android:watchapp-device Release build (debug-signed) onto the real OnePlus Watch 2; refuses while a track records
android:check :core:test :wear:assembleDebug lintDebug (§0.5)
android:app reserved for the phone app
docs (docs:serve) · docs:build Docs viewer in the browser with live reload; static build with --strict into tools/docs/site (git-ignored)
check doccheck check, the docs build when docs/ changed, plus the gates of the apps whose files changed (--all runs every gate)
guard … · doccheck … Guard (§4) and the spec checker (§2): check, drift, gate, ack "<reason>"
tracksim … Track simulator (§3.2)
secret NAME · op-env NAME,NAME cmd… · tofu … Secrets from 1Password (docs/credentials.md); mise tofu plan runs OpenTofu with its four secrets
check-tasks Tests of the tasks themselves

Rails binstubs stay in apps/server/bin/ (Kamal, the Dockerfile and Rails expect them); the tasks call them. The last device used is kept in ~/.local/state/sailever/.

3.2 Track simulator (tools/tracksim, shared by all watch apps)

Simulated sailing tracks: never over land, rocks, wrecks, fish farms or shallows. Use these instead of a hand-made fake GPS source. In Claude Code: /sim-track <prompt>.

mise tracksim generate scenario.yaml                       # → tracks/NNN-<slug>/
mise tracksim list / show 1 / map 1
mise tracksim play 1 --target android|apple|garmin --speed 10
mise tracksim upload 1 --token <device token>              # SLTB to local Rails (writes to the dev database)
- tracks/ is shared test data: committed, referenced by number ("track 001"), numbers never reused. Ports read ../../tracks/NNN-…/track.csv from their apps/ folder (1 Hz); don't copy tracks into the apps. - The track.csv columns are a contract: add columns, never rename or remove them.


4. Git safety and release tags

Guard (built): mise setup switches on the git hooks in .githooks/; they run mise guard (tools/guard/guard.py) on every commit, commit message and push. In Claude Code a PreToolUse hook runs mise guard agent on every Bash call. It blocks secrets (gitleaks, .gitleaks.toml), key and credential files (tools/guard/forbidden.txt), files over 5 MB, commit messages without a [prefix], pushes to remotes outside tools/guard/allowed-remotes.txt, force-pushes and deletions of main, and release tags that break the rules below.

  • Never bypass it: don't skip hooks, don't change core.hooksPath, don't force-push. A false positive is fixed by an entry with a reason in .gitleaks.toml or tools/guard/allowed-paths.txt, in a commit of its own, after asking the user.
  • Credentials: every secret, its password-manager entry name and where the code expects it: docs/credentials.md. Use those entry names when guiding the owner.
  • Never read or print secrets: .env*, developer_key, keystores, .p8/.p12/.pem, master keys, *.tfvars, *.tfstate, ~/.config/sailever/, ~/.ssh/, ~/.oci/. The Garmin signing key lives outside git (~/.config/sailever/developer_key, or SAILEVER_DEV_KEY).
  • GitHub settings, secrets and visibility are the user's. Agents don't run gh secret, gh variable, gh repo edit or settings-changing gh api calls.

Release tags (workflows planned; the tag rules are enforced by mise guard already): a store build starts only from a tag <product>/vX.Y.Z[-beta.N|-rc.N] on a commit on main, with <product> one of garmin-watchapp, garmin-datafield, wear, android, apple. Tags are never moved, deleted or reused; a failed release becomes the next patch version. The version in the tag must equal the version in the app. Agents never create or push release tags: they prepare the version bump and the notes and hand the exact git tag -a / git push commands to the user. The server has no tags; it deploys from main. Full rules, workflows and targets: docs/plans/monorepo_plan.md §5.