Skip to content

Runtime

kando.runtime.Runtime(ledger, responders, budget=None, cache=None)

Wires ledger, world projection, responders, and budget into a single event loop.

Fails loudly on responder errors — never swallows exceptions.

Source code in kando/runtime.py
def __init__(
    self,
    ledger: LedgerStore,
    responders: list[Responder],
    budget: Budget | None = None,
    cache: LLMCache | None = None,
) -> None:
    self._ledger = ledger
    self._responders = responders
    self._cache = cache or LLMCache()
    self._budget_enforcer = BudgetEnforcer(
        budget or Budget(),
        run_id=ledger.stream_name(),
    )

load()

Reconstruct the current world from the ledger.

Source code in kando/runtime.py
def load(self) -> World:
    """Reconstruct the current world from the ledger."""
    world = reproject(self._ledger)
    world.context["cache"] = self._cache
    return world

replay(strict=False)

Replay the run.

Permissive (default): reproject the ledger as-is — fast, no re-firing. Strict: re-execute seed events through responders to verify determinism. The resulting world must match the permissive projection; if it diverges, the run is non-deterministic under the current responders.

Source code in kando/runtime.py
def replay(self, strict: bool = False) -> World:
    """Replay the run.

    Permissive (default): reproject the ledger as-is — fast, no re-firing.
    Strict: re-execute seed events through responders to verify determinism.
        The resulting world must match the permissive projection; if it
        diverges, the run is non-deterministic under the current responders.
    """
    if not strict:
        return self.load()

    # Strict: re-run from root events through the full responder loop
    from kando.ledger.memory import MemoryLedgerStore
    all_events = list(self._ledger.read_all())
    seed_events = [e for e in all_events if not e.cause]
    if not seed_events:
        return self.load()

    replay_ledger = MemoryLedgerStore(self._ledger.stream_name() + ":strict-replay")
    replay_runtime = Runtime(
        ledger=replay_ledger,
        responders=self._responders,
        budget=Budget(max_events=len(all_events) * 2),
    )
    return replay_runtime.run(seed_events)

run(seed_events)

Main event loop: process seed events, fire responders, exhaust the queue.

Source code in kando/runtime.py
def run(self, seed_events: list[KandoEvent]) -> World:
    """Main event loop: process seed events, fire responders, exhaust the queue."""
    world = self.load()
    queue: deque[KandoEvent] = deque(seed_events)

    while queue:
        event = queue.popleft()
        self._ledger.append([event])
        apply(world, event)

        if self._handle_budget(event, world):
            return world

        self._dispatch(event, world, queue)

    return world

Usage

from kando.runtime import Runtime
from kando.ledger.memory import MemoryLedgerStore
from kando.responders.budget import Budget
from kando.cache.llm import LLMCache
from kits.diligence.kit import create_kit, seed_from_goal

run_id = "my-run-001"
store = MemoryLedgerStore(run_id)
cache = LLMCache()

runtime = Runtime(
    ledger=store,
    responders=create_kit(),
    budget=Budget(max_events=500, max_wall_seconds=60.0),
    cache=cache,
)

seed = seed_from_goal("Evaluate Stripe", run_id)
world = runtime.run(seed)

# World is also accessible via load() at any time
world2 = runtime.load()

Strict replay

# Permissive replay: reproject the ledger (fast, no re-firing)
world = runtime.replay(strict=False)

# Strict replay: re-execute seed events through responders
world = runtime.replay(strict=True)

Strict replay is useful for verifying that your responders are deterministic. If the strict world diverges from the permissive world, you have non-deterministic responders.