docs: Struktur .claude/docs versionieren, working/, Gitea-Index, Regeln
- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren - DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues - .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08) - Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben - docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md - CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht Made-with: Cursor
This commit is contained in:
parent
24daeeb83c
commit
7940dc7560
61
.claude/README.md
Normal file
61
.claude/README.md
Normal 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
|
||||
32
.claude/commands/add-endpoint.md
Normal file
32
.claude/commands/add-endpoint.md
Normal 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}`),
|
||||
```
|
||||
174
.claude/commands/check-issues.md
Normal file
174
.claude/commands/check-issues.md
Normal 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
|
||||
23
.claude/commands/db-add-column.md
Normal file
23
.claude/commands/db-add-column.md
Normal 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`
|
||||
85
.claude/commands/deploy.md
Normal file
85
.claude/commands/deploy.md
Normal 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.
|
||||
191
.claude/commands/document.md
Normal file
191
.claude/commands/document.md
Normal 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
100
.claude/commands/fix-bug.md
Normal 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
|
||||
```
|
||||
280
.claude/commands/implement-feature.md
Normal file
280
.claude/commands/implement-feature.md
Normal 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}}`.
|
||||
60
.claude/commands/merge-to-prod.md
Normal file
60
.claude/commands/merge-to-prod.md
Normal 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
|
||||
19
.claude/commands/new-feature.md
Normal file
19
.claude/commands/new-feature.md
Normal 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
47
.claude/commands/pi-db.md
Normal 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
|
||||
60
.claude/commands/pi-dev.md
Normal file
60
.claude/commands/pi-dev.md
Normal 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
|
||||
43
.claude/commands/pi-logs.md
Normal file
43
.claude/commands/pi-logs.md
Normal 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"
|
||||
```
|
||||
37
.claude/commands/pi-status.md
Normal file
37
.claude/commands/pi-status.md
Normal 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
|
||||
81
.claude/commands/refactor.md
Normal file
81
.claude/commands/refactor.md
Normal 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
226
.claude/commands/test.md
Normal 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
|
||||
42
.claude/commands/ui-component.md
Normal file
42
.claude/commands/ui-component.md
Normal 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
|
||||
59
.claude/commands/ui-page.md
Normal file
59
.claude/commands/ui-page.md
Normal 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:
|
||||
...
|
||||
```
|
||||
121
.claude/commands/ui-responsive.md
Normal file
121
.claude/commands/ui-responsive.md
Normal 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
242
.claude/docs/BACKLOG.md
Normal 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
|
||||
374
.claude/docs/CLEANUP_PLAN.md
Normal file
374
.claude/docs/CLEANUP_PLAN.md
Normal 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
|
||||
109
.claude/docs/GITEA_ISSUES_INDEX.md
Normal file
109
.claude/docs/GITEA_ISSUES_INDEX.md
Normal 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
177
.claude/docs/README.md
Normal 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 (0–3)
|
||||
├── 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
550
.claude/docs/ROADMAP.md
Normal 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
|
||||
94
.claude/docs/architecture/BACKEND.md
Normal file
94
.claude/docs/architecture/BACKEND.md
Normal 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
|
||||
319
.claude/docs/architecture/FEATURE_ENFORCEMENT.md
Normal file
319
.claude/docs/architecture/FEATURE_ENFORCEMENT.md
Normal 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`
|
||||
126
.claude/docs/architecture/FRONTEND.md
Normal file
126
.claude/docs/architecture/FRONTEND.md
Normal 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"/>
|
||||
```
|
||||
|
|
@ -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.*
|
||||
8
.claude/docs/audit/20260404_code_audit/README.md
Normal file
8
.claude/docs/audit/20260404_code_audit/README.md
Normal 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).
|
||||
|
|
@ -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`, …)
|
||||
|
|
@ -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`)
|
||||
|
|
@ -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`
|
||||
|
|
@ -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`
|
||||
|
|
@ -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.
|
||||
|
|
@ -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`
|
||||
10
.claude/docs/audit/README.md
Normal file
10
.claude/docs/audit/README.md
Normal 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.
|
||||
7078
.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.json
Normal file
7078
.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.json
Normal file
File diff suppressed because it is too large
Load Diff
147
.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.md
Normal file
147
.claude/docs/audit/platzhalter/PLACEHOLDER_CATALOG_EXTENDED.md
Normal 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
|
||||
24
.claude/docs/audit/platzhalter/PLACEHOLDER_EXPORT_SPEC.md
Normal file
24
.claude/docs/audit/platzhalter/PLACEHOLDER_EXPORT_SPEC.md
Normal 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.
|
||||
66
.claude/docs/audit/platzhalter/PLACEHOLDER_GAP_REPORT.md
Normal file
66
.claude/docs/audit/platzhalter/PLACEHOLDER_GAP_REPORT.md
Normal 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
|
||||
|
|
@ -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.
|
||||
5
.claude/docs/audit/platzhalter/README.md
Normal file
5
.claude/docs/audit/platzhalter/README.md
Normal 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`.
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
- 0–100?
|
||||
- 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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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`
|
||||
|
|
@ -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.
|
||||
422
.claude/docs/functional/ACTIVITY_QUALITY_GATES.md
Normal file
422
.claude/docs/functional/ACTIVITY_QUALITY_GATES.md
Normal 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
|
||||
458
.claude/docs/functional/AI_PROMPTS.md
Normal file
458
.claude/docs/functional/AI_PROMPTS.md
Normal 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"
|
||||
1066
.claude/docs/functional/DATA_ARCHITECTURE.md
Normal file
1066
.claude/docs/functional/DATA_ARCHITECTURE.md
Normal file
File diff suppressed because it is too large
Load Diff
417
.claude/docs/functional/DEVELOPMENT_ROUTES.md
Normal file
417
.claude/docs/functional/DEVELOPMENT_ROUTES.md
Normal 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)
|
||||
329
.claude/docs/functional/GOALS_VITALS.md
Normal file
329
.claude/docs/functional/GOALS_VITALS.md
Normal 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 4–6 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 6–8 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
|
||||
1790
.claude/docs/functional/PHASE_0B_IMPROVEMENTS.md
Normal file
1790
.claude/docs/functional/PHASE_0B_IMPROVEMENTS.md
Normal file
File diff suppressed because it is too large
Load Diff
228
.claude/docs/functional/RESPONSIVE_UI.md
Normal file
228
.claude/docs/functional/RESPONSIVE_UI.md
Normal 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?
|
||||
227
.claude/docs/functional/SLEEP_MODULE.md
Normal file
227
.claude/docs/functional/SLEEP_MODULE.md
Normal 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 1–5 (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 (0–10+)
|
||||
- **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 (1–5)
|
||||
- 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 22–23 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:00–23: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 22–23 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
|
||||
257
.claude/docs/functional/TRAINING_TYPES.md
Normal file
257
.claude/docs/functional/TRAINING_TYPES.md
Normal 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?
|
||||
840
.claude/docs/functional/TRAINING_TYPE_PROFILES.md
Normal file
840
.claude/docs/functional/TRAINING_TYPE_PROFILES.md
Normal 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
56
.claude/docs/prompts/prompt-pipeline-2026-03-26.json
Normal file
56
.claude/docs/prompts/prompt-pipeline-2026-03-26.json
Normal 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 (450–550 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
387
.claude/docs/technical/AGGREGATION_METHODS.md
Normal file
387
.claude/docs/technical/AGGREGATION_METHODS.md
Normal 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
|
||||
575
.claude/docs/technical/API_REFERENCE.md
Normal file
575
.claude/docs/technical/API_REFERENCE.md
Normal 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
|
||||
634
.claude/docs/technical/ARCHITECTURE.md
Normal file
634
.claude/docs/technical/ARCHITECTURE.md
Normal 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
|
||||
904
.claude/docs/technical/AUTH.md
Normal file
904
.claude/docs/technical/AUTH.md
Normal 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)
|
||||
205
.claude/docs/technical/CENTRAL_SUBSCRIPTION_SYSTEM.md
Normal file
205
.claude/docs/technical/CENTRAL_SUBSCRIPTION_SYSTEM.md
Normal 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)
|
||||
163
.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md
Normal file
163
.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md
Normal 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` (7–90) | 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 7–90`).
|
||||
|
||||
### 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 7–90.
|
||||
|
||||
### 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` |
|
||||
1088
.claude/docs/technical/DATABASE.md
Normal file
1088
.claude/docs/technical/DATABASE.md
Normal file
File diff suppressed because it is too large
Load Diff
1758
.claude/docs/technical/DATABASE_MODEL_COMPLETE.md
Normal file
1758
.claude/docs/technical/DATABASE_MODEL_COMPLETE.md
Normal file
File diff suppressed because it is too large
Load Diff
780
.claude/docs/technical/DATA_LAYER_EXTENSION_GUIDE.md
Normal file
780
.claude/docs/technical/DATA_LAYER_EXTENSION_GUIDE.md
Normal 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
|
||||
337
.claude/docs/technical/FEATURE_ENFORCEMENT_MAPPING.md
Normal file
337
.claude/docs/technical/FEATURE_ENFORCEMENT_MAPPING.md
Normal 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
|
||||
923
.claude/docs/technical/FRONTEND.md
Normal file
923
.claude/docs/technical/FRONTEND.md
Normal 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 375px–600px (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)
|
||||
212
.claude/docs/technical/INTERNAL_API_REFERENCE.md
Normal file
212
.claude/docs/technical/INTERNAL_API_REFERENCE.md
Normal 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
|
||||
1058
.claude/docs/technical/MEMBERSHIP_SYSTEM.md
Normal file
1058
.claude/docs/technical/MEMBERSHIP_SYSTEM.md
Normal file
File diff suppressed because it is too large
Load Diff
392
.claude/docs/technical/MIGRATIONS.md
Normal file
392
.claude/docs/technical/MIGRATIONS.md
Normal 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
|
||||
799
.claude/docs/technical/PLACEHOLDER_DEVELOPMENT_GUIDE.md
Normal file
799
.claude/docs/technical/PLACEHOLDER_DEVELOPMENT_GUIDE.md
Normal 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
|
||||
504
.claude/docs/technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md
Normal file
504
.claude/docs/technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md
Normal 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**
|
||||
53
.claude/docs/technical/PROFILE_REFERENCE_VALUES.md
Normal file
53
.claude/docs/technical/PROFILE_REFERENCE_VALUES.md
Normal 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.
|
||||
148
.claude/docs/technical/TRAINING_PROFILE_RESOLVER_LAYER1.md
Normal file
148
.claude/docs/technical/TRAINING_PROFILE_RESOLVER_LAYER1.md
Normal 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 0–1 |
|
||||
| `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
|
||||
```
|
||||
1097
.claude/docs/technical/TRAINING_TYPE_PROFILES_TECHNICAL.md
Normal file
1097
.claude/docs/technical/TRAINING_TYPE_PROFILES_TECHNICAL.md
Normal file
File diff suppressed because it is too large
Load Diff
1257
.claude/docs/technical/V9D_PHASE2_VITALS_SLEEP.md
Normal file
1257
.claude/docs/technical/V9D_PHASE2_VITALS_SLEEP.md
Normal file
File diff suppressed because it is too large
Load Diff
276
.claude/docs/working/PHASE_0C_TASKS.md
Normal file
276
.claude/docs/working/PHASE_0C_TASKS.md
Normal 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
|
||||
10
.claude/docs/working/README.md
Normal file
10
.claude/docs/working/README.md
Normal 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
Loading…
Reference in New Issue
Block a user