Merge pull request 'initial base setup' (#1) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 39s

Reviewed-on: #1
This commit is contained in:
Lars 2026-07-04 19:56:36 +02:00
commit b29fb2f538
72 changed files with 8459 additions and 498 deletions

View File

@ -0,0 +1,41 @@
---
description: Kairo Sprint-0 Architektur-Leitplanken und Dokumentenpriorität
globs: backend/**,frontend/**,docs/**,.gitea/**
alwaysApply: true
---
# Kairo Architecture Rules
Du arbeitest an **Jinkendo Kairo** (Sprint 0). Kairo ist mandantenfähig und Actor-first.
## Dokumentenpriorität bei Konflikten
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. `docs/reference/design-principles/alignment/*`
5. Mitai/Shinkan-Einzelprinzipien (nur Begründung, kein Scope)
## Reference design principles
Referenzmaterial:
- `docs/reference/design-principles/mitai/`
- `docs/reference/design-principles/shinkan/`
- `docs/reference/design-principles/alignment/`
Nicht automatisch Implementierungs-Scope. Übernahme nur via Principle Gate oder Architecture Decision.
## Harte Guardrails
- Kein Single-User ohne Tenant
- User ≠ Actor; Agenten sind Actors
- Auth, Capability, Feature, Governance trennen
- Keine hardcodierten Rechte, Prompts oder Fachkonfiguration
- Nummerierte SQL-Migrationen; kein ad-hoc DDL in Routern
- Keine Mitai-/Shinkan-Domänenlogik kopieren
- Keine Vorhaben-/Projektlogik vor Sprint-0-Abschluss
## Abweichungen
Architecture Decision Proposal nach Vorlage in `docs/architecture/ARCHITECTURE_DECISION_PROPOSAL_TEMPLATE.md`.

View File

@ -1,70 +1,18 @@
# === .env neben der jeweiligen docker-compose.*.yml kopieren ====================
# Docker Compose ersetzt ${VARIABLE} beim Start.
#
# Pro Umgebung eigene Datei (z. B. ~/docker/kairo/.env für Prod,
# ~/docker/kairo-dev/.env für Dev) — dieselben SCHLÜSSEL, unterschiedliche Werte.
# Kein separates DEV_APP_URL vs APP_URL: immer APP_URL, ALLOWED_ORIGINS, DB_*, …
# === .env neben docker-compose.*.yml ==========================================
# Pro Umgebung eigene Datei (~/docker/kairo/.env Prod, ~/docker/kairo-dev/.env Dev)
# ─── Typische Werte PROD (docker-compose.yml) ─────────────────────────────────
# DB_NAME=kairo
# DB_USER=kairo_user
# DB_PASSWORD=…
# 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) ─────────────────────────
# ─── DEV (docker-compose.dev-env.yml) ────────────────────────────────────────
# DB_NAME=kairo_dev
# DB_USER=kairo_dev
# DB_PASSWORD=dev_password
# 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
DB_PORT=5432
# ─── PROD (docker-compose.yml) ───────────────────────────────────────────────
DB_NAME=kairo
DB_USER=kairo_user
DB_PASSWORD=CHANGE_ME_SECURE_PASSWORD
OPENROUTER_API_KEY=your_api_key_here
OPENROUTER_MODEL=anthropic/claude-sonnet-4
# Vereins-Kontingente hart blockieren (KI-Kosten!). Nur 1, true oder yes aktivieren.
CLUB_FEATURE_ENFORCE=1
# KI-Debug (Docker): KAIRO_AI_DEBUG in docker-compose*.yml angebunden — 1 = ausführliche WARN-Logs.
# KAIRO_AI_DEBUG=1
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@jinkendo.de
SMTP_PASS=your_smtp_password
SMTP_FROM=noreply@jinkendo.de
SMTP_SSL=
SMTP_STARTTLS=
AUTO_ADMIN_FIRST_USER=true
ADMIN_BOOTSTRAP_EMAILS=
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

View File

@ -12,17 +12,25 @@ jobs:
run: |
set -e
echo "=== Deploying Kairo to DEVELOPMENT ==="
cd /home/lars/docker/kairo-dev
git fetch origin develop || git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
REPO="http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git"
TARGET="/home/lars/docker/kairo-dev"
mkdir -p "$TARGET"
cd "$TARGET"
if [ ! -d .git ]; then
git clone -b develop "$REPO" .
else
git fetch origin develop
git checkout develop
git reset --hard origin/develop
docker compose -f docker-compose.dev-env.yml build --no-cache
docker compose -f docker-compose.dev-env.yml up -d
sleep 5
if ! curl -sf http://localhost:8097/api/version; then
echo "✗ DEV API nicht erreichbar — Backend-Logs (Migration/Startup):"
fi
docker compose -f docker-compose.dev-env.yml build --no-cache backend frontend
echo "✓ Backend + Frontend gebaut (Frontend: npm run build im Dockerfile)"
docker compose -f docker-compose.dev-env.yml up -d --wait
if ! curl -sf http://localhost:8097/api/health; then
echo "✗ DEV API nicht erreichbar — Backend-Logs:"
docker compose -f docker-compose.dev-env.yml logs backend --tail 120 || true
exit 1
fi
echo "✓ DEV API healthy"
curl -sf http://localhost:3097/api/version && echo "✓ DEV über Frontend-Nginx (wie Browser) healthy"
echo "✓ DEV API /api/health OK"
curl -sf http://localhost:3097/api/health && echo "✓ DEV Frontend-Proxy /api/health OK"
echo "=== Kairo DEV Deploy complete ==="

View File

@ -12,12 +12,20 @@ jobs:
run: |
set -e
echo "=== Deploying Kairo to PRODUCTION ==="
cd /home/lars/docker/kairo
git fetch origin main || git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
REPO="http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git"
TARGET="/home/lars/docker/kairo"
mkdir -p "$TARGET"
cd "$TARGET"
if [ ! -d .git ]; then
git clone -b main "$REPO" .
else
git fetch origin main
git checkout main
git reset --hard origin/main
docker compose build --no-cache
docker compose up -d
sleep 5
curl -sf http://localhost:8004/api/version && echo "✓ PROD API (direkt) healthy"
curl -sf http://localhost:3004/api/version && echo "✓ PROD API über Frontend-Nginx (wie Browser) healthy"
fi
docker compose build --no-cache backend frontend
echo "✓ Backend + Frontend gebaut (Frontend: npm run build im Dockerfile)"
docker compose up -d --wait
curl -sf http://localhost:8004/api/health && echo "✓ PROD API /api/health OK"
curl -sf http://localhost:3004/api/health && echo "✓ PROD Frontend-Proxy /api/health OK"
echo "=== Kairo PROD Deploy complete ==="

View File

@ -1,8 +1,9 @@
name: Test Suite
# develop: push/PR → Tests gegen Dev (parallel oder vor Deploy Development).
# main: kein push/PR-Trigger — vermeidet doppelten Dev-Lauf beim Merge develop→main;
# Prod-Tests nur via workflow_run nach erfolgreichem Deploy Production.
# develop: push/PR → Tests gegen deployte Dev-Instanz (wartet auf Container).
# main: kein push/PR — Prod-Tests via workflow_run nach Deploy Production.
# Frontend-Build: deploy-dev/prod (docker compose build backend frontend), nicht hier.
# compose-smoke (PR): eigener Stack auf CI-Ports 18197/13197 (kein Konflikt mit kairo-dev).
on:
push:
branches: [develop]
@ -54,115 +55,95 @@ jobs:
done
docker compose -f "$COMPOSE_FILE" exec -T backend sh -lc "
pip install -r /app/requirements-dev.txt &&
cd /app &&
ACCESS_LAYER_STRICT=1 python scripts/check_access_layer_hints.py &&
python scripts/security_release_checks.py &&
ACCESS_LAYER_INTEGRATION=1 SKIP_DB_MIGRATE=1 python -m pytest tests -m 'not slow' -ra -vv --tb=short
pip install -q -r requirements-dev.txt &&
python -m pytest tests -ra -vv --tb=short
"
echo "✓ pytest OK"
lint-backend:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Check backend syntax
run: |
EVENT_NAME="${{ github.event_name }}"
REF_NAME="${{ github.ref_name }}"
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
APP_DIR="/home/lars/docker/kairo"
- name: Checkout repository
if: github.event_name != 'workflow_run'
uses: actions/checkout@v4
if [ "$EVENT_NAME" = "workflow_run" ]; then
- name: Check backend syntax (Checkout)
if: github.event_name != 'workflow_run'
run: |
python3 -m py_compile backend/main.py backend/run_migrations.py backend/db.py
echo "✓ Backend syntax OK"
- name: Check backend syntax (Deploy-Pfad)
if: github.event_name == 'workflow_run'
run: |
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then
APP_DIR="/home/lars/docker/kairo-dev"
else
APP_DIR="/home/lars/docker/kairo"
fi
elif [ "$REF_NAME" = "develop" ]; then
APP_DIR="/home/lars/docker/kairo-dev"
fi
python3 -m py_compile "$APP_DIR/backend/main.py"
echo "✓ Backend syntax OK"
build-frontend:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Build frontend
run: |
EVENT_NAME="${{ github.event_name }}"
REF_NAME="${{ github.ref_name }}"
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
APP_DIR="/home/lars/docker/kairo"
if [ "$EVENT_NAME" = "workflow_run" ]; then
if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then
APP_DIR="/home/lars/docker/kairo-dev"
fi
elif [ "$REF_NAME" = "develop" ]; then
APP_DIR="/home/lars/docker/kairo-dev"
fi
cd "$APP_DIR/frontend"
npm install
npm run build
echo "✓ Frontend build OK"
k6-health-baseline:
name: k6 /health Baseline
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
compose-smoke:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
env:
E2E_TARGET_URL: https://dev.kairo.jinkendo.de
COMPOSE_PROJECT_NAME: kairo-ci-smoke
DB_PASSWORD: ci_smoke_password
KAIRO_BACKEND_PORT: 18197
KAIRO_FRONTEND_PORT: 13197
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
id: e2e
- name: Isolierter Stack (CI-Ports, kein Konflikt mit kairo-dev)
run: |
set -e
COMPOSE="docker-compose.dev-env.yml"
API_PORT="${KAIRO_BACKEND_PORT}"
UI_PORT="${KAIRO_FRONTEND_PORT}"
echo "compose-smoke auf localhost:${API_PORT} / ${UI_PORT}"
docker compose -f "$COMPOSE" up -d --build --wait
curl -sf "http://localhost:${API_PORT}/api/health"
curl -sf "http://localhost:${UI_PORT}/api/health"
docker compose -f "$COMPOSE" exec -T backend pip install -q -r requirements-dev.txt
docker compose -f "$COMPOSE" exec -T backend python -m pytest tests -ra -vv --tb=short
docker compose -f "$COMPOSE" down -v
echo "✓ compose-smoke OK"
k6-health-baseline:
name: k6 /api/health Baseline
needs: [pytest-backend]
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: k6-Ziel wählen
id: k6
run: |
EVENT="${{ github.event_name }}"
WF_NAME="${{ github.event.workflow_run.name }}"
DEV_BASE="${{ env.E2E_TARGET_URL }}"
if [ "$EVENT" = "workflow_run" ] && [ "$WF_NAME" = "Deploy Production" ]; then
echo "mode=prod" >> $GITHUB_OUTPUT
echo "base_url=https://kairo.jinkendo.de" >> $GITHUB_OUTPUT
echo "→ k6 gegen Prod-Basis."
echo "base_url=http://localhost:8004" >> $GITHUB_OUTPUT
else
echo "mode=dev" >> $GITHUB_OUTPUT
echo "base_url=${DEV_BASE}" >> $GITHUB_OUTPUT
echo "→ k6 gegen Dev (${DEV_BASE})."
echo "base_url=http://localhost:8097" >> $GITHUB_OUTPUT
fi
- name: Dev /health abwarten
if: ${{ steps.e2e.outputs.mode == 'dev' }}
- name: /api/health abwarten
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
echo "Warte auf $BASE/health …"
BASE="${{ steps.k6.outputs.base_url }}"
for i in $(seq 1 90); do
if curl -sf "$BASE/health" >/dev/null 2>&1; then
if curl -sf "$BASE/api/health" >/dev/null 2>&1; then
echo "Health OK (Versuch $i)"
exit 0
fi
sleep 2
done
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
curl -v "$BASE/health" || true
exit 1
- name: Prod /health abwarten
if: ${{ steps.e2e.outputs.mode == 'prod' }}
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
echo "Warte auf $BASE/health …"
for i in $(seq 1 60); do
if curl -sf "$BASE/health" >/dev/null 2>&1; then
echo "Health OK (Versuch $i)"
exit 0
fi
sleep 5
done
echo "Timeout: Prod /health nicht erreichbar"
curl -v "$BASE/health" || true
curl -v "$BASE/api/health" || true
exit 1
- name: Install k6
@ -175,26 +156,26 @@ jobs:
aarch64|arm64) K6_ARCH=arm64 ;;
*) echo "k6: unbekannte Architektur: $ARCH"; exit 1 ;;
esac
echo "Installing k6 ${K6_VER} linux-${K6_ARCH}"
curl -sSL "https://github.com/grafana/k6/releases/download/${K6_VER}/k6-${K6_VER}-linux-${K6_ARCH}.tar.gz" -o /tmp/k6.tgz
tar -xzf /tmp/k6.tgz -C /tmp
sudo mv "/tmp/k6-${K6_VER}-linux-${K6_ARCH}/k6" /usr/local/bin/k6
k6 version
- name: k6 Health-Baseline (parallele /health)
env:
BASE_URL: ${{ steps.e2e.outputs.base_url }}
- name: k6 Health-Baseline
run: |
set -e
echo "k6 gegen BASE_URL=$BASE_URL"
k6 run scripts/load/k6-health-baseline.js
echo "✓ k6 Health-Baseline passed"
BASE_URL="${{ steps.k6.outputs.base_url }}"
if [ -z "$BASE_URL" ]; then
echo "BASE_URL leer — steps.k6.outputs.base_url prüfen"
exit 1
fi
echo "k6 gegen BASE_URL=$BASE_URL (K6_CI=1, quiet)"
BASE_URL="$BASE_URL" K6_CI=1 k6 run --quiet scripts/load/k6-health-baseline.js
echo "✓ k6 Health-Baseline OK"
playwright-tests:
playwright-smoke:
needs: [pytest-backend]
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
env:
E2E_TARGET_URL: https://dev.kairo.jinkendo.de
steps:
- name: Checkout repository
uses: actions/checkout@v4
@ -202,111 +183,36 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: "20"
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
id: e2e
- name: Playwright-Ziel wählen
id: pw
run: |
EVENT="${{ github.event_name }}"
WF_NAME="${{ github.event.workflow_run.name }}"
DEV_BASE="${{ env.E2E_TARGET_URL }}"
if [ "$EVENT" = "workflow_run" ] && [ "$WF_NAME" = "Deploy Production" ]; then
echo "mode=prod" >> $GITHUB_OUTPUT
echo "base_url=https://kairo.jinkendo.de" >> $GITHUB_OUTPUT
echo "→ Prod. Secrets: E2E_PROD_TEST_EMAIL / E2E_PROD_TEST_PASSWORD."
echo "base_url=http://localhost:3004" >> $GITHUB_OUTPUT
else
echo "mode=dev" >> $GITHUB_OUTPUT
echo "base_url=${DEV_BASE}" >> $GITHUB_OUTPUT
echo "→ Deployte Dev-Umgebung (${DEV_BASE}). Secrets: E2E_DEV_TEST_EMAIL / E2E_DEV_TEST_PASSWORD."
echo "base_url=http://localhost:3097" >> $GITHUB_OUTPUT
fi
- name: Dev /health abwarten
if: ${{ steps.e2e.outputs.mode == 'dev' }}
- name: /api/health abwarten
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
echo "Warte auf $BASE/health …"
BASE="${{ steps.pw.outputs.base_url }}"
for i in $(seq 1 90); do
if curl -sf "$BASE/health" >/dev/null 2>&1; then
echo "Health OK (Versuch $i)"
if curl -sf "$BASE/api/health" >/dev/null 2>&1; then
exit 0
fi
sleep 2
done
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
curl -v "$BASE/health" || true
exit 1
- name: Prod /health abwarten
if: ${{ steps.e2e.outputs.mode == 'prod' }}
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
echo "Warte auf $BASE/health …"
for i in $(seq 1 60); do
if curl -sf "$BASE/health" >/dev/null 2>&1; then
echo "Health OK (Versuch $i)"
exit 0
fi
sleep 5
done
echo "Timeout: Prod /health nicht erreichbar"
curl -v "$BASE/health" || true
exit 1
- name: Testnutzer registrieren (Dev, nur wenn möglich)
if: ${{ steps.e2e.outputs.mode == 'dev' }}
env:
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
if [ -z "$E2E_DEV_TEST_EMAIL" ] || [ -z "$E2E_DEV_TEST_PASSWORD" ]; then
echo "(Registrierung übersprungen — Secrets E2E_DEV_* nicht gesetzt.)"
exit 0
fi
curl -sf -X POST "$BASE/api/auth/register" \
-H "Content-Type: application/json" \
-d "{\"email\":\"${E2E_DEV_TEST_EMAIL}\",\"password\":\"${E2E_DEV_TEST_PASSWORD}\",\"name\":\"Playwright CI\"}" \
|| echo "(Register evtl. schon erfolgt oder Limits — Login-Test gilt trotzdem.)"
- name: Install Playwright
run: |
npm ci || npm install
npm install
npx playwright install --with-deps chromium
- name: Run Playwright tests
- name: Run Playwright smoke
env:
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
run: |
set -e
MODE="${{ steps.e2e.outputs.mode }}"
BASE_URL="${{ steps.e2e.outputs.base_url }}"
export PLAYWRIGHT_BASE_URL="$BASE_URL"
if [ "$MODE" = "prod" ]; then
export TEST_EMAIL="${{ secrets.E2E_PROD_TEST_EMAIL }}"
export TEST_PASSWORD="${{ secrets.E2E_PROD_TEST_PASSWORD }}"
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
echo "Fehler: E2E_PROD_TEST_EMAIL und E2E_PROD_TEST_PASSWORD setzen."
exit 1
fi
else
export TEST_EMAIL="$E2E_DEV_TEST_EMAIL"
export TEST_PASSWORD="$E2E_DEV_TEST_PASSWORD"
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
echo "Fehler: E2E_DEV_TEST_EMAIL und E2E_DEV_TEST_PASSWORD setzen (Playwright soll gegen Dev einloggen)."
exit 1
fi
fi
mkdir -p screenshots
npx playwright test
echo "✓ Playwright tests passed"
- name: Upload test screenshots
if: failure()
uses: actions/upload-artifact@v3
with:
name: playwright-screenshots
path: screenshots/
retention-days: 7
PLAYWRIGHT_BASE_URL: ${{ steps.pw.outputs.base_url }}
run: npx playwright test tests/smoke-health.spec.js

1
.gitignore vendored
View File

@ -59,6 +59,7 @@ coverage/
# Temp
tmp/
*.tmp
*_alt.md
# Claude: nur ausgewählte Bereiche versionieren
.claude/**

143
CLAUDE.md
View File

@ -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 (Backend, Frontend minimal, Tests, Deploy).

134
README.md
View File

@ -1,63 +1,111 @@
# 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
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`
## Arbeitsmodus
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/
```
Diese Dokumente sind Referenzen, kein direkter Sprint-Scope.
Verbindlich ist nur, was über das Kairo Sprint-0 Principle Gate oder eine Architecture Decision übernommen wurde.
## Deployment & CI
Zwei getrennte Gitea-Workflows (wie Shinkan):
| Workflow | Branch |
|----------|--------|
| **Deploy Development** | `develop` |
| **Deploy Production** | `main` |
| **Test Suite** | nach Deploy + bei Push/PR `develop` |
Details: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)
## Local Development
Voraussetzungen: Docker + Docker Compose.
```bash
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git
cd Kairo-Jinkendo
cp .env.example .env
# .env anpassen
git checkout develop
# Development
# Dev-Stack (PostgreSQL + Backend + Frontend)
docker compose -f docker-compose.dev-env.yml up --build
# Production (lokal)
docker compose up --build
# Health prüfen
curl http://localhost:8097/api/health
curl http://localhost:3097/api/health
# Tests im Backend-Container
docker compose -f docker-compose.dev-env.yml exec backend pip install -r requirements-dev.txt
docker compose -f docker-compose.dev-env.yml exec backend python -m pytest tests -ra -vv
```
Frontend (Dev): http://localhost:3097
Backend (Dev): http://localhost:8097
UI: http://localhost:3097 · API: http://localhost:8097
## Server-Einrichtung
## AP0.1 Stand Projektgrundlage
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
| Bereich | Status |
|---------|--------|
| Git + Gitea (`develop` / `main`) | erledigt |
| Docker Compose (Prod + Dev) | erledigt |
| Backend (FastAPI, Migrationen, `/api/health`) | erledigt |
| Frontend minimal (React + nginx Proxy) | erledigt |
| pytest (Health + Migrationen) | erledigt |
| Gitea Actions (Deploy + Test) | erledigt |

16
backend/Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y postgresql-client \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
ENV PIP_DEFAULT_TIMEOUT=120
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@ -1,14 +0,0 @@
# Backend
FastAPI-Anwendung — noch anzulegen.
Erwartete Struktur (analog shinkan-jinkendo):
```
backend/
├── Dockerfile
├── main.py
├── requirements.txt
├── migrations/
└── routers/
```

38
backend/db.py Normal file
View File

@ -0,0 +1,38 @@
"""PostgreSQL connection helpers."""
import os
import psycopg2
from psycopg2.extensions import connection
def db_params() -> dict[str, str]:
return {
"host": os.getenv("DB_HOST", "localhost"),
"port": os.getenv("DB_PORT", "5432"),
"dbname": os.getenv("DB_NAME", "kairo_dev"),
"user": os.getenv("DB_USER", "kairo_dev"),
"password": os.getenv("DB_PASSWORD", "dev_password"),
}
def get_connection() -> connection:
p = db_params()
return psycopg2.connect(
host=p["host"],
port=p["port"],
database=p["dbname"],
user=p["user"],
password=p["password"],
)
def check_db() -> bool:
conn = get_connection()
try:
with conn.cursor() as cur:
cur.execute("SELECT 1")
cur.fetchone()
return True
finally:
conn.close()

71
backend/main.py Normal file
View File

@ -0,0 +1,71 @@
"""Jinkendo Kairo — FastAPI application entry point."""
from __future__ import annotations
import os
import sys
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from db import check_db
from version import APP_NAME, APP_VERSION, DB_SCHEMA_VERSION
if os.getenv("SKIP_DB_MIGRATE", "").strip().lower() in ("1", "true", "yes"):
print("[SKIP_DB_MIGRATE] Migrationen übersprungen")
else:
import run_migrations
exit_code = run_migrations.main()
if exit_code != 0:
print(f"[FAIL] Migrationen fehlgeschlagen (Exit {exit_code})")
sys.exit(exit_code)
allowed_origins = [
origin.strip()
for origin in os.getenv("ALLOWED_ORIGINS", "http://localhost:3097").split(",")
if origin.strip()
]
app = FastAPI(
title="Jinkendo Kairo",
version=APP_VERSION,
docs_url="/api/docs" if os.getenv("ENVIRONMENT", "development") != "production" else None,
redoc_url=None,
)
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/health")
def api_health():
db_status = "ok"
try:
if not check_db():
db_status = "error"
except Exception:
db_status = "error"
status = "ok" if db_status == "ok" else "degraded"
return {
"status": status,
"app": APP_NAME,
"db": db_status,
"version": APP_VERSION,
"schema": DB_SCHEMA_VERSION,
}
@app.get("/api/version")
def api_version():
return {
"app": APP_NAME,
"version": APP_VERSION,
"schema": DB_SCHEMA_VERSION,
}

View File

@ -0,0 +1,12 @@
-- AP0.1: minimale Schema-Vorbereitung (keine Tenant/User/Actor-Fachlogik)
CREATE TABLE IF NOT EXISTS kairo_app_meta (
id SERIAL PRIMARY KEY,
key VARCHAR(128) UNIQUE NOT NULL,
value TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
INSERT INTO kairo_app_meta (key, value)
VALUES ('schema_phase', 'ap0.1')
ON CONFLICT (key) DO NOTHING;

View File

@ -0,0 +1,3 @@
-r requirements.txt
pytest==8.3.4
httpx==0.27.2

5
backend/requirements.txt Normal file
View File

@ -0,0 +1,5 @@
fastapi==0.111.0
uvicorn[standard]==0.29.0
psycopg2-binary==2.9.9
sqlparse>=0.5.0
pydantic==2.7.1

198
backend/run_migrations.py Normal file
View File

@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""Apply numbered SQL migrations with schema_migrations tracking."""
from __future__ import annotations
import os
import re
import shutil
import subprocess
import sys
import time
from typing import List, Tuple
import psycopg2
import sqlparse
from db import db_params, get_connection
_LEADING_DIGITS = re.compile(r"^(\d+)")
def init_migrations_table(conn) -> None:
with conn.cursor() as cur:
cur.execute(
"""
CREATE TABLE IF NOT EXISTS schema_migrations (
id SERIAL PRIMARY KEY,
migration VARCHAR(255) UNIQUE NOT NULL,
executed_at TIMESTAMP DEFAULT NOW()
)
"""
)
conn.commit()
def _migration_sort_key(stem: str) -> Tuple[int, str]:
match = _LEADING_DIGITS.match(stem)
return (int(match.group(1)) if match else 0, stem)
def migration_files(migrations_dir: str) -> List[Tuple[str, str]]:
rows: List[Tuple[str, str]] = []
for filename in os.listdir(migrations_dir):
if not filename.endswith(".sql") or not filename[0].isdigit():
continue
stem = filename[:-4]
rows.append((stem, os.path.join(migrations_dir, filename)))
rows.sort(key=lambda item: _migration_sort_key(item[0]))
return rows
def executed_migrations(conn) -> set[str]:
with conn.cursor() as cur:
cur.execute("SELECT migration FROM schema_migrations")
return {row[0] for row in cur.fetchall()}
def pending_migrations(conn, migrations_dir: str) -> List[Tuple[str, str]]:
done = executed_migrations(conn)
return [(name, path) for name, path in migration_files(migrations_dir) if name not in done]
def _split_statements(sql_text: str) -> List[str]:
parts = sqlparse.split(sql_text.strip())
return [part.strip() for part in parts if part and part.strip()]
def _run_with_psql(filepath: str) -> tuple[bool, str]:
psql = shutil.which("psql")
if not psql:
return False, ""
p = db_params()
env = os.environ.copy()
env["PGPASSWORD"] = str(p["password"])
cmd = [
psql,
"-h",
p["host"],
"-p",
str(p["port"]),
"-U",
p["user"],
"-d",
p["dbname"],
"-v",
"ON_ERROR_STOP=1",
"-1",
"-f",
filepath,
]
proc = subprocess.run(cmd, env=env, capture_output=True, text=True, timeout=7200)
if proc.returncode != 0:
tail = ((proc.stderr or "") + "\n" + (proc.stdout or "")).strip()
return False, tail[:8000] or f"exit {proc.returncode}"
return True, (proc.stdout or "").strip()
def _record_migration(conn, migration_name: str) -> None:
with conn.cursor() as cur:
cur.execute(
"""
INSERT INTO schema_migrations (migration)
VALUES (%s)
ON CONFLICT (migration) DO NOTHING
""",
(migration_name,),
)
def run_migration(conn, migration_name: str, filepath: str) -> bool:
print(f"Running migration: {migration_name}")
try:
if shutil.which("psql"):
ok, diag = _run_with_psql(filepath)
if not ok:
print(f" [FAIL] psql:\n{diag or '(kein Output)'}")
conn.rollback()
return False
else:
with open(filepath, "r", encoding="utf-8") as handle:
body = handle.read()
statements = _split_statements(body)
with conn.cursor() as cur:
for stmt in statements:
cur.execute(stmt)
_record_migration(conn, migration_name)
conn.commit()
print(f" [OK] {migration_name}")
return True
except Exception as exc:
conn.rollback()
print(f" [FAIL] {migration_name}: {exc}")
return False
def connect_with_retry(max_retries: int = 30):
p = db_params()
for attempt in range(max_retries):
try:
conn = get_connection()
conn.autocommit = False
print(f"[OK] Connected to database: {p['dbname']}")
return conn
except psycopg2.OperationalError:
if attempt >= max_retries - 1:
raise
print(f"Waiting for database... ({attempt + 1}/{max_retries})")
time.sleep(2)
def migrations_directory() -> str:
docker_path = "/app/migrations"
if os.path.isdir(docker_path):
return docker_path
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "migrations")
def main() -> int:
print("=" * 60)
print("Jinkendo Kairo — Database Migrations")
print("=" * 60)
migrations_dir = migrations_directory()
if not os.path.isdir(migrations_dir):
print(f"[FAIL] migrations directory missing: {migrations_dir}")
return 1
try:
conn = connect_with_retry()
init_migrations_table(conn)
pending = pending_migrations(conn, migrations_dir)
if not pending:
print("[OK] Keine ausstehenden Migrationen.")
conn.close()
return 0
print(f"{len(pending)} ausstehende Migration(en):")
for name, _ in pending:
print(f" - {name}")
for migration_name, filepath in pending:
if not run_migration(conn, migration_name, filepath):
conn.close()
return 1
conn.close()
print("[OK] Migrationen abgeschlossen.")
return 0
except Exception as exc:
print(f"[FAIL] {exc}")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,30 @@
import os
import pytest
from fastapi.testclient import TestClient
@pytest.fixture()
def client(monkeypatch):
monkeypatch.setenv("SKIP_DB_MIGRATE", "1")
import importlib
import main as main_module
importlib.reload(main_module)
return TestClient(main_module.app)
def test_app_importable():
import main
assert main.app is not None
def test_health_endpoint(client):
response = client.get("/api/health")
assert response.status_code == 200
payload = response.json()
assert payload["app"] == "jinkendo-kairo"
assert payload["status"] in {"ok", "degraded"}
assert payload["db"] in {"ok", "error"}

View File

@ -0,0 +1,57 @@
"""Migration runner tests (require PostgreSQL)."""
import os
from pathlib import Path
import psycopg2
import pytest
import run_migrations
def _db_available() -> bool:
try:
conn = psycopg2.connect(
host=os.getenv("DB_HOST", "localhost"),
port=os.getenv("DB_PORT", "5432"),
dbname=os.getenv("DB_NAME", "kairo_dev"),
user=os.getenv("DB_USER", "kairo_dev"),
password=os.getenv("DB_PASSWORD", "dev_password"),
)
conn.close()
return True
except psycopg2.OperationalError:
return False
pytestmark = pytest.mark.skipif(not _db_available(), reason="PostgreSQL nicht erreichbar")
def test_migration_runner_finds_migrations():
migrations_dir = run_migrations.migrations_directory()
files = run_migrations.migration_files(migrations_dir)
names = [name for name, _ in files]
assert "001_init_core" in names
def test_migration_runner_is_idempotent():
assert run_migrations.main() == 0
assert run_migrations.main() == 0
conn = run_migrations.connect_with_retry(max_retries=5)
executed = run_migrations.executed_migrations(conn)
conn.close()
assert "001_init_core" in executed
def test_core_table_exists():
conn = run_migrations.connect_with_retry(max_retries=5)
with conn.cursor() as cur:
cur.execute(
"""
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = 'kairo_app_meta'
"""
)
assert cur.fetchone()[0] == 1
conn.close()

3
backend/version.py Normal file
View File

@ -0,0 +1,3 @@
APP_VERSION = "0.1.0-ap0.1"
DB_SCHEMA_VERSION = "001"
APP_NAME = "jinkendo-kairo"

View File

@ -1,6 +1,6 @@
# 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.
# Host-Ports: KAIRO_BACKEND_PORT (Default 8097), KAIRO_FRONTEND_PORT (Default 3097).
# CI compose-smoke: 18197/13197 — siehe infra/ci-smoke.env.example
services:
postgres:
@ -11,8 +11,12 @@ services:
POSTGRES_PASSWORD: "${DB_PASSWORD:-dev_password}"
volumes:
- dev-kairo-db-data:/var/lib/postgresql/data
ports:
- "5437:5432"
# Kein Host-Port: DB nur im Compose-Netz (vermeidet Konflikte mit kairo-dev + CI)
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-kairo_dev} -d ${DB_NAME:-kairo_dev}"]
interval: 5s
timeout: 5s
retries: 10
restart: unless-stopped
networks:
- dev-kairo-network
@ -27,32 +31,26 @@ services:
DB_NAME: "${DB_NAME:-kairo_dev}"
DB_USER: "${DB_USER:-kairo_dev}"
DB_PASSWORD: "${DB_PASSWORD:-dev_password}"
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
KAIRO_AI_DEBUG: "${KAIRO_AI_DEBUG:-}"
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT}
SMTP_USER: ${SMTP_USER}
SMTP_PASS: ${SMTP_PASS}
SMTP_FROM: ${SMTP_FROM}
APP_URL: "${APP_URL:-https://dev.kairo.jinkendo.de}"
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.kairo.jinkendo.de,http://192.168.2.49:3097}"
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.kairo.jinkendo.de,http://192.168.2.49:3097,http://localhost: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"
- "${KAIRO_BACKEND_PORT:-8097}:8000"
depends_on:
- postgres
postgres:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"python",
"-c",
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health')",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
restart: unless-stopped
networks:
- dev-kairo-network
@ -64,9 +62,10 @@ services:
args:
VITE_API_URL: ""
ports:
- "3097:80"
- "${KAIRO_FRONTEND_PORT:-3097}:80"
depends_on:
- backend
backend:
condition: service_healthy
restart: unless-stopped
networks:
- dev-kairo-network

View File

@ -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
@ -5,11 +7,16 @@ services:
environment:
POSTGRES_DB: "${DB_NAME:-kairo}"
POSTGRES_USER: "${DB_USER:-kairo_user}"
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_PASSWORD: "${DB_PASSWORD:-change_me}"
volumes:
- kairo-db-data:/var/lib/postgresql/data
ports:
- "127.0.0.1:5436:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-kairo_user} -d ${DB_NAME:-kairo}"]
interval: 5s
timeout: 5s
retries: 10
restart: unless-stopped
networks:
- kairo-network
@ -22,39 +29,29 @@ services:
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: kairo
DB_USER: kairo_user
DB_PASSWORD: ${DB_PASSWORD}
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
KAIRO_AI_DEBUG: "${KAIRO_AI_DEBUG:-}"
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT}
SMTP_USER: ${SMTP_USER}
SMTP_PASS: ${SMTP_PASS}
SMTP_FROM: ${SMTP_FROM}
SMTP_SSL: ${SMTP_SSL:-}
SMTP_STARTTLS: ${SMTP_STARTTLS:-}
AUTO_ADMIN_FIRST_USER: "${AUTO_ADMIN_FIRST_USER:-true}"
ADMIN_BOOTSTRAP_EMAILS: "${ADMIN_BOOTSTRAP_EMAILS:-}"
DB_NAME: "${DB_NAME:-kairo}"
DB_USER: "${DB_USER:-kairo_user}"
DB_PASSWORD: "${DB_PASSWORD:-change_me}"
APP_URL: "${APP_URL:-https://kairo.jinkendo.de}"
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"
- "${KAIRO_BACKEND_PORT:-8004}:8000"
depends_on:
- postgres
postgres:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"python",
"-c",
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health')",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
restart: unless-stopped
networks:
- kairo-network
@ -67,9 +64,10 @@ services:
VITE_API_URL: ""
container_name: kairo-ui
ports:
- "3004:80"
- "${KAIRO_FRONTEND_PORT:-3004}:80"
depends_on:
- backend
backend:
condition: service_healthy
restart: unless-stopped
networks:
- kairo-network

View File

@ -1,6 +1,6 @@
# Deployment Kairo Jinkendo
**Stand:** 2026-07-04
**Stand:** 2026-07-04 (AP0.1)
**Server:** Raspberry Pi 5 (`192.168.2.49`) — gleicher Host wie Shinkan/Mitai
**Runner:** Gitea Actions (`/home/lars/gitea-runner/`)
@ -14,90 +14,65 @@
| **Server-Verzeichnis** | `/home/lars/docker/kairo` | `/home/lars/docker/kairo-dev` |
| **Frontend-Port** | 3004 | 3097 |
| **Backend-Port** | 8004 | 8097 |
| **PostgreSQL (localhost)** | 5436 | 5437 |
| **PostgreSQL** | nur im Container-Netz (Prod optional localhost:5436) | nur im Container-Netz |
| **Domain** | kairo.jinkendo.de | dev.kairo.jinkendo.de |
| **Medien-Host-Pfad** | `/kairo-media` | `/kairo-media/dev` |
**Familien-Referenz (bereits belegt):**
| App | Prod UI/API | Dev UI/API |
|-----|-------------|------------|
| Mitai | 3002 / 8002 | 3099 / 8099 |
| Shinkan | 3003 / 8003 | 3098 / 8098 |
| **Kairo** | **3004 / 8004** | **3097 / 8097** |
---
## Einmalige Server-Einrichtung
Auf dem Pi als User `lars` ausführen:
```bash
# Verzeichnisse anlegen
mkdir -p /home/lars/docker/kairo
mkdir -p /home/lars/docker/kairo-dev
mkdir -p /kairo-media /kairo-media/dev
mkdir -p /home/lars/docker/kairo /home/lars/docker/kairo-dev
# Production
cd /home/lars/docker/kairo
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
git checkout main
cp .env.example .env
# .env bearbeiten (DB_PASSWORD, SMTP, OPENROUTER, …)
# Development
cd /home/lars/docker/kairo-dev
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
git checkout develop
cp .env.example .env
# .env mit Dev-Werten bearbeiten (siehe Kommentare in .env.example)
```
# DB_PASSWORD setzen
> **Hinweis:** Erstes `docker compose up` funktioniert erst, wenn `backend/` und `frontend/` mit Dockerfiles vorhanden sind.
cd /home/lars/docker/kairo
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
git checkout main
cp .env.example .env
# Prod-DB_PASSWORD setzen
```
---
## Reverse Proxy (Synology / Fritz!Box)
## Gitea Actions Checkliste
Analog zu Shinkan — neue Hostnamen im Proxy eintragen:
1. **Actions aktiviert** (Repository → Settings → Actions)
2. **Pi-Runner registriert** — derselbe wie Shinkan/Mitai (`ubuntu-latest`)
3. Push `develop``deploy-dev.yml``curl http://localhost:8097/api/health`
4. Merge `main``deploy-prod.yml``curl http://localhost:8004/api/health`
5. Nach Deploy: `test.yml` (workflow_run) — pytest, k6, Playwright smoke gegen **localhost**
| Workflow | Trigger | Zweck |
|----------|---------|--------|
| `deploy-dev.yml` | Push `develop` | Deploy nach `/home/lars/docker/kairo-dev` |
| `deploy-prod.yml` | Push `main` | Deploy nach `/home/lars/docker/kairo` |
| `test.yml` | Push/PR `develop`, nach Deploy | lint, pytest, k6, Playwright (Frontend-Build läuft im Deploy via Docker) |
| `test.yml``compose-smoke` | Pull Request | Eigener Stack auf **CI-Ports 18197/13197** |
Struktur wie Shinkan: **Deploy** und **Test Suite** sind getrennte Workflows in Gitea.
Bei Push auf `develop` starten Deploy und Test Suite parallel; pytest wartet auf den Backend-Container der deployten Instanz (Ports 8097/3097). Nach Deploy Production feuert `workflow_run` die Prod-Tests (Ports 8004/3004).
---
## Medien (optional)
Aktuell keine Medien-Speicherung. Bei Bedarf: NAS-Mount + `docker-compose.override.yml` (siehe frühere Doku-Version im Git-Verlauf).
---
## Reverse Proxy (optional)
| Hostname | Ziel (Pi) |
|----------|-----------|
| `kairo.jinkendo.de` | `http://192.168.2.49:3004` |
| `dev.kairo.jinkendo.de` | `http://192.168.2.49:3097` |
SSL/TLS wie bei den Schwester-Apps (Let's Encrypt über Synology).
---
## Gitea Actions
Workflows unter `.gitea/workflows/`:
| Workflow | Trigger | Aktion |
|----------|---------|--------|
| `deploy-dev.yml` | Push auf `develop` | Build + Deploy nach `kairo-dev/` |
| `deploy-prod.yml` | Push auf `main` | Build + Deploy nach `kairo/` |
| `test.yml` | PR/Push `develop`, nach Deploy | pytest, Lint, Frontend-Build, k6, Playwright |
Health-Checks nach Deploy:
- Dev: `curl http://localhost:8097/api/version` und `http://localhost:3097/api/version`
- Prod: `curl http://localhost:8004/api/version` und `http://localhost:3004/api/version`
---
## Gitea Secrets (für E2E-Tests)
In Gitea unter Repository → Settings → Actions → Secrets (analog Shinkan):
| Secret | Verwendung |
|--------|------------|
| `E2E_DEV_TEST_EMAIL` | Playwright Dev-Login |
| `E2E_DEV_TEST_PASSWORD` | Playwright Dev-Login |
| `E2E_PROD_TEST_EMAIL` | Playwright Prod-Login |
| `E2E_PROD_TEST_PASSWORD` | Playwright Prod-Login |
---
## Manuelles Deploy
@ -116,12 +91,4 @@ docker compose build --no-cache
docker compose up -d
```
---
## Nächster Schritt (App-Code)
1. `backend/` mit FastAPI-Skeleton (main.py, Dockerfile, migrations/)
2. `frontend/` mit React/Vite-Skeleton (Dockerfile, nginx.conf)
3. Erster Push auf `develop` → Auto-Deploy Dev
4. Reverse-Proxy-Einträge für Domains aktivieren
5. Gitea E2E-Secrets setzen
Health: `curl http://localhost:8097/api/health` (Dev) bzw. `http://localhost:8004/api/health` (Prod)

View File

@ -0,0 +1,38 @@
# Architecture Decision Proposal
Status: Entwurf
Stand: YYYY-MM-DD
Autor:
---
## Problem
Was ist die konkrete Entscheidungssituation?
## Betroffene Regel
Welches verbindliche Dokument oder Prinzip (Gate, Foundation, Spec) ist betroffen?
## Optionen
| Option | Kurzbeschreibung | Pro | Contra |
|--------|------------------|-----|--------|
| A | | | |
| B | | | |
## Empfehlung
Welche Option und warum?
## Risiko
Was kann schiefgehen?
## Rückbaubarkeit
Wie lässt sich die Entscheidung rückgängig machen oder später verfeinern?
## Auswirkung auf Sprint 0
Betrifft das den Sprint-0-Scope oder nur spätere Arbeitspakete?

View File

@ -0,0 +1,200 @@
# Jinkendo Foundation
## Minimum Viable Foundation v0.2
Status: Arbeitsfassung
Stand: 2026-07-04
Zweck: Minimale produktübergreifende Grundlage, soweit sie für Jinkendo Kairo Sprint 0 zwingend relevant ist.
---
## 1. Zweck
Diese Minimum Viable Foundation beschreibt nur die produktübergreifenden Prinzipien, die Kairo von Anfang an korrekt berücksichtigen muss.
Sie ist keine vollständige Zielarchitektur der Jinkendo-Produktfamilie.
Sie erzwingt keine Konvergenz von Mitai und Shinkan.
Sie verhindert nur, dass Kairo grundlegende Plattformkonzepte falsch oder isoliert implementiert.
---
## 2. Geltungsbereich
Gilt für Kairo Sprint 0 und spätere neue Jinkendo-Produkte, soweit dieselben Prinzipien relevant sind.
Nicht Gegenstand:
- Umbau von Mitai
- Umbau von Shinkan
- zentrale Familienarchitektur
- Billing
- SSO
- vollständige Shared Packages
---
## 3. Drift-Schutz
Ein Prinzip wird nur Sprint-0-verbindlich, wenn alle drei Fragen mit „Ja“ beantwortet werden:
1. Verhindert es später teures strukturelles Refactoring in Kairo?
2. Ist es für Sprint 0 zwingend erforderlich?
3. Kann es minimal umgesetzt werden, ohne Kairo zu überladen?
Alles andere kommt ins Architektur-Backlog.
---
## 4. Verbindliche Foundation-Prinzipien für Kairo Sprint 0
### F-01 Tenant-first
Kairo ist mandantenfähig zu modellieren.
Alle fachlichen Objekte benötigen Tenant-Bezug oder einen expliziten globalen Scope.
### F-02 Actor-first
Operative Verantwortung wird Actors zugeordnet.
Actors können Menschen, KI-Agenten, Arbeitsgruppen oder externe Systeme sein.
### F-03 TenantContext pro Request
Jeder geschützte Request muss einen aufgelösten Kontext besitzen:
- User
- Tenant
- Actor
- Rollen
- Capabilities
### F-04 Auth, Capability, Feature und Governance trennen
Kairo vermischt nicht:
- Auth: Wer ist eingeloggt?
- Capability: Darf diese Funktion genutzt werden?
- Feature: Welche Produktfunktion ist betroffen?
- Governance: Darf dieses Objekt gesehen oder verändert werden?
Feature-Limits und Billing werden nur vorbereitet, nicht in Sprint 0 vollständig gebaut.
### F-05 Registry-first
Neue steuerungsrelevante Funktionen registrieren sich an zentralen Registries.
Mindestens:
- Rights / Capability Registry
- Feature Registry
- Prompt Registry
- Configuration Registry
### F-06 Prompts nicht hardcoden
Produktive interne Prompts liegen nicht hart im Code.
Sprint 0 benötigt:
- Prompt Template
- Prompt Version
- Placeholder
- Kontextart
- Admin-Scope
### F-07 Platzhalter kontrollieren
Platzhalter sind typisiert, dokumentiert und werden validiert.
Fehlende Pflichtplatzhalter führen zu kontrollierten Fehlern.
### F-08 Keine hardcodierte fachliche Konfiguration
Nicht hardcodieren:
- Rollen
- Rechte
- Prompts
- fachliche Statuslisten
- Feature-Flags mit fachlicher Bedeutung
- Workflowparameter
- AI-Modellzuordnung
Technische Defaults sind erlaubt, müssen aber dokumentiert und später überschreibbar sein.
### F-09 Audit für Admin- und Agentenaktionen
Administrative und agentenbezogene Aktionen müssen auditierbar sein.
Sprint 0 benötigt ein einfaches AuditLog-Modell.
### F-10 Nummerierte Migrationen und Fail-Fast-Startup
Schemaänderungen laufen über nummerierte Migrationen.
Fehlgeschlagene Migrationen dürfen den App-Start blockieren.
---
## 5. Bewusst nicht Sprint-0-verbindlich
Nicht in Sprint 0:
- vollständige Entitlement-/Billing-Engine
- Usage-Zähler
- Tiers
- Pläne
- Coupons
- SSO
- vollständige Mitai Prompt Engine
- Shinkan Access Layer mit Vereinslogik
- Widget Dashboard
- Data Layer nach Mitai-Vorbild
- Import-Framework
- Media Assets
- Content Reports
- Maturity Models
---
## 6. Referenzprinzipien
### Aus Mitai relevant
- Prompt Templates nicht hardcoden
- zentrale Prompt-Ausführung als Zielbild
- Registry-Muster
- Feature-Registry als Konzept
- Migration/Deploy-Standard
- Auth-Session-Basis
### Aus Shinkan relevant
- TenantContext
- Trennung von Portalrolle und fachlicher Rolle
- Rights Registry
- Capabilities vs. Features
- Entitlements-Snapshot als API-Idee
- schmale AI Prompt Runtime als Sprint-0-kompatibles Vorbild
---
## 7. Kairo-spezifische Entscheidung
Kairo übernimmt keine allgemeine Familienarchitektur.
Kairo übernimmt nur:
```text
Tenant + Actor + TenantContext
Capability Registry
Feature Registry minimal
Prompt Registry minimal
Placeholder validiert
Audit
Migration/Deploy
```
Alles andere ist späteres Architektur-Backlog.

View File

@ -0,0 +1,67 @@
# 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`
8. `docs/sprints/Sprint0_AP0_1_Project_Setup_Assignment_v0.1.md`
## Hilfsdokumente
- `docs/architecture/ARCHITECTURE_DECISION_PROPOSAL_TEMPLATE.md`
- `infra/README.md` — Platzhalter für serverseitige Infra-Ergänzungen
## 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.

View File

@ -0,0 +1,105 @@
# Jinkendo Kairo
## Sprint-0 Principle Gate v0.1
Status: verbindlicher Scope-Filter für Sprint 0
Stand: 2026-07-04
---
## 1. Zweck
Dieses Dokument verhindert Drift.
Es legt fest, welche Prinzipien aus Mitai/Shinkan/Family-Alignment für Kairo Sprint 0 verbindlich sind und welche ausdrücklich nicht umgesetzt werden.
---
## 2. Entscheidungsregel
Ein Prinzip ist nur dann Sprint-0-verbindlich, wenn alle drei Fragen mit Ja beantwortet werden:
1. Verhindert es später teures strukturelles Refactoring?
2. Ist es für Sprint 0 zwingend erforderlich?
3. Kann es minimal umgesetzt werden, ohne Kairo zu überladen?
---
## 3. Sprint-0-verbindliche Prinzipien
| ID | Prinzip | Sprint-0-Konsequenz |
|---|---|---|
| G-01 | Tenant-first | Tenant ist Kernobjekt; keine Single-User-Architektur |
| G-02 | Actor-first | Assignments gehen an Actors, nicht direkt an User |
| G-03 | TenantContext pro Request | Auth-Kontext wird zentral aufgelöst |
| G-04 | Auth / Capability / Feature / Governance trennen | Keine Vermischung von Login, Rollen, Limits und Objektzugriff |
| G-05 | Rights Registry | Rechte/Capabilities werden registriert, nicht verstreut hardcodiert |
| G-06 | Feature Registry minimal | Features sind bekannt und benannt, auch wenn Limits später kommen |
| G-07 | Prompt Registry minimal | Prompts sind administrierbare Templates, nicht Code-Strings |
| G-08 | Platzhalter validieren | Pflichtplatzhalter dürfen nicht stillschweigend fehlen |
| G-09 | Config statt Hardcoding | Fachliche Konfiguration nicht im Code verdrahten |
| G-10 | Audit | Admin- und Agentenaktionen werden protokolliert |
| G-11 | Migration Standard | Nummerierte SQL-Migrationen + Tracking |
| G-12 | Fail-Fast Startup | App startet nicht mit fehlerhaftem Schema |
---
## 4. Nur vorbereiten, nicht vollständig bauen
| Thema | Sprint-0-Umfang |
|---|---|
| Entitlements | Minimaler `/me/entitlements` Snapshot: tenant, actor, roles, capabilities |
| Feature Limits | Datenmodell optional vorbereiten; keine Usage-Zähler |
| Prompt Engine | Templates, Versionen, Platzhalter; keine Workflows/Pipelines |
| Admin UI | Minimal prüfbar; keine ausgereifte Oberfläche |
| MCP | Schnittstellen später; keine MCP-Implementierung in Sprint 0 |
| AI Agenten | Actor Type Agent vorbereiten; keine vollständige Agentensteuerung |
---
## 5. Explizit nicht Sprint 0
- Billing
- Tiers
- Pläne
- Coupons
- Usage-Zähler
- SSO
- Mitai Data Layer
- Mitai Widget Dashboard
- Universal Import
- Shinkan Exercise Catalog
- Shinkan Training Planning
- Shinkan Media Assets
- Shinkan Content Reports
- Shinkan Maturity Models
- vollständige Familien-Konvergenz
- Lebensmanager-/Seichō-Fachlogik
---
## 6. Review-Regel
Jeder neue Vorschlag im Sprint wird mit einer Kategorie versehen:
```text
A Sprint-0-verbindlich
B Architektur-Backlog
C Produktspezifisch / ignorieren
```
Nur Kategorie A darf in Sprint 0 umgesetzt werden.
---
## 7. Abweichungsregel
Ein Coding-Agent darf nicht eigenmächtig von diesem Gate abweichen.
Bei notwendiger Abweichung erstellt er ein Architecture Decision Proposal mit:
- Problem
- betroffene Regel
- vorgeschlagene Änderung
- Risiko
- Alternative
- Empfehlung

View File

@ -0,0 +1,448 @@
# Jinkendo Kairo
## Product Spec v0.2
Status: Arbeitsfassung
Stand: 2026-07-04
Zweck: Konsolidierte Produktspezifikation für den Start des neuen Kairo-Repositories.
---
## 1. Produktidentität
**Jinkendo Kairo** ist der operative Program Director der Jinkendo-Produktfamilie.
Kairo steuert Vorhaben, Programme, Projekte, Meilensteine, Maßnahmen, Backlogs, Reviews, Nachweise und die Zusammenarbeit von Menschen, Arbeitsgruppen und KI-Agenten.
**Leitfrage:**
> Welcher nächste Schritt bringt ein Vorhaben aktuell am wirkungsvollsten voran?
---
## 2. Produktgrenze
Kairo ist kein klassischer Projektmanager, keine reine ToDo-App und kein reines Softwareentwicklungstool.
Kairo ist auch kein Lebenssinn-, Identitäts- oder Gesundheitsprodukt.
### Kairo verantwortet
- Vorhaben
- Programme
- Projekte
- Meilensteine
- Maßnahmen
- Backlog Items
- Nachweise
- Reviews
- Entscheidungen
- Blocker
- Actor-Zuweisungen
- Arbeitsgruppen
- KI-Agenten
- operative Priorisierung
- Umsetzungstransparenz
### Kairo verantwortet nicht
- persönliche Identität
- Lebensvisionen
- persönliche Entwicklungsmodelle
- Gesundheitsdaten
- Trainingsfachlogik
- Wissensmanagement
- Ernährungslogik
- Skill- oder Reifegradmodelle aus Shinkan
Diese Domänen gehören zu anderen Jinkendo-Produkten wie mindnet, Mitai, Shinkan und perspektivisch Seichō.
---
## 3. Abgrenzung zu Seichō
**Seichō** verantwortet persönliche Entwicklung.
**Kairo** verantwortet operative Umsetzung.
Beispiel:
- Seichō: „Ich möchte als Vater präsenter werden.“
- Kairo: „Welche konkreten Vorhaben, Meilensteine und Maßnahmen setzen das um?“
Kairo darf persönliche Entwicklungsziele aus Seichō operativ unterstützen, aber nicht die persönliche Entwicklungslogik selbst übernehmen.
---
## 4. Stable Core und Agile Edge
### Stable Core
Der Stable Core wird nur bewusst und selten geändert.
Er umfasst:
- Fachsprache
- Kernobjekte
- Produktgrenzen
- Actor-Modell
- Tenant-Modell
- Backlog-Modell
- Meilensteinmodell
- Rechte-/Capability-Grundmodell
- Prompt-/Registry-Grundmodell
- Änderungsregeln
- Agentenregeln
### Agile Edge
Der Agile Edge darf iterativ wachsen.
Er umfasst:
- UI
- Dashboards
- Reports
- MCP-Tools
- Integrationen
- Automationen
- Priorisierungsstrategien
- Visualisierungen
- Komfortfunktionen
---
## 5. Ubiquitous Language
| Begriff | Bedeutung |
|---|---|
| Vision | Langfristig gewünschter Zustand; primär außerhalb Kairo |
| Vorhaben | Aktiv verfolgte Veränderung oder angestrebtes Ergebnis |
| Programm | Bündel zusammengehöriger Projekte |
| Projekt | Zeitlich begrenzte Ergebniserzeugung |
| Meilenstein | Überprüfbarer Zielzustand, keine Aufgabe |
| Maßnahme | Kleinste operative Arbeitseinheit mit eindeutigem Ergebnis |
| Backlog Item | Noch nicht verbindlich priorisierter Handlungsbedarf |
| Review | Strukturierte Bewertung eines Scopes |
| Nachweis | Beleg für Fortschritt, Abschluss oder Entscheidung |
| Entscheidung | Festgehaltene Auswahl zwischen Optionen |
| Blocker | Hindernis, das Fortschritt verhindert oder stark erschwert |
| Actor | Mensch, KI-Agent, Arbeitsgruppe oder externes System |
| Assignment | Zuweisung einer Maßnahme an einen oder mehrere Actors |
| Tenant | Abgegrenzter Nutzungsraum, z. B. Person, Familie, Team, Organisation |
| TenantContext | Aufgelöster Request-Kontext mit User, Tenant, Actor, Rollen und Capabilities |
| Capability | Funktionale Erlaubnis: „Darf Actor X diese Funktion nutzen?“ |
| Feature | Registrierte Produktfunktion, optional später mit Limit/Kontingent |
| Governance | Objektbezogene Zugriffslogik: „Darf Actor X dieses Objekt sehen/ändern?“ |
Neue Begriffe werden nur eingeführt, wenn bestehende Begriffe fachlich nicht ausreichen.
---
## 6. Grundstruktur
```text
Tenant
└── Actor
└── Vorhaben
├── Programm
│ └── Projekt
│ ├── Meilenstein
│ ├── Maßnahme
│ └── Backlog Item
├── Meilenstein
├── Maßnahme
├── Backlog Item
├── Nachweis
└── Review
```
Nicht jedes Vorhaben benötigt Programme oder Projekte.
Kleine Vorhaben können direkt Meilensteine, Maßnahmen und Backlog Items besitzen.
---
## 7. Kernobjekte
### Tenant
Ein Tenant ist ein abgegrenzter Nutzungsraum.
Beispiele:
- persönlicher Arbeitsraum
- Familie
- Verein
- Team
- Organisation
- Projektverbund
### User
Ein User ist ein authentifizierter Account.
Ein User kann Mitglied mehrerer Tenants sein.
### Actor
Ein Actor ist die operative Zurechnungseinheit.
Actor Types:
- Human
- Agent
- WorkingGroup
- ExternalSystem
Alle Assignments erfolgen an Actors, nicht direkt an User.
### Vorhaben
Ein Vorhaben beschreibt eine aktiv verfolgte Veränderung oder ein angestrebtes Ergebnis.
Es besitzt mindestens:
- Titel
- Zielzustand
- Status
- Priorität
- verantwortliche Actors
- Bezug zu Maßnahmen, Meilensteinen, Backlog und Reviews
### Programm
Ein Programm bündelt zusammengehörige Projekte oder Vorhabenanteile.
Programme sind optional.
### Projekt
Ein Projekt erzeugt ein konkretes Ergebnis innerhalb eines Vorhabens oder Programms.
### Meilenstein
Ein Meilenstein beschreibt einen überprüfbaren Zielzustand.
Er ist keine Aufgabe.
Mögliche Status:
- geplant
- aktiv
- gefährdet
- erreicht
- verschoben
- verworfen
### Maßnahme
Eine Maßnahme ist die kleinste operative Arbeitseinheit.
Mögliche Status:
- offen
- bereit
- in Arbeit
- blockiert
- Review erforderlich
- erledigt
- verworfen
### Backlog Item
Backlog Items entstehen aus Ideen, Bugs, Risiken, Verbesserungen oder Agentenvorschlägen.
Sie verpflichten nicht automatisch zur Umsetzung.
### Nachweis
Nachweise belegen Fortschritt oder Abschluss.
Beispiele:
- Commit
- Dokument
- Link
- Datei
- Messwert
- Foto
- Review-Ergebnis
- externe Bestätigung
### Review
Ein Review bewertet einen Scope und erzeugt ggf. Entscheidungen, Maßnahmen oder Backlog Items.
### Blocker
Ein Blocker verhindert oder gefährdet Fortschritt.
Blocker müssen sichtbar, zugeordnet und reviewbar sein.
---
## 8. Actor- und Assignment-Modell
Actors können Menschen, KI-Agenten, Arbeitsgruppen oder externe Systeme sein.
Unterstützte Assignment-Modi:
- Exclusive: genau ein verantwortlicher Actor
- Collaborative: mehrere Actors arbeiten gemeinsam
- Pool: eine Arbeitsgruppe oder ein Pool kann übernehmen
- Proposed: Zuweisungsvorschlag, noch nicht angenommen
- Auction: Actors bewerben sich oder übernehmen bewusst
Agenten sind keine Sonderfälle außerhalb des Modells.
---
## 9. Multiuser und Mandantenfähigkeit
Multiuser ist Kernfunktion, keine spätere Erweiterung.
Kairo unterstützt:
- private Vorhaben
- Familienvorhaben
- Teamvorhaben
- Organisationsvorhaben
- Arbeitsgruppen
- KI-Agenten innerhalb eines Tenants
- objektbezogene Sichtbarkeit und Bearbeitbarkeit
Jede fachliche Entität ist tenantfähig zu modellieren.
---
## 10. Rechte, Capabilities und Governance
Kairo trennt:
```text
Auth = Wer ist eingeloggt?
Capability = Darf Actor diese Funktion nutzen?
Feature = Welche Produktfunktion ist betroffen?
Governance = Darf Actor dieses konkrete Objekt sehen/ändern?
```
Feature-Limits, Usage-Zähler, Billing, Tiers und Pläne sind nicht Bestandteil von Sprint 0, werden aber im Modell nicht verbaut.
---
## 11. Prompt- und AI-Grundmodell
Kairo nutzt intern keine hardcodierten Produktionsprompts.
Mindestmodell:
- Prompt Templates in DB oder konfigurierbarer Persistenz
- Prompt Versions
- typisierte Platzhalter
- Kontextarten
- Adminbarkeit
- Preview/Testmöglichkeit später
- zentrale Prompt-Ausführung als Fassade
Sprint 0 baut keine komplexe Workflow- oder Pipeline-Engine.
---
## 12. Registry-Grundmodell
Neue Funktionen registrieren sich an zentralen Steuerungsschichten.
Mindestens vorgesehen:
- Rights / Capability Registry
- Feature Registry
- Prompt Registry
- Configuration Registry
- Audit Events
Ziel: keine verstreuten, unsichtbaren Sonderlogiken.
---
## 13. Agentenregeln
Agenten dürfen:
- Kontext anfragen
- nächste Maßnahme abfragen
- Maßnahmen claimen
- Fortschritt melden
- Blocker melden
- Backlog Items vorschlagen
- Nachweise einreichen
- Reviews anfordern
Agenten dürfen nicht ohne Freigabe:
- strategische Ziele ändern
- Vorhaben löschen
- Meilensteine verschieben
- Rechte oder Rollen ändern
- Prompts produktiv ändern
- Admin-Konfiguration ändern
- private Daten anderer Actors auswerten
---
## 14. Änderungsregime
Änderungen werden klassifiziert als:
| Ebene | Bedeutung |
|---|---|
| Operativ | Maßnahmen, Status, Nachweise, Kommentare |
| Taktisch | Prioritäten, Meilensteinzuschnitt, Assignment-Modus |
| Strategisch | Produktgrenze, Stable Core, grundlegende Ziele oder Rechte |
Strategische Änderungen benötigen menschliche Freigabe.
---
## 15. MVP-Zielbild
Der MVP umfasst nach Sprint 0 schrittweise:
- Tenant/User/Actor
- Vorhaben
- Projekte
- Meilensteine
- Maßnahmen
- Backlog
- Assignments
- Arbeitsgruppen
- Agentenstatus
- Reviews
- Nachweise
- Blocker
- Vorschlag der nächsten sinnvollen Maßnahme
- MCP-Minimalintegration für Coding-Agenten
---
## 16. Nicht-Ziele des MVP
Nicht Teil des MVP:
- vollständige Mitai-Integration
- vollständige Shinkan-Integration
- vollständige mindnet-Integration
- vollständige Seichō-Integration
- vollständiges Billing
- zentrales SSO
- komplexe Prompt-Workflows
- KI-Autopilot für strategische Entscheidungen
- vollständiger Lebensmanager
- Gesundheits- oder Trainingsdomänenlogik
---
## 17. Leitregel
Kairo steuert Umsetzung.
Sinn, Identität und persönliche Entwicklung verbleiben außerhalb seiner fachlichen Verantwortung.

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

View File

@ -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 #13 | Auth #12 |
| 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 #15 | Migration #13 |
| 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 #67 | 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 P0P1 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) |

View File

@ -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 23 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 (P1P4) 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 P1P4, Abweichungs-Katalog DEV-0111 |

View File

@ -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** | **mittelhoch** |
| **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 | ✅ |

View File

@ -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` 790, 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** | **mittelhoch** |
| **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 |
|---|-------|--------|
| 16 | … | ✅ |
| 7 | Dashboard Widgets | ✅ dieses Dokument |
| 8 | Navigation / IA | ✅ |
| 9 | Migration & Deploy | ✅ |

View File

@ -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** | **mittelhoch** |
| **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` (E1E5, A1A8, R1R5, C1C4).
---
## 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` |

View File

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

View File

@ -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** | **mittelhoch** |
| **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** | **mittelhoch** |
| **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/ # 001061+ 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 |

View File

@ -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** (67 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 |
|---|-------|--------|
| 17 | … | ✅ |
| 8 | Navigation / IA | ✅ dieses Dokument |
| 9 | Migration & Deploy | ✅ |

View File

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

View File

@ -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.24.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.

View File

@ -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** | **mittelhoch** |
| **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 |
|---|-------|--------|
| 15 | … | ✅ |
| 6 | Universal Import | ✅ dieses Dokument |
| 7 | Dashboard Widgets | ✅ `DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md` |
| 8 | Navigation / IA | ✅ |
| 9 | Migration & Deploy | ✅ |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 115 aus Shinkan-Ist-Stand extrahiert |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 035037 |
| **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)

View File

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

View File

@ -0,0 +1,466 @@
# Jinkendo Kairo
## Dokument 04 Sprint 0 Foundation v0.3
Status: ausführbares Sprint-0-Arbeitspaket
Stand: 2026-07-04
Zweck: Vibe-Coder-fähiger Sprint-0-Scope für das neue Kairo-Repository.
---
## 1. Sprintziel
Sprint 0 schafft das technische und fachliche Fundament für Kairo.
Nach Sprint 0 existiert noch kein vollständiger Program Director.
Aber die Anwendung besitzt die notwendige Plattformstruktur, damit spätere Fachfunktionen nicht falsch aufgebaut werden.
---
## 2. Sprint-Leitfrage
> Ist Kairo von Anfang an mandantenfähig, actor-basiert, registry-basiert, promptfähig, auditierbar und nicht hardcoded?
---
## 3. Verbindliche Leitdokumente
1. `Jinkendo_Kairo_Product_Spec_v0.2.md`
2. `Jinkendo_Foundation_Minimum_Viable_Foundation_v0.2.md`
3. `Kairo_Sprint0_Principle_Gate_v0.1.md`
4. `CLAUDE.md`
5. `.cursor/rules/kairo-architecture.mdc`
---
## 4. Scope
Sprint 0 umfasst:
- Repository und Grundarchitektur
- lokale Entwicklungsumgebung
- Datenbank und Migrationen
- Tenant-Modell
- User-Grundmodell
- Actor-Modell
- TenantMembership
- TenantContext
- Rollen-/Capability-Grundmodell
- Rights / Capability Registry
- minimale Feature Registry
- minimale Prompt Registry
- Prompt Templates
- Prompt Versions
- Placeholder-Modell
- Configuration-Grundmodell
- AuditLog
- minimaler `/api/me/entitlements` Snapshot
- minimale Admin-Prüfbarkeit
- technische Tests
---
## 5. Nicht-Scope
Nicht Teil von Sprint 0:
- Vorhabenverwaltung
- Projekte
- Meilensteine
- Maßnahmen
- Backlog
- MCP-Server
- Program-Director-Logik
- komplexe AI-Workflows
- Prompt-Pipelines
- Billing
- SSO
- Usage-Zähler
- Mitai-/Shinkan-Integration
- Seichō-Lebensmanagerlogik
- produktionsreifes Designsystem
- mobile App
---
## 6. Empfohlener Technologie-Start
Diese Empfehlung ist pragmatisch und darf durch ein Architecture Decision Proposal angepasst werden.
```text
Backend: FastAPI
Frontend: React oder Next.js
DB: PostgreSQL
Migration: nummerierte SQL-Dateien + schema_migrations
Deployment lokal: Docker Compose
Auth Sprint 0: einfache serverseitige Session oder vorbereitete Auth-Fassade
```
Wichtig: Die Technologieentscheidung darf die Architekturprinzipien nicht verletzen.
---
## 7. Kernobjekte Sprint 0
### Tenant
- id
- name
- slug
- status
- created_at
- updated_at
### User
- id
- email
- display_name
- status
- created_at
- updated_at
### TenantMembership
- id
- tenant_id
- user_id
- roles
- status
### Actor
- id
- tenant_id
- actor_type
- display_name
- linked_user_id optional
- status
- capabilities optional
Actor Types:
- Human
- Agent
- WorkingGroup
- ExternalSystem
### Role
- id
- tenant_id optional
- key
- name
- description
- scope
- status
### Capability
- id
- key
- module
- description
- status
### Feature
- id
- key
- module
- description
- status
### PromptTemplate
- id
- tenant_id optional
- feature_key optional
- key
- name
- purpose
- context_kind
- status
- active_version_id
### PromptVersion
- id
- prompt_template_id
- version
- body
- created_by
- created_at
- changelog
### Placeholder
- id
- key
- type
- description
- required
- source
- context_kind optional
### ConfigurationEntry
- id
- tenant_id optional
- key
- value
- scope
- description
### AuditLog
- id
- tenant_id optional
- actor_id optional
- action
- entity_type
- entity_id
- timestamp
- metadata
---
## 8. Minimaler TenantContext
Jeder geschützte Request soll perspektivisch auflösen:
```text
user_id
tenant_id
actor_id
global_roles
tenant_roles
capabilities
```
Sprint 0 darf dies technisch minimal umsetzen, aber die Fassade muss vorhanden sein.
---
## 9. Minimaler Entitlements Snapshot
Endpoint:
```text
GET /api/me/entitlements
```
Mindestantwort:
```json
{
"tenant": {
"tenant_id": "...",
"name": "..."
},
"actor": {
"actor_id": "...",
"actor_type": "Human"
},
"roles": [],
"capabilities": {},
"features": {},
"enforcement": {
"capabilities": "probe",
"features": "probe"
}
}
```
Keine Billing-/Usage-Logik in Sprint 0.
---
## 10. Arbeitspakete
### AP0.1 Projektgrundlage
- Repo-Struktur
- Backend-Grundgerüst
- Frontend-Grundgerüst optional
- Docker Compose
- PostgreSQL
- Migration Runner
- Health Endpoint
- Testbasis
### AP0.2 Tenant, User, Actor
- Tenant
- User
- Membership
- Actor
- WorkingGroup Actor
- Agent Actor
- Seed für lokalen Admin
### AP0.3 Auth, TenantContext, Capabilities
- einfache Auth-Fassade
- TenantContext-Fassade
- Role
- Capability
- Rights Registry
- zentrale Capability-Prüfung
### AP0.4 Prompt, Feature, Config Registry
- Feature Registry
- PromptTemplate
- PromptVersion
- Placeholder
- ConfigurationEntry
- einfaches Prompt Rendering mit Placeholder Validation
### AP0.5 Audit und minimale Admin-Prüfbarkeit
- AuditLog
- Audit bei Admin-/Registry-/Prompt-Änderungen
- minimale Admin-Routen oder Admin-Seite
- `/api/me/entitlements`
---
## 11. User Stories
### S0-01 Projektgrundlage
Als Entwickler möchte ich eine lauffähige Projektstruktur, damit Kairo iterativ erweitert werden kann.
Akzeptanzkriterien:
- Projekt kann lokal gestartet werden.
- Datenbank läuft lokal.
- Migrationen laufen reproduzierbar.
- Health Endpoint antwortet.
- Tests können ausgeführt werden.
### S0-02 Tenant anlegen
Als Platform Admin möchte ich Tenants verwalten, damit abgegrenzte Nutzungsräume entstehen.
Akzeptanzkriterien:
- Tenant kann angelegt werden.
- Tenant kann deaktiviert werden.
- Tenant besitzt eindeutigen Slug.
- Fachliche Entitäten können Tenant-Bezug herstellen.
### S0-03 User und Membership
Als Tenant Admin möchte ich User einem Tenant zuordnen.
Akzeptanzkriterien:
- User kann mehreren Tenants angehören.
- Membership besitzt Rollen.
- Deaktivierte Membership verliert Zugriff.
### S0-04 Actor-Modell
Als System möchte ich Menschen, Agenten und Arbeitsgruppen einheitlich als Actors behandeln.
Akzeptanzkriterien:
- Human Actor kann mit User verknüpft werden.
- Agent Actor kann ohne User existieren.
- WorkingGroup Actor kann angelegt werden.
- Actor ist tenantbezogen.
### S0-05 Capability Registry
Als Entwickler möchte ich Capabilities registrieren, damit Rechte nicht verstreut hardcodiert werden.
Akzeptanzkriterien:
- Capability kann registriert werden.
- Capability besitzt Key, Modul und Beschreibung.
- Registry kann in DB synchronisiert oder persistiert werden.
- Fehlende Capability führt zu kontrolliertem Fehler.
### S0-06 Prompt Registry
Als Superadmin möchte ich Prompt Templates administrieren können.
Akzeptanzkriterien:
- Prompt Template kann angelegt werden.
- Prompt Version kann angelegt werden.
- Aktive Version ist bestimmbar.
- Template kann gerendert werden.
- Pflichtplatzhalter werden validiert.
### S0-07 Audit
Als Betreiber möchte ich kritische Aktionen nachvollziehen können.
Akzeptanzkriterien:
- Änderungen an Tenant, Role, Capability, Feature, Prompt und Config erzeugen Audit Events.
- Audit Event enthält Actor, Tenant, Aktion, Entity und Zeitpunkt.
### S0-08 Entitlements Snapshot
Als Frontend möchte ich einen Berechtigungssnapshot laden.
Akzeptanzkriterien:
- `/api/me/entitlements` liefert Tenant, Actor, Rollen, Capabilities und Features.
- Keine UI muss Rollenlogik selbst ableiten.
---
## 12. Definition of Done Sprint 0
Sprint 0 gilt als abgeschlossen, wenn:
- Anwendung lokal lauffähig ist.
- Migrationen funktionieren.
- Tenant/User/Actor/Membership implementiert sind.
- TenantContext-Fassade existiert.
- Capability Registry existiert.
- Feature Registry minimal existiert.
- PromptTemplate/PromptVersion/Placeholder existieren.
- Prompt Rendering validiert Pflichtplatzhalter.
- AuditLog existiert.
- `/api/me/entitlements` existiert.
- Es gibt Tests für die Kernmodelle.
- README, CLAUDE.md und Cursor-Regel sind aktuell.
- Keine fachlichen Prompts, Rollen oder Rechte sind verstreut hardcodiert.
---
## 13. Verbotene Vereinfachungen
Ein Coding-Agent darf nicht:
- Tenant entfernen
- Actor durch reines User-Modell ersetzen
- Capabilities durch Ad-hoc-Rollenchecks ersetzen
- Prompts hardcoden
- Placeholder-Validation weglassen
- Feature Registry weglassen
- AuditLog weglassen
- Vorhaben/Projektlogik in Sprint 0 vorziehen
- Billing/Usage-Limits in Sprint 0 ausbauen
- Shinkan-Club-Logik oder Mitai-Tier-Logik kopieren
---
## 14. Übergang zu Sprint 1
Sprint 1 startet erst, wenn Sprint 0 das Fundament trägt.
Sprint 1 baut dann:
- Vorhaben
- Programme optional
- Projekte
- erste Übersicht
- Statusmodell
- Basis-Governance für Vorhaben

View File

@ -0,0 +1,167 @@
# Jinkendo Kairo
## Sprint 0 AP0.1 Projektgrundlage v0.1
Status: erster ausführbarer Coding-Auftrag
Stand: 2026-07-04
---
## 1. Auftrag
Erstelle die technische Projektgrundlage für Jinkendo Kairo.
AP0.1 baut noch keine fachliche Kairo-Domäne.
Ziel ist ein lauffähiges, testbares, migrationsfähiges Grundgerüst.
---
## 2. Verbindliche Leitplanken
Vor Umsetzung lesen:
- `docs/product/Jinkendo_Kairo_Product_Spec_v0.2.md`
- `docs/architecture/Kairo_Sprint0_Principle_Gate_v0.1.md`
- `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md`
- `CLAUDE.md`
- `.cursor/rules/kairo-architecture.mdc`
---
## 3. Scope AP0.1
Erstelle:
```text
backend/
frontend/ optional minimal
infra/
scripts/
tests/
```
Mindestumfang:
- Backend-Skeleton
- FastAPI-App oder begründete Alternative
- Health Endpoint
- PostgreSQL-Anbindung
- Docker Compose lokal
- Migrationsverzeichnis
- Migration Runner
- `schema_migrations` Tracking
- erste Migration `001_init_core.sql`
- Testbasis
- README-Abschnitt „Local Development“
---
## 4. Nicht-Scope AP0.1
Nicht implementieren:
- Tenant/User/Actor-Fachlogik über minimale Schema-Vorbereitung hinaus
- vollständige Auth
- Prompt Engine
- Feature Registry
- Capability Registry
- Vorhaben
- Projekte
- Maßnahmen
- Backlog
- MCP
- Billing
- UI-Komplexität
---
## 5. Migrationsanforderungen
Migrationen liegen unter:
```text
backend/migrations/
```
Namensschema:
```text
001_init_core.sql
002_*.sql
003_*.sql
```
Pflicht:
- `schema_migrations` Tracking
- lexikographische Ausführung
- bereits ausgeführte Migrationen überspringen
- Fehler blockiert App-Start oder Setup-Command
- keine ad-hoc DDL in Routern
---
## 6. Health Endpoint
Mindestendpoint:
```text
GET /api/health
```
Antwortbeispiel:
```json
{
"status": "ok",
"app": "jinkendo-kairo",
"db": "ok"
}
```
---
## 7. Tests
Mindestens:
- App importierbar
- Health Endpoint antwortet
- Migration Runner erkennt Migrationen
- Migration Runner trägt ausgeführte Migration ein
- wiederholter Migration Run ist idempotent
---
## 8. Akzeptanzkriterien
AP0.1 ist abgeschlossen, wenn:
- Projekt lokal startbar ist.
- Datenbank lokal startbar ist.
- Migrationen laufen.
- Health Endpoint funktioniert.
- Tests laufen.
- README beschreibt Setup.
- Keine fachliche Sprint-1-Logik wurde vorgezogen.
- Offene technische Entscheidungen sind dokumentiert.
---
## 9. Erwarteter Abschlussbericht
Der Coding-Agent liefert am Ende:
```markdown
## Umgesetzt
## Tests
## Lokaler Start
## Offene Fragen
## Abweichungen von der Spezifikation
## Empfehlung für AP0.2
```

View File

@ -0,0 +1,112 @@
# Jinkendo Kairo
## Sprint 0 Vibe-Coder Handover v0.1
Status: Übergabedokument
Stand: 2026-07-04
---
## 1. Auftrag
Baue nicht sofort den Program Director.
Baue zuerst das Fundament, damit der Program Director später sauber entstehen kann.
Sprint 0 ist erfolgreich, wenn Kairo technisch und fachlich vorbereitet ist für:
- Mandanten
- Actors
- Rechte/Capabilities
- Prompts
- Platzhalter
- Konfiguration
- Audit
- spätere KI-Agenten
---
## 2. Verbindliche Dokumente
Lies in dieser Reihenfolge:
1. `README.md`
2. `docs/product/Jinkendo_Kairo_Product_Spec_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. `CLAUDE.md`
6. `.cursor/rules/kairo-architecture.mdc`
---
## 3. Arbeitsprinzip
Du darfst technische Details ausarbeiten.
Du darfst keine fachlichen Architekturprinzipien ändern.
Wenn eine Änderung notwendig erscheint, erstelle ein Architecture Decision Proposal.
---
## 4. Harte Guardrails
Nicht erlaubt:
- Kairo als Single-User-App bauen
- Tenant weglassen
- Actor durch User ersetzen
- Agenten als Sonderlogik außerhalb Actor-Modell behandeln
- Rollen/Rechte hardcoden
- Prompts hardcoden
- Feature Registry weglassen
- Prompt Registry weglassen
- Placeholder-Validation weglassen
- Audit weglassen
- Vorhaben/Projektlogik vor Sprint-0-Abschluss implementieren
- Billing/Usage-Limits vorziehen
- Mitai- oder Shinkan-Domänenlogik kopieren
---
## 5. Erwartete Arbeitsweise
Für jedes Arbeitspaket:
1. Kurzplan erstellen
2. Implementieren
3. Tests ergänzen
4. README oder relevante Doku aktualisieren
5. Abweichungen dokumentieren
6. Offene Fragen klar markieren
---
## 6. Architecture Decision Proposal Format
```markdown
# Architecture Decision Proposal
## Problem
## Betroffene Regel
## Optionen
## Empfehlung
## Risiko
## Rückbaubarkeit
## Auswirkung auf Sprint 0
```
---
## 7. AP0.1 zuerst
Der erste Auftrag ist ausschließlich AP0.1 Projektgrundlage.
Kein Sprint-0-Gesamtbau.
Kein Vorziehen von AP0.2AP0.5 ohne Review.

15
frontend/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,13 +0,0 @@
# Frontend
React + Vite SPA — noch anzulegen.
Erwartete Struktur (analog shinkan-jinkendo):
```
frontend/
├── Dockerfile
├── nginx.conf
├── package.json
└── src/
```

12
frontend/index.html Normal file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jinkendo Kairo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

22
frontend/nginx.conf Normal file
View File

@ -0,0 +1,22 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
resolver 127.0.0.11 valid=10s ipv6=off;
location ^~ /api/ {
set $docker_backend_svc backend;
proxy_pass http://$docker_backend_svc:8000$request_uri;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
try_files $uri $uri/ /index.html;
}
}

19
frontend/package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "kairo-jinkendo-frontend",
"version": "0.1.0-ap0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3097",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.1.4"
}
}

31
frontend/src/App.jsx Normal file
View File

@ -0,0 +1,31 @@
import { useEffect, useState } from 'react'
export default function App() {
const [health, setHealth] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
fetch('/api/health')
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
})
.then(setHealth)
.catch((err) => setError(err.message))
}, [])
return (
<main className="shell">
<h1>Jinkendo Kairo</h1>
<p>Operativer Program Director Sprint 0 / AP0.1</p>
<section className="card">
<h2>API Health</h2>
{error && <p className="error">Fehler: {error}</p>}
{!error && !health && <p>Lade </p>}
{health && (
<pre>{JSON.stringify(health, null, 2)}</pre>
)}
</section>
</main>
)
}

30
frontend/src/app.css Normal file
View File

@ -0,0 +1,30 @@
:root {
font-family: system-ui, sans-serif;
color: #1a1a1a;
background: #f6f7fb;
}
body {
margin: 0;
}
.shell {
max-width: 720px;
margin: 2rem auto;
padding: 0 1rem;
}
.card {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
}
.error {
color: #b00020;
}
pre {
overflow: auto;
}

10
frontend/src/main.jsx Normal file
View File

@ -0,0 +1,10 @@
import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './app.css'
createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

12
frontend/vite.config.js Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
port: 3097,
proxy: {
'/api': 'http://127.0.0.1:8097',
},
},
})

7
infra/README.md Normal file
View File

@ -0,0 +1,7 @@
# Infra
Deployment- und Server-bezogene Ergänzungen (Reverse-Proxy-Snippets, NAS-Mount-Hinweise, Umgebungs-spezifische Notizen).
**Hauptdoku:** [docs/DEPLOYMENT.md](../docs/DEPLOYMENT.md)
Aktuell leer — bewusst minimal in AP0.1. Docker Compose liegt im Repository-Root.

View File

@ -0,0 +1,7 @@
# CI-Ports für isolierte compose-smoke-Läufe (Pull Requests).
# Deploy kairo-dev nutzt 8097/3097 — keine Überschneidung mit 18197/13197.
COMPOSE_PROJECT_NAME=kairo-ci-smoke
KAIRO_BACKEND_PORT=18197
KAIRO_FRONTEND_PORT=13197
DB_PASSWORD=ci_smoke_password

View File

@ -1,6 +1,6 @@
# k6 Load-Tests (Phase 0)
Parallele `/health`-Requests gegen Dev oder Prod.
Parallele `/api/health`-Requests gegen Dev oder Prod.
## Lokal / CI

View File

@ -1,16 +1,23 @@
/**
* Phase-0-Baseline: parallele GET /health (kein Auth).
* Phase-0-Baseline: parallele GET /api/health (kein Auth).
* BASE_URL optional, z. B. https://dev.kairo.jinkendo.de
*
* K6_CI=1: leichtere Last für Self-hosted Runner (Raspberry Pi).
*/
import http from 'k6/http'
import { check } from 'k6'
const isCi = __ENV.K6_CI === '1' || __ENV.K6_CI === 'true'
const vus = parseInt(__ENV.K6_VUS || (isCi ? '5' : '10'), 10)
const duration = __ENV.K6_DURATION || (isCi ? '15s' : '30s')
const p95Ms = parseInt(__ENV.K6_P95_MS || (isCi ? '5000' : '3000'), 10)
export const options = {
scenarios: {
health: {
executor: 'constant-vus',
vus: 10,
duration: '30s',
vus,
duration,
gracefulStop: '5s',
tags: { scenario: 'health' },
exec: 'health',
@ -18,15 +25,26 @@ export const options = {
},
thresholds: {
http_req_failed: ['rate<0.05'],
'http_req_duration{scenario:health}': ['p(95)<3000'],
'http_req_duration{scenario:health}': [`p(95)<${p95Ms}`],
},
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(90)', 'p(95)'],
}
const BASE = (__ENV.BASE_URL || 'https://dev.kairo.jinkendo.de').replace(/\/$/, '')
export function health() {
const res = http.get(`${BASE}/health`, { tags: { scenario: 'health' } })
const res = http.get(`${BASE}/api/health`, { tags: { scenario: 'health' } })
check(res, {
'health 2xx': (r) => r.status >= 200 && r.status < 300,
})
}
export function handleSummary(data) {
const failed = data.metrics.http_req_failed?.values?.rate ?? 0
const p95 = data.metrics.http_req_duration?.values?.['p(95)'] ?? 0
const lines = [
`k6 summary: BASE=${BASE} vus=${vus} duration=${duration}`,
` http_req_failed=${(failed * 100).toFixed(2)}% p(95)=${p95.toFixed(1)}ms (limit ${p95Ms}ms)`,
]
return { stdout: lines.join('\n') + '\n' }
}

View File

@ -0,0 +1,9 @@
const { test, expect } = require('@playwright/test');
test('GET /api/health liefert jinkendo-kairo', async ({ request }) => {
const response = await request.get('/api/health');
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.app).toBe('jinkendo-kairo');
expect(body.status).toMatch(/ok|degraded/);
});