# Staging-Freigabe für irreversible Aktionen Setzt A2 aus der [Roadmap](Roadmap.md) um (F-A1 + S4). Irreversible Aktionen werden **gestaged statt ausgeführt**: Vorschlag → Review im Hauptfenster → Freigabe/Ablehnung. Aufbauend auf dem Taskboard (A1, Fortsetzung nach Freigabe) und dem Audit-Log (A3, Approval-Records). Sicherheitswirkung: Eine Prompt-Injection (K2) kann dann nur noch einen **Vorschlag** erzeugen, keine Ausführung. ## Das Gate wird zum Durchsetzungspunkt (S4) Das bisher wirkungslose `PermissionGate` (es prüfte nur, ob ein Tool zugewiesen ist) wird zur zentralen Stelle: Policy-Prüfung, Staging-Entscheidung und Audit-Hook an **einer** Stelle (`AgentEngine.ExecuteToolCallAsync`), statt ad-hoc in jedem Tool. S4 geht hier auf. ## Policy: `auto | approve | deny` pro Tool/Aktion Je (Tool, Aktion) eine Entscheidung: - **`auto`** — läuft wie bisher. - **`approve`** — wird gestaged; die Ausführung wartet auf eine menschliche Freigabe. - **`deny`** — wird gar nicht erst vorgeschlagen, sondern abgelehnt. Auflösung vom Speziellen zum Allgemeinen: `Tool.Aktion` → `Tool` → Standard (`auto`). Die „Aktion" ist das `action`-Argument des Aufrufs (die meisten Tools haben es). **Eingebaute Standardregeln** (nach Sichtung der Tools, überschreibbar per Konfiguration) — genau die in der Roadmap genannten irreversiblen Aktionen: | Tool.Aktion | Standard | |---|---| | `Mail.send` | approve | | `Telegram.send_message` | approve | | `Database.insert`, `Database.upsert` | approve | | `FileRW.delete` | approve | | `FTP.upload`, `FTP.delete` | approve | | alles andere | auto | Lesende Aktionen (`Mail.read_inbox`, `FileRW.read`, `Database.query`, …) bleiben `auto` — Staging soll schützen, nicht lähmen. ## Plan-Freeze Freigegeben wird ein **eingefrorener, konkreter Aufruf**: Tool, Aktion und die **exakten Argumente** zum Zeitpunkt des Stagings. Ausgeführt wird genau das Eingefrorene (der gespeicherte Argument-JSON), nie eine nachträglich veränderte Fassung. Jede Änderung wäre eine neue Freigabe. Das ist auch der Grund, warum der Agent den Aufruf **nicht** nach der Freigabe erneut formuliert (er könnte etwas anderes bauen) — der eingefrorene JSON wird direkt an das Tool gegeben. ## Fortsetzung nach Freigabe — kein pausierter Lauf Die tragende Architektur-Vorgabe (aus der Roadmap-Einstufung): **Kein pausierter, im Speicher gehaltener Lauf.** Der Ablauf: 1. **Vorschlag.** Der Agent ruft eine `approve`-Aktion auf. Das Gate führt sie nicht aus, sondern legt einen **Pending**-Datensatz an (eingefrorener Aufruf) und gibt dem Agenten „Zur Freigabe vorgelegt (#id)" als Tool-Ergebnis zurück. Der Lauf endet **regulär** — der Agent schließt ab, nichts hängt im Speicher. 2. **Review.** Ein Mensch sieht die offenen Vorschläge im Hauptfenster und entscheidet. 3a. **Freigabe.** Der eingefrorene Aufruf wird **direkt** ausgeführt (standalone, nicht über einen Chat-Lauf). Das Ergebnis wird festgehalten, und ein **Folge-Task** (A1) weckt den Agenten: „Deine Aktion X wurde freigegeben und ausgeführt, Ergebnis: Y — mach weiter." Der Scanner stellt ihn zu (`ChatAsync`, bestehender Kontext). 3b. **Ablehnung.** Ein Folge-Task weckt den Agenten mit der Ablehnung (samt Grund). Echtes Suspend/Resume eines laufenden `ChatAsync` wäre Fable-Terrain — und ist mit dieser Vereinfachung unnötig. ## Approval-Records (A3) Jede Entscheidung — Freigabe wie Ablehnung — wird an zwei Stellen verankert: - Im **Staging-Datensatz** selbst: Status, `DecidedBy`, `DecidedAt`, Ergebnis-Verweis bzw. Ablehnungsgrund. - Als **Audit-Eintrag** (A3): Tool, Ausgang, `source: approval`, „Freigegeben von …" bzw. „Abgelehnt von …". Die Herkunft stempelt auch hier das System, nicht der Agent. Der Vorschlag selbst wird beim Anlegen als Audit-Eintrag mit Status **`Staged`** notiert — so ist die ganze Kette (Vorschlag → Entscheidung → Ausführung) im Log nachvollziehbar. ## Nebenläufigkeit Zwei Reviewer dürfen nicht denselben Vorschlag doppelt freigeben. Der Übergang `pending → approved/rejected` ist ein **atomares, bedingtes `UPDATE`** (dieselbe Claim-Technik wie beim Taskboard): Genau einer gewinnt, der zweite Klick läuft ins Leere. Erst nach gewonnenem Übergang wird der eingefrorene Aufruf ausgeführt. ## Verdrahtung - `StagingGate` (Policy + Anlegen des Vorschlags) hängt optional an der Engine — ohne es läuft alles wie bisher (`auto`). - Der eingefrorene Aufruf wird über `IFrozenCallExecutor` (von der Engine implementiert) ausgeführt: gültiger Tool-Kontext, aber ohne LLM-Schleife. - `StagingService` (Freigabe/Ablehnung) nutzt Executor, Audit und das Taskboard für den Folge-Task. Es ist die API, die die Review-Oberfläche aufruft. ## Offen - **Review-Oberfläche** im Hauptfenster (Liste der offenen Vorschläge, Freigeben/Ablehnen) — die Dienst-API steht bereit; die Freigabe-Ansicht in `src/ClawdDotNet.Desktop` ist die verbleibende Integration und der einzige offene Punkt von A2. - **Output-Scrubbing** greift auch hier auf den gespeicherten Argument-JSON, sobald es steht (eigener Roadmap-Punkt). - **Orders** (Handelsaufträge) reihen sich später als weitere `approve`-Aktionen ein.