shinkan-jinkendo/.claude/rules/ARCHITECTURE.md
Lars 7284c577d7
All checks were successful
Deploy Development / deploy (push) Successful in 35s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 27s
feat: enhance media management and governance in the project
- Added new documentation for media assets and lifecycle management, establishing a single source of truth in MEDIA_ASSETS_AND_ARCHIVE_SPEC.md.
- Updated project status to reflect the addition of media archive and lifecycle governance.
- Introduced a new API endpoint for platform media storage, allowing superadmin access for media management.
- Enhanced exercise media handling with improved database integration for media assets, including deduplication and effective media root resolution.
- Updated frontend API utilities to support new media storage functionalities, ensuring seamless integration with the backend.
- Incremented version to 0.8.41, reflecting the latest changes and improvements in media handling.
2026-05-07 12:36:46 +02:00

16 KiB
Raw Blame History

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.

# ✅ 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

# ✅ 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}

1.4 Mandanten & Zugriffsschicht (Shinkan / ACCESS_LAYER)

Verbindlicher Rahmen: .claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md
Medien-Assets (Archiv, Papierkorb, Promotion, Copyright, externer Speicher): .claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md
Fortlaufendes Inventar: .claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md

Definition of Done für neue oder geänderte geschützte APIs, sobald Daten Verein, Sichtbarkeit oder mandantenbezogene Listen betreffen:

  1. Request-Kontext: Depends(get_tenant_context) (oder dokumentierte Ausnahme mit Kommentar # ACCESS_LAYER exempt: + Audit).
  2. Lesen: gleiche Sichtbarkeitslogik wie vergleichbare Bibliotheks-/Planungsartefakte (library_content_visibility_sql, exercise_visible_to_profile / zentrale Helfer — nicht ad-hoc „alles aus der Tabelle“).
  3. Schreiben: assert_valid_governance_visibility wo visibility / club_id gesetzt oder geändert wird.
  4. Audit-Tabelle und bei Bedarf die EXEMPT-Liste im Script backend/scripts/check_access_layer_hints.py aktualisieren.
  5. Optional vor Commit: python backend/scripts/check_access_layer_hints.py (mit ACCESS_LAYER_STRICT=1 schlägt bei neuen Verstößen fehl).

Router ohne Vereinsbezug (z.B. globale Kataloge, Auth-Login) bleiben bewusst ohne get_tenant_context; sie stehen im Script auf der EXEMPT-Liste.


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

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

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)

{
  "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:

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

-- 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:

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:

-- 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

-- PostgreSQL Boolean (nicht SQLite 0/1):
WHERE active = true    
WHERE active = 1       

4. Frontend-Regeln

4.1 Alle API-Calls über api.js

// ✅ 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

// ✅ Richtig:
style={{color: 'var(--accent)'}}

// ❌ Falsch:
style={{color: '#1D9E75'}}

4.4 Fehlerbehandlung in allen async Funktionen

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

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 Lars/mitai-jinkendo#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

// 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.