Merge pull request 'Universal CSV Importer' (#70) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 56s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s

Reviewed-on: #70
This commit is contained in:
Lars 2026-04-11 07:06:47 +02:00
commit 12893cba78
187 changed files with 57999 additions and 1490 deletions

61
.claude/README.md Normal file
View File

@ -0,0 +1,61 @@
# Mitai Jinkendo Claude-Arbeitsumgebung
Dieser Ordner ist der **primäre Orientierungspunkt** für Claude Code / Cursor-Agenten. Dauerhafte Spezifikationen liegen unter **`.claude/docs/`** (`functional/`, `technical/` …). Das Repository **`docs/`** (Projektroot) ist u.a. für **`docs/issues/`** nicht verwechseln.
---
## Pflichtlektüre (vor größeren Änderungen)
| Reihenfolge | Datei |
|-------------|--------|
| 1 | `../CLAUDE.md` |
| 2 | **`rules/DOCUMENTATION.md`** Ablage- und Dokumentationsregeln |
| 3 | `rules/ARCHITECTURE.md`, `rules/CODING_RULES.md`, `rules/LESSONS_LEARNED.md` |
| 4 | Issue-Landkarte: **`.claude/docs/GITEA_ISSUES_INDEX.md`** |
Themen mit UI/Nav/PWA: siehe `../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` (im **Projekt**-`docs/`, nicht hier).
---
## Verzeichnisbaum (Kurz)
```
.claude/
├── README.md ← Diese Datei
├── rules/ ← Verbindliche Regeln (versioniert, wenn konfiguriert)
├── docs/ ← Spezifikationen + Arbeitspapiere
│ ├── functional/ ← Fachlich (WAS)
│ ├── technical/ ← Technisch (WIE)
│ ├── architecture/ ← Querschnitt
│ ├── working/ ← Zwischenstände, Analysen (nicht allein „Wahrheit“)
│ ├── audit/ ← Audits, Matrizen
│ ├── README.md ← Katalog aller Spec-Dateien
│ └── GITEA_ISSUES_INDEX.md
├── commands/ ← Slash-Befehle (optional versioniert)
├── task/ ← Lokale Arbeitspakete (meist nur lokal)
├── handover/ ← Session-Notizen (meist nur lokal)
├── skills/ ← Skills (lokal)
└── settings.json ← Editor-Konfiguration (lokal)
.settings.local.json ← Nicht committen
```
---
## Wo neue Inhalte ablegen
Siehe **`rules/DOCUMENTATION.md`** (Tabelle „Ablagepflicht“). Kurz:
- Langfristige **fachliche** Spec → `docs/functional/` (innerhalb von `.claude`, siehe Baum oben).
- Langfristige **technische** Spec → `docs/technical/`.
- **Session-Analyse / grobe Notizen**`docs/working/`.
- **Epic/Issue lang**`../docs/issues/` im Repository.
---
## Projekt-`docs/` (Repository)
`../docs/issues/` enthält issue-nummerierte Markdown-Dateien und Abnahme-Dokumente **parallel** zu diesem Ordner, für alle sichtbar die das Repo klonen.
---
**Stand:** 2026-04-08

View File

@ -0,0 +1,32 @@
# Add Endpoint
Füge einen neuen API-Endpoint zum Backend hinzu.
## Template:
```python
@app.get("/api/mein-endpoint")
def mein_endpoint(
limit: int = 100,
session: dict = Depends(require_auth) # ← IMMER als separater Parameter!
):
pid = session['profile_id']
with get_db() as conn:
rows = conn.execute(
"SELECT * FROM meine_tabelle WHERE profile_id=? LIMIT ?",
(pid, limit)
).fetchall()
return [r2d(r) for r in rows]
```
## Regeln:
- `session: dict = Depends(require_auth)` IMMER als letzter/separater Parameter
- NIEMALS innerhalb von `Header(default=None, ...)` einbetten
- Profile-ID immer aus `session['profile_id']` nie aus Header
- Admin-Endpoints: `session=Depends(require_admin)`
- Rate-Limiting für sensitive Endpoints: `@limiter.limit("5/minute")`
## Nach dem Endpoint:
api.js um neue Methode ergänzen:
```javascript
meinEndpoint: (params) => req(`/mein-endpoint?limit=${params}`),
```

View File

@ -0,0 +1,174 @@
# Check Issues
Prüfe offene Gitea Issues und schlage Fixes vor.
## Automatische Ausführung
Dieser Skill wird bei jeder Session automatisch ausgeführt und zeigt:
- Anzahl offener Issues
- High-Priority Issues (Bugs + Features)
- Vorschlag welches Issue als nächstes angegangen werden sollte
## Workflow
### 1. Offene Issues abrufen
```bash
curl -s -H "Authorization: token $GITEA_TOKEN" \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues?state=open&labels=develop" \
| python3 -c "
import sys, json
issues = json.load(sys.stdin)
print(f'📋 {len(issues)} offene Issues in develop:\n')
for issue in issues:
labels = {l['name'] for l in issue['labels']}
priority = 'high' if 'high' in labels else 'low' if 'low' in labels else 'medium'
itype = 'bug' if 'bug' in labels else 'feature' if 'feature' in labels else 'other'
icon = '🐛' if itype == 'bug' else '✨'
prio_icon = '🔥' if priority == 'high' else '💤' if priority == 'low' else '📌'
print(f\"{icon} {prio_icon} Issue #{issue['number']}: {issue['title']}\")
print(f\" {issue['html_url']}\n\")
"
```
### 2. Issues nach Priorität sortieren
**High Priority Issues (Bugs + Features):**
- Bugs mit Label `bug` + `high`
- Features mit Label `feature` + `high`
**Low Priority:**
- Features mit Label `low`
### 3. Issue-Details anzeigen
```bash
# Für ein spezifisches Issue (z.B. #13):
curl -s -H "Authorization: token $GITEA_TOKEN" \
http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/13 \
| jq -r '.body'
```
### 4. Fix vorschlagen
**Frage den User:**
- "Soll ich Issue #X angehen?"
- Zeige Beschreibung + betroffene Dateien
- Schätze Aufwand (Quick Win < 30min, Medium < 2h, Large > 2h)
### 5. Bei Fix: Issue referenzieren im Commit
**WICHTIG:** Verwende dieses Format für automatisches Issue-Schließen:
```bash
git commit -m "fix: resolve training type creation error (#13)
- Fixed POST endpoint validation
- Improved frontend error handling
Closes #13"
```
**Gitea Keywords die Issues automatisch schließen:**
- `Closes #123`
- `Fixes #123`
- `Resolves #123`
Das Issue wird automatisch geschlossen wenn der Commit in den main Branch gemerged wird!
### 6. Issue manuell schließen (falls nötig)
```bash
curl -X PATCH -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"state":"closed"}' \
http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/13
```
## Issue-Wartung (Standard-Task)
**WICHTIG:** Bei jeder Session Issue-Liste pflegen:
### 1. Veraltete Issues schließen
Wenn Features implementiert wurden, zugehörige Issues schließen:
```bash
# Issue schließen
curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"state":"closed"}' \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/17" > /dev/null
# Kommentar mit Commit-Referenz hinzufügen
curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"✅ Erledigt in v9d Phase 2d. Commits: 548a5a4, bf87e03"}' \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/17/comments" > /dev/null
```
### 2. Duplikate entfernen
```bash
# Duplikat schließen
curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"state":"closed"}' \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/19" > /dev/null
# Duplikat-Kommentar
curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"Duplikat von #21"}' \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/19/comments" > /dev/null
```
### 3. Issue-Prioritäten aktualisieren
```bash
# Label ändern (z.B. von medium zu high)
curl -s -X PUT -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"labels":[2,3,5]}' \
"http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues/15/labels"
```
**Checkliste nach jeder Implementation:**
- [ ] Zugehörige Issues mit Commit-Hash kommentieren
- [ ] Issues auf `closed` setzen
- [ ] Duplikate entfernen
- [ ] Neue Issues bei Bedarf erstellen
## Automatisierung (Hook)
**Optional:** Hook in `.claude/settings.json` für Session-Start:
```json
{
"hooks": {
"session-start": "check-issues"
}
}
```
→ Bei jeder neuen Session werden automatisch offene Issues geprüft.
## Issue erstellen (neue Bugs/Features)
```bash
curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "[BUG-XXX] Titel",
"body": "Beschreibung",
"labels": [1, 3, 5]
}' \
http://192.168.2.144:3000/api/v1/repos/Lars/mitai-jinkendo/issues
```
**Label-IDs:**
- 1 = bug
- 2 = feature
- 3 = high
- 4 = low
- 5 = develop

View File

@ -0,0 +1,23 @@
# DB Add Column
Füge eine neue Spalte zur SQLite-Datenbank hinzu.
## Vorgehen in backend/main.py:
1. Spalte in CREATE TABLE Statement hinzufügen
2. Spalte in `_safe_alters` Liste hinzufügen:
```python
_safe_alters = [
("profiles", "neue_spalte TEXT DEFAULT NULL"),
# ... weitere
]
```
Die `_safe_alters` Funktion fügt Spalten sicher hinzu ohne
bestehende Daten zu verlieren (ALTER TABLE IF NOT EXISTS).
## Wichtig:
- Immer DEFAULT Wert angeben
- Nie bestehende Spalten umbenennen oder löschen
- Nach Änderung Backend neu starten: `docker compose restart mitai-api`

View File

@ -0,0 +1,85 @@
# Deploy Commit, Test und Push
Führt Tests durch, bumpt die Version, committet und pusht auf develop.
Gitea startet dann automatisch den Dev-Deploy.
## Schritt 1: Syntax prüfen
```bash
python3 -m py_compile backend/main.py backend/routers/*.py 2>&1 && echo "Syntax OK"
```
## Schritt 2: Versions-Bump prüfen
Prüfe ob version.py und version.js aktualisiert wurden:
```bash
git diff --name-only | grep -E "version\.(py|js)"
```
Wenn NICHT aktualisiert → Stopp und fragen:
"Welche Version soll ich setzen? (aktuell: X.Y.Z)"
Dann version.py und version.js aktualisieren.
## Schritt 3: Smoke Tests auf Dev ausführen
```bash
npx playwright test 2>&1
```
Wenn Tests fehlschlagen:
- Screenshot analysieren: `start test-results\...\test-failed-1.png`
- Fehler korrigieren
- Tests erneut ausführen
- Erst wenn alle grün → weiter
## Schritt 4: Git Status prüfen
```bash
git status
git diff --stat
```
## Schritt 5: Commit
```bash
git add -A
git commit -m "COMMIT_MESSAGE
version: X.Y.Z
module: MODUL_NAME X.Y.Z"
```
## Schritt 6: Push auf develop
```bash
git push origin develop
```
## Schritt 7: Deploy-Status prüfen
```bash
# 30 Sekunden warten
# Dann Dev-System prüfen
ssh pi "docker logs dev-mitai-api --tail 10"
curl -sf https://dev.mitai.jinkendo.de/api/version | python3 -c "import sys,json; d=json.load(sys.stdin); print('Version:', d.get('app_version'))"
```
## Schritt 8: Gitea PR Link ausgeben
```bash
echo "PR erstellen: http://192.168.2.144:3000/Lars/mitai-jinkendo/compare/main...develop"
```
---
## Checkliste vor jedem Commit
```
[ ] backend/version.py aktualisiert (APP_VERSION + MODULE_VERSION)
[ ] frontend/src/version.js aktualisiert (APP_VERSION + PAGE_VERSION)
[ ] Changelog-Eintrag in version.py hinzugefügt
[ ] DB_SCHEMA_VERSION aktualisiert (wenn Schema geändert)
[ ] Alle Playwright Tests grün
[ ] Auth auf alle neuen Endpoints
[ ] api.js für alle neuen Frontend API-Calls
```
---
## Prod bleibt unberührt
Prod-Änderungen NUR über:
Gitea PR → Review → Merge → deploy-prod.yml
NIEMALS direkt auf Prod deployen.

View File

@ -0,0 +1,191 @@
# Dokumentation erstellen
Erstelle oder aktualisiere die technisch-fachliche Dokumentation der App.
Lies zuerst den aktuellen Code, dann generiere die Dokumentation.
## Wichtig
- Dokumentation basiert auf dem ECHTEN Code nicht auf Annahmen
- Jede Datei einzeln erstellen, nicht alles auf einmal
- Bestehende Dateien aktualisieren wenn sie existieren
## Schritt 1: Architektur-Übersicht
Lies folgende Dateien:
- `backend/main.py`
- `backend/db.py`
- `backend/auth.py`
- `backend/schema.sql`
- `frontend/src/App.jsx`
- `frontend/src/app.css`
- `frontend/src/utils/api.js`
- `docker-compose.yml`
Erstelle `.claude/docs/technical/ARCHITECTURE.md` mit:
```markdown
# Architektur-Übersicht Mitai Jinkendo
## System-Überblick
[Kurze Beschreibung der Gesamtarchitektur]
## Tech-Stack
[Tabelle mit allen Technologien + Versionen]
## Deployment-Architektur
[Infrastruktur: Raspberry Pi, Docker, Synology, Domains]
## Komponenten-Übersicht
[Alle Module/Router mit Kurzbeschreibung]
## Datenfluss
[Wie fließen Daten: Frontend → API → DB]
## Sicherheitsarchitektur
[Auth, CORS, Rate Limiting, bcrypt]
## Versions-Historie
[v9a, v9b, v9c was wurde wann eingeführt]
```
## Schritt 2: Frontend-Seiten + Komponenten
Lies alle Dateien in:
- `frontend/src/pages/`
- `frontend/src/context/`
- `frontend/src/utils/`
Erstelle `.claude/docs/technical/FRONTEND.md` mit:
```markdown
# Frontend-Dokumentation
## Seiten-Übersicht
[Tabelle: Seite | Route | Beschreibung | Auth erforderlich]
## Komponenten
[Wiederverwendbare Komponenten mit Props]
## Context / State Management
[AuthContext, ProfileContext was wird wo verwaltet]
## API-Integration (api.js)
[Alle API-Methoden mit Parametern]
## CSS-System
[CSS-Variablen, globale Klassen, Design-Tokens]
## PWA-Konfiguration
[Service Worker, Manifest, Icons]
```
## Schritt 3: Auth-Flow + Sicherheit
Lies:
- `backend/auth.py`
- `backend/routers/auth.py`
- `frontend/src/context/AuthContext.jsx`
Erstelle `.claude/docs/technical/AUTH.md` mit:
```markdown
# Auth-Flow & Sicherheit
## Login-Flow (Schritt für Schritt)
[Sequenz: Frontend → Backend → DB → Token]
## Session-Management
[Token-Format, Lebensdauer, Speicherort]
## Passwort-Sicherheit
[bcrypt, SHA256-Migration, Salting]
## Rate Limiting
[Welche Endpoints, Limits, slowapi-Konfiguration]
## CORS-Konfiguration
[Allowed Origins, Umgebungsvariablen]
## Öffentliche vs. geschützte Endpoints
[Tabelle: Endpoint | Public/Protected | Admin-only]
## Bekannte Sicherheitsentscheidungen
[Warum welche Entscheidung getroffen wurde]
```
## Schritt 4: API-Referenz
Lies alle Dateien in `backend/routers/`.
Erstelle `.claude/docs/technical/API_REFERENCE.md` mit:
```markdown
# API-Referenz
## Basis-URL
- Prod: https://mitai.jinkendo.de/api
- Dev: https://dev.mitai.jinkendo.de/api
## Authentifizierung
[Header-Format, Token-Handling]
## Endpoints nach Modul
### Auth (/api/auth/...)
| Method | Path | Auth | Parameter | Response |
...
### Profile (/api/profiles/...)
...
### Gewicht (/api/weight/...)
...
[alle 14 Router]
## Fehler-Codes
[Standard-Fehlercodes und Bedeutung]
## Rate Limits
[Tabelle: Endpoint | Limit | Zeitfenster]
```
## Schritt 5: Datenbankschema
Lies `backend/schema.sql`.
Erstelle `.claude/docs/technical/DATABASE.md` mit:
```markdown
# Datenbankschema
## Übersicht
[Alle Tabellen mit Kurzbeschreibung]
## Tabellen-Details
[Je Tabelle: Spalten, Typen, Constraints, Indizes]
## Beziehungen
[Foreign Keys, Beziehungsdiagramm als ASCII]
## Migrations-Historie
[Was wurde wann geändert]
## Design-Entscheidungen
[Warum PostgreSQL, warum welche Struktur]
## Wichtige Abfragen
[Häufig verwendete Queries als Referenz]
```
## Nach jeder Datei
Sage mir:
- Welche Datei wurde erstellt/aktualisiert
- Wie viele Zeilen
- Was war überraschend oder unklar im Code
## Starten
Beginne mit Schritt 1 (ARCHITECTURE.md).
Frage nach jedem Schritt ob du weitermachen sollst.
EOF

100
.claude/commands/fix-bug.md Normal file
View File

@ -0,0 +1,100 @@
# Fix Bug
Strukturierter Workflow für Fehlerbehebung mit Bug-Tracking.
## Workflow
### 1. Bug in KNOWN_ISSUES.md finden
```bash
cat .claude/docs/KNOWN_ISSUES.md | grep -A 20 "BUG-XXX"
```
### 2. Status auf "In Bearbeitung"
```markdown
**Status:** 🟡 In Bearbeitung
```
### 3. Logs analysieren
**Docker Logs:**
```bash
docker logs -f dev-mitai-api | grep "ERROR\|Exception"
docker logs dev-mitai-api --tail 100
```
**Feature-Logs:**
```bash
tail -f backend/logs/feature-usage.log | jq .
```
### 4. Bug reproduzieren
- Schritte aus KNOWN_ISSUES.md nachstellen
- Fehler verifizieren
- Root Cause identifizieren (nicht nur Symptom!)
### 5. Fix implementieren
- Minimal invasive Änderung
- Keine "Verbesserungen" nebenbei
- Kommentiere komplexe Fixes
### 6. Testen
```bash
# Backend Syntax
python3 -m py_compile backend/[file].py
# Frontend Build
cd frontend && npm run build
```
### 7. Commit
```bash
git add [files]
git commit -m "fix: [BUG-XXX] Kurzbeschreibung
Root Cause: [Ursache]
Lösung: [Was geändert]
Tested: [Wie getestet]
Closes: BUG-XXX"
```
### 8. KNOWN_ISSUES.md aktualisieren
Nach Deploy:
- Status: ✅ Behoben (Commit `hash`)
- Ins Archiv verschieben
## Debugging-Checkliste
### Frontend-Bugs
- Browser Console (F12) → Fehler lesen
- Network Tab → API-Call prüfen
- Häufig:
- 401: Token fehlt → api.js nutzen
- undefined: Optional chaining `?.` verwenden
- Weißer Screen: JS-Fehler in Console
### Backend-Bugs
- Docker Logs → Traceback analysieren
- Syntax: `python3 -m py_compile backend/[file].py`
- Häufig:
- TypeError: Type-Konvertierung fehlt
- 404: Router nicht registriert
- 500: Unbehandelte Exception
### Häufige Fehler
- `session=Depends(require_auth)` in `Header()` → separater Parameter!
- `dayjs.week()` → Native ISO-Wochenberechnung
- `datetime.date` vs. `str` → Type-Check
- PostgreSQL Boolean: `true` nicht `1`
## Commit-Format
```
fix: [BUG-XXX] Kurze Beschreibung
Problem: TypeError in /api/nutrition/weekly
Root Cause: d['date'] ist datetime.date, nicht str
Lösung: Check if already datetime.date object
Tested: /nutrition Tab lädt ohne Error
Closes: BUG-XXX
```

View File

@ -0,0 +1,280 @@
---
description: Strukturierter Workflow für konzept-basierte Feature-Implementierung mit Requirement Analysis + User Approval
args: <feature-name> <konzept-file>
model: sonnet
---
# Feature-Implementierung Workflow
Du wurdest aufgerufen mit:
- **Feature:** {{feature-name}}
- **Konzept:** {{konzept-file}}
Dieser Skill führt den strukturierten 5-Stufen-Prozess aus `.claude/rules/IMPLEMENTATION_RULES.md` aus.
---
## STUFE 1: Anforderungsanalyse
**Aufgabe:**
1. Lies das Konzept-Dokument `{{konzept-file}}` VOLLSTÄNDIG
2. Finde alle Anforderungen für `{{feature-name}}`
3. Erstelle eine detaillierte Checkliste
**Gehe dabei wie folgt vor:**
### 1.1 Konzept lesen
- Öffne `{{konzept-file}}` mit dem Read tool
- Suche nach Abschnitten die `{{feature-name}}` betreffen
- Lies ALLE relevanten Abschnitte vollständig (nicht nur überfliegen)
### 1.2 Anforderungs-Matrix erstellen
Erstelle eine Tabelle mit folgenden Spalten:
| Anforderung | Datenquelle | Status | Notizen |
|-------------|-------------|--------|---------|
| Kalorienaufnahme täglich | nutrition_log.kcal | ✓ Vorhanden | - |
| Trainingskalorien | activity_log.kcal | ✗ Fehlt | Muss aggregiert werden |
| ... | ... | ... | ... |
**Status-Codes:**
- ✓ Vorhanden: Bereits implementiert (data_layer Funktion existiert)
- ⚠ Partial: Teilweise vorhanden, muss erweitert werden
- ✗ Fehlt: Muss neu gebaut werden
### 1.3 Gap-Analyse
Listen auf:
- Welche data_layer Funktionen existieren bereits?
- Welche data_layer Funktionen müssen neu geschrieben werden?
- Welche Tabellen werden benötigt?
- Welche Berechnungen sind nötig? (Formeln dokumentieren)
### 1.4 Offene Fragen
Dokumentiere alle Unklarheiten:
- Interpretation unklar?
- Mehrere Umsetzungsmöglichkeiten?
- Fehlende Informationen im Konzept?
**OUTPUT:** Zeige dem User die Anforderungs-Matrix und Gap-Analyse.
---
## STUFE 2: Umsetzungskonzept erstellen
**Aufgabe:**
Erstelle ein detailliertes technisches Umsetzungskonzept mit:
### 2.1 Backend-Design
**Neue Endpoints:**
```
GET /api/charts/{{feature-name}}?param1=value1
Args:
param1: Beschreibung (default=X, range Y-Z)
session: Auth session (injected)
Returns:
Chart.js-compatible JSON (siehe unten)
```
**Neue data_layer Funktionen:**
```python
def get_xyz_data(profile_id: str, days: int) -> Dict:
"""
Beschreibung was die Funktion tut.
Berechnung:
1. Schritt 1 (SQL Query oder Formel)
2. Schritt 2
Returns:
{"key": value, ...}
"""
```
**Datenquellen:**
- Welche Tabellen? (nutrition_log, activity_log, etc.)
- Welche Spalten?
- Welche JOINs?
**Berechnungen:**
- Formeln dokumentieren (z.B. "7d avg = sum(values[-7:]) / 7")
- Aggregationen beschreiben
- Edge Cases behandeln (keine Daten, Division durch 0, etc.)
### 2.2 Response-Format
Zeige ein **vollständiges Beispiel** der JSON-Response:
```json
{
"chart_type": "line" | "bar" | "mixed" | "scatter",
"data": {
"labels": ["2026-01-01", "2026-01-02", ...],
"datasets": [
{
"type": "line", // nur bei chart_type: mixed
"label": "Label für Legende",
"data": [100, 105, 103, ...],
"borderColor": "#1D9E75",
"backgroundColor": "rgba(29, 158, 117, 0.1)",
"borderWidth": 2,
"tension": 0.3,
"fill": false,
"yAxisID": "y1" // bei Dual-Axis
}
]
},
"metadata": {
"confidence": "high" | "medium" | "low" | "insufficient",
"data_points": 28,
"avg_value": 123.4,
"custom_field": "..."
}
}
```
### 2.3 Frontend-Design
**Component:**
- Welche Datei? (z.B. `NutritionCharts.jsx`)
- Welche Funktion? (z.B. `renderEnergyBalance()`)
**Chart-Library:**
- Recharts oder Chart.js?
- Welcher Chart-Typ? (LineChart, BarChart, ComposedChart, etc.)
- Welche Features? (Tooltip, Legend, Dual-Axis, etc.)
**API-Integration:**
- API-Funktion in `api.js` hinzufügen
- State-Management (useState, useEffect)
- Loading + Error Handling
### 2.4 Dependencies
- Müssen andere Module angepasst werden?
- Neue npm-Pakete nötig?
- DB-Migrationen nötig?
**OUTPUT:** Zeige dem User das vollständige Umsetzungskonzept.
---
## STUFE 3: User-Approval einholen
**Aufgabe:**
1. Zeige Anforderungs-Matrix (Stufe 1)
2. Zeige Umsetzungskonzept (Stufe 2)
3. Liste offene Fragen auf
4. Frage explizit: **"Soll ich mit der Implementierung beginnen? (ja/nein/Änderungen)"**
**WICHTIG:**
- **Warte auf die Antwort des Users**
- Beginne NICHT mit der Implementierung ohne explizites "ja"
- Bei Änderungswünschen: Passe das Konzept an und zeige es erneut
---
## STUFE 4: Implementierung
**NUR ausführen wenn User "ja" gesagt hat!**
### 4.1 Backend implementieren
1. **Neue data_layer Funktionen:**
- Erstelle/erweitere Dateien in `backend/data_layer/`
- Implementiere genau nach Konzept (Stufe 2.1)
- Docstrings mit Beschreibung + Args + Returns
2. **Neue Endpoints:**
- Erstelle/erweitere Router in `backend/routers/`
- Implementiere genau nach Konzept (Stufe 2.1)
- Response-Format exakt wie spezifiziert (Stufe 2.2)
3. **Tests (falls vorhanden):**
- Unit-Tests für data_layer Funktionen
- Integration-Tests für Endpoints
### 4.2 Frontend implementieren
1. **API-Funktion hinzufügen:**
- Ergänze `frontend/src/utils/api.js`
- Syntax: `getXyzChart: (days=28) => req(\`/charts/xyz?days=${days}\`)`
2. **Component erstellen/erweitern:**
- Implementiere Chart-Darstellung
- Verwende Chart-Library wie spezifiziert
- Loading + Error Handling
3. **Integration:**
- Füge Component in Parent-Page ein
- Teste UI-Flow
### 4.3 Commit
```
feat: {{feature-name}} (konzept-konform)
Backend:
- data_layer: [Liste neue Funktionen]
- Endpoint: [Liste neue Endpoints]
Frontend:
- Component: [Beschreibung]
- Chart-Type: [Typ]
Konzept: {{konzept-file}}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
```
---
## STUFE 5: Compliance-Check
**Aufgabe:**
Prüfe JEDE Anforderung aus der Anforderungs-Matrix (Stufe 1):
```
□ Anforderung 1: [Beschreibung] - Status: ✓ / ✗
□ Anforderung 2: [Beschreibung] - Status: ✓ / ✗
...
```
**Alle Checkboxen müssen ✓ sein vor Push!**
Falls ✗:
- Identifiziere was fehlt
- Implementiere nach
- Erneut prüfen
**OUTPUT:** Zeige dem User die Final-Checkliste.
---
## WICHTIG: Workflow-Reihenfolge
```
Stufe 1 (Anforderungen) → Stufe 2 (Konzept) → Stufe 3 (Approval)
User sagt "ja"?
Stufe 4 (Implementierung) → Stufe 5 (Compliance-Check) → Push
```
**Niemals:**
- Stufen überspringen
- Ohne Approval implementieren
- "Ähnliches" statt Gefordertes bauen
---
## Los geht's!
Beginne jetzt mit **STUFE 1: Anforderungsanalyse**.
Lies `{{konzept-file}}` und erstelle die Anforderungs-Matrix für `{{feature-name}}`.

View File

@ -0,0 +1,60 @@
# Merge to Production
Erstelle einen Pull Request in Gitea: develop -> main
## Checkliste vor dem PR:
- [ ] dev.mitai.jinkendo.de funktioniert
- [ ] Login funktioniert
- [ ] Alle neuen Features getestet
- [ ] Keine Console-Fehler im Browser
- [ ] KI-Analyse funktioniert
- [ ] Backend-Syntax OK: `python3 -m py_compile backend/main.py`
## Schritt 1: Sicherstellen dass develop aktuell ist
```bash
git checkout develop
git status
git push origin develop
```
## Schritt 2: Pull Request in Gitea erstellen
Öffne im Browser:
http://192.168.2.144:3000/Lars/mitai-jinkendo/compare/main...develop
Oder:
```
Gitea ? Repository ? Pull Requests ? "Neuer Pull Request"
Basis: main
Vergleich: develop
Titel: beschreibung der Änderungen
? Pull Request erstellen
```
## Schritt 3: PR mergen
```
Gitea ? Pull Request ? "Merge Pull Request"
? "Merge Commit" wählen (NICHT "Squash" oder "Rebase")
? develop Branch NICHT löschen (Häkchen deaktivieren!)
```
## Schritt 4: Nach dem Merge
- Gitea Action `deploy-prod.yml` startet automatisch
- Prüfe: https://mitai.jinkendo.de
- Prüfe Gitea Actions ob alles grün ist
- Lokal auf develop zurückwechseln und aktualisieren:
```bash
git checkout develop
git pull origin develop
```
## Rollback falls nötig:
```
Gitea ? Commits auf main ? "Revert" auf dem letzten Merge-Commit
? neuer PR wird automatisch erstellt
? PR mergen ? Prod wird zurückgesetzt
```
## Wichtig:
- NIEMALS direkt auf main pushen
- develop Branch niemals löschen
- Immer über PR mergen nie `git merge` lokal auf main

View File

@ -0,0 +1,19 @@
# New Feature
Erstelle ein neues Feature für Mitai Jinkendo.
## Checkliste:
1. Backend: Neuer Endpoint in `backend/main.py`
- Mit `session: dict = Depends(require_auth)` absichern
- Neue DB-Spalten via `_safe_alters` hinzufügen
2. Frontend: Neue Seite oder Komponente
- API-Calls immer über `frontend/src/utils/api.js`
- Token wird automatisch injiziert
3. Syntax prüfen: `python3 -m py_compile backend/main.py`
4. CLAUDE.md aktualisieren wenn nötig
## Wichtige Regeln:
- Passwörter: bcrypt (nicht SHA256)
- API-Calls: api.js nutzen (nie direktes fetch ohne Token)
- dayjs.week(): nicht verwenden → native ISO-Wochenberechnung
- session=Depends(require_auth): immer als separater Parameter

47
.claude/commands/pi-db.md Normal file
View File

@ -0,0 +1,47 @@
# Pi Datenbank
Direkter Zugriff auf PostgreSQL Datenbanken auf dem Raspberry Pi.
## Verwendung
Sage was du prüfen möchtest, z.B.:
- "Zeige alle Profile"
- "Wie viele Gewichtseinträge hat Lars?"
- "Zeige die letzten 10 Schlafeinträge"
- "Prüfe ob die Tabelle sleep_log existiert"
## Prod-Datenbank Abfrage
```bash
ssh pi "docker exec mitai-db-prod psql -U mitai_prod -d mitai_prod -c 'DEINE_SQL_ABFRAGE'"
```
## Dev-Datenbank Abfrage
```bash
ssh pi "docker exec dev-mitai-postgres psql -U mitai_dev -d mitai_dev -c 'DEINE_SQL_ABFRAGE'"
```
## Nützliche Standard-Abfragen
### Alle Tabellen anzeigen
```bash
ssh pi "docker exec mitai-db-prod psql -U mitai_prod -d mitai_prod -c '\dt'"
```
### Tabellen-Größen
```bash
ssh pi "docker exec mitai-db-prod psql -U mitai_prod -d mitai_prod -c \"SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size FROM pg_tables WHERE schemaname='public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;\""
```
### Profile anzeigen
```bash
ssh pi "docker exec mitai-db-prod psql -U mitai_prod -d mitai_prod -c 'SELECT id, name, email, role, tier FROM profiles;'"
```
### DB-Version prüfen
```bash
ssh pi "docker exec mitai-db-prod psql -U mitai_prod -d mitai_prod -c 'SELECT version();'"
```
## Wichtig
- Nur SELECT-Abfragen ohne explizite Genehmigung
- Keine DELETE/DROP/TRUNCATE ohne ausdrückliche Bestätigung
- Prod-DB mit besonderer Vorsicht behandeln

View File

@ -0,0 +1,60 @@
# Pi Dev Entwicklungssystem Zugriff
Direkter Zugriff auf das Dev-System (dev.mitai.jinkendo.de).
**NUR für Dev niemals auf Prod anwenden!**
## Container Status
```bash
ssh pi "docker ps --filter name=dev-mitai --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"
```
## Backend neu starten (Dev)
```bash
ssh pi "docker restart dev-mitai-api && sleep 5 && docker logs dev-mitai-api --tail 20"
```
## Frontend neu starten (Dev)
```bash
ssh pi "docker restart dev-mitai-ui"
```
## Beide neu starten (Dev)
```bash
ssh pi "cd /home/lars/docker/bodytrack-dev && docker compose -f docker-compose.dev-env.yml restart"
```
## Komplett neu bauen (Dev)
```bash
ssh pi "cd /home/lars/docker/bodytrack-dev && git pull origin develop && docker compose -f docker-compose.dev-env.yml build --no-cache && docker compose -f docker-compose.dev-env.yml up -d"
```
## Backend Logs live (10 Sekunden)
```bash
ssh pi "timeout 10 docker logs dev-mitai-api --follow 2>&1 || true"
```
## Dev-DB Abfrage (nur SELECT ohne Genehmigung)
```bash
ssh pi "docker exec dev-mitai-postgres psql -U mitai_dev -d mitai_dev -c 'DEINE_ABFRAGE'"
```
## Health Check Dev
```bash
ssh pi "curl -sf http://localhost:8099/api/auth/status && echo '✓ DEV API OK' || echo '✗ DEV API FEHLER'"
ssh pi "curl -sf http://localhost:3099 | grep -c 'Mitai' && echo '✓ DEV UI OK' || echo '✗ DEV UI FEHLER'"
```
## Umgebungsvariablen prüfen (Dev)
```bash
ssh pi "docker exec dev-mitai-api env | grep -v PASSWORD | grep -v SECRET | grep -v KEY"
```
## ⛔ PROD-SCHUTZ
Folgende Befehle sind für Prod VERBOTEN:
- docker restart mitai-api
- docker restart mitai-ui
- docker exec mitai-api ...schreibend...
- Direkte Dateiänderungen in /home/lars/docker/bodytrack/
- Änderungen an Prod-DB außer SELECT
Prod-Änderungen NUR über: git push → Gitea PR → deploy-prod.yml

View File

@ -0,0 +1,43 @@
# Pi Logs
Zeigt Logs der Mitai-Container auf dem Raspberry Pi.
## Backend Logs (Prod)
```bash
ssh pi "docker logs mitai-api --tail 50 --timestamps"
```
## Backend Logs (Dev)
```bash
ssh pi "docker logs dev-mitai-api --tail 50 --timestamps"
```
## Frontend Logs (Prod)
```bash
ssh pi "docker logs mitai-ui --tail 20"
```
## Logs nach Fehler filtern
```bash
ssh pi "docker logs mitai-api --tail 100 2>&1 | grep -i 'error\|exception\|traceback'"
```
## Logs live verfolgen (10 Sekunden)
```bash
ssh pi "timeout 10 docker logs mitai-api --follow 2>&1 || true"
```
## Gitea Runner Logs
```bash
ssh pi "sudo journalctl -u gitea-runner --lines 30 --no-pager"
```
## Letzter Deploy-Status
```bash
ssh pi "cat /home/lars/docker/bodytrack/.last-deploy 2>/dev/null || echo 'Keine Deploy-Info'"
```
## System-Logs (Fehler letzte Stunde)
```bash
ssh pi "sudo journalctl --since '1 hour ago' --priority err --no-pager | tail -20"
```

View File

@ -0,0 +1,37 @@
# Pi Status
Zeigt den aktuellen Status aller Mitai-Container und Logs auf dem Raspberry Pi.
## Container Status
```bash
ssh pi "docker ps --filter name=mitai --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"
```
## Letzte Backend-Logs (Prod)
```bash
ssh pi "docker logs mitai-api --tail 30"
```
## Letzte Backend-Logs (Dev)
```bash
ssh pi "docker logs dev-mitai-api --tail 30"
```
## API Health Check
```bash
ssh pi "curl -sf http://localhost:8002/api/auth/status && echo '✓ PROD OK' || echo '✗ PROD FEHLER'"
ssh pi "curl -sf http://localhost:8099/api/auth/status && echo '✓ DEV OK' || echo '✗ DEV FEHLER'"
```
## Datenbank Status
```bash
ssh pi "docker exec mitai-db-prod pg_isready -U mitai_prod && echo '✓ DB PROD OK'"
ssh pi "docker exec dev-mitai-postgres pg_isready -U mitai_dev && echo '✓ DB DEV OK'"
```
## Disk Space
```bash
ssh pi "df -h /home/lars/docker"
```
## Führe alle Checks aus und zeige Zusammenfassung

View File

@ -0,0 +1,81 @@
# Refactor
Refactore den Code ohne die Funktionalität zu ändern.
## Wichtige Regel:
**Erst planen, dann umsetzen niemals beides gleichzeitig.**
Zeige den Plan und warte auf Bestätigung bevor du Code schreibst.
## Backend Refactoring (main.py aufteilen):
### Zielstruktur:
```
backend/
├── main.py # Nur App-Setup, Middleware, Router-Import (~100 Zeilen)
├── database.py # DB-Verbindung, init_db, safe_alters, r2d()
├── auth.py # require_auth, require_admin, hash_pin, verify_pin, sessions
├── models.py # Alle Pydantic Models
├── email.py # SMTP, send_email, email_html_wrapper
├── utils.py # make_token(), helper functions
└── routers/
├── __init__.py
├── weight.py
├── caliper.py
├── circumference.py
├── nutrition.py
├── activity.py
├── photos.py
├── insights.py
├── prompts.py
├── admin.py
└── export.py
```
### Vorgehen:
1. Analysiere main.py vollständig
2. Erstelle Plan welche Zeilen wohin kommen
3. Warte auf Bestätigung
4. Erstelle neue Dateien eine nach der anderen
5. Nach jeder Datei: Syntax prüfen
6. main.py anpassen (imports + router registrierung)
7. Finaler Test: Backend startet ohne Fehler
### Syntax-Check nach jedem Schritt:
```bash
python3 -m py_compile backend/main.py
python3 -m py_compile backend/database.py
# etc.
```
### Test nach Refactoring:
```bash
# Auf dem Pi:
docker compose build --no-cache backend
docker compose up -d
docker logs mitai-api --tail 20
curl -s http://localhost:8002/api/auth/status
```
## Frontend Refactoring (Komponenten extrahieren):
### Wiederverwendbare Komponenten extrahieren nach:
```
frontend/src/components/
├── MetricCard.jsx # Kennzahl-Karte
├── ChartCombo.jsx # Kombinations-Chart
├── LoadingSpinner.jsx # Ladekreis
├── EmptyState.jsx # Leerer Zustand
├── Avatar.jsx # Nutzer-Avatar
└── ConfirmDialog.jsx # Bestätigungs-Dialog
```
### Vorgehen:
1. Identifiziere wiederholte Code-Muster
2. Extrahiere in eigene Komponenten
3. Ersetze in allen Pages
4. Frontend Build testen: `cd frontend && npm run build`
## Nach dem Refactoring:
- Commit mit `refactor:` Prefix
- CLAUDE.md Verzeichnisstruktur aktualisieren
- Kein neues Feature in diesem Commit!

226
.claude/commands/test.md Normal file
View File

@ -0,0 +1,226 @@
# Test Vollständige Test-Suite
Führt Backend-Tests (API + Logs) und Frontend-Tests (Playwright) durch.
Claude Code schreibt, führt aus und korrigiert bis alle Tests grün sind.
## Workflow für neue Features
Nach jeder Implementierung:
1. Backend-Tests schreiben und ausführen
2. Frontend-Tests schreiben und ausführen
3. Logs auf Fehler prüfen
4. Alle grün → committen
---
## TEIL 1: Backend API-Tests (schnell, immer zuerst)
### 1.1 Login + Token holen
```bash
TOKEN=$(curl -s -X POST https://dev.mitai.jinkendo.de/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"email\":\"$TEST_EMAIL\",\"pin\":\"$TEST_PASSWORD\"}" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('token','KEIN_TOKEN'))")
echo "Token: ${TOKEN:0:30}..."
```
### 1.2 Basis-Endpoints prüfen
```bash
# Version
curl -sf https://dev.mitai.jinkendo.de/api/version \
| python3 -c "import sys,json; d=json.load(sys.stdin); print('Version:', d.get('app_version'), '| DB:', d.get('db_schema_version'))"
# Auth Status
curl -sf https://dev.mitai.jinkendo.de/api/auth/status \
&& echo "Auth OK" || echo "Auth FEHLER"
# Stats (mit Token)
curl -sf https://dev.mitai.jinkendo.de/api/stats \
-H "X-Auth-Token: $TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print('Stats OK:', list(d.keys())[:5])"
```
### 1.3 CRUD-Test für neues Modul
Template für jeden neuen Router:
```bash
# CREATE
ID=$(curl -s -X POST https://dev.mitai.jinkendo.de/api/MODUL \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{"date":"2026-01-01","FELD":"WERT"}' \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id','FEHLER'))")
echo "CREATE: ID=$ID"
# READ
curl -sf https://dev.mitai.jinkendo.de/api/MODUL?limit=5 \
-H "X-Auth-Token: $TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print('READ: Einträge:', len(d))"
# DELETE (Test-Eintrag aufräumen)
curl -s -X DELETE https://dev.mitai.jinkendo.de/api/MODUL/$ID \
-H "X-Auth-Token: $TOKEN" \
&& echo "DELETE OK" || echo "DELETE FEHLER"
```
### 1.4 Auth-Schutz prüfen
```bash
# Endpoint OHNE Token muss 401 zurückgeben
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://dev.mitai.jinkendo.de/api/stats)
[ "$STATUS" = "401" ] && echo "Auth-Schutz OK (401)" || echo "WARNUNG: Auth-Schutz fehlt! ($STATUS)"
```
### 1.5 Admin-Endpoint prüfen
```bash
# Admin-Endpoint mit normalem Token muss 403 zurückgeben
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://dev.mitai.jinkendo.de/api/admin/profiles \
-H "X-Auth-Token: $TOKEN")
echo "Admin-Schutz: $STATUS (erwartet 403 für nicht-Admin)"
```
---
## TEIL 2: Backend Logs prüfen
### 2.1 Letzte Logs auf Fehler prüfen
```bash
ssh pi "docker logs dev-mitai-api --tail 50 2>&1 | grep -i 'error\|exception\|traceback\|warning' | head -20"
```
### 2.2 Nach Test-Requests: Logs prüfen
```bash
# Direkt nach API-Tests ausführen
ssh pi "docker logs dev-mitai-api --tail 20 --timestamps 2>&1"
```
### 2.3 Alle neuen Endpoints im Log sichtbar?
```bash
# Prüft ob Router korrekt registriert
ssh pi "docker logs dev-mitai-api 2>&1 | grep 'router\|route\|endpoint' | head -10"
```
### 2.4 DB-Verbindung gesund?
```bash
ssh pi "docker exec dev-mitai-postgres pg_isready -U mitai_dev && echo 'DB OK'"
```
### 2.5 Neue Tabellen vorhanden?
```bash
ssh pi "docker exec dev-mitai-postgres psql -U mitai_dev -d mitai_dev -c '\dt' 2>/dev/null | grep -E 'TABELLEN_NAME'"
```
---
## TEIL 3: Frontend Playwright-Tests
### 3.1 Alle Smoke Tests
```bash
npx playwright test
```
### 3.2 Nur neues Feature testen
```bash
npx playwright test --grep "FEATURE_NAME"
```
### 3.3 Mit sichtbarem Browser
```bash
npx playwright test --headed
```
### 3.4 Screenshot bei Fehler ansehen
```bash
start test-results\TESTNAME\test-failed-1.png
```
---
## TEIL 4: Tests schreiben
### Backend-Test Template (in test-Abschnitt einfügen)
```bash
# Für jeden neuen Endpoint:
echo "=== Test: MODUL_NAME ==="
TOKEN=$(curl -s -X POST https://dev.mitai.jinkendo.de/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"email\":\"$TEST_EMAIL\",\"pin\":\"$TEST_PASSWORD\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))")
# GET Test
curl -sf https://dev.mitai.jinkendo.de/api/MODUL \
-H "X-Auth-Token: $TOKEN" \
&& echo "GET OK" || echo "GET FEHLER"
# POST Test
curl -s -X POST https://dev.mitai.jinkendo.de/api/MODUL \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
-d '{"date":"2026-01-01"}' \
| python3 -c "import sys,json; d=json.load(sys.stdin); print('POST OK, id:', d.get('id'))"
# Auth-Schutz
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://dev.mitai.jinkendo.de/api/MODUL)
[ "$STATUS" = "401" ] && echo "Auth-Schutz OK" || echo "Auth-Schutz FEHLER ($STATUS)"
```
### Frontend-Test Template (in dev-smoke-test.spec.js)
```javascript
test('MODUL: Seite lädt und Grundfunktion', async ({ page }) => {
await login(page);
// Navigation
await page.click('text=NAV_TEXT');
await page.waitForLoadState('networkidle');
await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 10000 });
// Formular ausfüllen
await page.fill('input[name="FELD"]', 'TESTWERT');
await page.click('button:has-text("Speichern")');
// Ergebnis prüfen
await expect(page.locator('text=gespeichert')).toBeVisible({ timeout: 5000 });
await page.screenshot({ path: 'screenshots/MODUL-test.png' });
console.log('MODUL OK');
});
```
---
## TEIL 5: Vollständiger Test-Lauf (vor jedem Deploy)
```bash
echo "=== BACKEND TESTS ===" && \
TOKEN=$(curl -s -X POST https://dev.mitai.jinkendo.de/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"email\":\"$TEST_EMAIL\",\"pin\":\"$TEST_PASSWORD\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))") && \
curl -sf https://dev.mitai.jinkendo.de/api/version | python3 -c "import sys,json; d=json.load(sys.stdin); print('Version:', d.get('app_version'))" && \
curl -sf https://dev.mitai.jinkendo.de/api/stats -H "X-Auth-Token: $TOKEN" > /dev/null && echo "Stats OK" && \
echo "" && \
echo "=== LOGS PRUEFEN ===" && \
ssh pi "docker logs dev-mitai-api --tail 20 2>&1 | grep -i 'error\|exception' | head -5 || echo 'Keine Fehler in Logs'" && \
echo "" && \
echo "=== FRONTEND TESTS ===" && \
npx playwright test && \
echo "" && \
echo "=== ALLE TESTS BESTANDEN ==="
```
---
## Credentials setzen (einmalig pro Session)
```bash
$env:TEST_EMAIL="lars@stommer.com"
$env:TEST_PASSWORD="5112"
```
---
## Prod-Schutz
Tests laufen AUSSCHLIESSLICH gegen:
- https://dev.mitai.jinkendo.de
- http://192.168.2.49:8099
NIEMALS gegen:
- https://mitai.jinkendo.de
- http://192.168.2.49:8002

View File

@ -0,0 +1,42 @@
# UI Component
Erstelle eine neue React-Komponente im Mitai Jinkendo Design-System.
## Design-Regeln:
- Hintergrund: `var(--bg)` oder `var(--surface)`
- Akzentfarbe: `var(--accent)` = #1D9E75
- Text: `var(--text1)` (primary), `var(--text2)` (secondary), `var(--text3)` (muted)
- Border: `var(--border)`
- Border-Radius: 12px für Cards, 8px für Buttons/Inputs
- Kein TypeScript reines JavaScript/JSX
- Inline-Styles bevorzugt, keine externen CSS-Dateien
- Keine neuen npm-Pakete ohne Absprache
## Komponenten-Struktur:
```jsx
function MeineKomponente({ prop1, prop2 }) {
const [state, setState] = useState(null)
return (
<div className="card" style={{padding:16}}>
...
</div>
)
}
```
## Vorhandene CSS-Klassen:
- `.card` weißer Container mit Schatten
- `.btn .btn-primary` grüner Button
- `.btn .btn-secondary` grauer Button
- `.btn-full` volle Breite
- `.form-input` Eingabefeld
- `.form-label` Label
- `.form-row` Label + Input + Unit nebeneinander
- `.section-gap` vertikaler Abstand
- `.spinner` Ladeindikator
## API-Calls:
- Immer über `import { api } from '../utils/api'`
- Nie direktes `fetch()` verwenden
- Token wird automatisch injiziert

View File

@ -0,0 +1,59 @@
# UI Page
Erstelle eine neue vollständige Seite für Mitai Jinkendo.
## Seiten-Struktur:
```jsx
import { useState, useEffect } from 'react'
import { api } from '../utils/api'
import { useAuth } from '../context/AuthContext'
export default function MeineSeite() {
const { session } = useAuth()
const [data, setData] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => { load() }, [])
const load = async () => {
try {
setLoading(true)
const result = await api.meinEndpoint()
setData(result)
} catch(e) {
setError(e.message)
} finally {
setLoading(false)
}
}
if (loading) return <div className="spinner"/>
if (error) return <div style={{color:'var(--danger)'}}>{error}</div>
return (
<div style={{padding:'0 0 80px'}}>
{/* Titelzeile */}
<div style={{display:'flex',alignItems:'center',
justifyContent:'space-between',marginBottom:20}}>
<div style={{fontSize:20,fontWeight:700}}>Seitentitel</div>
</div>
{/* Inhalt */}
</div>
)
}
```
## Navigation einbinden:
In `App.jsx` unter den bestehenden Routen hinzufügen.
## Backend-Endpoint:
Neuen Endpoint in `backend/main.py` mit:
```python
@app.get("/api/mein-endpoint")
def mein_endpoint(session: dict = Depends(require_auth)):
pid = session['profile_id']
with get_db() as conn:
...
```

View File

@ -0,0 +1,121 @@
# UI Responsive
Mache die App vollständig responsive Mobile, Tablet und Desktop.
## Ziel-Design
```
iPhone (<768px): Bottom Navigation, volle Breite (wie jetzt)
iPad (768-1024px): Bottom Navigation, 2-spaltige Cards
Desktop (>1024px): Sidebar Navigation links, Content rechts, volle Breite
```
## Wichtige Regel:
**Erst planen, dann umsetzen.** Zeige den Plan und warte auf Bestätigung.
## Schritt 1: Analyse
Analysiere folgende Dateien:
- `frontend/src/app.css` aktuelle CSS-Variablen und globale Styles
- `frontend/src/App.jsx` Navigation und Layout-Struktur
- `frontend/src/pages/Dashboard.jsx` repräsentative Seite
## Schritt 2: Plan vorlegen
Zeige:
1. Welche CSS-Breakpoints werden eingeführt
2. Wie ändert sich die Navigation (Bottom ? Sidebar)
3. Welche Komponenten müssen angepasst werden
4. Geschätzter Aufwand
## Schritt 3: CSS-Variablen und Breakpoints
Neue Breakpoints in `app.css`:
```css
/* Breakpoints */
--bp-mobile: 768px;
--bp-tablet: 1024px;
/* Layout */
--sidebar-width: 220px;
--content-max-width: 1200px;
```
## Schritt 4: Layout-Komponente erstellen
Neue Datei `frontend/src/components/AppLayout.jsx`:
```jsx
// Erkennt Screen-Größe und rendert:
// - Mobile/Tablet: Bottom Navigation (wie jetzt)
// - Desktop: Sidebar Navigation + Content-Bereich
```
## Schritt 5: Navigation anpassen
**Mobile (wie jetzt):**
```jsx
<nav style={{position:'fixed', bottom:0, ...}}>
{navItems}
</nav>
```
**Desktop (neu):**
```jsx
<aside style={{
position:'fixed', left:0, top:0,
width:'var(--sidebar-width)',
height:'100vh',
background:'var(--surface)',
borderRight:'1px solid var(--border)'
}}>
{/* Logo */}
{/* Nav Items vertikal */}
{/* User Info unten */}
</aside>
<main style={{
marginLeft:'var(--sidebar-width)',
padding:'24px',
minHeight:'100vh'
}}>
{children}
</main>
```
## Schritt 6: Cards responsiv machen
```css
/* Mobile: 1 Spalte */
.metric-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
/* Desktop: 4 Spalten */
@media (min-width: 1024px) {
.metric-grid {
grid-template-columns: repeat(4, 1fr);
}
}
```
## Schritt 7: Testen
```bash
# Frontend build testen
cd frontend && npm run build
```
Browser-Test auf:
- iPhone (375px) Safari
- iPad (768px) Browser DevTools
- Desktop (1440px) Browser normal
## Jinkendo Design-Regeln beibehalten:
- Farben: var(--accent) #1D9E75, var(--bg), var(--surface)
- Border-Radius: 12px Cards, 8px Buttons
- Keine externen CSS-Frameworks
- Inline-Styles + globale CSS-Variablen
- PWA auf iPhone muss weiterhin funktionieren
## Nach dem Umbau:
- Commit mit `feat: responsive layout sidebar on desktop`
- Push auf develop
- Testen auf dev.mitai.jinkendo.de auf verschiedenen Screens
- Erst nach Freigabe: /merge-to-prod

242
.claude/docs/BACKLOG.md Normal file
View File

@ -0,0 +1,242 @@
# Mitai Jinkendo Feature Backlog
Vollständige Übersicht aller geplanten Features nach Versionen.
**Dokumentationsstruktur:**
- 📚 `functional/` - Fachliche Specs (WAS wird gebaut)
- 📚 `technical/` - Technische Specs (WIE wird es gebaut)
- 🎯 **Konkrete Tasks & Issues** → [Gitea Issues](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues)
- 📋 **Strategische Planung**`ROADMAP.md`
**Aktueller Fokus:** v9d → v9e Transition (Ruhetage abgeschlossen, Vitalwerte in Arbeit, Entwicklungsrouten vorbereiten)
---
## v9c Membership & Subscription ✅ KOMPLETT
📚 **Specs:** `technical/MEMBERSHIP_SYSTEM.md` (kombiniert fachlich + technisch)
**Deployed:** 21. März 2026 (Production)
### Features
- ✅ Feature-Enforcement-System (4-Phasen-Modell)
- ✅ Selbst-Registrierung mit E-Mail-Verifizierung
- ✅ Trial-System UI (Countdown-Banner mit 3 Urgency-Level)
- ✅ Tier-basiertes Zugriffsmanagement
- ✅ Coupons & Access-Grants
- ✅ Usage-Badges in allen relevanten Screens
**Bugfixes v9c:**
- ✅ BUG-001: TypeError in `/api/nutrition/weekly` (datetime.date handling)
- ✅ BUG-002: Ernährungs-Daten Tab fehlte
- ✅ BUG-003: Korrelations-Chart Extrapolation (gestrichelte Linien)
- ✅ BUG-004: Import-Historie Refresh (Force remount)
- ✅ BUG-005: Login → leere Seite
- ✅ BUG-006: Email-Verifizierung → leere Seite
- ✅ BUG-007: Doppelklick Verifizierungslink → generischer Fehler
- ✅ BUG-008: Dashboard infinite loading bei API-Fehlern
---
## v9d Schlaf + Sport-Vertiefung
📚 **Specs:**
- `functional/SLEEP_MODULE.md` (Schlaf-Tracking)
- `functional/TRAINING_TYPES.md` (Trainingstypen + Abilities)
- `functional/DEVELOPMENT_ROUTES.md` (Routen-System, später v9e)
**Status:** 🟡 In Arbeit (Deployed: Schlaf, Trainingstypen, Ruhetage, Vitalwerte | Offen: HF-Zonen)
### Phase 1: Trainingstypen ✅ DEPLOYED (21.03.2026)
- ✅ 29 Trainingstypen in 7 Kategorien
- ✅ Lernendes Mapping-System (DB-basiert, Auto-Learning)
- ✅ 40+ Standard-Mappings (Deutsch + Englisch)
- ✅ Admin-UI für Trainingstypen-CRUD
- ✅ Admin-UI für Activity-Mappings (inline editing)
- ✅ TrainingTypeDistribution Chart in History
- ✅ Bulk-Kategorisierung (selbstlernend)
- ✅ Apple Health Import mit automatischem Mapping
### Phase 2a: Ruhetage ✅ DEPLOYED (23.03.2026)
- ✅ Multi-Dimensional Rest Days (Kraft, Cardio, Entspannung)
- ✅ Quick Mode Presets + Custom Entry
- ✅ Validierung gegen geplante Aktivitäten
- ✅ Dashboard Widget mit aktuellen Ruhetagen
- ✅ Multiple Rest Types pro Date
### Phase 2b: Schlaf-Modul ✅ DEPLOYED (23.03.2026)
- ✅ sleep_log Tabelle mit JSONB sleep_segments
- ✅ Schlafphasen (Deep, REM, Light, Awake)
- ✅ Apple Health CSV Import
- ✅ Schlaf-Statistiken & Trends
- ✅ Schlafschuld-Berechnung
### Phase 2d: Vitalwerte ✅ DEPLOYED (23.03.2026)
- ✅ **3-Tab Architektur:** Baseline (morgens) / Blutdruck (mehrfach täglich) / Import
- ✅ **Baseline Vitals:** Ruhepuls, HRV, VO2 Max, SpO2, Atemfrequenz
- ✅ **Blutdruck:** Systolisch/Diastolisch + Puls, WHO/ISH-Klassifizierung
- ✅ **Context-Tagging:** 8 Kontexte (nüchtern, nach Essen, Training, Stress, etc.)
- ✅ **Inline-Editing:** Alle Messungen direkt in der Liste bearbeitbar
- ✅ **Smart Upsert:** Baseline lädt existierende Einträge automatisch
- ✅ **CSV Import:** Omron (Deutsch) + Apple Health (Deutsch/Englisch)
- ✅ **Mobile-optimiert:** Volle Breite Felder, Sektions-Überschriften
- ✅ Unregelmäßiger Herzschlag & AFib-Warnungen
- ✅ Trend-Analyse (7d/14d/30d)
### Phase 2e: HF-Zonen + Erholung 🔲 OFFEN
- 🔲 HF-Zonen-Verteilung pro Training
- 🔲 Recovery Score basierend auf Ruhepuls + HRV + Schlaf
- 🔲 Übertraining-Warnung
**Migrations:**
- ✅ 004: training_types Tabelle + 23 Basis-Typen
- ✅ 005: Extended types (Gehen, Tanzen, Geist & Meditation)
- ✅ 006: abilities JSONB column (Platzhalter für v9f)
- ✅ 007: activity_type_mappings (lernendes System)
- ✅ 010: sleep_log Tabelle (JSONB segments)
- ✅ 011: rest_days Tabelle
- ✅ 012: Unique constraint rest_days
- ✅ 015: Vitals Refactoring (vitals_baseline + blood_pressure_log)
**Bugfixes v9d (23.03.2026):**
- ✅ Import-Zählung korrigiert (skipped vs. updated)
- ✅ Deutsche Spaltennamen für CSV-Imports
- ✅ Dezimalwerte-Parsing (safe_int/safe_float)
- ✅ Error-Details in Import-Response
---
## v9e Entwicklungsrouten & Wochenplanung ⭐ NEU
📚 **Specs:** `functional/DEVELOPMENT_ROUTES.md`
**Status:** 🔲 Geplant (Spezifikation vorhanden, nach v9d Phase 2)
### Geplante Features
- 🔲 **6 Entwicklungsrouten:** Kraft, Kondition, Mental, Koordination, Mobilität, Technik
- 🔲 Activity-Types → Routen-Zuordnung (training_types.route Spalte)
- 🔲 Multi-Route Rest Day Validation (Konflikt-Check für alle Routen)
- 🔲 Wochenplanung: Routen-basierte Soll/Ist-Übersicht
- 🔲 Regel-Engine: Auto-Ruhetag bei Poor Recovery
- 🔲 Dashboard: Route-Balance Widget
**Tracking:** Siehe [Gitea Milestone v9e](http://192.168.2.144:3000/Lars/mitai-jinkendo/milestones)
---
## v9f Ziele & KI-Prompts
📚 **Specs:**
- `functional/GOALS_VITALS.md` (Ziele-System)
- `functional/AI_PROMPTS.md` (KI-Prompt-Flexibilisierung)
**Status:** 🔲 Geplant (nach Phase 0 Infrastruktur)
### Ziele-System
- 🔲 Primärziele (Gewichtsabnahme, Muskelaufbau, Kondition, Wettkampf)
- 🔲 Ziel-spezifische Dashboard-Ansicht
- 🔲 Fortschrittsbalken & Prognose (lineare Regression)
- 🔲 KI-Integration: Ziel-Abgleich & Handlungsempfehlungen
### KI-Prompt Flexibilisierung
- 🔲 Prompt-Bibliothek mit Kategorien
- 🔲 Platzhalter-Browser (kategorisiert + Beispielwerte)
- 🔲 Prompt-Vorschau mit echten Daten
- 🔲 Pipeline konfigurierbar (Module, Gewichtung, Zeitraum)
- 🔲 Mehrere Pipeline-Konfigurationen speichern
### Vitalwerte erweitert
- 🔲 Erweiterte Vitalwerte (SpO2, Körpertemperatur, Atemfrequenz) - teilweise in v9d implementiert
- 🔲 Hydration-Tracking
- 🔲 Medikamenten-Tracking
**Tracking:** Siehe ROADMAP.md Phase 0-2
---
## v9g Habits & Meditation
📚 **Specs:**
- `functional/MEDITATION.md` (Meditation & Selbstwahrnehmung)
- `functional/DEVELOPMENT_ROUTES.md` (Habits pro Route)
**Status:** 🔲 Geplant (Spezifikation vorhanden)
### Geplante Features
- 🔲 **Habits pro Entwicklungsroute** (route_habits Tabelle)
- 🔲 Streak-Tracking pro Route (z.B. Mental: Meditation 🔥12 Tage)
- 🔲 Dashboard: Route-Habits Widget
- 🔲 Täglicher Check-in (Energie, Stimmung, Stress 1-5)
- 🔲 Meditationssessions erfassen (Dauer, Art)
- 🔲 Journal (Freitext täglich)
- 🔲 Korrelations-Analysen (Meditation ↔ Stresslevel, Schlafqualität)
- 🔲 → Basis für **miken.jinkendo.de** (Meditations-App)
**Tracking:** Siehe [Gitea Milestone v9g](http://192.168.2.144:3000/Lars/mitai-jinkendo/milestones)
---
## v9h Connectoren & Gamification
**Status:** 🔲 Konzept (keine Spezifikation vorhanden)
### Geplante Features
- 🔲 OAuth2-Grundgerüst
- 🔲 Strava, Withings, Garmin Integration
- 🔲 Bonus-System (Streaks → Punkte → Coupons)
- 🔲 Stripe-Integration (Zahlungsabwicklung)
---
## Responsive UI (parallel zu allen Versionen)
📚 **Specs:** `functional/RESPONSIVE_UI.md`
**Status:** 🔲 Geplant (parallel implementierbar)
### Geplante Features
- 🔲 Desktop: Sidebar Navigation
- 🔲 Tablet: 2-spaltige Cards
- 🔲 Mobile: Bottom Navigation (bleibt wie jetzt)
- 🔲 **Admin-Bereich separieren** (eigene Route `/admin`, nur für Admins)
- Tab-basierte Struktur: Benutzerverwaltung, Feature-Limits, System-Settings, Coupons, AI-Prompts
- Settings-Page: Nur noch persönliche Einstellungen
**Tracking:** Siehe [Gitea Issues mit Label "responsive"](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues?labels=responsive)
---
## UX-Verbesserungen & Quick Wins
**Status:** Verschiedene kleine Verbesserungen, siehe Gitea Issues
**Beispiele:**
- Keyboard Shortcuts (Enter für Submit, Esc für Cancel)
- Toast-Notifications statt confirm() Dialoge
- Loading-States bei langsamen API-Calls
- Mehrere Fotos pro Tag hochladen (aktuell nur 1 Foto/Tag)
- Fotos löschbar machen (aktuell keine Delete-Funktion)
📋 **Tracking:** [Gitea Issues mit Label "ux" oder "quick-win"](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues?labels=ux,quick-win)
---
## Versions-Übersicht (Zusammenfassung)
| Version | Status | Kernfeatures | Deployed |
|---------|--------|--------------|----------|
| **v9c** | ✅ KOMPLETT | Membership, Feature-Enforcement, Trial-System | 21.03.2026 |
| **v9d** | 🟡 In Arbeit | Schlaf, Trainingstypen, Ruhetage, Vitalwerte | 23.03.2026 (partial) |
| **v9e** | 🔲 Geplant | Development Routes, Wochenplanung | TBD |
| **v9f** | 🔲 Geplant | Ziele, KI-Prompts, Charts | TBD |
| **v9g** | 🔲 Geplant | Habits, Meditation, Streaks | TBD |
| **v9h** | 🔲 Konzept | Connectoren, Gamification, Stripe | TBD |
---
**Dokumentiert:** 23. März 2026 (Initial) · 24. März 2026 (Konsolidiert)
**Tracking:**
- Konkrete Issues → [Gitea](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues)
- Feature-Specs → `.claude/docs/functional/*.md`
- Strategische Planung → `ROADMAP.md`
- Versions-Übersicht → Diese Datei

View File

@ -0,0 +1,374 @@
# Dokumentations-Bereinigung & Struktur-Konzept
**Erstellt:** 2026-03-23
**Ziel:** Redundanzen entfernen, klare Trennung zwischen Gitea (Tracking) und Docs (Spezifikationen)
---
## Problem-Analyse
### Aktuelle Situation
**Planungs-/Tracking-Dokumente (.claude/docs/):**
- `BACKLOG.md` (~138 Zeilen) - Feature-Übersicht v9c-v9h
- `ROADMAP.md` (~414 Zeilen) - Detaillierte Phasenplanung (Phase 0-3, Issues #24-#30)
- `KNOWN_ISSUES.md` (~100+ Zeilen) - Bug-Tracking (BUG-009, BUG-003, etc.)
- `PENDING_FEATURES.md` (~50+ Zeilen) - Feature-Enforcement-TODOs
- `TODO_V9D_PHASE2.md` - Spezifische Phase-2-TODOs
- `ISSUES_TO_CREATE.md` - Temporär (Issues bereits erstellt)
- `.claude/feature-requests/logout-button-header.md` - Einzelner Feature Request
**Spezifikationen:**
- `functional/` - SLEEP_MODULE.md, TRAINING_TYPES.md, DEVELOPMENT_ROUTES.md, etc.
- `technical/` - MEMBERSHIP_SYSTEM.md, DATABASE.md, ARCHITECTURE.md, etc.
- `architecture/` - FEATURE_ENFORCEMENT.md
- `rules/` - ARCHITECTURE.md, CODING_RULES.md, LESSONS_LEARNED.md
### Redundanzen & Probleme
1. **Doppeltes Tracking:**
- ROADMAP.md enthält Issues #24-#30
- Diese sollten primär in Gitea sein
- ROADMAP.md enthält außerdem strategische Planung (Phase 0-3)
2. **Bug-Tracking außerhalb Gitea:**
- KNOWN_ISSUES.md dokumentiert Bugs
- BUG-009 ist offen, sollte als Gitea-Issue existieren
3. **Feature-Requests verstreut:**
- BACKLOG.md listet große Features
- PENDING_FEATURES.md listet TODOs
- .claude/feature-requests/ hat einzelne Requests
- Unklare Prioritäten
4. **TODOs ohne Kontext:**
- TODO_V9D_PHASE2.md ist phasenspezifisch
- Nach Phase-Abschluss veraltet
5. **Temporäre Dateien:**
- ISSUES_TO_CREATE.md (bereits erledigt, kann weg)
---
## Vorgeschlagene Struktur
### Prinzip: Gitea für Tracking, Docs für Spezifikationen
```
┌─────────────────────────────────────────────────────────────┐
│ GITEA ISSUES │
│ (Primäres Tracking für Tasks, Bugs, kleine Features) │
├─────────────────────────────────────────────────────────────┤
│ - Konkrete implementierbare Tasks │
│ - Bugs (BUG-001, BUG-002, ...) │
│ - Kleine Features (≤ 1 Tag Aufwand) │
│ - Phase-Tasks (#24-#30) │
│ - Technical Debt (#32-#35) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ .claude/docs/functional/ │
│ (Fachliche Spezifikationen für große Features) │
├─────────────────────────────────────────────────────────────┤
│ - SLEEP_MODULE.md (v9d) │
│ - TRAINING_TYPES.md (v9d) │
│ - DEVELOPMENT_ROUTES.md (v9e) │
│ - MEDITATION.md (v9g) │
│ - AI_PROMPTS.md (v9f) │
│ - GOALS_VITALS.md (v9f) │
│ - RESPONSIVE_UI.md │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ .claude/docs/technical/ │
│ (Technische Spezifikationen & Referenz-Dokumentation) │
├─────────────────────────────────────────────────────────────┤
│ - ARCHITECTURE.md (System-Übersicht) │
│ - FRONTEND.md (Seiten, Komponenten, API-Integration) │
│ - AUTH.md (Auth-Flow, Sicherheit) │
│ - API_REFERENCE.md (Alle Endpoints) │
│ - DATABASE.md (Schema, Tabellen) │
│ - MEMBERSHIP_SYSTEM.md (v9c Membership) │
│ - MIGRATIONS.md (falls vorhanden) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ .claude/docs/ (Root-Level Meta-Dokumentation) │
├─────────────────────────────────────────────────────────────┤
│ - README.md (Dokumentations-Index + Konventionen) │
│ - CONTRIBUTING.md (Entwickler-Onboarding) │
│ - CHANGELOG.md (Versions-Historie, generiert aus git) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ .claude/rules/ │
│ (Verbindliche Entwicklungsregeln) │
├─────────────────────────────────────────────────────────────┤
│ - ARCHITECTURE.md (Architektur-Regeln, Versionierung) │
│ - CODING_RULES.md (Code-Standards) │
│ - LESSONS_LEARNED.md (Fehler, die nicht wiederholt werden) │
└─────────────────────────────────────────────────────────────┘
```
---
## Konkrete Bereinigungsaktionen
### 1. LÖSCHEN (Redundant oder temporär)
| Datei | Grund | Aktion |
|-------|-------|--------|
| `ISSUES_TO_CREATE.md` | Temporär, Issues bereits erstellt (#32-#35) | **DELETE** |
| `TODO_V9D_PHASE2.md` | Phasenspezifisch, veraltet nach Abschluss | **DELETE** (oder archivieren) |
| `.claude/feature-requests/logout-button-header.md` | Einzelner Request, gehört in Gitea | **→ Gitea Issue, dann DELETE** |
### 2. MIGRIEREN zu Gitea Issues
| Quelle | Inhalt | Ziel-Aktion |
|--------|--------|-------------|
| `KNOWN_ISSUES.md` | BUG-009 (offen) | **→ Gitea Issue #36** (Bug) |
| `KNOWN_ISSUES.md` | BUG-003 (behoben) | **→ Archiv oder DELETE** (bereits behoben) |
| `PENDING_FEATURES.md` | Feature-Enforcement TODOs | **→ Gitea Issues #37-#39** (enhancement) |
| `feature-requests/logout-button-header.md` | Logout-Button Request | **→ Gitea Issue #40** (Quick Win) |
### 3. KONSOLIDIEREN
#### 3.1. ROADMAP.md → Aufteilen
**Problem:** Enthält sowohl strategische Planung als auch konkrete Issues
**Lösung:**
**BEHALTEN in ROADMAP.md (strategisch):**
- Phase-Übersicht (Phase 0-3)
- Meilensteine (M0.1-M3.x)
- Abhängigkeiten-Graph
- Timeline (KW-Planung)
- Risiken & Mitigationen
- Erfolgsmetriken
**MIGRIEREN zu Gitea (operativ):**
- Konkrete Issue-Beschreibungen (#24-#30) → Gitea Issues (bereits vorhanden?)
- Aufwands-Schätzungen → in Gitea Issue-Feldern
- Checklisten → in Gitea Issue-Beschreibungen
**RESULTAT:** ROADMAP.md wird zum strategischen Phasenplan (ohne Issue-Details)
#### 3.2. BACKLOG.md → Reduzieren
**Problem:** Mischung aus Feature-Übersicht und TODO-Listen
**Lösung:**
**BEHALTEN in BACKLOG.md:**
- Version-Übersicht (v9c-v9h Features)
- Links zu functional/-Specs
- Status-Übersicht (✅ KOMPLETT, 🟡 In Arbeit, 🔲 Geplant)
**ENTFERNEN aus BACKLOG.md:**
- Detaillierte Checklisten (gehören in Gitea Issues)
- "Offene Issues" Listen (primär in Gitea)
- Quick Wins (→ Gitea mit Label "quick-win")
**RESULTAT:** BACKLOG.md wird zum Feature-Katalog (Verweis auf Specs + Gitea)
#### 3.3. KNOWN_ISSUES.md → Archivieren oder löschen
**Vorschlag 1 (empfohlen):** DELETE
- Aktive Bugs → Gitea Issues
- Behobene Bugs → git commit messages + CHANGELOG.md
- Lessons Learned → `.claude/rules/LESSONS_LEARNED.md`
**Vorschlag 2:** Archivieren
- Umbenennen zu `KNOWN_ISSUES_ARCHIVE.md`
- Nur für Referenz (nicht aktiv gepflegt)
#### 3.4. PENDING_FEATURES.md → DELETE
**Grund:** Feature-Enforcement-TODOs sind konkrete Tasks
**Aktion:**
1. TODOs in Gitea Issues überführen (#37-#39)
2. Datei löschen
---
## Gitea Issues erstellen (aus existierenden Docs)
### Aus KNOWN_ISSUES.md
**#36: BUG-009 - Trainingstyp-Erstellung führt zu Internal Server Error**
- Priorität: High
- Label: bug, admin
- Aufwand: 1-2h
### Aus PENDING_FEATURES.md
**#37: Feature-Enforcement für Activity CSV-Import**
- Priorität: Medium
- Label: enhancement, feature-enforcement
- Aufwand: 2-3h
**#38: Feature-Enforcement für Nutrition CSV-Import UI**
- Priorität: Low
- Label: enhancement, feature-enforcement, ui
- Aufwand: 1h
**#39: Usage-Badges im Dashboard-Assistenten**
- Priorität: Low
- Label: enhancement, ux
- Aufwand: 1-2h
### Aus feature-requests/
**#40: Logout-Button im App-Header (neben Avatar)**
- Priorität: Low
- Label: quick-win, ux
- Aufwand: 15min
---
## Vorgeschlagene Konventionen
### Was gehört in Gitea Issues?
✅ **JA:**
- Bugs (alle Prioritäten)
- Konkrete implementierbare Tasks (≤ 5 Tage Aufwand)
- Quick Wins (≤ 1h Aufwand)
- Technical Debt Items
- Phase-spezifische Tasks (#24-#30)
- Refactorings
❌ **NEIN:**
- Große Feature-Pakete (v9e, v9f, v9g) → bleiben in functional/-Specs
- Strategische Planung (Phasen, Meilensteine) → bleibt in ROADMAP.md
- Architektur-Entscheidungen → bleiben in technical/-Docs
- Lessons Learned → bleiben in rules/LESSONS_LEARNED.md
### Was gehört in functional/-Specs?
✅ **Große Feature-Pakete (> 5 Tage Aufwand):**
- SLEEP_MODULE.md (v9d Phase 2b)
- TRAINING_TYPES.md (v9d Phase 1)
- DEVELOPMENT_ROUTES.md (v9e)
- AI_PROMPTS.md (v9f)
- MEDITATION.md (v9g)
**Format:**
- Fachliche Anforderungen (WAS wird gebaut)
- User Stories
- Use Cases
- Datenmodell (Konzept)
- UI-Mockups (optional)
- Verweis auf Gitea Epic/Milestone
### Was gehört in technical/-Specs?
✅ **Referenz-Dokumentation & Implementierungsdetails:**
- ARCHITECTURE.md (System-Übersicht)
- DATABASE.md (Schema-Referenz)
- API_REFERENCE.md (Endpoint-Katalog)
- Modul-spezifische Docs (MEMBERSHIP_SYSTEM.md)
**Format:**
- Technische Entscheidungen (WIE wird es gebaut)
- API-Spezifikationen
- Datenbank-Schema
- Code-Beispiele
- Design-Patterns
---
## Migrations-Plan
### Phase 1: Gitea Issues erstellen (1h)
1. **BUG-009** → Gitea Issue #36
2. **PENDING_FEATURES TODOs** → Gitea Issues #37-#39
3. **Logout-Button Request** → Gitea Issue #40
### Phase 2: Dokumentation bereinigen (30min)
1. **DELETE:**
- `ISSUES_TO_CREATE.md`
- `TODO_V9D_PHASE2.md`
- `.claude/feature-requests/logout-button-header.md`
- `KNOWN_ISSUES.md`
- `PENDING_FEATURES.md`
2. **KONSOLIDIEREN:**
- `ROADMAP.md` → Entferne Issue-Details, behalte Strategie
- `BACKLOG.md` → Entferne Checklisten, behalte Feature-Katalog
### Phase 3: README.md erstellen (30min)
**Datei:** `.claude/docs/README.md`
**Inhalt:**
- Dokumentations-Index (Was finde ich wo?)
- Konventionen (Gitea vs. Docs)
- Wie lege ich Issues an?
- Wie erstelle ich Spezifikationen?
---
## Ergebnis nach Bereinigung
### Datei-Reduktion
**Vorher:**
- 7 Tracking-Dokumente (.claude/docs/)
- 1 Feature-Request-Datei
- ~700+ Zeilen redundante Informationen
**Nachher:**
- 2 Meta-Dokumente (ROADMAP.md, BACKLOG.md) - deutlich schlanker
- 1 README.md (Dokumentations-Index)
- ~5-7 Gitea Issues (primäres Tracking)
### Klare Trennung
```
Gitea Issues (Tracking)
Konkrete Tasks, Bugs, Quick Wins
Labels: bug, enhancement, quick-win
Aufwand: ≤ 5 Tage
.claude/docs/functional/ (Spezifikationen)
Große Feature-Pakete (> 5 Tage)
Format: Fachliche Anforderungen
Verweis auf Gitea Epic/Milestone
.claude/docs/technical/ (Referenz)
Implementierungsdetails
Format: Technische Spezifikationen
Generiert/aktualisiert nach Implementierung
.claude/rules/ (Regeln)
Verbindliche Entwicklungsstandards
Format: Architektur-Regeln, Code-Standards
```
---
## Nächste Schritte
1. **Review:** User-Zustimmung zu diesem Plan einholen
2. **Gitea:** Issues #36-#40 anlegen
3. **Cleanup:** Dateien löschen/konsolidieren
4. **README:** `.claude/docs/README.md` erstellen
5. **ROADMAP/BACKLOG:** Kürzen auf strategische Infos
6. **Commit:** "docs: cleanup and consolidate documentation structure"
---
**Frage an User:**
- Sollen große Feature-Pakete (v9e, v9f, v9g) als Gitea **Milestones** angelegt werden?
- Pro: Alle Tasks/Issues zuordenbar
- Contra: Specs bleiben trotzdem in functional/
- **Empfehlung:** Ja, Milestones in Gitea + Verweis in functional/-Specs

View File

@ -0,0 +1,109 @@
# Gitea Issues Landkarte (Auswertung)
**Quelle:** Gitea `Lars/mitai-jinkendo`, Stand **2026-04-08** (Abfrage `state=all`).
**URL:** http://192.168.2.144:3000/Lars/mitai-jinkendo/issues
Dieses Dokument ist ein **Orientierungs-Index** für Agenten und Entwickler. Verbindliches Tracking bleibt **in Gitea**; hier: Kategorien, Dubletten-Hinweise, grobe Prioritätseinschätzung.
---
## Kurzdiagnose
- **Offene Issues (Stand Abfrage):** 28
- **Auffällig:** mehrere **titelgleiche oder nahezu gleiche** Einträge (z.B. Body Cluster, Placeholder Registry, Debug UI) in Gitea konsolidieren oder schließen.
- **#25 „Ziele-System“** wahrscheinlich **veraltet** (laut Projektstatus Goals/Focus bereits fortgeschritten) mit Produkt-Owner abgleichen und schließen oder umbenennen.
---
## Offene Issues nach Thema
### Architektur / Plattform
| # | Titel |
|---|--------|
| 68 | Architektur: durchgängige Feature-/Entitlement-Schicht (Ist → Zielbild, Phasen) |
| 32 | Version-System: /api/version, Frontend, main.py-Konsistenz |
| 34 | External Volumes dokumentieren (bodytrack_* Legacy-Cleanup) |
| 35 | Deprecated Tabelle „subscriptions“ entfernen |
### Dashboard & Widgets
| # | Titel |
|---|--------|
| 65 | Dashboard: Nutzer-konfigurierbare Übersicht (Widget-System, Persistenz, Standard-Reset) |
| 66 | Dashboard-Widgets: Feature-Gate-Zuordnung aus Admin/DB statt hardcodiertem Katalog |
| 39 | Usage-Badges im Dashboard-Assistenten |
### KI / Prompts / Transparenz
| # | Titel |
|---|--------|
| 49 | Feature: Prompt-Zuordnung zu Verlaufsseiten |
| 45 | Feature: KI Prompt-Optimierer |
| 46 | Feature: KI Prompt-Ersteller (Guided Creator) |
| 42 | Enhanced Debug/Prompt Analysis UI (Issue #28 Phase C) |
| 43 | Enhanced Debug/Prompt Analysis UI (Issue #28 Phase C) |
| 47 | Enhancement: Wertetabelle Optimierung |
### Placeholder / Registry
| # | Titel |
|---|--------|
| 54 | Placeholder Registry: UNRESOLVED & TO_VERIFY Metadaten prüfen |
| 55 | Placeholder Registry: UNRESOLVED & TO_VERIFY Metadaten prüfen |
### Data / Body / Cluster (Dubletten)
| # | Titel |
|---|--------|
| 56 | Body Cluster - Restarbeiten & Metadaten-Verifizierung |
| 57 | Body Cluster - Restarbeiten & Metadaten-Verifizierung |
| 58 | Body Cluster - Restarbeiten & Metadaten-Verifizierung |
### Frontend / UX
| # | Titel |
|---|--------|
| 30 | [FEAT] Responsive UI - Desktop Sidebar + 2-spaltige Layouts |
| 29 | [FEAT] Abilities-Matrix UI (v9f) |
| 40 | Logout-Button im App-Header (neben Avatar) |
| 26 | [FEAT] Charts & Visualisierungen erweitern |
| 27 | [FEAT] Korrelationen & Insights erweitern |
### Ziele / Product (prüfen)
| # | Titel |
|---|--------|
| 25 | [FEAT] Ziele-System (Goals) - v9e Kernfeature |
### Feature-Enforcement / Import
| # | Titel |
|---|--------|
| 37 | Feature-Enforcement für Activity CSV-Import |
| 38 | Feature-Enforcement für Nutrition CSV-Import UI |
### Qualität / Sonstiges
| # | Titel |
|---|--------|
| 15 | [FEAT-002] Quality-Filter für KI-Auswertungen & Charts integrieren |
| 21 | [FEATURE] Universeller CSV-Parser mit lernbarem Feldmapping |
| 36 | BUG-009: Trainingstyp-Erstellung führt zu Internal Server Error |
---
## Geschlossene Bereiche (Referenz, Auszug)
Bereits geschlossen u.a.: #28 AI-Prompts Flexibilisierung, #24 Quality-Filter (Variante), #48 Flexibles Prompt-System, #50/#51 Goals, #53 Goals/Platzhalter/Data Layer (laut Titel), #60 Platzhalter Ernährung/Körper/Aktivität, diverse Releases.
---
## Pflege
- Nach größeren Issue-Aufräumaktionen: diese Datei **aktualisieren** oder Datum im Kopf erhöhen.
- Dubletten (#42/#43, #54/#55, #56#58): in Gitea **zusammenführen** (ein Issue offen lassen).
---
**Ende Index**

177
.claude/docs/README.md Normal file
View File

@ -0,0 +1,177 @@
# Mitai Jinkendo Dokumentations-Index
Willkommen zur Entwicklerdokumentation von **Mitai Jinkendo** (身体 Jinkendo).
**Parallel im Repository:** Issue-Epics und Placeholder-Governance im Projekt-[`docs/`](../../docs/) · siehe [`docs/README.md`](../../docs/README.md).
**Ablage-Regeln:** [`.claude/rules/DOCUMENTATION.md`](../rules/DOCUMENTATION.md) · **Issue-Landkarte:** [`GITEA_ISSUES_INDEX.md`](./GITEA_ISSUES_INDEX.md).
_Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Specs + Regeln)._
---
## Dokumentationsstruktur (Ist-Stand)
```
.claude/docs/
├── README.md ← Index (diese Datei)
├── GITEA_ISSUES_INDEX.md ← Themen-Landkarte zu Gitea (lokal gepflegt)
├── BACKLOG.md ← Feature-Katalog nach Versionen
├── ROADMAP.md ← Strategische Phasen (03)
├── CLEANUP_PLAN.md ← Historie Bereinigung März 2026
├── prompts/ ← Exportierte Prompt-Artefakte (JSON)
├── functional/ ← Fachliche Spezifikationen (WAS)
├── technical/ ← Technische Spezifikationen & Referenz (WIE)
├── working/ ← Arbeitspapiere, Analysen, Session-Snapshots
├── architecture/ ← Querschnitt: Backend/Frontend/Enforcement (kompakt)
└── audit/ ← Audits, Matrizen, Gitea-Vorlagen
```
**Externes Tracking:** [Gitea Issues](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues)
**Erzeugte Library (Kurzreferenz, ggf. `/document`):** `.claude/library/` API-, DB- und Architektur-Übersichten; bei Abweichungen gewinnt der **Code**, dann **`technical/`** bzw. **`functional/`**.
---
## Rollen der Ordner
| Ordner | Zweck | Pflege |
|--------|--------|--------|
| `functional/` | Domäne, UX, fachliche Datenflüsse | Bei Feature-Änderungen / Abnahme |
| `technical/` | APIs, Migrationen, Implementierungsmuster, Agent-Guides | Nach größeren Code-Änderungen |
| `architecture/` | Kompakte Architektur-Snippets (z.B. Frontend-Struktur, Backend-Überblick) | Bei strukturellen Umbauten |
| `audit/` | Snapshots (Code-Audit, Platzhalter-Reconciliation) **nicht** normative Spec | Nur bei neuen Audits erweitern |
| `working/` | Zwischenstände (Migration, Goals-Analysen, STATUS, NEXT_STEPS, PHASE_0C_TASKS, …) | Archivarisch; keine alleinige Norm |
| Root (`ROADMAP`, `BACKLOG`, `GITEA_ISSUES_INDEX`) | Planung, Katalog, Issue-Landkarte | Mit Gitea / `working/` abstimmen |
---
## Abgleich „Dokument ↔ Code“ (Orientierung)
| Thema | Dokument(e) | Prüfpunkt im Repo |
|--------|-------------|-------------------|
| Data Layer / Charts (Phase 0c) | `functional/DATA_ARCHITECTURE.md`, `technical/DATA_LAYER_EXTENSION_GUIDE.md` | `backend/data_layer/`, `backend/routers/charts.py` |
| Platzhalter / Registry | `technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md`, `technical/PLACEHOLDER_DEVELOPMENT_GUIDE.md` | `backend/placeholder_registrations/`, `backend/placeholder_resolver.py` |
| Dashboard-Lab-Widgets | `technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` | Widget-Katalog + Registrierung (siehe Guide) |
| Training Profiler / Resolver | `technical/TRAINING_PROFILE_RESOLVER_LAYER1.md`, `functional/TRAINING_TYPE_PROFILES.md` | Resolver-Module wie im Guide genannt |
| Mitgliedschaft / Features | `technical/MEMBERSHIP_SYSTEM.md`, `architecture/FEATURE_ENFORCEMENT.md` | `backend/auth.py`, Feature-Logging, Router mit Enforcement |
---
## Projekt-Übersicht & Regeln (außerhalb dieses Ordners)
| Dokument | Inhalt |
|----------|--------|
| `CLAUDE.md` (Root) | Agent-Kontext, Tech-Stack, kritische Regeln |
| `.claude/rules/ARCHITECTURE.md` | Verbindliche Architektur-Regeln |
| `.claude/rules/CODING_RULES.md` | Code-Standards |
| `.claude/rules/LESSONS_LEARNED.md` | Wiederkehrende Fehler vermeiden |
---
## Fachliche Spezifikationen (`functional/`)
| Dokument | Thema | Hinweis |
|----------|--------|---------|
| `ACTIVITY_QUALITY_GATES.md` | Qualitäts-Gates Aktivität | |
| `AI_PROMPTS.md` | KI-Prompts, Pipeline, Platzhalter (fachlich) | |
| `DATA_ARCHITECTURE.md` | **Fachliche** Datenarchitektur, Domänenflüsse | Schema-Details → `.claude/library/DATABASE.md` |
| `DEVELOPMENT_ROUTES.md` | Entwicklungsrouten (Roadmap-Thema) | |
| `GOALS_VITALS.md` | Ziele & Vitalwerte (fachlich) | |
| `mitai_jinkendo_konzept_diagramme_auswertungen.md` | Konzept Diagramme (ältere Linie) | |
| `mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` | Konzept Diagramme v2 | |
| `PHASE_0B_IMPROVEMENTS.md` | Phase-0b-Verbesserungen | |
| `RESPONSIVE_UI.md` | Responsive Layout-Spec | siehe auch `docs/issues/PHASE_PLAN_RESPONSIVE_UI.md` |
| `SLEEP_MODULE.md` | Schlaf-Modul | |
| `TRAINING_TYPES.md` | Trainingstypen, Mapping | |
| `TRAINING_TYPE_PROFILES.md` | Trainings-Profile (fachlich) | Techn. Ergänzung: `technical/TRAINING_TYPE_PROFILES_TECHNICAL.md` |
---
## Technische Spezifikationen (`technical/`)
| Dokument | Thema |
|----------|--------|
| `AGGREGATION_METHODS.md` | Aggregation |
| `API_REFERENCE.md` | HTTP-API-Katalog |
| `ARCHITECTURE.md` | System-Architektur |
| `AUTH.md` | Authentifizierung |
| `CENTRAL_SUBSCRIPTION_SYSTEM.md` | Abo-Zentralismus |
| `DASHBOARD_WIDGETS_AGENT_GUIDE.md` | Dashboard-Widgets für Agents |
| `DATABASE.md` | DB-Referenz (docs-Kopie; Library kann aktueller sein) |
| `DATABASE_MODEL_COMPLETE.md` | Vollständiges Modell (Referenz) |
| `DATA_LAYER_EXTENSION_GUIDE.md` | Erweiterung Data Layer |
| `FEATURE_ENFORCEMENT_MAPPING.md` | Feature-IDs / Mapping |
| `FRONTEND.md` | Frontend ausführlich (Seiten, Patterns) |
| `INTERNAL_API_REFERENCE.md` | Interne APIs |
| `MEMBERSHIP_SYSTEM.md` | Tiers, Grants, Enforcement-Details |
| `MIGRATIONS.md` | Migrations-Prozess |
| `PLACEHOLDER_DEVELOPMENT_GUIDE.md` | Platzhalter entwickeln |
| `PLACEHOLDER_REGISTRY_FRAMEWORK.md` | Registry-Pflicht, Metadaten |
| `PROFILE_REFERENCE_VALUES.md` | Profil-Referenzwerte |
| `TRAINING_PROFILE_RESOLVER_LAYER1.md` | Training-Resolver Schicht 1 |
| `TRAINING_TYPE_PROFILES_TECHNICAL.md` | Trainingsprofile technisch |
| `V9D_PHASE2_VITALS_SLEEP.md` | v9d Vitalwerte/Schlaf (Release-Bezug) |
---
## Architektur-Kurzdoks (`architecture/`)
| Dokument | Inhalt |
|----------|--------|
| `BACKEND.md` | Backend-Querschnitt |
| `FEATURE_ENFORCEMENT.md` | 4-Phasen Enforcement |
| `FRONTEND.md` | Strukturbaum `frontend/src` (kompakt) |
*Hinweis:* `architecture/FRONTEND.md` ist die **kurze** Strukturübersicht; Details und Seitenliste stehen in `technical/FRONTEND.md`.
---
## Audit (`audit/`)
Siehe [`audit/README.md`](./audit/README.md).
---
## Gitea vs. Docs
**Prinzip:** Gitea für **Tracking**, Docs für **Spezifikationen** und **Referenz**.
### Gehört in Gitea Issues
- Konkrete Tasks (typisch ≤ wenige Tage), Bugs, Quick Wins, Technical Debt
### Gehört in `functional/`
- Größere Feature-Pakete, Domänenanforderungen, Use Cases, Konzeptdiagramme
### Gehört in `technical/`
- API-, DB- und Implementierungsreferenz, Agent-Leitfäden, Migrations-Details
### Gehört in `ROADMAP.md` / `BACKLOG.md`
- Strategische Phasen bzw. Versions-Katalog mit Links auf Specs und Issues
### Nicht dauerhaft in Docs
- Temporäre To-do-Listen ohne Kontext (→ Issue); reine Duplikate anderer Dateien
---
## Workflow nach Feature-Abschluss
1. Gitea Issue schließen (Commit-Referenz).
2. `BACKLOG.md` / `ROADMAP.md` bei Meilensteinen anpassen.
3. Betroffene `functional/` / `technical/` / `.claude/library/`-Dateien aktualisieren.
4. Bei sichtbaren Agent-Regeln: `CLAUDE.md` ergänzen.
---
## Deployment (Kurzreferenz)
| Umgebung | Frontend | Backend |
|----------|----------|---------|
| Production | Port 3002 | Port 8002 |
| Development | Port 3099 | Port 8099 |
**Gitea:** http://192.168.2.144:3000/Lars/mitai-jinkendo
---
**Letzte Aktualisierung:** 8. April 2026 (Struktur-Index, Duplikatbereinigung, Abgleich-Hinweise)

550
.claude/docs/ROADMAP.md Normal file
View File

@ -0,0 +1,550 @@
# Mitai Jinkendo Entwicklungs-Roadmap
**Version:** 2.2
**Status:** Aktiv - Phase 0a ✅ Complete, Phase 0b ✅ Complete, Phase 0c 🎯 Next
**Letzte Aktualisierung:** 28. März 2026
---
## Überblick
Diese Roadmap transformiert Mitai Jinkendo von einer **Datensammlungs-App** zu einem **aktiven Begleiter** mit Auswertungen, Empfehlungen und gezielten Entwicklungspfaden.
**Strategische Ziele:**
1. ✅ **Infrastruktur schaffen** (v9f) → Flexible KI-Analysen → **DONE (AI Prompts, Training Types)**
2. ✅ **Goals System Foundation** (v0.9g-h) → Strategic + Tactical Goals → **DONE (Phase 0a + Dynamic Focus Areas v2.0)**
3. ✅ **Goal-Aware Intelligence** (Phase 0b) → Platzhalter + Auto-Population → **DONE (28.03.2026)**
4. 🎯 **Data Architecture** (Phase 0c) → Multi-Layer Separation → **NEXT**
5. 🔲 **Visualisierung stärken** → Charts, Diagramme, Trends
6. 🔲 **Aktive Begleitung** → Wochenplanung, Development Routes
**Tracking:**
- Konkrete Tasks und Issues → [Gitea Issues](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues)
- Große Feature-Pakete → `.claude/docs/functional/*.md` Spezifikationen
- Strategische Planung → Diese ROADMAP.md
- Aktueller Status → `docs/STATUS_2026-03-28.md`
---
## Phasen-Übersicht
| Phase | Fokus | Dauer | Status |
|-------|-------|-------|--------|
| **Phase 0a** | Goals System Foundation | 1 Woche | ✅ **COMPLETE** (26-27.03.2026) |
| **Phase 0b** | Goal-Aware Placeholders + Auto-Population | 1 Tag | ✅ **COMPLETE** (28.03.2026) |
| **Phase 0c** | Multi-Layer Data Architecture | 5-7 Tage | 🎯 **NEXT** |
| **Phase 1** | Charts & Visualisierung (Frontend) | 2-3 Wochen | 🔲 Geplant |
| **Phase 2** | Engagement (Korrelationen) | 3-4 Wochen | 🔲 Geplant |
| **Phase 3** | Begleitung (Development Routes) | 4-6 Wochen | 🔲 Später |
**Gesamtdauer (Phase 0-2):** ~11-14 Wochen
**Gesamtaufwand (Phase 0-2):** ~80-100h
**Abgeschlossen:** Phase 0a (Goals System), Phase 0b (Placeholders + Auto-Population)
---
## ✅ Phase 0a: Goals System Foundation (COMPLETE)
**Zeitraum:** 26-27. März 2026
**Aufwand:** 4-5h (tatsächlich)
**Status:** ✅ ABGESCHLOSSEN
### Deliverables (alle ✅)
- ✅ Migration 022: goals, training_phases, fitness_tests tables
- ✅ Migration 027-032: Dynamic Focus Areas v2.0
- 26 Basis-Bereiche in 7 Kategorien
- Many-to-Many: Goals ↔ Focus Areas
- User-spezifische Gewichtungen
- ✅ Backend: goals.py + focus_areas.py Router
- ✅ Frontend: GoalsPage (strategic) + CustomGoalsPage (tactical)
- ✅ Admin: AdminFocusAreasPage (CRUD UI)
### Achievements
- Vollständiges Goal System mit Progress Tracking
- Dynamische Focus Areas (user-extensible)
- Mobile-friendly Design
- **Basis geschaffen für Phase 0b (120+ goal-aware Platzhalter)**
📚 **Dokumentation:**
- `docs/issues/issue-50-phase-0a-goal-system.md`
- `docs/issues/issue-51-dynamic-focus-areas-v2.md`
- `docs/NEXT_STEPS_2026-03-26.md`
---
## ✅ Phase 0b: Goal-Aware Placeholders + Auto-Population (COMPLETE)
**Zeitraum:** 28. März 2026
**Aufwand:** ~8h (tatsächlich)
**Status:** ✅ ABGESCHLOSSEN
### Deliverables (alle ✅)
- ✅ **Neue Platzhalter-Funktionen:**
- `{{body_progress_score}}` - Goal-mode-abhängiger Body Score
- `{{active_goals}}` - JSON Array aktiver Ziele
- `{{focus_areas}}` - JSON Array gewichteter Focus Areas
- `{{health_stability_score}}` - Vitalwerte-Stabilität
- Nutrition-Metriken (avg_per_week_30d, etc.)
- ✅ **Score-System:** `map_focus_to_score_components()` in goal_utils.py
- ✅ **Auto-Population System:**
- `_get_historical_value_for_goal_type()` - Findet erste Messung
- Auto-adjustment von start_date zu tatsächlichem Messdatum
- Windowing-Logik für alle Goal-Typen
- ✅ **Time-Based Tracking:**
- Linear Progress Model: `expected = (elapsed_days / total_days) × 100`
- Deviation Calculation: `actual - expected`
- Hybrid Display: mit/ohne target_date
- ✅ **20+ Bug Fixes:** Decimal/float conversion, column names, dict access
### Achievements
- Goal-aware KI-Platzhalter funktionieren
- Automatische Startwerterkennung aus Historie
- Zeit-basierte Fortschrittsverfolgung
- **Basis geschaffen für Phase 0c (Multi-Layer Architecture)**
### Git Commits
```
20+ commits mit "Phase 0b" prefix (28.03.2026)
30+ commits für Auto-Population + Time-Based Tracking
```
📚 **Dokumentation:**
- `docs/issues/issue-53-phase-0c-multi-layer-architecture.md` (Phase 0b als Blaupause)
- `C:\Users\lars\.claude\projects\...\memory\feedback_goal_system.md` (Learnings)
---
## 🎯 Phase 0c: Multi-Layer Data Architecture (NEXT)
**Zeitraum:** Geplant 29.03 - 03.04.2026
**Aufwand:** 20-27h (5-7 Tage bei 4h/Tag)
**Status:** 🎯 READY FOR IMPLEMENTATION
### Ziel
Refactoring von monolithischer Platzhalter-Logik zu dreischichtiger Architektur mit Separation of Concerns.
### Drei-Schichten-Architektur
```
Layer 1: DATA LAYER (neu)
→ Pure data retrieval + calculations
→ Returns: Structured data (dict/list)
→ Testable, reusable
Layer 2a: KI LAYER (refactored)
→ Formatierung für KI-Prompts
→ Uses data_layer functions
Layer 2b: VISUALIZATION LAYER (neu)
→ Chart.js compatible JSON
→ Uses data_layer functions
```
### Deliverables
- [ ] **Data Layer Module erstellen (8-10h):**
- `backend/data_layer/body_metrics.py` (Gewicht, FM, LBM, Umfänge)
- `backend/data_layer/nutrition_metrics.py` (Protein, Makros, Adherence)
- `backend/data_layer/activity_metrics.py` (Volumen, Qualität, Abilities)
- `backend/data_layer/recovery_metrics.py` (Recovery Score, Sleep, Vitals)
- `backend/data_layer/health_metrics.py` (BP, Health Stability)
- `backend/data_layer/goals.py` (Active goals, progress, projections)
- `backend/data_layer/correlations.py` (Lag-analysis, plateau detection)
- `backend/data_layer/utils.py` (Confidence, baseline, outliers)
- [ ] **Placeholder Resolver Refactoring (3-4h):**
- Von ~1100 Zeilen zu ~400 Zeilen
- Alle Platzhalter nutzen data_layer
- [ ] **Charts Router erstellen (6-8h):**
- `backend/routers/charts.py` (NEU)
- 10+ Chart-Endpoints (K1-K10, E1-E4, A1-A5, V1-V3, R1-R2)
- [ ] **goal_utils.py Refactoring (1h):**
- Nutzt data_layer.goals
- [ ] **Testing (2-3h):**
- Unit tests für Data Layer
- Integration tests für Charts API
- [ ] **Dokumentation (1-2h):**
- `.claude/docs/technical/DATA_LAYER_ARCHITECTURE.md`
- `docs/api/CHARTS_API.md`
### Vorteile
- ✅ **Single Source of Truth:** Jede Berechnung nur einmal
- ✅ **Wiederverwendbarkeit:** Gleiche Daten für KI + Charts + API
- ✅ **Testbarkeit:** Data Layer isoliert testbar
- ✅ **Erweiterbarkeit:** Neue Features ohne Code-Duplikation
- ✅ **Performance:** Caching auf Data Layer Ebene möglich
📚 **Vollständige Spezifikation:**
- `docs/issues/issue-53-phase-0c-multi-layer-architecture.md` (23.000 Tokens)
---
## Phase 0 (Legacy): Infrastruktur (v9f) - TEILWEISE ABGESCHLOSSEN
**Ziel:** Grundlagen schaffen für flexible KI-Analysen und Fähigkeiten-Tracking
### Kernfeatures
| Feature | Aufwand | Gitea Issues |
|---------|---------|--------------|
| Quality-Filter für Charts & KI-Pipeline | 3h | Siehe Gitea |
| AI-Prompts Flexibilisierung (v9f) | 16-20h | Siehe Gitea |
| Abilities-Matrix UI (v9f) | 6-8h | Siehe Gitea |
| Responsive UI | 8-10h | Siehe Gitea |
**Gesamt:** ~33-41h
📚 **Detaillierte Specs:**
- `.claude/docs/functional/AI_PROMPTS.md` (KI-Prompt-System)
- `.claude/docs/functional/RESPONSIVE_UI.md` (Desktop-Optimierung)
- `.claude/docs/functional/TRAINING_TYPES.md` (Abilities-Matrix)
### Meilensteine
#### M0.1: Quality-Filter (1 Woche)
- **Deliverable:** KI-Pipeline filtert nach `quality_label >= acceptable`
- **Deliverable:** History.jsx Toggle "Nur qualitativ hochwertige Aktivitäten"
- **Impact:** Sofort bessere KI-Analysen
#### M0.2: AI-Prompts System (3-4 Wochen)
- **Deliverable:** Prompt-Bibliothek mit 8 Kategorien
- **Deliverable:** Platzhalter-Browser mit Beispielwerten
- **Deliverable:** Pipeline-Konfigurationen (min. 3: Alltags-Check, Schlaf-Fokus, Wettkampf)
- **Deliverable:** 50+ Platzhalter implementiert
- **Deliverable:** Quality-Level Parameter für KI-Analysen (all, quality, very_good, excellent)
- **Impact:** Grundlage für Goals, Korrelationen, alle zukünftigen KI-Features
#### M0.3: Abilities-Matrix (2 Wochen)
- **Deliverable:** Admin-UI mit 5-Dimensionen Fähigkeiten-Matrix
- **Deliverable:** Basis-Mappings für alle 29 Trainingstypen
- **Deliverable:** Endpoint `GET /api/evaluation/abilities`
- **Deliverable:** KI-Platzhalter `{{faehigkeiten_*}}`
- **Impact:** Fähigkeiten-Balance-Analysen, bessere Trainingsempfehlungen
#### M0.4: Responsive UI (2 Wochen, parallel)
- **Deliverable:** Desktop Sidebar Navigation
- **Deliverable:** 2-spaltige Layouts (Verlauf, Analyse)
- **Deliverable:** 4-spaltige Dashboard-Karten
- **Impact:** Professionelle Desktop-Nutzung
### Abhängigkeiten
```
AI-Prompts ─┬─→ Goals (KI-Integration)
├─→ Korrelationen (Pipeline-Configs)
└─→ Zukünftige KI-Features
Abilities ──→ AI-Prompts (Fähigkeiten-Platzhalter)
└─→ Korrelationen (Fähigkeiten-Balance)
Quality ────→ Sofort nutzbar (keine Abhängigkeiten)
Responsive ─→ Parallel, keine funktionalen Abhängigkeiten
```
**Phase 0 Ende:** Vollständige v9f-Infrastruktur, Desktop-optimierte UI, Quality-Filter aktiv
---
## Phase 1: Foundation
**Ziel:** Visualisierung und Ziel-Tracking (Basis ohne volle KI-Integration)
### Kernfeatures
| Feature | Aufwand | Gitea Issues |
|---------|---------|--------------|
| Charts & Visualisierungen erweitern | 8-10h | Siehe Gitea |
| Ziele-System (Basis ohne KI) | 10-12h | Siehe Gitea |
**Gesamt:** ~18-22h
📚 **Detaillierte Specs:**
- `.claude/docs/functional/GOALS_VITALS.md` (Ziele-System)
### Meilensteine
#### M1.1: Charts erweitern (2 Wochen)
- **Deliverable:** Weight Trend Chart mit 7d/30d/90d Filter
- **Deliverable:** Umfänge-Verlauf Multi-Line Chart
- **Deliverable:** Vitals Trends (RHR, HRV, VO2 Max, BP)
- **Deliverable:** Schlaf-Analyse Chart (Dauer, Qualität, Phasen)
- **Deliverable:** Ruhetage-Kalender (Heatmap)
- **Impact:** Daten werden sichtbar, Trends erkennbar
#### M1.2: Ziele-System Basis (2-3 Wochen)
- **Deliverable:** DB-Schema `goals` Tabelle
- **Deliverable:** CRUD-Endpoints
- **Deliverable:** UI: Ziele erstellen/bearbeiten/löschen
- **Deliverable:** Dashboard: Fortschrittsbalken (aktuell vs. Ziel)
- **Deliverable:** Prognose-Berechnung (lineare Regression)
- **Impact:** Nutzer sehen Fortschritt, Motivation steigt
**Hinweis:** KI-Integration für Goals kommt in Phase 2.
### Abhängigkeiten
```
Charts ────→ Keine (sofort machbar)
Goals Basis → Keine (DB + UI ohne KI funktioniert)
Goals KI ──→ AI-Prompts (braucht {{ziel_*}} Platzhalter) [Phase 2]
```
**Phase 1 Ende:** Charts zeigen Trends, Ziele sind verfolgbar (ohne KI-Empfehlungen)
---
## Phase 2: Engagement
**Ziel:** Korrelationen, KI-Integration für Goals, aktive Empfehlungen
### Kernfeatures
| Feature | Aufwand | Gitea Issues |
|---------|---------|--------------|
| Korrelationen & Insights erweitern | 6-8h | Siehe Gitea |
| Goals: KI-Integration vervollständigen | 4h | Siehe Gitea |
**Gesamt:** ~10-12h
📚 **Detaillierte Specs:**
- `.claude/docs/functional/AI_PROMPTS.md` (Pipeline-Configs)
- `.claude/docs/functional/GOALS_VITALS.md` (KI-Integration)
### Meilensteine
#### M2.1: Korrelationen (2 Wochen)
- **Deliverable:** Schlaf ↔ Recovery (Pearson Korrelation)
- **Deliverable:** Training ↔ Vitalwerte (Trainingsvolumen vs. RHR/HRV)
- **Deliverable:** Ernährung ↔ Performance (Protein vs. Kraft, Kalorien vs. Ausdauer)
- **Deliverable:** Blutdruck ↔ Lifestyle (Stress, Schlaf, Aktivität)
- **Deliverable:** Fähigkeiten-Balance ↔ Verletzungen/Plateaus
- **Deliverable:** Pipeline-Config "Schlaf-Fokus"
- **Impact:** Nutzer verstehen Zusammenhänge, gezielte Optimierung möglich
#### M2.2: Goals KI-Integration (1 Woche)
- **Deliverable:** KI-Platzhalter `{{goal_weight}}`, `{{ziel_fortschritt}}`, `{{ziel_prognose}}`
- **Deliverable:** Pipeline-Prompt "Ziel-Abgleich" (Stufe 3)
- **Deliverable:** KI-Empfehlungen: "Erhöhe Protein um 20g für Muskelaufbau-Ziel"
- **Impact:** KI gibt konkrete Schritte zur Zielerreichung
### Abhängigkeiten
```
Korrelationen ─→ AI-Prompts (Pipeline-Configs) [KRITISCH]
└─→ Abilities (Fähigkeiten-Balance)
Goals KI ──────→ AI-Prompts (Platzhalter {{ziel_*}}) [KRITISCH]
```
**Phase 2 Ende:** App gibt aktive Empfehlungen, zeigt Korrelationen, begleitet Ziele mit KI
---
## Phase 3: Begleitung (später, v9g)
**Ziel:** Wochenplanung, Development Routes, Habits & Streaks
### Geplante Features
| Feature | Aufwand | Beschreibung |
|---------|---------|--------------|
| Development Routes | 12-16h | 6 unabhängige Routen (Kraft, Kondition, Mental, Koordination, Mobilität, Technik) |
| Wochenplanung | 8-10h | Routen-basierte Wochenansicht, Regeln-Engine, Auto-Ruhetag |
| Habits & Streaks | 6-8h | Streak-Tracking pro Route, Push-Notifications |
| Weekly Planning AI | 4-6h | KI-generierte Wochenpläne basierend auf Routen-Balance |
**Gesamt:** ~30-40h
📚 **Detaillierte Specs:**
- `.claude/docs/functional/DEVELOPMENT_ROUTES.md` (Routen-System)
- `.claude/docs/functional/MEDITATION.md` (Meditation & Mental-Route)
**Status:** Spezifikation vorhanden, Implementierung nach Phase 2
---
## Abhängigkeiten-Graph (Gesamt)
```
┌─────────────────────┐
│ Phase 0: v9f │
└─────────────────────┘
┌─────────────────────┼─────────────────────┬──────────────┐
▼ ▼ ▼ ▼
Quality-Filter AI-Prompts Abilities Responsive UI
(3h) (16-20h) (6-8h) (8-10h)
│ │ │
│ │ ┌───────────────────┘
│ │ │
│ ┌──────┴─┴──────┐
│ │ Phase 1 │
│ └───────────────┘
│ │
│ ┌──────┴──────┬──────────────┐
│ ▼ ▼ │
│ Charts Goals (Basis) │
│ (8-10h) (10-12h) │
│ │ │
│ ┌──────┴──────┐ │
│ │ Phase 2 │ │
│ └─────────────┘ │
│ │ │
└────────────────────────────┼──────────────┘
┌──────┴──────┬──────────────┐
▼ ▼ │
Korrelationen Goals KI │
(6-8h) (4h) │
┌─────────────────────┘
│ Phase 3 (später)
└──────────────────────
Development Routes
Wochenplanung
Habits & Streaks
```
---
## Timeline (geschätzt)
**Start:** KW 13/2026 (24. März)
| Woche | Phase | Aktivität | Deliverable |
|-------|-------|-----------|-------------|
| **KW 13** | Phase 0 | Quality-Filter | KI-Pipeline filtert, History Toggle |
| **KW 14-17** | Phase 0 | AI-Prompts | Prompt-Bibliothek, Platzhalter-System, Pipeline-Configs |
| **KW 14-15** | Phase 0 | Responsive UI (parallel) | Desktop Sidebar, 2-spaltige Layouts |
| **KW 18-19** | Phase 0 | Abilities-Matrix | Admin-UI, Aggregation, KI-Platzhalter |
| **KW 20-21** | Phase 1 | Charts | Weight/Umfänge/Vitals/Schlaf Charts, Ruhetage-Kalender |
| **KW 22-24** | Phase 1 | Goals Basis | DB, CRUD, UI, Fortschritt, Prognose |
| **KW 25-26** | Phase 2 | Korrelationen | 5 Korrelations-Analysen, Pipeline "Schlaf-Fokus" |
| **KW 27** | Phase 2 | Goals KI | KI-Platzhalter, Pipeline "Ziel-Abgleich" |
| **KW 28+** | Phase 3 | Development Routes | Multi-Route System, Wochenplanung, Habits |
**Phase 0 Ende:** KW 19 (Mitte Mai 2026)
**Phase 1 Ende:** KW 24 (Mitte Juni 2026)
**Phase 2 Ende:** KW 27 (Anfang Juli 2026)
---
## Risiken & Mitigationen
### Risiko 1: AI-Prompts Komplexität unterschätzt
**Wahrscheinlichkeit:** Mittel
**Impact:** Hoch (blockiert Goals, Korrelationen)
**Mitigation:**
- Scope klar definieren: Min. 3 Pipeline-Configs, 50+ Platzhalter
- Platzhalter-Resolver modular bauen (lazy loading, caching)
- Frühzeitig Beispiel-Pipeline testen
### Risiko 2: Ziele-System ohne KI wenig attraktiv
**Wahrscheinlichkeit:** Mittel
**Impact:** Mittel
**Mitigation:**
- Prognose-Berechnung (lineare Regression) bereits in Phase 1
- Dashboard-Integration mit Fortschrittsbalken (visuell ansprechend)
- KI-Integration in Phase 2 schnell nachziehen (nur 4h)
### Risiko 3: Responsive UI bricht Mobile-Experience
**Wahrscheinlichkeit:** Niedrig
**Impact:** Hoch (PWA auf iPhone ist kritisch)
**Mitigation:**
- Mobile Layout bleibt exakt wie es ist (< 1024px)
- Nur Desktop (≥ 1024px) bekommt Sidebar
- Ausführliches Testing auf iPhone nach Implementierung
### Risiko 4: Abilities-Matrix UI zu komplex
**Wahrscheinlichkeit:** Niedrig
**Impact:** Mittel
**Mitigation:**
- 5 Accordion-Sections (eine pro Dimension)
- Checkboxes statt Drag & Drop
- Basis-Mappings per Migration vordefiniert (Admin muss nicht alles manuell machen)
---
## Offene Fragen
### Phase 0
1. **Pipeline-Konfigurationen:** Neue DB-Tabelle oder JSONB in `ai_prompts`?
- **Empfehlung:** Neue Tabelle `pipeline_configs` (flexibler für Zukunft)
2. **Platzhalter-Definitionen:** Statisch im Code oder dynamisch in DB?
- **Empfehlung:** Statisch im Code (einfacher, schneller), DB nur für Custom-Platzhalter später
3. **Responsive UI:** Media Queries in app.css oder CSS-in-JS?
- **Empfehlung:** Media Queries in app.css (konsistent mit bestehendem Ansatz)
### Phase 1
4. **Ziele-System:** Mehrere Ziele parallel oder nur ein Primärziel?
- **Empfehlung:** Mehrere Ziele, eines als primär markiert (flexibler)
5. **Charts:** Welche Library? (Chart.js, Recharts, Custom SVG)
- **Status:** Chart.js bereits genutzt, erweitern
### Phase 2
6. **Korrelationen:** Pearson Korrelation ausreichend oder auch Spearman/Kendall?
- **Empfehlung:** Start mit Pearson, später erweitern wenn nötig
7. **Pipeline "Schlaf-Fokus":** Welche Module? (Schlaf 14T, Vitalwerte 7T, Training 14T?)
- **Empfehlung:** Ja, plus Korrelation Schlaf ↔ Recovery
---
## Meilenstein-Deliverables (Zusammenfassung)
### M0: Phase 0 abgeschlossen (KW 19)
- ✅ Quality-Filter aktiv (KI-Pipeline + History Toggle)
- ✅ Prompt-Bibliothek mit 8 Kategorien, 50+ Platzhalter
- ✅ Min. 3 Pipeline-Konfigurationen
- ✅ Abilities-Matrix mit 29 Basis-Mappings
- ✅ Desktop Sidebar Navigation
- ✅ 2-spaltige Layouts (Verlauf, Analyse)
### M1: Phase 1 abgeschlossen (KW 24)
- ✅ 5 neue Chart-Typen (Weight, Umfänge, Vitals, Schlaf, Ruhetage)
- ✅ Ziele-System: CRUD, Dashboard-Integration, Prognose
### M2: Phase 2 abgeschlossen (KW 27)
- ✅ 5 Korrelations-Analysen
- ✅ Pipeline "Schlaf-Fokus", "Ziel-Abgleich"
- ✅ KI gibt konkrete Handlungsempfehlungen
### M3: Phase 3 abgeschlossen (später)
- ✅ 6 Development Routes
- ✅ Routen-basierte Wochenplanung
- ✅ Habits & Streaks Tracking
- ✅ KI-generierte Wochenpläne
---
## Erfolgsmetriken
### Phase 0
- **Admin nutzt Abilities-Matrix:** Min. 20 von 29 Trainingstypen mit Fähigkeiten versehen
- **Pipeline-Configs genutzt:** Min. 1 Pipeline pro Woche ausgeführt
- **Desktop-Traffic:** Min. 30% der Sessions auf Desktop (≥ 1024px)
### Phase 1
- **Charts genutzt:** Min. 5 verschiedene Chart-Typen pro Nutzer pro Monat
- **Ziele gesetzt:** Min. 50% der aktiven Nutzer haben mind. 1 Ziel
- **Prognose geschätzt:** Min. 80% der Ziele haben realistische Prognose
### Phase 2
- **Korrelationen angeschaut:** Min. 3 verschiedene Korrelationen pro Nutzer pro Monat
- **KI-Empfehlungen umgesetzt:** Min. 30% der KI-Vorschläge führen zu Verhaltensänderung
- **Goals erreicht:** Min. 20% der Ziele werden innerhalb Prognose-Zeitraum erreicht
### Phase 3
- **Routen genutzt:** Min. 3 verschiedene Routen pro Nutzer aktiv
- **Wochenplan erstellt:** Min. 1 Wochenplan pro Nutzer pro Monat
- **Streaks gehalten:** Min. 50% der Habits haben Streak ≥ 7 Tage
---
**Dokumentiert:** 23. März 2026 (Initial) · 24. März 2026 (Konsolidiert)
**Autor:** User-Strategie + Claude-Planung
**Status:** Living Document (wird nach jeder Phase aktualisiert)
**Tracking:**
- Konkrete Issues → [Gitea](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues)
- Feature-Specs → `.claude/docs/functional/*.md`
- Strategische Planung → Diese Datei

View File

@ -0,0 +1,94 @@
# Backend-Architektur
## Struktur
```
backend/
├── main.py # App-Setup + Router-Registration (~75 Zeilen)
├── db.py # PostgreSQL Connection Pool + get_cursor() Helper
├── auth.py # hash_pin · verify_pin · require_auth · require_admin
├── models.py # Alle Pydantic Models (LoginRequest, ProfileUpdate, etc.)
├── schema.sql # Vollständiges PostgreSQL-Schema
└── routers/
├── auth.py # Login, Logout, Password Reset, PIN (7 Endpoints)
├── profiles.py # Profile CRUD + Current User (7 Endpoints)
├── weight.py # Weight Tracking (5 Endpoints)
├── circumference.py # Body Measurements (4 Endpoints)
├── caliper.py # Skinfold Tracking (4 Endpoints)
├── activity.py # Workout Logging + CSV Import (6 Endpoints)
├── nutrition.py # Nutrition + FDDB Import (4 Endpoints)
├── photos.py # Progress Photos (3 Endpoints)
├── insights.py # AI Analysis + Pipeline (8 Endpoints)
├── prompts.py # AI Prompt Management (2 Endpoints)
├── admin.py # User Management (7 Endpoints)
├── stats.py # Dashboard Stats (1 Endpoint)
├── exportdata.py # CSV/JSON/ZIP Export (3 Endpoints)
└── importdata.py # ZIP Import (1 Endpoint)
```
## Router registrieren (main.py Pattern)
```python
from routers import auth, profiles, weight
app.include_router(auth.router, prefix="/api")
app.include_router(profiles.router, prefix="/api")
```
## Neuen Router anlegen
1. `backend/routers/mein_modul.py` erstellen
2. `router = APIRouter()` definieren
3. Endpoints mit `@router.get/post/put/delete` definieren
4. In `main.py` importieren und registrieren
5. Schema-Änderungen in `schema.sql` + Migration
## Datenbank-Zugriff
```python
# ✅ Immer so:
from db import get_db, get_cursor
with get_db() as conn:
cur = get_cursor(conn) # RealDictCursor gibt Dicts zurück
cur.execute("SELECT * FROM weight_log WHERE profile_id = %s", (pid,))
rows = cur.fetchall() # Liste von Dicts
# ❌ Nie so (SQLite-Syntax):
conn.execute("SELECT * FROM weight_log WHERE profile_id=?", (pid,))
```
## Auth-Pattern
```python
# Standard-Endpoint mit Auth:
@router.get("/mein-endpoint")
def mein_endpoint(session: dict = Depends(require_auth)):
pid = session['profile_id']
role = session['role']
# Admin-only:
@router.get("/admin-endpoint")
def admin_endpoint(session: dict = Depends(require_admin)):
pass
# ❌ NIEMALS so (session innerhalb Header eingebettet):
def endpoint(x: str = Header(default=None, session=Depends(require_auth))):
```
## PostgreSQL vs. SQLite Unterschiede
```python
# Platzhalter:
%s # PostgreSQL ✅
? # SQLite ❌
# Boolean:
WHERE active = true # PostgreSQL ✅
WHERE active = 1 # SQLite ❌
# Returning:
INSERT INTO ... RETURNING id # PostgreSQL ✅ (für neue IDs)
# JSON:
JSONB # PostgreSQL (für flexible Felder)
```
## DB-Schema ändern
1. `backend/schema.sql` anpassen
2. Migration schreiben (ALTER TABLE oder neues Skript)
3. Container neu bauen: `docker compose build --no-cache backend`
4. NIEMALS Spalten direkt löschen/umbenennen ohne Datenmigration

View File

@ -0,0 +1,319 @@
# Feature Enforcement Pattern
## Übersicht
Das Membership-System verwendet ein **4-Phasen-Modell** für Feature-Limits:
1. **Phase 1: Cleanup** - Feature-Konsolidierung, DB-Migration
2. **Phase 2: Non-blocking Monitoring** - JSON-Logging, keine Blockierung
3. **Phase 3: Frontend Display** - Usage-Badges, Quota-Übersicht
4. **Phase 4: Enforcement** - Tatsächliche Blockierung bei Limit-Überschreitung
**Status (2026-03-21):** ✅ Alle 11 Features sind in Phase 4 (komplett implementiert)
## Wie neue Features hinzufügen
### 1. Feature in Datenbank registrieren
```sql
INSERT INTO features (id, name, description, category, limit_type, reset_period, default_limit)
VALUES (
'new_feature_id', -- eindeutige ID
'Feature Name', -- Anzeigename
'Beschreibung', -- Beschreibung
'data', -- Kategorie: data, ai, export, integration
'count', -- limit_type: 'count' oder 'boolean'
'monthly', -- reset_period: 'never', 'daily', 'monthly'
50 -- default_limit: INT (NULL = unlimited, 0 = disabled)
);
```
**limit_type:**
- `count`: Zählbare Features (z.B. 50 Einträge pro Monat)
- `boolean`: An/Aus-Features (0 = disabled, 1 = enabled)
**reset_period:**
- `never`: Counter reset sich nie (z.B. Lifetime-Limits)
- `daily`: Reset um Mitternacht
- `monthly`: Reset am 1. des Monats
### 2. Backend-Implementierung
#### a) Router-Endpoint anpassen
```python
from auth import require_auth, check_feature_access, increment_feature_usage
from feature_logger import log_feature_usage
@router.post("/api/resource")
def create_resource(data: dict, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Create new resource entry."""
pid = get_pid(x_profile_id)
# Phase 4: Check feature access and ENFORCE
access = check_feature_access(pid, 'new_feature_id')
log_feature_usage(pid, 'new_feature_id', access, 'create')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} blocked: "
f"new_feature_id {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für [Feature-Name] überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
# ... Business Logic: INSERT into DB ...
# Phase 2: Increment usage counter (only for NEW entries, not updates!)
increment_feature_usage(pid, 'new_feature_id')
return {"id": resource_id}
```
#### b) Wichtige Regeln
**Counter nur bei INSERT, nicht bei UPDATE:**
```python
cur.execute("SELECT id FROM resource WHERE profile_id=%s AND date=%s", (pid, date))
existing = cur.fetchone()
if existing:
# UPDATE - NICHT incrementieren
cur.execute("UPDATE resource SET ... WHERE id=%s", (..., existing['id']))
else:
# INSERT - incrementieren
cur.execute("INSERT INTO resource (...) VALUES (...)", (...))
increment_feature_usage(pid, 'new_feature_id') # ← Nur hier!
```
**Für Bulk-Operationen (CSV-Import):**
```python
new_entries = 0
for row in csv_data:
cur.execute("SELECT id FROM resource WHERE profile_id=%s AND date=%s", (pid, row['date']))
if not cur.fetchone():
# INSERT
cur.execute("INSERT INTO resource (...) VALUES (...)", (...))
new_entries += 1
# Increment counter für alle neuen Einträge
for _ in range(new_entries):
increment_feature_usage(pid, 'new_feature_id')
```
### 3. Frontend-Implementierung
#### a) Import UsageBadge
```javascript
import UsageBadge from '../components/UsageBadge'
```
#### b) State Management
```javascript
export default function ResourcePage() {
const [saving, setSaving] = useState(false)
const [saved, setSaved] = useState(false)
const [error, setError] = useState(null)
const [resourceUsage, setResourceUsage] = useState(null) // Phase 4
const load = () => api.listResources().then(setResources)
const loadUsage = () => {
api.getFeatureUsage().then(features => {
const feature = features.find(f => f.feature_id === 'new_feature_id')
setResourceUsage(feature)
}).catch(err => console.error('Failed to load usage:', err))
}
useEffect(() => {
load()
loadUsage()
}, [])
const handleSave = async () => {
setSaving(true)
setError(null)
try {
await api.createResource(data)
setSaved(true)
await load()
await loadUsage() // Reload usage nach save
setTimeout(() => setSaved(false), 2000)
} catch (err) {
console.error('Save failed:', err)
setError(err.message || 'Fehler beim Speichern')
setTimeout(() => setError(null), 5000)
} finally {
setSaving(false)
}
}
```
#### c) UI mit Badge und deaktiviertem Button
```javascript
return (
<div className="card">
{/* Titel mit Badge */}
<div className="card-title badge-container-right">
<span>Neuer Eintrag</span>
{resourceUsage && <UsageBadge {...resourceUsage} />}
</div>
{/* Error-Meldung */}
{error && (
<div style={{
padding:'10px',
background:'var(--danger-bg)',
border:'1px solid var(--danger)',
borderRadius:8,
fontSize:13,
color:'var(--danger)',
marginBottom:12
}}>
{error}
</div>
)}
{/* Button mit Tooltip-Wrapper (disabled buttons zeigen keine nativen tooltips!) */}
<div
title={resourceUsage && !resourceUsage.allowed
? `Limit erreicht (${resourceUsage.used}/${resourceUsage.limit}). Kontaktiere den Admin oder warte bis zum nächsten Reset.`
: ''}
style={{display:'inline-block', width:'100%'}}
>
<button
className="btn btn-primary btn-full"
onClick={handleSave}
disabled={saving || !data || (resourceUsage && !resourceUsage.allowed)}
style={{cursor: (resourceUsage && !resourceUsage.allowed) ? 'not-allowed' : 'pointer'}}
>
{saved ? <><Check size={15}/> Gespeichert!</>
: saving ? <><div className="spinner" style={{width:14,height:14}}/></>
: (resourceUsage && !resourceUsage.allowed) ? '🔒 Limit erreicht'
: 'Speichern'}
</button>
</div>
</div>
)
```
#### d) CSS-Styling (bereits vorhanden)
Die Badge-Styles sind zentral in `frontend/src/components/UsageBadge.css`:
```css
/* Badge container rechts-aligniert */
.badge-container-right {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
/* Badge selbst */
.usage-badge {
font-size: 0.65rem;
font-weight: 600;
padding: 2px 6px;
border-radius: 4px;
opacity: 0.6;
white-space: nowrap;
}
.usage-badge--ok { color: #888; background: #f0f0f0; }
.usage-badge--warning { color: #EF9F27; background: #FFF4E5; }
.usage-badge--exceeded { color: #D85A30; background: #FCEBEB; }
```
### 4. Admin-UI (optional)
Wenn das Feature tier-spezifische Limits braucht, in Admin-UI ergänzen:
```javascript
// frontend/src/pages/admin/TierLimitsPage.jsx
const FEATURE_OPTIONS = [
{ value: 'new_feature_id', label: 'Feature Name' },
// ... existing features
]
```
## Checkliste für neue Features
- [ ] Feature in `features` Tabelle registriert
- [ ] Backend: `check_feature_access()` + `log_feature_usage()` vor Business Logic
- [ ] Backend: `raise HTTPException(403, ...)` wenn `!access['allowed']`
- [ ] Backend: `increment_feature_usage()` nach INSERT (nicht bei UPDATE!)
- [ ] Frontend: `UsageBadge` importiert
- [ ] Frontend: `resourceUsage` State mit `loadUsage()`
- [ ] Frontend: Badge im Titel (`badge-container-right`)
- [ ] Frontend: Button deaktiviert bei `!resourceUsage.allowed`
- [ ] Frontend: Tooltip-Wrapper um Button (`<div title="...">`)
- [ ] Frontend: Error-Handling in `handleSave()` mit `catch`
- [ ] Frontend: `loadUsage()` nach erfolgreichem Speichern
- [ ] Feature-ID in CLAUDE.md dokumentiert (falls relevant)
## Bestehende Features (Referenz)
| Feature ID | Typ | Reset | Beschreibung |
|-----------|-----|-------|--------------|
| weight_entries | count | never | Gewichtseinträge |
| circumference_entries | count | never | Umfangseinträge |
| caliper_entries | count | never | Caliper-Messungen |
| activity_entries | count | monthly | Aktivitätseinträge |
| nutrition_entries | count | monthly | Ernährungseinträge (meist CSV-Import) |
| photos | count | monthly | Progress-Fotos |
| ai_calls | count | monthly | KI-Analysen (Einzelanalysen) |
| ai_pipeline | boolean | - | KI-Pipeline (An/Aus) |
| data_export | count | monthly | Daten-Exporte (CSV/JSON/ZIP) |
| data_import | count | monthly | Daten-Importe (ZIP) |
## Debugging
**Log-Datei prüfen:**
```bash
tail -f backend/logs/feature-usage.log | jq .
```
**Struktur:**
```json
{
"timestamp": "2026-03-21T14:23:45.123456",
"profile_id": "uuid",
"feature_id": "weight_entries",
"action": "create",
"allowed": true,
"used": 7,
"limit": 50,
"remaining": 43,
"reason": "within_limit"
}
```
**Datenbank prüfen:**
```sql
-- Aktuelle Usage
SELECT * FROM user_feature_usage WHERE profile_id = 'xxx';
-- Feature-Definition
SELECT * FROM features WHERE id = 'new_feature_id';
-- Tier-Limits
SELECT * FROM tier_limits WHERE feature_id = 'new_feature_id';
-- User-Overrides
SELECT * FROM user_feature_restrictions
WHERE profile_id = 'xxx' AND feature_id = 'new_feature_id';
```
## Weiterführende Dokumentation
- **Membership-System:** `.claude/docs/technical/MEMBERSHIP_SYSTEM.md`
- **Backend-Architektur:** `.claude/docs/architecture/BACKEND.md`
- **Frontend-Architektur:** `.claude/docs/architecture/FRONTEND.md`
- **Coding Rules:** `.claude/docs/rules/CODING_RULES.md`

View File

@ -0,0 +1,126 @@
# Frontend-Architektur
## Struktur
```
frontend/src/
├── App.jsx # Root: Auth-Gates, Navigation, Routing
├── app.css # CSS-Variablen + globale Klassen
├── main.jsx # Vite Entry Point
├── context/
│ ├── AuthContext.jsx # Session, Login, Logout, getToken()
│ └── ProfileContext.jsx # Aktives Profil, Profile-Liste
├── pages/ # Eine Datei pro Screen
├── utils/
│ ├── api.js # Alle API-Calls (Token automatisch injiziert)
│ ├── calc.js # Körperfett-Formeln (Jackson/Pollock etc.)
│ ├── interpret.js # Regelbasierte Auswertungen
│ ├── Markdown.jsx # Eigener Markdown-Renderer
│ └── guideData.js # Messanleitungen (statisch)
└── public/ # Icons (Jinkendo Ensō-Logo)
```
## API-Calls IMMER über api.js
```javascript
// ✅ Richtig Token wird automatisch injiziert:
import { api } from '../utils/api'
const data = await api.listWeight()
await api.upsertWeight(date, weight, note)
// ❌ Falsch kein Token, gibt 401:
const r = await fetch('/api/weight')
```
## Neue API-Methode hinzufügen
In `frontend/src/utils/api.js`:
```javascript
export const api = {
// ...bestehende Methoden...
meinEndpoint: (param) => req(`/mein-endpoint?p=${param}`),
createItem: (data) => req('/items', json(data)),
updateItem: (id, d) => req(`/items/${id}`, jput(d)),
deleteItem: (id) => req(`/items/${id}`, {method:'DELETE'}),
}
```
## Navigation (Haupt-App & Admin)
- **Hauptmenü (Mobile + Desktop):** `frontend/src/config/appNav.js` (`getMainNavItems`) in `App.jsx` (Bottom-Nav) und `DesktopSidebar.jsx` nutzen.
- **Admin-Bereich:** `frontend/src/config/adminNav.js` + `layouts/AdminShell.jsx` + `layouts/RequireAdmin.jsx`; Shell wie Analyse (`.analysis-split*`).
- **Bottom-Nav / Safe Area (PWA):** `--nav-h`, `.bottom-nav`, `.app-main` in `app.css`.
- **Agent-Doku:** `docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md`
## Neue Seite hinzufügen
1. `frontend/src/pages/MeineSeite.jsx` erstellen
2. In `App.jsx` importieren und Route hinzufügen
3. Navigation: Eintrag in **`config/appNav.js`** (und ggf. Admin in **`adminNav.js`**) nicht mehr nur in `App.jsx` duplizieren
## Komponenten-Pattern
```jsx
export default function MeineSeite() {
const [data, setData] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => { load() }, [])
const load = async () => {
try {
setLoading(true)
setData(await api.meinEndpoint())
} catch(e) { setError(e.message) }
finally { setLoading(false) }
}
if (loading) return <div style={{display:'flex',justifyContent:'center',padding:40}}><div className="spinner"/></div>
if (error) return <div style={{color:'var(--danger)',padding:16}}>{error}</div>
return (
<div style={{padding:'0 0 80px'}}>
{/* Inhalt */}
</div>
)
}
```
## CSS-Variablen (Kurzreferenz)
```css
--accent: #1D9E75 --accent-dark: #085041 --accent-light: #E1F5EE
--danger: #D85A30
--bg · --surface · --surface2 · --border · --text1 · --text2 · --text3
```
## CSS-Klassen
```
.card Weißer Container, border-radius 12px
.btn Basis-Button
.btn-primary Grüner Button
.btn-secondary Grauer Button
.btn-full 100% Breite
.form-input Eingabefeld
.form-label Label (klein, uppercase)
.spinner Ladekreis
```
## Bekannte Fallstricke
### dayjs.week() NIEMALS verwenden
```javascript
// ❌ Falsch:
dayjs(date).week()
// ✅ Richtig (ISO 8601):
const weekNum = (() => {
const dt = new Date(date)
dt.setHours(0,0,0,0)
dt.setDate(dt.getDate()+4-(dt.getDay()||7))
return Math.ceil(((dt-new Date(dt.getFullYear(),0,1))/86400000+1)/7)
})()
```
### Recharts Bar fill
```jsx
// ❌ Falsch:
<Bar fill={(entry) => entry.color}/>
// ✅ Richtig:
<Bar fill="#1D9E75"/>
```

View File

@ -0,0 +1,186 @@
# Code-Audit Mitai Jinkendo
| Feld | Wert |
|------|------|
| **Datum** | 2026-04-04 |
| **Bezeichnung** | Vollständige Code-Analyse (Backend FastAPI, Frontend React, Konfiguration) |
| **Umfang** | Stichproben + gezielte Suche (Security-Patterns, Auth, SQL, Hardcoding, Duplikate) |
| **Einschränkung** | Kein Penetrationstest, keine Laufzeit-Fuzzing-Tests; statische Analyse und Architektur-Review |
---
## Kurzfassung
Die Codebasis ist insgesamt strukturiert (modulare Router, parametrisierte SQL-Queries, bcrypt, Rate-Limits auf sensiblen Auth-Endpunkten). Es gibt jedoch **kritische Autorisierungslücken** rund um `X-Profile-Id` und die öffentlichen `/api/profiles`-Routen sowie eine **fehlende Objekt-Zuordnung bei Foto-Downloads**. Zusätzlich fallen **Konfigurations- und Versions-Inkonsistenzen**, **Legacy-/Duplikat-Code** und **Abweichungen von den eigenen Coding Rules** (direktes `fetch` im Frontend) auf.
**Gitea-Issue-Vorlagen** für die wichtigsten Punkte liegen unter `gitea/`.
---
## Kritisch (P0) Sicherheit
### 1. `X-Profile-Id` ohne Abgleich mit der Session (`get_pid`)
**Datei:** `backend/routers/profiles.py` (Hilfsfunktion `get_pid`)
```20:29:backend/routers/profiles.py
def get_pid(x_profile_id: Optional[str] = Header(default=None)) -> str:
"""Get profile_id - from header for legacy endpoints."""
if x_profile_id:
return x_profile_id
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT id FROM profiles ORDER BY created LIMIT 1")
row = cur.fetchone()
if row: return row['id']
raise HTTPException(400, "Kein Profil gefunden")
```
Ist der Header gesetzt, wird **jede** authentifizierte Session auf die **beliebig gewählte** Profil-ID umgebogen. `get_pid` wird in zahlreichen Routern genutzt (Gewicht, Aktivität, Ernährung, Fotos-Upload/Liste, Vitals, Insights, …). Ein Angreifer mit gültigem Nutzer-Token kann damit **fremde Daten lesen und schreiben** (klassische IDOR-Schwachstelle).
**Empfehlung:** Profil-Kontext ausschließlich aus `session['profile_id']` ableiten; `X-Profile-Id` nur für **Admins** erlauben und gegen eine explizite Admin-Prüfung validieren oder entfernen, wenn nicht mehr benötigt.
**Vorlage:** `gitea/TEMPLATE_P0-profile-scoping-x-profile-id.md`
---
### 2. `/api/profiles`-Endpunkte ohne Admin-Pflicht
**Datei:** `backend/routers/profiles.py`
Endpunkte wie `GET/POST /api/profiles`, `GET/PUT/DELETE /api/profiles/{pid}` nutzen `Depends(require_auth)`, **nicht** `require_admin`. Damit kann jeder eingeloggte Nutzer u. a. **alle Profile auflisten**, **Profildaten abrufen** und unter Umständen **Profile löschen** (wenn mehr als eines existiert).
Das Frontend nutzt `GET /api/profiles` in `ProfileContext.jsx` für die Profilliste funktional erklärbar, **sicherheitstechnisch** aber nur akzeptabel, wenn ausschließlich Admins je Instanz ein Konto haben (Annahme, die im Code nicht erzwungen wird).
**Empfehlung:** Öffentliche/„current user“-API von Admin-CRUD trennen; Listen/Löschen/Anlegen fremder Profile nur mit `require_admin` und expliziter Policy.
**Vorlage:** `gitea/TEMPLATE_P0-profiles-router-authorization.md`
---
### 3. Foto-Abruf ohne Profil-Ownership-Prüfung
**Datei:** `backend/routers/photos.py` `GET /api/photos/{fid}`
Die Route lädt die Datei nur anhand der Foto-`id`, **ohne** zu prüfen, ob `photos.profile_id` zur Session passt. UUIDs erschweren Raten, bieten aber **keinen** Zugriffsschutz, wenn IDs bekannt sind oder geleakt werden.
**Empfehlung:** `SELECT ... WHERE id=%s AND profile_id=%s` mit `session['profile_id']` (bzw. adminseitig explizite Ausnahme).
**Vorlage:** `gitea/TEMPLATE_P1-photo-ownership-enforcement.md`
---
## Hoch (P1)
### 4. CORS: Default `allow_origins=["*"]`
**Datei:** `backend/main.py`
Wenn `ALLOWED_ORIGINS` nicht gesetzt ist, erlaubt die Middleware **`'*'`** in Kombination mit `allow_credentials=True`. Das ist in Browsern problematisch (kann je nach Client zu unerwartetem Verhalten führen) und in Produktion **riskant**, falls die Umgebungsvariable fehlt.
**Empfehlung:** In Produktion kein Wildcard-Default; Startfehler oder restriktive Default-Liste für Dev dokumentieren.
**Vorlage:** `gitea/TEMPLATE_P1-cors-allowed-origins-hardening.md`
---
### 5. Session-Token in der URL (Fotos)
**Datei:** `frontend/src/utils/api.js` Foto-URL mit `?token=...` für `<img>`-Tags; Backend: `require_auth_flexible` mit Query-Parameter.
Token in URLs landen in **Logs, Referer, Browser-Historie** erhöhtes Leak-Risiko.
**Empfehlung:** Kurzlebige signierte URLs, Cookie-Session für Same-Origin, oder dedizierter Download-Endpoint mit Header-Auth und Blob-URL im Frontend (mit Aufwand abzuwägen).
---
### 6. Veraltete / widersprüchliche Versionsangaben
| Ort | Befund |
|-----|--------|
| `backend/main.py` | `FastAPI(..., version="3.0.0")` wirkt wie API-/Framework-Version, nicht App-Version |
| `backend/version.py` | `APP_VERSION = "0.9l"` |
| `backend/routers/auth.py` `/api/auth/status` | `"version": "v9b"` offensichtlich veraltet |
| `CLAUDE.md` | erwähnt `frontend/src/version.js` **Datei im Repo nicht vorhanden** (Dokumentationsdrift) |
**Empfehlung:** Eine Quelle (`version.py` + optional Endpoint `/api/version`), Frontend aus derselben Quelle oder Build-Inject.
---
## Mittel (P2) Inkonsistenzen & Wartung
### 7. Frontend: direktes `fetch` statt `api.js`
Laut `.claude/rules/CODING_RULES.md` sollen alle API-Calls über `api.js` laufen. Tatsächlich gibt es u. a.:
- `frontend/src/context/AuthContext.jsx`, `ProfileContext.jsx`, `PasswordRecovery.jsx`
- `frontend/src/pages/AdminPanel.jsx`, `SettingsPage.jsx`
Risiko: inkonsistente Header/Fehlerbehandlung, schwerer zu auditieren.
**Vorlage:** `gitea/TEMPLATE_P2-consolidate-api-client.md`
---
### 8. Duplikat- und Legacy-Code
| Befund | Hinweis |
|--------|---------|
| `backend/main_old.py` | Großes, nicht eingebundenes Modul verwirrt Reviews und Suche |
| `backend/calculations/*` vs `backend/data_layer/*` | Parallelstruktur; Phase-0c nutzt `data_layer`, aber z. B. `placeholder_resolver.py` importiert noch `calculations.scores` |
| `backend/apply_v9c_migration.py` in `startup_event` | Legacy-Hook neben `db_init` technische Schuld |
**Vorlage:** `gitea/TEMPLATE_P2-dead-code-and-metrics-dedup.md`
---
### 9. Hardcodierte Werte (Auswahl)
- **Tests:** `tests/dev-smoke-test.spec.js` Fallback-Passwort `'5112'` wenn `TEST_PASSWORD` fehlt (nur für lokale/CI-Umgebung akzeptabel, sollte in Doku stehen und nie in Prod-Daten).
- **Playwright:** `playwright.config.js` feste `baseURL: 'https://dev.mitai.jinkendo.de'`.
- **Admin-UI:** `AdminPanel.jsx` Beispielzeile mit `APP_URL=http://192.168.2.49:3002` (Dokumentation im UI).
- **Farben:** Viele Hex-Werte in JSX (z. B. `NutritionCharts.jsx`, `History.jsx`) Verstoß gegen die eigene CSS-Variablen-Regel, aber eher UX/Wartbarkeit als Security.
### 10. Passwortlänge: `/api/auth/pin` vs Registrierung
Registrierung verlangt mindestens **8** Zeichen; PIN-/Passwort-Änderung über `/api/auth/pin` erlaubt **4** Zeichen. Inkonsistent für Sicherheitskommunikation.
### 11. Dynamische SQL-`UPDATE`-Fragmente
Muster `UPDATE ... SET {', '.join(f'{k}=%s' for k in d)}` erscheint in mehreren Routern. **Soweit geprüft**, werden Keys aus kontrollierten Dicts/Pydantic-Modellen gebaut **kein** offensichtlicher SQL-Injection-Pfad durch Rohtext. Risiko steigt, wenn später unkontrollierte Keys eingespeist werden. Whitelist pro Endpoint wäre robuster.
---
## Positiv hervorgehoben
- Parametrisierte Queries (`%s`) durchgängig üblich; keine `execute(f"...{userinput}")` in den geprüften kritischen Pfaden.
- `bcrypt` + Migration von Legacy-SHA256 in `auth.py` / Login.
- Rate Limiting auf Login, Forgot-Password, Register (`slowapi`).
- Forgot-Password gibt keine E-Mail-Existenz preis.
- Admin-Router (`/api/admin/*`) konsequent mit `require_admin`.
- Modulare Router-Registrierung in `main.py`.
---
## Mapping: Vorlagen → Themen
| Priorität | Thema | Datei unter `gitea/` |
|-----------|--------|----------------------|
| P0 | IDOR `X-Profile-Id` / `get_pid` | `TEMPLATE_P0-profile-scoping-x-profile-id.md` |
| P0 | `/api/profiles` ohne Admin | `TEMPLATE_P0-profiles-router-authorization.md` |
| P1 | Foto-Ownership | `TEMPLATE_P1-photo-ownership-enforcement.md` |
| P1 | CORS-Default | `TEMPLATE_P1-cors-allowed-origins-hardening.md` |
| P2 | `fetch` vs `api.js` | `TEMPLATE_P2-consolidate-api-client.md` |
| P2 | Legacy/Duplikate | `TEMPLATE_P2-dead-code-and-metrics-dedup.md` |
---
## Nächste Schritte (empfohlen)
1. **Sofort:** P0-Issues planen und `get_pid` / Profil-Router designen (Session-first, Admin-Override explizit).
2. **Kurzfristig:** Foto-GET mit `profile_id`-Check; CORS in allen Umgebungen prüfen.
3. **Mittelfristig:** Versionierung vereinheitlichen; `fetch`-Calls bündeln; `main_old.py` und Metrik-Duplikate bereinigen oder archivieren.
---
*Erstellt im Rahmen einer statischen Code-Analyse; bei Änderungen am Code bitte Report-Datum und Befunde aktualisieren.*

View File

@ -0,0 +1,8 @@
# Audit 2026-04-04 Code Review
| Inhalt | Pfad |
|--------|------|
| Vollständiger Report | [`CODE_AUDIT_REPORT_2026-04-04.md`](./CODE_AUDIT_REPORT_2026-04-04.md) |
| Gitea-Issue-Vorlagen (Copy & Paste) | [`gitea/`](./gitea/) |
Beim Anlegen in Gitea: **Titel** und **Labels** aus dem YAML-Kopf der jeweiligen Vorlage übernehmen, **Beschreibung** = Rest des Markdown (ohne die `---`-Blöcke, falls gewünscht).

View File

@ -0,0 +1,37 @@
---
# Gitea: Beim Anlegen des Issues Titel und Labels manuell übernehmen (YAML wird von Gitea nicht automatisch geparst dient als Meta-Hinweis).
title: "[Security/P0] X-Profile-Id / get_pid ermöglicht IDOR über alle Datendomänen"
labels: security, backend, priority-critical
---
## Kontext
Code-Audit 2026-04-04 (`/.claude/docs/audit/20260404_code_audit/CODE_AUDIT_REPORT_2026-04-04.md`).
## Problem
Die Funktion `get_pid` in `backend/routers/profiles.py` gibt bei gesetztem Header `X-Profile-Id` diesen Wert **unverändert** zurück, **ohne** Abgleich mit `session['profile_id']` oder Admin-Rolle.
Damit kann jede authentifizierte Session mit gültigem Token Daten eines **beliebigen** Profils über alle Endpunkte ansprechen, die `get_pid(x_profile_id)` nutzen (u. a. Gewicht, Aktivität, Ernährung, Fotos-Upload/Liste, Vitals, Insights, Schlaf, Ruhetage, Export).
## Reproduktion (konzeptionell)
1. Als Nutzer A einloggen, Token erhalten.
2. Profil-ID von Nutzer B ermitteln (z. B. über `GET /api/profiles` siehe separates Issue).
3. Request gegen geschützten Endpoint mit Header `X-Profile-Id: <uuid-von-B>` und Token von A.
## Erwartetes Verhalten
- Standard: `profile_id` **ausschließlich** aus der Session.
- Optional: `X-Profile-Id` nur für **Admins** und mit expliziter Prüfung `require_admin` + dokumentierter Use-Case (Multi-User-Admin-UI).
## Akzeptanzkriterien
- [ ] Alle betroffenen Router verwenden session-basierte Profil-IDs oder einen validierten Admin-Override.
- [ ] Regressionstests oder manuelle Checkliste für mindestens einen Endpoint pro Router-Gruppe.
- [ ] Kurze Doku in `.claude/rules` oder technischer Doc, ob `X-Profile-Id` noch existiert und wann.
## Betroffene Dateien (Ausgangspunkt)
- `backend/routers/profiles.py` (`get_pid`)
- Alle Importe von `get_pid` (z. B. `weight`, `activity`, `nutrition`, `photos`, `insights`, `vitals_baseline`, `blood_pressure`, `rest_days`, `sleep`, …)

View File

@ -0,0 +1,37 @@
---
title: "[Security/P0] /api/profiles CRUD ohne require_admin Datenabfluss und Missbrauch"
labels: security, backend, priority-critical
---
## Kontext
Code-Audit 2026-04-04.
## Problem
In `backend/routers/profiles.py` nutzen u. a. folgende Routen `Depends(require_auth)` **ohne** `require_admin`:
- `GET /api/profiles` Liste aller Profile
- `POST /api/profiles` Profil anlegen
- `GET/PUT/DELETE /api/profiles/{pid}` Abruf/Update/Löschen nach ID
Jeder eingeloggte Nutzer kann damit die Profilliste sehen und je nach Datenbestand auch fremde Profile manipulieren oder löschen (sofern mehr als ein Profil existiert).
Das Frontend (`ProfileContext.jsx`) ruft `GET /api/profiles` für die Profilauswahl auf das erklärt die aktuelle UX, ersetzt aber keine serverseitige Autorisierung.
## Erwartetes Verhalten
- Listen, Anlegen, Löschen und Bearbeiten **fremder** Profile: nur `require_admin`.
- „Aktuelles Profil“: dedizierte Routen (`/api/profile` o. ä.) strikt an `session['profile_id']` gebunden.
## Akzeptanzkriterien
- [ ] Rollenmodell dokumentiert (wer darf `/api/profiles` sehen?).
- [ ] Admin-only für sensible Operationen erzwungen.
- [ ] Frontend angepasst (falls nötig: Admin vs. normaler Nutzer unterschiedliche Endpunkte).
- [ ] Keine Regression für Single-User-Selfhosting ohne unnötige UX-Brüche (optional: Feature-Flag oder Rolle `admin` für ersten User).
## Betroffene Dateien
- `backend/routers/profiles.py`
- `frontend/src/context/ProfileContext.jsx` (und ggf. weitere Aufrufer von `/api/profiles`)

View File

@ -0,0 +1,36 @@
---
title: "[Security/P1] CORS: Default ALLOWED_ORIGINS=* mit allow_credentials=True"
labels: security, backend, configuration, priority-high
---
## Kontext
Code-Audit 2026-04-04.
## Problem
`backend/main.py`:
```python
allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
allow_credentials=True,
```
Fehlt die Umgebungsvariable, ist die Default-Konfiguration `*`. Zusammen mit `allow_credentials=True` ist das in Produktion unerwünscht und erschwert eine klare Security-Posture.
## Erwartetes Verhalten
- Produktion: explizite Origins aus `ALLOWED_ORIGINS` (kommasepariert), kein stiller Wildcard-Fallback.
- Entwicklung: dokumentierter Default (z. B. `http://localhost:5173`) oder explizite Opt-in-Umgebungsvariable `CORS_ALLOW_ALL=true`.
## Akzeptanzkriterien
- [ ] Verhalten in `.env.example` und Deployment-Doku beschrieben.
- [ ] Optional: Anwendung startet nicht, wenn `ALLOWED_ORIGINS` in `ENV=production` fehlt.
- [ ] Manuelle Prüfung: Browser-Requests von fremden Origins werden blockiert.
## Betroffene Dateien
- `backend/main.py`
- `.env.example`
- ggf. `docker-compose*.yml`

View File

@ -0,0 +1,36 @@
---
title: "[Security/P1] GET /api/photos/{fid} ohne Prüfung auf profile_id (Objekt-Zugriff)"
labels: security, backend, priority-high
---
## Kontext
Code-Audit 2026-04-04.
## Problem
`backend/routers/photos.py`: `GET /{fid}` lädt ein Foto nur anhand der `id` aus der Tabelle `photos`, ohne zu prüfen, ob der Datensatz zu `session['profile_id']` gehört.
Authentifizierung ist vorhanden (`require_auth_flexible`), aber **keine Objekt-Level-Autorisierung**.
## Risiko
Bei bekannter oder erratener Foto-UUID können Inhalte anderer Nutzer derselben Instanz abgerufen werden.
## Erwartetes Verhalten
```sql
SELECT path FROM photos WHERE id = %s AND profile_id = %s
```
mit `profile_id` aus der Session; Admins optional mit separatem Codepfad.
## Akzeptanzkriterien
- [ ] Abruf nur für eigenes Profil (bzw. dokumentierter Admin-Fall).
- [ ] Bestehende Clients (PWA) unverändert nutzbar.
- [ ] Kurzer Testfall oder manuelle Prüfung: Nutzer A kann Foto von B nicht öffnen.
## Betroffene Dateien
- `backend/routers/photos.py`

View File

@ -0,0 +1,30 @@
---
title: "[Tech-Debt/P2] Frontend: direktes fetch statt zentral api.js (Auth, Admin, Import)"
labels: frontend, refactoring, consistency
---
## Kontext
Code-Audit 2026-04-04. Verbindliche Regel: `.claude/rules/CODING_RULES.md` alle API-Calls über `frontend/src/utils/api.js`.
## Problem
Mehrere Komponenten nutzen natives `fetch('/api/...')` mit manuell gesetztem `X-Auth-Token`, u. a.:
- `frontend/src/context/AuthContext.jsx`
- `frontend/src/context/ProfileContext.jsx`
- `frontend/src/pages/PasswordRecovery.jsx`
- `frontend/src/pages/AdminPanel.jsx`
- `frontend/src/pages/SettingsPage.jsx`
Nachteile: doppelte Fehlerbehandlung, schwerere Wartung, leichter zu übersehen bei Security-Reviews.
## Erwartetes Verhalten
Alle Aufrufe über `api.js` (oder ein einziges dünnes Wrapper-Modul), inkl. Upload/Export-Sonderfälle.
## Akzeptanzkriterien
- [ ] Kein direktes `fetch('/api/` mehr außerhalb von `api.js` (Ausnahmen dokumentieren, falls nötig).
- [ ] Einheitliche Fehler- und 401-Behandlung.
- [ ] Kurze Notiz in CODING_RULES oder LESSONS_LEARNED, falls Ausnahmen bleiben.

View File

@ -0,0 +1,34 @@
---
title: "[Tech-Debt/P2] Legacy main_old.py, calculations/ vs data_layer/, Startup-Migration-Hooks"
labels: backend, refactoring, maintenance
---
## Kontext
Code-Audit 2026-04-04.
## Problemstellung (Bündel)
1. **`backend/main_old.py`**: große, nicht in `main.py` eingebundene Datei verwirrt Suche, Reviews und Onboarding.
2. **Parallele Metrik-Pfade**: `backend/data_layer/` (Phase 0c) und `backend/calculations/` existieren nebeneinander; `placeholder_resolver.py` importiert z. B. noch `calculations.scores`.
3. **`startup_event` in `main.py`**: zusätzlicher Hook `apply_v9c_migration` neben `db_init` Legacy-Pfad neben dem Migrations-System.
## Ziele
- Single Source of Truth für Metriken (klar dokumentiert: `data_layer` vs. verbleibende Hilfsmodule).
- Entfernen oder Archivieren (`docs/archive/` oder Git-History-only) von `main_old.py`, sofern nicht mehr referenziert.
4. Migration-Startup vereinheitlichen (nur `db_init` / nummerierte SQL-Migrationen, sofern v9c-Hook obsolet).
## Akzeptanzkriterien
- [ ] Entscheidung dokumentiert: welches Paket autoritativ ist.
- [ ] Keine doppelten Implementierungen für dieselbe Metrik ohne Grund.
- [ ] `main_old.py` entfernt oder klar als „archived“ markiert und aus IDEs/CI-Suche ausgeschlossen (optional).
- [ ] Build/Start ohne v9c-Sonderpfad, falls redundant.
## Betroffene Pfade (Start)
- `backend/main_old.py`
- `backend/main.py` (startup)
- `backend/data_layer/`, `backend/calculations/`
- `backend/placeholder_resolver.py`

View File

@ -0,0 +1,10 @@
# Audit- und Reconcile-Artefakte
Zeitlich begrenzte Reviews, Matrizen und Gitea-Vorlagen. **Keine** verbindlichen Produkt-Spezifikationen die bleiben unter [`functional/`](../functional/) und [`technical/`](../technical/).
| Ordner | Inhalt |
|--------|--------|
| [`20260404_code_audit/`](./20260404_code_audit/) | Code-Audit 2026-04-04, Report + Issue-Vorlagen |
| [`platzhalter/`](./platzhalter/) | Platzhalter-Audits, Kataloge, Reconciliation 2026-03 (JSON/Matrix) |
**Hinweis:** Doppelablagen zu `functional/` wurden entfernt (z.B. fachliche `DATA_ARCHITECTURE`, Konzept-PDF-Markdown v2). Immer die Pfade unter `functional/` bzw. `technical/` als Quelle nutzen.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,147 @@
# Placeholder Catalog (Extended)
**Generated:** 2026-03-29 19:26:40
**Total Placeholders:** 111
## Placeholders by Category
### Ernährung (8 placeholders)
- `{{carb_avg}}` - Durchschn. Kohlenhydrate in g (30d)
- `{{energy_balance_7d}}` - Energiebilanz 7d (kcal/Tag)
- `{{fat_avg}}` - Durchschn. Fett in g (30d)
- `{{kcal_avg}}` - Durchschn. Kalorien (30d)
- `{{macro_consistency_score}}` - Makro-Konsistenz Score (0-100)
- `{{protein_adequacy_28d}}` - Protein Adequacy Score (0-100)
- `{{protein_avg}}` - Durchschn. Protein in g (30d)
- `{{protein_g_per_kg}}` - Protein g/kg Körpergewicht
### Focus Areas (8 placeholders)
- `{{focus_cat_aktivität_progress}}` - Kategorie Aktivität - Progress (%)
- `{{focus_cat_aktivität_weight}}` - Kategorie Aktivität - Gewichtung (%)
- `{{focus_cat_ernährung_progress}}` - Kategorie Ernährung - Progress (%)
- `{{focus_cat_ernährung_weight}}` - Kategorie Ernährung - Gewichtung (%)
- `{{focus_cat_körper_progress}}` - Kategorie Körper - Progress (%)
- `{{focus_cat_körper_weight}}` - Kategorie Körper - Gewichtung (%)
- `{{top_focus_area_name}}` - Top Focus Area Name
- `{{top_focus_area_progress}}` - Top Focus Area Progress (%)
### Körper (11 placeholders)
- `{{bmi}}` - Body Mass Index
- `{{fm_28d_change}}` - Fettmasse Änderung 28d (kg)
- `{{kf_aktuell}}` - Aktueller Körperfettanteil in %
- `{{lbm_28d_change}}` - Magermasse Änderung 28d (kg)
- `{{recomposition_quadrant}}` - Rekomposition-Status
- `{{waist_28d_delta}}` - Taillenumfang Änderung 28d (cm)
- `{{waist_hip_ratio}}` - Taille/Hüfte-Verhältnis
- `{{weight_28d_slope}}` - Gewichtstrend 28d (kg/Tag)
- `{{weight_7d_median}}` - Gewicht 7d Median (kg)
- `{{weight_aktuell}}` - Aktuelles Gewicht in kg
- `{{weight_trend}}` - Gewichtstrend (7d/30d)
### Profil (4 placeholders)
- `{{age}}` - Alter in Jahren
- `{{geschlecht}}` - Geschlecht
- `{{height}}` - Körpergröße in cm
- `{{name}}` - Name des Nutzers
### Schlaf & Erholung (7 placeholders)
- `{{rest_days_count}}` - Anzahl Ruhetage (30d)
- `{{sleep_avg_duration}}` - Durchschn. Schlafdauer (7d)
- `{{sleep_avg_duration_7d}}` - Schlaf 7d (Stunden)
- `{{sleep_avg_quality}}` - Durchschn. Schlafqualität (7d)
- `{{sleep_debt_hours}}` - Schlafschuld (Stunden)
- `{{sleep_quality_7d}}` - Schlafqualität 7d (0-100)
- `{{sleep_regularity_proxy}}` - Schlaf-Regelmäßigkeit (Min Abweichung)
### Scores (Phase 0b) (6 placeholders)
- `{{activity_score}}` - Activity Score (0-100)
- `{{body_progress_score}}` - Body Progress Score (0-100)
- `{{data_quality_score}}` - Data Quality Score (0-100)
- `{{goal_progress_score}}` - Goal Progress Score (0-100)
- `{{nutrition_score}}` - Nutrition Score (0-100)
- `{{recovery_score}}` - Recovery Score (0-100)
### Training (9 placeholders)
- `{{ability_balance_endurance}}` - Ability Balance - Ausdauer (0-100)
- `{{ability_balance_strength}}` - Ability Balance - Kraft (0-100)
- `{{activity_summary}}` - Aktivitäts-Zusammenfassung (7d)
- `{{proxy_internal_load_7d}}` - Proxy Load 7d
- `{{quality_sessions_pct}}` - Qualitätssessions (%)
- `{{rest_day_compliance}}` - Ruhetags-Compliance (%)
- `{{training_frequency_7d}}` - Trainingshäufigkeit 7d
- `{{training_minutes_week}}` - Trainingsminuten pro Woche
- `{{trainingstyp_verteilung}}` - Verteilung nach Trainingstypen
### Unknown (49 placeholders)
- `{{ability_balance_coordination}}` - No description available
- `{{ability_balance_mental}}` - No description available
- `{{ability_balance_mobility}}` - No description available
- `{{active_goals_json}}` - No description available
- `{{active_goals_md}}` - No description available
- `{{activity_detail}}` - No description available
- `{{arm_28d_delta}}` - No description available
- `{{caliper_summary}}` - No description available
- `{{chest_28d_delta}}` - No description available
- `{{circ_summary}}` - No description available
- `{{correlation_energy_weight_lag}}` - No description available
- `{{correlation_load_hrv}}` - No description available
- `{{correlation_load_rhr}}` - No description available
- `{{correlation_protein_lbm}}` - No description available
- `{{correlation_sleep_recovery}}` - No description available
- `{{energy_deficit_surplus}}` - No description available
- `{{focus_area_weights_json}}` - No description available
- `{{focus_areas_weighted_json}}` - No description available
- `{{focus_areas_weighted_md}}` - No description available
- `{{focus_cat_lebensstil_progress}}` - No description available
- `{{focus_cat_lebensstil_weight}}` - No description available
- `{{focus_cat_mental_progress}}` - No description available
- `{{focus_cat_mental_weight}}` - No description available
- `{{focus_cat_recovery_progress}}` - No description available
- `{{focus_cat_recovery_weight}}` - No description available
- `{{focus_cat_vitalwerte_progress}}` - No description available
- `{{focus_cat_vitalwerte_weight}}` - No description available
- `{{goal_bf_pct}}` - No description available
- `{{goal_weight}}` - No description available
- `{{hip_28d_delta}}` - No description available
- `{{intake_volatility}}` - No description available
- `{{monotony_score}}` - No description available
- `{{nutrition_days}}` - No description available
- `{{plateau_detected}}` - No description available
- `{{protein_days_in_target}}` - No description available
- `{{protein_ziel_high}}` - No description available
- `{{protein_ziel_low}}` - No description available
- `{{recent_load_balance_3d}}` - No description available
- `{{strain_score}}` - No description available
- `{{thigh_28d_delta}}` - No description available
- `{{top_3_focus_areas}}` - No description available
- `{{top_3_goals_behind_schedule}}` - No description available
- `{{top_3_goals_on_track}}` - No description available
- `{{top_drivers}}` - No description available
- `{{top_goal_name}}` - No description available
- `{{top_goal_progress_pct}}` - No description available
- `{{top_goal_status}}` - No description available
- `{{weight_90d_slope}}` - No description available
- `{{zeitraum_90d}}` - No description available
### Vitalwerte (6 placeholders)
- `{{hrv_vs_baseline_pct}}` - HRV vs. Baseline (%)
- `{{rhr_vs_baseline_pct}}` - RHR vs. Baseline (%)
- `{{vitals_avg_hr}}` - Durchschn. Ruhepuls (7d)
- `{{vitals_avg_hrv}}` - Durchschn. HRV (7d)
- `{{vitals_vo2_max}}` - Aktueller VO2 Max
- `{{vo2max_trend_28d}}` - VO2max Trend 28d
### Zeitraum (3 placeholders)
- `{{datum_heute}}` - Heutiges Datum
- `{{zeitraum_30d}}` - 30-Tage-Zeitraum
- `{{zeitraum_7d}}` - 7-Tage-Zeitraum

View File

@ -0,0 +1,24 @@
# Placeholder Export Specification
**Version:** 1.0.0
**Generated:** 2026-03-29 19:26:40
## API Endpoints
### Extended Export
```
GET /api/prompts/placeholders/export-values-extended
Header: X-Auth-Token: <token>
```
Returns complete metadata for all 116 placeholders.
### ZIP Export (Admin)
```
GET /api/prompts/placeholders/export-catalog-zip
Header: X-Auth-Token: <token>
```
Returns ZIP with all catalog files.

View File

@ -0,0 +1,66 @@
# Placeholder Metadata Gap Report
**Generated:** 2026-03-29 19:26:40
**Total Placeholders:** 111
## Gaps Summary
### Missing Data Layer Module
Count: 100
- {{name}}
- {{age}}
- {{height}}
- {{geschlecht}}
- {{bmi}}
- {{goal_weight}}
- {{goal_bf_pct}}
- {{nutrition_days}}
- {{protein_ziel_low}}
- {{protein_ziel_high}}
- ... and 90 more
### Missing Semantic Contract
Count: 25
- {{bmi}}
- {{goal_bf_pct}}
- {{rest_days_count}}
- {{vitals_vo2_max}}
- {{data_quality_score}}
- {{top_goal_progress_pct}}
- {{waist_hip_ratio}}
- {{energy_deficit_surplus}}
- {{protein_days_in_target}}
- {{macro_consistency_score}}
- ... and 15 more
### Missing Source Tables
Count: 90
- {{name}}
- {{age}}
- {{height}}
- {{geschlecht}}
- {{bmi}}
- {{goal_weight}}
- {{goal_bf_pct}}
- {{nutrition_days}}
- {{protein_ziel_low}}
- {{protein_ziel_high}}
- ... and 80 more
### Unknown Time Window
Count: 74
- {{bmi}}
- {{caliper_summary}}
- {{goal_weight}}
- {{goal_bf_pct}}
- {{nutrition_days}}
- {{protein_ziel_low}}
- {{protein_ziel_high}}
- {{activity_summary}}
- {{activity_detail}}
- {{trainingstyp_verteilung}}
- ... and 64 more

View File

@ -0,0 +1,386 @@
# Normative Standardspezifikation
## Placeholder-Metadaten, Exportverträge und Governance
**Version:** 1.0.0
**Stand:** 29.03.2026
**Status:** Verbindlicher Standard
**Geltungsbereich:** Alle bestehenden, neuen und zukünftigen Platzhalter des Systems
---
## 1. Normativer Status
Dieses Dokument ist **keine bloße Projektbeschreibung**, sondern eine **verbindliche Standardspezifikation** für das Placeholder-System.
Es gilt für:
- alle aktuell existierenden Platzhalter,
- alle künftig neu eingeführten Platzhalter,
- alle technisch abgeleiteten Exportformate,
- alle Prompt-, Pipeline- und Analyseverwendungen, die auf Placeholders zugreifen,
- alle künftigen Erweiterungen der Placeholder-Registry, des Data Layers und der Exportfunktionen.
### 1.1 Verbindlichkeit
Alle neuen und geänderten Placeholder-Implementierungen **müssen** dieser Spezifikation entsprechen. Abweichungen sind nur zulässig, wenn sie:
1. explizit dokumentiert,
2. versioniert,
3. mit `known_issues` und/oder `deprecated` markiert,
4. und in einem Gap- oder Decision-Log nachvollziehbar begründet werden.
### 1.2 Vorrang
Diese Spezifikation hat Vorrang vor:
- unvollständigen Altbeschreibungen,
- impliziten Verhaltensannahmen in Legacy-Prompts,
- nicht dokumentierten Exportkonventionen,
- informellen Teamabsprachen.
Wenn bestehender Code oder bestehende Exporte von dieser Spezifikation abweichen, ist die Abweichung als **Legacy-Zustand** zu markieren und über Versionierung/Deprecation zu behandeln.
---
## 2. Zielbild
Das System besitzt bereits einen funktionierenden Placeholder-Export und einen Data Layer als Single Source of Truth für Charts, Scores und KI-Platzhalter. Dieser Standard definiert, wie Placeholder künftig **semantisch, technisch und governance-seitig** beschrieben, exportiert und weiterentwickelt werden.
Jeder Placeholder muss als **stabiler, dokumentierter API-Vertrag** behandelbar sein.
Der Standard verlangt, dass zu jedem Placeholder mindestens folgende Ebenen eindeutig bestimmbar oder explizit als unbekannt markiert sind:
1. **Ist-Wert / Beispielwert**
2. **Technische Herkunft**
3. **Semantischer Vertrag**
4. **Fehlwert- und Ausnahmebehandlung**
5. **Qualitäts-/Confidence-Logik**
6. **Verwendungsnachweise**
7. **Versionierung / Deprecation / Nachfolger**
---
## 3. Verbindliche Architekturprinzipien
### 3.1 Placeholder = API-Vertrag
Placeholder sind keine lose Prompt-Hilfe, sondern **stabile API-Verträge**. Semantik, Einheit, Zeitfenster, Rückgabetyp und Fehlwertverhalten müssen dokumentiert und versioniert sein.
### 3.2 Non-breaking first
Bestehende Exporte und bestehende Placeholder-Namen dürfen nicht stillschweigend breaking geändert werden. Änderungen an Semantik, Zeitfenster, Einheit oder Rückgabetyp sind:
- entweder über neue Versionen,
- oder über neue Nachfolger-Platzhalter,
- oder über explizite Deprecation-Pfade
abzubilden.
### 3.3 JSON vor Freitext
Komplexe Metadaten, Rohdaten, strukturierte Diagnosen und technische Hinweise sind primär als JSON bereitzustellen. Freitext darf ergänzen, aber keine strukturierte Pflichtinformation ersetzen.
### 3.4 Zeitfenster explizit
Zeitbezogene Placeholder müssen ein explizites Zeitfenster tragen mindestens als Metadatum. Legacy-Namen ohne eindeutiges Zeitfenster dürfen im Bestand fortbestehen, müssen aber als semantisch unpräzise markiert werden und einen empfohlenen Nachfolger erhalten.
### 3.5 Fehlwerte explizit
Fehlende Werte dürfen im strukturierten Export nicht nur als String wie `"nicht verfügbar"` modelliert werden. Verbindlich sind strukturierte Felder wie:
- `available`
- `value_raw`
- `missing_reason`
- `missing_value_policy`
### 3.6 Typisierung ist Pflicht
Jeder Placeholder muss einem Typ zugeordnet werden:
- `atomic`
- `raw_data`
- `interpreted`
- `legacy_unknown` (nur als Übergang)
### 3.7 Data Layer bleibt Single Source of Truth
Fachliche Berechnungen dürfen nicht im Exporter dupliziert oder neu erfunden werden. Der Exporter sammelt Metadaten und referenziert technische Herkunft; er ersetzt keine fachlichen Kernfunktionen.
### 3.8 Kein stilles Raten
Wenn technische Herkunft, Zeitfenster, Ausnahmebehandlung oder sonstige Metadaten nicht sicher aus dem Code bestimmbar sind, muss der Zustand als `unknown`, `not_resolved` oder `partially_resolved` markiert werden.
### 3.9 Zukunftsgeltung
Jeder neue Placeholder **muss vor produktivem Einsatz** in Registry, Export und Katalog vollständig gegen dieses Dokument validiert werden.
---
## 4. Geltungsbereich und Pflichtanwendung
### 4.1 Diese Spezifikation gilt für
- bestehende produktive Placeholder,
- neue Placeholder in aktiver Entwicklung,
- experimentelle Placeholder, sobald sie außerhalb eines reinen Dev-Modus verwendet werden,
- Placeholder in Multi-Stage-Pipelines,
- Placeholder in Reports, Dashboards, Prompt-Templates und Exporten.
### 4.2 Diese Spezifikation gilt auch für
- neue Goal-/Score-/Correlation-/Driver-Placeholder,
- künftige Trainingstyp-/Subcategory-Placeholder,
- Placeholder für Diagnose-, Risiko- und Recovery-Logik,
- technische Export-Erweiterungen,
- Two-Stage-Artefakte.
### 4.3 Nicht zulässig ohne Standardkonformität
Nicht zulässig ist:
- ein neuer Placeholder ohne dokumentierten Semantikvertrag,
- ein neuer Placeholder ohne Zeitfenster-Metadatum,
- ein strukturierter Placeholder ohne definierten Output-Typ,
- ein produktiver Placeholder ohne dokumentierte Fehlwertbehandlung,
- ein interpretierter Placeholder ohne Kennzeichnung als solcher.
---
## 5. Scope des technischen Auftrags
### 5.1 In Scope
Der Coding Agent soll:
1. alle existierenden Placeholder inventarisieren,
2. die Metadaten je Placeholder technisch erheben oder als unresolved markieren,
3. einen erweiterten Export erzeugen,
4. einen menschenlesbaren und maschinenlesbaren Katalog erzeugen,
5. die Katalog-/Exportstruktur so implementieren, dass sie **für alle zukünftigen Placeholder wiederverwendbar** ist,
6. Tests und Validierungen ergänzen,
7. Gap-Reports für nicht automatisch auflösbare Metadaten erzeugen.
### 5.2 Out of Scope
Nicht gefordert ist:
- sofortige komplette fachliche Neudefinition aller Legacy-Placeholder,
- sofortige Umbenennung aller problematischen Namen,
- Umsetzung aller in anderen Dokumenten vorgeschlagenen neuen Wunsch-Placeholder,
- Neuimplementierung des gesamten Prompt-Systems.
### 5.3 Zukunftsanforderung
Die technische Lösung muss so gebaut sein, dass **jeder zukünftige Placeholder** denselben Katalog- und Exportmechanismus automatisch oder mit minimalem Zusatzaufwand durchlaufen kann.
---
## 6. Ziel-Deliverables
### 6.1 Code / Export
1. **Erweiterter Placeholder-Export**
- neuer Endpoint, CLI, Service-Funktion oder Exportmodus
- Legacy-Export bleibt kompatibel
2. **Zentrales Metadatenmodell / Registry-Erweiterung**
- wiederverwendbar für bestehende und zukünftige Placeholder
3. **Validierungslogik für neue Placeholder**
- technische Prüfung gegen dieses Dokument
### 6.2 Dateien / Dokumentation
4. `PLACEHOLDER_CATALOG_EXTENDED.json`
5. `PLACEHOLDER_CATALOG_EXTENDED.md`
6. `PLACEHOLDER_EXPORT_SPEC.md`
7. `PLACEHOLDER_GAP_REPORT.md`
8. optional: `PLACEHOLDER_VALIDATION_REPORT.md`
### 6.3 Tests
9. Tests für:
- Vollständigkeit des Exports,
- Konsistenz der Metadaten,
- Legacy-Kompatibilität,
- Referenzierbarkeit der Verwendungen,
- Pflichtfeld-Validierung für neue Placeholder,
- normkonforme Typisierung und Zeitfenster-Markierung.
---
## 7. Verbindliches Metadaten-Schema pro Placeholder
Jeder Placeholder im erweiterten Export muss mindestens die folgenden Felder besitzen oder explizit als `unknown`/`null`/leer markiert sein.
```json
{
"key": "weight_aktuell",
"placeholder": "{{weight_aktuell}}",
"category": "Körper",
"type": "atomic",
"description": "Aktuelles Gewicht in kg",
"semantic_contract": "Letzter verfügbarer Gewichtseintrag des Profils, keine Mittelung",
"unit": "kg",
"time_window": "latest",
"output_type": "number_or_string",
"format_hint": "85.8 kg",
"example_output": "85.8 kg",
"value_display": "85.8 kg",
"value_raw": 85.8,
"available": true,
"missing_reason": null,
"missing_value_policy": {
"legacy_display": "nicht verfügbar",
"structured_null": true,
"reason_codes": ["no_data", "insufficient_data", "resolver_error"]
},
"exception_handling": {
"on_error": "return_null_and_reason",
"notes": "Keine Exception bis in Prompt-Ebene durchreichen"
},
"quality_filter_policy": null,
"confidence_logic": {
"supported": false,
"notes": "Für latest-Werte nicht erforderlich"
},
"source": {
"resolver": "get_latest_weight_placeholder",
"module": "placeholder_resolver.py",
"function": "get_latest_weight",
"data_layer_module": "body_metrics.py",
"source_tables": ["weight_log"]
},
"dependencies": ["profile_id"],
"used_by": {
"prompts": ["gesamt", "koerper"],
"pipelines": ["pipeline", "wettkampf-analyse"],
"charts": []
},
"version": "1.0.0",
"deprecated": false,
"replacement": null,
"known_issues": [],
"notes": []
}
```
### 7.1 Pflichtfelder
Pflichtfelder sind:
- `key`
- `placeholder`
- `category`
- `type`
- `description`
- `semantic_contract`
- `unit`
- `time_window`
- `output_type`
- `value_display`
- `available`
- `missing_value_policy`
- `source`
- `version`
- `deprecated`
### 7.2 Erlaubte Werte für `type`
- `atomic`
- `raw_data`
- `interpreted`
- `legacy_unknown`
### 7.3 Erlaubte Werte für `time_window`
- `latest`
- `7d`
- `14d`
- `28d`
- `30d`
- `90d`
- `custom`
- `mixed`
- `unknown`
### 7.4 Erlaubte Werte für `output_type`
- `string`
- `number`
- `integer`
- `boolean`
- `json`
- `markdown`
- `date`
- `enum`
- `unknown`
---
## 8. Exportformat
### 8.1 Legacy-Export bleibt erhalten
Der bestehende Export bleibt unverändert nutzbar.
### 8.2 Neuer erweiterter Export
Zusätzlich muss ein erweiterter Export existieren, z. B. mit:
- `legacy`
- `metadata.by_category`
- `metadata.flat`
- `metadata.summary`
- `metadata.gaps`
- `metadata.schema_version`
### 8.3 Zukunftsfähigkeit
Der erweiterte Export muss so strukturiert sein, dass neue Placeholder ohne Redesign der Gesamtstruktur ergänzt werden können.
---
## 9. Pflichtregeln für neue und zukünftige Placeholder
Jeder neue Placeholder muss vor Freigabe folgende Fragen beantworten können:
1. Was ist die exakte fachliche Semantik?
2. Welches Zeitfenster gilt?
3. Welcher Typ liegt vor?
4. Was ist der Output-Typ?
5. Welche Datenquelle/Funktion berechnet ihn?
6. Wie werden fehlende Werte dargestellt?
7. Welche Ausnahmebehandlung gilt?
8. Welche bekannten Grenzen/Issues gibt es?
9. Wird ein Quality-Filter angewendet?
10. Ist der Placeholder in Prompts/Pipelines referenziert?
Wenn eine dieser Fragen unbeantwortet bleibt, darf der Placeholder nicht als voll standardkonform gelten.
---
## 10. Legacy-, Deprecation- und Abweichungsregeln
### 10.1 Legacy-Markierung
Bestehende problematische Placeholder dürfen als Legacy bestehen bleiben, wenn sie markiert werden durch:
- `deprecated: true/false`
- `known_issues`
- `replacement`
- dokumentierten Semantikhinweis
### 10.2 Deprecation-Pflicht
Wenn ein Placeholder semantisch unpräzise, technisch instabil oder fachlich überholt ist, muss ein Deprecation-Pfad vorgesehen werden.
### 10.3 Abweichungsprozess
Abweichungen von diesem Standard müssen in einem Decision-/Gap-Log dokumentiert werden, mit:
- Begründung,
- Impact,
- geplanter Nachbesserung,
- Verantwortlichkeit.
---
## 11. Validierung und Governance
### 11.1 Pflichtvalidierungen
Für bestehende und neue Placeholder müssen validierbar sein:
- Pflichtfeld-Vollständigkeit,
- gültige Typisierung,
- gültiges Zeitfenster,
- dokumentierte Quelle,
- dokumentiertes Fehlwertverhalten,
- dokumentierte Legacy-/Deprecation-Information.
### 11.2 CI-/Test-Eignung
Die technische Lösung soll so gebaut werden, dass künftige Validierungen automatisierbar sind.
### 11.3 Two-Stage-Artefakte
Interpretierte Outputs aus Basis-Prompts müssen als `interpreted` gekennzeichnet werden und dürfen nicht als rohe Datenplaceholder ausgegeben werden.
---
## 12. Konkrete Arbeitsanweisung an den Coding Agent
Der Coding Agent soll diese Spezifikation als **verbindlichen Standard** behandeln und die Implementierung so auslegen, dass sie:
- den aktuellen Bestand vollständig erfasst,
- für künftige Placeholder wiederverwendbar ist,
- Legacy-Verhalten nicht still bricht,
- technische Lücken transparent markiert,
- und als Grundlage für zukünftige Placeholder-Governance dient.
---
## 13. Akzeptanzkriterien
Die Umsetzung gilt nur dann als erfolgreich, wenn:
1. alle existierenden Placeholder im erweiterten Katalog enthalten sind,
2. der Legacy-Export weiter funktioniert,
3. das Metadatenmodell für neue Placeholder wiederverwendbar ist,
4. Pflichtfelder validiert werden,
5. unresolved Informationen sauber markiert werden,
6. die Dokumentation ausdrücklich festhält, dass der Standard **für alle neuen und zukünftigen Placeholder** gilt.
---
## 14. Schlussregel
Ab Inkrafttreten dieses Dokuments ist ein neuer Placeholder ohne Standardkonformität nur als explizit dokumentierte Ausnahme zulässig. Der Default ist Standardpflicht, nicht informelle Flexibilität.

View File

@ -0,0 +1,5 @@
# Platzhalter-Audits (Archiv)
Enthält Kataloge, Gap-Reports und Reconciliation-Artefakte (2026-03). **Normative Entwicklerregeln** für Platzhalter: `../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md`.
**Entfernte Doppelungen:** `DATA_ARCHITECTURE.md` und `mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` lagen identisch auch hier kanonisch sind jetzt nur noch `../../functional/DATA_ARCHITECTURE.md` bzw. `../../functional/mitai_jinkendo_konzept_diagramme_auswertungen_v2.md`.

View File

@ -0,0 +1,463 @@
# Placeholder-Audit: Executive Summary
**Audit-Datum:** 29. März 2026
**Umfang:** 111 Platzhalter (vollständig)
**Normative Basis:** PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md v1.0.0
**Audit-Methodik:** 4 spezialisierte Agents (Code, Semantik, Zeit/Confidence, Usage)
**Audit-Dauer:** ~590 Sekunden (4 Agents parallel)
---
## Compliance-Übersicht
### Gesamtergebnis: 7% voll normkonform (8/111)
| Compliance-Level | Anzahl | Prozent | Hauptprobleme |
|------------------|--------|---------|---------------|
| **Compliant** | 8 | 7% | Best-Practice-Beispiele (nutrition_avg, weight_aktuell) |
| **Partially Compliant** | 22 | 20% | 1-2 Gaps (meist time_window oder confidence) |
| **Non-Compliant** | 81 | 73% | 3+ kritische Gaps |
---
## Kritische Zahlen
### Systemische Gaps (nach Normative Requirements)
| Gap-Typ | Anzahl | Prozent | Norm-Verstoß |
|---------|---------|---------|--------------|
| **time_window: unknown** | 74 | 67% | §3.4 "Zeitfenster explizit" |
| **Keine Confidence-Logik** | 103 | 93% | §5 "Qualitäts-/Confidence-Logik" |
| **Kein data_layer_module** | 100 | 90% | §7 "Source" vollständig |
| **Keine Mindestdaten definiert** | 99 | 89% | Implizit in §5 |
| **category: Unknown** | 49 | 44% | §7.1 "Pflichtfelder: category" |
| **description: "No description"** | 49 | 44% | §7.1 "Pflichtfelder: description" |
| **Keine source_tables** | 90 | 81% | §7 "Source: source_tables" |
| **metadata_completeness_score: 0** | 111 | 100% | Kein Placeholder produktionsreif |
| **schema_status: draft** | 111 | 100% | §13 Akzeptanzkriterien |
---
## Wichtigste Systemische Schwächen
### 1. Metadaten-Dokumentation fundamental unvollständig
**Problem:**
- 100% der Platzhalter haben `metadata_completeness_score: 0`
- 44% ohne fachliche Kategorie ("Unknown")
- 44% ohne Beschreibung ("No description available")
- Kein einziger Platzhalter hat Status "production"
**Impact:**
- Platzhalter können nicht als stabile API-Verträge genutzt werden
- Prompt-Bibliothek hat keine verlässliche Metadaten-Basis
- Neue Platzhalter haben kein Qualitätsvorbild
**Root Cause:**
- Export-System generiert Struktur, füllt aber nicht alle Felder
- Keine systematische Dokumentationspflicht im Development-Workflow
---
### 2. Zeitfenster-Chaos
**Problem:**
- 67% ohne definiertes Zeitfenster (`time_window: unknown`)
- Code-Inkonsistenzen:
- `activity_summary`: Docs "7d", Code 14d, Metadaten "unknown"
- `weight_trend`: Docs "7d/30d", Code 28d, Metadaten "unknown"
- Namen-Metadaten-Mismatch:
- `zeitraum_7d`, `zeitraum_30d`, `zeitraum_90d` → alle `time_window: unknown`
- `sleep_avg_duration_7d``time_window: unknown`
**Impact:**
- KI kann Zeitfenster nicht interpretieren
- Reports können nicht korrekt zeitlich einordnen
- Vergleiche zwischen Platzhaltern problematisch
**Root Cause:**
- Zeitfenster wird oft im Code definiert, aber nicht in Metadaten übertragen
- Keine automatische Extraktion aus Funktionsnamen/Parametern
---
### 3. Fehlende Confidence-Systeme
**Problem:**
- Nur 8 Platzhalter (7%) haben Confidence-Logik
- 103 ohne jegliche Datenqualitäts-Signale
- Kritisch bei:
- **Trend-Platzhaltern** (slope, delta, change) → keine Warnung bei zu wenig Datenpunkten
- **Score-Platzhaltern** (nutrition_score, activity_score) → keine Reliability-Info
- **Korrelations-Platzhaltern** → keine Min-Data-Thresholds
**Impact:**
- KI kann nicht zwischen "sicher" und "unsicher" unterscheiden
- User erhält keine Warnung bei unreliablen Werten
- Prompt-Logik muss blind vertrauen oder eigene Heuristiken entwickeln
**Root Cause:**
- Nur nutrition_metrics, body_metrics, activity_metrics haben `calculate_confidence()`
- Andere Data-Layer-Module ohne Confidence-Pattern
---
### 4. Unstrukturierte Fehlwertbehandlung
**Problem:**
- 70 Platzhalter mit "nicht verfügbar"-String statt strukturiertem Format
- Verstoß gegen Norm §3.5 "Fehlwerte explizit"
- Legacy-Only: `missing_value_policy.legacy_display: "nicht verfügbar"`
- Keine strukturierten Felder: `available`, `value_raw`, `missing_reason`
**Impact:**
- KI muss String-Parsing betreiben ("nicht verfügbar" vs. "0" vs. "null")
- Keine maschinenlesbare Unterscheidung zwischen Fehlertypen
- Folgeprompts können nicht unterscheiden: Daten fehlen vs. Fehler aufgetreten
**Root Cause:**
- Historisches Legacy-Format aus v1 des Placeholder-Systems
- Kein strukturierter Refactor durchgeführt
---
### 5. 67 Platzhalter noch nicht produktiv eingebunden
**Status:**
- 67 Platzhalter (60%) haben 0 Verwendungen in Prompts/Pipelines/Charts
- **Wichtig:** Dies ist KEIN Technical Debt, sondern erwartbar bei Prompt-Bibliothek im Aufbau
- Viele in Kategorie "Unknown" (41 von 49 Unknown-Platzhaltern ungenutzt)
**Fachliche Klassifizierung (siehe USAGE_ROLE_CLASSIFICATION.md):**
- **30 Platzhalter (45%):** Explizit in Roadmap Phase 0c/1/2 geplant
- Scores (6), Correlations (5), Ability Balance (5), Goals Details (11), Sleep Debt, Plateau Detection, Top Drivers
- **37 Platzhalter (55%):** Fachlich plausibel, noch nicht in Prompts integriert
- Body Deltas, Nutrition Details, Training Quality, Focus Category Weights/Progress, Meta/Convenience
- **0 Platzhalter:** Redundant oder deprecation-würdig
**Interpretation:**
- Prompt-Bibliothek ist in Phase 0b/0c (Goals, Data Architecture)
- Phase 1 (Charts), Phase 2 (Correlations) werden 30+ Platzhalter aktivieren
- Kein Deprecation-Bedarf Integration statt Deletion erforderlich
**Next Steps:**
- Integration-Timeline für 30 geplante Platzhalter (Phase 0c/1/2)
- Prompt-Use-Cases für 10-15 plausible Platzhalter identifizieren
- Nutzungsrate wird von 40% auf 50-60% steigen (organisch)
---
## Größte Risiken für Prompt-Bibliothek und Reporting
### 1. Breaking-Change-Risiko (🔴 HIGH)
**12 produktkritische Platzhalter mit 3-19 Verwendungen:**
| Placeholder | Uses | Risk | Reason |
|-------------|------|------|--------|
| `{{name}}` | 19 | 🔴 EXTREM | In 9 Prompts + 10 Pipelines |
| `{{geschlecht}}` | 14 | 🔴 EXTREM | Gender-specific Logik in 7 Prompts |
| `{{height}}` | 12 | 🔴 EXTREM | BMI-Berechnungen, Body Composition |
| `{{weight_aktuell}}` | 10 | 🔴 HOCH | Numerisch sensitiv, Format-Breaking |
| `{{weight_trend}}` | 10 | 🔴 HOCH | **Code-Docs-Konflikt!** |
| `{{goal_bf_pct}}` | 10 | 🔴 HOCH | Body Composition Goals |
| `{{caliper_summary}}` | 8 | 🟡 MITTEL | Body Fat Summary |
| `{{circ_summary}}` | 8 | 🟡 MITTEL | Circumference Summary |
| `{{goal_weight}}` | 8 | 🟡 MITTEL | Weight Goals |
| `{{protein_ziel_low/high}}` | 7 | 🟡 MITTEL | Nutrition Guidance |
| `{{activity_detail}}` | 4 | 🟡 MITTEL | **Time-Window unklar!** |
**Mitigation:**
- Jede Änderung an diesen 12 erfordert:
1. Identifikation aller betroffenen Prompts/Pipelines
2. Koordinierte Migration über alle Templates
3. Backward-Compatibility-Periode (2-4 Wochen)
4. Full Regression-Tests
---
### 2. Code-Dokumentations-Konflikte (🔴 HIGH)
**Bekannte Inkonsistenzen:**
| Placeholder | Description | Code | Metadaten | Status |
|-------------|-------------|------|-----------|--------|
| `weight_trend` | "7d/30d" | 28d | unknown | 🔴 KONFLIKT |
| `activity_summary` | "7d" | 14d | unknown | 🔴 KONFLIKT |
| `activity_detail` | unklar | 14d (default) | unknown | 🟡 UNKLAR |
**Impact:**
- Kein Single Source of Truth
- KI nutzt evtl. falsche Zeitfenster-Annahmen
- User-Verwirrung bei Zeit-Interpretation
**Mitigation:**
- **P0 Fix:** Code als autoritativ nehmen, Docs/Metadaten aktualisieren
- Automatische Konsistenz-Checks (CI/CD)
---
### 3. Prompt-Fragility (🟡 MEDIUM)
**Problem:**
- Platzhalter ohne Zeitfenster → KI kann Perioden nicht interpretieren
- Fehlende Confidence → KI kann Datenqualität nicht bewerten
- "nicht verfügbar"-Strings → KI muss raten ob Fehler oder kein Wert
**Beispiel-Szenarien:**
```
Prompt: "Analysiere die Gewichtsentwicklung basierend auf {{weight_trend}}"
Problem: KI weiß nicht, ob 7d, 28d oder 90d → falsche Interpretation möglich
Prompt: "Bewerte die Korrelation {{correlation_energy_weight_lag}}"
Problem: Keine Confidence → KI kann nicht warnen "nur 5 Datenpunkte, unreliabel"
```
**Mitigation:**
- Metadata-Enrichment für alle kritischen Platzhalter
- Strukturierte Missing-Value-Policies
---
### 4. Fehlende Produktions-Governance (🟡 MEDIUM)
**Indikatoren:**
- 100% Draft-Status → keine produktionsreifen Platzhalter
- 90% ohne Data-Layer-Module dokumentiert
- Keine Validierungslogik für neue Platzhalter
- Keine klaren Production-Ready-Kriterien
**Langfristige Konsequenzen:**
- Neue Entwickler können nicht erkennen, welche Platzhalter "safe to use" sind
- Keine Guidance für Prompt-Autoren (welche Platzhalter für welchen Use-Case)
- Akkumulation weiterer inkonsistenter Platzhalter
**KEIN Technical Debt:**
- 60% ungenutzte Platzhalter ≠ "tote Codepfade"
- Prompt-Bibliothek ist im Aufbau (Phase 0b/0c/1/2)
- Ungenutzte Platzhalter sind fachlich geplant oder plausibel
---
## Dokumentierte Normkonflikte zwischen Dateien
| Konflikt | Quelle 1 | Quelle 2 | Severity | Resolution |
|----------|----------|----------|----------|------------|
| **Zeitfenster weight_trend** | Description: "7d/30d" | Code: 28d | 🟡 MEDIUM | Code ist autoritativ → Docs aktualisieren |
| **Zeitfenster activity_summary** | Description: "7d" | Code: 14d | 🟡 MEDIUM | Code ist autoritativ → Docs aktualisieren |
| **Neue Platzhalter** | requirements_dev.md: 27 neue P1-P27 | Extended Catalog: 111 existierend | 🟢 INFO | requirements_dev ist Arbeitsdokument, kein Normativ |
| **Bestandszahlen** | Export Spec: 116 | Gap Report/Catalog: 111 | 🟢 INFO | 5 Platzhalter = Metafelder (schema_version, etc.) |
**Rangfolgen-Resolution (aus Audit-Auftrag):**
1. **NORMATIVE.md** (höchste Instanz) - Verbindliche Standardspezifikation
2. **Code** (aktueller Ist-Zustand) - Bei Konflikt Code > Docs
3. **Extended Catalog** (dokumentierter Stand) - Generated Truth
4. **requirements_dev.md** (Arbeitsdokument) - NICHT normativ, nur Planning
---
## Best-Practice-Modelle identifiziert
### Vollständig normkonforme Platzhalter (8):
**1. Nutrition Averages (4):**
- `protein_avg`, `kcal_avg`, `fat_avg`, `carb_avg`
**Warum Best-Practice:**
```
✅ time_window: 30d (explizit)
✅ aggregation: AVG() via nutrition_metrics.py
✅ min_data: 8 (low), 12 (medium), 18 (high)
✅ confidence_logic: calculate_confidence(data_points, 30, 'general')
✅ missing_value_policy: strukturiert (available=false, reason='insufficient_data')
✅ data_layer_module: nutrition_metrics.py
✅ source_tables: ['nutrition_log']
```
**2. Weight Aktuell (1):**
- `weight_aktuell`
**Warum Best-Practice:**
```
✅ time_window: latest
✅ aggregation: Latest entry ORDER BY date DESC LIMIT 1
✅ confidence_logic: high if exists, insufficient if no data
✅ data_layer_module: body_metrics.py
✅ source_tables: ['weight_log']
```
**3. Andere (3):**
- `weight_trend`, `circ_summary`, `age` (verschiedene Best-Practice-Aspekte)
---
## Audit-Methodik-Transparenz
### 4 Spezialisierte Agents (Parallel)
**1. Code-Evidence-Agent** (124s)
- **Aufgabe:** Vollständige Code-Analyse aller Resolver-Funktionen
- **Scope:** `placeholder_resolver.py` (Zeilen 1075-1221), `data_layer/*.py`, `routers/prompts.py`
- **Output:** Technische Herkunft, Source-Tables, Return-Types für alle 111
- **Evidence-Level:** `code_verified` (100%)
**2. Semantic-Contract-Agent** (218s)
- **Aufgabe:** Fachliche Bedeutung aus Dokumentation ableiten
- **Scope:** `DATA_ARCHITECTURE.md`, `mitai_jinkendo_konzept_diagramme_auswertungen_v2.md`, Extended Catalog
- **Output:** Description, Category, Semantic Contract für alle 111
- **Evidence-Level:** `documentation_verified` (56%), `fachlich_abgeleitet` (44%)
**3. Time-Window-Confidence-Agent** (90s)
- **Aufgabe:** Zeitfenster klassifizieren, Confidence-Patterns identifizieren
- **Scope:** Code + Catalog + Normative Spec (erlaubte time_window-Werte)
- **Output:** Zeit-Compliance, Aggregationslogik, Min-Data-Thresholds
- **Evidence-Level:** `code_verified` (75 unklar), `code_inferred` (rest)
**4. Prompt-Usage-Agent** (157s)
- **Aufgabe:** Verwendung in Prompts/Pipelines/Charts analysieren
- **Scope:** `ai_prompts` table (via Catalog), Grep-Suche nach `{{placeholder}}`
- **Output:** Used-By, Criticality, Rename-Risk
- **Evidence-Level:** `catalog_verified` (100%)
**Konsolidierung:**
- Cross-Agent-Validation (4 Perspektiven auf jeden Placeholder)
- Konflikt-Resolution nach Rangfolge (Norm > Code > Catalog > Docs)
- Evidence-Level-Klassifizierung (code_verified > documentation_verified > fachlich_abgeleitet > unclear)
---
## Nächste Kritische Schritte
### P0 (VOR PRODUCTION - BLOCKING)
**1. Zeitfenster-Klassifizierung (74 Platzhalter)**
- **Aufwand:** 8-12 Stunden
- **Methode:**
1. Namen-Analyse (`*_7d`, `*_28d`, etc.) → automatisch
2. Code-Analyse (default days-Parameter) → semi-automatisch
3. Fach-Entscheidung für Unklare (ability_balance, scores) → manuell
- **Deliverable:** Alle `time_window: unknown` → gültige Werte (latest/7d/14d/28d/30d/90d/mixed)
**2. Code-Docs-Konflikte auflösen (3 Platzhalter)**
- **Aufwand:** 2 Stunden
- **Items:**
- `weight_trend`: Description + Metadaten auf "28d" ändern
- `activity_summary`: Description + Metadaten auf "14d" ändern
- `activity_detail`: Default-Zeitfenster dokumentieren
- **Validation:** Automatische Konsistenz-Checks (Code ↔ Catalog)
**3. Kategorie + Beschreibung (49 Platzhalter)**
- **Aufwand:** 4-6 Stunden
- **Methode:** Bulk-Update aus Semantic-Contract-Agent-Report
- **Deliverable:**
- Alle `category: Unknown` → fachliche Kategorien
- Alle `description: "No description available"` → aussagekräftige Beschreibungen
---
### P1 (THIS SPRINT - HIGH PRIORITY)
**4. Confidence-Logik für Trend-/Delta-Platzhalter (11)**
- **Aufwand:** 12-16 Stunden
- **Items:**
- weight_28d_slope, weight_90d_slope, weight_7d_median
- fm_28d_change, lbm_28d_change
- waist_28d_delta, hip_28d_delta, chest_28d_delta, arm_28d_delta, thigh_28d_delta
- vo2max_trend_28d
- **Pattern:** `confidence = calculate_confidence(data_points, time_window_days, 'trend')`
- **Thresholds:** high >= 70%, medium >= 50%, low >= 30% data coverage
**5. Strukturierte Missing-Value-Policy (70 Platzhalter)**
- **Aufwand:** 8-10 Stunden
- **Refactor:**
- Legacy-String "nicht verfügbar" beibehalten (Backward-Compatibility)
- Zusätzlich strukturierte Felder: `available`, `missing_reason`, `value_raw`
- **Deliverable:** Dual-Mode-Support (Legacy + Structured)
**6. Data-Layer-Module dokumentieren (100 Platzhalter)**
- **Aufwand:** 6-8 Stunden
- **Methode:** Code-Trace von Resolver → Data-Layer (aus Code-Evidence-Agent)
- **Deliverable:** Alle `data_layer_module: null` → korrekte Module
---
### P2 (NEXT SPRINT - MEDIUM PRIORITY)
**7. Ungenutzte Platzhalter - Integration planen (67)**
- **Aufwand:** 4-6 Stunden
- **Methode:**
- Produktmanagement-Review: Timeline für 30 geplante Platzhalter (Phase 0c/1/2)
- Technische Review: Prompt-Use-Cases für 37 plausible Platzhalter
- **Deliverable:** Integration-Roadmap, Prompt-Templates (5-10 Quick Wins)
**8. Metadata-Completeness-Score auf >60% (111)**
- **Aufwand:** 10-12 Stunden
- **Methode:** Systematisches Füllen aller Pflichtfelder
- **Target:** Mindestens 60% der Platzhalter mit Score >60
**9. Schema-Status auf production (20-30 Core-Platzhalter)**
- **Aufwand:** 4-6 Stunden
- **Kriterien:**
- Metadata-Completeness >= 80%
- Used-By >= 1
- Keine Known-Issues
- Zeitfenster + Confidence definiert
- **Deliverable:** 20-30 produktionsreife Platzhalter
---
### P3 (LATER - NICE TO HAVE)
**10. Validation-Framework für neue Platzhalter**
- **Aufwand:** 16-20 Stunden
- **Features:**
- Pre-Commit-Hook: Validierung gegen Normative Spec
- CI/CD: Automatische Konsistenz-Checks (Code ↔ Catalog)
- Template-Generator für neue Platzhalter
**11. Migration-Guides für Prompt-Bibliothek**
- **Aufwand:** 8-12 Stunden
- **Content:**
- Best-Practice-Guide (basierend auf nutrition_avg)
- Anti-Patterns (was vermeiden)
- Upgrade-Path für Legacy-Prompts
---
## Geschätzter Gesamt-Remediationsaufwand
| Priority | Aufwand | Timeline | Dependencies |
|----------|---------|----------|--------------|
| **P0** | 14-20h | Week 1 | Keine (sofort startbar) |
| **P1** | 26-34h | Week 2-3 | Nach P0 |
| **P2** | 18-24h | Week 4-5 | Nach P1 |
| **P3** | 24-32h | Later | Nach P2 |
| **TOTAL** | **82-110h** | **4-6 Wochen** | Gestaffelt |
**Mit Team von 2 Entwicklern:** 2-3 Wochen für P0+P1, weitere 1-2 Wochen für P2.
---
## Abschluss-Statement
Der Placeholder-Audit hat **massive systemische Gaps** identifiziert, aber auch einen **klaren Remediation-Pfad** aufgezeigt. Die größten Probleme sind:
1. **Dokumentations-Gaps** (44% ohne Kategorie/Beschreibung)
2. **Zeitfenster-Chaos** (67% ohne definiertes Fenster)
3. **Fehlende Confidence-Systeme** (93% ohne Qualitäts-Signale)
Die **gute Nachricht**: Alle 111 Platzhalter sind im Code implementiert, haben eine saubere Architektur, und 8 dienen bereits als Best-Practice-Modelle. Die Lücken sind primär **dokumentarisch und metadatenbezogen**, nicht funktional.
Mit einem strukturierten P0-P3-Plan (82-110h Aufwand) kann das System in 4-6 Wochen auf **>60% Normkonformität** gebracht werden.
**Empfohlene nächste Schritte:**
1. Review dieses Executive Summary mit Product/Tech Lead
2. P0-Priorisierung bestätigen (Zeitfenster, Code-Docs-Konflikte, Kategorien)
3. Kickoff für P0-Sprint (Ziel: 74 Zeitfenster + 49 Kategorien/Beschreibungen)
---
**Audit durchgeführt von:** Claude Code (Lead Audit Agent)
**Agent-Team:** Code-Evidence, Semantic-Contract, Time-Window-Confidence, Prompt-Usage
**Normative Basis:** PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md v1.0.0
**Vollständige Artefakte:** Siehe `audit-report-2026-03-29/` für Gap-Cluster, Maßnahmenplan, Prüfmatrix

View File

@ -0,0 +1,651 @@
# Gap-Cluster-Bericht: Placeholder-Audit
**Audit-Datum:** 29. März 2026
**Basis:** 111 Platzhalter, 4-Agent-Analyse
**Normative Referenz:** PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md v1.0.0
---
## Übersicht
Dieser Bericht gruppiert die identifizierten Gaps nach **systemischen Clustern**, um gezielte Remediation-Strategien zu ermöglichen.
**Gesamtzahl Gaps:** 481 einzelne Gap-Instanzen über alle 111 Platzhalter
**Gap-Verteilung:**
```
Non-Compliant (81): 324 Gaps (durchschnittlich 4.0 pro Placeholder)
Partially Compliant (22): 88 Gaps (durchschnittlich 4.0 pro Placeholder)
Compliant (8): 0 Gaps
```
---
## GAP-CLUSTER
### CLUSTER 1: Missing Semantic Contract
**Anzahl betroffene Platzhalter:** 49 (44%)
**Severity:** 🔴 CRITICAL
**Norm-Verstoß:** §7.1 Pflichtfelder (description, semantic_contract)
**Sub-Kategorien:**
#### 1.1 No Description Available (49)
Alle 49 haben `description: "No description available"`
**Betroffene Kategorien:**
- **Unknown (49):** Alle 49 Non-Compliant sind in "Unknown"-Kategorie
- Ability Balance (5): coordination, endurance, mental, mobility, strength
- Correlations (5): energy_weight_lag, load_hrv, load_rhr, protein_lbm, sleep_recovery
- Body Deltas (5): arm_28d_delta, chest_28d_delta, hip_28d_delta, thigh_28d_delta, waist_28d_delta (doppelt)
- Goals (16): active_goals_json/md, focus_areas, top_goal_*, top_3_goals_*, goal_weight, goal_bf_pct
- Nutrition (6): energy_deficit_surplus, intake_volatility, nutrition_days, protein_days_in_target, protein_ziel_*
- Training (3): activity_detail, monotony_score, strain_score
- Summaries (3): caliper_summary, circ_summary, weight_90d_slope
- Meta (2): zeitraum_90d, recent_load_balance_3d
- Plateau (1): plateau_detected
- Top Drivers (1): top_drivers
**Remediation:**
- **Bulk-Update möglich:** Semantic-Contract-Agent-Report hat Target-Descriptions für alle 49
- **Aufwand:** 4-6 Stunden (Copy-Paste aus Agent-Report)
- **Priority:** P0 (blocking für Production)
---
#### 1.2 Weak Semantic Contract (25)
Haben Beschreibung, aber schwachen/unvollständigen Semantic Contract
**Beispiele:**
- `bmi`: Description OK, aber Semantic Contract = Description (sollte Formel enthalten)
- `rest_days_count`: Description OK, aber Aggregationslogik fehlt
- `vitals_vo2_max`: Description OK, aber Quelle/Berechnungsmethode fehlt
**Remediation:**
- **Individuelle Überarbeitung:** Semantic Contract mit Formeln/Quellen/Aggregationen anreichern
- **Aufwand:** 3-4 Stunden
- **Priority:** P1
---
### CLUSTER 2: Missing Data Layer Module
**Anzahl betroffene Platzhalter:** 100 (90%)
**Severity:** 🔴 CRITICAL
**Norm-Verstoß:** §7 Source vollständig (data_layer_module erforderlich)
**Analyse:**
- Nur 11 Platzhalter haben `data_layer_module` gesetzt
- 100 haben `data_layer_module: null`
**Root Cause:**
- Export-System extrahiert `data_layer_module` nur, wenn explizit in Code-Kommentaren
- Resolver nutzen oft Helper-Funktionen (`_safe_int`, `_safe_float`) statt direkter Data-Layer-Calls
- `_safe_int` ruft intern Data-Layer auf, aber Mapping ist nicht dokumentiert
**Code-Evidence-Agent Findings:**
- **81 nutzen Helper** (`_safe_int`, `_safe_float`, `_safe_str`, `_safe_json`)
- Diese HABEN Data-Layer-Module, sind aber nicht dokumentiert
- Beispiel: `_safe_int('nutrition_score', pid)` → ruft `data_layer.nutrition_metrics.calculate_nutrition_score()`
- **19 haben direkte Data-Layer-Calls** (z.B. nutrition_avg, weight_aktuell)
- **0 echte Fälle ohne Data-Layer** (alle 111 haben technische Herkunft)
**Remediation:**
- **Code-Trace von Resolvern:** Für jede `_safe_*`-Funktion die gerufene Data-Layer-Funktion identifizieren
- **Bulk-Update:** Mapping aus Code-Evidence-Agent-Report übernehmen
- **Aufwand:** 6-8 Stunden
- **Priority:** P1
---
### CLUSTER 3: Missing Source Tables
**Anzahl betroffene Platzhalter:** 90 (81%)
**Severity:** 🔴 CRITICAL
**Norm-Verstoß:** §7 Source vollständig (source_tables erforderlich)
**Analyse:**
- Nur 21 Platzhalter haben `source_tables` gesetzt
- 90 haben `source_tables: []`
**Root Cause:**
- Identisch zu CLUSTER 2: Export-System extrahiert nicht aus Code
- Source-Tables sind in Data-Layer-SQL-Queries definiert, aber nicht in Resolver-Metadaten
**Code-Evidence-Agent Findings:**
- **Alle 111** haben identifizierbare Source-Tables im Code
- **9 Core-Tables:**
1. `profiles` (4 Platzhalter)
2. `weight_log` (11)
3. `nutrition_log` (12)
4. `activity_log` (14)
5. `sleep_log` (6)
6. `vitals_baseline` (8)
7. `circumference_log` (5)
8. `caliper_log` (5)
9. `goals`, `focus_areas`, `rest_days` (11)
**Remediation:**
- **Automatische Extraktion:** SQL-Query-Parsing in Data-Layer-Modulen
- **Bulk-Update:** Mapping aus Code-Evidence-Agent-Report übernehmen
- **Aufwand:** 6-8 Stunden
- **Priority:** P1
---
### CLUSTER 4: Unknown Time Window
**Anzahl betroffene Platzhalter:** 74 (67%)
**Severity:** 🔴 CRITICAL
**Norm-Verstoß:** §3.4 "Zeitfenster explizit"
**Analyse:**
- 74 haben `time_window: unknown`
- 37 haben definiertes Zeitfenster (latest, 7d, 28d, 30d, 90d, mixed)
**Sub-Kategorien:**
#### 4.1 Name enthält Zeitfenster, aber Metadaten unknown (15)
**KRITISCHER MISMATCH:**
| Placeholder | Name-Hint | Code | Metadaten | Fix |
|-------------|-----------|------|-----------|-----|
| `zeitraum_7d` | 7d | Hardcoded "letzte 7 Tage" | unknown | → 7d |
| `zeitraum_30d` | 30d | Hardcoded "letzte 30 Tage" | unknown | → 30d |
| `zeitraum_90d` | 90d | Hardcoded "letzte 90 Tage" | unknown | → 90d |
| `sleep_avg_duration_7d` | 7d | 7 days | unknown | → 7d |
| `sleep_quality_7d` | 7d | 7 days | unknown | → 7d |
| `training_frequency_7d` | 7d | 7 days (inferred) | unknown | → 7d |
| `proxy_internal_load_7d` | 7d | 7 days | unknown | → 7d |
| `energy_balance_7d` | 7d | 7 days | unknown | → 7d |
| `protein_adequacy_28d` | 28d | 28 days | unknown | → 28d |
| `weight_28d_slope` | 28d | 28 days | 28d | ✓ OK |
| `weight_90d_slope` | 90d | 90 days | 90d | ✓ OK |
| `fm_28d_change` | 28d | 28 days | 28d | ✓ OK |
| `lbm_28d_change` | 28d | 28 days | 28d | ✓ OK |
| `vo2max_trend_28d` | 28d | 28 days | 28d | ✓ OK |
| ... | | | | |
**Remediation:**
- **Automatische Regelextraktion:** Name-Pattern (`*_7d`, `*_28d`, etc.) → time_window
- **Aufwand:** 1 Stunde (automatisch)
- **Priority:** P0
---
#### 4.2 Code enthält Default-Zeitfenster (20)
Code nutzt `days=X` Parameter, aber Metadaten unknown
**Beispiele:**
- `activity_summary`: Code `days=14` (default), Metadaten unknown
- `sleep_avg_duration`: Code `days=7`, Metadaten unknown
- `rest_days_count`: Code `days=30`, Metadaten unknown
**Remediation:**
- **Code-Parameter-Extraktion:** Default-Werte aus Funktionssignaturen lesen
- **Aufwand:** 2-3 Stunden (semi-automatisch)
- **Priority:** P0
---
#### 4.3 Fach-Entscheidung erforderlich (39)
Zeitfenster nicht aus Name/Code ableitbar, fachliche Klassifizierung nötig
**Kategorien:**
**A. Scores (6):**
- `activity_score`, `nutrition_score`, `recovery_score`, `body_progress_score`, `goal_progress_score`, `data_quality_score`
- **Vorschlag:** `custom` oder `mixed` (kombinieren verschiedene Zeitfenster)
**B. Ability Balance (5):**
- `ability_balance_coordination`, `ability_balance_endurance`, `ability_balance_mental`, `ability_balance_mobility`, `ability_balance_strength`
- **Vorschlag:** `28d` (Rolling-Window-Balance über 28 Tage)
**C. Correlations (5):**
- `correlation_energy_weight_lag`, `correlation_load_hrv`, `correlation_load_rhr`, `correlation_protein_lbm`, `correlation_sleep_recovery`
- **Vorschlag:** `28d` (Min-Daten für reliable Korrelationen)
**D. Goals & Focus (16):**
- `active_goals_json`, `focus_areas_weighted_json`, `top_goal_*`, `top_3_goals_*`, `focus_cat_*`
- **Vorschlag:** `latest` (Snapshot) oder `custom` (je nach Progress-Berechnung)
**E. Snapshots (7):**
- `bmi`, `goal_weight`, `goal_bf_pct`, `waist_hip_ratio`, `recomposition_quadrant`, `plateau_detected`, `top_drivers`
- **Vorschlag:** `latest` (Momentaufnahme) oder `custom` (bei berechneten Werten)
**Remediation:**
- **Fach-Review mit Product:** Kategorisierungs-Entscheidungen abstimmen
- **Aufwand:** 4-6 Stunden (Meeting + Dokumentation)
- **Priority:** P0
---
### CLUSTER 5: Missing Confidence Logic
**Anzahl betroffene Platzhalter:** 103 (93%)
**Severity:** 🟡 HIGH
**Norm-Verstoß:** §5 "Qualitäts-/Confidence-Logik"
**Analyse:**
- Nur 8 Platzhalter haben `confidence_logic` implementiert
- 103 haben `confidence_logic: null`
**Implementierte Confidence (8):**
1. `protein_avg`, `kcal_avg`, `fat_avg`, `carb_avg` (nutrition_metrics)
2. `weight_aktuell`, `weight_trend` (body_metrics)
3. `caliper_summary`, `circ_summary` (body_metrics)
**Pattern:**
```python
confidence = calculate_confidence(data_points, time_window_days, metric_type)
Thresholds:
- high: >= 60% data coverage (e.g., >= 18/30 days)
- medium: >= 40% data coverage (e.g., >= 12/30 days)
- low: >= 26% data coverage (e.g., >= 8/30 days)
- insufficient: < 26% data coverage
```
**Fehlende Confidence-Logik besonders kritisch für:**
#### 5.1 Trend-Platzhalter (11)
**Warum kritisch:** Slopes/Trends mit nur 2-3 Datenpunkten sind unreliabel
| Placeholder | Time-Window | Min-Data empfohlen | Confidence-Logik |
|-------------|-------------|-------|------------------|
| `weight_28d_slope` | 28d | 18+ | ❌ Fehlt |
| `weight_90d_slope` | 90d | 60+ | ❌ Fehlt |
| `weight_7d_median` | 7d | 5+ | ❌ Fehlt |
| `fm_28d_change` | 28d | 2 (min), 18+ (ideal) | ❌ Fehlt |
| `lbm_28d_change` | 28d | 2 (min), 18+ (ideal) | ❌ Fehlt |
| `waist_28d_delta` | 28d | 2 (min) | ❌ Fehlt |
| `hip_28d_delta` | 28d | 2 (min) | ❌ Fehlt |
| `chest_28d_delta` | 28d | 2 (min) | ❌ Fehlt |
| `arm_28d_delta` | 28d | 2 (min) | ❌ Fehlt |
| `thigh_28d_delta` | 28d | 2 (min) | ❌ Fehlt |
| `vo2max_trend_28d` | 28d | 4+ | ❌ Fehlt |
**Remediation:**
- **Pattern-Anwendung:** `calculate_confidence()` in alle Delta/Slope-Funktionen einbauen
- **Aufwand:** 12-16 Stunden
- **Priority:** P1
---
#### 5.2 Score-Platzhalter (6)
**Warum kritisch:** Composite Scores sollten Confidence der Subkomponenten reflektieren
| Placeholder | Sub-Components | Confidence-Logik |
|-------------|----------------|------------------|
| `activity_score` | training_volume, frequency, quality | ❌ Fehlt |
| `nutrition_score` | energy_balance, protein_adequacy, macro_consistency | ❌ Fehlt |
| `recovery_score` | sleep_duration, sleep_quality, hrv, rhr | ❌ Fehlt |
| `body_progress_score` | weight_trend, bf_change, lbm_change | ❌ Fehlt |
| `goal_progress_score` | weighted goals progress | ❌ Fehlt |
| `data_quality_score` | data availability per domain | ❌ Fehlt |
**Remediation:**
- **Composite-Confidence:** Min(subcomponent_confidences)
- **Aufwand:** 8-10 Stunden
- **Priority:** P1
---
#### 5.3 Korrelations-Platzhalter (5)
**Warum kritisch:** Korrelationen mit <14 Paaren sind statistisch unreliabel
| Placeholder | Min-Pairs empfohlen | Confidence-Logik |
|-------------|---------------------|------------------|
| `correlation_energy_weight_lag` | 21+ | ❌ Fehlt |
| `correlation_load_hrv` | 21+ | ❌ Fehlt |
| `correlation_load_rhr` | 21+ | ❌ Fehlt |
| `correlation_protein_lbm` | 28+ (mit Training moderiert) | ❌ Fehlt |
| `correlation_sleep_recovery` | 21+ | ❌ Fehlt |
**Remediation:**
- **Correlation-Confidence:** Based on n_pairs (high >= 28, medium >= 21, low >= 14)
- **Aufwand:** 4-6 Stunden
- **Priority:** P1
---
### CLUSTER 6: Unstrukturierte Missing-Value-Policy
**Anzahl betroffene Platzhalter:** 110 (99%)
**Severity:** 🟡 MEDIUM
**Norm-Verstoß:** §3.5 "Fehlwerte explizit"
**Analyse:**
- 110 haben nur `missing_value_policy.legacy_display: "nicht verfügbar"`
- Nur 1 hat strukturierte Policy (weight_aktuell mit available=false, reason='no_data')
**Problem:**
- Legacy-String ist nicht maschinenlesbar
- Keine Unterscheidung zwischen Fehlertypen:
- `no_data`: Keine Daten in Datenbank
- `insufficient_data`: Zu wenig Daten für Berechnung
- `resolver_error`: Technischer Fehler
- `calculation_error`: Mathematischer Fehler (z.B. Division by zero)
**Norm-konformes Format (§3.5):**
```json
{
"available": false,
"value_raw": null,
"missing_reason": "insufficient_data",
"missing_value_policy": {
"legacy_display": "nicht verfügbar",
"structured_null": true,
"reason_codes": ["no_data", "insufficient_data", "resolver_error"]
}
}
```
**Remediation:**
- **Dual-Mode-Ansatz:**
- Legacy-String beibehalten (Backward-Compatibility)
- Zusätzlich strukturierte Felder (`available`, `missing_reason`, `value_raw`)
- **Aufwand:** 8-10 Stunden (Pattern-Anwendung auf alle Resolver)
- **Priority:** P1
---
### CLUSTER 7: Metadata Completeness Score 0
**Anzahl betroffene Platzhalter:** 111 (100%)
**Severity:** 🟡 MEDIUM
**Norm-Verstoß:** Kein direkter Verstoß, aber Indikator für fehlende Pflichtfelder
**Analyse:**
- Alle 111 haben `metadata_completeness_score: 0`
- Score-Berechnung (vermutlich):
```
score = (filled_fields / total_required_fields) * 100
```
- Da Score 0 → vermutlich wurden Pflichtfelder nicht als "gefüllt" erkannt
**Pflichtfelder (aus Norm §7.1):**
1. `key` ✅ (alle gefüllt)
2. `placeholder` ✅ (alle gefüllt)
3. `category` ❌ (49 Unknown)
4. `type` ✅ (alle gefüllt)
5. `description` ❌ (49 "No description")
6. `semantic_contract` ❌ (49 "No description")
7. `unit` ✅ (alle gefüllt)
8. `time_window` ❌ (74 unknown)
9. `output_type` ✅ (alle gefüllt)
10. `value_display` ❌ (111 null)
11. `available` ✅ (alle true)
12. `missing_value_policy` ✅ (alle gefüllt, aber schwach)
13. `source` ❌ (100 data_layer_module null, 90 source_tables [])
14. `version` ✅ (alle 1.0.0)
15. `deprecated` ✅ (alle false)
**Score 0 Root Cause:**
- Vermutlich zählen `null`/`unknown`/`[]`/`"No description"` als "nicht gefüllt"
- → 7 von 15 Pflichtfeldern nicht korrekt gefüllt = 0% Score
**Remediation:**
- **Nach P0+P1 Fixes:** Score sollte automatisch auf 40-60% steigen
- **Zusätzlich:** value_display für alle Platzhalter füllen (aus Beispieldaten)
- **Aufwand:** Inkludiert in anderen Clustern
- **Priority:** P2 (Indikator-Fix, nicht eigenständig)
---
### CLUSTER 8: Schema Status "draft"
**Anzahl betroffene Platzhalter:** 111 (100%)
**Severity:** 🟡 MEDIUM
**Norm-Verstoß:** §13 Akzeptanzkriterien (production-ready Platzhalter fehlen)
**Analyse:**
- Alle 111 haben `schema_status: draft`
- Keine Platzhalter im Status `production`, `beta`, oder `stable`
**Erlaubte Werte (vermutlich):**
- `draft`: Work in Progress
- `beta`: In Testing
- `stable`: Production-Ready, aber noch nicht finalisiert
- `production`: Voll produktionsreif, Breaking Changes verboten
**Kriterien für `production` (vorgeschlagen):**
1. `metadata_completeness_score >= 80`
2. `used_by.prompts.length >= 1 OR used_by.pipelines.length >= 1`
3. `time_window != 'unknown'`
4. `category != 'Unknown'`
5. `description != 'No description available'`
6. `confidence_logic != null OR type == 'atomic' with time_window == 'latest'`
7. `known_issues.length == 0`
**Kandidaten für `production` nach P0+P1 Fixes:**
- Nutrition Averages (4): protein_avg, kcal_avg, fat_avg, carb_avg
- Body Metrics (2): weight_aktuell, weight_trend
- Profil (4): name, age, height, geschlecht
- Summaries (2): caliper_summary, circ_summary
- **Total: 12-15 Kandidaten**
**Remediation:**
- **Automatische Bewertung:** Script zur Berechnung von Production-Eligibility
- **Manuelle Review:** Product/Tech Lead bestätigt Production-Status
- **Aufwand:** 4-6 Stunden
- **Priority:** P2
---
### CLUSTER 9: Ungenutzte Platzhalter (Orphans)
**Anzahl betroffene Platzhalter:** 67 (60%)
**Severity:** 🟢 LOW (aber Technical Debt)
**Norm-Verstoß:** Kein direkter Verstoß, aber Governance-Issue
**Analyse:**
- 67 Platzhalter haben `used_by.prompts: []`, `used_by.pipelines: []`, `used_by.charts: []`
- 0 Verwendungen = potentielle Deprecation-Kandidaten
**Kategorisierung:**
#### 9.1 Geplante Features (Phase 1b/1c) - behalten (30)
- Correlations (5): energy_weight_lag, load_hrv, load_rhr, protein_lbm, sleep_recovery
- Goals Details (11): active_goals_json/md, focus_areas, top_goals
- Ability Balance (5): coordination, endurance, mental, mobility, strength
- Scores (6): activity_score, nutrition_score, recovery_score, body_progress_score, goal_progress_score, data_quality_score
- Plateau (1): plateau_detected
- Top Drivers (1): top_drivers
- Sleep Debt (1): sleep_debt_hours
#### 9.2 Redundant/Obsolet - Deprecation prüfen (15)
- Body Deltas (4): arm_28d_delta, chest_28d_delta, hip_28d_delta, thigh_28d_delta (redundant zu waist_28d_delta Pattern)
- Training Load (3): monotony_score, strain_score, recent_load_balance_3d (noch nicht genutzt)
- Meta (3): zeitraum_90d, datum_heute (System-Platzhalter, wenig Wert)
- Vitals Trends (2): hrv_vs_baseline_pct, rhr_vs_baseline_pct (noch nicht genutzt)
- Sleep Regularity (1): sleep_regularity_proxy (experimentell)
- BMI (1): bmi (berechenbar, redundant)
- Waist-Hip-Ratio (1): waist_hip_ratio (berechenbar, redundant)
#### 9.3 Unklar - Product-Review (22)
- Rest: Weitere Ability Balance, Focus Cat Weights, etc.
**Remediation:**
- **Product-Review:** Welche für Phase 1b/1c/2 geplant? Welche streichen?
- **Deprecation-Strategie:** `deprecated: true`, `replacement: "new_placeholder"`, Sunset-Datum
- **Aufwand:** 4-6 Stunden (Meeting + Dokumentation)
- **Priority:** P2
---
### CLUSTER 10: Export-Inkonsistenzen
**Anzahl betroffene Platzhalter:** 5 (Meta-Gap)
**Severity:** 🟢 INFO
**Norm-Verstoß:** Kein direkter Verstoß, aber Governance-Issue
**Problem:**
- Export Spec sagt: "116 Platzhalter"
- Extended Catalog + Gap Report: "111 Platzhalter"
- Differenz: 5 Platzhalter
**Analyse:**
- Die 5 zusätzlichen sind vermutlich **Metafelder** im Export:
1. `schema_version`
2. `generated_at`
3. `normative_standard`
4. `total_placeholders`
5. `metadata.summary` (oder ähnlich)
**Remediation:**
- **Dokumentation:** Export-Spec klarstellen (111 User-Placeholders + 5 Metafelder)
- **Aufwand:** 30 Minuten
- **Priority:** P3
---
### CLUSTER 11: Code-Dokumentations-Konflikte
**Anzahl betroffene Platzhalter:** 3 (kritische Konflikte)
**Severity:** 🔴 CRITICAL
**Norm-Verstoß:** §2 "Vorrang" (Code > Docs bei Konflikt)
**Konflikte:**
#### 11.1 weight_trend
- **Description:** "Gewichtstrend (7d/30d)"
- **Code:** `get_weight_trend_data(profile_id, days=28)`
- **Metadaten:** `time_window: unknown`
- **Used-By:** 10 Prompts/Pipelines
- **Impact:** 🔴 HIGH (Prompts gehen von 7d/30d aus, erhalten aber 28d)
**Fix:**
```
description: "Gewichtstrend 28d (lineare Regression)"
semantic_contract: "Lineare Regression über 28 Tage, Richtung + Delta"
time_window: "28d"
```
---
#### 11.2 activity_summary
- **Description:** "Aktivitäts-Zusammenfassung (7d)"
- **Code:** `get_activity_summary_data(profile_id, days=14)`
- **Metadaten:** `time_window: unknown`
- **Used-By:** 2 Prompts
- **Impact:** 🟡 MEDIUM (weniger Verwendungen, aber Inkonsistenz)
**Fix:**
```
description: "Aktivitäts-Zusammenfassung 14d"
semantic_contract: "Aggregierte Aktivitäts-Metriken über 14 Tage"
time_window: "14d"
```
---
#### 11.3 activity_detail
- **Description:** "Keine explizite Zeitangabe"
- **Code:** `get_activity_detail_data(profile_id, days=14, limit=20)`
- **Metadaten:** `time_window: unknown`
- **Used-By:** 4 Prompts
- **Impact:** 🟡 MEDIUM
**Fix:**
```
description: "Detaillierte Aktivitäts-Liste 14d (max 20 Einträge)"
semantic_contract: "Sortierte Liste letzter 20 Aktivitäten aus 14-Tage-Fenster"
time_window: "14d"
```
---
**Remediation:**
- **Sofortige Korrektur:** Description, semantic_contract, time_window auf Code-Wahrheit setzen
- **Aufwand:** 1 Stunde
- **Priority:** P0
---
## GAP-CLUSTER ZUSAMMENFASSUNG
| Cluster | Platzhalter | Severity | Priority | Aufwand | Abhängigkeiten |
|---------|-------------|----------|----------|---------|----------------|
| **1. Semantic Contract** | 49 | 🔴 CRITICAL | P0 | 4-6h | Keine |
| **2. Data Layer Module** | 100 | 🔴 CRITICAL | P1 | 6-8h | Cluster 1 |
| **3. Source Tables** | 90 | 🔴 CRITICAL | P1 | 6-8h | Cluster 1 |
| **4. Time Window** | 74 | 🔴 CRITICAL | P0 | 6-10h | Keine |
| **5. Confidence Logic** | 103 | 🟡 HIGH | P1 | 24-32h | Cluster 4 |
| **6. Missing-Value-Policy** | 110 | 🟡 MEDIUM | P1 | 8-10h | Cluster 5 |
| **7. Completeness Score 0** | 111 | 🟡 MEDIUM | P2 | Inkludiert | Nach P0+P1 |
| **8. Schema Status draft** | 111 | 🟡 MEDIUM | P2 | 4-6h | Nach P0+P1 |
| **9. Ungenutzte Platzhalter** | 67 | 🟢 LOW | P2 | 4-6h | Product-Review |
| **10. Export-Inkonsistenzen** | 5 | 🟢 INFO | P3 | 0.5h | Keine |
| **11. Code-Docs-Konflikte** | 3 | 🔴 CRITICAL | P0 | 1h | Keine |
**Gesamt-Remediationsaufwand:** 64-87 Stunden (gestaffelt über P0-P3)
---
## REMEDIATION-PRIORISIERUNG
### P0 (BLOCKING - Week 1)
1. **Cluster 11:** Code-Docs-Konflikte (1h)
2. **Cluster 1:** Semantic Contract (4-6h)
3. **Cluster 4:** Time Window (6-10h)
- **Sub:** Name-Pattern (1h, automatisch)
- **Sub:** Code-Parameter (2-3h, semi-automatisch)
- **Sub:** Fach-Entscheidung (4-6h, manuell mit Product)
**Total P0:** 11-17 Stunden
---
### P1 (HIGH - Week 2-3)
1. **Cluster 2:** Data Layer Module (6-8h)
2. **Cluster 3:** Source Tables (6-8h)
3. **Cluster 5:** Confidence Logic (24-32h)
- **Sub:** Trend-Platzhalter (12-16h)
- **Sub:** Score-Platzhalter (8-10h)
- **Sub:** Korrelations-Platzhalter (4-6h)
4. **Cluster 6:** Missing-Value-Policy (8-10h)
**Total P1:** 44-58 Stunden
---
### P2 (MEDIUM - Week 4-5)
1. **Cluster 7:** Completeness Score (inkludiert in P0+P1)
2. **Cluster 8:** Schema Status Production (4-6h)
3. **Cluster 9:** Ungenutzte Platzhalter Review (4-6h)
**Total P2:** 8-12 Stunden
---
### P3 (LOW - Later)
1. **Cluster 10:** Export-Inkonsistenzen Doku (0.5h)
**Total P3:** 0.5 Stunden
---
**GESAMT:** 63.5-87.5 Stunden über 4-6 Wochen
---
## NÄCHSTE SCHRITTE
1. **Review dieses Gap-Reports** mit Tech Lead
2. **Priorisierungs-Bestätigung:** P0-Liste final absegnen
3. **Kickoff P0-Sprint:**
- Code-Docs-Konflikte sofort fixen (1h)
- Semantic Contract Bulk-Update (4-6h)
- Zeitfenster-Klassifizierung (6-10h)
4. **Nach P0:** P1-Sprint planen (Confidence + Data Layer + Missing-Value)
**Zie
l nach P0+P1:** 60-70% Normkonformität erreicht
---
**Audit-Report erstellt von:** Claude Code (Lead Audit Agent)
**Basis:** 4-Agent-Analyse (Code-Evidence, Semantic-Contract, Time-Window-Confidence, Prompt-Usage)

View File

@ -0,0 +1,983 @@
# Priorisierter Maßnahmenplan: Placeholder-Remediation
**Audit-Datum:** 29. März 2026
**Basis:** Gap-Cluster-Analyse, 111 Platzhalter
**Ziel:** Normkonformität auf 60-70% steigern (aktuell 7%)
---
## ROADMAP-ÜBERSICHT
| Phase | Fokus | Dauer | Aufwand | Ziel-Compliance |
|-------|-------|-------|---------|-----------------|
| **P0** | Blocking Issues | Week 1 | 11-17h | 25-30% |
| **P1** | High Priority Gaps | Week 2-3 | 44-58h | 50-60% |
| **P2** | Medium Priority | Week 4-5 | 8-12h | 65-70% |
| **P3** | Nice-to-Have | Later | 0.5h | 70%+ |
| **TOTAL** | | 4-6 Wochen | 63.5-87.5h | 70%+ |
---
## P0: BLOCKING ISSUES (Week 1)
**Ziel:** Kritische Inkonsistenzen und Pflichtfelder beheben
**Aufwand:** 11-17 Stunden
**Team:** 1-2 Entwickler
**Dependencies:** Keine - sofort startbar
---
### P0.1: Code-Dokumentations-Konflikte (SOFORT)
**Gap-Cluster:** 11
**Betroffene Platzhalter:** 3 (weight_trend, activity_summary, activity_detail)
**Severity:** 🔴 CRITICAL
**Aufwand:** 1 Stunde
**Maßnahmen:**
1. **weight_trend Fix**
```diff
# PLACEHOLDER_CATALOG_EXTENDED.json
{
"key": "weight_trend",
- "description": "Gewichtstrend (7d/30d)",
+ "description": "Gewichtstrend 28d (lineare Regression)",
- "semantic_contract": "Gewichtstrend (7d/30d)",
+ "semantic_contract": "Lineare Regression über 28 Tage, Richtung + Delta in kg",
- "time_window": "unknown",
+ "time_window": "28d",
}
```
2. **activity_summary Fix**
```diff
{
"key": "activity_summary",
- "description": "Aktivitäts-Zusammenfassung (7d)",
+ "description": "Aktivitäts-Zusammenfassung 14d",
- "semantic_contract": "Aktivitäts-Zusammenfassung (7d)",
+ "semantic_contract": "Aggregierte Aktivitäts-Metriken über 14 Tage",
- "time_window": "unknown",
+ "time_window": "14d",
}
```
3. **activity_detail Fix**
```diff
{
"key": "activity_detail",
- "description": "No description available",
+ "description": "Detaillierte Aktivitäts-Liste 14d (max 20 Einträge)",
- "semantic_contract": "No description available",
+ "semantic_contract": "Sortierte Liste letzter 20 Aktivitäten aus 14-Tage-Fenster, DESC nach Datum",
- "time_window": "unknown",
+ "time_window": "14d",
}
```
**Validation:**
- Code-Review: Zeitfenster im Code bestätigen
- Prompt-Review: Alle 10+2+4 betroffenen Prompts/Pipelines prüfen
**Owner:** Backend Lead
**Blocker:** Keine
**Output:** 3 Fixed Platzhalter, Updated Catalog
---
### P0.2: Zeitfenster Name-Pattern (AUTOMATISCH)
**Gap-Cluster:** 4.1
**Betroffene Platzhalter:** 15 (zeitraum_*, sleep_*_7d, training_frequency_7d, etc.)
**Severity:** 🔴 CRITICAL
**Aufwand:** 1 Stunde (automatisches Skript)
**Maßnahmen:**
**Skript: `fix_time_window_from_name.py`**
```python
import json
import re
# Regex-Pattern für Zeitfenster im Namen
patterns = {
r'_7d$|_7d_': '7d',
r'_14d$|_14d_': '14d',
r'_28d$|_28d_': '28d',
r'_30d$|_30d_': '30d',
r'_90d$|_90d_': '90d',
}
catalog = load_catalog()
fixes = []
for placeholder in catalog['placeholders'].values():
key = placeholder['key']
# Überspringe bereits definierte
if placeholder['time_window'] != 'unknown':
continue
# Pattern-Match
for pattern, window in patterns.items():
if re.search(pattern, key):
placeholder['time_window'] = window
fixes.append(f"{key}: unknown → {window}")
break
print(f"Fixed {len(fixes)} placeholders")
save_catalog(catalog)
```
**Erwartete Fixes:**
- `zeitraum_7d`: unknown → 7d
- `zeitraum_30d`: unknown → 30d
- `zeitraum_90d`: unknown → 90d
- `sleep_avg_duration_7d`: unknown → 7d
- `sleep_quality_7d`: unknown → 7d
- `training_frequency_7d`: unknown → 7d
- `proxy_internal_load_7d`: unknown → 7d
- `energy_balance_7d`: unknown → 7d
- `protein_adequacy_28d`: unknown → 28d
- ... (insgesamt ~15)
**Validation:**
- Manuelle Review der Auto-Fixes
- Diff-Check vor Commit
**Owner:** DevOps / Automation
**Blocker:** Keine
**Output:** 15 Fixed Platzhalter
---
### P0.3: Zeitfenster Code-Parameter (SEMI-AUTOMATISCH)
**Gap-Cluster:** 4.2
**Betroffene Platzhalter:** ~20 (activity_summary, sleep_avg_duration, rest_days_count, etc.)
**Severity:** 🔴 CRITICAL
**Aufwand:** 2-3 Stunden
**Maßnahmen:**
1. **Code-Parameter-Extraktion**
- Suche in `placeholder_resolver.py` nach `days=X` Parametern
- Greppen in `data_layer/*.py` nach Default-Werten
```bash
# Beispiel-Grep
grep -n "def get_.*_data.*days=" backend/data_layer/*.py
grep -n "lambda.*days=" backend/placeholder_resolver.py
```
2. **Manuelle Zuordnung**
- Für jeden Treffer: Placeholder Key identifizieren
- Default-Wert → time_window setzen
**Erwartete Fixes:**
- `sleep_avg_duration`: Code `days=7` → time_window: 7d
- `rest_days_count`: Code `days=30` → time_window: 30d
- `nutrition_days`: Code `days=30` → time_window: 30d
- ... (~20 total)
**Validation:**
- Code-Review der Mappings
- Stichproben-Tests
**Owner:** Backend Developer
**Blocker:** P0.2 abgeschlossen
**Output:** 20 Fixed Platzhalter
---
### P0.4: Semantic Contract Bulk-Update
**Gap-Cluster:** 1.1
**Betroffene Platzhalter:** 49 ("No description available")
**Severity:** 🔴 CRITICAL
**Aufwand:** 4-6 Stunden
**Maßnahmen:**
**Input:** Semantic-Contract-Agent-Report (bereits vorhanden)
**Prozess:**
1. Für jeden der 49 Platzhalter:
- `description` aus Agent-Report übernehmen
- `semantic_contract` aus Agent-Report übernehmen
- `category` aus Agent-Report setzen (statt "Unknown")
2. Bulk-Update via JSON-Merge-Skript
3. Manuelle Review der Top-10 kritischsten (nach Usage)
**Beispiel-Update (ability_balance_coordination):**
```diff
{
"key": "ability_balance_coordination",
- "category": "Unknown",
+ "category": "Training",
- "description": "No description available",
+ "description": "Koordinationsfähigkeit-Balance-Anteil (%)",
- "semantic_contract": "No description available",
+ "semantic_contract": "Gewichteter Anteil der Trainings-Last für Koordinations-Fähigkeit über 28-Tage-Fenster",
- "time_window": "unknown",
+ "time_window": "28d",
}
```
**Prioritäts-Reihenfolge (nach Usage):**
1. Goals & Focus (16) - 7 produktkritisch
2. Correlations (5) - experimentell, aber wichtig
3. Body Deltas (5) - Körper-Tracking
4. Nutrition (6) - Ernährungs-Metriken
5. Training (8) - Ability Balance, Load
6. Summaries (3) - circ_summary, caliper_summary
7. Meta (2) - zeitraum_90d
8. Plateau (1) - plateau_detected
9. Top Drivers (1) - top_drivers
**Validation:**
- Review der Top-10 produktkritischsten Platzhalter manuell
- Stichprobe 10% der restlichen
- Automated Consistency Check (keine "No description" mehr)
**Owner:** Backend Lead + Product (für fachliche Review)
**Blocker:** Keine
**Output:** 49 Fixed Platzhalter, alle mit Kategorie + Description
---
### P0.5: Zeitfenster Fach-Entscheidung
**Gap-Cluster:** 4.3
**Betroffene Platzhalter:** ~39 (Scores, Ability Balance, Correlations, Goals, Snapshots)
**Severity:** 🔴 CRITICAL
**Aufwand:** 4-6 Stunden (Meeting + Dokumentation)
**Maßnahmen:**
**Phase 1: Kategorisierung (2h)**
- Product + Tech Meeting
- Entscheidung pro Kategorie:
| Kategorie | Platzhalter | Vorschlag | Rationale |
|-----------|-------------|-----------|-----------|
| **Scores (6)** | activity_score, nutrition_score, recovery_score, body_progress_score, goal_progress_score, data_quality_score | `custom` oder `mixed` | Kombinieren verschiedene Zeitfenster |
| **Ability Balance (5)** | coordination, endurance, mental, mobility, strength | `28d` | Rolling-Window über 28 Tage |
| **Correlations (5)** | energy_weight_lag, load_hrv, load_rhr, protein_lbm, sleep_recovery | `28d` | Min-Daten für Reliability |
| **Goals & Focus (16)** | active_goals_json, focus_areas_*, top_goal_*, top_3_goals_*, focus_cat_* | `latest` (Snapshot) oder `custom` | Je nach Progress-Berechnung |
| **Snapshots (7)** | bmi, goal_weight, goal_bf_pct, waist_hip_ratio, recomposition_quadrant, plateau_detected, top_drivers | `latest` oder `custom` | Momentaufnahme vs. Berechnet |
**Phase 2: Dokumentation (2h)**
- Entscheidungen in Decision-Log festhalten
- time_window-Werte in Catalog setzen
- Semantic Contracts anpassen (Zeit-Bezug aufnehmen)
**Phase 3: Validation (1h)**
- Review mit Stakeholdern
- Prompt-Autoren informieren (Breaking Change bei "custom" → spezifisches Fenster)
**Owner:** Product Manager + Tech Lead
**Blocker:** P0.4 Semantic Contract abgeschlossen
**Output:** Decision-Log, 39 Fixed Platzhalter
---
### P0 SUMMARY
**Gesamt-Aufwand:** 11-17 Stunden
**Deliverables:**
- 3 Code-Docs-Konflikte gelöst
- 74 time_window: unknown → definierte Werte
- 49 description/category: Unknown/No description → vollständig
- **Total Fixed:** 77 Platzhalter (15+20+39+3 unique)
**Impact:**
- **Compliance:** 7% → 25-30%
- **Blocked Gaps:** time_window, description, category, code-conflicts
**Next:** P1 kann starten (Confidence, Data Layer, Source Tables)
---
## P1: HIGH PRIORITY (Week 2-3)
**Ziel:** Technische Tiefe hinzufügen (Confidence, Data Layer, Source Tables)
**Aufwand:** 44-58 Stunden
**Team:** 2-3 Entwickler
**Dependencies:** P0 abgeschlossen
---
### P1.1: Confidence-Logik für Trend-Platzhalter
**Gap-Cluster:** 5.1
**Betroffene Platzhalter:** 11 (weight_*_slope, *_28d_delta, vo2max_trend_28d)
**Severity:** 🟡 HIGH
**Aufwand:** 12-16 Stunden
**Maßnahmen:**
**Pattern:**
```python
# In data_layer/body_metrics.py
def calculate_weight_28d_slope(profile_id: int, conn):
"""Gewichtstrend über 28 Tage mit Confidence."""
# 1. Daten holen
rows = fetch_weight_data(profile_id, days=28, conn=conn)
# 2. Confidence berechnen
confidence = calculate_confidence(
data_points=len(rows),
time_window_days=28,
metric_type='trend'
)
if confidence == 'insufficient':
return {
'slope': None,
'confidence': 'insufficient',
'data_points': len(rows),
'min_required': 8 # 28% coverage
}
# 3. Slope berechnen (nur wenn sufficient)
slope = linear_regression_slope(rows)
return {
'slope': slope,
'confidence': confidence, # high/medium/low
'data_points': len(rows),
'r_squared': calculate_r_squared(rows, slope)
}
```
**Thresholds (trend-specific):**
```python
def calculate_confidence(data_points, time_window_days, metric_type):
coverage = data_points / time_window_days
if metric_type == 'trend':
# Strengere Thresholds für Trends
if coverage >= 0.70: # >= 70%
return 'high'
elif coverage >= 0.50: # >= 50%
return 'medium'
elif coverage >= 0.30: # >= 30%
return 'low'
else:
return 'insufficient'
# ... andere Typen
```
**Zu implementieren für:**
1. `weight_28d_slope` (28d, min 8, ideal 20)
2. `weight_90d_slope` (90d, min 27, ideal 63)
3. `weight_7d_median` (7d, min 3, ideal 5)
4. `fm_28d_change` (28d, min 2, ideal 18)
5. `lbm_28d_change` (28d, min 2, ideal 18)
6. `waist_28d_delta` (28d, min 2, ideal 18)
7. `hip_28d_delta` (28d, min 2, ideal 18)
8. `chest_28d_delta` (28d, min 2, ideal 18)
9. `arm_28d_delta` (28d, min 2, ideal 18)
10. `thigh_28d_delta` (28d, min 2, ideal 18)
11. `vo2max_trend_28d` (28d, min 4, ideal 18)
**Testing:**
- Unit-Tests für calculate_confidence() mit verschiedenen Coverages
- Integration-Tests mit real data (0%, 30%, 50%, 70%, 100% coverage)
- Regression-Tests (alte Werte ohne Confidence bleiben gleich)
**Owner:** Backend Developer (Senior)
**Blocker:** P0 time_window abgeschlossen
**Output:** 11 Platzhalter mit Confidence-Logik
---
### P1.2: Confidence-Logik für Score-Platzhalter
**Gap-Cluster:** 5.2
**Betroffene Platzhalter:** 6 (activity_score, nutrition_score, recovery_score, etc.)
**Severity:** 🟡 HIGH
**Aufwand:** 8-10 Stunden
**Maßnahmen:**
**Composite-Confidence-Pattern:**
```python
def calculate_nutrition_score(profile_id: int, conn):
"""Nutrition Score mit Composite Confidence."""
# 1. Sub-Scores berechnen (mit eigenen Confidences)
energy_balance = calculate_energy_balance_7d(profile_id, conn)
protein_adequacy = calculate_protein_adequacy_28d(profile_id, conn)
macro_consistency = calculate_macro_consistency_score(profile_id, conn)
# 2. Composite Confidence = MIN(sub-confidences)
confidence_levels = ['high', 'medium', 'low', 'insufficient']
confidences = [
energy_balance['confidence'],
protein_adequacy['confidence'],
macro_consistency['confidence']
]
# Min-Confidence
min_confidence = min(confidences, key=lambda c: confidence_levels.index(c))
if min_confidence == 'insufficient':
return {
'score': None,
'confidence': 'insufficient',
'sub_scores': {...},
'note': 'Mindestens eine Komponente hat insufficient data'
}
# 3. Score berechnen (gewichtet)
score = (
energy_balance['score'] * 0.4 +
protein_adequacy['score'] * 0.4 +
macro_consistency['score'] * 0.2
)
return {
'score': round(score),
'confidence': min_confidence,
'sub_scores': {
'energy_balance': energy_balance,
'protein_adequacy': protein_adequacy,
'macro_consistency': macro_consistency
}
}
```
**Zu implementieren für:**
1. `activity_score` (training_volume + frequency + quality)
2. `nutrition_score` (energy_balance + protein_adequacy + macro_consistency)
3. `recovery_score` (sleep_duration + sleep_quality + hrv + rhr)
4. `body_progress_score` (weight_trend + bf_change + lbm_change)
5. `goal_progress_score` (weighted goals progress)
6. `data_quality_score` (data availability per domain)
**Testing:**
- Scenario-Tests: alle Sub-Scores high → Composite high
- Scenario-Tests: eine Sub-Score insufficient → Composite insufficient
- Edge-Cases: missing Sub-Components
**Owner:** Backend Developer (Mid/Senior)
**Blocker:** P1.1 Trend-Confidence abgeschlossen (Pattern etabliert)
**Output:** 6 Platzhalter mit Composite Confidence
---
### P1.3: Confidence-Logik für Korrelations-Platzhalter
**Gap-Cluster:** 5.3
**Betroffene Platzhalter:** 5 (correlation_*)
**Severity:** 🟡 HIGH
**Aufwand:** 4-6 Stunden
**Maßnahmen:**
**Correlation-Confidence-Pattern:**
```python
def calculate_lag_correlation(profile_id, metric_a, metric_b, lag_days=[0,3,7,14], conn):
"""Lag-Korrelation mit Pair-basiertem Confidence."""
results = []
for lag in lag_days:
pairs = fetch_paired_data(profile_id, metric_a, metric_b, lag, conn)
n_pairs = len(pairs)
# Confidence basierend auf Anzahl Paare
if n_pairs >= 28:
confidence = 'high'
elif n_pairs >= 21:
confidence = 'medium'
elif n_pairs >= 14:
confidence = 'low'
else:
confidence = 'insufficient'
if confidence == 'insufficient':
correlation = None
else:
correlation = pearson_correlation(pairs)
results.append({
'lag_days': lag,
'correlation': correlation,
'n_pairs': n_pairs,
'confidence': confidence,
'note': 'explorativ, nicht kausal'
})
return {
'correlations': results,
'overall_confidence': min([r['confidence'] for r in results])
}
```
**Zu implementieren für:**
1. `correlation_energy_weight_lag` (min 21 Paare)
2. `correlation_load_hrv` (min 21 Paare)
3. `correlation_load_rhr` (min 21 Paare)
4. `correlation_protein_lbm` (min 28 Paare, Training moderiert)
5. `correlation_sleep_recovery` (min 21 Paare)
**Testing:**
- Synthetic Data: 10, 14, 21, 28, 35 Paare → Confidence-Check
- Real Data: Validate gegen tatsächliche Daten-Dichte
**Owner:** Backend Developer (Senior, Statistik-Know-how)
**Blocker:** P1.1 Confidence-Pattern etabliert
**Output:** 5 Platzhalter mit Correlation-Confidence
---
### P1.4: Data-Layer-Module dokumentieren
**Gap-Cluster:** 2
**Betroffene Platzhalter:** 100 (data_layer_module: null)
**Severity:** 🔴 CRITICAL
**Aufwand:** 6-8 Stunden
**Maßnahmen:**
**Input:** Code-Evidence-Agent-Report (technische Herkunft für alle 111)
**Prozess:**
1. **Mapping-Extraktion:**
- Für jede `_safe_*`-Funktion: Gerufene Data-Layer-Funktion identifizieren
- Mapping: Placeholder Key → Data-Layer-Module
2. **Bulk-Update-Skript:**
```python
# Mapping aus Code-Evidence-Report
data_layer_mapping = {
'goal_progress_score': 'data_layer.scores',
'body_progress_score': 'data_layer.body_metrics',
'nutrition_score': 'data_layer.nutrition_metrics',
'activity_score': 'data_layer.activity_metrics',
'recovery_score': 'data_layer.recovery_metrics',
# ... 95 more
}
# Catalog Update
for key, module in data_layer_mapping.items():
catalog['placeholders'][key]['source']['data_layer_module'] = module
```
3. **Validation:**
- Automated Check: Alle 111 haben data_layer_module gesetzt
- Stichprobe 10%: Manuell im Code validieren
**Owner:** DevOps / Backend Lead
**Blocker:** Code-Evidence-Agent-Report finalisiert (✓)
**Output:** 100 Fixed Platzhalter
---
### P1.5: Source-Tables dokumentieren
**Gap-Cluster:** 3
**Betroffene Platzhalter:** 90 (source_tables: [])
**Severity:** 🔴 CRITICAL
**Aufwand:** 6-8 Stunden
**Maßnahmen:**
**Identisch zu P1.4:**
- Mapping aus Code-Evidence-Report übernehmen
- Bulk-Update-Skript
- Validation
**Mapping-Beispiel:**
```python
source_tables_mapping = {
'weight_aktuell': ['weight_log'],
'weight_trend': ['weight_log'],
'kf_aktuell': ['caliper_log'],
'bmi': ['weight_log', 'profiles'],
'nutrition_score': ['nutrition_log'],
'activity_score': ['activity_log'],
'recovery_score': ['sleep_log', 'vitals_baseline', 'rest_days'],
# ... 83 more
}
```
**Owner:** DevOps / Backend Lead
**Blocker:** Code-Evidence-Agent-Report finalisiert (✓)
**Output:** 90 Fixed Platzhalter
---
### P1.6: Strukturierte Missing-Value-Policy
**Gap-Cluster:** 6
**Betroffene Platzhalter:** 110 (nur legacy_display)
**Severity:** 🟡 MEDIUM
**Aufwand:** 8-10 Stunden
**Maßnahmen:**
**Dual-Mode-Ansatz:**
```python
# Alte Resolver-Return (Backward-Compatible)
def get_weight_trend(profile_id, days=28, conn=None):
data = get_weight_trend_data(profile_id, days, conn)
if data['confidence'] == 'insufficient':
# Legacy: String-Return
return "nicht verfügbar"
return f"{data['direction']} {data['delta']} kg"
# Neue Structured-Return (Parallel)
def get_weight_trend_structured(profile_id, days=28, conn=None):
data = get_weight_trend_data(profile_id, days, conn)
if data['confidence'] == 'insufficient':
return {
'available': False,
'value_raw': None,
'value_display': "nicht verfügbar",
'missing_reason': 'insufficient_data',
'missing_value_policy': {
'legacy_display': "nicht verfügbar",
'structured_null': True,
'reason_codes': ['no_data', 'insufficient_data', 'resolver_error']
},
'metadata': {
'data_points': data['data_points'],
'min_required': data['min_required'],
'time_window': '28d'
}
}
return {
'available': True,
'value_raw': data['slope'],
'value_display': f"{data['direction']} {data['delta']} kg",
'confidence': data['confidence'],
'metadata': {
'data_points': data['data_points'],
'r_squared': data['r_squared'],
'time_window': '28d'
}
}
```
**Migration-Strategie:**
1. **Phase 1:** Neue `*_structured()`-Funktionen neben alten (Parallel)
2. **Phase 2:** Extended Export nutzt structured
3. **Phase 3:** Legacy-Export bleibt unverändert (Backward-Compat)
4. **Phase 4 (Later):** Deprecate alte Funktionen nach 6-12 Monaten
**Zu implementieren für:**
- Alle 110 Platzhalter (außer weight_aktuell, der bereits structured ist)
**Testing:**
- Legacy-Tests: Alte Funktionen bleiben unverändert
- Structured-Tests: Neue Funktionen returnieren korrekte Struktur
- Export-Tests: Extended Export nutzt structured
**Owner:** Backend Team (2 Entwickler)
**Blocker:** P1.1-P1.3 Confidence abgeschlossen
**Output:** 110 Platzhalter mit Dual-Mode-Support
---
### P1 SUMMARY
**Gesamt-Aufwand:** 44-58 Stunden
**Deliverables:**
- 11 Trend-Platzhalter mit Confidence
- 6 Score-Platzhalter mit Composite Confidence
- 5 Correlation-Platzhalter mit Pair-Confidence
- 100 Platzhalter mit data_layer_module
- 90 Platzhalter mit source_tables
- 110 Platzhalter mit Structured Missing-Value-Policy
- **Total Fixed:** 103 unique Platzhalter (alle außer 8 bereits conforme)
**Impact:**
- **Compliance:** 25-30% → 50-60%
- **Blocked Gaps:** Confidence, Data Layer, Source Tables, Missing-Value-Policy
**Next:** P2 Production-Ready + Deprecation
---
## P2: MEDIUM PRIORITY (Week 4-5)
**Ziel:** Production-Status und Deprecation-Strategie
**Aufwand:** 8-12 Stunden
**Team:** Product + Tech Lead
**Dependencies:** P1 abgeschlossen
---
### P2.1: Schema-Status auf Production (Top 20)
**Gap-Cluster:** 8
**Betroffene Platzhalter:** 20-30 Kandidaten
**Severity:** 🟡 MEDIUM
**Aufwand:** 4-6 Stunden
**Maßnahmen:**
**Kriterien für `schema_status: production`:**
1. `metadata_completeness_score >= 80`
2. `used_by.prompts.length >= 1 OR used_by.pipelines.length >= 1`
3. `time_window != 'unknown'`
4. `category != 'Unknown'`
5. `description != 'No description available'`
6. `confidence_logic != null OR (type == 'atomic' AND time_window == 'latest')`
7. `known_issues.length == 0`
**Prozess:**
1. **Automated Eligibility-Check:**
```python
def is_production_ready(placeholder):
checks = [
placeholder['metadata_completeness_score'] >= 80,
len(placeholder['used_by']['prompts']) >= 1 or
len(placeholder['used_by']['pipelines']) >= 1,
placeholder['time_window'] != 'unknown',
placeholder['category'] != 'Unknown',
placeholder['description'] != 'No description available',
placeholder['confidence_logic'] is not None or
(placeholder['type'] == 'atomic' and placeholder['time_window'] == 'latest'),
len(placeholder['known_issues']) == 0
]
return all(checks)
```
2. **Kandidaten-Liste generieren**
3. **Manuelle Review** (Product + Tech Lead)
- Sind diese wirklich production-ready?
- Fehlt noch etwas?
4. **Schema-Status-Update**
**Erwartete Kandidaten (12-15):**
- Nutrition Averages (4): protein_avg, kcal_avg, fat_avg, carb_avg
- Body Metrics (3): weight_aktuell, weight_trend, kf_aktuell
- Profil (4): name, age, height, geschlecht
- Summaries (2): caliper_summary, circ_summary
- Goals (2-3): goal_weight, goal_bf_pct, top_goal_name
**Owner:** Tech Lead + Product Manager
**Blocker:** P1 abgeschlossen (Confidence, Data Layer)
**Output:** 12-15 Platzhalter mit `schema_status: production`
---
### P2.2: Ungenutzte Platzhalter - Integration planen
**Gap-Cluster:** 9 (REINTERPRETIERT: Nicht Deprecation, sondern Integration)
**Betroffene Platzhalter:** 67 (ungenutzt)
**Severity:** 🟡 MEDIUM (Prompt-Bibliothek Vollständigkeit)
**Aufwand:** 4-6 Stunden
**Maßnahmen:**
**Neue Klassifizierung (siehe USAGE_ROLE_CLASSIFICATION.md):**
1. **unused_but_planned (30)** - Explizit in Roadmap Phase 0c/1/2
- Scores (6), Correlations (5), Ability Balance (5), Goals Details (11), etc.
- **Action:** Timeline bestätigen, Prototyping-Prompts erstellen
2. **unused_but_plausible (37)** - Fachlich sinnvoll, noch nicht in Prompts
- Body Deltas, Nutrition Details, Training Quality, Focus Category, Meta
- **Action:** Prompt-Use-Cases identifizieren, Templates für Quick Wins
3. **redundant_or_duplicate (0)** - Keine!
- Alle 67 haben fachliche Berechtigung
**Prozess:**
1. **Meeting (2h):** Product + Tech Review aller 67
- Gruppe A (30 geplant): Timeline bestätigen (Phase 0c/1/2)
- Gruppe B (37 plausibel): Prompt-Use-Cases identifizieren (10-15 Quick Wins)
2. **Dokumentation (1h):** Integration-Roadmap, Prompt-Kandidaten-Liste
3. **Implementation (1-2h):** Prompt-Templates erstellen (5-10 Quick Wins)
4. **Communication (1h):** Prompt-Autoren: "Neue Platzhalter verfügbar"
**Integration-Beispiel (statt Deprecation):**
```json
{
"key": "arm_28d_delta",
"usage_role": "unused_but_plausible",
"integration_priority": "medium",
"prompt_use_cases": [
"Fortschritts-Analyse spezifischer Körperteile",
"Asymmetrie-Erkennung (linker vs. rechter Arm)",
"Trainingsplan-Effektivität (Armtraining Tracking)"
],
"example_prompt_template": "Deine Armumfänge haben sich in den letzten 28 Tagen um {{arm_28d_delta}}cm verändert. Analyse: ..."
}
```
**Owner:** Product Manager + Tech Lead
**Blocker:** P1 abgeschlossen
**Output:** Integration-Roadmap, Prompt-Templates (5-10), Nutzungsrate +10-20%
---
### P2 SUMMARY
**Gesamt-Aufwand:** 8-12 Stunden
**Deliverables:**
- 12-15 Production-Ready Platzhalter
- Integration-Roadmap für 30 geplante Platzhalter (Phase 0c/1/2)
- Prompt-Templates für 5-10 Quick Wins (ungenutzte Platzhalter aktivieren)
**Impact:**
- **Compliance:** 50-60% → 65-70%
- **Governance:** Production-Pipeline etabliert, Placeholder-Integration vorangetrieben
- **Nutzungsrate:** 40% → 50-60% (organisch durch Integration)
**Next:** P3 (Nice-to-Have)
---
## P3: NICE-TO-HAVE (Later)
**Ziel:** Dokumentations-Feinschliff
**Aufwand:** 0.5 Stunden
**Team:** Tech Writer / DevOps
**Dependencies:** P2 abgeschlossen
---
### P3.1: Export-Inkonsistenzen Dokumentation
**Gap-Cluster:** 10
**Betroffene:** Dokumentation
**Severity:** 🟢 INFO
**Aufwand:** 0.5 Stunden
**Maßnahmen:**
**Export Spec Update:**
```diff
# PLACEHOLDER_EXPORT_SPEC.md
- **Total Placeholders:** 116
+ **User Placeholders:** 111
+ **Meta Fields:** 5 (schema_version, generated_at, normative_standard, total_placeholders, metadata.summary)
+ **Total Export Entries:** 116
```
**Owner:** Tech Writer
**Blocker:** Keine
**Output:** Updated Export Spec
---
## ERFOLGS-METRIKEN
### Nach P0 (Week 1)
- ✅ **Compliance:** 7% → 25-30%
- ✅ **time_window: unknown:** 74 → 0
- ✅ **category: Unknown:** 49 → 0
- ✅ **description: No description:** 49 → 0
- ✅ **Code-Docs-Konflikte:** 3 → 0
### Nach P1 (Week 3)
- ✅ **Compliance:** 25-30% → 50-60%
- ✅ **Confidence-Logik:** 8 → 28 (Trend + Score + Correlation)
- ✅ **data_layer_module:** 11 → 111
- ✅ **source_tables:** 21 → 111
- ✅ **Structured Missing-Value:** 1 → 111
### Nach P2 (Week 5)
- ✅ **Compliance:** 50-60% → 65-70%
- ✅ **schema_status: production:** 0 → 12-15
- ✅ **deprecated:** 0 → 15-20
- ✅ **Technical Debt:** Reduziert
### Nach P3
- ✅ **Compliance:** 65-70% → 70%+
- ✅ **Dokumentation:** Vollständig konsistent
---
## RISIKEN & MITIGATION
| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|--------|--------------------|----|-----------|
| **P0 Zeitfenster-Fach-Entscheidung dauert länger** | MEDIUM | MEDIUM | Vorbereitete Kategorisierung, klare Optionen, Decision-Meeting zeitig ansetzen |
| **P1 Confidence-Implementierung komplexer als geschätzt** | MEDIUM | HIGH | Pattern aus nutrition_avg wiederverwenden, Senior Dev assignen |
| **Breaking Changes durch Zeitfenster-Fixes** | LOW | HIGH | Code ist autoritativ, Docs passen sich an → kein Breaking Change |
| **Prompt-Autoren akzeptieren Deprecations nicht** | LOW | MEDIUM | Klare Communication, Replacement-Guides, Grace Period (3 Monate) |
---
## KOMMUNIKATIONS-PLAN
### Week 1 (P0 Kickoff)
- **Email:** Alle Stakeholder über Audit-Ergebnisse informieren
- **Meeting:** P0-Prioritäten mit Tech Lead abstimmen
- **Kickoff:** Development Team P0-Tasks zuweisen
### Week 2 (P1 Kickoff)
- **Standup:** Daily Updates zu Confidence-Implementierung
- **Review:** Mid-Sprint Review nach P1.1-P1.3
### Week 3 (P1 Abschluss)
- **Demo:** P1-Ergebnisse dem Team zeigen
- **Docs:** Updated Catalog veröffentlichen
### Week 4-5 (P2)
- **Meeting:** Production-Readiness-Review
- **Communication:** Deprecation-Plan an Prompt-Autoren
### Week 6 (Abschluss)
- **Retrospektive:** Lessons Learned
- **Documentation:** Finale Compliance-Metrics veröffentlichen
---
## TOOLING & AUTOMATION
### Skripte (entwickeln während P0-P1)
1. `fix_time_window_from_name.py` - Automatische Name-Pattern-Fixes
2. `extract_code_parameters.py` - Code-Parameter → time_window
3. `bulk_update_catalog.py` - JSON-Merge für Bulk-Updates
4. `validate_compliance.py` - Automatische Compliance-Checks
5. `check_production_ready.py` - Production-Eligibility-Check
### CI/CD-Integration (P2-P3)
- Pre-Commit-Hook: Validate neue Platzhalter gegen Normative Spec
- CI: Consistency-Checks (Code ↔ Catalog)
- CD: Automated Catalog-Deployment
---
## ABSCHLUSS-KRITERIEN
**P0 erfolgreich wenn:**
- Alle 3 Code-Docs-Konflikte gelöst
- Alle 74 time_window: unknown → definiert
- Alle 49 No description → vollständig
- Automated Tests grün
**P1 erfolgreich wenn:**
- Mindestens 20 Platzhalter mit Confidence-Logik
- Alle 100 data_layer_module gesetzt
- Alle 90 source_tables gesetzt
- Structured Missing-Value für alle 110 (Dual-Mode)
**P2 erfolgreich wenn:**
- Mindestens 12 Platzhalter `schema_status: production`
- Mindestens 15 Platzhalter `deprecated: true`
- Decision-Log veröffentlicht
**Gesamt-Projekt erfolgreich wenn:**
- **Compliance >= 65%**
- **0 Code-Docs-Konflikte**
- **0 time_window: unknown**
- **12+ Production-Ready Platzhalter**
---
**Maßnahmenplan erstellt von:** Claude Code (Lead Audit Agent)
**Basis:** Gap-Cluster-Analyse, 4-Agent-Evidence
**Genehmigung:** Pending (Review mit Tech Lead + Product Manager)

View File

@ -0,0 +1,459 @@
# Offene Entscheidungen: Placeholder-Audit
**Audit-Datum:** 29. März 2026
**Status:** Pending Product/Tech Review
**Entscheidungsträger:** Product Manager + Tech Lead
---
## ENTSCHEIDUNGSPFLICHTIGE PUNKTE
Diese Punkte können nicht rein aus Code und Fachlogik gelöst werden und benötigen eine **Produkt-/Fachentscheidung**.
---
## KATEGORIE 1: Zeitfenster-Klassifizierung (BLOCKING P0)
### E1.1: Scores - Zeitfenster definieren
**Betroffene Platzhalter:** 6
- `activity_score`
- `nutrition_score`
- `recovery_score`
- `body_progress_score`
- `goal_progress_score`
- `data_quality_score`
**Problem:**
Scores kombinieren verschiedene Zeitfenster-Metriken. Welches Zeitfenster soll dokumentiert werden?
**Optionen:**
| Option | Beschreibung | Pro | Contra |
|--------|--------------|-----|--------|
| **A: `custom`** | Scores haben keinen festen Zeitrahmen | Technisch korrekt | Unspezifisch für User |
| **B: `mixed`** | Scores kombinieren verschiedene Zeitfenster explizit | Kommuniziert Komplexität | Ähnlich unklar wie `custom` |
| **C: Dominantes Zeitfenster** | Z.B. `28d` für nutrition_score (da Hauptkomponenten 28d) | Einfach zu verstehen | Vereinfachung, evtl. irreführend |
| **D: Zeitfenster pro Sub-Score dokumentieren** | Semantic Contract listet alle Sub-Zeitfenster | Vollständig transparent | Komplex für einfache Use-Cases |
**Empfehlung Audit-Agent:**
- **Option D** für `semantic_contract` (vollständige Transparenz)
- **Option B** für `time_window` Metadatum (signalisiert Komplexität)
**Beispiel (nutrition_score):**
```json
{
"key": "nutrition_score",
"time_window": "mixed",
"semantic_contract": "Composite Score aus: energy_balance (7d), protein_adequacy (28d), macro_consistency (28d). Gewichtung: 40%/40%/20%"
}
```
**Entscheidung benötigt bis:** P0 Week 1 (Zeitfenster-Klassifizierung)
**Impact:** MEDIUM (betrifft Prompt-Interpretation)
**Entscheider:** Product Manager
---
### E1.2: Ability Balance - Zeitfenster definieren
**Betroffene Platzhalter:** 5
- `ability_balance_coordination`
- `ability_balance_endurance`
- `ability_balance_mental`
- `ability_balance_mobility`
- `ability_balance_strength`
**Problem:**
Code implementiert noch keine Ability Balance-Berechnung. Welches Zeitfenster ist fachlich sinnvoll?
**Optionen:**
| Option | Beschreibung | Pro | Contra |
|--------|--------------|-----|--------|
| **A: `28d`** | Rolling-Window über 28 Tage | Konsistent mit anderen Trend-Metriken | Evtl. zu lang für Balance-Shifts |
| **B: `14d`** | Rolling-Window über 14 Tage | Schnellere Reaktion auf Training-Changes | Weniger stabil bei Lücken |
| **C: `latest`** | Snapshot der aktuellen Balance | Einfach | Keine Trend-Info |
**Empfehlung Audit-Agent:**
- **Option A (`28d`)** - Konsistent mit body_metrics, ausreichend stabil
**Rationale:**
- Ability Balance = Verteilung der Trainings-Last über Fähigkeiten
- 28 Tage = ~4 Wochen = typischer Mikrozyklus
- Matching mit anderen Tracking-Metriken
**Entscheidung benötigt bis:** P0 Week 1
**Impact:** MEDIUM (betrifft zukünftige Implementierung)
**Entscheider:** Product Manager + Training-Experte
---
### E1.3: Correlations - Zeitfenster definieren
**Betroffene Platzhalter:** 5
- `correlation_energy_weight_lag`
- `correlation_load_hrv`
- `correlation_load_rhr`
- `correlation_protein_lbm`
- `correlation_sleep_recovery`
**Problem:**
Korrelationen brauchen Mindestdaten. Welches Zeitfenster ist statistisch sinnvoll?
**Optionen:**
| Option | Beschreibung | Pro | Contra |
|--------|--------------|-----|--------|
| **A: `28d`** | Min 21-28 Paare für Reliability | Statistisch robust | Reagiert langsam auf Änderungen |
| **B: `custom`** | Variabel je nach Datenverfügbarkeit | Flexibel | Unklar für User |
| **C: Im Semantic Contract spezifizieren** | Z.B. "28d min, 90d ideal" | Transparent | Komplex |
**Empfehlung Audit-Agent:**
- **Option A (`28d`)** für `time_window`
- **Option C** zusätzlich in `semantic_contract`
**Beispiel:**
```json
{
"key": "correlation_energy_weight_lag",
"time_window": "28d",
"semantic_contract": "Lag-Korrelation (0d/3d/7d/14d) zwischen Energiebilanz und Gewicht. Min 21 Paare (28d) für Reliability, 90d ideal. Explorativ, nicht kausal."
}
```
**Entscheidung benötigt bis:** P0 Week 1
**Impact:** MEDIUM (betrifft Statistik-Interpretation)
**Entscheider:** Tech Lead (Statistik-Know-how)
---
### E1.4: Goals & Focus - Zeitfenster definieren
**Betroffene Platzhalter:** 16
- `active_goals_json`, `active_goals_md`
- `focus_areas_weighted_json`, `focus_areas_weighted_md`, `focus_area_weights_json`
- `top_goal_name`, `top_goal_progress_pct`, `top_goal_status`
- `top_3_goals_behind_schedule`, `top_3_goals_on_track`, `top_3_focus_areas`
- `focus_cat_körper_progress`, `focus_cat_ernährung_progress`, `focus_cat_aktivität_progress`, `focus_cat_recovery_progress`, `focus_cat_vitalwerte_progress`, `focus_cat_mental_progress`, `focus_cat_lebensstil_progress` (jeweils + weight)
**Problem:**
Goals sind Snapshots, aber Progress wird über Zeit berechnet. Wie klassifizieren?
**Optionen:**
| Option | Beschreibung | Pro | Contra |
|--------|--------------|-----|--------|
| **A: `latest`** | Aktueller Stand der Goals | Technisch korrekt (Snapshot) | Impliziert keine Zeit-Dimension |
| **B: `custom`** | Progress-Berechnung ist zeitabhängig | Signalisiert Komplexität | Unklar für Snapshot-Werte |
| **C: Differenzierung** | `active_goals_json`: `latest`, Progress-Felder: `custom` | Präzise | 16 individuelle Entscheidungen |
**Empfehlung Audit-Agent:**
- **Option C** (Differenzierung):
- **Snapshot-Platzhalter** (active_goals_json, focus_area_weights_json, top_goal_name): `latest`
- **Progress-Platzhalter** (top_goal_progress_pct, focus_cat_*_progress): `custom` (da verschiedene Ziele verschiedene Zeiträume haben)
**Entscheidung benötigt bis:** P0 Week 1
**Impact:** LOW (primär dokumentarisch)
**Entscheider:** Product Manager
---
### E1.5: Snapshots - Zeitfenster definieren
**Betroffene Platzhalter:** 7
- `bmi`
- `goal_weight`, `goal_bf_pct`
- `waist_hip_ratio`
- `recomposition_quadrant`
- `plateau_detected`
- `top_drivers`
**Problem:**
Einige sind echte Snapshots (bmi, goal_weight), andere berechnete Werte über Zeit (recomposition_quadrant, plateau_detected).
**Optionen:**
| Option | Beschreibung | Pro | Contra |
|--------|--------------|-----|--------|
| **A: Alle `latest`** | Vereinfachung | Einfach | Ungenau für berechnete |
| **B: Differenzierung** | bmi/goal_weight: `latest`, recomposition_quadrant/plateau_detected: `28d` | Präzise | 7 individuelle Entscheidungen |
**Empfehlung Audit-Agent:**
- **Option B**:
- **Echte Snapshots** (bmi, goal_weight, goal_bf_pct, waist_hip_ratio): `latest`
- **Berechnete über Zeit** (recomposition_quadrant, plateau_detected, top_drivers): `28d` oder `custom`
**Entscheidung benötigt bis:** P0 Week 1
**Impact:** LOW
**Entscheider:** Tech Lead
---
## KATEGORIE 2: Integration ungenutzter Platzhalter (P2)
### E2.1: Ungenutzte Platzhalter - Roadmap-Priorisierung und Integration-Timeline
**Betroffene Platzhalter:** 67 (ungenutzt)
**Kontext:**
67 Platzhalter (60%) haben 0 Verwendungen. **WICHTIG:** Dies ist kein Deprecation-Bedarf, sondern Integration-Planung.
**Fachliche Klassifizierung (siehe USAGE_ROLE_CLASSIFICATION.md):**
- **30 Platzhalter (45%):** Explizit in Roadmap Phase 0c/1/2 geplant
- **37 Platzhalter (55%):** Fachlich plausibel, noch nicht in Prompts integriert
- **0 Platzhalter:** Redundant oder deprecation-würdig
**Neue Fragestellung:**
Nicht "Behalten oder Deprecaten?", sondern "WANN und WIE integrieren?"
---
**Sub-Entscheidungen:**
#### E2.1.1: Geplante Platzhalter (30) - Integration-Timeline bestätigen
**Platzhalter-Gruppen:**
- **Scores (6):** activity_score, nutrition_score, recovery_score, body_progress_score, goal_progress_score, data_quality_score
- **Roadmap:** Phase 0c (Multi-Layer Architecture) + Phase 1 (Charts)
- **Correlations (5):** energy_weight_lag, load_hrv, load_rhr, protein_lbm, sleep_recovery
- **Roadmap:** Phase 2 (Engagement - Korrelationen)
- **Ability Balance (5):** coordination, endurance, mental, mobility, strength
- **Roadmap:** Phase 1 (Charts - Training Balance)
- **Goals Details (11):** active_goals_json/md, focus_areas_weighted, top_3_goals_*, focus_cat_*_progress
- **Roadmap:** Phase 0b/0c (Backend DONE, Prompt-Integration ausstehend)
- **Sleep/Plateau/Drivers (3):** sleep_debt, plateau_detected, top_drivers
- **Roadmap:** Phase 1/2 (Diagnostics)
**Frage:** Timeline bestätigen oder anpassen?
**Optionen:**
- **A: Roadmap wie geplant** - Phase 0c/1/2 (nächste 6-12 Wochen)
- **B: Priorisierung anpassen** - Quick Wins zuerst (z.B. Goals Details sofort)
- **C: Prototyping** - 5-10 Beispiel-Prompts erstellen (Wert demonstrieren)
**Empfehlung:** C → B → A (Prototyping zeigt Wert, dann Priorisierung, dann Roadmap)
---
#### E2.1.2: Plausible Platzhalter (37) - Prompt-Use-Cases identifizieren
**Platzhalter-Gruppen:**
- **Body Deltas (5):** arm_28d_delta, chest_28d_delta, hip_28d_delta, thigh_28d_delta, waist_28d_delta
- **Nutrition Details (6):** energy_deficit_surplus, intake_volatility, nutrition_days, protein_days_in_target, protein_ziel_low/high
- **Training Quality/Load (3):** monotony_score, strain_score, rest_day_compliance
- **Focus Category Weights/Progress (14):** focus_cat_*_weight, focus_cat_*_progress
- **Meta/Convenience (9):** bmi, waist_hip_ratio, datum_heute, zeitraum_*
**Frage:** Für welche Use-Cases Prompt-Templates erstellen?
**Optionen:**
- **A: Alle 37 dokumentieren** - Vollständig, aber aufwändig
- **B: Quick Wins (10-15)** - Offensichtliche Use-Cases zuerst
- **C: Warten bis Bedarf** - Nur auf Anfrage aktivieren
**Empfehlung:** B (10-15 Quick Wins: Body Deltas für Fortschritts-Prompts, Nutrition Details für Coaching-Prompts, Focus Category für Dashboard-Widgets)
---
**Entscheidung benötigt bis:** P2 Week 4
**Impact:** MEDIUM (Prompt-Bibliothek Vollständigkeit)
**Entscheider:** Product Manager + Tech Lead
**Methode:** Integration-Planungs-Meeting (4-6h, siehe Maßnahmenplan P2.2 REVIDIERT)
---
## KATEGORIE 3: Production-Readiness-Kriterien (P2)
### E3.1: Welche Platzhalter sind Production-Ready?
**Problem:**
Aktuell haben alle 111 `schema_status: draft`. Welche Kriterien für `production`?
**Vorgeschlagene Kriterien (aus Maßnahmenplan):**
1. `metadata_completeness_score >= 80`
2. `used_by.prompts.length >= 1 OR used_by.pipelines.length >= 1`
3. `time_window != 'unknown'`
4. `category != 'Unknown'`
5. `description != 'No description available'`
6. `confidence_logic != null OR (type == 'atomic' AND time_window == 'latest')`
7. `known_issues.length == 0`
**Frage:** Sind diese Kriterien zu streng/zu locker?
**Optionen:**
- **A: Wie vorgeschlagen** - Balanced
- **B: Strengere Kriterien** - Z.B. `used_by >= 3`, `completeness_score >= 90`
- **C: Lockerere Kriterien** - Z.B. `completeness_score >= 60`, keine Usage-Requirement
**Empfehlung:** A (wie vorgeschlagen)
**Zusatzfrage:** Soll es Zwischen-Status geben (`beta`, `stable`)?
**Optionen:**
- **A: Nur draft/production** - Einfach
- **B: draft/beta/stable/production** - Differenzierter
**Empfehlung:** B (mehr Nuancen ermöglichen schrittweise Freigabe)
**Entscheidung benötigt bis:** P2 Week 4
**Impact:** MEDIUM (Governance)
**Entscheider:** Tech Lead + Product Manager
---
## KATEGORIE 4: Neue Platzhalter (FUTURE)
### E4.1: Requirements Dev vs. Extended Catalog - Wie priorisieren?
**Problem:**
- **requirements_dev.md** schlägt 27 neue Platzhalter vor (P1-P27)
- **Extended Catalog** hat bereits 111
- Viele der "neuen" sind evtl. schon abgedeckt
**Frage:** Sollen die 27 P1-P27 implementiert werden, oder sind sie durch bestehende abgedeckt?
**Beispiele:**
- **P1: `goal_summary_json`** - Abgedeckt durch `active_goals_json` + `active_goals_md`?
- **P2: `focus_area_summary_json`** - Abgedeckt durch `focus_areas_weighted_json`?
- **P5: `domain_availability_json`** - Neu, nicht vorhanden
- **P12: `body_change_summary_json`** - Teilweise durch `caliper_summary`, `circ_summary`
**Empfehlung:**
- **Mapping-Exercise:** Neue vs. Bestehende
- **Gap-Analyse:** Welche sind echte Lücken?
- **Priorisierung:** Nur echte Gaps implementieren
**Entscheidung benötigt bis:** Nach P2 (Phase 1b Planning)
**Impact:** HIGH (Feature-Roadmap)
**Entscheider:** Product Manager + Tech Lead
---
## KATEGORIE 5: Governance-Prozesse (POST-AUDIT)
### E5.1: Wie werden neue Platzhalter künftig validiert?
**Problem:**
Aktuell gibt es keine systematische Validierung neuer Platzhalter gegen Normative Spec.
**Frage:** Soll ein Pre-Commit-Hook/CI-Check eingeführt werden?
**Optionen:**
- **A: Manuelle Reviews** - Flexibel, aber fehleranfällig
- **B: Automated Checks** - Robust, aber Aufwand für Setup
- **C: Hybrid** - Automated Checks + manuelle Review für komplexe Fälle
**Empfehlung:** C (Hybrid)
**Entscheidung benötigt bis:** Nach P2 (Tooling-Phase)
**Impact:** HIGH (langfristige Qualität)
**Entscheider:** Tech Lead
---
### E5.2: Wer ist Owner für Placeholder-Governance?
**Problem:**
Aktuell kein klarer Owner für Placeholder-Lifecycle.
**Frage:** Soll es eine dedizierte Rolle geben?
**Optionen:**
- **A: Tech Lead** - Technische Verantwortung
- **B: Product Manager** - Fachliche Verantwortung
- **C: Shared (beide)** - Consensus-basiert
- **D: Dedizierte Rolle "Data-Contract-Owner"** - Spezialisiert
**Empfehlung:** C (Shared) für jetzt, D langfristig
**Entscheidung benötigt bis:** Nach P2
**Impact:** MEDIUM (Governance)
**Entscheider:** Management
---
## KATEGORIE 6: Technische Schulden (POST-P2)
### E6.1: Legacy-String "nicht verfügbar" - Wann abschalten?
**Problem:**
P1.6 führt Dual-Mode ein (Legacy + Structured). Wann soll Legacy deprecated werden?
**Optionen:**
- **A: Nach 3 Monaten** - Schnell
- **B: Nach 6 Monaten** - Standard
- **C: Nach 12 Monaten** - Konservativ
- **D: Nie (dauerhaft Dual-Mode)** - Ewig Backward-Compatible
**Empfehlung:** B (6 Monate) mit klarer Migration-Guidance
**Entscheidung benötigt bis:** P1 Abschluss (Sunset-Datum festlegen)
**Impact:** MEDIUM (Breaking Change für Prompts)
**Entscheider:** Tech Lead + Prompt-Autoren
---
## ZUSAMMENFASSUNG OFFENER ENTSCHEIDUNGEN
| ID | Kategorie | Entscheidung | Dringlichkeit | Entscheider |
|----|-----------|--------------|---------------|-------------|
| **E1.1** | Zeitfenster Scores | custom/mixed/dominant? | 🔴 P0 Week 1 | Product |
| **E1.2** | Zeitfenster Ability Balance | 28d/14d/latest? | 🔴 P0 Week 1 | Product + Training-Expert |
| **E1.3** | Zeitfenster Correlations | 28d mit Min-Data? | 🔴 P0 Week 1 | Tech Lead |
| **E1.4** | Zeitfenster Goals & Focus | latest/custom/differenziert? | 🔴 P0 Week 1 | Product |
| **E1.5** | Zeitfenster Snapshots | latest/differenziert? | 🔴 P0 Week 1 | Tech Lead |
| **E2.1** | Integration-Timeline | 67 ungenutzte integrieren | 🟡 P2 Week 4 | Product |
| **E3.1** | Production-Kriterien | draft/beta/stable/production? | 🟡 P2 Week 4 | Tech + Product |
| **E4.1** | Neue Platzhalter P1-P27 | Welche implementieren? | 🟢 Post-P2 | Product |
| **E5.1** | Validierungs-Prozess | Automated/Manual/Hybrid? | 🟢 Post-P2 | Tech Lead |
| **E5.2** | Governance-Owner | Wer verantwortet Placeholders? | 🟢 Post-P2 | Management |
| **E6.1** | Legacy-Sunset | Wann "nicht verfügbar" abschalten? | 🟡 P1 Abschluss | Tech Lead |
**Legende:**
- 🔴 BLOCKING (P0) - Entscheidung innerhalb Week 1 benötigt
- 🟡 HIGH (P1-P2) - Entscheidung innerhalb Week 4-5 benötigt
- 🟢 MEDIUM (Post-P2) - Kann warten
---
## EMPFOHLENER ENTSCHEIDUNGS-PROZESS
### Week 1 (P0 Kickoff)
1. **Meeting (2h):** Product + Tech Lead + Training-Expert
2. **Agenda:**
- E1.1-E1.5: Alle Zeitfenster-Entscheidungen
- Decision-Log erstellen
3. **Output:** Entschiedene Zeitfenster für alle 39 unkla
ren Platzhalter
### Week 4 (P2)
1. **Meeting (2h):** Product + Tech Lead
2. **Agenda:**
- E2.1: Deprecation-Review (67 ungenutzte)
- E3.1: Production-Kriterien
3. **Output:** Deprecation-Liste, Production-Kriterien finalisiert
### Post-P2
1. **Meeting (1h):** Management + Tech + Product
2. **Agenda:**
- E4.1: Neue Platzhalter-Roadmap
- E5.1-E5.2: Governance-Prozesse
- E6.1: Legacy-Sunset-Datum
3. **Output:** Langfristige Placeholder-Governance etabliert
---
## NÄCHSTE SCHRITTE
1. **Dieses Dokument reviewen** mit Product Manager
2. **P0-Meeting ansetzen** (Week 1) für Zeitfenster-Entscheidungen
3. **Decision-Log starten** (Living Document)
4. **Nach Entscheidungen:** Maßnahmenplan P0 final anpassen
---
**Offene Entscheidungen dokumentiert von:** Claude Code (Lead Audit Agent)
**Review benötigt von:** Product Manager + Tech Lead
**Deadline kritische Entscheidungen:** P0 Week 1 (innerhalb 1 Woche)

View File

@ -0,0 +1,351 @@
# Quellenprotokoll: Placeholder-Audit
**Audit-Datum:** 29. März 2026
**Audit-Dauer:** ~590 Sekunden (4 Agents parallel)
**Methodik:** 4 spezialisierte Agents mit Cross-Validation
---
## 1. GELESENE DATEIEN
### 1.1 Normative und Spezifikations-Dokumente (HÖCHSTE PRIORITÄT)
| Datei | Pfad | Zeilen | Priorität | Nutzung | Authoritative |
|-------|------|--------|-----------|---------|---------------|
| **NORMATIVE.md** | `.claude/docs/audit/platzhalter/PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md` | 387 | 1 (Höchste) | Verbindliche Standardspezifikation, alle Norm-Referenzen | ✅ JA |
| **EXPORT_SPEC.md** | `.claude/docs/audit/platzhalter/PLACEHOLDER_EXPORT_SPEC.md` | 24 | 2 | API-Endpoint-Spezifikation, Export-Struktur | ✅ JA |
| **GAP_REPORT.md** | `.claude/docs/audit/platzhalter/PLACEHOLDER_GAP_REPORT.md` | 67+ | 3 | Initial Gap-Zahlen (74 unknown, 100 data_layer fehlt) | ⚠️ TEILWEISE |
| **CATALOG.json** | `.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.json` | 64817 Tokens | 4 | Ist-Zustand alle 111 Platzhalter (vollständig) | ✅ JA |
| **CATALOG.md** | `.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.md` | 148+ | 4 | Human-readable Catalog (Kategorien, Übersicht) | ⚠️ TEILWEISE |
---
### 1.2 Fachliche Dokumentation
| Datei | Pfad | Zeilen | Nutzung | Authoritative |
|-------|------|--------|---------|---------------|
| **DATA_ARCHITECTURE.md** | `.claude/docs/audit/platzhalter/DATA_ARCHITECTURE.md` | 300+ (Teil) | Fachliche Datenarchitektur, Domänen-Entitäten, Berechnungslogiken | ⚠️ FACHLICH |
| **requirements_dev.md** | `.claude/docs/audit/platzhalter/placeholder_requirements_for_development.md` | 704 | Entwickler-Arbeitsdokument, P1-P27 neue Platzhalter-Vorschläge | ❌ NICHT normativ |
| **konzept_v2.md** | `.claude/docs/audit/platzhalter/mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` | 2086 | Chart-Spezifikationen (K1-K5, E1-E5, A1-A8, C1-C7, S1-S3) | ⚠️ FACHLICH |
---
### 1.3 Code-Dateien (Code-Evidence-Agent)
| Datei | Pfad | Zeilen | Nutzung | Authoritative |
|-------|------|--------|---------|---------------|
| **placeholder_resolver.py** | `backend/placeholder_resolver.py` | ~1300 | PLACEHOLDER_MAP (Zeilen 1075-1221), Helper-Funktionen (_safe_*) | ✅ JA (Code) |
| **body_metrics.py** | `backend/data_layer/body_metrics.py` | 831 | Weight, BF%, Circumferences, Trends, Deltas | ✅ JA (Code) |
| **nutrition_metrics.py** | `backend/data_layer/nutrition_metrics.py` | 1093 | Nutrition Averages, Energy Balance, Protein Adequacy | ✅ JA (Code) |
| **activity_metrics.py** | `backend/data_layer/activity_metrics.py` | 906 | Training Volume, Quality, Load Monitoring, Ability Balance | ✅ JA (Code) |
| **recovery_metrics.py** | `backend/data_layer/recovery_metrics.py` | 879 | Sleep Metrics, HRV/RHR Baselines, Recovery Scoring | ✅ JA (Code) |
| **scores.py** | `backend/data_layer/scores.py` | 584 | Focus Weights, Goal Progress, Category Scores | ✅ JA (Code) |
| **correlations.py** | `backend/data_layer/correlations.py` | 504 | Lag Correlations, Plateau Detection, Top Drivers | ✅ JA (Code) |
| **prompts.py (Router)** | `backend/routers/prompts.py` | ~2246 | Export-Endpoints (Zeilen 370-467: extended export) | ✅ JA (Code) |
| **goal_utils.py** | `backend/goal_utils.py` | Teilweise | Goal-Helper-Funktionen | ✅ JA (Code) |
---
### 1.4 Zusätzliche Projektdokumente
| Datei | Pfad | Nutzung | Authoritative |
|-------|------|---------|---------------|
| **CLAUDE.md** | `.claude/CLAUDE.md` | Projekt-Kontext, Tech-Stack, Versions-Historie | ⚠️ KONTEXT |
| **ARCHITECTURE.md** | `.claude/rules/ARCHITECTURE.md` | Architektur-Regeln, Versionskontrolle | ⚠️ KONTEXT |
| **CODING_RULES.md** | `.claude/rules/CODING_RULES.md` | Coding-Standards | ⚠️ KONTEXT |
| **LESSONS_LEARNED.md** | `.claude/rules/LESSONS_LEARNED.md` | Bekannte Fehler-Patterns | ⚠️ KONTEXT |
---
## 2. RANGFOLGE DER QUELLEN (BEI KONFLIKTEN)
Bei Widersprüchen zwischen Quellen gilt folgende Priorität:
| Rang | Quelle | Regel | Beispiel-Konflikt |
|------|--------|-------|-------------------|
| **1** | **Normative Spec** | Verbindliche Standardspezifikation | time_window-Werte: Nur latest/7d/14d/28d/30d/90d/custom/mixed/unknown erlaubt |
| **2** | **Code (Ist-Zustand)** | Bei Konflikt Docs vs. Code → Code ist autoritativ | `weight_trend`: Docs "7d/30d", Code 28d → **Code gewinnt** |
| **3** | **Extended Catalog** | Generated Truth (Ist-Zustand dokumentiert) | Bestandszahlen: 111 Platzhalter |
| **4** | **Export Spec / Gap Report** | Technische Spezifikation/Diagnose | Export-Format, Gap-Zahlen |
| **5** | **DATA_ARCHITECTURE.md** | Fachliche Referenz | Semantic Contracts, Berechnungslogiken |
| **6** | **konzept_v2.md** | Chart-/Feature-Spezifikationen | Diagramm-Anforderungen |
| **7** | **requirements_dev.md** | **NICHT NORMATIV** - Arbeitsdokument | P1-P27 Vorschläge (nicht bindend) |
| **8** | **Projekt-Context** | Hintergrund-Information | CLAUDE.md, ARCHITECTURE.md |
**Wichtig:** `requirements_dev.md` ist **KEIN normativer Standard**, sondern ein Entwickler-Arbeitsdokument für Planung!
---
## 3. MAẞGEBLICHE AUSSAGEN PRO QUELLE
### 3.1 NORMATIVE.md (Verbindliche Standard-Instanz)
**Maßgebliche Aussagen:**
- §3.4: **"Zeitfenster explizit"** → Pflicht für alle Platzhalter
- §3.5: **"Fehlwerte explizit"** → Strukturierte Felder statt nur String
- §5: **"Qualitäts-/Confidence-Logik"** → Confidence für Trends/Scores
- §7.1: **Pflichtfelder:** key, placeholder, category, type, description, semantic_contract, unit, time_window, output_type, value_display, available, missing_value_policy, source, version, deprecated
- §7.2-7.4: **Erlaubte Werte** für type, time_window, output_type
**Verwendung im Audit:**
- Compliance-Bewertung (compliant/partially_compliant/non_compliant)
- Gap-Identifikation (Verstoß gegen §X)
- Remediation-Priorisierung (Norm-Verstöße = P0)
---
### 3.2 Code (placeholder_resolver.py + data_layer/*)
**Maßgebliche Aussagen:**
- **Zeitfenster im Code:**
- `weight_trend`: `days=28` (Zeile ~1084)
- `activity_summary`: `days=14` (Zeile ~1102)
- `sleep_avg_duration`: `days=7` (Zeile ~1107)
- **Resolver-Mappings:** 111 Platzhalter vollständig implementiert (Zeilen 1075-1221)
- **Helper-Funktionen:** `_safe_int`, `_safe_float`, `_safe_str`, `_safe_json` (Zeilen 406-602)
- **Data-Layer-Calls:** Code-Evidence-Agent hat vollständige Traces
- **Confidence-Logik:** Nur in nutrition_metrics, body_metrics, activity_metrics implementiert
**Verwendung im Audit:**
- **Autoritativ bei Konflikten** (Code > Docs)
- Technische Herkunft (data_layer_module, source_tables)
- Zeitfenster-Extraktion (Default-Parameter)
---
### 3.3 Extended Catalog (PLACEHOLDER_CATALOG_EXTENDED.json)
**Maßgebliche Aussagen:**
- **111 Platzhalter** (vollständiger Bestand)
- **Kategorien-Verteilung:**
- 49 Unknown (44%)
- 11 Körper, 9 Training, 8 Ernährung, 8 Focus Areas, 7 Schlaf/Erholung, 6 Vitalwerte, 6 Scores, 4 Profil, 3 Zeitraum
- **time_window: unknown:** 74 (67%)
- **metadata_completeness_score: 0** für alle 111
- **schema_status: draft** für alle 111
**Verwendung im Audit:**
- Bestandsaufnahme (Was existiert?)
- Ist-Zustand-Baseline
- Gap-Zahlen-Validierung
---
### 3.4 DATA_ARCHITECTURE.md (Fachliche Ableitung)
**Maßgebliche Aussagen:**
- **Domänen-Struktur:** Nutzer & Zielsetzung, Körper, Training, Lifestyle, Auswertungen
- **Berechnungslogiken:**
- BMI = weight / (height_m ^ 2)
- Protein-Ziel = weight × 1.6-2.2 g/kg
- Energy-Balance = intake - (TDEE + training_kcal)
- **Zeitfenster-Heuristiken:**
- Trends: 7d/28d/90d
- Scores: mixed (verschiedene Sub-Komponenten)
- Correlations: 28d (Min-Daten für Reliability)
**Verwendung im Audit:**
- Semantic Contracts ableiten (wo Code unklar)
- Fachliche Kategorien bestimmen
- Zeitfenster-Empfehlungen (bei Code-Unklarheit)
---
### 3.5 requirements_dev.md (NICHT AUTORITATIV)
**Status:** Arbeitsdokument, **KEINE** normative Instanz
**Maßgebliche Aussagen (mit Vorsicht):**
- Vorschläge für 27 neue Platzhalter (P1-P27)
- Governance-Regeln (G1-G7) - **sollten** in Normative Spec überführt werden
**Verwendung im Audit:**
- **Referenz für geplante Features** (nicht für Ist-Zustand)
- **Konflikte mit Extended Catalog:** requirements_dev ist NICHT autoritativ
- Beispiel-Konflikt: requirements_dev nennt P1 `goal_summary_json` als "neu nötig", aber Extended Catalog hat bereits `active_goals_json` → **Catalog gewinnt**
---
## 4. WIDERSPRÜCHE ZWISCHEN QUELLEN
### 4.1 Dokumentierte Konflikte
| Konflikt | Quelle 1 | Quelle 2 | Resolution | Audit-Entscheidung |
|----------|----------|----------|------------|---------------------|
| **weight_trend Zeitfenster** | Description: "7d/30d" | Code: 28d | Code gewinnt (Rang 2 > Rang 5) | Code ist autoritativ → Docs aktualisieren |
| **activity_summary Zeitfenster** | Description: "7d" | Code: 14d | Code gewinnt | Code ist autoritativ → Docs aktualisieren |
| **Bestandszahlen** | Export Spec: 116 | Extended Catalog: 111 | 5 Metafelder (Rang 3 > Rang 4) | 111 User-Placeholders + 5 Meta |
| **Neue Platzhalter** | requirements_dev: P1-P27 "neu" | Extended Catalog: 111 existierend | Catalog ist Truth (Rang 3 > Rang 7) | requirements_dev ist Planning, nicht Ist-Zustand |
---
### 4.2 Inkonsistenzen zwischen Dokumenten
| Dokument A | Aussage A | Dokument B | Aussage B | Audit-Behandlung |
|------------|-----------|------------|-----------|------------------|
| GAP_REPORT.md | "74 unknown time_window" | Code-Evidence | "75 unklar im Code" | Beide korrekt (leichte Differenz durch Fuzzy-Matching) |
| requirements_dev.md | "Ability Balance fehlt" | Extended Catalog | "Ability Balance existiert (5 Platzhalter)" | Extended Catalog gewinnt (existiert, aber ungenutzt) |
| konzept_v2.md | "E1 Energiebilanz Chart braucht X" | Code | "Nicht implementiert" | Konzept ist Soll, Code ist Ist → Kein Konflikt (Feature-Gap) |
---
## 5. FACHLICH ABGELEITETE AUSSAGEN
Diese Aussagen stammen **nicht direkt aus Code**, sondern wurden **fachlich inferiert**:
| Aussage | Quelle | Ableitung | Evidence-Level |
|---------|--------|-----------|----------------|
| **Ability Balance Zeitfenster: 28d** | DATA_ARCHITECTURE 3.2 + Audit-Heuristik | Konsistent mit anderen Trend-Metriken | `fachlich_abgeleitet` |
| **Correlations Zeitfenster: 28d** | konzept_v2.md (C1-C7) + Statistik-Heuristik | Min 21-28 Paare für Reliability | `fachlich_abgeleitet` |
| **Scores Zeitfenster: mixed** | DATA_ARCHITECTURE + Code-Analyse | Kombinieren verschiedene Sub-Zeitfenster | `fachlich_abgeleitet` |
| **49 Non-Compliant Semantic Contracts** | Semantic-Contract-Agent + DATA_ARCHITECTURE | Aus Datenarchitektur inferiert | `fachlich_abgeleitet` |
**Wichtig:** Alle fachlich abgeleiteten Aussagen sind im Audit **klar gekennzeichnet** mit `evidence_level: "fachlich_abgeleitet"`.
---
## 6. NICHT VERWENDETE QUELLEN
### 6.1 Bewusst ausgeschlossen
| Quelle | Grund für Ausschluss |
|--------|----------------------|
| **Frontend-Code** | Nicht relevant für Backend-Placeholder-Definitionen |
| **Prompt-Templates in DB** | Verwendung wird aus Catalog-Metadaten (`used_by`) extrahiert |
| **Alte Dokumentationen** | Legacy, nicht mehr aktuell |
| **Git History** | Nicht relevant für Ist-Zustand (Audit fokussiert auf Current State) |
---
### 6.2 Fehlende Quellen (würden Audit verbessern)
| Fehlende Quelle | Warum hilfreich | Impact |
|-----------------|-----------------|--------|
| **Unit-Tests für Platzhalter** | Würden erwartete Outputs dokumentieren | MEDIUM (bessere Semantic Contracts) |
| **Prompt-Library-Dokumentation** | Würde zeigen, welche Platzhalter wie genutzt werden | MEDIUM (bessere Usage-Analyse) |
| **Historical Change-Log** | Würde Breaking Changes dokumentieren | LOW (nur für Deprecation-History) |
| **User-Feedback zu Platzhaltern** | Würde Probleme/Unklarheiten zeigen | LOW (kein Bestand vorhanden) |
---
## 7. AGENT-SPEZIFISCHE QUELLENNUTZUNG
### 7.1 Code-Evidence-Agent (124s)
**Genutzte Quellen:**
1. `placeholder_resolver.py` (vollständig, Zeilen 1-1300)
2. `data_layer/*.py` (alle 6 Module)
3. `routers/prompts.py` (Export-Logik)
4. `goal_utils.py` (Goal-Helper)
**Output:**
- Resolver-Funktionen für alle 111 Platzhalter
- Data-Layer-Module (81 via Helper, 19 direkt)
- Source-Tables (aus SQL-Queries extrahiert)
- Zeitfenster aus Default-Parametern
**Evidence-Level:** `code_verified` (100%)
---
### 7.2 Semantic-Contract-Agent (218s)
**Genutzte Quellen:**
1. `PLACEHOLDER_CATALOG_EXTENDED.json` (Ist-Zustand)
2. `DATA_ARCHITECTURE.md` (Fachliche Bedeutung)
3. `mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` (Chart-Specs)
**Output:**
- Target-Descriptions für alle 49 Non-Compliant
- Target-Categories (Unknown → fachliche Kategorien)
- Semantic Contracts (aus Datenarchitektur abgeleitet)
**Evidence-Level:**
- `documentation_verified` (56% - in Konzeptdokumenten begründet)
- `fachlich_abgeleitet` (44% - aus Datenarchitektur inferiert)
---
### 7.3 Time-Window-Confidence-Agent (90s)
**Genutzte Quellen:**
1. `PLACEHOLDER_CATALOG_EXTENDED.json` (Ist-Zustand time_window)
2. `placeholder_resolver.py` + `data_layer/*.py` (Code-Parameter)
3. `PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md` (Erlaubte Werte)
**Output:**
- Zeitfenster-Status für alle 111 (74 unknown, 37 definiert)
- Aggregationslogik (mean, median, slope, etc.)
- Confidence-Logik-Status (8 haben, 103 fehlt)
- Min-Data-Thresholds (wo identifizierbar)
**Evidence-Level:**
- `code_verified` (Zeitfenster aus Code: 37)
- `code_inferred` (75 unklar im Code, fachlich geschätzt)
---
### 7.4 Prompt-Usage-Agent (157s)
**Genutzte Quellen:**
1. `PLACEHOLDER_CATALOG_EXTENDED.json` (`used_by` Metadaten)
2. Grep-Suche nach `{{placeholder_name}}` in Backend (Code-Search)
**Output:**
- Usage-Count für alle 111 (12 kritisch, 32 wichtig, 67 ungenutzt)
- Criticality-Klassifizierung (produktkritisch/wichtig/ungenutzt)
- Rename-Risk-Bewertung (HIGH/MEDIUM/LOW)
**Evidence-Level:** `catalog_verified` (100% aus Extended Catalog)
---
## 8. CROSS-VALIDATION ZWISCHEN AGENTS
**Validierungs-Methodik:**
- Jeder Placeholder wurde von **4 Perspektiven** geprüft
- Konflikte wurden nach Rangfolge aufgelöst (Norm > Code > Catalog > Docs)
- Evidence-Levels dokumentieren Sicherheit der Aussage
**Beispiel-Cross-Validation (weight_trend):**
| Agent | Perspektive | Aussage | Evidence-Level |
|-------|-------------|---------|----------------|
| **Code-Evidence** | Technisch | `days=28` im Code | `code_verified` |
| **Semantic-Contract** | Fachlich | Description sagt "7d/30d" | `documentation_verified` |
| **Time-Window-Confidence** | Zeitfenster | Code 28d vs. Docs 7d/30d → **KONFLIKT** | `code_verified` |
| **Prompt-Usage** | Verwendung | 10 Prompts/Pipelines nutzen es | `catalog_verified` |
| **→ Resolution** | | Code gewinnt → time_window: 28d | `code_verified` |
---
## 9. QUELLENPROTOKOLL-ZUSAMMENFASSUNG
**Gelesene Dateien:** 20+
- **Normative/Spec:** 5 (NORMATIVE.md, EXPORT_SPEC, GAP_REPORT, CATALOG.json, CATALOG.md)
- **Fachlich:** 3 (DATA_ARCHITECTURE, requirements_dev, konzept_v2)
- **Code:** 9+ (placeholder_resolver.py, data_layer/*, routers/prompts.py)
- **Kontext:** 4 (CLAUDE.md, ARCHITECTURE.md, CODING_RULES, LESSONS_LEARNED)
**Maßgebliche Quellen:** 8
- **Rang 1 (Norm):** NORMATIVE.md
- **Rang 2 (Code):** placeholder_resolver.py, data_layer/*
- **Rang 3 (Truth):** PLACEHOLDER_CATALOG_EXTENDED.json
**Konflikte:** 4 dokumentiert, alle nach Rangfolge aufgelöst
**Widersprüche:** 3 dokumentiert
- weight_trend: Code (28d) vs. Docs (7d/30d) → **Code gewinnt**
- activity_summary: Code (14d) vs. Docs (7d) → **Code gewinnt**
- Bestandszahlen: 116 vs. 111 → **111 User-Placeholders + 5 Meta**
**Fachlich abgeleitete Aussagen:** 49 Non-Compliant Semantic Contracts
- **Evidence-Level:** `fachlich_abgeleitet` (klar gekennzeichnet)
**Nicht verwendete Quellen:** Frontend-Code, Prompt-DB, Git History (bewusst ausgeschlossen)
---
**Quellenprotokoll erstellt von:** Claude Code (Lead Audit Agent)
**Audit-Methodik:** 4-Agent-Cross-Validation mit Rangfolgen-basierter Konflikt-Resolution
**Transparenz:** Alle Aussagen mit Evidence-Level dokumentiert (code_verified/documentation_verified/fachlich_abgeleitet/unclear)

View File

@ -0,0 +1,340 @@
# Normative Prüfmatrix: Summary
**Audit-Datum:** 29. März 2026
**Umfang:** 111 Platzhalter
**Normative Basis:** PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md v1.0.0
---
## HINWEIS ZUR VOLLSTÄNDIGEN MATRIX
Die **vollständige Prüfmatrix** (111 Platzhalter × ~40 Felder = ~4500 Datenpunkte) würde ein extrem großes Dokument ergeben.
Stattdessen:
1. **Dieser Summary** zeigt die kritischsten Findings pro Compliance-Level
2. **Detaillierte Einzelberichte** siehe Agent-Outputs (bereits vorhanden)
3. **Maschinenlesbare Version:** Nutze `PLACEHOLDER_CATALOG_EXTENDED.json` direkt
---
## COMPLIANCE-ÜBERSICHT (111 PLATZHALTER)
### Voll Normkonform (8 Platzhalter - 7%)
| Key | Category | Time-Window | Confidence | Used-By | Why Compliant |
|-----|----------|-------------|------------|---------|---------------|
| `protein_avg` | Ernährung | 30d | ✅ calculate_confidence | 0 | Best-Practice-Modell |
| `kcal_avg` | Ernährung | 30d | ✅ calculate_confidence | 0 | Best-Practice-Modell |
| `fat_avg` | Ernährung | 30d | ✅ calculate_confidence | 0 | Best-Practice-Modell |
| `carb_avg` | Ernährung | 30d | ✅ calculate_confidence | 0 | Best-Practice-Modell |
| `weight_aktuell` | Körper | latest | ✅ high/insufficient | 10 | Latest-Pattern-Modell |
| `weight_trend` | Körper | 28d | ✅ calculate_confidence | 10 | **Code-Konflikt (Docs 7d/30d)** |
| `circ_summary` | Körper | mixed | ✅ high/insufficient | 8 | Best-of-Each-Pattern |
| `age` | Profil | keine | ❌ (not needed) | 2 | Snapshot, no confidence needed |
**Pattern:** Nutrition-Averages + Weight-Aktuell = Best-Practice-Modelle für alle anderen
---
### Partially Compliant (22 Platzhalter - 20%)
**Charakteristik:** 1-2 Gaps, meist time_window oder confidence
| Key | Category | Missing | Priority | Action |
|-----|----------|---------|----------|--------|
| `caliper_summary` | Körper | time_window: unknown | P0 | → latest |
| `kf_aktuell` | Körper | time_window: unknown | P0 | → latest |
| `sleep_avg_duration` | Schlaf & Erholung | time_window: unknown (Code: 7d) | P0 | → 7d |
| `sleep_avg_quality` | Schlaf & Erholung | time_window: unknown (Code: 7d) | P0 | → 7d |
| `rest_days_count` | Schlaf & Erholung | time_window: unknown (Code: 30d) | P0 | → 30d |
| `vitals_avg_hr` | Vitalwerte | time_window: unknown (Code: 7d) | P0 | → 7d |
| `vitals_avg_hrv` | Vitalwerte | time_window: unknown (Code: 7d) | P0 | → 7d |
| `vitals_vo2_max` | Vitalwerte | time_window: unknown, weak semantic_contract | P0 + P1 | → latest + sharpen contract |
| `bmi` | Körper | time_window: unknown, weak semantic_contract | P0 | → latest + Formel dokumentieren |
| ... (13 weitere) | | | | |
**Remediation:** P0 time_window + teilweise P1 semantic_contract sharpening
---
### Non-Compliant (81 Platzhalter - 73%)
**Charakteristik:** 3+ Gaps, meist Unknown category + No description + unknown time_window
#### Cluster 1: Unknown Category + No Description (49)
**Sub-Gruppen:**
**A. Ability Balance (5):**
- `ability_balance_coordination`, `ability_balance_endurance`, `ability_balance_mental`, `ability_balance_mobility`, `ability_balance_strength`
- **Gaps:** category: Unknown, description: No description, time_window: unknown
- **Fix:** category: Training, time_window: 28d, descriptions aus Semantic-Agent-Report
- **Priority:** P0 (Bulk-Update)
**B. Correlations (5):**
- `correlation_energy_weight_lag`, `correlation_load_hrv`, `correlation_load_rhr`, `correlation_protein_lbm`, `correlation_sleep_recovery`
- **Gaps:** category: Unknown, description: No description, time_window: unknown, confidence: null
- **Fix:** category: Auswertung, time_window: 28d, confidence: pair-based, descriptions aus Semantic-Agent
- **Priority:** P0 (description) + P1 (confidence)
**C. Goals & Focus (16):**
- `active_goals_json`, `active_goals_md`, `focus_areas_weighted_json`, `focus_areas_weighted_md`, `focus_area_weights_json`
- `top_goal_name`, `top_goal_progress_pct`, `top_goal_status`
- `top_3_goals_behind_schedule`, `top_3_goals_on_track`, `top_3_focus_areas`
- `focus_cat_körper_progress/weight`, `focus_cat_ernährung_progress/weight`, `focus_cat_aktivität_progress/weight`, `focus_cat_recovery_progress/weight`, `focus_cat_vitalwerte_progress/weight`
- **Gaps:** category: Unknown, description: No description, time_window: unknown
- **Fix:** category: Ziele, time_window: latest (Snapshot) oder custom (Progress), descriptions aus Semantic-Agent
- **Priority:** P0 (Bulk-Update)
**D. Body Deltas (5):**
- `arm_28d_delta`, `chest_28d_delta`, `hip_28d_delta`, `thigh_28d_delta` (waist_28d_delta ist Partially Compliant)
- **Gaps:** category: Unknown, description: No description, time_window: unknown (sollte 28d sein), confidence: null
- **Fix:** category: Körper, time_window: 28d, confidence: delta-confidence, descriptions aus Semantic-Agent
- **Priority:** P0 (description) + P1 (confidence)
**E. Nutrition (6):**
- `energy_deficit_surplus`, `intake_volatility`, `nutrition_days`, `protein_days_in_target`, `protein_ziel_low`, `protein_ziel_high`
- **Gaps:** category: Unknown, description: No description, time_window: unknown
- **Fix:** category: Ernährung, time_window: 7d/28d/30d (je nach Funktion), descriptions aus Semantic-Agent
- **Priority:** P0 (Bulk-Update)
**F. Training (8):**
- `activity_detail`, `activity_summary` (Code-Konflikt!), `trainingstyp_verteilung`, `training_minutes_week`, `training_frequency_7d`
- `quality_sessions_pct`, `monotony_score`, `strain_score`, `rest_day_compliance`, `proxy_internal_load_7d`
- **Gaps:** category: Unknown (teilweise Training), description: No description (teilweise), time_window: unknown
- **Fix:** category: Training, time_window: 7d/14d/28d (aus Code), descriptions aus Semantic-Agent
- **Priority:** P0 (time_window, Code-Konflikte) + P1 (confidence für Scores)
**G. Meta/Sonstiges (4):**
- `zeitraum_90d`, `goal_weight`, `goal_bf_pct`, `weight_90d_slope`, `plateau_detected`, `top_drivers`, etc.
- **Gaps:** Gemischt
- **Fix:** Individuell je Platzhalter
- **Priority:** P0-P1
---
#### Cluster 2: Fehlende Confidence (103 - inkl. Partially Compliant)
**Kritischste:** Trend-Platzhalter (11)
- `weight_28d_slope`, `weight_90d_slope`, `weight_7d_median`
- `fm_28d_change`, `lbm_28d_change`
- `waist_28d_delta`, `hip_28d_delta`, `chest_28d_delta`, `arm_28d_delta`, `thigh_28d_delta`
- `vo2max_trend_28d`
**Gap:** confidence_logic: null
**Fix:** Implementiere calculate_confidence(data_points, time_window_days, 'trend')
**Priority:** P1 (12-16h)
---
#### Cluster 3: Fehlende Data-Layer-Module (100)
**Alle außer:**
- nutrition_avg (4), weight_aktuell, weight_trend, kf_aktuell, caliper_summary, circ_summary, age, height, name, geschlecht
**Gap:** data_layer_module: null
**Fix:** Mapping aus Code-Evidence-Agent-Report übernehmen (Bulk-Update)
**Priority:** P1 (6-8h, automatisierbar)
---
#### Cluster 4: Fehlende Source-Tables (90)
**Ähnlich Cluster 3**
**Gap:** source_tables: []
**Fix:** Mapping aus Code-Evidence-Agent-Report übernehmen (Bulk-Update)
**Priority:** P1 (6-8h, automatisierbar)
---
## COMPLIANCE-MATRIX NACH KATEGORIE
| Kategorie | Total | Compliant | Partially | Non-Compliant | Kritischste Gaps |
|-----------|-------|-----------|-----------|---------------|------------------|
| **Ernährung** | 8 | 4 (50%) | 0 | 4 | time_window, description |
| **Körper** | 11 | 3 (27%) | 5 | 3 | time_window, confidence (Trends) |
| **Training** | 9 | 0 | 2 | 7 | category, description, time_window |
| **Profil** | 4 | 1 (25%) | 3 | 0 | time_window (unkritisch) |
| **Schlaf & Erholung** | 7 | 0 | 3 | 4 | time_window, description |
| **Vitalwerte** | 6 | 0 | 3 | 3 | time_window, semantic_contract |
| **Scores (Phase 0b)** | 6 | 0 | 0 | 6 | time_window, confidence |
| **Focus Areas** | 8 | 0 | 0 | 8 | category, description, time_window |
| **Zeitraum** | 3 | 0 | 0 | 3 | time_window (Name-Mismatch!) |
| **Unknown** | 49 | 0 | 6 | 43 | Alles (category, description, time_window, confidence) |
**Auffälligkeiten:**
- **Ernährung:** 50% Compliance dank Best-Practice nutrition_avg
- **Unknown-Kategorie:** 88% Non-Compliant (43/49)
- **Training:** 0% Fully Compliant, aber nur 2 Partially → hoher Remediation-Bedarf
---
## PRODUKTKRITISCHE PLATZHALTER (12)
**Diese 12 haben >3 Verwendungen = Breaking-Change-Risk bei Änderungen**
| Rank | Key | Uses | Category | Compliance | Kritische Gaps | Risk |
|------|-----|------|----------|------------|----------------|------|
| 1 | `name` | 19 | Profil | Partially | time_window (unkritisch) | 🔴 EXTREM |
| 2 | `geschlecht` | 14 | Profil | Partially | time_window (unkritisch) | 🔴 EXTREM |
| 3 | `height` | 12 | Profil | Partially | time_window (unkritisch) | 🔴 EXTREM |
| 4 | `weight_aktuell` | 10 | Körper | ✅ Compliant | Keine | 🔴 HOCH (Format-sensitiv) |
| 5 | `weight_trend` | 10 | Körper | ✅ Compliant | **Code-Docs-Konflikt** | 🔴 HOCH (Code-Fix P0) |
| 6 | `goal_bf_pct` | 10 | Unknown | Non-Compliant | category, description, time_window | 🔴 HOCH |
| 7 | `caliper_summary` | 8 | Körper | Partially | time_window | 🟡 MITTEL |
| 8 | `circ_summary` | 8 | Körper | ✅ Compliant | Keine | 🟡 MITTEL |
| 9 | `goal_weight` | 8 | Unknown | Non-Compliant | category, description, time_window | 🟡 MITTEL |
| 10 | `protein_ziel_low` | 7 | Unknown | Non-Compliant | category, description, time_window | 🟡 MITTEL |
| 11 | `protein_ziel_high` | 7 | Unknown | Non-Compliant | category, description, time_window | 🟡 MITTEL |
| 12 | `activity_detail` | 4 | Unknown | Non-Compliant | **Code-Docs-Konflikt**, category, description, time_window | 🟡 MITTEL |
**Kritische Beobachtungen:**
- **6 von 12 (50%)** sind Non-Compliant, davon 5 in "Unknown"-Kategorie
- **3 haben Code-Docs-Konflikte** (weight_trend, activity_summary, activity_detail) → P0 Fix
- **name, geschlecht, height:** Zwar nur "Partially" (time_window fehlt), aber unkritisch da Profil-Snapshots
---
## UNGENUTZTE PLATZHALTER (67 - 60%)
**WICHTIG:** Methodische Korrektur (30.03.2026)
- Nichtnutzung ≠ Deprecation-Bedarf
- Prompt-Bibliothek ist im Aufbau (Phase 0b/0c/1/2)
- Neue Klassifizierung: usage_role_classification statt Deprecation-Kategorien
**Neue Klassifizierung (siehe USAGE_ROLE_CLASSIFICATION.md):**
### unused_but_planned (30 - 45%)
- **Scores (6):** activity_score, nutrition_score, recovery_score, body_progress_score, goal_progress_score, data_quality_score
- Roadmap: Phase 0c/1
- **Correlations (5):** energy_weight_lag, load_hrv, load_rhr, protein_lbm, sleep_recovery
- Roadmap: Phase 2
- **Ability Balance (5):** coordination, endurance, mental, mobility, strength
- Roadmap: Phase 1
- **Goals Details (11):** active_goals_json/md, focus_areas_weighted, top_3_goals_*, focus_cat_*_progress
- Roadmap: Phase 0b/0c (Backend DONE)
- **Sleep/Plateau/Drivers (3):** sleep_debt, plateau_detected, top_drivers
- Roadmap: Phase 1/2
- **Action:** Timeline bestätigen, Prototyping-Prompts erstellen
### unused_but_plausible (37 - 55%)
- **Body Deltas (5):** arm_28d_delta, chest_28d_delta, hip_28d_delta, thigh_28d_delta, waist_28d_delta
- Plausibel für: Fortschritts-Prompts spezifischer Körperteile
- **Nutrition Details (6):** energy_deficit_surplus, intake_volatility, nutrition_days, protein_days_in_target, protein_ziel_*
- Plausibel für: Ernährungs-Coaching-Prompts
- **Training Quality/Load (3):** monotony_score, strain_score, rest_day_compliance
- Plausibel für: Übertrainings-Erkennung
- **Focus Category (14):** focus_cat_*_weight, focus_cat_*_progress
- Plausibel für: Dashboard-Widgets, Kategorie-Analysen
- **Meta/Convenience (9):** bmi, waist_hip_ratio, datum_heute, zeitraum_*
- Plausibel für: Prompt-Templates (Convenience)
- **Action:** Prompt-Use-Cases identifizieren, Templates für 10-15 Quick Wins
### redundant_or_duplicate (0 - 0%)
- **Keine** - alle 67 haben fachliche Berechtigung
---
## GAP-SUMMARY PRO PLATZHALTER (TOP 20 KRITISCHSTE)
| Rank | Key | Gaps | Priority | Aufwand | Blocker |
|------|-----|------|----------|---------|---------|
| 1 | `goal_bf_pct` | category, description, time_window, semantic_contract | P0 | 15 Min | 10 Uses = Breaking-Risk |
| 2 | `goal_weight` | category, description, time_window, semantic_contract | P0 | 15 Min | 8 Uses = Breaking-Risk |
| 3 | `protein_ziel_low` | category, description, time_window, semantic_contract | P0 | 15 Min | 7 Uses = Breaking-Risk |
| 4 | `protein_ziel_high` | category, description, time_window, semantic_contract | P0 | 15 Min | 7 Uses = Breaking-Risk |
| 5 | `activity_detail` | **Code-Konflikt**, category, description, time_window | P0 | 30 Min | 4 Uses + Konflikt |
| 6 | `weight_trend` | **Code-Konflikt (Docs 7d/30d, Code 28d)** | P0 | 15 Min | 10 Uses + Konflikt |
| 7 | `activity_summary` | **Code-Konflikt (Docs 7d, Code 14d)** | P0 | 15 Min | 2 Uses + Konflikt |
| 8 | `weight_28d_slope` | confidence: null | P1 | 2h | Trend-Pattern |
| 9 | `weight_90d_slope` | confidence: null | P1 | 2h | Trend-Pattern |
| 10 | `fm_28d_change` | confidence: null | P1 | 2h | Delta-Pattern |
| 11 | `lbm_28d_change` | confidence: null | P1 | 2h | Delta-Pattern |
| 12 | `activity_score` | time_window, confidence | P0 + P1 | 1h (window) + 3h (confidence) | Score-Pattern |
| 13 | `nutrition_score` | time_window, confidence | P0 + P1 | 1h + 3h | Score-Pattern |
| 14 | `recovery_score` | time_window, confidence | P0 + P1 | 1h + 3h | Score-Pattern |
| 15 | `correlation_energy_weight_lag` | category, description, time_window, confidence | P0 + P1 | 30 Min + 2h | Correlation-Pattern |
| 16 | `ability_balance_coordination` | category, description, time_window | P0 | 15 Min | Bulk-Update |
| 17 | `ability_balance_endurance` | category, description, time_window | P0 | 15 Min | Bulk-Update |
| 18 | `active_goals_json` | category, description, time_window | P0 | 15 Min | Bulk-Update |
| 19 | `caliper_summary` | time_window | P0 | 5 Min | 8 Uses |
| 20 | `sleep_avg_duration` | time_window (Code: 7d) | P0 | 5 Min | Code-Extraction |
**Gesamt Top-20-Aufwand:** ~20h (P0: 8h, P1: 12h)
---
## REQUIRED-ACTION VERTEILUNG
| Action | Anzahl | Priority | Typischer Aufwand |
|--------|--------|----------|-------------------|
| **time_window_fix** | 74 | P0 | 5-10 Min/Platzhalter (automatisierbar: 15, semi-auto: 20, manuell: 39) |
| **description_add** | 49 | P0 | 5 Min/Platzhalter (Bulk-Update aus Semantic-Agent) |
| **category_fix** | 49 | P0 | 2 Min/Platzhalter (Bulk-Update aus Semantic-Agent) |
| **semantic_contract_add** | 49 | P0 | 10 Min/Platzhalter (Bulk-Update aus Semantic-Agent) |
| **confidence_logic_add** | 103 | P1 | 2-4h/Pattern (Trend: 12h, Score: 8h, Correlation: 4h, Rest: 20h) |
| **data_layer_module_add** | 100 | P1 | Bulk-Update 6-8h gesamt (aus Code-Evidence) |
| **source_tables_add** | 90 | P1 | Bulk-Update 6-8h gesamt (aus Code-Evidence) |
| **missing_value_policy_fix** | 110 | P1 | Pattern-Implementierung 8-10h gesamt (Dual-Mode) |
| **code_fix** | 3 | P0 | 1h gesamt (Code-Docs-Konflikte) |
| **governance_decision** | 39 | P0 | Meeting 4-6h (Zeitfenster-Entscheidungen) |
---
## PRIORITÄTS-VERTEILUNG
| Priority | Platzhalter | Gesamt-Aufwand | Timeline |
|----------|-------------|----------------|----------|
| **P0** | 77 (unique) | 11-17h | Week 1 |
| **P1** | 103 (unique, teilweise Overlap mit P0) | 44-58h | Week 2-3 |
| **P2** | 67 (Deprecation-Review) | 8-12h | Week 4-5 |
| **P3** | 5 (Doku-Feinschliff) | 0.5h | Later |
---
## COMPLIANCE-SCORE-VERTEILUNG (NACH P0+P1+P2)
**Aktuell:**
```
Compliance Score 0-20%: 81 Platzhalter (73%)
Compliance Score 21-40%: 0 Platzhalter (0%)
Compliance Score 41-60%: 22 Platzhalter (20%)
Compliance Score 61-80%: 0 Platzhalter (0%)
Compliance Score 81-100%: 8 Platzhalter (7%)
```
**Nach P0+P1:**
```
Compliance Score 0-20%: 0 Platzhalter (0%)
Compliance Score 21-40%: 15 Platzhalter (14%) - Deprecation-Kandidaten
Compliance Score 41-60%: 30 Platzhalter (27%)
Compliance Score 61-80%: 48 Platzhalter (43%)
Compliance Score 81-100%: 18 Platzhalter (16%)
```
**Nach P2:**
```
Compliance Score 0-20%: 0 Platzhalter (0%)
Compliance Score 21-40%: 15 Platzhalter (14%) - DEPRECATED
Compliance Score 41-60%: 20 Platzhalter (18%)
Compliance Score 61-80%: 50 Platzhalter (45%)
Compliance Score 81-100%: 26 Platzhalter (23%)
```
**Ziel erreicht:** 68% der Platzhalter mit Score >60% (Norm-Ziel: >60%)
---
## NÄCHSTE SCHRITTE
1. **Review dieser Prüfmatrix** mit Tech Lead
2. **Validierung:** Stichprobe 10% manuell gegen Code prüfen
3. **P0-Kickoff:** Zeitfenster-Meeting + Bulk-Updates starten
4. **Tracking:** Compliance-Fortschritt wöchentlich messen
---
**Prüfmatrix-Summary erstellt von:** Claude Code (Lead Audit Agent)
**Basis:** 4-Agent-Cross-Validation, 111 Platzhalter vollständig geprüft
**Vollständige Details:** Siehe Agent-Outputs + PLACEHOLDER_CATALOG_EXTENDED.json

View File

@ -0,0 +1,193 @@
# Placeholder-Audit Report - 29. März 2026
Vollständiger Audit-Report zur Normkonformität aller 111 Platzhalter des Systems.
---
## DOKUMENTE IN DIESEM VERZEICHNIS
### 1. Executive Summary (`01_EXECUTIVE_SUMMARY.md`)
**Was:** Zusammenfassung aller kritischen Findings
**Für wen:** Management, Product Lead, Tech Lead
**Lesezeit:** 15-20 Minuten
**Key-Findings:**
- Nur 7% der Platzhalter sind voll normkonform
- 67% haben unklares Zeitfenster
- 93% haben keine Confidence-Logik
- 12 Platzhalter sind produktkritisch (3-19 Verwendungen)
---
### 2. Gap-Cluster-Bericht (`02_GAP_CLUSTER_REPORT.md`)
**Was:** Systematische Gruppierung aller Gaps nach Clustern
**Für wen:** Tech Lead, Backend Team
**Lesezeit:** 30-40 Minuten
**Key-Content:**
- 11 Gap-Cluster identifiziert
- Detaillierte Remediation-Strategien pro Cluster
- Priorisierung nach Severity (P0-P3)
---
### 3. Maßnahmenplan (`03_MASSNAHMENPLAN.md`)
**Was:** Priorisierter Umsetzungsplan für alle Fixes
**Für wen:** Development Team, Project Manager
**Lesezeit:** 45-60 Minuten
**Key-Content:**
- P0 (Week 1): 11-17h - Code-Docs-Konflikte, Zeitfenster, Semantic Contracts
- P1 (Week 2-3): 44-58h - Confidence-Logik, Data Layer, Missing-Value-Policy
- P2 (Week 4-5): 8-12h - Production-Status, Deprecation
- Gesamt: 63.5-87.5h über 4-6 Wochen
---
### 4. Offene Entscheidungen (`04_OFFENE_ENTSCHEIDUNGEN.md`)
**Was:** Alle Punkte die Product/Management-Entscheidung brauchen
**Für wen:** Product Manager, Tech Lead
**Lesezeit:** 20-30 Minuten
**Key-Content:**
- E1: Zeitfenster-Klassifizierung (BLOCKING P0)
- E2: Integration-Timeline (67 ungenutzte Platzhalter - Roadmap-Priorisierung)
- E3: Production-Readiness-Kriterien
- E4-E6: Governance, Neue Platzhalter, Legacy-Sunset
---
### 5. Quellenprotokoll (`05_QUELLENPROTOKOLL.md`)
**Was:** Vollständige Dokumentation aller verwendeten Quellen
**Für wen:** Audit-Reviewer, Compliance
**Lesezeit:** 15-20 Minuten
**Key-Content:**
- 20+ gelesene Dateien dokumentiert
- Rangfolge bei Konflikten (Norm > Code > Catalog > Docs)
- 4 Agent-Methodik transparent dargestellt
- Alle Widersprüche zwischen Quellen aufgelöst
---
### 6. Normative Prüfmatrix Summary (`06_PRUEFMATRIX_SUMMARY.md`)
**Was:** Summary der Prüfmatrix mit kritischen Findings
**Für wen:** Backend Team, QA
**Lesezeit:** 30-40 Minuten
**Key-Content:**
- Compliance-Übersicht (Compliant/Partially/Non-Compliant)
- Produktkritische Platzhalter (12 mit 3-19 Verwendungen)
- Ungenutzte Platzhalter - Neue Klassifizierung (30 geplant, 37 plausibel)
- Gap-Summary pro Platzhalter (Top 20)
---
### 7. Revisionsprotokoll (`REVISIONSPROTOKOLL.md` - NEU)
**Was:** Dokumentation der methodischen Korrektur (30.03.2026)
**Für wen:** Audit-Reviewer, Management
**Lesezeit:** 15-20 Minuten
**Key-Content:**
- Methodische Fehlsteuerung identifiziert: "ungenutzt ≠ redundant"
- Neue Klassifizierung: usage_role_classification
- Detaillierte Änderungen in 5 Dokumenten
- Verbindliche methodische Regel für künftige Audits
---
### 8. Usage Role Classification (`USAGE_ROLE_CLASSIFICATION.md` - NEU)
**Was:** Fachliche Klassifizierung aller 67 ungenutzten Placeholder
**Für wen:** Product Manager, Tech Lead
**Lesezeit:** 20-25 Minuten
**Key-Content:**
- 30 unused_but_planned (Roadmap Phase 0c/1/2)
- 37 unused_but_plausible (fachlich sinnvoll)
- 0 redundant_or_duplicate
- Empfohlene Integration-Maßnahmen
---
## SCHNELLSTART
**Für Entscheider (15 Min):**
1. Lese `01_EXECUTIVE_SUMMARY.md`
2. Review `04_OFFENE_ENTSCHEIDUNGEN.md` Kategorie 1 (Zeitfenster - BLOCKING)
3. Setze P0-Meeting an (Week 1)
**Für Entwickler (60 Min):**
1. Lese `02_GAP_CLUSTER_REPORT.md`
2. Fokus auf P0-Cluster (11, 1, 4)
3. Review `03_MASSNAHMENPLAN.md` P0-Maßnahmen
**Für Audit-Review (3h):**
1. Alle 6 Dokumente sequenziell lesen
2. Cross-Check mit `05_QUELLENPROTOKOLL.md`
3. Validierung gegen `06_PRUEFMATRIX.md`
---
## AUDIT-METHODIK
**4 spezialisierte Agents (parallel):**
1. **Code-Evidence-Agent** (124s) - Technische Herkunft
2. **Semantic-Contract-Agent** (218s) - Fachliche Bedeutung
3. **Time-Window-Confidence-Agent** (90s) - Zeitfenster & Confidence
4. **Prompt-Usage-Agent** (157s) - Verwendung & Risiken
**Cross-Validation:**
- Jeder Platzhalter von 4 Perspektiven geprüft
- Konflikte nach Rangfolge aufgelöst (Norm > Code > Catalog > Docs)
- Evidence-Levels dokumentiert (code_verified / documentation_verified / fachlich_abgeleitet)
---
## COMPLIANCE-ZIELE
**Aktuell:** 7% voll normkonform (8/111)
**Nach P0 (Week 1):** 25-30%
- 74 time_window: unknown → definiert
- 49 description/category: Unknown/No description → vollständig
- 3 Code-Docs-Konflikte gelöst
**Nach P1 (Week 3):** 50-60%
- 103 Platzhalter mit Confidence-Logik (neu: 20+)
- 100 data_layer_module gesetzt
- 90 source_tables gesetzt
- 110 Structured Missing-Value-Policy
**Nach P2 (Week 5):** 65-70%
- 12-15 Platzhalter schema_status: production
- Integration-Roadmap für 30 geplante Platzhalter (Phase 0c/1/2)
- Prompt-Templates für 5-10 Quick Wins (ungenutzte Platzhalter aktivieren)
- Nutzungsrate 40% → 50-60%
---
## KRITISCHE NÄCHSTE SCHRITTE
**Innerhalb 1 Woche (P0):**
1. ✅ Review `01_EXECUTIVE_SUMMARY.md` mit Tech Lead
2. ✅ Setze P0-Meeting an (Zeitfenster-Entscheidungen, siehe `04_OFFENE_ENTSCHEIDUNGEN.md`)
3. ✅ Kickoff P0-Sprint (11-17h, siehe `03_MASSNAHMENPLAN.md` P0)
**Innerhalb 2-3 Wochen (P1):**
1. Implementiere Confidence-Logik (Pattern aus nutrition_avg)
2. Dokumentiere Data-Layer-Module (Mapping aus Code-Evidence)
3. Strukturierte Missing-Value-Policy (Dual-Mode)
**Innerhalb 4-5 Wochen (P2):**
1. Production-Readiness-Review (12-15 Kandidaten)
2. Deprecation-Meeting (67 ungenutzte)
---
## KONTAKT & FRAGEN
**Audit durchgeführt von:** Claude Code (Lead Audit Agent)
**Audit-Datum:** 29. März 2026
**Normative Basis:** PLACEHOLDER_METADATA_REQUIREMENTS_V2_NORMATIVE.md v1.0.0
**Bei Fragen:**
- Technische Fragen → `05_QUELLENPROTOKOLL.md` (Welche Quelle ist autoritativ?)
- Umsetzungs-Fragen → `03_MASSNAHMENPLAN.md` (Wie implementieren?)
- Entscheidungs-Fragen → `04_OFFENE_ENTSCHEIDUNGEN.md` (Wer entscheidet?)
- Gap-Fragen → `02_GAP_CLUSTER_REPORT.md` (Welcher Cluster?)
---
**STATUS:** ✅ Audit abgeschlossen | ⏳ Remediation pending (P0 Kickoff)

View File

@ -0,0 +1,395 @@
# Revisionsprotokoll: Methodische Korrektur "Ungenutzte Placeholder"
**Revisions-Datum:** 30. März 2026
**Revisions-Grund:** Methodische Fehlsteuerung bei Bewertung ungenutzter Placeholder
**Durchgeführt von:** Claude Code (auf Anforderung des Users)
---
## 1. Kernproblem
### Ursprüngliche Bewertung (FEHLERHAFT):
- 67 ungenutzte Placeholder (60%) wurden als "Technical Debt" bewertet
- Deprecation wurde als primäre Maßnahme vorgeschlagen
- "Ungenutzt" wurde gleichgesetzt mit "redundant/überflüssig"
### Warum methodisch falsch:
**Für dieses Projekt gilt:**
- Die Prompt-Bibliothek befindet sich noch im Aufbau (Phase 0b/0c/1/2 Roadmap)
- Aktuelle Nichtnutzung in Prompts ist **kein gültiges Kriterium** für Redundanz
- Viele Placeholder sind explizit in Roadmap geplant oder fachlich plausibel
**Korrektes Prinzip:**
> Ein Placeholder darf nur als redundant eingestuft werden, wenn **keine fachliche Rolle** in Roadmap, Konzept oder Datenmodell erkennbar ist.
---
## 2. Neue Klassifizierung
### Entwickelte Klassifizierungs-Schema:
| Status | Definition | Kriterien | Anzahl |
|--------|------------|-----------|--------|
| **used_productively** | Aktiv in Prompts/Pipelines/Charts | used_by > 0 | 44 |
| **unused_but_planned** | Explizit in Roadmap Phase 0c/1/2 | Dokumentiert in Roadmap/Konzept | 30 (45%) |
| **unused_but_plausible** | Fachlich sinnvolle Rolle erkennbar | Data-Layer-Anbindung + plausible Rolle | 37 (55%) |
| **unused_and_unclear** | Fachliche Rolle unklar | Keine dokumentierte Rolle | 0 |
| **redundant_or_duplicate** | Echte Doppelung oder ersetzt | Keine fachliche Rolle nachweisbar | 0 |
**Ergebnis:** Alle 67 ungenutzten Placeholder haben fachliche Berechtigung.
---
## 3. Durchgeführte Änderungen
### A. Executive Summary (`01_EXECUTIVE_SUMMARY.md`)
**Geändert:**
#### Abschnitt 5: "60% ungenutzte Platzhalter" (Zeilen 124-142)
**VORHER (FALSCH):**
```markdown
### 5. 60% ungenutzte Platzhalter
**Problem:**
- Technical Debt akkumuliert
- Unklare Deprecation-Strategie
**Impact:**
- Neue Features nutzen evtl. falsche/veraltete Platzhalter
**Root Cause:**
- Platzhalter wurden für geplante Features erstellt, aber noch nicht genutzt
```
**NACHHER (KORREKT):**
```markdown
### 5. 67 Platzhalter noch nicht produktiv eingebunden
**Status:**
- **Wichtig:** Dies ist KEIN Technical Debt, sondern erwartbar bei Prompt-Bibliothek im Aufbau
**Fachliche Klassifizierung:**
- **30 Platzhalter (45%):** Explizit in Roadmap Phase 0c/1/2 geplant
- **37 Platzhalter (55%):** Fachlich plausibel, noch nicht in Prompts integriert
- **0 Platzhalter:** Redundant oder deprecation-würdig
**Interpretation:**
- Kein Deprecation-Bedarf Integration statt Deletion erforderlich
```
---
#### Abschnitt 4: "Technical Debt" (Zeilen 217-230)
**VORHER (FALSCH):**
```markdown
### 4. Technical Debt (🟡 MEDIUM)
**Indikatoren:**
- 60% ungenutzt → tote Codepfade
```
**NACHHER (KORREKT):**
```markdown
### 4. Fehlende Produktions-Governance (🟡 MEDIUM)
**KEIN Technical Debt:**
- 60% ungenutzte Platzhalter ≠ "tote Codepfade"
- Prompt-Bibliothek ist im Aufbau (Phase 0b/0c/1/2)
- Ungenutzte Platzhalter sind fachlich geplant oder plausibel
```
---
#### Maßnahme P2.1 (Zeilen 377-383)
**VORHER (FALSCH):**
```markdown
**7. Ungenutzte Platzhalter Deprecation-Review (67)**
- Deliverable: Deprecation-Liste mit Replacement-Platzhaltern
```
**NACHHER (KORREKT):**
```markdown
**7. Ungenutzte Platzhalter - Integration planen (67)**
- Deliverable: Integration-Roadmap, Prompt-Templates (5-10 Quick Wins)
```
---
### B. Offene Entscheidungen (`04_OFFENE_ENTSCHEIDUNGEN.md`)
**Geändert:**
#### E2.1: Komplette Neuformulierung (Zeilen 193-252)
**VORHER (FALSCH):**
```markdown
### E2.1: Ungenutzte Platzhalter - Behalten oder Deprecaten?
**Problem:**
60% der Platzhalter haben 0 Verwendungen. Welche sollen gedeprecated werden?
**Sub-Entscheidungen:**
- E2.1.1: Body Deltas - Redundanz reduzieren
- E2.1.2: Ability Balance - Warten oder Deprecaten?
- E2.1.3: Meta-Platzhalter - Redundant, trivial berechenbar → deprecaten
```
**NACHHER (KORREKT):**
```markdown
### E2.1: Ungenutzte Platzhalter - Roadmap-Priorisierung und Integration-Timeline
**Neue Fragestellung:**
Nicht "Behalten oder Deprecaten?", sondern "WANN und WIE integrieren?"
**Fachliche Klassifizierung:**
- **30 Platzhalter (45%):** Explizit in Roadmap Phase 0c/1/2 geplant
- **37 Platzhalter (55%):** Fachlich plausibel, noch nicht in Prompts integriert
- **0 Platzhalter:** Redundant oder deprecation-würdig
**Sub-Entscheidungen:**
- E2.1.1: Geplante Platzhalter (30) - Integration-Timeline bestätigen
- Roadmap wie geplant oder Priorisierung anpassen?
- E2.1.2: Plausible Platzhalter (37) - Prompt-Use-Cases identifizieren
- Für welche Use-Cases Templates erstellen? (Quick Wins)
```
---
#### Zusammenfassung (Zeile 395)
**VORHER:** `Deprecation-Strategie | 67 ungenutzte prüfen`
**NACHHER:** `Integration-Timeline | 67 ungenutzte integrieren`
---
### C. Maßnahmenplan (`03_MASSNAHMENPLAN.md`)
**Geändert:**
#### P2.2: Komplette Neuformulierung (Zeilen 773-813)
**VORHER (FALSCH):**
```markdown
### P2.2: Ungenutzte Platzhalter Deprecation-Review
**Severity:** 🟢 LOW (Technical Debt)
**Kategorisierung:**
1. Behalten - Geplant (30) - Keine Action
2. Deprecate - Redundant/Obsolet (15)
3. Unklar - Product-Review (22)
**Deprecation-Beispiel:**
{
"key": "bmi",
"deprecated": true,
"sunset_date": "2026-06-30"
}
**Output:** Deprecation-Liste, 15-20 Deprecated Platzhalter
```
**NACHHER (KORREKT):**
```markdown
### P2.2: Ungenutzte Platzhalter - Integration planen
**Severity:** 🟡 MEDIUM (Prompt-Bibliothek Vollständigkeit)
**Neue Klassifizierung:**
1. unused_but_planned (30) - Timeline bestätigen, Prototyping
2. unused_but_plausible (37) - Prompt-Use-Cases, Templates
3. redundant_or_duplicate (0) - Keine!
**Integration-Beispiel:**
{
"key": "arm_28d_delta",
"usage_role": "unused_but_plausible",
"prompt_use_cases": ["Fortschritts-Analyse spezifischer Körperteile", ...]
}
**Output:** Integration-Roadmap, Prompt-Templates (5-10), Nutzungsrate +10-20%
```
---
#### P2 Summary (Zeilen 817-828)
**VORHER:**
- Deliverables: "15-20 Deprecated Platzhalter"
- Impact: "Technical Debt reduziert"
**NACHHER:**
- Deliverables: "Integration-Roadmap für 30 geplante, Prompt-Templates für 5-10 Quick Wins"
- Impact: "Placeholder-Integration vorangetrieben, Nutzungsrate 40% → 50-60%"
---
### D. Prüfmatrix Summary (`06_PRUEFMATRIX_SUMMARY.md`)
**Geändert:**
#### "Ungenutzte Platzhalter" Abschnitt (Zeilen 199-218)
**VORHER (FALSCH):**
```markdown
## UNGENUTZTE PLATZHALTER (67 - 60%)
**Deprecation-Kandidaten:**
### Kategorie A: Behalten - Geplant (30)
### Kategorie B: Deprecate - Redundant/Obsolet (15)
- bmi, waist_hip_ratio (berechenbar)
- Body Deltas (redundant zu waist_28d_delta Pattern)
- monotony_score, strain_score (experimentell)
### Kategorie C: Unklar - Product-Review (22)
```
**NACHHER (KORREKT):**
```markdown
## UNGENUTZTE PLATZHALTER (67 - 60%)
**WICHTIG:** Methodische Korrektur (30.03.2026)
- Nichtnutzung ≠ Deprecation-Bedarf
- Prompt-Bibliothek ist im Aufbau
**Neue Klassifizierung:**
### unused_but_planned (30 - 45%)
- Scores (6), Correlations (5), Ability Balance (5), Goals Details (11), etc.
- Roadmap: Phase 0c/1/2
- Action: Timeline bestätigen
### unused_but_plausible (37 - 55%)
- Body Deltas (5), Nutrition Details (6), Training Quality (3), Focus Category (14), Meta (9)
- Plausibel für: Fortschritts-Prompts, Coaching, Dashboard
- Action: Prompt-Use-Cases identifizieren
### redundant_or_duplicate (0 - 0%)
- Keine - alle 67 haben fachliche Berechtigung
```
---
### E. README (`06_README.md`)
**Geändert:**
#### Zeile 50:
**VORHER:** `E2: Deprecation-Strategie (67 ungenutzte Platzhalter)`
**NACHHER:** `E2: Integration-Timeline (67 ungenutzte Platzhalter - Roadmap-Priorisierung)`
#### Zeilen 129-133:
**VORHER:**
```markdown
**Nach P2 (Week 5):** 65-70%
- 15-20 deprecated Platzhalter
- Technical Debt reduziert
```
**NACHHER:**
```markdown
**Nach P2 (Week 5):** 65-70%
- Integration-Roadmap für 30 geplante Platzhalter
- Prompt-Templates für 5-10 Quick Wins
- Nutzungsrate 40% → 50-60%
```
---
## 4. Neue Artefakte
### `USAGE_ROLE_CLASSIFICATION.md` (NEU)
Vollständige fachliche Klassifizierung aller 67 ungenutzten Placeholder:
- 30 unused_but_planned (mit Roadmap-Referenzen)
- 37 unused_but_plausible (mit fachlicher Begründung)
- 0 redundant_or_duplicate
Enthält:
- Detaillierte Gruppierung (Scores, Correlations, Ability Balance, etc.)
- Fachliche Rollen pro Gruppe
- Roadmap-Zuordnungen
- Prompt-Use-Case-Beispiele
---
## 5. Was sich NICHT geändert hat
**Unberührt (technisch korrekt):**
- Gap-Cluster-Report (02) - technische Gaps bleiben identisch
- Quellenprotokoll (05) - keine methodische Regel dokumentiert
- Alle technischen Findings (time_window, confidence, data_layer_module)
- P0/P1 Maßnahmen (vollständig technisch)
- Code-Docs-Konflikte (bleibt P0-kritisch)
- Best-Practice-Modelle (nutrition_avg, etc.)
---
## 6. Zusammenfassung der Revisionslogik
### Alt (FALSCH):
```
ungenutzt → Technical Debt → Deprecation-Review → 15-20 deprecaten
```
### Neu (KORREKT):
```
ungenutzt → Fachliche Klassifizierung → Integration-Planung → 5-10 aktivieren
45% geplant (Roadmap Phase 0c/1/2)
55% plausibel (Prompt-Use-Cases)
0% redundant
```
---
## 7. Methodische Regel (verbindlich)
**Für alle künftigen Audits:**
Ein Placeholder darf **nicht** als redundant, überflüssig oder Deprecation-Kandidat eingestuft werden, nur weil er aktuell:
- in keinem Prompt verwendet wird
- in keiner Pipeline referenziert wird
- in keinem Chart vorkommt
- derzeit nicht produktiv eingebunden ist
**Erlaubte Kriterien für "redundant":**
1. Keine fachliche Rolle im Datenmodell
2. Keine Rolle in Roadmap/Konzept
3. Echte inhaltliche Doppelung (2+ Placeholder mit identischer Semantik)
4. Fachlich ersetzt (expliziter Replacement dokumentiert)
5. Explizite Deprecation-Entscheidung (dokumentiert)
**"Heute nicht verwendet" allein ist ausdrücklich kein Redundanzbeweis.**
---
## 8. Impact der Revision
### Quantitativ:
- **Dokumente geändert:** 5 von 6 (nur Quellenprotokoll unberührt)
- **Abschnitte revidiert:** 10 (Executive Summary: 3, Offene Entscheidungen: 2, Maßnahmenplan: 2, Prüfmatrix: 1, README: 2)
- **Neue Artefakte:** 1 (USAGE_ROLE_CLASSIFICATION.md)
- **Gesamttext geändert:** ~30% der Audit-Berichte (primär Abschnitte zu ungenutzten Platzhaltern)
### Qualitativ:
- **Methodische Basis:** Roadmap-Konformität statt Nutzungsstatus
- **Ton:** Von "Problem/Technical Debt" zu "Geplant/Integration erforderlich"
- **Maßnahmen:** Von "Deprecation-Review" zu "Integration-Planung"
- **Ziel:** Von "15-20 deprecaten" zu "5-10 aktivieren"
---
**Revision abgeschlossen:** 30. März 2026
**Revisions-Aufwand:** ~2h (Klassifizierung + 5 Dokumente + Protokoll)
**Qualitätssicherung:** Cross-Check gegen Roadmap + Diagramm-Konzept v2 + DATA_ARCHITECTURE.md
---
**Nächste Schritte:**
1. User-Review der revidierten Dokumente
2. Validierung der Klassifizierung (30 geplant / 37 plausibel korrekt?)
3. Bei Approval: Audit als abgeschlossen markieren

View File

@ -0,0 +1,317 @@
# Usage Role Classification: 67 Ungenutzte Platzhalter
**Erstellt:** 30. März 2026 (Revision)
**Basis:** Roadmap, Diagramm-Konzept v2, DATA_ARCHITECTURE.md
**Zweck:** Methodische Korrektur - Nichtnutzung ≠ Redundanz
---
## Klassifizierungs-Prinzip
**Für dieses Projekt gilt:**
Die Prompt-Bibliothek ist im Aufbau (Phase 0b/0c/1/2 Roadmap).
Aktuelle Nichtnutzung ist kein Beweis für Überflüssigkeit.
**Strenge Kriterien für "redundant":**
Nur wenn **keine fachliche Rolle** in Roadmap, Konzept oder Datenmodell erkennbar ist.
---
## Klassifizierung der 67 ungenutzten Platzhalter
### 1. used_productively (44 Platzhalter - bereits in Nutzung)
**Nicht relevant** - diese sind NICHT ungenutzt.
---
### 2. unused_but_planned (30 Platzhalter - 45%)
**Definition:** Explizit in Roadmap Phase 0c/1/2 oder Diagramm-Konzept v2 dokumentiert.
#### A. Scores (6) - Phase 0c/Phase 1
- `activity_score`
- `nutrition_score`
- `recovery_score`
- `body_progress_score`
- `goal_progress_score`
- `data_quality_score`
**Roadmap:** Phase 0c (Multi-Layer Architecture) + Phase 1 (Charts)
**Konzept:** Diagramm-Konzept v2, Section 5 "Scores"
**Fachliche Rolle:** Composite Scores mit Confidence, Goal-Mode-abhängig
---
#### B. Correlations (5) - Phase 2
- `correlation_energy_weight_lag`
- `correlation_load_hrv`
- `correlation_load_rhr`
- `correlation_protein_lbm`
- `correlation_sleep_recovery`
**Roadmap:** Phase 2 (Engagement - Korrelationen)
**Konzept:** Diagramm-Konzept v2, Section 3.5 "Lag-basierte Zusammenhänge"
**Fachliche Rolle:** Diagnostische Auswertung, Min 21-28 Datenpunkte
---
#### C. Ability Balance (5) - Phase 1
- `ability_balance_coordination`
- `ability_balance_endurance`
- `ability_balance_mental`
- `ability_balance_mobility`
- `ability_balance_strength`
**Roadmap:** Phase 1 (Charts - Training Balance)
**Konzept:** DATA_ARCHITECTURE.md, abilities JSONB column
**Fachliche Rolle:** Trainingsverteilung über Fähigkeiten (28d)
---
#### D. Goals Details (11) - Phase 0b/0c (bereits vorhanden, noch nicht in Prompts integriert)
- `active_goals_json`, `active_goals_md`
- `focus_areas_weighted_json`, `focus_areas_weighted_md`, `focus_area_weights_json`
- `top_goal_name`, `top_goal_progress_pct`, `top_goal_status`
- `top_3_goals_behind_schedule`, `top_3_goals_on_track`, `top_3_focus_areas`
**Roadmap:** Phase 0b (Goal-Aware Placeholders) - DONE
**Konzept:** Goals System v2.0, Dynamic Focus Areas
**Fachliche Rolle:** Goal-Tracking für Prompts, JSON für strukturierte Ausgabe
**Status:** Backend implementiert, noch nicht in Prompt-Bibliothek integriert
---
#### E. Sleep Debt (1) - Phase 1
- `sleep_debt`
**Roadmap:** Phase 1 (Charts - Recovery)
**Konzept:** Diagramm-Konzept v2, Section 4.3 "Schlafschuld akkumuliert"
**Fachliche Rolle:** Kumulative Schlafschuld vs. Ziel (7-8h)
---
#### F. Plateau Detection (1) - Phase 1/2
- `plateau_detected`
**Roadmap:** Phase 1/2 (Diagnostics)
**Konzept:** Diagramm-Konzept v2, Section 3.4 "Muster, Plateaus"
**Fachliche Rolle:** Stagnations-Erkennung (Gewicht, BF%, LBM)
---
#### G. Top Drivers (1) - Phase 2
- `top_drivers`
**Roadmap:** Phase 2 (Correlations)
**Konzept:** Diagramm-Konzept v2, Section 3.5 "Wechselwirkungen"
**Fachliche Rolle:** Top-3 Einflussfaktoren auf Fortschritt
---
### 3. unused_but_plausible (37 Platzhalter - 55%)
**Definition:** Fachlich sinnvolle Rolle im Datenmodell, plausibel für Prompt-Bibliothek.
#### H. Body Deltas (5)
- `arm_28d_delta`, `chest_28d_delta`, `hip_28d_delta`, `thigh_28d_delta`, `waist_28d_delta`
**Fachliche Rolle:** Granulare Delta-Berechnungen für Umfänge (28d)
**Plausibel für:** Prompts die spezifische Körperteile analysieren (z.B. "Armumfang Fortschritt")
**Redundant?** NEIN - circ_summary ist Best-of-Each, Deltas sind zeitbasiert
**Status:** unused_but_plausible (für spezifische Prompts nützlich)
---
#### I. Nutrition Details (6)
- `energy_deficit_surplus`
- `intake_volatility`
- `nutrition_days`
- `protein_days_in_target`
- `protein_ziel_low`
- `protein_ziel_high`
**Fachliche Rolle:** Detaillierte Ernährungs-Metriken
**Plausibel für:** Prompts für Ernährungs-Coaching (Protein-Target-Range, Consistency)
**Redundant?** NEIN - ergänzen nutrition_avg/nutrition_score
**Status:** unused_but_plausible
---
#### J. Training Quality/Load (3)
- `monotony_score`
- `strain_score`
- `rest_day_compliance`
**Fachliche Rolle:** Übertrainings-Erkennung, Load-Monitoring
**Plausibel für:** Prompts für Recovery-Management
**Redundant?** NEIN - spezifische Load-Metriken
**Status:** unused_but_plausible
---
#### K. Focus Category Weights (7)
- `focus_cat_körper_weight`
- `focus_cat_ernährung_weight`
- `focus_cat_aktivität_weight`
- `focus_cat_recovery_weight`
- `focus_cat_vitalwerte_weight`
- `focus_cat_mental_weight`
- `focus_cat_lebensstil_weight`
**Fachliche Rolle:** User-Präferenzen für Score-Gewichtung
**Plausibel für:** Goal-Mode-abhängige Prompts
**Redundant?** NEIN - Teil des Dynamic Focus Areas v2.0 System
**Status:** unused_but_plausible (Phase 0b Feature)
---
#### L. Focus Category Progress (7)
- `focus_cat_körper_progress`
- `focus_cat_ernährung_progress`
- `focus_cat_aktivität_progress`
- `focus_cat_recovery_progress`
- `focus_cat_vitalwerte_progress`
- `focus_cat_mental_progress`
- `focus_cat_lebensstil_progress`
**Fachliche Rolle:** Fortschritts-Scores pro Kategorie
**Plausibel für:** Dashboard-Widgets, Kategorie-spezifische Analysen
**Redundant?** NEIN - Teil des Dynamic Focus Areas v2.0 System
**Status:** unused_but_plausible (Phase 0b Feature)
---
#### M. Meta/Convenience (3)
- `bmi`
- `waist_hip_ratio`
- `datum_heute`
**Fachliche Rolle:** Convenience-Placeholder für Prompts
**Plausibel für:** Prompt-Templates (bmi = "aktueller BMI", datum_heute = "Kontext-Datum")
**Redundant?** Technisch berechenbar, ABER Convenience hat Wert
**Status:** unused_but_plausible
---
#### N. Zeitraum Meta (3)
- `zeitraum_7d`
- `zeitraum_30d`
- `zeitraum_90d`
**Fachliche Rolle:** String-Konstanten für Zeitfenster in Prompts
**Plausibel für:** Prompt-Templates ("in den letzten {{zeitraum_7d}}")
**Redundant?** NEIN - Sprachliche Convenience
**Status:** unused_but_plausible
---
#### O. Weight Trend (1)
- `weight_90d_slope`
**Fachliche Rolle:** Langfrist-Trend (90d statt 28d)
**Plausibel für:** Prompts für langfristige Gewichtsentwicklung
**Redundant?** NEIN - ergänzt weight_trend (28d)
**Status:** unused_but_plausible
---
#### P. Recent Load (1)
- `recent_load_balance_3d`
**Fachliche Rolle:** Kurzfrist-Load-Balance (3d)
**Plausibel für:** Prompts für akute Recovery-Bewertung
**Redundant?** NEIN - ergänzt load_monitoring (7d/28d)
**Status:** unused_but_plausible
---
### 4. unused_and_unclear (0 Platzhalter)
**Keine** - alle 67 haben erkennbare fachliche Rolle.
---
### 5. redundant_or_duplicate (0 Platzhalter)
**Keine** - kein Platzhalter erfüllt die strengen Kriterien für Redundanz:
- Alle haben fachliche Rolle im Datenmodell
- Alle haben plausible Rolle in Roadmap/Konzept oder als Convenience
- Keine echten Duplikate
- Keine explizit ersetzten Platzhalter
---
## Zusammenfassung
| Status | Anzahl | Prozent | Interpretation |
|--------|--------|---------|----------------|
| **unused_but_planned** | 30 | 45% | Explizit in Roadmap Phase 0c/1/2 |
| **unused_but_plausible** | 37 | 55% | Fachlich sinnvoll, noch nicht in Prompts |
| **unused_and_unclear** | 0 | 0% | Keine |
| **redundant_or_duplicate** | 0 | 0% | Keine |
| **TOTAL** | 67 | 100% | Alle haben fachliche Berechtigung |
---
## Implikationen für Audit-Berichte
### Was sich ändert:
1. **Executive Summary:**
- ~~"60% ungenutzte Platzhalter"~~ als systemische Schwäche #5 → ENTFERNEN
- ~~"Technical Debt akkumuliert"~~ → ENTFERNEN
- ~~"Unklare Deprecation-Strategie"~~ → ENTFERNEN
- **NEU:** "67 Platzhalter noch nicht produktiv eingebunden (45% geplant in Phase 0c/1/2, 55% plausibel für Prompt-Bibliothek)"
2. **Offene Entscheidungen E2.1:**
- ~~"Ungenutzte Platzhalter - Behalten oder Deprecaten?"~~ → UMFORMULIEREN
- **NEU:** "Ungenutzte Platzhalter - Roadmap-Priorisierung und Integration-Timeline"
- Fokus: WANN integrieren, nicht OB deprecaten
3. **Maßnahmenplan P2.2:**
- ~~"Ungenutzte Platzhalter Deprecation-Review"~~ → UMFORMULIEREN
- **NEU:** "Ungenutzte Platzhalter - Fachliche Rolle klären und Integration planen"
- ~~"15-20 Deprecated Platzhalter"~~ als Deliverable → ENTFERNEN
4. **Prüfmatrix Summary:**
- ~~"Deprecation-Kandidaten"~~ Kategorien A/B/C → ERSETZEN
- **NEU:** "usage_role_classification" Kategorien (planned/plausible/unclear/redundant)
5. **README:**
- ~~"Technical Debt reduziert"~~ → UMFORMULIEREN
- **NEU:** "Platzhalter-Integration in Prompt-Bibliothek vorangetrieben"
---
## Empfohlene Maßnahmen (NEU)
**Statt Deprecation:**
### P2: Placeholder-Integration-Planung (4-6h)
**Ziel:** Integration ungenutzter Platzhalter in Prompt-Bibliothek priorisieren
**Prozess:**
1. **Meeting (2h):** Product + Tech Review der 67
- Gruppe A (unused_but_planned, 30): Timeline für Phase 0c/1/2 bestätigen
- Gruppe B (unused_but_plausible, 37): Prompt-Use-Cases identifizieren
2. **Dokumentation (1h):** Integration-Roadmap, Prompt-Kandidaten
3. **Implementation (1-2h):** Prompt-Templates erstellen (Quick Wins)
4. **Communication (1h):** Prompt-Autoren: "Neue Platzhalter verfügbar"
**Deliverables:**
- Integration-Timeline für 30 geplante Platzhalter
- Prompt-Use-Cases für 10-15 plausible Platzhalter
- 5-10 neue Prompt-Templates mit bisher ungenutzten Platzhaltern
**Impact:**
- Nutzungsrate steigt von 40% auf 50-60%
- Prompt-Bibliothek wird reichhaltiger
- Platzhalter-System zeigt Wert statt "Technical Debt"
---
**Audit-Revision durchgeführt von:** Claude Code
**Revisions-Datum:** 30. März 2026
**Methodische Basis:** Roadmap-Konformität statt Nutzungsstatus

View File

@ -0,0 +1,703 @@
# Placeholder-Gap- und Governance-Dokument für Vibe-Coding / Entwickler
**Datei:** `placeholder_requirements_for_development.md`
**Zweck:** Arbeitsgrundlage für Entwickler, damit die Prompt-Bibliothek mit stabilen, fachlich sauberen Platzhaltern betrieben werden kann.
**Status:** Draft / arbeitsfähig
**Bezug:** Prompt-Bibliothek, Report-Steckbriefe, bestehender Placeholder-Export, Datenarchitektur
---
## 1. Ziel dieses Dokuments
Dieses Dokument beschreibt:
1. **welche Platzhalter bereits vorhanden und nutzbar sind**
2. **welche Platzhalter fachlich geschärft werden müssen**
3. **welche neuen Platzhalter für V1 zusätzlich bereitgestellt werden sollten**
4. **welche Platzhalter nur als spätere Erweiterung gelten**
5. **welche Governance-Regeln für Benennung, Semantik und Versionierung gelten**
Wichtig:
Die Prompt-Bibliothek darf **nicht** darauf angewiesen sein, dass Folgechats spontan neue Platzhalter erfinden oder bestehende umdeuten.
Die API-/Export-Seite muss deshalb eine **kontrollierte, stabile Placeholder-Oberfläche** bereitstellen.
---
## 2. Grundprinzipien für Platzhalter
### 2.1 Platzhalter sind API-Verträge
Ein Platzhalter ist kein freier Text, sondern ein **stabiler Vertrag** zwischen:
- Datenmodell / Berechnungslogik
- Export-/Aggregationsebene
- Prompt-System
- UI / Auswertung / QA
### 2.2 Platzhalter dürfen nicht stillschweigend umdefiniert werden
Ein einmal eingeführter Platzhalter darf nicht:
- semantisch verändert werden
- in einem anderen Zeitraum berechnet werden
- seine Einheit ändern
- stillschweigend von Rohwert auf Trendwert wechseln
### 2.3 Fehlende Werte müssen explizit sein
Wenn ein Wert nicht berechnet werden kann, soll dies **strukturiert** erfolgen, nicht als vager Freitext.
Bevorzugt:
- `null`
- oder ein strukturierter Verfügbarkeitsstatus
Nur wenn das bestehende System es erzwingt, darf vorläufig `"nicht verfügbar"` verwendet werden.
### 2.4 Platzhalter sollen möglichst atomar sein
Bevorzugt:
- einzelne, klar definierte Felder
Nicht bevorzugt:
- sehr große, uneinheitliche Freitext-Blobs, die mehrere Logiken mischen
Trotzdem dürfen kompakte Summary-Platzhalter als Zusatz bleiben, z. B. für einfache Prompts oder UI-Kurztexte.
### 2.5 Zeitbezug muss immer eindeutig sein
Jeder zeitabhängige Platzhalter braucht klaren Fensterbezug:
- `*_today`
- `*_7d`
- `*_14d`
- `*_28d`
- `*_90d`
---
## 3. Bereits vorhandene und gut nutzbare Platzhalter
Diese Platzhalter sind bereits vorhanden und für V1 sinnvoll nutzbar.
## 3.1 Profil / Stammdaten
- `name`
- `age`
- `height`
- `geschlecht`
## 3.2 Körper
- `weight_aktuell`
- `weight_trend`
- `kf_aktuell`
- `bmi`
- `weight_7d_median`
- `waist_hip_ratio`
- `caliper_summary`
- `circ_summary`
## 3.3 Ernährung
- `kcal_avg`
- `protein_avg`
- `carb_avg`
- `fat_avg`
- `energy_balance_7d`
- `protein_g_per_kg`
- `protein_adequacy_28d`
- `macro_consistency_score`
- `energy_deficit_surplus`
## 3.4 Training / Aktivität
- `activity_summary`
- `activity_detail`
- `trainingstyp_verteilung`
- `training_minutes_week`
- `training_frequency_7d`
- `quality_sessions_pct`
- `proxy_internal_load_7d`
- `monotony_score`
- `strain_score`
- `rest_day_compliance`
## 3.5 Schlaf / Erholung
- `sleep_avg_duration`
- `sleep_avg_quality`
- `rest_days_count`
- `sleep_avg_duration_7d`
- `sleep_debt_hours`
- `sleep_regularity_proxy`
- `sleep_quality_7d`
## 3.6 Vitalwerte
- `vitals_avg_hr`
- `vitals_avg_hrv`
- `vitals_vo2_max`
- `rhr_vs_baseline_pct`
- `vo2max_trend_28d`
## 3.7 Scores / Meta
- `nutrition_score`
- `activity_score`
- `recovery_score`
- `data_quality_score`
## 3.8 Ziele / Fokus
- `top_goal_name`
- `top_goal_progress_pct`
- `top_goal_status`
- `top_focus_area_name`
- `top_focus_area_progress`
- `focus_cat_körper_progress`
- `focus_cat_körper_weight`
- `focus_cat_ernährung_progress`
- `focus_cat_ernährung_weight`
- `focus_cat_aktivität_progress`
- `focus_cat_aktivität_weight`
- `focus_cat_recovery_progress`
- `focus_cat_recovery_weight`
- `focus_cat_vitalwerte_progress`
- `focus_cat_vitalwerte_weight`
- `focus_cat_mental_progress`
- `focus_cat_mental_weight`
- `focus_cat_lebensstil_progress`
- `focus_cat_lebensstil_weight`
## 3.9 Zeitraum
- `datum_heute`
- `zeitraum_7d`
- `zeitraum_30d`
- `zeitraum_90d`
## 3.10 Korrelation / Diagnose
- `correlation_energy_weight_lag`
---
## 4. Bereits vorhandene Platzhalter mit Schärfungsbedarf
Diese Platzhalter existieren, sind aber fachlich oder operativ noch nicht sauber genug.
## 4.1 "nicht verfügbar" als String
Betroffen u. a.:
- `goal_progress_score`
- `body_progress_score`
- `fm_28d_change`
- `lbm_28d_change`
- `waist_28d_delta`
- `recomposition_quadrant`
- `ability_balance_strength`
- `ability_balance_endurance`
- `hrv_vs_baseline_pct`
- weitere ähnliche Felder
### Problem
- Die Prompt-Logik muss String-Inhalte interpretieren.
- Folgeprompts können schwer unterscheiden zwischen:
- echter Null
- fehlendem Wert
- noch nicht implementiert
- Daten reichen nicht
### Empfehlung
Zusätzlich zu jedem kritischen Feld:
- `*_available: true|false`
- optional `*_reason_unavailable`
Oder besser:
- ein strukturiertes Domain-Availability-Objekt
---
## 4.2 Prozent-/Score-Platzhalter ohne standardisierte Skala
Betroffen:
- `quality_sessions_pct`
- `protein_adequacy_28d`
- `macro_consistency_score`
- `nutrition_score`
- `activity_score`
- `recovery_score`
- `data_quality_score`
### Problem
Die Skala ist im Prompt nicht immer selbsterklärend:
- 0100?
- Prozent?
- normierter Score?
- je höher desto besser?
### Empfehlung
Für jeden solchen Platzhalter intern dokumentieren:
- Skala
- Richtung
- Berechnungsbasis
- sinnvolle Interpretationszonen
Optional zusätzliche Entwickler-Metadaten:
- `score_meta_<name>`
---
## 4.3 Summary-Felder als alleinige Quelle
Betroffen:
- `activity_summary`
- `caliper_summary`
- `circ_summary`
### Problem
Diese Felder sind nützlich für kompakte Reports, aber für robuste Mehrstufigkeit oft zu grob.
### Empfehlung
Summary-Felder behalten, aber ergänzen durch strukturierte Roh-/Aggregatfelder.
---
## 4.4 Fokuskategorien nicht sauber auf 100 normiert
Im Export fallen z. B. Gewichte wie `135.0` oder andere Summen auf.
### Problem
Für Prompts und Zielgewichtung ist unklar:
- sind das Rohgewichte?
- normierte Gewichte?
- gruppeninterne Punkte?
- Prozentangaben?
### Empfehlung
Klar trennen zwischen:
- `focus_cat_*_weight_raw`
- `focus_cat_*_weight_normalized`
Und ebenso für einzelne Fokusbereiche.
---
## 4.5 Zeitfenster-Mischung
Einige Felder sind 7d, andere 14d, 28d oder unklar verdichtet.
### Problem
Prompts können unabsichtlich Zeitfenster vergleichen, die nicht zusammenpassen.
### Empfehlung
Zeitfenster im Feldnamen vereinheitlichen und konsequent dokumentieren.
---
## 5. Neue Platzhalter, die für V1 zusätzlich bereitgestellt werden sollten
Diese Liste ist die wichtigste operative To-do-Liste für die Entwicklung.
## 5.1 Höchste Priorität Zielsystem / Fokus / Kontext
### P1. `goal_summary_json`
**Zweck:** kompakte, strukturierte Zielübersicht für Zielreports und zielgewichtete Synthesen.
**Sollinhalt:**
- Liste aktiver Ziele
- je Ziel:
- ID
- Name
- Typ
- Status
- Startwert
- Zielwert
- aktueller Wert
- Fortschritt
- Zieltermin (falls vorhanden)
- primary_flag
- zugeordnete Fokusbereiche
**Warum wichtig:**
Die aktuelle Prompt-Bibliothek braucht mehr als nur `top_goal_name`.
---
### P2. `focus_area_summary_json`
**Zweck:** strukturierte Liste der aktiven Fokusbereiche.
**Sollinhalt:**
- Fokusbereichsname
- Gruppe/Kategorie
- Rohgewicht
- normiertes Gewicht
- aktueller Progress
- verknüpfte Ziele
**Warum wichtig:**
Top-Focus allein reicht für personalisierte Reports nicht.
---
### P3. `goal_progress_score_available`
**Typ:** bool
**Warum:** sauber zwischen fehlend und vorhanden unterscheiden.
### P4. `body_progress_score_available`
**Typ:** bool
**Warum:** dito.
---
## 5.2 Höchste Priorität Domain Availability / Confidence
### P5. `domain_availability_json`
**Zweck:** zentrale Verfügbarkeit pro Domäne.
**Sollinhalt:**
- body
- nutrition
- activity
- sleep
- recovery
- vitals
- goals
- focus_areas
- correlations
jeweils mit:
- available
- confidence
- main_gaps
- last_valid_window
**Warum wichtig:**
Vereinfacht Fallbacks und verhindert Scheinpräzision.
---
### P6. `domain_confidence_json`
Falls `domain_availability_json` nicht reicht oder zu groß wird.
---
### P7. `critical_missing_fields_json`
**Zweck:** welche fehlenden Daten würden den größten Erkenntnisgewinn bringen?
**Warum wichtig:**
Für Datenqualitätsreport und Nutzerführung.
---
## 5.3 Höchste Priorität Körperentwicklung
### P8. `fm_28d_change_available`
### P9. `lbm_28d_change_available`
### P10. `waist_28d_delta_available`
### P11. `recomposition_quadrant_available`
**Warum:**
Körper- und Plateau-Reports brauchen robuste Verfügbarkeitslogik.
---
### P12. `body_change_summary_json`
**Zweck:** strukturierte Körperentwicklung.
**Sollinhalt:**
- weight trend
- FM delta
- LBM delta
- waist delta
- hip delta
- recomposition quadrant
- confidence
---
## 5.4 Höchste Priorität Training / Belastung
### P13. `activity_structure_json`
**Zweck:** kompakter Trainingsstrukturblock.
**Sollinhalt:**
- Volumen
- Frequenz
- Typverteilung
- Qualitätsanteil
- Lastniveau
- Monotonie
- Strain
- Rest-day compliance
- auffällige Muster
---
### P14. `training_quality_score`
**Zweck:** standardisierte Trainingsqualitätsbewertung über Sessions hinweg
**Warum:**
Aktuell gibt es `quality_sessions_pct`, aber noch keinen robusten verdichteten Qualitätsanker.
---
### P15. `load_balance_class`
Wertebeispiele:
- low
- moderate
- high
- strained
**Warum:**
Erleichtert Diagnose-Prompts.
---
## 5.5 Höchste Priorität Schlaf / Recovery / Vitals
### P16. `sleep_summary_json`
**Sollinhalt:**
- duration
- quality
- debt
- regularity
- trend
- confidence
### P17. `recovery_summary_json`
**Sollinhalt:**
- recovery score
- main drivers
- RHR status
- HRV status
- recent load interaction
- confidence
### P18. `vitals_summary_json`
**Sollinhalt:**
- resting HR
- HRV
- VO2max
- trend
- baseline deviation
- confidence
### P19. `hrv_vs_baseline_pct_available`
### P20. `rhr_vs_baseline_pct_available`
---
## 5.6 Höchste Priorität Korrelation / Diagnose
### P21. `correlation_summary_json`
**Zweck:** strukturierte Zusammenfassung mehrerer Korrelationen / Lag-Beziehungen.
**Sollinhalt:**
- energy_weight_lag
- training_hrv_lag
- training_rhr_lag
- sleep_recovery_lag
- confidence je Zusammenhang
---
### P22. `plateau_status`
Wertebeispiele:
- likely
- possible
- not_detected
- insufficient_data
### P23. `top_drivers_positive_json`
### P24. `top_drivers_negative_json`
**Warum:**
Top-Driver-Report und Plateau-Detektor profitieren stark von einer vorberechneten Zwischenebene.
---
## 5.7 Höchste Priorität Energieverfügbarkeit / Schutzlogik
### P25. `underfueling_risk_flag`
**Typ:** bool / enum
### P26. `underfueling_risk_reason`
**Typ:** text / enum / json
### P27. `energy_availability_summary_json`
**Sollinhalt:**
- intake adequacy
- deficit magnitude
- load context
- recovery context
- warning level
- confidence
---
## 6. Mittlere Priorität sollte bald folgen
## 6.1 Gesundheitsstabilität
### P28. `health_stability_score`
### P29. `health_stability_summary_json`
## 6.2 Blutdruck
### P30. `blood_pressure_summary_json`
**Sollinhalt:**
- mean systolic/diastolic
- category
- contexts
- trend
- irregularity flags
- confidence
## 6.3 Fokus-/Zielgewichtung
### P31. `goal_weighted_priority_json`
**Zweck:** priorisierte Ziel-/Fokuslogik für Synthese
## 6.4 Reporting-Hilfsfelder
### P32. `main_constraint_json`
### P33. `main_strength_json`
### P34. `next_best_actions_json`
---
## 7. Niedrigere Priorität / spätere Ausbaustufe
Diese Platzhalter sind wertvoll, aber für V1 nicht zwingend:
- `session_rpe`
- `fatigue_score`
- `pain_flag`
- `illness_flag`
- `travel_flag`
- `competition_flag`
- `measurement_time_consistency`
- `training_age`
- `secondary_goals_json`
- `waist_to_height_ratio`
- `sleep_segment_quality_score`
- `steps_summary_json`
---
## 8. Platzhalter, die geschärft statt neu erfunden werden sollten
Statt wild neue Namen einzuführen, sollten folgende Bereiche zuerst **semantisch sauber definiert** werden.
## 8.1 Goal / Focus
Bestehend, aber zu flach:
- `top_goal_name`
- `top_goal_progress_pct`
- `top_focus_area_name`
- `top_focus_area_progress`
**Schärfung:**
Ergänzen durch strukturierte JSONs, nicht ersetzen.
## 8.2 Körperentwicklung
Bestehend, aber oft nicht verfügbar:
- `fm_28d_change`
- `lbm_28d_change`
- `waist_28d_delta`
- `recomposition_quadrant`
**Schärfung:**
Verfügbarkeitsflags + strukturierte Summary.
## 8.3 Activity-Qualität
Bestehend:
- `quality_sessions_pct`
**Schärfung:**
ergänzen durch:
- `training_quality_score`
- ggf. `quality_sessions_count`
- optional `quality_definition_version`
## 8.4 Recovery
Bestehend:
- `recovery_score`
**Schärfung:**
ergänzen durch Komponenten-/Treiberdarstellung.
## 8.5 Data Quality
Bestehend:
- `data_quality_score`
**Schärfung:**
ergänzen durch Domain-Ebene und konkrete Lückenbeschreibung.
---
## 9. Governance-Regeln für Platzhalter
Diese Regeln sollten für Entwicklung und Prompting verbindlich sein.
### G1. Keine eigenmächtige Umbenennung
Bestehende Platzhalter dürfen nicht ohne Migration umbenannt werden.
### G2. Keine stillschweigende Semantikänderung
Wenn Berechnungslogik geändert wird, braucht der Platzhalter:
- Versionierung intern
- oder einen neuen Namen
### G3. Neue Platzhalter nur kontrolliert
Neue Platzhalter nur einführen, wenn:
- Zweck klar
- Datenquelle klar
- Zeitfenster klar
- Einheit klar
- Fallback klar
### G4. JSON vor Freitext
Für komplexe Mehrstufenlogik sollen neue Platzhalter bevorzugt als strukturierte JSON-Container bereitgestellt werden.
### G5. Zeitfenster im Namen
Zeitfenster müssen im Namen explizit sein, falls nicht universell.
### G6. Verfügbarkeit trennen von Inhalt
Für kritische Felder besser:
- Wert
- Verfügbarkeitsflag
- optional Grund
### G7. Keine Ad-hoc-Platzhalter in Folgechats
Folgechats für Prompt-Ausarbeitung dürfen keine neuen Platzhalter stillschweigend voraussetzen.
---
## 10. Konkrete Entwickler-Backlog-Empfehlung
## Sprint / Paket 1 notwendig für robuste V1-Reports
1. `goal_summary_json`
2. `focus_area_summary_json`
3. `domain_availability_json`
4. `critical_missing_fields_json`
5. Verfügbarkeitsflags für Body-Change-Felder
6. `sleep_summary_json`
7. `recovery_summary_json`
8. `activity_structure_json`
9. `correlation_summary_json`
10. `plateau_status`
## Sprint / Paket 2 starke Verbesserung der Ziel- und Diagnoseebene
1. `top_drivers_positive_json`
2. `top_drivers_negative_json`
3. `energy_availability_summary_json`
4. `underfueling_risk_flag`
5. `health_stability_score`
6. `blood_pressure_summary_json`
7. `goal_weighted_priority_json`
## Sprint / Paket 3 spätere Verfeinerung
1. `training_quality_score`
2. `session_rpe`
3. `fatigue_score`
4. `measurement_time_consistency`
5. `illness_flag` / `travel_flag`
6. `waist_to_height_ratio`
---
## 11. Abnahmekriterien für die Placeholder-Schicht
Die Placeholder-Schicht ist fachlich gut genug, wenn:
1. **alle Core-Reports der V1-Bibliothek ohne stillschweigende Erfindungen lauffähig sind**
2. fehlende Werte **strukturiert** behandelt werden können
3. Ziel-/Fokus-Logik nicht nur über ein einzelnes „Top“-Feld abgebildet wird
4. Datenqualität und Confidence nicht nur global, sondern domänenspezifisch beschrieben werden
5. Diagnose-Reports mindestens auf vorbereiteten Zwischenobjekten statt nur Freitextsummaries aufsetzen können
---
## 12. Kurzfazit
Für V1 ist die Placeholder-Basis bereits gut, aber noch nicht stabil genug für eine große professionelle Prompt-Bibliothek.
Der größte Handlungsbedarf liegt in vier Bereichen:
- **strukturierte Ziel-/Fokus-JSONs**
- **domänenspezifische Availability-/Confidence-Objekte**
- **robustere Diagnose-/Driver-/Plateau-Zwischenfelder**
- **saubere Verfügbarkeits- und Schärfungslogik für kritische Body-/Recovery-Felder**
Dieses Dokument ist bewusst entwicklungsnah formuliert und soll als Arbeitsgrundlage für die Implementierung dienen.

View File

@ -0,0 +1,151 @@
# Draft Corrections Required
**Generated:** 2026-03-30 14:58:37
This document lists all placeholders where the draft specification is missing, wrong, or incomplete.
## Summary
- **Missing:** 111 placeholders
- **Partial:** 0 placeholders
- **Wrong:** 0 placeholders
## Missing Draft Specifications
**111 placeholders** lack draft specifications:
- `ability_balance_coordination` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `ability_balance_endurance` (Category: Training, Compliance: non_compliant, Priority: P0)
- `ability_balance_mental` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `ability_balance_mobility` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `ability_balance_strength` (Category: Training, Compliance: non_compliant, Priority: P0)
- `active_goals_json` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `active_goals_md` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `activity_detail` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `activity_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `activity_summary` (Category: Training, Compliance: non_compliant, Priority: P0)
- `age` (Category: Profil, Compliance: compliant, Priority: P3)
- `arm_28d_delta` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `bmi` (Category: Körper, Compliance: partially_compliant, Priority: P0)
- `body_progress_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `caliper_summary` (Category: Unknown, Compliance: partially_compliant, Priority: P0)
- `carb_avg` (Category: Ernährung, Compliance: compliant, Priority: P3)
- `chest_28d_delta` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `circ_summary` (Category: Unknown, Compliance: compliant, Priority: P3)
- `correlation_energy_weight_lag` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `correlation_load_hrv` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `correlation_load_rhr` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `correlation_protein_lbm` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `correlation_sleep_recovery` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `data_quality_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `datum_heute` (Category: Zeitraum, Compliance: non_compliant, Priority: P0)
- `energy_balance_7d` (Category: Ernährung, Compliance: non_compliant, Priority: P2)
- `energy_deficit_surplus` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `fat_avg` (Category: Ernährung, Compliance: compliant, Priority: P3)
- `fm_28d_change` (Category: Körper, Compliance: non_compliant, Priority: P2)
- `focus_area_weights_json` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_areas_weighted_json` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_areas_weighted_md` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_aktivität_progress` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_aktivität_weight` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_ernährung_progress` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_ernährung_weight` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_körper_progress` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_körper_weight` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `focus_cat_lebensstil_progress` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_lebensstil_weight` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_mental_progress` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_mental_weight` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_recovery_progress` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_recovery_weight` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_vitalwerte_progress` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `focus_cat_vitalwerte_weight` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `geschlecht` (Category: Profil, Compliance: non_compliant, Priority: P0)
- `goal_bf_pct` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `goal_progress_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `goal_weight` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `height` (Category: Profil, Compliance: non_compliant, Priority: P0)
- `hip_28d_delta` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `hrv_vs_baseline_pct` (Category: Vitalwerte, Compliance: partially_compliant, Priority: P0)
- `intake_volatility` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `kcal_avg` (Category: Ernährung, Compliance: compliant, Priority: P3)
- `kf_aktuell` (Category: Körper, Compliance: partially_compliant, Priority: P1)
- `lbm_28d_change` (Category: Körper, Compliance: non_compliant, Priority: P2)
- `macro_consistency_score` (Category: Ernährung, Compliance: non_compliant, Priority: P0)
- `monotony_score` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `name` (Category: Profil, Compliance: non_compliant, Priority: P0)
- `nutrition_days` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `nutrition_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `plateau_detected` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `protein_adequacy_28d` (Category: Ernährung, Compliance: non_compliant, Priority: P2)
- `protein_avg` (Category: Ernährung, Compliance: compliant, Priority: P3)
- `protein_days_in_target` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `protein_g_per_kg` (Category: Ernährung, Compliance: non_compliant, Priority: P0)
- `protein_ziel_high` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `protein_ziel_low` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `proxy_internal_load_7d` (Category: Training, Compliance: partially_compliant, Priority: P1)
- `quality_sessions_pct` (Category: Training, Compliance: partially_compliant, Priority: P0)
- `recent_load_balance_3d` (Category: Unknown, Compliance: partially_compliant, Priority: P0)
- `recomposition_quadrant` (Category: Körper, Compliance: non_compliant, Priority: P0)
- `recovery_score` (Category: Scores (Phase 0b), Compliance: non_compliant, Priority: P0)
- `rest_day_compliance` (Category: Training, Compliance: partially_compliant, Priority: P0)
- `rest_days_count` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P0)
- `rhr_vs_baseline_pct` (Category: Vitalwerte, Compliance: partially_compliant, Priority: P0)
- `sleep_avg_duration` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P1)
- `sleep_avg_duration_7d` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P1)
- `sleep_avg_quality` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P1)
- `sleep_debt_hours` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P0)
- `sleep_quality_7d` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P1)
- `sleep_regularity_proxy` (Category: Schlaf & Erholung, Compliance: partially_compliant, Priority: P0)
- `strain_score` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `thigh_28d_delta` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_3_focus_areas` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_3_goals_behind_schedule` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_3_goals_on_track` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_drivers` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_focus_area_name` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `top_focus_area_progress` (Category: Focus Areas, Compliance: non_compliant, Priority: P0)
- `top_goal_name` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_goal_progress_pct` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `top_goal_status` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `training_frequency_7d` (Category: Training, Compliance: partially_compliant, Priority: P1)
- `training_minutes_week` (Category: Training, Compliance: partially_compliant, Priority: P1)
- `trainingstyp_verteilung` (Category: Training, Compliance: non_compliant, Priority: P0)
- `vitals_avg_hr` (Category: Vitalwerte, Compliance: partially_compliant, Priority: P1)
- `vitals_avg_hrv` (Category: Vitalwerte, Compliance: partially_compliant, Priority: P1)
- `vitals_vo2_max` (Category: Vitalwerte, Compliance: partially_compliant, Priority: P0)
- `vo2max_trend_28d` (Category: Vitalwerte, Compliance: non_compliant, Priority: P2)
- `waist_28d_delta` (Category: Körper, Compliance: non_compliant, Priority: P2)
- `waist_hip_ratio` (Category: Körper, Compliance: partially_compliant, Priority: P0)
- `weight_28d_slope` (Category: Körper, Compliance: non_compliant, Priority: P2)
- `weight_7d_median` (Category: Körper, Compliance: non_compliant, Priority: P2)
- `weight_90d_slope` (Category: Unknown, Compliance: non_compliant, Priority: P0)
- `weight_aktuell` (Category: Körper, Compliance: compliant, Priority: P3)
- `weight_trend` (Category: Körper, Compliance: compliant, Priority: P3)
- `zeitraum_30d` (Category: Zeitraum, Compliance: non_compliant, Priority: P2)
- `zeitraum_7d` (Category: Zeitraum, Compliance: non_compliant, Priority: P2)
- `zeitraum_90d` (Category: Unknown, Compliance: non_compliant, Priority: P0)
## Partial Draft Specifications
No placeholders with partial drafts.
## Wrong Draft Specifications
No placeholders with wrong drafts.
## Recommendations
1. **Priority 1:** Add draft specifications for all missing placeholders
2. **Priority 2:** Complete partial draft specifications
3. **Priority 3:** Correct wrong draft specifications
### Bulk Update Strategy
Use audit semantic analysis reports to bulk-populate:
- Category classifications
- Descriptions
- Time windows
- Source information
Estimated effort: 6-10 hours for bulk updates.

View File

@ -0,0 +1,119 @@
# Executive Reconciliation Summary
**Generated:** 2026-03-30 14:58:37
**Scope:** 111 Placeholders
**Data Sources:** Export Catalog, Audit Reports (2026-03-29), Draft Document
## Overview
This reconciliation consolidates data from three authoritative sources to create a single source of truth for all 111 placeholders.
### Key Findings
- **71** placeholders (63%) are implemented in code
- **8** placeholders (7%) are fully compliant with normative requirements
- **22** placeholders (20%) are partially compliant
- **81** placeholders (73%) are non-compliant
### Reconciliation Results
- **Verified Match (7):** Draft, Export, and Audit align perfectly
- **Needs Sharpening (49):** Correct but incomplete metadata
- **Needs Refactor (16):** Code-docs conflicts exist
- **Draft Wrong (0):** Draft specification incorrect
- **New Required (39):** Not implemented, needs build
- **Unclear (0):** Manual review needed
### Priority Distribution
- **P0 (Critical):** 83 placeholders
- High usage (breaking change risk)
- Missing documentation (category/description)
- Unknown time window
- **P1 (High):** 10 placeholders
- Partially compliant with minor gaps
- **P2 (Medium):** 10 placeholders
- Non-compliant, needs work
- **P3 (Low):** 8 placeholders
- Compliant, maintenance only
## Critical Issues
### 1. Known Code-Documentation Conflicts
- **`weight_trend`**: Code uses 28d, docs say 7d/30d
- Resolution: Update docs to match code (28d)
- **`activity_summary`**: Code uses 14d, docs say 7d
- Resolution: Update docs to match code (14d)
- **`activity_detail`**: Time window unclear in code
- Resolution: Needs code review to determine actual time window
### 2. High-Usage Placeholders (Breaking Change Risk)
- **`name`** (19 uses, non_compliant)
- **`geschlecht`** (14 uses, non_compliant)
- **`height`** (12 uses, non_compliant)
- **`weight_aktuell`** (10 uses, compliant)
- **`weight_trend`** (10 uses, compliant)
- **`goal_bf_pct`** (10 uses, non_compliant)
- **`caliper_summary`** (8 uses, partially_compliant)
- **`circ_summary`** (8 uses, compliant)
- **`goal_weight`** (8 uses, non_compliant)
- **`protein_ziel_low`** (7 uses, non_compliant)
- **`protein_ziel_high`** (7 uses, non_compliant)
- **`activity_detail`** (4 uses, non_compliant)
### 3. Missing Documentation (P0)
**49** placeholders lack category classification
**49** placeholders lack descriptions
## Recommendations
### Immediate Actions (P0)
1. **Resolve Known Conflicts (3 placeholders, 2-4 hours)**
- Update documentation to match code implementation
- Validate time windows in code
2. **Classify Time Windows (74 placeholders, 8-12 hours)**
- Use name-based extraction for `*_7d`, `*_28d` patterns
- Extract from code default parameters
- Manual classification for unclear cases
3. **Add Categories and Descriptions (49 placeholders, 4-6 hours)**
- Bulk update from audit semantic analysis
- Use existing audit report classifications
### Next Sprint (P1)
1. **Add Confidence Logic (103 placeholders, 12-16 hours)**
- Implement for trend/delta placeholders
- Add min-data thresholds
2. **Document Data Layer Modules (100 placeholders, 6-8 hours)**
- Trace resolver functions to data layer
- Document source tables
### Later (P2-P3)
1. **Integrate Unused Placeholders (67 placeholders)**
- Create prompt use cases
- Plan integration timeline
2. **Achieve Production Status (30+ placeholders)**
- Reach 80%+ metadata completeness
- Complete all normative requirements
## Conclusion
The reconciliation process successfully analyzed all 111 placeholders.
While only 8 are currently fully compliant,
the systematic gaps are primarily documentation-related rather than functional.
With a structured remediation plan (estimated 82-110 hours), the system can reach
>60% normative conformity within 4-6 weeks.
**Next Steps:**
1. Review this summary with product/tech leads
2. Prioritize P0 items for immediate action
3. Begin systematic remediation following the implementation waves

View File

@ -0,0 +1,142 @@
# Implementation Waves
**Generated:** 2026-03-30 14:58:37
This document outlines a cluster-based implementation plan for placeholder remediation.
## Overview
Total remediation effort: **82-110 hours** over 4-6 weeks
### Timeline
- **Wave 1 (P0):** Week 1 - Critical fixes (14-20 hours)
- **Wave 2 (P1):** Weeks 2-3 - High priority (26-34 hours)
- **Wave 3 (P2):** Weeks 4-5 - Medium priority (18-24 hours)
- **Wave 4 (P3):** Later - Nice to have (24-32 hours)
## Wave 1: Critical Fixes (P0)
**Scope:** 83 placeholders
**Timeline:** Week 1
**Effort:** 14-20 hours
### 1.1 Resolve Known Conflicts (3 placeholders, 2 hours)
- `weight_trend`: Update docs to match code (28d)
- `activity_summary`: Update docs to match code (14d)
- `activity_detail`: Needs code review to determine actual time window
### 1.2 Classify Time Windows (74 placeholders, 8-12 hours)
**Method:**
1. Name-based extraction (`*_7d`, `*_28d` patterns) - automatic
2. Code parameter extraction - semi-automatic
3. Manual classification for unclear cases
### 1.3 Add Categories and Descriptions (49 placeholders, 4-6 hours)
**Method:**
- Bulk update from audit semantic analysis
- Use provided classifications from audit report
## Wave 2: High Priority (P1)
**Scope:** 10 placeholders
**Timeline:** Weeks 2-3
**Effort:** 26-34 hours
### 2.1 Add Confidence Logic (11 trend/delta placeholders, 12-16 hours)
**Placeholders:**
- `weight_28d_slope`, `weight_90d_slope`, `weight_7d_median`
- `fm_28d_change`, `lbm_28d_change`
- `waist_28d_delta`, `hip_28d_delta`, `chest_28d_delta`, `arm_28d_delta`, `thigh_28d_delta`
- `vo2max_trend_28d`
**Pattern:** `confidence = calculate_confidence(data_points, time_window_days, 'trend')`
### 2.2 Structured Missing-Value Policy (70 placeholders, 8-10 hours)
**Refactor:**
- Keep legacy string for backward compatibility
- Add structured fields: `available`, `missing_reason`, `value_raw`
### 2.3 Document Data Layer Modules (100 placeholders, 6-8 hours)
**Method:**
- Trace resolver functions to data layer
- Document source tables from SQL queries
## Wave 3: Medium Priority (P2)
**Scope:** 10 placeholders
**Timeline:** Weeks 4-5
**Effort:** 18-24 hours
### 3.1 Integrate Unused Placeholders (67 placeholders, 4-6 hours)
**Method:**
- Product management review for 30 planned placeholders
- Technical review for 37 plausible placeholders
- Create prompt use cases (5-10 quick wins)
### 3.2 Metadata Completeness (111 placeholders, 10-12 hours)
**Target:** Minimum 60% of placeholders with score >60
### 3.3 Production Status (20-30 core placeholders, 4-6 hours)
**Criteria:**
- Metadata completeness >= 80%
- Used-by >= 1
- No known issues
- Time window + confidence defined
## Wave 4: Nice to Have (P3)
**Scope:** 8 placeholders
**Timeline:** Later
**Effort:** 24-32 hours
### 4.1 Validation Framework (16-20 hours)
**Features:**
- Pre-commit hook for normative spec validation
- CI/CD consistency checks (code-catalog)
- Template generator for new placeholders
### 4.2 Migration Guides (8-12 hours)
**Content:**
- Best-practice guide (based on compliant examples)
- Anti-patterns to avoid
- Upgrade path for legacy prompts
## Dependencies
- **Wave 1 → Wave 2:** Time window classification must be complete before confidence logic
- **Wave 2 → Wave 3:** Data layer documentation enables better integration planning
- **Wave 3 → Wave 4:** Production-ready placeholders provide best-practice models
## Success Metrics
### After Wave 1:
- 0 unknown time windows
- 0 unknown categories
- 0 code-documentation conflicts
### After Wave 2:
- 70%+ placeholders with confidence logic
- 100% placeholders with structured missing-value policies
- 100% placeholders with documented data layers
### After Wave 3:
- 50-60% placeholder usage rate
- 60%+ placeholders with metadata completeness >60
- 20-30 production-ready placeholders
### After Wave 4:
- Automated validation in CI/CD
- Best-practice documentation for developers
- Sustainable maintenance process

View File

@ -0,0 +1,242 @@
# Placeholder Reconciliation Matrix
**Generated:** 2026-03-30 14:58:37
**Total Placeholders:** 111
## Statistics Summary
- **Exists in Code:** 71/111 (63%)
### Draft Status
- Full: 0
- Partial: 0
- Missing: 111
- Wrong: 0
### Compliance Level (from Audit)
- Compliant: 8 (7%)
- Partially Compliant: 22 (20%)
- Non-Compliant: 81 (73%)
### Reconciliation Status
- Verified Match: 7
- Needs Sharpening: 49
- Needs Refactor: 16
- Draft Wrong: 0
- New Required: 39
- Unclear: 0
### Confidence Distribution
- High: 10
- Medium: 49
- Low: 52
### Priority Distribution
- P0 (Critical): 83
- P1 (High): 10
- P2 (Medium): 10
- P3 (Low): 8
## Full Matrix
| # | Key | Exists | Draft | Category | Time Window | Used By | Compliance | Priority | Reconciliation | Action | Confidence | Notes |
|---|-----|--------|-------|----------|-------------|---------|------------|----------|----------------|--------|------------|-------|
| 1 | `ability_balance_coordination` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 2 | `ability_balance_endurance` | YES | missing | Training | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 3 | `ability_balance_mental` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 4 | `ability_balance_mobility` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 5 | `ability_balance_strength` | YES | missing | Training | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 6 | `active_goals_json` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 7 | `active_goals_md` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 8 | `activity_detail` | YES | missing | Unknown | unknown | 4 | non_compliant | P0 | needs_refactor | code_change | high | Implementation exists but lacks documentation | Category needs classification | ... |
| 9 | `activity_score` | YES | missing | Scores (Phase 0b) | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 10 | `activity_summary` | YES | missing | Training | unknown | 2 | non_compliant | P0 | needs_refactor | code_change | high | Time window needs classification | Known conflict: Code uses 14d, docs say 7d | ... |
| 11 | `age` | YES | missing | Profil | latest | 2 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | Draft specification missing for im... |
| 12 | `arm_28d_delta` | NO | missing | Unknown | 28d | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 13 | `bmi` | YES | missing | Körper | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 14 | `body_progress_score` | YES | missing | Scores (Phase 0b) | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 15 | `caliper_summary` | YES | missing | Unknown | unknown | 8 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 16 | `carb_avg` | YES | missing | Ernährung | 30d | 2 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | Draft specification missing for im... |
| 17 | `chest_28d_delta` | NO | missing | Unknown | 28d | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 18 | `circ_summary` | YES | missing | Unknown | mixed | 8 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | High usage (8 references) - breaki... |
| 19 | `correlation_energy_weight_lag` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 20 | `correlation_load_hrv` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 21 | `correlation_load_rhr` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 22 | `correlation_protein_lbm` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 23 | `correlation_sleep_recovery` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 24 | `data_quality_score` | YES | missing | Scores (Phase 0b) | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 25 | `datum_heute` | YES | missing | Zeitraum | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 26 | `energy_balance_7d` | YES | missing | Ernährung | 7d | 1 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 27 | `energy_deficit_surplus` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 28 | `fat_avg` | YES | missing | Ernährung | 30d | 2 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | Draft specification missing for im... |
| 29 | `fm_28d_change` | YES | missing | Körper | 28d | 1 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 30 | `focus_area_weights_json` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 31 | `focus_areas_weighted_json` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 32 | `focus_areas_weighted_md` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 33 | `focus_cat_aktivität_progress` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 34 | `focus_cat_aktivität_weight` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 35 | `focus_cat_ernährung_progress` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 36 | `focus_cat_ernährung_weight` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 37 | `focus_cat_körper_progress` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 38 | `focus_cat_körper_weight` | YES | missing | Focus Areas | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 39 | `focus_cat_lebensstil_progress` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 40 | `focus_cat_lebensstil_weight` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 41 | `focus_cat_mental_progress` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 42 | `focus_cat_mental_weight` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 43 | `focus_cat_recovery_progress` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 44 | `focus_cat_recovery_weight` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 45 | `focus_cat_vitalwerte_progress` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 46 | `focus_cat_vitalwerte_weight` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 47 | `geschlecht` | YES | missing | Profil | latest | 14 | non_compliant | P0 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | High usage (14 references) - breaking ch... |
| 48 | `goal_bf_pct` | YES | missing | Unknown | unknown | 10 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
| 49 | `goal_progress_score` | YES | missing | Scores (Phase 0b) | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 50 | `goal_weight` | YES | missing | Unknown | unknown | 8 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
| 51 | `height` | YES | missing | Profil | latest | 12 | non_compliant | P0 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | High usage (12 references) - breaking ch... |
| 52 | `hip_28d_delta` | NO | missing | Unknown | 28d | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 53 | `hrv_vs_baseline_pct` | YES | missing | Vitalwerte | unknown | 1 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 54 | `intake_volatility` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 55 | `kcal_avg` | YES | missing | Ernährung | 30d | 1 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | Draft specification missing for im... |
| 56 | `kf_aktuell` | YES | missing | Körper | latest | 2 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 57 | `lbm_28d_change` | YES | missing | Körper | 28d | 1 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 58 | `macro_consistency_score` | YES | missing | Ernährung | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 59 | `monotony_score` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 60 | `name` | YES | missing | Profil | latest | 19 | non_compliant | P0 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | High usage (19 references) - breaking ch... |
| 61 | `nutrition_days` | YES | missing | Unknown | unknown | 2 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
| 62 | `nutrition_score` | YES | missing | Scores (Phase 0b) | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 63 | `plateau_detected` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 64 | `protein_adequacy_28d` | YES | missing | Ernährung | 28d | 1 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 65 | `protein_avg` | YES | missing | Ernährung | 30d | 1 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | Draft specification missing for im... |
| 66 | `protein_days_in_target` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 67 | `protein_g_per_kg` | YES | missing | Ernährung | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 68 | `protein_ziel_high` | YES | missing | Unknown | unknown | 7 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
| 69 | `protein_ziel_low` | YES | missing | Unknown | unknown | 7 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
| 70 | `proxy_internal_load_7d` | YES | missing | Training | 7d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 71 | `quality_sessions_pct` | YES | missing | Training | unknown | 1 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 72 | `recent_load_balance_3d` | NO | missing | Unknown | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 73 | `recomposition_quadrant` | YES | missing | Körper | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 74 | `recovery_score` | YES | missing | Scores (Phase 0b) | unknown | 1 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 75 | `rest_day_compliance` | YES | missing | Training | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 76 | `rest_days_count` | YES | missing | Schlaf & Erholung | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 77 | `rhr_vs_baseline_pct` | YES | missing | Vitalwerte | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 78 | `sleep_avg_duration` | YES | missing | Schlaf & Erholung | 30d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 79 | `sleep_avg_duration_7d` | YES | missing | Schlaf & Erholung | 7d | 1 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 80 | `sleep_avg_quality` | YES | missing | Schlaf & Erholung | 30d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 81 | `sleep_debt_hours` | YES | missing | Schlaf & Erholung | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 82 | `sleep_quality_7d` | YES | missing | Schlaf & Erholung | 7d | 1 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 83 | `sleep_regularity_proxy` | YES | missing | Schlaf & Erholung | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 84 | `strain_score` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 85 | `thigh_28d_delta` | NO | missing | Unknown | 28d | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 86 | `top_3_focus_areas` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 87 | `top_3_goals_behind_schedule` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 88 | `top_3_goals_on_track` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 89 | `top_drivers` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 90 | `top_focus_area_name` | YES | missing | Focus Areas | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 91 | `top_focus_area_progress` | YES | missing | Focus Areas | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 92 | `top_goal_name` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 93 | `top_goal_progress_pct` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 94 | `top_goal_status` | NO | missing | Unknown | unknown | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 95 | `training_frequency_7d` | YES | missing | Training | 7d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 96 | `training_minutes_week` | YES | missing | Training | 7d | 1 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 97 | `trainingstyp_verteilung` | YES | missing | Training | unknown | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Time window needs classification | Draft specification missing for implemented f... |
| 98 | `vitals_avg_hr` | YES | missing | Vitalwerte | 30d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 99 | `vitals_avg_hrv` | YES | missing | Vitalwerte | 30d | 0 | partially_compliant | P1 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Draft specification missing for... |
| 100 | `vitals_vo2_max` | YES | missing | Vitalwerte | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 101 | `vo2max_trend_28d` | YES | missing | Vitalwerte | 28d | 0 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 102 | `waist_28d_delta` | YES | missing | Körper | 28d | 0 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 103 | `waist_hip_ratio` | YES | missing | Körper | unknown | 0 | partially_compliant | P0 | needs_sharpening | metadata_only | medium | Partial compliance - needs metadata enrichment | Time window needs classificatio... |
| 104 | `weight_28d_slope` | YES | missing | Körper | 28d | 0 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 105 | `weight_7d_median` | YES | missing | Körper | 7d | 1 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 106 | `weight_90d_slope` | NO | missing | Unknown | 90d | 0 | non_compliant | P0 | new_required | build_new | low | Not implemented, needs build |
| 107 | `weight_aktuell` | YES | missing | Körper | latest | 10 | compliant | P3 | verified_match | no_change | high | Fully compliant with normative requirements | High usage (10 references) - break... |
| 108 | `weight_trend` | YES | missing | Körper | 28d | 10 | compliant | P3 | needs_refactor | code_change | high | Known conflict: Code uses 28d, docs say 7d/30d | Resolution: Update docs to matc... |
| 109 | `zeitraum_30d` | YES | missing | Zeitraum | 30d | 0 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 110 | `zeitraum_7d` | YES | missing | Zeitraum | 7d | 0 | non_compliant | P2 | needs_refactor | code_change | low | Code-docs conflicts or quality issues | Draft specification missing for implemen... |
| 111 | `zeitraum_90d` | YES | missing | Unknown | 90d | 0 | non_compliant | P0 | needs_sharpening | metadata_only | medium | Implementation exists but lacks documentation | Category needs classification | ... |
## Critical Placeholders (P0)
These placeholders require immediate attention:
- `name`: Code-docs conflicts or quality issues | High usage (19 references) - breaking change risk | Draft specification missing for implemented feature
- `geschlecht`: Code-docs conflicts or quality issues | High usage (14 references) - breaking change risk | Draft specification missing for implemented feature
- `height`: Code-docs conflicts or quality issues | High usage (12 references) - breaking change risk | Draft specification missing for implemented feature
- `goal_bf_pct`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | High usage (10 references) - breaking change risk | Draft specification missing for implemented feature
- `caliper_summary`: Partial compliance - needs metadata enrichment | Time window needs classification | High usage (8 references) - breaking change risk | Draft specification missing for implemented feature
- `goal_weight`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | High usage (8 references) - breaking change risk | Draft specification missing for implemented feature
- `protein_ziel_high`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | High usage (7 references) - breaking change risk | Draft specification missing for implemented feature
- `protein_ziel_low`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | High usage (7 references) - breaking change risk | Draft specification missing for implemented feature
- `activity_detail`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | Known conflict: Time window unclear in code | High usage (4 references) - breaking change risk | Draft specification missing for implemented feature
- `activity_summary`: Time window needs classification | Known conflict: Code uses 14d, docs say 7d | Draft specification missing for implemented feature
- `nutrition_days`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | Draft specification missing for implemented feature
- `ability_balance_strength`: Time window needs classification | Draft specification missing for implemented feature
- `activity_score`: Time window needs classification | Draft specification missing for implemented feature
- `body_progress_score`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_aktivität_progress`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_aktivität_weight`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_ernährung_progress`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_ernährung_weight`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_körper_progress`: Time window needs classification | Draft specification missing for implemented feature
- `focus_cat_körper_weight`: Time window needs classification | Draft specification missing for implemented feature
- `goal_progress_score`: Time window needs classification | Draft specification missing for implemented feature
- `hrv_vs_baseline_pct`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `nutrition_score`: Time window needs classification | Draft specification missing for implemented feature
- `protein_g_per_kg`: Time window needs classification | Draft specification missing for implemented feature
- `quality_sessions_pct`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `recomposition_quadrant`: Time window needs classification | Draft specification missing for implemented feature
- `recovery_score`: Time window needs classification | Draft specification missing for implemented feature
- `ability_balance_coordination`: Not implemented, needs build
- `ability_balance_endurance`: Time window needs classification | Draft specification missing for implemented feature
- `ability_balance_mental`: Not implemented, needs build
- `ability_balance_mobility`: Not implemented, needs build
- `active_goals_json`: Not implemented, needs build
- `active_goals_md`: Not implemented, needs build
- `arm_28d_delta`: Not implemented, needs build
- `bmi`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `chest_28d_delta`: Not implemented, needs build
- `correlation_energy_weight_lag`: Not implemented, needs build
- `correlation_load_hrv`: Not implemented, needs build
- `correlation_load_rhr`: Not implemented, needs build
- `correlation_protein_lbm`: Not implemented, needs build
- `correlation_sleep_recovery`: Not implemented, needs build
- `data_quality_score`: Time window needs classification | Draft specification missing for implemented feature
- `datum_heute`: Time window needs classification | Draft specification missing for implemented feature
- `energy_deficit_surplus`: Not implemented, needs build
- `focus_area_weights_json`: Not implemented, needs build
- `focus_areas_weighted_json`: Not implemented, needs build
- `focus_areas_weighted_md`: Not implemented, needs build
- `focus_cat_lebensstil_progress`: Not implemented, needs build
- `focus_cat_lebensstil_weight`: Not implemented, needs build
- `focus_cat_mental_progress`: Not implemented, needs build
- `focus_cat_mental_weight`: Not implemented, needs build
- `focus_cat_recovery_progress`: Not implemented, needs build
- `focus_cat_recovery_weight`: Not implemented, needs build
- `focus_cat_vitalwerte_progress`: Not implemented, needs build
- `focus_cat_vitalwerte_weight`: Not implemented, needs build
- `hip_28d_delta`: Not implemented, needs build
- `intake_volatility`: Not implemented, needs build
- `macro_consistency_score`: Time window needs classification | Draft specification missing for implemented feature
- `monotony_score`: Not implemented, needs build
- `plateau_detected`: Not implemented, needs build
- `protein_days_in_target`: Not implemented, needs build
- `recent_load_balance_3d`: Partial compliance - needs metadata enrichment | Time window needs classification
- `rest_day_compliance`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `rest_days_count`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `rhr_vs_baseline_pct`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `sleep_debt_hours`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `sleep_regularity_proxy`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `strain_score`: Not implemented, needs build
- `thigh_28d_delta`: Not implemented, needs build
- `top_3_focus_areas`: Not implemented, needs build
- `top_3_goals_behind_schedule`: Not implemented, needs build
- `top_3_goals_on_track`: Not implemented, needs build
- `top_drivers`: Not implemented, needs build
- `top_focus_area_name`: Time window needs classification | Draft specification missing for implemented feature
- `top_focus_area_progress`: Time window needs classification | Draft specification missing for implemented feature
- `top_goal_name`: Not implemented, needs build
- `top_goal_progress_pct`: Not implemented, needs build
- `top_goal_status`: Not implemented, needs build
- `trainingstyp_verteilung`: Time window needs classification | Draft specification missing for implemented feature
- `vitals_vo2_max`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `waist_hip_ratio`: Partial compliance - needs metadata enrichment | Time window needs classification | Draft specification missing for implemented feature
- `weight_90d_slope`: Not implemented, needs build
- `zeitraum_90d`: Implementation exists but lacks documentation | Category needs classification | Description needs to be written | Draft specification missing for implemented feature

View File

@ -0,0 +1,254 @@
# Placeholder Reconciliation Report
**Generated:** 2026-03-30
**Scope:** 111 Placeholders
**Status:** COMPLETE ✓
## Overview
This reconciliation consolidates data from three authoritative sources to create a **single source of truth** for all 111 placeholders in the Mitai Jinkendo system.
### Data Sources
1. **Export Catalog** (`.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.json`)
- Authoritative placeholder metadata export
- Contains 111 placeholder definitions with categories, descriptions, time windows, usage data
2. **Audit Reports** (`audit-report-2026-03-29/`)
- Comprehensive compliance audit against normative requirements
- Identifies gaps, conflicts, and remediation priorities
- Classifies placeholders: 8 compliant, 22 partially compliant, 81 non-compliant
3. **Draft Document** (`.claude/docs/concepts/canonical_placeholder_requirements_draft.md`)
- Normative placeholder specifications (4895 lines)
- Currently: 0 placeholders documented (draft in progress)
## Reconciliation Results
### Completeness Validation
**PASS** - All 111 placeholders successfully reconciled
- Export-Keys: 111
- Matrix-Keys: 111
- Differenz: 0
- Fehlende Keys: []
- Doppelte Keys: []
### Key Statistics
#### Implementation Status
- **Exists in Code:** 71/111 (63%)
- **Not Implemented:** 40/111 (36%)
#### Compliance Level (from Audit)
- **Compliant:** 8 (7%) - Production-ready
- **Partially Compliant:** 22 (19%) - Minor gaps
- **Non-Compliant:** 81 (73%) - Major gaps
#### Reconciliation Status
- **Verified Match (7):** Draft ↔ Export ↔ Audit align perfectly
- **Needs Sharpening (49):** Correct but incomplete metadata
- **Needs Refactor (16):** Code-docs conflicts exist
- **Draft Wrong (0):** Draft specification incorrect
- **New Required (39):** Not implemented, needs build
- **Unclear (0):** Manual review needed
#### Priority Distribution
- **P0 (Critical):** 83 (74%) - Immediate attention required
- **P1 (High):** 10 (9%) - Next sprint
- **P2 (Medium):** 10 (9%) - Later
- **P3 (Low):** 8 (7%) - Maintenance only
#### Confidence Distribution
- **High:** 10 (9%) - Reliable data
- **Medium:** 49 (44%) - Some uncertainty
- **Low:** 52 (46%) - Needs verification
## Generated Documents
### 1. RECONCILIATION_COMPLETENESS_CHECK.md
Validation proof that all 111 placeholders were processed.
### 2. PLACEHOLDER_RECONCILIATION_MATRIX.json (119 KB)
Machine-readable complete reconciliation data:
- Schema version: 1.0.0
- All 111 placeholders with:
- Implementation status
- Draft status
- Export metadata (category, description, time_window, usage)
- Audit findings (compliance, priority, confidence logic)
- Architecture verification
- Reconciliation status and recommended action
- Confidence level and notes
### 3. PLACEHOLDER_RECONCILIATION_MATRIX.md (32 KB)
Human-readable table format:
- Full matrix with 111 rows
- Statistics summary
- Critical placeholders (P0) section
### 4. EXECUTIVE_RECONCILIATION_SUMMARY.md (4.1 KB)
High-level findings and recommendations:
- Key findings and statistics
- Known code-documentation conflicts (3)
- High-usage placeholders (12 with breaking change risk)
- Immediate actions (P0) and sprint plan (P1/P2/P3)
### 5. DRAFT_CORRECTIONS_REQUIRED.md (11 KB)
Lists all placeholders needing draft specifications:
- Missing: 111 placeholders
- Partial: 0 placeholders
- Wrong: 0 placeholders
- Bulk update strategy and effort estimates
### 6. IMPLEMENTATION_WAVES.md (4.1 KB)
Cluster-based remediation plan:
- Wave 1 (P0): Week 1 - 14-20 hours
- Wave 2 (P1): Weeks 2-3 - 26-34 hours
- Wave 3 (P2): Weeks 4-5 - 18-24 hours
- Wave 4 (P3): Later - 24-32 hours
- Total: 82-110 hours over 4-6 weeks
## Critical Issues
### Known Code-Documentation Conflicts
1. **`weight_trend`** - Code uses 28d, docs say 7d/30d
- Resolution: Update docs to match code (28d)
2. **`activity_summary`** - Code uses 14d, docs say 7d
- Resolution: Update docs to match code (14d)
3. **`activity_detail`** - Time window unclear in code
- Resolution: Needs code review to determine actual time window
### High-Usage Placeholders (Breaking Change Risk)
12 placeholders with 4+ uses require careful handling:
| Placeholder | Uses | Compliance |
|-------------|------|------------|
| `name` | 19 | non_compliant |
| `geschlecht` | 14 | non_compliant |
| `height` | 12 | non_compliant |
| `weight_aktuell` | 10 | compliant ✓ |
| `weight_trend` | 10 | compliant ✓ |
| `goal_bf_pct` | 10 | non_compliant |
| `caliper_summary` | 8 | partially_compliant |
| `circ_summary` | 8 | compliant ✓ |
| `goal_weight` | 8 | non_compliant |
| `protein_ziel_low` | 7 | non_compliant |
| `protein_ziel_high` | 7 | non_compliant |
| `activity_detail` | 4 | non_compliant |
### Missing Documentation (P0)
- **49 placeholders** lack category classification
- **49 placeholders** lack descriptions
- **74 placeholders** have unknown time windows
## Recommended Actions
### Immediate (P0) - Week 1
1. **Resolve Known Conflicts** (3 placeholders, 2-4 hours)
- Update documentation to match code implementation
2. **Classify Time Windows** (74 placeholders, 8-12 hours)
- Use name-based extraction (`*_7d`, `*_28d` patterns)
- Extract from code default parameters
- Manual classification for unclear cases
3. **Add Categories and Descriptions** (49 placeholders, 4-6 hours)
- Bulk update from audit semantic analysis
- Use existing audit report classifications
**Total P0 Effort:** 14-20 hours
### Next Sprint (P1) - Weeks 2-3
1. **Add Confidence Logic** (103 placeholders, 12-16 hours)
2. **Document Data Layer Modules** (100 placeholders, 6-8 hours)
3. **Structured Missing-Value Policy** (70 placeholders, 8-10 hours)
**Total P1 Effort:** 26-34 hours
### Later (P2-P3) - Weeks 4-6
1. **Integrate Unused Placeholders** (67 placeholders, 4-6 hours)
2. **Metadata Completeness** (111 placeholders, 10-12 hours)
3. **Production Status** (20-30 core placeholders, 4-6 hours)
4. **Validation Framework** (16-20 hours)
**Total P2-P3 Effort:** 42-56 hours
## Best-Practice Models
### Compliant Placeholders (8 total)
These serve as best-practice models for all others:
**Nutrition Averages (4):**
- `protein_avg`, `kcal_avg`, `fat_avg`, `carb_avg`
- Pattern: 30d time window, calculate_confidence, nutrition_metrics.py
**Body Metrics (3):**
- `weight_aktuell` (latest pattern, body_metrics.py)
- `weight_trend` (28d trend, calculate_confidence) *has conflict*
- `circ_summary` (mixed time window, best-of-each pattern)
**Profile (1):**
- `age` (snapshot, no confidence needed)
## Architecture Verification
- **71 placeholders (63%)** exist in code with sound architecture
- **40 placeholders (36%)** not implemented, need build
- **0 placeholders** with fundamental architecture issues
All implemented placeholders have valid resolvers and data sources.
## Success Metrics
### After P0 Remediation (Week 1)
- 0 unknown time windows ✓
- 0 unknown categories ✓
- 0 code-documentation conflicts ✓
### After P1 Remediation (Weeks 2-3)
- 70%+ placeholders with confidence logic
- 100% placeholders with structured missing-value policies
- 100% placeholders with documented data layers
### After P2-P3 Remediation (Weeks 4-6)
- 50-60% placeholder usage rate
- 60%+ placeholders with metadata completeness >60
- 20-30 production-ready placeholders
## Conclusion
The reconciliation process successfully analyzed all 111 placeholders and created a comprehensive single source of truth. While only 7% are currently fully compliant with normative requirements, the systematic gaps are primarily **documentation-related rather than functional**.
All 111 placeholders are:
- ✅ Properly inventoried
- ✅ Categorized by compliance level
- ✅ Prioritized for remediation
- ✅ Mapped to implementation plan
With a structured 4-6 week remediation plan (82-110 hours), the system can reach **>60% normative conformity** and establish a sustainable maintenance process.
## Next Steps
1. **Review** this reconciliation with product/tech leads
2. **Prioritize** P0 items for Week 1 sprint
3. **Execute** systematic remediation following implementation waves
4. **Track** progress against success metrics
5. **Iterate** based on findings and feedback
---
**For Questions or Issues:**
- Reconciliation Data: `PLACEHOLDER_RECONCILIATION_MATRIX.json`
- Executive Summary: `EXECUTIVE_RECONCILIATION_SUMMARY.md`
- Implementation Plan: `IMPLEMENTATION_WAVES.md`

View File

@ -0,0 +1,14 @@
# Reconciliation Completeness Validation
## Vollstaendigkeitscheck
- Export-Keys: 111
- Matrix-Keys: 111
- Differenz: 0
- Fehlende Keys: []
- Doppelte Keys: []
## Status: PASS
## Details
All placeholders successfully reconciled. Matrix contains exactly 111 entries matching the export catalog.

View File

@ -0,0 +1,422 @@
# Activity Quality Gates Fachliches Konzept
**Issue:** #15
**Status:** Design Phase
**Erstellt:** 2026-03-23
---
## Problem-Statement
### Aktuelles Problem:
Apple Health und andere Tracker importieren **alle** Workouts, unabhängig von ihrer Qualität:
- 5-Minuten-Spaziergang → "Outdoor Run"
- 10-Minuten-Krafttraining ohne echte Belastung
- Versehentlich gestartete Workouts
- Aufwärm-Sessions ohne echtes Training
**Folgen:**
- KI-Analysen werden verfälscht ("Du trainierst täglich!" - aber nur 5min)
- Statistiken zeigen unrealistische Trainingsfrequenz
- Korrelationen (Training ↔ Erholung) werden unbrauchbar
- User verliert Vertrauen in die Auswertungen
---
## Fachliche Anforderungen
### Must-Have:
1. **Pro Trainingstyp** unterschiedliche Qualitätskriterien
2. **Automatische Bewertung** beim Import und bei manueller Eingabe
3. **Nicht-destruktiv** - keine Daten löschen, nur markieren
4. **Transparenz** - User sieht warum eine Aktivität als minderwertig gilt
5. **Opt-Out möglich** - User kann Quality Gates pro Aktivität überschreiben
### Nice-to-Have:
6. **Admin-Presets** für gängige Trainingstypen (Laufen, Krafttraining, etc.)
7. **User-spezifische Anpassung** (z.B. für Reha-Patienten andere Schwellwerte)
8. **Historische Nachbearbeitung** - bestehende Aktivitäten neu bewerten
---
## Lösungsansätze (Evaluation)
### Ansatz A: Quality Flag (Boolean)
```sql
ALTER TABLE activity_log ADD COLUMN is_valid BOOLEAN DEFAULT true;
```
**Pro:**
- Einfach zu implementieren
- Schnelle Queries (`WHERE is_valid = true`)
- Binäre Entscheidung: gültig oder nicht
**Contra:**
- ❌ Keine Abstufungen (was ist mit "grenzwertig"?)
- ❌ Kein Grund dokumentiert (warum ungültig?)
- ❌ Schwer erweiterbar
**Bewertung:** ⭐⭐☆☆☆ (zu simpel)
---
### Ansatz B: Quality Score (0-100)
```sql
ALTER TABLE activity_log ADD COLUMN quality_score INTEGER DEFAULT 100;
```
**Pro:**
- Abstufungen möglich (100 = perfekt, 0 = wertlos)
- Filterbar: `WHERE quality_score >= 70`
- Flexibel für zukünftige Erweiterungen
**Contra:**
- ❌ Score-Berechnung komplex (wie gewichten?)
- ❌ Kein Grund dokumentiert
- ❌ Schwellwert (70? 80?) willkürlich
**Bewertung:** ⭐⭐⭐☆☆ (besser, aber intransparent)
---
### Ansatz C: Validation Result (JSONB) ⭐ **EMPFEHLUNG**
```sql
ALTER TABLE activity_log ADD COLUMN quality_check JSONB DEFAULT NULL;
-- Beispiel-Daten:
{
"evaluated_at": "2026-03-23T10:30:00Z",
"passed": false,
"score": 45,
"reasons": [
{"rule": "duration_min", "expected": 15, "actual": 8, "passed": false},
{"rule": "avg_hr_min", "expected": 100, "actual": 95, "passed": false},
{"rule": "max_hr_min", "expected": 120, "actual": 125, "passed": true}
],
"override": null // User kann auf "valid" oder "invalid" setzen
}
```
**Pro:**
- ✅ Transparent: Jede Regel einzeln nachvollziehbar
- ✅ Erweiterbar: Neue Regeln einfach hinzufügbar
- ✅ Override-Mechanismus eingebaut
- ✅ Historisch: Wann wurde evaluiert?
- ✅ Flexibel: Score + Boolean + Details
**Contra:**
- Mehr Speicherplatz (aber marginal bei JSONB)
- Komplexere Queries (aber PostgreSQL JSONB ist schnell)
**Bewertung:** ⭐⭐⭐⭐⭐ **BESTE LÖSUNG**
---
## Empfohlene Lösung: Ansatz C (Validation Result)
### Datenmodell
#### 1. Training Types (Regel-Definition)
```sql
ALTER TABLE training_types ADD COLUMN quality_rules JSONB DEFAULT NULL;
-- Beispiel: Laufen
UPDATE training_types SET quality_rules = '{
"enabled": true,
"rules": {
"duration_min": {"min": 15, "weight": 3},
"avg_hr_min": {"min": 100, "weight": 2},
"max_hr_min": {"min": 120, "weight": 1},
"distance_km": {"min": 1.0, "weight": 1}
},
"pass_threshold": 0.6,
"description": "Mindestens 15min, Durchschnittspuls > 100"
}'::jsonb WHERE name_de = 'Laufen';
-- Beispiel: Krafttraining
UPDATE training_types SET quality_rules = '{
"enabled": true,
"rules": {
"duration_min": {"min": 20, "weight": 5},
"avg_hr_min": {"min": 90, "weight": 1}
},
"pass_threshold": 0.8,
"description": "Mindestens 20 Minuten"
}'::jsonb WHERE name_de = 'Krafttraining';
```
**Erklärung:**
- `enabled`: Quality Gates aktiv für diesen Typ?
- `rules`: Dictionary der Regeln mit Schwellwerten + Gewichtung
- `weight`: Wichtigkeit der Regel (1-5)
- `pass_threshold`: Mindest-Score (0.0-1.0) für "bestanden"
- `description`: User-freundliche Erklärung
#### 2. Activity Log (Validierungs-Ergebnis)
```sql
ALTER TABLE activity_log ADD COLUMN quality_check JSONB DEFAULT NULL;
```
**Struktur siehe Ansatz C oben.**
---
## Validierungs-Logik
### Backend-Funktion: `validate_activity_quality()`
```python
def validate_activity_quality(activity: dict, training_type: dict) -> dict:
"""
Evaluiert eine Aktivität gegen die Quality Rules des Trainingstyps.
Returns:
{
"evaluated_at": ISO timestamp,
"passed": bool,
"score": float (0.0-1.0),
"reasons": [
{"rule": str, "expected": value, "actual": value, "passed": bool}
],
"override": null | "valid" | "invalid"
}
"""
rules = training_type.get('quality_rules', {})
if not rules or not rules.get('enabled'):
return None # Keine Quality Gates aktiv
results = []
total_weight = 0
passed_weight = 0
for rule_name, rule_config in rules['rules'].items():
weight = rule_config.get('weight', 1)
total_weight += weight
actual_value = activity.get(rule_name)
expected_min = rule_config.get('min')
passed = actual_value is not None and actual_value >= expected_min
if passed:
passed_weight += weight
results.append({
"rule": rule_name,
"expected": expected_min,
"actual": actual_value,
"passed": passed
})
score = passed_weight / total_weight if total_weight > 0 else 1.0
passed = score >= rules.get('pass_threshold', 0.6)
return {
"evaluated_at": datetime.now().isoformat(),
"passed": passed,
"score": round(score, 2),
"reasons": results,
"override": None
}
```
### Wann wird validiert?
1. **Beim Import** (CSV, API)
- Automatisch nach INSERT
- Spalte `quality_check` wird befüllt
2. **Beim manuellen Anlegen**
- Automatisch nach INSERT
- Spalte `quality_check` wird befüllt
3. **Beim Ändern der Quality Rules** (Admin)
- Optionaler Batch-Job: Alle Aktivitäten neu evaluieren
4. **Beim User-Override**
- User setzt `quality_check.override = "valid"` oder `"invalid"`
---
## User Experience
### 1. Activity-Liste (User-Ansicht)
```
┌─────────────────────────────────────────────────────────┐
│ Aktivitäten (März 2026) [Filter ▼] │
├─────────────────────────────────────────────────────────┤
│ │
│ 🏃 Laufen - 23. März 2026 │
│ 45 Minuten · Ø 142 bpm · 5.2 km │
│ ✅ Hochwertige Aktivität │
│ │
│ 🏃 Laufen - 22. März 2026 ⚠️ │
│ 8 Minuten · Ø 95 bpm · 0.8 km │
│ ⚠️ Niedrige Qualität - wird nicht gezählt │
│ [Details anzeigen ▼] │
│ │
│ → Dauer zu kurz (8 min < 15 min erforderlich)
│ → Durchschnittspuls zu niedrig (95 < 100)
│ → Maximalpuls OK (125 ≥ 120) ✅ │
│ │
│ [Als gültig markieren] [Als ungültig markieren] │
│ │
└─────────────────────────────────────────────────────────┘
```
**Filter-Optionen:**
- ☑ Hochwertige Aktivitäten
- ☑ Minderwertige Aktivitäten
- ☐ Nur in Statistiken gezählt
- ☐ Nur manuell überschrieben
### 2. Admin-UI (Quality Rules konfigurieren)
```
┌─────────────────────────────────────────────────────────┐
│ Trainingstyp bearbeiten: Laufen 🏃 │
├─────────────────────────────────────────────────────────┤
│ │
│ Kategorie: Cardio │
│ Name (DE): Laufen │
│ Name (EN): Running │
│ │
│ ━━━ Quality Gates ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ ☑ Quality Gates aktiviert │
│ │
│ Mindest-Schwellwerte: │
│ │
│ • Dauer (Minuten) [15] min Gewicht: ●●●○○ │
│ • Ø Herzfrequenz [100] bpm Gewicht: ●●○○○ │
│ • Max. Herzfrequenz [120] bpm Gewicht: ●○○○○ │
│ • Distanz [1.0] km Gewicht: ●○○○○ │
│ │
│ Erfolgs-Schwelle: [60]% (gewichteter Score) │
│ │
│ Beschreibung (User-sichtbar): │
│ "Mindestens 15 Minuten, Durchschnittspuls > 100" │
│ │
│ [Speichern] [Abbrechen] [Alle Aktivitäten neu prüfen]│
│ │
└─────────────────────────────────────────────────────────┘
```
### 3. Dashboard / Statistiken
**Transparenz:**
```
📊 Training (März 2026)
15 Aktivitäten erfasst
12 hochwertig ✅
3 minderwertig ⚠️ (werden nicht gezählt)
[Details zu minderwertigen Aktivitäten anzeigen]
```
---
## Betroffene Bereiche (Impact Analysis)
### 1. Aktivitäten-Listen
- **Query-Änderung:**
```sql
-- Alt:
SELECT * FROM activity_log WHERE profile_id = %s
-- Neu (nur hochwertige):
SELECT * FROM activity_log
WHERE profile_id = %s
AND (quality_check IS NULL
OR quality_check->>'passed' = 'true'
OR quality_check->>'override' = 'valid')
```
### 2. KI-Pipeline
- **insights.py:** Filter bei Daten-Aggregation
- Nur hochwertige Aktivitäten für KI-Prompts
### 3. Charts / Statistiken
- **Training Type Distribution:** Nur hochwertige zählen
- **Korrelations-Charts:** Separate Kurven für "alle" vs "hochwertig"?
### 4. Activity Import (CSV)
- Nach INSERT: `validate_activity_quality()` aufrufen
- Quality Check speichern
---
## Offene Fragen (für Entscheidung)
### Frage 1: Rückwirkende Evaluierung?
**Option A:** Nur neue Aktivitäten evaluieren
**Option B:** Alle bestehenden Aktivitäten nachträglich evaluieren
**Empfehlung:** Option B mit Batch-Job (einmalig beim Rollout)
### Frage 2: Standard-Verhalten bei fehlenden Quality Rules?
**Option A:** Alle Aktivitäten gelten als hochwertig (NULL = valid)
**Option B:** Alle Aktivitäten gelten als minderwertig (NULL = invalid)
**Empfehlung:** Option A (konservativ, keine Daten-Verlust-Angst)
### Frage 3: User-Override-Berechtigung?
**Option A:** Jeder User kann eigene Aktivitäten überschreiben
**Option B:** Nur Admin kann überschreiben
**Empfehlung:** Option A (User kennt seinen Kontext am besten)
### Frage 4: Wie viele Default-Rules?
**Option A:** Nur für Top 5 Trainingstypen (Laufen, Krafttraining, Radfahren, Schwimmen, HIIT)
**Option B:** Für alle 29 Trainingstypen
**Empfehlung:** Option A (Rest kann Admin/User nachpflegen)
### Frage 5: Quality Check in Liste anzeigen?
**Option A:** Immer sichtbar (Badge/Icon)
**Option B:** Nur bei minderwertigen Aktivitäten
**Empfehlung:** Option B (weniger visuelles Rauschen)
---
## Implementierungs-Reihenfolge (Vorschlag)
### Phase 1: Foundation (MVP)
1. DB-Migration: `quality_rules` + `quality_check` Spalten
2. Backend: `validate_activity_quality()` Funktion
3. Backend: Validierung beim INSERT (activity.py)
4. Admin-UI: Quality Rules konfigurieren (basic)
### Phase 2: User Experience
5. Frontend: Badge/Warning in Activity-Liste
6. Frontend: Details-Ansicht (welche Regeln failed?)
7. Frontend: User-Override (Als gültig/ungültig markieren)
### Phase 3: Integration
8. KI-Pipeline: Filter für hochwertige Aktivitäten
9. Charts: Nur hochwertige zählen
10. Batch-Job: Bestehende Aktivitäten evaluieren
### Phase 4: Polish
11. Admin-UI: Presets für gängige Trainingstypen
12. Filter-Optionen in Activity-Liste
13. Dashboard: Statistik hochwertig vs. minderwertig
**Geschätzter Aufwand:** 4-6 Stunden (mit Tests)
---
## Nächste Schritte
1. **Entscheidung:** Offene Fragen klären
2. **Technisches Design:** DB-Schema + API-Endpoints spezifizieren
3. **Implementation:** Phase 1 starten
4. **Testing:** Mit echten Apple Health Daten testen
---
**Erstellt:** 2026-03-23
**Review:** Pending User-Feedback

View File

@ -0,0 +1,458 @@
# Fachliche Anforderungen: KI-Prompt Flexibilisierung
**Modul:** v9f
**Status:** Fachlich freigegeben, technische Implementierung ausstehend
**Letzte Aktualisierung:** März 2026
---
## 1. Überblick
Das bestehende KI-Prompt-System wird von einer fixen Sammlung vordefinierter
Prompts zu einer vollständig konfigurierbaren Prompt-Bibliothek erweitert.
Admins können Prompts kategorisieren, duplizieren, bearbeiten und mit einem
visuellen Platzhalter-Browser ausstatten. Die Pipeline wird konfigurierbar
mehrere Konfigurationen für unterschiedliche Analysezwecke sind möglich.
---
## 2. Prompt-Bibliothek
### 2.1 Kategorien
Alle Prompts werden einer Kategorie zugeordnet:
| Kategorie | Beschreibung | Beispiel-Prompts |
|-----------|-------------|-----------------|
| Körper | Gewicht, KF, Umfänge, Caliper | Körperkomposition, Gewichtstrend |
| Ernährung | Kalorien, Makros, Timing | Kalorienbilanz, Proteinversorgung |
| Training | Volumen, Typen, HF | Trainingsanalyse, Erholungsstatus |
| Schlaf | Qualität, Dauer, Muster | Schlafauswertung, Schlaftrend |
| Vitalwerte | Ruhepuls, HRV, VO2Max | Leistungsfähigkeit, Erholung |
| Mentales | Stress, Stimmung, Energie | Wohlbefinden, Stressanalyse |
| Ziele | Fortschritt, Prognose, Zeitplan | Zielerreichung, Zeitplanung |
| Ganzheitlich | Korrelationen, Übersicht | Gesamtanalyse, Pipeline-Synthese |
### 2.2 Prompt-Verwaltung (Admin)
Ein Admin kann mit jedem Prompt folgendes tun:
**Aktivieren / Deaktivieren**
- Inaktive Prompts sind für Nutzer nicht sichtbar und nicht ausführbar
- Pipeline-Prompts können separat aktiviert/deaktiviert werden
**Duplizieren und anpassen**
- Bestehenden Prompt als Vorlage kopieren
- Kopie erhält Suffix " (Kopie)" und kann unabhängig bearbeitet werden
**Neu erstellen**
- Titel, Beschreibung, Kategorie, Template
- Platzhalter über den Platzhalter-Browser einfügen (siehe Abschnitt 3)
**Kategorie zuordnen**
- Beim Erstellen und Bearbeiten wählbar
- Nachträgliche Änderung möglich
**Reihenfolge festlegen**
- Drag & Drop oder Zahleneingabe (sort_order)
- Reihenfolge bestimmt Anzeige in der Nutzer-Ansicht
**Auf Standard zurücksetzen**
- Systemseitig definierte Prompts haben einen "Standard"-Zustand
- Reset stellt Original-Template wieder her
- Eigens erstellte Prompts haben keinen Standard-Reset
### 2.3 Prompt-Felder
Jeder Prompt hat:
- **Titel** kurzer Name (z.B. "Körperkomposition")
- **Beschreibung** wofür ist dieser Prompt? (für Admin sichtbar)
- **Kategorie** aus der Kategorienliste
- **Template** der eigentliche Prompt-Text mit Platzhaltern
- **Aktiv** true/false
- **Reihenfolge** Zahl für Sortierung
- **Typ** `single` (Einzelanalyse) oder `pipeline` (Pipeline-Stufe)
- **Pipeline-Stufe** nur bei Typ `pipeline`: Stufen-Nummer (1, 2, 3...)
---
## 3. Platzhalter-System
### 3.1 Platzhalter-Browser
Beim Bearbeiten eines Prompts steht ein visueller Platzhalter-Browser zur
Verfügung Admin tippt Platzhalter nicht mehr manuell, sondern wählt sie aus.
**Funktionen:**
- **Filterung nach Kategorie** zeigt nur Platzhalter der gewählten Kategorie
- **Beispielwert** zeigt was der Platzhalter aktuell mit echten Daten ausgeben würde
- **Klick zum Einfügen** Platzhalter wird an Cursor-Position ins Template eingefügt
- **Warnung bei fehlendem Platzhalter** wenn ein eingetippter Platzhalter nicht existiert
- **Warnung bei fehlenden Daten** wenn Daten für diesen Platzhalter nicht vorhanden sind (z.B. noch kein Schlaf-Tracking)
### 3.2 Prompt-Vorschau
Vor dem Speichern kann der Admin eine Vorschau anfordern:
- System befüllt alle Platzhalter mit echten aktuellen Daten
- Zeigt den fertigen Prompt-Text wie er an die KI gesendet würde
- KI wird dabei **nicht** aufgerufen nur der Prompt-Text wird angezeigt
### 3.3 Platzhalter-Kategorien und Übersicht
**Körper:**
```
{{weight_aktuell}} → "86,1 kg"
{{weight_trend}} → "sinkend (-0,8 kg letzte 4 Wochen)"
{{kf_aktuell}} → "19,9%"
{{kf_trend}} → "stabil"
{{magermasse}} → "69,4 kg"
{{whr}} → "0,90"
{{whtr}} → "0,51"
{{bmi}} → "27,2"
{{circ_summary}} → "Taille: 88cm, Hüfte: 98cm, ..."
{{caliper_summary}} → "KF nach Jackson/Pollock: 19,9%"
```
**Ernährung:**
```
{{kcal_avg}} → "1.847 kcal/Tag (Ø 30 Tage)"
{{protein_avg}} → "138g/Tag"
{{protein_ziel_low}} → "138"
{{protein_ziel_high}} → "172"
{{fat_avg}} → "72g/Tag"
{{carb_avg}} → "195g/Tag"
{{nutrition_summary}} → kompakte Zusammenfassung
{{nutrition_detail}} → ausführliche Tabelle
{{nutrition_days}} → "28 Tage mit Daten"
{{activity_kcal_summary}} → "Ø 320 kcal aktiver Verbrauch/Tag"
```
**Training:**
```
{{activity_summary}} → kompakte Zusammenfassung
{{activity_detail}} → ausführliche Liste
{{trainingstyp_verteilung}} → "60% Kraft, 30% Cardio, 10% Mobility"
{{trainingstyp_haupttyp}} → "Kraft" (häufigster Typ)
{{ruhetage_letzte_woche}} → "2"
{{trainingsphase}} → "Aufbau / Erholung / Plateau"
{{hf_zonen_verteilung}} → "Zone 2: 45%, Zone 3: 35%, Zone 4: 20%"
{{faehigkeiten_analyse}} → Detaillierte Analyse der trainierten Fähigkeiten
{{faehigkeiten_koordinativ}} → "Orientierung: 3/14 Einheiten, Gleichgewicht: 5/14, ..."
{{faehigkeiten_konditionell}} → "Kraft: 8/14, Ausdauer: 4/14, Schnelligkeit: 2/14, Flexibilität: 3/14"
{{faehigkeiten_kognitiv}} → "Konzentration: 2/14, Entscheidung: 1/14, ..."
{{faehigkeiten_psychisch}} → "Willenskraft: 5/14, Stressresistenz: 2/14, ..."
{{faehigkeiten_taktisch}} → "Timing: 1/14, Antizipation: 2/14, ..."
{{faehigkeiten_balance}} → "Ausgewogen / Einseitig + Empfehlung"
```
**Fähigkeiten-System (v9d Integration):**
- Jeder Trainingstyp ist mit 1-N Fähigkeiten aus 5 Dimensionen verknüpft
- KI erhält aggregierte Statistik: wie oft wurden welche Fähigkeiten trainiert
- Ermöglicht Aussagen wie: "Dein Training fokussiert stark auf Kraft (8 von 14 Einheiten), aber koordinative Fähigkeiten kommen zu kurz."
- Basis für gezielte Trainingsempfehlungen
**Herzfrequenz & Vitalwerte:**
```
{{ruhepuls_aktuell}} → "52 bpm"
{{ruhepuls_trend}} → "sinkend (-3 bpm letzte 4 Wochen)"
{{hrv_aktuell}} → "58 ms"
{{hrv_baseline}} → "62 ms (30-Tage-Durchschnitt)"
{{erholungsstatus}} → "gut / teilweise / schlecht"
{{vo2max}} → "48,2 ml/kg/min"
```
**Schlaf:**
```
{{schlaf_avg_dauer}} → "7,2h/Nacht (Ø 7 Tage)"
{{schlaf_qualitaet}} → "3,8/5 (Ø 7 Tage)"
{{schlaf_trend}} → "stabil"
{{schlaf_detail}} → ausführliche Tabelle
```
**Mentales:**
```
{{energie_niveau}} → "3,2/5 (Ø 7 Tage)"
{{stress_niveau}} → "2,8/5 (Ø 7 Tage)"
{{stimmung}} → "3,9/5 (Ø 7 Tage)"
{{meditation_streak}} → "5 Tage in Folge"
```
**Ziele:**
```
{{goal_weight}} → "82 kg"
{{goal_bf_pct}} → "14%"
{{goal_name}} → "Muskelaufbau"
{{ziel_fortschritt}} → "Gewicht: 72% erreicht, KF: 45% erreicht"
{{ziel_prognose}} → "Ziel voraussichtlich in 8 Wochen erreicht"
```
**Profil:**
```
{{name}} → "Lars"
{{geschlecht}} → "männlich"
{{height}} → "178"
{{age}} → "45"
{{sprache}} → "Deutsch" (konfigurierbar per Prompt)
```
**Zeitraum:**
```
{{zeitraum_7d}} → "letzte 7 Tage"
{{zeitraum_30d}} → "letzte 30 Tage"
{{zeitraum_90d}} → "letzte 90 Tage"
{{datum_heute}} → "20. März 2026"
```
**Pipeline (intern):**
```
{{stage1_body}} → JSON-Summary Körper (Pipeline-Stufe 1)
{{stage1_nutrition}} → JSON-Summary Ernährung
{{stage1_activity}} → JSON-Summary Training
{{stage1_sleep}} → JSON-Summary Schlaf (neu)
{{stage1_vitals}} → JSON-Summary Vitalwerte (neu)
```
---
## 4. Pipeline-Konfiguration
### 4.1 Was ist eine Pipeline-Konfiguration?
Eine Pipeline-Konfiguration definiert:
- **Name** z.B. "Alltags-Check", "Wettkampf-Analyse", "Schlaf-Fokus"
- **Aktive Module** welche Datenquellen fließen ein (Körper, Ernährung, Training, Schlaf, Vitalwerte, Mentales)
- **Zeitraum je Modul** z.B. Körper 30 Tage, Schlaf 7 Tage, Training 14 Tage
- **Stufen** welcher Prompt wird in welcher Stufe verwendet
- **Standard** eine Konfiguration kann als Standard markiert werden
### 4.2 Pipeline-Stufen
**Stufe 1 (parallel):** Mehrere Module-Prompts laufen gleichzeitig, jeder gibt ein JSON-Summary zurück
**Stufe 2:** Synthese-Prompt kombiniert alle Stage-1-Summaries zu einer narrativen Analyse
**Stufe 3 (optional):** Ziel-Abgleich oder spezifische Zusatzanalyse
Admin kann:
- Anzahl der Stufen festlegen (min. 2, max. 4)
- Jeden Stufen-Prompt zuweisen
- Module für Stufe 1 aktivieren/deaktivieren
### 4.3 Vordefinierte Konfigurationen (Beispiele)
**Alltags-Check (Standard):**
- Module: Körper (30T), Ernährung (30T), Training (14T)
- Stufe 1: pipeline_body + pipeline_nutrition + pipeline_activity
- Stufe 2: pipeline_synthesis
- Stufe 3: pipeline_goals
**Schlaf-Fokus:**
- Module: Schlaf (14T), Vitalwerte (7T), Training (14T)
- Stufe 1: pipeline_sleep + pipeline_vitals + pipeline_activity
- Stufe 2: pipeline_synthesis_sleep
**Wettkampf-Analyse:**
- Module: Körper (90T), Training (90T), Vitalwerte (30T), Ziele
- Stufe 1: alle Körper/Training/Vital-Module
- Stufe 2: pipeline_synthesis_performance
---
## 5. Nutzer-Ansicht: KI-Analyse starten
### 5.1 Einzelanalyse
1. Nutzer wählt Prompt aus der Bibliothek (nach Kategorie gefiltert)
2. KI-Analyse startet sofort
3. Ergebnis wird angezeigt und gespeichert
### 5.2 Pipeline
1. Nutzer wählt Pipeline-Konfiguration
2. Pipeline startet sofort (alle Stufen laufen automatisch)
3. Ergebnis wird angezeigt und gespeichert
### 5.3 Kein Konfigurationsschritt für Nutzer
Der Nutzer wählt nur Prompt oder Pipeline kein weiterer Konfigurationsschritt.
Zeitraum und Module sind durch Admin in der Pipeline-Konfiguration festgelegt.
---
## 6. Analyse-Ergebnisse verwalten
### 6.1 Kategorie-Zuordnung
Jedes gespeicherte Analyse-Ergebnis erbt die Kategorie des ausgeführten Prompts.
Ermöglicht spätere Filterung nach Kategorie.
### 6.2 Filtern nach Kategorie
Im Analyse-Verlauf kann nach Kategorie gefiltert werden:
- "Alle" / "Körper" / "Ernährung" / "Training" / etc.
### 6.3 Analyse-Verlauf pro Prompt
Für jeden Prompt ist ein Verlauf einsehbar:
- Liste aller bisherigen Analysen mit diesem Prompt (Datum + Kurzauszug)
- Zeigt wie sich die KI-Aussage über Zeit verändert hat
### 6.4 Favoriten
Nutzer kann Analysen als Favorit markieren (Stern-Symbol).
Favoriten sind in der Verlaufs-Ansicht filterbar.
### 6.5 Löschen
Nutzer kann einzelne Analyse-Ergebnisse manuell löschen.
Keine Massen-Löschung (außer Admin).
---
## 7. Sprache
Die Ausgabesprache wird im Prompt-Template selbst gesteuert über den
Platzhalter `{{sprache}}`. Der Wert wird aus den Profil-Einstellungen gezogen.
**Mögliche Werte:** "Deutsch", "Englisch", "Französisch" (erweiterbar)
**Standard:** "Deutsch"
Kein separater Sprach-Schalter auf der Analyse-Seite Sprache wird einmalig
im Profil konfiguriert.
---
## 8. Abgrenzung & offene Fragen
### In diesem Modul enthalten:
- Prompt-Bibliothek mit 8 Kategorien
- Vollständige Admin-Verwaltung (CRUD, Reihenfolge, Reset)
- Platzhalter-Browser mit Beispielwerten und Vorschau
- Pipeline-Konfigurationen (mehrere, speicherbar)
- Analyse-Ergebnisse: Kategorie, Filter, Verlauf, Favoriten, Löschen
- Sprach-Platzhalter
### Nicht in diesem Modul:
- Community-Prompts / Prompt-Import von extern → später
- KI-gestützte Prompt-Optimierung → später
- Automatisch geplante Analysen (z.B. wöchentlich) → später
- Prompts für noch nicht implementierte Module (Schlaf, Mentales) werden
vorbereitet aber haben keine echten Daten bis die Module existieren
### Offene Fragen für technische Planung:
1. Wie werden Pipeline-Konfigurationen gespeichert? (neue DB-Tabelle oder JSON in ai_prompts?)
2. Platzhalter-Definitionen: statisch im Code oder dynamisch in DB (für zukünftige Erweiterung)?
3. Wie wird der Beispielwert für den Platzhalter-Browser berechnet gleicher Endpunkt wie die echte Analyse?
4. Reihenfolge der Prompts: getrennte sort_order pro Kategorie oder global?
---
## 9. Trainingstypen & Fähigkeiten-Mapping (v9d → v9f Integration)
### 9.1 Hintergrund
In v9d wurde die Basis geschaffen:
- `training_types` Tabelle mit `abilities` JSONB-Spalte
- Admin-CRUD für Trainingstypen (ohne Abilities-UI)
- Taxonomie-Endpoint: `/api/admin/training-types/taxonomy/abilities`
- 5 Fähigkeiten-Dimensionen definiert:
- 🎯 **Koordinativ:** Orientierung, Differenzierung, Kopplung, Gleichgewicht, Rhythmus, Reaktion, Umstellung
- 💪 **Konditionell:** Kraft, Ausdauer, Schnelligkeit, Flexibilität
- 🧠 **Kognitiv:** Konzentration, Aufmerksamkeit, Wahrnehmung, Entscheidungsfindung
- 🎭 **Psychisch:** Motivation, Willenskraft, Stressresistenz, Selbstvertrauen
- ♟️ **Taktisch:** Timing, Strategie, Antizipation, Situationsanalyse
### 9.2 Was wird in v9f ergänzt?
**Admin-UI: Fähigkeiten-Matrix**
Im AdminTrainingTypesPage Formular wird eine Fähigkeiten-Matrix hinzugefügt:
- 5 Sections (eine pro Dimension)
- Pro Dimension: Checkboxes für alle Fähigkeiten
- Multi-Select: Ein Trainingstyp kann mehrere Fähigkeiten pro Dimension trainieren
- Beispiel: "Kampfsport-Sparring" trainiert:
- Koordinativ: Orientierung, Reaktion, Umstellung
- Konditionell: Schnelligkeit, Ausdauer
- Kognitiv: Entscheidung, Wahrnehmung
- Psychisch: Stressresistenz, Selbstvertrauen
- Taktisch: Timing, Antizipation
**Gespeichert als JSONB:**
```json
{
"koordinativ": ["orientierung", "reaktion", "umstellung"],
"konditionell": ["schnelligkeit", "ausdauer"],
"kognitiv": ["entscheidung", "wahrnehmung"],
"psychisch": ["stressresistenz", "selbstvertrauen"],
"taktisch": ["timing", "antizipation"]
}
```
**Backend: Fähigkeiten-Aggregation**
Neue Endpoint-Funktion (oder Platzhalter-Funktion):
```python
def calculate_abilities_summary(profile_id, days=14):
"""
Aggregiert trainierte Fähigkeiten über Zeitraum.
Returns:
{
"total_activities": 14,
"koordinativ": {
"orientierung": 3, # 3 von 14 Trainings
"gleichgewicht": 5,
...
},
"konditionell": {
"kraft": 8,
"ausdauer": 4,
...
},
...
"balance_score": 0.67, # 0-1, wie ausgewogen
"top_dimension": "konditionell",
"weak_dimension": "kognitiv"
}
"""
```
**KI-Prompt Integration:**
Die Fähigkeiten-Platzhalter werden befüllt mit aggregierten Daten:
- `{{faehigkeiten_analyse}}` → vollständige Aufschlüsselung
- `{{faehigkeiten_koordinativ}}` → nur koordinative Dimension
- etc.
Pipeline-Prompt "Training" erhält zusätzlichen Context:
```
Analyse die Fähigkeitenverteilung:
{{faehigkeiten_analyse}}
Gib Empfehlungen für untertrainierte Bereiche.
```
### 9.3 Nutzen für KI-Analyse
**Beispiel-Insight:**
> "Deine letzten 14 Trainings fokussieren stark auf **konditionelle Fähigkeiten** (Kraft: 8×, Ausdauer: 4×). Koordinative Fähigkeiten kommen zu kurz (nur 3× Gleichgewicht, 2× Reaktion). Für einen ausgewogenen Trainingsplan empfehle ich 2-3 koordinationslastige Einheiten pro Woche z.B. Kampfsport-Techniktraining, Balance-Übungen oder Mobility-Routinen mit Fokus auf Bewegungskontrolle."
**Korrelationen:**
- Verletzungen / Überlastung ↔ Koordinations-Defizit
- Plateaus im Krafttraining ↔ fehlende kognitive/taktische Komponente
- Erholungsprobleme ↔ psychische Fähigkeiten untertrainiert
### 9.4 Implementierungs-Reihenfolge
1. ✅ v9d Phase 1b: DB-Schema + Admin-CRUD (ohne Abilities-UI) → **erledigt**
2. 🔲 v9f: Abilities-Matrix UI im Admin-Formular
3. 🔲 v9f: Backend-Funktion `calculate_abilities_summary()`
4. 🔲 v9f: Platzhalter `{{faehigkeiten_*}}` implementieren
5. 🔲 v9f: Standard-Prompts aktualisieren mit Fähigkeiten-Context
6. 🔲 v9f: Admin-Doku: "Best Practices" für Fähigkeiten-Zuordnung
---
EOF
echo "OK"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,417 @@
# Entwicklungsrouten Multi-Dimensionales Training & Planung
**Version:** 1.0
**Status:** Konzept
**Ziel-Release:** v9e / v9f
**Basis:** Rest Days Multi-Dimensional Architecture (v9d Phase 2a)
---
## Überblick
**Entwicklungsrouten** sind unabhängige Dimensionen der körperlichen und mentalen Entwicklung, die jeweils eigene:
- Trainingspläne
- Ruhetag-Regeln
- Fortschritts-Tracking
- Ziele
haben.
**Kernidee:** Ein Tag kann gleichzeitig ein "Kraft-Ruhetag" UND ein "Mental-Aktivtag" sein.
---
## Die 6 Entwicklungsrouten
### 1. 💪 Kraft (Strength)
**Fokus:** Muskelaufbau, Maximalkraft, Power
**Trainingstypen:**
- Strength (Krafttraining allgemein)
- Power (Schnellkraft, Explosivität)
- HIIT (Hochintensiv)
**Ruhetag-Regeln:**
- Nach schwerer Beineinheit → 48-72h Kraft-Ruhetag
- Andere Routen (Cardio, Mental) bleiben erlaubt
- Intensität: Max 60% bei erlaubten Aktivitäten
**Metriken:**
- Gewicht, Umfänge, Caliper
- 1RM-Schätzung (optional)
- Muskelregeneration (Ruhepuls + HRV)
---
### 2. 🏃 Kondition (Conditioning)
**Fokus:** Ausdauer, VO2Max, Herz-Kreislauf
**Trainingstypen:**
- Cardio Low (GA1, Grundlagenausdauer)
- Cardio High (GA2, Tempo-Läufe)
- Endurance (Lange Einheiten)
**Ruhetag-Regeln:**
- Nach Wettkampf/Tempo-Lauf → 24-48h Cardio-Ruhetag
- Kraft & Mobility erlaubt
- Intensität: Max 70% HFmax
**Metriken:**
- HF-Zonen-Verteilung
- VO2Max-Schätzung
- Lauf-Pace / Rad-Watt
---
### 3. 🧘 Mental (Mental)
**Fokus:** Stressmanagement, Fokus, Wettkampf-Readiness
**Trainingstypen:**
- Meditation
- Wettkampf (Competition)
- High-Pressure Training
**Ruhetag-Regeln:**
- Nach Wettkampf → 1-3 Tage Mental-Ruhetag
- Physisches Training erlaubt (aber kein Wettkampf-Druck)
- Nur entspannende Aktivitäten (Meditation, Spaziergang)
**Metriken:**
- Stresslevel (1-5 subjektiv)
- Schlafqualität
- HRV (Mental-Recovery-Indikator)
---
### 4. 🤸 Koordination (Coordination)
**Fokus:** Balance, Agilität, Reaktion, propriozeptives Training
**Trainingstypen:**
- Coordination (Agilität, Balance)
- Sport-spezifische Drills
- Reaktionstraining
**Ruhetag-Regeln:**
- Nach intensivem Koordinationstraining → 24h Pause
- Andere Routen erlaubt
- Kein koordinativ anspruchsvolles Training bei Müdigkeit
**Metriken:**
- Subjektive Koordinations-Scores
- Balance-Tests (optional)
---
### 5. 🧘‍♂️ Mobilität (Mobility)
**Fokus:** Flexibilität, ROM (Range of Motion), Faszien
**Trainingstypen:**
- Mobility (Stretching, Yoga)
- Faszien-Training
- Passive Dehnung
**Ruhetag-Regeln:**
- Normalerweise kein Ruhetag nötig
- Bei Überdehnung / Verletzung → Pause für betroffene Bereiche
**Metriken:**
- ROM-Messungen (optional)
- Subjektive Beweglichkeit (1-5)
---
### 6. 🎯 Technik (Technique)
**Fokus:** Sport-spezifische Skills, Bewegungsqualität
**Trainingstypen:**
- Technique Drills
- Skill Work
- Video-Analyse
**Ruhetag-Regeln:**
- Technik-Training bei Müdigkeit kontraproduktiv
- Mental-Ruhetag → auch Technik pausieren
- Physisches Training erlaubt (ohne Technik-Fokus)
**Metriken:**
- Video-Analyse-Scores (optional)
- Coach-Feedback
---
## Datenmodell
### rest_days (Multi-Entry per Date)
**Änderung:** UNIQUE constraint `(profile_id, date)` entfernt (Migration 011)
**Erlaubt:**
```sql
-- Gleiches Datum, verschiedene Routen:
INSERT INTO rest_days (profile_id, date, rest_config, note)
VALUES
('user-1', '2026-03-23', '{"focus": "muscle_recovery", ...}', 'Beine heavy'),
('user-1', '2026-03-23', '{"focus": "mental_rest", ...}', 'Wettkampf-Pause');
```
**UI:**
```
23. März 2026
💪 Muskelregeneration | "Beine heavy"
🧘 Mental Rest | "Wettkampf-Pause"
→ Cardio erlaubt, Kraft pausiert, keine Wettkämpfe
```
---
## Wochenplanung mit Routen
### Tabelle: weekly_goals (erweitert)
**JSONB Schema:**
```json
{
"routes": {
"strength": {
"goal": 3, // 3x Kraft pro Woche
"min_rest_days": 1, // Mind. 1 Kraft-Ruhetag
"intensity_distribution": {
"heavy": 1, // 1x schwer (80-90%)
"medium": 1, // 1x mittel (70-80%)
"light": 1 // 1x leicht (60-70%)
}
},
"conditioning": {
"goal": 2,
"min_rest_days": 0,
"hr_zones": {
"zone_2": 60, // 60% der Zeit in Zone 2
"zone_3": 30, // 30% in Zone 3
"zone_4": 10 // 10% in Zone 4
}
},
"mental": {
"goal": 7, // Täglich Meditation
"min_duration": 10, // Mind. 10 Min
"rest_after_competition": 2 // 2 Tage Pause nach Wettkampf
}
},
"rules": {
"max_consecutive_strength": 2, // Max 2 Krafteinheiten hintereinander
"require_rest_after": ["competition", "strength_heavy"],
"auto_rest_on_poor_recovery": true // Bei HRV < Baseline Auto-Ruhetag
}
}
```
---
## Activity Validation mit Routen
### Endpoint: `POST /api/rest-days/validate-activity`
**Request:**
```json
{
"date": "2026-03-23",
"activity_type": "strength",
"route": "strength" // Neue Dimension
}
```
**Response:**
```json
{
"conflicts": [
{
"route": "strength",
"severity": "warning",
"message": "Kraft-Ruhetag Muskelregeneration nach Beineinheit. Trotzdem trainieren?"
}
],
"allowed": false
}
```
**Logik:**
```python
def validate_activity_multi_route(profile_id, date, activity_type, route=None):
"""
Prüft alle rest_days für das Datum.
- Wenn route angegeben: Nur diese Route prüfen
- Sonst: Alle passenden Routen prüfen
"""
rest_days = get_rest_days(profile_id, date)
conflicts = []
for rest_day in rest_days:
config = rest_day['rest_config']
# Prüfe ob Activity in rest_from
if activity_type in config.get('rest_from', []):
conflicts.append({
'route': config['focus'],
'severity': 'warning',
'message': f"{ROUTE_LABELS[config['focus']]} Ruhetag {activity_type} sollte pausiert werden."
})
return {
'conflicts': conflicts,
'allowed': len(conflicts) == 0
}
```
---
## Habits & Streaks pro Route
### Tabelle: route_habits (neu in v9f)
```sql
CREATE TABLE route_habits (
id SERIAL PRIMARY KEY,
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
route VARCHAR(20) NOT NULL, -- 'strength', 'conditioning', 'mental', etc.
habit_type VARCHAR(50) NOT NULL, -- 'meditation', 'mobility', 'strength_training'
frequency VARCHAR(20) NOT NULL, -- 'daily', 'weekly_3', 'weekly_5'
streak_current INTEGER DEFAULT 0,
streak_longest INTEGER DEFAULT 0,
last_completed DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
**Beispiele:**
- Mental-Route: "Tägliche Meditation" (7x/Woche)
- Mobility-Route: "Stretching" (3x/Woche)
- Strength-Route: "Krafttraining" (3x/Woche)
**UI:**
```
🧘 Mental-Route
Meditation 🔥 12 Tage Streak
Stress-Check 🔥 5 Tage Streak
💪 Kraft-Route
Krafttraining 🔥 2 Wochen Streak
Protein > 150g 🔥 10 Tage Streak
```
---
## Dashboard Integration
### Route-Übersicht Widget
```
Entwicklungsrouten (diese Woche)
─────────────────────────────────────
💪 Kraft 2/3 [██░] 1 Ruhetag
🏃 Kondition 1/2 [█░░] 0 Ruhetage
🧘 Mental 7/7 [███] ✓ Täglich
🤸 Koordination 0/1 [░░░] Geplant: Do
🧘‍♂️ Mobilität 3/3 [███] ✓ Ziel erreicht
Empfehlung: Kraft-Training heute, Cardio morgen
```
---
## KI-Integration
### Neue Platzhalter (v9f)
```python
{{route_balance}} # "Kraft überlastet, Cardio unterfordert"
{{route_progress_strength}} # "3/3 Einheiten, +2kg seit letztem Monat"
{{route_recovery_strength}} # "Gut erholt, schweres Training möglich"
{{route_next_recommended}} # "Kraft-Oberkörper empfohlen (Push-Fokus)"
{{route_conflict_today}} # "Kraft-Ruhetag, Cardio/Mental erlaubt"
```
### KI-Analyse-Funktion
```python
def analyze_route_balance(profile_id, weeks=4):
"""
Analysiert Balance über alle Routen.
Warnt bei:
- Übertraining in einer Route
- Vernachlässigung einer Route
- Fehlende Ruhetage in Route
"""
routes = ['strength', 'conditioning', 'mental', 'coordination', 'mobility']
analysis = {}
for route in routes:
activities = get_activities_by_route(profile_id, route, weeks)
rest_days = get_rest_days_by_route(profile_id, route, weeks)
analysis[route] = {
'activity_count': len(activities),
'rest_days_count': len(rest_days),
'intensity_avg': calculate_intensity_avg(activities),
'status': calculate_route_status(activities, rest_days) # 'balanced', 'overload', 'neglected'
}
return analysis
```
---
## Implementierungs-Phasen
### Phase 1: Foundation (v9d Phase 2a) ✅
- ✅ Migration 011: UNIQUE constraint entfernen
- ✅ Multiple rest_days per date erlauben
- ✅ UI: Mehrere Kacheln pro Tag anzeigen
### Phase 2: Route-Konzept (v9e)
- 🔲 Routen-Taxonomie festlegen (6 Routen)
- 🔲 Activity-Types den Routen zuordnen
- 🔲 `training_types` Tabelle um `route` Spalte erweitern
- 🔲 Validation: Multi-Route-Konflikt-Check
### Phase 3: Wochenplanung (v9f)
- 🔲 `weekly_goals` mit Routen-JSONB erweitern
- 🔲 Planungs-UI: Routen-basierte Wochenansicht
- 🔲 Regeln-Engine: Auto-Ruhetag bei Poor Recovery
- 🔲 Empfehlungs-System: "Heute: Kraft-Oberkörper empfohlen"
### Phase 4: Habits & Streaks (v9g)
- 🔲 `route_habits` Tabelle
- 🔲 Streak-Tracking pro Route
- 🔲 Dashboard-Integration
- 🔲 Push-Notifications bei Streak-Gefahr
### Phase 5: KI-Integration (v9f/g)
- 🔲 Route-Balance-Analyse
- 🔲 Übertraining-Warnung pro Route
- 🔲 Neue KI-Platzhalter
- 🔲 Route-spezifische Empfehlungen
---
## Offene Fragen
1. **Route-Hierarchie:** Gibt es Abhängigkeiten? (z.B. Mental-Rest → auch Technik-Rest?)
2. **Auto-Zuordnung:** Sollen Activities automatisch Routen zugeordnet werden? (Strength → Kraft-Route)
3. **Konflikt-Priorität:** Was passiert bei widersprüchlichen Ruhetagen? (Kraft erlaubt, Mental verbietet → ?)
4. **Routen-Gewichtung:** Sind manche Routen wichtiger als andere? (User-konfigurierbar?)
---
## Referenzen
- **Migration:** `backend/migrations/011_allow_multiple_rest_days_per_date.sql`
- **Technical Spec:** `.claude/docs/technical/V9D_PHASE2_VITALS_SLEEP.md` (zu aktualisieren)
- **Routen-Mapping:** `.claude/docs/functional/TRAINING_TYPES.md` (zu erweitern)
---
**Dokumentiert:** 2026-03-22
**Autor:** User Konzept + Claude Implementation
**Status:** Living Document (wird mit v9e/f erweitert)

View File

@ -0,0 +1,329 @@
# Fachliche Anforderungen: Ziele, Vitalwerte & Aktive Tests
**Modul:** v9e
**Status:** Fachlich freigegeben, technische Implementierung ausstehend
**Letzte Aktualisierung:** März 2026
---
## 1. Überblick
Dieses Modul erweitert Mitai Jinkendo um drei eng verbundene Bereiche:
1. **Ziele** Nutzer definiert konkrete Zielwerte (Gewicht, KF, Kondition etc.)
2. **Trainingsphasen** Automatische Erkennung der aktuellen Phase aus Daten
3. **Aktive Tests** Standardisierte Fitness-Tests zur Messung nicht-trackbarer Werte
4. **Vitalwerte** Blutdruck, SpO2, HRV, Temperatur, Blutzucker
Die KI verbindet alle vier Bereiche: Sie schlägt Zielwerte vor, erkennt Phasen,
empfiehlt Tests und passt Analysen an die aktuelle Phase an.
---
## 2. Ziele
### 2.1 Unterstützte Zieltypen
| Zieltyp | Felder | Einheit |
|---------|--------|---------|
| Gewichtsziel | Zielgewicht + Zieldatum | kg, Datum |
| Körperfett-Ziel | Ziel-KF% | % |
| Magermasse-Ziel | Ziel-Magermasse | kg |
| Konditionsziel | Ziel-VO2Max | ml/kg/min |
| Kraftziel | Übungsname + Zielwiederholungen/-gewicht | Wdh. oder kg |
| Flexibilitätsziel | Ziel-Sit&Reach | cm |
| Blutdruck-Zielbereich | Systolisch / Diastolisch | mmHg |
| Ruhepuls-Ziel | Ziel-Ruhepuls | bpm |
### 2.2 Ziel-Verwaltung
**Aktives Ziel:**
- **Unbegrenzt viele Ziele** können gleichzeitig aktiv sein
- Ein Ziel kann als **Primärziel** markiert werden dieses bestimmt
Dashboard-Fokus und KI-Analyse-Ausrichtung
- Alle anderen aktiven Ziele sind gleichwertige Nebenziele
**Ziel-Lebenszyklus:**
```
Entwurf → Aktiv → Erreicht / Aufgegeben / Abgelaufen
```
**Ziel-Geschichte:**
- Alle vergangenen Ziele bleiben gespeichert
- Fortschrittsverlauf je Ziel einsehbar
### 2.3 Ziel-Anzeige
**Dashboard-Widget (kompakt):**
- Zeigt alle aktiven Ziele als kompakte Karten
- Je Ziel: Name + Fortschrittsbalken (aktuell vs. Zielwert in %)
- Countdown bis Zieldatum (z.B. "noch 42 Tage")
- Prognose: "auf Kurs" / "X Tage früher" / "X Tage Verzögerung"
- Klick öffnet die Ziele-Seite
**Eigene Ziele-Seite (Navigation):**
- Vollständige Übersicht aller aktiven + vergangenen Ziele
- Je Ziel: Detailansicht mit Verlauf, Trend, Prognose
- Aktionen: Ziel bearbeiten / pausieren / als erreicht markieren / löschen
- "+ Neues Ziel" Button
**Fortschrittsbalken:**
```
Gewichtsziel: 86,1 kg → 82 kg
████████░░ 72% erreicht (noch 4,1 kg)
Prognose: 30.05.2026 ✓ auf Kurs
```
**Countdown:**
- Zeigt Tage bis Zieldatum
- Farbcodierung: grün (auf Kurs) / gelb (leicht hinter Plan) / rot (deutlich hinter Plan)
### 2.4 KI-Zielvorschläge
Die KI analysiert aktuelle Werte und schlägt realistische Zielwerte vor:
**Beispiele:**
- Gewichtsziel: "Basierend auf deinem aktuellen Gewicht von 86 kg und Kalorientrend
wäre ein Ziel von 82 kg in 12 Wochen realistisch (+0,5 kg/Woche Defizit)"
- Ruhepuls-Ziel: "Dein Ruhepuls liegt bei 58 bpm. Ein Ziel von 52 bpm ist bei
konsequentem Ausdauertraining in 16 Wochen erreichbar"
- KF-Ziel: "Von 19,9% auf 16% sind ~3,9 Prozentpunkte realistisch in 14 Wochen
bei Kaloriendefizit + Krafttraining"
**Vorschlag-Trigger:**
- Beim erstmaligen Anlegen eines Ziels
- Wenn kein aktives Ziel vorhanden
- Auf explizite Anfrage ("Neues Ziel vorschlagen")
---
## 3. Trainingsphasen
### 3.1 Unterstützte Phasen
| Phase | Erkennungsmerkmale | Empfohlene Strategie |
|-------|-------------------|---------------------|
| Kaloriendefizit | Kcal-Bilanz < -300/Tag (Ø 7T), Gewicht sinkend | Krafttraining erhalten, Cardio moderat |
| Kalorienstabilisierung | Kcal-Bilanz -100 bis +100/Tag, Gewicht stabil | Ausgeglichenes Training |
| Kalorienüberschuss | Kcal-Bilanz > +300/Tag, Gewicht steigend | Krafttraining priorisieren |
| Konditionsaufbau | >60% Cardio-Anteil, VO2Max-Trend steigend | Zone 2 Training ausbauen |
| Schnellkraft / HIIT | >40% HIIT/Schnellkraft, HF-Zonen 4-5 dominant | Erholung beachten |
| Maximalkraft-Aufbau | >50% Krafttraining, Hypertrophie/Maximalkraft | Progressive Überlastung |
| Regeneration | Trainingsvolumen -40% vs. Vorwoche, HRV niedrig | Aktive Erholung, Schlaf |
| Wettkampfvorbereitung | Nutzer-definiert mit Datum | Periodisierung, Peak-Performance |
### 3.2 Phasenerkennung
**Automatische Erkennung:**
- System analysiert letzte 14 Tage: Kalorienbilanz, Trainingstypen, Volumen, HRV, Ruhepuls
- Schlägt erkannte Phase als Benachrichtigung vor
- Erkennungs-Intervall: täglich (Hintergrund)
**Nutzer-Bestätigung:**
- Vorschlag erscheint als Banner: "Wir haben erkannt: Du befindest dich in einer
Kaloriendefizit-Phase. Stimmt das?"
- Optionen: ✓ Bestätigen / ✗ Ablehnen / ✎ Anpassen
- Manuelles Setzen jederzeit möglich
**Phasenwechsel-Erkennung:**
- Automatisch erkannt wenn Datenmuster **7 Tage konsistent** einer neuen Phase entspricht
- Erst nach dieser Stabilitätsprüfung wird der Wechsel vorgeschlagen
- Verhindert falsche Alarme bei kurzzeitigen Abweichungen (z.B. Urlaub, Krankheit)
- Nutzer wird benachrichtigt: "Phasenwechsel erkannt: Kaloriendefizit → Stabilisierung"
### 3.3 Phase und Dashboard
- Aktuelle Phase prominent auf Dashboard angezeigt (Badge/Label)
- Dashboard-Kennzahlen passen sich an Phase an:
- Kaloriendefizit: Kaloriendefizit/Tag als Hauptkennzahl
- Muskelaufbau: Proteinversorgung + Trainingsvolumen prominent
- Kondition: VO2Max-Trend + Cardio-Anteil prominent
---
## 4. Aktive Tests
### 4.1 Konzept
Aktive Tests messen Fitness-Parameter die nicht aus passivem Tracking ableitbar sind.
Jeder Test hat:
- **Durchführungsanleitung** (Schritt-für-Schritt in der App)
- **Messung** (Eingabe des Ergebnisses)
- **Auswertung** (Einordnung nach Alter/Geschlecht + Trend)
- **Testintervall** (empfohlene Wiederholung)
- **KI-Verknüpfung** (Ergebnis fließt in Analyse ein)
### 4.2 Standardtests
#### Cooper-Test (Ausdauer / VO2Max)
- **Ziel:** VO2Max-Schätzung
- **Durchführung:** 12 Minuten laufen, maximale Distanz messen
- **Eingabe:** Distanz in Metern
- **Berechnung:** `VO2Max = (Distanz - 504.9) / 44.73`
- **Intervall:** alle 46 Wochen
- **Anleitung in App:** Aufwärmen 5 min → 12 min laufen → Distanz eingeben
#### Stufentest (Herzfrequenz-Ausdauer)
- **Ziel:** Aerobe Schwelle, HF-Zonen kalibrieren
- **Durchführung:** Steigende Belastung (Laufen/Radfahren), HF alle 3 min notieren
- **Eingabe:** HF-Werte je Stufe (5 Stufen à 3 min)
- **Auswertung:** HF-Kurve, Schwellenbestimmung
- **Intervall:** alle 68 Wochen
#### Liegestützen-Test (Kraftausdauer Oberkörper)
- **Ziel:** Maximale Liegestützen ohne Pause
- **Eingabe:** Anzahl Wiederholungen
- **Einordnung:** Normwerte nach Alter/Geschlecht
- **Intervall:** alle 4 Wochen
#### Kniebeugen-Test (Kraftausdauer Unterkörper)
- **Ziel:** Maximale Kniebeugen ohne Pause
- **Eingabe:** Anzahl Wiederholungen
- **Einordnung:** Normwerte nach Alter/Geschlecht
- **Intervall:** alle 4 Wochen
#### Sit & Reach (Flexibilität)
- **Ziel:** Rumpf- und Beinflexibilität messen
- **Durchführung:** Sitzen, Beine gestreckt, Oberkörper vorbeugen, maximale Reichweite
- **Eingabe:** cm (positiv = über Fußsohle, negativ = davor)
- **Einordnung:** Normwerte nach Alter/Geschlecht
- **Intervall:** alle 4 Wochen
#### Gleichgewichtstest (Einbeinstand)
- **Ziel:** Propriozeption, Gleichgewicht
- **Durchführung:** Einbeinstand mit geschlossenen Augen, Zeit messen
- **Eingabe:** Sekunden (bestes von 3 Versuchen)
- **Einordnung:** <10s = verbesserungswürdig, 10-30s = gut, >30s = sehr gut
- **Intervall:** alle 4 Wochen
#### Griffstärke
- **Ziel:** Allgemeine Kraftindikator, Gesundheitsmarker
- **Durchführung:** Dynamometer oder Schätzung (maximaler Händedruck)
- **Eingabe:** kg (oder subjektiv 1-5 wenn kein Gerät)
- **Intervall:** alle 4 Wochen
#### Benutzerdefinierter Test
- **Ziel:** Eigene Tests anlegen
- **Felder:** Name, Beschreibung, Einheit, Eingabetyp (Zahl/Zeit/Skala)
- **Einordnung:** Manuelle Referenzwerte (gut/mittel/verbesserungswürdig)
- **Intervall:** Frei wählbar
### 4.3 Test-Verwaltung
**Test-Historie:**
- Alle Testergebnisse chronologisch gespeichert
- Trend über Zeit sichtbar (Liniengrafik pro Test)
- Verbesserung in % seit letztem Test
**Test-Kalender:**
- Übersicht: welche Tests wann fällig
- KI empfiehlt: "Cooper-Test ist seit 5 Wochen überfällig"
**Normwerte:**
- Einordnung nach Alter (in Profil hinterlegt) und Geschlecht
- Kategorien: Sehr gut / Gut / Durchschnittlich / Verbesserungswürdig
- Quelle: Etablierte Fitness-Normtabellen
---
## 5. Vitalwerte
### 5.1 Erfasste Vitalwerte
| Vitalwert | Felder | Einheit | Warnbereich |
|-----------|--------|---------|-------------|
| Blutdruck | Systolisch + Diastolisch + Puls | mmHg | >140/90 = erhöht |
| SpO2 | Sauerstoffsättigung | % | <95% = Warnung |
| HRV | Herzratenvariabilität | ms | persönliche Baseline |
| Körpertemperatur | Temperatur | °C | >37,5°C = erhöht |
| Blutzucker | Nüchtern-Wert | mg/dl oder mmol/l | >100 mg/dl nüchtern |
### 5.2 Erfassung
- Manuelle Eingabe mit Datum + Uhrzeit
- Import aus Apple Health (wenn vorhanden)
- Messkontext optional: Nüchtern / Nach Sport / Morgens / Abends
### 5.3 Auswertung
- Trend über Zeit je Vitalwert
- Ampel-System: Grün (normal) / Gelb (Grenzbereich) / Rot (außerhalb Normbereich)
- Korrelation: Blutdruck ↔ Training, Blutdruck ↔ Gewicht
- **Hinweis in App:** Vitalwerte ersetzen keine ärztliche Diagnose
---
## 6. KI-Analyse
### 6.1 Neue KI-Platzhalter
```
{{primärziel_typ}} → "Gewichtsabnahme"
{{primärziel_wert}} → "82 kg bis 30.06.2026"
{{primärziel_fortschritt}} → "72% erreicht (4 kg von 5,5 kg)"
{{primärziel_prognose}} → "Ziel voraussichtlich in 6 Wochen erreicht"
{{aktuelle_phase}} → "Kaloriendefizit"
{{phase_seit}} → "seit 14 Tagen"
{{phase_empfehlung}} → "Krafttraining erhalten, Cardio moderat"
{{test_letzter}} → "Cooper-Test: 2.800m (VO2Max ~52) vor 3 Wochen"
{{test_faellig}} → "Sit & Reach (seit 5 Wochen nicht gemacht)"
{{vitalwerte_blutdruck}} → "125/82 mmHg (letzter Wert)"
{{vitalwerte_spo2}} → "98% (letzter Wert)"
{{vitalwerte_trend}} → "Blutdruck stabil, SpO2 normal"
{{fitness_score}} → "Kondition: gut · Kraft: durchschnittlich · Flex: verbesserungswürdig"
```
### 6.2 KI-Analyse-Funktionen
**Zielwerte vorschlagen:**
- Trigger: kein aktives Ziel oder auf Anfrage
- Basis: aktuelle Messwerte + historischer Trend + Alter/Geschlecht
- Output: konkreter Zielwert + realistischer Zeitrahmen + Begründung
**Phase automatisch erkennen:**
- Tägliche Hintergrundanalyse
- Kalorienbilanz + Trainingstypen + Gewichtstrend + HRV
- Output: Phasen-Vorschlag mit Begründung
**Test empfehlen wenn fällig:**
- Prüft Testintervall vs. letztes Ergebnis
- Output: "Cooper-Test ist seit X Wochen fällig führe ihn durch um deinen
VO2Max-Trend zu aktualisieren"
**Fortschritt mit Prognose:**
- Lineare Extrapolation aktueller Trend → Zieldatum
- Output: "Bei aktuellem Tempo erreichst du dein Gewichtsziel in 6 Wochen
(statt geplanter 8 Wochen)"
**Empfehlungen an Phase anpassen:**
- KI-Analysen berücksichtigen aktuelle Phase
- Beispiel in Kaloriendefizit-Phase: Protein-Empfehlung erhöht,
Krafttraining-Empfehlung priorisiert, Schlafmangel-Warnung verstärkt
- Beispiel in Wettkampf-Phase: Erholungsstatus und Tapering im Fokus
---
## 7. Abgrenzung & offene Fragen
### In diesem Modul enthalten:
- 8 Zieltypen mit Primärziel-Konzept
- 8 Trainingsphasen + automatische Erkennung + Nutzerbestätigung
- 8 Standardtests + benutzerdefinierte Tests
- Normwerte-Einordnung nach Alter/Geschlecht
- 5 Vitalwerte mit Ampel-System
- 13 neue KI-Platzhalter
- 5 KI-Analyse-Funktionen
### Nicht in diesem Modul:
- Periodisierungsplaner (Makro-/Mikrozyklen) → später
- Ärztliche Auswertung / Diagnose → bewusst ausgeschlossen
- Automatische Smartwatch-Tests → v9h Connectoren
- Ernährungsplan-Generator → später
### Offene Fragen für technische Planung:
1. Ziele: eigene Tabelle `goals` oder als JSONB in `profiles`?
2. Phasenerkennung: täglicher Cron-Job im Backend oder Frontend-Berechnung?
3. Aktive Tests: generisches Schema (test_type + result_value + result_unit)
oder je Test eigene Felder?
4. Normwerte: in DB gespeichert oder als statische JSON-Datei im Backend?
5. Fitness-Score: wie wird er aus mehreren Tests aggregiert (gewichteter Durchschnitt)?
6. Vitalwerte: eigene Tabelle je Typ oder generische `vitals_log` Tabelle?
7. Phasen-Vorschlag: als Push-Notification oder nur In-App-Banner?
EOF

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
# Fachliche Anforderungen: Responsive UI
**Modul:** parallel zu Features (kein festes Versions-Label)
**Status:** Fachlich freigegeben, technische Implementierung ausstehend
**Letzte Aktualisierung:** März 2026
---
## 1. Überblick
Die App soll vollständig responsive sein und auf allen Bildschirmgrößen optimal
funktionieren vom iPhone über iPad bis zum Desktop-Browser. Die PWA-Funktionalität
auf dem iPhone bleibt vollständig erhalten.
---
## 2. Breakpoints
| Bereich | Breite | Layout |
|---------|--------|--------|
| Mobile | < 1024px | Bottom Navigation (wie jetzt) |
| Desktop | ≥ 1024px | Sidebar Navigation links, Content rechts |
Ein separates Tablet-Layout (768px) wird **nicht** eingeführt unterhalb
1024px verhält sich alles wie Mobile.
---
## 3. Mobile Layout (< 1024px) unverändert
Das bestehende Mobile-Layout bleibt exakt so wie es ist:
- Bottom Navigation mit Icons + Labels
- Volle Breite, 16px seitliches Padding
- 80px Bottom-Padding für Navigation
- PWA auf iPhone funktioniert wie bisher
---
## 4. Desktop Layout (≥ 1024px)
### 4.1 Grundstruktur
```
┌─────────────────────────────────────────────┐
│ Sidebar (220px) │ Content (flex 1) │
│ │ │
│ [Logo] │ Seiteninhalt │
│ │ (max-width: 1200px, │
│ Dashboard │ zentriert) │
│ Erfassung │ │
│ Verlauf │ │
│ Analyse │ │
│ Einstellungen │ │
│ Admin │ │
│ │ │
│ [Avatar + Name] │ │
└─────────────────────────────────────────────┘
```
### 4.2 Sidebar
**Position:** Fixed links, volle Höhe (100vh)
**Breite:** 220px
**Farben:** `var(--surface)` Hintergrund, `var(--border)` rechte Trennlinie
**Inhalt von oben nach unten:**
```
┌──────────────────┐
│ [Ensō-Logo] │ ← Icon (40px) + "Mitai Jinkendo" Text
│ Mitai Jinkendo │
├──────────────────┤
│ │
│ 🏠 Dashboard │ ← Icons + Text (gleiche Nav-Items wie Bottom Nav)
│ ✏️ Erfassung │ ← Aktive Seite: var(--accent) Hintergrund (leicht)
│ 📊 Verlauf │ + var(--accent) Text + linke Akzent-Linie (3px)
│ 🧠 Analyse │
│ ⚙️ Einstellungen│
│ 👑 Admin │ ← nur sichtbar wenn role='admin'
│ │
├──────────────────┤
│ [Avatar] │ ← Nutzer-Avatar (32px) + Name + Tier-Badge
│ Lars │
│ selfhosted │
└──────────────────┘
```
**Aktive Seite hervorheben:**
```css
/* Aktiver Nav-Item Desktop */
background: var(--accent-light); /* #E1F5EE leicht grüner Hintergrund */
color: var(--accent); /* #1D9E75 grüner Text */
border-left: 3px solid var(--accent); /* linke Akzent-Linie */
border-radius: 0 8px 8px 0;
```
### 4.3 Content-Bereich
- `margin-left: 220px` (Sidebar-Breite)
- `padding: 24px 32px`
- `max-width: 1200px` (zentriert innerhalb des verfügbaren Raums)
- Kein Bottom-Padding für Navigation (Sidebar ersetzt Bottom Nav)
---
## 5. Seiten-spezifische Desktop-Layouts
### 5.1 Dashboard
**Mobile:** Alle Karten untereinander
**Desktop:** 2-spaltig oder 4-spaltig je nach Kartentyp
```
┌──────────────────────────────────────────────┐
│ Hallo Lars 👋 Mo, 20. März 2026 │
├──────────┬──────────┬──────────┬─────────────┤
│ Gewicht │ KF │ Mager- │ Kalorien │
│ 86,1 kg │ 19,9% │ masse │ 1.447 kcal │
├──────────┴──────────┴──────────┴─────────────┤
│ Ziele [Details →] │
│ ████████░░ Gewicht: 27% ████░░░░ KF: 45% │
├──────────────────────────────────────────────┤
│ Kalorien + Gewicht (Chart volle Breite) │
└──────────────────────────────────────────────┘
```
### 5.2 Verlauf
**Mobile:** Tabs oben, Chart/Tabelle darunter
**Desktop:** Tabs links als vertikale Liste, Chart/Tabelle rechts
```
┌────────────┬─────────────────────────────────┐
│ Gewicht ← │ Chart + Tabelle │
│ Körperfett │ (nimmt volle rechte Breite) │
│ Umfänge │ │
│ Ernährung │ │
│ Aktivität │ │
└────────────┴─────────────────────────────────┘
```
### 5.3 Analyse
**Mobile:** Prompt-Liste, dann Ergebnis darunter
**Desktop:** Prompt-Liste links (300px), Ergebnis rechts
```
┌──────────────────┬──────────────────────────┐
│ Prompts │ Ergebnis / Verlauf │
│ ───────────── │ │
│ 🔍 Gesamt │ [Letzte Analyse] │
│ 🫧 Körper ← │ [Text der Analyse...] │
│ 🍽️ Ernährung │ │
│ 🏋️ Training │ [Ältere Analysen] │
│ ───────────── │ │
│ Pipeline ▶ │ │
└──────────────────┴──────────────────────────┘
```
### 5.4 Erfassung
**Mobile:** Formular volle Breite
**Desktop:** Formular zentriert, max-width 600px
```
┌──────────────────────────────────────────────┐
│ Gewicht erfassen │
│ │
│ ┌─────────────────────────────┐ │
│ │ Datum [20.03.2026] │ │
│ │ Gewicht [86,1 ] kg │ │
│ │ Notiz [ ] │ │
│ │ │ │
│ │ [Speichern] │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────────────────┘
```
### 5.5 Admin-Panel
**Mobile:** Tabellen horizontal scrollbar
**Desktop:** Tabellen nutzen volle Breite, mehr Spalten sichtbar
---
## 6. Übergangsverhalten
### Fenster-Resize
- Beim Verkleinern unter 1024px: Sidebar verschwindet, Bottom Nav erscheint
- Beim Vergrößern über 1024px: Bottom Nav verschwindet, Sidebar erscheint
- Kein Flackern CSS media queries, kein JavaScript-Resize-Listener nötig
### Aktiver Zustand synchronisiert
- Aktive Seite wird in Sidebar UND Bottom Nav korrekt hervorgehoben
- (Technisch: gleiche State-Variable, unterschiedliches Rendering)
---
## 7. Was sich nicht ändert
- Farben, CSS-Variablen, Design-Tokens bleiben identisch
- Alle Funktionen bleiben auf Desktop verfügbar
- PWA auf iPhone bleibt vollständig funktionsfähig
- Keine externen CSS-Frameworks (kein Tailwind, kein Bootstrap)
- Inline-Styles + CSS-Variablen-Ansatz bleibt erhalten
---
## 8. Abgrenzung & offene Fragen
### In diesem Modul enthalten:
- Sidebar Navigation Desktop
- Content-Bereich mit max-width
- Dashboard 4-spaltig
- Verlauf 2-spaltig (Tabs + Chart)
- Analyse 2-spaltig (Prompts + Ergebnis)
- Erfassung zentriert schmal
- Admin-Panel breite Tabellen
### Nicht in diesem Modul:
- Dark Mode separates Feature
- Animationen / Transitions optional später
- Collapsed Sidebar (Icon-only Modus) optional später
### Offene Fragen für technische Planung:
1. Wird AppLayout als neue Wrapper-Komponente eingeführt oder App.jsx direkt angepasst?
2. CSS Media Queries in app.css oder CSS-in-JS (Inline-Style mit window.innerWidth)?
3. Wie wird der aktive Nav-Item-State zwischen Sidebar und Bottom Nav geteilt?
4. Verlauf-Tabs: werden sie zu einer vertikalen Liste refactored oder per CSS umgestaltet?

View File

@ -0,0 +1,227 @@
# Fachliche Anforderungen: Schlaf-Modul
**Modul:** v9d
**Status:** Fachlich freigegeben, technische Implementierung ausstehend
**Letzte Aktualisierung:** März 2026
---
## 1. Überblick
Das Schlaf-Modul ist ein eigenständiges Tracking-Modul für tägliche Schlafdaten.
Es ergänzt die bestehenden Körper-, Ernährungs- und Aktivitäts-Module um eine
zentrale Gesundheitsdimension. Schlaf wird sowohl manuell erfasst als auch aus
Apple Health / Garmin importiert. Korrelationen mit Ruhepuls, Training und Gewicht
fließen in die KI-Analyse ein.
---
## 2. Erfasste Daten
### 2.1 Pflichtfelder (Schnelleingabe)
- **Datum** welche Nacht (Standard: letzte Nacht)
- **Schlafdauer** in Stunden und Minuten (berechnet aus Ein-/Aufwachzeit oder direkt)
- **Schlafqualität** subjektiv 15 (1 = sehr schlecht, 5 = sehr gut)
### 2.2 Optionale Felder (Detaileingabe)
- **Einschlafzeit** Uhrzeit (z.B. 23:15)
- **Aufwachzeit** Uhrzeit (z.B. 06:45)
- **Aufwach-Häufigkeit** wie oft in der Nacht aufgewacht (010+)
- **Schlafphasen** Dauer in Minuten je Phase:
- Tiefschlaf
- REM-Schlaf
- Leichtschlaf
- Wach (in Bett)
- **Notiz** Freitext (z.B. "Stress, schlechte Nacht", "früh ins Bett")
### 2.3 Abgeleitete Felder (automatisch berechnet)
- **Schlafdauer** aus Einschlafzeit + Aufwachzeit wenn nicht direkt eingegeben
- **Schlafeffizienz** Schlafdauer / Zeit im Bett × 100% (wenn Phasen vorhanden)
- **Tiefschlaf-Anteil %** Tiefschlaf / Gesamtschlaf × 100%
- **REM-Anteil %** REM / Gesamtschlaf × 100%
---
## 3. Erfassungs-Modi
### 3.1 Schnelleingabe (≤ 30 Sekunden)
Minimaler Aufwand für den täglichen Gebrauch:
```
Datum: [gestern / heute Nacht]
Schlafdauer: [7] h [30] min
Qualität: ★★★★☆ (1-5 Sterne)
[Speichern]
```
### 3.2 Detaileingabe (alle Felder)
Vollständige Erfassung für Power-User oder wenn Gerätedaten vorliegen:
```
Datum: [22.03.2026]
Eingeschlafen: [23:15]
Aufgewacht: [06:45]
Schlafdauer: [7h 30min] (automatisch berechnet)
Qualität: ★★★★☆
Aufwachungen: [1]
Tiefschlaf: [95] min
REM-Schlaf: [110] min
Leichtschlaf: [230] min
Wach im Bett: [15] min
Notiz: [...]
[Speichern]
```
### 3.3 Morgendlicher Check-in
- Beim Öffnen der App morgens optional: "Wie hast du geschlafen?"
- Zeigt Schnelleingabe direkt auf dem Dashboard
- Kann in den Einstellungen aktiviert/deaktiviert werden
- Reminder-Logik: nur anzeigen wenn noch kein Eintrag für die letzte Nacht
### 3.4 Import (Apple Health / Garmin)
- Beim CSV/Health-Import werden Schlafdaten automatisch erkannt und zugeordnet
- Importierte Daten befüllen alle verfügbaren Felder (Phasen wenn vorhanden)
- Manuelle Eingabe ergänzt oder überschreibt Importdaten für denselben Tag
- Quelle wird gespeichert (manuell / apple_health / garmin)
---
## 4. Navigation & Dashboard
### 4.1 Eigene Seite in der Navigation
- Neuer Nav-Eintrag: **Schlaf** (zwischen Verlauf und Analyse)
- Icon: Mond oder Bett-Symbol
- Zeigt: Letzte 7 Tage Übersicht + Verlauf + Eingabe-Button
### 4.2 Dashboard-Widget
- Kompaktes Widget auf dem Dashboard (unter den Hauptkennzahlen)
- Zeigt: Letzte Nacht (Dauer + Qualität) + 7-Tage-Durchschnitt
- Klick öffnet die Schlaf-Seite
- Wenn noch kein Eintrag heute: "Schlaf erfassen" Button
---
## 5. Auswertungen
### 5.1 Schlafdauer-Trend
- Zeitraum wählbar: 7 / 30 / 90 Tage
- Liniengrafik: tägliche Schlafdauer
- Referenzlinie: persönliches Schlafziel (konfigurierbar in Einstellungen)
- Durchschnittslinie: gleitender 7-Tage-Durchschnitt
- Farbcodierung: unter Ziel = orange/rot, über Ziel = grün
### 5.2 Schlafqualität-Trend
- Liniengrafik: tägliche Qualität (15)
- 7-Tage-Durchschnitt als Referenzlinie
- Kombiniert mit Schlafdauer in einer Grafik (zwei Y-Achsen)
### 5.3 Schlafphasen-Verteilung
- Balkengrafik: Tiefschlaf / REM / Leichtschlaf / Wach je Nacht
- Durchschnittliche Verteilung über Zeitraum
- Referenzwerte: empfohlene Anteile (Tiefschlaf 15-25%, REM 20-25%)
- Nur sichtbar wenn Phasendaten vorhanden
### 5.4 Schlafschulden-Berechnung
- Definition: Differenz zwischen Schlafziel und tatsächlichem Schlaf (kumuliert)
- Zeitraum: letzte 7 / 14 Tage
- Anzeige: "+2h 15min Schlafschuld" oder "0 kein Defizit"
- Positiver Saldo (Übererfüllung) wird als Puffer angezeigt
### 5.5 Optimales Schlaffenster
- Berechnung aus Einschlafzeit + Schlafqualität über Zeit
- Erkennt wann Schlaf am besten war (z.B. "Beste Qualität bei Einschlafzeit 2223 Uhr")
- Mindestdatenbasis: 14 Einträge mit Einschlafzeit
### 5.6 Korrelationen
Alle Korrelationen benötigen mindestens 14 gemeinsame Datenpunkte:
**Schlaf ↔ Ruhepuls:**
- Streudiagramm: Schlafdauer / Qualität vs. Ruhepuls nächster Morgen
- KI-Aussage: "Nach <7h Schlaf ist dein Ruhepuls Ø X bpm höher"
**Schlaf ↔ Trainingsleistung:**
- Vergleich: Trainingsintensität/-volumen nach gut vs. schlecht geschlafenen Nächten
- KI-Aussage: "Nach Nächten mit >7,5h Schlaf trainierst du Ø X% länger/intensiver"
**Schlaf ↔ Gewicht:**
- Korrelation: Schlafdauer vs. Gewichtsentwicklung (wöchentlich)
- KI-Aussage: "In Wochen mit <6h Schlaf im Schnitt +X kg Gewichtsschwankung"
---
## 6. Schlafziel (konfigurierbar)
- In den Einstellungen: "Mein Schlafziel" → Stunden + Minuten
- Standard: 7h 30min
- Wird als Referenzlinie in allen Grafiken angezeigt
- Fließt in Schlafschulden-Berechnung ein
- KI nutzt den Wert als Basis für Schlafmangel-Warnung
---
## 7. KI-Analyse
### 7.1 Neue KI-Platzhalter
```
{{schlaf_avg_dauer}} → "7h 12min (Ø 7 Tage)"
{{schlaf_avg_qualitaet}} → "3,8/5 (Ø 7 Tage)"
{{schlaf_trend_dauer}} → "sinkend / stabil / steigend"
{{schlaf_trend_qualitaet}} → "sinkend / stabil / steigend"
{{schlaf_schulden}} → "+2h 15min (letzte 7 Tage)"
{{schlaf_ziel}} → "7h 30min"
{{schlaf_optimum_fenster}} → "22:0023:00 Uhr (beste Qualität)"
{{schlaf_tiefschlaf_anteil}} → "18% (Ø letzte 14 Tage)"
{{schlaf_rem_anteil}} → "22% (Ø letzte 14 Tage)"
{{schlaf_aufwachungen}} → "1,2x pro Nacht (Ø 7 Tage)"
{{schlaf_korrelation_puls}} → "Ruhepuls +4 bpm nach <7h Schlaf"
{{schlaf_detail}} → tabellarische Übersicht letzte 7 Nächte
```
### 7.2 KI-Analyse-Funktionen
**Schlafmangel erkennen und warnen:**
- Trigger: Schlafdurchschnitt letzte 3 Tage < (Schlafziel 60min)
- KI-Ausgabe: Warnung + konkrete Empfehlung
- Beispiel: "Du schläfst seit 3 Tagen unter deinem Ziel. Schlafschuld: +2h 15min."
**Schlaf ↔ Trainingsleistung korrelieren:**
- Automatische Berechnung sobald genug Daten (14+ Einträge beider Module)
- KI-Ausgabe: narrative Korrelationsbeschreibung mit konkreten Zahlen
**Optimales Schlaffenster empfehlen:**
- Basierend auf historischen Einschlafzeiten + Qualitäten
- KI-Ausgabe: "Deine beste Schlafqualität erzielst du wenn du zwischen 2223 Uhr einschläfst"
**Schlaf ↔ Gewicht:**
- Wöchentliche Korrelation
- KI-Ausgabe: "In Wochen mit mehr Schlaf zeigt dein Gewicht weniger Schwankungen"
**Schlafphasen-Verteilung kommentieren:**
- Nur wenn Phasendaten vorhanden
- Referenz: Tiefschlaf 15-25%, REM 20-25%, Leichtschlaf 50-60%
- KI-Ausgabe: "Dein Tiefschlaf-Anteil liegt bei X% das ist [über/unter/im] empfohlenen Bereich"
---
## 8. Abgrenzung & offene Fragen
### In diesem Modul enthalten:
- Tägliche Schlaferfassung (Schnell + Detail)
- Dashboard-Widget + eigene Navigationsseite
- Morgendlicher Check-in (optional)
- Import aus Apple Health / Garmin CSV
- 5 Auswertungsansichten inkl. Korrelationen
- Konfigurierbares Schlafziel
- 12 neue KI-Platzhalter
- 5 KI-Analyse-Funktionen
### Nicht in diesem Modul:
- Automatischer Live-Sync aus Garmin/Apple Watch → v9h (Connectoren)
- Schlaf-Coaching / Schlafhygiene-Tipps → optional später
- Schnarchen / Atemaussetzer (SpO2) → Teil Vitalwerte v9e
- Push-Notification "Schlafenszeit-Reminder" → optional später
### Offene Fragen für technische Planung:
1. Neue DB-Tabelle `sleep_log` oder Erweiterung von `activity_log`?
2. Schlafphasen als separate Spalten oder als JSONB-Feld?
3. Morgendlicher Check-in: Frontend-State oder Backend-Logik (letzte Nacht bereits erfasst?)?
4. Korrelationsberechnung: im Backend (Python) oder im Frontend (JavaScript)?
5. Apple Health Export enthält Schlafphasen-Daten welches CSV-Format wird erwartet?
EOF

View File

@ -0,0 +1,257 @@
# Fachliche Anforderungen: Trainingstypen & Herzfrequenz
**Modul:** v9d
**Status:** Fachlich freigegeben, technische Implementierung ausstehend
**Letzte Aktualisierung:** März 2026
---
## 1. Überblick
Dieses Modul erweitert die bestehende Aktivitätserfassung um eine strukturierte
Kategorisierung von Trainingstypen sowie die Erfassung und Auswertung von
Herzfrequenz-Daten. Ziel ist eine tiefere Analyse der Trainingsqualität,
Erholung und Zielerreichung.
---
## 2. Trainingstypen
### 2.1 Kategorie-Hierarchie
Jedes Training hat einen **Haupttyp** und optional einen **Untertyp**:
```
Cardio (Ausdauer)
→ Laufen
→ Radfahren
→ Schwimmen
→ Rudern
→ Sonstiges Cardio
Kraft
→ Hypertrophie (Muskelaufbau, 8-12 Wdh, mittleres Gewicht)
→ Maximalkraft (1-5 Wdh, hohes Gewicht)
→ Kraftausdauer (15+ Wdh, leichtes Gewicht)
→ Funktionell (Kettlebell, TRX, Bodyweight)
Schnellkraft / HIIT
→ HIIT (High Intensity Interval Training)
→ Explosiv (Sprints, Sprünge, Wurfübungen)
→ Circuit Training
Kampfsport / Technikkraft
→ Techniktraining (Bewegungsabläufe, Formen)
→ Sparring / Wettkampf
→ Kraft für Kampfsport (kampfsportspezifische Übungen)
Mobility & Dehnung
→ Statisches Dehnen
→ Dynamisches Dehnen
→ Yoga
→ Faszienarbeit
Erholung (aktiv)
→ Spaziergang
→ Leichtes Schwimmen
→ Regenerationseinheit
```
### 2.2 Ruhetage
Ruhetage werden als **bewusste Einheit** erfasst nicht nur als Abwesenheit
von Training. Ein Ruhetag hat:
- Datum
- Typ: Vollständige Ruhe / Aktive Erholung
- Optional: Notiz (z.B. "Muskelkater Beine", "Reise")
Ruhetage fließen in die Wochenplanung und KI-Analyse ein.
### 2.3 Erfassung
**Bestehende Aktivitäten (Apple Health Import):**
- Beim Import wird Trainingstyp automatisch vorgeschlagen basierend auf
dem Apple Health Workout-Typ (z.B. "Running" → Cardio / Laufen)
- Nutzer kann den vorgeschlagenen Typ bestätigen oder korrigieren
- Bereits importierte Aktivitäten können nachträglich kategorisiert werden
**Manuell erfasste Aktivitäten:**
- Trainingstyp und Untertyp als Pflichtfeld beim Anlegen
- Untertyp ist optional
---
## 3. Herzfrequenz-Daten
### 3.1 Ruhepuls
**Definition:** Herzfrequenz in Ruhe, idealerweise morgens direkt nach dem
Aufwachen, vor dem Aufstehen.
**Erfassung:**
- Manuell: Nutzer gibt Wert täglich ein (ganze Zahl, Schläge/Minute)
- Import: Aus Apple Health / Garmin automatisch übernommen
- Bei beiden Quellen: Import als Standard, manuelle Eingabe ergänzt oder
überschreibt den Importwert für den jeweiligen Tag
**Bedeutung:**
- Ruhepuls-Anstieg (+5-7 bpm über persönlichem Durchschnitt) = Warnsignal
für Übertraining, Krankheit oder unzureichende Erholung
- Langfristiger Ruhepuls-Abfall = Verbesserung der Ausdauerleistung
### 3.2 HF während Training
**Felder pro Aktivitätseintrag:**
- HF Durchschnitt (bpm)
- HF Maximum (bpm)
**Erfassung:**
- Primär aus Apple Health / Garmin Import (wird automatisch befüllt)
- Manuelle Eingabe als Ergänzung wenn kein Gerät genutzt
### 3.3 HF-Zonen
**5-Zonen-Modell** (basierend auf maximaler Herzfrequenz):
| Zone | Name | % HFmax | Zweck |
|------|------|---------|-------|
| 1 | Regeneration | 50-60% | Aktive Erholung |
| 2 | Grundlagenausdauer | 60-70% | Fettverbrennung, Basis |
| 3 | Aerobe Ausdauer | 70-80% | Ausdaueraufbau |
| 4 | Anaerobe Schwelle | 80-90% | Leistungssteigerung |
| 5 | Maximale Intensität | 90-100% | Spitzenleistung |
**HFmax-Berechnung:**
- Standard-Formel: 220 - Alter
- Alternativ: Manuell vom Nutzer definierbar (aus Leistungstest)
**Auswertung:**
- Zeitverteilung in Zonen pro Training (falls HF-Kurve vorhanden)
- Oder: Zone basierend auf HF-Durchschnitt schätzen
### 3.4 HRV (Herzratenvariabilität)
**Definition:** Variation der Zeitabstände zwischen Herzschlägen.
Hohe HRV = gute Erholung / niedriger Stress.
Niedrige HRV = Belastung / Übertraining / Stress.
**Erfassung:**
- Manuell (ms, aus Smartwatch-App abgelesen)
- Import aus Apple Health (wenn vorhanden)
- Messung: morgens, gleiche Bedingungen wie Ruhepuls
**Interpretation:**
- Persönlicher Baseline-Wert wird über 4 Wochen berechnet
- Abweichung >10% unter Baseline = Warnung
### 3.5 VO2Max
**Definition:** Maximale Sauerstoffaufnahme, Indikator für aerobe Leistungsfähigkeit.
**Erfassung:**
- Import aus Apple Health / Garmin (falls Gerät berechnet)
- Alternativ: Schätzung aus Ruhepuls + HFmax (Cooper-Formel):
`VO2Max ≈ 15 × (HFmax / HF_Ruhe)`
**Anzeige:**
- Aktueller Wert + Trend
- Einordnung nach Alters-/Geschlechtsnorm (sehr gut / gut / durchschnittlich / verbesserungswürdig)
---
## 4. Auswertungen & Dashboards
### 4.1 Trainingstyp-Verteilung
- Zeitraum wählbar: letzte 4 / 8 / 12 Wochen
- Kreisdiagramm oder Balkendiagramm: Anteil je Haupttyp in %
- Zusätzlich: Anzahl Einheiten je Typ
### 4.2 Trainingshäufigkeit pro Typ
- Wochenansicht: Wie viele Einheiten je Typ pro Woche
- Trend: Vergleich aktuelle Woche vs. Durchschnitt
### 4.3 Ruhepuls-Trend
- Liniengrafik über Zeit (7 / 30 / 90 Tage)
- Persönlicher Durchschnitt als Referenzlinie
- Warnung wenn Ruhepuls >5 bpm über 7-Tage-Durchschnitt
### 4.4 HF-Zonen-Verteilung
- Pro Training: Zeitanteil in jeder Zone (falls Daten vorhanden)
- Aggregiert: Zonenverteilung der letzten 30 Tage
### 4.5 Erholungsstatus
- Kombination aus: HRV + Ruhepuls + letzter Trainingsbelastung
- Ampel-System: 🟢 Gut erholt / 🟡 Teilweise erholt / 🔴 Erholung nötig
- Empfehlung: "Heute intensives Training möglich" / "Leichtes Training empfohlen" / "Ruhetag empfohlen"
### 4.6 Wochenplanung Soll vs. Ist
- Nutzer definiert Wochenziel: z.B. "3x Kraft, 2x Cardio, 1x Mobility, 1 Ruhetag"
- Dashboard zeigt: erreicht / offen / überschritten
- Einfache Eingabe, kein komplexer Planer
---
## 5. KI-Analyse
Die KI-Analyse wird um folgende Platzhalter erweitert:
### 5.1 Übertraining erkennen
**Trigger-Bedingungen (Beispiele):**
- >5 Krafteinheiten in 7 Tagen ohne Ruhetag
- Ruhepuls-Anstieg + gleichzeitig hohe Trainingsbelastung
- HRV unter persönlicher Baseline
**KI-Ausgabe:** Warnung + konkrete Empfehlung (Ruhetag, Intensität reduzieren)
### 5.2 Ruhetag empfehlen
**Basierend auf:** HRV-Trend + Ruhepuls-Trend + Trainingsbelastung letzte 5 Tage
**KI-Ausgabe:** "Dein Erholungsstatus zeigt X heute wäre ein Ruhetag oder leichtes Training sinnvoll"
### 5.3 Trainingstypen und Primärziel
**Beispiel Muskelaufbau:** KI erkennt wenn <40% Krafttraining Hinweis
**Beispiel Kondition:** KI erkennt wenn <40% Cardio Hinweis
**Voraussetzung:** Primärziel-Modul (v9e) muss implementiert sein
### 5.4 Phasen erkennen und kommentieren
**Phasen:**
- Aufbauphase: Steigende Belastung über mehrere Wochen
- Erholungsphase (Deload): Reduzierte Belastung nach intensiver Phase
- Plateau: Gleichbleibende Belastung ohne Progression
**KI-Ausgabe:** Phase benennen + situative Empfehlung
### 5.5 Neue KI-Platzhalter
```
{{trainingstyp_verteilung}} → "60% Kraft, 30% Cardio, 10% Mobility (letzte 4 Wochen)"
{{ruhetage_letzte_woche}} → Anzahl Ruhetage letzte 7 Tage
{{ruhepuls_aktuell}} → heutiger / letzter Ruhepuls
{{ruhepuls_trend}} → "sinkend / stabil / steigend"
{{hrv_aktuell}} → letzter HRV-Wert
{{hrv_baseline}} → persönlicher HRV-Durchschnitt (30 Tage)
{{erholungsstatus}} → "gut / teilweise / schlecht"
{{vo2max}} → aktueller VO2Max-Wert
{{trainingsphase}} → "Aufbau / Erholung / Plateau / unbekannt"
{{hf_zonen_verteilung}} → "Zone 2: 45%, Zone 3: 35%, Zone 4: 20%"
```
---
## 6. Abgrenzung & offene Fragen
### In diesem Modul enthalten:
- Kategorisierung bestehender + neuer Aktivitäten
- Ruhepuls, HRV, VO2Max erfassen und auswerten
- HF-Zonen berechnen und anzeigen
- Erholungsstatus-Ampel
- Wochenplanung (einfach)
- KI-Platzhalter und Analyse-Logik
### Nicht in diesem Modul (spätere Versionen):
- Periodisierungsplaner (Makrozyklen, Mikrozyklen) → v9g
- Sportart-spezifische Metriken (Pace, Watt) → v9e
- Verknüpfung mit Primärziel → v9e (Primärziel-Modul)
- Connector zu Garmin/Strava für Live-HF → v9h
### Offene Fragen für technische Planung:
1. Werden HF-Kurven (Zeit × HF) aus Apple Health importiert oder nur Avg/Max?
2. Soll HRV als eigener Tageseintrag oder als Ergänzung zum Ruhepuls-Eintrag erfasst werden?
3. Soll die Wochenplanung persistiert werden (DB) oder nur als Session-Einstellung?

View File

@ -0,0 +1,840 @@
# Training Type Profiles Umfassendes Trainingsmanagement-System
**Issue:** #15 (erweitert)
**Status:** Phase 1 KOMPLETT ✅ (Foundation + Auto-Evaluation) | Phase 2 ausstehend (Admin-UI)
**Erstellt:** 2026-03-23
**Implementiert:** Phase 1 - 2026-03-23
---
## Vision
Jeder Trainingstyp erhält ein **umfassendes Profil** mit Parametern, die:
1. **Mindestanforderungen** definieren (wann ist es "echtes" Training?)
2. **Intensitätsbereiche** beschreiben (Zonen: regenerativ, moderat, intensiv)
3. **Trainingswirkung** charakterisieren (welche Fähigkeiten werden trainiert?)
4. **Periodisierung** unterstützen (optimale Frequenz, Erholungsbedarf)
5. **Dynamische Bewertung** ermöglichen (ist diese konkrete Aktivität gut/schlecht?)
---
## Trainingstyp-Profil: Vollständige Parameter-Definition
### Struktur (JSONB in training_types.profile)
```json
{
"version": "1.0",
"created_at": "2026-03-23",
// ━━━ 1. MINDESTANFORDERUNGEN (Quality Gates) ━━━
"minimum_requirements": {
"enabled": true,
"rules": {
"duration_min": {
"value": 15,
"weight": 5,
"reason": "Unter 15min keine signifikante Trainingswirkung"
},
"avg_hr_min": {
"value": 100,
"weight": 3,
"reason": "Puls muss für Ausdauerreiz erhöht sein"
},
"max_hr_min": {
"value": 120,
"weight": 1,
"reason": "Maximalpuls zeigt echte Belastungsspitze"
},
"distance_km": {
"value": 1.0,
"weight": 2,
"reason": "Mindestdistanz für Lauftraining"
},
"kcal_active": {
"value": 100,
"weight": 1,
"reason": "Mindest-Energieverbrauch"
}
},
"pass_threshold": 0.6,
"fail_action": "mark_low_quality"
},
// ━━━ 2. INTENSITÄTSBEREICHE (HF-Zonen) ━━━
"intensity_zones": {
"method": "percentage_max_hr", // oder "percentage_reserve_hr"
"zones": [
{
"name": "regenerativ",
"min_percent": 50,
"max_percent": 60,
"color": "#4CAF50",
"effect": "Erholung, Grundlagenausdauer 1",
"target_duration_min": 30
},
{
"name": "grundlagenausdauer",
"min_percent": 60,
"max_percent": 70,
"color": "#2196F3",
"effect": "Aerobe Kapazität, Fettstoffwechsel",
"target_duration_min": 45
},
{
"name": "entwicklungsbereich",
"min_percent": 70,
"max_percent": 80,
"color": "#FF9800",
"effect": "VO2max-Training, Laktattoleranz",
"target_duration_min": 20
},
{
"name": "schwellentraining",
"min_percent": 80,
"max_percent": 90,
"color": "#F44336",
"effect": "Anaerobe Schwelle, Wettkampftempo",
"target_duration_min": 10
},
{
"name": "maximale_intensität",
"min_percent": 90,
"max_percent": 100,
"color": "#9C27B0",
"effect": "Maximalkraft, Sprint, HIIT",
"target_duration_min": 5
}
],
"evaluation_rules": {
"time_in_zone_required": true,
"min_time_in_target_zone_percent": 70
}
},
// ━━━ 3. TRAININGSWIRKUNG (Abilities Mapping) ━━━
"training_effects": {
"primary_abilities": [
{
"category": "konditionell",
"ability": "ausdauer",
"intensity": 5,
"description": "Hauptfokus: Aerobe Ausdauer"
},
{
"category": "konditionell",
"ability": "schnelligkeit",
"intensity": 2,
"description": "Nebenfokus: Lauftempo"
}
],
"secondary_abilities": [
{
"category": "koordinativ",
"ability": "rhythmus",
"intensity": 3,
"description": "Laufrhythmus, Schrittfrequenz"
},
{
"category": "psychisch",
"ability": "willenskraft",
"intensity": 4,
"description": "Durchhaltevermögen bei langen Läufen"
}
],
"metabolic_focus": ["aerobic", "fat_oxidation"],
"muscle_groups": ["legs_posterior", "legs_anterior", "core"],
"energy_systems": ["aerobic_oxidative", "anaerobic_lactic"]
},
// ━━━ 4. PERIODISIERUNG & FREQUENZ ━━━
"periodization": {
"recommended_frequency": {
"per_week_min": 2,
"per_week_max": 5,
"per_week_optimal": 3,
"consecutive_days_max": 2
},
"recovery_requirement": {
"hours_before_same_type": 48,
"hours_before_high_intensity": 24,
"rpe_threshold_for_extra_rest": 8
},
"progression": {
"beginner_duration_min": 20,
"intermediate_duration_min": 30,
"advanced_duration_min": 45,
"volume_increase_percent_per_week": 10
},
"deload_recommendation": {
"every_n_weeks": 4,
"volume_reduction_percent": 40
}
},
// ━━━ 5. LEISTUNGSINDIKATOREN (KPIs) ━━━
"performance_indicators": {
"primary_metrics": ["pace_min_per_km", "avg_hr", "distance_km"],
"secondary_metrics": ["kcal_per_km", "cadence", "elevation_gain"],
"benchmark_values": {
"beginner": {"pace_min_per_km": 7.0, "distance_km": 3.0},
"intermediate": {"pace_min_per_km": 6.0, "distance_km": 5.0},
"advanced": {"pace_min_per_km": 5.0, "distance_km": 10.0}
}
},
// ━━━ 6. KONTEXT & VARIANTEN ━━━
"context": {
"environment": ["outdoor", "indoor_treadmill"],
"weather_sensitivity": "high",
"equipment_required": ["running_shoes"],
"location_types": ["road", "trail", "track"],
"variants": [
{
"name": "intervall_lauf",
"modifications": {
"intensity_zones.focus": "schwellentraining",
"minimum_requirements.avg_hr_min": 140
}
},
{
"name": "long_jog",
"modifications": {
"intensity_zones.focus": "grundlagenausdauer",
"minimum_requirements.duration_min": 60
}
}
]
},
// ━━━ 7. SICHERHEIT & KONTRAINDIKATIONEN ━━━
"safety": {
"max_hr_warning": 180,
"min_recovery_hr": 100,
"contraindications": [
"acute_injury_lower_body",
"cardiovascular_episode_recent"
],
"weather_limits": {
"temp_celsius_min": -5,
"temp_celsius_max": 32,
"wind_speed_kmh_max": 40
}
},
// ━━━ 8. KI-ANALYSE KONTEXT ━━━
"ai_context": {
"questions_to_ask": [
"Wie fühlte sich dein Tempo an?",
"Hattest du Atembeschwerden?",
"Wie war die Regeneration danach?"
],
"evaluation_focus": [
"Pace-Entwicklung über Distanz",
"Herzfrequenz-Stabilität",
"Erholungspuls nach Training"
],
"comparison_metrics": [
"Tempo vs. letzte 4 Wochen",
"Durchschnittspuls bei gleicher Distanz",
"Subjektive Anstrengung (RPE) vs. objektive Daten"
]
}
}
```
---
## Dynamische Bewertung: Wie das Profil genutzt wird
### 1. Mindestanforderungen-Check (Quality Gate)
**Funktion:** `evaluate_minimum_requirements(activity, profile)`
```python
def evaluate_minimum_requirements(activity: dict, profile: dict) -> dict:
"""
Prüft ob Aktivität Mindestanforderungen erfüllt.
Returns:
{
"passed": bool,
"score": float,
"failed_rules": [{"rule": str, "expected": val, "actual": val}],
"recommendation": str
}
"""
requirements = profile['minimum_requirements']
if not requirements['enabled']:
return {"passed": True, "score": 1.0, "failed_rules": []}
total_weight = 0
passed_weight = 0
failed_rules = []
for rule_name, rule_config in requirements['rules'].items():
weight = rule_config['weight']
total_weight += weight
actual = activity.get(rule_name)
expected = rule_config['value']
if actual is not None and actual >= expected:
passed_weight += weight
else:
failed_rules.append({
"rule": rule_name,
"expected": expected,
"actual": actual,
"reason": rule_config['reason']
})
score = passed_weight / total_weight if total_weight > 0 else 1.0
passed = score >= requirements['pass_threshold']
recommendation = generate_recommendation(failed_rules, profile)
return {
"passed": passed,
"score": round(score, 2),
"failed_rules": failed_rules,
"recommendation": recommendation
}
```
### 2. Intensitäts-Bewertung (Zone Analysis)
**Funktion:** `evaluate_intensity_distribution(activity, profile)`
```python
def evaluate_intensity_distribution(activity: dict, profile: dict) -> dict:
"""
Analysiert in welcher HF-Zone das Training stattfand.
Returns:
{
"dominant_zone": str,
"time_in_zones": {zone: minutes},
"zone_quality": float (0-1),
"recommendation": str
}
"""
zones = profile['intensity_zones']['zones']
max_hr = activity.get('user_max_hr', 180) # From user profile
avg_hr = activity.get('avg_hr')
if not avg_hr:
return {"dominant_zone": "unknown", "zone_quality": 0}
avg_hr_percent = (avg_hr / max_hr) * 100
# Finde passende Zone
dominant_zone = None
for zone in zones:
if zone['min_percent'] <= avg_hr_percent <= zone['max_percent']:
dominant_zone = zone
break
if not dominant_zone:
return {"dominant_zone": "out_of_range", "zone_quality": 0}
# Prüfe ob Dauer passt zur Zone
duration = activity.get('duration_min', 0)
target_duration = dominant_zone['target_duration_min']
duration_quality = min(duration / target_duration, 1.0)
recommendation = f"Training in Zone '{dominant_zone['name']}' (Effekt: {dominant_zone['effect']}). "
if duration < target_duration:
recommendation += f"Für optimale Wirkung: {target_duration}min empfohlen."
return {
"dominant_zone": dominant_zone['name'],
"zone_color": dominant_zone['color'],
"zone_effect": dominant_zone['effect'],
"avg_hr_percent": round(avg_hr_percent, 1),
"duration_quality": round(duration_quality, 2),
"recommendation": recommendation
}
```
### 3. Trainingswirkung-Analyse (Abilities Development)
**Funktion:** `evaluate_training_effects(activity, profile)`
```python
def evaluate_training_effects(activity: dict, profile: dict) -> dict:
"""
Berechnet welche Fähigkeiten durch diese Aktivität trainiert wurden.
Returns:
{
"abilities_trained": [
{"category": str, "ability": str, "intensity": int, "quality": float}
],
"total_training_load": float
}
"""
effects = profile['training_effects']
intensity_eval = evaluate_intensity_distribution(activity, profile)
min_requirements_eval = evaluate_minimum_requirements(activity, profile)
# Qualitätsfaktor basierend auf Mindestanforderungen
quality_factor = min_requirements_eval['score']
abilities_trained = []
# Primary abilities mit voller Intensität
for ability in effects['primary_abilities']:
abilities_trained.append({
"category": ability['category'],
"ability": ability['ability'],
"intensity": ability['intensity'],
"quality": quality_factor,
"contribution": ability['intensity'] * quality_factor
})
# Secondary abilities mit reduzierter Intensität
for ability in effects['secondary_abilities']:
abilities_trained.append({
"category": ability['category'],
"ability": ability['ability'],
"intensity": ability['intensity'],
"quality": quality_factor * 0.7, # Sekundär = 70%
"contribution": ability['intensity'] * quality_factor * 0.7
})
total_training_load = sum(a['contribution'] for a in abilities_trained)
return {
"abilities_trained": abilities_trained,
"total_training_load": round(total_training_load, 2),
"metabolic_focus": effects['metabolic_focus'],
"muscle_groups": effects['muscle_groups']
}
```
### 4. Periodisierungs-Check (Recovery & Frequency)
**Funktion:** `evaluate_periodization_compliance(activity, profile, recent_activities)`
```python
def evaluate_periodization_compliance(
activity: dict,
profile: dict,
recent_activities: list
) -> dict:
"""
Prüft ob Aktivität zu Periodisierungs-Empfehlungen passt.
Returns:
{
"frequency_status": str,
"recovery_adequate": bool,
"warning": str | None
}
"""
periodization = profile['periodization']
activity_date = activity['date']
# Zähle gleiche Trainingstypen in letzter Woche
same_type_count = sum(
1 for a in recent_activities
if a['training_type_id'] == activity['training_type_id']
and days_between(a['date'], activity_date) <= 7
)
# Prüfe Erholungszeit
last_same_type = next(
(a for a in recent_activities
if a['training_type_id'] == activity['training_type_id']),
None
)
recovery_adequate = True
warning = None
if last_same_type:
hours_since_last = hours_between(last_same_type['date'], activity_date)
required_hours = periodization['recovery_requirement']['hours_before_same_type']
if hours_since_last < required_hours:
recovery_adequate = False
warning = f"Zu wenig Erholung: {hours_since_last}h statt {required_hours}h empfohlen"
# Frequenz-Status
optimal_freq = periodization['recommended_frequency']['per_week_optimal']
if same_type_count < optimal_freq:
frequency_status = "under_optimal"
elif same_type_count > periodization['recommended_frequency']['per_week_max']:
frequency_status = "over_optimal"
warning = "Übertraining-Risiko: Zu viele Einheiten diese Woche"
else:
frequency_status = "optimal"
return {
"frequency_status": frequency_status,
"weekly_count": same_type_count,
"recovery_adequate": recovery_adequate,
"warning": warning
}
```
### 5. Leistungsentwicklung (Performance Tracking)
**Funktion:** `evaluate_performance_development(activity, profile, historical_activities)`
```python
def evaluate_performance_development(
activity: dict,
profile: dict,
historical_activities: list
) -> dict:
"""
Vergleicht aktuelle Leistung mit Historie.
Returns:
{
"trend": str, # "improving" | "stable" | "declining"
"metrics_comparison": {metric: {"current": val, "avg_4weeks": val, "change_percent": val}},
"benchmark_level": str # "beginner" | "intermediate" | "advanced"
}
"""
kpis = profile['performance_indicators']
primary_metrics = kpis['primary_metrics']
# Berechne 4-Wochen-Durchschnitte
comparison = {}
for metric in primary_metrics:
current_value = activity.get(metric)
if not current_value:
continue
historical_values = [
a[metric] for a in historical_activities[-12:] # Letzte 12 Einheiten
if a.get(metric) is not None
]
if historical_values:
avg_historical = sum(historical_values) / len(historical_values)
change_percent = ((current_value - avg_historical) / avg_historical) * 100
comparison[metric] = {
"current": current_value,
"avg_4weeks": round(avg_historical, 2),
"change_percent": round(change_percent, 1)
}
# Trend-Bewertung
if all(c['change_percent'] > 5 for c in comparison.values()):
trend = "improving"
elif all(c['change_percent'] < -5 for c in comparison.values()):
trend = "declining"
else:
trend = "stable"
# Benchmark-Level ermitteln
benchmarks = kpis['benchmark_values']
benchmark_level = determine_benchmark_level(activity, benchmarks)
return {
"trend": trend,
"metrics_comparison": comparison,
"benchmark_level": benchmark_level
}
```
---
## Dynamische Gesamt-Bewertung
**Master-Funktion:** `evaluate_activity(activity, profile, context)`
```python
def evaluate_activity(activity: dict, profile: dict, context: dict) -> dict:
"""
Vollständige Bewertung einer Aktivität anhand des Trainingstyp-Profils.
Args:
activity: Die zu bewertende Aktivität
profile: Das Trainingstyp-Profil
context: {
"recent_activities": [...],
"historical_activities": [...],
"user_profile": {...}
}
Returns:
{
"evaluated_at": ISO timestamp,
"version": "1.0",
"minimum_requirements": {...},
"intensity_analysis": {...},
"training_effects": {...},
"periodization_check": {...},
"performance_development": {...},
"overall_score": float (0-1),
"quality_label": str, # "excellent" | "good" | "acceptable" | "poor"
"recommendations": [str],
"warnings": [str]
}
"""
results = {
"evaluated_at": datetime.now().isoformat(),
"version": "1.0",
"minimum_requirements": evaluate_minimum_requirements(activity, profile),
"intensity_analysis": evaluate_intensity_distribution(activity, profile),
"training_effects": evaluate_training_effects(activity, profile),
"periodization_check": evaluate_periodization_compliance(
activity, profile, context['recent_activities']
),
"performance_development": evaluate_performance_development(
activity, profile, context['historical_activities']
)
}
# Gesamt-Score berechnen (gewichtet)
weights = {
"minimum_requirements": 0.4,
"intensity_analysis": 0.2,
"periodization_check": 0.2,
"performance_development": 0.2
}
overall_score = (
results['minimum_requirements']['score'] * weights['minimum_requirements'] +
results['intensity_analysis']['duration_quality'] * weights['intensity_analysis'] +
(1.0 if results['periodization_check']['recovery_adequate'] else 0.5) * weights['periodization_check'] +
(1.0 if results['performance_development']['trend'] == 'improving' else 0.7) * weights['performance_development']
)
# Quality Label
if overall_score >= 0.9:
quality_label = "excellent"
elif overall_score >= 0.7:
quality_label = "good"
elif overall_score >= 0.5:
quality_label = "acceptable"
else:
quality_label = "poor"
# Recommendations sammeln
recommendations = []
warnings = []
if not results['minimum_requirements']['passed']:
for failed in results['minimum_requirements']['failed_rules']:
recommendations.append(
f"{failed['rule']}: {failed['reason']} (Ist: {failed['actual']}, Soll: {failed['expected']})"
)
if results['periodization_check']['warning']:
warnings.append(results['periodization_check']['warning'])
results.update({
"overall_score": round(overall_score, 2),
"quality_label": quality_label,
"recommendations": recommendations,
"warnings": warnings
})
return results
```
---
## DB-Schema (Erweitert)
```sql
-- Training Types: Vollständiges Profil
ALTER TABLE training_types ADD COLUMN profile JSONB DEFAULT NULL;
-- Activity Log: Evaluation Results
ALTER TABLE activity_log ADD COLUMN evaluation JSONB DEFAULT NULL;
-- Beispiel-Daten:
UPDATE training_types SET profile = '{...siehe oben...}'::jsonb
WHERE name_de = 'Laufen';
```
---
## User Interface: Aktivitäts-Detail-Ansicht
```
┌────────────────────────────────────────────────────────────────┐
│ 🏃 Laufen - 23. März 2026 │
├────────────────────────────────────────────────────────────────┤
│ │
│ ━━━ ZUSAMMENFASSUNG ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Gesamtbewertung: ⭐⭐⭐⭐⭐ Exzellent (Score: 0.92) │
│ │
│ 45 Minuten · 5.2 km · Ø 142 bpm (79% Max-HF) · 380 kcal │
│ │
│ ━━━ MINDESTANFORDERUNGEN ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ ✅ Dauer: 45min (≥ 15min erforderlich) │
│ ✅ Ø Herzfrequenz: 142 bpm (≥ 100 bpm) │
│ ✅ Max. Herzfrequenz: 165 bpm (≥ 120 bpm) │
│ ✅ Distanz: 5.2 km (≥ 1.0 km) │
│ │
│ ━━━ INTENSITÄTSANALYSE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Trainingszone: Entwicklungsbereich (70-80% Max-HF) │
│ Effekt: VO2max-Training, Laktattoleranz │
│ │
│ [████████████████████░░] 79% Max-HF │
│ │
│ Zeitverteilung: │
│ • Regenerativ (50-60%): 0 min │
│ • Grundlagenausdauer (60-70%): 8 min │
│ • Entwicklungsbereich (70-80%): 32 min ✅ Optimal │
│ • Schwellentraining (80-90%): 5 min │
│ │
│ ━━━ TRAININGSWIRKUNG ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Trainierte Fähigkeiten: │
│ • Ausdauer (Konditionell): ●●●●● (5/5) - Primärfokus │
│ • Schnelligkeit (Konditionell): ●●○○○ (2/5) - Nebenfokus │
│ • Rhythmus (Koordinativ): ●●●○○ (3/5) - Sekundär │
│ • Willenskraft (Psychisch): ●●●●○ (4/5) - Sekundär │
│ │
│ Gesamte Trainingsbelastung: 18.2 Punkte │
│ │
│ ━━━ PERIODISIERUNG ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Frequenz diese Woche: 3 Einheiten ✅ Optimal (2-5 empfohlen) │
│ Erholung seit letztem Lauf: 52 Stunden ✅ Ausreichend │
│ Nächstes Training empfohlen: Frühestens in 48h │
│ │
│ ━━━ LEISTUNGSENTWICKLUNG ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Trend: ↗ Verbesserung │
│ │
│ Pace: 8:39 min/km (Ø 4 Wochen: 9:15 min/km) +6.5% schneller │
│ Ø HF: 142 bpm (Ø 4 Wochen: 148 bpm) -4% niedriger │
│ │
│ Dein Level: Fortgeschritten (Intermediate) │
│ │
│ ━━━ EMPFEHLUNGEN ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ ✨ Exzellentes Training! Du trainierst im optimalen │
│ Entwicklungsbereich für VO2max-Verbesserung. │
│ │
│ 💡 Deine Pace verbessert sich stetig bei gleichzeitig │
│ niedrigerer Herzfrequenz - klares Zeichen für bessere │
│ Ausdauerkapazität. │
│ │
│ 📅 Nächste Schritte: │
│ • 1-2 Tage Regeneration │
│ • Dann: Langer, langsamer Lauf (60-70% Max-HF, 60min) │
│ │
└────────────────────────────────────────────────────────────────┘
```
---
## Implementierungs-Phasen
### Phase 1: Foundation (MVP)
1. DB-Migration: `profile` Spalte in training_types
2. DB-Migration: `evaluation` Spalte in activity_log
3. Backend: `evaluate_activity()` Master-Funktion
4. Backend: Evaluation beim INSERT/UPDATE
### Phase 2: Profile Editor (Admin-UI)
5. Admin-UI: Trainingstyp-Profil-Editor (JSON-basiert)
6. Admin-UI: Profile-Templates für Top 5 Trainingstypen
7. Admin-UI: Preview-Funktion (wie wird evaluiert?)
### Phase 3: User Experience
8. Frontend: Aktivitäts-Detail-Ansicht mit allen Bewertungen
9. Frontend: Badge-System (Excellent/Good/Acceptable/Poor)
10. Frontend: Trainingswirkungs-Visualisierung
### Phase 4: Advanced Features
11. KI-Integration: Nutze Evaluation-Daten für Prompts
12. Periodisierungs-Warnungen im Dashboard
13. Leistungsentwicklung-Charts
14. Historische Re-Evaluation (Batch-Job)
**Geschätzter Aufwand:** 12-16 Stunden (komplett)
---
## Offene Fragen
1. **Profil-Komplexität:** Alle Parameter von Anfang an oder iterativ erweitern?
2. **User-Anpassbarkeit:** Soll User eigene Profile erstellen können?
3. **Performance:** JSONB-Queries optimiert genug für große Datenmengen?
4. **UI-Komplexität:** Ist die Detail-Ansicht zu überladen?
5. **Backward Compatibility:** Was passiert mit Aktivitäten ohne Profil?
---
---
## Implementierungs-Status
### Phase 1: Foundation ✅ KOMPLETT (23.03.2026)
**Phase 1.1: Database & Core Engine** ✅
- ✅ Migration 013: training_parameters Tabelle (16 Standard-Parameter)
- ✅ Migration 014: training_types.profile + activity_log.evaluation + triggers
- ✅ rule_engine.py: RuleEvaluator mit 9 Operatoren
- ✅ rule_engine.py: IntensityZoneEvaluator für HF-Zonen
- ✅ rule_engine.py: TrainingEffectsEvaluator für Fähigkeiten
- ✅ profile_evaluator.py: TrainingProfileEvaluator (7 Dimensionen)
- ✅ evaluation_helper.py: Parameter-Loading + Context-Loading + Batch-Evaluation
- ✅ routers/evaluation.py: API-Endpoints für manuelle Evaluation
- ✅ Commit: 1b9cd6d
**Phase 1.2: Auto-Evaluation** ✅
- ✅ activity.py: create_activity() → Auto-Evaluation nach INSERT
- ✅ activity.py: update_activity() → Auto-Evaluation nach UPDATE
- ✅ activity.py: import_activity_csv() → Auto-Evaluation nach CSV-Import
- ✅ activity.py: bulk_categorize_activities() → Auto-Evaluation nach Bulk-Update
- ✅ Fehlerbehandlung mit try/except (verhindert Blockierung bei Evaluation-Fehlern)
- ✅ Commit: e119537
**Backend Implementation:** 100% komplett
- Parameter-Registry: extensibel via SQL
- Regel-System: flexibel, unterstützt >= und <= (Laufen vs. Meditation)
- 7 Dimensionen: Minimum Requirements, Intensity Zones, Training Effects, Periodization, Performance, Safety, AI Context
- Evaluation-Results in activity_log.evaluation gespeichert
**Getestet:** Syntax-Check bestanden (py_compile)
### Phase 2: Admin-UI 🔲 Ausstehend
**Aufgaben:**
- Admin-UI: Parameter-Registry-Verwaltung (CRUD)
- Admin-UI: Trainingstyp-Profil-Editor (JSON-Editor mit Syntax-Highlighting)
- Admin-UI: Profile-Templates für Top 5 Trainingstypen (Laufen, Radfahren, Schwimmen, Krafttraining, Meditation)
- Admin-UI: Preview-Funktion (Test-Evaluation mit Beispiel-Aktivität)
- Validation: Profil-Schema-Validator
**Geschätzter Aufwand:** 4-6 Stunden
### Phase 3: User-UI 🔲 Ausstehend
**Aufgaben:**
- Frontend: ActivityDetailPage mit vollständiger Evaluation-Anzeige
- Frontend: Quality Badges (excellent/good/acceptable/poor) in Listen
- Frontend: Filter nach Quality Label
- Frontend: Trainingswirkungs-Visualisierung (Fähigkeiten-Übersicht)
- Frontend: Dashboard-Stats (Durchschnitt Quality Score)
**Geschätzter Aufwand:** 4-6 Stunden
### Phase 4: Advanced Features 🔲 Future
**Optional:**
- KI-Integration: Nutze Evaluation-Daten in AI-Prompts
- Periodisierungs-Warnungen im Dashboard
- Leistungsentwicklung-Charts
- Historische Re-Evaluation (Batch-Job über Admin-Panel)
**Was denkst du zu diesem erweiterten Ansatz?**

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
{
"name": "Mehrstufige Gesamtanalyse (2)",
"slug": "pipeline",
"display_name": "🔬 Mehrstufige Gesamtanalyse (2)",
"description": "Master-Schalter für die gesamte Pipeline. Deaktiviere diese Analyse, um die Pipeline komplett zu verstecken.",
"type": "pipeline",
"category": "ganzheitlich",
"active": true,
"sort_order": -10,
"output_format": "text",
"template": null,
"stages": [
{
"stage": 1,
"prompts": [
{
"slug": "pipeline_body",
"source": "reference",
"template": "PIPELINE_MASTER",
"output_key": "stage1_body",
"output_format": "json",
"output_schema": null
},
{
"slug": "pipeline_nutrition",
"source": "reference",
"template": "",
"output_key": "stage1_nutrition",
"output_format": "json",
"output_schema": null
},
{
"slug": "pipeline_activity",
"source": "reference",
"template": "",
"output_key": "stage1_activity",
"output_format": "json",
"output_schema": null
}
]
},
{
"stage": 2,
"prompts": [
{
"slug": null,
"source": "inline",
"template": "Du bist ein Gesundheits- und Fitnesscoach. Erstelle eine vollständige, \npersonalisierte Analyse für {{name}} auf Deutsch (450550 Wörter).\n\nDATENZUSAMMENFASSUNGEN AUS STUFE 1:\nKörper: {{stage1_body}}\nErnährung: {{stage1_nutrition}}\nAktivität: {{stage1_activity}}\nProtein-Ziel: {{protein_ziel_low}}{{protein_ziel_high}}g/Tag\n\nSchreibe alle Abschnitte vollständig aus:\n⚖ **Gewichts- & Körperzusammensetzung**\n🍽 **Ernährungsanalyse**\n🏋 **Aktivität & Energiebilanz**\n🔗 **Zusammenhänge** (Verbindungen zwischen Ernährung, Training, Körper)\n💪 **3 Empfehlungen** (nummeriert, konkret, datenbasiert)\n\nSachlich, motivierend, Zahlen zitieren, keine Diagnosen.",
"output_key": "output_1774507015689",
"output_format": "text",
"output_schema": null
}
]
}
]
}

View File

@ -0,0 +1,387 @@
# Aggregation Methods Goal Value Calculation
**Zweck:** Dokumentation für Entwicklung und Erweiterung von Aggregationsmethoden im Goal-System.
**Datum:** 2026-03-28
**Version:** 1.0
**Modul:** `backend/goal_utils.py``_fetch_by_aggregation_method()`
---
## Übersicht
Aggregationsmethoden berechnen den `current_value` von Goals aus Rohdaten (z.B. Trainings, Gewicht, Ernährung). Sie sind der Kern des dynamischen Goal-Tracking-Systems.
**Beispiel:**
```python
Goal: "Trainingshäufigkeit Krafttraining"
source_table: activity_log
source_column: id (nur für COUNT relevant)
aggregation_method: avg_per_week_30d
filter_conditions: {"training_category": "strength"}
→ Berechnet: Durchschnittliche Anzahl Krafttrainings pro Woche (über 30 Tage)
```
---
## Architektur
### 1. Wo sind Methoden definiert?
**Datei:** `backend/goal_utils.py`
**Funktion:** `_fetch_by_aggregation_method(conn, profile_id, table, column, method, filter_conditions)`
**Aufruf-Hierarchie:**
```
goal_utils.fetch_goal_value()
└─> _fetch_by_aggregation_method()
└─> SQL Query mit method-spezifischer Logik
```
### 2. Verfügbare Methoden (Stand: 2026-03-28)
| Methode | Beschreibung | SQL Aggregat | Zeitfenster | Use Case |
|---------|--------------|--------------|-------------|----------|
| `latest` | Aktuellster Wert | SELECT {column} ORDER BY date DESC LIMIT 1 | — | Gewicht, Körperfett, VO2max |
| `avg_7d` | 7-Tage-Durchschnitt | AVG({column}) | 7 Tage | Durchschn. Ruhepuls, HRV |
| `avg_30d` | 30-Tage-Durchschnitt | AVG({column}) | 30 Tage | Durchschn. Kalorien, Protein |
| `sum_30d` | 30-Tage-Summe | SUM({column}) | 30 Tage | Gesamtkalorien, Trainingsminuten |
| `count_7d` | Anzahl Einträge (7d) | COUNT(*) | 7 Tage | Trainings letzte Woche |
| `count_30d` | Anzahl Einträge (30d) | COUNT(*) | 30 Tage | Trainings letzter Monat |
| `min_30d` | Minimum (30d) | MIN({column}) | 30 Tage | Niedrigster Ruhepuls |
| `max_30d` | Maximum (30d) | MAX({column}) | 30 Tage | Höchster VO2max |
| `avg_per_week_30d` | Durchschn. pro Woche | COUNT(*) / 4.3 | 30 Tage | Trainingsfrequenz/Woche |
### 3. Filter-Mechanismus
Alle Methoden unterstützen **optionale Filter** via `filter_conditions` (JSON):
```python
filter_conditions = {"training_category": "strength"}
# Wird zu SQL:
# ... WHERE profile_id = %s AND training_category = %s
```
**Unterstützte Filter-Typen:**
- **Equality:** `{"column": "value"}``WHERE column = 'value'`
- **IN-Clause:** `{"column": ["val1", "val2"]}``WHERE column IN ('val1', 'val2')`
---
## Neue Aggregationsmethode hinzufügen
### Schritt 1: Anforderungen definieren
**Checkliste:**
- [ ] **Name:** Eindeutig, beschreibend (z.B. `avg_per_week_30d`)
- [ ] **SQL-Aggregat:** Welche Funktion? (COUNT, AVG, SUM, MIN, MAX, oder Custom)
- [ ] **Zeitfenster:** Fixed (7d, 30d) oder dynamisch?
- [ ] **Spaltentyp:** Numerisch (DECIMAL, INT) oder UUID/TEXT (nur COUNT)?
- [ ] **Filter-Support:** Ja/Nein?
- [ ] **Return-Typ:** `float` oder `None`
### Schritt 2: Code-Template
**Location:** `backend/goal_utils.py``_fetch_by_aggregation_method()`
```python
elif method == 'neue_methode':
# 1. Zeitfenster definieren (falls relevant)
days_ago = date.today() - timedelta(days=30)
# 2. Parameter vorbereiten (inkl. filter_params)
params = [profile_id, days_ago] + filter_params
# 3. SQL Query (mit date_col und filter_sql)
cur.execute(f"""
SELECT AGG_FUNCTION({column}) as result_value
FROM {table}
WHERE profile_id = %s
AND {date_col} >= %s
AND {column} IS NOT NULL{filter_sql}
""", params)
# 4. Result extrahieren und konvertieren
row = cur.fetchone()
return float(row['result_value']) if row and row['result_value'] is not None else None
```
### Schritt 3: Spaltentyp-Validierung
**Wichtig:** Nur numerische Aggregationen (AVG, SUM, MIN, MAX) auf numerischen Spalten!
**Spaltentypen:**
- ✅ **AVG/SUM/MIN/MAX:** DECIMAL, INT, FLOAT
- ❌ **AVG/SUM/MIN/MAX:** UUID, TEXT, VARCHAR
- ✅ **COUNT:** Beliebiger Typ (UUID, TEXT, etc.)
**Bei Fehlkonfiguration:**
```python
# Wird automatisch geloggt + None returned (siehe except-Block Zeile 414-430)
[ERROR] Failed to fetch value from activity_log.id using avg_7d:
function avg(uuid) does not exist
```
### Schritt 4: Testen
**Manueller Test:**
```python
from goal_utils import _fetch_by_aggregation_method
from db import get_db
with get_db() as conn:
result = _fetch_by_aggregation_method(
conn,
profile_id='...',
table='activity_log',
column='id',
method='avg_per_week_30d',
filter_conditions={"training_category": "strength"}
)
print(f"Result: {result}")
```
**Unit-Test (TODO):**
```python
# backend/tests/test_goal_utils.py
def test_avg_per_week_30d():
# Setup: Insert 12 activities in last 30 days
# Expected: 12 / 4.3 ≈ 2.79
assert result == pytest.approx(2.79, abs=0.1)
```
---
## Beispiel-Implementierung: avg_per_week_30d
**Use Case:** Trainingshäufigkeit pro Woche (geglättet über 30 Tage)
**Berechnung:** `(Anzahl Trainings in 30 Tagen) / 4.3 Wochen`
**Code:**
```python
elif method == 'avg_per_week_30d':
days_ago = date.today() - timedelta(days=30)
params = [profile_id, days_ago] + filter_params
cur.execute(f"""
SELECT COUNT(*) as count_value FROM {table}
WHERE profile_id = %s AND {date_col} >= %s{filter_sql}
""", params)
row = cur.fetchone()
if row and row['count_value'] is not None:
# 30 Tage = 4.285 Wochen (30/7)
return round(float(row['count_value']) / 4.285, 2)
return None
```
**Warum 4.285?**
- 30 Tage ÷ 7 Tage/Woche = 4.285 Wochen
- Alternativ: 4.3 (gerundet) für einfachere Rechnung
---
## Best Practices
### 1. Naming Conventions
**Pattern:** `{aggregat}_{spalte}_{zeitfenster}`
- ✅ `avg_hr_7d` Average heart rate, 7 days
- ✅ `count_per_week_30d` Count per week, averaged over 30 days
- ✅ `sum_calories_30d` Sum of calories, 30 days
- ❌ `get_training_count` Unklar, kein Zeitfenster
- ❌ `calc_average` Zu generisch
### 2. Return-Werte
**Konsistenz:**
- **Erfolg:** `float` (auch bei 0.0)
- **Keine Daten:** `None` (nicht 0.0!)
- **Fehler:** `None` (geloggt im except-Block)
**Warum None statt 0.0?**
```python
# None = "Keine Daten vorhanden"
# 0.0 = "Gemessen, aber Wert ist tatsächlich 0"
```
### 3. Date-Columns
Nicht alle Tabellen nutzen `date` als Spaltenname:
```python
DATE_COLUMN_MAP = {
'blood_pressure_log': 'measured_at', # TIMESTAMP
'activity_log': 'date', # DATE
'fitness_tests': 'test_date', # DATE
# ... siehe goal_utils.py Zeile 289-300
}
```
**Nutzung:** `date_col = DATE_COLUMN_MAP.get(table, 'date')`
### 4. Filter-Safety
**SQL-Injection-Schutz:**
- ✅ **Parametrisierte Queries:** `WHERE col = %s` + `params`
- ❌ **String-Interpolation:** `WHERE col = '{value}'`
**Filter-Validierung:**
```python
try:
filters = json.loads(filter_conditions) if isinstance(filter_conditions, str) else filter_conditions
# ... build filter_sql
except (json.JSONDecodeError, TypeError, AttributeError) as e:
print(f"[WARNING] Invalid filter_conditions: {e}, ignoring filters")
```
### 5. Performance
**Query-Optimierung:**
- `WHERE profile_id = %s` ist **immer** erste Bedingung (Index)
- `AND {column} IS NOT NULL` vor Aggregation (reduziert NULL-Handling)
- `ORDER BY {date_col} DESC LIMIT 1` für `latest` (schneller als MAX)
---
## Erweiterte Methoden (Future)
### Statistische Analysen
**Median:**
```python
elif method == 'median_30d':
# PostgreSQL: PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY column)
cur.execute(f"""
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY {column}) as median_value
FROM {table}
WHERE profile_id = %s AND {date_col} >= %s{filter_sql}
""", params)
```
**Standard Deviation:**
```python
elif method == 'stddev_30d':
cur.execute(f"""
SELECT STDDEV({column}) as stddev_value FROM {table}
WHERE profile_id = %s AND {date_col} >= %s{filter_sql}
""", params)
```
**Trend (Linear Regression):**
```python
elif method == 'trend_30d':
# Slope via REGR_SLOPE(y, x)
cur.execute(f"""
SELECT REGR_SLOPE(
{column},
EXTRACT(EPOCH FROM {date_col})
) as slope FROM {table}
WHERE profile_id = %s AND {date_col} >= %s{filter_sql}
""", params)
```
### Kalenderwoche
```python
elif method == 'count_calendar_week':
# Montag der aktuellen Woche
today = date.today()
monday = today - timedelta(days=today.weekday())
cur.execute(f"""
SELECT COUNT(*) as count_value FROM {table}
WHERE profile_id = %s
AND {date_col} >= %s
AND {date_col} < %s + INTERVAL '7 days'{filter_sql}
""", [profile_id, monday] + filter_params)
```
---
## Fehlerbehandlung
### Exception-Handling
**Alle Methoden sind wrapped in try-except** (Zeile 329-430):
```python
try:
# ... method logic
except Exception as e:
print(f"[ERROR] Failed to fetch value from {table}.{column} using {method}: {e}")
print(f"[ERROR] Filter conditions: {filter_conditions}")
# CRITICAL: Rollback transaction
conn.rollback()
return None
```
**Warum Rollback?**
- PostgreSQL bleibt in `InFailedSqlTransaction` bis Rollback
- Ohne Rollback: Alle nachfolgenden Queries schlagen fehl
### Typische Fehler
| Fehler | Ursache | Lösung |
|--------|---------|--------|
| `function avg(uuid) does not exist` | AVG auf UUID-Spalte | Methode auf `count_*` ändern |
| `column "xyz" does not exist` | Falsche source_column | Schema prüfen, Spalte korrigieren |
| `division by zero` | Keine Daten für Durchschnitt | None-Check vor Division |
| `UndefinedColumn: training_category` | Filter-Spalte existiert nicht | Filter entfernen oder Spalte anlegen |
---
## Migration zu neuer Methode
**Szenario:** Bestehende Goal-Type-Definition ändern
**Beispiel:** `sport_pro_woche` von `avg_7d` zu `avg_per_week_30d`
**SQL:**
```sql
UPDATE goal_type_definitions
SET aggregation_method = 'avg_per_week_30d'
WHERE type_key = 'sport_pro_woche';
```
**Wichtig:**
- Bestehende Goals behalten ihre `current_value` (historisch)
- Nächste Berechnung nutzt neue Methode
- UI zeigt dann neuen Wert
---
## Dokumentations-Pflicht
**Bei jeder neuen Methode:**
1. ✅ Eintrag in dieser Datei (Tabelle "Verfügbare Methoden")
2. ✅ Docstring in `_fetch_by_aggregation_method()`
3. ✅ Beispiel-Anwendung (Use Case)
4. ✅ Unit-Test (wenn möglich)
5. ✅ Update in `goal_types.py` Schema-Info (falls relevant für Admin-UI)
---
## Zusammenfassung
**Aggregationsmethoden sind:**
- ✅ Zentral in `goal_utils.py`
- ✅ SQL-basiert (PostgreSQL-Funktionen)
- ✅ Filter-fähig (JSON-basiert)
- ✅ Error-safe (Rollback + None-Return)
- ✅ Erweiterbar (einfaches elif-Pattern)
**Für neue Methoden:**
1. Name definieren (`{aggregat}_{zeitfenster}`)
2. SQL Query schreiben (mit filter_sql)
3. Testen (manuell + Unit-Test)
4. Dokumentieren (diese Datei)
**Bei Fragen:**
- Siehe `backend/goal_utils.py` Zeile 259-430
- Siehe bestehende Methoden als Template
- Siehe `.claude/docs/working/GOALS_SYSTEM_UNIFIED_ANALYSIS.md` für Kontext

View File

@ -0,0 +1,575 @@
# API-Referenz
## Basis-URLs
| Umgebung | URL |
|----------|-----|
| **Production** | `https://mitai.jinkendo.de/api` |
| **Development** | `https://dev.mitai.jinkendo.de/api` |
| **Local** | `http://localhost:8000/api` (Backend direkt) |
---
## Authentifizierung
**Alle geschützten Endpoints** benötigen einen Auth-Token im Header:
```http
GET /api/weight
X-Auth-Token: jT9z3xK...pQ2vL
```
**Token-Beschaffung:** `POST /api/auth/login``{"token": "..."}`
**Storage:** `localStorage.bodytrack_token` (Frontend)
---
## Fehler-Codes
| Status | Bedeutung | Beispiel |
|--------|-----------|----------|
| **200** | Erfolg | `{"data": [...]}` |
| **201** | Erstellt | `{"id": "uuid", ...}` |
| **400** | Bad Request | `{"detail": "Ungültige Eingabe"}` |
| **401** | Unauthorized | `{"detail": "Nicht eingeloggt"}` |
| **403** | Forbidden | `{"detail": "Nur für Admins"}` oder `{"detail": "Feature-Limit erreicht"}` |
| **404** | Not Found | `{"detail": "Eintrag nicht gefunden"}` |
| **429** | Too Many Requests | `{"detail": "Rate limit exceeded: 5 per 1 minute"}` |
| **500** | Server Error | `{"detail": "Interner Fehler"}` |
**Standard-Fehler-Format:**
```json
{
"detail": "Fehlermeldung"
}
```
---
## Rate Limits
| Endpoint | Limit | Grund |
|----------|-------|-------|
| `POST /api/auth/login` | 5/minute | Brute-Force-Schutz |
| `POST /api/auth/register` | 3/hour | Spam-Prevention |
| `POST /api/auth/forgot-password` | 3/minute | E-Mail-Flooding-Schutz |
| `POST /api/auth/resend-verification` | 3/hour | E-Mail-Flooding-Schutz |
**Andere Endpoints:** Keine Rate-Limits (Feature-Limits via Membership-System)
---
## Endpoints nach Modul
### 1. Auth (`/api/auth/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `POST` | `/auth/login` | ❌ | `{email, password}` | `{token, profile_id, name, role, expires_at}` | Login mit E-Mail + Passwort |
| `POST` | `/auth/logout` | ✅ | | `{ok: true}` | Logout (löscht Session) |
| `GET` | `/auth/me` | ✅ | | `{id, name, email, role, tier, ...}` | Aktuelles Profil abrufen |
| `GET` | `/auth/status` | ❌ | | `{status: "ok", version: "v9b"}` | Health Check |
| `PUT` | `/auth/pin` | ✅ | `{pin}` | `{ok: true}` | PIN/Passwort ändern |
| `POST` | `/auth/forgot-password` | ❌ | `{email}` | `{ok: true, message}` | Passwort-Reset anfordern |
| `POST` | `/auth/reset-password` | ❌ | `{token, new_password}` | `{ok: true, message}` | Passwort-Reset bestätigen |
| `POST` | `/auth/register` | ❌ | `{name, email, password}` | `{ok: true, message}` | Selbst-Registrierung |
| `GET` | `/auth/verify/{token}` | ❌ | | `{ok: true, token, profile}` | E-Mail-Verifizierung |
| `POST` | `/auth/resend-verification` | ❌ | `{email}` | `{ok: true, message}` | Verifizierungs-E-Mail erneut senden |
**Rate Limits:** Siehe Tabelle oben
---
### 2. Profiles (`/api/profiles/*`, `/api/profile`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/profile` | ✅ | | `{id, name, email, role, tier, ...}` | Aktives Profil |
| `PUT` | `/profile` | ✅ | `{name?, sex?, dob?, height?, goal_weight?, goal_bf_pct?, avatar_color?}` | `{ok: true}` | Profil aktualisieren |
| `GET` | `/profiles` | ✅ | | `[{id, name, ...}, ...]` | Alle Profile (Multi-User, derzeit nicht genutzt) |
| `POST` | `/profiles` | ✅ | `{name, ...}` | `{id, ...}` | Profil erstellen (Multi-User) |
| `PUT` | `/profiles/{id}` | ✅ | `{name?, ...}` | `{ok: true}` | Profil bearbeiten |
| `DELETE` | `/profiles/{id}` | ✅ | | `{ok: true}` | Profil löschen |
---
### 3. Weight (`/api/weight/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/weight` | ✅ | `?limit=365` | `[{id, date, weight, note, source, created}, ...]` | Gewichtseinträge abrufen |
| `POST` | `/weight` | ✅ | `{date, weight, note?}` | `{id, date, weight, ...}` | Gewicht eintragen (Upsert) |
| `PUT` | `/weight/{id}` | ✅ | `{date, weight, note?}` | `{ok: true}` | Eintrag bearbeiten |
| `DELETE` | `/weight/{id}` | ✅ | | `{ok: true}` | Eintrag löschen |
| `GET` | `/weight/stats` | ✅ | | `{latest, avg_7d, avg_30d, delta_7d, delta_30d, trend}` | Gewichts-Statistiken |
**Feature-Limits:** `weight_entries` (v9c Phase 4: Enforcement aktiv)
---
### 4. Circumferences (`/api/circumferences/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/circumferences` | ✅ | `?limit=100` | `[{id, date, c_neck, c_chest, c_waist, c_belly, c_hip, c_thigh, c_calf, c_arm, notes, photo_id}, ...]` | Umfangsmessungen |
| `POST` | `/circumferences` | ✅ | `{date, c_neck?, c_chest?, ...}` | `{id, ...}` | Messung eintragen (Upsert) |
| `PUT` | `/circumferences/{id}` | ✅ | `{date, ...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/circumferences/{id}` | ✅ | | `{ok: true}` | Löschen |
**Feature-Limits:** `circumference_entries`
---
### 5. Caliper (`/api/caliper/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/caliper` | ✅ | `?limit=100` | `[{id, date, sf_method, sf_chest, sf_abdomen, ..., body_fat_pct, lean_mass, fat_mass, notes}, ...]` | Hautfaltenmessungen |
| `POST` | `/caliper` | ✅ | `{date, sf_method, sf_chest?, ...}` | `{id, body_fat_pct, ...}` | Messung eintragen (berechnet KF% automatisch) |
| `PUT` | `/caliper/{id}` | ✅ | `{date, ...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/caliper/{id}` | ✅ | | `{ok: true}` | Löschen |
**Methoden:** `jackson3`, `jackson7`, `durnin`, `parrillo`
**Feature-Limits:** `caliper_entries`
---
### 6. Activity (`/api/activity/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/activity` | ✅ | `?limit=200` | `[{id, date, start_time, end_time, activity_type, duration_min, kcal_active, hr_avg, hr_max, distance_km, rpe, source, notes}, ...]` | Aktivitäten |
| `POST` | `/activity` | ✅ | `{date, activity_type, duration_min, ...}` | `{id, ...}` | Aktivität erstellen |
| `PUT` | `/activity/{id}` | ✅ | `{date, ...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/activity/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/activity/stats` | ✅ | | `{total_activities, total_kcal, avg_duration, ...}` | Statistiken |
| `GET` | `/activity/uncategorized` | ✅ | | `[{activity_type, count}, ...]` | Unkategorisierte Aktivitäten |
| `POST` | `/activity/bulk-categorize` | ✅ | `[{activity_type, training_type_id}, ...]` | `{ok: true, updated_count}` | Bulk-Kategorisierung (lernendes System) |
| `POST` | `/activity/import-csv` | ✅ | `FormData(file)` | `{imported, skipped, failed, errors}` | Apple Health CSV-Import |
**Feature-Limits:** `activity_entries`
---
### 7. Nutrition (`/api/nutrition/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/nutrition` | ✅ | `?limit=365` | `[{id, date, kcal, protein_g, fat_g, carbs_g, source}, ...]` | Ernährungsdaten |
| `GET` | `/nutrition/by-date/{date}` | ✅ | | `{id, date, kcal, ...}` oder `null` | Eintrag für bestimmtes Datum |
| `POST` | `/nutrition` | ✅ | `?date=YYYY-MM-DD&kcal=2000&protein_g=150&fat_g=70&carbs_g=200` | `{id, ...}` | Eintrag erstellen (Upsert) |
| `PUT` | `/nutrition/{id}` | ✅ | `?kcal=2000&...` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/nutrition/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/nutrition/correlations` | ✅ | | `{weight_vs_kcal: [...], bf_vs_protein: [...]}` | Korrelationen |
| `GET` | `/nutrition/weekly` | ✅ | `?weeks=16` | `[{week, year, avg_kcal, avg_protein, ...}, ...]` | Wochendaten |
| `GET` | `/nutrition/import-history` | ✅ | | `[{date, count}, ...]` | Import-Historie |
| `POST` | `/nutrition/import-csv` | ✅ | `FormData(file)` | `{imported, skipped, failed, errors}` | CSV-Import (FDDB, MyFitnessPal) |
**Feature-Limits:** `nutrition_entries`, `data_import`
---
### 8. Photos (`/api/photos/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/photos` | ✅ | | `[{id, date, path, created}, ...]` | Alle Fotos |
| `GET` | `/photos/{id}` | ✅ | `?token=...` (optional) | Binary (JPEG) | Foto abrufen (Token für <img> tag) |
| `POST` | `/photos` | ✅ | `FormData(file, date?)` | `{id, path, date}` | Foto hochladen |
| `DELETE` | `/photos/{id}` | ✅ | | `{ok: true}` | Foto löschen |
**Feature-Limits:** `photos`
**Hinweis:** Token-Parameter für `GET /photos/{id}` erlaubt Zugriff via `<img>` tag (ohne Header-Support)
---
### 9. AI Insights (`/api/insights/*`, `/api/ai/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/insights` | ✅ | | `[{id, scope, content, created}, ...]` | Alle Insights |
| `GET` | `/insights/latest` | ✅ | | `{slug: content, ...}` | Neueste Insights pro Scope |
| `POST` | `/insights/trend` | ✅ | | `{content, usage}` | Trend-Analyse (Gewicht) |
| `POST` | `/insights/run/{slug}` | ✅ | | `{content, usage}` | Einzelnen Prompt ausführen |
| `POST` | `/insights/pipeline` | ✅ | | `{content, usage}` | 3-stufige Pipeline |
**Feature-Limits:** `ai_calls` (pro Aufruf), `ai_pipeline` (für Pipeline)
**Prompts:** Konfigurierbar via `/api/prompts`
---
### 10. AI Prompts (`/api/prompts/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/prompts` | ✅ | | `[{id, slug, name, description, template, active, sort_order}, ...]` | Alle Prompts |
| `PUT` | `/prompts/{id}` | 🔒 Admin | `{name?, description?, template?, active?, sort_order?}` | `{ok: true}` | Prompt bearbeiten |
**Standard-Prompts:**
- `weight-trend` Gewichts-Trend-Analyse
- `nutrition-analysis` Ernährungs-Auswertung
- `training-plan` Trainings-Empfehlungen
- `body-composition` Körperzusammensetzung
- `progress-summary` Fortschritts-Zusammenfassung
- `pipeline` Master-Schalter für Pipeline
---
### 11. Admin (`/api/admin/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/admin/profiles` | 🔒 Admin | | `[{id, name, email, role, tier, ...}, ...]` | Alle Profile |
| `POST` | `/admin/profiles` | 🔒 Admin | `{name, email, password, role?}` | `{id, ...}` | Profil erstellen |
| `DELETE` | `/admin/profiles/{id}` | 🔒 Admin | | `{ok: true}` | Profil löschen |
| `PUT` | `/admin/profiles/{id}/permissions` | 🔒 Admin | `{ai_enabled?, ai_limit_day?, export_enabled?}` | `{ok: true}` | Permissions setzen |
---
### 12. Stats (`/api/stats`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/stats` | ✅ | | `{weight: {...}, nutrition: {...}, activity: {...}, body_comp: {...}}` | Dashboard-Stats |
**Response-Struktur:**
```json
{
"weight": {
"latest": 75.5,
"avg_7d": 75.8,
"avg_30d": 76.2,
"delta_7d": -0.3,
"delta_30d": -0.7
},
"nutrition": {
"avg_kcal_7d": 2100,
"avg_protein_7d": 150,
"days_logged_7d": 6
},
"activity": {
"total_activities_7d": 5,
"total_kcal_7d": 2500,
"avg_duration_7d": 45
},
"body_comp": {
"latest_bf_pct": 12.5,
"latest_lean_mass": 65.8
}
}
```
---
### 13. Export (`/api/export/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/export/csv` | ✅ | | Binary (CSV) | Alle Daten als CSV |
| `GET` | `/export/json` | ✅ | | Binary (JSON) | Alle Daten als JSON |
| `GET` | `/export/zip` | ✅ | | Binary (ZIP) | Alle Daten + Fotos als ZIP |
**Feature-Limits:** `data_export`
**Dateiname:** `mitai-export-YYYY-MM-DD.[csv|json|zip]`
**ZIP-Struktur:**
```
mitai-export-2026-03-23.zip
├── data.json
├── photos/
│ ├── photo_uuid1.jpg
│ └── photo_uuid2.jpg
└── README.txt
```
---
### 14. Import (`/api/import/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `POST` | `/import/backup` | ✅ | `FormData(file)` | `{imported: {...}, skipped: {...}, failed: {...}}` | JSON-Backup importieren |
**Feature-Limits:** `data_import`
**Format:** JSON-Export von `/export/json`
**Hinweis:** Überschreibt keine existierenden Einträge (Upsert-Logik)
---
## Subscription System (v9c)
### 15. Subscription (`/api/subscription/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/subscription/me` | ✅ | | `{tier, tier_expires_at, trial_ends_at, invited_by, ...}` | Abo-Status |
| `GET` | `/subscription/usage` | ✅ | | `{feature_id: {used, limit, remaining, allowed}, ...}` | Feature-Usage |
| `GET` | `/subscription/limits` | ✅ | | `{feature_id: limit, ...}` | Feature-Limits für aktuelles Tier |
---
### 16. Coupons (`/api/coupons/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `POST` | `/coupons/redeem` | ✅ | `{code}` | `{ok: true, tier_id, valid_days, message}` | Coupon einlösen |
| `GET` | `/coupons` | 🔒 Admin | | `[{id, code, tier_id, valid_days, max_uses, ...}, ...]` | Alle Coupons |
| `POST` | `/coupons` | 🔒 Admin | `{code, tier_id, valid_days, max_uses?}` | `{id, ...}` | Coupon erstellen |
| `PUT` | `/coupons/{id}` | 🔒 Admin | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/coupons/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
| `GET` | `/coupons/{id}/redemptions` | 🔒 Admin | | `[{profile_id, redeemed_at}, ...]` | Einlösungen |
---
### 17. Features (`/api/features/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/features` | ✅ | | `[{id, name, description, limit_type, reset_period, default_limit, active}, ...]` | Alle Features |
| `GET` | `/features/usage` | ✅ | | `[{feature_id, feature_name, limit, used, remaining, allowed, reason}, ...]` | Feature-Usage für aktuellen User |
| `POST` | `/features` | 🔒 Admin | `{id, name, description, limit_type, reset_period, default_limit}` | `{id, ...}` | Feature erstellen |
| `PUT` | `/features/{id}` | 🔒 Admin | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/features/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
**Limit-Types:** `count` (zählbar), `boolean` (on/off)
**Reset-Periods:** `never`, `daily`, `monthly`
---
### 18. Tiers (`/api/tiers/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/tiers` | 🔒 Admin | | `[{id, name, description, price_monthly, active}, ...]` | Alle Tiers |
| `POST` | `/tiers` | 🔒 Admin | `{id, name, description, price_monthly}` | `{id, ...}` | Tier erstellen |
| `PUT` | `/tiers/{id}` | 🔒 Admin | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/tiers/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
**Standard-Tiers:** `free`, `basic`, `premium`, `selfhosted`
---
### 19. Tier Limits (`/api/tier-limits/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/tier-limits` | 🔒 Admin | | `{tiers: [...], features: [...], matrix: {...}}` | Tier-Limits-Matrix |
| `PUT` | `/tier-limits` | 🔒 Admin | `{tier_id, feature_id, limit_value}` | `{ok: true}` | Limit setzen |
| `PUT` | `/tier-limits/batch` | 🔒 Admin | `{updates: [{tier_id, feature_id, limit_value}, ...]}` | `{ok: true}` | Batch-Update |
**Matrix-Format:**
```json
{
"tiers": ["free", "basic", "premium", "selfhosted"],
"features": [
{"id": "weight_entries", "name": "Gewichtseinträge", ...},
...
],
"matrix": {
"weight_entries": {
"free": 100,
"basic": 1000,
"premium": null,
"selfhosted": null
},
...
}
}
```
---
### 20. User Restrictions (`/api/user-restrictions/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/user-restrictions` | 🔒 Admin | `?profile_id=...` | `[{id, profile_id, feature_id, limit_value}, ...]` | User-spezifische Limits |
| `POST` | `/user-restrictions` | 🔒 Admin | `{profile_id, feature_id, limit_value}` | `{id, ...}` | Limit setzen |
| `PUT` | `/user-restrictions/{id}` | 🔒 Admin | `{limit_value}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/user-restrictions/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
**Verwendung:** Individuelle Limits überschreiben Tier-Limits
---
### 21. Access Grants (`/api/access-grants/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/access-grants` | 🔒 Admin | `?profile_id=...&active_only=true` | `[{id, profile_id, tier_id, valid_from, valid_until, source, is_active}, ...]` | Zeitlich begrenzte Tier-Zugriffe |
| `POST` | `/access-grants` | 🔒 Admin | `{profile_id, tier_id, valid_from, valid_until, source?}` | `{id, ...}` | Grant erstellen |
| `PUT` | `/access-grants/{id}` | 🔒 Admin | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/access-grants/{id}` | 🔒 Admin | | `{ok: true}` | Revoke |
**Quellen:** `coupon`, `trial`, `manual`, `gift`
---
## Training Types (v9d)
### 22. Training Types (`/api/training-types/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/training-types` | ✅ | | `{category: [{id, name, category, color, icon}, ...], ...}` | Gruppiert nach Kategorie |
| `GET` | `/training-types/flat` | ✅ | | `[{id, name, category, ...}, ...]` | Flache Liste |
| `GET` | `/training-types/categories` | ✅ | | `[{id, name, icon, color}, ...]` | Kategorien-Metadaten |
**Kategorien:** Kraft, Cardio, Flexibilität, Spiel & Sport, Alltag & Bewegung, Outdoor & Natur, Geist & Meditation
---
### 23. Admin Training Types (`/api/admin/training-types/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/admin/training-types` | 🔒 Admin | | `[{id, name, category, color, icon, abilities}, ...]` | Alle Typen (inkl. abilities JSONB) |
| `GET` | `/admin/training-types/{id}` | 🔒 Admin | | `{id, name, ...}` | Einzelner Typ |
| `POST` | `/admin/training-types` | 🔒 Admin | `{name, category, color?, icon?}` | `{id, ...}` | Typ erstellen |
| `PUT` | `/admin/training-types/{id}` | 🔒 Admin | `{name?, category?, color?, icon?, abilities?}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/admin/training-types/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
| `GET` | `/admin/training-types/taxonomy/abilities` | 🔒 Admin | | `{categories: [...], abilities: [...]}` | Abilities-Taxonomie (v9f) |
---
### 24. Activity Mappings (`/api/admin/activity-mappings/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/admin/activity-mappings` | 🔒 Admin | `?profile_id=...&global_only=true` | `[{id, activity_type, training_type_id, profile_id, is_global}, ...]` | Lernendes Mapping-System |
| `GET` | `/admin/activity-mappings/{id}` | 🔒 Admin | | `{id, ...}` | Einzelnes Mapping |
| `POST` | `/admin/activity-mappings` | 🔒 Admin | `{activity_type, training_type_id, profile_id?, is_global?}` | `{id, ...}` | Mapping erstellen |
| `PUT` | `/admin/activity-mappings/{id}` | 🔒 Admin | `{training_type_id?, is_global?}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/admin/activity-mappings/{id}` | 🔒 Admin | | `{ok: true}` | Löschen |
| `GET` | `/admin/activity-mappings/stats/coverage` | 🔒 Admin | | `{total_activities, mapped, unmapped, coverage_pct}` | Mapping-Coverage |
**Auto-Learning:** Bulk-Kategorisierung in ActivityPage speichert neue Mappings automatisch
---
### 25. Training Profiles (`/api/evaluation/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/evaluation/parameters` | 🔒 Admin | | `{categories: [...], abilities: [...]}` | Training-Parameter-Taxonomie |
| `POST` | `/evaluation/batch` | 🔒 Admin | | `{evaluated, failed, errors}` | Batch-Evaluierung aller Aktivitäten (v9d #15) |
---
## Sleep & Vitals (v9d Phase 2)
### 26. Sleep (`/api/sleep/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/sleep` | ✅ | `?limit=90` | `[{id, date, bedtime, wakeup, duration_min, quality, sleep_segments, notes, source}, ...]` | Schlaf-Einträge |
| `GET` | `/sleep/by-date/{date}` | ✅ | | `{id, date, ...}` oder `null` | Eintrag für Datum |
| `POST` | `/sleep` | ✅ | `{date, bedtime, wakeup, duration_min, quality?, sleep_segments?, notes?}` | `{id, ...}` | Eintrag erstellen (Upsert) |
| `PUT` | `/sleep/{id}` | ✅ | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/sleep/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/sleep/stats` | ✅ | `?days=7` | `{avg_duration, avg_quality, total_deep, total_rem, ...}` | Stats |
| `GET` | `/sleep/debt` | ✅ | `?days=14` | `{sleep_debt_min, avg_duration, target_duration}` | Schlafschuld |
| `GET` | `/sleep/trend` | ✅ | `?days=30` | `[{date, duration_min, quality}, ...]` | Trend |
| `GET` | `/sleep/phases` | ✅ | `?days=30` | `[{date, deep_min, rem_min, light_min, awake_min}, ...]` | Schlafphasen |
| `POST` | `/sleep/import/apple-health` | ✅ | `FormData(file)` | `{imported, skipped, failed, errors}` | Apple Health CSV-Import |
**sleep_segments Format (JSONB):**
```json
[
{"phase": "deep", "start": "23:30", "end": "01:15"},
{"phase": "rem", "start": "01:15", "end": "02:45"},
{"phase": "light", "start": "02:45", "end": "06:00"},
{"phase": "awake", "start": "06:00", "end": "06:15"}
]
```
---
### 27. Rest Days (`/api/rest-days/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/rest-days` | ✅ | `?limit=90` | `[{id, date, rest_type, reason, notes}, ...]` | Ruhetage |
| `GET` | `/rest-days/{id}` | ✅ | | `{id, ...}` | Einzelner Eintrag |
| `POST` | `/rest-days` | ✅ | `{date, rest_type, reason?, notes?}` | `{id, ...}` | Ruhetag eintragen |
| `PUT` | `/rest-days/{id}` | ✅ | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/rest-days/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/rest-days/stats` | ✅ | `?weeks=4` | `{total_rest_days, kraft_days, cardio_days, entspannung_days}` | Stats |
| `POST` | `/rest-days/validate-activity` | ✅ | `{date, activity_type}` | `{conflicts: [{rest_type, reason}, ...]}` | Validierung gegen Ruhetage |
**rest_type:** `kraft`, `cardio`, `entspannung` (Multi-Dimensional Rest)
---
### 28. Vitals Baseline (`/api/vitals/baseline/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/vitals/baseline` | ✅ | `?limit=90` | `[{id, date, resting_hr, hrv, vo2_max, spo2, respiratory_rate, notes}, ...]` | Morgenmessungen |
| `GET` | `/vitals/baseline/by-date/{date}` | ✅ | | `{id, ...}` oder `null` | Eintrag für Datum |
| `POST` | `/vitals/baseline` | ✅ | `{date, resting_hr?, hrv?, vo2_max?, spo2?, respiratory_rate?, notes?}` | `{id, ...}` | Eintrag erstellen (Upsert) |
| `PUT` | `/vitals/baseline/{id}` | ✅ | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/vitals/baseline/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/vitals/baseline/stats` | ✅ | `?days=30` | `{avg_resting_hr, avg_hrv, trend_resting_hr, trend_hrv}` | Stats |
| `POST` | `/vitals/baseline/import/apple-health` | ✅ | `FormData(file)` | `{imported, skipped, failed, errors}` | Apple Health CSV-Import |
**Messung:** 1x täglich, morgens nüchtern
---
### 29. Blood Pressure (`/api/blood-pressure/*`)
| Methode | Pfad | Auth | Parameter | Response | Beschreibung |
|---------|------|------|-----------|----------|--------------|
| `GET` | `/blood-pressure` | ✅ | `?limit=90` | `[{id, date, time, systolic, diastolic, pulse, context, irregular_heartbeat, afib_warning, notes}, ...]` | Blutdruck-Messungen |
| `GET` | `/blood-pressure/by-date/{date}` | ✅ | | `[{id, time, ...}, ...]` | Alle Messungen für Datum |
| `POST` | `/blood-pressure` | ✅ | `{date, time, systolic, diastolic, pulse?, context?, irregular_heartbeat?, afib_warning?, notes?}` | `{id, ...}` | Messung eintragen |
| `PUT` | `/blood-pressure/{id}` | ✅ | `{...}` | `{ok: true}` | Bearbeiten |
| `DELETE` | `/blood-pressure/{id}` | ✅ | | `{ok: true}` | Löschen |
| `GET` | `/blood-pressure/stats` | ✅ | `?days=30` | `{avg_systolic, avg_diastolic, avg_pulse, classification, trend}` | Stats |
| `POST` | `/blood-pressure/import/omron` | ✅ | `FormData(file)` | `{imported, skipped, failed, errors}` | Omron CSV-Import (Deutsch) |
**Contexts:** `fasting`, `after_meal`, `exercise`, `stress`, `rest`, `before_sleep`, `after_sleep`, `medication`
**WHO/ISH-Klassifizierung:**
- Optimal: <120/<80
- Normal: 120-129/80-84
- Hoch-Normal: 130-139/85-89
- Hypertonie Grad 1: 140-159/90-99
- Hypertonie Grad 2: 160-179/100-109
- Hypertonie Grad 3: ≥180/≥110
---
## Zusammenfassung
**Gesamt:** 29 Router-Module, ~200 Endpoints
**Authentifizierung:** Token-basiert (X-Auth-Token Header)
**Fehlerformat:** `{"detail": "Fehlermeldung"}`
**Rate Limits:** Nur Auth-Endpoints (5/min Login, 3/hour Register)
**Feature-Limits:** Membership-basiert (v9c), enforcement via HTTP 403
**Import-Formate:** CSV (Apple Health, Omron, FDDB), JSON (Backup)
**Export-Formate:** CSV, JSON, ZIP (mit Fotos)
**Besonderheiten:**
- Upsert-Logik für viele Endpoints (Date-basiert)
- Inline-Editing-Support (GET by date, PUT by id)
- CSV-Import mit Duplikat-Erkennung
- Lernendes Mapping-System (Activity Types)
- JSONB für flexible Datenstrukturen (sleep_segments, abilities)
- Auto-Migration SHA256 → bcrypt
- E-Mail-Verifizierung + Passwort-Reset

View File

@ -0,0 +1,634 @@
# Architektur-Übersicht Mitai Jinkendo
## System-Überblick
**Mitai Jinkendo** ist eine selbst-gehostete Progressive Web App (PWA) für Körper-Tracking mit KI-gestützter Auswertung. Die Architektur folgt einem klassischen **3-Tier-Modell** mit klarer Trennung von Präsentation, Business-Logik und Datenhaltung.
```
Internet
Fritz!Box (privat.stommer.com)
Synology NAS
Raspberry Pi 5 (192.168.2.49)
┌────────────────────────────────────┐
│ Docker Compose Environment │
├────────────────────────────────────┤
│ Frontend (React + Vite) │ Port 3002 (Prod) / 3099 (Dev)
│ Backend (FastAPI + Python) │ Port 8002 (Prod) / 8099 (Dev)
│ Database (PostgreSQL 16 Alpine) │ Port 5432 (internal)
└────────────────────────────────────┘
```
---
## Tech-Stack
| Komponente | Technologie | Version | Zweck |
|-----------|-------------|---------|-------|
| **Frontend** | React | 18 | UI Framework |
| | Vite | Latest | Build Tool + Dev Server |
| | React Router | 6 | Client-side Routing |
| | Lucide React | Latest | Icon Library |
| **Backend** | FastAPI | Latest | REST API Framework |
| | Python | 3.12 | Programmiersprache |
| | psycopg2-binary | Latest | PostgreSQL Driver |
| | bcrypt | Latest | Passwort-Hashing |
| | slowapi | Latest | Rate Limiting |
| **Database** | PostgreSQL | 16 Alpine | Primäre Datenbank |
| **Container** | Docker | Latest | Container Runtime |
| | Docker Compose | v2 | Multi-Container Orchestration |
| **KI** | OpenRouter API | - | Claude Sonnet 4 |
| | Anthropic API | - | Direkt-Integration (optional) |
| **Email** | SMTP | - | E-Mail-Versand (Verifikation, Reset) |
| **Infrastruktur** | Raspberry Pi 5 | - | Host-System |
| | Gitea | 3000 | Git Server + CI/CD |
| | Synology NAS | - | Reverse Proxy |
---
## Deployment-Architektur
### Umgebungen
| Umgebung | Domain | Branch | Ports | Deployment |
|----------|--------|--------|-------|-----------|
| **Production** | mitai.jinkendo.de | `main` | 3002/8002 | Auto-Deploy via Gitea |
| **Development** | dev.mitai.jinkendo.de | `develop` | 3099/8099 | Auto-Deploy via Gitea |
### Git-Workflow
```
develop → Push → Gitea Webhook → Runner → docker-compose.dev-env.yml → dev.mitai.jinkendo.de
main → Push → Gitea Webhook → Runner → docker-compose.yml → mitai.jinkendo.de
```
**Runner-Details:**
- Läuft auf Raspberry Pi 5 (`/home/lars/gitea-runner/`)
- Watchtower für automatische Updates
- Docker Compose Build + Restart bei neuen Commits
### Verzeichnisstruktur auf dem Server
```
/home/lars/docker/
├── bodytrack/ # Production
│ ├── docker-compose.yml
│ └── .env
└── bodytrack-dev/ # Development
├── docker-compose.dev-env.yml
└── .env
```
**Externe Volumes:**
- `bodytrack_bodytrack-data``/app/data` (Backend JSON-Daten, Legacy)
- `bodytrack_bodytrack-photos``/app/photos` (Progress-Fotos)
- `mitai_postgres_data` → PostgreSQL Datenbank
---
## Komponenten-Übersicht
### Backend-Module (26 Router)
| Router | Endpunkte | Beschreibung |
|--------|-----------|--------------|
| **auth** | `/api/auth/*` | Login, Register, Verify, Password Reset |
| **profiles** | `/api/profiles/*`, `/api/profile` | Nutzerverwaltung |
| **weight** | `/api/weight/*` | Gewichts-Tracking |
| **circumference** | `/api/circumferences/*` | Umfangsmessungen (8 Punkte) |
| **caliper** | `/api/caliper/*` | Hautfaltenmessungen (4 Methoden) |
| **activity** | `/api/activity/*` | Training & Aktivitäten |
| **nutrition** | `/api/nutrition/*` | Ernährungsdaten (Kalorien + Makros) |
| **photos** | `/api/photos/*` | Progress-Fotos |
| **insights** | `/api/insights/*`, `/api/ai/*` | KI-Auswertungen |
| **prompts** | `/api/prompts/*` | Konfigurierbare KI-Prompts |
| **admin** | `/api/admin/*` | Admin-Panel (Profile, Permissions) |
| **stats** | `/api/stats` | Statistiken für Dashboard |
| **exportdata** | `/api/export/*` | CSV/JSON/ZIP Export |
| **importdata** | `/api/import/*` | CSV Import |
| **subscription** | `/api/subscription/*` | Abo-Status (v9c) |
| **coupons** | `/api/coupons/*` | Coupon-System (v9c) |
| **features** | `/api/features/*` | Feature-Verwaltung (v9c) |
| **tiers_mgmt** | `/api/tiers/*` | Tier-Verwaltung (v9c) |
| **tier_limits** | `/api/tier-limits/*` | Tier-Limits Matrix (v9c) |
| **user_restrictions** | `/api/user-restrictions/*` | User-spezifische Limits (v9c) |
| **access_grants** | `/api/access-grants/*` | Zeitlich begrenzte Zugriffe (v9c) |
| **training_types** | `/api/training-types/*` | Trainingstypen (v9d) |
| **admin_training_types** | `/api/admin/training-types/*` | Trainingstypen-Admin (v9d) |
| **admin_activity_mappings** | `/api/admin/activity-mappings/*` | Activity Mapping Admin (v9d) |
| **sleep** | `/api/sleep/*` | Schlaf-Modul (v9d Phase 2b) |
| **rest_days** | `/api/rest-days/*` | Ruhetage (v9d Phase 2a) |
| **vitals_baseline** | `/api/vitals/baseline/*` | Morgenmessungen (RHR, HRV, VO2 Max) |
| **blood_pressure** | `/api/blood-pressure/*` | Blutdruck (mehrfach täglich) |
| **evaluation** | `/api/evaluation/*` | Training Type Profiling (v9d Phase 2 #15) |
**Registrierung in `main.py`:**
```python
app.include_router(auth.router)
app.include_router(profiles.router)
# ... alle 26 Router
```
### Frontend-Struktur
```
frontend/src/
├── App.jsx # Root-Komponente + Routing
├── app.css # Globales Design-System
├── context/
│ ├── AuthContext.jsx # Session-Management
│ └── ProfileContext.jsx # Aktives Profil
├── pages/ # 30+ Seiten
│ ├── LoginScreen.jsx # Login + Auto-Migration SHA256→bcrypt
│ ├── Register.jsx # Selbst-Registrierung (v9c)
│ ├── Verify.jsx # E-Mail-Verifizierung
│ ├── Dashboard.jsx # Übersicht mit Stats + Charts
│ ├── CaptureHub.jsx # Quick-Entry-Auswahl
│ ├── WeightScreen.jsx # Gewichts-Erfassung
│ ├── CircumScreen.jsx # Umfänge
│ ├── CaliperScreen.jsx # Caliper
│ ├── ActivityPage.jsx # Training (mit Trainingstypen v9d)
│ ├── NutritionPage.jsx # Ernährung (3-Tab: Entry/Import/Charts)
│ ├── SleepPage.jsx # Schlaf (v9d Phase 2b)
│ ├── RestDaysPage.jsx # Ruhetage (v9d Phase 2a)
│ ├── VitalsPage.jsx # Vitalwerte (3-Tab: Baseline/BP/Import)
│ ├── History.jsx # Verlauf-Charts
│ ├── Analysis.jsx # KI-Analyse + Pipeline
│ ├── SettingsPage.jsx # Einstellungen
│ ├── SubscriptionPage.jsx # Membership-UI (v9c)
│ └── Admin*.jsx # 9 Admin-Seiten
└── utils/
├── api.js # ALLE API-Calls (285 Zeilen)
├── calc.js # Body-Fat-Berechnungen
├── interpret.js # Daten-Interpretation
├── Markdown.jsx # Markdown-Renderer
└── guideData.js # Guide-Inhalte
```
---
## Datenfluss
### 1. User Request → Auth → API → Database
```
User Action (Frontend)
api.js (Token injiziert via hdrs())
FastAPI Endpoint
require_auth() Dependency (auth.py)
↓ (validiert X-Auth-Token → session)
get_session() → SELECT FROM sessions JOIN profiles
↓ (returns session dict mit profile_id, role, ai_enabled, ...)
Router-Funktion
db.py → get_db() Context Manager
psycopg2 → PostgreSQL Query (RealDictCursor)
Response (JSON)
```
### 2. Feature Access Control (v9c)
```
Endpoint (z.B. POST /api/insights/run)
check_feature_access(profile_id, 'ai_calls')
Prüf-Hierarchie:
1. user_feature_restrictions (user-spezifisch)
2. tier_limits (Tier-basiert)
3. features.default_limit (Fallback)
get_effective_tier(profile_id)
→ access_grants (Coupon/Trial) ODER profiles.tier
user_feature_usage (aktueller Zähler)
Ergebnis: {allowed: bool, limit: int, used: int, remaining: int, reason: str}
Falls allowed == false → HTTP 403
Falls allowed == true → increment_feature_usage() + execute
```
### 3. KI-Pipeline (3-stufig)
```
User: "Analyse starten"
POST /api/insights/pipeline
check_feature_access('ai_pipeline')
Stufe 1: Gewicht-Trend → claude-sonnet-4 (OpenRouter)
Stufe 2: Ernährung → claude-sonnet-4
Stufe 3: Gesamtanalyse → claude-sonnet-4 (mit Ergebnissen aus 1+2)
Jeder Call: increment_feature_usage('ai_calls')
INSERT INTO ai_insights (scope='pipeline', content=...)
Return: {content: "...", usage: {...}}
```
---
## Sicherheitsarchitektur
### 1. Passwort-Sicherheit
**Hash-Verfahren:**
- **Aktuell:** bcrypt (Salting + Work Factor)
- **Legacy:** SHA256 (wird beim Login automatisch zu bcrypt migriert)
**Passwort-Hashing (`auth.py`):**
```python
def hash_pin(pin: str) -> str:
return bcrypt.hashpw(pin.encode(), bcrypt.gensalt()).decode()
def verify_pin(pin: str, stored_hash: str) -> bool:
if stored_hash.startswith('$2'): # bcrypt
return bcrypt.checkpw(pin.encode(), stored_hash.encode())
# Legacy SHA256 → auto-upgrade beim nächsten Login
return stored_hash == hashlib.sha256(pin.encode()).hexdigest()
```
### 2. Session-Management
**Token-Format:**
- `secrets.token_urlsafe(32)` → 43 Zeichen Base64-URL-safe
- Gespeichert in `sessions` Tabelle mit `expires_at`
- Standard-Lebensdauer: 30 Tage (konfigurierbar pro Profil)
**Auth-Flow:**
```python
# Backend
@router.post("/api/auth/login")
def login(email, password):
profile = SELECT ... WHERE email=...
verify_pin(password, profile.pin_hash) # ✓
token = make_token()
INSERT INTO sessions (token, profile_id, expires_at)
return {"token": token, "profile": {...}}
# Frontend (AuthContext.jsx)
localStorage.setItem('mitai-jinkendo_token', token)
```
**Auth-Middleware (`auth.py`):**
```python
def require_auth(x_auth_token: Optional[str] = Header(default=None)):
session = get_session(x_auth_token)
if not session:
raise HTTPException(401, "Nicht eingeloggt")
return session # dict mit profile_id, role, ai_enabled, ...
```
### 3. CORS-Konfiguration
**Produktion (`docker-compose.yml`):**
```yaml
ALLOWED_ORIGINS: https://mitai.jinkendo.de
```
**Development:**
```yaml
ALLOWED_ORIGINS: https://dev.mitai.jinkendo.de,http://localhost:3099
```
**FastAPI Setup (`main.py`):**
```python
app.add_middleware(
CORSMiddleware,
allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
allow_credentials=True,
allow_methods=["GET","POST","PUT","DELETE","OPTIONS"],
allow_headers=["*"],
)
```
### 4. Rate Limiting
**Library:** slowapi (Redis-freie In-Memory Rate Limiting)
**Anwendung:**
```python
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@router.post("/auth/login")
@limiter.limit("5/minute") # Max 5 Login-Versuche pro Minute
def login(request: Request, ...):
...
```
**Geschützte Endpoints:**
- `/api/auth/login` → 5/minute
- `/api/auth/register` → 3/minute
- KI-Endpoints → via Feature-Limits (nicht Rate Limiting)
### 5. SQL Injection Protection
**Parameter-Binding:**
```python
# ✅ Sicher (psycopg2 escaped automatisch):
cur.execute("SELECT * FROM profiles WHERE id = %s", (profile_id,))
# ❌ NIEMALS:
cur.execute(f"SELECT * FROM profiles WHERE id = '{profile_id}'")
```
**RealDictCursor:**
- Automatisches Escaping
- Keine String-Konkatenation
---
## Versions-Historie
### v9c (Komplett) Production seit 21.03.2026 ✅
**Features:**
- Membership-System (5 Tiers: free/basic/premium/selfhosted)
- Coupon-System + Trial (14 Tage)
- Feature-Enforcement (4 Phasen):
- Phase 1: Cleanup ✅
- Phase 2: Monitoring ✅
- Phase 3: Frontend Display ✅
- Phase 4: Enforcement (HTTP 403) ✅
- Selbst-Registrierung + E-Mail-Verifizierung
- Export: CSV/JSON/ZIP
- Ernährungs-Modul erweitert (3-Tab Layout)
### v9d Phase 1b ✅
**Trainingstypen-System:**
- 29 Trainingstypen in 7 Kategorien
- Lernendes Mapping-System (DB-basiert)
- Apple Health Import (Deutsch + English)
- Bulk-Kategorisierung (selbstlernend)
### v9d Phase 2 ✅ (Deployed 23.03.2026)
**Vitalwerte & Erholung:**
- **Schlaf-Modul (v9d Phase 2b):**
- Tabelle `sleep_log` mit JSONB sleep_segments
- Schlafphasen (Deep, REM, Light, Awake)
- Apple Health CSV Import
- **Ruhetage (v9d Phase 2a):**
- Multi-dimensionale Ruhetage (Kraft/Cardio/Entspannung)
- Quick Mode Presets + Custom Entry
- **Vitalwerte erweitert (v9d Phase 2d):**
- **3-Tab Architektur:** Baseline (morgens) / Blutdruck (mehrfach täglich) / Import
- **Baseline Vitals:** Ruhepuls, HRV, VO2 Max, SpO2, Atemfrequenz
- **Blutdruck:** Systolisch/Diastolisch + Puls, WHO/ISH-Klassifizierung
- **Context-Tagging:** 8 Kontexte (nüchtern, nach Essen, Training, Stress, etc.)
- **Inline-Editing:** Alle Messungen direkt in der Liste bearbeitbar
- CSV Import: Omron (Deutsch) + Apple Health (Deutsch/Englisch)
### v9b (PostgreSQL-Migration)
- Migration SQLite → PostgreSQL 16
- Connection Pooling (psycopg2)
- UUID statt Integer Primary Keys
- Trigger für auto-update timestamps
### v9a (Basis)
- Login + Auth-Middleware
- Gewicht, Umfänge, Caliper, Ernährung, Aktivität
- KI-Analyse (6 Prompts + 3-stufige Pipeline)
- PWA + Dashboard
- Admin-Panel
---
## Design-Entscheidungen
### Warum PostgreSQL statt SQLite?
**Vorteile:**
- Concurrent Writes (SQLite locked bei Writes)
- Native UUID Support
- JSONB für flexible Datenstrukturen (z.B. sleep_segments)
- Trigger + Stored Procedures
- Production-ready für Self-Hosting
**Migration:**
- Automatisches Migrations-System (`db_init.py`)
- Rollback-fähig via SQL-Dateien
- Tracking in `schema_migrations` Tabelle
### Warum FastAPI statt Flask/Django?
**Vorteile:**
- Async Support (für zukünftige WebSocket-Features)
- Automatic OpenAPI Docs
- Type Hints → automatische Validierung
- Dependency Injection (z.B. `require_auth`)
- Performance (ASGI statt WSGI)
### Warum React statt Vue/Svelte?
**Entscheidung:**
- Bekanntes Ecosystem
- Context API ausreichend (kein Redux nötig)
- Gute PWA-Integration
- React Router maturity
### Warum kein TypeScript?
**Entscheidung (bewusst):**
- Schnellere Prototyping-Geschwindigkeit
- Weniger Build-Komplexität
- Dokumentation via JSDoc-Kommentare
- Type Safety durch Backend (Pydantic)
### Warum Connection Pooling?
**Problem ohne Pooling:**
- Jeder API-Call → neue DB-Verbindung → Overhead
- PostgreSQL max_connections erreicht bei vielen gleichzeitigen Requests
**Lösung:**
```python
_pool = psycopg2.pool.SimpleConnectionPool(minconn=1, maxconn=10)
@contextmanager
def get_db():
conn = _pool.getconn()
try:
yield conn
conn.commit()
except:
conn.rollback()
raise
finally:
_pool.putconn(conn)
```
**Vorteil:**
- Max 10 gleichzeitige Verbindungen
- Automatisches Recycling
- Auto-Commit/Rollback
---
## Bekannte Limitationen & Workarounds
### 1. Docker Build auf Raspberry Pi
**Problem:** `apt-get install postgresql-client` hängt 30+ Minuten
**Lösung:** Reine Python-Lösung mit `psycopg2-binary` (keine System-Dependencies)
### 2. Apple Health CSV-Import
**Problem:** Dezimalwerte werden als String exportiert (`"65.0"` statt `65`)
**Lösung:**
```python
def safe_float(val):
try:
return float(val) if val else None
except:
return None
```
### 3. Bun Crash nach langen Claude Code Sessions
**Problem:** Bun (JS Runtime für Claude Code CLI) crashed bei >30 Min Sessions
**Workaround:** Bei komplexen Tasks früher committen + neue Session starten
### 4. dayjs.week() ohne Plugin
**Problem:** `dayjs().week()` existiert nicht ohne `isoWeek`-Plugin
**Lösung:** Native ISO-Wochenberechnung:
```javascript
const isoWeek = (d => Math.ceil(((new Date(d.setDate(d.getDate()+4-(d.getDay()||7)))-
new Date(d.getFullYear(),0,1))/86400000+1)/7))(new Date(date))
```
---
## Performance-Optimierungen
### 1. Frontend
- **Code Splitting:** React.lazy() für Admin-Seiten (nicht initial geladen)
- **Image Loading:** `loading="lazy"` für Photos
- **CSS:** Inline Critical CSS, kein CSS-in-JS Overhead
- **PWA Cache:** Service Worker cached statische Assets
### 2. Backend
- **Connection Pooling:** Max 10 Connections statt unlimited
- **Query Optimization:** Indizes auf häufig abgefragte Spalten
- **Lazy Loading:** KI-Analysen nur auf Abruf, nicht im Dashboard
### 3. Database
**Indizes:**
```sql
CREATE INDEX idx_weight_log_profile_date ON weight_log(profile_id, date DESC);
CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
CREATE INDEX idx_ai_insights_profile_scope ON ai_insights(profile_id, scope, created DESC);
```
**Unique Constraints:**
```sql
CREATE UNIQUE INDEX idx_weight_log_profile_date_unique ON weight_log(profile_id, date);
```
---
## Monitoring & Logging
### 1. Feature Usage Logging (v9c Phase 2)
**Format:** JSON Lines (JSONL)
**Speicherort:** `/app/logs/feature-usage.log`
**Beispiel:**
```json
{"timestamp":"2026-03-23T10:15:30Z","profile_id":"uuid-here","feature":"ai_calls","allowed":true,"limit":10,"used":3,"remaining":7}
{"timestamp":"2026-03-23T10:16:00Z","profile_id":"uuid-here","feature":"data_export","allowed":false,"limit":0,"used":0,"remaining":0,"reason":"limit_exceeded"}
```
**Auswertung:**
```bash
cat feature-usage.log | jq -s 'group_by(.feature) | map({feature: .[0].feature, total: length})'
```
### 2. Health Checks
**Database:**
```yaml
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mitai_prod"]
interval: 10s
```
**API:**
```python
@app.get("/")
def root():
return {"status": "ok", "service": "mitai-jinkendo", "version": "v9c-dev"}
```
### 3. Error Handling
**Einheitliches Format:**
```python
raise HTTPException(status_code=404, detail="Eintrag nicht gefunden")
# → {"detail": "Eintrag nicht gefunden"}
```
**Frontend (`api.js`):**
```javascript
if (!res.ok) {
const err = await res.text()
try {
const parsed = JSON.parse(err)
throw new Error(parsed.detail || err)
} catch {
throw new Error(err)
}
}
```
---
## Zusammenfassung
**Architektur-Highlights:**
- ✅ Modulare Router-Struktur (26 Module, single responsibility)
- ✅ Connection Pooling (max 10 concurrent DB connections)
- ✅ Token-basiertes Auth + bcrypt
- ✅ Feature Access Control mit Tier-System
- ✅ Docker Compose für einfaches Deployment
- ✅ Auto-Deploy via Gitea Webhooks
- ✅ PostgreSQL mit Migrations-System
- ✅ PWA mit Service Worker
- ✅ API-First Prinzip (Backend = Single Source of Truth)
**Nächste Schritte (v9e+):**
- Ziele-Modul (Goal Tracking)
- HF-Zonen + Erholungsstatus
- Stripe-Integration
- Connectoren (Garmin, Withings, etc.)
- Meditation + Selbstwahrnehmung

View File

@ -0,0 +1,904 @@
# Auth-Flow & Sicherheit
## Übersicht
Mitai Jinkendo nutzt **Token-basiertes Session-Management** mit bcrypt-Passwort-Hashing. Die Authentifizierung ist als FastAPI Dependency implementiert (`require_auth()`), die automatisch auf alle geschützten Endpoints angewendet wird.
**Sicherheits-Features:**
- ✅ bcrypt-Hashing (Work Factor ~12 Rounds)
- ✅ Auto-Migration SHA256 → bcrypt
- ✅ Rate Limiting (slowapi)
- ✅ CORS-Konfiguration
- ✅ E-Mail-Verifizierung für Registrierung
- ✅ Passwort-Reset via E-Mail (1h Token)
- ✅ Session-Expiry (30 Tage Standard)
- ✅ Admin-Role-Check
---
## Login-Flow (Schritt für Schritt)
### 1. User-Eingabe (Frontend)
**LoginScreen.jsx:**
```javascript
const handleLogin = async () => {
const data = await login({ email: email.trim().toLowerCase(), password })
window.location.href = '/' // Hard-Redirect nach Login
}
```
### 2. POST /api/auth/login (Backend)
**Request:**
```json
{
"email": "user@example.com",
"password": "geheim123"
}
```
**Backend-Logik (`backend/routers/auth.py`):**
```python
@router.post("/login")
@limiter.limit("5/minute") # Rate Limiting
async def login(req: LoginRequest, request: Request):
# 1. E-Mail-Lookup
cur.execute("SELECT * FROM profiles WHERE email=%s", (req.email.lower().strip(),))
prof = cur.fetchone()
if not prof:
raise HTTPException(401, "Ungültige Zugangsdaten")
# 2. Passwort-Verifizierung
if not verify_pin(req.password, prof['pin_hash']):
raise HTTPException(401, "Ungültige Zugangsdaten")
# 3. Auto-Migration SHA256 → bcrypt
if not prof['pin_hash'].startswith('$2'): # bcrypt-Hash startet mit $2b$ oder $2a$
new_hash = hash_pin(req.password)
cur.execute("UPDATE profiles SET pin_hash=%s WHERE id=%s", (new_hash, prof['id']))
# 4. Session erstellen
token = make_token() # secrets.token_urlsafe(32) → 43 Zeichen
session_days = prof.get('session_days', 30)
expires = datetime.now() + timedelta(days=session_days)
cur.execute("""
INSERT INTO sessions (token, profile_id, expires_at, created)
VALUES (%s, %s, %s, CURRENT_TIMESTAMP)
""", (token, prof['id'], expires))
return {
"token": token,
"profile_id": prof['id'],
"name": prof['name'],
"role": prof['role'],
"expires_at": expires.isoformat()
}
```
**Response:**
```json
{
"token": "jT9z3xK...(43 chars)...pQ2vL",
"profile_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Max Mustermann",
"role": "user",
"expires_at": "2026-04-22T14:30:00"
}
```
### 3. Token-Speicherung (Frontend)
**AuthContext.jsx:**
```javascript
const login = async (credentials) => {
const data = await api.login(credentials)
// Token speichern
localStorage.setItem('bodytrack_token', data.token)
localStorage.setItem('bodytrack_active_profile', data.profile_id)
// Volles Profil laden
const profile = await fetch('/api/auth/me', {
headers: { 'X-Auth-Token': data.token }
})
setSession({
token: data.token,
profile_id: data.profile_id,
role: data.role,
profile: await profile.json()
})
}
```
### 4. Nachfolgende Requests
**api.js:**
```javascript
function hdrs(extra={}) {
const h = {...extra}
const token = getToken() // localStorage.getItem('bodytrack_token')
if (token) h['X-Auth-Token'] = token
return h
}
async function req(path, opts={}) {
const res = await fetch(BASE+path, {...opts, headers:hdrs(opts.headers||{})})
// ...
}
```
**Jeder API-Call sendet automatisch:**
```
GET /api/weight
Headers:
X-Auth-Token: jT9z3xK...pQ2vL
```
### 5. Auth-Validierung (Backend)
**require_auth() Dependency:**
```python
def require_auth(x_auth_token: Optional[str] = Header(default=None)):
"""FastAPI Dependency - requires valid authentication."""
session = get_session(x_auth_token)
if not session:
raise HTTPException(401, "Nicht eingeloggt")
return session
def get_session(token: str):
"""Get session data for a given token."""
if not token:
return None
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("""
SELECT s.*, p.role, p.name, p.ai_enabled, p.ai_limit_day, p.export_enabled
FROM sessions s
JOIN profiles p ON s.profile_id=p.id
WHERE s.token=%s AND s.expires_at > CURRENT_TIMESTAMP
""", (token,))
return cur.fetchone()
```
**Endpoint-Beispiel:**
```python
@router.get("/api/weight")
def list_weight(session: dict = Depends(require_auth)):
profile_id = session['profile_id'] # Immer aus Session, nie aus Header!
# ... Query mit profile_id
```
**Session-Dict:**
```python
{
'profile_id': 'uuid-string',
'role': 'user' | 'admin',
'name': 'Max Mustermann',
'ai_enabled': True,
'ai_limit_day': 10,
'export_enabled': True,
'expires_at': datetime(2026, 4, 22, 14, 30, 0)
}
```
---
## Registrierung-Flow (v9c)
### 1. Selbst-Registrierung
**POST /api/auth/register:**
```json
{
"name": "Max Mustermann",
"email": "max@example.com",
"password": "sicheresPasswort123"
}
```
**Validierung:**
- E-Mail: Muss `@` enthalten
- Passwort: Mindestens 8 Zeichen
- Name: Mindestens 2 Zeichen
- E-Mail-Duplikat-Check
**Backend:**
```python
@router.post("/register")
@limiter.limit("3/hour") # Rate Limiting
async def register(req: RegisterRequest, request: Request):
email = req.email.lower().strip()
# Duplikat-Check
cur.execute("SELECT id FROM profiles WHERE email=%s", (email,))
if cur.fetchone():
raise HTTPException(400, "E-Mail-Adresse bereits registriert")
# Profil erstellen (inaktiv bis verifiziert)
profile_id = str(secrets.token_hex(16))
pin_hash = hash_pin(req.password)
verification_token = secrets.token_urlsafe(32)
verification_expires = datetime.now() + timedelta(hours=24)
trial_ends = datetime.now() + timedelta(days=14) # 14-Tage-Trial
cur.execute("""
INSERT INTO profiles (
id, name, email, pin_hash, auth_type, role, tier,
email_verified, verification_token, verification_expires,
trial_ends_at, created
) VALUES (%s, %s, %s, %s, 'email', 'user', 'free', FALSE, %s, %s, %s, CURRENT_TIMESTAMP)
""", (profile_id, req.name, email, pin_hash, verification_token, verification_expires, trial_ends))
# Verifizierungs-E-Mail senden
send_email(email, "Willkommen bei Mitai Jinkendo", f"Verify: {APP_URL}/verify?token={verification_token}")
```
**Response:**
```json
{
"ok": true,
"message": "Registrierung erfolgreich! Bitte prüfe dein E-Mail-Postfach."
}
```
### 2. E-Mail-Verifizierung
**User klickt Link in E-Mail:**
```
https://mitai.jinkendo.de/verify?token=jT9z3xK...pQ2vL
```
**GET /api/auth/verify/{token}:**
```python
@router.get("/verify/{token}")
async def verify_email(token: str):
# Token-Lookup
cur.execute("""
SELECT id, name, email, email_verified, verification_expires
FROM profiles
WHERE verification_token=%s
""", (token,))
prof = cur.fetchone()
if not prof:
raise HTTPException(400, "Verifikations-Link ungültig")
if prof['email_verified']:
raise HTTPException(400, "E-Mail bereits bestätigt")
if datetime.now() > prof['verification_expires']:
raise HTTPException(400, "Link abgelaufen")
# Verifizierung
cur.execute("""
UPDATE profiles
SET email_verified=TRUE, verification_token=NULL, verification_expires=NULL
WHERE id=%s
""", (prof['id'],))
# Auto-Login (Session erstellen)
session_token = make_token()
expires = datetime.now() + timedelta(days=30)
cur.execute("""
INSERT INTO sessions (token, profile_id, expires_at, created)
VALUES (%s, %s, %s, CURRENT_TIMESTAMP)
""", (session_token, prof['id'], expires))
return {
"ok": True,
"token": session_token,
"profile": {"id": prof['id'], "name": prof['name'], "email": prof['email']}
}
```
**Frontend (Verify.jsx):**
```javascript
const verifyToken = urlParams.get('token')
const data = await api.verifyEmail(verifyToken)
// Auto-Login nach Verifizierung
setAuthFromToken(data.token, data.profile)
window.location.href = '/'
```
### 3. Resend Verification
**POST /api/auth/resend-verification:**
```json
{
"email": "max@example.com"
}
```
**Backend:**
- Generiert neuen Token (24h Gültigkeit)
- Sendet erneut E-Mail
- Rate Limit: 3/hour
---
## Passwort-Reset-Flow
### 1. Passwort vergessen
**POST /api/auth/forgot-password:**
```json
{
"email": "max@example.com"
}
```
**Backend:**
```python
@router.post("/forgot-password")
@limiter.limit("3/minute")
async def password_reset_request(req: PasswordResetRequest, request: Request):
email = req.email.lower().strip()
cur.execute("SELECT id, name FROM profiles WHERE email=%s", (email,))
prof = cur.fetchone()
if not prof:
# Don't reveal if email exists (Anti-Enumeration)
return {"ok": True, "message": "Falls die E-Mail existiert, wurde ein Link gesendet."}
# Reset-Token erstellen
token = secrets.token_urlsafe(32)
expires = datetime.now() + timedelta(hours=1) # 1 Stunde gültig
# In sessions-Tabelle speichern (mit Präfix "reset_")
cur.execute("""
INSERT INTO sessions (token, profile_id, expires_at, created)
VALUES (%s, %s, %s, CURRENT_TIMESTAMP)
""", (f"reset_{token}", prof['id'], expires))
# E-Mail senden
send_email(prof['email'], "Passwort zurücksetzen", f"Reset: {APP_URL}/reset-password?token={token}")
```
### 2. Neues Passwort setzen
**User klickt Link in E-Mail:**
```
https://mitai.jinkendo.de/reset-password?token=jT9z3xK...pQ2vL
```
**POST /api/auth/reset-password:**
```json
{
"token": "jT9z3xK...pQ2vL",
"new_password": "neuesPasswort123"
}
```
**Backend:**
```python
@router.post("/reset-password")
def password_reset_confirm(req: PasswordResetConfirm):
cur.execute("""
SELECT profile_id FROM sessions
WHERE token=%s AND expires_at > CURRENT_TIMESTAMP
""", (f"reset_{req.token}",))
sess = cur.fetchone()
if not sess:
raise HTTPException(400, "Ungültiger oder abgelaufener Reset-Link")
# Passwort ändern
new_hash = hash_pin(req.new_password)
cur.execute("UPDATE profiles SET pin_hash=%s WHERE id=%s", (new_hash, sess['profile_id']))
# Reset-Token löschen
cur.execute("DELETE FROM sessions WHERE token=%s", (f"reset_{req.token}",))
return {"ok": True, "message": "Passwort erfolgreich zurückgesetzt"}
```
---
## Session-Management
### Session-Struktur (Datenbank)
**Tabelle: `sessions`**
```sql
CREATE TABLE sessions (
token VARCHAR(64) PRIMARY KEY, -- secrets.token_urlsafe(32)
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_sessions_profile_id ON sessions(profile_id);
CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
```
### Token-Format
**`secrets.token_urlsafe(32)`**
- 32 Bytes Zufallsdaten
- Base64-URL-safe-kodiert
- Resultierende Länge: 43 Zeichen
- Charset: `A-Za-z0-9_-`
**Beispiel:** `jT9z3xKpLmN8vQrStUwXyZ1aB2cD3eF4gH5iJ6kL7mN8oP9qR`
### Session-Lebensdauer
**Standard:** 30 Tage (konfigurierbar pro Profil via `profiles.session_days`)
**Nach Login:**
```python
session_days = prof.get('session_days', 30)
expires = datetime.now() + timedelta(days=session_days)
```
**Automatische Bereinigung:**
- Abgelaufene Sessions werden bei Login automatisch gelöscht (via `WHERE expires_at > CURRENT_TIMESTAMP`)
- Geplant: Cron-Job für Cleanup alter Sessions (v10+)
### Logout
**POST /api/auth/logout:**
```python
@router.post("/logout")
def logout(x_auth_token: Optional[str]=Header(default=None)):
if x_auth_token:
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("DELETE FROM sessions WHERE token=%s", (x_auth_token,))
return {"ok": True}
```
**Frontend:**
```javascript
const logout = async () => {
await fetch('/api/auth/logout', {
method: 'POST',
headers: { 'X-Auth-Token': token }
})
localStorage.removeItem('bodytrack_token')
setSession(null)
}
```
---
## Passwort-Sicherheit
### 1. bcrypt-Hashing
**Verwendete Bibliothek:** `bcrypt` (Python)
**Konfiguration:**
- Work Factor: Default (~12 Rounds)
- Automatisches Salting (integriert in bcrypt)
**Hash-Format:**
```
$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz3/yDZL1AJ.zCBU8fKWChSrW8GZF1a
│ │ │ │ │
│ │ │ └─ Salt (22 Zeichen) └─ Hash (31 Zeichen)
│ │ └─ Cost-Faktor (12 = 2^12 = 4096 Iterationen)
│ └─ bcrypt-Variante ('a' oder 'b')
└─ Präfix
```
**Hashing-Funktion (`auth.py`):**
```python
import bcrypt
def hash_pin(pin: str) -> str:
"""Hash password with bcrypt."""
return bcrypt.hashpw(pin.encode(), bcrypt.gensalt()).decode()
```
**Verifizierung:**
```python
def verify_pin(pin: str, stored_hash: str) -> bool:
"""Verify password - supports both bcrypt and legacy SHA256."""
if not stored_hash:
return False
# Detect bcrypt hash (starts with $2b$ or $2a$)
if stored_hash.startswith('$2'):
try:
return bcrypt.checkpw(pin.encode(), stored_hash.encode())
except Exception:
return False
# Legacy SHA256 support (auto-upgrade to bcrypt on next login)
return stored_hash == hashlib.sha256(pin.encode()).hexdigest()
```
### 2. SHA256 → bcrypt Auto-Migration
**Problem:** Alte Accounts hatten SHA256-Hashes (unsicher, kein Salting)
**Lösung:** Automatische Migration beim Login
**Logik:**
```python
# Beim Login
if prof['pin_hash'] and not prof['pin_hash'].startswith('$2'):
# Passwort verifiziert erfolgreich (via SHA256)
# → Upgrade zu bcrypt
new_hash = hash_pin(req.password)
cur.execute("UPDATE profiles SET pin_hash=%s WHERE id=%s", (new_hash, prof['id']))
```
**Ablauf:**
1. User loggt sich mit altem Passwort ein
2. Backend verifiziert gegen SHA256-Hash (erfolgreich)
3. Backend erstellt bcrypt-Hash vom Klartext-Passwort
4. SHA256-Hash wird in DB durch bcrypt-Hash ersetzt
5. Nächster Login → bcrypt-Verifizierung
**Vorteil:** Keine Passwort-Resets nötig, Migration transparent
### 3. Passwort-Anforderungen
**Minimum:**
- Länge: 8 Zeichen (bei Registrierung)
- Länge: 4 Zeichen (bei PIN-Change, für Abwärtskompatibilität)
**Empfohlen (nicht erzwungen):**
- Groß- und Kleinbuchstaben
- Zahlen
- Sonderzeichen
**Keine Komplexitäts-Prüfung:** Bewusst nicht implementiert (Fokus auf Länge statt Komplexität)
---
## Rate Limiting
**Bibliothek:** slowapi (Redis-freie In-Memory Rate Limiting)
### Konfiguration
**main.py:**
```python
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
```
### Rate Limits
| Endpoint | Limit | Grund |
|----------|-------|-------|
| `/api/auth/login` | 5/minute | Brute-Force-Schutz |
| `/api/auth/register` | 3/hour | Spam-Prevention |
| `/api/auth/forgot-password` | 3/minute | E-Mail-Flooding-Schutz |
| `/api/auth/resend-verification` | 3/hour | E-Mail-Flooding-Schutz |
**Verwendung:**
```python
from slowapi import Limiter
@router.post("/login")
@limiter.limit("5/minute")
async def login(req: LoginRequest, request: Request):
# Request-Objekt muss übergeben werden für IP-Extraktion
pass
```
**Response bei Überschreitung:**
```
HTTP 429 Too Many Requests
{
"detail": "Rate limit exceeded: 5 per 1 minute"
}
```
**Key-Funktion:** `get_remote_address` → IP-basiert (nicht User-basiert)
**Hinweis:** In-Memory = Reset bei Server-Neustart
---
## CORS-Konfiguration
### Produktion
**docker-compose.yml:**
```yaml
ALLOWED_ORIGINS: https://mitai.jinkendo.de
```
**main.py:**
```python
app.add_middleware(
CORSMiddleware,
allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
allow_credentials=True,
allow_methods=["GET","POST","PUT","DELETE","OPTIONS"],
allow_headers=["*"],
)
```
### Development
**docker-compose.dev-env.yml:**
```yaml
ALLOWED_ORIGINS: https://dev.mitai.jinkendo.de,http://localhost:3099
```
**Wichtig:** Keine Wildcards (`*`) in Produktion!
---
## Öffentliche vs. geschützte Endpoints
### Öffentliche Endpoints (kein Auth)
| Endpoint | Methode | Zweck |
|----------|---------|-------|
| `/` | GET | Health Check |
| `/api/auth/status` | GET | Status-Check (First-Run-Detection) |
| `/api/auth/login` | POST | Login |
| `/api/auth/register` | POST | Registrierung |
| `/api/auth/verify/{token}` | GET | E-Mail-Verifizierung |
| `/api/auth/forgot-password` | POST | Passwort-Reset anfordern |
| `/api/auth/reset-password` | POST | Passwort-Reset bestätigen |
| `/api/auth/resend-verification` | POST | Verifizierungs-E-Mail erneut senden |
### Geschützte Endpoints (require_auth)
**Alle anderen Endpoints** benötigen `X-Auth-Token` Header.
**Beispiel:**
```python
@router.get("/api/weight")
def list_weight(session: dict = Depends(require_auth)):
profile_id = session['profile_id']
# ...
```
### Admin-Only Endpoints (require_admin)
| Endpoint | Zweck |
|----------|-------|
| `/api/admin/*` | Admin-Panel |
| `/api/features` | Feature-Verwaltung (POST/PUT/DELETE) |
| `/api/tiers` | Tier-Verwaltung (POST/PUT/DELETE) |
| `/api/tier-limits` | Tier-Limits-Matrix (PUT) |
| `/api/coupons` | Coupon-Verwaltung (POST/PUT/DELETE) |
| `/api/user-restrictions` | User-Restrictions (POST/PUT/DELETE) |
| `/api/access-grants` | Access-Grants (POST/PUT/DELETE) |
| `/api/admin/training-types` | Trainingstypen-Admin (POST/PUT/DELETE) |
| `/api/admin/activity-mappings` | Activity-Mappings (POST/PUT/DELETE) |
**require_admin() Dependency:**
```python
def require_admin(x_auth_token: Optional[str] = Header(default=None)):
"""FastAPI dependency - requires admin authentication."""
session = get_session(x_auth_token)
if not session:
raise HTTPException(401, "Nicht eingeloggt")
if session['role'] != 'admin':
raise HTTPException(403, "Nur für Admins")
return session
```
---
## E-Mail-System
### SMTP-Konfiguration
**Umgebungsvariablen:**
```env
SMTP_HOST=smtp.strato.de
SMTP_PORT=587
SMTP_USER=noreply@jinkendo.de
SMTP_PASS=*****
SMTP_FROM=noreply@jinkendo.de
APP_URL=https://mitai.jinkendo.de
```
### E-Mail-Versand
**Helper-Funktion (`backend/routers/auth.py`):**
```python
def send_email(to_email: str, subject: str, body: str):
"""Send email via SMTP (reusable helper)."""
try:
smtp_host = os.getenv("SMTP_HOST")
smtp_port = int(os.getenv("SMTP_PORT", 587))
smtp_user = os.getenv("SMTP_USER")
smtp_pass = os.getenv("SMTP_PASS")
smtp_from = os.getenv("SMTP_FROM", "noreply@jinkendo.de")
if not smtp_host or not smtp_user or not smtp_pass:
print("SMTP not configured, skipping email")
return False
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = smtp_from
msg['To'] = to_email
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)
return True
except Exception as e:
print(f"Email error: {e}")
return False
```
### E-Mail-Templates
**Registrierung (Verifizierung):**
```
Betreff: Willkommen bei Mitai Jinkendo E-Mail bestätigen
Hallo {name},
willkommen bei Mitai Jinkendo!
Bitte bestätige deine E-Mail-Adresse um die Registrierung abzuschließen:
https://mitai.jinkendo.de/verify?token={verification_token}
Der Link ist 24 Stunden gültig.
Dein Mitai Jinkendo Team
```
**Passwort-Reset:**
```
Betreff: Passwort zurücksetzen Mitai Jinkendo
Hallo {name},
du hast einen Passwort-Reset angefordert.
Reset-Link: https://mitai.jinkendo.de/reset-password?token={token}
Der Link ist 1 Stunde gültig.
Falls du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail.
Dein Mitai Jinkendo Team
```
---
## Bekannte Sicherheitsentscheidungen
### 1. Profile-ID aus Session, nie aus Header
**❌ Falsch (Sicherheitslücke):**
```python
@router.get("/weight")
def list_weight(x_profile_id: str = Header(default=None), session=Depends(require_auth)):
profile_id = x_profile_id # User könnte beliebige ID senden!
```
**✅ Richtig:**
```python
@router.get("/weight")
def list_weight(session: dict = Depends(require_auth)):
profile_id = session['profile_id'] # Immer aus validierter Session
```
**Grund:** Session ist an Token gebunden → User kann nur eigene Daten abrufen
### 2. E-Mail-Enumeration-Schutz
**Problem:** Registrierung könnte verraten ob E-Mail bereits existiert
**Lösung:**
```python
if cur.fetchone():
raise HTTPException(400, "E-Mail-Adresse bereits registriert")
```
**Passwort-Reset:**
```python
if not prof:
# Don't reveal if email exists
return {"ok": True, "message": "Falls die E-Mail existiert, wurde ein Link gesendet."}
```
**Trade-off:** Registrierung gibt Info preis (UX > Sicherheit), Reset nicht (Sicherheit > UX)
### 3. Reset-Token-Präfix
**Problem:** Reset-Token könnte mit regulären Session-Token kollidieren
**Lösung:**
```python
cur.execute("INSERT INTO sessions (token, ...) VALUES (%s, ...)", (f"reset_{token}", ...))
```
**Vorteil:** Eindeutige Identifikation, kein Risiko von Kollisionen
### 4. Keine Passwort-Komplexitäts-Prüfung
**Entscheidung:** Nur Mindestlänge (8 Zeichen), keine Sonderzeichen-Pflicht
**Grund:**
- Länge > Komplexität (NIST-Empfehlung)
- Komplexitäts-Anforderungen führen zu schlechteren Passwörtern (z.B. `Password123!`)
- Benutzerfreundlichkeit
**Alternative:** Passwort-Strength-Meter im Frontend (geplant)
### 5. In-Memory Rate Limiting
**Problem:** Rate Limits resetten bei Server-Neustart
**Akzeptiert weil:**
- Redis-Overhead für Self-Hosting zu hoch
- Server-Neustarts selten (<1x pro Woche)
- Bei Neustart = Attack-Vektoren resetten sich auch (DDoS-Protection)
**Geplant (v10+):** Redis-Integration optional
---
## Zusammenfassung: Auth-Flow
```
┌─────────────────────────────────────────────────────────────┐
│ 1. Login (POST /api/auth/login) │
│ ↓ E-Mail + Passwort │
│ ↓ verify_pin(password, pin_hash) │
│ ↓ SHA256 → bcrypt Migration (falls nötig) │
│ ↓ Token generieren (secrets.token_urlsafe(32)) │
│ ↓ INSERT INTO sessions (token, profile_id, expires_at) │
│ → Return {token, profile_id, role, expires_at} │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. Frontend speichert Token │
│ localStorage.setItem('bodytrack_token', token) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. Nachfolgende Requests │
│ GET /api/weight │
│ Headers: X-Auth-Token: {token} │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. Backend validiert Token (require_auth) │
│ ↓ SELECT FROM sessions WHERE token=... AND expires_at>NOW│
│ ↓ JOIN profiles ON profile_id │
│ → Return session dict {profile_id, role, ai_enabled, ...}│
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. Endpoint-Logik │
│ profile_id = session['profile_id'] │
│ data = SELECT FROM weight_log WHERE profile_id=... │
│ → Return data │
└─────────────────────────────────────────────────────────────┘
```
**Sicherheits-Highlights:**
- ✅ bcrypt mit Auto-Salting
- ✅ SHA256 → bcrypt Migration (transparent)
- ✅ Rate Limiting (5/min Login, 3/hour Register)
- ✅ E-Mail-Verifizierung (24h Token)
- ✅ Passwort-Reset (1h Token via E-Mail)
- ✅ Session-Expiry (30 Tage)
- ✅ Admin-Role-Check (require_admin)
- ✅ Profile-ID-Isolation (immer aus Session)
- ✅ CORS-Whitelisting (Production)
**Bekannte Limitationen:**
- In-Memory Rate Limiting (Reset bei Server-Neustart)
- Keine 2FA (geplant für v10+)
- Keine Passwort-Strength-Meter (geplant)
- Keine Session-Revocation-UI (nur via DB)

View File

@ -0,0 +1,205 @@
# Zentrales Abo-System (Zukunft)
## Vision
**Ein zentrales Abo-System für alle Jinkendo Apps:**
- mitai.jinkendo.de (Körper-Tracking) 身体
- miken.jinkendo.de (Meditation) 眉間
- ikigai.jinkendo.de (Lebenssinn) 生き甲斐
- shinkan.jinkendo.de (Kampfsport) 真観
## Konzept
### Zentrale Webseite: jinkendo.de
- Zentrale Landing-Page mit allen Apps
- **Zentrale Abo-Verwaltung** (Stripe-Integration)
- User-Account übergreifend für alle Apps
- Single Sign-On (SSO) zwischen Apps
### Abo-Modelle (Ideen)
#### Option 1: App-spezifische Abos
```
mitai Basic: €5/Monat → Nur Mitai Premium
miken Basic: €5/Monat → Nur Miken Premium
```
#### Option 2: Kombinierte Abos
```
Jinkendo Basic: €8/Monat → 2 Apps
Jinkendo Premium: €12/Monat → Alle 4 Apps
Jinkendo Family: €20/Monat → Alle Apps + 3 Profile
```
#### Option 3: Feature-basiert
```
Free: Basis-Features alle Apps
Basic: Erweiterte Features (KI, Export, etc.)
Premium: Unlimited + Priority Support
```
---
## Technische Umsetzung
### Backend
#### Zentrale Auth-API
```
auth.jinkendo.de
POST /register → User-Account erstellen
POST /login → JWT Token für alle Apps
POST /refresh → Token erneuern
GET /me → User-Info mit Abo-Status
```
#### Subscription-API
```
subscriptions.jinkendo.de
GET /plans → Verfügbare Abos
POST /subscribe → Stripe Checkout Session
GET /my-subscription → Aktuelles Abo + Features
POST /cancel → Abo kündigen
POST /webhook → Stripe Webhook
```
#### App-Integration
Jede App prüft beim Start:
```javascript
const subscription = await fetch('https://subscriptions.jinkendo.de/my-subscription', {
headers: { 'Authorization': `Bearer ${jwt_token}` }
})
// subscription.features: ['mitai_premium', 'miken_basic', ...]
// App aktiviert entsprechende Features
```
### Frontend
#### Zentrale Webseite (jinkendo.de)
- Next.js oder React + Vite
- Stripe Elements für Payment
- Dashboard: Übersicht alle Apps + Abo-Status
- Rechnung-Historie
#### App-Anpassungen
**TrialBanner:**
```jsx
<a href="https://jinkendo.de/upgrade?app=mitai">
Jetzt upgraden
</a>
```
**Settings → Abo:**
- Link zu `https://jinkendo.de/account/subscription`
- Oder Embedded iFrame
---
## Datenbank-Schema (zentral)
### users
```sql
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
### subscriptions
```sql
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id),
stripe_customer_id VARCHAR(100),
stripe_subscription_id VARCHAR(100),
plan VARCHAR(50), -- 'basic', 'premium', 'family'
status VARCHAR(20), -- 'active', 'canceled', 'past_due'
current_period_end TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
canceled_at TIMESTAMP
);
```
### subscription_features
```sql
CREATE TABLE subscription_features (
subscription_id INT REFERENCES subscriptions(id),
app VARCHAR(50), -- 'mitai', 'miken', 'ikigai', 'shinkan'
tier VARCHAR(50), -- 'basic', 'premium'
PRIMARY KEY (subscription_id, app)
);
```
### app_access_tokens
```sql
-- Mapping: Zentrale User → App-spezifische Profile
CREATE TABLE app_access_tokens (
user_id UUID REFERENCES users(id),
app VARCHAR(50),
app_profile_id VARCHAR(100), -- ID in der jeweiligen App-DB
PRIMARY KEY (user_id, app)
);
```
---
## Migration: Bestehende Apps
### Schritt 1: Zentrale Auth aufbauen
1. `auth.jinkendo.de` API deployen
2. User aus `mitai` DB migrieren zu zentraler DB
3. Mapping erstellen: zentrale User ID → mitai Profile ID
### Schritt 2: Apps auf zentrale Auth umstellen
1. Login/Register in Apps deaktivieren
2. "Mit Jinkendo anmelden" Button → SSO-Flow
3. JWT von `auth.jinkendo.de` verwenden
4. Profile-ID Mapping bei jedem Request
### Schritt 3: Subscription-System
1. `subscriptions.jinkendo.de` API deployen
2. Stripe-Integration
3. Apps prüfen Abo-Status bei jedem Feature-Zugriff
### Schritt 4: Zentrale Webseite
1. `jinkendo.de` Landing Page
2. Account-Dashboard
3. Abo-Verwaltung
---
## Status (März 2026)
🔲 **Noch nicht gestartet**
**Aktuell:**
- Jede App hat eigene User-Verwaltung
- `mitai` hat Membership-System (v9c)
- TrialBanner Link → `mailto:mitai@jinkendo.de`
**Nächste Schritte:**
1. Weitere Apps entwickeln (miken, ikigai, shinkan)
2. Zentrale Infrastruktur planen
3. Migration vorbereiten
---
## Offene Fragen
- **Pricing:** Welche Preise pro App / kombiniert?
- **Stripe vs. Paddle:** Welcher Payment Provider?
- **Single DB vs. Separate:** Eine PostgreSQL-DB für alles oder separate?
- **Hosting:** Eigener Server oder Cloud (Vercel, Railway, Fly.io)?
- **Domain-Strategie:** Subdomains (api.jinkendo.de) oder Paths (jinkendo.de/api)?
---
## Related
- `MEMBERSHIP_SYSTEM.md` - Aktuelles System in mitai (v9c)
- `FEATURE_ENFORCEMENT.md` - Feature-Limiting Mechanismus
- Backlog: v9h (Connectoren & Stripe)

View File

@ -0,0 +1,163 @@
# Dashboard-Lab-Widgets Anleitung für Coding-Agenten
Ziel: Ein neues Dashboard-Widget **end-to-end** korrekt einbinden (Backend-Katalog, Validierung, API-Layout, Frontend-Registrierung, optional Lab-Editor für `config`).
Kontext: **Dashboard-Lab** unter geschützten Endpoints `GET/PUT /api/app/...` (siehe `backend/routers/app_dashboard.py`). Layout liegt pro Profil in `profiles.dashboard_layout` (JSON).
---
## 0. Architekturanforderung: Subscription / Feature-System vs. Widget-Katalog
### 0.1 Ist-Stand (Verifikation)
- **Bereits vorhanden:** Membership- und Feature-Modell (`features`, `tier_limits`, `user_feature_restrictions`, `check_feature_access` in `backend/auth.py`). Siehe `.claude/docs/architecture/FEATURE_ENFORCEMENT.md`.
- **Umgesetzt:** `GET /api/app/widgets/catalog` liefert pro Widget **`allowed`** (aus `requires_feature` im Katalog + `check_feature_access`). `GET/PUT /api/app/dashboard-layout` wendet **`apply_entitlements_to_layout_dict`** an (nicht erlaubte Einträge: `enabled: false`; Standard-Layout ebenfalls bereinigt). Implementierung: `backend/dashboard_widget_entitlements.py`.
- Optional pro Katalogzeile: **`requires_feature`** (`features.id`) in `widget_catalog.py`; fehlt der Key → Widget für alle authentifizierten Nutzer katalog-sichtbar (ohne zusätzliches Feature-Gate).
### 0.2 Soll: eine Wahrheit für „darf angezeigt werden“
- **Komplexität** (Module aus, Cluster, Stufen: z.B. Ernährung an, aber bestimmte Auswertungen nur in höherem Tier) gehört in die **Feature-/Subscription-Schicht** (inkl. späterer Feature-Cluster), nicht in einzelne React-Widgets.
- **Widgets** sollen das Ergebnis nur **abrufen** (z.B. `allowed` / sichtbar im Katalog), nicht die Tier-Logik duplizieren.
### 0.3 Bindende Anforderungen (wenn Feature-Gating umgesetzt wird)
| Anforderung | Beschreibung |
|-------------|--------------|
| **A1 Zentrale Auflösung** | Backend ermittelt pro Profil (effektiver Tier + Restrictions), welche Widget-IDs **erlaubt** sind idealerweise in **einer** Stelle (Erweiterung des Katalog-Endpoints oder dedizierter Entitlements-Teil der Response). Intern: `check_feature_access` und später ggf. Mapping Widget-ID → Feature-ID(n) / Cluster. |
| **A2 Nutzer-Konfigurator** | Im Dashboard-Lab (und jedem späteren Layout-Konfigurator): Widgets **ohne Berechtigung nicht anbieten** (ausgeblendet oder gar nicht in der Liste). Alle **erlaubten** Widgets bleiben wie heute wählbar. |
| **A3 Layout-Persistenz** | `PUT /api/app/dashboard-layout`: Layout darf **keine** nicht erlaubten Widgets dauerhaft speichern entweder **ablehnen** (422) oder **beim Speichern entfernen/deaktivieren** (Policy festlegen und dokumentieren). Verhindert „gespeichert, aber nie sichtbar“-Zombies. |
| **A4 API-/Datenschutz** | Sichtbarkeit im UI reicht nicht: Endpoints, die **Inhalte** für gated Widgets liefern (Charts, KI, …), müssen weiterhin wie heute **eigenständig** über Features abgesichert sein (`check_feature_access`, 403). |
### 0.4 Katalog-Erweiterung (Vorbereitung ohne feste Tier-Namen)
- Tiers bleiben **in der DB konfigurierbar**; im Code keine Annahme „free vs. pro“.
- Pro Widget-Eintrag (oder separater Mapping-Layer) kann später **`required_feature_id`** (ein Key aus `features.id`) oder ein **Cluster-Key** ergänzt werden, der auf **eine oder mehrere** `check_feature_access`-Abfragen abgebildet wird Details bei Implementierung festlegen.
- Neue Widget-Doku: Wenn ein Widget an ein Feature hängt, in `widget_catalog`-`description` und in dieser Anleitung vermerken.
**Verweis:** Verbindliche Regel auf Projektebene: `.claude/rules/ARCHITECTURE.md` § 9.
---
## 1. Datenfluss (kurz)
1. **`backend/widget_catalog.py`** `WIDGET_CATALOG`: erlaubte Widget-IDs, Reihenfolge, Titel/Beschreibung für API und Default-Layout.
2. **`backend/dashboard_layout_schema.py`** `DashboardLayoutPayload`: jede Zeile hat `id`, `enabled`, optional `config`. IDs müssen in `ALLOWED_WIDGET_IDS` sein (aus dem Katalog abgeleitet).
3. **`backend/dashboard_widget_config.py`** `validate_widget_entry_config`: **nur** Widgets in `WIDGETS_ALLOWING_CONFIG` dürfen **nicht-leere** `config` haben; Keys werden streng validiert (unbekannte Keys → Fehler).
4. **Frontend** `ensurePilotLabWidgetsRegistered()` in `frontend/src/widgetSystem/registerPilotLabWidgets.js`: verbindet jede Katalog-ID mit einer React-Komponente und mappt `ctx.layoutEntry.config` auf Props.
5. **Dashboard-Lab-UI** `frontend/src/pages/DashboardLabPage.jsx`: Umsortieren, Ein/Aus, Speichern; **zusätzliche** UI nur nötig, wenn das Widget konfigurierbare Felder braucht.
---
## 2. Checkliste: neues Widget ohne Konfiguration
| Schritt | Datei | Aktion |
|--------|--------|--------|
| A | `backend/widget_catalog.py` | Neuen Eintrag `{ "id", "title", "description" }` in `WIDGET_CATALOG` einfügen (Reihenfolge = Default-Reihenfolge im Layout). Optional `"requires_feature": "<features.id>"` für Tarif-Gating (`dashboard_widget_entitlements`). |
| B | `backend/widget_catalog.py` | Optional: ID zu `DEFAULT_LAB_WIDGET_IDS` hinzufügen, wenn es im Standard-Lab **aktiv** sein soll. |
| C | `frontend/src/components/dashboard-widgets/MyWidget.jsx` (oder Pilot-Komponente) | React-Komponente implementieren; typischerweise `refreshTick` aus `mapProps` nutzen, um Daten neu zu laden. |
| D | `frontend/src/widgetSystem/registerPilotLabWidgets.js` | `import` + `registerDashboardWidget({ id, Component, mapProps })` `id` **exakt** wie im Katalog. |
| E | `backend/tests/test_widget_catalog.py` | Läuft implizit mit; bei Strukturänderungen Katalog-Tests beachten. |
| F | `backend/version.py` | `MODULE_VERSIONS["app_dashboard"]` MINOR erhöhen und kurz kommentieren. |
| G | Build/Tests | `pytest` (z.B. `tests/test_dashboard_layout_schema.py`, `test_widget_catalog.py`); `npm run build` im `frontend`. |
**Nicht nötig:** `WIDGETS_ALLOWING_CONFIG` oder `validate_widget_entry_config`-Zweig, solange `config` immer `{}` bleibt.
**Wichtig:** Widget-IDs im Frontend-Registry **ohne** Registrierung führen im UI zu „Unbekanntes Widget“ (`dashboardWidgetRegistry.jsx`).
---
## 3. Checkliste: Widget mit konfigurierbaren Einstellungen (`config`)
### 3.1 Backend
1. **`WIDGETS_ALLOWING_CONFIG`** in `backend/dashboard_widget_config.py` um die neue `widget_id` ergänzen.
2. In **`validate_widget_entry_config`** einen eigenen Zweig oder Aufruf einer Hilfsfunktion hinzufügen (siehe bestehende Muster unten).
3. **`MAX_WIDGET_CONFIG_JSON_BYTES`** (3072): keine großen Blobs in `config`.
4. Regeln konsistent halten:
- Unbekannte Keys **ablehnen** (wie bei `kpi_board`, `quick_capture`, `chart_days`-only).
- Leeres Objekt `{}` erlauben, wenn alle Keys optional sind (Validator entscheidet).
**Referenz-Muster im Code:**
| Muster | Verwendung | Implementierung |
|--------|------------|-----------------|
| Nur `chart_days` (790) | Chart-Kacheln | `_validate_chart_days_only(raw, label="...")` |
| KPI-Kacheln | `tiles`-Liste, max. 9 | `_validate_kpi_board_config` |
| Booleans + Mindestens eines true | Schnelleingabe-Sichtbarkeit | `_validate_quick_capture_config` |
Neue komplexe Config: eigene `_validate_my_widget_config` schreiben, Keys als `frozenset` whitelisten, Typen prüfen, sinnvoll normalisieren/abrunden.
5. **Tests** in `backend/tests/test_dashboard_widget_config.py`: Happy-Path, ein ungültiger Wert, unbekannter Key, ggf. Größe/Limits.
### 3.2 Katalog-Beschreibung
In `widget_catalog.py` bei `description` die **konfigurierbaren Keys** kurz nennen (hilft Admin/API-Nutzern). Einheitliche Benennung mit dem Backend (z.B. `chart_days 790`).
### 3.3 Frontend: Props aus Layout
`registerDashboardWidget` erhält `mapProps(ctx)`:
- **`ctx.layoutEntry`**: `{ id, enabled, config? }` hierher kommt die gespeicherte Konfiguration.
- **`ctx.refreshTick`** / **`ctx.requestRefresh()`**: Datenaktualisierung nach Aktionen.
Typische Zuordnung:
```javascript
mapProps: (ctx) => ({
refreshTick: ctx.refreshTick,
myOption: ctx.layoutEntry?.config?.my_option,
})
```
**Abgleich mit Chart-Zeitraum:** Für `chart_days` existiert `frontend/src/widgetSystem/bodyChartDays.js` (`BODY_CHART_DAYS_MIN/MAX`, `normalizeBodyChartDays`). Entweder in `mapProps` normalisieren (wie `body_overview`) oder rohen Wert durchreichen und in der Widget-Komponente normalisieren (wie `nutrition_detail_charts` / `TrendKcalWeightWidget`) **beides** ist im Projekt vertreten; wichtig ist Konsistenz mit der Backend-Grenze 790.
### 3.4 Dashboard-Lab-Editor (`DashboardLabPage.jsx`)
Ohne UI-Änderung bleibt `config` beim Nutzer `{}` konfigurierbare Widgets brauchen **Editor-Controls**:
- **Einfaches Zahlfeld `chart_days`:** Eintrag in `CHART_DAYS_WIDGET_IDS` (Set oben in der Datei) + bestehendes Label/`aria-label`-Pattern für die Zeitraum-Zeile erweitern (siehe `body_overview`, `nutrition_detail_charts`).
- **Strukturierte Config (Listen, mehrere Booleans):** Eigenes Editor-Komponenten-File nach Vorbild `KpiBoardConfigEditor.jsx` / `QuickCaptureConfigEditor.jsx` einbinden und `setLayout` + `normalizeLayoutForEditor` wie bei den bestehenden Blöcken verwenden.
Nach Speichern ruft die Seite `api.putAppDashboardLayout(layout)` auf; das Backend validiert über `DashboardLayoutPayload``validate_widget_entry_config`.
---
## 4. Grenzen und Fehlerbilder
| Thema | Detail |
|--------|--------|
| Erlaubte IDs | Nur IDs aus `WIDGET_CATALOG`. `ALLOWED_WIDGET_IDS` wird daraus abgeleitet nicht manuell duplizieren. |
| Doppelte IDs | Im Layout sind **keine** doppelten `widget.id` erlaubt (`DashboardLayoutPayload`). |
| Max. Widgets | `widgets` max. 32 Einträge (`DashboardLayoutPayload`). |
| Config verboten | Widget **nicht** in `WIDGETS_ALLOWING_CONFIG` → jede nicht-leere `config` → Validierungsfehler beim Speichern. |
| Frontend ≠ Katalog | Komponente registriert, ID fehlt im Katalog → PUT schlägt fehl. |
| Katalog ohne Registry | GET Layout ok, Render zeigt „Unbekanntes Widget“. |
---
## 5. API zum Prüfen
- `GET /api/app/widgets/catalog` Katalog inkl. `allowed` je Widget (Auth + `X-Profile-Id` wie andere App-Endpoints).
- `GET /api/app/dashboard-layout` `layout` (effektiv, bereinigt), `custom`, `product_default_layout` (Übersichts-Standard), `lab_default_layout` (Dashboard-Lab-Standard).
- `PUT /api/app/dashboard-layout` Body `{ "version": 1, "widgets": [ ... ] }` (unerlaubte Widgets werden auf `enabled: false` gesetzt).
---
## 6. Nach getaner Arbeit
- `pytest` für `dashboard_widget_config` und `widget_catalog` / `dashboard_layout_schema`.
- `npm run build`.
- `MODULE_VERSIONS["app_dashboard"]` in `backend/version.py` anheben.
---
## 7. Verwandte Dateien (Referenz)
| Zweck | Pfad |
|--------|------|
| Katalog | `backend/widget_catalog.py` |
| Config-Validierung | `backend/dashboard_widget_config.py` |
| Layout-Pydantic | `backend/dashboard_layout_schema.py` |
| HTTP | `backend/routers/app_dashboard.py` |
| Registry + Render | `frontend/src/widgetSystem/dashboardWidgetRegistry.jsx` |
| Pilot/Lab-Registrierung | `frontend/src/widgetSystem/registerPilotLabWidgets.js` |
| Lab-UI | `frontend/src/pages/DashboardLabPage.jsx` |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,780 @@
# Data Layer Extension Guide
**Version:** 1.0
**Erstellt:** 28. März 2026
**Zielgruppe:** Entwickler, Claude Code
**Phase:** Post Phase 0c
---
## Überblick
Dieser Guide beschreibt, wie man das Data Layer System erweitert mit:
- Neuen Modulen
- Neuen Funktionen in bestehenden Modulen
- Neuen Berechnungslogiken
- Neuen Aggregationsmethoden
**Voraussetzung:** Phase 0c abgeschlossen (Multi-Layer Architecture implementiert)
---
## Modul-Struktur
### Bestehende Module (Phase 0c)
```
backend/data_layer/
├── __init__.py # Exports all functions
├── body_metrics.py # Gewicht, FM, LBM, Umfänge
├── nutrition_metrics.py # Kalorien, Protein, Makros
├── activity_metrics.py # Training, Volumen, Abilities
├── recovery_metrics.py # Sleep, RHR, HRV, Recovery Score
├── health_metrics.py # BP, VO2Max, Health Stability
├── goals.py # Active goals, progress
├── correlations.py # Lag-analysis, plateau detection
└── utils.py # Shared: confidence, baseline, outliers
```
### Modul-Namenskonventionen
- **Singular:** `body_metrics.py` (nicht `bodies_metrics.py`)
- **Domain-focused:** Ein Modul pro fachlichem Bereich
- **Max ~500 Zeilen:** Bei >500 Zeilen → Split erwägen
---
## Neue Funktion hinzufügen
### Template
```python
# backend/data_layer/<module>.py
def get_<metric>_data(
profile_id: str,
days: int = 28,
**kwargs
) -> dict:
"""
[Eine Zeile: Was liefert diese Funktion?]
[Optional: Ausführliche Beschreibung der Berechnung]
Args:
profile_id: User profile ID
days: Analysis window (default 28)
**kwargs: Additional parameters (z.B., goal_mode)
Returns:
{
"<field>": <type>, # Main result
"confidence": str, # REQUIRED: "high"/"medium"/"low"/"insufficient"
"data_points": int, # REQUIRED: Number of data points used
"<additional>": <type> # Any additional data
}
Confidence Rules:
- "high": >= X points
- "medium": >= Y points
- "low": >= Z points
- "insufficient": < Z points
Example:
>>> data = get_<metric>_data("profile_123", days=28)
>>> print(data['<field>'])
42.0
"""
with get_db() as conn:
cur = get_cursor(conn)
# 1. DATA RETRIEVAL
cur.execute("""
SELECT ...
FROM ...
WHERE profile_id = %s
AND date >= NOW() - INTERVAL '%s days'
ORDER BY date
""", (profile_id, days))
rows = cur.fetchall()
# 2. CONFIDENCE CALCULATION
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(
data_points=len(rows),
days_requested=days,
metric_type="general" # or "correlation" or "trend"
)
# 3. EARLY RETURN IF INSUFFICIENT
if confidence == 'insufficient':
return {
"confidence": "insufficient",
"data_points": len(rows),
# Include all fields with safe defaults
"<field>": 0.0,
}
# 4. CALCULATION
# ... your logic here ...
# 5. RETURN STRUCTURED DATA
return {
"<field>": result,
"confidence": confidence,
"data_points": len(rows),
# Additional fields as needed
}
```
### Pflicht-Felder
**Jede Funktion MUSS zurückgeben:**
```python
{
"confidence": str, # "high" | "medium" | "low" | "insufficient"
"data_points": int, # Anzahl verwendeter Datenpunkte
}
```
**Warum?**
- Confidence: UI kann User warnen bei niedriger Datenqualität
- Data Points: Debugging + Monitoring
### Optionale Felder (Best Practices)
```python
{
"first_date": date, # Ältester Datenpunkt
"last_date": date, # Neuester Datenpunkt
"avg": float, # Durchschnitt
"std_dev": float, # Standardabweichung
"min": float, # Minimum
"max": float, # Maximum
"outliers": list[int], # Indices von Ausreißern
}
```
---
## Neue Berechnungslogik hinzufügen
### 1. Statistik-Funktionen (utils.py)
**Wenn du eine neue statistische Berechnung brauchst:**
```python
# backend/data_layer/utils.py
def calculate_<statistic>(
values: list[float],
**kwargs
) -> float:
"""
[Beschreibung der Statistik]
Args:
values: List of measurements
**kwargs: Additional parameters
Returns:
Calculated statistic (float)
Example:
>>> calculate_<statistic>([1.0, 2.0, 3.0])
2.0
"""
# Implementation
...
```
**Beispiele:**
```python
def calculate_median_absolute_deviation(values: list[float]) -> float:
"""
MAD = median(|xi - median(x)|)
More robust than standard deviation for outlier detection.
"""
import statistics
median = statistics.median(values)
deviations = [abs(x - median) for x in values]
return statistics.median(deviations)
def calculate_coefficient_of_variation(values: list[float]) -> float:
"""
CV = (std_dev / mean) * 100
Measures relative variability.
"""
import statistics
mean = statistics.mean(values)
std_dev = statistics.stdev(values)
return (std_dev / mean) * 100 if mean != 0 else 0.0
def calculate_z_score(value: float, mean: float, std_dev: float) -> float:
"""
Z = (x - μ) / σ
Standardized score.
"""
return (value - mean) / std_dev if std_dev != 0 else 0.0
```
### 2. Aggregations-Funktionen (utils.py)
**Neue Aggregationsmethoden für Goal Types:**
```python
# backend/data_layer/utils.py
def aggregate_data(
values: list[tuple], # [(date, value), ...]
method: str,
**kwargs
) -> float:
"""
Aggregate data points using specified method.
Args:
values: List of (date, value) tuples
method: Aggregation method (see below)
**kwargs: Method-specific parameters
Returns:
Aggregated value (float)
Supported Methods:
- "latest": Most recent value
- "avg_7d": Average last 7 days
- "avg_30d": Average last 30 days
- "avg_90d": Average last 90 days
- "sum_7d": Sum last 7 days
- "sum_30d": Sum last 30 days
- "count_7d": Count last 7 days
- "count_30d": Count last 30 days
- "min_30d": Minimum last 30 days
- "max_30d": Maximum last 30 days
- "median_7d": Median last 7 days
- "median_30d": Median last 30 days
- "rolling_avg": Rolling average (window from kwargs)
- "percentile": Nth percentile (n from kwargs)
Example:
>>> values = [(date1, 85.0), (date2, 84.5), ...]
>>> aggregate_data(values, "avg_7d")
84.7
"""
from datetime import date, timedelta
import statistics
if not values:
return 0.0
# Sort by date (most recent first)
sorted_values = sorted(values, key=lambda x: x[0], reverse=True)
if method == "latest":
return float(sorted_values[0][1])
elif method.startswith("avg_"):
days = int(method.split("_")[1].replace("d", ""))
cutoff = date.today() - timedelta(days=days)
recent = [v for d, v in sorted_values if d >= cutoff]
return statistics.mean(recent) if recent else 0.0
elif method.startswith("sum_"):
days = int(method.split("_")[1].replace("d", ""))
cutoff = date.today() - timedelta(days=days)
recent = [v for d, v in sorted_values if d >= cutoff]
return sum(recent)
elif method.startswith("count_"):
days = int(method.split("_")[1].replace("d", ""))
cutoff = date.today() - timedelta(days=days)
return len([v for d, v in sorted_values if d >= cutoff])
elif method.startswith("min_") or method.startswith("max_"):
func_name, days_str = method.split("_")
days = int(days_str.replace("d", ""))
cutoff = date.today() - timedelta(days=days)
recent = [v for d, v in sorted_values if d >= cutoff]
if not recent:
return 0.0
return min(recent) if func_name == "min" else max(recent)
elif method.startswith("median_"):
days = int(method.split("_")[1].replace("d", ""))
cutoff = date.today() - timedelta(days=days)
recent = [v for d, v in sorted_values if d >= cutoff]
return statistics.median(recent) if recent else 0.0
elif method == "rolling_avg":
window = kwargs.get("window", 7)
if len(sorted_values) < window:
return statistics.mean([v for _, v in sorted_values])
recent = sorted_values[:window]
return statistics.mean([v for _, v in recent])
elif method == "percentile":
n = kwargs.get("n", 50) # Default: median
values_only = [v for _, v in sorted_values]
return statistics.quantiles(values_only, n=100)[n - 1] if len(values_only) > 1 else values_only[0]
else:
raise ValueError(f"Unknown aggregation method: {method}")
```
### 3. Korrelations-Funktionen (correlations.py)
**Neue Korrelations-Analysen:**
```python
# backend/data_layer/correlations.py
def get_<metric_a>_<metric_b>_correlation(
profile_id: str,
days: int = 90,
max_lag: int = 7
) -> dict:
"""
Correlation between <metric_a> and <metric_b> with lag analysis.
Args:
profile_id: User profile ID
days: Analysis window
max_lag: Maximum lag in days to test
Returns:
{
"correlation": float, # Pearson r at best lag
"best_lag": int, # Days of lag
"p_value": float, # Statistical significance
"confidence": str,
"paired_points": int,
"interpretation": str # "strong"/"moderate"/"weak"/"none"
}
Interpretation:
|r| > 0.7: "strong"
|r| > 0.5: "moderate"
|r| > 0.3: "weak"
|r| <= 0.3: "none"
"""
# Implementation using scipy.stats or numpy
...
```
---
## Neues Modul erstellen
### Wann ein neues Modul?
**Erstelle ein neues Modul wenn:**
- ✅ Neue fachliche Domäne (z.B., `stress_metrics.py`, `hormone_metrics.py`)
- ✅ Bestehendes Modul >500 Zeilen
- ✅ Klare thematische Trennung möglich
**KEIN neues Modul wenn:**
- ❌ Nur 1-2 Funktionen (füge zu bestehendem Modul hinzu)
- ❌ Starke Abhängigkeit zu bestehendem Modul (merge statt split)
### Modul-Template
```python
# backend/data_layer/<new_module>.py
"""
<Module Name> - <Brief description>
This module provides data functions for <domain>.
Functions:
- get_<metric1>_data()
- get_<metric2>_data()
- ...
Usage:
from data_layer.<new_module> import get_<metric>_data
data = get_<metric>_data(profile_id="123", days=28)
"""
from typing import Optional, List, Dict, Tuple
from datetime import date, timedelta
from db import get_db, get_cursor
# ── PUBLIC FUNCTIONS ─────────────────────────────────────────────
def get_<metric>_data(
profile_id: str,
days: int = 28,
**kwargs
) -> dict:
"""
[Docstring as per template above]
"""
...
# ── PRIVATE HELPERS ──────────────────────────────────────────────
def _calculate_<internal_metric>(values: list[float]) -> float:
"""
Internal helper for <module>.
NOT exported from module.
"""
...
def _validate_<data>(data: dict) -> bool:
"""
Internal validation helper.
"""
...
```
### Exports in __init__.py
```python
# backend/data_layer/__init__.py
# Existing modules
from .body_metrics import *
from .nutrition_metrics import *
from .activity_metrics import *
from .recovery_metrics import *
from .health_metrics import *
from .goals import *
from .correlations import *
from .utils import *
# NEW MODULE
from .<new_module> import *
__all__ = [
# Existing exports...
# NEW MODULE exports
'get_<metric1>_data',
'get_<metric2>_data',
]
```
---
## Integration mit Goal Types
### Goal Type mit neuer Aggregationsmethode
**Scenario:** Du hast eine neue Aggregationsmethode `avg_per_week_30d` implementiert.
#### 1. In utils.py implementieren
```python
# backend/data_layer/utils.py
def aggregate_data(values, method, **kwargs):
# ... existing methods ...
elif method == "avg_per_week_30d":
# Group by week, calculate average per week
from collections import defaultdict
weeks = defaultdict(list)
for d, v in values:
week_start = d - timedelta(days=d.weekday())
weeks[week_start].append(v)
week_avgs = [sum(vals) / len(vals) for vals in weeks.values()]
return sum(week_avgs) / len(week_avgs) if week_avgs else 0.0
# ...
```
#### 2. In goal_utils.py nutzen
```python
# backend/goal_utils.py
def _fetch_by_aggregation_method(
cur,
profile_id: str,
source_table: str,
source_column: str,
aggregation_method: str,
date_column: str = 'date',
filter_conditions: dict = None
) -> Optional[float]:
"""
Fetch current value using aggregation method.
Now supports:
- latest, avg_7d, avg_30d, sum_30d, count_7d, etc.
- avg_per_week_30d (NEW)
"""
# Fetch data
cur.execute(f"""
SELECT {date_column}, {source_column}
FROM {source_table}
WHERE profile_id = %s
ORDER BY {date_column} DESC
LIMIT 100
""", (profile_id,))
rows = cur.fetchall()
if not rows:
return None
# Use aggregate_data from utils
from data_layer.utils import aggregate_data
return aggregate_data(rows, aggregation_method)
```
#### 3. In Frontend verfügbar machen
```javascript
// frontend/src/pages/AdminGoalTypesPage.jsx
const AGGREGATION_METHODS = [
{ value: 'latest', label: 'Aktuellster Wert' },
{ value: 'avg_7d', label: 'Durchschnitt 7 Tage' },
{ value: 'avg_30d', label: 'Durchschnitt 30 Tage' },
{ value: 'sum_30d', label: 'Summe 30 Tage' },
{ value: 'avg_per_week_30d', label: 'Durchschnitt pro Woche (30d)' }, // NEW
// ...
]
```
---
## Testing-Strategie
### Unit Tests für neue Funktionen
```python
# backend/tests/test_data_layer.py
import pytest
from data_layer.<module> import get_<metric>_data
@pytest.fixture
def test_profile_with_data(db_connection):
"""Create test profile with sample data"""
# Setup
profile_id = "test_profile_123"
# Insert test data into relevant tables
...
yield profile_id
# Teardown
...
def test_get_metric_data_sufficient(test_profile_with_data):
"""Test with sufficient data points"""
data = get_<metric>_data(test_profile_with_data, days=28)
assert data['confidence'] in ['high', 'medium', 'low']
assert data['data_points'] >= 18
assert '<field>' in data
assert isinstance(data['<field>'], float)
def test_get_metric_data_insufficient():
"""Test with insufficient data"""
data = get_<metric>_data("no_data_profile", days=28)
assert data['confidence'] == 'insufficient'
assert data['data_points'] == 0
def test_get_metric_data_edge_cases(test_profile_with_data):
"""Test edge cases: outliers, missing values, etc."""
# Test with extreme values
# Test with gaps in data
# Test with all same values
...
def test_get_metric_data_parameters(test_profile_with_data):
"""Test different parameter combinations"""
# Test different days values
for days in [7, 28, 90]:
data = get_<metric>_data(test_profile_with_data, days=days)
assert data is not None
# Test additional parameters
data = get_<metric>_data(test_profile_with_data, days=28, goal_mode="strength")
assert data is not None
```
### Integration Tests
```python
# backend/tests/test_charts_integration.py
def test_chart_uses_data_layer(client, auth_token):
"""Test that chart endpoint uses data layer correctly"""
response = client.get(
"/api/charts/<metric>",
headers={"X-Auth-Token": auth_token}
)
assert response.status_code == 200
data = response.json()
# Verify Chart.js structure
assert 'chart_type' in data
assert 'data' in data
assert 'metadata' in data
# Verify metadata includes confidence
assert 'confidence' in data['metadata']
```
---
## Performance Considerations
### 1. Query Optimization
**Problem:** N+1 Queries
```python
# ❌ BAD:
for goal_id in goal_ids:
cur.execute("SELECT * FROM goals WHERE id = %s", (goal_id,))
# ... process each goal ...
# ✅ GOOD:
cur.execute("SELECT * FROM goals WHERE id = ANY(%s)", (goal_ids,))
```
**Problem:** Unindexed Columns
```sql
-- Add index if querying frequently by date range
CREATE INDEX IF NOT EXISTS idx_weight_log_profile_date
ON weight_log(profile_id, date DESC);
```
### 2. Caching
**For expensive calculations:**
```python
from functools import lru_cache
@lru_cache(maxsize=128)
def get_expensive_calculation(profile_id: str, days: int) -> dict:
"""Cache results for 128 most recent calls"""
...
```
**Note:** In-memory cache resets on restart. For persistent cache → Redis (later).
### 3. Pagination
**For large datasets:**
```python
def get_<metric>_data(
profile_id: str,
days: int = 28,
limit: int = 1000,
offset: int = 0
) -> dict:
"""
Paginated data retrieval.
"""
cur.execute("""
SELECT ...
FROM ...
WHERE profile_id = %s
ORDER BY date DESC
LIMIT %s OFFSET %s
""", (profile_id, limit, offset))
```
---
## Checkliste: Neue Funktion
```
[ ] Richtiges Modul gewählt (oder neues Modul erstellt)
[ ] Funktion implementiert mit korrekter Signatur
[ ] Docstring vollständig (Args, Returns, Example)
[ ] Confidence calculation included
[ ] Returns structured data (dict with primitives)
[ ] NO formatting (no strings with units)
[ ] Decimal → Float conversion wo nötig
[ ] Safe dict access (.get() mit defaults)
[ ] SQL parameter binding (keine String-Concatenation)
[ ] Unit tests geschrieben (sufficient/insufficient/edge cases)
[ ] Integration test geschrieben (wenn Chart/API endpoint)
[ ] Performance geprüft (< 500ms)
[ ] In __init__.py exportiert
[ ] Dokumentation aktualisiert (CLAUDE.md)
[ ] Commit mit aussagekräftiger Message
```
---
## Häufige Fehler
### 1. Vergessen Confidence zu berechnen
```python
# ❌ WRONG:
return {"value": result}
# ✅ CORRECT:
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(len(rows), days, "general")
return {"value": result, "confidence": confidence, "data_points": len(rows)}
```
### 2. Formatierung im Data Layer
```python
# ❌ WRONG (Data Layer):
return {"slope": f"{slope:.2f} kg/Woche"}
# ✅ CORRECT (Data Layer):
return {"slope": 0.23} # Just the number
# ✅ FORMATTING (KI Layer):
return f"{data['slope']:.2f} kg/Woche"
```
### 3. Hardcoded Thresholds
```python
# ❌ WRONG:
if len(rows) < 18: # Magic number
return {"confidence": "insufficient"}
# ✅ CORRECT:
confidence = calculate_confidence(len(rows), days, "general")
if confidence == "insufficient":
return {"confidence": "insufficient", ...}
```
---
## Support & Hilfe
**Bei Fragen:**
1. Lies PLACEHOLDER_DEVELOPMENT_GUIDE.md
2. Prüfe bestehende Funktionen als Beispiel
3. Frag im Team oder erstelle Gitea Issue
**Debugging:**
1. Unit Test schreiben
2. Print intermediate results
3. Check SQL query mit `EXPLAIN ANALYZE`
4. Profile mit `cProfile` wenn Performance-Problem
---
**Autor:** Claude Sonnet 4.5
**Version:** 1.0
**Letzte Aktualisierung:** 28. März 2026

View File

@ -0,0 +1,337 @@
# Feature Enforcement Mapping
**Version:** v9c Phase 2
**Status:** Planning
**Datum:** 20. März 2026
---
## Übersicht
Dieses Dokument definiert, welche API-Endpoints welche Features prüfen müssen.
---
## Feature-Katalog (nach Cleanup)
### Data Features (count, never)
1. `weight_entries` - Gewichtseinträge
2. `circumference_entries` - Umfangsmessungen
3. `caliper_entries` - Hautfaltenmessungen
4. `nutrition_entries` - Ernährungseinträge
5. `activity_entries` - Trainingseinträge
6. `photos` - Progress-Fotos
### AI Features
7. `ai_calls` - KI-Einzelanalysen (count, monthly)
8. `ai_pipeline` - KI-Pipeline-Analyse (boolean, never)
### Export/Import Features
9. `data_export` - Daten exportieren (count, monthly)
10. `data_import` - Daten importieren (count, monthly)
---
## Endpoint → Feature Mapping
### Weight Router (`/api/weight`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/weight` | POST | `weight_entries` | Check before create, increment after |
| `/api/weight` | GET | - | No check (reading is always allowed) |
| `/api/weight/{id}` | PUT | - | No check (editing existing is allowed) |
| `/api/weight/{id}` | DELETE | - | No check (deleting is allowed) |
**Rationale:** Limit bezieht sich auf Gesamtanzahl Einträge (COUNT), nicht auf API-Calls.
---
### Circumference Router (`/api/circumference`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/circumference` | POST | `circumference_entries` | Check before, increment after |
| `/api/circumference` | GET | - | No check |
| `/api/circumference/{id}` | PUT | - | No check |
| `/api/circumference/{id}` | DELETE | - | No check |
---
### Caliper Router (`/api/caliper`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/caliper` | POST | `caliper_entries` | Check before, increment after |
| `/api/caliper` | GET | - | No check |
| `/api/caliper/{id}` | PUT | - | No check |
| `/api/caliper/{id}` | DELETE | - | No check |
---
### Nutrition Router (`/api/nutrition`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/nutrition` | POST | `nutrition_entries` | Check before, increment after |
| `/api/nutrition` | GET | - | No check |
| `/api/nutrition/{id}` | PUT | - | No check |
| `/api/nutrition/{id}` | DELETE | - | No check |
---
### Activity Router (`/api/activity`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/activity` | POST | `activity_entries` | Check before, increment after |
| `/api/activity` | GET | - | No check |
| `/api/activity/{id}` | PUT | - | No check |
| `/api/activity/{id}` | DELETE | - | No check |
---
### Photos Router (`/api/photos`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/photos/upload` | POST | `photos` | Check before, increment after |
| `/api/photos` | GET | - | No check |
| `/api/photos/{id}` | DELETE | - | No check (deleting is allowed) |
---
### Insights Router (`/api/insights`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/insights/run/{slug}` | POST | `ai_calls` | Check before, increment after |
| `/api/insights/pipeline` | POST | `ai_pipeline` (boolean) | Check before (no increment for boolean) |
| `/api/insights` | GET | - | No check |
| `/api/insights/{id}` | GET | - | No check |
**Rationale:**
- `ai_calls` = count-based, monthly reset
- `ai_pipeline` = boolean (enabled/disabled), no usage tracking
---
### Export Router (`/api/export`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/export/csv` | GET | `data_export` | Check before, increment after |
| `/api/export/json` | GET | `data_export` | Check before, increment after |
| `/api/export/zip` | GET | `data_export` | Check before, increment after |
**Rationale:** Ein Feature für alle 3 Export-Typen (konsolidiert).
---
### Import Router (`/api/import`)
| Endpoint | Method | Feature | Action |
|----------|--------|---------|--------|
| `/api/nutrition/import/fddb` | POST | `data_import` | Check before, increment after |
| `/api/activity/import/csv` | POST | `data_import` | Check before, increment after |
| `/api/import/zip` | POST | `data_import` | Check before, increment after |
**Rationale:** Ein Feature für alle Import-Typen.
---
## Implementation Pattern (Phase 2: Non-Blocking Logging)
### Pattern für count-based Features
```python
from auth import require_auth, check_feature_access, increment_feature_usage
import logging
logger = logging.getLogger(__name__)
@router.post("/api/weight")
def create_weight(data: dict, session: dict = Depends(require_auth)):
profile_id = session['profile_id']
# Phase 2: Check access (log only, don't block)
access = check_feature_access(profile_id, 'weight_entries')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
f"weight_entries limit_exceeded ({access['used']}/{access['limit']})"
)
# NOTE: Phase 2 does NOT raise HTTPException - just logs!
# Actual logic
# ... create weight entry ...
# Phase 2: Increment usage (even if limit would be exceeded)
increment_feature_usage(profile_id, 'weight_entries')
return {"ok": True, "id": entry_id}
```
### Pattern für boolean Features
```python
@router.post("/api/insights/pipeline")
def run_pipeline(session: dict = Depends(require_auth)):
profile_id = session['profile_id']
# Phase 2: Check access (log only)
access = check_feature_access(profile_id, 'ai_pipeline')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
f"ai_pipeline disabled"
)
# NOTE: Phase 2 does NOT raise HTTPException!
# Actual logic
# ... run pipeline ...
# No increment for boolean features
return {"ok": True}
```
---
## Phase 3: Frontend Display (ohne Gates)
### Usage-Counter anzeigen
```jsx
// Example: WeightPage.jsx
import { useEffect, useState } from 'react'
import api from '../utils/api'
function WeightPage() {
const [usage, setUsage] = useState(null)
useEffect(() => {
// Fetch usage info
api.get('/api/features/weight_entries/check-access')
.then(res => setUsage(res))
}, [])
return (
<div>
<h1>Gewicht</h1>
{/* Phase 3: Display usage (non-blocking) */}
{usage && usage.limit !== null && (
<div className="usage-badge">
{usage.used} / {usage.limit} Einträge
{usage.remaining !== null && usage.remaining < 5 && (
<span className="warning">
Nur noch {usage.remaining} Einträge verfügbar
</span>
)}
</div>
)}
{/* Button is NOT disabled in Phase 3 */}
<button onClick={createEntry}>
Gewicht hinzufügen
</button>
</div>
)
}
```
---
## Phase 4: Enforcement aktivieren (opt-in)
### Feature-Flag System
```python
# In app_settings table
INSERT INTO app_settings (key, value, description)
VALUES ('feature_enforcement_enabled', 'false', 'Enable/disable feature limit enforcement');
```
### Modified Pattern (mit Enforcement)
```python
def create_weight(data: dict, session: dict = Depends(require_auth)):
profile_id = session['profile_id']
# Check if enforcement is enabled
enforcement_enabled = get_app_setting('feature_enforcement_enabled', False)
# Check access
access = check_feature_access(profile_id, 'weight_entries')
if not access['allowed']:
if enforcement_enabled:
# Phase 4: BLOCK
raise HTTPException(
status_code=429,
detail=f"Limit erreicht: {access['used']}/{access['limit']} Gewichtseinträge. Upgrade für mehr."
)
else:
# Phase 2/3: LOG ONLY
logger.warning(
f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
f"weight_entries limit_exceeded ({access['used']}/{access['limit']})"
)
# Actual logic
# ...
```
---
## Rollout-Strategie
### Phase 2: Log-Only (1-2 Wochen)
- Alle Checks implementiert
- Nur Logging, keine Blocks
- **Monitoring**: Wie oft würde blockiert?
- **Analyse**: Gibt es falsche Limits?
### Phase 3: Display-Only (1 Woche)
- Frontend zeigt Usage an
- Buttons NICHT disabled
- **User-Feedback**: Ist Usage-Anzeige klar?
- **Testing**: Funktioniert Counter korrekt?
### Phase 4: Enforcement (schrittweise)
1. Admin-Account testen (enforcement=true nur für Admin)
2. Test-User (1-2 Accounts)
3. Rollout an alle (feature_enforcement_enabled=true)
### Rollback-Plan
- `UPDATE app_settings SET value='false' WHERE key='feature_enforcement_enabled'`
- Sofortiger Rollback ohne Code-Deploy
---
## Testing-Checklist
### Unit-Tests (Backend)
- [ ] `check_feature_access()` mit allen Hierarchien
- [ ] `increment_feature_usage()` mit Reset-Logik
- [ ] Count-based Features (limit erreicht)
- [ ] Boolean Features (enabled/disabled)
- [ ] Monthly reset funktioniert
### Integration-Tests
- [ ] POST weight-entry bis Limit erreicht
- [ ] Limit wird korrekt in Response angezeigt
- [ ] Reset nach Monatswechsel
- [ ] User-Override überschreibt Tier-Limit
- [ ] Access-Grant überschreibt Base-Tier
### Frontend-Tests
- [ ] Usage-Counter aktualisiert nach Create
- [ ] Warning bei < 5 remaining
- [ ] Unlimited zeigt "∞"
- [ ] Disabled-Features zeigen Upgrade-Hinweis
---
**Letzte Aktualisierung:** 20. März 2026
**Autor:** Lars Stommer + Claude Opus 4.6

View File

@ -0,0 +1,923 @@
# Frontend-Dokumentation
## Übersicht
Das Frontend ist eine **Progressive Web App (PWA)** gebaut mit React 18, Vite und React Router. Die Architektur folgt einem **Component-based Pattern** mit Context-basiertem State Management (kein Redux).
**Technologien:**
- React 18 (ohne TypeScript)
- Vite (Build Tool + Dev Server)
- React Router v6 (Client-side Routing)
- Recharts (Chart-Bibliothek)
- Lucide React (Icon Library)
- Day.js (Datum-Handling)
**Bundle-Größe:** ~450 KB (gzip), PWA-Cache für Offline-Nutzung
---
## Seiten-Übersicht
| Seite | Route | Beschreibung | Auth | Admin |
|-------|-------|--------------|------|-------|
| **LoginScreen** | `/` (ohne Auth) | E-Mail + Passwort Login, SHA256→bcrypt Auto-Migration | ❌ | ❌ |
| **Register** | `/register` | Selbst-Registrierung + E-Mail-Verifizierung | ❌ | ❌ |
| **Verify** | `/verify?token=...` | E-Mail-Verifizierung nach Registrierung | ❌ | ❌ |
| **SetupScreen** | `/` (First Run) | Initiales Setup (erster Admin-Account) | ❌ | ❌ |
| **Dashboard** | `/` | Übersicht: Quick Weight, Stats, Charts, Widgets | ✅ | ❌ |
| **CaptureHub** | `/capture` | Quick-Entry-Auswahl (Gewicht/Umfänge/Caliper/Fotos/Aktivität/Schlaf) | ✅ | ❌ |
| **WeightScreen** | `/weight` | Gewichts-Tracking mit Inline-Edit | ✅ | ❌ |
| **CircumScreen** | `/circum` | Umfänge (8 Punkte) | ✅ | ❌ |
| **CaliperScreen** | `/caliper` | Hautfaltenmessungen (4 Methoden) | ✅ | ❌ |
| **MeasureWizard** | `/wizard` | Geführte Messung (Schritt-für-Schritt) | ✅ | ❌ |
| **ActivityPage** | `/activity` | Training + Trainingstypen (v9d) | ✅ | ❌ |
| **NutritionPage** | `/nutrition` | 3-Tab Layout: Entry / Import / Charts | ✅ | ❌ |
| **SleepPage** | `/sleep` | Schlaf-Tracking + Phasen + Apple Health Import | ✅ | ❌ |
| **RestDaysPage** | `/rest-days` | Ruhetage (Kraft/Cardio/Entspannung) | ✅ | ❌ |
| **VitalsPage** | `/vitals` | 3-Tab: Baseline / Blutdruck / Import | ✅ | ❌ |
| **History** | `/history` | Verlauf mit Charts (Gewicht, KF%, Umfänge, etc.) | ✅ | ❌ |
| **Analysis** | `/analysis` | KI-Auswertung + Pipeline | ✅ | ❌ |
| **SettingsPage** | `/settings` | Profil, PIN-Change, Export, Feature-Usage-Übersicht | ✅ | ❌ |
| **SubscriptionPage** | `/subscription` | Membership-Status (v9c) | ✅ | ❌ |
| **GuidePage** | `/guide` | Anleitungen (Caliper, Umfänge) | ✅ | ❌ |
| **AdminPanel** | `/admin/*` (in Settings) | Admin-Übersicht | ✅ | ✅ |
| **AdminTierLimitsPage** | `/admin/tier-limits` | Tier-Limits Matrix (v9c) | ✅ | ✅ |
| **AdminFeaturesPage** | `/admin/features` | Feature-Verwaltung (v9c) | ✅ | ✅ |
| **AdminTiersPage** | `/admin/tiers` | Tier-Verwaltung (v9c) | ✅ | ✅ |
| **AdminCouponsPage** | `/admin/coupons` | Coupon-System (v9c) | ✅ | ✅ |
| **AdminUserRestrictionsPage** | `/admin/user-restrictions` | User-spezifische Limits (v9c) | ✅ | ✅ |
| **AdminTrainingTypesPage** | `/admin/training-types` | Trainingstypen-CRUD (v9d) | ✅ | ✅ |
| **AdminActivityMappingsPage** | `/admin/activity-mappings` | Activity Mapping-Verwaltung (v9d) | ✅ | ✅ |
| **AdminTrainingProfiles** | `/admin/training-profiles` | Training Type Profiling (v9d #15) | ✅ | ✅ |
**Gesamt:** 31 Seiten (22 User-facing, 9 Admin)
---
## Komponenten
### Wiederverwendbare Komponenten
| Komponente | Props | Beschreibung |
|-----------|-------|--------------|
| **Avatar** | `profile, size` | Runder Avatar mit Initialen + Farbe |
| **Markdown** | `text` | Lightweight Markdown-Renderer (## Headings, **bold**, Listen) |
| **TrialBanner** | | Trial-Countdown-Banner (3 Urgency-Level) |
| **EmailVerificationBanner** | | E-Mail-Verifizierungs-Hinweis |
| **FeatureUsageOverview** | | Tabelle mit allen Feature-Limits + Usage (v9c Phase 3) |
| **UsageBadge** | `feature` | Inline-Badge mit Limit-Status (z.B. "3/10") |
| **TrainingTypeDistribution** | `days` | Pie-Chart für Trainingstypen-Verteilung |
| **SleepWidget** | `days` | Dashboard-Widget mit Schlaf-Stats |
| **RestDaysWidget** | `weeks` | Dashboard-Widget mit aktuellen Ruhetagen |
**Location:** `frontend/src/components/`
### Inline-Komponenten (in Seiten definiert)
**Dashboard.jsx:**
- `QuickWeight` Schnelle Gewichts-Eingabe mit Feature-Limit-Check
- `StatCard` Statistik-Karte mit Delta-Anzeige
- `Pill` Status-Pill mit Tooltip (WHR, WHtR, KF, Protein Ø7T)
**SettingsPage.jsx:**
- `ProfileForm` Formular für Profil-Bearbeitung
**NutritionPage.jsx:**
- `EntryTab` Manuelle Eingabe + CSV-Import
- `ImportHistoryTab` Import-Historie mit Gruppierung
- `ChartsTab` Korrelationen + Wochendaten
**VitalsPage.jsx:**
- `BaselineTab` Morgenmessungen (RHR, HRV, VO2 Max, SpO2)
- `BloodPressureTab` Blutdruck mehrfach täglich + Context-Tagging
- `ImportTab` CSV-Import (Omron Deutsch, Apple Health)
---
## Context / State Management
### 1. AuthContext (`frontend/src/context/AuthContext.jsx`)
**Verantwortlichkeit:** Session-Management + Login/Logout
**State:**
```javascript
{
session: {
token: string,
profile_id: string,
role: 'user' | 'admin',
profile: { id, name, email, tier, ... }
},
loading: boolean,
needsSetup: boolean, // First-run detection
}
```
**Methods:**
- `login(credentials)` Login mit E-Mail + Passwort (oder Legacy profile_id + PIN)
- `setup(formData)` Initial Setup (First Run)
- `logout()` Logout + Token-Löschung
- `setAuthFromToken(token, profile)` Direkt-Login (für E-Mail-Verifizierung)
- `checkStatus()` Auth-Status prüfen (beim App-Start)
**Computed:**
- `isAdmin` `session.role === 'admin'`
- `canUseAI` `session.profile.ai_enabled !== 0`
- `canExport` `session.profile.export_enabled !== 0`
**Storage:**
- `localStorage.bodytrack_token` Auth-Token
- `localStorage.bodytrack_active_profile` Aktive Profile-ID
**Flow:**
```
App-Start → checkStatus()
GET /api/auth/status → {needs_setup: true/false}
↓ (wenn needs_setup = false)
GET /api/auth/me (mit Token aus localStorage)
Session gesetzt → App.jsx zeigt Dashboard
```
### 2. ProfileContext (`frontend/src/context/ProfileContext.jsx`)
**Verantwortlichkeit:** Aktives Profil + Profil-Liste
**State:**
```javascript
{
profiles: Array<Profile>, // Alle Profile
activeProfile: Profile, // Aktuelles Profil
loading: boolean,
}
```
**Methods:**
- `setActiveProfile(profile)` Profil wechseln (speichert in localStorage)
- `refreshProfiles()` Profile neu laden (nach Update)
**Flow:**
```
session.profile_id ändert sich
GET /api/profiles (mit X-Auth-Token)
profiles gesetzt, activeProfile = match(session.profile_id)
```
**Hinweis:** Profile-Wechsel ist derzeit Single-User-optimiert (Multi-User-Support in Planung).
---
## API-Integration (`frontend/src/utils/api.js`)
**Zweck:** Zentrale API-Schnittstelle **ALLE** API-Calls gehen über `api.js`
**Features:**
- Automatisches Token-Injection (`X-Auth-Token` Header)
- Automatisches Profile-ID-Injection (`X-Profile-Id` Header, derzeit deprecated)
- Einheitliche Fehlerbehandlung (parst `{detail: "..."}` aus Backend)
- Typed-like API (alle Methoden dokumentiert)
**Beispiel:**
```javascript
import { api } from '../utils/api'
// GET-Request
const weights = await api.listWeight(365) // limit=365
// POST-Request
await api.upsertWeight('2026-03-23', 75.5, 'Morgens nüchtern')
// DELETE-Request
await api.deleteWeight(entryId)
// File-Upload
const result = await api.importCsv(file)
```
**Headers-Injection:**
```javascript
function hdrs(extra={}) {
const h = {...extra}
if (_profileId) h['X-Profile-Id'] = _profileId // Deprecated, bleibt für Legacy
const token = getToken()
if (token) h['X-Auth-Token'] = token
return h
}
```
**Error-Handling:**
```javascript
if (!res.ok) {
const err = await res.text()
try {
const parsed = JSON.parse(err)
throw new Error(parsed.detail || err)
} catch {
throw new Error(err)
}
}
```
**API-Methoden (285 Zeilen):**
- **Profiles:** `getActiveProfile, listProfiles, createProfile, updateProfile, deleteProfile`
- **Weight:** `listWeight, upsertWeight, updateWeight, deleteWeight, weightStats`
- **Circumferences:** `listCirc, upsertCirc, updateCirc, deleteCirc`
- **Caliper:** `listCaliper, upsertCaliper, updateCaliper, deleteCaliper`
- **Activity:** `listActivity, createActivity, updateActivity, deleteActivity, activityStats, bulkCategorizeActivities, importActivityCsv`
- **Nutrition:** `importCsv, listNutrition, nutritionCorrelations, nutritionWeekly, nutritionImportHistory, createNutrition, updateNutrition, deleteNutrition`
- **Photos:** `uploadPhoto, listPhotos, photoUrl`
- **AI:** `insightTrend, listPrompts, runInsight, insightPipeline, listInsights, latestInsights`
- **Export:** `exportZip, exportJson, exportCsv` (Download-Handling inkludiert)
- **Admin:** `adminListProfiles, adminCreateProfile, adminDeleteProfile, adminSetPermissions, changePin`
- **Auth:** `register, verifyEmail, resendVerification`
- **Subscription (v9c):** `getMySubscription, getMyUsage, getMyLimits, redeemCoupon, getFeatureUsage`
- **Admin Features (v9c):** `listFeatures, createFeature, updateFeature, deleteFeature`
- **Admin Tiers (v9c):** `listTiers, createTier, updateTier, deleteTier, getTierLimitsMatrix, updateTierLimit, updateTierLimitsBatch`
- **Admin User Restrictions (v9c):** `listUserRestrictions, createUserRestriction, updateUserRestriction, deleteUserRestriction`
- **Admin Coupons (v9c):** `listCoupons, createCoupon, updateCoupon, deleteCoupon, getCouponRedemptions`
- **Admin Access Grants (v9c):** `listAccessGrants, createAccessGrant, updateAccessGrant, revokeAccessGrant`
- **Training Types (v9d):** `listTrainingTypes, listTrainingTypesFlat, getTrainingCategories, adminListTrainingTypes, adminCreateTrainingType, adminUpdateTrainingType, adminDeleteTrainingType, getAbilitiesTaxonomy`
- **Training Profiles (v9d #15):** `getProfileStats, getProfileTemplates, getProfileTemplate, applyProfileTemplate, getTrainingParameters, batchEvaluateActivities`
- **Activity Mappings (v9d):** `adminListActivityMappings, adminCreateActivityMapping, adminUpdateActivityMapping, adminDeleteActivityMapping, adminGetMappingCoverage`
- **Sleep (v9d):** `listSleep, getSleepByDate, createSleep, updateSleep, deleteSleep, getSleepStats, getSleepDebt, getSleepTrend, getSleepPhases, importAppleHealthSleep`
- **Rest Days (v9d):** `listRestDays, createRestDay, getRestDay, updateRestDay, deleteRestDay, getRestDaysStats, validateActivity`
- **Vitals Baseline (v9d):** `listBaseline, getBaselineByDate, createBaseline, updateBaseline, deleteBaseline, getBaselineStats, importBaselineAppleHealth`
- **Blood Pressure (v9d):** `listBloodPressure, getBPByDate, createBloodPressure, updateBloodPressure, deleteBloodPressure, getBPStats, importBPOmron`
---
## Berechnungs-Utils
### 1. calc.js (`frontend/src/utils/calc.js`)
**Zweck:** Körperfett-Berechnungen + Derived Metrics
**Funktionen:**
**`calcBodyFat(method, skinfolds, sex, age)`**
- Berechnet Körperfett-% nach 4 Methoden:
- `jackson3` Jackson-Pollock 3-Punkt (Standard)
- `jackson7` Jackson-Pollock 7-Punkt
- `durnin` Durnin-Womersley 4-Punkt
- `parrillo` Parrillo 9-Punkt (linear)
- Nutzt Siri-Formel: `BF% = (495 / D) - 450`
- Parameter:
- `method`: String ('jackson3', 'jackson7', 'durnin', 'parrillo')
- `skinfolds`: Object mit Hautfalten in mm (z.B. `{chest: 12, abdomen: 24, thigh: 18}`)
- `sex`: 'm' | 'f'
- `age`: Number
**`getBfCategory(pct, sex)`**
- Kategorisiert Körperfett-% in Bereiche:
- Männer: Essenziell (<6%), Athletisch (6-14%), Fit (14-18%), Durchschnitt (18-25%), Übergewicht (>25%)
- Frauen: Essenziell (<14%), Athletisch (14-21%), Fit (21-25%), Durchschnitt (25-32%), Übergewicht (>32%)
- Returns: `{max, label, color, desc}`
**`calcDerived(measurement, height)`**
- Berechnet abgeleitete Metriken:
- **WHR** (Waist-Hip-Ratio): `waist / hip` (Ziel: <0.90 M / <0.85 F)
- **WHtR** (Waist-to-Height-Ratio): `waist / height` (Ziel: <0.50)
- **FFMI** (Fat-Free Mass Index): `lean_mass / (height_m²)` (Natural Limit: ~25 M / ~22 F)
- Returns: `{whr, whtr, ffmi}`
**`getRuleBasedAssessment(current, previous, profile)`**
- Generiert automatische Interpretationen basierend auf:
- Körperfett-Kategorie
- Änderungen seit letzter Messung
- FFMI (Muskel-Index)
- WHR / WHtR (Fettverteilung)
- Taillenumfang (WHO-Grenzwerte)
- Returns: `{findings: Array, summary: string, summaryType: 'good'|'warn'|'bad'}`
**Guide-Daten:**
- `CIRCUMFERENCE_GUIDE` Messanleitung für 8 Umfangspunkte (wo, wie, Posture, Tipps)
- `CALIPER_GUIDE` Messanleitung für Hautfalten-Punkte
### 2. interpret.js (`frontend/src/utils/interpret.js`)
**Zweck:** Interpretation von Messwerten
**`getInterpretation(measurement, profile, prevMeasurement)`**
- Analysiert Messung und generiert strukturierte Interpretation:
- Körperfett-Status (mit Kategorie + Farbe)
- WHR-Status
- WHtR-Status
- FFMI-Status
- BMI-Status
- Vergleich zur letzten Messung (Deltas)
- Returns: Array von Interpretations-Objects:
```javascript
{
category: 'Körperfett',
icon: '🫧',
status: 'good' | 'warn' | 'bad',
title: 'Athletischer Körperfettanteil',
detail: 'Ausgezeichnet. Typisch für aktive Sportler...',
value: '12.5%',
badge: 'Athletisch',
color: '#1D9E75',
}
```
**`getStatusColor(status)`** Farbe für Status ('good'→Grün, 'warn'→Orange, 'bad'→Rot)
**`getStatusBg(status)`** Background-Farbe für Status
### 3. Markdown.jsx (`frontend/src/utils/Markdown.jsx`)
**Zweck:** Leichtgewichtiger Markdown-Renderer für KI-Texte
**Unterstützte Syntax:**
- `# Heading 1`, `## Heading 2`, `### Heading 3`
- `**bold**`, `*italic*`
- `- Bullet List`, `1. Numbered List`
- `---` (Horizontal Rule)
- Line Breaks
**Verwendung:**
```jsx
<Markdown text={aiInsight.content} />
```
**Vorteil:** Kein remark/rehype-Dependency nur 134 Zeilen pures React
---
## CSS-System (`frontend/src/app.css`)
### CSS-Variablen (Light + Dark Mode)
**Farben:**
```css
:root {
--bg: #f4f3ef; /* Hintergrund */
--surface: #ffffff; /* Cards */
--surface2: #f9f8f5; /* Inputs, Secondary */
--border: rgba(0,0,0,0.09); /* Standard-Border */
--border2: rgba(0,0,0,0.16);/* Input-Border */
--text1: #1c1b18; /* Primär-Text */
--text2: #5a5955; /* Sekundär-Text */
--text3: #9a9892; /* Muted */
--accent: #1D9E75; /* Primär-Farbe */
--accent-light: #E1F5EE; /* Accent-Background */
--accent-dark: #0a5c43; /* Hover */
--danger: #D85A30; /* Fehler/Löschen */
--warn: #EF9F27; /* Warnung */
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #181816;
--surface: #222220;
--surface2: #1e1e1c;
--border: rgba(255,255,255,0.08);
--text1: #eeecea;
--text2: #aaa9a4;
--text3: #686762;
--accent-light: #04342C;
--accent-dark: #5DCAA5;
}
}
```
**Layout:**
```css
--nav-h: 64px; /* Bottom Navigation Höhe */
--header-h: 52px; /* App-Header Höhe */
```
### Utility Classes
**Cards:**
```css
.card /* Standard-Card (white background, border, rounded) */
.card-title /* Card-Überschrift (uppercase, small, muted) */
```
**Stats:**
```css
.stats-grid /* 2-Column Grid für Stats */
.stat-card /* Einzelne Stat-Card */
.stat-val /* Wert (groß, bold) */
.stat-label /* Label (klein, muted) */
.stat-delta /* Delta (z.B. "+2.5 kg") */
.delta-pos /* Positive Änderung (grün) */
.delta-neg /* Negative Änderung (rot) */
```
**Forms:**
```css
.form-section /* Formular-Sektion mit Abstand */
.form-section-title /* Sektions-Titel (uppercase, border-bottom) */
.form-row /* Zeile mit Label + Input + Unit */
.form-label /* Label (links, flex:1) */
.form-input /* Input (90px breit, text-align:right) */
.form-unit /* Einheit (z.B. "kg", 24px breit) */
.form-select /* Select-Dropdown */
.form-sub /* Sub-Label (klein, muted) */
```
**Buttons:**
```css
.btn /* Base Button */
.btn-primary /* Primär-Button (accent) */
.btn-secondary /* Sekundär-Button (grau) */
.btn-danger /* Löschen-Button (rot) */
.btn-full /* Full-Width Button */
```
**Tabs:**
```css
.tabs /* Tab-Container (segmented control) */
.tab /* Einzelner Tab */
.tab.active /* Aktiver Tab (white background, shadow) */
```
**Misc:**
```css
.badge /* Inline-Badge (klein, rounded) */
.spinner /* Loading-Spinner (CSS-Animation) */
.empty-state /* Leerer Zustand (zentriert, muted) */
.muted /* Muted-Text (text3) */
```
### Responsive Design
**Mobile-First Approach:**
- Standard-Layout für 375px600px (Mobile)
- Max-Width: 600px (zentriert auf Desktop)
- Bottom-Navigation für Mobile (64px hoch)
- Touch-optimierte Button-Größen (min 44px)
**Bottom Navigation:**
```css
.bottom-nav {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 600px;
height: var(--nav-h);
z-index: 20;
}
```
**Safe Area (iPhone):**
```css
padding-bottom: env(safe-area-inset-bottom, 0); /* Notch-Handling */
```
**Desktop-Optimierung:**
- App zentriert mit max-width: 600px
- Kein responsives Layout für >600px (bewusst Mobile-optimiert)
---
## PWA-Konfiguration
### Service Worker
**Location:** `frontend/public/service-worker.js`
**Cache-Strategie:**
- **Static Assets:** Cache-First (HTML, CSS, JS, Icons)
- **API-Calls:** Network-First mit Fallback
- **Photos:** Cache-First mit Expiry
**Registrierung:**
```javascript
// frontend/src/main.jsx
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
}
```
### Manifest
**Location:** `frontend/public/manifest.json`
**Wichtige Felder:**
```json
{
"name": "Mitai Jinkendo",
"short_name": "Mitai",
"theme_color": "#1D9E75",
"background_color": "#f4f3ef",
"display": "standalone",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
```
**Installation:**
- iOS: "Zum Home-Bildschirm"
- Android: "Installieren"-Prompt
- Desktop: Chrome/Edge Install-Button
---
## Chart-Bibliothek (Recharts)
**Verwendete Charts:**
| Chart-Typ | Verwendung | Seite |
|-----------|-----------|-------|
| **LineChart** | Gewicht, Körperfett, Umfänge, Vitalwerte | Dashboard, History |
| **BarChart** | Wöchentliche Ernährung, Aktivität | NutritionPage, History |
| **PieChart** | Trainingstypen-Verteilung | Dashboard, ActivityPage |
| **ScatterChart** | Korrelationen (Gewicht vs. Kalorien) | NutritionPage |
| **ComposedChart** | Multi-Axis (Gewicht + KF% kombiniert) | History |
**Standard-Konfiguration:**
```jsx
<ResponsiveContainer width="100%" height={240}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
<XAxis dataKey="date" tick={{ fontSize: 11 }} stroke="var(--text3)" />
<YAxis tick={{ fontSize: 11 }} stroke="var(--text3)" />
<Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)' }} />
<Line type="monotone" dataKey="weight" stroke="var(--accent)" strokeWidth={2} dot={false} />
</LineChart>
</ResponsiveContainer>
```
**Farb-Schema:**
- Gewicht: `var(--accent)` (#1D9E75)
- Körperfett: `#D85A30` (Danger)
- Umfänge: Individual Colors (siehe `CIRCUMFERENCE_GUIDE`)
- 7-Tage-Durchschnitt: `var(--accent-dark)` (#0a5c43, gestrichelt)
---
## Feature Usage Badges (v9c Phase 3)
**Zweck:** Sichtbarkeit der Feature-Limits direkt in der UI
**Komponenten:**
### 1. UsageBadge (`components/UsageBadge.jsx`)
**Inline-Badge mit Limit-Status:**
```jsx
<UsageBadge feature="weight_entries" />
// Rendert: "5/10" (grün) oder "10/10 🔒" (rot)
```
**Logik:**
```javascript
const { allowed, used, limit, remaining } = await api.getFeatureUsage()
const color = allowed ? 'var(--accent)' : 'var(--danger)'
const text = limit === null ? '∞' : `${used}/${limit}`
```
**Verwendung:** In Buttons (z.B. "Speichern 5/10")
### 2. FeatureUsageOverview (`components/FeatureUsageOverview.jsx`)
**Tabelle mit allen Features:**
```jsx
<FeatureUsageOverview />
```
**Darstellung:**
| Feature | Genutzt | Limit | Verbleibend | Status |
|---------|---------|-------|-------------|--------|
| Gewichtseinträge | 45 | 100 | 55 | ✓ OK |
| KI-Aufrufe | 10 | 10 | 0 | 🔒 Limit erreicht |
| Daten-Export | 1 | 5 | 4 | ✓ OK |
**Farbcodierung:**
- Grün: `allowed === true`
- Rot: `allowed === false`
- Gelb: `remaining < 10% && allowed`
**Location:** Settings-Seite (Tab "Quota")
---
## Routing-Architektur
### App-Struktur (`App.jsx`)
```
AuthProvider
ProfileProvider
BrowserRouter
AppShell
├── Public Routes (ohne Auth)
│ ├── /register → Register
│ ├── /verify?token=... → Verify
│ └── /reset-password?token=... → ResetPassword
├── Auth Gates
│ ├── authLoading → Spinner
│ ├── needsSetup → SetupScreen
│ └── !session → LoginScreen
└── Authenticated Routes
├── Header (Logo + Logout + Avatar)
├── Main (Scrollable Content)
│ └── Routes (31 Seiten)
└── Nav (Bottom Navigation, 5 Items)
```
### Navigation-Items
```javascript
const links = [
{ to: '/', icon: <LayoutDashboard/>, label: 'Übersicht' },
{ to: '/capture', icon: <PlusSquare/>, label: 'Erfassen' },
{ to: '/history', icon: <TrendingUp/>, label: 'Verlauf' },
{ to: '/analysis', icon: <BarChart2/>, label: 'Analyse' },
{ to: '/settings', icon: <Settings/>, label: 'Einst.' },
]
```
**Besonderheit:** Active-State via React Router `NavLink` (`isActive` prop)
### Route-Guards
**Auth-Schutz:**
```jsx
if (!session) return <LoginScreen/>
```
**Admin-Schutz:**
```jsx
// In AdminPanel-Seiten:
const { isAdmin } = useAuth()
if (!isAdmin) return <div>Nur für Admins</div>
```
**Setup-Check:**
```jsx
if (needsSetup) return <SetupScreen/>
```
---
## Performance-Optimierungen
### 1. Code Splitting
**React.lazy() für Admin-Seiten:**
```javascript
const AdminPanel = React.lazy(() => import('./pages/AdminPanel'))
```
**Vorteil:** Admin-Code nicht im Initial Bundle (~80 KB gespart)
### 2. Memoization
**useMemo für teure Berechnungen:**
```javascript
const stats = useMemo(() => {
return calculateStats(data)
}, [data])
```
**Verwendung:** Chart-Daten-Transformation, Aggregationen
### 3. Lazy Loading
**Images:**
```jsx
<img src={photoUrl} loading="lazy" />
```
**Charts:**
- Nur sichtbare Charts rendern (Intersection Observer in Planung)
### 4. API-Call-Batching
**Parallel-Loading:**
```javascript
const [stats, insights, weights] = await Promise.all([
api.getStats(),
api.latestInsights(),
api.listWeight(30),
])
```
**Verwendung:** Dashboard initial load
---
## Error-Handling
### 1. API-Fehler
**Pattern in allen Seiten:**
```javascript
const [error, setError] = useState(null)
try {
const data = await api.someEndpoint()
setData(data)
} catch(e) {
setError(e.message) // api.js parsed bereits {detail: "..."}
} finally {
setLoading(false)
}
```
**Anzeige:**
```jsx
{error && (
<div style={{
background: 'rgba(216,90,48,0.1)',
color: 'var(--danger)',
padding: '10px 14px',
borderRadius: 8,
border: '1px solid rgba(216,90,48,0.2)'
}}>
{error}
</div>
)}
```
### 2. Network-Fehler
**Offline-Detection:**
```javascript
useEffect(() => {
const handleOnline = () => setOnline(true)
const handleOffline = () => setOnline(false)
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
}
}, [])
```
**Anzeige:** Banner "Keine Internetverbindung Änderungen werden gespeichert sobald Online"
### 3. Form-Validierung
**Client-Side:**
```javascript
if (!weight || weight < 20 || weight > 300) {
setError('Gewicht zwischen 20 und 300 kg')
return
}
```
**Server-Side:**
- Backend wirft `HTTPException(400, detail="...")` → Frontend zeigt `detail`
---
## Besonderheiten & Design-Entscheidungen
### 1. Warum kein TypeScript?
**Entscheidung:** Bewusst auf TypeScript verzichtet
**Gründe:**
- Schnellere Prototyping-Geschwindigkeit
- Weniger Build-Komplexität
- JSDoc-Kommentare für Dokumentation ausreichend
- Type Safety durch Backend (Pydantic validiert alle Inputs)
### 2. Warum Context statt Redux?
**Entscheidung:** Context API ausreichend für diesen Use-Case
**Gründe:**
- Nur 2 globale States (Auth + Profile)
- Kein komplexes State-Update-Pattern nötig
- Weniger Boilerplate
- Performance ausreichend (keine häufigen Re-Renders)
**Hinweis:** Bei >5 Contexts würde Redux Sinn machen
### 3. Warum Custom Markdown statt remark/rehype?
**Entscheidung:** Eigener Markdown-Renderer (134 Zeilen)
**Gründe:**
- Nur Subset von Markdown benötigt (Headings, Bold, Listen)
- remark + rehype + plugins = ~200 KB Bundle-Size
- Custom-Renderer = 0 Dependencies
- Full Control über Styling
### 4. Warum Recharts statt Chart.js?
**Entscheidung:** Recharts für alle Charts
**Gründe:**
- React-native (kein Canvas, sondern SVG)
- Declarative API passt zu React
- Responsive by default
- Kleineres Bundle als Chart.js
### 5. Inline-Editing statt Modal-Forms
**Entscheidung:** Inline-Edit für alle Listen (Gewicht, Ernährung, Vitalwerte)
**Pattern:**
```javascript
const [editingId, setEditingId] = useState(null)
{entries.map(e => (
editingId === e.id
? <EditForm entry={e} onSave={...} onCancel={...} />
: <ViewRow entry={e} onEdit={() => setEditingId(e.id)} />
))}
```
**Vorteil:**
- Keine Modal-Komponente nötig
- Besserer Mobile-UX (kein Overlay)
- Schnelleres Editing (kein Dialog öffnen)
---
## Bekannte Limitationen
### 1. Desktop-Optimierung
**Problem:** App ist Mobile-First, Desktop-Layout nicht optimiert
**Aktuell:** Max-Width 600px, zentriert auf Desktop
**Geplant (v10+):** Responsive Grid-Layout für Desktop (Sidebar + Multi-Column)
### 2. Offline-Modus
**Problem:** Service Worker cached nur Static Assets, nicht API-Responses
**Aktuell:** Offline = Keine Daten-Eingabe möglich
**Geplant (v10+):** IndexedDB für Offline-Queue
### 3. Multi-Profil-Support
**Problem:** Profile-Wechsel funktioniert, aber Session ist Single-User
**Aktuell:** Logout + Login für Profil-Wechsel
**Geplant (v9f+):** Multi-Session-Support (Switch ohne Logout)
### 4. Accessibility
**Problem:** ARIA-Labels fehlen, Keyboard-Navigation unvollständig
**Aktuell:** Maus/Touch-optimiert
**Geplant (v10+):** WCAG 2.1 AA Compliance
---
## Testing
**Aktueller Stand:** Kein automatisiertes Testing implementiert
**Geplant (v10+):**
- Unit-Tests: Vitest für utils (calc.js, interpret.js)
- Component-Tests: React Testing Library
- E2E-Tests: Playwright für kritische Flows (Login, Gewicht-Eingabe, KI-Analyse)
**Manual Testing:**
- Alle Features manuell auf iOS Safari, Android Chrome, Desktop Firefox getestet
- Regression-Tests bei jedem Deploy
---
## Zusammenfassung
**Architektur-Highlights:**
- ✅ 31 Seiten (22 User, 9 Admin)
- ✅ Context-basiertes State Management (Auth + Profile)
- ✅ Zentrale API-Schnittstelle (api.js)
- ✅ Berechnungs-Utils für Body-Metrics (calc.js, interpret.js)
- ✅ CSS-Variablen für Light/Dark Mode
- ✅ PWA mit Service Worker
- ✅ Recharts für alle Charts
- ✅ Feature Usage Badges (v9c Phase 3)
- ✅ Mobile-First Design (max-width 600px)
- ✅ Inline-Editing statt Modals
- ✅ Custom Markdown-Renderer (0 Dependencies)
**Performance:**
- Initial Bundle: ~450 KB (gzip)
- Code Splitting: Admin-Seiten lazy-loaded
- API-Call-Batching: Parallel-Loading für Dashboard
**Nächste Schritte:**
- Testing (Vitest + React Testing Library)
- Desktop-Responsive-Layout
- Offline-Modus (IndexedDB)
- Accessibility (WCAG 2.1 AA)

View File

@ -0,0 +1,212 @@
# Internal API Reference
**Purpose:** Prevent guessing function signatures and module exports.
Last updated: 2026-03-28
---
## goal_utils.py
### Focus Area Functions
```python
def get_focus_weights(conn, profile_id: str) -> Dict[str, float]
```
Returns user's focus area weights as `{area_key: weight_percent}`.
Keys are **English** (weight_loss, muscle_gain, etc.).
```python
def get_focus_weights_v2(conn, profile_id: str) -> Dict[str, float]
```
Newer version with auto-normalization. Use this for new code.
```python
def get_primary_focus(conn, profile_id: str) -> str
```
Returns area_key of highest weighted focus area.
```python
def get_focus_description(focus_area: str) -> str
```
Returns German description for a focus area key.
---
### Goal Functions
```python
def get_active_goals(profile_id: str) -> List[Dict]
```
Returns ALL active goals for a profile.
Each dict has: id, type_key, current_value, target_value, is_primary, etc.
**To filter by type:**
```python
goals = get_active_goals(profile_id)
weight_goals = [g for g in goals if g.get('type_key') == 'weight']
```
```python
def get_goal_by_id(goal_id: str) -> Optional[Dict]
```
Returns single goal by UUID.
```python
def get_goal_type_config(conn, type_key: str) -> Optional[Dict[str, Any]]
```
Returns goal type metadata from `goal_type_definitions` table:
- source_table, source_column, aggregation_method, unit
```python
def get_current_value_for_goal(conn, profile_id: str, goal_type: str) -> Optional[float]
```
Calculates current value for a goal type using its aggregation method.
---
### Aggregation Functions
```python
def calculate_current_value(
profile_id: str,
table: str,
column: str,
method: str,
date_column: str = 'date',
filter_conditions: Optional[List[Tuple[str, Any]]] = None
) -> Optional[float]
```
**Available methods:**
- `latest` - Most recent value
- `avg_7d` - 7-day average (numeric values only)
- `avg_30d` - 30-day average
- `max_30d` - Maximum in 30 days
- `avg_per_week_30d` - Count per week over 30 days (for frequency tracking)
**Filter conditions format:**
```python
[("training_type", "strength"), ("quality", "good")]
```
---
## calculations/body_metrics.py
### Score Functions
```python
def calculate_body_progress_score(profile_id: str, focus_weights: Dict[str, float] = None) -> Optional[int]
```
Returns 0-100 score for body composition progress.
Weighted by focus areas: weight_loss, muscle_gain, body_recomposition.
**Sub-functions (private):**
- `_score_weight_trend(profile_id)` - Alignment with weight goal
- `_score_body_composition(profile_id)` - BF% + lean mass progress
---
## calculations/nutrition_metrics.py
### Score Functions
```python
def calculate_nutrition_score(profile_id: str, focus_weights: Dict[str, float] = None) -> Optional[int]
```
Returns 0-100 score for nutrition adherence.
Weighted by: protein_intake, calorie_balance, macro_consistency, meal_timing, hydration.
**Sub-functions (private):**
- `_score_calorie_adherence(profile_id)` - Energy balance vs goal
- `_score_protein_adequacy(profile_id)` - Protein target adherence
---
## calculations/activity_metrics.py
### Score Functions
```python
def calculate_activity_score(profile_id: str, focus_weights: Dict[str, float] = None) -> Optional[int]
```
Returns 0-100 score for training quality.
Weighted by: strength, endurance (aerobic+anaerobic+cardiovascular), mobility/coordination.
**Sub-functions (private):**
- `_score_training_quality(profile_id)` - Session quality %
- `_score_training_consistency(profile_id)` - Frequency adherence
---
## calculations/scores.py
### Main Score Aggregator
```python
def calculate_goal_progress_score(profile_id: str) -> Optional[int]
```
Master score aggregating body, nutrition, activity progress.
Weighted by user's category weights from focus areas.
```python
def calculate_category_progress(profile_id: str, category: str) -> Optional[int]
```
Maps category name to appropriate score function.
Categories: körper, ernährung, aktivität, erholung, vitalwerte, mental, lebensstil
---
## Common Patterns
### Getting Goals for a Specific Type
```python
# ❌ DON'T (function doesn't exist):
from goal_utils import get_goals_by_type
goals = get_goals_by_type(profile_id, 'weight')
# ✅ DO:
from goal_utils import get_active_goals
goals = get_active_goals(profile_id)
weight_goals = [g for g in goals if g.get('type_key') == 'weight']
```
### Accessing Focus Area Weights
```python
# ❌ DON'T (German keys don't exist in DB):
weight_focus = focus_weights.get('körpergewicht', 0)
# ✅ DO (English keys from Migration 031):
weight_loss = focus_weights.get('weight_loss', 0)
muscle_gain = focus_weights.get('muscle_gain', 0)
```
### Querying Sleep Data
```python
# ❌ DON'T:
SELECT duration FROM sleep_log
# ✅ DO:
SELECT duration_minutes, deep_minutes, rem_minutes FROM sleep_log
```
---
## Verification Checklist
Before using any function:
1. ✅ Verify function exists: `grep "^def function_name" backend/module.py`
2. ✅ Check signature: `Read module.py` to see parameters
3. ✅ Check what it returns: Read docstring or implementation
4. ✅ Verify imported correctly: Check module path
Before writing SQL:
1. ✅ Check table schema: `grep "CREATE TABLE" backend/schema.sql`
2. ✅ Verify column names: Check migration files
3. ✅ Test query logic: Use actual data, not assumptions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,392 @@
# Database Migrations System
**Version:** v9c
**Implementiert:** 2026-03-21
**Dokumentiert:** 2026-03-21
---
## Übersicht
Mitai Jinkendo verwendet ein automatisches Migrations-System für strukturierte Schema-Änderungen. Alle Migrationen werden beim Container-Start automatisch ausgeführt.
## Architektur
### Migration-Tracking
**Tabelle:** `schema_migrations`
```sql
CREATE TABLE schema_migrations (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) UNIQUE NOT NULL,
applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
)
```
Diese Tabelle trackt, welche Migrationen bereits angewendet wurden.
### Ablauf beim Container-Start
1. **PostgreSQL Connection Check** (`db_init.py`)
2. **Schema-Initialisierung** (falls `profiles` Tabelle nicht existiert)
- Lädt `backend/schema.sql` (Basis-Schema)
3. **Migrations-System** (`run_migrations()`)
- Erstellt `schema_migrations` Tabelle (falls nicht vorhanden)
- Scannt `backend/migrations/` nach `.sql` Dateien
- Filtert nur nummerierte Dateien: `\d{3}_*.sql` (z.B. `001_feature.sql`)
- Sortiert alphabetisch (aufsteigende Reihenfolge)
- Wendet nur noch nicht angewendete Migrationen an
- Trackt jede erfolgreich angewendete Migration
4. **SQLite-zu-PostgreSQL Migration** (falls vorhanden)
## Datei-Konventionen
### Naming-Pattern
**Format:** `XXX_descriptive_name.sql`
- `XXX` = Dreistellige Nummer (001, 002, 003, ...)
- Unterstrich `_` als Trennzeichen
- Kleinbuchstaben, keine Leerzeichen
- `.sql` Extension
**Beispiele:**
```
✅ 001_subscription_system.sql
✅ 002_fix_features.sql
✅ 003_add_email_verification.sql
❌ v9c_subscription_system.sql # Keine Nummer
❌ check_features.sql # Keine Nummer
❌ 1_feature.sql # Nur eine Ziffer
❌ 001-feature.sql # Bindestrich statt Unterstrich
```
### Datei-Struktur
Jede Migration sollte folgende Struktur haben:
```sql
-- ================================================================
-- Migration XXX: Beschreibung
-- Version: vX.X
-- Date: YYYY-MM-DD
-- ================================================================
-- Beschreibung der Änderung
ALTER TABLE table_name ...
-- Weitere SQL-Statements
-- Kommentare für Dokumentation
COMMENT ON COLUMN table.column IS 'Beschreibung';
```
### SQL-Einschränkungen
**Erlaubt:**
- Standard SQL DDL (CREATE, ALTER, DROP)
- Standard SQL DML (INSERT, UPDATE, DELETE)
- CREATE INDEX, CREATE FUNCTION, etc.
- Multi-Statement-Scripts (durch `;` getrennt)
**Nicht erlaubt:**
- psql Meta-Kommandos (`\echo`, `\set`, `\connect`, etc.)
- Interactive Commands (`\i`, `\include`)
- Transaktions-Kontrolle (automatisch gehandhabt)
## Anwendung
### Automatisch (Production/Dev)
Migrationen werden **automatisch** beim Container-Start angewendet:
```bash
# Container startet
docker compose up -d
# db_init.py wird ausgeführt:
# 1. PostgreSQL ready check
# 2. Schema initialisiert (falls nötig)
# 3. Migrationen ausgeführt
# 4. SQLite-Migration (falls nötig)
```
**Log-Output:**
```
═══════════════════════════════════════════════════════════
MITAI JINKENDO - Database Initialization (v9c)
═══════════════════════════════════════════════════════════
Checking PostgreSQL connection...
✓ PostgreSQL ready
Checking database schema...
✓ Schema already exists
Running database migrations...
Found 2 pending migration(s)...
✓ Applied: 001_subscription_system.sql
✓ Applied: 003_add_email_verification.sql
✓ Database initialization complete
```
### Manuell (Entwicklung)
Für Testing während der Entwicklung:
```bash
# Im laufenden Container
docker exec -it dev-mitai-api python3 /app/db_init.py
# Oder direkt mit psql (für einzelne Migrationen)
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
-f /path/to/migration.sql
```
## Migration erstellen
### Schritt 1: Datei erstellen
```bash
# Nächste freie Nummer ermitteln
ls backend/migrations/ | grep -E '^\d{3}_' | sort | tail -1
# → 003_add_email_verification.sql
# Neue Migration mit Nummer 004
touch backend/migrations/004_add_new_feature.sql
```
### Schritt 2: SQL schreiben
```sql
-- ================================================================
-- Migration 004: Add New Feature
-- Version: v9d
-- Date: 2026-03-22
-- ================================================================
-- Add new column
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS new_feature_enabled BOOLEAN DEFAULT TRUE;
-- Create index if needed
CREATE INDEX IF NOT EXISTS idx_profiles_new_feature
ON profiles(new_feature_enabled)
WHERE new_feature_enabled = TRUE;
-- Update existing data if needed
UPDATE profiles
SET new_feature_enabled = TRUE
WHERE tier = 'premium';
COMMENT ON COLUMN profiles.new_feature_enabled IS 'Feature flag for new feature';
```
### Schritt 3: Testen
```bash
# Lokal testen (Dev-Container)
git add backend/migrations/004_add_new_feature.sql
git commit -m "feat: add migration for new feature"
git push origin develop
# Container wird neu gebaut und Migration automatisch angewendet
```
### Schritt 4: Verifizieren
```bash
# Prüfen ob Migration angewendet wurde
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
-c "SELECT * FROM schema_migrations ORDER BY applied_at DESC LIMIT 5;"
# Output:
# id | filename | applied_at
# ---+------------------------------+------------------------
# 4 | 004_add_new_feature.sql | 2026-03-22 10:30:15+00
# 3 | 003_add_email_verification.sql| 2026-03-21 15:20:10+00
# 2 | 002_fix_features.sql | 2026-03-20 12:10:05+00
# 1 | 001_subscription_system.sql | 2026-03-20 12:10:00+00
```
## Rollback-Strategie
**Automatischer Rollback:** Nicht implementiert
**Manueller Rollback:**
1. **Identifizieren der problematischen Migration:**
```bash
docker logs dev-mitai-api | grep "Failed to apply"
```
2. **Migration aus Tracking entfernen:**
```sql
DELETE FROM schema_migrations
WHERE filename = '004_broken_migration.sql';
```
3. **Änderungen manuell rückgängig machen:**
```sql
-- Beispiel: Spalte entfernen
ALTER TABLE profiles DROP COLUMN new_feature_enabled;
```
4. **Migration-Datei korrigieren**
```bash
vim backend/migrations/004_broken_migration.sql
```
5. **Container neu starten** (Migration wird erneut ausgeführt)
```bash
docker compose restart api
```
## Best Practices
### ✅ DO
- **Immer `IF NOT EXISTS` / `IF EXISTS` verwenden:**
```sql
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS email_verified BOOLEAN;
CREATE INDEX IF NOT EXISTS idx_profiles_email ON profiles(email);
```
- **Idempotente Migrationen schreiben:** Migration kann mehrfach ausgeführt werden ohne Fehler
- **Daten-Migrationen mit Bedacht:**
```sql
UPDATE profiles
SET email_verified = TRUE
WHERE email IS NOT NULL AND email_verified IS NULL;
```
- **Kommentare für Dokumentation:**
```sql
COMMENT ON COLUMN profiles.email_verified IS 'Whether email has been verified';
```
- **Indices für Performance:**
```sql
CREATE INDEX IF NOT EXISTS idx_profiles_verification_token
ON profiles(verification_token)
WHERE verification_token IS NOT NULL; -- Partial index
```
### ❌ DON'T
- **Keine psql Meta-Kommandos:**
```sql
\echo "Starting migration" -- ❌ Funktioniert nicht
SELECT 'Starting migration'; -- ✅ Funktioniert
```
- **Keine Breaking Changes ohne Staging:**
```sql
ALTER TABLE profiles DROP COLUMN tier; -- ❌ App wird brechen
```
- **Keine Hardcoded Values für produktive Daten:**
```sql
INSERT INTO profiles (id, email, ...) VALUES (1, 'admin@example.com', ...); -- ❌
```
- **Keine Foreign Keys ohne Fallback:**
```sql
-- ❌ Ohne ON DELETE Behavior
ALTER TABLE subscriptions
ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id) REFERENCES profiles(id);
-- ✅ Mit Cascade
ALTER TABLE subscriptions
ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id)
REFERENCES profiles(id) ON DELETE CASCADE;
```
## Troubleshooting
### Migration schlägt fehl
**Symptom:** Container startet nicht, Logs zeigen `✗ Failed to apply XXX.sql`
**Lösung:**
1. Logs prüfen: `docker logs dev-mitai-api | tail -50`
2. Fehlerhafte Migration identifizieren
3. Aus Tracking entfernen + Schema manuell korrigieren
4. Migration-Datei fixen
5. Container neu starten
### Migration wurde nicht angewendet
**Symptom:** Neue Migration-Datei wird ignoriert
**Ursachen:**
- Datei entspricht nicht dem Pattern `\d{3}_*.sql`
- Datei wurde bereits in `schema_migrations` getrackt
- Datei ist nicht im Container (`backend/migrations/` Volume gemountet?)
**Lösung:**
```bash
# Prüfen ob Datei gemountet ist
docker exec -it dev-mitai-api ls -la /app/migrations/
# Prüfen ob bereits getrackt
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
-c "SELECT * FROM schema_migrations WHERE filename = '004_feature.sql';"
# Falls fälschlich getrackt, entfernen
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
-c "DELETE FROM schema_migrations WHERE filename = '004_feature.sql';"
# Container neu starten
docker compose restart api
```
### Datenbank-Schema inkonsistent
**Symptom:** `schema_migrations` zeigt Migration als angewendet, aber Änderungen fehlen
**Ursachen:**
- Migration wurde manuell ausgeführt, aber Tracking war kaputt
- Datenbank wurde zurückgesetzt, aber Tracking-Tabelle nicht
**Lösung:**
```bash
# Schema neu aufbauen (ACHTUNG: Datenverlust!)
docker compose down -v # Löscht Volumes
docker compose up -d # Baut alles neu auf
# ODER: Tracking-Tabelle manuell korrigieren
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
-c "TRUNCATE schema_migrations; -- Alle Tracking-Einträge löschen"
# Container neu starten → Alle Migrationen werden erneut angewendet
docker compose restart api
```
## Migrations-Historie
| Nr. | Datei | Version | Datum | Beschreibung |
|-----|-------|---------|-------|--------------|
| 003 | `003_add_email_verification.sql` | v9c | 2026-03-21 | Email-Verifizierung (verification_token, email_verified, verification_expires) |
| 002 | `002_fix_features.sql` (manuell) | v9c | 2026-03-20 | Feature-System Fixes |
| 001 | `001_subscription_system.sql` (manuell) | v9c | 2026-03-20 | Membership-System (tiers, subscriptions, coupons, access_grants) |
**Hinweis:** Migrationen 001 und 002 wurden vor Einführung des automatischen Systems manuell angewendet und sind nicht nummeriert. Ab Migration 003 läuft alles automatisch.
---
## Referenzen
- **Code:** `backend/db_init.py` (Zeilen 94-200)
- **Migrations-Ordner:** `backend/migrations/`
- **Tracking-Tabelle:** `schema_migrations`
- **Startup-Script:** `backend/startup.sh` (ruft `db_init.py` auf)
- **Dokumentation:** `.claude/docs/technical/MIGRATIONS.md` (diese Datei)
---
**Dokumentiert:** 2026-03-21
**Letzte Änderung:** 2026-03-21

View File

@ -0,0 +1,799 @@
# Placeholder Development Guide
**Version:** 1.0
**Erstellt:** 28. März 2026
**Zielgruppe:** Entwickler, Claude Code
---
## Überblick
Dieses Dokument beschreibt, wie neue KI-Platzhalter hinzugefügt, getestet und dokumentiert werden.
**Wichtig für Phase 0c:** Nach dem Refactoring zu Multi-Layer Architecture nutzen alle Platzhalter das Data Layer. Dieser Guide beschreibt beide Architekturen.
---
## Phase 0b Architektur (Aktuell - bis Phase 0c)
### Anatomie eines Platzhalters
```python
# backend/placeholder_resolver.py
def resolve_weight_28d_trend_slope(profile_id: str) -> str:
"""
Returns kg/week slope for 28-day weight trend.
This function:
1. Retrieves data from database
2. Performs calculation
3. Formats result for KI consumption
Args:
profile_id: User profile ID
Returns:
Formatted string (e.g., "0.23 kg/Woche")
or "Nicht genug Daten" if insufficient data
"""
with get_db() as conn:
cur = get_cursor(conn)
# 1. DATA RETRIEVAL
cur.execute("""
SELECT date, weight
FROM weight_log
WHERE profile_id = %s
AND date >= NOW() - INTERVAL '28 days'
ORDER BY date
""", (profile_id,))
rows = cur.fetchall()
# 2. VALIDATION
if len(rows) < 18: # Confidence threshold
return "Nicht genug Daten"
# 3. CALCULATION
x = [(row[0] - rows[0][0]).days for row in rows]
y = [row[1] for row in rows]
# Linear regression
n = len(x)
sum_x = sum(x)
sum_y = sum(y)
sum_xy = sum(xi * yi for xi, yi in zip(x, y))
sum_x2 = sum(xi ** 2 for xi in x)
slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x ** 2)
slope_per_week = slope * 7
# 4. FORMATTING
return f"{slope_per_week:.2f} kg/Woche"
```
### Schritte zum Hinzufügen eines neuen Platzhalters (Phase 0b)
#### 1. Funktion implementieren
**Datei:** `backend/placeholder_resolver.py`
**Namenskonvention:**
- `resolve_<placeholder_name>(profile_id: str) -> str`
- Snake_case
- Immer `profile_id` als Parameter
- Immer `str` als Return-Type
**Template:**
```python
def resolve_my_new_metric(profile_id: str) -> str:
"""
[Beschreibung was der Platzhalter zurückgibt]
Args:
profile_id: User profile ID
Returns:
[Beschreibung des Return-Formats]
"""
with get_db() as conn:
cur = get_cursor(conn)
# 1. DATA RETRIEVAL
cur.execute("""
SELECT ...
FROM ...
WHERE profile_id = %s
""", (profile_id,))
# 2. VALIDATION
if <insufficient_data_condition>:
return "Nicht genug Daten"
# 3. CALCULATION
result = ...
# 4. FORMATTING
return f"{result}"
```
#### 2. In Mapping registrieren
**Datei:** `backend/placeholder_resolver.py`
**Finde `PLACEHOLDER_FUNCTIONS` Dictionary:**
```python
PLACEHOLDER_FUNCTIONS = {
# ... existing placeholders ...
# Add your new placeholder:
"my_new_metric": resolve_my_new_metric,
}
```
**Naming:**
- Key = Platzhalter-Name (snake_case)
- Value = Funktions-Referenz (ohne Klammern!)
#### 3. In Katalog dokumentieren
**Datei:** `backend/placeholder_resolver.py`
**Finde `get_placeholder_catalog()` Funktion:**
```python
def get_placeholder_catalog(profile_id: str) -> Dict[str, List[Dict[str, str]]]:
placeholders = {
'Körper': [
# ... existing ...
('my_new_metric', 'Beschreibung des Platzhalters'),
],
# ...
}
```
**Kategorien:**
- `Profil`
- `Körper`
- `Ernährung`
- `Training`
- `Schlaf & Erholung`
- `Vitalwerte`
- `Scores (Phase 0b)`
- `Focus Areas`
- `Zeitraum`
#### 4. Testen
**Manueller Test:**
```python
# In Python REPL oder test script:
from placeholder_resolver import resolve_my_new_metric
result = resolve_my_new_metric("test_profile_id")
print(result) # Should return formatted string
```
**Integration Test:**
```python
# Test in actual prompt
from placeholder_resolver import resolve_placeholders
template = "Dein {{my_new_metric}} ist ..."
result = resolve_placeholders(template, "test_profile_id")
print(result) # Should have placeholder replaced
```
---
## Phase 0c Architektur (Nach Refactoring)
### Anatomie eines Platzhalters (3-Layer)
```python
# Layer 1: DATA LAYER
# backend/data_layer/body_metrics.py
def get_weight_trend_data(profile_id: str, days: int = 90) -> dict:
"""
Returns weight trend data with slopes and projections.
This is pure data retrieval and calculation.
NO FORMATTING. NO STRINGS.
Args:
profile_id: User profile ID
days: Analysis window
Returns:
{
"raw_values": [(date, weight), ...],
"rolling_median_7d": [(date, value), ...],
"slope_7d": float,
"slope_28d": float,
"slope_90d": float,
"confidence": str,
...
}
"""
with get_db() as conn:
cur = get_cursor(conn)
# DATA RETRIEVAL
cur.execute("""...""", (profile_id, days))
rows = cur.fetchall()
# VALIDATION + CONFIDENCE
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(len(rows), days, "trend")
if confidence == 'insufficient':
return {
"confidence": "insufficient",
"slope_28d": 0.0,
# ... minimal data
}
# CALCULATION
# ... (same logic as before)
# RETURN STRUCTURED DATA (not formatted!)
return {
"raw_values": rows,
"slope_7d": slope_7d,
"slope_28d": slope_28d,
"confidence": confidence,
# ... all data as dict/list/float
}
# Layer 2a: KI LAYER
# backend/placeholder_resolver.py
from data_layer.body_metrics import get_weight_trend_data
def resolve_weight_28d_trend_slope(profile_id: str) -> str:
"""
Formats weight trend slope for KI consumption.
This function is now THIN - just calls data layer and formats.
"""
data = get_weight_trend_data(profile_id, days=28)
if data['confidence'] == 'insufficient':
return "Nicht genug Daten"
return f"{data['slope_28d']:.2f} kg/Woche"
```
### Schritte zum Hinzufügen eines neuen Platzhalters (Phase 0c)
#### 1. Data Layer Funktion implementieren
**Datei:** Passendes Modul in `backend/data_layer/`
- Body metrics → `body_metrics.py`
- Nutrition → `nutrition_metrics.py`
- Activity → `activity_metrics.py`
- Recovery → `recovery_metrics.py`
- Health → `health_metrics.py`
- Goals → `goals.py`
- Correlations → `correlations.py`
**Template:**
```python
# backend/data_layer/<module>.py
def get_<metric>_data(
profile_id: str,
days: int = 28,
**kwargs
) -> dict:
"""
[Beschreibung der Daten]
Args:
profile_id: User profile ID
days: Analysis window
**kwargs: Additional parameters
Returns:
{
"<field>": <value>,
"confidence": str, # ALWAYS include!
"data_points": int, # ALWAYS include!
}
"""
with get_db() as conn:
cur = get_cursor(conn)
# 1. DATA RETRIEVAL
cur.execute("""...""", (profile_id,))
rows = cur.fetchall()
# 2. CONFIDENCE CALCULATION
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(
len(rows),
days,
"general" # or "correlation" or "trend"
)
# 3. VALIDATION
if confidence == 'insufficient':
return {
"confidence": "insufficient",
"data_points": len(rows),
# Return minimal safe data
}
# 4. CALCULATION
# ... your logic here ...
# 5. RETURN STRUCTURED DATA
return {
# All data as primitives: dict, list, float, int, str, bool
# NO FORMATTING (no "0.23 kg/Woche" - just 0.23)
"result": result_value,
"confidence": confidence,
"data_points": len(rows),
}
```
**WICHTIG:**
- ❌ Keine Strings mit Einheiten: `"0.23 kg/Woche"`
- ✅ Nur Zahlen: `0.23`
- ❌ Keine Formatierung für Menschen
- ✅ Strukturierte Daten für Maschinen
#### 2. KI Layer Wrapper erstellen
**Datei:** `backend/placeholder_resolver.py`
```python
from data_layer.<module> import get_<metric>_data
def resolve_<placeholder_name>(profile_id: str) -> str:
"""
[Beschreibung was zurückgegeben wird]
Phase 0c: Uses data_layer.<module>.get_<metric>_data()
"""
data = get_<metric>_data(profile_id)
if data['confidence'] == 'insufficient':
return "Nicht genug Daten"
# FORMAT for KI consumption
return f"{data['<field>']:.2f} <unit>"
```
#### 3. In Mapping registrieren
**UNVERÄNDERT - gleich wie Phase 0b:**
```python
PLACEHOLDER_FUNCTIONS = {
"my_new_metric": resolve_my_new_metric,
}
```
#### 4. In Katalog dokumentieren
**UNVERÄNDERT - gleich wie Phase 0b:**
```python
def get_placeholder_catalog(profile_id: str):
placeholders = {
'Körper': [
('my_new_metric', 'Beschreibung'),
],
}
```
#### 5. Testen
**Unit Test für Data Layer:**
```python
# backend/tests/test_data_layer.py
def test_get_metric_data_sufficient():
data = get_<metric>_data("test_profile_1", days=28)
assert data['confidence'] in ['high', 'medium', 'low', 'insufficient']
assert 'data_points' in data
assert isinstance(data['<field>'], float)
def test_get_metric_data_insufficient():
data = get_<metric>_data("profile_no_data", days=28)
assert data['confidence'] == 'insufficient'
```
**Integration Test für KI Layer:**
```python
# backend/tests/test_placeholders.py
def test_resolve_placeholder():
result = resolve_<placeholder_name>("test_profile_1")
assert isinstance(result, str)
assert result != "Nicht genug Daten"
```
---
## Best Practices
### 1. Confidence Scoring
**IMMER `calculate_confidence()` verwenden:**
```python
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(
data_points=len(rows),
days_requested=days,
metric_type="general" # or "correlation" or "trend"
)
```
**Confidence Thresholds:**
- General (28d): high >= 18, medium >= 12, low >= 8
- Correlation: high >= 28, medium >= 21, low >= 14
- Trend: high >= (days * 0.7), medium >= (days * 0.5)
### 2. Decimal → Float Conversion
**PostgreSQL gibt Decimal zurück - immer zu float konvertieren:**
```python
# ❌ WRONG:
value = row['column']
# ✅ CORRECT:
value = float(row['column']) if row['column'] else 0.0
```
### 3. Safe Dict Access
**Nie direkter Key-Zugriff ohne Fallback:**
```python
# ❌ WRONG:
value = data['key'] # KeyError if missing
# ✅ CORRECT:
value = data.get('key', default_value)
```
### 4. Date Serialization
**Python date objects sind nicht JSON-serializable:**
```python
from data_layer.utils import serialize_dates
data = {
"date": date(2026, 3, 28),
"values": [...]
}
# Serialize before returning from API
return serialize_dates(data)
```
### 5. SQL Parameter Binding
**IMMER Parameter-Binding, NIE String-Concatenation:**
```python
# ✅ CORRECT:
cur.execute("SELECT * FROM t WHERE id = %s", (id,))
# ❌ WRONG (SQL Injection Risk):
cur.execute(f"SELECT * FROM t WHERE id = {id}")
```
### 6. Column Name Consistency
**Prüfe Schema BEVOR du Column-Namen verwendest:**
```python
# ❌ WRONG (assumed name):
SELECT bf_jpl FROM caliper_log
# ✅ CORRECT (check schema first):
SELECT body_fat_pct FROM caliper_log
```
**Schema prüfen:**
```sql
\d caliper_log -- in psql
-- oder
SELECT column_name FROM information_schema.columns
WHERE table_name = 'caliper_log';
```
---
## Fehler-Handling
### 1. Insufficient Data
**Return-Value bei zu wenig Daten:**
```python
# Data Layer:
return {
"confidence": "insufficient",
"data_points": 0,
# Alle anderen Felder mit safe defaults (0.0, [], etc.)
}
# KI Layer:
if data['confidence'] == 'insufficient':
return "Nicht genug Daten"
```
### 2. Missing Optional Data
**Wenn optionale Daten fehlen (z.B. keine Vitals):**
```python
# Data Layer:
return {
"hrv": None, # or 0.0, depending on semantic
"confidence": "low", # downgrade confidence
}
# KI Layer:
if data['hrv'] is None:
return "Keine HRV-Daten verfügbar"
```
### 3. Calculation Errors
**Bei Math-Errors (Division by Zero, etc.):**
```python
try:
result = numerator / denominator
except ZeroDivisionError:
result = 0.0 # or None, depending on semantic
```
---
## Dokumentations-Pflicht
### 1. Docstring
**Jede Funktion braucht Docstring:**
```python
def get_metric_data(profile_id: str, days: int = 28) -> dict:
"""
[Eine Zeile Zusammenfassung]
[Ausführliche Beschreibung wenn nötig]
Args:
profile_id: User profile ID
days: Analysis window (default 28)
Returns:
{
"field": value,
"confidence": str,
"data_points": int
}
Confidence Rules:
- high: >= X points
- medium: >= Y points
- low: >= Z points
- insufficient: < Z points
"""
```
### 2. Inline Comments
**Nur bei nicht-offensichtlicher Logik:**
```python
# Calculate trimmed mean (remove top/bottom 10%)
sorted_values = sorted(values)
trim_count = len(values) // 10
trimmed = sorted_values[trim_count:-trim_count]
result = sum(trimmed) / len(trimmed)
```
### 3. Type Hints
**IMMER Type Hints verwenden:**
```python
from typing import Optional, List, Dict, Tuple
def get_data(
profile_id: str,
days: int = 28,
include_raw: bool = False
) -> Dict[str, any]:
...
```
---
## Testing-Strategie
### 1. Unit Tests (Data Layer)
**Teste jede Data Layer Funktion isoliert:**
```python
# backend/tests/test_data_layer.py
import pytest
from data_layer.body_metrics import get_weight_trend_data
@pytest.fixture
def test_profile():
# Setup test data in database
...
yield profile_id
# Teardown
...
def test_weight_trend_sufficient_data(test_profile):
data = get_weight_trend_data(test_profile, days=28)
assert data['confidence'] in ['high', 'medium']
assert data['slope_28d'] != 0.0
assert len(data['raw_values']) >= 18
def test_weight_trend_insufficient_data():
data = get_weight_trend_data("no_data_profile", days=28)
assert data['confidence'] == 'insufficient'
```
### 2. Integration Tests (KI Layer)
**Teste Placeholder-Resolution:**
```python
# backend/tests/test_placeholders.py
def test_placeholder_resolution(test_profile):
result = resolve_weight_28d_trend_slope(test_profile)
assert isinstance(result, str)
assert "kg/Woche" in result or "Nicht genug Daten" in result
def test_placeholder_in_template(test_profile):
template = "Trend: {{weight_28d_trend_slope}}"
result = resolve_placeholders(template, test_profile)
assert "{{" not in result # All placeholders resolved
assert result.startswith("Trend:")
```
### 3. Manual Testing Checklist
```
[ ] Funktion mit verschiedenen days-Parametern testen
[ ] Mit vollständigen Daten testen
[ ] Mit unvollständigen Daten testen
[ ] Mit NO DATA testen
[ ] Edge Cases: Extreme Werte, Outliers
[ ] Performance: < 500ms für typische Queries
[ ] Memory: Kein Leak bei großen Datasets
```
---
## Checkliste: Neuer Platzhalter
### Phase 0b (Aktuell):
```
[ ] Funktion in placeholder_resolver.py implementiert
[ ] resolve_<name>(profile_id: str) -> str Signatur
[ ] Docstring vollständig
[ ] Confidence-Check implementiert
[ ] In PLACEHOLDER_FUNCTIONS registriert
[ ] In get_placeholder_catalog() dokumentiert
[ ] Manuell getestet
[ ] In echtem Prompt getestet
```
### Phase 0c (Nach Refactoring):
```
[ ] Data Layer Funktion implementiert
[ ] Richtiges Modul gewählt
[ ] get_<metric>_data(profile_id, ...) -> dict Signatur
[ ] Returns structured data (dict/list/primitives)
[ ] NO formatting, NO strings with units
[ ] Confidence calculation included
[ ] Docstring vollständig
[ ] KI Layer Wrapper implementiert
[ ] resolve_<name>(profile_id: str) -> str Signatur
[ ] Calls data_layer function
[ ] Formats result for KI
[ ] In PLACEHOLDER_FUNCTIONS registriert
[ ] In get_placeholder_catalog() dokumentiert
[ ] Unit Test für Data Layer geschrieben
[ ] Integration Test für KI Layer geschrieben
[ ] Manual Testing durchgeführt
```
---
## Häufige Fehler (Learnings from Phase 0b)
### 1. Vergessen float() Conversion
```python
# SYMPTOM: "Object of type Decimal is not JSON serializable"
# FIX:
value = float(row['column']) if row['column'] else 0.0
```
### 2. Hardcoded Column Names
```python
# SYMPTOM: "column bf_jpl does not exist"
# FIX: Check schema first
SELECT column_name FROM information_schema.columns
WHERE table_name = 'caliper_log';
```
### 3. KeyError bei fehlenden Daten
```python
# SYMPTOM: "KeyError: 'hrv'"
# FIX: Use .get() with default
hrv = data.get('hrv', 0.0)
```
### 4. Confidence nicht berechnet
```python
# SYMPTOM: Platzhalter liefert Daten bei <3 Punkten
# FIX: calculate_confidence() verwenden
from data_layer.utils import calculate_confidence
confidence = calculate_confidence(len(rows), days, "general")
```
### 5. Date nicht serialized
```python
# SYMPTOM: "Object of type date is not JSON serializable"
# FIX:
from data_layer.utils import serialize_dates
return serialize_dates(data)
```
### 6. SQL Injection Risk
```python
# SYMPTOM: Security Scanner warnt
# FIX: ALWAYS use parameter binding
cur.execute("SELECT * FROM t WHERE id = %s", (id,))
```
---
## Nächste Schritte
### Nach Implementierung eines neuen Platzhalters:
1. **Commit Message:**
```
feat: add {{my_new_metric}} placeholder
- Implements resolve_my_new_metric() in placeholder_resolver.py
- Adds entry to PLACEHOLDER_FUNCTIONS
- Documents in get_placeholder_catalog()
- Tested with profile XYZ
Category: <Körper/Ernährung/Training/etc.>
Returns: <description>
```
2. **Dokumentation aktualisieren:**
- `CLAUDE.md` - Neue Platzhalter auflisten
- `docs/api/PLACEHOLDERS.md` - API-Dokumentation
3. **Testing:**
- Mindestens 1 manueller Test mit echtem Profil
- Optional: Unit Test hinzufügen
4. **Review:**
- Prüfe ob Platzhalter in Prompt-Bibliothek sinnvoll
- Teste mit verschiedenen Prompts
- Performance-Check (< 500ms)
---
**Autor:** Claude Sonnet 4.5
**Version:** 1.0
**Letzte Aktualisierung:** 28. März 2026

View File

@ -0,0 +1,504 @@
# Placeholder Registry Framework - Verbindliche Dokumentation
**Status:** VERBINDLICH (ab 2026-04-02)
**Version:** 1.0
**Geltungsbereich:** Alle Placeholder/Metrics im System
---
## 1. Zweck
Das **Placeholder Registry Framework** ist die zentrale, verbindliche Metadaten-Verwaltung für alle Placeholder und Metrics im System.
**Kernprinzip:** Single Source of Truth
**Ziele:**
1. Einheitliche Metadaten-Struktur für alle Placeholder
2. Vermeidung von Duplikation und Inkonsistenzen
3. Zentrale Verwaltung für alle Konsumenten
4. Evidence-basierte Transparenz
5. Erweiterbarkeit und Wartbarkeit
---
## 2. Verbindlichkeit
### 2.1 Pflicht zur Nutzung
**ALLE neuen Placeholder/Metrics MÜSSEN über das Registry Framework registriert werden.**
Keine Ausnahmen ohne explizite technische Begründung und Freigabe.
### 2.2 Betroffene Systeme
Folgende Systeme MÜSSEN die Registry als Single Source of Truth nutzen:
1. **Backend Prompt-Injektion** (Layer 2a)
- Placeholder-Resolver
- Prompt-Template-Engine
2. **GUI Auswahllisten**
- Placeholder-Picker
- Kategorie-Filter
- Metadata-Anzeige
3. **Extended Export**
- `/api/prompts/placeholders/export-values-extended`
- Catalog-Generierung
- ZIP-Export
4. **Validierung** (zukünftig)
- Metadata-Completeness-Checks
- Evidence-Quality-Assurance
- Compliance-Reports
5. **Diagramm-Zuordnung** (zukünftig)
- Chart-Metadata-Mapping
- Layer-2b-Integration
### 2.3 Verbotene Praktiken
**VERBOTEN:**
- Hardcoded Metadaten außerhalb der Registry
- Duplizierte Metadaten-Definitionen
- Placeholder ohne Registry-Registrierung
- Direkte Manipulation von Metadaten im Export-Code
- Inkonsistente Metadaten zwischen Systemen
---
## 3. Framework-Architektur
### 3.1 Kernkomponenten
**Modul:** `backend/placeholder_registry.py`
**Klassen:**
- `PlaceholderMetadata` - Metadata-Dataclass (22 Pflichtfelder)
- `MissingValuePolicy` - Strukturierte Missing-Value-Behandlung
- `PlaceholderRegistry` - Zentrale Registry (Singleton)
- `EvidenceType` - Enum für Evidenz-Tagging
- `OutputType` - Enum für Output-Typen
- `PlaceholderType` - Enum für Placeholder-Typen
**Singleton-Instanz:**
```python
from placeholder_registry import get_registry
registry = get_registry()
```
### 3.2 Registrierungs-Package
**Package:** `backend/placeholder_registrations/`
**Struktur:**
```
placeholder_registrations/
├── __init__.py # Auto-Import aller Registrations
├── nutrition_part_a.py # Nutrition Basis-Metriken (4 Placeholder)
├── nutrition_part_b.py # Protein-Ziele (5 Placeholder) - TODO
├── body_metrics.py # Körper-Metriken - TODO
├── activity_metrics.py # Aktivitäts-Metriken - TODO
└── ... # Weitere Cluster
```
**Auto-Registration:**
- Import des Package triggert automatische Registrierung aller Placeholder
- Keine manuelle Registrierung erforderlich
### 3.3 Export-Integration
**Modul:** `backend/placeholder_registry_export.py`
**Funktionen:**
- `get_registry_metadata_for_export()` - Metadata aus Registry
- `merge_registry_with_legacy_export()` - Backward-Compatibility
- `get_enhanced_export_with_registry()` - Vollständiger Export
**Endpoint-Integration:**
```python
# backend/routers/prompts.py
import placeholder_registrations # Auto-registers
from placeholder_registry_export import get_registry_metadata_for_export
registry_data = get_registry_metadata_for_export(profile_id)
export_data['registry_metadata'] = registry_data
```
---
## 4. Metadata-Schema
### 4.1 Pflichtfelder (22 Felder)
**Core Identification (3):**
- `key` - Placeholder-Schlüssel (z.B. "kcal_avg")
- `category` - Kategorie (z.B. "Ernährung")
- `description` - Kurzbeschreibung
**Technical (6):**
- `resolver_module` - Modul-Pfad des Resolvers
- `resolver_function` - Funktionsname des Resolvers
- `data_layer_module` - Data Layer Modul (optional)
- `data_layer_function` - Data Layer Funktion (optional)
- `source_tables` - Liste der Quelltabellen
- `_resolver_func` - Runtime-Resolver (nicht exportiert)
**Semantic (8):**
- `semantic_contract` - Semantische Definition
- `business_meaning` - Fachliche Bedeutung
- `unit` - Einheit (z.B. "kcal/day")
- `time_window` - Zeitfenster (z.B. "30d")
- `output_type` - Output-Typ (Enum)
- `placeholder_type` - Placeholder-Typ (Enum)
- `format_hint` - Format-Information
- `example_output` - Beispiel-Ausgabe
**Quality (5):**
- `minimum_data_requirements` - Mindestanforderungen (optional)
- `quality_filter_policy` - Qualitätsfilter (optional)
- `confidence_logic` - Confidence-Berechnung (optional)
- `missing_value_policy` - Missing-Value-Handling
- `known_limitations` - Bekannte Einschränkungen (optional)
**Architecture (5):**
- `layer_1_decision` - Layer-1-Zuordnung (optional)
- `layer_2a_decision` - Layer-2a-Zuordnung (optional)
- `layer_2b_reuse_possible` - Chart-Reuse möglich (optional)
- `architecture_alignment` - Architektur-Konformität (optional)
- `issue_53_alignment` - Issue #53 Konformität (optional)
**Evidence Tracking (1):**
- `evidence` - Dict mapping Feldname → EvidenceType
### 4.2 Evidence-Typen
**Enum:** `EvidenceType`
**Werte:**
- `CODE_DERIVED` - Aus Code belegt (z.B. aus Import-Statement, SQL-Query)
- `DRAFT_DERIVED` - Aus Canonical Requirements Draft übernommen
- `MIXED` - Teilweise Code, teilweise Draft/abgeleitet
- `UNRESOLVED` - Nicht explizit dokumentiert, offen
- `TO_VERIFY` - Behauptung, muss noch verifiziert werden
**Verwendung:**
```python
metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
```
---
## 5. Registrierungs-Workflow
### 5.1 Neuen Placeholder registrieren
**Schritt 1: Metadata-Objekt erstellen**
```python
# backend/placeholder_registrations/my_cluster.py
from placeholder_registry import (
PlaceholderMetadata,
MissingValuePolicy,
EvidenceType,
OutputType,
PlaceholderType,
register_placeholder
)
metadata = PlaceholderMetadata(
key="my_placeholder",
category="Meine Kategorie",
description="Kurzbeschreibung",
# Technical (CODE_DERIVED)
resolver_module="backend/placeholder_resolver.py",
resolver_function="get_my_placeholder",
data_layer_module="backend/data_layer/my_metrics.py",
data_layer_function="get_my_data",
source_tables=["my_table"],
# Semantic
semantic_contract="Was liefert dieser Placeholder?",
business_meaning="Fachliche Bedeutung",
unit="einheit",
time_window="30d",
output_type=OutputType.NUMERIC,
placeholder_type=PlaceholderType.INTERPRETED,
format_hint="Ganzzahl",
example_output="42",
# Quality
confidence_logic="Wie wird Verlässlichkeit berechnet?",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="insufficient_data",
legacy_display="nicht genug Daten"
),
known_limitations="Einschränkungen dokumentieren",
# Architecture
layer_1_decision="Data Layer (my_metrics.get_my_data)",
layer_2a_decision="Placeholder Resolver (formatting only)",
architecture_alignment="Phase 0c conform"
)
```
**Schritt 2: Evidence setzen**
```python
# Code-derived Felder
metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
# Draft-derived Felder
metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
metadata.set_evidence("known_limitations", EvidenceType.DRAFT_DERIVED)
# Unresolved Felder
metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
```
**Schritt 3: Registrieren**
```python
register_placeholder(metadata)
```
**Schritt 4: Auto-Import sicherstellen**
```python
# backend/placeholder_registrations/__init__.py
from . import my_cluster
__all__ = ['nutrition_part_a', 'my_cluster']
```
### 5.2 Resolver-Funktion bereitstellen (optional)
Wenn der Placeholder runtime-resolved werden soll:
```python
from placeholder_resolver import get_my_placeholder
register_placeholder(
metadata,
resolver_func=lambda pid: get_my_placeholder(pid)
)
```
---
## 6. API-Nutzung
### 6.1 Metadata abrufen
```python
from placeholder_registry import get_registry
registry = get_registry()
# Einzelner Placeholder
meta = registry.get("kcal_avg")
print(meta.unit) # "kcal/day"
print(meta.time_window) # "30d"
# Alle Placeholder
all_metadata = registry.get_all() # Dict[str, PlaceholderMetadata]
# Nach Kategorie
ernaehrung = registry.get_by_category("Ernährung") # List[PlaceholderMetadata]
```
### 6.2 Export
```python
# Für Extended Export
export_data = registry.get_all_for_export() # List[Dict]
# Mit Runtime-Werten
from placeholder_registry_export import get_registry_metadata_for_export
registry_data = get_registry_metadata_for_export(profile_id)
# Returns: {flat, by_category, evidence_report, validation_report}
```
### 6.3 Validierung
```python
# Alle Placeholder validieren
issues = registry.validate_all() # Dict[str, List[str]]
if issues:
for key, problems in issues.items():
print(f"{key}: {problems}")
```
### 6.4 QA / Evidence-Tracking
```python
# Placeholder mit unresolved Fields finden
unresolved = registry.get_by_evidence_type(EvidenceType.UNRESOLVED)
# Returns: Dict[placeholder_key, List[field_names]]
# Placeholder die verifiziert werden müssen
to_verify = registry.get_by_evidence_type(EvidenceType.TO_VERIFY)
```
---
## 7. Best Practices
### 7.1 Evidence-Tagging
**DO:**
- Jedes Feld mit Evidence-Tag versehen
- `CODE_DERIVED` nur wenn direkt aus Code ableitbar
- `TO_VERIFY` für Behauptungen, die noch geprüft werden müssen
- `UNRESOLVED` für fehlende/unklare Informationen
**DON'T:**
- Felder ohne Evidence lassen
- Evidence halluzinieren (wenn nicht belegt, `UNRESOLVED` nutzen)
- `CODE_DERIVED` für Draft-Informationen nutzen
### 7.2 Metadata-Vollständigkeit
**Minimum Required:**
- `key`, `category`, `description`
- `resolver_module`, `resolver_function`
- `semantic_contract`
- `unit`, `time_window`
- `output_type`, `placeholder_type`
**Optional but Recommended:**
- `data_layer_module`, `data_layer_function`
- `source_tables`
- `confidence_logic`, `missing_value_policy`
- `layer_1_decision`, `layer_2a_decision`
### 7.3 Modularisierung
**Registrations nach Cluster gruppieren:**
- `nutrition_part_a.py` - Nutrition Basis (4 Placeholder)
- `nutrition_part_b.py` - Nutrition Protein (5 Placeholder)
- `body_metrics.py` - Körper-Metriken (N Placeholder)
**Nicht:**
- Alle Placeholder in eine riesige Datei
- Placeholder ohne thematische Gruppierung
### 7.4 Backward-Compatibility
**Export-Endpoint MUSS:**
- Legacy-Export beibehalten (`export_data['legacy']`)
- Graceful degradation bei Registry-Fehler
- Registry-Metadata als separate Sektion (`export_data['registry_metadata']`)
---
## 8. Migration bestehender Placeholder
### 8.1 Priorität
**Part A (erledigt):** Nutrition Basis (kcal_avg, protein_avg, carb_avg, fat_avg)
**Nächste Priorität:**
1. Part B - Nutrition Protein (5 Placeholder)
2. Part C - Nutrition Balance (4 Placeholder)
3. Part D - Nutrition Meta (1 Placeholder)
4. Body Metrics (ca. 15 Placeholder)
5. Activity Metrics (ca. 20 Placeholder)
### 8.2 Migration-Workflow
**Für jeden Placeholder:**
1. Code inspizieren (Resolver, Data Layer, SQL)
2. Evidence ableiten (was ist code-derived, was draft-derived?)
3. Metadata-Objekt erstellen
4. Registrieren
5. Export testen
6. Werte-Identität bestätigen
**Keine Logikänderung während Migration!**
---
## 9. Compliance & Enforcement
### 9.1 Code-Review-Checkliste
**Für neue Placeholder:**
- [ ] Registry-Registrierung vorhanden?
- [ ] Evidence-Tags gesetzt?
- [ ] Metadata-Vollständigkeit (minimum required)?
- [ ] Auto-Import in `__init__.py`?
- [ ] Export getestet?
### 9.2 CI/CD-Integration (zukünftig)
**Geplante Checks:**
- Alle Placeholder in PLACEHOLDER_MAP sind in Registry registriert
- Keine Placeholder ohne Evidence-Tags
- Keine doppelten Registrierungen
- Metadata-Vollständigkeit für production-ready Placeholder
---
## 10. Support & Weiterentwicklung
### 10.1 Fragen & Issues
**Bei Unklarheiten:**
1. Diese Dokumentation prüfen
2. Bestehende Registrations als Vorlage nutzen (`nutrition_part_a.py`)
3. Code-Review anfragen
### 10.2 Framework-Erweiterungen
**Geplante Features:**
- GUI-Integration (Placeholder-Picker mit Registry-Metadata)
- Validation-Dashboard (QA-Monitoring)
- Evidence-Report-Endpoint (Metadata-Qualität)
- Resolver-Test-Framework (Automatisierte Werteänderungs-Detektion)
- Chart-Metadata-Mapping (Layer-2b-Integration)
### 10.3 Versions-History
**v1.0 (2026-04-02):**
- Initial Release
- Part A Implementation (4 Nutrition Placeholders)
- Core Framework + Export-Integration
---
## 11. Referenzen
**Code:**
- `backend/placeholder_registry.py` - Core Framework
- `backend/placeholder_registrations/nutrition_part_a.py` - Part A Implementation
- `backend/placeholder_registry_export.py` - Export-Integration
- `backend/routers/prompts.py` - Export-Endpoint
**Dokumentation:**
- `.claude/task/rework_0b_placeholder/NUTRITION_PART_A_CHANGE_PLAN.md`
- `.claude/task/rework_0b_placeholder/NUTRITION_PART_A_IMPLEMENTATION_REPORT.md`
**Beispiel-Export:**
```bash
curl "https://dev.mitai.jinkendo.de/api/prompts/placeholders/export-values-extended?token=XXX"
```
---
**Ende Verbindliche Dokumentation**

View File

@ -0,0 +1,53 @@
# Persönliche Referenzwerte (Profil)
## Überblick
Nutzer-spezifische, **historische** Kennwerte (z. B. HF-Schwellen, Trainingshäufigkeit), die **nicht** zur Admin-/Focus-Area-Konfiguration gehören, sondern zum **aktiven Profil** wie Größe oder Ziele.
## Tabellen
| Tabelle | Zweck |
|--------|--------|
| `reference_value_types` | System-seedete Typdefinitionen: stabiler `key`, Anzeige-`label`, optional `default_unit`, `sort_order`, `active`, `metadata` (JSONB). |
| `profile_reference_values` | Historische Einträge: `profile_id`, `reference_value_type_id`, `effective_date`, `value_numeric` und/oder `value_text`, `unit`, optionale Felder `source`, `confidence`, `method`, `notes`, `extra` (JSONB). |
**Kein** Überschreiben eines einzelnen „aktuellen“ Werts: jede Messung ist eine eigene Zeile.
## Seed-Typen (Migration 037)
- `max_heart_rate` (bpm)
- `resting_heart_rate` (bpm)
- `anaerobic_threshold_hr` (bpm)
- `aerobic_threshold_hr` (bpm)
- `training_frequency_weekly` (Sessions/Woche)
- `fitness_level` (Stufe)
Es werden **keine** Benutzerwerte automatisch angelegt.
## Admin (nur Rolle `admin`)
- `GET/POST/PUT/DELETE /api/admin/reference-value-types` — vollständiger CRUD auf `reference_value_types` (inkl. inaktiver Typen).
- Löschen nur, wenn keine Zeilen in `profile_reference_values` zu diesem Typ existieren (sonst HTTP 409).
- UI: **Admin → Ziele & Fokus → Referenz-Kennwerte** (`/admin/reference-value-types`).
## API (Prefix `/api`)
- `GET /reference-value-types` — aktive Typen (dynamische UI)
- `GET /profile-reference-values?type_key=…` — Liste pro Typ, neueste zuerst
- `POST /profile-reference-values` — neuer Eintrag
- `PUT /profile-reference-values/{id}` — Aktualisierung
- `DELETE /profile-reference-values/{id}` — Löschen
Authentifizierung wie üblich; Profil über `X-Profile-Id` / `get_pid` wie andere Module.
## UI
**Einstellungen → Karte „Referenzwerte“ → „Referenzwerte verwalten“** (`/settings/reference-values`).
Typauswahl per Dropdown (aus API); Verlauf als Tabelle mit Bearbeiten/Löschen; Formular für neue Einträge bzw. Bearbeitung.
## Erweiterbarkeit
- Neue Messgrößen: nur **INSERT** in `reference_value_types` (Migration oder Admin-Skript), kein Schema-Wechsel für Nutzerdaten.
- Zusatzmetadaten: Spalten `source`, `confidence`, `method`, `notes`, `extra` bereits vorhanden; UI kann später erweitert werden.
- Platzierung **profilorientiert**, damit klar ist: Nutzerdaten, keine systemweite Semantik wie Focus Areas.

View File

@ -0,0 +1,148 @@
# Training Profile Resolver (Layer 1) — technisches Scaffold
**Stand:** 2026-04-06
**Zweck:** Erweiterbare technische Basis für spätere trainingsprofil-basierte Auswertungen, ohne freie Formel-/Skript-Engine und ohne Kopplung an KI oder Charts.
---
## 1. Einordnung in Layer 1
| Aspekt | Beschreibung |
|--------|----------------|
| Ort | `backend/data_layer/training_profile/` |
| Rolle | Reine Orchestrierung: Templates → registrierte Built-in-Algorithmen → strukturiertes Ergebnis inkl. Focus-Area-Beiträge |
| Single Source of Truth | Berechnungslogik der Algorithmen lebt in Python-Modulen; Templates wählen nur **Algorithmus-ID + Parameter + Dimensionen + FA-Mapping** |
| Rückwärtskompatibel | Keine Änderungen an bestehenden Data-Layer-Metriken, Placeholdern oder Chart-Routern |
---
## 2. Modulübersicht
| Pfad | Inhalt |
|------|--------|
| `models.py` | `CalculationTemplate`, `DimensionSpec`, `FocusAreaMapping`, `TrainingBaseProfile`, `TrainingEvaluationResult`, `AlgorithmRunResult` |
| `resolver.py` | `resolve_training_evaluation()`, `resolve_for_base_profile()` |
| `algorithms/registry.py` | `register_algorithm`, `get_algorithm`, `list_algorithm_ids` |
| `algorithms/builtin/threshold_band.py` | Beispiel: Schwellen-Bänder → Score 01 |
| `algorithms/builtin/linear_range.py` | Beispiel: lineare Abbildung [min,max] → [0,1] |
| `templates/registry.py` | Beispiel-Templates (deklarativ, in-code) |
| `profiles/registry.py` | Beispiel-Trainings-Basisprofile (Default-Template, optionale Dimensions-Whitelist) |
---
## 3. Built-in-Algorithmen
- Algorithmen sind **fest im Code** implementiert und über eine **ID** referenzierbar.
- Neue Algorithmen: Funktion mit Signatur `(*, inputs, params) -> AlgorithmRunResult` und `register_algorithm(id, fn)` (Start-up-Registrierung in `registry.py` oder Import eines Moduls, das registriert).
- **Nicht** vorgesehen: Nutzerdefinierte Ausdrücke, DSL, `eval`, externe Skripte.
Implementiert (Beispiele):
- `threshold_band``params.value_key`, `params.bands` (Liste mit `max` / `score`)
- `linear_range``params.value_key`, `min_value`, `max_value`, optional `invert`
---
## 4. Templates (deklarativ)
Ein `CalculationTemplate` besteht aus:
- `id`, `version`, `label`
- `dimensions`: Liste von `DimensionSpec` mit:
- `key` — Dimensionsname
- `algorithm_id` — referenziert registrierten Algorithmus
- `inputs` — erwartete Schlüssel im flachen `activity_inputs`-Dict des Aufrufers
- `params` — JSON-serialisierbare Parameter für den Algorithmus
- `maps_to` — Tupel `(focus_area_key, weight)` — gewichteter Anteil der **normalisierten** Dimension am jeweiligen Focus Area
Aggregation: Pro Dimension wird `normalized_score * weight` pro Focus Area addiert (`focus_area_contributions`).
---
## 5. Trainings-Basisprofile (Scaffold)
`TrainingBaseProfile`:
- `key`, `label`
- `default_template_id` — verweist auf ein `CalculationTemplate`
- optional `allowed_dimension_keys` — nur diese Dimensionen aus dem Template werden ausgeführt (Filter)
Aktuell **nur In-Code-Registry**; später denkbar: DB-Verknüpfung Trainingstyp → Profil-Key.
---
## 6. Ergebnisstruktur (`TrainingEvaluationResult`)
- `template_id`, `template_version`, `base_profile_key`
- `dimension_results[]` — pro Dimension: Scores, fehlende Inputs, Evidence
- `focus_area_contributions``dict[str, float]` (Focus-Area-Key → aggregierter Beitrag)
- `confidence``high` | `medium` | `low` | `insufficient` (heuristisch aus fehlenden Pflicht-Inputs)
- `evidence` — Metadaten (z. B. Anzahl Dimensionen, Input-Keys)
- optional `trace` — bei `include_trace=True` für Debugging
`to_serializable()` liefert ein JSON-taugliches Dict für APIs/Persistenz.
---
## 7. Öffentliche API (Import)
```python
from data_layer.training_profile import (
resolve_training_evaluation,
resolve_for_base_profile,
TrainingEvaluationResult,
CalculationTemplate,
)
```
Registries:
```python
from data_layer.training_profile.templates.registry import get_calculation_template
from data_layer.training_profile.profiles.registry import get_training_base_profile
from data_layer.training_profile.algorithms.registry import get_algorithm, list_algorithm_ids
```
---
## 8. Erweiterungspunkte (für spätere Produktlogik)
1. **Neue Algorithmen** — neue Datei unter `algorithms/builtin/`, in `registry.py` registrieren.
2. **Templates** — weitere `CalculationTemplate`-Instanzen in `templates/registry.py` oder später aus DB laden (Loader baut dieselben Dataclasses).
3. **Profile** — weitere `TrainingBaseProfile`-Einträge; Anbindung an `training_types` / Aktivität.
4. **Confidence/Evidence** — feinere Regeln (Datenqualität, Mindestkriterien) im Resolver oder in den Algorithmen.
5. **Normalisierung der FA-Beiträge** — optional globale Skalierung/Cap (aktuell rein additive Gewichtung).
---
## 9. Was absichtlich offen ist
- Finale Domänenregeln (welche Dimensionen für welchen Sport)
- Vollständige Liste Basisprofile und Templates
- Persistenz von Evaluationsergebnissen
- Integration in Placeholder-Resolver, Charts, Admin-UI
- Validierung gegen `focus_area_definitions` (DB-Keys)
---
## 10. Kritische Einschätzung
| Bereich | Bewertung |
|---------|-----------|
| Registry + Algorithmus-Schnittstelle | Robust, testbar, erweiterbar |
| Template-Modelle (frozen dataclasses) | Stabil, typsicher, serialisierbar |
| Beispiel-Algorithmen | Minimal, nur zur Demonstration der Pipeline |
| Confidence | Heuristik — für Produktion noch abzustimmen |
| Focus-Area-Gewichte | Additiv, nicht normiert — Domänenentscheidung für später |
---
## 11. Tests
`backend/tests/test_training_profile_resolver.py` — Registry, Resolver, Profil-Filter, Serialisierung, unbekannter Algorithmus.
Ausführen (aus `backend/`):
```bash
python -m pytest tests/test_training_profile_resolver.py -v
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,276 @@
# Phase 0c Architecture Cleanup & Refactoring
**Datum:** 2026-03-28
**Voraussetzung:** Phase 0b abgeschlossen
**Geschätzter Aufwand:** 12-16 Stunden
---
## Ziele
Phase 0c fokussiert auf **Architektur-Verbesserungen** und **Structured Data**, ohne neue Features hinzuzufügen.
**Kernprinzip:** Von Formatted Strings zu Structured Data → Basis für Phase 0c Refactoring (Charts/Diagramme).
---
## Task 1: Dynamic Aggregation Methods (3-4h)
**Problem:** Aggregationsmethoden sind im Frontend hardcoded.
**Lösung:**
### Backend
```python
# backend/routers/goal_types.py
@router.get("/aggregation-methods")
def get_aggregation_methods():
"""Return all available aggregation methods with metadata"""
return [
{
"value": "latest",
"label_de": "Letzter Wert",
"label_en": "Latest",
"description": "Aktuellster Wert aus Tabelle",
"compatible_types": ["DECIMAL", "INTEGER", "FLOAT"],
"requires_numeric": False
},
{
"value": "avg_7d",
"label_de": "Durchschnitt 7 Tage",
"label_en": "7-day average",
"description": "Mittelwert über 7 Tage",
"compatible_types": ["DECIMAL", "INTEGER", "FLOAT"],
"requires_numeric": True
},
# ... alle Methoden aus goal_utils.py
]
```
**Implementierung:**
1. Endpoint erstellen (goal_types.py)
2. Auto-Discovery aus goal_utils.py Docstrings oder Introspection
3. Frontend anpassen (AdminGoalTypesPage.jsx)
4. Migration für Backfill (falls nötig)
**Dokumentation:** `.claude/docs/technical/AGGREGATION_METHODS.md` (exists)
---
## Task 2: Dynamic Placeholder Catalog (4-5h)
**Problem:** Platzhalter sind im Frontend nicht sichtbar/suchbar.
**Lösung:**
### Backend
```python
# backend/routers/prompts.py
@router.get("/placeholders/catalog")
def get_placeholder_catalog(session: dict = Depends(require_auth)):
"""
Return all registered placeholders with metadata.
Enables auto-complete and placeholder selector in frontend.
"""
return {
"categories": [
{
"name": "Profil",
"placeholders": [
{
"key": "name",
"placeholder": "{{name}}",
"description": "Name des Nutzers",
"example": "Lars",
"data_type": "string"
},
# ...
]
},
# ... weitere Kategorien
],
"total_count": 111
}
```
### Frontend
```javascript
// PlaceholderSelector Component
<PlaceholderSelector
onSelect={(placeholder) => insertAtCursor(placeholder)}
categories={fetchedCatalog.categories}
searchable={true}
showExamples={true}
/>
```
**Implementierung:**
1. Endpoint erstellen (nutzt PLACEHOLDER_CATALOG aus placeholder_resolver.py)
2. React Component: PlaceholderSelector
3. Integration in UnifiedPromptModal (Prompt-Editor)
4. Keyboard-Shortcuts (Strg+Space für Auto-Complete)
**Dokumentation:** `.claude/docs/technical/PLACEHOLDER_SYSTEM.md` (neu erstellen)
---
## Task 3: Calculation Functions → Structured Return (5-7h)
**Problem:** Calculation Functions returnen formatted strings statt strukturierte Daten.
**Lösung:** Refactoring aller Calculation Functions zu structured return.
### Beispiel-Refactoring
**Vorher (Phase 0b):**
```python
def calculate_weight_trend(profile_id: str) -> str:
# ... calculation ...
return f"sinkend (-0.9 kg in 28 Tagen)"
```
**Nachher (Phase 0c):**
```python
def calculate_weight_trend(profile_id: str) -> Dict:
# ... calculation ...
return {
"raw_value": -0.9,
"unit": "kg",
"period_days": 28,
"direction": "decreasing", # "increasing", "stable"
"confidence": "high", # "high", "medium", "low"
"data_points": 15,
"slope_per_day": -0.032
}
```
### Formatting Layer
```python
# placeholder_resolver.py
def format_weight_trend(data: Dict) -> str:
"""Format structured weight trend for AI prompts"""
direction_de = {"decreasing": "sinkend", "increasing": "steigend", "stable": "stabil"}
return f"{direction_de[data['direction']]} ({data['raw_value']} {data['unit']} in {data['period_days']} Tagen)"
```
### Betroffene Module
- `calculations/body_metrics.py` (~15 Funktionen)
- `calculations/nutrition_metrics.py` (~12 Funktionen)
- `calculations/activity_metrics.py` (~18 Funktionen)
- `calculations/recovery_metrics.py` (~10 Funktionen)
- `calculations/scores.py` (~8 Funktionen)
**Gesamt:** ~63 Funktionen zu refactoren
### Strategie
1. **Phase 0c/1:** Body Metrics (Proof of Concept)
2. **Phase 0c/2:** Nutrition + Activity
3. **Phase 0c/3:** Recovery + Scores
4. **Phase 0c/4:** Placeholder Resolver anpassen (Formatter)
**Dokumentation:**
- `.claude/docs/technical/CALCULATION_FUNCTIONS.md` (neu)
- `.claude/docs/functional/mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` (exists, Kontext für Chart-System)
---
## Task 4: Stats/Chart Endpoints (Optional, 3-4h)
**Voraussetzung:** Task 3 abgeschlossen
**Lösung:** Separate Endpoints für Chart-Daten (nutzen structured calculation returns)
```python
# backend/routers/stats.py (new)
@router.get("/stats/body/weight-trend")
def get_weight_trend_stats(
days: int = 30,
session: dict = Depends(require_auth)
):
"""Return structured weight trend data for charts"""
trend_data = calculate_weight_trend(session['profile_id'])
return {
"trend": trend_data,
"timeseries": [...], # Daily data points
"projections": {...} # Linear projection
}
```
**Implementierung:**
1. Neuer Router: `stats.py`
2. Endpoints für alle Chart-relevanten Metriken
3. OpenAPI Schema (für Frontend Type-Generation)
---
## Abhängigkeiten
```
Task 1 (Aggregation Methods) ─┐
├─> Unabhängig, parallel möglich
Task 2 (Placeholder Catalog) ─┘
Task 3 (Structured Returns) ──> Voraussetzung für Task 4
Task 4 (Stats Endpoints) ──> Optional, nutzt Task 3 Results
```
---
## Akzeptanzkriterien Phase 0c
**Muss (Must-Have):**
- ✅ Aggregation Methods API-gesteuert
- ✅ Placeholder Catalog API verfügbar
- ✅ Mindestens 1 Modul (Body Metrics) zu structured return refactored
- ✅ Dokumentation für alle 3 Tasks
**Sollte (Should-Have):**
- ✅ Alle Calculation Functions refactored
- ✅ Frontend Placeholder Selector implementiert
- ✅ Stats Endpoints für Body + Nutrition
**Kann (Nice-to-Have):**
- ⚪ Stats Endpoints für alle Module
- ⚪ Chart-Preview in Admin-Panel
- ⚪ TypeScript Type-Generation aus OpenAPI
---
## Rollback-Plan
Falls Task 3 (Structured Returns) Probleme verursacht:
1. Calculation Functions returnen **beide** Formate:
```python
return {
"formatted": "sinkend (-0.9 kg)", # Backward-compat
"structured": {...} # New format
}
```
2. Placeholder Resolver nutzt `formatted` weiterhin
3. Stats Endpoints nutzen `structured`
4. Schrittweise Migration ohne Breaking Changes
---
## Timeline (geschätzt)
**Option A (Serial):** 12-16 Stunden über 2-3 Tage
**Option B (Parallel):** 8-10 Stunden mit 2 Personen
**Empfehlung:** Option A, da Tasks eng verzahnt sind.
---
## Nach Phase 0c
**Enabler für:**
- ✅ Chart/Diagram System (Phase 1)
- ✅ Goal Progress Visualizations
- ✅ Custom Dashboards
- ✅ PDF/Excel Export mit strukturierten Daten
- ✅ API für externe Tools (z.B. Home Assistant)
**Nächste Phase (Phase 1):**
- Chart Library Integration (Recharts/Victory)
- Interactive Dashboards
- Goal Progress Tracking UI

View File

@ -0,0 +1,10 @@
# Arbeitspapiere & Zwischenstände (`working/`)
**Zweck:** Analysen, Migrations-Notizen, Session-Zusammenfassungen und andere **zeitlich begrenzte** Texte. Sie ergänzen **nicht** die kanonischen Spezifikationen in `../functional/` und `../technical/`.
**Regeln:** Siehe `.claude/rules/DOCUMENTATION.md`.
- Bei Widerspruch gewinnt die **aktuelle Spec** in `functional/` / `technical/` bzw. der **Code**.
- Dateien mit Datum im Namen oder Kopf bevorzugen; veraltete Inhalte nicht löschen ohne Prüfung ggf. als „archiviert“ markieren.
**Herkunft:** Mehrere Dateien wurden aus `docs/` (Projektroot) hierher verschoben, damit `docs/issues/` und ausgewiesene Governance-Dateien klarer bleiben.

Some files were not shown because too many files have changed in this diff Show More