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
Showing only changes of commit 7e89a791d2 - Show all commits
+46
View File
@@ -104,6 +104,52 @@ def test_boot_reconcile_emits_action_and_verdicts(
assert commit[0]["cause"] == "reconciliation verdict: effect verified"
def test_literal_log_lines_for_human_readability(
store: StateStore, stream: io.StringIO, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Asserts the exact bytes written per line, so the on-disk format is
visible here rather than only in a running system."""
class _FrozenDatetime:
@staticmethod
def now(tz: object) -> object:
from datetime import UTC, datetime
return datetime(2026, 9, 5, 12, 0, 0, tzinfo=UTC)
monkeypatch.setattr("nisse_dev.oplog.datetime", _FrozenDatetime)
store.transition(
"PLANNING_ARCH", cause="operator command: use demo", project="demo"
)
store.oplog.task_id = "demo-m2"
intent_id = store.begin_intent(
"push_branch", {"branch": "milestone-2"}, cause="milestone 2 execution"
)
store.commit_intent(intent_id, cause="push verified by Gitea query")
assert stream.getvalue().splitlines() == [
(
'{"ts": "2026-09-05T12:00:00+00:00", "agent": "nisse-dev",'
' "event": "state.transition", "task_id": null,'
' "cause": "operator command: use demo",'
' "from": null, "to": "PLANNING_ARCH", "project": "demo"}'
),
(
'{"ts": "2026-09-05T12:00:00+00:00", "agent": "nisse-dev",'
' "event": "intent.begin", "task_id": "demo-m2",'
' "cause": "milestone 2 execution", "intent_id": 1,'
' "kind": "push_branch"}'
),
(
'{"ts": "2026-09-05T12:00:00+00:00", "agent": "nisse-dev",'
' "event": "intent.commit", "task_id": "demo-m2",'
' "cause": "push verified by Gitea query",'
' "intent_id": 1, "kind": "push_branch"}'
),
]
def test_failed_resolution_emits_nothing(
store: StateStore, stream: io.StringIO
) -> None: