# Audit-Log & Receipts — Nachvollziehbarkeit

> **Bauplan zu einem gebauten System.** Umgesetzt; der Stand steht in der
> [Roadmap](Roadmap.md) 3.1.

Setzt A3 aus der [Roadmap](Roadmap.md) um (F-A2). Zwei zusammengehörige Dinge:

- **Audit-Log** — ein Eintrag je Tool-Aufruf: wer, wann, welches Tool, mit welchem
  Ausgang.
- **Receipts** — ein Abschluss-Beleg je Lauf: Ergebnis, Schritte, Tokens, Kosten,
  verknüpft mit dem Task.

A3 ist das Fundament für A2 (jede Staging-Entscheidung wird als Datensatz verankert)
und für C7 („Kosten pro Ergebnis", fällt aus den Receipts ab). Die Tool-Fehlerquote aus
der Leistungsanalyse liest sich direkt aus dem Log.

## Warum eigene Tabellen, nicht der State-Store

Dieselbe Überlegung wie bei Gedächtnis und Taskboard: `IStateStore` ist Schlüssel-Wert.
Ein Log, das man nach Lauf, Task oder Tool filtern und dessen Fehlerquote man auswerten
will, braucht typisierte Spalten. Zwei Tabellen auf dem vorhandenen
[`SqliteStorage`](../src/ClawdDotNet.Core/Storage/SqliteStorage.cs): `AuditLog` und
`RunReceipts`.

## Provenienz — von der Engine gestempelt, nie vom Agenten behauptet

Die entscheidende Regel (aus dem OpenAlice-Provenance-Konzept):

- **Herkunft stempelt die Engine.** `AgentId`, `Model` und `Source` kommen aus dem
  Wissen der Engine über den Lauf, nicht aus dem Tool-Ergebnis. Ein Tool kann seine
  Herkunft nicht fälschen, weil es sie gar nicht schreibt.
- **Einträge sind unveränderlich.** Das Repository hat kein Update und kein Delete —
  eine Korrektur ist ein neuer Eintrag. Das ist die eigentliche Zusage, keine fehlende
  Funktion.
- **Unbekanntes wird als unbekannt markiert, nicht geraten.** Fehlt die Quelle, steht
  `unknown`, nicht ein plausibel geratener Kanal.
- **Worker-Typ und Session sind getrennt.** `Model` (das ausführende Modell) und
  `Source` (die verantwortliche Session: `webview`, `telegram`, `task`, `agentcomm`,
  `job`, `direct`) sind verschiedene Begriffe und stehen in eigenen Spalten.

## Audit-Log

Gestempelt an genau einer Stelle: `AgentEngine.ExecuteToolCallAsync` — dort, wo jeder
Tool-Aufruf durchläuft. Je Aufruf ein Eintrag mit Ausgang:

| Status | Wann |
|---|---|
| `Ok` | Tool lief und lieferte ein Ergebnis |
| `Error` | Tool meldete einen Fehler oder warf |
| `Denied` | das `PermissionGate` hat abgelehnt |
| `NotFound` | Tool dem Agenten nicht zugewiesen/unbekannt |

Ein Abbruch (Cancellation) wird **nicht** protokolliert — der Aufruf kam nicht zum
Abschluss. Die Argumente werden roh, aber gekappt abgelegt (4 000 Zeichen); die
Ausgangsnotiz kurz (500).

**Best effort:** Ein Fehler beim Schreiben des Audits darf den Lauf nie scheitern
lassen — dieselbe Linie wie bei der Verbrauchserfassung. Der Eintrag wird geschrieben,
nachdem die eigentliche Arbeit getan ist.

## Receipts

Je Lauf ein Beleg, geschrieben beim Abschluss von `RunAsync`/`ChatAsync` (neben der
vorhandenen `RunUsage`-Erfassung). Er trägt Status, Schritte, Prompt-/Completion-/
Cached-Tokens, geschätzte Kosten (aus dem `ModelPricingCatalog`, mit
`CostIsKnown`-Flag) und einen kurzen Ergebnis-Verweis.

**Verknüpfung `RunUsage` ↔ Task:** Der Receipt trägt die `TaskId`, wenn der Lauf aus dem
Taskboard kam — der `EngineTaskDispatcher` reicht sie (samt `source: task`) durch. Damit
ist „Kosten pro Ergebnis" (C7) ein Abfallprodukt: `ListReceiptsForTaskAsync` liefert
alle Belege zu einem Task.

## RunId — die Klammer

Jeder Lauf bekommt zu Beginn eine `RunId` (GUID). Alle Audit-Einträge **und** der
Receipt eines Laufs tragen sie. So lässt sich ein Lauf lückenlos rekonstruieren:
`ListForRunAsync(runId)` gibt die Aufrufe in Reihenfolge, `GetReceiptForRunAsync(runId)`
den Abschluss.

## Verdrahtung

`IAuditRepository` ist optional (wie Gedächtnis und Taskboard): ohne Repo läuft die
Engine unverändert. In `Program.cs` wird ein `SqliteAuditRepository` auf der Instanz-DB
erzeugt und der Engine übergeben.

## Offen

- **Output-Scrubbing** — die `Arguments` können Secrets enthalten. Das zentrale
  Maskieren bekannter Secret-Werte (eigener beschlossener Roadmap-Punkt) greift, sobald
  es steht; der Andockpunkt (`ExecuteToolCallAsync`) ist derselbe.
- **Review-Oberfläche** — die Anzeige/Durchsicht des Logs und der Receipts gehört zu A2
  (Staging-Review im Hauptfenster); die Abfragemethoden dafür stehen bereit.
- **Export** — ein JSONL-Export des Logs wäre für externe Auswertung nützlich (später,
  passt zum A6-Spiegel).
