Skip to content

Releases: edxleon/runspector

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 07:27

Every small item of the roadmap: a plan that says in advance what to do on a day it is too much, reps by pace, your short days kept short, a second load signal, the morning in one command, and Garmin's own readiness and your body weight beside the app's numbers.

uv tool upgrade runspector      # or, without uv: pipx upgrade runspector
runspector serve

The plan

  • Every hard session and long run comes with its easier option: "if tired: an easy run of 48 min instead" in the preview, as the workout's description on the watch, beside today's session in the readiness tool, and in the red-day swap. One rule makes it - a hard session 10 minutes shorter and easy, at least 30; the long run one of the week's easy runs - the same rule the calendar fit already used.
  • Reps go to the watch as a pace band (± 5 s/km) once your pace has a source: a measured threshold, or Garmin's 5 km and 10 km predictions. Heart rate lags a minute behind a four-minute rep. Without a source the reps keep their heart-rate zone.
  • A weekday you always keep short - Tuesday at 35 minutes while the other runs go an hour - stays that short in the plan and gets no quality session while another day can take it. Read from your history, shown on the You tab with its evidence.
  • Seven alike, heavy days (Foster's monotony above 2.0 with a week at least as heavy as the four before) hold the next week: no step up. The Trend tab shows the number; the coach hears it only when it is high.
  • A held week after a down week is no bigger than the last regular week (it could jump past it before).

The morning

  • runspector today prints today's call in a few lines - GO / EASY / REST with its sentence, the signals behind it, today's planned session with its easier option. --sync syncs first. Exit 3 when there is nothing from today yet, so a cron and a notifier can tell "not synced" from "rest day".
  • Coming back to the page after more than 30 minutes (RUNSPECTOR_AUTOSYNC_MIN) syncs first instead of only re-reading.

Garmin's numbers beside the app's

  • Garmin's own morning Training Readiness on the Today tab, in the readiness tool and in runspector today, on watches that compute it (a fenix 6S does not). The call stays the app's; no rule reads Garmin's score.
  • Body weight from Garmin's weigh-ins (kg; nothing else of the body composition is kept), under the VO2max chart and in the VO2max tool, with how much the weight alone moves a per-kilogram VO2max once it changed by a kilogram or more.
  • One more call per sync for each (today's readiness, 56 days of weigh-ins in one range call).

Coming from v0.5

  • The old RUNCOACH_* variables are no longer read, as the v0.5 notes said. Every start names each one still set with its new name (RUNCOACH_MODEL → RUNSPECTOR_MODEL). RUNCOACH_HOME without RUNSPECTOR_HOME stops the start with the rename to make, instead of opening an empty data folder. The move of your data from ~/.runcoach stays.

Tested

  • Four more synthetic athletes run sixteen weeks of the app: one who runs the same every day, one with a short Tuesday, one whose profile asks for five runs with three days off, one who says yes to the week three days late (through the real write path against a Garmin double). The simulator found the held-week jump above.
  • Three new eval cases: Garmin says 88, the app says easy; too tired for the intervals; a VO2max rise that is the weight.

Not affiliated with Garmin. A training tool, not medical advice.

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 16:50

The first start signs you in to Garmin on the page, not in the terminal.

uv tool upgrade runspector      # or, without uv: pipx upgrade runspector
runspector serve

Garmin sign-in where it was built for

  • runspector serve on your own computer no longer asks "Sign in now?" in the terminal - a question that Enter answered with yes, so a new user landed in the terminal login. It says to sign in on the page that opens: Setup → Sign in to Garmin, with e-mail, password and Garmin's code if your account uses one; the first sync starts right after.
  • Served to your network for the phone (--host 0.0.0.0), the terminal still offers the login: from the phone the page takes no password.
  • runspector login works as before, from any terminal.

Connecting Claude, said plainly

  • A new README section, Connecting Claude, says what needs Claude and what does not, what "a Claude plan" means (Claude Code needs a paid plan - Pro, Max, Team or Enterprise - or an Anthropic API account; the free plan does not include it), how to install and sign in to Claude Code in three commands, and how to coach in Claude Desktop without any command-line tool for Claude.
  • The page's setup step, the Coach tab, runspector doctor and the first start say the same in one line each and link to it.

A sync you can see running

  • While a sync or refresh runs, the strip under the header turns the app's green: a tinted background, a dot with a breathing ring, and a light running along its bottom edge. A grey dot on the page's own background was easy to miss on a first sync, with nothing else on the page yet. With reduced motion set in the system, the tint and a green edge stay and nothing moves.

Also

  • Installing without uv is named next to it: pipx install runspector.
  • The README says in its first lines what Runspector is, and the demo video says and shows the new name.

Not affiliated with Garmin. A training tool, not medical advice.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 11:27

runcoach-ai is now Runspector: same project, same data, a name of its own. "RUNCOACH" is a registered trademark of a running-coaching company, and about ten other products share the old name. Runspector is on PyPI now, so installing no longer goes through a git URL.

uv tool install runspector
runspector serve

Coming from runcoach-ai

First stop the old app: quit runcoach serve (Ctrl-C), and close Claude Code or Claude Desktop sessions that use the runcoach MCP server, because a running server would keep writing to the old place. Then:

uv tool uninstall runcoach-ai
uv tool install runspector
  • Your data moves by itself. On the first start, ~/.runcoach becomes ~/.runspector, and the database, the Garmin session, the cards and the proposals go with it. A runcoach serve that still runs keeps the move from happening, and the start says so; runcoach mcp and a runcoach sync job take no lock, which is why they are stopped first.
  • Claude Code: claude mcp list shows where runcoach is registered. Remove it there (claude mcp remove runcoach, with -s user or -s project if that is its scope) and add claude mcp add runspector -- runspector mcp the same way.
  • Claude Desktop: in its MCP configuration, change "command": "runcoach" to "runspector".
  • A cron job or scheduled task with runcoach sync becomes runspector sync. Until then it fails, and the data goes stale.
  • The app's RUNCOACH_* settings still work for this release, with a warning, and stop working in v0.6. Rename them to RUNSPECTOR_* (for example RUNSPECTOR_HOME).
  • A browser that used the old version stays signed in, a page left open across the upgrade included, and its theme is kept.
  • Old links to github.com/edxleon/runcoach-ai lead here.
  • To go back by hand, rename ~/.runspector to ~/.runcoach and runspector.db inside it to runcoach.db, then reinstall the old version.

Garmin sign-in from the page

  • The setup has a "Sign in to Garmin" button, and a sync that failed for want of a session shows "Sign in again". Both take your e-mail, password and, if your account asks for one, Garmin's code. Only the session token is kept, exactly as with runspector login, and the first sync starts right after.

Claude

  • Coach cards run on Claude Code with your own sign-in; a Claude plan is enough and no API key is needed. Variables such as ANTHROPIC_API_KEY, exported for other work, are still stripped from the card runs, so they never bill a card. Anyone who wants them for the cards sets RUNSPECTOR_ALLOW_API_KEY=1.
  • The setup check now asks Claude Code in the same environment the cards run in. With a plan and a key exported for other work, it says "signed in with your Claude subscription" instead of pointing at the key. An API key or a cloud provider set up in Claude Code itself is named as what it is: the cards run on it, and are billed there.

Also

  • The apply and undo paths of the write path have tests for what the first mutation run found: a week where one session goes wrong still writes (and takes back) the others, a swap's undo never puts an entry on twice, and sessions the claim table holds stay out of the card's "put the rest on the watch".

Not affiliated with Garmin. A training tool, not medical advice.

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 29 Oct 16:20

Your week is yours to change. The You tab shows the week the planner goes by, filled from your history and kept up to date by it - and every value can now be set by you. A note in your own words tells the coach what the numbers cannot.

uv tool install --force git+https://github.com/edxleon/runcoach-ai@v0.4.1
runcoach serve

Your week (You tab)

  • Runs per week, your running days, the long run, days you can't run, another sport - each says where it comes from: "from your history" or "set by you".
  • Edit it with a tap. What you set wins until you hand it back with "use my history"; everything you leave open the history keeps updating.
  • A day you can't run is never planned on. If it was your usual long-run day, the long run moves (the weekend first) and the proposal says why.
  • A week asking for more days than your days off leave gets the free days, with a line saying so; on the You tab the runs a week you set cannot exceed them.
  • A day off you add leaves your own running days; the tab, the coach and the proposal all name the week the planner actually plans.
  • The history stays visible next to what you set: "set by you; your history says sat".

For your coach

  • A free-text note (up to 600 characters): "gym Mon, Wed, Fri", "less time until December". The coach reads it before every card and week plan, and the MCP tool get_athlete_profile hands it to the chat.
  • It is context for the coach, not a rule: today's decision, the spacing and the load rules stay the app's, and nothing goes on the watch without your yes. What the planner itself keeps to are the days above.

Fields that look like the app

  • The question field, the route form and the card inputs share one style: the card's own surface, a hairline border, and a quiet accent ring on focus instead of the browser's white box.

Fixed

  • A sync read only the newest 50 workouts, about 14 weeks with runs and gym side by side. It now pages back 26 weeks, so the habits of a runner back from a pause are no longer empty. Zone detail is still fetched for the last 35 days.
  • A change from a rotating rhythm to fixed days read "before: " with nothing; it says "before: no fixed weekdays".
  • runcoach profile checks the week the same way the You tab does: a long run on a day off is refused.
  • A return week of six days was planned as five, without a word: the easy runs never tried Tuesday and Thursday. Every free day is tried now (the golden test changed in exactly those 336 easy-only weeks).

Not affiliated with Garmin. A training tool, not medical advice.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 13:02

The coach that knows you. runcoach now reads your habits from your own history, shows what it has learned about you on a new You tab, feeds that into every answer, and takes questions in your own words.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.4.0
runcoach serve

Your week, on your days

  • Without a profile, the planner reads from the last eight weeks with running (looking back half a year):

    • how many days you run,
    • which weekdays,
    • your long-run day.

    Every value says where it came from: "long run on sat - your longest run fell on sat in 8 of 8 weeks".

  • After a pause it reads the weeks before the pause, not the empty weeks of the pause.

  • A weekly other sport (a gym Tuesday) keeps the quality runs off that day and the next. If that leaves room for only one quality session, the week has one.

  • Someone who runs twice a week is planned two days, not three.

  • A week with one quality session gets the one that is due: VO2max until the zone-5 stimulus is set, then threshold, or the measurement.

What the coach has learned about you (new: the You tab)

  • Each item comes with the evidence it rests on:
    • your running days, weekdays and long-run day,
    • a weekly other sport,
    • how consistent you are,
    • your intensity pattern over 28 days,
    • the VO2max trend,
    • Garmin's load focus,
    • your resting-HR baseline and zones.
  • Where a value changed and the change held for two weeks, the tab says since when.
  • Computed from your data every time it is read, never stored, so it cannot go stale.
  • Every coach card and the new MCP tool get_athlete_profile read it.

Ask the coach

  • A question field on the Coach tab: "what am I doing wrong?" becomes a short card with the biggest lever first, its number, and what it means for this week.

Garmin's load focus

  • Garmin's 4-week load per band (low aerobic, high aerobic, anaerobic) against the range it sets for you, with its verdict, for example "aerobic high shortage". It sits on the Trend tab and in get_training_load.
  • It is Garmin's opinion, shown next to the app's own minutes. No rule reads it.

The week's size, found by synthetic athletes

  • A week held on the load ratio is never bigger than the week planned before it, even three holds in a row. Before, an athlete who kept running above plan saw the held week grow.
  • A step is never smaller than the week before.
  • Back from a pause you rebuild: the week grows from your last seven days, with no down week until you are back to 80 % of your old level. Before, a down week mid-rebuild and a lagging median stretched the way back.

Under the hood

  • Garmin account settings (units, language, first weekday) are synced once a day. Nothing else from the account is kept.
  • Migration 0004 adds the load-focus columns.
  • A golden test pins the week the planner builds without habits, over 3,150 combinations, so it stays exactly as before.

Evals: 32/32

  • Three new cases:

    • a learned long-run day is changed by proposing again;
    • Garmin's load focus is a second opinion;
    • an open question gets the biggest lever first, short.

    All three fail without the skill files.

  • A case can now set a word limit that is checked in code (max_words).

Not affiliated with Garmin. A training tool, not medical advice.

v0.3.1 — synthetic athletes

Choose a tag to compare

@edxleon edxleon released this 29 Oct 07:36

The synthetic-athletes release. Eight simulated runners now go through sixteen weeks of the app, day by day against the real tools. Then the release checks whether a coach behaved over the whole course the way a runner would expect. They found two planner bugs, both fixed.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.3.1
runcoach serve

Fixed: small weeks

  • When a week's budget was too small for the planned days, the planner cut the easy runs and kept both quality sessions at their old size. The result was a three-day week of VO2max, threshold and the long run, or a single VO2max session as the whole week.
  • Because the remaining sessions kept their size, such a week could never grow. A runner coming back from a pause stayed at 70 minutes a week for good.
  • Now a small budget gets a shorter week, built like any other: one quality session at three days, never only hard sessions, scaled to the budget.

Fixed: the week after the return

  • After a full return week (4 × 24 min), the next week came out smaller than it: 72 instead of at least 96 minutes. The median of the weeks since the pause lagged behind the climb.
  • In the four weeks after a pause, the week now grows from the last seven days.

Synthetic athletes (evals/sim.py)

  • Eight runners:
    • one who follows the plan
    • one who skips 40 % of the sessions
    • three weeks off
    • ill for five days
    • always runs harder than planned
    • VO2max plateau
    • no zone detail
    • no plan at all
  • What they check, over the whole course:
    • no hard day when ill or right after a hard run
    • the first week back from a pause is easy
    • no week far above what was actually run
    • no week of mostly hard sessions
    • a lighter week at regular intervals
    • a plateau gets measured
    • after a pause, the weeks climb back to 70 % of before
  • uv run python evals/sim.py pause_and_return prints the course week by week. RUNCOACH_SIM_SEEDS=40 sweeps 40 more seeds per runner with the events moved around.
  • 320 courses pass after the fixes.
  • Honest limits: the simulated body is a toy, so this shows that the rules behave, not that the training works. It also finds only what its data model knows, so the test on a real account stays.

Evals: 29/29

  • Four eval inputs are now a day of a simulated course, pinned byte for byte against what the tools said that day.
  • Two new cases:
    • the days after a pause stay easy, however good the legs feel;
    • a week held on Garmin's load ratio is not raised on request. This is a new rule in coach.md.
  • Both new cases fail without the skill files.

Verified

  • 811 Python tests and 66 frontend tests, on Linux, Windows and macOS with Python 3.12 and 3.13, plus the install-as-a-user job.

Not affiliated with Garmin. A training tool, not medical advice.

v0.3.0 — what comes next

Choose a tag to compare

@edxleon edxleon released this 29 Oct 06:24

The what-comes-next release. You don't need a training plan or a race date: the planner reads your own history to decide what the next week and the next hard session should be. It does this in code, and every rule shows up as a line in the preview.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.3.0
runcoach serve

The week grows from what you ran

  • The base is the median of your recent running weeks. Weeks are rolling seven-day windows that end on the day you plan, so a Sunday plan counts the week you just ran.
  • The planner adds 10 % to that base, but never goes above 1.1 × the bigger of your last two weeks. Big weeks from months ago no longer set the next one.
  • Brake: a Garmin ACWR above 1.3 holds the week, and above 1.5 cuts it to 75 %. A computed ACWR can only hold.
  • Down week: every fourth week runs at 70 %. That small week is the plan, not detraining.
  • Return week after 10 or more days without a run:
    • easy running only, at 70 % of the weeks before the gap, or 50 % after more than three weeks;
    • your first days back are never a hard day.
  • If a week is too small for all its days, the planner drops days and keeps the sessions the same size. No session is longer than 1.25 × your longest run of the last four weeks.

Which hard session comes next

  • On a hard day the Today tab and get_training_readiness name the stimulus:
    • VO2max intervals if you had no zone-5 stimulus in the last 7 days;
    • a steady measurement block once VO2max intervals are done and a measurement is due;
    • threshold otherwise.
  • A run already on today's calendar is the plan. The app does not suggest a second session next to it.
  • Measurement: if Garmin has given you no new VO2max value from a run for 28 days, one steady block of 12–15 minutes at threshold gives the watch a reading. A value that drifts on a day without a run does not count.
  • Paces: without a measured threshold, the planner uses Garmin's 10k and 5k predictions, if they are recent and consistent, instead of a flat 5:00/km.
  • The rep structure alternates week to week, so you don't run the same 5×4 forever.

Reviewed on purpose

  • Before release, three reviewers and a fresh reader went through these rules. They found 23 cases where the week was wrong exactly when it mattered: after a pause, after weeks that shrank, and right behind a return. All 23 are fixed, each with a test from its scenario that failed on the old code.
  • Live test on a real Garmin account, read-only, after 45 days without a run: an easy day, a return week of 2 × 20 min easy, and a measurement due.

Evals: 27/27

  • Two new cases:
    • a measurement week's steady block is not swapped for intervals because you feel fresh;
    • a down week is explained as progression, with no volume added back.
  • Both fail without the skill files.

Verified

  • 795 Python tests and 66 frontend tests, on Linux, Windows and macOS with Python 3.12 and 3.13, plus the install-as-a-user job.

What's next is in docs/roadmap.md.

Not affiliated with Garmin. A training tool, not medical advice.

v0.2.2 — first start in one command

Choose a tag to compare

@edxleon edxleon released this 28 Oct 20:16

The first-start release: one command to get going, a clear answer on where Claude connects, and the usual worries answered where they come up.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.2.2
runcoach serve

One command to start

  • runcoach serve checks Garmin and Claude before the app opens. In a terminal it asks for the Garmin login right there. If Claude Code is installed but not signed in, it offers claude auth login.
    • Before, it took four steps in two windows (serve, runcoach login, runcoach sync --days 30, and usually a restart).
  • The first sync fetches the last 30 days by itself.
  • A login in another terminal is picked up with ↻, no restart.

Where Claude connects

  • The Today tab opens with a Setup checklist: Garmin, Claude and the first sync, each marked done, running or to do, with exactly one step for each.
  • The Claude step says plainly: install Claude Code, run claude auth login, sign in with your Claude plan. There is no API key, and nothing to enter in the app.
  • The status comes from claude auth status, so the app, doctor and the terminal all say the same thing.

What the Garmin login keeps

  • Before the e-mail prompt, runcoach login explains:
    • the password goes to Garmin only and is never stored;
    • only a session token is kept, on this computer, in a folder only your user account can read;
    • the token is sent to Garmin and nowhere else;
    • how to revoke it.
  • The token folder is set to 0700 and its files to 0600, also when RUNCOACH_GARMIN_TOKENS points outside the app's own folder.

Fewer reasons to hesitate

  • The Today headline is today's decision. The recovery light is shown as one input to it ("Easy - today's call · recovery light: Go"), not as a green "Go" above "Easy today".
  • Under the coach buttons:
    • it runs on your Claude subscription, with no bill per card;
    • Claude gets the numbers the app shows, never your Garmin password;
    • an analysis never changes your watch.
  • Under "Put on watch": only what is listed goes to Garmin, and "Take off watch" removes it again.
  • The footer shows where your data lives and that deleting that folder removes everything.
  • The README has a new section, "Before you start: the usual worries": password, cost, what Claude sees, your watch, a Garmin block (with measured request counts), medical advice, removing it all.

Demo video

  • The video now plays inline in the README. It opens on a title card, the first frame a player shows, and has no burned-in captions (scripts/video.py --captions adds them back).

Evals: 25/25 in two full runs

  • The judge is now told to grade only the conditions a case names. It had started failing correct answers for options the case never asked for.
  • Two cases list their allowed outcomes outright instead of leaving the judge to do the arithmetic.
  • Without the skill files, the safety cases still fail, so the judge still tells good answers from bad ones.

Verified

  • 731 Python tests and 66 frontend tests, on Linux, Windows and macOS with Python 3.12 and 3.13, plus the install-as-a-user job.
  • A real first start in a terminal on an empty data folder.

Not affiliated with Garmin. A training tool, not medical advice.

v0.2.1 — what a deep review found

Choose a tag to compare

@edxleon edxleon released this 28 Oct 18:59

A review release. v0.2.0 put the write path in front of a real watch; v0.2.1 is what a deep review of it turned up, each fix with a test that was red without it. It also has a demo video, and the coach's rules finally reach a chat in Claude Code.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.2.1

Nothing reaches the watch twice

  • A 502 after Garmin had created a workout, a timed-out calendar answer, a connection dropped after the request went out, and a process that died mid-upload were all read as "nothing was created". The claim was released and the athlete was told to apply again, which put the session on the watch a second time. Failures are now classified by HTTP status first and then by the exception chain, read from the top. Only a connection that was never made counts as "nothing created".
  • A proposal clicked after its day had passed was scheduled on that past day. Past sessions are now skipped.
  • "Apply again" is no longer advised for a step that may already have happened, on the card, in doctor or in chat.

A week that fits the calendar it lands on

  • propose_week used to add its sessions to whatever was already on the calendar, so one yes could mean two hard sessions on Tuesday and two long runs on Sunday. Now a day that already has an entry gets nothing. A quality session less than 48 h from another hard day, or on the day before the long run, becomes an easy run. Each change is listed on a calendar: line, and every session in the package is listed with its day.
  • Undoing a red-day swap now also puts the replaced session back on its day. Applying the swap again after that takes it off again, and the summary reports only what actually happened.
  • Calendar titles are read as whole words: "Langsamer Dauerlauf" is no longer taken for a long run.
  • A week that was only partly applied can now be taken off the watch from the card as well.

Numbers from runs, not from every sport

  • Weekly running volume, the "where you stand" comparison and the week planner's size used to count walks and rides: a 90-minute runner could be planned a 480-minute week. They now count runs only. Zone bounds are taken from the last run, not from a ride.
  • A steady session asked for by time keeps a 15-minute block (90 minutes used to become one 77-minute block at threshold).
  • "Last hard session" is counted from the same day as the number it shows.

The chat gets the coach

  • In Claude Code and Claude Desktop the model used to see only the tool descriptions: no 48-hour rule, no energy boundary. Claude Code keeps only 4,096 characters of a server's instructions, so the server now sends the core rules in about 1,900 characters. A new read-only tool, get_coaching_reference, returns the full rule set, and in practice the model calls it first.
  • get_training_readiness now syncs first when the last sync is more than 30 minutes old (RUNCOACH_AUTOSYNC_MIN), so a run from this afternoon is no longer invisible to the next "should I train today?".

Privacy

  • The bootstrap token no longer reaches any log line.
  • The README now says plainly that while serve runs, other local accounts can reach it. Set RUNCOACH_TOKEN on a shared machine.
  • The release gate now also checks commit author and committer addresses, plus home paths written as dashed or URL-encoded names.
  • The server keeps its single-instance lock fresh when it runs for days.

Evals

  • The suite has grown from 18 to 25 cases. The new ones cover the 48-hour rule against an athlete who wants intervals, a moved session that no day can take, an unclear apply, a week fitted to a full calendar, "take it back off", unsynced days not read as detraining, and tempo runs that do not count as VO2max work.
  • The judge is now sonnet. Measured: haiku gave the same answer PASS once and FAIL once.
  • One rule was sharpened. "A short easy jog if it's pain-free" is a session built around a symptom.

Verified

  • 719 Python tests and 62 frontend tests, on Linux, Windows and macOS with Python 3.12 and 3.13, plus the install-as-a-user job.
  • A live chat against a real Garmin account, in which the auto-sync and the coaching reference were both used.
  • Evals: the last two full runs after the final prompt change passed 24 of 25 each, and the case that missed was a different one each time. In one of them the judge's own reason calls the result valid. The badge says 24/25.

Not verified yet

  • The fitted week and the swap with its undo have only run against the fake Garmin client, not against a real account.

Not affiliated with Garmin. A training tool, not medical advice.

v0.2.0 — the write path

Choose a tag to compare

@edxleon edxleon released this 28 Oct 16:54

The write path. runcoach can now put a session on your watch, and take it back off.

uv tool install git+https://github.com/edxleon/runcoach-ai@v0.2.0
claude mcp add runcoach -- runcoach mcp
# "plan me an easy 40 minutes for tomorrow" -> you see the session -> "yes" -> it is on the watch

What is new

  • propose_workout builds a structured session for your route or time budget from your own Garmin zones. propose_week files a polarised week as one package, and on a red day a proposal can replace the hard session on the calendar. Proposing never writes anything.
  • apply_workout uploads, schedules, pushes to the watch and reads the workout back to verify structure and targets. undo_workout takes it off the calendar again. In the app it is one button on the coach card ("Put on watch", two taps); in Claude Code it is your "yes". The app's own coach runs cannot call either write tool (--disallowedTools, pinned by a test).
  • Job, card and proposal files are written thread-safely: two writers in the same server process no longer share a temp file (a double tap on "Put on watch" could raise FileNotFoundError).
  • The coach comments on Garmin's auto-detected rep count in one clause at most and then judges the session the athlete says they ran.
  • garminconnect 0.3.20. The check on the vendor's exceptions now fails on a new exception and no longer on one that went away.

What is verified

  • 672 Python tests and 60 frontend tests on Linux, Windows and macOS × Python 3.12 and 3.13, plus a job that installs the wheel as a user would and drives the installed tool.
  • The write path against a real account, on 2026-10-28: one easy session through runcoach mcp, proposed, uploaded, scheduled, pushed, seen on the watch, read back and verified, then undone. A second, independent Garmin client cross-checked every step.
  • Eval suite: 14/18 on the current sonnet with a haiku judge, twice, with a different four failing each time. Across four runs every case passed at least once; the failures read as a strict judge rather than a wrong coach, with one exception that is fixed (above). The badge says 14/18 because that is what the last run said.

What is not

  • propose_week as a package and the red-day swap that unschedules a calendar entry have not been run against a real account, only against the fake client the suite uses.
  • One athlete, one account, one watch. garminconnect tracks an unofficial API; runcoach doctor says so when Garmin changes underneath it.

Not affiliated with Garmin. A training tool, not medical advice.