diff --git a/docs/Architecture-Build-Spec.md b/docs/Architecture-Build-Spec.md index 64b16b9..416290b 100644 --- a/docs/Architecture-Build-Spec.md +++ b/docs/Architecture-Build-Spec.md @@ -283,6 +283,40 @@ Requirements: Requirements: structured logs to journald / external log stack, **not** kept in-container. Every gate crossing, tool call, and escalation is logged. +**State-change log conventions.** Every durable mutation of agent state emits +exactly one structured log event, so problems can be diagnosed after the fact +by replaying the operational trail: + +- **Emission is owned by the state store.** The log line is written by the same + code path that performs the durable write, immediately after the commit + returns — a state-change line exists iff the change hit disk. Callers supply + the *cause*; no other layer logs durable mutations, so the trail cannot drift + from what was persisted. +- **Format: one JSON object per line**, with a fixed envelope on every event: + - `ts` — UTC ISO-8601 timestamp + - `agent` — `nisse-dev` | `nisse-ops` + - `event` — dotted type from the taxonomy below + - `task_id` — correlation ID for the enclosing unit of work, minted when the + unit starts (`use `, milestone start, ticket pickup) and threaded + through every event it causes; `null` only outside any unit (e.g. boot). + - `cause` — what triggered the mutation: operator command, approval + (`rev-N` + channel), escalation trigger, reconciliation verdict, or crash + recovery. +- **Event taxonomy** (extended as components are built; names are never reused + or renamed): + + | Event | Event-specific fields | + |---|---| + | `state.transition` | `from`, `to`, `project` | + | `intent.begin` / `intent.commit` / `intent.abort` | `intent_id`, `kind` | + | `gate.open` / `gate.approve` / `gate.reject` | `gate`, `rev`, `channel` | + | `boot.reconcile` | `action`, per-intent verdicts | + | `schema.migrate` | `from_version`, `to_version` | + +- **Logs are diagnostic, never load-bearing.** Nothing reads logs back to + derive state — the state store (A2) is the sole source of truth, the same + rule that keeps Matrix room history (A1) non-authoritative. + ### A10. Canonical Transcript Schema The durable record is a **canonical event log** in SQLite, not a provider-shaped