feat: training module — plan generator, session logging, progression#2
Merged
Conversation
What this adds:
- Equipment-aware Iron Mike plan generator (lib/training/plan-generator.ts)
- Pure function, fullbody_x2 with fallback chain per exercise slot
- Day A: squat + horizontal press + vertical pull + tricep + bicep + calves
- Day B: hip hinge + incline press + horizontal pull + OHP + side delts + glutes
- 7 unit tests covering equipment fallback + preference overrides
- Server actions (app/(app)/training/actions.ts):
- saveEquipment — wipes + reinserts user_equipment rows
- generatePlan — deactivates old plan, inserts plan + days + exercises
- startSession / startNextSession (alternates A/B based on last session)
- logSet — writes a workout_set with set_type
- endSession — sets ended_at
- UI:
- /training — landing page (active plan summary + recent sessions + Start button)
- /training/setup — equipment wizard with grouped checkboxes (Hanteln, Maschinen,
Bodyweight, Cardio); generates plan on submit
- /training/session/[id] — active session with progress indicator,
auto-advances through exercises, set-by-set logging with Brzycki-based
weight suggestions from progression.ts
- /training/history — list of past sessions
- Queries layer (lib/training/queries.ts) with explicit row types
to handle Supabase's ambiguous nested-relation typing
- scripts/setup-household.mjs — links a Supabase auth user to a Household
with admin role + sets default user_preferences/training_preferences/macro_targets
Verified:
- npm run typecheck — green
- npm run lint — green
- npm run test — 73 passing (was 66, added 7 plan-generator tests)
- npm run build — 8 routes, training/* compiles
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each user now has multiple equipment-locations (Gym, Home, optional Travel/custom)
with its own equipment inventory and active plan.
Schema migrations (idempotent, in supabase/migrations/):
- 0001_multi_location.sql:
- new table equipment_locations (user_id, key, display_name, position)
- user_equipment.location_id (FK), PK migrated to (location_id, equipment)
- training_plans.location_id, workout_sessions.location_id
- data migration: existing user_equipment rows assigned to a default 'gym' location
- 0002_home_exercises.sql:
- 22 new exercises seeded for KB / Bands / Bodyweight (idempotent ON CONFLICT)
- kb-goblet-squat, kb-swing, kb-floor-press, kb-rdl, kb-bent-row,
kb-press-overhead, kb-suitcase-deadlift, kb-curl, kb-tricep-extension,
kb-bulgarian-split-squat
- band-pulldown, band-row-seated, band-pull-apart, band-lateral-raise,
band-press, band-curl, band-tricep-pushdown, band-overhead-tricep
- push-up, inverted-row, bw-bulgarian-split-squat, bw-pike-push-up
Plan generator (lib/training/plan-generator.ts):
- generateFullbodyX2 — unchanged, for gym
- new generateHomeQuick30 — 5 slots, KB > Band > Bodyweight fallback chain,
reduced warmup (1 set instead of 2), ~25-30 min working
- new generatePlanForLocation(key, available, prefs) — dispatches by location key
- 9 new tests for home variant and dispatch logic (was 7, total now 16 in this file)
UI:
- /training/setup — wizard now lists Locations as cards with editable name +
per-location equipment grid; "+ Location hinzufügen" button supports custom
locations beyond gym/home
- /training — landing page shows one card per Location with its plan + a
"Heute hier trainieren ([Location])" button
- StartSessionButton takes locationId, picks next plan day specifically for
that location's history
Server actions:
- saveLocations: upserts the full list, deletes locations no longer in input
(cascades to user_equipment)
- generateAllPlans: iterates locations, generates an active plan for each
using the appropriate generator (Gym → Iron Mike, Home/Travel → Home Quick 30)
- startNextSessionForLocation(locationId): per-location alternation of A/B days
Migration tooling:
- scripts/apply-migration.mjs — runs a single SQL file, idempotent
Verified:
- npm run typecheck — green
- npm run lint — green
- npm run test — 82 passing (was 73, +9 plan-generator tests)
- npm run build — 8 routes
- Migrations applied successfully to dev DB
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…laceholder routes - 0003_fix_rls_membership.sql: drop the recursive 'or household_id = current_household_id()' clause from the household_members SELECT policy. The recursive lookup made the role return null in the app layout, hiding admin-only nav items. - src/app/(app)/page.tsx: home cards are now Link components routing to /training and /nutrition, with hover state. - New placeholder pages: /nutrition, /body, /chat — phase-N teaser content so the bottom nav links don't 404. - scripts/debug-membership.mjs: small helper that prints each auth user's membership role + household for sanity-checking. Verified: - npm run typecheck — green - npm run lint — green - 82 tests still passing Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migration 0004 + 0004b — split because Postgres ALTER TYPE ADD VALUE darf nicht in derselben Transaktion wie INSERTs den neuen Wert nutzen: - 0004_trx_equipment.sql: alter type equipment_type add value 'trx' - 0004b_trx_exercises.sql: 6 TRX-Übungen (idempotent ON CONFLICT) - trx-row (Horizontal Pull, Body-Angle modulierbar) - trx-push-up (Horizontal Push mit Suspension-Instabilität) - trx-bulgarian-split-squat (Quads, hinterer Fuss in Schlinge) - trx-pike (Vertical Push surrogate) - trx-curl (Bicep, suspension) - trx-tricep-extension (long-head stretch via Schulter-Position) Code-Updates: - src/lib/db/schema.ts equipmentType enum bekommt 'trx' - src/lib/training/plan-generator.ts EquipmentType union + Home-Slot-Optionen: - Day A Quads: TRX-BSS als 2. Wahl nach KB-BSS - Day A Horizontal Press: TRX-Push-up als 2. Wahl nach KB-Floor-Press - Day A Tricep + Bicep: TRX-Variante als Mittelweg - Day B Horizontal Pull: TRX-Row als BESTE Wahl (vor KB-Row) - Day B Vertical Press: TRX-Pike als Mittelweg - src/app/(app)/training/setup/setup-wizard.tsx: 'TRX (Suspension Trainer)' als Toggle in Bodyweight-Gruppe, default aktiv für Home-Location Tests: 84 passing (+2 TRX-spezifisch) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beim Project-Setup hatten wir 'Automatically expose new tables' deaktiviert (Sicherheits-Empfehlung von Supabase). Folge: keine SELECT/INSERT/UPDATE/DELETE GRANTs für die app-roles → 'permission denied for table equipment_locations' beim Plan-Erstellen. Migration 0005 setzt explizit: - authenticated + service_role: full CRUD (RLS filtert für authenticated, bypass für service_role) - anon: nur SELECT (für künftigen Demo-Mode auf shared catalogs wie exercises) - Default privileges für künftige Tables/Sequences/Functions, sodass dieser Fehler bei neuen Migrationen nicht wieder auftritt. scripts/check-grants.mjs zum schnellen Audit was gewährt ist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User wollte vor dem Plan-Erstellen eine KI-Anamnese statt Defaults — und Mike's
Methodik genauer respektiert (Beinpresse statt Kniebeuge, Pec Deck vor Bench).
KI-Anamnese (Phase 6 vorgezogen):
- src/lib/ai/prompts/training-anamnese.md: System-Prompt mit Mike's Regeln,
alternativen Stilen (klassisch 3×8-12), Gesprächs-Regeln (max 7 Fragen, knapp,
auf Deutsch), tool-driven Output
- src/app/api/coach/anamnese/route.ts: streamText-Endpoint mit Vercel AI SDK v4
- Tool 'set_training_preferences': upsert in training_preferences mit
style → split_style + volume_style mapping
- Tool 'complete_anamnese': signal für UI-Redirect
- maxSteps: 8 (Coach kann mehrere Tool-Calls + finale Antwort machen)
- Modell: anthropic/claude-opus-4.7 via OpenRouter
- /training/setup/anamnese: Chat-UI mit useChat hook, Stream-Display für Tool-
Calls, "Weiter zu Equipment-Setup" Button erscheint nach complete_anamnese
- /training/setup wird umgangen wenn keine Locations: jetzt zu /anamnese first
Mike-konforme Plan-Generator-Updates:
- Quads-Slot: Beinpresse FIRST → Hack-Squat → Beinstrecker → Kniebeuge LAST
(Mike: Kniebeuge ineffizient wegen aktiver Insuffizienz, Beinpresse + Iso
trainiert Quads vollständiger)
- Chest-Slot: Pec Deck (chest-fly-machine) FIRST → Cable Crossover → DB-Bench
→ Barbell Bench LAST (Mike: konstante Spannung über ROM wichtiger als
klassisches Bankdrücken)
- Tests: 86 passing (war 84, +2 für Mike-konform-Slot-Order, -2 weil 2 alte
Tests zur neuen Reihenfolge updated)
Podcast-Quelle:
- scripts/extract-podcast-text.mjs: extrahiert Text aus dem Iron-Mike-JSON
- docs/iron-mike-transcript.txt: 138k chars Transkript für Referenz
Verified: typecheck + lint + 86 tests + build alle grün.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wenn ein User schon Locations + Pläne hat, leitet /training nicht mehr zur Anamnese — Coach war damit verdeckt. Jetzt: - Card auf /training landing mit 'Coach starten' Button - Tipp-Banner auf /training/setup mit Link zur Anamnese vor dem Equipment-Setup Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… ergibt 10) Bug: Plan-Generator hat repRangeMin als targetReps verwendet → Iron Mike kam mit 5 Reps statt 6 raus. Mike sagt explizit Ziel ist 6 Reps RIR=0, Range 5-7 ist der akzeptable Korridor. Fix: rangeTarget(min, max, fallback) nimmt die Mitte (gerundet). - Iron Mike 5-7 → 6 - Classic 8-12 → 10 - RIR Mitte: 0-1 → 1, 1-3 → 2 Tests: 87 passing (war 86, +1 für classic-style verification). User muss bestehende Pläne neu generieren (auf /training → Locations + Equipment editieren → Pläne erstellen) damit die korrigierten Target-Reps in training_plan_exercises landen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem: KB-Swing landet im Home-Plan mit 2×6 Reps RIR=0 — das ist Hardstyle-Conditioning (15-25 Reps mit Power), nicht klassisches Working-Set bis Versagen. Bodyweight-Push-up trifft Versagen erst bei 15+ Reps. Festgewichts- KBs erlauben keine Brzycki-Progression mit 5-7 Reps. Schema-Update (Migration 0006): - exercises bekommt 4 neue Spalten: recommended_rep_min, recommended_rep_max, recommended_rir_min, recommended_rir_max - Werte gesetzt für KB / Bands / TRX / Bodyweight / Iso-Maschinen / Waden / Core - Compound-Lifts (Bench, Squat, Press, Row) bleiben null → folgen User-Default Plan-Generator-Logik (actions.ts generateAllPlans): - Beim Insert von training_plan_exercises: lade exercise-Recommendations - Wenn vorhanden, Mitte der Range → target_reps + target_rir - Sonst: Template-Default (basierend auf user training_preferences) Konkrete Effekte: - KB-Swing: 2×20 Reps RIR 2 (Conditioning, Mitte von 15-25) - KB-RDL / KB-Goblet-Squat / KB-Curl etc.: 2×10 RIR 1 (Mitte 8-12) - KB-Floor-Press / KB-OHP: 2×7 RIR 1 (Mitte 5-8 — KB-Pressen sind schwer) - Push-up / Inverted-Row: 2×12 RIR 1 (Mitte 8-15) - TRX-Übungen: 2×12 RIR 1 (Schwierigkeit über Körperwinkel) - Bands: 2×13 RIR 1 (Tension nicht standardisiert) - Bench Press / Squat / Bench: keine recommendation → User-Preference (6 RIR 0) Plus: HOME_DAY_B Hinge-Slot reordered. KB-RDL FIRST (lineare Progression möglich), KB-Suitcase mid, KB-Swing LAST (nur als Conditioning-Move wenn nichts anderes verfügbar). User muss bestehende Pläne neu generieren um die Werte in training_plan_exercises zu sehen — auf /training/setup → 'Pläne erstellen'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y B Rear Delts Drei Mike-Konformitäts-Korrekturen aus User-Feedback: 1. Übung 'chest-fly-machine' umbenannt: 'Brustpresse Maschine (Fly)' → 'Pec Deck (Butterfly)'. Pec Deck ist Iso-Adduktion, keine Press-Bewegung. Migration 0007. 2. DAY_A Slot 'Calves' → 'Hamstring iso (Beinbeuger sitzend/liegend)'. Begründung: Mike trifft jeden Muskel 2×/Woche. Mit dem alten Plan kam Hamstring nur in Day B (RDL) — jetzt auch in Day A. Beinbeuger sitzend ist Mike's Lieblings-Iso wegen Knie-Flexion mit gestreckter Hüfte. 3. DAY_B Slot 'Vertical Press (Front Delts)' entfernt → ersetzt durch 'Rear Delts (Cable Reverse Fly / Face Pull)'. Mike sagt explizit: Front-Press ist redundant — Frontdelt wird durch Brust-Pressen schon getroffen. Hinterer Delta ist dagegen Posture-relevant und sonst unter- repräsentiert. Day B hat jetzt zusätzlich Calves Slot (1×/Woche reicht). Tests: 90 passing (war 87, +3 für Mike-konform-Verifikation): - Day A enthält Hamstring iso - Day B hat KEIN Schulterdrücken - Day B hat Rear Delts + Calves User muss wieder neu generieren — auf /training/setup → 'Pläne erstellen'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e limit CI Runner teilen sich IP → der yt-dlp postinstall download hits 5k/h Limit. Binary wird nur im Recipe-Worker (Phase 4 runtime) gebraucht, nicht für die CI-Steps (typecheck/lint/test/build). Also: skip download im CI. Lokal funktioniert npm install weiter wie gewohnt — Binary wird heruntergeladen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Equipment-aware Iron Mike fullbody_x2 plan generator + Home-Gym alternative + session logging:
Multi-Location: User definiert N Locations (z.B. Gym + Home), pro Location ein Equipment-Set + ein passender Plan:
Session-Logging: Active session UI mit Brzycki-basierten Vorschlägen für Gewicht, Warmup vs Working Set Tracking, Progress-Indicator pro Übung.
Schema: Migrations 0001 (multi_location) + 0002 (22 home-gym exercises seeded).
Verification
npm run typecheck— greennpm run lint— greennpm run test— 82 passing (16 in plan-generator: gym + home + dispatch)npm run build— 8 routesTest plan
Next
Nach Merge: Phase 4 = Recipes-Modul. Externe Blocker: ElevenLabs API-Key + yt-dlp.
🤖 Generated with Claude Code