Applies 1/2/3-strike adjustment rules to plan.json based on a single workout's analysis dict. Mutates plan only when thresholds fire. Called after every workout log.
Renders today's workout card from plan.json and daily_state.json as a Markdown block. Use for every /run-today invocation. No writes — pure read-and-render.
Conversationally captures a completed workout (duration, distance, RPE, HR, splits, notes) and writes it to workouts.json. Triggers analyze-workout and adapt-plan downstream. Use for /run-log.
Inserts a post-race or injury-flag recovery block into plan.json: 4 days rest + 3 easy jog days (post-race), or 3 rest + 1 recovery (red-flag-fatigue), or 7 rest days (injury). Use after /run-race-recap or when adapt-plan returns red_flag_recovery.
Web-search-backed Q&A for novel coaching questions outside the plan's standard logic: heat acclimation, recovery from illness, pacing strategies for unique courses. Use for /run-research and when the Coach cannot answer from training data alone.
Aggregates the past 7 days of completed workouts: total mileage, quality session adherence, fatigue indicators (RPE creep, HR drift). Decides hold/advance/recovery for the next week. Use for /run-week.
Pulls recent run activities from the garmin-givemydata MCP server: triggers garmin_sync to refresh the local SQLite, queries activity IDs in the requested date range, fetches per-activity detail (with splits, HR zones, weather, running dynamics), normalizes to workouts.json schema via garmin_normalize.py, deduplicates by garmin_activity_id with preferred_source tiebreaker, and runs the same analyze-workout + adapt-plan pipeline as manual logging. Requires users.integrations.garmin.connected = true.
Pulls recent run activities from the Strava MCP server, normalizes them to the workouts.json schema using strava_normalize.py helpers, deduplicates by strava_activity_id with preferred_source tiebreaker, and runs the same analyze-workout + adapt-plan pipeline as manual logging. Requires users.integrations.strava.connected = true.