Skip to content

Run ledger

The run ledger is how BMO remembers runs after the moment passes: durable agent_runs / agent_run_events for the agent runtime, plus an optional in-memory OpenAI-compat ring when that HTTP surface is enabled.

Operators rarely “configure the ledger” — you read posture, confirm stores are healthy, then follow a run into Run observability or the Agent Debugger.

LedgerStorageWhen it matters
agent_runs (durable)SQLite in the app data dirAlways on with the daemon — powers /debugger, spawn history, APIs
OpenAI-compat (in-memory)Process ring bufferOnly when [options.openai_compat] enabled = true and HTTP/autopilot serves chat

runledger.BuildSnapshot feeds one formatted posture string to:

  • bmo config show-run-ledger (CLI)
  • /run-ledger (TUI slash; aliases /run_ledger, /runledger, /ledger)
  • Runs sidebar chip
  • run_ledger_status (agent tool)

Every surface still shares the same posture shape, but the CLI is now an explicit detached projection: it reports durable-store and config truth from the current process while leaving the live ring and sticky runtime tracker to the attached runtime surfaces.

After at least one real turn (so the ring has something to show), run:

/run-ledger
Run ledger posture
Live BMO TUI: `/run-ledger` prints the shared posture readout — durable `agent_runs` state, OpenAI-compat ledger gate, ring capacity, outcome histogram, and hash-redacted recent `run_ledger.*` events.

How to read the report:

BlockMeaning
agent_runs: bootstrappedDurable store is present — debugger/API can list rows
openai_compat: …Whether the compat ledger is enabled, idle, or degraded
reconciliation: …How the durable and OpenAI-compat ledgers relate right now
sticky-degraded noteRecent persist ops failed; clears on next success
ring capacity / saturatedIn-memory telemetry bounds (metadata only)
outcomes (action × outcome × count)Histogram of ledger operations
recent eventsHash-redacted run_ledger.fired / run_ledger.action tail

IDs in this surface are FNV32-hashed prefixes — never raw run bodies, session text, or stack traces.

bmo config show-run-ledger does not claim another process’s in-memory ring. It can truthfully tell you whether the local agent_runs store bootstrapped and whether OpenAI-compat is enabled in config, but it reports the live ring and sticky-degraded tracker as unavailable until you inspect an attached runtime surface such as /run-ledger, GET /v1/run-ledger/posture, run_ledger_status, or bmo_get_run_ledger_status.

The shared posture family now calls out how the two ledgers relate:

StateMeaning
alignedThe current durable and OpenAI-compat states are internally consistent. This includes the normal config-disabled OpenAI-compat case.
detachedYou are reading the detached CLI projection, so live ring and sticky recovery evidence are intentionally unavailable.
one_sided_unavailableOne ledger is observable and the other is not. Treat this as a fail-closed status mismatch, not as healthy parity.
degradedOne ledger is currently degraded, including sticky OpenAI-compat persistence failure.
recoveringRecent bounded ring evidence shows a failure followed by a success. The live runtime is healthy again, but operators should still treat it as an active recovery window.

Restart keeps durable agent_runs rows and OpenAI-compat retention policy, but the process-local recent ring starts empty again. Detached inspection after restart should therefore show durable-store truth without replaying old live runtime telemetry.

Step 2 — Confirm a session has runnable history

Section titled “Step 2 — Confirm a session has runnable history”

Switch to the session-family lens:

/run-observability

You should see recent_runs ≥ 1, latest_run_id, and view pointers to /debugger and /cue-ledger. See Run observability.

QuestionGo to
What happened step by step?Agent Debugger/debugger
Who acts next, with what evidence?/cue-ledger on Run observability
Which tools fired in one run?inspect_run_trace / HTTP trace lens

When compat is enabled, walk one HTTP completion end-to-end:

  1. /run-ledger — confirm not sticky-degraded.
  2. bmo openai-compat runs list --limit 10 — find a recent request id.
  3. bmo openai-compat runs events <request_id>chat_started → tool events → chat_done.
  4. If run_ledger.action … outcome=failed error_category=persist_failed appears in the posture ring, treat the ledger as degraded until the next successful op.
  5. If posture moves from degraded to recovering, confirm the next attached /run-ledger read stays healthy before treating the incident as closed.

Enable compat in bmo.toml when you need the HTTP ledger:

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

Pair with bmo service start http or bmo service start autopilot.

Each operation emits paired records into the bounded ring (capacity 32):

  • run_ledger.fired — one per op entry
  • run_ledger.action — bounded ledger, action, outcome after completion

Filter examples:

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

On OpenAI-compat persist failure the in-process tracker:

  1. Sets sticky-degraded visible on the live runtime surfaces: slash, sidebar, run_ledger_status, bmo_get_run_ledger_status, and GET /v1/run-ledger/posture.
  2. Publishes runevents.SubsystemDegraded.
  3. Clears on the first subsequent successful op (SubsystemRecovered).

Restart clears the flag — it is process-scoped.

  • run_ledger_status — live JSON posture twin; safe, read-only, no prompt.
  • list_recent_run_ledger_events — raw bounded ring export; safe, metadata-only, and distinct from durable row/event APIs.
  • list_agent_runs / get_agent_run_events — same durable rows as /debugger.
  • session_observability / run_cue_ledger / inspect_run_traceRun observability family.