training-sync — three cron passes that turn Garmin's plan into calendar events¶
A local claude -p run three times a day with a mode-specific prompt from ~/.claude/routines/, prefixed by _shared-facts.md and a CLOCK line. Local because the Garmin MCP is localhost-only; the claude.ai cloud routine (trig_01Ke8GAbB9q1y4J7UXqRhtm2) is disabled and holds the old single-pass design.
Input → Movement → Output¶
Reads Garmin (coach assignment, sleep onset, body battery) via 127.0.0.1:8000/mcp and the calendar via the Google Calendar connector. Decides placement under the evening-default, earned-mornings policy (constraints live in memory angel-training-schedule-constraints.md). Writes/updates calendar events; an "Early wake" event's start time drives an iPhone Shortcuts automation that toggles one of five pre-made alarms (04:45/05:00/05:15/05:30/05:50).
Why this shape¶
Evening (21:00) is where a morning session is decided and an actionable bedtime set. Prealarm (03:45) reads the actual sleep onset and cancels an early wake before the alarm fires. Morning (06:10) reconciles today against the authoritative coach assignment and never places anything before 07:00.
Steps¶
~/.local/bin/training-sync <mode>setsTZ, picks prompt + deadline (:19-23: evening 23:45, prealarm 04:30, morning 13:00), skips if started after the deadline unlessTRAINING_SYNC_FORCE=1(:33-37).- Waits up to 5 min for Garmin MCP
/healthz(:39-45), else exitsgarmin-down. - Runs
claude -p --output-format json --tools ""(built-in tools off) from/var/tmp/claude-headlesswith--setting-sources project. Garmin comes in via~/.claude/routines/garmin-mcp.json, and the 150 garmin tools it doesn't use are denied (garmin-unused-tools.txt; regenerate it if the server adds tools). See claude-tooling. On a usage-limit rejection, parses "resets H:MMam", sleeps, retries until the deadline. - Writes
.last-output-<mode>.json,last-run-<mode>.json, appends totraining-sync.log.
Test by hand in the foreground (background Bash gets killed by the memory supervisor here: it reads free, ~300 MB on this box because qbittorrent + page cache hold ~11 GB reclaimable; dmesg is clean, cron is unaffected). Match adaptive-plan workouts by name + sport + duration; uuids change overnight.
Rules the passes carry (in _shared-facts.md, not here)¶
- Swim placement (2026-09-10): weekday swims 11:30-12:30 at Millbrook Exchange Pool (1911 Spring Forest Rd, Raleigh NC 27615) with location +
🚗title prefix; the Tesla pre-cool reads the personal calendar and fires 11:15 and 12:15. Only swims carry🚗from the coach plan. - Car-trip tagging (2026-09-10): the passes may prefix
🚗onto non-managed events matching BARBERSHOP/MARACO/D&D (haircut, Thu 11:30, D&D Luxury Barbershop, 1648 N Market Dr Ste B, Raleigh NC 27609). No other write to non-managed events is allowed. Pre-cool side:projects/tesla-automation.md. - Dated overrides in
_shared-facts.md: delete the block when its date passes. The race-week override (practice sprint tri, 2026-09-13) was removed 2026-10-19; backup was/tmp/_shared-facts.backup-20261019.md(not kept).
Log¶
- 2026-09-04: set up. 2026-09-05: wrapper hardened (usage-limit parse and sleep); a 06:10 morning pass hit the 5-hour session limit (Max 5x, overage off, no API key) because phone/web use consumed the window, since nothing else on the box runs 03:45-06:10. Prompt backups:
~/.claude/routines/backup-2026-09-05/,-09-08/,-09-10/. - Race-week Garmin workouts (Tue tempo 1692355492, Sat shakeout 1692392517 + 1692392581, Sun multisport practice 1693043006 with per-section speed/cadence) and the Sandling bike courses: see
projects/routes.md.
If you change this¶
- Hits: the three prompt files and
_shared-facts.md· the deadlines · the phone's Shortcuts automation (event title "Early wake") · the 5-hour usage window shared with interactive use. - Does not hit: anything on the array or in the docker stack except
garmin-mcp-server.
See¶
- Objects: garmin-mcp, claude-tooling
- Memory:
garmin-calendar-morning-routine.md,angel-training-schedule-constraints.md - Source:
~/.local/bin/training-sync·~/.claude/routines/