Skip to content

Run ledger

The run ledger is BMO’s persistence and observability surface for runs: the durable lifecycle records of agent spawns, mesh delegations, A2A invocations, OpenAI-compat chat completions, and any other long-running operation that should survive restart and be auditable later.

It is two ledgers behind one read model:

  • agent_runs (durable) — SQLite-backed agent_runs and agent_run_events tables, owned by the daemon and present whenever the agent runtime is wired.
  • OpenAI-compat (in-memory) — gated by options.openai_compat.enabled; bounded run history exposed by the in-process HTTP server. A sticky-degraded tracker promotes any persistence failure to the operator surfaces and only clears on the next successful op.

The durable agent ledger is always on when the daemon is running. It is required for bmo agents history, the agent debugger, and resumable spawn inspection. There is no config knob.

The OpenAI-compat ledger is off by default. Enable in bmo.toml:

[options.openai_compat]
enabled = true
tool_policy = "chat_only" # or "auto_approve"
ambiguity_policy = "ask"
model_override_policy = "reject"

Pair with bmo serve to expose the OpenAI-compat HTTP surface.

SurfacePurpose
bmo config show-run-ledgerPosture summary: agent_runs state, OpenAI-compat state plus sticky-degraded flag, ring capacity and saturation, action/outcome histogram, recent ring entries
/run-ledger (TUI slash; aliases /run_ledger, /runledger, /ledger)Same posture in the chat transcript, sharing the CLI formatter
Runs sidebar chipLive status badge: ready, enabled, degraded, server idle, store off
run_ledger_status (agent tool)Read-only posture as JSON for agents
runledger.BuildSnapshotLibrary accessor used by every surface above

The same runledger.PostureSnapshot flows through CLI, slash, sidebar, and agent-tool layers. This is the formatter-divergence mitigation called out in the maturity-iteration plan: one read model, one renderer, every surface byte-matches.

Each operation emits a paired record into the bounded process-global ring (capacity 32):

  • run_ledger.fired — one per op entry, before the action runs.
  • run_ledger.action — bounded ledger, action, and outcome enum after the op completes.

Filter examples:

run_ledger.fired
run_ledger.action
run_ledger.action ledger=openai_compat outcome=failed
run_ledger.action error_category=persist_failed

Field discipline (KTD-5): records carry only FNV32-hashed run/session prefixes, bounded scalar metadata (action, outcome, error_category, latency_ms, level), and timestamps. Raw run IDs, request IDs, session IDs, message bodies, and stack traces never reach the log layer or the posture surfaces.

When the OpenAI-compat run store fails to persist, the in-process tracker:

  1. Sets a sticky degraded=true flag, visible immediately in CLI, slash, sidebar, and the run_ledger_status tool.
  2. Publishes a runevents.SubsystemDegraded event.
  3. Clears the flag and publishes runevents.SubsystemRecovered on the first subsequent successful op.

This is intentional: a one-off persist failure stays visible long enough for an operator to notice, but recovers automatically on its own. The flag is process-scoped; restarting the daemon clears it.

Walk a single OpenAI-compat run end-to-end without leaving the operator seat:

  1. /run-ledger — confirm the ledger is enabled and not sticky-degraded.
  2. bmo openai-compat runs list --limit 10 — find the recent request ID.
  3. bmo openai-compat runs events <request_id> — print the per-event timeline, from chat_started through tool-call events to chat_done.
  4. If run_ledger.action ... outcome=failed error_category=persist_failed appears in step 1’s recent ring, the ledger has been degraded; check the serve logs and the agent_run_events table. The sticky flag will clear on the next success.
  • run_ledger_status — read-only posture as JSON, covering durable and in-memory ledgers, histogram, and KTD-5-redacted recent ring. It is safe on every OS, requires no permission prompt, and matches the CLI/slash surfaces.