ADR 0010: Verbatim JSON Lines audit log plus unified-logging diagnostics
Date: 2026-09-17. Status: accepted.
Context
A tool-using agent that runs commands needs an audit trail (what was asked, decided, run, returned) and debuggability. The coming risk classifier and approval flow are only auditable if the plumbing exists first.
Decision
- Two logs. An append-only JSON Lines audit log at
~/.wisp/logs/audit.jsonl, and diagnostics throughos.Loggerunder one subsystem with a category per component, mirrored to stderr whenWISP_LOGis set. The MCP SDK’s swift-log is bridged into diagnostics. - Audit content is verbatim (owner’s decision): an audit log that cannot be read is not one. The file is mode 0600 and rotated by size.
- One envelope for every event (schema, time, version, pid, session, turn, call, kind, details), with
stable kind names and documented detail fields, so the file is queryable with
jqand readable bywisp logs. - Hooks sit at existing boundaries:
Agentfor turns and condensation,AuditedToolaround every registered tool,CommandRunnerfor policy and outcome,WispServerfor MCP requests and results, and the CLI for session start and end.AuditLogassigns turn numbers so every event in a turn agrees. AuditSinkis a protocol; tests use an in-memory sink, disabled config uses a null sink.
Consequences
- Every new tool is audited for free; every new event kind must be added to
docs/logging.md. - Writes are synchronous per event. The volume is small (tens of events per turn) so this is fine, and it means an event is on disk before the action it describes completes.
- Concurrent wisps append to the same file;
pidseparates them. Lines are written whole. - Reserved kinds (
classifier.verdict,approval.*) are in place for the classifier.