Code Review — Gesamte Codebase
Datum: 2026-06-23
Range: a93ccac (Init) → 807569a (HEAD)
Reviewer: Claude Sonnet 4.6 (Subagent)
Stärken
Architektur ist sauber und problemadäquat.
Die Zwei-Schichten-Aufteilung (Go für deterministische Logik, Claude für qualitative Einordnung) ist durchgehalten — keine hardcodierten Textbewertungen in Go, keine Datenlogik die Claude rekonstruieren muss.
Idempotenter JSONL-Write ist korrekt implementiert.
Das mergeWorkout + atomic tmp → rename-Muster ist der richtige Ansatz für einen lokalen Append-only-Store. Daten gehen bei Re-Run nicht verloren, Partial-Writes können die Datei nicht korrumpieren.
Parser ist produktionsreif.
CRLF-Handling (TrimRight(..., "\r")), Unicode × und ASCII x werden akzeptiert, Strong-App-Links werden korrekt verworfen, fehlende Working Sets werden mit Fehler abgefangen. Regex-Muster sind tight und gut gewählt.
Code-Qualität über den Review-Bereich deutlich verbessert.
Custom itoa/ftoa-Helper mit echten Bugs (fehlendes .0, negative Fraction-Truncation) wurden durch fmt.Sprintf("%.1f", ...) ersetzt. Fehlerbehandlung beim CSV-Float/Int-Parsing ergänzt, wo zuvor Fehler still verworfen wurden. Numeric-Sort-Fix für Workout-Nummern ist korrekt.
Test-Suite ist auf Unit-Ebene umfassend.
34 Tests in 6 Packages decken ab: Parser-Edge-Cases (Day B, warmup-lose Übungen, Link-Verwerfen), Plan-Matching, Rep-Range-Checks, Konsistenz-Streak, ASCII-Bar-Alignment und Report-Rendering inklusive Multi-Set-Empfehlungen. Tests prüfen echtes Verhalten, keine Mocks.
preserveEinordnung ist ein durchdachtes Design.
Report neu generieren ohne eine bereits befüllte Einordnung zu überschreiben ist der richtige Kompromiss für ein Werkzeug das bei Re-Analyse wiederverwendet wird. Die Placeholder-Erkennung ist robust.
mainLiftsFromPlan entkoppelt Lift-Auswahl vom Code.
Die alte Version hatte "Bench Press (Barbell)", "Squat (Barbell)" etc. hardcodiert in RenderWeeklySkeleton. Der aktuelle Ansatz liest main_lift: true aus plan.yaml — korrekt und zukunftssicher für Programmwechsel.
Probleme
Critical (Must Fix)
Keine gefunden.
Important (Should Fix)
1. NextDay-Preview zeigt Plan-Targets, nicht angepasste Gewichte — still irreführend wenn User über Plan-Target trainiert
Datei: internal/report/skeleton.go, renderNextDayPreview / BuildFindings, ca. Zeilen 89–115
Problem:
Der NextDay-Preview verwendet ex.Sets[0].TargetW aus plan.yaml bedingungslos. Wenn Filip Bench bei 50 kg macht (Plan-Target: 42.5 kg), zeigt der "Next Day: Day 3"-Preview 37.5 kg (5–8) für Day C Bench — das ist das Day C Plan-Target, aber das tatsächlich passende Gewicht sollte aus dem aktuellen Empfehlungs-Delta abgeleitet werden.
AGENTS.md dokumentiert diesen Quirk bereits für RecommendNextWeight in der laufenden Session. Der NextDay-Fall ist nicht erwähnt und könnte den User für Day C Bench oder Incline Curl (die auf Day A und Day C vorkommen) aktiv in die falsche Richtung schicken.
Warum es wichtig ist:
Die Day A Bench-Empfehlung (Set 1: 52.5 kg) und der Day C Preview (Set 1: 37.5 kg) stehen im selben Report. Das sieht widersprüchlich aus. Der User wird den Preview wahrscheinlich ignorieren oder verwirrt sein.
Fix:
Für Übungen die auch im nächsten Tag vorkommen das Empfehlungs-Delta aus der aktuellen Session in die NextDay-Preview-Gewichte übertragen. Oder einen Hinweis im Preview ergänzen, dass die gezeigten Gewichte Plan-Targets sind (nicht personalisiert), damit der User weiß, dass er die Übungen-Sektion konsultieren soll.
2. SortByDate ist exportiert aber Dead Code
Datei: internal/analyze/consistency.go, Zeile 88
Problem:
SortByDate ist exportiert (Großbuchstabe S) wird aber nirgendwo im Produktionscode aufgerufen und hat keinen Test. Der String-Vergleich auf YYYY-MM-DD wäre lexikografisch korrekt, aber es ist eine Maintenance-Fläche ohne Nutzen.
Fix:
Entweder auf sortByDate kleinschreiben oder löschen. Falls für runImportHistory oder topSetTrend gedacht: diese Pfade sortieren auf anderem Weg (über keys-Slice im CSV-Loader, über sort.Strings im Report).
3. NextWeightForSet2/3 != 0-Guard unterdrückt still Empfehlungen für Bodyweight-/Low-Target-Übungen
Dateien: cmd/fitness/main.go, Zeilen 199–203 und internal/report/skeleton.go, Zeilen 245–249
Problem:
Sowohl der JSON-Output als auch das Markdown-Skeleton verwenden if ex.NextWeightForSet2 != 0 um zu entscheiden, ob eine Set-2/3-Empfehlung angezeigt wird. Für Leg Press (target_w: 0.0) wo der User in-Range trainiert (Delta = 0) ist die Set-2/3-Empfehlung genau 0.0 + 0 = 0 und wird still ausgeblendet. Der User sieht nur Set 1 — was wie ein Bug wirkt.
Warum es wichtig ist:
Momentan Low-Severity, da Leg Press die einzige betroffene Übung im aktuellen Plan ist. Wird schlimmer sobald eine Übung mit target_w: 0.0 über mehrere Sets hinzugefügt wird.
Fix:
Sentinel oder explizites Flag verwenden. HasRecommendationSet2/3 bool-Felder hinzufügen (analog zu HasRecommendation) oder *float64-Pointer verwenden. Alternativ: Übungen mit target_w == 0 als Sonderfall behandeln und nur die Reps-Empfehlung ausgeben.
Minor (Nice to Have)
4. formatViolation-Parameternamen sind invertiert relativ zur Formatausgabe
Datei: internal/analyze/checks.go, Zeilen 55–59
Parameter sind (prev, curr) aber der Format-String stellt curr zuerst: "Set {curr.Index} > Set {prev.Index}". Semantisch ist der Output korrekt ("Set 2 wiegt mehr als Set 1") aber beim Lesen der Funktionssignatur erwartet man prev zuerst. Ein zukünftiger Editor wird sie wahrscheinlich vertauschen.
Fix: Parameter in (lower, higher) umbenennen oder Format-String auf "Set {curr.Index} erhöht Gewicht gegenüber Set {prev.Index}" anpassen.
5. Leg Press zeigt 0.0 kg (3–5) im NextDay-Preview
Datei: internal/report/skeleton.go, formatSetCells
formatSetCells gibt "—" nur zurück wenn alle drei Werte (Gewicht, repMin, repMax) null sind. Leg Press hat target_w: 0.0 aber rep_min: 3, rep_max: 5, also wird 0.0 kg (3–5) gerendert — was bedeutungslos ist.
Fix: Nur w == 0 prüfen (da Gewicht der bedeutsame Diskriminator ist), oder YAML-Feld weight_untracked: true hinzufügen und in der Formatierung behandeln.
6. start_date-Feld in Plan-Struct wird geladen, aber nie genutzt
Datei: internal/plan/plan.go, Zeile 46
Das Feld existiert in YAML und Struct, aber nichts liest es aus. Kein Code berechnet "Woche N von 12" oder validiert die Programm-Timeline. Für MVP ok. Die Lücke bedeutet, dass es keinen Guard gibt gegen das Analysieren eines Workouts vor Programmstart — historische Daten könnten Streak-Counts aufblähen.
Fix: Mit // unused in v1, reserved for week-number display dokumentieren, oder in WeekConsistency / StreakWeeks nutzen um auf das Programmstart-Datum zu filtern.
7. AGENTS.md hat veralteten Test-Count
AGENTS.md sagt 22 Tests müssen grün bleiben — aktuell sind es 34. 12 zusätzliche Tests wurden ergänzt (CSV, Skeleton, Multi-Set-Empfehlung). Den Count aktualisieren damit der nächste Agent nicht denkt, 34 Tests bedeutet etwas sei kaputt.
Empfehlungen
- NextDay-Preview-Gewichte (Issue 1) sind das user-sichtbarste Gap und sollten vor dem ersten Multi-Wochen-Run gefixt werden.
SortByDateDead Export (Issue 2) ist ein Ein-Zeilen-Fix und sollte sofort erledigt werden um die exportierte API-Fläche minimal zu halten.- Integration-Smoke-Test erwägen:
runAnalyzeauf einer Sample-Workout-Datei aufrufen und JSON-Output-Felder prüfen. Das würde Regressions im End-to-End-Pipeline fangen die Unit-Tests verpassen (besonders denpreserveEinordnung-Re-Run-Pfad). - Partial-Failure dokumentieren:
writeJSONL→ Write-Report-Sequenz hat kein Rollback. Wenn der Markdown-Write nach dem JSONL-Update fehlschlägt, sind Daten persistiert aber Report fehlt. Für ein lokales Tool akzeptabel, aber ein Kommentar zu diesem Szenario hilft zukünftigen Maintainern.
Assessment
Merge-ready: Ja, mit Fixes
Issues 2 (Dead Export) und 3 (Zero-Weight Empfehlung) sollten vor dem ersten Leg-Press-Workout erledigt sein. Issue 1 (NextDay-Preview-Targets) ist eine UX-Verbesserung die Funktionalität nicht blockt, da die Übungen-Sektion bereits korrekte Empfehlungen enthält.
Der Core-Pipeline — Parse, Plan-Match, JSONL-Persistenz, Report-Generierung, idempotenter Re-Run — ist korrekt und gut getestet. Keine Data-Loss-Risiken, keine Security-Probleme, keine kaputte Funktionalität.