Skip to content

Run Observability

Run observability is the fast operator read over recent run state. It answers bounded questions — counts, cue phase, latest run_id, routing — without loading the full Agent Debugger event stream.

Think of it as three lenses on the same app-owned ledger builders in internal/app:

  1. Session summary — “what does this session’s recent window look like?”
  2. Cue ledger — “who acts next, with what expected evidence?”
  3. Trace lens — “which tools ran in this one run?”

The summary and cue ledger now also carry a bounded progress-owner digest for the latest run. It distinguishes provider recovery/wait, active tool work, and terminal residue without forcing an immediate jump into /debugger.

This page teaches the path in order, with live TUI captures from a real provider turn.

QuestionStart hereThen if needed
Recent run counts / latest idStep 1 below
Next actor / expected evidenceStep 2/debugger for full history
Ordered tool calls in one runinspect_run_trace/debugger for non-tool events
Step-by-step auditAgent Debugger
Wedge, stall, or opaque runtime behaviorRuntime incident capture
Cross-run narrative or operator annotation trailJournal
Durable interruption or handoff stateSession continuity capsules
Ledger store healthRun ledger /run-ledger

Run one real turn, then:

/run-observability

Alias: /run_observability.

Run observability summary
Live BMO TUI: the /run-observability readout reports the bounded run summary (recent / active / completed run counts and the recent-run window), the turn-intent and cue phase, the latest run id, and the next-step view pointers (/debugger, /run_cue_ledger, inspect_run_trace) — the durable, session-family lens over what the agent has been doing.

Read the frame:

FieldTeaches
session_idCorrelation for HTTP/tools (local operator use)
recent_runs / recent_run_windowHow many rows feed the summary
active_runs / completed_runsWhether work is in flight
cue_phase / cue_countWhere cue serialization is in the turn
latest_run_idAnchor for trace reads
turn_state / prompt_segmentsLive turn intent merge
views: blockRouting — where to go next

The summary is read-only. It does not mutate runs or grant authority.

bmo config show-run-observability lists the family and truth contract without claiming live session access — the CLI runs out-of-process.

When the question is who acts next, open the modal ledger:

/cue-ledger

Aliases: /cue_ledger, /run-cue-ledger, /run_cue_ledger.

Run cue ledger
Live BMO TUI: `/cue-ledger` projects the session-family run rows into active actors, cue phase, expected next action, and recovery controls — the “who acts next?” read.

Cue rows project the same session-family run list into:

  • active actors
  • cue state and phase
  • expected next action
  • expected evidence and recovery controls

Use this before spawning follow-on work — it is the “cue book” complement to the debugger’s “film reel.”

The same non-interactive bmo run --quiet envelope also carries run_summary.enforcement when you need a bounded answer to “what did the guardrails do during this invocation?” without replaying the full trace.

For the tool-execution gate, read:

  • enforcement_projection_statusmeasured means BMO captured an invocation-start baseline and the counters below are a real invocation delta; unavailable means no baseline existed, so the projection refuses to attribute process-lifetime totals to this one run.
  • tool_gate.invocation_by_outcome — bounded mix of allow, deny, require_approval, and unevaluated.
  • tool_gate.invocation_by_deny_class — bounded denial partition that keeps “approval friction” separate from profile or session-mode denials.
  • tool_gate.invocation_denied_unclassified and tool_gate.invocation_denied_unmapped_evaluator — drift signals. Both are expected to stay at zero; a nonzero value means the deny vocabulary or evaluator mapping moved ahead of the projection.

The sibling run_summary.enforcement.verdict receipt is documented on Verdict grades.

For a bounded per-run trace without the broader event stream:

  • TUI / agent: inspect_run_trace with run_id (defaults to latest when bound)
  • HTTP: GET /v1/agent-runs/{run_id}/trace

The trace lens includes a tool-event list containing only kind=tool rows. It also projects lifecycle outcome, activity-ledger, and corridor_shadow evidence when those durable rows exist. Raw phases, lifecycle event rows, and assistant text stay in /debugger / get_agent_run_events.

SurfaceScopePurpose
bmo config show-run-observabilityconfig onlyDiscoverability; no live store
/run-observabilitysession familySummary hub + view routing
/cue-ledgersession familyNext-step cue projection
GET /v1/sessions/{id}/observabilitysession familyHTTP summary
GET /v1/sessions/{id}/run-cue-ledgersession familyHTTP cue twin
GET /v1/agent-runs/{run_id}/traceone runHTTP trace lens
session_observabilitysession familyNative agent summary
run_cue_ledgersession familyNative agent cue tool
inspect_run_traceone runNative agent trace lens
MCP twinssamebmo_get_session_observability, etc.

Live surfaces read shared app builders (SessionObservabilityPayload, RunCueLedgerPayload, RunTracePayload). HTTP, TUI, native tools, and MCP stay aligned without HTTP-as-internal-dependency.

A typical investigation after a bad turn:

  1. /run-observability — confirm recent_runs and copy latest_run_id
  2. /cue-ledger — see whether a cue is blocked or waiting on evidence
  3. /debugger — open the run list, then the timeline (see Agent Debugger)
  4. /run-ledger — if stores look wrong, read posture and ring tail ( Run ledger)

If the problem is no longer “what did this run do?” but instead “why did the runtime wedge?”, move to Runtime incident capture. If the question becomes “what is the dated story across runs or handoffs?”, leave this surface for Journal or Session continuity capsules.

flowchart LR
  turn[Completed turn] --> summary["/run-observability"]
  summary --> cue["/cue-ledger"]
  summary --> dbg["/debugger timeline"]
  dbg --> fork[fork checkpoint]
  summary --> trace[inspect_run_trace]
  posture["/run-ledger"] -.-> summary