From 41e103c35db3e5b8ab17e8520326a2c73feef74d Mon Sep 17 00:00:00 2001 From: Don Garrett Date: Sat, 5 Sep 2026 19:29:27 +0000 Subject: [PATCH] Amend A9 with state-change log conventions (issue #3) Every durable mutation emits one JSON-lines event, emitted by the state store itself immediately after the commit returns, with a fixed envelope (ts, agent, event, task_id correlation ID, cause) and a dotted event taxonomy covering transitions, intents, gates, boot reconciliation, and migrations. Logs stay diagnostic-only, never load-bearing. Co-Authored-By: Claude Fable 5 --- docs/Architecture-Build-Spec.md | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) 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 -- 2.54.0