initial base setup #1
18
.cursor/rules/kairo-architecture.mdc
Normal file
18
.cursor/rules/kairo-architecture.mdc
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
## Reference design principles
|
||||
|
||||
Reference material lives in:
|
||||
|
||||
- docs/reference/design-principles/mitai/
|
||||
- docs/reference/design-principles/shinkan/
|
||||
- docs/reference/design-principles/alignment/
|
||||
|
||||
These files explain why certain patterns exist. They are not implementation scope unless the Kairo Sprint-0 Principle Gate or an Architecture Decision explicitly pulls them into scope.
|
||||
|
||||
Conflict order:
|
||||
1. Kairo_Sprint0_Principle_Gate_v0.1.md
|
||||
2. Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md
|
||||
3. Jinkendo_Kairo_Product_Spec_v0.2.md
|
||||
4. Alignment documents
|
||||
5. Mitai/Shinkan individual principle documents
|
||||
22
.env.example
22
.env.example
|
|
@ -12,8 +12,6 @@
|
|||
# APP_URL=https://kairo.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://kairo.jinkendo.de
|
||||
# ENVIRONMENT=production
|
||||
# KAIRO_MEDIA_HOST=/kairo-media
|
||||
# MEDIA_ROOT=/app/media
|
||||
|
||||
# ─── Typische Werte DEV (docker-compose.dev-env.yml) ─────────────────────────
|
||||
# DB_NAME=kairo_dev
|
||||
|
|
@ -22,8 +20,6 @@
|
|||
# APP_URL=https://dev.kairo.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://dev.kairo.jinkendo.de,http://192.168.2.49:3097
|
||||
# ENVIRONMENT=development
|
||||
# KAIRO_MEDIA_HOST=/kairo-media/dev
|
||||
# MEDIA_ROOT=/app/media
|
||||
|
||||
# ─── Ab hier: eine ausfüllbare Vorlage (bei uns meist Prod-Defaults) ───────────
|
||||
DB_HOST=postgres
|
||||
|
|
@ -56,15 +52,9 @@ APP_URL=https://kairo.jinkendo.de
|
|||
ALLOWED_ORIGINS=https://kairo.jinkendo.de
|
||||
ENVIRONMENT=production
|
||||
|
||||
# Medien (Docker Compose): KAIRO_MEDIA_HOST = Verzeichnis auf dem Host (Bind-Mount),
|
||||
# MEDIA_ROOT = gleicher Pfad im Container (muss mit dem Mount-Ziel übereinstimmen).
|
||||
KAIRO_MEDIA_HOST=/kairo-media
|
||||
MEDIA_ROOT=/app/media
|
||||
|
||||
MEDIAWIKI_API_URL=https://karatetrainer.net/api.php
|
||||
MEDIAWIKI_USER=Jinkendo
|
||||
MEDIAWIKI_PASSWORD=CHANGE_ME
|
||||
MEDIAWIKI_CATEGORY_EXERCISES=Übungen
|
||||
MEDIAWIKI_CATEGORY_SKILLS=Fähigkeitsbeschreibung
|
||||
MEDIAWIKI_CATEGORY_METHODS=Methodenbeschreibung
|
||||
MEDIAWIKI_CATEGORY_MODELS=Reifegradmodelle
|
||||
# ─── Medien (optional, derzeit nicht aktiv) ───────────────────────────────────
|
||||
# Kairo benötigt aktuell keine Medien-Speicherung. Falls später nötig:
|
||||
# 1. NAS-Freigabe auf dem Pi mounten (nicht lokal auf dem Raspberry!)
|
||||
# 2. docker-compose.override.yml mit Bind-Mount ergänzen (siehe docs/DEPLOYMENT.md)
|
||||
# KAIRO_MEDIA_HOST=/mnt/nas/kairo-media
|
||||
# MEDIA_ROOT=/app/media
|
||||
|
|
|
|||
143
CLAUDE.md
143
CLAUDE.md
|
|
@ -1,59 +1,120 @@
|
|||
# Kairo Jinkendo – Entwickler-Kontext
|
||||
# CLAUDE.md – Jinkendo Kairo
|
||||
|
||||
## Projekt-Übersicht
|
||||
Du arbeitest im Projekt **Jinkendo Kairo**.
|
||||
|
||||
**Kairo Jinkendo** (回廊 Jinkendo) — Schwesterprodukt in der **Jinkendo**-App-Familie (人拳道).
|
||||
Domains: kairo.jinkendo.de · dev.kairo.jinkendo.de
|
||||
Kairo ist der operative Program Director der Jinkendo-Produktfamilie.
|
||||
|
||||
**Status:** Infrastruktur (Docker, Gitea Actions, Deployment-Doku) ist eingerichtet. Anwendungscode folgt.
|
||||
## Leitfrage
|
||||
|
||||
## Tech-Stack (geplant)
|
||||
Welcher nächste Schritt bringt ein Vorhaben aktuell am wirkungsvollsten voran?
|
||||
|
||||
| Komponente | Technologie |
|
||||
|-----------|-------------|
|
||||
| Frontend | React 18 + Vite + PWA (Node 20) |
|
||||
| Backend | FastAPI Python 3.12 |
|
||||
| Datenbank | PostgreSQL 16 Alpine |
|
||||
| Container | Docker + Docker Compose |
|
||||
| Auth | Token-basiert + bcrypt (Familien-Standard) |
|
||||
---
|
||||
|
||||
**Ports:** Prod 3004/8004 · Dev 3097/8097 — nicht ändern ohne explizite Freigabe (Reverse Proxy/Fritz!Box).
|
||||
## 1. Aktueller Entwicklungsstand
|
||||
|
||||
## Deployment
|
||||
Sprint 0.
|
||||
|
||||
```
|
||||
Internet → Fritz!Box (privat.stommer.com) → Synology NAS → Raspberry Pi 5 (192.168.2.49)
|
||||
Es geht noch nicht um die vollständige Fachanwendung, sondern um das Fundament.
|
||||
|
||||
Git Workflow:
|
||||
develop → Auto-Deploy → dev.kairo.jinkendo.de (kairo-dev/, Port 3097/8097)
|
||||
main → Auto-Deploy → kairo.jinkendo.de (kairo/, Port 3004/8004)
|
||||
Sprint 0 baut:
|
||||
|
||||
Gitea: http://192.168.2.144:3000/Lars/Kairo-Jinkendo
|
||||
Runner: Raspberry Pi (/home/lars/gitea-runner/) — gemeinsam mit Shinkan/Mitai
|
||||
- Tenant
|
||||
- User
|
||||
- Actor
|
||||
- TenantContext
|
||||
- Auth-Gates
|
||||
- Capability / Rights Registry
|
||||
- minimale Feature Registry
|
||||
- minimale Prompt Registry
|
||||
- Placeholder Validation
|
||||
- Configuration-Grundmodell
|
||||
- Audit
|
||||
- Migration/Deploy-Grundlage
|
||||
|
||||
Manuell:
|
||||
cd /home/lars/docker/kairo[-dev]
|
||||
docker compose -f docker-compose[.dev-env].yml build --no-cache && up -d
|
||||
---
|
||||
|
||||
## 2. Verbindliche Primärdokumente
|
||||
|
||||
Lies bei Projektstart in dieser Reihenfolge:
|
||||
|
||||
1. `docs/product/Jinkendo_Kairo_Product_Spec_v0.2.md`
|
||||
2. `docs/architecture/Kairo_Sprint0_Principle_Gate_v0.1.md`
|
||||
3. `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
|
||||
4. `docs/sprints/Sprint0_Vibe_Coder_Handover_v0.1.md`
|
||||
5. `docs/architecture/Kairo_Architecture_References_v0.1.md`
|
||||
6. `.cursor/rules/kairo-architecture.mdc`
|
||||
|
||||
---
|
||||
|
||||
## 3. Designprinzipien als Referenz
|
||||
|
||||
Die Designprinzipien aus Mitai und Shinkan liegen unter:
|
||||
|
||||
```text
|
||||
docs/reference/design-principles/
|
||||
mitai/
|
||||
shinkan/
|
||||
alignment/
|
||||
```
|
||||
|
||||
## Verzeichnisstruktur (Zielbild)
|
||||
Diese Dokumente sind **Referenzmaterial**, nicht direkter Arbeitsauftrag.
|
||||
|
||||
```
|
||||
backend/ # FastAPI — noch anzulegen
|
||||
frontend/ # React + Vite — noch anzulegen
|
||||
.gitea/workflows/ # CI/CD (deploy + test)
|
||||
docs/ # Deployment-Doku
|
||||
scripts/load/ # k6 Health-Baseline
|
||||
```
|
||||
Sie dienen dazu, Entscheidungen zu begründen und bekannte Anti-Patterns zu vermeiden.
|
||||
|
||||
## Referenz
|
||||
Sie dürfen nicht dazu verwendet werden, Kairo mit Mitai- oder Shinkan-Domänenlogik zu überladen.
|
||||
|
||||
Deployment-Muster und Familien-Standards: Schwesterprojekt **shinkan-jinkendo** (`c:\Dev\shinkan-jinkendo`).
|
||||
---
|
||||
|
||||
## Jinkendo-Familie
|
||||
## 4. Auslegungsreihenfolge bei Konflikten
|
||||
|
||||
```
|
||||
mitai.jinkendo.de → Körper-Tracker (身体)
|
||||
shinkan.jinkendo.de → Trainingsplanung (真観)
|
||||
kairo.jinkendo.de → (回廊 — Produktdefinition folgt)
|
||||
```
|
||||
Bei Konflikten gilt:
|
||||
|
||||
1. `Kairo_Sprint0_Principle_Gate_v0.1.md`
|
||||
2. `Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
|
||||
3. `Jinkendo_Kairo_Product_Spec_v0.2.md`
|
||||
4. `Jinkendo_Foundation_Minimum_Viable_Foundation_v0.2.md`
|
||||
5. `DESIGN_PRINCIPLES_ALIGNMENT.md`
|
||||
6. Mitai/Shinkan Einzelprinzipien
|
||||
|
||||
Mitai/Shinkan-Prinzipien dürfen Kairo nicht überstimmen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Verbindliche Regeln
|
||||
|
||||
1. Kairo ist mandantenfähig.
|
||||
2. Alle operativen Zuweisungen laufen über Actors.
|
||||
3. User und Actor sind nicht dasselbe.
|
||||
4. Agenten sind Actors, keine Sonderlogik.
|
||||
5. Jeder geschützte Request soll über TenantContext aufgelöst werden.
|
||||
6. Auth, Capability, Feature und Governance sind getrennte Konzepte.
|
||||
7. Rechte und Capabilities werden nicht verstreut hardcodiert.
|
||||
8. Prompts werden nicht hardcodiert.
|
||||
9. Platzhalter werden validiert.
|
||||
10. Fachliche Konfiguration wird nicht hardcodiert.
|
||||
11. Admin- und Agentenaktionen werden auditiert.
|
||||
12. Migrationen sind nummeriert und reproduzierbar.
|
||||
|
||||
---
|
||||
|
||||
## 6. Nicht tun
|
||||
|
||||
- keine Mitai-Domänenlogik kopieren
|
||||
- keine Shinkan-Domänenlogik kopieren
|
||||
- keine Trainingsplanung bauen
|
||||
- keine Gesundheitslogik bauen
|
||||
- keine Lebensmanager-/Seichō-Logik in Kairo einbauen
|
||||
- keine Vorhaben-/Projektlogik in AP0.1 vorziehen
|
||||
- kein Billing oder SSO bauen
|
||||
- keine strategischen Produktentscheidungen eigenmächtig ändern
|
||||
- keine Designprinzipien aus `docs/reference/` ohne Principle-Gate oder Architecture Decision in Scope ziehen
|
||||
|
||||
---
|
||||
|
||||
## 7. Abweichungen
|
||||
|
||||
Bei notwendiger Abweichung erstelle ein Architecture Decision Proposal.
|
||||
|
||||
## 8. Aktueller erster Auftrag
|
||||
|
||||
AP0.1 – Projektgrundlage.
|
||||
|
|
|
|||
98
README.md
98
README.md
|
|
@ -1,63 +1,65 @@
|
|||
# Kairo Jinkendo (回廊)
|
||||
# Jinkendo Kairo
|
||||
|
||||
**Programmmanager für alles im Leben** — Produktfamilie Jinkendo (人拳道), Schwesterprodukt zu Shinkan und Mitai.
|
||||
Operativer Program Director der Jinkendo-Produktfamilie.
|
||||
|
||||
> Infrastruktur-Setup: Deployment, Docker und Gitea Actions sind vorbereitet.
|
||||
> Anwendungscode (Backend/Frontend) folgt in einem separaten Schritt.
|
||||
Kairo steuert Vorhaben, Programme, Projekte, Meilensteine, Maßnahmen, Backlogs, Reviews, Nachweise und die Zusammenarbeit von Menschen, Arbeitsgruppen und KI-Agenten.
|
||||
|
||||
## Deployment-Übersicht
|
||||
## Leitfrage
|
||||
|
||||
| Umgebung | Branch | Server-Pfad | Host-Ports (UI/API) | Domain |
|
||||
|----------|--------|-------------|---------------------|--------|
|
||||
| **Development** | `develop` | `/home/lars/docker/kairo-dev` | 3097 / 8097 | https://dev.kairo.jinkendo.de |
|
||||
| **Production** | `main` | `/home/lars/docker/kairo` | 3004 / 8004 | https://kairo.jinkendo.de |
|
||||
> Welcher nächste Schritt bringt ein Vorhaben aktuell am wirkungsvollsten voran?
|
||||
|
||||
Auto-Deploy via Gitea Actions auf dem Raspberry Pi (gleicher Runner wie Shinkan/Mitai).
|
||||
## Startzustand
|
||||
|
||||
**Gitea:** http://192.168.2.144:3000/Lars/Kairo-Jinkendo
|
||||
Dieses Repository startet mit einem reinen Spezifikations- und Sprint-0-Handover-Paket.
|
||||
|
||||
## Tech-Stack (geplant)
|
||||
Noch nicht enthalten:
|
||||
|
||||
- **Frontend:** React 18 + Vite + PWA
|
||||
- **Backend:** FastAPI (Python 3.12)
|
||||
- **Datenbank:** PostgreSQL 16
|
||||
- **Container:** Docker + Docker Compose
|
||||
- produktiver Anwendungscode
|
||||
- endgültiger Technologie-Stack
|
||||
- vollständige Jinkendo-Foundation
|
||||
- Mitai- oder Shinkan-Code
|
||||
- Billing / SSO / zentrale Produktfamilien-Konvergenz
|
||||
|
||||
## Lokales Setup (wenn App-Code vorhanden)
|
||||
## Verbindliche Dokumente für Sprint 0
|
||||
|
||||
```bash
|
||||
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git
|
||||
cd Kairo-Jinkendo
|
||||
cp .env.example .env
|
||||
# .env anpassen
|
||||
1. `docs/product/Jinkendo_Kairo_Product_Spec_v0.2.md`
|
||||
2. `docs/architecture/Jinkendo_Foundation_Minimum_Viable_Foundation_v0.2.md`
|
||||
3. `docs/architecture/Kairo_Sprint0_Principle_Gate_v0.1.md`
|
||||
4. `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
|
||||
5. `docs/sprints/Sprint0_Vibe_Coder_Handover_v0.1.md`
|
||||
6. `docs/sprints/Sprint0_AP0_1_Project_Setup_Assignment_v0.1.md`
|
||||
7. `CLAUDE.md`
|
||||
8. `.cursor/rules/kairo-architecture.mdc`
|
||||
|
||||
# Development
|
||||
docker compose -f docker-compose.dev-env.yml up --build
|
||||
## Arbeitsmodus
|
||||
|
||||
# Production (lokal)
|
||||
docker compose up --build
|
||||
Kairo wird iterativ entwickelt.
|
||||
|
||||
Sprint 0 baut nur das Fundament:
|
||||
|
||||
- Tenant
|
||||
- User
|
||||
- Actor
|
||||
- TenantContext
|
||||
- Auth-Gates
|
||||
- Capability/Rights Registry
|
||||
- minimale Feature Registry
|
||||
- minimale Prompt Registry
|
||||
- Platzhaltermodell
|
||||
- Audit
|
||||
- Migration/Deploy-Grundlage
|
||||
|
||||
Vorhaben, Projekte, Meilensteine und Maßnahmen kommen erst nach Sprint 0.
|
||||
|
||||
|
||||
## Designprinzipien-Referenzen
|
||||
|
||||
Die extrahierten Designprinzipien aus Mitai und Shinkan liegen im Repository unter:
|
||||
|
||||
```text
|
||||
docs/reference/design-principles/
|
||||
```
|
||||
|
||||
Frontend (Dev): http://localhost:3097
|
||||
Backend (Dev): http://localhost:8097
|
||||
Diese Dokumente sind Referenzen, kein direkter Sprint-Scope.
|
||||
|
||||
## Server-Einrichtung
|
||||
|
||||
Siehe [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) für Verzeichnisse, `.env`-Dateien und Reverse-Proxy-Hinweise auf dem Pi.
|
||||
|
||||
## Git-Workflow
|
||||
|
||||
```
|
||||
develop → Auto-Deploy → kairo-dev (Dev)
|
||||
main → Auto-Deploy → kairo (Prod)
|
||||
```
|
||||
|
||||
## Dokumentation
|
||||
|
||||
- **Deployment:** [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)
|
||||
- **Entwickler-Kontext:** [CLAUDE.md](CLAUDE.md)
|
||||
- **Schwesterprojekt (Referenz):** [shinkan-jinkendo](http://192.168.2.144:3000/Lars/shinkan-jinkendo)
|
||||
|
||||
## Lizenz
|
||||
|
||||
Proprietary – Lars Stommer
|
||||
Verbindlich ist nur, was über das Kairo Sprint-0 Principle Gate oder eine Architecture Decision übernommen wurde.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# Keine festen container_name — Compose-Namen haben Projektprefix (<projekt>-postgres-1).
|
||||
# Medien: In .env KAIRO_MEDIA_HOST (Host-Pfad) und optional MEDIA_ROOT (Container-Pfad) setzen.
|
||||
# Default Host /kairo-media/dev — Verzeichnis ggf. anlegen oder Compose legt es an.
|
||||
# Medien: aktuell nicht vorgesehen. Bei Bedarf NAS-Mount + docker-compose.override.yml (siehe docs/DEPLOYMENT.md).
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
|
@ -39,16 +38,6 @@ services:
|
|||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.kairo.jinkendo.de,http://192.168.2.49:3097}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-development}"
|
||||
CLUB_FEATURE_ENFORCE: "${CLUB_FEATURE_ENFORCE:-1}"
|
||||
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
|
||||
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-Jinkendo}"
|
||||
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-CHANGE_ME}"
|
||||
MEDIAWIKI_CATEGORY_EXERCISES: "${MEDIAWIKI_CATEGORY_EXERCISES:-Übungen}"
|
||||
MEDIAWIKI_CATEGORY_SKILLS: "${MEDIAWIKI_CATEGORY_SKILLS:-Fähigkeitsbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_METHODS: "${MEDIAWIKI_CATEGORY_METHODS:-Methodenbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_MODELS: "${MEDIAWIKI_CATEGORY_MODELS:-Reifegradmodelle}"
|
||||
MEDIA_ROOT: "${MEDIA_ROOT:-/app/media}"
|
||||
volumes:
|
||||
- ${KAIRO_MEDIA_HOST:-/kairo-media/dev}:${MEDIA_ROOT:-/app/media}
|
||||
ports:
|
||||
- "8097:8000"
|
||||
depends_on:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# Medien: aktuell nicht vorgesehen. Bei Bedarf NAS-Mount + docker-compose.override.yml (siehe docs/DEPLOYMENT.md).
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
|
|
@ -41,16 +43,6 @@ services:
|
|||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://kairo.jinkendo.de}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-production}"
|
||||
CLUB_FEATURE_ENFORCE: "${CLUB_FEATURE_ENFORCE:-1}"
|
||||
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
|
||||
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-}"
|
||||
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-}"
|
||||
MEDIAWIKI_CATEGORY_EXERCISES: "${MEDIAWIKI_CATEGORY_EXERCISES:-Übungen}"
|
||||
MEDIAWIKI_CATEGORY_SKILLS: "${MEDIAWIKI_CATEGORY_SKILLS:-Fähigkeitsbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_METHODS: "${MEDIAWIKI_CATEGORY_METHODS:-Methodenbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_MODELS: "${MEDIAWIKI_CATEGORY_MODELS:-Reifegradmodelle}"
|
||||
MEDIA_ROOT: "${MEDIA_ROOT:-/app/media}"
|
||||
volumes:
|
||||
- ${KAIRO_MEDIA_HOST:-/kairo-media}:${MEDIA_ROOT:-/app/media}
|
||||
ports:
|
||||
- "8004:8000"
|
||||
depends_on:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
| **Backend-Port** | 8004 | 8097 |
|
||||
| **PostgreSQL (localhost)** | 5436 | 5437 |
|
||||
| **Domain** | kairo.jinkendo.de | dev.kairo.jinkendo.de |
|
||||
| **Medien-Host-Pfad** | `/kairo-media` | `/kairo-media/dev` |
|
||||
|
||||
**Familien-Referenz (bereits belegt):**
|
||||
|
||||
|
|
@ -33,10 +32,9 @@
|
|||
Auf dem Pi als User `lars` ausführen:
|
||||
|
||||
```bash
|
||||
# Verzeichnisse anlegen
|
||||
# Deploy-Verzeichnisse anlegen
|
||||
mkdir -p /home/lars/docker/kairo
|
||||
mkdir -p /home/lars/docker/kairo-dev
|
||||
mkdir -p /kairo-media /kairo-media/dev
|
||||
|
||||
# Production
|
||||
cd /home/lars/docker/kairo
|
||||
|
|
@ -57,6 +55,31 @@ cp .env.example .env
|
|||
|
||||
---
|
||||
|
||||
## Medien (optional, derzeit nicht eingerichtet)
|
||||
|
||||
Kairo benötigt **aktuell keine Medien-Speicherung**. Es sind keine Medien-Verzeichnisse auf dem Pi anzulegen.
|
||||
|
||||
Falls später Datei-Uploads o. Ä. nötig werden:
|
||||
|
||||
1. **NAS-Freigabe** auf dem Synology anlegen (nicht auf dem Raspberry Pi speichern).
|
||||
2. **Mount auf dem Pi** einrichten (z. B. `/mnt/nas/kairo-media` bzw. `/mnt/nas/kairo-media/dev`).
|
||||
3. **`docker-compose.override.yml`** im jeweiligen Deploy-Verzeichnis ergänzen (wird von Git ignoriert):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
backend:
|
||||
environment:
|
||||
MEDIA_ROOT: /app/media
|
||||
volumes:
|
||||
- /mnt/nas/kairo-media:/app/media # Dev: …/kairo-media/dev
|
||||
```
|
||||
|
||||
4. Im Backend `MEDIA_ROOT` auswerten — erst wenn die App Medien unterstützt.
|
||||
|
||||
Analog Shinkan (`SHINKAN_MEDIA_HOST`), aber bewusst **nicht** Teil des Initial-Setups.
|
||||
|
||||
---
|
||||
|
||||
## Reverse Proxy (Synology / Fritz!Box)
|
||||
|
||||
Analog zu Shinkan — neue Hostnamen im Proxy eintragen:
|
||||
|
|
|
|||
61
docs/architecture/Kairo_Architecture_References_v0.1.md
Normal file
61
docs/architecture/Kairo_Architecture_References_v0.1.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Kairo Architecture References v0.1
|
||||
|
||||
Status: verbindliche Referenzliste für Coding-Agenten
|
||||
Stand: 2026-07-04
|
||||
|
||||
## Primäre Kairo-Dokumente
|
||||
|
||||
1. `docs/product/Jinkendo_Kairo_Product_Spec_v0.2.md`
|
||||
2. `docs/architecture/Jinkendo_Foundation_Minimum_Viable_Foundation_v0.2.md`
|
||||
3. `docs/architecture/Kairo_Sprint0_Principle_Gate_v0.1.md`
|
||||
4. `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
|
||||
5. `docs/sprints/Sprint0_Vibe_Coder_Handover_v0.1.md`
|
||||
6. `CLAUDE.md`
|
||||
7. `.cursor/rules/kairo-architecture.mdc`
|
||||
|
||||
## Referenzdokumente
|
||||
|
||||
### Alignment
|
||||
|
||||
- `docs/reference/design-principles/alignment/DESIGN_PRINCIPLES_ALIGNMENT.md`
|
||||
- `docs/reference/design-principles/alignment/FAMILY_ENTITLEMENT_MODEL.md`
|
||||
|
||||
### Mitai
|
||||
|
||||
- `docs/reference/design-principles/mitai/PROMPT_ENGINE_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/mitai/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/mitai/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/mitai/AUTH_SESSION_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/mitai/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md`
|
||||
|
||||
### Shinkan
|
||||
|
||||
- `docs/reference/design-principles/shinkan/ACCESS_LAYER_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/shinkan/RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/shinkan/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/shinkan/AI_PROMPT_RUNTIME_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/shinkan/AUTH_SESSION_DESIGN_PRINCIPLES.md`
|
||||
- `docs/reference/design-principles/shinkan/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md`
|
||||
|
||||
## Auslegungsregel
|
||||
|
||||
Bei Konflikten gilt diese Reihenfolge:
|
||||
|
||||
1. Kairo Sprint-0 Principle Gate
|
||||
2. Sprint-0 Foundation v0.3
|
||||
3. Kairo Product Spec
|
||||
4. Kairo Foundation v0.2
|
||||
5. Alignment-Dokumente
|
||||
6. Mitai/Shinkan Einzelprinzipien
|
||||
|
||||
Mitai/Shinkan-Dokumente dürfen Kairo nicht überstimmen.
|
||||
|
||||
## Drift-Regel
|
||||
|
||||
Ein Agent darf Designprinzipien nur als Begründung verwenden, nicht als Erweiterungsauftrag.
|
||||
|
||||
Erlaubt:
|
||||
> Shinkan Rights Registry begründet, warum Kairo eine Capability Registry braucht.
|
||||
|
||||
Nicht erlaubt:
|
||||
> Shinkan hat Club Features, also bauen wir Club Features in Kairo.
|
||||
56
docs/reference/design-principles/README.md
Normal file
56
docs/reference/design-principles/README.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Designprinzipien – Referenzpaket für Kairo
|
||||
|
||||
Status: Referenz / nicht automatisch verbindlich
|
||||
Stand: 2026-07-04
|
||||
|
||||
Dieses Verzeichnis enthält die extrahierten Designprinzipien aus Mitai und Shinkan sowie den Abgleich.
|
||||
|
||||
Diese Dokumente sind **Referenzmaterial**, kein direkter Sprint-Scope.
|
||||
|
||||
Verbindlich für Kairo ist nur, was in folgenden Dokumenten steht:
|
||||
|
||||
1. `docs/architecture/Kairo_Sprint0_Principle_Gate_v0.1.md`
|
||||
2. `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
|
||||
3. `docs/product/Jinkendo_Kairo_Product_Spec_v0.2.md`
|
||||
4. eine spätere explizite Architecture Decision
|
||||
|
||||
## Struktur
|
||||
|
||||
```text
|
||||
docs/reference/design-principles/
|
||||
mitai/
|
||||
shinkan/
|
||||
alignment/
|
||||
```
|
||||
|
||||
## Für Kairo Sprint 0 relevant
|
||||
|
||||
- TenantContext / mandantenfähiger Request-Kontext
|
||||
- Actor-first statt User-first
|
||||
- Auth, Capability, Feature und Governance trennen
|
||||
- Rights / Capability Registry
|
||||
- minimale Feature Registry
|
||||
- minimale Prompt Registry
|
||||
- Prompt Templates nicht hardcoden
|
||||
- Template und Kontext trennen
|
||||
- typisierte und validierte Platzhalter
|
||||
- nummerierte Migrationen und Fail-Fast
|
||||
|
||||
## Nicht automatisch übernehmen
|
||||
|
||||
- Mitai Data Layer
|
||||
- Mitai Widget Dashboard
|
||||
- Mitai Universal Import
|
||||
- vollständige Mitai Prompt Engine
|
||||
- Shinkan Exercise Catalog
|
||||
- Shinkan Training Planning
|
||||
- Shinkan Skill Scoring
|
||||
- Shinkan Media Assets
|
||||
- Shinkan Content Reports
|
||||
- Shinkan Maturity Models
|
||||
- Billing, Tiers, Coupons, Usage-Limits
|
||||
- zentrale Familien-Konvergenz
|
||||
|
||||
## Regel
|
||||
|
||||
Wenn ein Agent ein Prinzip aus diesem Referenzpaket übernehmen möchte, das nicht im Sprint-0-Gate steht, muss er ein Architecture Decision Proposal erstellen.
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
# Designprinzipien – Abgleich Mitai ↔ Shinkan
|
||||
|
||||
**Status:** Review / Entscheidungsgrundlage
|
||||
**Stand:** 2026-07-04
|
||||
**Zweck:** Widersprüche, bewusste Abweichungen und Implementierungslücken zwischen den Designprinzipien-Serien identifizieren — Basis für **Familien-Entscheidungen** und langfristige Konvergenz von Mitai und Shinkan.
|
||||
|
||||
**Quellen:**
|
||||
|
||||
| App | Index |
|
||||
|-----|--------|
|
||||
| Mitai (Foundation) | [mitai-jinkendo/.claude/docs/jinkendo-foundation/design-principles/README.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/jinkendo-foundation/design-principles/README.md) — 9 Module |
|
||||
| Shinkan | [design-principles/DESIGN_PRINCIPLES_INDEX.md](./design-principles/DESIGN_PRINCIPLES_INDEX.md) — 15 Module |
|
||||
|
||||
---
|
||||
|
||||
## 1. Kurzfassung
|
||||
|
||||
| Kategorie | Anzahl | Bedeutung |
|
||||
|-----------|--------|-----------|
|
||||
| **Familien-Konsens** | 12 Muster | In beiden Serien gleich oder kompatibel — **verbindlich für neue Apps** |
|
||||
| **Bewusste Produkt-Abweichung** | 8 | Fachlich/Architektur begründet — **nicht angleichen**, aber im Familienmodell verankern |
|
||||
| **Konzeptuelle Spannung** | 6 | Widersprüche oder gegenläufige Defaults — **Familien-Entscheidung nötig** |
|
||||
| **Ist vs. Prinzip (Schuld)** | 14+ | Mindestens eine App verletzt eigene oder Schwester-Prinzipien — **Remediation** |
|
||||
| **Nur Shinkan** | 6 Module | Mandanten-/Domänen-Bausteine ohne Mitai-Pendant |
|
||||
| **Nur Mitai (reifer)** | 3 Muster | Data Layer, Widget-Dashboard, Universal Import — Shinkan vereinfacht oder fehlt |
|
||||
|
||||
**Kernbefund:** Mitai und Shinkan teilen dieselbe **technische Basis** (Auth, Migration, Nav-SSoT, Registry-Denken, Probe→Enforce), divergieren aber strukturell bei **Entitlement-Subjekt** (Profil vs. Verein), **Berechnungsarchitektur** (generischer Data Layer vs. domänenspezifisches Scoring) und **KI-Reife** (Unified Executor vs. schmale Laufzeit).
|
||||
|
||||
---
|
||||
|
||||
## 2. Familien-Konsens (für neue Produkte übernehmen)
|
||||
|
||||
Diese Muster sind in beiden Serien explizit oder implizit tragfähig:
|
||||
|
||||
| # | Muster | Mitai | Shinkan |
|
||||
|---|--------|-------|---------|
|
||||
| F1 | Server-Sessions + `Depends(require_auth)` | Auth #1–3 | Auth #1–2 |
|
||||
| F2 | `profile_id` aus Session, nie aus Client-Header | Auth #3 | Auth #3 |
|
||||
| F3 | Auth getrennt von Authorization/Entitlements | Auth #10 | Capabilities + Access Layer |
|
||||
| F4 | Nummerierte SQL-Migrationen + Tracking beim Container-Start | Migration #1–5 | Migration #1–3 |
|
||||
| F5 | Fail-fast, kein Auto-Rollback | Migration #5 | Migration #2 |
|
||||
| F6 | `appNav` / zentrale Nav-Config als SSoT | Navigation #1 | Navigation #1 |
|
||||
| F7 | Admin als eigener Hub/Realm | Navigation #6–7 | Navigation #2 |
|
||||
| F8 | DB-konfigurierbare KI-Prompts (nicht hardcoded Prod) | Prompt #2 | AI Runtime #2 |
|
||||
| F9 | Template vs. Kontext/Daten trennen | Prompt #5 | AI Runtime #4 |
|
||||
| F10 | Ingest ≠ Interpretation beim Import | Import #2 | Wiki Import #1 |
|
||||
| F11 | Preview/Dry-Run vor Massenimport | Import #3+ | Wiki Import #2 |
|
||||
| F12 | 4-Phasen-Rollout Entitlements (Log → Enforce) | Feature #8 | Capability #2 |
|
||||
|
||||
**Empfehlung:** Als **`JINKENDO_FOUNDATION_CHECKLIST`** in künftigen Apps verpflichtend; Details pro Modul in den Einzeldokumenten.
|
||||
|
||||
---
|
||||
|
||||
## 3. Modul-Abgleich (9 vergleichbare Paare)
|
||||
|
||||
Legende **Bewertung:**
|
||||
|
||||
| Symbol | Bedeutung |
|
||||
|--------|-----------|
|
||||
| ✅ | Prinzipien aligned / kompatibel |
|
||||
| ⚠️ | Teilweise aligned; Lücken in Implementierung oder Doku |
|
||||
| 🔀 | Bewusste Produkt-Divergenz (kein Bug) |
|
||||
| ❌ | Widerspruch oder gegenläufiges Konzept — Entscheidung nötig |
|
||||
| 🏗️ | Ist-Stand verletzt dokumentierte Prinzipien (Architekturschuld) |
|
||||
|
||||
---
|
||||
|
||||
### 3.1 Prompt Engine (Mitai #1) ↔ AI Prompt Runtime (Shinkan #5)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Single Entry Point | `execute_prompt` / Unified System | `ai_prompt_runtime` + verteilte Orchestratoren | ❌ Konzept |
|
||||
| Prompt-Typen | base / pipeline / workflow | nur slug + Mustache | 🔀 Shinkan bewusst schlanker |
|
||||
| Platzhalter-Registry | zentral, API-Verträge | Kontext-Arten (`AiPromptContextKind`), kein Registry-Katalog | ⚠️ |
|
||||
| Data Layer-Anbindung | Layer 1 → Resolver | Domänen-Builder ad hoc | ⚠️ |
|
||||
| Debug/Preview | ausgereift | Admin-Vorschau, weniger Runtime-Transparenz | ⚠️ |
|
||||
| Feature-Gating an Execute | teils fehlend (Legacy) | Capability geplant, teils Probe | 🏗️ beide |
|
||||
|
||||
**Widersprüche / gegenläufig:**
|
||||
|
||||
- Mitai: **Ein Executor** ist Kernprinzip. Shinkan: **kein** vergleichbarer Executor — Planungs-KI umgeht teils die Laufzeit.
|
||||
- Beide warnen vor **parallelen KI-Pfaden**; beide haben sie noch (Mitai `insights.py`, Shinkan Router-OpenRouter).
|
||||
|
||||
**Familien-Entscheidung (Vorschlag):**
|
||||
|
||||
| Option | Inhalt |
|
||||
|--------|--------|
|
||||
| **Zielbild** | Gemeinsame **`prompt_executor`-Fassade** (Package oder Copy mit Namespace); Shinkan-Kontext-Builder als Plugins |
|
||||
| **Shinkan-Roadmap** | Planungs-Orchestrierung in Laufzeit ziehen; keine Workflow-Graphs vor Planungs-Kontext-Reife |
|
||||
| **Nicht kopieren** | Mitai: doppelte Pipeline-Modelle, PLACEHOLDER_MAP-Duplikat, Roh-SQL im Executor |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Data Layer (Mitai #2) ↔ Skill Scoring (Shinkan #8)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Berechnungs-SSoT | `data_layer/` Layer 0→1→2 | nur `skill_scoring.py` | ❌ Abdeckung |
|
||||
| Router delegieren | explizites Prinzip #13 | Skill-Router ja; Planung teils nicht | ⚠️ |
|
||||
| Confidence / data_points | Pflicht-Metadaten | nicht analog | 🔀 Domäne anders |
|
||||
| Chart/KPI-Anbindung | Layer 2b Adapter | KPI-Dashboard ruft Router-Helfer | ⚠️ |
|
||||
| Import-Grenze | keine Scores beim Insert | Wiki: explizit kein Scoring beim Insert | ✅ |
|
||||
|
||||
**Widerspruch:**
|
||||
|
||||
- Mitai postuliert **generische Berechnungsschicht** für die ganze App. Shinkan hat **kein** Data Layer — nur ein **domänenspezifisches** Scoring-Modul. Das ist keine Implementierungslücke allein, sondern **unterschiedliche Architektur-Tiefe**.
|
||||
|
||||
**Familien-Entscheidung (Vorschlag):**
|
||||
|
||||
| Option | Inhalt |
|
||||
|--------|--------|
|
||||
| **Familien-Prinzip** | „Berechnungen in benannter Schicht, nicht in Router/React“ — **Ja** |
|
||||
| **Implementierung** | Mitai: `data_layer/` bleibt Referenz. Shinkan: Skill Scoring **ist** Layer-1-Vorbild; langfristig **`planning_metrics/`** o. ä. statt Router-SQL |
|
||||
| **Nicht verallgemeinern** | Mitai-Formeln (TDEE, WHR) — nur Schichtenmodell übernehmen |
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Feature & Entitlement (Mitai #3) ↔ Capability & Club Features (Shinkan #2)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Subjekt | **Profil** + Tier | **Verein** (`club_id`) + Plan | ❌ Scope |
|
||||
| Auflösungs-API | `check_feature_access` | `check_capability` + `club_features` + `/me/entitlements` | 🔀 |
|
||||
| Rollout 4 Phasen | ja | ja (Env-Flags) | ✅ |
|
||||
| Registry | DB `features` + Tiers | Code-Registry → DB Sync | ⚠️ |
|
||||
| Capabilities vs. Features | Features only | Capabilities **und** Kontingente getrennt | 🔀 Shinkan feiner |
|
||||
| Widget/Layout-Gating | zentral | Entitlements-API, kein Widget-Layout | ⚠️ |
|
||||
|
||||
**Größter Familien-Konflikt:**
|
||||
|
||||
Mitai-Dokument #3 „Nicht übernehmen“ Punkt 10: *„Profile as Entitlement-Subject — Multi-App-Familie braucht separates Identity/Subscription-Boundary.“*
|
||||
Shinkan **ist** die Antwort mit Verein als Subjekt — aber es gibt **kein gemeinsames Familienmodell**, das Profil-Tier **und** Org-Limits kombiniert.
|
||||
|
||||
**Familien-Entscheidung (Vorschlag):**
|
||||
|
||||
```
|
||||
Entitlement-Subjekt (familie):
|
||||
├── account (profile_id) → Tier, persönliche Limits (Mitai)
|
||||
└── tenant (club_id?) → Org-Plan, Capabilities (Shinkan, optional null)
|
||||
|
||||
API: GET /me/entitlements?tenant_id=
|
||||
Enforcement: eine resolve_entitlement(subject, capability|feature)
|
||||
```
|
||||
|
||||
| App | Remediation |
|
||||
|-----|-------------|
|
||||
| Mitai | Org-Scope reservieren; Tier-Drift (#3 Schuld) bereinigen |
|
||||
| Shinkan | `CLUB_FEATURE_ENFORCE=1` produktiv; Mitai-Legacy `check_feature_access` nicht nutzen (bereits Regel) |
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Registry / Plugin (Mitai #4) ↔ Rights Registry (Shinkan #3)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Registry-first | Platzhalter, Widgets, CSV-Module | Capabilities + Features only | ⚠️ Abdeckung |
|
||||
| Validierung an Grenze | ja | ja (`register_*` wirft) | ✅ |
|
||||
| Runtime + DB | Dual (Katalog + DB-Overrides) | Code → DB Upsert | ✅ |
|
||||
| Dual Registry FE/BE | Widgets + `registerDashboardWidgets` | nicht vorhanden | 🔀 |
|
||||
| Metadaten-Tiefe | nach Risiko (Matrix in Doc) | schlankere Dataclasses | ✅ |
|
||||
|
||||
**Kein Widerspruch** — Shinkan Rights Registry ist **Teilmenge** des Mitai-Meta-Musters.
|
||||
|
||||
**Familien-Entscheidung:** Mitai-Registry-Matrix (Platzhalter / UI-Plugin / Import-Modul / Rechte) als **Familien-Taxonomie**; Shinkan erweitert um Import-/Prompt-Registry wenn Wiki-Import generisch wird.
|
||||
|
||||
---
|
||||
|
||||
### 3.5 Auth & Session (Mitai #5 ↔ Shinkan #4)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Session-Modell | opaque token | gleich (shared `auth.py`) | ✅ |
|
||||
| Depends-Pattern | ja | ja (+ TenantContext) | ✅ |
|
||||
| IDOR Profile-Header | dokumentierte Schwäche | Shinkan: Session-only betont | ⚠️ prüfen |
|
||||
| RBAC | `role` admin/user | Portal-Rolle **+** Vereinsrollen | 🔀 |
|
||||
| Rate Limiting | ja | (Mitai-spezifisch in Router) | ⚠️ |
|
||||
| Feature-Flags in Session | Legacy-Spalten | Account-Lifecycle separat | 🏗️ Mitai |
|
||||
|
||||
**Gegenläufig:** Shinkan **erweitert** Auth um Mandanten — darf Mandantenlogik **nicht** in `auth.py` legen (Shinkan-Prinzip „Nicht übernehmen“).
|
||||
|
||||
**Familien-Entscheidung:** Gemeinsames Auth-Modul; **TenantContext** als optionales Add-on-Pattern für mandantenfähige Apps.
|
||||
|
||||
---
|
||||
|
||||
### 3.6 Universal Import (Mitai #6) ↔ Wiki Import (Shinkan #11)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| Ingest ≠ Interpretation | ✅ | ✅ | ✅ |
|
||||
| Modul-Registry | zentral | fehlt (wiki-hardcoded) | ⚠️ |
|
||||
| SAVEPOINT pro Zeile | ja | nicht dokumentiert | ⚠️ |
|
||||
| Vorlagen/Mappings | generisch | SMW-Kategorien via Env | 🔀 |
|
||||
| Feature-Limits | an Import gebunden | Admin-only | ⚠️ |
|
||||
|
||||
**Kein Konflikt** — unterschiedliche Reife. Shinkan ist **Spezialfall** des Mitai-Musters.
|
||||
|
||||
**Familien-Entscheidung:** Neue Import-Quellen über **Universal-Import-Gerüst** (Mitai); Wiki als `import_type=mediawiki` registrieren.
|
||||
|
||||
---
|
||||
|
||||
### 3.7 Dashboard Widgets (Mitai #7) ↔ Dashboard KPIs (Shinkan #14)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| UX-Modell | konfigurierbares Widget-Layout | festes KPI-Aggregat | ❌ UX-Konzept |
|
||||
| Chatty Client vermeiden | via Widget-Daten | via `/dashboard/kpis` | ✅ Ziel |
|
||||
| Entitlements | `allowed` pro Widget | TenantContext auf KPIs | ✅ |
|
||||
| Data Layer | Widgets konsumieren Layer 1 | intern Router-Helfer | ⚠️ |
|
||||
|
||||
**Gegenläufig:** Mitai: **Nutzer konfiguriert Dashboard**. Shinkan: **Produkt definiert feste Kacheln** — bewusste MVP-Vereinfachung.
|
||||
|
||||
**Familien-Entscheidung:**
|
||||
|
||||
| App-Typ | Dashboard-Pattern |
|
||||
|---------|-------------------|
|
||||
| Personal Tracking (Mitai) | Widget-Katalog + Layout-JSON |
|
||||
| Trainer/Verein (Shinkan) | Aggregierte KPI-Endpoints ausreichend; Widget-System optional Phase 2 |
|
||||
| Neue App | Aggregat-Endpoint **mindestens**; Widget-System wenn Personalisierung nötig |
|
||||
|
||||
---
|
||||
|
||||
### 3.8 Navigation / IA (Mitai #8 ↔ Shinkan #12)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| appNav SSoT | ja | ja | ✅ |
|
||||
| Admin-Hub | Shell + Hub-Gruppen | horizontale `AdminPageNav` | ⚠️ |
|
||||
| Breakpoint 1024px | explizit | „prüfen“ | ⚠️ |
|
||||
| Onboarding-Nav | — | reduziert ohne Verein | 🔀 Shinkan |
|
||||
| adminNav.js SSoT | empfohlen | hardcoded Array in JSX | 🏗️ Shinkan |
|
||||
| Safe Area PWA | ja | Design-System, weniger explizit | ⚠️ |
|
||||
|
||||
**Familien-Entscheidung:** `appNav.js` + **`adminNav.js`** als Pflicht; Shinkan `AdminPageNav` refactoren.
|
||||
|
||||
---
|
||||
|
||||
### 3.9 Migration & Deploy (Mitai #9 ↔ Shinkan #13)
|
||||
|
||||
| Aspekt | Mitai | Shinkan | Bewertung |
|
||||
|--------|-------|---------|-----------|
|
||||
| XXX_*.sql + schema_migrations | ✅ | ✅ | ✅ |
|
||||
| Startup vor App | ✅ | ✅ | ✅ |
|
||||
| develop/main | ✅ | ✅ | ✅ |
|
||||
| Feste Ports | ✅ | ✅ | ✅ |
|
||||
| Immutabler Docker-Build | dokumentiert | nicht im Shinkan-Doc | ⚠️ Doku |
|
||||
| Health-Check / PG wait | ausführlich | kürzer | ⚠️ Doku |
|
||||
|
||||
**Aligned** — Shinkan-Dokument ist **Untermenge**; Implementierung vermutlich gleich (shared Infra).
|
||||
|
||||
---
|
||||
|
||||
## 4. Nur Shinkan (6 Module) — Einordnung für die Familie
|
||||
|
||||
| Modul | Familien-Relevanz | Mitai-Bezug |
|
||||
|-------|-------------------|------------|
|
||||
| **Access Layer & Tenant** | **Pflicht** für mandantenfähige Apps | Mitai #3 fordert Org-Boundary — hier ausformuliert |
|
||||
| **Media Assets & Archiv** | Optional (Content-Apps) | — |
|
||||
| **Exercise Catalog** | Shinkan-Domäne | — |
|
||||
| **Training Planning** | Shinkan-Domäne | — |
|
||||
| **Content Reports (P-13)** | Empfohlen für UGC/Plattform | — |
|
||||
| **Maturity Models** | Optional (Kompetenz-Apps) | — |
|
||||
|
||||
**Kein Widerspruch zu Mitai** — ergänzen das Familienmodell um **Mandant + Content-Governance**.
|
||||
|
||||
---
|
||||
|
||||
## 5. Querschnitt: Ist-Stand vs. dokumentierte Prinzipien
|
||||
|
||||
Gemeinsame **Architekturschuld** (beide Apps verletzen teils eigene „Nicht übernehmen“-Listen):
|
||||
|
||||
| Thema | Mitai | Shinkan |
|
||||
|-------|-------|---------|
|
||||
| Parallele KI-Pfade | `insights.py` Legacy | OpenRouter direkt in Routern |
|
||||
| Entitlement Enforcement | teils UI-only / Legacy-Spalten | Env Probe-only |
|
||||
| Registry-Sync / Duplikat | PLACEHOLDER_MAP + Registry | Capabilities-Sync, kein Prompt-Registry |
|
||||
| Frontend ohne Backend-Gate | teils | Capabilities Probe |
|
||||
| Dokumentations-Drift | Tier vs. Enforcement-Docs | Endpoint-Audit unvollständig |
|
||||
| Monolithische Pages/Client | God Pages, api.js | God Pages, api.js (Roadmap Phase 4) |
|
||||
| Fehlende JSON-Schema-KI | TODO | TODO (explizit vermeiden) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Entscheidungs-Matrix (Priorisiert)
|
||||
|
||||
| Prio | Entscheidung | Betroffene Apps | Empfohlene Familien-Regel |
|
||||
|------|--------------|-----------------|---------------------------|
|
||||
| **P0** | Entitlement-Subjekt: Profil **und** optional Tenant | Mitai, Shinkan, neu | Ein API-Shape `/me/entitlements`; zwei Subjekt-Ebenen |
|
||||
| **P0** | Kein Client-`profile_id` für AuthZ | alle | Session-only; Tenant via Header + Membership |
|
||||
| **P1** | KI: ein Executor pro App | Mitai (fertig), Shinkan (Ziel) | `execute_prompt(slug, context_dto)` |
|
||||
| **P1** | Berechnungs-SSoT-Schicht | Shinkan erweitern | Mindestens ein `*/metrics.py` pro Domäne mit KPIs |
|
||||
| **P1** | Enforcement produktiv | beide | Phase 4 Enforce in Prod für kritische Features |
|
||||
| **P2** | Registry-Taxonomie vereinheitlichen | beide | Rechte / Platzhalter / Import / UI-Plugin |
|
||||
| **P2** | Admin-Nav SSoT | Shinkan | `adminNav.js` wie Mitai |
|
||||
| **P2** | Import: Universal + Spezialmodule | Shinkan | Wiki als registriertes Modul |
|
||||
| **P3** | Dashboard: Aggregat vs. Widgets | produktabhängig | Entscheidungsbaum §3.7 |
|
||||
| **P3** | Shared `auth.py` / `db_init` | beide | Monorepo-Package oder Sync-Disziplin |
|
||||
|
||||
---
|
||||
|
||||
## 7. Konvergenz-Roadmap (langfristig)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph foundation [Familien-Foundation]
|
||||
M9[Migration]
|
||||
M5[Auth]
|
||||
F3[Entitlements 2-Ebenen]
|
||||
R4[Registry Meta]
|
||||
end
|
||||
|
||||
subgraph mitai [Mitai]
|
||||
DL[Data Layer]
|
||||
PE[Prompt Engine]
|
||||
DW[Dashboard Widgets]
|
||||
end
|
||||
|
||||
subgraph shinkan [Shinkan]
|
||||
AL[Access Layer]
|
||||
AR[AI Runtime → Executor]
|
||||
SS[Skill Scoring → Layer 1]
|
||||
end
|
||||
|
||||
M9 --> mitai
|
||||
M9 --> shinkan
|
||||
M5 --> mitai
|
||||
M5 --> shinkan
|
||||
F3 --> mitai
|
||||
F3 --> shinkan
|
||||
R4 --> mitai
|
||||
R4 --> shinkan
|
||||
AL -.->|Mandanten-Apps| foundation
|
||||
PE -.->|Konvergenz| AR
|
||||
DL -.->|Schichtenmodell| SS
|
||||
```
|
||||
|
||||
| Phase | Mitai | Shinkan |
|
||||
|-------|-------|---------|
|
||||
| **Kurz** | Legacy KI-Pfade entfernen; Enforcement-Doku vereinheitlichen | Access-Layer-Audit abschließen; `CAPABILITY_ENFORCE` |
|
||||
| **Mittel** | Org-Scope in Entitlements vorbereiten | `ai_prompt_runtime` → Unified Executor; Router-Helfer statt KPI-Duplikat |
|
||||
| **Lang** | SSO/Identity (Vision) | Data-Layer-ähnliche Module für Planung; optional Widget-Dashboard |
|
||||
|
||||
---
|
||||
|
||||
## 8. Nächste Schritte
|
||||
|
||||
1. **Review-Workshop:** Tabelle §6 P0–P1 durchgehen und Familien-Regeln verbindlich markieren.
|
||||
2. ~~**`FAMILY_ENTITLEMENT_MODEL.md`** anlegen (P0)~~ → [FAMILY_ENTITLEMENT_MODEL.md](./FAMILY_ENTITLEMENT_MODEL.md) (Entwurf 2026-07-04)
|
||||
3. **Shinkan-Index** und **Mitai-Foundation-README** auf dieses Dokument verlinken.
|
||||
4. **`FAMILY_ENTITLEMENT_MODEL.md`** — Entwurf angelegt (§6 Produkt-Abweichungen, §8 Regeln neue Apps).
|
||||
5. Pro **P1-Punkt** Issue/Remediation-Eintrag in jeweiliger `SCHULDEN_UND_REMEDIATION` / Mitai-Äquivalent.
|
||||
|
||||
---
|
||||
|
||||
## 9. Changelog
|
||||
|
||||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
| 2026-07-04 | Erstfassung Abgleich Mitai Foundation (9) ↔ Shinkan (15) |
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
# Familien-Modell: Entitlements & Limits
|
||||
|
||||
**Status:** Entwurf / verbindliche Zielrichtung (mit bewussten Produkt-Ausnahmen)
|
||||
**Stand:** 2026-07-04
|
||||
**Bezüge:**
|
||||
|
||||
- [DESIGN_PRINCIPLES_ALIGNMENT.md](./DESIGN_PRINCIPLES_ALIGNMENT.md) — Abgleich Mitai ↔ Shinkan
|
||||
- Mitai: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/jinkendo-foundation/design-principles/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)
|
||||
- Shinkan: [CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md](./design-principles/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Zweck
|
||||
|
||||
Dieses Dokument definiert das **gemeinsame Entitlement-Modell** der Jinkendo-Produktfamilie:
|
||||
|
||||
- **Was** für neue Apps und Refactors **Standard** ist
|
||||
- **Welche Produkt-Profile** welche Scopes nutzen (Account, Tenant, beides, keins)
|
||||
- **Wie** bewusste Abweichungen dokumentiert werden — Abweichung ist erlaubt, **Undokumentiertheit** nicht
|
||||
|
||||
**Nicht enthalten:** Stripe/SSO (`auth.jinkendo.de`), vollständige Billing-Implementierung, app-spezifische Feature-IDs.
|
||||
|
||||
---
|
||||
|
||||
## 2. Leitgedanke: Vier getrennte Fragen
|
||||
|
||||
Jede geschützte Aktion durchläuft konzeptionell **vier unabhängige Prüfungen**. Nicht jede App implementiert alle vier — siehe §6.
|
||||
|
||||
| # | Frage | Familien-Begriff | Typische Quelle |
|
||||
|---|--------|------------------|-----------------|
|
||||
| **A** | Wer ist eingeloggt? | **Auth** (Identität) | Session → `profile_id` |
|
||||
| **B** | Darf diese Rolle die **Funktion** ausführen? | **Capability** (Permission) | Rollen-Matrix, `min_account_state` |
|
||||
| **C** | Ist das **Kontingent** erschöpft? | **Feature / Limit** (Quota) | Plan, Tier, Usage-Zähler |
|
||||
| **D** | Darf ich **dieses Objekt** lesen/ändern? | **Governance** (Object ACL) | `visibility`, `club_id`, Owner |
|
||||
|
||||
```
|
||||
Request → Auth (A) → [Capability (B)] → [Feature-Limit (C)] → [Governance (D)] → Handler
|
||||
```
|
||||
|
||||
**Familien-Regel:** B, C und D **nicht** in React-Widgets oder Router-Inline-Logik vermischen — jeweils eine Auflösungsfunktion pro Ebene.
|
||||
|
||||
**Shinkan-Ergänzung:** Governance ist dort ausgebaut (`TenantContext`, Access Layer); Mitai fokussiert A+B+C auf Profil-Ebene.
|
||||
|
||||
---
|
||||
|
||||
## 3. Familien-Standard: Zwei Subjekt-Ebenen
|
||||
|
||||
Limits und Pläne können an **zwei Subjekte** hängen. Beide sind im Familienmodell **first-class** — Apps wählen, welche sie nutzen (§6).
|
||||
|
||||
| Subjekt | ID | Typische Frage | Beispiel |
|
||||
|---------|-----|----------------|----------|
|
||||
| **Account** | `profile_id` | Was darf **ich** als Nutzer (Tarif)? | Mitai Free vs. Premium |
|
||||
| **Tenant** | `tenant_id` (z. B. `club_id`) | Was darf **meine Organisation**? | Shinkan Vereinsplan, KI-Kontingent |
|
||||
|
||||
### 3.1 Auflösungs-Reihenfolge (wenn beide Ebenen aktiv)
|
||||
|
||||
Für eine Aktion mit Capability `X` und Feature `Y`:
|
||||
|
||||
1. **Account-Lifecycle** — z. B. E-Mail verifiziert, Onboarding abgeschlossen
|
||||
2. **Capability(B)** — Rolle darf Funktion (Account- und/oder Tenant-Rollen)
|
||||
3. **Feature(C)** — Kontingent am **primären Billing-Subjekt** der App (siehe Produkt-Profil)
|
||||
4. **Governance(D)** — Objekt sichtbar/bearbeitbar
|
||||
|
||||
**AND-Verknüpfung:** Alle aktiven Ebenen müssen passieren. Ausnahmen nur in §6.3 dokumentiert.
|
||||
|
||||
### 3.2 Primäres Billing-Subjekt pro App
|
||||
|
||||
| Profil | Primäres Subjekt für Limits | Capability-Subjekt |
|
||||
|--------|----------------------------|-------------------|
|
||||
| Personal App (Mitai) | **Account** | Account |
|
||||
| Mandanten-App (Shinkan) | **Tenant** | Account + Tenant-Rolle |
|
||||
| Hybrid (Zukunft) | konfigurierbar | beide |
|
||||
|
||||
---
|
||||
|
||||
## 4. Familien-Standard: Capabilities vs. Features
|
||||
|
||||
| Konzept | Familien-Definition | Subjekt | Beispiel |
|
||||
|---------|---------------------|---------|----------|
|
||||
| **Capability** | Binäre oder rollenbasierte **Erlaubnis** („darf ich?“) | meist Account + Tenant-Kontext | `exercises.ai.suggest` |
|
||||
| **Feature** | **Kontingent** oder Boolean-Limit („wie oft/noch?“) | Account **oder** Tenant | `ai_calls` / Monat |
|
||||
| **Verknüpfung** | Capability kann `linked_feature_id` haben | — | KI-Capability → KI-Kontingent |
|
||||
|
||||
**Familien-Regel:**
|
||||
|
||||
- Capabilities **registry-first** registrieren (Code → DB-Sync, Shinkan-Muster).
|
||||
- Feature-IDs **nicht** in UI hardcoden — nur aus Entitlements-Response.
|
||||
- `NULL` Limit = unbegrenzt; `0` = deaktiviert (Mitai-Semantik, familienweit).
|
||||
|
||||
---
|
||||
|
||||
## 5. Familien-Standard: API & Enforcement
|
||||
|
||||
### 5.1 Ziel-API (neue Apps)
|
||||
|
||||
Ein **einheitlicher Snapshot** für das Frontend:
|
||||
|
||||
```
|
||||
GET /api/me/entitlements
|
||||
?tenant_id=<optional>
|
||||
|
||||
Response (skizziert):
|
||||
{
|
||||
"account": {
|
||||
"profile_id": 1,
|
||||
"account_state": "active_member",
|
||||
"tier_id": "premium", // optional, Account-Apps
|
||||
"features": { "ai_calls": { "allowed", "used", "limit", "remaining", "reset_at" } },
|
||||
"capabilities": { "analysis.run": { "allowed": true, "reason": null } }
|
||||
},
|
||||
"tenant": { // null wenn App keinen Tenant kennt
|
||||
"tenant_id": 42,
|
||||
"tenant_type": "club",
|
||||
"plan_id": "pro",
|
||||
"features": { ... },
|
||||
"capabilities": { ... },
|
||||
"roles": ["trainer"]
|
||||
},
|
||||
"enforcement": {
|
||||
"capabilities": "enforce|probe",
|
||||
"features": "enforce|probe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Familien-Regel:** UI liest **nur** diesen Snapshot (oder domänenspezifische Teilmenge) — keine parallelen `/subscription/me` + `/features/usage` + Ad-hoc-Checks in neuen Apps.
|
||||
|
||||
### 5.2 Ist-API (bestehende Apps — Abweichung dokumentiert)
|
||||
|
||||
| App | Endpoint heute | Familien-Ziel |
|
||||
|-----|----------------|---------------|
|
||||
| **Mitai** | `/subscription/me`, `/features/usage`, `check_feature_access` | Snapshot schrittweise; Account-Block reicht |
|
||||
| **Shinkan** | `GET /api/me/entitlements?club_id=` | Tenant-Block + Capabilities; Account-Tier fehlt bewusst |
|
||||
|
||||
Migration: **kein Big-Bang** — alte Endpoints als Facade auf Snapshot mappen.
|
||||
|
||||
### 5.3 Vier-Phasen-Rollout (familienweit verbindlich)
|
||||
|
||||
| Phase | Verhalten | Env-Beispiel |
|
||||
|-------|-----------|--------------|
|
||||
| **1** | Cleanup Legacy-Flags | — |
|
||||
| **2** | **Probe** — JSON-Log, HTTP 200 | `*_ENFORCE=0` |
|
||||
| **3** | Frontend-Gates aus Entitlements | — |
|
||||
| **4** | **Enforce** — HTTP 403 | `*_ENFORCE=1` |
|
||||
|
||||
**Familien-Regel:** Phase 4 für **neue** kritische Features von Anfang an planbar; Bestands-Apps dürfen in Phase 2–3 bleiben bis kalibriert.
|
||||
|
||||
### 5.4 Enforcement-Priorität
|
||||
|
||||
1. **API** — autoritativ (`403`)
|
||||
2. **Frontend** — UX (Badges, disabled Buttons)
|
||||
3. **Niemals** — nur UI ohne API-Gate
|
||||
|
||||
---
|
||||
|
||||
## 6. Produkt-Profile & bewusste Abweichungen
|
||||
|
||||
Abweichungen vom Familien-Standard sind **zulässig**, wenn sie in der Tabelle **§6.2** stehen und begründet sind.
|
||||
|
||||
### 6.1 Profil-Matrix (Soll)
|
||||
|
||||
| Profil | Apps | Account-Limits | Tenant-Limits | Capabilities | Governance (Objekt) |
|
||||
|--------|------|----------------|---------------|--------------|---------------------|
|
||||
| **P1 Personal** | Mitai | ✅ primär | ❌ | ✅ Account | minimal / privat |
|
||||
| **P2 Mandant** | Shinkan | ⚠️ Lifecycle only | ✅ primär | ✅ Account + Tenant-Rollen | ✅ Access Layer |
|
||||
| **P3 Minimal** | Miken, Ikigai (geplant) | optional | ❌ | optional | minimal |
|
||||
| **P4 Hybrid** | (Reserve) | ✅ | ✅ | ✅ beide | ✅ |
|
||||
|
||||
### 6.2 Registrierter Abweichungs-Katalog
|
||||
|
||||
| ID | App | Abweichung vom Familien-Standard | Begründung | Review |
|
||||
|----|-----|----------------------------------|------------|--------|
|
||||
| **DEV-01** | Mitai | Kein `tenant`-Block in Entitlements | Persönliche Tracking-App; kein Verein | Beibehalten (P1) |
|
||||
| **DEV-02** | Mitai | Kein Capability-Katalog (nur Features+Tier) | RBAC = admin/user ausreichend | Optional später `account.*` Capabilities |
|
||||
| **DEV-03** | Mitai | Mehrere Nutzer-APIs statt einem Snapshot | Historisch gewachsen | Facade → Snapshot (mittelfristig) |
|
||||
| **DEV-04** | Mitai | Legacy Session-Spalten (`ai_enabled`, …) parallel Features | Migrationsschuld | Bereinigen, nicht in neue Apps |
|
||||
| **DEV-05** | Shinkan | Kein Account-Tier / `profiles.tier` | Verein zahlt, nicht Trainer | Beibehalten (P2) |
|
||||
| **DEV-06** | Shinkan | Capabilities **und** Governance (zwei Achsen) | Trainer vs. Objekt-Rechte | Familien-Vorbild für P2/P4 |
|
||||
| **DEV-07** | Shinkan | `check_feature_access` (Mitai-Legacy) explizit verboten | Falsches Subjekt | Beibehalten |
|
||||
| **DEV-08** | Shinkan | Enforcement oft Probe-only | Rollout-Sicherheit | → Phase 4 bis Datum X |
|
||||
| **DEV-09** | Shinkan | Inventar-Features live gezählt | Drift-Vermeidung | Abweichung OK; in P2 dokumentieren |
|
||||
| **DEV-10** | Beide | Kein atomares Check+Increment | Race bei Parallel-Requests | Familien-Backlog; Workaround dokumentieren |
|
||||
| **DEV-11** | Neue Apps | dürfen **nur** Account **oder** nur Tenant wählen | MVP | Eintrag hier anlegen vor Launch |
|
||||
|
||||
**Neue Abweichung:** Zeile in §6.2 + ggf. ein Satz im App-`CLAUDE.md`.
|
||||
|
||||
### 6.3 Dokumentierte Ausnahmen (Bypass)
|
||||
|
||||
| Ausnahme | Apps | Regel |
|
||||
|----------|------|-------|
|
||||
| Plattform-Admin Audit | Shinkan | Quota-Bypass über Grants, nicht pauschal superadmin |
|
||||
| Admin ohne Auto-Bypass | Mitai | Admins unterliegen Limits (Produktentscheid) |
|
||||
| Öffentliche Routen ohne Auth | alle | Kein Entitlement-Check |
|
||||
|
||||
---
|
||||
|
||||
## 7. Mapping: Familien-Begriff ↔ Implementierung
|
||||
|
||||
### 7.1 Mitai (Profil P1)
|
||||
|
||||
| Familien | Mitai-Implementierung |
|
||||
|----------|----------------------|
|
||||
| Account-Features | `features`, `tier_limits`, `user_feature_usage` |
|
||||
| Account-Tier | `get_effective_tier()`, `access_grants` |
|
||||
| Check | `check_feature_access(profile_id, feature_id)` |
|
||||
| Increment | `increment_feature_usage(profile_id, …)` |
|
||||
| UI | `UsageBadge`, Widget `allowed` |
|
||||
|
||||
### 7.2 Shinkan (Profil P2)
|
||||
|
||||
| Familien | Shinkan-Implementierung |
|
||||
|----------|-------------------------|
|
||||
| Tenant-Features | `club_plan_limits`, `club_feature_usage`, `club_features.py` |
|
||||
| Tenant-Plan | `get_effective_club_plan(club_id)` |
|
||||
| Capabilities | `check_capability`, `capabilities` + `rights_registry` |
|
||||
| Snapshot | `build_me_entitlements()` → `GET /me/entitlements` |
|
||||
| Governance | `TenantContext`, `club_tenancy` — **kein** Ersatz für Capabilities |
|
||||
|
||||
### 7.3 Gemeinsame Muster (copy-ready)
|
||||
|
||||
| Muster | Mitai | Shinkan |
|
||||
|--------|-------|---------|
|
||||
| Feature-Registry in DB | ✅ | ✅ (`app='shinkan'`) |
|
||||
| Plan × Feature Matrix | `tier_limits` | `club_plan_limits` |
|
||||
| Admin-Override | `user_feature_restrictions` | `club_feature_overrides` |
|
||||
| Promo/Trial | `access_grants` | `club_access_grants` |
|
||||
| 4-Phasen-Rollout | ✅ | ✅ |
|
||||
| Registry-first neue IDs | ⚠️ teils hardcoded | ✅ `rights_registrations/` |
|
||||
|
||||
---
|
||||
|
||||
## 8. Entscheidungsregeln für neue Produkte
|
||||
|
||||
### 8.1 Pflicht (alle Apps mit Auth)
|
||||
|
||||
- [ ] Session → `profile_id`; kein Client-Header als Autorität
|
||||
- [ ] Entitlements-Auflösung **eine Funktion pro Ebene** (Capability, Feature)
|
||||
- [ ] API-Enforcement vor UI-Gate
|
||||
- [ ] 4-Phasen-Rollout dokumentiert
|
||||
- [ ] Produkt-Profil (P1–P4) gewählt + Abweichungen in §6.2
|
||||
|
||||
### 8.2 Wenn Personal App (P1)
|
||||
|
||||
- [ ] Primäres Subjekt = Account
|
||||
- [ ] `tenant`-Block in API = `null` (DEV-01-Analog)
|
||||
- [ ] Feature-Registry + Tier-Matrix
|
||||
|
||||
### 8.3 Wenn Mandanten-App (P2)
|
||||
|
||||
- [ ] Primäres Subjekt = Tenant
|
||||
- [ ] `TenantContext` + Governance getrennt von Capabilities
|
||||
- [ ] Capabilities registry-first
|
||||
- [ ] Account nur Lifecycle (verified, member) — kein Tier nötig (DEV-05-Analog erlaubt)
|
||||
|
||||
### 8.4 Wenn Minimal App (P3)
|
||||
|
||||
- [ ] Explizit: „keine Limits“ oder nur Boolean-Features — in §6.2 eintragen
|
||||
- [ ] Kein halbes Mitai-v9c kopieren
|
||||
|
||||
---
|
||||
|
||||
## 9. Konvergenz-Roadmap (optional, nicht blockierend)
|
||||
|
||||
| Schritt | Mitai | Shinkan | Familie |
|
||||
|---------|-------|---------|---------|
|
||||
| **Kurz** | Legacy Session-Flags entfernen | `CAPABILITY_ENFORCE` / `CLUB_FEATURE_ENFORCE` Prod | DEV-04, DEV-08 schließen |
|
||||
| **Mittel** | `/me/entitlements` Account-Block | Snapshot um `tenant`-Typ metadata erweitern | Facade alte APIs |
|
||||
| **Lang** | Optional `tenant_id` reservieren (null) | Optional Account-Tier für Cross-Sell | Shared package `jinkendo_entitlements` |
|
||||
| **Vision** | SSO + zentraler Billing | Vereins-Abo Stripe | `CENTRAL_SUBSCRIPTION_SYSTEM` |
|
||||
|
||||
**Wichtig:** Konvergenz ist **empfohlen**, nicht Pflicht — solange §6.2 aktuell bleibt.
|
||||
|
||||
---
|
||||
|
||||
## 10. Anti-Patterns (familienweit verboten)
|
||||
|
||||
1. Tier- oder Plan-Namen in React-Komponenten hardcoden
|
||||
2. Limit-Logik nur im Frontend
|
||||
3. Shinkan-Vereinslimits über Mitai `check_feature_access(profile_id)`
|
||||
4. Capability-Check durch Governance ersetzen (oder umgekehrt)
|
||||
5. Neue Feature-IDs nur in SQL-Migration ohne Registry
|
||||
6. Enforcement Phase 4 „vergessen“ bei paid Features ohne dokumentierte Probe-Phase
|
||||
7. Undokumentierte Produkt-Abweichung (nicht in §6.2)
|
||||
|
||||
---
|
||||
|
||||
## 11. Offene Familien-Entscheidungen (Backlog)
|
||||
|
||||
| ID | Frage | Optionen | Default wenn unentschieden |
|
||||
|----|-------|----------|----------------------------|
|
||||
| **FD-01** | Atomares check+increment | DB-Lock / Transaction / Queue | Status quo + Retry-Hinweis in Doku |
|
||||
| **FD-02** | Shared Python-Modul | Monorepo-Paket vs. Copy+Sync | Copy+Sync mit gleicher API-Shape |
|
||||
| **FD-03** | Capability-Namespace global | `jinkendo.*` vs. app-prefix | `{app}.{domain}.{action}` |
|
||||
| **FD-04** | Mitai bekommt Capability-Layer? | ja/nein/später | nein (DEV-02) bis Bedarf |
|
||||
| **FD-05** | Ein `features.app` für alle Apps | gemeinsame DB vs. pro Deploy | pro Deploy (heute) |
|
||||
|
||||
---
|
||||
|
||||
## 12. Verwandte Dokumente
|
||||
|
||||
| Dokument | Inhalt |
|
||||
|----------|--------|
|
||||
| [DESIGN_PRINCIPLES_ALIGNMENT.md](./DESIGN_PRINCIPLES_ALIGNMENT.md) | Vollständiger Mitai ↔ Shinkan Abgleich |
|
||||
| [design-principles/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md](./design-principles/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md) | Shinkan Ist-Prinzipien |
|
||||
| [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./design-principles/ACCESS_LAYER_DESIGN_PRINCIPLES.md) | Governance (Ebene D) |
|
||||
| Mitai Foundation #3 | Feature & Entitlement Ist Mitai |
|
||||
| Shinkan `CLUB_MEMBERSHIP_AND_FEATURES.v1.md` | Vereins-Abo Detail |
|
||||
| Shinkan `CAPABILITY_CATALOG.v1.md` | Capability-IDs |
|
||||
|
||||
---
|
||||
|
||||
## 13. Changelog
|
||||
|
||||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
| 2026-07-04 | Entwurf: Familien-Standard, Produkt-Profile P1–P4, Abweichungs-Katalog DEV-01–11 |
|
||||
|
|
@ -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, `<img>`, 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 | ✅ |
|
||||
|
|
@ -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 | ✅ |
|
||||
|
|
@ -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` |
|
||||
|
|
@ -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` |
|
||||
|
|
@ -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/<branch>` — 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 |
|
||||
|
|
@ -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 `<Outlet />`; 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 | ✅ |
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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 | ✅ |
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
# Access Layer & Tenant – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Access Layer & Tenant Governance“ — keine Shinkan-Gesamtarchitektur, keine Kampfsport-Domänenlogik
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 1 von 15
|
||||
**Mitai-Vergleich:** kein direktes Gegenstück (Mitai ist profil-zentriert, kein Vereins-Mandant)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| TenantContext | `backend/tenant_context.py` |
|
||||
| Governance-Helfer | `backend/club_tenancy.py` |
|
||||
| Listenfilter SQL | `library_content_visibility_sql()` in `tenant_context.py` |
|
||||
| Endpoint-Audit | `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` |
|
||||
| Cursor-Regel | `.cursor/rules/access-layer.mdc` |
|
||||
| Heuristik-Check | `backend/scripts/check_access_layer_hints.py` |
|
||||
| Normative Spec | `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Access Layer & Tenant Governance**
|
||||
|
||||
Zentraler Querschnitt für Mandanten-Kontext (`club_id`), Sichtbarkeit (`private`/`club`/`official`) und einheitliche Les-/Schreibregeln für Bibliotheksartefakte (Übungen, Medien, Rahmenprogramme, Vorlagen, Progressionsgraphen).
|
||||
|
||||
---
|
||||
|
||||
## Fachliche Verantwortung
|
||||
|
||||
1. **TenantContext pro HTTP-Request** — Auflösung aus Session + Header `X-Active-Club-Id` + Profilfeld `active_club_id`.
|
||||
2. **Datenisolierung** — `club_id` als Grenze für vereinsgeteilte Inhalte; Cross-Verein ausgeschlossen.
|
||||
3. **Einheitliche Sichtbarkeits-Semantik** — gleiche Enums und Prüflogik über alle Bibliotheksmodule.
|
||||
4. **Governance-Transitionen** — Regeln beim Wechsel `private` → `club` → `official` und beim Löschen.
|
||||
5. **Listenfilter** — SQL-Baustein statt „SELECT *“ in jedem Router.
|
||||
|
||||
### Administrierte Konfigurationen
|
||||
|
||||
| Konfiguration | Speicherort | Inhalt |
|
||||
|---------------|-------------|--------|
|
||||
| Aktiver Verein | `profiles.active_club_id` | Persistierter UI-Kontext |
|
||||
| Request-Override | Header `X-Active-Club-Id` | Client-seitiger Mandantenwechsel |
|
||||
| Sichtbarkeit | Spalte `visibility` je Objekt | `private`, `club`, `official` |
|
||||
| Vereinszuordnung | Spalte `club_id` | Pflicht bei `club`-Inhalten |
|
||||
|
||||
### Bewusst nicht hardcodiert
|
||||
|
||||
- Welche Objekte welchen Verein haben (Daten)
|
||||
- Individuelle Freigabeentscheidungen (Workflow)
|
||||
|
||||
### Hardcodiert (Code)
|
||||
|
||||
- Enum-Werte und Leseregeln in `club_tenancy.py` / `library_content_visibility_sql`
|
||||
- Plattform-Admin-Ausnahmen (`is_platform_admin`, `is_superadmin`)
|
||||
- Rollencodes für Schreib-/Löschregeln (`club_admin`, `trainer`, …)
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Ein Mandant pro Request (`TenantContext`)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `Depends(get_tenant_context)` liefert `profile_id`, `global_role`, `effective_club_id`, Mitgliedschaften — einmal pro Request. |
|
||||
| **Begründung** | Kein verteiltes „Rates“ aus Headers; konsistente Filter in allen Routern. |
|
||||
| **Quelle** | `tenant_context.py`; `ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` §2 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Nicht alle Endpoints migriert; Audit-Tabelle zeigt Restbestand mit nur `require_auth`. |
|
||||
|
||||
### 2. `club_id` als Datenisolierungsgrenze
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Vereinsgeteilte Inhalte sind nur für aktive Mitglieder des Objekt-`club_id` lesbar — nie Cross-Verein. |
|
||||
| **Begründung** | Mandantenfähigkeit für Vereinsplattform; Compliance bei geteilten Trainingsinhalten. |
|
||||
| **Quelle** | `library_content_visibility_sql()`; Tests `test_access_layer*.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | `division`-Verschärfung noch nicht durchgängig; reserviert im Plan. |
|
||||
|
||||
### 3. Einheitliche Visibility-Semantik
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `private` \| `club` \| `official` mit gleicher Bedeutung für Übungen, Medien, Rahmen, Module, Graphen. |
|
||||
| **Begründung** | Nutzer und Trainer verstehen ein Freigabemodell; UI-Feld „Freigabelevel“ durchgängig. |
|
||||
| **Quelle** | `club_tenancy.py`; `FACHLICHE_NUTZERFUNKTIONEN.md` §4.7 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | `community`-Stufe nur dokumentiert, nicht implementiert. |
|
||||
|
||||
### 4. Zentraler SQL-Filter für Bibliothekslisten
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Listen nutzen `library_content_visibility_sql(alias, profile_id, role, effective_club_id)` — nicht handgeschriebene WHERE-Kopien. |
|
||||
| **Begründung** | Drift-Vermeidung; ein Fix gilt für alle Kataloge. |
|
||||
| **Quelle** | `tenant_context.py`; Router `exercises.py`, `training_framework_programs.py`, … |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Einzelne Legacy-Queries können noch abweichen. |
|
||||
|
||||
### 5. Governance-Transitionen explizit prüfen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Wechsel von `visibility`/`club_id` über `assert_library_content_governance_transition` + `assert_valid_governance_visibility`. |
|
||||
| **Begründung** | „Privat → Verein teilen“ und „official herabstufen“ sind sicherheitsrelevante Aktionen. |
|
||||
| **Quelle** | `club_tenancy.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Nicht jedes Modul ruft Transition-Helper bei PATCH auf. |
|
||||
|
||||
### 6. Löschregeln nach Visibility-Stufe
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `assert_library_content_deletable`: privat → Ersteller/Vereinsadmin-Kontext; club → Vereinsadmin; official → Plattform-Admin. |
|
||||
| **Begründung** | Schutz vor versehentlichem Löschen fremder oder offizieller Inhalte. |
|
||||
| **Quelle** | `club_tenancy.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Medien-Lifecycle hat zusätzliche Stufen (Papierkorb) — Schnittmenge beachten. |
|
||||
|
||||
### 7. Aktiver Verein: Header + Profil synchron
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Frontend sendet `X-Active-Club-Id`; Backend validiert gegen Mitgliedschaft; Profil speichert `active_club_id`. |
|
||||
| **Begründung** | Einheitlicher Mandanten-Kontext über API und UI. |
|
||||
| **Quelle** | `frontend/src/api/client.js` (`mergeActiveClubHeader`); `profiles` Router |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Onboarding-Nutzer ohne Verein: eingeschränkter Nav-Modus. |
|
||||
|
||||
### 8. Plattform-Admin als Audit-Pfad, nicht als Bypass
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Plattform-Admins sehen fremde `club`-Inhalte nur mit expliziter Regel (Mitgliedschaft oder Audit-Ausnahme in SQL). |
|
||||
| **Begründung** | Superuser-Zugriff ohne Mandanten-Leak in normalen Trainer-Flows. |
|
||||
| **Quelle** | `library_content_visibility_sql` — `club_ok_plat`-Zweig |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Feinheiten zwischen `admin` und `superadmin` (z. B. `official`, Legal Hold) separat geregelt. |
|
||||
|
||||
### 9. Endpoint-Audit als lebendes Inventar
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Jeder sicherheitsrelevante Endpoint-Eintrag in `ACCESS_LAYER_ENDPOINT_AUDIT.md`; PR-Checkliste verlangt Update. |
|
||||
| **Begründung** | Sichtbarkeit des Migrationsstands; kein stilles Ausweichen auf `require_auth` allein. |
|
||||
| **Quelle** | `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md`; `check_access_layer_hints.py` |
|
||||
| **Tragfähigkeit** | **hoch** (prozessual) |
|
||||
| **Einschränkung** | CI-Strict-Modus optional, nicht überall aktiv. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Endpoints nur mit `require_auth`** bei tenant-sensitiven Daten — führt zu IDOR und fehlenden Listenfiltern.
|
||||
2. **Visibility-Logik pro Router duplizieren** — historische Drift zwischen Übungen und Planung.
|
||||
3. **`division` vor stabiler Vereins-Isolation** — Reihenfolge im Plan: erst Stufe C, dann D.
|
||||
4. **Community-Freigabe ohne additive Felder** — würde `club`-Isolation brechen.
|
||||
5. **Client-seitige Mandantenfilter ohne Server-Enforcement** — UI-Hiding reicht nicht.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [ACCESS_LAYER_AND_GOVERNANCE_PLAN.md](../../../.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md)
|
||||
- [MULTI_TENANCY_RBAC_ARCHITECTURE.md](../../../.claude/docs/technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
# AI Prompt Runtime – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „AI Prompt Runtime“ — Shinkan-KI-Schicht, keine Planungs-Gesamtarchitektur
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 5 von 15
|
||||
**Mitai-Vergleich:** [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/PROMPT_ENGINE_DESIGN_PRINCIPLES.md) (#1)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Laufzeit | `backend/ai_prompt_runtime.py`, `backend/prompt_resolver.py` |
|
||||
| Domänen-Orchestrierung | `backend/exercise_ai.py`, `backend/planning_exercise_*.py` |
|
||||
| OpenRouter | `backend/openrouter_chat.py` |
|
||||
| Admin | `backend/routers/ai_prompts_admin.py` |
|
||||
| Zielbild | `.claude/docs/technical/AI_PROMPT_TARGET_ARCHITECTURE.md` |
|
||||
| Job-Kontext | `backend/ai_prompt_job.py`, `backend/ai_prompt_context.py` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**AI Prompt Runtime**
|
||||
|
||||
Schmale Ausführungsschicht für admin-konfigurierbare Prompts in `ai_prompts`: Laden, Mustache-Rendering, Kontext-Arten, OpenRouter-Aufruf. **Kein** vollständiges Unified Prompt System wie Mitai (keine Workflows/Pipelines in Produktion).
|
||||
|
||||
---
|
||||
|
||||
## Fachliche Verantwortung
|
||||
|
||||
1. **Prompt-Laden aus DB** — `load_ai_prompt_row`, `load_and_render_ai_prompt`
|
||||
2. **Platzhalter-Ersetzung** — Mustache `{{key}}` via `prompt_resolver.py`
|
||||
3. **Kontext-Arten** — `AiPromptContextKind` trennt Übungs-KI vs. Planungs-KI
|
||||
4. **Domänen-Builder** — `exercise_ai`, Planungs-Pipelines bauen Variablen-Maps
|
||||
5. **Admin CRUD + Preview** — ohne LLM in Preview-Pfaden wo vorgesehen
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Eine Laufzeit-Fassade für DB-Prompts
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Produktive Aufrufe laden Slugs über `ai_prompt_runtime` — nicht Roh-SQL auf `ai_prompts` in Routern. |
|
||||
| **Begründung** | Einheitliches inactive-Handling, Modell-Feld, Render-Metadaten. |
|
||||
| **Quelle** | `AI_PROMPT_TARGET_ARCHITECTURE.md` §2.1; `ai_prompt_runtime.py` |
|
||||
| **Tragfähigkeit** | **hoch** (Zielrichtung) |
|
||||
| **Einschränkung** | Planungs-KI hat noch verteilte Orchestratoren; kein einzelner `execute_prompt` wie Mitai. |
|
||||
|
||||
### 2. Konfigurierbare Bibliothek in `ai_prompts`
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Template-Texte in DB; Admins ändern ohne Deploy (`ai_prompts_admin`). |
|
||||
| **Begründung** | Gleiches Familien-Muster wie Mitai Prompt-Bibliothek. |
|
||||
| **Quelle** | Migration 069+; Admin-UI |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Keine Pipeline/Workflow-Typen; Slugs hardcoded in `context_kind_for_slug`. |
|
||||
|
||||
### 3. Kontext-Namespaces statt globaler Platzhalter-Soup
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `AiPromptContextKind` (z. B. `exercise_form_ai`, `planning_exercise_search`) begrenzt erlaubte Builder. |
|
||||
| **Begründung** | Planungs-Kontext wächst ohne Kollision mit Übungs-Keys. |
|
||||
| **Quelle** | `AI_PROMPT_TARGET_ARCHITECTURE.md` §2.3 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Noch keine zentrale Platzhalter-Registry wie Mitai — Mustache ad hoc pro Builder. |
|
||||
|
||||
### 4. Trennung Template vs. Domänen-Kontext
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | UI/Router liefern Pydantic-DTOs → Builder erzeugen `variables`-Map → `render_mustache_template`. |
|
||||
| **Begründung** | Prompt-Autoren ändern Text, nicht Python in Routern. |
|
||||
| **Quelle** | `prompt_resolver.py`; `ExerciseFormAiPromptContext` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Große Planungs-Kontexte noch nicht vollständig über DTOs. |
|
||||
|
||||
### 5. Transport (OpenRouter) getrennt von Semantik
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `openrouter_chat.py` für HTTP; Validierung/JSON-Parsing in Domänen-Schicht. |
|
||||
| **Begründung** | Modellwechsel ohne Router-Anpassung. |
|
||||
| **Quelle** | `AI_PROMPT_TARGET_ARCHITECTURE.md` §2.2 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Modell teils global Env, teils Spalte `openrouter_model` — Konvergenz offen. |
|
||||
|
||||
### 6. Reset-to-default für System-Prompts
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `default_template` + Admin-Reset — kein vollständiges Versionsmodell. |
|
||||
| **Begründung** | Familien-Muster aus Mitai; Schutz vor Fehlkonfiguration. |
|
||||
| **Quelle** | Migration 069 |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Keine Historie benutzerdefinierter Änderungen. |
|
||||
|
||||
### 7. Admin-only Schreiben, authentifiziertes Ausführen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Prompt-CRUD nur Admin; Ausführung mit Capability + Feature-Kontingent. |
|
||||
| **Begründung** | Systemkonfiguration vs. Nutzung; Kostenkontrolle. |
|
||||
| **Quelle** | `ai_prompts_admin.py`; `exercises.ai.suggest` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Enforcement teils noch Probe-Phase. |
|
||||
|
||||
### 8. Skill-Retrieval orthogonal zu Prompts
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `ai_skill_retrieval_profiles` steuert Katalog für `{{skills_catalog}}` — unabhängig vom Prompt-Text. |
|
||||
| **Begründung** | Anweisung vs. Kontextfenster trennbar konfigurierbar. |
|
||||
| **Quelle** | `AI_PROMPT_TARGET_ARCHITECTURE.md` §3.3 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen (Shinkan-Ist / Mitai-Vermeidung)
|
||||
|
||||
1. **Direkte OpenRouter-Calls in Routern** ohne Laufzeit-Schicht — historische Schuld, abbauen.
|
||||
2. **Hardcodierte Prompt-Strings in Produktion** — nur Fallback/Dev.
|
||||
3. **Mitai-Workflow-Graph vorreifen** — Shinkan braucht erst Planungs-Kontext-Reife.
|
||||
4. **Globale Platzhalter-Map ohne Namespace** — Mitai-Lektion `PLACEHOLDER_MAP`-Duplikat.
|
||||
5. **Fehlende JSON-Schema-Validierung** bei `output_format=json` — Mitai-TODO übernehmen vermeiden.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [AI_PROMPT_TARGET_ARCHITECTURE.md](../../../.claude/docs/technical/AI_PROMPT_TARGET_ARCHITECTURE.md)
|
||||
- [AI_PROMPT_SYSTEM_SPEC.md](../../../.claude/docs/technical/AI_PROMPT_SYSTEM_SPEC.md)
|
||||
- [PLANNING_PROGRESSION_GRAPH_KI.md](../../architecture/PLANNING_PROGRESSION_GRAPH_KI.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
# Auth & Session – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Auth & Session“ — gemeinsame Mitai-Basis in Shinkan
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 4 von 15
|
||||
**Mitai-Vergleich:** [AUTH_SESSION_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/AUTH_SESSION_DESIGN_PRINCIPLES.md) (#5)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Auth-Kern | `backend/auth.py` |
|
||||
| Router | `backend/routers/auth.py`, `profiles.py` |
|
||||
| Frontend | `frontend/src/context/AuthContext.jsx`, `frontend/src/api/client.js` |
|
||||
| Account-Lifecycle | `backend/account_lifecycle.py` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Auth & Session**
|
||||
|
||||
Token-basierte Server-Sessions (`sessions`-Tabelle), bcrypt-Passwörter, FastAPI-Dependencies `require_auth` / `require_admin`. Geteilter Code mit Mitai (App-Familie).
|
||||
|
||||
---
|
||||
|
||||
## Fachliche Verantwortung
|
||||
|
||||
1. **Login/Logout/Session-Lebensdauer**
|
||||
2. **Passwort-Hashing** (bcrypt, Legacy-SHA256-Upgrade)
|
||||
3. **Auth-Dependencies** für Router
|
||||
4. **Account-States** (Verifizierung, Onboarding-Gates)
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Server-Side Sessions mit Token
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `X-Auth-Token` Header → Lookup in `sessions` mit Ablaufzeit. |
|
||||
| **Begründung** | Widerrufbar; kein JWT-Drift zwischen Apps. |
|
||||
| **Quelle** | `auth.py` `get_session`, `require_auth` |
|
||||
| **Tragfähigkeit** | **hoch** (Familien-Standard) |
|
||||
| **Einschränkung** | Kein Refresh-Token-Rotation-Modell. |
|
||||
|
||||
### 2. `require_auth` als separater Depends-Parameter
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `session: dict = Depends(require_auth)` — nie in Header-Default eingebettet. |
|
||||
| **Begründung** | Bekannter FastAPI-Footgun führt zu ungeschützten Endpoints. |
|
||||
| **Quelle** | `CLAUDE.md` Kritische Regeln |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Code-Review/Lint erzwingt das nicht automatisch. |
|
||||
|
||||
### 3. Profile-ID immer aus Session
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `profile_id` aus `session['profile_id']`, nie aus Client-Header als Autorität. |
|
||||
| **Begründung** | IDOR-Vermeidung. |
|
||||
| **Quelle** | Architektur-Regeln; Shinkan ergänzt `TenantContext.profile_id` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Mitai-Dokument nennt Profile-Header-Schwäche — in Shinkan prüfen ob analog. |
|
||||
|
||||
### 4. bcrypt für alle Passwort-Operationen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `hash_pin` / `verify_pin` mit bcrypt; SHA256 nur Legacy-Verify + Upgrade. |
|
||||
| **Begründung** | Familien-konsistente Kryptografie. |
|
||||
| **Quelle** | `auth.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 5. Portal-Rollen vs. Vereinsrollen trennen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `profiles.role` (admin/superadmin/user) ≠ `club_member_roles` im Verein. |
|
||||
| **Begründung** | Shinkan-Mandantenmodell; Plattform-Admin ≠ Vereins-Trainer. |
|
||||
| **Quelle** | `club_tenancy.py`; `TenantContext.global_role` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | UI muss beide Ebenen korrekt anzeigen. |
|
||||
|
||||
### 6. Account-Lifecycle als Capability-Voraussetzung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `min_account_state` auf Capabilities (z. B. verifiziertes Mitglied). |
|
||||
| **Begründung** | Gates vor sensiblen Aktionen ohne Sonderchecks in Routern. |
|
||||
| **Quelle** | `account_lifecycle.py`; `capabilities.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Nicht alle Flows nutzen Lifecycle einheitlich. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Auth-Parameter in Header-Defaults vermischen** — dokumentierter Anti-Pattern.
|
||||
2. **Client-gesteuerte `profile_id`** für Autorisierung.
|
||||
3. **Shinkan-spezifische Mandantenlogik in `auth.py`** — gehört in `tenant_context.py`.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./ACCESS_LAYER_DESIGN_PRINCIPLES.md)
|
||||
- Mitai: [AUTH_SESSION_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/AUTH_SESSION_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
# Capability & Club Features – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Capabilities & Vereins-Feature-Kontingente“ — nicht Billing/Stripe
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 2 von 15
|
||||
**Mitai-Vergleich:** [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) (#3)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Capabilities | `backend/capabilities.py` |
|
||||
| Vereins-Features | `backend/club_features.py` |
|
||||
| Entitlements-API | `backend/entitlements.py`, `backend/routers/me_entitlements.py` |
|
||||
| Quota-Bypass | `backend/club_quota_bypass.py` |
|
||||
| Spez | `CAPABILITY_CATALOG.v1.md`, `CLUB_MEMBERSHIP_AND_FEATURES.v1.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Capability & Club Feature Entitlements**
|
||||
|
||||
Zwei Schichten: **Capabilities** (darf Nutzer X im Verein Y?) und **Club Features** (Kontingente/Limits pro Verein, Subjekt `club_id`). Zusammenführung in `GET /api/me/entitlements`.
|
||||
|
||||
---
|
||||
|
||||
## Fachliche Verantwortung
|
||||
|
||||
1. **Capability-Checks** — `check_capability`, `probe_capability`, `require_capability` mit Env `CAPABILITY_ENFORCE`.
|
||||
2. **Vereins-Kontingente** — `probe_club_feature_access`, `consume_club_feature_with_usage` mit Env `CLUB_FEATURE_ENFORCE`.
|
||||
3. **Entitlements-Snapshot** — Frontend erhält `capabilities` + `features` + Plan für UI-Gating ohne Tier-Logik in Widgets.
|
||||
4. **Account-Lifecycle** — `min_account_state` blockiert Capabilities vor Verifizierung.
|
||||
|
||||
### Unterschied zu Mitai
|
||||
|
||||
| Aspekt | Mitai | Shinkan |
|
||||
|--------|-------|---------|
|
||||
| Limit-Subjekt | Profil / Subscription | **Verein** (`club_id`) |
|
||||
| Rollen | Tier + Features | Vereinsrollen + Portal-Rolle |
|
||||
| Legacy | `check_feature_access` (001) | Explizit **nicht** für Shinkan-Limits nutzen |
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Eine Entitlements-API für das Frontend
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `GET /api/me/entitlements?club_id=` liefert Capabilities-Map + Feature-Kontingente + Plan. |
|
||||
| **Begründung** | Keine Tier-Logik in React-Komponenten; ein Roundtrip pro Mandantenwechsel. |
|
||||
| **Quelle** | `entitlements.py`, `me_entitlements.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Nicht alle UI-Stellen nutzen Entitlements konsequent. |
|
||||
|
||||
### 2. 4-Phasen-Rollout (Probe → Enforce)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Phase 2: JSON-Log ohne Block; Phase 3+: `CAPABILITY_ENFORCE=1` / `CLUB_FEATURE_ENFORCE=1` → HTTP 403. |
|
||||
| **Begründung** | Sicheres Einführen ohne Produktions-Crash; Audit vor Hard-Block. |
|
||||
| **Quelle** | `capabilities.py`, `club_features.py`; Mitai-Vorbild in Spec |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Env-Flags müssen pro Umgebung bewusst gesetzt werden. |
|
||||
|
||||
### 3. Capabilities verknüpft mit Features
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Capability kann `linked_feature_id` haben — Kontingent-Check vor Ausführung. |
|
||||
| **Begründung** | Recht und Limit bleiben getrennt modelliert aber gemeinsam enforcebar. |
|
||||
| **Quelle** | `capabilities`-Tabelle; `check_capability` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Nicht jede Capability hat linked Feature. |
|
||||
|
||||
### 4. Enforcement an der API, nicht in der UI
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Router rufen `require_capability` / `probe_club_feature_access` — UI blendet nur vor. |
|
||||
| **Begründung** | API ist Source of Truth; UI-Gating allein ist umgehbar. |
|
||||
| **Quelle** | `exercise_ai.py`, Planungs-KI-Router |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Teilweise noch Probe-only in Prod. |
|
||||
|
||||
### 5. Bestands-Features als Live-Zählung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Inventar-Features (`exercises`, `training_groups`, …) zählen live in DB, nicht nur `club_feature_usage`. |
|
||||
| **Begründung** | Keine Drift zwischen tatsächlichem Bestand und Usage-Tabelle. |
|
||||
| **Quelle** | `_INVENTORY_FEATURES` in `club_features.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Performance bei großen Vereinen — ggf. Cache nötig. |
|
||||
|
||||
### 6. Quota-Bypass für Plattform-Rollen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Konfigurierbare Bypass-Capabilities (`domain=quota_bypass`) für Support/Admin ohne harte Limits. |
|
||||
| **Begründung** | Betrieb und Demos ohne Plan-Upgrade. |
|
||||
| **Quelle** | `club_quota_bypass.py`; `entitlements.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Missbrauchsrisiko — nur dokumentierte Grants. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Mitai `auth.check_feature_access` für Shinkan-Vereinslimits** — profil-zentriert, falscher Subjekt-Scope.
|
||||
2. **Tier-Logik in Frontend-Widgets** — gehört in Entitlements-Response.
|
||||
3. **Enforcement ohne Probe-Phase** — bricht bestehende Vereine ohne Vorwarnung.
|
||||
4. **Capabilities ohne DB-Sync aus Registry** — siehe Rights-Registry-Dokument.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [CAPABILITY_CATALOG.v1.md](../../../.claude/docs/technical/CAPABILITY_CATALOG.v1.md)
|
||||
- [CLUB_MEMBERSHIP_AND_FEATURES.v1.md](../../../.claude/docs/technical/CLUB_MEMBERSHIP_AND_FEATURES.v1.md)
|
||||
- [RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md](./RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
# Content Reports (P-13) – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Content Reports“ — Meldeverfahren, Posteingang, Legal Hold
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 10 von 15
|
||||
**Mitai-Vergleich:** — (Compliance-spezifisch)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| API | `backend/routers/content_reports.py` |
|
||||
| Legal Hold | `backend/media_legal_hold.py` |
|
||||
| Inbox-Integration | `GET /api/me/inbox/content-reports` |
|
||||
| Frontend | `InboxPage.jsx` |
|
||||
| Migration | 052, 053 |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Content Reports & Compliance (P-13)**
|
||||
|
||||
Meldeverfahren für problematische Inhalte (Medien, Übungen), Admin-Posteingang mit Statusworkflow, Priorisierung sensibler Gründe, Anbindung Legal Hold und Medien-Audit.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Eine Tabelle, ein Workflow — keine separate Admin-Queue
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `content_reports` + bestehende Inbox-UI für Änderungsanfragen und Meldungen. |
|
||||
| **Begründung** | Admin-Arbeit an einem Ort; weniger Navigations-Fragmentierung. |
|
||||
| **Quelle** | `content_reports.py` Docstring |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Zwei Vorgangstypen in einer UI — klare Typ-Kennzeichnung nötig. |
|
||||
|
||||
### 2. Melden optional ohne Auth (eingeschränkt)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Anonym/offline Meldung für `official`-Medien erlaubt; sonst Auth empfohlen. |
|
||||
| **Begründung** | DSA-Anforderungen; öffentliche Plattform-Inhalte meldbar. |
|
||||
| **Quelle** | Router-Berechtigungen |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Missbrauchsschutz (Rate Limit) prüfen. |
|
||||
|
||||
### 3. Priorität bei sensiblen Gründen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `minors`, `illegal_content`, `youth_protection` → HIGH_PRIORITY automatisch. |
|
||||
| **Begründung** | SLA und Admin-Aufmerksamkeit fachlich korrekt. |
|
||||
| **Quelle** | `HIGH_PRIORITY_REASONS` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 4. Rollengetrennte Sicht (Plattform vs. Verein)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Plattform-Admin: alle; Club-Admin: nur Vereinsmedien-Meldungen. |
|
||||
| **Begründung** | Mandanten-Grenze auch im Compliance-Kontext. |
|
||||
| **Quelle** | Router-Listenfilter |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 5. Legal Hold nur Superadmin aus Meldung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Mapping `report_reason` → `legal_hold reason_code`; `set_legal_hold` superadmin-geschützt. |
|
||||
| **Begründung** | Hochrisiko-Aktion; Anschluss P-11. |
|
||||
| **Quelle** | `_REASON_TO_HOLD_CODE`; `media_legal_hold.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 6. Audit-Spur bei Medien-Meldungen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `media_asset_audit_log` Event `content_report_filed` bei media_asset-Bezug. |
|
||||
| **Begründung** | Lifecycle-Entscheidungen nachvollziehbar. |
|
||||
| **Quelle** | Migration 053; `write_audit_log_entry` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 7. E-Mail best-effort, kein Hard-Fail
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Bestätigung an Melder + Admin-Benachrichtigung; SMTP-Fehler blockieren Speichern nicht. |
|
||||
| **Begründung** | Meldung geht nicht verloren wenn Mail down. |
|
||||
| **Quelle** | Router-Implementierung |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Admins müssen Posteingang auch ohne Mail prüfen. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Separate Compliance-Queue-App** — Inbox-Reuse ist bewusst.
|
||||
2. **Legal Hold durch Club-Admin** — Superadmin-only.
|
||||
3. **Meldungen ohne Statusworkflow/Archiv** — Wiedereröffnen muss möglich sein.
|
||||
4. **Fehlende Verknüpfung Medien-Lifecycle** — Hold muss Purge blockieren.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [MEDIA_ASSETS_DESIGN_PRINCIPLES.md](./MEDIA_ASSETS_DESIGN_PRINCIPLES.md)
|
||||
- [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./ACCESS_LAYER_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# Dashboard KPIs – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Dashboard KPI Aggregation“ — vereinfacht vs. Mitai Widgets
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 14 von 15
|
||||
**Mitai-Vergleich:** [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md) (#7, vereinfacht)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| API | `backend/routers/dashboard.py` — `GET /api/dashboard/kpis` |
|
||||
| Frontend | Dashboard/Übersicht-Page |
|
||||
| Refaktor-Kontext | `docs/architecture/SCHULDEN_UND_REMEDIATION.md` A3, B1 |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Dashboard KPI Aggregation**
|
||||
|
||||
Ein Backend-Roundtrip liefert Übungs-KPIs, YTD-Einheiten, Trainings-Home (nächste Termine, Vermerke, offene Rückschau) — Ersatz für mehrere parallele Client-Listen-Calls.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Aggregierter Endpoint statt Chatty Client
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `GET /dashboard/kpis` ruft intern `list_exercises_like_get` + `list_training_units` mit gleichen Filtern wie zuvor im UI. |
|
||||
| **Begründung** | Weniger Latenz; eine TenantContext-Auflösung; Refaktor Phase 1 Dashboard. |
|
||||
| **Quelle** | `dashboard.py`; SCHULDEN A3/B1 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Noch keine konfigurierbaren Widgets wie Mitai. |
|
||||
|
||||
### 2. Gleiche Filtersemantik wie Einzel-Endpoints
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | KPI-Zählungen nutzen dieselben Helfer wie `/exercises` und `/training-units` — keine zweite Query-Logik. |
|
||||
| **Begründung** | Zahlen auf Dashboard = Zahlen in Fachmodulen. |
|
||||
| **Quelle** | Import aus `exercises`, `training_planning` Routern |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Interne Funktionsaufrufe statt HTTP — Kopplung an Router-Helfer. |
|
||||
|
||||
### 3. TenantContext für Mandanten-KPIs
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `Depends(get_tenant_context)` — assigned_to_me, created_by_me respektieren Verein/Rolle. |
|
||||
| **Begründung** | Keine globalen KPIs für Trainer fremder Vereine. |
|
||||
| **Quelle** | `get_dashboard_kpis` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 4. Festes Dashboard-Layout (kein Widget-Katalog)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Shinkan-Übersicht zeigt definierte Kacheln — nicht nutzerkonfigurierbares Layout-JSON. |
|
||||
| **Begründung** | MVP-Fokus Trainer-Verein; weniger Komplexität als Mitai. |
|
||||
| **Quelle** | Produktentscheidung vs. Mitai #7 |
|
||||
| **Tragfähigkeit** | **mittel** (bewusste Vereinfachung) |
|
||||
| **Einschränkung** | Erweiterung braucht Backend+Frontend-Change, nicht Admin-Config. |
|
||||
|
||||
### 5. Profil nicht redundant laden
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Dashboard soll Auth-Profil nutzen — kein zweites `/profiles/me` nach Login+Reload (E2E Test 8). |
|
||||
| **Begründung** | Architekturschuld A3 explizit adressiert. |
|
||||
| **Quelle** | `tests/dev-smoke-test.spec.js`; Roadmap |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Frontend-Umsetzung muss mitziehen. |
|
||||
|
||||
### 6. Slice-Logik für Trainings-Home im Backend
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `_slice_training_home_notes` filtert Einheiten mit Vermerken — max. N Stück serverseitig. |
|
||||
| **Begründung** | Kleiner Payload; klare Semantik. |
|
||||
| **Quelle** | `dashboard.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Grenzwert hardcoded — ggf. Query-Param später. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Drei parallele fast gleiche `listTrainingUnits`-Calls** im Client — behoben durch KPI-Endpoint.
|
||||
2. **Mitai Widget-Dual-Registry vorreifen** ohne Produktbedarf — Over-Engineering für Shinkan MVP.
|
||||
3. **KPI-Berechnung im Frontend** aus Volllisten — skaliert nicht.
|
||||
4. **Dashboard ohne Tenant-Filter** — Mandanten-Leak.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [docs/architecture/SCHULDEN_UND_REMEDIATION.md](../../architecture/SCHULDEN_UND_REMEDIATION.md)
|
||||
- Mitai: [DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
# Designprinzipien – Index (Jinkendo Produktfamilie)
|
||||
|
||||
**Status:** Arbeitspapier / Übergabe
|
||||
**Stand:** 2026-07-04
|
||||
**Zweck:** Zentraler Einstieg für **tragfähige Designprinzipien** der Jinkendo-Produktfamilie — Shinkan-Serie (15 Module), Abgleich mit Mitai (9 Module), Basis für Schwester-Apps.
|
||||
|
||||
**Nicht enthalten:** App-Gesamtarchitektur, domänenspezifische Fachlogik im Detail, vollständige API-Referenz.
|
||||
|
||||
**Ablage (Shinkan-Serie):** `docs/jinkendo-family/design-principles/*_DESIGN_PRINCIPLES.md`
|
||||
**Übergeordnet:** [docs/jinkendo-family/README.md](../README.md)
|
||||
**Mitai-Serie (vorläufig):** `mitai-jinkendo/.claude/docs/technical/DESIGN_PRINCIPLES_*.md`
|
||||
|
||||
---
|
||||
|
||||
## Wofür diese Serie?
|
||||
|
||||
Shinkan implementiert wiederkehrende **Querschnittsmuster** (Mandanten-Zugriff, Capabilities, Medien-Archiv, Planungsdomäne, KI-Laufzeit, Import, Navigation, Deploy) sowie **domänenspezifische Bausteine** (Übungskatalog, Fähigkeiten-Scoring, Trainingsplanung, Compliance-Meldungen). Die 15 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, Specs)
|
||||
- **Mitai-Abgleich** — welches Schwester-Dokument vergleichbar ist
|
||||
|
||||
Jedes Dokument ist **eigenständig lesbar**; dieser Index ordnet Abhängigkeiten, Lese-Reihenfolge und den geplanten Familien-Review.
|
||||
|
||||
---
|
||||
|
||||
## Dokumente (15/15)
|
||||
|
||||
| # | Modul | Datei | Kernidee (1 Satz) | Mitai-Vergleich |
|
||||
|---|-------|-------|-------------------|-----------------|
|
||||
| 1 | Access Layer & Tenant | [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./ACCESS_LAYER_DESIGN_PRINCIPLES.md) | Ein `TenantContext` pro Request; einheitliche `visibility`/`club_id`-Semantik für Bibliotheksartefakte. | — (Shinkan-spezifisch) |
|
||||
| 2 | Capability & Club Features | [CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md](./CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md) | Capabilities + Vereins-Kontingente; `GET /me/entitlements`; 4-Phasen-Rollout mit Env-Flags. | #3 Feature & Entitlement |
|
||||
| 3 | Rights Registry | [RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md](./RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md) | Module registrieren Capabilities/Features bei Startup — kein vollständiger Vorab-Katalog in Migrationen. | #4 Registry / Plugin |
|
||||
| 4 | Auth & Session | [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md) | Server-Sessions, `require_auth` als Depends; gemeinsame Mitai-Basis. | #5 Auth & Session |
|
||||
| 5 | AI Prompt Runtime | [AI_PROMPT_RUNTIME_DESIGN_PRINCIPLES.md](./AI_PROMPT_RUNTIME_DESIGN_PRINCIPLES.md) | Schmale Laufzeit (`ai_prompt_runtime`); DB-Templates + Mustache; Kontext-Arten pro Domäne. | #1 Prompt Engine |
|
||||
| 6 | Media Assets & Archiv | [MEDIA_ASSETS_DESIGN_PRINCIPLES.md](./MEDIA_ASSETS_DESIGN_PRINCIPLES.md) | Physisches Asset einmal, mehrfach verknüpft; Lifecycle, Legal Hold, Inline-Rich-Text. | — |
|
||||
| 7 | Exercise Catalog | [EXERCISE_CATALOG_DESIGN_PRINCIPLES.md](./EXERCISE_CATALOG_DESIGN_PRINCIPLES.md) | Übung als Kernobjekt; Varianten, Governance, Progressionsgraph, Kombinationsübungen. | — |
|
||||
| 8 | Skill Scoring | [SKILL_SCORING_DESIGN_PRINCIPLES.md](./SKILL_SCORING_DESIGN_PRINCIPLES.md) | Regelbasiertes gewichtetes Profil; Peer-Vergleich nur unter gleichem Artefakttyp. | #2 Data Layer (teilweise) |
|
||||
| 9 | Training Planning | [TRAINING_PLANNING_DESIGN_PRINCIPLES.md](./TRAINING_PLANNING_DESIGN_PRINCIPLES.md) | Einheiten mit Phasen/Streams; Rahmen-Bibliothek + Module; Coach/Durchführung getrennt. | — |
|
||||
| 10 | Content Reports (P-13) | [CONTENT_REPORTS_DESIGN_PRINCIPLES.md](./CONTENT_REPORTS_DESIGN_PRINCIPLES.md) | Melde-Workflow in Posteingang; Priorität sensibler Gründe; Legal-Hold-Anschluss. | — |
|
||||
| 11 | Wiki Import | [WIKI_IMPORT_DESIGN_PRINCIPLES.md](./WIKI_IMPORT_DESIGN_PRINCIPLES.md) | SMW-API-Ingest + Mapper; Preview/Dry-Run; Duplikat-Tracking — kein Raw-Wiki in DB. | #6 Universal Import |
|
||||
| 12 | Navigation / IA | [NAVIGATION_IA_DESIGN_PRINCIPLES.md](./NAVIGATION_IA_DESIGN_PRINCIPLES.md) | `appNav.js` als SSoT; Admin-Hub horizontal; Onboarding-Nav ohne Verein. | #8 Navigation / IA |
|
||||
| 13 | Migration & Deploy | [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](./MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md) | `XXX_*.sql` beim Container-Start; develop/main → Dev/Prod; fail-fast. | #9 Migration & Deploy |
|
||||
| 14 | Dashboard KPIs | [DASHBOARD_KPI_DESIGN_PRINCIPLES.md](./DASHBOARD_KPI_DESIGN_PRINCIPLES.md) | Aggregierter `/dashboard/kpis`-Roundtrip statt mehrerer Listen-Calls. | #7 Dashboard Widgets (vereinfacht) |
|
||||
| 15 | Maturity Models | [MATURITY_MODELS_DESIGN_PRINCIPLES.md](./MATURITY_MODELS_DESIGN_PRINCIPLES.md) | Kontextsensitive Matrix-Auflösung; Export/Import-Stack für Admin-Portabilität. | — |
|
||||
|
||||
---
|
||||
|
||||
## Empfohlene Lesereihenfolge
|
||||
|
||||
### Schnellüberblick (45 Min)
|
||||
|
||||
1. [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./ACCESS_LAYER_DESIGN_PRINCIPLES.md) — Shinkan-Kernunterscheidung zu Mitai
|
||||
2. [RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md](./RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md) — Meta-Muster für Erweiterbarkeit
|
||||
3. [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](./MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md) — Familien-Basis
|
||||
|
||||
### Vollständige Implementierung (neues Produkt)
|
||||
|
||||
```
|
||||
Foundation: (13) Migration & Deploy → (4) Auth → (1) Access Layer → (2) Capabilities → (3) Registry
|
||||
Domäne: (7) Exercise Catalog → (6) Media → (9) Training Planning → (8) Skill Scoring
|
||||
Erweiterung: (5) AI Prompt Runtime → (11) Wiki Import → (15) Maturity Models
|
||||
Compliance: (10) Content Reports
|
||||
Oberfläche: (12) Navigation → (14) Dashboard KPIs
|
||||
```
|
||||
|
||||
### Nur Familien-Review (Mitai ↔ Shinkan)
|
||||
|
||||
| Mitai-Dokument | Shinkan-Gegenstück | Review-Fokus |
|
||||
|----------------|-------------------|--------------|
|
||||
| #1 Prompt Engine | #5 AI Prompt Runtime | Executor-Reife, Registry, Workflows |
|
||||
| #2 Data Layer | #8 Skill Scoring | Berechnungs-SSoT vs. Router-Duplikat |
|
||||
| #3 Feature & Entitlement | #2 Capability & Club Features | Subjekt: Profil vs. Verein |
|
||||
| #4 Registry | #3 Rights Registry | Registrierungsmuster |
|
||||
| #5 Auth | #4 Auth & Session | Gemeinsamer Code, IDOR-Risiken |
|
||||
| #6 Universal Import | #11 Wiki Import | Ingest ≠ Interpretation |
|
||||
| #7 Dashboard Widgets | #14 Dashboard KPIs | Konfigurierbarkeit vs. Aggregation |
|
||||
| #8 Navigation | #12 Navigation | appNav-Pattern |
|
||||
| #9 Migration & Deploy | #13 Migration & Deploy | Gleiches Startup-Muster |
|
||||
|
||||
---
|
||||
|
||||
## Querschnittsthemen (über alle Docs)
|
||||
|
||||
| Thema | Primär | Ergänzend |
|
||||
|-------|--------|-----------|
|
||||
| Mandanten-Isolation (`club_id`) | #1 Access Layer | #6 Media, #7 Exercise, #9 Planning |
|
||||
| Sichtbarkeit `private`/`club`/`official` | #1 Access Layer | #6 Media, #7 Exercise |
|
||||
| Capability-Gating | #2 Entitlement | #3 Registry, #5 AI Runtime |
|
||||
| Validierung an der Grenze | #3 Registry | #6 Inline-Media, #11 Import-Mapper |
|
||||
| Single Source of Truth (Berechnung) | #8 Skill Scoring | #5 AI Kontext-Builder |
|
||||
| Dual Registry (Code + DB) | #3 Rights Registry | #2 Capabilities in DB |
|
||||
| Bekannte Lücken dokumentieren | alle „Nicht übernehmen“ | Endpoint-Audit, Architekturschuld |
|
||||
|
||||
---
|
||||
|
||||
## Verwandte normative Docs (Shinkan-spezifisch)
|
||||
|
||||
| Thema | Agent-Guide / Spec |
|
||||
|-------|-------------------|
|
||||
| Zugriffsschicht | [ACCESS_LAYER_AND_GOVERNANCE_PLAN.md](../../../.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md), [ACCESS_LAYER_ENDPOINT_AUDIT.md](../../../.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md) |
|
||||
| Capabilities | [CAPABILITY_CATALOG.v1.md](../../../.claude/docs/technical/CAPABILITY_CATALOG.v1.md) |
|
||||
| Vereins-Features | [CLUB_MEMBERSHIP_AND_FEATURES.v1.md](../../../.claude/docs/technical/CLUB_MEMBERSHIP_AND_FEATURES.v1.md) |
|
||||
| Medien | [MEDIA_ASSETS_AND_ARCHIVE_SPEC.md](../../../.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md) |
|
||||
| KI-Zielbild | [AI_PROMPT_TARGET_ARCHITECTURE.md](../../../.claude/docs/technical/AI_PROMPT_TARGET_ARCHITECTURE.md) |
|
||||
| Planung Streams | [PARALLEL_TRAINING_STREAMS_SPEC.md](../../../.claude/docs/technical/PARALLEL_TRAINING_STREAMS_SPEC.md) |
|
||||
| Skill Scoring | [SKILL_SCORING_SPEC.md](../../../.claude/docs/technical/SKILL_SCORING_SPEC.md) |
|
||||
| Architektur-Schuld | [docs/architecture/SCHULDEN_UND_REMEDIATION.md](../../architecture/SCHULDEN_UND_REMEDIATION.md) |
|
||||
|
||||
---
|
||||
|
||||
## Übergabe-Checkliste (Familien-Review)
|
||||
|
||||
```
|
||||
[ ] Pro Modul: Prinzipien vs. Mitai-Gegenstück abgleichen
|
||||
[ ] Architekturschuld pro Modul in SCHULDEN_UND_REMEDIATION / „Nicht übernehmen“ verknüpfen
|
||||
[ ] Gemeinsame Familien-Prinzipien aus Übereinstimmungen ableiten
|
||||
[ ] Abweichungen bewusst dokumentieren (z. B. Vereins- vs. Profil-Entitlements)
|
||||
[ ] Shared Code (auth.py, db_init) — eine Quelle oder Fork-Drift?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pflege
|
||||
|
||||
| Aktion | Wo |
|
||||
|--------|-----|
|
||||
| Neues Querschnittsmodul extrahiert | Neues `*_DESIGN_PRINCIPLES.md` + Zeile in Tabelle oben |
|
||||
| Shinkan-Implementierung ändert Muster | Betroffenes Einzeldokument + ggf. Querschnittstabelle |
|
||||
| Mitai-Review abgeschlossen | Abschnitt „Familien-Prinzipien“ (separates Doc, Backlog) |
|
||||
|
||||
---
|
||||
|
||||
## Changelog Index
|
||||
|
||||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
| 2026-07-04 | Shinkan-Serie nach `docs/jinkendo-family/design-principles/` verschoben (Familien-Foundation) |
|
||||
| 2026-07-04 | Index angelegt; Serie 1–15 aus Shinkan-Ist-Stand extrahiert |
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# Exercise Catalog – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Exercise Catalog“ — Kernobjekt Übung, Varianten, Graphen, Kombination
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 7 von 15
|
||||
**Mitai-Vergleich:** — (domänenspezifisch)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| API | `backend/routers/exercises.py`, `exercise_progression_graphs.py` |
|
||||
| Rich-Text | `backend/exercise_rich_text.py` |
|
||||
| KI | `backend/exercise_ai.py` |
|
||||
| Frontend | `frontend/src/pages/Exercises*.jsx`, Tab-Formular |
|
||||
| Specs | `EXERCISES_ARCHITECTURE.md`, `EXERCISES_API_SPEC.md`, Kombinations-Spec |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Exercise Catalog**
|
||||
|
||||
Shinkans Kernobjekt: Übungen mit mehrdimensionaler Einordnung (Skills, Fokus, Stile), Varianten, Progressionsgraphen, Kombinationsübungen (`method_archetype`, Stationen), Governance und Medien-Anbindung.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Übung als zentrales Aggregate Root
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Varianten, Medien, Skills, Graph-Knoten hängen an `exercises.id` — Owner/Governance auf Eltern-Übung. |
|
||||
| **Begründung** | Eine Freigabe- und Lösch-Semantik; Varianten ohne eigenen Owner. |
|
||||
| **Quelle** | `FACHLICHE_NUTZERFUNKTIONEN.md` §4.1, §4.7 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Große Monolith-Router/Seiten — Refaktor-Roadmap. |
|
||||
|
||||
### 2. Tab-Formular statt Scroll-Monolith
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Register: Stammdaten · Anleitung · Einordnung · Kombination · Varianten · Medien — Varianten/Medien erst nach erstem Save. |
|
||||
| **Begründung** | UX für komplexe Objekte; klare Abhängigkeiten (IDs für Medien/Varianten). |
|
||||
| **Quelle** | Nutzerfunktionen §4.1 |
|
||||
| **Tragfähigkeit** | **hoch** (UI-Muster) |
|
||||
| **Einschränkung** | Frontend-God-Page-Schuld dokumentiert. |
|
||||
|
||||
### 3. Mehrdimensionale Filter-SSoT
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Suche/Filter über Skills, Fokus, Stil, Zielgruppe, Status, Freigabelevel — Backend-Query + gespeicherte Präferenzen. |
|
||||
| **Begründung** | Trainer finden Inhalte in großen Vereins-Katalogen. |
|
||||
| **Quelle** | `SEARCH_FILTER_SPEC.md`; `exercises.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Performance schwere Listen — Baseline/Roadmap. |
|
||||
|
||||
### 4. Varianten mit Voraussetzungskette
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `exercise_variants` mit Reihenfolge, optional `prerequisite_variant_id`. |
|
||||
| **Begründung** | Didaktische Abstufung innerhalb einer Übung. |
|
||||
| **Quelle** | Migration 030; Planung nutzt Varianten-ID pro Eintrag |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 5. Progressionsgraph als gerichtete Übungs-Beziehungen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Knoten = Übungen/Varianten; Kanten = „weiter“-Beziehungen; eigener Router + UI in Übungswelt. |
|
||||
| **Begründung** | Didaktik und Planungs-KI nutzen denselben Graph. |
|
||||
| **Quelle** | `exercise_progression_graphs.py`; `PLANNING_PROGRESSION_GRAPH_KI.md` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Graph-Editor-Komplexität; KI-Artefakte separat. |
|
||||
|
||||
### 6. Kombinationsübungen als Sonderform im gleichen Katalog
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `exercise_type=combination` mit Stationen, `method_archetype`, optionalem `method_profile`. |
|
||||
| **Begründung** | In Planung wie normale Übung; Coach zeigt Stations-Layer. |
|
||||
| **Quelle** | Migration 056/057; Kombinations-Spec V2 |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Archetyp-Stufen B/C noch ausbaubar. |
|
||||
|
||||
### 7. Governance integriert (nicht separates CMS)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `visibility`, `status` (draft/review/…), Access-Layer-Lösch/Transition-Regeln. |
|
||||
| **Begründung** | Trainer-Workflow ohne externes Freigabe-Tool. |
|
||||
| **Quelle** | `club_tenancy.py`; Content Change Requests → Posteingang |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Formales Review-Workflow noch leichtgewichtig. |
|
||||
|
||||
### 8. Rich-Text-Felder mit Inline-Medien
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Einheitliche Platzhalter/Render für `summary`, `goal`, `execution`, … |
|
||||
| **Begründung** | Medienreicher Inhalt ohne iframe-Split. |
|
||||
| **Quelle** | `exercise_rich_text.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Varianten mit eigenem Owner/Freigabe** — widerspricht Domänenmodell.
|
||||
2. **Übungsliste ohne Tenant-Filter** — Access-Layer-Pflicht.
|
||||
3. **KI-generierte Übungen ohne Governance-Felder** — immer draft/private Default.
|
||||
4. **Progressionsgraph-Logik im Frontend allein** — Server validiert Kanten.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [EXERCISES_ARCHITECTURE.md](../../../.claude/docs/technical/EXERCISES_ARCHITECTURE.md)
|
||||
- [MEDIA_ASSETS_DESIGN_PRINCIPLES.md](./MEDIA_ASSETS_DESIGN_PRINCIPLES.md)
|
||||
- [SKILL_SCORING_DESIGN_PRINCIPLES.md](./SKILL_SCORING_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
# Maturity Models – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Maturity Models / Fähigkeitsmatrix“ — kontextsensitive Auflösung
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 15 von 15
|
||||
**Mitai-Vergleich:** — (domänenspezifisch)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| API | `backend/routers/maturity_models.py`, `matrix_editor.py`, `matrix_stack_bundle.py` |
|
||||
| Admin-UI | `/admin/maturity-models` |
|
||||
| Import | Wiki-Import Typ Modelle; Matrix-Stack Export/Import |
|
||||
| Spec | `.claude/docs/technical/SKILLS_MATRIX_SPEC.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Maturity Models & Matrix Stack**
|
||||
|
||||
Matrixbasierte Reifegradmodelle mit Stufen und Zelltexten; kontextsensitive Auflösung über Bindings (Fokusbereich, Stilrichtung, Zielgruppe); Admin-Export/Import einzelner Modelle und Komplett-Stack.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Kontext-Bindings M:N (leer = überall)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Modell verknüpft mit Fokus/Stil/Zielgruppe; leere Bindings = global gültig. |
|
||||
| **Begründung** | Ein Stack deckt mehrere Trainingskontexte ab. |
|
||||
| **Quelle** | `maturity_models.py` `_attach_context` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Auflösungs-Priorität bei mehreren Treffern dokumentieren. |
|
||||
|
||||
### 2. Resolve-API für Laufzeit-Nutzung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Authentifizierte Nutzer listen/auflösen; Admin-only für Roh-ID-GET in Admin-UI. |
|
||||
| **Begründung** | Trainer sehen passende Matrix; Rohdaten-Edit geschützt. |
|
||||
| **Quelle** | Router-Docstring; Rollen-Checks |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 3. Matrix-Editor als separates Admin-Tool
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `matrix_editor` Router für Zellbearbeitung — nicht im Trainer-Flow. |
|
||||
| **Begründung** | Komplexe UI; Plattform-Redaktionsaufgabe. |
|
||||
| **Quelle** | Admin-Nav „Fähigkeitsmatrix“ |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Frontend-Komplexität — eigene Schuld-Kategorie. |
|
||||
|
||||
### 4. Stack-Bundle Export/Import
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `matrix_stack_bundle` — Komplett-Stack zwischen Umgebungen (Dev→Prod, Backup). |
|
||||
| **Begründung** | Analog Prompt Import/Export — Konfiguration versionierbar außerhalb DB. |
|
||||
| **Quelle** | Admin-Werkzeuge; Wiki-Import ergänzt |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Kein Diff/Merge wie Mitai Prompt-Import. |
|
||||
|
||||
### 5. Plattform-Admin-Schreibschutz
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Schreiben nur `admin`/`superadmin`; Lesen breiter für authentifizierte Nutzer (Resolve). |
|
||||
| **Begründung** | Offizielle Kompetenzrahmen zentral gepflegt. |
|
||||
| **Quelle** | `_require_admin` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 6. Integration Wiki-Import für Modelle
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | SMW-Kategorie Modelle → Import-Pfad neben Übungen/Skills. |
|
||||
| **Begründung** | Bestehende Wissensbasis karatetrainer.net nutzen. |
|
||||
| **Quelle** | `import_wiki.py` `CATEGORY_MODELS` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Gap-Analyse SMW — nicht alle Wiki-Felder gemappt. |
|
||||
|
||||
### 7. Orthogonal zu Skill Scoring
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Matrix = beschreibende Stufen; Skill Scoring = gewichtete Übungs-Aggregation — getrennte Module. |
|
||||
| **Begründung** | Keine Vermischung von Kompetenz-Raster und Trainings-KPI. |
|
||||
| **Quelle** | Domänen-Trennung in Specs |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | UI kann beides nebenan zeigen — klare Labels nötig. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Matrix-Zellen in Übungs-Score-Formel mischen** — ohne fachliche Spec.
|
||||
2. **Trainer-Edit globaler offizieller Matrizen** — Admin-only.
|
||||
3. **Import ohne Stack-Integrität** — Bundle-Validierung beachten.
|
||||
4. **Resolve ohne Kontext-Parameter** wenn Mehrdeutigkeit — falsche Matrix.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [SKILLS_MATRIX_SPEC.md](../../../.claude/docs/technical/SKILLS_MATRIX_SPEC.md)
|
||||
- [WIKI_IMPORT_DESIGN_PRINCIPLES.md](./WIKI_IMPORT_DESIGN_PRINCIPLES.md)
|
||||
- [SKILL_SCORING_DESIGN_PRINCIPLES.md](./SKILL_SCORING_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
# Media Assets & Archiv – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Media Assets & Archiv“ — physische Medien, Lifecycle, Inline
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 6 von 15
|
||||
**Mitai-Vergleich:** — (Mitai hat kein vergleichbares Medien-Archiv)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| API | `backend/routers/media_assets.py`, `platform_media_storage.py` |
|
||||
| Speicher | `backend/media_storage.py`, `MEDIA_ROOT` |
|
||||
| Rechte/Audit | `backend/media_rights.py`, `media_legal_hold.py` |
|
||||
| Inline Rich-Text | `backend/exercise_rich_text.py` |
|
||||
| Retention-Job | `backend/scripts/media_retention_job.py` |
|
||||
| Spec | `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Media Assets & Archiv**
|
||||
|
||||
Zentrale Verwaltung physischer Dateien (`media_assets`), Verknüpfung zu Übungen (`exercise_media`), mehrstufiger Lifecycle (Papierkorb, Legal Hold), Inline-Einbettung in Rich-Text.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Physisches Asset einmal, mehrfach verknüpft
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Datei in `media_assets`; Übungen referenzieren via `exercise_media.media_asset_id`. |
|
||||
| **Begründung** | Keine Dubletten auf Platte; Wiederverwendung im Archiv. |
|
||||
| **Quelle** | `MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` §1 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Legacy-Pfade ohne Asset-ID können noch existieren. |
|
||||
|
||||
### 2. Gleiche Visibility-Semantik wie Übungen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `private`/`club`/`official` + `club_id` — Access Layer für Download und Liste. |
|
||||
| **Begründung** | Ein Freigabemodell für alle Bibliotheksartefakte. |
|
||||
| **Quelle** | Spec §4; `media_rights.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Promotion Übung→official muss Assets mithheben — UI-Dialog Pflicht. |
|
||||
|
||||
### 3. Lifecycle getrennt von Übungs-Verknüpfung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Verknüpfung in Übung lösen ≠ Asset physisch löschen; Papierkorb-Stufen separat. |
|
||||
| **Begründung** | Trainer dürfen Link entfernen ohne Archiv-Löschrecht. |
|
||||
| **Quelle** | Spec §5 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Retention-Job muss in Betrieb überwacht werden. |
|
||||
|
||||
### 4. Legal Hold blockiert automatisierten Lifecycle
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `legal_hold` schützt Asset vor Purge; Anbindung an Content Reports (Superadmin). |
|
||||
| **Begründung** | Compliance bei Meldungen (P-11/P-13). |
|
||||
| **Quelle** | `media_legal_hold.py`; `content_reports.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 5. Inline-Medien: kanonisches Markup + Validierung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `{{exerciseMedia:id}}` → `<span data-shinkan-exercise-media="id">`; IDs müssen zur Übung gehören. |
|
||||
| **Begründung** | Ein Render-Pfad; keine broken References nach Medien-Löschung. |
|
||||
| **Quelle** | `exercise_rich_text.py`; Spec §11 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Beim Erst-Anlegen der Übung keine Inline-Refs (Chicken-Egg). |
|
||||
|
||||
### 6. Speicher-Abstraktion (local + konfigurierbarer Root)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `get_effective_media_root()` + `library/…`-Pfadkonvention; kein hardcodierter `/app/media` in Routern. |
|
||||
| **Begründung** | NAS/externer Speicher vorbereitet. |
|
||||
| **Quelle** | `media_storage.py`; `platform_media_storage` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | S3-Backend noch nicht vollständig. |
|
||||
|
||||
### 7. Audit-Log für sensitive Aktionen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `media_asset_audit_log` bei Meldungen, Hold, kritischen Lifecycle-Events. |
|
||||
| **Begründung** | Nachvollziehbarkeit für Admins und Compliance. |
|
||||
| **Quelle** | `media_rights.write_audit_log_entry` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Nicht jede Admin-Aktion geloggt. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Download nur mit Übungs-ID ohne Asset-Governance** — Seitenkanal-Risiko.
|
||||
2. **Copyright leer bei `official`** — Spec verbietet das fachlich.
|
||||
3. **Physisches Löschen bei Referenzanzahl > 0** — Spec §5.3.
|
||||
4. **Embed-URLs durch Lifecycle-Purge** — Embeds haben anderen Lebenszyklus.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [MEDIA_ASSETS_AND_ARCHIVE_SPEC.md](../../../.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md)
|
||||
- [ACCESS_LAYER_DESIGN_PRINCIPLES.md](./ACCESS_LAYER_DESIGN_PRINCIPLES.md)
|
||||
- [CONTENT_REPORTS_DESIGN_PRINCIPLES.md](./CONTENT_REPORTS_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
# Migration & Deploy – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Migration & Deploy“ — Schema-Evolution, Container-Start
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 13 von 15
|
||||
**Mitai-Vergleich:** [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md) (#9)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| DB-Init | `backend/db_init.py` |
|
||||
| Migrationen | `backend/migrations/XXX_*.sql` |
|
||||
| Version | `backend/version.py` (`DB_SCHEMA_VERSION`, `MODULE_VERSIONS`) |
|
||||
| Docker | `docker-compose.yml`, `docker-compose.dev-env.yml` |
|
||||
| Deploy | develop → dev.shinkan · main → shinkan (Pi) |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Migration & Deploy**
|
||||
|
||||
Nummerierte SQL-Migrationen beim Container-Start, Tracking in `schema_migrations`, Git-Branch → Umgebung, fail-fast ohne Auto-Rollback.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Nummerierte Migrationen `XXX_*.sql`
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Nur nummerierte Dateien in `backend/migrations/`; lexikographische Reihenfolge. |
|
||||
| **Begründung** | Familien-Standard Mitai/Shinkan; vorhersagbare Anwendung. |
|
||||
| **Quelle** | `db_init.py`; `CLAUDE.md` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Manuelle Nummern-Kollisionen vermeiden — Team-Disziplin. |
|
||||
|
||||
### 2. Startup vor App — Migrationen blockieren Start bei Fehler
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `db_init.py` wartet auf Postgres, wendet fehlende Migrationen an, dann FastAPI. |
|
||||
| **Begründung** | Keine App mit veraltetem Schema. |
|
||||
| **Quelle** | Container-Entrypoint / startup |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Kein automatisches Rollback — manuelle Recovery. |
|
||||
|
||||
### 3. `schema_migrations` Tracking-Tabelle
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Jede angewendete Datei wird persistiert; Wiederholung überspringt Bekannte. |
|
||||
| **Begründung** | Idempotenz über Deploys hinweg. |
|
||||
| **Quelle** | `ensure_migration_table`, `get_applied_migrations` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Geänderte Migration nach Apply — nicht neu ausführen (neue Nummer). |
|
||||
|
||||
### 4. `DB_SCHEMA_VERSION` als dokumentierter Stand
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `version.py` führt Schema-Version; MODULE_VERSIONS für Subsysteme. |
|
||||
| **Begründung** | Support und Handover wissen erwarteten Stand. |
|
||||
| **Quelle** | `backend/version.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Manuell pflegen bei Migration — Drift möglich. |
|
||||
|
||||
### 5. develop/main → Dev/Prod mit festen Ports
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Dev 3098/8098 · Prod 3003/8003 — nie ändern ohne explizite Freigabe. |
|
||||
| **Begründung** | Deploy-Infrastruktur auf Pi/Synology stabil. |
|
||||
| **Quelle** | `CLAUDE.md` Deployment |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 6. Neue Spalten nur via Migration
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Kein ad-hoc ALTER in Routern; Coding Rules. |
|
||||
| **Begründung** | Reproduzierbare Umgebungen. |
|
||||
| **Quelle** | `.claude/rules/CODING_RULES.md` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 7. IF NOT EXISTS / defensive SQL wo sinnvoll
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Migrationen tolerant bei Wiederanlauf in Dev — aber Tracking verhindert Doppel-Apply. |
|
||||
| **Begründung** | Recovery in Entwicklung erleichtern. |
|
||||
| **Quelle** | Mitai-Migrations-Muster |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Nicht alles idempotent — komplexe Migrationen brauchen Transaktion. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Manuelle psql-Schritte in Prod** ohne nummerierte Migration im Repo.
|
||||
2. **Schema-Drift nur in schema.sql** ohne Migration — Init vs. Upgrade verwechseln.
|
||||
3. **Auto-Rollback bei fehlgeschlagener Migration** — fail-fast, manuell fixen.
|
||||
4. **Port-Änderung ohne Infra-Update** — bricht Fritz!Box/NAS-Routing.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [DATABASE_SCHEMA.md](../../../.claude/docs/technical/DATABASE_SCHEMA.md)
|
||||
- Mitai: [MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# Navigation / IA – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Navigation & Information Architecture“
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 12 von 15
|
||||
**Mitai-Vergleich:** [NAVIGATION_IA_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/NAVIGATION_IA_DESIGN_PRINCIPLES.md) (#8)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Hauptnav | `frontend/src/config/appNav.js` |
|
||||
| Admin-Nav | `frontend/src/components/AdminPageNav.jsx` |
|
||||
| Shells | `RequireAdmin`, App-Layout in `App.jsx` |
|
||||
| Return-Kontext | `.claude/docs/technical/NAV_RETURN_CONTEXT_SPEC.md` |
|
||||
| Styles | `frontend/src/app.css` (`.admin-top-nav`, Bottom-Nav) |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Navigation / IA**
|
||||
|
||||
Single Source of Truth für Hauptnavigation (Mobile Bottom + Desktop Sidebar), separater Admin-Hub, Onboarding-Nav ohne Vereinsfeatures, rollen- und kontextabhängige Einblendung (Posteingang, Admin).
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. `appNav.js` als SSoT für Hauptnavigation
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `getMainNavItems(isAdmin, opts)` liefert Route, Label, Icon — eine Liste für Mobile und Desktop. |
|
||||
| **Begründung** | Gleiches Familien-Muster wie Mitai `appNav`; keine divergierenden Nav-Arrays. |
|
||||
| **Quelle** | `appNav.js` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Tiefe Unterrouten (Übung bearbeiten) nicht in Top-Nav — Shell/Back. |
|
||||
|
||||
### 2. Admin als separater Hub
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `/admin/*` mit horizontaler `AdminPageNav` — Plattform-Werkzeuge gebündelt. |
|
||||
| **Begründung** | Trainer-Nav bleibt schlank; Admin-IA skaliert unabhängig. |
|
||||
| **Quelle** | `AdminPageNav.jsx` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Admin-Nav hardcoded Array — kein `adminNav.js` SSoT wie Mitai ideal. |
|
||||
|
||||
### 3. Onboarding-Nav reduziert
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `getOnboardingNavItems()` — nur Verein + Einstellungen ohne Übungen/Planung. |
|
||||
| **Begründung** | Nutzer ohne Vereinsmitgliedschaft nicht in leere Bereiche führen. |
|
||||
| **Quelle** | `appNav.js` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 4. Kontextabhängige Items (Posteingang)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `showInbox` Flag steuert Posteingang-Eintrag — Berechtigung aus Entitlements/Rolle. |
|
||||
| **Begründung** | Kein toter Nav-Link für Trainer ohne Inbox-Recht. |
|
||||
| **Quelle** | `baseItems({ showInbox })` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Logik zur `showInbox`-Setzung in App.jsx pflegen. |
|
||||
|
||||
### 5. Responsive: Bottom-Nav Mobile, Sidebar Desktop
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Gleiche Items, unterschiedliche Präsentation; CSS-Variablen für Abstände. |
|
||||
| **Begründung** | PWA-typisches Muster; 80px Bottom-Padding für Nav. |
|
||||
| **Quelle** | `app.css`; Design-System in `CLAUDE.md` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Breakpoint-Konsistenz mit Mitai (1024px) prüfen beim Familien-Review. |
|
||||
|
||||
### 6. Return-Kontext für tiefe Bearbeitung
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Spez `NAV_RETURN_CONTEXT_SPEC` — zurück zur Herkunftsliste mit Filter-State. |
|
||||
| **Begründung** | Übungs-Editor aus Suche/Planung ohne Navigations-Verlust. |
|
||||
| **Quelle** | Return-Context Spec |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Nicht alle Flows implementiert. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Zwei unterschiedliche Nav-Arrays** für Mobile vs. Desktop.
|
||||
2. **Admin-Routen in Haupt-Bottom-Nav** mischen (außer ein Admin-Einstieg).
|
||||
3. **Hardcodierte Nav in jeder Page** — zentral in `appNav.js`.
|
||||
4. **Fehlender Onboarding-Gate** — volle Nav ohne Verein verwirrt.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [NAV_RETURN_CONTEXT_SPEC.md](../../../.claude/docs/technical/NAV_RETURN_CONTEXT_SPEC.md)
|
||||
- Mitai: [NAVIGATION_IA_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/NAVIGATION_IA_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
# Rights Registry – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Rights Registry“ — Capabilities & Features zur Laufzeit registrieren
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 3 von 15
|
||||
**Mitai-Vergleich:** [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) (#4)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Registry-Kern | `backend/rights_registry.py` |
|
||||
| Modul-Registrierungen | `backend/rights_registrations/` (`exercises.py`, `planning.py`, `platform.py`, `club_creation.py`) |
|
||||
| Startup-Sync | Import in `backend/main.py` |
|
||||
| Tests | `backend/tests/test_rights_registry.py` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Rights Registry (Registry-first für Capabilities & Features)**
|
||||
|
||||
Module deklarieren bei Implementierung, welche Rechte und Kontingente sie anbieten. Beim App-Start werden Definitionen in die DB synchronisiert (`capabilities`, `features`, Default-Grants).
|
||||
|
||||
---
|
||||
|
||||
## Fachliche Verantwortung
|
||||
|
||||
1. **Runtime-Registrierung** — `register_capability()`, `register_feature()` vor DB-Sync.
|
||||
2. **Modul-Ownership** — Jedes Feature/Capability trägt `module`-Feld für Admin-Filter „Rollen & Rechte“.
|
||||
3. **Default Club Grants** — Rollen → Capability-Mapping bei Erst-Sync.
|
||||
4. **Kein vollständiger Vorab-Katalog in SQL-Migration** — nur was Module wirklich liefern.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Registry-first statt Migrations-Monolith
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Neue Rechte erscheinen durch Code-Registrierung + Startup-Upsert — nicht durch manuelle 079-Katalog-Migration pro Feature. |
|
||||
| **Begründung** | Modul und Recht entstehen zusammen; weniger vergessene Katalog-Einträge. |
|
||||
| **Quelle** | `rights_registry.py`; Docstring; `rights_registrations/` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Erste Basismigration seedet noch initiale Zeilen. |
|
||||
|
||||
### 2. Modul-Datei pro Domäne
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `rights_registrations/exercises.py` registriert nur Übungs-Rechte; Planung/Platform analog. |
|
||||
| **Begründung** | Ownership klar; Merge-Konflikte lokalisiert. |
|
||||
| **Quelle** | `rights_registrations/__init__.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Import-Reihenfolge muss in `main.py` garantiert sein. |
|
||||
|
||||
### 3. Frozen Dataclass-Definitionen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `CapabilityRegistration` / `FeatureRegistration` als immutable `@dataclass(frozen=True)`. |
|
||||
| **Begründung** | Keine nachträgliche Mutation nach Registrierung. |
|
||||
| **Quelle** | `rights_registry.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 4. Validierung an der Registrierungsgrenze
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `register_*` wirft bei fehlendem `id` oder `module`. |
|
||||
| **Begründung** | Fehler beim Import/Startup, nicht erst im Admin-UI. |
|
||||
| **Quelle** | `rights_registry.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Keine Schema-Validierung für `limit_type`/`reset_period` zur Compile-Zeit. |
|
||||
|
||||
### 5. DB als persistierter Katalog, Code als SSoT für neue IDs
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Startup `sync_rights_registry_to_db()` upsertet aus In-Memory-Registry. |
|
||||
| **Begründung** | Admin-UI liest DB; Entwickler erweitern Code-Registry. |
|
||||
| **Quelle** | `rights_registry.py`; `main.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Deaktivierte Capabilities in DB vs. fehlende im Code — Reconcile-Policy dokumentieren. |
|
||||
|
||||
### 6. Default Grants als Code-Daten
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `default_club_grants: (role_code, capability_id)` pro Capability. |
|
||||
| **Begründung** | Neue Module bringen sinnvolle Standard-Rollen mit. |
|
||||
| **Quelle** | `rights_registrations/exercises.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Admin-Overrides in DB können bei Re-Sync überschrieben werden — ON CONFLICT-Verhalten beachten. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Capabilities nur in SQL-Migration pflegen** — driftet vom implementierten Modul weg.
|
||||
2. **Registrierung ohne Endpoint-Verdrahtung** — Spec: „nur Rechte mit echter Endpoint-Verdrahtung“.
|
||||
3. **Zweite Registry-Philosophie** für Custom Roles — gleiche Capability-IDs wiederverwenden (Plan Stufe E).
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md](./CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md)
|
||||
- [CAPABILITY_CATALOG.v1.md](../../../.claude/docs/technical/CAPABILITY_CATALOG.v1.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# Skill Scoring – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Skill Scoring & Profile“ — gewichtete Fähigkeiten-KPIs
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 8 von 15
|
||||
**Mitai-Vergleich:** [DATA_LAYER_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/DATA_LAYER_DESIGN_PRINCIPLES.md) (#2, analog: Berechnungs-SSoT)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Kern | `backend/skill_scoring.py` |
|
||||
| Profile-API | `backend/routers/skill_profiles.py` |
|
||||
| Planungs-Vorschläge | Planung-Router + Fähigkeiten-Seite |
|
||||
| Spec | `.claude/docs/technical/SKILL_SCORING_SPEC.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Skill Scoring & Profiles**
|
||||
|
||||
Regelbasierte Aggregation von `exercise_skills` über Artefakte (Module, Rahmenprogramme, Pläne, Graphen) zu gewichteten Profilen mit Peer-Vergleich innerhalb desselben Artefakttyps.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Berechnung in einer Schicht (`skill_scoring.py`)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Scores, Gewichte, Peer-Perzentile — nicht in React oder Router-SQL duplizieren. |
|
||||
| **Begründung** | Analog Mitai Data Layer: Charts, Listen-KPIs, Planungs-Vorschläge nutzen dieselbe Logik. |
|
||||
| **Quelle** | `SKILL_SCORING_SPEC.md`; `skill_scoring.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Einzelne UI-Fallbacks können noch vereinfacht rechnen. |
|
||||
|
||||
### 2. Gewichtung aus Trainings-Signalen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Dauer, Vorkommen, Intensität (`niedrig`/`mittel`/`hoch`), Stufen-Spanne — explizite Multiplikatoren. |
|
||||
| **Begründung** | Nachvollziehbares Ranking ohne Black-Box-ML. |
|
||||
| **Quelle** | `_INTENSITY_MULT`, `_level_range_multiplier` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | `is_primary` / `development_contribution` bewusst ignoriert. |
|
||||
|
||||
### 3. Peer-Vergleich nur unter gleichem Artefakttyp
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Modul vs. Modul, Rahmen vs. Rahmen — nie Modul vs. Plan gemischt. |
|
||||
| **Begründung** | Fachlich sinnvoller Vergleich; vermeidet irreführende Prozentwerte. |
|
||||
| **Quelle** | Phase 3 Lieferung; Nutzerfunktionen §4.2 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | UI muss Typ-Kontext klar labeln. |
|
||||
|
||||
### 4. Sichtbarkeits-filterte Peer-Menge
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Peer-Pool = nur für Nutzer sichtbare Artefakte (Access Layer). |
|
||||
| **Begründung** | Keine Leaks über Scores fremder Vereins-Inhalte. |
|
||||
| **Quelle** | `skill_scoring.py` + Tenant-Filter in Aufrufern |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Performance bei großen Pools. |
|
||||
|
||||
### 5. Planungs-Vorschläge aus Profil-Delta
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Fähigkeiten-Schwerpunkte → sortierte Vorschläge für Module/Rahmen/Regressionspfade. |
|
||||
| **Begründung** | Schließt Loop zwischen Katalog und Planung. |
|
||||
| **Quelle** | Fähigkeiten-Seite Phase 3 |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | KI-Suche über Volltext — Backlog. |
|
||||
|
||||
### 6. Default-Minuten für fehlende Dauer
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `DEFAULT_ITEM_MINUTES` / `GRAPH_DEFAULT_ITEM_MINUTES` als explizite Konstanten. |
|
||||
| **Begründung** | Deterministische Scores bei unvollständigen Planungsdaten. |
|
||||
| **Quelle** | `skill_scoring.py` |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Fachlich kalibrierbar — dokumentieren statt verstecken. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Score-Berechnung im Frontend** für Listen-KPIs.
|
||||
2. **Peer-Vergleich über Artefakttypen hinweg** — irreführend.
|
||||
3. **ML-Black-Box statt regelbasierter Gewichte** — ohne explizite Produktentscheidung.
|
||||
4. **Ignorieren der Tenant-Sichtbarkeit** im Peer-Pool.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [SKILL_SCORING_SPEC.md](../../../.claude/docs/technical/SKILL_SCORING_SPEC.md)
|
||||
- [EXERCISE_CATALOG_DESIGN_PRINCIPLES.md](./EXERCISE_CATALOG_DESIGN_PRINCIPLES.md)
|
||||
- [TRAINING_PLANNING_DESIGN_PRINCIPLES.md](./TRAINING_PLANNING_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
# Training Planning – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „Training Planning“ — Einheiten, Phasen, Rahmen, Coach
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 9 von 15
|
||||
**Mitai-Vergleich:** — (domänenspezifisch)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Kalender-Einheiten | `backend/routers/training_planning.py` |
|
||||
| Module | `backend/routers/training_modules.py` |
|
||||
| Rahmen | `backend/routers/training_framework_programs.py` |
|
||||
| Phasen/Streams | Migration 063; `PARALLEL_TRAINING_STREAMS_SPEC.md` |
|
||||
| Frontend | `TrainingPlanningPage`, `TrainingCoachPage`, `TrainingUnitRunPage` |
|
||||
| Utils | `frontend/src/utils/trainingPlanUtils.js` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Training Planning & Frameworks**
|
||||
|
||||
Planbare Trainingseinheiten mit Sektionen, **Phasen** (Ganzgruppe/Parallel) und **Streams**, Bibliotheks-**Rahmenprogramme** (Ziele/Slots), **Trainingsmodule**, Materialisierung aus Slots, Durchführungs- und Coaching-Ansichten.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Einheit als planbares Aggregate
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `training_units` + `training_unit_sections` + Items; Kopf: Gruppe, Datum, Trainer, Status. |
|
||||
| **Begründung** | Klare Grenze Kalender vs. Bibliothek. |
|
||||
| **Quelle** | Domain Model; `training_planning.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Große Router-Datei — Refaktor-Schuld. |
|
||||
|
||||
### 2. Phasen/Streams als explizites Modell (nicht Marker-Sektionen)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `training_unit_phases` + `training_unit_parallel_streams`; Sektionen an Phase oder Stream gebunden. |
|
||||
| **Begründung** | Breakout-Trainings fachlich korrekt; Coach/Rejoin-Logik. |
|
||||
| **Quelle** | `PARALLEL_TRAINING_STREAMS_SPEC.md` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Legacy-Einheiten → Default-Ganzgruppenphase; Vorlagen-Phasen teils offen. |
|
||||
|
||||
### 3. API: verschachtelte `phases` + flache `sections`
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | GET liefert beides; PUT akzeptiert `phases` atomar; höchstens eines von phases/sections/exercises pro Request. |
|
||||
| **Begründung** | Frontend normalisiert; Server validiert CHECK-Regeln. |
|
||||
| **Quelle** | Spec §4; Planning-Router |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Server-Spiegelung neuer Abschnitte in phases — Handover offen. |
|
||||
|
||||
### 4. Rahmen-Bibliothek: Slot = Blueprint-Unit
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `framework_slot_id` auf Blueprint-`training_units`; Materialisierung → Kalender-Einheit für Gruppe. |
|
||||
| **Begründung** | Wiederverwendbare Programme ohne Duplikat-Logik pro Slot-Typ. |
|
||||
| **Quelle** | Migration 035–037 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | UI „Aus Rahmen übernehmen“ nicht flächendeckend. |
|
||||
|
||||
### 5. Trainingsmodule als wiederverwendbare Übungsfolgen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Bibliotheks-Objekt mit Skill-Profil; Übernahme in geplante Einheit. |
|
||||
| **Begründung** | Trainer-Bausteine zwischen Einzelübung und Rahmen. |
|
||||
| **Quelle** | `training_modules.py`; Skill Scoring Phase 3 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 6. Drei Durchführungsmodi getrennt
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Planung (edit) · Plan & Ablauf (run) · Coaching (step timeline, Stream-Picks, Nachbereitung). |
|
||||
| **Begründung** | Unterschiedliche UX und Payloads; Coach speichert → Run-Ansicht. |
|
||||
| **Quelle** | Nutzerfunktionen §4.4; `TrainingCoachPage` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Stream-Tabs in Run-Ansicht optional offen. |
|
||||
|
||||
### 7. Governance über Gruppe/Verein, keine neuen Mandanten-Entitäten
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Einheit → `training_group` → Verein; Access Layer für Bibliotheks-Rahmen/Module. |
|
||||
| **Begründung** | Planung erbt Organisations-Kontext. |
|
||||
| **Quelle** | `PARALLEL_TRAINING_STREAMS_SPEC.md` §4 |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Stream-Trainer-Zuweisung UI unvollständig. |
|
||||
|
||||
### 8. Kombinationsübungen in Planung transparent
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | Items ohne Variante; Coach zeigt Stations-Kandidaten + Archetyp-Hinweise. |
|
||||
| **Begründung** | Gleiche Item-Schicht für Standard- und Kombi-Übungen. |
|
||||
| **Quelle** | Migration 057; Kombinations-Spec Anhang A |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Archetyp-Stufen B/C ausbaubar. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Parallele Phasen als reine UI-Konvention ohne DB-Phasen** — Minimalvariante verworfen.
|
||||
2. **Rahmen-Slots als separate Exercise-Join-Tabelle** — Legacy `training_framework_slot_exercises` abgelöst.
|
||||
3. **Planungs-KI direkt in Router-Strings** — AI Prompt Runtime nutzen.
|
||||
4. **Blueprint-Einheiten in Kalenderlisten** — Filter `framework_slot_id IS NOT NULL`.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [PARALLEL_TRAINING_STREAMS_SPEC.md](../../../.claude/docs/technical/PARALLEL_TRAINING_STREAMS_SPEC.md)
|
||||
- [TRAINING_FRAMEWORK_SPEC.md](../../../.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md)
|
||||
- [SKILL_SCORING_DESIGN_PRINCIPLES.md](./SKILL_SCORING_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
# Wiki Import – Designprinzipien (Extraktion)
|
||||
|
||||
**Status:** Analyse / Arbeitspapier
|
||||
**Stand:** 2026-07-04
|
||||
**Geltungsbereich:** Modul „MediaWiki Import“ — SMW-Ingest, Mapping, Tracking
|
||||
|
||||
**Serie:** Designprinzipien für Produktfamilie · Shinkan Dokument 11 von 15
|
||||
**Mitai-Vergleich:** [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md) (#6)
|
||||
|
||||
**Kernkomponenten:**
|
||||
|
||||
| Bereich | Pfade |
|
||||
|---------|-------|
|
||||
| Router | `backend/routers/import_wiki.py`, `import_wiki_admin.py` |
|
||||
| Client | `backend/smw_client.py` |
|
||||
| Mapper | `backend/smw_mapper.py` |
|
||||
| Tracking | `wiki_import_log`, `wiki_import_references` |
|
||||
| Spec | `.claude/docs/technical/MEDIAWIKI_IMPORT_SPEC.md` |
|
||||
|
||||
---
|
||||
|
||||
## Modul
|
||||
|
||||
**Wiki Import (Semantic MediaWiki)**
|
||||
|
||||
Import von Übungen, Fähigkeiten, Methoden und Reifegradmodellen aus externem Wiki via API — Preview, Dry-Run, Duplikat-Erkennung, Admin-only.
|
||||
|
||||
---
|
||||
|
||||
## Designprinzipien
|
||||
|
||||
### 1. Ingest ≠ Interpretation
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `SmwClient` holt Rohdaten; `smw_mapper` mappt auf Shinkan-Modelle — getrennte Schichten. |
|
||||
| **Begründung** | Analog Mitai Import: Transport/Parser ≠ Domänen-Insert. |
|
||||
| **Quelle** | `import_wiki.py`; Mitai Universal Import |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Keine generische Import-Registry wie Mitai CSV — wiki-spezifisch. |
|
||||
|
||||
### 2. Preview und Dry-Run vor Execute
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `/preview` zeigt Kandidaten; `dry_run=true` ohne DB-Schreiben. |
|
||||
| **Begründung** | Admin sieht Auswirkungen; sichere Iteration. |
|
||||
| **Quelle** | `ImportExecuteRequest` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 3. Duplikat-Tracking über Wiki-Referenzen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `wiki_import_references` speichert Wiki-Titel ↔ Shinkan-ID für Re-Import. |
|
||||
| **Begründung** | Idempotenz und Update statt blindem Duplicate. |
|
||||
| **Quelle** | Domain Model Import-Tabellen |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Gap-Analyse in `SMW_IMPORTER_GAP_ANALYSIS.md` beachten. |
|
||||
|
||||
### 4. Import-Typ als expliziter Parameter
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `import_type`: `exercise` \| `skill` \| `method` \| Modelle — eigener Mapper-Pfad. |
|
||||
| **Begründung** | Klare Verantwortung pro Ziel-Entität. |
|
||||
| **Quelle** | `map_wiki_to_*` in `smw_mapper.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | Kein Plug-in-Registry-Pattern wie Mitai Module-Registry. |
|
||||
|
||||
### 5. Superadmin/Admin-only Execute
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `require_admin` auf Execute — Massenimport ist Plattform-Risiko. |
|
||||
| **Begründung** | Governance und Datenqualität. |
|
||||
| **Quelle** | `import_wiki.py` |
|
||||
| **Tragfähigkeit** | **hoch** |
|
||||
| **Einschränkung** | — |
|
||||
|
||||
### 6. Kategorie aus Env mit Fallback
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | `MEDIAWIKI_CATEGORY_*` Env-Variablen; leere Query → Default je Typ. |
|
||||
| **Begründung** | Wiki-Struktur konfigurierbar ohne Code-Deploy. |
|
||||
| **Quelle** | `CATEGORY_EXERCISES` etc. |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Hardcoded Wiki-URL in Doku — Umgebungsspezifisch halten. |
|
||||
|
||||
### 7. Background Tasks für lange Imports
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Prinzip** | FastAPI `BackgroundTasks` für Execute — HTTP nicht blockieren. |
|
||||
| **Begründung** | Große Kategorien ohne Timeout. |
|
||||
| **Quelle** | Execute-Endpoint |
|
||||
| **Tragfähigkeit** | **mittel** |
|
||||
| **Einschränkung** | Kein Job-Status-Polling-UI wie Mitai — Log-Tabelle nutzen. |
|
||||
|
||||
---
|
||||
|
||||
## Nicht übernehmen
|
||||
|
||||
1. **Rohe Wiki-HTML ungemappt in DB** — immer Mapper.
|
||||
2. **Import ohne Log/Re-Import-Referenz** — Duplikat-Chaos.
|
||||
3. **Trainer-self-service Wiki-Import** — Admin-only.
|
||||
4. **Skill-Scoring beim Insert** — Scores gehören in `skill_scoring`-Schicht.
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [MEDIAWIKI_IMPORT_SPEC.md](../../../.claude/docs/technical/MEDIAWIKI_IMPORT_SPEC.md)
|
||||
- [EXERCISE_CATALOG_DESIGN_PRINCIPLES.md](./EXERCISE_CATALOG_DESIGN_PRINCIPLES.md)
|
||||
- Mitai: [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](file:///c:/Dev/mitai-jinkendo/.claude/docs/technical/UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md)
|
||||
- [DESIGN_PRINCIPLES_INDEX.md](./DESIGN_PRINCIPLES_INDEX.md)
|
||||
Loading…
Reference in New Issue
Block a user