# 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-Lab-Widgets und Feature-System **Kontext:** Dashboard-Widgets (`backend/widget_catalog.py`, Lab 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** (z. B. Dashboard-Lab): 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.