| from __future__ import annotations |
|
|
| from collections.abc import Iterable |
|
|
| from src import observability as obs |
| from src.core.events import Event |
|
|
|
|
| class Ledger: |
| """Append-only in-memory ledger for the first vertical slice.""" |
|
|
| def __init__(self) -> None: |
| self._events: list[Event] = [] |
| self._seen_ids: set[str] = set() |
|
|
| @property |
| def events(self) -> tuple[Event, ...]: |
| return tuple(self._events) |
|
|
| def append(self, event: Event) -> Event: |
| if event.id in self._seen_ids: |
| return event |
| self._events.append(event) |
| self._seen_ids.add(event.id) |
| obs.log("ledger.append", level="debug", id=event.id, kind=event.kind, actor=event.actor, turn=event.turn) |
| obs.incr("ledger.events", 1, kind=event.kind) |
| return event |
|
|
| def extend(self, events: Iterable[Event]) -> None: |
| for event in events: |
| self.append(event) |
|
|
| def reset(self) -> None: |
| obs.log("ledger.reset", level="debug", events=len(self._events)) |
| self._events.clear() |
| self._seen_ids.clear() |
|
|
| def events_for_run(self, run_id: str) -> tuple[Event, ...]: |
| """Return the events of *run_id* in append/offset order.""" |
| return tuple(e for e in self._events if e.run_id == run_id) |
|
|
| def runs(self) -> tuple[str, ...]: |
| """Return the distinct run_ids in first-seen order.""" |
| seen: dict[str, None] = {} |
| for e in self._events: |
| seen.setdefault(e.run_id, None) |
| return tuple(seen) |
|
|