jarvis-assist/docs/superpowers/specs/2026-09-13-settings-design.md

6.9 KiB

JARVIS Einstellungen - Design

Datum: 2026-09-13 Status: Approved, bereit fuer Implementierungsplan

Kontext

JARVIS hat aktuell keine editierbaren Identitaets-/Branding-Werte. Der Assistenten-Name ("JARVIS") und die Einleitung im System-Prompt ("Du bist JARVIS, ein KI-Assistent fuer Business-Automatisierung.") stehen fest im Code bzw. als CLAUDE_SYSTEM_PROMPT-Env-Var-Default (main.py:58-61)

  • letztere wird im Deployment nirgends gesetzt, ist also faktisch immer der hartcodierte Default. Es gibt weder eine Backend-Tabelle noch eine Frontend-Ansicht, um das zu aendern. Feature-Wunsch (.claude/JARVIS_FEATURES.md, Punkt 9): ein Einstellungen-Menuepunkt mit Werten wie E-Mailadresse, Firmenname, Name des Assistenten.

Dies ist Feature 2 von 3 einer priorisierten Roadmap (Feature 1, Bestellungen erfassen via Nextcloud Deck, ist fertig implementiert und deployt).

Scope-Entscheidungen (im Brainstorming geklaert)

  • E-Mailadresse ist ein reiner Anzeige-/Branding-Wert. Das technische Postfach (EMAIL_USER/EMAIL_PASSWORD, IMAP/SMTP-Zugangsdaten fuer die echten Mail-Tools) bleibt unveraendert ein Deployment-Secret in .env - diese Einstellung dient nur dazu, dass Claude bei Bedarf eine Kontakt-Adresse nennen kann, nicht um das tatsaechliche Postfach zu wechseln.
  • Speicherort: neue Postgres-Tabelle, nicht .env. Aenderungen wirken sofort auf den naechsten Chat-Request, kein Deploy/Neustart noetig.
  • Assistenten-Name wirkt auch im Frontend (Seitentitel, Chat-Header/ Platzhalter), nicht nur im Chat-Verhalten selbst.
  • Aenderung nur ueber die Einstellungen-Seite, kein zusaetzliches Chat-Tool (anders als z.B. remember_fact) - Einstellungen werden selten geaendert, ein dediziertes Formular reicht.
  • Bewusste Luecke: Der Login-Screen (Login.tsx, <h1>JARVIS</h1>) bleibt statisch. Settings liegen hinter X-Admin-Key wie alle anderen /api/v1/*-Routen und sind vor dem Login nicht abrufbar - Branding gilt nur fuer den eingeloggten Bereich.

Datenmodell (Claude outputs/migrations/007_settings.sql)

CREATE TABLE settings (
    key VARCHAR(100) PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Reine Key-Value-Tabelle, keine Seed-Daten. Ein fester Katalog in main.py definiert die gueltigen Keys und ihre Defaults:

SETTINGS_DEFAULTS = {
    "assistant_name": "JARVIS",
    "company_name": "MBO-Tech-IT",
    "contact_email": "kontakt@mbo-tech-it.de",
}

Fehlt ein Key in der Tabelle, gilt der Default - die Tabelle kann also leer bleiben, bis der Nutzer zum ersten Mal etwas aendert, und das Verhalten bleibt exakt wie heute.

DB-Helper (analog memory_facts-Pattern aus main.py)

  • get_setting(key: str) -> str - SELECT value FROM settings WHERE key = %s, faellt auf SETTINGS_DEFAULTS[key] zurueck, wenn keine Zeile existiert.
  • get_all_settings() -> dict - liest alle vorhandenen Zeilen, merged sie ueber SETTINGS_DEFAULTS (Defaults zuerst, dann DB-Werte drueber) und gibt ein vollstaendiges Dict mit allen drei Keys zurueck.
  • set_setting(key: str, value: str) - INSERT ... ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP.

REST-Endpoints

GET  /api/v1/settings   -> {"assistant_name": "...", "company_name": "...", "contact_email": "..."}
PUT  /api/v1/settings   Body: beliebige Teilmenge der drei Keys -> aktualisiertes vollstaendiges Dict

Beide hinter dependencies=[Depends(require_admin_key)], wie alle bestehenden /api/v1/*-Routen ausser /health. PUT validiert jeden Key gegen SETTINGS_DEFAULTS.keys() - unbekannter Key -> 400.

Einbindung in den Chat (run_chat_completion)

Die bisher statische Identitaets-Zeile (CLAUDE_SYSTEM_PROMPT-Env-Var) wird ersetzt durch einen dynamisch aus den Settings gebauten Text, der bei jedem Chat-Aufruf frisch berechnet wird (gleiches Prinzip wie der bestehende _current_datetime_context()):

async def _identity_system_prompt() -> str:
    settings = await get_all_settings()
    return (
        f"Du bist {settings['assistant_name']}, der KI-Assistent von "
        f"{settings['company_name']}. Bei Fragen zur Erreichbarkeit kannst "
        f"du auf {settings['contact_email']} verweisen."
    )

Die CLAUDE_SYSTEM_PROMPT-Env-Var und ihr Default-Text entfallen komplett (im Deployment ohnehin nirgends gesetzt) - die Settings-Tabelle ist ab jetzt die einzige Quelle fuer die Identitaets-Zeile.

Frontend

  • Neue Komponente web/src/components/Settings.tsx: Formular mit drei Textfeldern (Assistentenname, Firmenname, Kontakt-E-Mail) + "Speichern"-Button. Laedt aktuelle Werte per GET /api/v1/settings beim Mounten (Pattern wie Dashboard.tsx), speichert per PUT.
  • App.tsx: View-Type um "settings" erweitert, neuer Nav-Button "Einstellungen". Laedt die Settings einmalig nach dem Login, setzt document.title auf assistant_name und reicht den Namen als Prop an Chat.tsx durch, das damit den bisher festen Platzhaltertext ("Nachricht an JARVIS...") ersetzt.

Fehlerbehandlung

  • GET /api/v1/settings schlaegt fehl (DB nicht erreichbar): Frontend zeigt eine Fehlermeldung im Settings-Formular (gleiches Muster wie CalendarWidget/EmailWidget); App.tsx faellt beim Titel/Platzhalter auf den hartcodierten Default ("JARVIS") zurueck, kein harter Fehler.
  • PUT mit unbekanntem Key: 400, Formular zeigt die Fehlermeldung an, nichts wird gespeichert (auch nicht die gueltigen Keys aus demselben Request - alles oder nichts, kein Teil-Update bei Validierungsfehlern).

Testing

  • Unit-Tests (tests/test_settings.py, gemockte DB wie bei bestehenden Tool-Tests) fuer get_setting/get_all_settings/set_setting (Default-Fallback, Merge-Verhalten, Upsert) und die REST-Endpoints (Erfolg, unbekannter Key -> 400, fehlender Admin-Key -> 401).
  • Test fuer run_chat_completion: _identity_system_prompt() wird gemockt, Ergebnis muss im system-Parameter des Claude-Aufrufs landen (gleiches Pattern wie der bestehende Datums-Kontext-Test).
  • Frontend: kein neuer Komponenten-Test (Projekt-Konvention, siehe CalendarWidget/EmailWidget/OrdersWidget), Verifikation ueber npm run build.
  • Manueller End-to-End-Test nach Deployment: Einstellungen-Seite oeffnen, Assistentenname aendern und speichern, pruefen dass Seitentitel und Chat-Platzhalter sich sofort aktualisieren, im Chat fragen "wie heisst du?" und pruefen, dass die Antwort den neuen Namen nutzt.

Out of Scope (bewusst nicht Teil dieser Phase)

  • Aenderung der Einstellungen per Chat-Tool (siehe Scope-Entscheidungen).
  • Aendern des technischen Postfachs (EMAIL_USER/EMAIL_PASSWORD) ueber die UI - bleibt Deployment-Secret.
  • Dynamisches Branding auf dem Login-Screen (siehe "bewusste Luecke" oben).
  • Weitere Einstellungen ueber die drei genannten hinaus (die Tabelle ist aber generisch genug, dass spaetere Erweiterung nur einen neuen Eintrag in SETTINGS_DEFAULTS + ein Formularfeld braucht, keine Schema-Aenderung).