# SPEC-maintenance: Pflege-Agent, Vorschläge, Freigabe-Richtlinie Paket: `knaster.maintenance`, `knaster.policy`. ## 1. Vorschlag (`Proposal`) ``` id: "P-" kind: request_graduation | promote_skill | demote_skill | merge_skills | retire_skill | mark_slot | restart_container | restart_vm | reboot_host | notify | create_candidate | promote_candidate | rollback_candidate | train_adapter | recalibrate_context params: {...} summary: str # menschenlesbar reason: str risk: low | medium | high state: open | approved | rejected | executed | failed | expired source: routine | command | healer | lifecycle created_at, decided_at, decided_by, result ``` ## 2. Freigabe-Richtlinie (`config/policy.yaml`) ```yaml default: approve actions: demote_skill: {mode: auto} mark_slot: {mode: auto} restart_container: {mode: auto} restart_vm: {mode: auto} recalibrate_context:{mode: auto} promote_skill: {mode: approve} ... reboot_host: {mode: approve, window: "02:00-05:00"} ``` `Policy.decide(kind) -> auto | approve | deny`, dazu `Policy.in_window(kind, now)`. Ausnahme unabhängig von der Richtlinie: `mark_slot` mit `status: ok` (Slot wieder freischalten) braucht immer eine Freigabe, weil es die unsichere Richtung ist. Liegt eine Aktion mit `window` außerhalb des Zeitfensters, wird sie zwar freigegeben, aber erst im nächsten Fenster ausgeführt (`deferred`). Ein Vorschlag mit Modus `auto` wird sofort ausgeführt und mit `decided_by = policy` protokolliert. ## 3. Service-Routine (`MaintenanceAgent.run_routine`) Befunde werden aus diesen Quellen gesammelt (alle Grenzwerte in `config/maintenance.yaml`): | Befund | Bedingung | Vorschlag | |---|---|---| | Skill-Drift | Pass-Rate der letzten N Fälle auf Stufe ≥ 1 < `demote_below` (0,80), n ≥ `min_cases` (20) | `demote_skill` (auto) | | Graduierungs-Kandidat | Skill auf Stufe 0, ≥ `min_cases` Fälle, Pass-Rate auf Stufe 0 ≥ `promote_min_rate` (0,95) | `request_graduation` (approve): Auftrag an Hermes, Varianten zu erzeugen und `POST /v1/skills/{name}/graduate` aufzurufen. Ein erfolgreicher Lauf erzeugt `promote_skill` (approve) | | Ähnliche Skills | cos(description_a, description_b) ≥ `merge_similarity` (0,92) | `merge_skills` (approve) | | Slot degradiert | Laufzeitzustand ≠ ok seit > 1 h | Hinweis + `mark_slot ok` nach Prüfung (approve) | | Kontext-Ausweicher | Signal `context_exceeded` oder `output_truncated` ≥ `context_signal_limit` (10) pro Slot/Woche | `recalibrate_context` (auto) | | Adapter-Bedarf | Cluster mit ≥ `adapter_min_cases` (300) verifizierten Train-Fällen und Pass-Rate auf Stufe 1 < 0,9 | `train_adapter` (approve) | | Canary auswerten | Kandidat im Zustand `canary` mit ≥ `min_n` Fällen über das Canary-Modell | automatischer Report `canary` an die Pipeline | | Offene Kandidaten | Lifecycle-Kandidat wartet auf Freigabe | `promote_candidate` (approve, Risiko hoch) | Das Ergebnis ist ein `RoutineReport{findings[], proposals[], auto_executed[], text}`. `text` ist ein deutscher Bericht für den Chat. ## 4. Befehle per Prompt (`MaintenanceAgent.handle_command`) Feste Grammatik (Groß-/Kleinschreibung egal, deutsch und kurze englische Synonyme): | Muster | Wirkung | |---|---| | `status` / `hilfe` | Überblick bzw. Befehlsliste | | `routine` / `starte (die) service-routine` | Service-Routine | | `vorschläge` / `zeige vorschläge` | offene Vorschläge | | `genehmige P-3 [P-5 …]` / `approve P-3` | Freigabe und Ausführung | | `genehmige alle (mit )?risiko niedrig` | alle offenen Vorschläge mit `risk == low` | | `lehne P-3 ab[: Grund]` / `reject P-3` | Ablehnung | | `wechsle auf ` | Vorschlag `create_candidate{kind: base_model}` | | `trainiere adapter für ` | Vorschlag `train_adapter` | | `rollback ` | Vorschlag `rollback_candidate` | | `stufe zurück` / `degradiere ` | Vorschlag `demote_skill` | | `markiere (als)? (ok\|degraded\|unavailable)` | Vorschlag `mark_slot` | Bei unbekanntem Text wird, falls konfiguriert, ein `IntentMapper` (LLM, Slot `tier1-general`) gefragt, der **nur** einen der obigen Befehle als String zurückgeben darf. Der Agent antwortet dann: „Meinst du: ``? Antworte mit ‚ja‘.“ Ein nachfolgendes `ja` in derselben Sitzung führt den Befehl aus, einen neu erzeugten Vorschlag aber ebenfalls nur bis zur Freigabestufe. Aus Prompt-Aufträgen erzeugte Vorschläge werden **nie** sofort ausgeführt, auch wenn die Richtlinie `auto` sagt. Der Mensch bestätigt sie mit `genehmige`. ## 5. Ausführung `ActionExecutor.execute(proposal) -> result`. Aktionen sind registrierte Handler. Infrastruktur-Aktionen (`restart_*`, `reboot_host`) gehen an dieselben Treiber wie beim Healer (SPEC-healer §4). Standard ist **Dry-Run**: Das Ergebnis beschreibt dann, was getan worden wäre.