diff --git a/plans/2026-06-21-system-standards-todo.md b/plans/2026-06-21-system-standards-todo.md new file mode 100644 index 0000000..2c232a4 --- /dev/null +++ b/plans/2026-06-21-system-standards-todo.md @@ -0,0 +1,176 @@ +# BSN System- & Prozess-Standards — TODO + +> **Quelle:** Externes Review-Feedback (21. Juni 2026) +> **Kontext:** Code-Standards (v1.0) decken die Datei-Ebene ab. Diese Liste adressiert die System- und Prozess-Ebene — wo der größere Hebel liegt. + +--- + +## P0 — Sofort (höchste Hebelwirkung) + +### sys-1: Gitea Actions CI-Gate +**Problem:** `pre-commit` ist mit `--no-verify` in 2 Sekunden umgangen — und KI-generierter Code (Cody, Kevin) hat keinen lokalen pre-commit-Pfad. Die eigentliche Durchsetzung muss serverseitig im Repo sitzen. + +**Lösung:** Gitea Actions Workflow, der bei jedem Push ausgeführt wird: +- `ruff check .` (Linting + Sicherheit) +- `ruff format --check .` (Formatierung) +- `mypy .` (Typ-Prüfung, im echten venv) +- `pytest --cov --cov-fail-under=80` (Tests + Coverage) + +**Warum P0:** Ohne serverseitigen Gate sind alle Coding-Standards freiwillig. Mit Gitea Actions ist kein Merge ohne bestandene Checks möglich — unabhängig davon, wer oder was den Code geschrieben hat. + +**Umsetzung:** `.gitea/workflows/quality.yml` im Repo anlegen. + +--- + +### sys-2: Edge-Queue + Idempotenz (Chatbot) +**Problem:** Wenn der WireGuard-Tunnel zur Heim-GPU wackelt oder die Box neu startet, gehen Einreichungen verloren. Telegram/WhatsApp-Webhooks stellen bei Timeout erneut zu → ohne Idempotenz wird dieselbe Einreichung mehrfach verarbeitet. + +**Lösung (3 Komponenten):** +1. **Queue am Hetzner-Edge** — Redis Streams oder NATS, puffert Submissions statt sie synchron an die Heim-GPU durchzureichen +2. **Idempotenz** — Webhook-Signatur/Deduplication-Key vor Verarbeitung prüfen, doppelte Zustellungen erkennen und verwerfen +3. **Webhook-Signaturprüfung** — X-Hub-Signature-256 (WhatsApp) / SHA256-HMAC (Telegram) am Edge validieren, bevor irgendwas in die Queue geht + +**Warum P0:** Einzige Stelle, wo Nutzer direkt zuschauen. „Tunnel down = Datenverlust" → „Tunnel down = Verarbeitung verzögert". Das ist der Unterschied zwischen „funktioniert" und „Produktion". + +**Umsetzung:** Architektur-Skizze + Redis-Integration in den bestehenden Flask-Intake. + +--- + +## P1 — Bald (hohe Hebelwirkung, etwas Aufwand) + +### sys-3: uv + Lockfile +**Problem:** `pip install ruff mypy ...` ohne Lockfile → kein reproduzierbarer Stand. Drei Maschinen (GB10/ARM64, Strix Halo/x86_64, Hetzner/AMD64) mit unterschiedlicher Architektur → „läuft bei mir" ist vorprogrammiert. + +**Lösung:** Wechsel auf `uv` (Astral-Ökosystem, gleicher Hersteller wie Ruff): +- `uv pip compile pyproject.toml -o requirements.txt` → lockfile +- `uv sync` → installiert exakt die gelockten Versionen +- `uv lock --check` in CI → bricht Build, wenn Lockfile nicht aktuell + +**Vorteile:** Drastisch schneller als pip, reproduzierbar, selbes Ökosystem wie Ruff. + +**Umsetzung:** `uv` installieren, `uv.lock` generieren, Doku-Abschnitt in Coding-Standards ergänzen. + +--- + +### sys-4: Pydantic an API-Grenzen +**Problem:** Externe Daten (Gamefound-API, Amazon PAAPI, FantasyWelt-CSV, BGG, Scraper) werden als `dict[str, Any]` modelliert — mypy strict hilft null, und zur Laufzeit knallt's, wenn ein Feld fehlt. + +**Lösung:** Pydantic-Modelle an jeder API-/Scraper-Grenze: +- Laufzeit-Validierung → sofortiger Fehler mit Kontext, nicht erst 3 Schichten später +- Echte Typen → mypy kann innen durchvalidieren +- `Any` bleibt auf die Grenzschicht beschränkt, innen nur validierte Objekte + +**Beispiel:** +```python +from pydantic import BaseModel + +class GamefoundProject(BaseModel): + id: str + title: str + funding_eur: float + backers: int | None = None + end_date: datetime + +# API-Response → validiertes Modell +project = GamefoundProject.model_validate(api_response) +# Ab hier: mypy-safe, keine Überraschungen +``` + +**Umsetzung:** Pydantic-Modelle für Gamefound, BGG, Amazon PAAPI, SPIEL-Essen-API definieren. + +--- + +### sys-5: Observability (structlog + GlitchTip) +**Problem:** „Mit Kontext loggen" steht in den Standards, aber ohne konkrete Werkzeuge. Bei einem öffentlichen Chatbot mit mehreren Servern erfährst du von Fehlern erst, wenn ein Nutzer sich beschwert. + +**Lösung:** +1. **structlog** — strukturiertes Logging, JSON-Output, kontextbehaftet +2. **GlitchTip** — selbstgehostet, Sentry-kompatibel, ein Bruchteil der Ressourcen + +```python +import structlog +logger = structlog.get_logger() + +logger.info("submission_processed", + submission_id=sub.id, + channel="whatsapp", + duration_ms=142, +) +``` + +**Umsetzung:** structlog konfigurieren, GlitchTip-Instanz aufsetzen, Error-Tracking in Flask integrieren. + +--- + +## P2 — Später (wichtig, nicht dringend) + +### sys-6: Secret-Scanning (gitleaks) +**Problem:** Viele Keys im Umlauf (PAAPI, Telegram-Token, Tavily, IPRoyal, Gamefound). Bandits B105/B106 sind schwächer als dediziertes Secret-Scanning. + +**Lösung:** `gitleaks` in pre-commit + Gitea Actions: +- Pre-commit: `gitleaks protect --staged` +- CI: `gitleaks detect --source .` + +**Umsetzung:** `.gitleaks.toml` konfigurieren, Hook in `.pre-commit-config.yaml` ergänzen. + +--- + +### sys-7: Regressions-Evals für Agenten-Pipeline +**Problem:** Du benchmarkst LLMs systematisch — aber wenn du ein Modell oder einen Prompt änderst, gibt es keine automatische Prüfung, ob die Agenten-Pipeline noch die gleiche Qualität liefert. + +**Lösung:** Golden-Set (10-20 typische Fälle) mit Constraints: +- Enthält alle Pflichtfelder +- Keine Halluzination der BGG-ID +- Deutsche Sprache +- Länge im erwarteten Bereich + +Nach jeder Prompt-/Modelländerung gegen das Golden-Set laufen lassen und auf Constraint-Verletzungen prüfen. + +**Umsetzung:** Golden-Set definieren, Evaluations-Skript schreiben, in Cronjob integrieren. + +--- + +### sys-8: ADRs in Obsidian +**Problem:** Architekturentscheidungen (Edge-Scaling, Modell-Routing, Proxy-Strategie) gehen im Alltag unter — in 6 Monaten weiß niemand mehr, warum etwas so gebaut wurde. + +**Lösung:** Architecture Decision Records (ADR) — ein Markdown pro Entscheidung: +- **Titel:** Kurz und prägnant +- **Kontext:** Was war die Situation? +- **Entscheidung:** Was haben wir entschieden? +- **Konsequenzen:** Was folgt daraus? (Positiv + negativ) + +In der Obsidian-KB ablegen, für Agenten lesbar. + +--- + +### sys-9: Backup/DR (3-2-1) +**Problem:** Gitea, DBs, Joomla-Installationen — wenn die Heim-Box stirbt, was ist weg? Ungetestete Backups sind keine Backups. + +**Lösung:** 3-2-1-Schema: +- **3** Kopien der Daten +- Auf **2** verschiedenen Medientypen +- **1** Kopie off-site + +Mit automatisierten, **getesteten** Restores (regelmäßiger Restore-Test). + +--- + +## Priorisierung + +| Prio | Item | Hebel | Aufwand | +|---|---|---|---| +| **P0** | Gitea Actions CI-Gate | 🔴 Macht Standards erst real durchsetzbar | 2-4h | +| **P0** | Edge-Queue + Idempotenz | 🔴 Verhindert Datenverlust an sichtbarster Stelle | 4-8h | +| P1 | uv + Lockfile | 🟡 Reproduzierbarkeit auf 3 Architekturen | 1-2h | +| P1 | Pydantic an API-Grenzen | 🟡 Fängt Laufzeitfehler an der Wurzel | 3-6h | +| P1 | Observability | 🟡 Du fliegst nicht mehr blind | 2-4h | +| P2 | Secret-Scanning | 🟢 Safety-Netz für Commits | 1h | +| P2 | Regressions-Evals | 🟢 Schützt vor stillen Qualitätseinbrüchen | 3-5h | +| P2 | ADRs | 🟢 Langfristiges Wissensmanagement | Laufend, je 15min | +| P2 | Backup/DR | 🟢 Existenzsicherung | 4-8h initial | + +> **Empfohlener Start:** sys-1 (Gitea Actions) + sys-2 (Edge-Queue). Danach sys-3 (uv) + sys-4 (Pydantic) im Projekt-Setup. + +--- + +*Erstellt: 21. Juni 2026 · Quelle: Externes Review-Feedback*