# Intent: Spaltenweite Modell-/Iterations-Konfiguration — Backend-Speicher und API ## Ziel Ein neuer, additiver State-Bereich `column_config` in `state.json` sowie zugehörige GET/POST-HTTP-Endpunkte in `board-agent/trigger.py` speichern für jede der drei job-auslösenden Spalten (`in_umsetzung`, `review`, `recherche_laeuft`) eine eigene Konfiguration aus Runtime, Modell und Reasoning-Effort — bei `in_umsetzung` zusätzlich ein Iterationslimit. Dies ist reine Speicher-/API-Infrastruktur; noch keine UI und noch kein Verbraucher (Job-Trigger) liest diese Werte — das bestehende Verhalten (globaler `board_runtime()`) bleibt unverändert und unangetastet bestehen, bis Geschwister-Intents darauf umstellen. ## Umfang - Festgelegtes Schema (verbindlicher Vertrag für alle Geschwister- Intents dieses Splits — Feldnamen exakt so übernehmen): ``` column_config = { "in_umsetzung": {"runtime": "codex", "model": "", "reasoning_effort": "", "iteration_limit": 20}, "review": {"runtime": "codex", "model": "", "reasoning_effort": ""}, "recherche_laeuft": {"runtime": "codex", "model": "", "reasoning_effort": ""}, } ``` `model`/`reasoning_effort` = `""` bedeutet „kein Override, bestehender Env-Var-Default greift downstream weiter" — diese Semantik ist wichtig für die Geschwister-Intents, die daraus Job-Payloads bauen (dort muss `""` beim Zusammenbau des Payloads als *fehlendes* Feld behandelt werden, nicht als explizite leere Zeichenkette, damit `common.append_model_options()`/`append_claude_model_options()` weiterhin ihren `model=None`-Fallback auf die bestehenden Env-Vars nehmen). - `board-agent/trigger.py`, neben dem bestehenden `board_runtime()`/ `set_board_runtime()` (Zeile 93-103, bleibt unverändert): - `validate_column_config_value(column, payload)`: validiert `runtime` (wiederverwendet `validate_runtime()`, Zeile 86-90), `model` (Regex `[A-Za-z0-9][A-Za-z0-9._:+-]*`, max. 128 Zeichen, leer erlaubt), `reasoning_effort` (Regex `[A-Za-z0-9_-]{1,32}`, leer erlaubt), sowie bei `column == "in_umsetzung"` zusätzlich `iteration_limit` (Integer, gültiger Bereich 5–40 inklusive). - `load_column_config()`: liest `state.json["column_config"]`, füllt fehlende Spalten/Felder mit den Default-Werten aus dem Schema oben auf (Rückwärtskompatibilität für `state.json`-Dateien, die den Schlüssel noch nicht kennen). - `set_column_config(column, updates)`: merged validierte `updates` in die bestehende gespeicherte Konfiguration der genannten Spalte und persistiert via `save_state()` (Zeile 128) unter demselben `STATE_LOCK` (Zeile 83) wie `set_board_runtime()`. - `GET /api/board/column-config` → gibt das vollständige, default-aufgefüllte `column_config`-Objekt aller drei Spalten zurück. - `POST /api/board/column-config/` (Pfad-Parameter `column` ∈ `{in_umsetzung, review, recherche_laeuft}`, sonst 404) → Body ist ein JSON-Objekt mit einer beliebigen Teilmenge der erlaubten Felder dieser Spalte; bei Validierungsfehler 400 mit `{"error": "..."}`; bei Erfolg 200 mit der aktualisierten Konfiguration dieser einen Spalte. - `GET /api/board` (bestehender Handler, Zeile 717) bekommt zusätzlich den Schlüssel `"column_config"` in der Antwort, additiv neben dem bestehenden `"runtime"`-Schlüssel — Letzterer bleibt unverändert bestehen (wird von keinem Geschwister-Intent dieses Kindes entfernt). - `board-agent/test_board_agent.py`: Tests für `validate_column_config_value()`/`load_column_config()`/ `set_column_config()` sowie für die beiden neuen HTTP-Endpunkte (gültige/ungültige `runtime`/`model`/`reasoning_effort`/ `iteration_limit`, unbekannte Spalte → 404, Default-Werte bei leerem `state.json`). - `board-agent/README.md`: neuen Abschnitt für `GET/POST /api/board/column-config[/]` ergänzen, analog zum bestehenden Eintrag für `POST /api/board/runtime` (Zeile 55). ## Explizit ausgeschlossen - Keine UI-Änderung — `board.html` bleibt in diesem Kind unangetastet (eigenes Geschwister-Intent). - Kein Umstellen der bestehenden Job-Trigger-Stellen (`in_umsetzung`-Trigger, Research-Trigger, Review-Completion) auf `column_config` — die lesen weiterhin `board_runtime()` wie bisher (eigene Geschwister-Intents). - Kein Entfernen von `board_runtime()`/`set_board_runtime()` oder der bestehenden `GET/POST /api/board/runtime`-Endpunkte — die bleiben vollständig funktionsfähig, bis das Frontend-Geschwister-Intent sie ablöst. - Keine dynamische Modell-Liste aus `agents/catalog.json` — Validierung bleibt reine Regex-/Bereichsprüfung, keine Whitelist-Abfrage gegen eine externe Quelle. - Kein Pro-Karte-Override — Konfiguration ist ausschließlich spaltenweit. ## Annahmen - Der oben dokumentierte `column_config`-Schema-Vertrag (Feldnamen, Default-Werte, Bedeutung von `""`) ist für alle Geschwister-Intents dieses Splits verbindlich und wird dort unverändert vorausgesetzt. - `state.json` ohne den Schlüssel `column_config` ist ein gültiger, häufiger Zustand (frischer Rollout) und muss transparent auf die Default-Werte zurückfallen, ohne einen Fehler zu werfen. - Spaltenweiter statt Pro-Karte-Scope ist die gewünschte Granularität. ## Offene Risiken / offene Fragen - Modell-Whitelist im späteren Dropdown muss von Hand mit `agents/catalog.json` synchron gehalten werden (kein automatischer Sync) — Drift-Risiko, betrifft aber primär das Frontend-Geschwister. - Das Iterationslimit-Feld hat hier bereits eine Obergrenze (5–40) als Ressourcenschutz — falls ein anderer Wertebereich gewünscht wird, muss das hier und im Frontend-Geschwister-Intent synchron geändert werden. - `STATE_LOCK` schützt nur Schreiboperationen innerhalb dieses Prozesses; das bestehende Nebenläufigkeitsmodell (ThreadingHTTPServer) bleibt unverändert und war bereits vor diesem Intent so. ## Erfolgskriterien - `state.json` ohne `column_config`-Schlüssel liefert bei `GET /api/board/column-config` die dokumentierten Default-Werte. - `POST /api/board/column-config/in_umsetzung` mit gültigem Body persistiert `runtime`/`model`/`reasoning_effort`/`iteration_limit` und ein nachfolgendes GET liefert exakt diese Werte. - `POST /api/board/column-config/review` mit `iteration_limit` im Body wird abgelehnt (400) oder das Feld wird ignoriert — Review kennt kein Iterationslimit. - Ungültiger Spaltenname im Pfad → 404. Ungültiges `model`/ `reasoning_effort`/`iteration_limit` → 400 mit Fehlermeldung. - `GET /api/board` enthält weiterhin den bisherigen `"runtime"`- Schlüssel unverändert, zusätzlich den neuen `"column_config"`- Schlüssel. - Bestehende `board_runtime()`/`set_board_runtime()`-Tests und die bestehenden `/api/board/runtime`-Endpunkte bleiben unverändert grün. - Neue Tests für Validierung, Defaults und beide neuen Endpunkte sind grün.