Implement A9 state-change logging in the state store #5

Merged
dongar merged 2 commits from state-change-logging into main 2026-09-05 21:22:46 +00:00
Collaborator

Implements the conventions merged in #4 (closes the loop on issue #3). This is the StateStore retrofit named in that PR's plan; the gate.* events land with the step-2 control-loop work.

  • OpLog — JSON-lines emitter with the fixed A9 envelope (ts, agent, event, task_id, cause). task_id is a plain attribute the orchestrator sets at unit-of-work start; null until then.
  • Emission owned by the store: transition (with from/to/project), intent.begin/intent.commit/intent.abort (with intent_id/kind), and schema.migrate (with versions) each emit immediately after their durable write commits. A failed resolution emits nothing — a line exists iff the change hit disk. Callers pass the cause.
  • reconcile_on_boot emits boot.reconcile with the action and per-intent verdicts, and resolves intents with reconciliation-verdict causes.
  • Default sink is stderr (journald captures it under systemd); tests inject a StringIO.

7 new unit tests covering envelope shape, from/to threading, task_id correlation, migration events, boot summaries, and the emit-iff-committed rule. 29 unit + 3 integration green locally.

🤖 Generated with Claude Code

Implements the conventions merged in #4 (closes the loop on issue #3). This is the StateStore retrofit named in that PR's plan; the `gate.*` events land with the step-2 control-loop work. - **`OpLog`** — JSON-lines emitter with the fixed A9 envelope (`ts`, `agent`, `event`, `task_id`, `cause`). `task_id` is a plain attribute the orchestrator sets at unit-of-work start; `null` until then. - **Emission owned by the store**: `transition` (with `from`/`to`/`project`), `intent.begin`/`intent.commit`/`intent.abort` (with `intent_id`/`kind`), and `schema.migrate` (with versions) each emit immediately after their durable write commits. A failed resolution emits nothing — a line exists iff the change hit disk. Callers pass the `cause`. - **`reconcile_on_boot`** emits `boot.reconcile` with the action and per-intent verdicts, and resolves intents with reconciliation-verdict causes. - Default sink is stderr (journald captures it under systemd); tests inject a StringIO. 7 new unit tests covering envelope shape, from/to threading, task_id correlation, migration events, boot summaries, and the emit-iff-committed rule. 29 unit + 3 integration green locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dev-agent added 1 commit 2026-09-05 21:13:45 +00:00
Implement A9 state-change logging in the state store
CI / checks (push) Successful in 9s
CI / checks (pull_request) Successful in 8s
18d4fcd701
Retrofit for the conventions merged in #4 (issue #3):

- OpLog: JSON-lines emitter with the fixed envelope (ts, agent,
  event, task_id, cause); task_id set by the orchestrator at
  unit-of-work start
- StateStore owns emission: transition, intent begin/commit/abort,
  and schema migrations each emit their event right after the
  durable write commits; callers pass the cause
- reconcile_on_boot emits boot.reconcile with action and verdicts,
  and resolves intents with reconciliation-verdict causes
- Failed resolutions emit nothing (a line exists iff the write
  committed)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dev-agent added 1 commit 2026-09-05 21:20:43 +00:00
Add literal-format test showing exact log lines
CI / checks (push) Successful in 8s
CI / checks (pull_request) Successful in 8s
7e89a791d2
Freezes the clock and asserts the exact bytes emitted per event, so
the on-disk log format is reviewable in the test file itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dongar approved these changes 2026-09-05 21:22:41 +00:00
dongar merged commit 78122c407e into main 2026-09-05 21:22:46 +00:00
dongar deleted branch state-change-logging 2026-09-05 21:22:46 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#5