From 532e17c4cd94067e634e5b34191b2ab59b21e535 Mon Sep 17 00:00:00 2001 From: Lars Date: Wed, 22 Jul 2026 11:11:07 +0200 Subject: [PATCH] feat: add Jinkendo Foundation design principles documentation - Introduced a new section for the Jinkendo Foundation, detailing design principles for the product family. - Updated README files to include references to the new design principles documentation. - Enhanced the overall documentation structure to improve navigation and accessibility of design resources. - Ensured consistency across documentation related to the Jinkendo Foundation and its principles. --- .claude/README.md | 4 +- .claude/docs/README.md | 14 +- .claude/docs/jinkendo-foundation/README.md | 46 +++ .../AUTH_SESSION_DESIGN_PRINCIPLES.md | 326 +++++++++++++++ .../DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md | 363 +++++++++++++++++ .../DATA_LAYER_DESIGN_PRINCIPLES.md | 359 +++++++++++++++++ .../FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md | 370 ++++++++++++++++++ .../MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md | 369 +++++++++++++++++ .../NAVIGATION_IA_DESIGN_PRINCIPLES.md | 346 ++++++++++++++++ .../PROMPT_ENGINE_DESIGN_PRINCIPLES.md | 305 +++++++++++++++ .../design-principles/README.md | 136 +++++++ .../REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md | 315 +++++++++++++++ .../UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md | 325 +++++++++++++++ .claude/rules/DOCUMENTATION.md | 2 + CLAUDE.md | 1 + .../activity_persistence_orchestrator.py | 2 +- backend/scripts/audit_insert_placeholders.py | 88 +++++ backend/tests/test_activity_insert_sql.py | 49 +++ 18 files changed, 3417 insertions(+), 3 deletions(-) create mode 100644 .claude/docs/jinkendo-foundation/README.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/AUTH_SESSION_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/DATA_LAYER_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/NAVIGATION_IA_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/PROMPT_ENGINE_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/README.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md create mode 100644 .claude/docs/jinkendo-foundation/design-principles/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md create mode 100644 backend/scripts/audit_insert_placeholders.py create mode 100644 backend/tests/test_activity_insert_sql.py diff --git a/.claude/README.md b/.claude/README.md index 3c91fc5..bb8b481 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -12,7 +12,8 @@ Dieser Ordner ist der **primäre Orientierungspunkt** für Claude Code / Cursor- | 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`** | -| 5 | **Universal CSV Import** (Modul/Executor/Vorlagen): **`docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** (unter `.claude/`) | +| 5 | **Jinkendo Foundation** (Designprinzipien 1–9): **`docs/jinkendo-foundation/design-principles/README.md`** | +| 6 | **Universal CSV Import** (Modul/Executor/Vorlagen): **`docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** (unter `.claude/`) | Themen mit UI/Nav/PWA: siehe `../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` (im **Projekt**-`docs/`, nicht hier). @@ -25,6 +26,7 @@ Themen mit UI/Nav/PWA: siehe `../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` (im ├── README.md ← Diese Datei ├── rules/ ← Verbindliche Regeln (versioniert, wenn konfiguriert) ├── docs/ ← Spezifikationen + Arbeitspapiere +│ ├── jinkendo-foundation/ ← Foundation Produktfamilie (Designprinzipien) │ ├── functional/ ← Fachlich (WAS) │ ├── technical/ ← Technisch (WIE) │ ├── architecture/ ← Querschnitt diff --git a/.claude/docs/README.md b/.claude/docs/README.md index 4e26d9a..8699b03 100644 --- a/.claude/docs/README.md +++ b/.claude/docs/README.md @@ -20,6 +20,7 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp ├── ROADMAP.md ← Strategische Phasen (0–3) ├── CLEANUP_PLAN.md ← Historie Bereinigung März 2026 ├── prompts/ ← Exportierte Prompt-Artefakte (JSON) +├── jinkendo-foundation/ ← Foundation Produktfamilie (Designprinzipien) ├── functional/ ← Fachliche Spezifikationen (WAS) ├── technical/ ← Technische Spezifikationen & Referenz (WIE) ├── working/ ← Arbeitspapiere, Analysen, Session-Snapshots @@ -55,6 +56,7 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp | Dashboard-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 | | Universal CSV Import | `technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | `backend/csv_parser/`, `routers/csv_import.py`, `routers/admin_csv_templates.py` | +| **Designprinzipien (Produktfamilie)** | **`jinkendo-foundation/design-principles/README.md`** | Querschnittsmuster; Foundation für Schwester-Apps | | Aktivität Produktionsreife | `technical/ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` (+ EAV-Guide) | `backend/data_layer/activity_session_metrics.py`, `activity_metrics.py`, CSV-Orchestrierung | | Mitgliedschaft / Features | `technical/MEMBERSHIP_SYSTEM.md`, `architecture/FEATURE_ENFORCEMENT.md` | `backend/auth.py`, Feature-Logging, Router mit Enforcement | @@ -92,6 +94,16 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp ## Technische Spezifikationen (`technical/`) +### Jinkendo Foundation (Produktfamilie) + +| Dokument | Inhalt | +|----------|--------| +| **[jinkendo-foundation/README.md](jinkendo-foundation/README.md)** | Einstieg Foundation-Ordner | +| **[design-principles/README.md](jinkendo-foundation/design-principles/README.md)** | Index: 9 Designprinzipien, Lesereihenfolge, Übergabe-Checkliste | +| `design-principles/*_DESIGN_PRINCIPLES.md` | Einzeldokumente (#1–#9) | + +### Referenz & Agent-Guides (`technical/`) + | Dokument | Thema | |----------|--------| | `AGGREGATION_METHODS.md` | Aggregation | @@ -183,4 +195,4 @@ Siehe [`audit/README.md`](./audit/README.md). --- -**Letzte Aktualisierung:** 9. April 2026 (Universal CSV Agent-Guide, Abgleich-Tabelle) +**Letzte Aktualisierung:** 4. Juli 2026 (Jinkendo Foundation, Designprinzipien nach `jinkendo-foundation/`) diff --git a/.claude/docs/jinkendo-foundation/README.md b/.claude/docs/jinkendo-foundation/README.md new file mode 100644 index 0000000..4479fa2 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/README.md @@ -0,0 +1,46 @@ +# Jinkendo Foundation + +**Stand:** 2026-07-04 +**Zweck:** Geteilte **Architektur-Foundation** für die Jinkendo-Produktfamilie (Mitai, Miken, Ikigai, Shinkan, …) — unabhängig von app-spezifischer Domänenlogik. + +Dieser Ordner enthält **keine** Mitai-Fachspecs und **keine** operativen Agent-Guides zu einzelnen Features. Die Referenzimplementierung bleibt in **Mitai Jinkendo**; die Foundation beschreibt **übertragbare Muster**. + +--- + +## Inhalt + +| Pfad | Beschreibung | +|------|--------------| +| **[design-principles/README.md](./design-principles/README.md)** | **Einstieg:** Index aller 9 Designprinzipien-Dokumente, Lesereihenfolge, Übergabe-Checkliste | +| `design-principles/*_DESIGN_PRINCIPLES.md` | Einzeldokumente (#1 Prompt Engine … #9 Migration & Deploy) | + +--- + +## Mitai-spezifische Implementierung + +Agent-Guides, API-Referenz und Domänen-Specs liegen weiterhin unter: + +- `.claude/docs/technical/` — WIE (Implementierung) +- `.claude/docs/functional/` — WAS (Fachlich) +- `docs/issues/` — Issue-Epics + +Die Designprinzipien verlinken dorthin, wo Mitai als **Beispiel** dient. + +--- + +## Apps der Familie + +| Domain | App | +|--------|-----| +| mitai.jinkendo.de | Körper-Tracking (Referenz) | +| miken.jinkendo.de | Meditation | +| ikigai.jinkendo.de | Lebenssinn | +| shinkan.jinkendo.de | Kampfsport | + +--- + +## Pflege + +Neue Querschnittsmuster → neues Dokument unter `design-principles/` + Eintrag im [Index](./design-principles/README.md). + +Regeln zur Ablage: [DOCUMENTATION.md](../../rules/DOCUMENTATION.md) diff --git a/.claude/docs/jinkendo-foundation/design-principles/AUTH_SESSION_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/AUTH_SESSION_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..2e18900 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/AUTH_SESSION_DESIGN_PRINCIPLES.md @@ -0,0 +1,326 @@ +# Auth & Session – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Authentifizierung, Session-Management, rollenbasierte API-Zugriffe — kein Mandanten-/SSO-System, keine Zahlungs-Auth + +**Serie:** Designprinzipien für Produktfamilie · Dokument 5 von n +**Vorgänger:** [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Auth-Kern | `backend/auth.py` | +| Auth-Endpoints | `backend/routers/auth.py` | +| Profile | `backend/routers/profiles.py` | +| Frontend | `frontend/src/context/AuthContext.jsx`, `ProfileContext.jsx`, `utils/api.js` | +| DB | `profiles`, `sessions` | +| Architektur-Regeln | `.claude/rules/ARCHITECTURE.md`, `CLAUDE.md` § Auth | +| Vision (nicht implementiert) | `CENTRAL_SUBSCRIPTION_SYSTEM.md` (SSO/JWT) | + +--- + +## Modul + +**Auth & Session** + +Server-seitige, token-basierte Authentifizierung mit FastAPI-Dependencies — **Identität und Rolle**, getrennt von Feature-Entitlements und fachlicher Logik. + +--- + +## Fachliche Verantwortung + +Das Modul übernimmt: + +1. **Identität** — Wer ist eingeloggt? (`profiles` + Passwort/bcrypt) +2. **Session** — Opaque Token in `sessions`, Ablaufzeit, Logout +3. **API-Gate** — `require_auth`, `require_admin`, `require_auth_flexible` +4. **Passwort-Lifecycle** — Hash, Verify, Reset, Registrierung, E-Mail-Verifizierung +5. **Rollen** — `profiles.role`: `user` \| `admin` (grobbinsenartig) + +Es übernimmt **nicht**: + +- Feature-Limits / Tier (→ Feature & Entitlement System, gleiche `auth.py`-Datei aber logisch getrennt) +- Mandanten-Isolation / Org-Workspaces +- OAuth/SSO/JWT (nur Vision) +- Authorization auf Datensatzebene (Row-Level Security) + +### Was Mitai **ist** vs. **nicht ist** + +| Mitai | Produktfamilien-Muster | +|-------|------------------------| +| 1 Login = 1 Profil (E-Mail) | ✅ Account-Modell | +| Historisch Multi-Profil auf einer Instanz | ⚠️ Legacy (`/profiles`, `X-Profile-Id`) | +| Self-hosted Einzelinstanz | ✅ Kein Multi-Tenant-SaaS | +| Session-Token in DB | ✅ Server-side Session Store | +| Zentrale Jinkendo-Auth (Vision) | ❌ nicht gebaut | + +--- + +## Administrierte vs. code-definierte Konfiguration + +| Konfiguration | Speicherort | Administrierbar? | +|---------------|-------------|------------------| +| Nutzer-Stammdaten, Rolle | `profiles` | Admin (User-Verwaltung) / Self-Service | +| Session-Laufzeit | `profiles.session_days` (Default 30) | Profil/Admin | +| Passwort-Hash | `profiles.pin_hash` | Nutzer (change pin) | +| E-Mail-Verifizierung | `email_verified`, Token-Felder | System | +| Trial-Ende | `trial_ends_at` | System bei Registrierung | +| SMTP | Env (`SMTP_*`, `APP_URL`) | Deploy | +| Rate Limits Login/Register | Code (`5/min`, `3/hour`) | Code | + +**Hardcodiert:** bcrypt, Token-Länge (`secrets.token_urlsafe(32)`), Rollen-Enum (`user`/`admin`), Header-Name `X-Auth-Token`. + +--- + +## Session- und Auth-Flow + +``` +Login (email + password) + → verify_pin (bcrypt | legacy SHA256) + → optional bcrypt upgrade + → INSERT sessions (token, profile_id, expires_at) + → Client: localStorage bodytrack_token + +Request + → Header X-Auth-Token (api.js / AuthContext) + → get_session(token) JOIN profiles + → require_auth → session dict (profile_id, role, …) + +Logout + → DELETE sessions WHERE token=… + → Client: localStorage clear +``` + +**Sonderfall:** `require_auth_flexible` — Token via Header **oder** Query `ssetoken` (SSE, ``, Downloads). + +--- + +## Rollen + +| Rolle | Mechanismus | Typische Rechte | +|-------|-------------|-----------------| +| **user** | `profiles.role = 'user'` | Eigene Daten, Features nach Tier | +| **admin** | `require_admin` | Admin-Shell, Prompts, User, System | + +Kein feingranulares RBAC (keine Permission-Matrix). Admin ist Binär-Schalter. + +--- + +## Designprinzipien + +### 1. FastAPI-Dependencies als Auth-Gate + +| | | +|---|---| +| **Prinzip** | Jeder geschützte Endpoint nutzt `session: dict = Depends(require_auth)` als **separaten** Parameter — nie in `Header()` eingebettet. | +| **Begründung** | Verhindert ungeschützte Endpoints durch falsche Parameter-Signatur. | +| **Quelle** | `CLAUDE.md` § Kritische Regeln; `auth.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht linter-erzwungen; Legacy-Endpoints existieren. | + +### 2. Server-side opaque Sessions + +| | | +|---|---| +| **Prinzip** | Token ist zufällig, in DB gespeichert; Validierung über `sessions` + Ablauf — kein JWT mit Client-Claims. | +| **Begründung** | Revocation (Logout), kein Trust in Client-Payload; einfaches Modell für Self-Hosted. | +| **Quelle** | `sessions` Tabelle; `make_token()`, `get_session()` | +| **Tragfähigkeit** | **hoch** (Single-App, Self-Hosted) | +| **Einschränkung** | Skalierung multi-node braucht shared session store; SSO-Familie braucht anderes Modell. | + +### 3. profile_id aus Session, nicht aus Client + +| | | +|---|---| +| **Prinzip** | Autoritative Identität für neue Endpoints: `session['profile_id']` — Client darf Profil nicht wählen. | +| **Begründung** | Verhindert IDOR (Zugriff auf fremde Profile). | +| **Quelle** | `routers/goals.py`, `routers/prompts.py`; Architektur-Intent in `CLAUDE.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy `get_pid(x_profile_id)` akzeptiert `X-Profile-Id` **ohne** Session-Abgleich — siehe Nicht übernehmen. | + +### 4. bcrypt mit Legacy-Migration + +| | | +|---|---| +| **Prinzip** | Passwörter mit bcrypt; SHA256-Legacy beim Login erkannt und transparent auf bcrypt upgraded. | +| **Begründung** | Kein Big-Bang-Migration; sichere Hashes ohne Nutzer-Zwangs-Reset. | +| **Quelle** | `verify_pin()`, Login in `routers/auth.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Upgrade nur bei erfolgreichem Login. | + +### 5. Rate Limiting auf Auth-Endpoints + +| | | +|---|---| +| **Prinzip** | Login, Register, Forgot-Password, Resend-Verification mit `slowapi`-Limits (IP-basiert). | +| **Begründung** | Brute-Force- und Abuse-Schutz. | +| **Quelle** | `routers/auth.py` (`5/minute`, `3/hour`); `main.py` Limiter | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | IP-only; kein account-based lockout. | + +### 6. Keine E-Mail-Enumeration bei sensiblen Flows + +| | | +|---|---| +| **Prinzip** | Forgot-Password und Resend-Verification liefern generische Erfolgsmeldung, auch wenn E-Mail unbekannt. | +| **Begründung** | Privacy; erschwert Account-Scraping. | +| **Quelle** | `password_reset_request`, `resend_verification` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Register sagt „E-Mail bereits registriert“ (Enumeration möglich). | + +### 7. E-Mail-Verifizierung vor voller Nutzung + +| | | +|---|---| +| **Prinzip** | Self-Register setzt `email_verified=FALSE`; Verify-Endpoint aktiviert + Auto-Login-Session. | +| **Begründung** | Valide Kontaktadresse; Spam-Reduktion. | +| **Quelle** | `register`, `verify_email` in `routers/auth.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht überall im Backend erzwungen (Login ohne verified check?). | + +### 8. Flexible Auth für technische Clients + +| | | +|---|---| +| **Prinzip** | `require_auth_flexible`: gleiche Session-Validierung via Header oder `?ssetoken=` für SSE/Bilder. | +| **Begründung** | Browser-APIs ohne Custom Headers. | +| **Quelle** | `auth.py`; Prompt SSE `/execute-stream` | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Token in URL kann in Logs/Referrer leaken — kurze Sessions / HTTPS Pflicht. | + +### 9. Zentraler API-Client mit Token-Injektion + +| | | +|---|---| +| **Prinzip** | Frontend: `api.js` injiziert `X-Auth-Token` automatisch — kein scattered `fetch` ohne Auth. | +| **Begründung** | Konsistenz; eine Stelle für Token-Handling. | +| **Quelle** | `utils/api.js` → `hdrs()`; `getToken()` aus AuthContext | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Einzelne Komponenten umgehen noch `api.js` (SettingsPage, EmailSettings). | + +### 10. Auth getrennt von Authorization (Features) + +| | | +|---|---| +| **Prinzip** | `require_auth` = identifiziert; `check_feature_access` = berechtigt für Aktion — nacheinander im Router. | +| **Begründung** | Klare Schichten; Auth-Modul nicht mit Tier-Logik vermischen (auch wenn Datei `auth.py` beides enthält). | +| **Quelle** | `FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md`; Router-Muster | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy Profil-Flags `ai_enabled`, `export_enabled` parallel zum Feature-System. | + +### 11. Admin-Gate im Frontend und Backend + +| | | +|---|---| +| **Prinzip** | Backend: `require_admin`; Frontend: `RequireAdmin` + `isAdmin` aus Session-Rolle. | +| **Begründung** | UX-Navigation + API-Sicherheit (Frontend allein reicht nicht). | +| **Quelle** | `RequireAdmin.jsx`; `require_admin()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Einzelne Routen (Workflow-Editor) ohne Frontend-Admin-Gate. | + +### 12. Session-Kontext im Frontend + +| | | +|---|---| +| **Prinzip** | `AuthProvider` hält `{ token, profile_id, role, profile }`; App setzt `setProfileId(session.profile_id)` für API. | +| **Begründung** | Single React-Tree für Login-State; Re-Validate via `/auth/me` beim Start. | +| **Quelle** | `AuthContext.jsx`; `App.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `ProfileContext` lädt alle Profile — Multi-Profil-UX Rest; Session-Profil ist Kanon. | + +--- + +## Nicht übernehmen + +1. **`get_pid(X-Profile-Id)` ohne Session-Bindung** — Client kann fremde `profile_id` senden; IDOR-Risiko. Kanon: immer `session['profile_id']` oder explizite Admin-Impersonation mit Audit. + +2. **Profile-CRUD nur mit `require_auth`** — `/profiles` listet alle Nutzer für jeden Authentifizierten (Kommentar „admin“, kein `require_admin`). Für Familien-Architektur: strikte Admin-Gates. + +3. **Dual-System Profil-Flags vs. Features** — `ai_enabled`, `export_enabled`, `ai_limit_day` in Session-Query neben v9c Feature-Registry. + +4. **localStorage-Key-Inkonsistenz** — `bodytrack_token` vs. `mitai-jinkendo_active_profile` (historischer App-Name). + +5. **Direktes `fetch` ohne `api.js`** — umgeht Token-/Error-Konvention. + +6. **Reset-Token in `sessions`-Tabelle** — `reset_{token}` mischt Session-Typen in einer Tabelle; OK für MVP, für Familie: getrennte Token-Typen/Tabellen. + +7. **Kein JWT/SSO trotz Produktfamilien-Vision** — `CENTRAL_SUBSCRIPTION_SYSTEM.md` beschreibt `auth.jinkendo.de` — Mitai-Implementierung ist **nicht** das Zielbild für Cross-App-SSO. + +8. **Multi-Profil-Haushalt ohne klares Modell** — Legacy Multi-Profile auf einer Instanz vs. 1 Account = 1 Profil; für neue Apps Modell explizit wählen. + +9. **Role als einziges RBAC** — reicht für Admin/User, nicht für feingranulare Permissions. + +10. **Session-Query mit veralteten Profil-Spalten** — `get_session` SELECT enthält Legacy-Felder statt nur Identität + Rolle. + +11. **Fehlende erzwungene E-Mail-Verified-Prüfung** — Registrierung setzt Flag, Login prüft es nicht offensichtlich. + +12. **Debug-Print in Auth-Modul** — `print("[AUTH.PY] Module loaded…")` in Produktionscode. + +--- + +## Abgrenzung zu anderen Serien-Dokumenten + +| Thema | Dokument | +|-------|----------| +| Tier, Limits, Quotas | [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) | +| Zentrale SSO/Abo-Vision | [CENTRAL_SUBSCRIPTION_SYSTEM.md](../../technical/CENTRAL_SUBSCRIPTION_SYSTEM.md) | +| API-First / Router | `ARCHITECTURE.md` §1 | + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/ +├── auth.py # Session, require_*, Feature-Access (v9c) +└── routers/ + ├── auth.py # login, logout, register, verify, reset + └── profiles.py # CRUD, get_pid (Legacy) + +frontend/src/ +├── context/AuthContext.jsx +├── context/ProfileContext.jsx +├── layouts/RequireAdmin.jsx +└── utils/api.js # Token-Injektion + +DB: +├── profiles # Identität, Rolle, Hash, Tier, Trial +└── sessions # token → profile_id, expires_at +``` + +**Endpoints (Auswahl):** + +| Endpoint | Auth | +|----------|------| +| `POST /api/auth/login` | Public + Rate limit | +| `POST /api/auth/logout` | Token optional | +| `GET /api/auth/me` | require_auth | +| `POST /api/auth/register` | Public + Rate limit | +| `GET /api/auth/verify/{token}` | Public | + +--- + +## Verwandte Dokumentation + +- Feature-Entitlements: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) +- Architektur-Regeln Auth: `CLAUDE.md`, `.claude/rules/ARCHITECTURE.md` +- GUI Admin-Guard: `docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` +- SSO-Vision: [CENTRAL_SUBSCRIPTION_SYSTEM.md](../../technical/CENTRAL_SUBSCRIPTION_SYSTEM.md) + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1 | Prompt Engine | ✅ | +| 2 | Data Layer | ✅ | +| 3 | Feature & Entitlement | ✅ | +| 4 | Registry-/Plugin-Muster | ✅ | +| 5 | Auth & Session | ✅ dieses Dokument | +| 6 | Universal Import | ✅ `UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md` | +| 7 | Dashboard Widgets | ✅ | +| 8 | Navigation / IA | ✅ `NAVIGATION_IA_DESIGN_PRINCIPLES.md` | +| 9 | Migration & Deploy | ✅ | diff --git a/.claude/docs/jinkendo-foundation/design-principles/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..ae47122 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md @@ -0,0 +1,363 @@ +# Dashboard Widgets – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Konfigurierbare Übersicht (Widget-Katalog, Layout, Entitlements, Frontend-Registry) — keine Chart-/Metrik-Berechnung + +**Serie:** Designprinzipien für Produktfamilie · Dokument 7 von n +**Vorgänger:** [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](./UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Katalog (SSoT) | `backend/widget_catalog.py` | +| Layout-Schema | `backend/dashboard_layout_schema.py` | +| Config-Validierung | `backend/dashboard_widget_config.py` | +| Entitlements | `backend/dashboard_widget_entitlements.py`, `widget_feature_requirements_db.py` | +| Produkt-Standard | `backend/system_dashboard_product_default.py` | +| HTTP | `backend/routers/app_dashboard.py` | +| Frontend-Registry | `frontend/src/widgetSystem/dashboardWidgetRegistry.jsx` | +| Registrierung | `frontend/src/widgetSystem/registerDashboardWidgets.js` | +| Layout-Editor | `frontend/src/pages/DashboardConfigurePage.jsx` | +| Fehler-Isolation | `frontend/src/widgetSystem/WidgetErrorBoundary.jsx` | +| Leitfaden | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` | +| Registry-Meta | [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) | + +--- + +## Modul + +**Dashboard Widgets** + +Erweiterbares System für **konfigurierbare Startübersicht**: Backend-Katalog definiert erlaubte Widget-IDs; Nutzer speichern Reihenfolge, Ein/Aus und optionale `config` pro Profil; Frontend rendert über eine lokale Komponenten-Registry. + +--- + +## Fachliche Verantwortung + +Das Modul übernimmt: + +1. **Widget-Katalog** — IDs, Titel, Beschreibung, optionale Feature-Anforderung (`requires_feature`). +2. **Layout-Persistenz** — `profiles.dashboard_layout` (JSON v1: `{ version, widgets[] }`). +3. **Validierung** — Erlaubte IDs, keine Duplikate, max. 32 Widgets, mindestens eines aktiv. +4. **Pro-Widget-Config** — Whitelist pro Widget-ID; Normalisierung beim Speichern. +5. **Standard-Layouts** — Code-Fallback (`DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS`), Admin-Override (`system_config`), Lab-Template (`DEFAULT_LAB_WIDGET_IDS`). +6. **Entitlements** — `allowed` im Katalog; Layout bereinigt bei fehlender Berechtigung. +7. **Frontend-Rendering** — Registry mappt Katalog-ID → React-Komponente + Props aus `layoutEntry.config`. +8. **Nutzer-Konfigurator** — „Übersicht anpassen“ (Sortieren, Toggle, Config-Editoren). + +Es übernimmt **nicht**: + +- Berechnung von KPIs, Charts, Scores (→ Data Layer + Chart-Endpoints, siehe [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md)) +- Tier-/Subscription-Logik in Widgets (→ Feature System, siehe [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)) +- Prompt-/KI-Ausführung (Widget zeigt nur UI; Pipeline läuft über eigene API) + +### Datenfluss (Happy Path) + +``` +WIDGET_CATALOG (Backend) + → GET /api/app/widgets/catalog (+ allowed via check_feature_access) + → GET /api/app/dashboard-layout + → coalesce_effective_layout (Profil oder Standard) + → merge_missing_catalog_widgets (neue IDs anhängen) + → apply_entitlements_to_layout_dict + → Frontend: ensureDashboardWidgetsRegistered() + → WidgetRenderer: enabled widgets → mapProps(layoutEntry.config) → Component + → PUT /api/app/dashboard-layout (Pydantic + Entitlements + speichern) +``` + +### Layout-Eintrag (Struktur) + +| Feld | Bedeutung | +|------|-----------| +| `id` | Muss in `WIDGET_CATALOG` existieren | +| `enabled` | Sichtbar auf der Übersicht | +| `config` | Optional; nur für whitelisted Widgets mit Inhalt erlaubt | + +--- + +## Administrierte vs. code-definierte Konfiguration + +| Konfiguration | Speicherort | Wer pflegt? | +|---------------|-------------|-------------| +| Widget-IDs, Metadaten, Default-Aktivierung | `widget_catalog.py` | Entwickler | +| Produkt-Standard-Layout (live) | `system_config.dashboard_product_default` | Admin | +| Produkt-Standard (Fallback) | `DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS` | Entwickler | +| Lab-/Editor-Standard | `DEFAULT_LAB_WIDGET_IDS` | Entwickler | +| Nutzer-Layout | `profiles.dashboard_layout` | Nutzer | +| Feature-Gate (Katalog) | `requires_feature` pro Eintrag | Entwickler | +| Feature-Gate (Override) | `widget_feature_requirements` + Marker | Admin | +| Config-Schema pro Widget | `dashboard_widget_config.py` | Entwickler | +| React-Komponente | `registerDashboardWidgets.js` | Entwickler | + +--- + +## Designprinzipien + +### 1. Backend-Katalog als Single Source of Truth für IDs + +| | | +|---|---| +| **Prinzip** | `WIDGET_CATALOG` ist die einzige autoritative Liste erlaubter Widget-IDs; `ALLOWED_WIDGET_IDS` wird daraus abgeleitet — nicht manuell duplizieren. | +| **Begründung** | Layout-Validator, API und Default-Layouts bleiben synchron; unbekannte IDs werden beim PUT abgewiesen. | +| **Quelle** | `widget_catalog.py`; Agent-Guide §4 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Frontend-Registry ist zweite manuelle Bindung (kein Build-Time-Gate). | + +### 2. Dual Registry: Backend-Kanon + Frontend-Komponentenbindung + +| | | +|---|---| +| **Prinzip** | Jede Katalog-ID braucht einen Eintrag in `registerDashboardWidget({ id, Component, mapProps })`; idempotent via `ensureDashboardWidgetsRegistered()`. | +| **Begründung** | React-Komponenten können nicht im Python-Katalog leben; explizite Zuordnung hält Bundle tree-shakeable. | +| **Quelle** | `registerDashboardWidgets.js`, `dashboardWidgetRegistry.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Fehlende Registrierung → Laufzeit „Unbekanntes Widget“, kein CI-Fail. | + +### 3. Layout als versioniertes Profil-JSON + +| | | +|---|---| +| **Prinzip** | Nutzer-Layout in `profiles.dashboard_layout`; Schema `version: 1`, Liste `{ id, enabled, config? }`. | +| **Begründung** | Pro Profil anpassbar; Reset auf NULL → System-Standard. | +| **Quelle** | `DashboardLayoutPayload`, `app_dashboard.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nur v1; Schema-Evolution braucht Migrationspfad. | + +### 4. Validierung an der API-Grenze (Pydantic) + +| | | +|---|---| +| **Prinzip** | Jeder GET/PUT-Pfad normalisiert über `DashboardLayoutPayload`: Duplikat-IDs, unbekannte IDs, leeres Layout (kein enabled) → Fehler. | +| **Begründung** | Keine korrupten Layouts in der DB; Frontend kann auf gültige Struktur vertrauen. | +| **Quelle** | `dashboard_layout_schema.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Ungültiges gespeichertes Layout → Fallback auf Standard (`coalesce_effective_layout`). | + +### 5. Config nur für explizit whitelisted Widgets + +| | | +|---|---| +| **Prinzip** | `WIDGETS_ALLOWING_CONFIG`: Widgets **ohne** Eintrag dürfen nur leere `config` haben; sonst Validierungsfehler. | +| **Begründung** | Verhindert unkontrollierte JSON-Blobs und stille Ignorierung unbekannter Keys. | +| **Quelle** | `dashboard_widget_config.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Pro Widget heterogene Schemas (chart_days vs. KPI-Tiles vs. show_*-Booleans). | + +### 6. Strikte Config-Keys (Whitelist, Normalisierung) + +| | | +|---|---| +| **Prinzip** | Unbekannte Keys in `config` werden abgelehnt; bekannte Keys typgeprüft und normalisiert (z. B. `chart_days` 7–90, KPI max. 9 Kacheln). | +| **Begründung** | Vorhersagbares Verhalten; Editor und Backend stimmen überein. | +| **Quelle** | `_validate_chart_days_only`, `_validate_kpi_board_config`, History-Viz-Defaults | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Frontend-Normalizer (`bodyChartDays.js`, `*VizConfig.js`) teils parallel — Abweichungsrisiko. | + +### 7. Config-Größenlimit + +| | | +|---|---| +| **Prinzip** | `MAX_WIDGET_CONFIG_JSON_BYTES` (3072) — keine großen Blobs in Layout-JSON. | +| **Begründung** | DB-Spalte und API-Payload bleiben schlank; Config = Präferenzen, nicht Datenspeicher. | +| **Quelle** | `dashboard_widget_config.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 8. Katalog-Erweiterung ohne Layout-Reset + +| | | +|---|---| +| **Prinzip** | `merge_missing_catalog_widgets` hängt neue Katalog-IDs ans bestehende Layout an (`enabled: false`). | +| **Begründung** | Nutzer müssen nach Deploy nicht resetten; „Übersicht anpassen“ zeigt neue Optionen. | +| **Quelle** | `dashboard_layout_schema.py`; Agent-Guide | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Reihenfolge neuer Widgets immer am Ende. | + +### 9. Mehrere Standard-Layouts (Produkt vs. Lab vs. Admin) + +| | | +|---|---| +| **Prinzip** | **Produkt:** `get_product_default_base_dict` (DB-Override oder `DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS`). **Lab:** `lab_default_layout_dict` für Editor/Reset. **Nutzer:** eigenes JSON oder NULL. | +| **Begründung** | Onboarding-Default getrennt von Entwickler-/Lab-Template; Admin kann Produkt-Standard ohne Deploy ändern. | +| **Quelle** | `system_dashboard_product_default.py`, `widget_catalog.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Feldname `lab_default_layout` historisch irreführend (Servertemplate, nicht nur Lab). | + +### 10. Entitlements zentral, Widgets konsumieren nur `allowed` + +| | | +|---|---| +| **Prinzip** | Sichtbarkeit über `check_feature_access` in `widget_id_allowed`; Katalog liefert `allowed` pro Zeile. Widgets/React duplizieren **keine** Tier-Logik. | +| **Begründung** | Eine Wahrheit für „darf angezeigt werden“; spätere Feature-Cluster ohne Widget-Refactor. | +| **Quelle** | `dashboard_widget_entitlements.py`; Agent-Guide §0 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Inhalts-Endpoints (Charts, KI) brauchen **eigenes** Feature-Gate (Defense in Depth). | + +### 11. Layout-Persistenz bereinigt nicht erlaubte Widgets + +| | | +|---|---| +| **Prinzip** | `apply_entitlements_to_layout_dict`: bei fehlender Berechtigung `enabled: false`; mindestens `welcome` bleibt aktiv. GET und PUT wenden an. | +| **Begründung** | Keine „gespeichert aber nie sichtbar“-Zombies; Downgrade/Tier-Wechsel degradieren gracefully. | +| **Quelle** | `dashboard_widget_entitlements.py`, `app_dashboard.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Policy ist deaktivieren, nicht entfernen — IDs bleiben im JSON. | + +### 12. DB-Override für Widget-Feature-Anforderungen + +| | | +|---|---| +| **Prinzip** | Katalog-`requires_feature` ist Default; Admin kann per `dashboard_widget_requirement_custom` + `widget_feature_requirements` überschreiben (AND-Semantik). | +| **Begründung** | Runtime-Anpassung ohne Code-Deploy; Marker-Zeile trennt Custom von Fallback. | +| **Quelle** | `widget_feature_requirements_db.py`, Migration 041 | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Zwei Quellen (Code + DB) — Dokumentation und Admin-UI nötig. | + +### 13. mapProps: Layout-Config → Komponenten-Props + +| | | +|---|---| +| **Prinzip** | Registry-Eintrag mappt `ctx.layoutEntry.config` auf typisierte Props (`chartDays`, `kpiConfig`, `bodyHistoryVizConfig`, …). | +| **Begründung** | Widget-Komponenten bleiben layout-agnostisch; Normalisierung an einer Stelle pro ID. | +| **Quelle** | `registerDashboardWidgets.js` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Teilweise Normalisierung in Widget statt in `mapProps` (inkonsistent, aber dokumentiert). | + +### 14. Refresh-Koordination über Context + +| | | +|---|---| +| **Prinzip** | `refreshTick` + `requestRefresh()` im Render-Context; Widgets laden Daten bei Tick-Änderung neu; Aktionen (z. B. Schnelleingabe) rufen `requestRefresh`. | +| **Begründung** | Kein globales State-Monster; gezielte Invalidierung nach Capture. | +| **Quelle** | `dashboardWidgetRegistry.jsx`, Widget-Implementierungen | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein feingranulares Cache pro Widget. | + +### 15. Fehler-Isolation pro Widget + +| | | +|---|---| +| **Prinzip** | `WidgetErrorBoundary` um jede Instanz — Render-Fehler crashen nicht die ganze Übersicht. | +| **Begründung** | Robuste PWA; ein defektes Chart blockiert nicht Gewicht-Eingabe. | +| **Quelle** | `WidgetErrorBoundary.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein automatisches Retry/Reporting. | + +### 16. Konfigurator filtert nach `allowed` + +| | | +|---|---| +| **Prinzip** | `DashboardConfigurePage` blendet Widgets mit `allowed === false` aus der bearbeitbaren Liste aus. | +| **Begründung** | Nutzer sehen keine Optionen, die sie nicht nutzen dürfen (Agent-Guide A2). | +| **Quelle** | `DashboardConfigurePage.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Bereits gespeicherte disabled Einträge können im JSON verbleiben. | + +### 17. Widgets konsumieren Data Layer, duplizieren keine Logik + +| | | +|---|---| +| **Prinzip** | Chart-/KPI-Widgets rufen Chart-Endpoints bzw. API-Fassaden auf; Berechnungen leben in `data_layer/`, nicht in Widget-JS. | +| **Begründung** | Gleiche Zahlen wie Verlauf, KI-Platzhalter und Export. | +| **Quelle** | Layer-2b `*_history_viz`-Widgets; [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy-Widgets unter `dashboard-widgets-legacy/` teils ältere Fetch-Pfade. | + +### 18. Dedizierte Config-Editoren für komplexe Widgets + +| | | +|---|---| +| **Prinzip** | Einfache `chart_days`: Set `CHART_DAYS_WIDGET_IDS` im Layout-Editor; komplexe Config: eigene Editor-Komponenten (`KpiBoardConfigEditor`, `*VizConfigEditor`). | +| **Begründung** | UX skaliert mit Config-Komplexität; Backend-Schema und Editor bleiben parallel pflegbar. | +| **Quelle** | `widgetSystem/*ConfigEditor.jsx`, Agent-Guide §3.4 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Jedes neue komplexe Widget = Editor + Validator + Tests. | + +--- + +## Nicht übernehmen + +1. **Tier-Logik in React-Widgets** — nur `allowed` aus API; keine hardcodierten Plan-Namen. + +2. **`ALLOWED_WIDGET_IDS` manuell pflegen** — immer aus Katalog ableiten. + +3. **Config ohne Backend-Whitelist** — stille Ignorierung unbekannter Keys in Widgets. + +4. **Nur UI-Gating ohne API-Absicherung** — Chart-/KI-/Export-Endpoints weiterhin `check_feature_access` (403). + +5. **Frontend-Registry vergessen** — Katalog-Eintrag ohne `registerDashboardWidget` → Laufzeitfehler statt Build-Fail. + +6. **Große Daten in `config`** — Layout ist Präferenzspeicher, kein Blob-Store (>3072 Bytes). + +7. **Doppelte Widget-IDs im Layout** — Validator verbietet; Editor muss dasselbe erzwingen. + +8. **Neue Katalog-IDs ohne `merge_missing_catalog_widgets`-Pfad** — Nutzer-Layouts veralten unsichtbar. + +9. **Kompletter Katalog nur in DB** — schwer testbar; Code-Katalog + optionale Feature-Overrides ist das Muster. + +10. **Evidence-Pflicht à la Placeholder-Registry** — 22 Metadatenfelder pro Widget wären Overkill; Tiefe an Risiko anpassen ([REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md)). + +11. **Ein Default für alles** — Produkt-Onboarding, Lab-Template und Admin-Override haben unterschiedliche Zwecke; nicht vermischen. + +12. **Fehlender Cross-Check Backend ↔ Frontend IDs** — empfohener Test/Gate fehlt im Ist-Stand; nicht als „optional“ ignorieren. + +13. **Berechnungslogik im Widget** — KPIs/Scores gehören in Data Layer, nicht in `useEffect`-Mathe. + +14. **Entitlements beim Speichern ablehnen statt deaktivieren** — Mitai wählt deaktivieren; Policy bewusst festlegen und dokumentieren. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/ +├── widget_catalog.py # WIDGET_CATALOG, DEFAULT_*_IDS +├── dashboard_layout_schema.py # Pydantic, merge_missing, defaults +├── dashboard_widget_config.py # WIDGETS_ALLOWING_CONFIG, Validatoren +├── dashboard_widget_entitlements.py # allowed, layout cleanup +├── widget_feature_requirements_db.py # Admin-Override +├── system_dashboard_product_default.py +└── routers/app_dashboard.py + +frontend/src/ +├── widgetSystem/ +│ ├── dashboardWidgetRegistry.jsx +│ ├── registerDashboardWidgets.js +│ ├── layoutEditor.js +│ ├── bodyChartDays.js, *VizConfig.js +│ └── *ConfigEditor.jsx +├── components/dashboard-widgets/ # Produkt-Widgets +├── components/dashboard-widgets-legacy/ # ältere Kern-Widgets +└── pages/DashboardConfigurePage.jsx + +DB: +├── profiles.dashboard_layout +├── system_config.dashboard_product_default +├── dashboard_widget_requirement_custom +└── widget_feature_requirements +``` + +**Katalog-Umfang:** ~24 Widget-IDs (Stand `widget_catalog.py`); ~13 mit konfigurierbarer `config`. + +--- + +## Verwandte Dokumentation + +- Agent-Guide (normativ): [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md) +- Registry-Meta: [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) +- Feature-Gates: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) +- Datenberechnung: [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) +- Architektur §9: `.claude/rules/ARCHITECTURE.md` + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1–6 | … | ✅ | +| 7 | Dashboard Widgets | ✅ dieses Dokument | +| 8 | Navigation / IA | ✅ | +| 9 | Migration & Deploy | ✅ | diff --git a/.claude/docs/jinkendo-foundation/design-principles/DATA_LAYER_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/DATA_LAYER_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..2278a89 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/DATA_LAYER_DESIGN_PRINCIPLES.md @@ -0,0 +1,359 @@ +# Data Layer – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Multi-Layer Data Architecture (Phase 0c, Issue #53) — keine Mitai-Gesamtarchitektur, keine konkrete Gesundheits-/Ernährungsfachlogik als Produktinhalt + +**Serie:** Designprinzipien für Produktfamilie · Dokument 2 von n +**Vorgänger:** [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Metriken (Layer 1) | `backend/data_layer/*_metrics.py`, `scores.py`, `correlations.py` | +| Utilities | `backend/data_layer/utils.py` | +| Visualisierung (Layer 2b) | `*_chart_payloads.py`, `*_viz.py` | +| KI-Formatierung (Layer 2a-Hilfe) | `prompt_output_compact.py` | +| Persistenz-Orchestrierung | `activity_persistence_orchestrator.py`, `activity_session_metrics.py` | +| Konsumenten | `routers/charts.py`, `placeholder_resolver.py`, `routers/exportdata.py` | +| Leitfäden | `DATA_LAYER_EXTENSION_GUIDE.md`, `docs/issues/issue-53-phase-0c-multi-layer-architecture.md` | +| Architektur-Regel Import-Grenze | `.claude/rules/ARCHITECTURE.md` §8 | + +--- + +## Modul + +**Data Layer** (Phase 0c Multi-Layer Architecture, Issue #53) + +Zentrale Schicht für **Datenabruf, Berechnung und strukturierte Aufbereitung** — ohne UI-Formatierung, ohne Prompt-Texte, ohne Chart.js-spezifische Ausgabe in den Kern-Metrik-Modulen. + +--- + +## Fachliche Verantwortung + +Der Data Layer ist die **Single Source of Truth für alle abgeleiteten Messwerte und Metriken**. Er übernimmt: + +1. **Datenabruf** — Lesen aus PostgreSQL (profile-scoped), optional mit Quality-Filter. +2. **Berechnung** — Trends, Scores, Korrelationen, Aggregationen, Projektionen. +3. **Strukturierte Rückgabe** — Dicts/Listen mit numerischen Werten, Datumsfeldern, Metadaten (`confidence`, `data_points`). +4. **Konsumenten-Bereitstellung** — Charts (Layer 2b), KI-Platzhalter (Layer 2a via Resolver), Export, Router-Anreicherung. + +Er übernimmt **nicht**: + +- CSV-Parsing und Feld-Mapping (Import-Schicht) +- Prompt-Template-Auflösung (Prompt Engine) +- React-Rendering oder Frontend-Berechnungen +- Autorisierung / Feature-Limits (Auth-Schicht) + +### Schichtenmodell (Multi-Layer) + +``` +┌─────────────────────────────────────────────────────────┐ +│ Layer 0: Persistenz (PostgreSQL) │ +│ weight_log, nutrition_log, activity_log, sleep_log, … │ +└──────────────────────────┬──────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────┐ +│ Layer 1: DATA LAYER (Metriken) │ +│ Strukturierte Daten · confidence · data_points │ +│ KEINE formatierten Strings · KEINE Chart.js-Objekte │ +└──────────────┬───────────────────────┬──────────────────┘ + │ │ + ▼ ▼ +┌──────────────────────────┐ ┌────────────────────────────┐ +│ Layer 2a: KI / Prompts │ │ Layer 2b: Visualisierung │ +│ placeholder_resolver │ │ *_chart_payloads, *_viz │ +│ prompt_output_compact │ │ routers/charts.py │ +└──────────────────────────┘ └────────────────────────────┘ +``` + +### Administrierte vs. code-definierte Konfiguration + +| Was | Wo | Administrierbar? | +|-----|-----|------------------| +| Berechnungslogik (Formeln, Fenster) | `data_layer/*.py` | ❌ Code + Review | +| Confidence-Schwellen | `data_layer/utils.py` | ❌ Code | +| Goal Mode / Focus Weights | DB (`profiles`, `user_focus_area_weights`) | ✅ Nutzer/Admin | +| Quality Filter (Profil) | DB (`profiles`) | ✅ Admin | +| Chart-Zeitfenster | Query-Parameter an API | ✅ Request | +| Referenzwerte (persönlich) | DB + `reference_values.py` | ✅ Nutzer | +| EAV Session Metrics | DB (`training_*_parameter`) | ✅ Admin | + +**Bewusst nicht hardcodiert in Routern:** Metrik-Berechnungen — Router delegieren an Data Layer. + +**Hardcodiert (Code):** Domänen-Module, Confidence-Regeln, Schwellen pro Metrik-Typ, TDEE-Fallback-Logik, Chart-Payload-Struktur. + +### Trennung: Metriken · Chart-Payloads · KI-Formatierung · Persistenz + +| Schicht | Module | Verantwortung | +|---------|--------|---------------| +| **Metriken** | `body_metrics.py`, `nutrition_metrics.py`, … | Reine Berechnung, strukturierte Dicts | +| **Chart-Payloads** | `nutrition_chart_payloads.py`, `correlation_chart_payloads.py`, … | Chart.js-kompatible `{ labels, datasets, metadata }` aus Layer-1-Daten | +| **Viz-Bundles** | `body_viz.py`, `fitness_viz.py`, … | Zusammengesetzte Dashboard-/History-Pakete für Frontend | +| **KI-Kompaktierung** | `prompt_output_compact.py` | Token-sparende Zahlen/JSON für Platzhalter | +| **Interpretation** | `*_interpretation.py`, `vital_signs_assessment.py` | Textliche Einordnung (WHO-Klassen etc.) — Grenze zu Layer 2a | +| **Persistenz-Orchestrator** | `activity_persistence_orchestrator.py` | Schreibpfade REST/CSV → DB + Nebenwirkungen (EAV, Eval) | + +### Konsumenten (wer ruft den Data Layer auf?) + +| Konsument | Muster | +|-----------|--------| +| `routers/charts.py` | Layer-1-Funktion + Chart-Payload-Builder | +| `placeholder_resolver.py` | Layer-1 → Formatierung/JSON für `{{placeholders}}` | +| `routers/exportdata.py` | `enrich_sessions_with_metrics`, `serialize_dates` | +| `routers/activity.py`, `csv_import.py` | `activity_persistence_orchestrator` (Schreiben) | +| `prompt_executor.execute_prompt_with_data` | ⚠️ teils Roh-SQL parallel zum Data Layer (Legacy) | + +### Rollen + +Der Data Layer hat **keine eigene Admin-UI**. Konfiguration erfolgt indirekt: + +- **Admin:** Training-Parameter, Attributprofile, Referenzwert-Typen, Quality-Filter +- **Nutzer:** Profildaten, Referenzwerte, Focus-Area-Gewichte (beeinflussen Scores) +- **Entwickler:** Neue Funktionen in `data_layer/` nach Extension Guide + +--- + +## Designprinzipien + +### 1. Single Source of Truth für Berechnungen + +| | | +|---|---| +| **Prinzip** | Jede Metrik wird **einmal** in `data_layer/` berechnet; Charts, KI und Export konsumieren dieselbe Funktion. | +| **Begründung** | Verhindert divergierende Zahlen zwischen Dashboard, Analyse und KI-Ausgabe. | +| **Quelle** | Issue #53 Executive Summary; `nutrition_chart_payloads.py` Kommentar „identisch zu GET /api/charts/energy-balance“ | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht alle Pfade migriert (`insights._prepare_template_vars`, `execute_prompt_with_data` Roh-SQL). | + +### 2. Layer 1 liefert strukturierte Daten, keine formatierten Strings + +| | | +|---|---| +| **Prinzip** | Kern-Metrik-Funktionen geben Dicts mit `float`/`int`/`date` zurück — **keine** Strings mit Einheiten („86,1 kg“). | +| **Begründung** | Formatierung ist konsumentenspezifisch (DE-Locale, Chart-Achsen, KI-Token). | +| **Quelle** | `data_layer/__init__.py` Docstring: „NO FORMATTING. NO STRINGS WITH UNITS.“ | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `placeholder_resolver` und `*_interpretation` Module formatieren teils direkt — Grenze Layer 1/2a nicht überall scharf. | + +### 3. Pflicht-Metadaten: confidence + data_points + +| | | +|---|---| +| **Prinzip** | Jede Metrik-Funktion liefert mindestens `confidence` (`high`\|`medium`\|`low`\|`insufficient`) und `data_points`. | +| **Begründung** | UI/KI können Datenqualität kommunizieren; Debugging und Monitoring vereinfacht. | +| **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` § Pflicht-Felder; `calculate_confidence()` in `utils.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht runtime-validiert; Disziplin per Code-Review. | + +### 4. Confidence nach Metrik-Typ und Zeitfenster + +| | | +|---|---| +| **Prinzip** | Schwellen unterscheiden `general`, `correlation`, `trend` und Fensterlänge (7d / 28d / 90d). | +| **Begründung** | Korrelationen brauchen mehr Paare; Trends messen Abdeckung (% der Tage). | +| **Quelle** | `data_layer/utils.py` → `calculate_confidence()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Schwellen global hardcodiert, nicht pro Metrik konfigurierbar. | + +### 5. Domänen-Module statt Monolith + +| | | +|---|---| +| **Prinzip** | Ein Python-Modul pro fachlichem Bereich (`body_metrics`, `nutrition_metrics`, …), max. ~500 Zeilen, dann Split. | +| **Begründung** | Wartbarkeit, klare Ownership, parallele Entwicklung. | +| **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` § Modul-Struktur | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Einige Module deutlich >500 Zeilen (Phase-0c-Wachstum). | + +### 6. Layer 2b: Chart-Payloads als Adapter + +| | | +|---|---| +| **Prinzip** | Chart.js-Strukturen leben in dedizierten `*_chart_payloads.py` / `*_viz.py`, nicht in Metrik-Modulen. | +| **Begründung** | Gleiche Metrik, verschiedene Visualisierungen; API-Endpoints bleiben dünn. | +| **Quelle** | `nutrition_chart_payloads.py`; `routers/charts.py` Imports | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Teilweise noch SQL-Duplikation in Payload-Buildern neben Layer-1-Aufruf. | + +### 7. Layer 2a-Hilfe: KI-spezifische Kompaktierung getrennt + +| | | +|---|---| +| **Prinzip** | Token-Reduktion für LLM-Kontext (`compact_float_for_prompt`, `compact_json_payload_for_prompts`) ist eigenes Modul, nicht in Metrik-Kern. | +| **Begründung** | KI hat andere Anforderungen als Charts (Präzision vs. Token-Kosten). | +| **Quelle** | `prompt_output_compact.py`; Tests in `tests/test_prompt_output_compact.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nur für KI-Pfad; Charts nutzen eigene Rundung. | + +### 8. Import-Grenze: Ingest vs. Interpretation + +| | | +|---|---| +| **Prinzip** | CSV-Import macht Mapping + Typkonvertierung + Duplikatlogik — **keine** fachliche Auswertung beim Insert. | +| **Begründung** | Semantik gehört in Layer 1+, sonst versteckte Business-Logik in Import-Adaptern. | +| **Quelle** | `ARCHITECTURE.md` §8; Issue #53 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy-Adapter (Apple-Schlaf-Aggregat, dedizierte Import-Endpoints) noch aktiv. | + +### 9. Persistenz-Orchestrator für Schreibpfade + +| | | +|---|---| +| **Prinzip** | Alle Schreibwege eines Domänenobjekts (REST, CSV, Legacy) laufen durch **einen** Orchestrator mit Nebenwirkungen (EAV, Evaluation). | +| **Begründung** | Konsistente Duplikat-Erkennung, Registry-Felder, keine divergierenden Insert-Logiken. | +| **Quelle** | `activity_persistence_orchestrator.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Bisher vor allem Aktivität; andere Domänen noch direkt in Routern. | + +### 10. Registry als Feld-Kanon (Activity) + +| | | +|---|---| +| **Prinzip** | Erlaubte persistierbare Felder für CSV/REST leiten sich aus `module_registry` ab, nicht aus Router-Hardcoding. | +| **Begründung** | Single Source of Truth für Import-Mappings und DB-Updates. | +| **Quelle** | `activity_data_canon.py`, `activity_persistence_orchestrator.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nur Activity vollständig; andere Module noch klassische Spalten-CRUD. | + +### 11. EAV-Anreicherung als Read-Layer + +| | | +|---|---| +| **Prinzip** | Session-Metriken (EAV) werden beim **Lesen** angereichert (`enrich_sessions_with_metrics`), nicht pro Consumer dupliziert. | +| **Begründung** | Ein Merge-Kanon für Liste, Detail, Export, Platzhalter. | +| **Quelle** | `activity_session_metrics.py`; `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Domänenspezifisch (Training); Muster übertragbar. | + +### 12. Scores als composable Layer + +| | | +|---|---| +| **Prinzip** | Composite Scores (`scores.py`) kombinieren Domänen-Metriken mit nutzer-spezifischen Focus Weights — keine Score-Logik in Routern. | +| **Begründung** | Goal-Mode-/Focus-abhängige Gewichtung zentral, für KI und Dashboard gleich. | +| **Quelle** | `data_layer/scores.py`; Phase-0b-Fokus-System | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Eng an Mitai-Zielsystem gekoppelt; Muster „gewichtete Composite Scores“ ist generisch. | + +### 13. API-First: Router delegieren, rechnen nicht + +| | | +|---|---| +| **Prinzip** | `routers/charts.py` und ähnliche Endpoints rufen Data-Layer-Funktionen auf und mappen auf HTTP — keine Trend-Berechnung im Router. | +| **Begründung** | Testbarkeit; Frontend ohne Business-Logik. | +| **Quelle** | `ARCHITECTURE.md` §1.2 API-First | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `charts.py` ist groß (2246+ Zeilen) — viel Adapter-Code, aber Berechnung delegiert. | + +### 14. serialize_dates / safe_float als Querschnitt + +| | | +|---|---| +| **Prinzip** | JSON/API-Serialisierung (Dates, Decimal) zentral in `utils.py`, nicht pro Modul neu erfunden. | +| **Begründung** | PostgreSQL-Typen (DATE, DECIMAL) konsistent für API und Export. | +| **Quelle** | `data_layer/utils.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 15. Extension Guide als verbindlicher Entwicklungsvertrag + +| | | +|---|---| +| **Prinzip** | Neue Metriken folgen Template (Retrieve → Confidence → Early Return → Calculate → Return) und werden in `__init__.py` exportiert. | +| **Begründung** | Einheitliche Struktur für 97+ Funktionen und wachsende Codebase. | +| **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Guide und Ist-Code divergieren teils (Modulgröße, `goals.py` noch nicht in `__init__`). | + +--- + +## Nicht übernehmen + +Muster, die sich nicht bewährt haben oder zu produktspezifisch sind: + +1. **Berechnungslogik in `placeholder_resolver.py`** — Phase-0b-Legacy; Resolver soll nur formatieren/aggregieren, nicht rechnen. + +2. **Paralleler Roh-SQL-Kontext in `prompt_executor.execute_prompt_with_data`** — lädt Modul-Rohdaten per SQL, obwohl Layer 1 existiert; zweite Wahrheit. + +3. **Legacy Insights-Pfad (`insights._prepare_template_vars`)** — eigene Variablen-Vorbereitung ohne Data Layer. + +4. **Import mit fachlicher Interpretation** — Apple-Schlaf-Aggregat und ähnliche Adapter verstecken Semantik im Ingest (Gitea #69). + +5. **Monolithische Router mit Inline-Berechnung** — vor Phase 0c; gelegentlich noch Reste in nicht migrierten Pfaden. + +6. **Interpretation vermischt mit Layer 1** — `*_interpretation.py` liefert teils fertige Texte; für Familien-Architektur klar als Layer 2a/2b markieren oder auslagern. + +7. **SQL-Duplikation in Chart-Payloads** — manche Payload-Builder führen eigene Queries statt ausschließlich Layer-1-Ergebnisse zu visualisieren. + +8. **Hardcodierte Confidence global** — funktioniert, aber nicht pro Metrik/Domäne konfigurierbar; Skalierung in Multi-Tenant-Produktfamilie prüfen. + +9. **Domänen-Module als Produktinhalt** — `body_metrics`, TDEE, WHR etc. sind Mitai-spezifisch; **Schichtenmodell** übernehmen, **Formeln** nicht blind kopieren. + +10. **Fehlende runtime-Validierung des Return-Schemas** — `confidence`/`data_points` per Konvention, nicht per TypedDict/Pydantic erzwungen. + +11. **Uneinheitliche Schreib-Orchestrierung** — nur Activity hat `persistence_orchestrator`; andere Domänen noch fragmentiert. + +12. **Riesige Einzeldateien** — einige Metrik-Module >>500 Zeilen widersprechen eigenem Extension Guide. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/data_layer/ +├── Kern-Metriken (Layer 1) +│ ├── body_metrics.py +│ ├── nutrition_metrics.py +│ ├── activity_metrics.py +│ ├── recovery_metrics.py +│ ├── health_metrics.py +│ ├── scores.py +│ └── correlations.py +├── Visualisierung (Layer 2b) +│ ├── *_chart_payloads.py (nutrition, recovery, correlation) +│ └── *_viz.py (body, nutrition, fitness, recovery, history_overview) +├── KI / Format (Layer 2a-Nähe) +│ ├── prompt_output_compact.py +│ └── *_interpretation.py +├── Persistenz / EAV +│ ├── activity_persistence_orchestrator.py +│ ├── activity_session_metrics.py +│ └── activity_data_canon.py +├── Querschnitt +│ ├── utils.py +│ ├── reference_values.py +│ └── nutrition_body_merge.py +└── __init__.py (Exports) +``` + +**Konsumenten-Endpoints (Auswahl):** 20+ Chart-Endpoints in `routers/charts.py` (E1–E5, A1–A8, R1–R5, C1–C4). + +--- + +## Verwandte Dokumentation + +- Issue #53 Abschluss: [issue-53-phase-0c-multi-layer-architecture.md](../../../../docs/issues/issue-53-phase-0c-multi-layer-architecture.md) +- Extension Guide: [DATA_LAYER_EXTENSION_GUIDE.md](../../technical/DATA_LAYER_EXTENSION_GUIDE.md) +- Fachliche Datenarchitektur: [DATA_ARCHITECTURE.md](../../functional/DATA_ARCHITECTURE.md) +- Import-Grenze: [ARCHITECTURE.md](../../../rules/ARCHITECTURE.md) §8 +- Platzhalter-Anbindung: [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md) +- Prompt Engine (Konsument Layer 2a): [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) +- Feature & Entitlement: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Datei (geplant) | +|---|-------|-----------------| +| 1 | Prompt Engine | ✅ `PROMPT_ENGINE_DESIGN_PRINCIPLES.md` | +| 2 | Data Layer | ✅ dieses Dokument | +| 3 | Feature & Entitlement System | ✅ `FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md` | +| 4 | Registry-/Plugin-Muster | ✅ `REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md` | +| 5 | Auth & Session | ✅ `AUTH_SESSION_DESIGN_PRINCIPLES.md` | +| 6 | Universal Import | ✅ `UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md` | +| 7 | Dashboard Widgets | ✅ `DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md` | +| 8 | Navigation / IA | ✅ `NAVIGATION_IA_DESIGN_PRINCIPLES.md` | +| 9 | Migration & Deploy | ✅ `MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md` | diff --git a/.claude/docs/jinkendo-foundation/design-principles/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..f2f3fcd --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md @@ -0,0 +1,370 @@ +# Feature & Entitlement System – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Membership-, Tier- und Feature-Limit-System (v9c) — keine Mitai-Domänenlogik, kein zentrales SSO/Stripe (Vision) + +**Serie:** Designprinzipien für Produktfamilie · Dokument 3 von n +**Vorgänger:** [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Entitlement-Auflösung | `backend/auth.py` (`get_effective_tier`, `check_feature_access`, `increment_feature_usage`) | +| Monitoring | `backend/feature_logger.py` | +| Nutzer-API | `backend/routers/subscription.py`, `backend/routers/features.py` | +| Admin | `routers/tiers_mgmt.py`, `tier_limits.py`, `coupons.py`, `access_grants.py`, `user_restrictions.py` | +| Widget-Gating | `backend/dashboard_widget_entitlements.py`, `widget_feature_requirements_db.py` | +| Frontend | `UsageBadge.jsx`, Feature-Usage in Seiten (z. B. `Analysis.jsx`, `WeightPage`) | +| Doku | `MEMBERSHIP_SYSTEM.md`, `FEATURE_ENFORCEMENT.md`, `CENTRAL_SUBSCRIPTION_SYSTEM.md` (Vision) | + +--- + +## Modul + +**Feature & Entitlement System** (Membership v9c) + +Zentrale Schicht für **„Darf dieser Nutzer diese Funktion wie oft nutzen?“** — unabhängig von Auth (Identität) und unabhängig von fachlicher Business-Logik in Routern. + +--- + +## Fachliche Verantwortung + +Das System übernimmt: + +1. **Feature-Registry** — Deklarative Liste aller limitierbaren Produktfunktionen mit Metadaten. +2. **Tier-Auflösung** — Effektiver Tarif eines Profils (Basis-Tier + zeitliche Grants). +3. **Limit-Auflösung** — Pro Feature: Override → Tier-Limit → Feature-Default. +4. **Usage-Tracking** — Zähler für Count-Features mit optionalem Reset (daily/monthly/never). +5. **Enforcement** — HTTP 403 bei Überschreitung; Frontend-Vorschaum via Badges. +6. **Beobachtbarkeit** — Strukturiertes JSON-Logging aller Access-Checks. +7. **Promotionen** — Coupons → Access Grants (temporäre Tier-Elevation, Pause/Resume). + +Es übernimmt **nicht**: + +- Login, Session, Passwort (Auth-Modul) +- Zahlungsabwicklung / Stripe (geplant, `CENTRAL_SUBSCRIPTION_SYSTEM.md`) +- Mandanten-Isolation (Org/Workspace) — Entitlements sind **profile-scoped** +- Inhaltliche Berechtigung pro Datensatz (nur Feature-Gates) + +### Zwei Entscheidungsebenen + +| Ebene | Frage | Funktion | +|-------|--------|----------| +| **Tier** | Welcher Tarif gilt? | `get_effective_tier()` | +| **Feature** | Darf Feature X genutzt werden (wie oft)? | `check_feature_access()` | + +Tier beeinflusst Feature-Limits über `tier_limits`; User-Overrides können Limits unabhängig vom Tier setzen. + +### Administrierte Konfigurationen + +| Konfiguration | Speicherort | Admin-UI | +|---------------|-------------|----------| +| Feature-Definitionen | `features` | Admin Features | +| Tier-Stufen | `tiers` | Admin Tiers | +| Tier × Feature Matrix | `tier_limits` | Admin Tier Limits | +| User-Overrides | `user_feature_restrictions` | Admin User Restrictions | +| Coupons | `coupons`, `coupon_redemptions` | Admin Coupons | +| Temporäre Tier-Grants | `access_grants` | (via Coupon/Admin) | +| Widget → Feature Mapping | `widget_feature_requirements`, Katalog | Admin Widget Features | +| Usage-Zähler | `user_feature_usage` | (automatisch) | + +**Nicht hardcodiert:** Limits pro Tier, Feature-Metadaten, Coupon-Parameter, User-Overrides. + +**Hardcodiert (Code):** Feature-IDs in Routern (`'ai_calls'`, `'weight_entries'`, …), Reset-Berechnung, 4-Phasen-Muster, 11 initial registrierte Features. + +### Auflösungs-Hierarchien + +**Effektiver Tier** (`get_effective_tier`): + +1. Aktiver `access_grants`-Eintrag (`is_active`, `valid_from`/`valid_until`) +2. Fallback: `profiles.tier` + +**Feature-Limit** (`check_feature_access` → `_check_impl`): + +1. `user_feature_restrictions.limit_value` (höchste Priorität) +2. `tier_limits` für effektiven Tier +3. `features.default_limit` + +**Limit-Semantik:** + +| `limit_type` | Bedeutung | +|--------------|-----------| +| `count` | Zählbares Kontingent; `used < limit` | +| `boolean` | An/Aus; `limit == 1` erlaubt, `0` gesperrt | + +| `limit_value` | Bedeutung | +|---------------|-----------| +| `NULL` | Unbegrenzt | +| `0` | Deaktiviert | +| `> 0` | Kontingent oder Boolean „an“ | + +### Rollen + +| Rolle | Darf | +|-------|------| +| **Admin** | Features/Tiers/Limits/Coupons/Restrictions CRUD; alle Nutzer-Overrides | +| **Nutzer** | Eigene Subscription/Usage lesen (`/subscription/me`, `/features/usage`); keine Limit-Änderung | + +Enforcement gilt für alle authentifizierten Nutzer gleich — Admins haben keine automatische Bypass-Logik in `check_feature_access`. + +### Versionierung, Freigabe, Test + +| Mechanismus | Status | +|-------------|--------| +| 4-Phasen-Rollout (Monitor → UI → Enforce) | ✅ dokumentiert & angewendet | +| JSON-Log `feature-usage.log` | ✅ Phase 2 Monitoring | +| DB-Migration v9c für Schema | ✅ | +| Automatisierte Enforcement-Tests pro Router | ⚠️ teilweise (Widgets getestet) | +| Zentrale Policy „jeder Endpoint muss checken“ | ❌ nicht erzwungen | + +--- + +## Designprinzipien + +### 1. Feature-Registry statt hardcodierter Limits + +| | | +|---|---| +| **Prinzip** | Jedes limitierbare Produkt-Feature ist Zeile in `features` — neue Features ohne Schema-Migration für Limits. | +| **Begründung** | Admin-UI, Usage-API und Backend-Checks teilen dieselbe ID und Metadaten. | +| **Quelle** | `MEMBERSHIP_SYSTEM.md` § Feature-Registry; `routers/features.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Feature-IDs müssen trotzdem in Router-Code referenziert werden. | + +### 2. Eine Auflösungsfunktion für Entitlements + +| | | +|---|---| +| **Prinzip** | Alle Backend- und Widget-Checks rufen `check_feature_access(profile_id, feature_id)` auf. | +| **Begründung** | Keine duplizierte Tier/Limit-Logik in Routern, Widgets oder Frontend. | +| **Quelle** | `auth.py`; `dashboard_widget_entitlements.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht alle Endpoints nutzen es (z. B. `/prompts/execute` fehlt). | + +### 3. Getrennte Tier- und Feature-Auflösung + +| | | +|---|---| +| **Prinzip** | `get_effective_tier()` für Tarif; `check_feature_access()` für konkretes Feature — Tier ist Input, nicht Output der Feature-Prüfung. | +| **Begründung** | Temporäre Grants heben Tier an; User-Override kann einzelnes Feature unabhängig anpassen. | +| **Quelle** | `auth.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `get_effective_tier` im Code einfacher als in `MEMBERSHIP_SYSTEM.md` (kein `tier_locked`, Trial nicht in Tier-Funktion). | + +### 4. Prioritäts-Kette für Limits + +| | | +|---|---| +| **Prinzip** | User-Override > Tier-Limit > Feature-Default — explizit und dokumentiert. | +| **Begründung** | Support/Beta-Fälle ohne Tier-Wechsel; vorhersehbares Verhalten. | +| **Quelle** | `_check_impl()` in `auth.py`; `MEMBERSHIP_SYSTEM.md` § Zugriffs-Hierarchie | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `user_feature_restrictions.enabled` im Schema, aber nicht in `_check_impl` ausgewertet. | + +### 5. Count vs. Boolean als zwei Feature-Klassen + +| | | +|---|---| +| **Prinzip** | Zählbare Aktionen (`count` + Usage) vs. Schalter-Features (`boolean`, kein Counter). | +| **Begründung** | Pipeline-An/Aus vs. monatliche KI-Calls — unterschiedliche UX und Backend-Logik. | +| **Quelle** | `features.limit_type`; `FEATURE_ENFORCEMENT.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Boolean-Features nutzen `limit_value` 0/1 — leicht mit Count zu verwechseln. | + +### 6. Reset-Perioden für Count-Features + +| | | +|---|---| +| **Prinzip** | `reset_period`: `never` \| `daily` \| `monthly` — Counter-Reset in `check_feature_access` bei abgelaufenem `reset_at`. | +| **Begründung** | Monats-Kontingente vs. Lifetime-Limits in einem Modell. | +| **Quelle** | `auth.py` → `_calculate_next_reset()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Reset beim Check, nicht per Cron — Edge Cases bei seltenem Zugriff. | + +### 7. Usage nur bei neuen Entitäten incrementieren + +| | | +|---|---| +| **Prinzip** | `increment_feature_usage()` nur nach **INSERT**, nicht nach UPDATE/Upsert-Deduplikat. | +| **Begründung** | Limits messen „neue Nutzung“, nicht Bearbeitung bestehender Daten. | +| **Quelle** | `FEATURE_ENFORCEMENT.md` § Wichtige Regeln | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Bulk-Import muss explizit zählen; Fehler anfällig. | + +### 8. Vier-Phasen-Rollout (Observe before Enforce) + +| | | +|---|---| +| **Prinzip** | Phase 1 Cleanup → 2 Logging → 3 Frontend-Badges → 4 HTTP 403. | +| **Begründung** | Limits einführen ohne blind Nutzer zu blockieren; Daten für Limit-Kalibrierung. | +| **Quelle** | `FEATURE_ENFORCEMENT.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Disziplin pro Feature; kein zentraler Feature-Flag pro Endpoint-Phase. | + +### 9. Strukturiertes Feature-Logging + +| | | +|---|---| +| **Prinzip** | Jeder Check: `log_feature_usage(profile_id, feature_id, access, action)` → JSON in `feature-usage.log`. | +| **Begründung** | Audit, Debugging, Kalibrierung — auch wenn noch nicht enforced. | +| **Quelle** | `feature_logger.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Log-Pfad `/app/logs` container-spezifisch. | + +### 10. Defense in Depth: API 403 + Frontend-Gate + +| | | +|---|---| +| **Prinzip** | Backend blockiert autoritativ; Frontend zeigt `UsageBadge`, deaktiviert Buttons, Tooltip bei Limit. | +| **Begründung** | UX (frühes Feedback) + Sicherheit (API nicht umgehbar via curl). | +| **Quelle** | `FEATURE_ENFORCEMENT.md`; `UsageBadge.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Frontend-Gate optional pro Seite; nicht generisch erzwungen. | + +### 11. Nutzer-Usage-API ohne Code-Änderung bei neuen Features + +| | | +|---|---| +| **Prinzip** | `GET /features/usage` iteriert alle aktiven `features` und ruft `check_feature_access` pro Zeile. | +| **Begründung** | Neues DB-Feature erscheint automatisch in Quota-Übersicht. | +| **Quelle** | `routers/features.py` → `get_feature_usage()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Frontend muss Feature-ID kennen, um Badge zu binden. | + +### 12. Access Grants für temporäre Tier-Elevation + +| | | +|---|---| +| **Prinzip** | Coupons/Admin erzeugen `access_grants`; effektiver Tier steigt zeitlich begrenzt. | +| **Begründung** | Promotions, Partner (Wellpass), Trials ohne permanente Tier-Änderung. | +| **Quelle** | `access_grants`; `routers/coupons.py` (Pause/Resume) | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Coupon-Stacking-Logik komplex; dokumentiert vs. Code prüfen bei Neuentwicklung. | + +### 13. Entitlements als Querschnitt für UI-Module (Widgets) + +| | | +|---|---| +| **Prinzip** | Dashboard-Widgets mappen auf `features.id`; Katalog liefert `allowed` pro Profil. | +| **Begründung** | Tier-Logik nicht in React-Widgets duplizieren (`DASHBOARD_WIDGETS_AGENT_GUIDE` §0). | +| **Quelle** | `dashboard_widget_entitlements.py`; `ARCHITECTURE.md` §9 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Widget-Sichtbarkeit ≠ API-Schutz — Chart-Endpoints brauchen eigenes Gating (A4). | + +### 14. Admin-konfigurierbare Tier × Feature Matrix + +| | | +|---|---| +| **Prinzip** | `tier_limits` trennt Tier-Definition von Limits; Tiers ohne hardcodierte Spalten pro Feature. | +| **Begründung** | Neue Tiers/Preise ohne Code-Deploy der Limit-Logik. | +| **Quelle** | `MEMBERSHIP_SYSTEM.md` § Tier-System; `tier_limits` Tabelle | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Tier-Namen in Seed-Daten (`free`, `premium`, …) — erweiterbar, aber Konvention. | + +### 15. NULL = unlimited, 0 = disabled + +| | | +|---|---| +| **Prinzip** | Einheitliche Semantik für Limit-Werte in allen Schichten. | +| **Begründung** | Vermeidet Sonderfälle „-1 means unlimited“; klare Admin-UI. | +| **Quelle** | `_check_impl()` in `auth.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | SQL NULL vs. Python None — konsistent, aber in UI erklärungsbedürftig. | + +--- + +## Registrierte Features (Referenz) + +| Feature ID | Typ | Reset | Typische Aktion | +|------------|-----|-------|-----------------| +| `weight_entries` | count | never | Gewicht anlegen | +| `circumference_entries` | count | never | Umfang anlegen | +| `caliper_entries` | count | never | Caliper anlegen | +| `activity_entries` | count | monthly | Training anlegen/import | +| `nutrition_entries` | count | monthly | Ernährung anlegen/import | +| `photos` | count | monthly | Foto hochladen | +| `ai_calls` | count | monthly | KI-Analyse | +| `ai_pipeline` | boolean | — | Pipeline-Analyse | +| `data_export` | count | monthly | Export/PDF | +| `data_import` | count | monthly | ZIP/Universal-Import | + +**Enforcement-Lücken (Ist):** `routers/prompts.py` (`/execute`, `/execute-stream`) ohne `check_feature_access` — Legacy `insights.py` hat Enforcement für `ai_calls`/`ai_pipeline`. + +--- + +## Nicht übernehmen + +1. **Dokumentations-Drift** — `MEMBERSHIP_SYSTEM.md` („Enforcement deaktiviert“) vs. `FEATURE_ENFORCEMENT.md` (Phase 4 komplett) vs. Ist-Code; bei Neuentwicklung einen Kanon festlegen. + +2. **Unvollständige Tier-Auflösung** — Doku beschreibt `tier_locked`, Trial-in-Tier; Code nutzt primär Grants + `profiles.tier`. Trial (`trial_ends_at`) eher UI-Banner als Tier-Engine. + +3. **Feature-IDs in Routern verstreut** — kein zentraler Endpoint-Registry-Eintrag „welcher Router prüft welches Feature“. + +4. **Check und Increment nicht atomar** — Race bei parallelen Requests möglich; kein DB-Level Locking. + +5. **Legacy Profil-Spalten parallel** — `ai_enabled`, `ai_limit_day`, `export_enabled` in Sessions-Query neben Feature-System. + +6. **Frontend ohne Backend-Gate** — reine UI-Deaktivierung ohne 403 ist unsicher (manche Seiten nur teilweise umgesetzt). + +7. **Boolean via limit_value 0/1** — funktioniert, aber für Familien-Architektur explizites `enabled`-Flag oder Capability-Tokens erwägen. + +8. **Unused Schema-Felder** — `user_feature_restrictions.enabled` nicht in Auflösung eingebunden. + +9. **App-lokales Abo ohne Zahlungsanbindung** — Stripe/SSO nur Vision (`CENTRAL_SUBSCRIPTION_SYSTEM.md`); nicht als fertiges Familien-Muster übernehmen. + +10. **Profile als Entitlement-Subject** — kein Org/Mandant; Multi-App-Familie braucht separates Identity/Subscription-Boundary. + +11. **Self-hosted Tier als Sonderfall** — `selfhosted` ist Deploy-Modell, kein generisches SaaS-Tier-Muster. + +12. **Increment-Schleifen bei Bulk** — `for _ in range(new_entries): increment_feature_usage()` — ineffizient; batch-Inkrement besser. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/ +├── auth.py # get_effective_tier, check_feature_access, increment_feature_usage +├── feature_logger.py # JSON-Logging +├── dashboard_widget_entitlements.py # Widget allowed + Layout-Sanitisierung +├── widget_feature_requirements_db.py +└── routers/ + ├── subscription.py # /me, /usage, /limits (Nutzer) + ├── features.py # Admin CRUD + /usage, /check-access + ├── tiers_mgmt.py, tier_limits.py + ├── coupons.py, access_grants.py + └── user_restrictions.py + +frontend/src/components/ +└── UsageBadge.jsx # Quota-Anzeige (Phase 3) +``` + +**DB (v9c):** `features`, `tiers`, `tier_limits`, `user_feature_restrictions`, `user_feature_usage`, `coupons`, `coupon_redemptions`, `access_grants`, `user_activity_log` + +--- + +## Verwandte Dokumentation + +- Membership-Detail: [MEMBERSHIP_SYSTEM.md](../../technical/MEMBERSHIP_SYSTEM.md) +- Enforcement-Howto: [FEATURE_ENFORCEMENT.md](../../architecture/FEATURE_ENFORCEMENT.md) +- Vision Produktfamilie: [CENTRAL_SUBSCRIPTION_SYSTEM.md](../../technical/CENTRAL_SUBSCRIPTION_SYSTEM.md) +- Widget-Gating: [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md) §0 +- Prompt Engine (Enforcement-Lücke): [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1 | Prompt Engine | ✅ | +| 2 | Data Layer | ✅ | +| 3 | Feature & Entitlement | ✅ dieses Dokument | +| 4 | Registry-/Plugin-Muster | ✅ | +| 5 | Auth & Session | ✅ | +| 6 | Universal Import | ✅ | +| 7 | Dashboard Widgets | ✅ | +| 8 | Navigation / IA | ✅ | +| 9 | Migration & Deploy | ✅ `MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md` | diff --git a/.claude/docs/jinkendo-foundation/design-principles/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..ce10396 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md @@ -0,0 +1,369 @@ +# Migration & Deploy – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** DB-Migrationen, Container-Startup, CI/CD-Deploy — keine Anwendungsdomäne + +**Serie:** Designprinzipien für Produktfamilie · Dokument 9 von n (Abschluss) +**Vorgänger:** [NAVIGATION_IA_DESIGN_PRINCIPLES.md](./NAVIGATION_IA_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| DB-Init & Migrationen | `backend/db_init.py`, `backend/startup.sh` | +| Migrationen | `backend/migrations/XXX_*.sql` | +| Basis-Schema (Greenfield) | `backend/schema.sql` | +| Tracking | Tabelle `schema_migrations` | +| Compose Prod/Dev | `docker-compose.yml`, `docker-compose.dev-env.yml` | +| CI/CD | `.gitea/workflows/deploy-dev.yml`, `deploy-prod.yml`, `test.yml` | +| Versionierung | `backend/version.py` (`APP_VERSION`, `DB_SCHEMA_VERSION`, `MODULE_VERSIONS`) | +| Doku (operativ) | `MIGRATIONS.md` | +| Architektur-Regeln | `.claude/rules/ARCHITECTURE.md` §2, §7 | + +--- + +## Modul + +**Migration & Deploy** + +Automatische **PostgreSQL-Schema-Evolution** beim Container-Start plus **Git-getriebene Deploy-Pipeline** (develop → Dev, main → Prod) auf selbst-gehosteter Infrastruktur (Docker auf Raspberry Pi). + +--- + +## Fachliche Verantwortung + +Das Modul übernimmt: + +1. **Schema-Migrationen** — Nummerierte SQL-Dateien, idempotent wo möglich, getrackt in `schema_migrations`. +2. **Startup-Orchestrierung** — Postgres ready → Schema/Migrationen → optional SQLite-Import → Uvicorn. +3. **Umgebungstrennung** — Dev (`3099`/`8099`) vs. Prod (`3002`/`8002`), getrennte DBs/Volumes. +4. **Deploy-Automatisierung** — Push auf Branch → Runner → `git reset --hard` → `docker compose build --no-cache` → Health-Check. +5. **Post-Deploy-Tests** — Pytest/Lint/Frontend-Build gegen **deployed** Container auf dem Runner. +6. **Versions-Metadaten** — App-/Modul-Version und dokumentierte `DB_SCHEMA_VERSION`. + +Es übernimmt **nicht**: + +- Fachliche Datenberechnungen (→ Data Layer) +- Automatisches Downgrade/Rollback von Schema +- Blue-Green oder Multi-Region-Deploy + +### Deploy-Pipeline (Happy Path) + +``` +Entwickler: commit → push develop + → Gitea Runner: deploy-dev.yml + → cd /home/lars/docker/bodytrack-dev + → git fetch + reset --hard origin/develop + → docker compose -f docker-compose.dev-env.yml build --no-cache && up -d + → backend startup.sh → db_init.py (Migrationen) + → curl localhost:8099/api/auth/status + → test.yml (push + nach Deploy): pytest im Container, py_compile, npm run build + +Prod: PR develop → main → deploy-prod.yml (Port 8002, bodytrack/) +``` + +--- + +## Administrierte vs. code-definierte Konfiguration + +| Konfiguration | Speicherort | Wer pflegt? | +|---------------|-------------|-------------| +| Migration-SQL | `backend/migrations/` | Entwickler | +| Welche Migrationen angewendet | `schema_migrations` (DB) | Automatisch | +| Greenfield-Basis | `schema.sql` | Entwickler (selten) | +| Compose/Ports/Env | `docker-compose*.yml`, `.env` auf Server | Betrieb | +| Deploy-Workflow | `.gitea/workflows/*.yml` | Entwickler | +| App-Version / Changelog | `backend/version.py` | Entwickler (pro Release) | +| Prod-Geheimnisse | Server-`.env`, nicht im Repo | Betrieb | + +--- + +## Designprinzipien + +### 1. Migrationen beim Container-Start ( nicht manuell in Prod) + +| | | +|---|---| +| **Prinzip** | `startup.sh` ruft `db_init.py` auf **bevor** Uvicorn startet; pending Migrationen werden automatisch angewendet. | +| **Begründung** | Kein vergessenes Schema-Update; Deploy und DB-Stand bleiben gekoppelt. | +| **Quelle** | `startup.sh`, `db_init.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Fehlgeschlagene Migration blockiert API-Start (`sys.exit(1)`). | + +### 2. Nummeriertes Datei-Pattern als Gate + +| | | +|---|---| +| **Prinzip** | Nur `\d{3}_*.sql` wird ausgeführt (z. B. `054_activity_session_metrics_eav.sql`); alles andere wird ignoriert. | +| **Begründung** | Sortierbare Reihenfolge; Ad-hoc-Skripte (`check_features.sql`, `v9c_*.sql`) verunreinigen nicht den Lauf. | +| **Quelle** | `run_migrations()` Regex | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy-Dateien ohne Nummer liegen noch im Ordner (historischer Ballast). | + +### 3. Tracking-Tabelle als Single Source of „applied“ + +| | | +|---|---| +| **Prinzip** | `schema_migrations(filename)` — jede erfolgreiche Datei genau einmal eingetragen; Pending = Dateien minus Applied. | +| **Begründung** | Idempotenter Startup; wiederholter Container-Start wendet nichts doppelt an. | +| **Quelle** | `ensure_migration_table`, `apply_migration` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein checksum — geänderte Datei nach Apply wird nicht erneut ausgeführt. | + +### 4. Alphabetische Reihenfolge = Migrations-Reihenfolge + +| | | +|---|---| +| **Prinzip** | `sorted(glob)` — dreistellige Präfixe (`001`, `054`, `061`) definieren die Apply-Order. | +| **Begründung** | Einfach, git-freundlich, keine separate Migrations-Registry. | +| **Quelle** | `run_migrations()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nummern-Kollisionen oder nachträgliches Einfügen erfordern Disziplin (immer nächste freie Nummer). | + +### 5. Fail-Fast bei Migrationsfehler + +| | | +|---|---| +| **Prinzip** | Schlägt eine Migration fehl → kein Commit in Tracking (bei Exception vor INSERT), Prozess exit 1, Container unhealthy. | +| **Begründung** | API läuft nicht mit halb angewendetem Schema. | +| **Quelle** | `apply_migration`, `main` in `db_init.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Manueller Recovery-Prozess nötig (siehe MIGRATIONS.md Rollback). | + +### 6. Greenfield: schema.sql, Bestand: nur Migrationen + +| | | +|---|---| +| **Prinzip** | Existiert `profiles` nicht → einmalig `schema.sql` laden; danach nur noch nummerierte Migrationen. | +| **Begründung** | Frische Instanz schnell bootstrapped; langlebige DBs evolvieren incremental. | +| **Quelle** | `check_table_exists`, `load_schema` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `schema.sql` kann hinter Migrationen zurückfallen wenn nicht gepflegt. | + +### 7. Idempotente DDL bevorzugen + +| | | +|---|---| +| **Prinzip** | `ADD COLUMN IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, defensive UPDATEs — Migration soll mehrfach ausführbar sein ohne Schaden. | +| **Begründung** | Recovery nach partiellem Apply; manuelles Re-Run sicherer. | +| **Quelle** | `MIGRATIONS.md` Best Practices | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht alle Änderungen sind idempotent (DROP, irreversible Datenmigration). | + +### 8. Kein psql-Meta in Migrationsdateien + +| | | +|---|---| +| **Prinzip** | Nur SQL — kein `\echo`, `\i`, `\connect`; Ausführung via psycopg2, nicht interaktiv. | +| **Begründung** | Parser/Runner versteht nur SQL-Statements. | +| **Quelle** | `MIGRATIONS.md`, `apply_migration` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 9. Schema-Änderung = nummerierte Migration (nie ad-hoc in Prod) + +| | | +|---|---| +| **Prinzip** | Neue Tabellen/Spalten **nur** via `backend/migrations/XXX_*.sql`; nicht direkt in laufender Prod-DB editieren. | +| **Begründung** | Reproduzierbarkeit Dev→Prod; Review im Git-Diff. | +| **Quelle** | ARCHITECTURE.md, CLAUDE.md | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Agent-Regel — technisch nicht erzwungen. | + +### 10. DB_SCHEMA_VERSION als dokumentierter Marker + +| | | +|---|---| +| **Prinzip** | `backend/version.py` → `DB_SCHEMA_VERSION` bei Schema-Änderung manuell bumpen (Format z. B. `YYYYMMDD` + Suffix). | +| **Begründung** | API `/api/version` und Changelog zeigen Schema-Stand unabhängig von App-Minor. | +| **Quelle** | ARCHITECTURE.md §2.6 | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Nicht automatisch aus `schema_migrations` abgeleitet — Drift möglich. | + +### 11. Branch → Umgebung (develop / main) + +| | | +|---|---| +| **Prinzip** | `develop` → Dev-Deploy automatisch; `main` → Prod-Deploy automatisch; Prod nur nach expliziter Freigabe/Merge. | +| **Begründung** | Klare Promotion; Dev als Integrationsumgebung. | +| **Quelle** | Workflows, CLAUDE.md Deployment | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein Staging-Branch zwischen Dev und Prod. | + +### 12. Deploy-Arbeitskopie = exakt Remote-Branch + +| | | +|---|---| +| **Prinzip** | Runner: `git fetch` + `git reset --hard origin/` — keine `pull`-Merge-Konflikte, kein schmutziger `package-lock` auf dem Pi. | +| **Begründung** | Reproduzierbarer Deploy-Baum; Fix aus GUI-IA-Abnahme 2026-04-05. | +| **Quelle** | `deploy-prod.yml`, `deploy-dev.yml` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Lokale Hotfixes auf dem Server werden beim Deploy überschrieben. | + +### 13. Immutabler Build pro Deploy (`--no-cache`) + +| | | +|---|---| +| **Prinzip** | `docker compose build --no-cache` bei jedem Deploy — frisches Image aus Dockerfile + Repo-Stand. | +| **Begründung** | Keine veralteten Layer; Migrationen und Code garantiert im Image. | +| **Quelle** | Deploy-Workflows | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Langsamere Deploys; kein Registry-basiertes Image-Promotion. | + +### 14. Health-Check nach Deploy + +| | | +|---|---| +| **Prinzip** | Nach `up -d`: kurz warten, dann `curl -sf …/api/auth/status` (8099 Dev / 8002 Prod). | +| **Begründung** | Minimale Smoke-Verification dass API antwortet (inkl. DB-Init durchlaufen). | +| **Quelle** | Deploy-Workflows | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Prüft nicht fachliche Endpoints oder Migration-Inhalt. | + +### 15. Persistente Volumes für Daten und Fotos + +| | | +|---|---| +| **Prinzip** | Postgres-Daten, `/app/data`, `/app/photos` in benannten/external Volumes — überleben Container-Rebuild. | +| **Begründung** | Deploy = neues Image, nicht Datenverlust. | +| **Quelle** | `docker-compose*.yml` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Volume-Backup/Restore ist Betriebsaufgabe außerhalb Repo. | + +### 16. Postgres Healthcheck vor Backend-Start + +| | | +|---|---| +| **Prinzip** | `depends_on: condition: service_healthy` — Backend startet erst wenn DB `pg_isready`. | +| **Begründung** | `wait_for_postgres` in db_init ist zweite Absicherung; reduziert Race beim ersten Start. | +| **Quelle** | Compose-Files | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 17. Tests gegen deployed Stack (Self-Hosted Runner) + +| | | +|---|---| +| **Prinzip** | `test.yml` führt pytest **im laufenden Backend-Container** auf dem Pi aus, nicht in isolierter GitHub-Cloud. | +| **Begründung** | Tests laufen gegen echte Dev/Prod-Compose-Umgebung des Projekts. | +| **Quelle** | `.gitea/workflows/test.yml` | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Prod-Deploy triggert Tests auf Prod-Pfad — Risiko wenn Tests schreibend; `-m 'not slow'` begrenzt Laufzeit. | + +### 18. Feste Ports pro Umgebung + +| | | +|---|---| +| **Prinzip** | Dev `3099/8099`, Prod `3002/8002` — nicht ändern (Reverse Proxy/Fritz!Box hängen daran). | +| **Begründung** | Externe URLs (`dev.mitai.jinkendo.de`, `mitai.jinkendo.de`) stabil. | +| **Quelle** | CLAUDE.md, Compose | +| **Tragfähigkeit** | **hoch** (betriebsspezifisch) | +| **Einschränkung** | Andere Projekte brauchen eigene Port-Matrix. | + +### 19. Prod-Schutz: Deploy nur über Git + +| | | +|---|---| +| **Prinzip** | Keine direkten Prod-Container-/DB-Schreibzugriffe für Automation; Prod-Änderung = Merge `main` → Workflow. | +| **Begründung** | Audit-Trail, Review, keine Drift. | +| **Quelle** | ARCHITECTURE.md §7.1, `/deploy` Command | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Menschlicher SSH-Zugriff bleibt möglich — Prozess, nicht Technik. | + +### 20. Versions-Bump als Release-Disziplin + +| | | +|---|---| +| **Prinzip** | Jede lieferbare Änderung: `APP_VERSION`, betroffene `MODULE_VERSIONS`, `CHANGELOG` in `version.py`; bei Schema auch `DB_SCHEMA_VERSION`. | +| **Begründung** | `/api/version`, Support, Korrelation Deploy ↔ Code. | +| **Quelle** | ARCHITECTURE.md §2.5, `deploy.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Frontend-`version.js` in Spec erwähnt, im Repo teils nicht vorhanden — Dual-Bump unvollständig. | + +--- + +## Nicht übernehmen + +1. **Unnummerierte Migrationsdateien** — `v9c_*.sql`, `check_*.sql` werden nicht auto-applied; nicht als Vorbild. + +2. **Migration-Datei nach Apply ändern** — Tracking verhindert Re-Run; neue Nummer statt Edit. + +3. **Automatischer Downgrade** — nicht implementiert; Rollback manuell + Tracking-Eintrag löschen. + +4. **Direktes Schema in Prod** — immer Git-Migration + Deploy. + +5. **Breaking DROP ohne Koordination** — App-Code und Migration in einem Release. + +6. **psql-Metacommands in `.sql`** — bricht Python-Runner. + +7. **`git pull` auf Deploy-Server** — Merge-Schmutz; `reset --hard` ist das Muster. + +8. **Prod-Deploy ohne Dev-Validierung** — develop-First ist implizite Policy. + +9. **Schema-Drift ohne `DB_SCHEMA_VERSION`-Bump** — dokumentarische Lücke. + +10. **Cached Docker-Build als Default** — Mitai wählt Reproduzierbarkeit über Geschwindigkeit. + +11. **Migrationen außerhalb Container-Start vergessen** — manuelles psql in Prod als Normalfall. + +12. **Hardcoded Seed-Daten in Migration** — produktive User/Secrets nicht in SQL. + +13. **Port-Änderung „nebenbei“** — Infrastruktur-Kopplung. + +14. **Tests nur lokal, nie auf Runner-Stack** — Mitai testet bewusst post-deploy im Pi-Container (Trade-off verstehen). + +15. **Transaktionssteuerung in SQL-Datei** — Runner committet pro Datei; komplexe multi-step Rollbacks nicht eingebaut. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/ +├── db_init.py # wait, schema, run_migrations, sqlite import +├── startup.sh # db_init → uvicorn +├── schema.sql # Greenfield +├── migrations/ # 001–061+ nummeriert (+ Legacy ohne Nummer) +└── version.py # APP_VERSION, DB_SCHEMA_VERSION, MODULE_VERSIONS + +docker-compose.yml # Prod: 3002/8002 +docker-compose.dev-env.yml # Dev: 3099/8099 + +.gitea/workflows/ +├── deploy-dev.yml # push develop +├── deploy-prod.yml # push main +└── test.yml # pytest, lint, npm build on Pi + +Server (Pi): +/home/lars/docker/bodytrack-dev/ # develop +/home/lars/docker/bodytrack/ # main +``` + +**Migrationen (Stand):** 60+ nummerierte Dateien (`001` … `061`); höchste Nummer im Repo prüfen vor neuer Migration. + +--- + +## Verwandte Dokumentation + +- Operativ: [MIGRATIONS.md](../../technical/MIGRATIONS.md) +- Architektur: `.claude/rules/ARCHITECTURE.md` §2 (Versionierung), §7 (Prod-Schutz) +- Deploy-Command: `.claude/commands/deploy.md`, `merge-to-prod.md` +- Import/Migration-Grenze: [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](./UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md) +- Auth auf Prod: [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md) + +--- + +## Serie – Übersicht (abgeschlossen) + +| # | Modul | Status | +|---|-------|--------| +| 1 | Prompt Engine | ✅ `PROMPT_ENGINE_DESIGN_PRINCIPLES.md` | +| 2 | Data Layer | ✅ `DATA_LAYER_DESIGN_PRINCIPLES.md` | +| 3 | Feature & Entitlement | ✅ `FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md` | +| 4 | Registry / Plugin (Meta) | ✅ `REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md` | +| 5 | Auth & Session | ✅ `AUTH_SESSION_DESIGN_PRINCIPLES.md` | +| 6 | Universal Import | ✅ `UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md` | +| 7 | Dashboard Widgets | ✅ `DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md` | +| 8 | Navigation / IA | ✅ `NAVIGATION_IA_DESIGN_PRINCIPLES.md` | +| 9 | Migration & Deploy | ✅ dieses Dokument | diff --git a/.claude/docs/jinkendo-foundation/design-principles/NAVIGATION_IA_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/NAVIGATION_IA_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..6771a52 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/NAVIGATION_IA_DESIGN_PRINCIPLES.md @@ -0,0 +1,346 @@ +# Navigation & Informationsarchitektur – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** App-Navigation, Bereichs-Shells, Admin-IA, Responsive Shell — keine Seiteninhalte oder Domänenlogik + +**Serie:** Designprinzipien für Produktfamilie · Dokument 8 von n +**Vorgänger:** [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](./DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Hauptnavigation | `frontend/src/config/appNav.js` | +| Erfassung | `frontend/src/config/captureNav.js`, `layouts/CaptureShell.jsx` | +| Einstellungen | `frontend/src/config/settingsNav.js`, `layouts/SettingsShell.jsx` | +| Admin | `frontend/src/config/adminNav.js`, `layouts/AdminShell.jsx`, `RequireAdmin.jsx` | +| KI-Analyse (Kategorien) | `frontend/src/config/analysisCategories.js`, `pages/Analysis.jsx` | +| Routing | `frontend/src/App.jsx` | +| Desktop-Sidebar | `frontend/src/components/DesktopSidebar.jsx` | +| Responsive CSS | `frontend/src/app.css` (`--nav-h`, `.bottom-nav`, `.analysis-split`, `.desktop-sidebar`) | +| Abnahme-Doku | `docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` | +| Responsive-Spec | `.claude/docs/functional/RESPONSIVE_UI.md` | + +--- + +## Modul + +**Navigation & Informationsarchitektur (IA)** + +Schichtenmodell für die PWA: **eine primäre Hauptnavigation** (6–7 Bereiche), darunter **Bereichs-Shells** mit eigener Sub-Navigation, getrennte **Admin-Realm**, **Auth-Gates** und **ein Breakpoint** für Mobile vs. Desktop. + +--- + +## Fachliche Verantwortung + +Das Modul übernimmt: + +1. **Hauptnav-SSoT** — Reihenfolge, Labels, Icons, Admin-Sichtbarkeit (`getMainNavItems`). +2. **Routing-Struktur** — Welche URL gehört zu welchem Bereich (Übersicht, Erfassen, Verlauf, Ziele, Analyse, Einstellungen, Admin). +3. **Sub-Navigation pro Bereich** — Capture-Hub, Settings-Tabs, Admin-Gruppen, Analyse-Kategorien. +4. **Layout-Muster** — Bottom-Nav (mobil), Sidebar (Desktop), `analysis-split` für tiefe Bereiche. +5. **Zugriffskontrolle (UI)** — `RequireAdmin`, Admin-Link nur bei `role === 'admin'`. +6. **Active-State** — Nested Routes (Erfassung unter `/capture`, Admin unter `/admin/*`). +7. **PWA-Tauglichkeit** — Safe Area, Scrollbare Bottom-Nav, Content-Padding. + +Es übernimmt **nicht**: + +- Backend-Autorisierung (→ [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md)) +- Feature-Entitlements in der Nav (Tier-Gates an Endpoints/Widgets, nicht an jedem NavLink) +- Inhaltliche Tab-Logik innerhalb von Verlauf/Analyse (Seiten concern) + +### IA-Modell (Nutzerperspektive) + +| Ebene | Mental Model | Beispiel-Routen | +|-------|--------------|-----------------| +| **Primär** | Wo bin ich in der App? | `/`, `/capture`, `/history`, `/goals`, `/analysis`, `/settings` | +| **Sekundär (Shell)** | Was mache ich in diesem Bereich? | `/weight`, `/admin/g/features`, `/settings/dashboard-layout` | +| **Tertiär (Seite)** | Tabs/Filter innerhalb einer Maske | Verlauf-Tabs, Analyse-Kategorien | + +### Strategisch vs. taktisch (Ziele) + +| Ebene | Ort | Zweck | +|-------|-----|-------| +| **Strategisch** | `/goals` (Hauptnav) | Ziele definieren, Prioritäten, Focus Areas | +| **Taktisch** | `/custom-goals` (Erfassung) | Tägliche Ist-Werte für eigene Ziele | +| **Auswertung** | `/history` | Trends, Charts, Vergleiche | + +--- + +## Administrierte vs. code-definierte Konfiguration + +| Konfiguration | Speicherort | Wer pflegt? | +|---------------|-------------|-------------| +| Hauptnav-Reihenfolge & Labels | `appNav.js` | Entwickler | +| Erfassungs-Kacheln & Shell-Nav | `captureNav.js` | Entwickler | +| Admin-Gruppen & Hub-Karten | `adminNav.js` | Entwickler | +| Settings-Subnav | `settingsNav.js` | Entwickler | +| Analyse-Kategorie-Reihenfolge | `analysisCategories.js` | Entwickler | +| KI-Prompt-Kategorien (Runtime) | DB `ai_prompts.category` | Admin (Prompts) | +| React-Routes | `App.jsx` | Entwickler (muss zu Nav-Configs passen) | + +--- + +## Designprinzipien + +### 1. Eine Quelle für die Hauptnavigation + +| | | +|---|---| +| **Prinzip** | `getMainNavItems(isAdmin)` liefert dieselbe Item-Liste für **Bottom-Nav** und **Desktop-Sidebar** — keine parallelen Hardcodings. | +| **Begründung** | Reihenfolge und Labels bleiben synchron; Admin-Conditional an einer Stelle. | +| **Quelle** | `appNav.js`, `App.jsx`, `DesktopSidebar.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Active-State-Logik ist in zwei Dateien dupliziert (`navItemActive` / `sidebarLinkActive`). | + +### 2. Feste primäre IA-Reihenfolge (Produkt-Story) + +| | | +|---|---| +| **Prinzip** | Übersicht → Erfassen → Verlauf → **Ziele** → Analyse → Einstellungen → [Admin] — spiegelt Nutzerfluss: sehen → eingeben → auswerten → steuern → interpretieren → konfigurieren. | +| **Begründung** | Ziele als eigener Hauptpunkt (nicht unter Analyse versteckt); klare Trennung Capture vs. History vs. Analysis. | +| **Quelle** | `GUI_IA_ADMIN_NAV_2026-04-05.md`; `appNav.js` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Produkt-spezifisch; andere Apps können andere Reihenfolge brauchen. | + +### 3. Config-Dateien pro Bereich (Nav-as-Data) + +| | | +|---|---| +| **Prinzip** | Sub-Navigation lebt in dedizierten Config-Modulen (`captureNav`, `adminNav`, `settingsNav`, `analysisCategories`) — Shell-Komponenten iterieren nur. | +| **Begründung** | Neue Erfassungsmaske = Eintrag in Config + Route; kein Nav-HTML in jeder Page. | +| **Quelle** | `captureNav.js` Kommentar „Pfade müssen mit Routes übereinstimmen“ | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein Build-Time-Check Config ↔ Routes. | + +### 4. Bereichs-Shells für tiefe Navigation + +| | | +|---|---| +| **Prinzip** | Capture, Settings und Admin nutzen **Shell-Layouts** mit ``; Nutzer wechselt Sub-Bereiche ohne Hauptnav zu verlassen. | +| **Begründung** | Erfassung hat 12+ Masken — wären als Hauptnav-Einträge unbrauchbar. | +| **Quelle** | `CaptureShell`, `SettingsShell`, `AdminShell` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Verlauf und Analyse haben eigene Tab-Muster (kein gemeinsames Shell-Config). | + +### 5. Wiederverwendbares `analysis-split`-Layout + +| | | +|---|---| +| **Prinzip** | Admin, Settings und KI-Analyse teilen CSS-Muster: mobil horizontale Chips, Desktop linke Spalte + `__main` für Inhalt. | +| **Begründung** | Ein visuelles Muster für „Kategorie links, Arbeit rechts“; weniger UI-Drift. | +| **Quelle** | `AdminShell.jsx`, `SettingsShell.jsx`, `Analysis.jsx`, `app.css` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Capture nutzt eigenes `capture-shell` (Emoji-Icons, Hub-Kacheln). | + +### 6. Admin: Gruppen in der Shell, Seiten über Hub + +| | | +|---|---| +| **Prinzip** | Shell-Nav zeigt nur **Admin-Gruppen** (+ Übersicht); konkrete Seiten als Karten auf `/admin/g/:groupId`. | +| **Begründung** | Skaliert bei wachsender Admin-Oberfläche; keine 20er-Sidebar. | +| **Quelle** | `adminNav.js` (`ADMIN_GROUPS`, `getAdminShellNavEntries`) | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Ein Klick mehr als flache Nav; bewusster Trade-off. | + +### 7. Admin als eigener Realm + +| | | +|---|---| +| **Prinzip** | `/admin/*` hinter `RequireAdmin`; kein Admin-Block mehr in Einstellungen; Profil-Anlage nur Admin → Benutzerverwaltung. | +| **Begründung** | Trennung Nutzer- vs. Betreiber-Kontext; weniger Verwechslung. | +| **Quelle** | `RequireAdmin.jsx`, `GUI_IA_ADMIN_NAV_2026-04-05.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | UI-Guard ersetzt nicht Backend-`require_admin` auf APIs. | + +### 8. Route-Guard mit Nutzer-Feedback + +| | | +|---|---| +| **Prinzip** | Nicht-Admin auf `/admin` → Redirect `/` mit `state.adminDenied`; Dashboard zeigt Hinweis. | +| **Begründung** | Stilles Scheitern vermeiden; klare Erwartung. | +| **Quelle** | `RequireAdmin.jsx`, `Dashboard.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 9. Nested Active-State für Section-Prefixes + +| | | +|---|---| +| **Prinzip** | Custom Active-Logik: `/capture` aktiv bei allen Erfassungs-Pfaden; `/admin` bei gesamten Admin-Baum; `/goals` mit `end: true` (exakt). | +| **Begründung** | React-Router `end` allein reicht für Section-Gruppen nicht. | +| **Quelle** | `navItemActive`, `adminShellEntryIsActive` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Neue Section-Prefixes brauchen explizite Regel. | + +### 10. Erfassungs-Hub + direkte Deep-Links + +| | | +|---|---| +| **Prinzip** | `/capture` = Kachel-Hub; jede Maske auch direkt erreichbar (`/weight`, …); Shell-Nav immer sichtbar. | +| **Begründung** | Onboarding über Hub; Power-User/Dashboard-Links springen direkt. | +| **Quelle** | `CaptureHub`, `CAPTURE_HUB_TILES` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Hub und Shell-Nav listen dieselben Ziele (Doppelpflege). | + +### 11. Einstellungen: nur aktives Profil + +| | | +|---|---| +| **Prinzip** | Settings = Self-Service für **aktives** Profil (Name, E-Mail, Avatar, Quality-Filter); keine Profil-Liste für Endnutzer. | +| **Begründung** | Multi-Profil-Verwaltung ist Admin-Aufgabe; reduziert Komplexität. | +| **Quelle** | `SettingsPage.jsx`, IA-Doku | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Session-bound Profile-Id-Schwäche bleibt Backend-Thema. | + +### 12. Settings-Subnav für Layout & Export + +| | | +|---|---| +| **Prinzip** | Konfiguration schwerer Features (Dashboard-Layout, PDF-Berichte, Referenzwerte) als eigene Settings-Routen unter Shell — nicht in „Allgemein“ verstecken. | +| **Begründung** | Entspricht [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](./DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md) (Nutzer-Konfigurator). | +| **Quelle** | `settingsNav.js` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Admin-Dashboard-Default liegt unter `/admin/...` (getrennte Rolle). | + +### 13. KI-Analyse: Ergebnis im Hauptspalt + +| | | +|---|---| +| **Prinzip** | Neue Analyse-Ergebnisse rendern in `analysis-split__main`, nicht in der Kategorie-Nav — Nav bleibt wählbar. | +| **Begründung** | Lange Ergebnisse verdrängen sonst die Prompt-Auswahl (Mobile). | +| **Quelle** | `GUI_IA_ADMIN_NAV_2026-04-05.md`, `Analysis.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 14. Ein Breakpoint Mobile / Desktop (1024px) + +| | | +|---|---| +| **Prinzip** | `< 1024px`: Bottom-Nav + Mobile-Header; `≥ 1024px`: Desktop-Sidebar, Bottom-Nav ausgeblendet, breiterer Content. **Kein** separates Tablet-Layout. | +| **Begründung** | Einfache Spec, PWA-first; iPad im Portrait = Mobile-Verhalten. | +| **Quelle** | `RESPONSIVE_UI.md`, `app.css` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Große Phones und kleine Tablets identisch behandelt. | + +### 15. PWA Safe Area für Bottom-Navigation + +| | | +|---|---| +| **Prinzip** | `--nav-h`, `--nav-pad-top`, `env(safe-area-inset-bottom)` auf `.bottom-nav`; Content-`padding-bottom` inkl. Nav-Höhe; horizontal scrollbare Nav bei vielen Items. | +| **Begründung** | iPhone Home-Indicator und Notch — kein Clipping, kein verdeckter Content. | +| **Quelle** | `app.css`, IA-Doku | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Safe Area nur auf Nav/Content-Padding, nicht global überall. | + +### 16. Auth-Routen außerhalb der App-Shell + +| | | +|---|---| +| **Prinzip** | Login, Register, Verify, Reset-Password rendern **ohne** Bottom-Nav/Sidebar — minimale Vollbild-Cards. | +| **Begründung** | Keine Navigation ohne Session; klarer Fokus. | +| **Quelle** | `App.jsx` (early returns vor `AppShell`) | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Public Routes nicht zentral in einer Route-Config. | + +### 17. Rollen-sichtbare Nav-Einträge (UI only) + +| | | +|---|---| +| **Prinzip** | Admin-Link erscheint nur wenn `isAdmin`; Backend schützt APIs separat. | +| **Begründung** | Progressive disclosure; normale Nutzer sehen keinen toten Link. | +| **Quelle** | `getMainNavItems(isAdmin)` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Security nicht durch Ausblenden ersetzt. | + +### 18. Deep-Link-State für Verlauf + +| | | +|---|---| +| **Prinzip** | Nav zu `/history` setzt optional `state: { tab: 'overview' }` — konsistenter Einstieg von Hauptnav. | +| **Begründung** | Verlauf merkt sich Tabs; Hauptnav soll nicht zufälligen alten Tab öffnen. | +| **Quelle** | `App.jsx`, `DesktopSidebar.jsx`, `History.jsx` | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Nur für History implementiert, nicht app-weit. | + +--- + +## Nicht übernehmen + +1. **Hauptnav an mehreren Stellen hardcoden** — immer `appNav.js`. + +2. **Admin-Funktionen in Einstellungen** — eigener `/admin`-Bereich. + +3. **Alle Erfassungsmasken in die Bottom-Nav** — Shell + Hub skaliert. + +4. **Alle Admin-Seiten in der Shell-Sidebar** — Hub-Gruppen-Muster beibehalten. + +5. **Nav-Config ohne Route-Pflege** — jeder neue Pfad: Config + `App.jsx` + ggf. Active-State. + +6. **UI-Admin-Guard ohne Backend-Guard** — `RequireAdmin` ist UX, APIs brauchen `require_admin`. + +7. **Zwei Tablet-/Desktop-Breakpoints** — Mitai: ein Cut bei 1024px. + +8. **Safe Area ignorieren** — PWA auf iOS bricht sonst an Bottom-Nav. + +9. **Profil-Liste für Endnutzer in Settings** — Multi-Profil = Admin. + +10. **Feature-Tier-Logik in Nav-Komponenten** — Entitlements an Widgets/APIs ([FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)). + +11. **Inkonsistente Layout-Muster pro Bereich** — wo `analysis-split` passt, nicht neues Ad-hoc-Layout erfinden. + +12. **Orphan-Routes ohne Nav-Ergänzung** — z. B. `/subscription`, `/workflow-editor/:id` existieren außerhalb Haupt-IA; bewusst dokumentieren, nicht unkontrolliert multiply. + +13. **Active-State nur per Router-Default** — Section-Prefixes (`/capture/*`, `/admin/*`) brauchen explizite Regeln. + +14. **Analyse-Ergebnisse in der Nav-Spalte** — verdrängt Prompt-Auswahl auf Mobile. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +frontend/src/config/ +├── appNav.js # Hauptnav (6 + Admin) +├── captureNav.js # Erfassungs-Hub + Shell +├── settingsNav.js # Settings-Subnav +├── adminNav.js # ADMIN_GROUPS, Shell-Entries +└── analysisCategories.js # KI-Analyse-Gruppen + +frontend/src/layouts/ +├── CaptureShell.jsx +├── SettingsShell.jsx +├── AdminShell.jsx +└── RequireAdmin.jsx + +frontend/src/components/ +└── DesktopSidebar.jsx + +frontend/src/App.jsx # Routes + Bottom-Nav + Auth-Gates +frontend/src/app.css # Shell, split, safe-area, 1024px breakpoint +``` + +**Hauptnav (7 Einträge mit Admin):** Übersicht · Erfassen · Verlauf · Ziele · Analyse · Einstellungen · Admin + +**Admin-Gruppen (8):** users · features · subscription · training · goals · prompts · system + +--- + +## Verwandte Dokumentation + +- Abnahme-Stand: [GUI_IA_ADMIN_NAV_2026-04-05.md](../../../../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md) +- Responsive-Spec: [RESPONSIVE_UI.md](../../functional/RESPONSIVE_UI.md) +- Auth/Session: [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md) +- Dashboard-Konfigurator: [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](./DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md) +- Gitea #30 (Responsive UI, teilweise erledigt) + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1–7 | … | ✅ | +| 8 | Navigation / IA | ✅ dieses Dokument | +| 9 | Migration & Deploy | ✅ | diff --git a/.claude/docs/jinkendo-foundation/design-principles/PROMPT_ENGINE_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/PROMPT_ENGINE_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..7ce5360 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/PROMPT_ENGINE_DESIGN_PRINCIPLES.md @@ -0,0 +1,305 @@ +# Prompt Engine – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Modul „Prompt Engine“ (Unified Prompt System, Issue #28) — keine Mitai-Gesamtarchitektur, keine Domänenlogik (Gesundheit, Ernährung, Messwerte) + +**Serie:** Designprinzipien für Produktfamilie · Dokument 1 von n + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Executor | `backend/prompt_executor.py`, `backend/workflow_executor.py` | +| Platzhalter | `backend/placeholder_resolver.py`, `backend/placeholder_registry.py`, `backend/placeholder_registrations/` | +| API | `backend/routers/prompts.py`, `backend/routers/workflows.py` | +| Admin-UI | `frontend/src/pages/AdminPromptsPage.jsx`, `UnifiedPromptModal.jsx`, `WorkflowEditorPage.jsx` | +| Fachliche Spec | `.claude/docs/functional/AI_PROMPTS.md` | +| Platzhalter-Governance | `.claude/docs/technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md`, `docs/PLACEHOLDER_GOVERNANCE.md` | + +--- + +## Modul + +**Prompt Engine** (Unified Prompt System, Issue #28) + +Backend-Kern: `prompt_executor.py`, `placeholder_resolver.py`, `placeholder_registry` / `placeholder_registrations/`, `workflow_executor.py` +API: `routers/prompts.py` +Admin-UI: `AdminPromptsPage`, `UnifiedPromptModal`, `WorkflowEditorPage` + +--- + +## Fachliche Verantwortung + +Die Prompt Engine ist die **zentrale Ausführungs- und Konfigurationsschicht für KI-Analysen**. Sie übernimmt: + +1. **Prompt-Orchestrierung** — Laden aktiver Prompt-Definitionen aus der DB und Ausführung als `base` (Einzelprompt), `pipeline` (mehrstufig) oder `workflow` (Graph). +2. **Kontextaufbereitung** — Befüllen von Platzhaltern mit profilbezogenen Daten (Resolver → Data Layer). +3. **LLM-Aufruf** — Einheitlicher OpenRouter-Call über injizierte Callback-Funktion. +4. **Ergebnisbehandlung** — JSON-Validierung, strukturierte Container (Fragenergänzungen), Debug-Metadaten, optionales Speichern in `ai_insights`. +5. **Admin-Konfiguration** — CRUD für Prompts/Workflows, Import/Export, Vorschau und Test ohne Produktions-Ausführung. + +### Administrierte Konfigurationen + +| Konfiguration | Speicherort | Inhalt | +|---------------|-------------|--------| +| Prompt-Metadaten | `ai_prompts` | `name`, `slug`, `category`, `active`, `sort_order`, `display_name` | +| Templates | `ai_prompts` | `template` | +| Pipeline-Stages | `ai_prompts.stages` (JSONB) | Stages mit `inline` / `reference` | +| Workflow-Graphen | `ai_prompts.graph_data` | Knoten, Kanten, Metadaten | +| Output-Regeln | `ai_prompts` | `output_format`, `output_schema` | +| Fragenergänzungen | `ai_prompts.question_augmentations` | Optionale Standard-Fragen (Hybridmodell: Knoten > Prompt) | +| System-Reset | `ai_prompts` | `is_system_default`, `default_template` | +| Legacy-Pipeline-Configs | `pipeline_configs` | Module, Zeiträume, Stage-Slugs (parallel zum Unified System) | +| Workflow-Fragenkatalog | `workflow_question_catalog` | Fragetypen, Templates, Normalisierung | + +### Bewusst nicht hardcodiert + +- Prompt-Texte, Pipeline-Zusammensetzung, Workflow-Topologie +- Kategorie, Sichtbarkeit (`active`), Sortierung +- Output-Format und Schema pro Prompt +- Referenz vs. Inline in Pipeline-Stages + +### Hardcodiert (Code / Env) + +- Platzhalter-Definitionen und Resolver (`PLACEHOLDER_MAP`, Registry) +- LLM-Modell (`OPENROUTER_MODEL`) +- Default-Module und -Zeiträume in `/prompts/execute` +- Domänen-Kategorien im Frontend (`analysisCategories.js`) +- Meta-Prompts für Generate/Optimize (Admin-Tooling) + +### Trennung: Template · Platzhalter · Kontext · Workflow + +| Schicht | Ort | Rolle | +|---------|-----|-------| +| **Templates** | `ai_prompts.template`, `stages`, Knoten-Templates im Graph | Was an die KI geht | +| **Platzhalter** | `placeholder_resolver` + Registry | Semantische API-Keys `{{key}}`, Resolver-Funktionen | +| **Kontextdaten** | `execute_prompt_with_data` + Resolver → `data_layer/` | Werte für Platzhalter | +| **Workflows** | `graph_data` + `workflow_executor` | Ausführungsgraph, Verzweigung, Join, Aggregation | + +### Durchsetzung: keine Sonderlogik außerhalb der Engine + +**Konzeptionell:** Ein Executor (`execute_prompt` → `execute_prompt_with_data`) als Single Entry Point. + +**Praktisch unvollständig:** Legacy-Pfade in `insights.py` (`/insights/run`, `/insights/pipeline`) mit eigener Template-Logik (`_prepare_template_vars`, `_render_template`) und direkten LLM-Calls; `History.jsx` nutzt noch `runInsight`. Kein technischer Guard (Lint/Policy), nur Konvention. + +### Rollen und Berechtigungen + +| Rolle | Darf | +|-------|------| +| **Admin** (`require_admin`) | Prompts/Workflows/Pipeline-Configs CRUD, Import/Export, Reset-to-default, Generate/Optimize, Platzhalter-Metadaten-ZIP | +| **Nutzer** (`require_auth`) | Aktive Prompts listen (ohne Pipeline-Slugs), ausführen (`/prompts/execute`), Preview, Platzhalter-Katalog, eigene Werte exportieren | + +Workflow-Editor-Route (`/workflow-editor/:id`) ist nicht hinter `RequireAdmin`; Schreib-APIs sind admin-geschützt. + +### Versionierung, Freigabe, Test + +| Mechanismus | Status | +|-------------|--------| +| Prompt-Versionsverlauf in DB | ❌ Overwrite | +| Reset-to-default für System-Prompts | ✅ `is_system_default` + `default_template` | +| JSON Import/Export (Dev→Prod) | ✅ `/export-all`, `/import` | +| Admin-Test mit Debug | ✅ `debug=true`, UnifiedPromptModal | +| Preview ohne LLM | ✅ `POST /preview` | +| Platzhalter-Deprecation-Prozess | 📄 dokumentiert, nicht runtime-erzwungen | +| Formales Freigabe-Workflow | ❌ | +| Executor-E2E-Tests | ⚠️ punktuell (Modifier, Output-Compact) | + +--- + +## Designprinzipien + +### 1. Single Executor für Prompt-Ausführung + +| | | +|---|---| +| **Prinzip** | Alle KI-Analysen laufen über `execute_prompt` / `execute_prompt_with_data`. | +| **Begründung** | Einheitliche Platzhalter-Auflösung, Debug, JSON-Validierung, Speicher-Metadaten. | +| **Quelle** | `backend/prompt_executor.py`; `POST /api/prompts/execute`; `Analysis.jsx` → `executeUnifiedPromptStream` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy `insights.py` und teils `History.jsx` umgehen den Executor noch. | + +### 2. Konfigurierbare Prompt-Bibliothek statt fest verdrahteter Texte + +| | | +|---|---| +| **Prinzip** | Prompt-Inhalte und Workflows liegen in `ai_prompts`, nicht im Anwendungscode. | +| **Begründung** | Admins können Analysen anpassen, duplizieren, deaktivieren, ohne Deploy. | +| **Quelle** | Migration 020; `UnifiedPromptCreate`/`Update` in `models.py`; `AdminPromptsPage.jsx` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Parallel existieren noch `pipeline_configs` und hardcodierte Default-Module/Zeiträume. | + +### 3. Drei Prompt-Typen mit klarer Verantwortung + +| | | +|---|---| +| **Prinzip** | `base` = wiederverwendbarer Baustein; `pipeline` = sequenzielle Stages; `workflow` = Graph mit Verzweigung. | +| **Begründung** | Komposition ohne Copy-Paste; Reference-Prompts in Pipelines (`source: 'reference'`). | +| **Quelle** | `execute_prompt()` Typ-Verzweigung; `StagePromptCreate` in `models.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Pipeline-Stages laufen sequentiell, obwohl konzeptionell „parallel“; `workflow_definitions` und `ai_prompts.graph_data` doppelt. | + +### 4. Platzhalter als API-Verträge (Registry) + +| | | +|---|---| +| **Prinzip** | Platzhalter sind registrierte, dokumentierte Verträge — keine freien Prompt-Hilfsvariablen. | +| **Begründung** | Konsistenz für Injektion, GUI-Picker, Export, Validierung. | +| **Quelle** | `PLACEHOLDER_REGISTRY_FRAMEWORK.md`; `docs/PLACEHOLDER_GOVERNANCE.md`; `import placeholder_registrations` in `main.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Duplikat `PLACEHOLDER_MAP` in `placeholder_resolver.py` neben Registry; Metadaten teils noch Legacy. | + +### 5. Trennung Template (Was) vs. Resolver (Daten) + +| | | +|---|---| +| **Prinzip** | Templates enthalten nur `{{keys}}`; Berechnung liegt in Resolver/Data Layer. | +| **Begründung** | Prompt-Autoren ändern Text, nicht Berechnungslogik. | +| **Quelle** | `resolve_placeholders()` in `prompt_executor.py`; Registry-Felder `resolver_function`, `data_layer_function` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `execute_prompt_with_data` lädt zusätzlich Roh-SQL pro Modul — zweite Kontext-Schicht. | + +### 6. Layer-1-Daten vs. Layer-2a-Prompt-Injektion + +| | | +|---|---| +| **Prinzip** | Berechnungen in `data_layer/`; Prompt Engine konsumiert nur formatierte Werte. | +| **Begründung** | Single Source of Truth für Charts, Platzhalter, KI. | +| **Quelle** | Phase-0c-Architektur; Registry-Felder `data_layer_module` / `layer_1_decision` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy `_prepare_template_vars` in `insights.py` umgeht Data Layer. | + +### 7. Transparenz durch Debug- und Preview-Modus + +| | | +|---|---| +| **Prinzip** | Aufgelöste/unaufgelöste Platzhalter, Final-Prompt und Stage-Outputs sind inspizierbar; Preview ohne LLM. | +| **Begründung** | Admin kann Prompts testen und Wertetabelle/Expertenmodus speisen. | +| **Quelle** | `debug`-Parameter; `/preview`; `UnifiedPromptModal` Test-Button; `ai_insights.metadata` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Debug-Daten in Responses können groß/sensibel sein; kein separates Staging. | + +### 8. Wiederverwendbare Base-Prompts via Reference + +| | | +|---|---| +| **Prinzip** | Pipeline-Stages referenzieren Slugs statt Templates zu duplizieren. | +| **Begründung** | Ein Baustein, mehrere Workflows; zentral wartbar. | +| **Quelle** | `source == 'reference'` in `execute_pipeline_prompt()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Keine Referenz-Versionierung; Änderung am Base-Prompt wirkt sofort auf alle Referenzen. | + +### 9. Strukturierte LLM-Ausgaben per Output-Format + +| | | +|---|---| +| **Prinzip** | Pro Prompt/Prompt-Def: `output_format: text\|json`, optional `output_schema`; Pipeline-Outputs als Stage-Keys im Kontext. | +| **Begründung** | Maschinenlesbare Zwischenergebnisse für Multi-Stage und Wertetabelle. | +| **Quelle** | `validate_json_output()`; Stage `output_key` in Pipeline | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | JSON-Schema-Validierung ist TODO (`jsonschema`); Markdown-Unwrap als Heuristik. | + +### 10. Admin-only Konfiguration, User-only Ausführung + +| | | +|---|---| +| **Prinzip** | Schreibende Prompt-/Workflow-Operationen nur mit `require_admin`. | +| **Begründung** | Produktions-Prompts sind Systemkonfiguration, nicht Nutzerdaten. | +| **Quelle** | `require_admin` in `routers/prompts.py`; `RequireAdmin` für `/admin/prompts` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `/workflow-editor/:id` ohne Frontend-Admin-Gate; `/prompts/execute` ohne `check_feature_access` (Legacy-Pfad in `insights.py` hat Enforcement). | + +### 11. Import/Export als Umgebungs-Sync + +| | | +|---|---| +| **Prinzip** | Prompt-Sätze als JSON exportierbar/importierbar (Dev→Prod). | +| **Begründung** | Konfiguration versionierbar in Git, nicht in der App-DB. | +| **Quelle** | `GET /export-all`, `POST /import` in `routers/prompts.py`; Admin-UI Buttons | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Kein Diff, keine Merge-Strategie, kein Rollback; Overwrite-Flag manuell. | + +### 12. Workflow-Erweiterung: Graph + Fragenergänzungen + Signale + +| | | +|---|---| +| **Prinzip** | Workflows als Knoten/Kanten-Graph; optionale Fragen am Knoten; Normalisierung/Logic/Join als Engine-Schicht. | +| **Begründung** | Bedingte, verzweigte Analysen jenseits linearer Pipelines. | +| **Quelle** | `workflow_executor.py`; Migration 034; `question_augmenter.py` | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Hohe Komplexität; zwei Speicherorte (`graph_data` vs. `workflow_definitions`); Jinja2 im Workflow-Pfad zusätzlich zu `{{}}`-Resolver. | + +### 13. Platzhalter-Modifier für KI-Kontext + +| | | +|---|---| +| **Prinzip** | `{{key\|d}}` (Wert + Beschreibung), `{{key\|x}}` (Erklärung ohne Zahl) über Katalog-Metadaten. | +| **Begründung** | Prompts können Kontext für das Modell reichhaltiger machen ohne Template-Duplikate. | +| **Quelle** | `resolve_placeholders()` Modifier-Logik; `get_placeholder_catalog()` | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Modifier-Syntax ad hoc; Katalog-Pflicht für sinnvolle `\|x`-Nutzung. | + +### 14. System-Prompt-Reset statt DB-Versionierung + +| | | +|---|---| +| **Prinzip** | Shipped Prompts mit `is_system_default` + `default_template`; Admin-Reset auf Original. | +| **Begründung** | Schutz vor irreversiblen Fehlkonfigurationen ohne vollständiges Versionsmodell. | +| **Quelle** | Migration 019; `POST /{prompt_id}/reset-to-default` | +| **Tragfähigkeit** | **mittel** | +| **Einschränkung** | Nur ein Default-Snapshot; keine Historie benutzerdefinierter Änderungen. | + +### 15. Governance für Platzhalter-Änderungen + +| | | +|---|---| +| **Prinzip** | Breaking Changes nur über Deprecation + Replacement; semantische Verträge dokumentiert. | +| **Begründung** | Prompts in Produktion brechen nicht still. | +| **Quelle** | `docs/PLACEHOLDER_GOVERNANCE.md` §4 | +| **Tragfähigkeit** | **mittel** (prozessual) | +| **Einschränkung** | Prozess in Doku, nicht im Runtime erzwungen; Checkliste verweist noch auf Legacy-Dateien. | + +--- + +## Nicht übernehmen + +Muster, die sich nicht bewährt haben oder zu produktspezifisch sind — bei Neuentwicklung vermeiden: + +1. **Parallele Ausführungspfade** — Legacy `insights.py` (`/insights/run`, `/insights/pipeline`) mit eigener Template-Engine und LLM-Calls neben `prompt_executor`; Frontend-Split (`Analysis` vs. `History`). + +2. **Doppelte Metadaten für Platzhalter** — `PLACEHOLDER_MAP`, Registry, `placeholder_metadata_complete.py` und Katalog-Fallbacks parallel; erzeugt Sync-Risiko (114 Keys müssen deckungsgleich bleiben). + +3. **Zwei Pipeline-Modelle gleichzeitig** — `pipeline_configs` (3 fixe Stages) und Unified `type=pipeline` in `ai_prompts`; Migration 020 migriert, Tabelle bleibt aktiv. + +4. **Zwei Workflow-Speicher** — `workflow_definitions.graph` und `ai_prompts.graph_data`; unklare Single Source of Truth. + +5. **Roh-SQL-Kontextladung im Executor** — `execute_prompt_with_data` lädt Modul-Rohdaten per SQL, obwohl Resolver/Data Layer existieren; Domänenwissen in der Engine. + +6. **Hardcodierte Execute-Defaults** — Module/Zeiträume in `/execute` fest verdrahtet statt aus Prompt-/Pipeline-Konfiguration. + +7. **Fehlende Feature-Enforcement-Konsistenz** — `check_feature_access` auf Legacy-Insights, nicht auf `/prompts/execute`. + +8. **„Parallel“ als sequentiell implementiert** — Pipeline-Stages kommentiert als parallel, Code sequentiell; irreführendes Modell. + +9. **Unvollständige Output-Validierung** — JSON-Parse + Markdown-Unwrap, Schema-Check auskommentiert/TODO. + +10. **Workflow-Editor ohne klares Admin-Gate in Routing** — Schreib-API geschützt, UI-Route für alle Authentifizierten erreichbar. + +11. **Domänen-spezifische Hardcodings in der Engine** — Kategorien (`körper`, `ernährung`, …), deutsche Meta-Prompts für Generate/Optimize, Fitness-Kontext in Prompt-Generator. + +12. **Kein integriertes Prompt-Versions- und Freigabemodell** — Overwrite + JSON-Export ersetzt keine Revision/Review/Publish-Pipeline. + +13. **Issue #51 (Seitenzuordnung) nicht umgesetzt** — Prompt-Verfügbarkeit kontextuell nicht konfigurierbar; alles über zentrale Analyse-Seite. + +14. **Globales LLM-Modell per Env** — `workflow_executor` übergibt Modell pro Call, `call_openrouter` ignoriert es; keine prompt-spezifische Modellwahl trotz API-Shape. + +--- + +## Verwandte Dokumentation + +- Fachliche Spec: [AI_PROMPTS.md](../../functional/AI_PROMPTS.md) +- Platzhalter-Registry: [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md) +- Platzhalter-Governance: [PLACEHOLDER_GOVERNANCE.md](../../../../docs/PLACEHOLDER_GOVERNANCE.md) +- Issue #28 (Unified Prompt System): abgeschlossen, siehe `CLAUDE.md` +- Issue #51 (Prompt-Seitenzuordnung): [issue-51-prompt-page-assignment.md](../../../../docs/issues/issue-51-prompt-page-assignment.md) +- **Serie (abgeschlossen):** [Index](./README.md) · [Jinkendo Foundation](../README.md) · Dokumente #1–#9 diff --git a/.claude/docs/jinkendo-foundation/design-principles/README.md b/.claude/docs/jinkendo-foundation/design-principles/README.md new file mode 100644 index 0000000..914fc0a --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/README.md @@ -0,0 +1,136 @@ +# Designprinzipien – Index (Jinkendo Foundation) + +**Teil von:** [Jinkendo Foundation](../README.md) + +**Status:** Arbeitspapier / Übergabe +**Stand:** 2026-07-04 +**Zweck:** Zentraler Einstieg für die aus Mitai extrahierte Serie **tragfähiger Designprinzipien** — für neue Apps der Jinkendo-Produktfamilie oder vergleichbare Self-Hosted-PWAs. + +**Nicht enthalten:** Mitai-Gesamtarchitektur, Domänenlogik (Gesundheit/Ernährung), Kairo-/Framework-Empfehlungen. + +**Ablage:** `.claude/docs/jinkendo-foundation/design-principles/` · Regeln: [DOCUMENTATION.md](../../../rules/DOCUMENTATION.md) + +--- + +## Wofür diese Serie? + +Mitai Jinkendo implementiert wiederkehrende **Querschnittsmuster** (Prompt-Ausführung, Data Layer, Entitlements, Registries, Auth, Import, Dashboard, Navigation, Deploy). Die neun Dokumente destillieren daraus: + +- **Was** übertragbar ist (Prinzip + Begründung + Tragfähigkeit) +- **Was** bewusst nicht kopiert werden soll („Nicht übernehmen“) +- **Wo** im Code nachgeschaut werden kann (Pfade, Agent-Guides) + +Jedes Dokument ist **eigenständig lesbar**; dieser Index ordnet Abhängigkeiten und Lese-Reihenfolge. + +--- + +## Dokumente (9/9) + +| # | Modul | Datei | Kernidee (1 Satz) | +|---|-------|-------|-------------------| +| 1 | Prompt Engine | [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) | Ein Executor, Pipeline/Workflow-Typen, Platzhalter über Registry — keine Raw-Template-Ausführung. | +| 2 | Data Layer | [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) | Layer 0→1→2: Single Source of Truth für Berechnungen; Charts und KI nutzen dieselbe Schicht. | +| 3 | Feature & Entitlement | [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) | Zentrale `check_feature_access`, DB-Registry, 4-Phasen-Rollout — Enforcement an der API. | +| 4 | Registry / Plugin (Meta) | [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) | Drei Registry-Muster (Platzhalter, Widgets, CSV): SSoT, Validierung an der Grenze, Runtime-Registrierung. | +| 5 | Auth & Session | [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md) | Server-Sessions, `require_auth` als Depends — bekannte Schwäche: Profile-Header ohne Session-Bindung. | +| 6 | Universal Import | [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](./UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md) | Modul-Registry + Executor + Vorlagen; Ingest ≠ Interpretation; SAVEPOINT pro Zeile. | +| 7 | Dashboard Widgets | [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](./DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md) | Backend-Katalog, Profil-Layout, Config-Whitelist, Entitlements — Dual Registry mit Frontend. | +| 8 | Navigation / IA | [NAVIGATION_IA_DESIGN_PRINCIPLES.md](./NAVIGATION_IA_DESIGN_PRINCIPLES.md) | `appNav` als SSoT, Shells für tiefe Bereiche, Admin-Hub, ein Breakpoint 1024px. | +| 9 | Migration & Deploy | [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](./MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md) | `XXX_*.sql` beim Container-Start; develop/main → Dev/Prod; fail-fast, kein Auto-Rollback. | + +--- + +## Empfohlene Lesereihenfolge + +### Schnellüberblick (30 Min) + +1. [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) — Meta-Muster für viele Module +2. [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) — Daten vs. Darstellung +3. [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](./MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md) — Betrieb & Schema-Evolution + +### Vollständige Implementierung (neues Produkt) + +``` +Foundation: (9) Migration & Deploy → (5) Auth → (3) Feature & Entitlement +Daten: (2) Data Layer → (6) Universal Import +Erweiterung: (4) Registry Meta → (1) Prompt Engine → (7) Dashboard Widgets +Oberfläche: (8) Navigation / IA +``` + +### Nur ein Modul nachbauen + +| Ziel | Lese zuerst | Dann | +|------|-------------|------| +| KI-Analysen | #1 Prompt Engine | #2 Data Layer, #4 Registry | +| Charts & KPIs | #2 Data Layer | #7 Dashboard Widgets | +| Freemium / Limits | #3 Feature & Entitlement | #7 Widgets (Gating) | +| CSV-Import | #6 Universal Import | #2 Data Layer, #4 Registry | +| Admin-PWA | #8 Navigation / IA | #3, #5 | + +--- + +## Querschnittsthemen (über alle Docs) + +| Thema | Primär | Ergänzend | +|-------|--------|-----------| +| Single Source of Truth | #2 Data Layer | #4 Registry, #7 Widget-Katalog, #6 Modul-Registry | +| Validierung an der Grenze | #4 Registry | #7 Layout-Pydantic, #6 Template-Validator | +| Feature-Gating | #3 Entitlement | #7 Widget `allowed`, #6 Import-Limits | +| Dual Registry (Backend + Frontend) | #4 Registry | #7 `registerDashboardWidgets`, Platzhalter-UI | +| Idempotenz / Recovery | #9 Migration | #6 SAVEPOINT, Migration `IF NOT EXISTS` | +| Bekannte Lücken dokumentieren | alle „Nicht übernehmen“ | z. B. IDOR Profile-Header (#5), fehlender Cross-Check Widget-IDs (#7) | + +--- + +## Verwandte normative Docs (Mitai-spezifisch) + +Diese Agent-Guides sind **Implementierungsdetail**; die Designprinzipien-Serie ist **extrahiertes Muster**: + +| Thema | Agent-Guide / Spec | +|-------|-------------------| +| Platzhalter | [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md) | +| Data Layer erweitern | [DATA_LAYER_EXTENSION_GUIDE.md](../../technical/DATA_LAYER_EXTENSION_GUIDE.md) | +| CSV Import | [UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md](../../technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md) | +| Dashboard Widgets | [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md) | +| GUI / Admin / Nav | [GUI_IA_ADMIN_NAV_2026-04-05.md](../../../../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md) | +| Migrationen (operativ) | [MIGRATIONS.md](../../technical/MIGRATIONS.md) | +| Membership | [MEMBERSHIP_SYSTEM.md](../../technical/MEMBERSHIP_SYSTEM.md), [FEATURE_ENFORCEMENT.md](../../architecture/FEATURE_ENFORCEMENT.md) | +| Architektur-Regeln | [ARCHITECTURE.md](../../../rules/ARCHITECTURE.md) | + +--- + +## Übergabe-Checkliste (neues Produkt in der Familie) + +Nutze diese Liste beim Start eines Schwester-Projekts — **Prinzipien ja, Mitai-Pfade nein**: + +``` +[ ] Deploy: nummerierte SQL-Migrationen + Tracking-Tabelle + Startup vor App +[ ] Auth: Session server-side; Depends(require_auth); Admin-Route-Guard +[ ] Entitlements: eine check_feature_access-Funktion; keine Tier-Logik in UI-Widgets +[ ] Data Layer: Berechnungen nicht in Router/React duplizieren +[ ] Registry: neue erweiterbare IDs nur über zentralen Katalog + Validierung +[ ] Import (falls CSV): Modul-Registry + Vorlagen + Import-Grenze (keine Scores beim Insert) +[ ] Dashboard (falls konfigurierbar): Backend-Katalog + Layout-JSON + allowed-Flag +[ ] Navigation: eine appNav-SSoT; Shells für >6 Top-Level-Bereiche +[ ] Prompt/KI (falls): ein Executor; Platzhalter-Registry; kein Raw-Template an LLM +[ ] Dokumentieren: pro Modul „Nicht übernehmen“ aus Mitai-Lücken mitnehmen +``` + +--- + +## Pflege + +| Aktion | Wo | +|--------|-----| +| Neues Querschnittsmodul extrahiert | Neues `*_DESIGN_PRINCIPLES.md` + Zeile in Tabelle oben | +| Mitai-Implementierung ändert Muster | Betroffenes Einzeldokument + ggf. Querschnittstabelle hier | +| Nur Mitai-Bugfix ohne Muster-Änderung | Agent-Guides / Code; Designprinzipien optional | + +--- + +## Changelog Index + +| Datum | Änderung | +|-------|----------| +| 2026-07-04 | Index angelegt; Serie 1–9 abgeschlossen | +| 2026-07-04 | Nach `jinkendo-foundation/design-principles/` verschoben | diff --git a/.claude/docs/jinkendo-foundation/design-principles/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..b4fb650 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md @@ -0,0 +1,315 @@ +# Registry- & Plugin-Muster – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Querschnittsmuster für erweiterbare Registries — drei Implementierungen in Mitai (Platzhalter, Dashboard-Widgets, CSV-Import) + +**Serie:** Designprinzipien für Produktfamilie · Dokument 4 von n +**Vorgänger:** [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) + +**Die drei Registries:** + +| Registry | Backend-Kanon | Frontend-/Runtime-Registry | Leitfaden | +|----------|---------------|----------------------------|-----------| +| **Platzhalter** | `placeholder_registry.py` + `placeholder_registrations/` | `PLACEHOLDER_MAP` in `placeholder_resolver.py` | `PLACEHOLDER_REGISTRY_FRAMEWORK.md` | +| **Dashboard-Widgets** | `widget_catalog.py` | `registerDashboardWidgets.js` → `dashboardWidgetRegistry.jsx` | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` | +| **CSV-Import-Module** | `csv_parser/module_registry.py` | — (Executor + Admin-UI konsumieren API) | `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | + +--- + +## Modul + +**Registry- & Plugin-Muster** (Meta-Schicht) + +Wiederkehrendes Architekturmuster: **Zentral registrierte, ID-basierte Erweiterungspunkte** mit Metadaten, Validierung und getrennten Konsumenten (GUI, API, Batch). Kein einzelnes Runtime-Modul — ein **Familien-Designpattern**, das in Mitai dreimal konkret umgesetzt ist. + +--- + +## Fachliche Verantwortung + +Registries übernehmen: + +1. **Autoritative ID-Liste** — Was existiert, was ist erlaubt, in welcher Reihenfolge (optional). +2. **Metadaten für Mensch & Maschine** — Titel, Beschreibung, Typen, Abhängigkeiten, semantische Verträge. +3. **Validierung** — Unbekannte IDs werden abgelehnt; Konfigurationen gegen Kanon geprüft. +4. **Entkopplung** — Implementierung (Resolver, React-Komponente, Import-Executor) registriert sich an den Kanon, nicht umgekehrt. +5. **Erweiterbarkeit ohne Schema-Explosion** — Neue Einträge über Code-Registrierung (+ ggf. DB-Overrides), nicht über neue DB-Spalten pro Feature. + +Registries übernehmen **nicht**: + +- Fachliche Berechnung (→ Data Layer) +- Entitlement-Auflösung (→ Feature System; Widgets *referenzieren* Features) +- Auth / Mandanten + +### Gemeinsames Strukturschema + +``` +┌─────────────────────────────────────────────────────────┐ +│ REGISTRY (Kanon) │ +│ ID + Metadaten + optionale Policies/Abhängigkeiten │ +└────────────────────┬────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + Implementierung Validierung Konsumenten + (Resolver/ (Schema/ (GUI-Picker, + Component/ Tests) API, Executor) + Executor) +``` + +### Vergleich der drei Implementierungen + +| Aspekt | Platzhalter | Dashboard-Widgets | CSV-Import | +|--------|-------------|-------------------|------------| +| **Primär-ID** | `key` (snake_case) | `id` (snake_case) | Modul-Name (`nutrition`, `activity`, …) | +| **Kanon-Speicher** | Python Singleton + Cluster-Module | Python-Liste `WIDGET_CATALOG` | Python-Dict `MODULE_DEFINITIONS` | +| **Metadaten-Tiefe** | Sehr hoch (22+ Felder, Evidence) | Mittel (title, description, requires_feature) | Hoch (fields, types, duplicate_key, aggregates) | +| **Runtime-Registry** | `register_placeholder()` beim Import | `registerDashboardWidget()` idempotent | Keine — Executor liest Dict | +| **Frontend-Spiegel** | PlaceholderPicker, Admin-Prompt-Modal | `ensureDashboardWidgetsRegistered()` | Admin CSV Template Editor | +| **Validierung** | `metadata.validate()`, Governance-Docs | Pydantic Layout + `validate_widget_entry_config` | `validate_field_mappings`, `validate_csv_template` | +| **Tests** | `test_placeholder_metadata.py`, … | `test_widget_catalog.py` | `test_template_validator.py`, … | +| **DB-Override** | Nein (nur Code) | Ja (`widget_feature_requirements`, Layout pro Profil) | Ja (Vorlagen, Nutzer-Mappings) | +| **Entitlements** | Indirekt (Data/Features) | `requires_feature` → `check_feature_access` | Feature-Limits beim Import | + +--- + +## Designprinzipien (übergreifend) + +### 1. Single Source of Truth für erlaubte IDs + +| | | +|---|---| +| **Prinzip** | Jede erweiterbare Einheit hat **eine** autoritative ID-Liste; Router und UI duplizieren keine Feld-/Widget-/Platzhalter-Listen. | +| **Begründung** | Verhindert „funktioniert in der UI, scheitert in der API“ und umgekehrt. | +| **Quelle** | `module_registry.py` Kommentar; `widget_catalog.py`; `PLACEHOLDER_REGISTRY_FRAMEWORK.md` §2.3 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Platzhalter: paralleles `PLACEHOLDER_MAP` neben Registry. | + +### 2. ID-Stabilität als Vertrag + +| | | +|---|---| +| **Prinzip** | IDs/Keys sind stabile API-Verträge; Umbenennung = neuer Key + Deprecation, nicht stilles Rename. | +| **Begründung** | Prompts, Layouts und CSV-Vorlagen referenzieren IDs persistent. | +| **Quelle** | `docs/PLACEHOLDER_GOVERNANCE.md` §4.2–4.3; Widget-Layout in `profiles.dashboard_layout` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht überall runtime-erzwungen (Platzhalter-Governance prozessual). | + +### 3. Metadaten getrennt von Implementierung + +| | | +|---|---| +| **Prinzip** | Registry speichert **Was** (ID, Beschreibung, Typ, Policies); Implementierung lebt in separaten Modulen. | +| **Begründung** | GUI, Export, Validierung und Docs können Metadaten nutzen ohne Resolver/Component zu laden. | +| **Quelle** | `PlaceholderMetadata` Dataclass; `WidgetCatalogEntry`; `MODULE_DEFINITIONS.fields` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Platzhalter bindet `_resolver_func` optional an Metadata-Objekt. | + +### 4. Zwei-Phasen-Registrierung (Backend-Kanon + Runtime-Binding) + +| | | +|---|---| +| **Prinzip** | Phase A: Kanon definiert IDs und Metadaten. Phase B: Implementierung registriert sich (Platzhalter-Cluster-Import, `registerDashboardWidget`, Executor nutzt Modul-Def). | +| **Begründung** | Backend bleibt autoritativ; Frontend/plugins können nachziehen, Tests können Lücken finden. | +| **Quelle** | `import placeholder_registrations` in `main.py`; `ensureDashboardWidgetsRegistered()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Fehlende Frontend-Registrierung zeigt „Unbekanntes Widget“ — kein Build-Time-Fail. | + +### 5. Auto-Registration via Package-Import + +| | | +|---|---| +| **Prinzip** | Side-Effect-Import eines Packages triggert vollständige Registrierung (`placeholder_registrations/__init__.py`). | +| **Begründung** | Keine vergessene manuelle Registrierungsliste in `main.py` pro Eintrag. | +| **Quelle** | `placeholder_registrations/__init__.py`; `main.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Import-Reihenfolge und zirkuläre Imports beachten. | + +### 6. Validierung am Registry-Rand + +| | | +|---|---| +| **Prinzip** | Unbekannte Keys/Widget-IDs/Feld-Mappings werden an Registry-Grenzen abgewiesen, nicht erst in der Business-Logik. | +| **Begründung** | Frühes, klares Fehlerbild für Admins und Entwickler. | +| **Quelle** | `DashboardWidgetEntry` + `ALLOWED_WIDGET_IDS`; `validate_field_mappings()`; `get_unknown_placeholders()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Prompt-Templates können unbekannte Platzhalter erst zur Laufzeit offenbaren. | + +### 7. Konsumenten-Agnostik + +| | | +|---|---| +| **Prinzip** | Dieselbe Registry bedient mehrere Konsumenten (KI, Charts, Export, Admin-Browser) ohne duplizierte Metadaten. | +| **Begründung** | DRY für Beschreibungen, Kategorien, Beispielwerte. | +| **Quelle** | `PLACEHOLDER_REGISTRY_FRAMEWORK.md` §2.2; `get_placeholder_catalog()` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Legacy-Export-Pfade mergen noch „Registry + Legacy“. | + +### 8. Erweiterungs-Checkliste statt Ad-hoc + +| | | +|---|---| +| **Prinzip** | Jedes Registry hat dokumentierte Schritte A→G (Katalog-Eintrag, Validierung, Tests, Version-Bump). | +| **Begründung** | Agenten und Menschen erweitern konsistent; Review an Checkliste. | +| **Quelle** | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` §2; `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` §2; `PLACEHOLDER_DEVELOPMENT_GUIDE.md` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Drei separate Guides — kein unified „Registry Agent Guide“. | + +### 9. Tests auf Katalog-Konsistenz + +| | | +|---|---| +| **Prinzip** | Automatisierte Tests prüfen Eindeutigkeit, Reihenfolge, Payload-Shape, ID-Abgleich Kanon ↔ abgeleitete Sets. | +| **Begründung** | Regression wenn Katalog wächst but Registry/Layout nicht mitzieht. | +| **Quelle** | `test_widget_catalog.py`; Placeholder-Metadata-Tests | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Kein Cross-Registry-Test „Frontend widget IDs == backend catalog“. | + +### 10. Optionale Entitlement-Referenz im Kanon + +| | | +|---|---| +| **Prinzip** | Registry-Einträge **referenzieren** Feature-IDs (`requires_feature`), lösen Entitlements aber nicht selbst auf. | +| **Begründung** | Tier-Logik bleibt in `check_feature_access`; Katalog bleibt deklarativ. | +| **Quelle** | `WidgetCatalogEntry.requires_feature`; `dashboard_widget_entitlements.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Platzhalter haben kein direktes `requires_feature` — Gating nur indirekt. | + +--- + +## Designprinzipien (spezifisch pro Registry) + +### Platzhalter-Registry + +| # | Prinzip | Tragfähigkeit | Schwäche | +|---|---------|---------------|----------| +| P1 | **Semantischer Vertrag** (`semantic_contract`) pro Key — Platzhalter sind API, nicht Prompt-Hilfe | hoch | Viele Legacy-Keys mit schwachem Vertrag | +| P2 | **Evidence-Tagging** — Herkunft jedes Metadatenfelds nachvollziehbar | mittel | Pflegeaufwand | +| P3 | **Cluster-Module** — Registrierung nach Domäne (`nutrition_part_a`, `body_metrics`, …) | hoch | 114 Keys Sync mit `PLACEHOLDER_MAP` | +| P4 | **Data-Layer-Referenz** in Metadata (`data_layer_function`) — Bindung an Layer 1 | hoch | Nicht alle Keys vollständig verknüpft | +| P5 | **Singleton** `get_registry()` — globaler Kanon | hoch | Test-Isolation braucht Disziplin | + +### Dashboard-Widget-Registry + +| # | Prinzip | Tragfähigkeit | Schwäche | +|---|---------|---------------|----------| +| W1 | **Backend-Katalog = ALLOWED_WIDGET_IDS** — Layout-Schema leitet ab | hoch | Frontend-Registry manuell parallel | +| W2 | **`merge_missing_catalog_widgets`** — neue Katalog-IDs erscheinen im Layout ohne User-Reset | hoch | — | +| W3 | **Strikte `config`-Validierung** nur für whitelisted Widgets (`WIDGETS_ALLOWING_CONFIG`) | hoch | Config-Schemas pro Widget heterogen | +| W4 | **Graceful Degradation** — unregistrierte ID → Fehler-Karte, nicht Crash | mittel | Maskiert Deploy-Fehler | +| W5 | **WidgetErrorBoundary** pro Instanz | hoch | — | + +### CSV-Modul-Registry + +| # | Prinzip | Tragfähigkeit | Schwäche | +|---|---------|---------------|----------| +| C1 | **`MODULE_DEFINITIONS` = einzige Feldliste** — Router duplizieren nicht | hoch | Activity erweitert dynamisch um `training_parameters` | +| C2 | **Deklarative Duplikat-Strategie** (`duplicate_key`, `update`/`skip`) | hoch | Modul-spezifische Executor-Sonderfälle | +| C3 | **`import_row_processing`** — Aggregation in Registry, nicht im Router | hoch | Legacy-Defaults in Modul-Def | +| C4 | **`validate_field_mappings`** vor Persistenz | hoch | Nutzer-Mappings teils ohne volle Validator-Parität (#71) | +| C5 | **Persistenz-Orchestrator** liest Registry-Felder (`activity_persistence_orchestrator`) | hoch | Nur Activity voll ausgebaut | + +--- + +## Anti-Pattern: Doppel-Registry + +Mitai zeigt an **Platzhaltern** das Risiko explizit: + +``` +placeholder_registrations/ ──register──► PlaceholderRegistry (Metadata) + │ ▲ + └── resolver in code ──► PLACEHOLDER_MAP (Runtime, 114 Keys) +``` + +**Regel für Produktfamilie:** Runtime-Auflösung soll Metadata-Registry **lesen**, nicht spiegeln. + +Widgets sind näher am Ideal: Backend `WIDGET_CATALOG` ist Kanon; Frontend muss IDs manuell in `registerDashboardWidgets.js` binden — akzeptabel, aber testbar machen (Cross-Check). + +--- + +## Nicht übernehmen + +1. **Parallele Runtime-Maps** — `PLACEHOLDER_MAP` + Registry; eine Quelle für Keys und Resolver-Pfad. + +2. **Frontend-Registry ohne Build-/Test-Gate** — fehlende `registerDashboardWidget`-Einträge erst zur Laufzeit sichtbar. + +3. **Metadaten-Duplikation in Export-Code** — hardcodierte Beschreibungen außerhalb der Registry. + +4. **Registry-Einträge ohne Validierungs-Tests** — besonders bei 100+ Platzhaltern. + +5. **Import-Feldlisten in Routern** — alles über `module_registry` (Mitai-Zielbild, noch nicht überall). + +6. **Evidence-/Metadata-Pflicht für einfache Plugins** — 22 Felder für Widgets wären Overkill; **Metadaten-Tiefe an Risiko anpassen**. + +7. **Dynamische Registry aus DB ohne Versionierung** — Widget-Feature-Overrides OK; kompletter Kanon nur in DB wäre schwer testbar. + +8. **Registry ohne Deprecation-Pfad** — Breaking Key-Changes still (Platzhalter-Governance existiert, durchsetzen). + +9. **Entitlements in Registry auflösen** — Widgets richtig: referenzieren; nicht Tier-Logik im Katalog. + +10. **Schlaf-Modul leeres `fields: {}`** — Sondermodus (`import_mode`) statt sauberem Registry-Eintrag; technische Schuld. + +--- + +## Entscheidungsmatrix: Welche Registry-Tiefe? + +| Wenn … | dann Metadaten-Tiefe … | Beispiel | +|--------|------------------------|----------| +| Externe Verträge / KI / Compliance | Hoch (Vertrag, Evidence, Missing-Policy) | Platzhalter | +| UI-Plugin mit optionaler Config | Mittel (ID, title, config schema, feature ref) | Widgets | +| Daten-Ingest / Schema-Mapping | Hoch (Typen, keys, constraints) | CSV-Module | +| Internes Hilfsmodul | Minimal (ID + Handler-Ref) | — | + +--- + +## Modul-Inventar (Querschnitt) + +``` +backend/ +├── placeholder_registry.py +├── placeholder_registrations/ # Auto-import Cluster +├── placeholder_resolver.py # PLACEHOLDER_MAP (Legacy-Spiegel) +├── placeholder_registry_export.py +├── widget_catalog.py +├── dashboard_layout_schema.py +├── dashboard_widget_config.py +├── dashboard_widget_entitlements.py +├── widget_feature_requirements_db.py +└── csv_parser/ + └── module_registry.py + +frontend/src/ +├── widgetSystem/ +│ ├── registerDashboardWidgets.js +│ └── dashboardWidgetRegistry.jsx +└── components/workflow/panels/PlaceholderPicker.jsx +``` + +--- + +## Verwandte Dokumentation + +- Platzhalter: [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md), [PLACEHOLDER_GOVERNANCE.md](../../../../docs/PLACEHOLDER_GOVERNANCE.md) +- Widgets: [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md) +- Import: [UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md](../../technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md) +- Entitlements (Widget-Gating): [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) +- Data Layer (Platzhalter-Berechnung): [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) +- Prompt Engine (Platzhalter-Konsument): [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1 | Prompt Engine | ✅ | +| 2 | Data Layer | ✅ | +| 3 | Feature & Entitlement | ✅ | +| 4 | Registry-/Plugin-Muster | ✅ dieses Dokument | +| 5 | Auth & Session | ✅ `AUTH_SESSION_DESIGN_PRINCIPLES.md` | +| 6 | Universal Import | ✅ | +| 7 | Dashboard Widgets | ✅ | +| 8 | Navigation / IA | ✅ | +| 9 | Migration & Deploy | ✅ | + +*Hinweis:* Dokumente 6 und 7 vertiefen Einzel-Registries; dieses Meta-Dokument ist die übergreifende Extraktion. diff --git a/.claude/docs/jinkendo-foundation/design-principles/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md b/.claude/docs/jinkendo-foundation/design-principles/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md new file mode 100644 index 0000000..5687e71 --- /dev/null +++ b/.claude/docs/jinkendo-foundation/design-principles/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md @@ -0,0 +1,325 @@ +# Universal CSV Import – Designprinzipien (Extraktion) + +**Status:** Analyse / Arbeitspapier +**Stand:** 2026-07-04 +**Geltungsbereich:** Universal CSV Import (Issue #21) — Ingest/Mapping/Persistenz, keine Auswertungslogik + +**Serie:** Designprinzipien für Produktfamilie · Dokument 6 von n +**Vorgänger:** [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md) + +**Kernkomponenten:** + +| Bereich | Pfade | +|---------|-------| +| Modul-Kanon | `backend/csv_parser/module_registry.py` | +| Ausführung | `backend/csv_parser/executor.py` | +| Parsing/Typen | `core.py`, `type_converter.py`, `field_units.py` | +| Aggregation | `import_row_processing.py` | +| Validierung | `template_validator.py` | +| Fehler-Hints | `import_errors.py` | +| Mapping-Vorschläge | `mapping_suggest.py` | +| Nutzer-API | `backend/routers/csv_import.py` | +| Admin-Vorlagen | `backend/routers/admin_csv_templates.py` | +| Persistenz-Orchestrator | `data_layer/activity_persistence_orchestrator.py` | +| Leitfaden | `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | +| Import-Grenze | `.claude/rules/ARCHITECTURE.md` §8 | + +--- + +## Modul + +**Universal CSV Import** + +Konfigurierbare Pipeline: **CSV-Datei → Feld-Mapping → Typkonvertierung → (optional Aggregation) → DB-Upsert** — mit Vorlagen, Audit-Log und row-level Fehlertoleranz. + +--- + +## Fachliche Verantwortung + +Das Modul übernimmt: + +1. **Modul-Registry** — Welche Zieltabellen/Felder importierbar sind (Typen, Duplikat-Keys, Strategien). +2. **Vorlagen (Mappings)** — System-Templates (Admin) + Nutzer-Kopien (`csv_field_mappings`). +3. **Analyse** — Delimiter-Erkennung, Spalten-Signatur, Mapping-Vorschläge, Diagnose einzelner Zeilen. +4. **Ausführung** — Upsert pro Modul, `source=csv`, Statistik, `affected_ids`. +5. **Fehlertransparenz** — Row-level Errors mit `code`/`hint`; kein Silent-Fail der ganzen Transaktion. +6. **Audit** — `csv_import_log` mit Status, Counts, betroffenen IDs. +7. **Limits** — Dateigröße/Zeilen aus `system_config`; Feature-Entitlements pro Modul. + +Es übernimmt **nicht**: + +- Fachliche Metriken / Scores (→ Data Layer, siehe [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md)) +- Prompt-/KI-Logik +- Vollständiger Ersatz aller Legacy-Import-Endpoints (noch parallel) + +### Pipeline (Happy Path) + +``` +Upload CSV + → decode_raw_bytes + resolve_effective_csv_delimiter + → Vorlage laden (csv_field_mappings) + → validate (optional Admin) / feature check + → run_universal_csv_import(cur, …) // eine Transaktion + → build_row_after_mapping (type_converter) + → aggregate_mapped_rows (import_row_processing) + → UPSERT / activity_persistence_orchestrator + → csv_import_log UPDATE + increment_feature_usage +``` + +### Unterstützte Module (Registry) + +| Modul | Zieltabelle | Besonderheit | +|-------|-------------|--------------| +| `nutrition` | `nutrition_log` | Tages-Aggregation | +| `weight` | `weight_log` | Duplikat: profile + date | +| `activity` | `activity_log` | SAVEPOINT pro Zeile; EAV via Orchestrator | +| `vitals_baseline` | `vitals_baseline` | Tages-Aggregation | +| `blood_pressure` | `blood_pressure_log` | Composite `measured_at` | +| `sleep` | `sleep_log` | Legacy-Adapter `import_mode: apple_sleep_aggregate` | + +--- + +## Administrierte vs. code-definierte Konfiguration + +| Konfiguration | Speicherort | Wer pflegt? | +|---------------|-------------|-------------| +| Zielfelder, Typen, Duplikat-Keys | `MODULE_DEFINITIONS` | Entwickler (Code) | +| System-Vorlagen | `csv_field_mappings` (`is_system=true`) | Admin (+ Migration Seeds) | +| Nutzer-Mappings | `csv_field_mappings` (`profile_id`) | Nutzer (Kopie/Anpassung) | +| `field_mappings`, `type_conversions`, `import_row_processing` | JSONB in Vorlage | Admin/Nutzer | +| Import-Limits | `system_config.csv_import` | Admin | +| Delimiter-Sniffing-Heuristik | `core.py` | Code | +| Header-Aliases (Vorschläge) | `mapping_suggest.py` | Code | + +**Bewusst nicht in Routern hardcodiert:** Feldlisten, Duplikat-Logik — nur Registry + Executor. + +--- + +## Designprinzipien + +### 1. Module Registry als Single Source of Truth + +| | | +|---|---| +| **Prinzip** | Alle erlaubten Zielfelder, Typen und Duplikat-Keys leben in `MODULE_DEFINITIONS` — Router duplizieren nicht. | +| **Begründung** | Admin-UI, Validator, Executor und `/api/csv/modules` bleiben synchron. | +| **Quelle** | `module_registry.py`; Agent-Guide §1 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Activity erweitert Felder dynamisch aus `training_parameters` (DB). | + +### 2. Ingest vs. Interpretation (Import-Grenze) + +| | | +|---|---| +| **Prinzip** | Import: Mapping + Typ/Einheit + Duplikat/Upsert. **Keine** fachliche Auswertung beim Insert. | +| **Begründung** | Semantik gehört in Data Layer; Import bleibt austauschbar und testbar. | +| **Quelle** | `ARCHITECTURE.md` §8; [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | `sleep_apple_import.py` ist Legacy-Adapter mit quellenspezifischer Logik. | + +### 3. Vorlagen trennen Struktur von Datei + +| | | +|---|---| +| **Prinzip** | `csv_field_mappings` speichert Modul, Delimiter, Header-Flag, Mappings, Conversions, Row-Processing — unabhängig vom Upload. | +| **Begründung** | Wiederverwendung (Apple Health, Omron, …); Nutzer wählt Vorlage statt jedes Mal neu zu mappen. | +| **Quelle** | Migration 042; Admin + User APIs | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nutzer-Kopien nicht immer durch `validate_csv_template` (#71). | + +### 4. Effektives Trennzeichen aus Datei, nicht blind aus Vorlage + +| | | +|---|---| +| **Prinzip** | `resolve_effective_csv_delimiter` — DE-Export (`;`) vs. EN-Vorlage (`,`) wird aus Header-Feldanzahl erkannt. | +| **Begründung** | Regionale CSV-Exporte brechen sonst das gesamte Mapping (eine Spalte). | +| **Quelle** | `core.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Heuristik, kein 100%-Garant für exotische Formate. | + +### 5. Typ- und Einheiten-Konvertierung deklarativ + +| | | +|---|---| +| **Prinzip** | `type_conversions` + `source_unit` in Vorlage; Logik in `type_converter` / `field_units`. | +| **Begründung** | kJ→kcal, Datumsformate, Dezimal-Komma ohne Code pro Quelle. | +| **Quelle** | `type_converter.py`, `field_units.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Falsche `source_unit` → DB-Overflow; `enrich_row_error` hilft nachträglich. | + +### 6. Zeilen-Aggregation vor Upsert + +| | | +|---|---| +| **Prinzip** | `import_row_processing` (group_by + aggregates) fasst mehrere CSV-Zeilen pro logischem Tag/Datensatz zusammen. | +| **Begründung** | Ernährung/Vitals: viele Rohzeilen → ein Tageseintrag. | +| **Quelle** | `import_row_processing.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Modul-Default als Legacy-Fallback wenn Vorlage leer; Admin „Format prüfen“ kann Processing auslassen. | + +### 7. Ein Cursor, eine Transaktion, SAVEPOINT pro Zeile + +| | | +|---|---| +| **Prinzip** | `run_universal_csv_import(cur, …)` nutzt **bestehenden** Cursor; bei Row-Fehlern SAVEPOINT + ROLLBACK TO, nicht ganze Xact abbrechen. | +| **Begründung** | PostgreSQL „transaction aborted“; partielle Imports mit Fehlerliste. | +| **Quelle** | `executor.py` (activity, vitals); `csv_import.py` SAVEPOINT `csv_import_exec` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nicht alle Module gleich implementiert; Disziplin pro Modul. | + +### 8. Kein verschachteltes get_db im Importpfad + +| | | +|---|---| +| **Prinzip** | FK-Auflösung (z. B. Trainingstyp) und Activity-Persistenz mit **demselben** `cur` wie der Import. | +| **Begründung** | Pool-Deadlocks, konsistente Transaktion. | +| **Quelle** | `_resolve_training_type_for_activity`; Agent-Guide §2 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Lazy-Import aus Router in Executor (Kopplung). | + +### 9. Strukturierte Fehler mit Hints + +| | | +|---|---| +| **Prinzip** | `enrich_row_error()` mappt DB-/Parse-Fehler auf `code` + menschenlesbaren `hint`. | +| **Begründung** | Nutzer/Admin können Vorlagen korrigieren ohne PostgreSQL-Kenntnis. | +| **Quelle** | `import_errors.py`; Import-Response `error_details` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Heuristische String-Matches, nicht vollständig. | + +### 10. Vorlagen-Validierung vor Persistenz (Admin) + +| | | +|---|---| +| **Prinzip** | `validate_csv_template` → `{ valid, errors[], warnings[] }`; Admin Create/Update → HTTP 422 bei Fehlern. | +| **Begründung** | Fehler früh, nicht erst beim Nutzer-Import. | +| **Quelle** | `template_validator.py`; `admin_csv_templates.py` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Dry-Run / User-Mappings Lücken (#71). | + +### 11. System- vs. User-Mappings (Permissions) + +| | | +|---|---| +| **Prinzip** | `is_system=true`: nur Admin editierbar; Nutzer kopiert und passt eigene Zeile an. | +| **Begründung** | Shipped Templates schützen; Individualisierung erlauben. | +| **Quelle** | `permissions.py`; DB CHECK + Unique Indexes | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | — | + +### 12. Import-Audit und Rollback-Vorbereitung + +| | | +|---|---| +| **Prinzip** | Jeder Lauf schreibt `csv_import_log` mit Counts, `error_details`, `affected_ids` (PKs pro Tabelle). | +| **Begründung** | Nachvollziehbarkeit, spätere Bereinigung/Rollback, Erfolgsrate pro Vorlage. | +| **Quelle** | Migration 042; `csv_import_execute` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Automatischer Rollback-Button nicht überall umgesetzt. | + +### 13. Feature-Entitlements an Import gebunden + +| | | +|---|---| +| **Prinzip** | `data_import` global + modulspezifisch (`nutrition_entries`, …); Increment nur für **neue** Zeilen. | +| **Begründung** | Konsistent mit Membership-System. | +| **Quelle** | `csv_import.py` `_check_module_feature_access`, `increment_feature_usage` | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Bulk-Increment-Schleife ineffizient (wie Feature-Doc). | + +### 14. Mapping-Vorschläge (Heuristik, nicht Autorität) + +| | | +|---|---| +| **Prinzip** | `mapping_suggest.py` schlägt Spalten-Zuordnung aus Header-Aliases vor — Admin bestätigt. | +| **Begründung** | Schneller Editor-Start; Kanon bleibt menschlich/administrativ freigegeben. | +| **Quelle** | `_MODULE_HEADER_ALIASES` | +| **Tragfähigkeit** | **mittel–hoch** | +| **Einschränkung** | Domänenspezifische Aliases hardcodiert (DE/EN). | + +### 15. Persistenz-Orchestrator für komplexe Domänen + +| | | +|---|---| +| **Prinzip** | Activity: nach Registry-Mapping → `activity_persistence_orchestrator` (Upsert + EAV + Eval-Hook). | +| **Begründung** | Gleiche Schreiblogik wie REST-API; kein divergierender CSV-Pfad. | +| **Quelle** | `activity_persistence_orchestrator.py`; [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) §9 | +| **Tragfähigkeit** | **hoch** | +| **Einschränkung** | Nur Activity vollständig; andere Module direkt im Executor. | + +--- + +## Nicht übernehmen + +1. **Parallele Legacy-Import-Endpoints** — `/api/nutrition/import-csv`, `/api/activity/import-csv` neben Universal-Pfad; neue Quellen nur über Universal + Vorlage (ARCHITECTURE §8.2). + +2. **Quellenspezifische Aggregat-Logik im Import** — `sleep_apple_import` als Dauerlösung; Ziel: mapping-nah + Layer 1 (Gitea #69). + +3. **Feldlisten in Routern** — jede neue Spalte nur via `module_registry` + Migration. + +4. **Verschachtelte DB-Connections im Executor** — Pool-Risiko; immer Caller-`cur` durchreichen. + +5. **Transaktion ohne SAVEPOINT bei Multi-Row-Import** — ein Fehler killt gesamten Import + opaque „transaction aborted“. + +6. **Blindes Vorlagen-Delimiter** — regionaler Export bricht Mapping. + +7. **Nutzer-Mappings ohne Validierung** — #71; Copy-from-System muss durch Validator. + +8. **Dry-Run ohne `import_row_processing`** — Admin „Format prüfen“ unvollständig vs. echter Import. + +9. **`source`-CHECK in DB vergessen** — Import setzt `csv`, Constraint muss Migration sein. + +10. **NUMERIC-Overflow durch falsche Einheit** — Schema + `source_unit` + Migrationbreite gemeinsam planen. + +11. **Interpretation/Auswertung beim Import** — Scores, TDEE, Training-Quality nicht in `executor.py`. + +12. **Executor-Monolith ohne Modul-Split** — `executor.py` wächst pro Modul; langfristig Executor-Strategie pro Registry-Key. + +--- + +## Modul-Inventar (Ist-Stand) + +``` +backend/csv_parser/ +├── module_registry.py # MODULE_DEFINITIONS +├── executor.py # run_universal_csv_import +├── core.py # decode, delimiter, limits +├── type_converter.py +├── field_units.py +├── import_row_processing.py +├── template_validator.py +├── import_errors.py +├── mapping_suggest.py +├── permissions.py +└── sleep_apple_import.py # Legacy-Adapter + +backend/routers/ +├── csv_import.py # Nutzer: modules, analyze, import, mappings +└── admin_csv_templates.py # Admin: CRUD + validate + +DB: +├── csv_field_mappings +└── csv_import_log +``` + +--- + +## Verwandte Dokumentation + +- Agent-Guide (normativ): [UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md](../../technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md) +- Registry-Meta: [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) +- Import-Grenze: [ARCHITECTURE.md](../../../rules/ARCHITECTURE.md) §8 +- Feature-Limits: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) +- Gitea #71: Dry-Run, User-Mapping-Validierung + +--- + +## Geplante Folgedokumente (Serie) + +| # | Modul | Status | +|---|-------|--------| +| 1–5 | … | ✅ | +| 6 | Universal Import | ✅ dieses Dokument | +| 7 | Dashboard Widgets | ✅ `DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md` | +| 8 | Navigation / IA | ✅ | +| 9 | Migration & Deploy | ✅ | diff --git a/.claude/rules/DOCUMENTATION.md b/.claude/rules/DOCUMENTATION.md index f760324..6b483e8 100644 --- a/.claude/rules/DOCUMENTATION.md +++ b/.claude/rules/DOCUMENTATION.md @@ -20,6 +20,7 @@ |-----|------|----------------| | **Fachliche Spec (WAS)** | `.claude/docs/functional/` | Domäne, Use Cases, UX-Ziele, fachliche Datenarchitektur. **Keine** reine API-Parameterliste (→ technical). | | **Technische Spec (WIE)** | `.claude/docs/technical/` | API-, DB-, Implementierungsmuster, Agent-Guides, Migrationen. | +| **Produktfamilie / Foundation** | `.claude/docs/jinkendo-foundation/` | Übertragbare Designprinzipien (nicht Mitai-Domäne); Einstieg `design-principles/README.md`. | | **Architektur-Querschnitt** | `.claude/docs/architecture/` | Kurze Überblicke (z. B. Frontend-Baum), ergänzend zu technical. | | **Arbeitspapier / Zwischenstand** | `.claude/docs/working/` | Analysen, Sessions, Migration-Notizen, **keine** langfristige Norm. Kann veraltet sein → Datum im Dokument. **Nicht** als alleinige „Wahrheit“ für Produkt zitieren. | | **Audits & Matrizen** | `.claude/docs/audit/` | Zeitlich begrenzte Reviews, Reconciliation, Gitea-Vorlagen. | @@ -63,6 +64,7 @@ Diese Ordner sind **kein** Ersatz für `working/` oder `docs/issues/`, wenn das ``` .claude/README.md ← Einstieg Agent/Human .claude/docs/README.md ← Spec-Katalog +.claude/docs/jinkendo-foundation/ ← Foundation Produktfamilie (Designprinzipien) .claude/docs/functional/ ← WAS .claude/docs/technical/ ← WIE .claude/docs/working/ ← Arbeitspapiere / Analysen diff --git a/CLAUDE.md b/CLAUDE.md index df89feb..9e0c534 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,6 +11,7 @@ > | **Universal CSV Import** (neues Modul / Executor / Vorlagen) | **`.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** | > | **GUI / IA / Admin / Nav / PWA-Leiste** | **`docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md`** | > | **Dashboard-Widgets** (Katalog, Registrierung, `config`) | **`.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md`** | +> | **Designprinzipien Produktfamilie** (Serie 1–9, Foundation) | **`.claude/docs/jinkendo-foundation/design-principles/README.md`** | > | **Agent-Einstieg** | **`.claude/README.md`** | > | **Activity Session Metrics (EAV, Attributprofile)** | **`.claude/docs/technical/ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md`** | diff --git a/backend/data_layer/activity_persistence_orchestrator.py b/backend/data_layer/activity_persistence_orchestrator.py index 2e128e2..8bf4a3d 100644 --- a/backend/data_layer/activity_persistence_orchestrator.py +++ b/backend/data_layer/activity_persistence_orchestrator.py @@ -135,7 +135,7 @@ def insert_activity_from_entry(cur, profile_id: str, eid: str, e: ActivityEntry) hr_avg,hr_max,hr_min,distance_km,pace_min_per_km,cadence,avg_power,elevation_gain, temperature_celsius,humidity_percent,avg_hr_percent,kcal_per_km,rpe,source,notes, training_type_id,training_category,training_subcategory,created) - VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""", + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""", ( eid, profile_id, diff --git a/backend/scripts/audit_insert_placeholders.py b/backend/scripts/audit_insert_placeholders.py new file mode 100644 index 0000000..e7ed997 --- /dev/null +++ b/backend/scripts/audit_insert_placeholders.py @@ -0,0 +1,88 @@ +"""Audit: INSERT %s-Platzhalter vs. Parameter-Anzahl in backend/.""" + +from __future__ import annotations + +import ast +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def _count_tuple_elements(node: ast.AST) -> int | None: + if isinstance(node, ast.Tuple): + return len(node.elts) + if isinstance(node, ast.List): + return len(node.elts) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): + if node.func.attr in {"values", "keys"}: + return None + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add): + left = _count_tuple_elements(node.left) + right = _count_tuple_elements(node.right) + if left is not None and right is not None: + return left + right + if isinstance(node, ast.ListComp): + return None + return None + + +def audit_file(path: Path) -> list[str]: + issues: list[str] = [] + try: + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + except SyntaxError: + return issues + + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + func = node.func + if not ( + isinstance(func, ast.Attribute) + and func.attr == "execute" + and isinstance(func.value, ast.Name) + and func.value.id == "cur" + ): + continue + if len(node.args) < 2: + continue + sql_node, params_node = node.args[0], node.args[1] + sql = None + if isinstance(sql_node, ast.Constant) and isinstance(sql_node.value, str): + sql = sql_node.value + elif isinstance(sql_node, ast.JoinedStr): + continue + if not sql or "INSERT" not in sql.upper(): + continue + if "VALUES" not in sql.upper(): + continue + ph = sql.count("%s") + param_count = _count_tuple_elements(params_node) + if param_count is None: + continue + if ph != param_count: + rel = path.relative_to(ROOT) + issues.append( + f"{rel}:{node.lineno} INSERT placeholders={ph} params={param_count}" + ) + return issues + + +def main() -> int: + issues: list[str] = [] + for path in ROOT.rglob("*.py"): + if "venv" in path.parts or "__pycache__" in path.parts: + continue + issues.extend(audit_file(path)) + if issues: + print("MISMATCHES:") + for issue in sorted(issues): + print(issue) + return 1 + print("OK: keine INSERT-Platzhalter-Mismatches gefunden (statisch prüfbar).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/backend/tests/test_activity_insert_sql.py b/backend/tests/test_activity_insert_sql.py new file mode 100644 index 0000000..4b12e63 --- /dev/null +++ b/backend/tests/test_activity_insert_sql.py @@ -0,0 +1,49 @@ +"""Regression: INSERT-Platzhalter müssen zur Wert-Tuple passen.""" + +from data_layer.activity_persistence_orchestrator import ( + insert_activity_csv_minimal, + insert_activity_from_entry, +) +from models import ActivityEntry + + +class _CaptureCursor: + def __init__(self): + self.captured = {} + + def execute(self, sql, params): + self.captured["sql"] = sql + self.captured["params"] = params + + +def test_insert_activity_from_entry_smoke_with_mock_cursor(): + cur = _CaptureCursor() + entry = ActivityEntry(date="2026-07-22", activity_type="Laufen") + insert_activity_from_entry(cur, "profile-1", "entry-1", entry) + assert cur.captured["params"] is not None + assert cur.captured["sql"].count("%s") == len(cur.captured["params"]) + + +def test_insert_activity_csv_minimal_smoke_with_mock_cursor(): + cur = _CaptureCursor() + insert_activity_csv_minimal( + cur, + "profile-1", + "entry-1", + date_iso="2026-07-22", + start_time=None, + end_time=None, + activity_type="Laufen", + duration_min=45, + kcal_active=None, + kcal_resting=None, + hr_avg=None, + hr_max=None, + distance_km=None, + training_type_id=1, + training_category="cardio", + training_subcategory=None, + source="csv", + ) + assert cur.captured["params"] is not None + assert cur.captured["sql"].count("%s") == len(cur.captured["params"])