A pure move; behaviour unchanged. - models (L9, L7): llm, catalog, probe, render, render_anthropic, context - log (L7, L6): canonical, views - state (L8): store (was state), reconcile, worker, cancel, dump - knowledge (L10, L6.4): vault, memory - conversation (L5): transport, matrix - execution (L13): ssh (was execution), testing - authority (P3, P4, L4): actions - gitea, oplog, envfile stay at the top until each gains a second module. Every import, monkeypatch target, script and doc reference rewritten. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""nisse-dev's state store (architecture L8): the shared one, with the dev
|
|
agent's own roles, views and tables (build VM blocks, D3)."""
|
|
|
|
import sqlite3
|
|
from collections.abc import Mapping
|
|
from dataclasses import dataclass
|
|
|
|
from nisse_core.state import store as state
|
|
from nisse_core.state.store import Migration, utc_now
|
|
|
|
_VM_BLOCKS = """
|
|
CREATE TABLE vm_blocks (
|
|
host TEXT PRIMARY KEY,
|
|
cause TEXT NOT NULL,
|
|
reason TEXT NOT NULL,
|
|
work_item TEXT,
|
|
since TEXT NOT NULL
|
|
)
|
|
"""
|
|
|
|
|
|
def _add_vm_blocks(conn: sqlite3.Connection) -> None:
|
|
conn.execute(_VM_BLOCKS)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VMBlock:
|
|
"""A build VM nothing may run on until it is recovered (D3): every
|
|
project on it waits."""
|
|
|
|
host: str
|
|
cause: str # broken (the driver said so), unreachable, host key (#137)
|
|
reason: str
|
|
work_item: str | None # the item that found it
|
|
since: str
|
|
|
|
|
|
class StateStore(state.StateStore):
|
|
AGENT = "nisse-dev"
|
|
SCHEMA_VERSION = 5
|
|
SCHEMA = (_VM_BLOCKS,)
|
|
MIGRATIONS: Mapping[int, Migration] = {4: _add_vm_blocks}
|
|
# planner and executor are the old loop's (until M2.8), kept so its
|
|
# records still read.
|
|
ACTORS = state.ACTORS | {
|
|
"planner",
|
|
"executor",
|
|
"toolchain", # the toolchain step (D3): the driver's model, apart
|
|
"memory", # the memory pass at an item's close (L6.4), apart too
|
|
}
|
|
# What each role is shown of the others' work (``nisse_core.log.views``).
|
|
SHOWN: Mapping[str, Mapping[str, str]] = {
|
|
"driver": {"helper": "report"},
|
|
"helper": {"driver": "narrate"},
|
|
# The toolchain step works in its own transcript: the driver sees
|
|
# only its report (a tool result, or facts), the step the driver's
|
|
# work.
|
|
"toolchain": {"driver": "narrate"},
|
|
# The memory pass at an item's close (L6.4): the item as it went.
|
|
"memory": {"driver": "narrate"},
|
|
}
|
|
|
|
# --- build VMs (D3) ----------------------------------------------------
|
|
|
|
def block_vm(
|
|
self, host: str, *, cause: str, reason: str, work_item: str | None
|
|
) -> None:
|
|
"""Block a build VM; a second block on it keeps the first."""
|
|
row = self._conn.execute(
|
|
"INSERT INTO vm_blocks (host, cause, reason, work_item, since) "
|
|
"VALUES (?, ?, ?, ?, ?) ON CONFLICT(host) DO NOTHING RETURNING host",
|
|
(host, cause, reason, work_item, utc_now()),
|
|
).fetchone()
|
|
if row is not None:
|
|
self.oplog.emit("vm.block", cause=cause, host=host, work_item=work_item)
|
|
|
|
def reblock_vm(self, host: str, *, cause: str, reason: str) -> None:
|
|
"""A blocked VM's block, for another cause (an unreachable VM that
|
|
answers with a new host key)."""
|
|
self._conn.execute(
|
|
"UPDATE vm_blocks SET cause = ?, reason = ? WHERE host = ?",
|
|
(cause, reason, host),
|
|
)
|
|
self.oplog.emit("vm.block", cause=cause, host=host)
|
|
|
|
def unblock_vm(self, host: str, *, cause: str) -> VMBlock | None:
|
|
"""Lift a build VM's block; returns it, or None if there was none."""
|
|
row = self._conn.execute(
|
|
"DELETE FROM vm_blocks WHERE host = ? RETURNING *", (host,)
|
|
).fetchone()
|
|
if row is None:
|
|
return None
|
|
self.oplog.emit("vm.unblock", cause=cause, host=host)
|
|
return VMBlock(**dict(row))
|
|
|
|
def vm_blocks(self) -> dict[str, VMBlock]:
|
|
rows = self._conn.execute("SELECT * FROM vm_blocks").fetchall()
|
|
return {row["host"]: VMBlock(**dict(row)) for row in rows}
|