mitai-jinkendo/.claude/rules/ARCHITECTURE.md
Lars 141df021c1
All checks were successful
Deploy Development / deploy (push) Successful in 1m2s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
refactor: rename Dashboard-Lab-Widgets to Dashboard-Widgets and update related documentation
- Renamed references from "Dashboard-Lab-Widgets" to "Dashboard-Widgets" across documentation and codebase for consistency.
- Removed the deprecated Dashboard-Lab page and integrated its functionality into the new Dashboard-Widgets layout.
- Updated widget registration and configuration handling to reflect the new naming convention.
- Adjusted documentation in `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` and other related files to ensure clarity on the updated structure.
- Bumped application version to reflect these changes.
2026-04-23 16:18:10 +02:00

471 lines
15 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Architektur-Regeln Mitai Jinkendo
> **PFLICHTLEKTÜRE für Claude Code vor jeder Implementierung.**
> Diese Regeln sind verbindlich und dürfen nicht ohne explizite
> Genehmigung des Nutzers abgeändert werden.
>
> **Dokumentationsablage:** siehe **`DOCUMENTATION.md`** (gleicher Ordner) fachlich/technisch/working/issues.
---
## 1. Router-Architektur
### 1.1 Ein Modul = Ein Router
Jedes fachliche Modul hat genau eine Router-Datei in `backend/routers/`.
```
backend/routers/
├── auth.py # Authentifizierung
├── profiles.py # Nutzerprofile
├── weight.py # Gewichts-Tracking
├── sleep.py # Schlaf-Modul
├── training_types.py # Trainingstypen + HF
└── ... # je neues Modul = neue Datei
```
**Regeln:**
- Kein Endpoint darf außerhalb seines thematischen Routers definiert werden
- Neue Module immer als neue Router-Datei anlegen, nie in bestehende einfügen
- Router in `main.py` registrieren: `app.include_router(modul.router, prefix="/api")`
- Router-Datei-Name = Modul-Name in `version.py` MODULE_VERSIONS
### 1.2 API-First Prinzip
Jede Funktion ist zuerst als API-Endpoint implementiert die UI nutzt ausschließlich
diese Endpoints über `api.js`. Keine Business-Logik im Frontend.
```python
# ✅ Richtig: Logik im Backend-Endpoint
@router.get("/sleep/stats")
def get_sleep_stats(session=Depends(require_auth)):
# Berechnung hier
return {"avg_duration": ..., "sleep_debt": ...}
# ❌ Falsch: Berechnung im Frontend
const sleepDebt = entries.reduce((sum, e) => sum + (goal - e.duration), 0)
```
### 1.3 Einheitliche Fehlerbehandlung
```python
# ✅ Immer dieses Format:
raise HTTPException(status_code=404, detail="Eintrag nicht gefunden")
# Response: {"detail": "Eintrag nicht gefunden"}
# ❌ Nie eigene Formate:
return {"error": "not found"}
return {"message": "Fehler", "success": False}
```
---
## 2. Versionskontrollsystem
### 2.1 Versionierungsschema
**Semantic Versioning: `MAJOR.MINOR.PATCH`**
| Typ | Wann | Beispiel |
|-----|------|---------|
| MAJOR | Breaking Change, DB-Migration inkompatibel | 9.0.0 → 10.0.0 |
| MINOR | Neues Feature, neues Modul | 9.2.0 → 9.3.0 |
| PATCH | Bugfix, kleine Änderung, Refactor | 9.3.0 → 9.3.1 |
### 2.2 Versions-Dateien
**Backend: `backend/version.py`**
```python
APP_VERSION = "9.3.0"
BUILD_DATE = "2026-03-22"
MODULE_VERSIONS = {
"auth": "1.2.0",
"profiles": "1.1.0",
"weight": "1.0.3",
"circumference": "1.0.1",
"caliper": "1.0.1",
"activity": "1.1.0",
"nutrition": "1.0.2",
"photos": "1.0.0",
"insights": "1.3.0",
"prompts": "1.1.0",
"admin": "1.2.0",
"stats": "1.0.1",
"exportdata": "1.1.0",
"importdata": "1.0.0",
"membership": "2.1.0",
}
CHANGELOG = [
{
"version": "9.3.0",
"date": "2026-03-22",
"changes": [
"Feature: Sleep Module (sleep_log, JSONB-Segmente)",
"Feature: Vitalwerte-Seite in Navigation",
"Feature: Trainingstypen-Kategorisierung",
]
},
{
"version": "9.2.1",
"date": "2026-03-20",
"changes": [
"Fix: Feature-Enforcement Rollback",
"Fix: Erholungsstatus-Gewichtung korrigiert",
]
},
]
```
**Frontend: `frontend/src/version.js`**
```javascript
export const APP_VERSION = "9.3.0"
export const BUILD_DATE = "2026-03-22"
export const PAGE_VERSIONS = {
Dashboard: "1.3.0",
LoginScreen: "1.1.0",
WeightPage: "1.0.3",
ActivityPage: "1.2.0",
NutritionPage: "1.1.0",
AnalysisPage: "1.3.0",
SettingsPage: "1.4.0",
AdminPanel: "1.2.0",
SubscriptionPage: "1.0.0",
// Neue Seiten hier eintragen
}
```
### 2.3 Versions-Endpoint
**`GET /api/version`** öffentlich (kein Auth erforderlich)
```json
{
"app_version": "9.3.0",
"build_date": "2026-03-22",
"backend_version": "9.3.0",
"modules": {
"auth": "1.2.0",
"sleep": "1.0.0"
},
"db_schema_version": "20260322",
"environment": "production"
}
```
Dieser Endpoint wird in `backend/routers/version.py` implementiert und liest
direkt aus `version.py`.
### 2.4 Versions-Anzeige in der App
**Settings-Seite Versions-Panel:**
```
System-Versionen
─────────────────────────────────────
App (gesamt) 9.3.0
Backend 9.3.0 ✓ erreichbar
Frontend 9.3.0 ✓ geladen
DB-Schema 20260322
Umgebung production
─────────────────────────────────────
Module
auth 1.2.0
sleep 1.0.0
membership 2.1.0
[alle Module...]
─────────────────────────────────────
[Changelog] [Cache leeren]
```
Frontend ruft beim Laden der Settings-Seite `/api/version` ab und vergleicht
mit der eigenen `APP_VERSION` aus `version.js`. Bei Abweichung: Warnung anzeigen.
### 2.5 Pflicht-Regel: Versions-Bump bei jedem Commit
**Jede Code-Änderung erfordert:**
1. Versions-Bump in `backend/version.py` (APP_VERSION + betroffenes MODULE_VERSION)
2. Versions-Bump in `frontend/src/version.js` (APP_VERSION + betroffene PAGE_VERSION)
3. Changelog-Eintrag in `backend/version.py` CHANGELOG
**Claude Code prüft das im `/deploy` Command automatisch.**
Kein Commit ohne Versions-Bump keine Ausnahme.
### 2.6 DB-Schema-Version
Format: `YYYYMMDD` (Datum der letzten Migration)
Gespeichert in `backend/version.py`:
```python
DB_SCHEMA_VERSION = "20260322"
```
Bei jeder Schema-Änderung (ALTER TABLE, neue Tabelle) → DB_SCHEMA_VERSION aktualisieren.
---
## 3. Datenbankregeln
### 3.1 Pflichtfelder für neue Tabellen
```sql
-- Jede neue Tabelle braucht:
id SERIAL PRIMARY KEY,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
```
### 3.2 Source-Tracking bei Import-Daten
Tabellen die Daten aus externen Quellen empfangen brauchen:
```sql
source VARCHAR(50) DEFAULT 'manual'
-- Werte u. a.: 'manual' | 'apple_health' | 'garmin' | 'withings' | 'csv'
```
Importe über den **Universal CSV**-Pfad setzen `source = 'csv'`, sofern die Tabelle ein `source`-Feld hat; CHECK-Constraints und Migrationen müssen diesen Wert erlauben.
**Agent-Pflicht bei neuen Import-Zielen oder Executor-Änderungen:** `.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`
Manuelle Einträge (`source = 'manual'`) haben IMMER Vorrang bei Reimport:
```sql
-- Reimport überschreibt nur nicht-manuelle Einträge:
INSERT INTO sleep_log (...) ON CONFLICT (profile_id, date)
DO UPDATE SET ... WHERE sleep_log.source != 'manual'
```
### 3.3 Profile-ID Isolation
Jede Tabelle mit Nutzerdaten hat `profile_id` als Foreign Key.
Kein Endpoint gibt Daten eines anderen Profils zurück.
Profile-ID kommt IMMER aus der Session, nie aus Request-Parametern.
### 3.4 Boolean-Werte
```sql
-- PostgreSQL Boolean (nicht SQLite 0/1):
WHERE active = true
WHERE active = 1
```
---
## 4. Frontend-Regeln
### 4.1 Alle API-Calls über api.js
```javascript
// ✅ Richtig:
import { api } from '../utils/api'
const data = await api.listSleep()
// ❌ Falsch:
const r = await fetch('/api/sleep')
```
### 4.2 Neue Seite = Eintrag in PAGE_VERSIONS
Jede neue Seite in `frontend/src/version.js` registrieren.
### 4.3 CSS-Variablen statt Hardcoded-Farben
```javascript
// ✅ Richtig:
style={{color: 'var(--accent)'}}
// ❌ Falsch:
style={{color: '#1D9E75'}}
```
### 4.4 Fehlerbehandlung in allen async Funktionen
```javascript
try {
const data = await api.meinEndpoint()
setData(data)
} catch(e) {
setError(e.message)
} finally {
setLoading(false)
}
```
---
## 5. Git & Deployment-Regeln
### 5.1 Nie direkt auf main pushen
Immer über Pull Request in Gitea: develop → main.
develop Branch niemals löschen.
### 5.2 Commit-Message Format
```
feat: neues Feature oder Modul
fix: Bugfix
refactor: Umbau ohne Funktionsänderung
docs: Dokumentation
version: Versions-Bump
ci: CI/CD Änderungen
chore: Maintenance
```
### 5.3 Versions-Bump im Commit
```
feat: Sleep Module v1.0.0
- sleep_log Tabelle mit JSONB-Segmenten
- Import aus Apple Health CSV
- Korrelationen Schlaf <-> Ruhepuls
version: 9.3.0 (backend + frontend)
module: sleep 1.0.0
```
---
## 6. Dokumentations-Regeln
### 6.1 Neue Module dokumentieren
Bei jedem neuen Modul:
1. Fachliche Spec: `.claude/docs/functional/MODUL_NAME.md`
2. Technische Spec: `.claude/docs/technical/MODUL_NAME.md`
3. Nach Fertigstellung: `.claude/library/` aktualisieren
### 6.2 CLAUDE.md aktuell halten
Nach größeren Änderungen CLAUDE.md Versions-Tabelle aktualisieren.
### 6.3 Lessons Learned dokumentieren
Jeder Rollback oder schwerer Bug → Eintrag in `.claude/rules/LESSONS_LEARNED.md`
---
## Zusammenfassung: Checkliste vor jedem Commit
```
[ ] Versions-Bump in backend/version.py (APP_VERSION + MODULE)
[ ] Versions-Bump in frontend/src/version.js (APP_VERSION + PAGE)
[ ] Changelog-Eintrag in backend/version.py
[ ] DB_SCHEMA_VERSION aktualisiert (wenn Schema geändert)
[ ] Neues Modul in PAGE_VERSIONS / MODULE_VERSIONS eingetragen
[ ] Auth auf alle neuen Endpoints (require_auth)
[ ] Fehlerformat einheitlich (HTTPException mit detail)
[ ] Neue Tabellen haben created_at + updated_at
[ ] Import-Tabellen haben source-Feld
[ ] api.js für alle Frontend API-Calls
```
---
## 7. Prod-Schutz & Dev-Zugriff
### 7.1 Absoluter Prod-Schutz
Claude Code darf auf dem Prod-System (mitai.jinkendo.de) NIEMALS:
- Container neustarten (`docker restart mitai-*`)
- Schreibend in Container ausführen (`docker exec mitai-api ...`)
- Dateien direkt ändern (`/home/lars/docker/bodytrack/`)
- Prod-Datenbank schreiben (nur SELECT erlaubt)
**Prod-Änderungen ausschließlich über:**
```
git push origin develop → Gitea PR → Merge → deploy-prod.yml
```
### 7.2 Dev-System voller Zugriff erlaubt
Claude Code darf auf dem Dev-System (dev.mitai.jinkendo.de):
- Container neustarten (`docker restart dev-mitai-*`)
- Logs lesen und filtern
- DB lesen und schreiben (für Tests)
- Container neu bauen
### 7.3 Test-Umgebung
- API-Tests: gegen http://dev.mitai.jinkendo.de
- Playwright-Tests: gegen https://dev.mitai.jinkendo.de
- Screenshots: in `screenshots/` Ordner (in .gitignore)
- Test-Credentials: in Umgebungsvariablen (TEST_EMAIL, TEST_PASSWORD)
- NIEMALS Test-Credentials in Code committen
### 7.4 Erkennungsmerkmale Prod vs. Dev
```
Prod-Container: mitai-api, mitai-ui, mitai-db-prod
Dev-Container: dev-mitai-api, dev-mitai-ui, dev-mitai-postgres
Prod-Ports: 8002 (Backend), 3002 (Frontend)
Dev-Ports: 8099 (Backend), 3099 (Frontend)
Prod-URL: mitai.jinkendo.de
Dev-URL: dev.mitai.jinkendo.de
```
---
## 8. CSV-Import vs. Data Layer (Issue #53)
### 8.1 Leitlinie: Wo Interpretation stattfindet
| Schicht | Erlaubt | Nicht Sinn der Schicht |
|--------|---------|-------------------------|
| **Import (Ingest)** | Zuordnung CSV→Speicherfeld, **Typ-/Einheits-Konvertierung** (`type_conversions`), Duplikat-/Constraint-Logik | Fachliche **Interpretation**, Aggregation von „Bedeutung“, Metriken für Auswertung |
| **Data Layer (Issue #53, Layer 1+)** | Daten lesen, aufbereiten, ableiten, für Charts/KI/Prompts bereitstellen | — |
Verbindlich: **Semantik und Auswertung** nicht dauerhaft im Import verstecken; neue Features werden an dieser Grenze geprüft.
**Detail & Zielbild (Multi-Layer, Single Source of Truth):** `docs/issues/issue-53-phase-0c-multi-layer-architecture.md`
**Umsetzung Schlaf-Import (Refactoring, Offen):** Gitea http://192.168.2.144:3000/Lars/mitai-jinkendo/issues/69
### 8.2 Ist-Einordnung Import-Pfade (Übergang)
Bis sukzessive auf das Zielbild umgestellt ist, gilt:
| Pfad | Einordnung |
|------|----------------|
| Universal-CSV (`csv_parser`, `routers/csv_import.py`, Executor für u. a. Gewicht/Ernährung/Blutdruck/Aktivität/Vitals) | **Zielrichtung:** Mapping + Typkonvertierung |
| Apple-Schlaf-Aggregat (`csv_parser/sleep_apple_import.py`, `import_mode: apple_sleep_aggregate`) | **Legacy-Adapter** (quellenspezifische Aufbereitung) Austausch gegen mapping-nah + Layer 1 geplant |
| Dedizierte Import-Endpoints (z. B. `/api/activity/import-csv`, Vitals Apple) | **Legacy/Parallel** neue Quellen bevorzugt über Universal-Pfad + Vorlagen |
Änderungen an Import-Pfaden: Legacy nur erweitern mit **expliziter** Issue-/Review-Begründung; kein neues „wir rechnen Auswertung beim Insert“ ohne Data-Layer-Bezug.
---
## 9. Test-Regeln
### 9.1 Tests schreiben ist Pflicht
Jedes neue Feature bekommt mindestens einen Playwright-Test in
`tests/dev-smoke-test.spec.js`.
### 9.2 Reihenfolge: Test vor Commit
```
Implementieren → Tests schreiben → Tests grün → Committen
NIEMALS: Implementieren → Committen → Tests später
```
### 9.3 Claude Code schreibt Tests selbst
Nach jeder Implementierung:
1. Passende Tests in dev-smoke-test.spec.js ergänzen
2. `npx playwright test` ausführen
3. Fehler korrigieren bis alle Tests grün
4. Erst dann committen
### 9.4 Test-Kategorien
```javascript
// UI-Test (Playwright)
test('FEATURE: Beschreibung', async ({ page }) => { ... })
// API-Test (Playwright request)
test('API: Endpoint', async ({ request }) => { ... })
```
### 9.5 Screenshots bei Fehlern
Fehlgeschlagene Tests erzeugen automatisch Screenshots in:
`test-results/TESTNAME/test-failed-1.png`
→ Immer ansehen bevor Code geändert wird
### 9.6 Prod nie testen
Tests laufen IMMER gegen dev.mitai.jinkendo.de
NIEMALS gegen mitai.jinkendo.de
---
## 10. Dashboard-Widgets und Feature-System
**Kontext:** Dashboard-Widgets (`backend/widget_catalog.py`, API unter `/api/app/...`) und das **Subscription-/Feature-Modell** (`features`, `tier_limits`, `check_feature_access` in `backend/auth.py`) sind **getrennte Schichten**, müssen aber bei tariffrelevanten Widgets **verknüpft** werden.
**Bindend:**
1. **Keine fest codierten Tier-Namen** für Widget-Rechte Tiers und Limits kommen aus der DB.
2. **Komplexität** (Module aus, Unter-Stufen, KI vs. Standard) liegt in der **Feature-/Subscription-Logik**, nicht verteilt in Widget-Komponenten.
3. **Nutzer-Konfigurator** (**Übersicht anpassen** / `DashboardConfigurePage`): Widgets **ohne** passende Berechtigung **nicht anzeigen**; alle erlaubten Widgets bleiben verfügbar.
4. **Backend** liefert die effektive Erlaubnis (z.B. über erweiterten Katalog oder Entitlements), und **validiert beim Speichern** des Layouts, dass keine unerlaubten Widget-IDs persistiert werden (Policy: ablehnen oder strippen einheitlich halten).
5. **Daten/API:** Zusätzlich zur UI-Filterung müssen die **inhaltsliefernden Endpoints** weiterhin über `check_feature_access` geschützt sein (kein Leck über direkte API-Aufrufe).
**Detail-Doku (Checklisten, Dateipfade):** `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` § 0.