"""Integration: a restart mid-gate resumes the open gate with its revision history intact; unresolved intents gate the whole boot to AWAIT_INPUT (architecture L8 and L3 together).""" import io from pathlib import Path import pytest from nisse_dev.control import ( AWAIT_ARCH_SIGNOFF, AWAIT_INPUT, AWAIT_MILESTONE_SIGNOFF, Orchestrator, StubAgent, ) from nisse_dev.oplog import OpLog from nisse_dev.state import StateStore from nisse_dev.transport import InMemoryTransport pytestmark = pytest.mark.integration def boot(db: Path) -> tuple[Orchestrator, StateStore, InMemoryTransport]: store = StateStore(db, oplog=OpLog("nisse-dev", io.StringIO())) transport = InMemoryTransport() orch = Orchestrator(store, transport, StubAgent(), operators={"don"}) orch.start() return orch, store, transport def test_restart_mid_gate_resumes_with_revisions_intact(tmp_path: Path) -> None: db = tmp_path / "state.db" _, store, transport = boot(db) transport.receive("don", "use demo") transport.receive("don", "needs work") # arch gate now at rev-2 task_id = store.oplog.task_id store.close() _, store2, transport2 = boot(db) state = store2.get_state() assert state is not None assert state.state == AWAIT_ARCH_SIGNOFF assert store2.oplog.task_id == task_id # correlation survives restart notices = [m.text for m in transport2.outbound] assert any("rev-2" in n and "still open" in n for n in notices) transport2.receive("don", "approve rev-1") # stale across the restart too assert store2.get_state().state == AWAIT_ARCH_SIGNOFF transport2.receive("don", "approve rev-2") assert store2.get_state().state == AWAIT_MILESTONE_SIGNOFF def test_restart_with_unresolved_intent_gates_boot(tmp_path: Path) -> None: db = tmp_path / "state.db" _, store, transport = boot(db) transport.receive("don", "use demo") store.begin_intent("push_branch", {"branch": "m1"}, cause="test") store.close() _, store2, transport2 = boot(db) assert store2.get_state().state == AWAIT_INPUT question = transport2.outbound[-1] assert question.kind == "question" assert "unresolved work" in question.text