Finding lifecycle
Finding lifecycle keeps a durable, inspectable record of the issues BMO discovers — from the moment a finding is first surfaced through to resolved, suppressed, or accepted-as-risk. Instead of scraping logs to figure out what the agent flagged and whether it was ever closed out, you get a stable read model across the TUI, CLI, and agent tools.
Reach for it when you want to answer “what did BMO find, and what’s still open?” — for example checking whether a verify pass resolved earlier findings, auditing why a finding was suppressed, or spotting that diagnostic ingest silently failed mid-session.
The records are metadata-only by design (see Privacy posture), so you can keep this observability on without leaking finding bodies, source ranges, or file paths.
Bounded actions
Section titled “Bounded actions”The finding-lifecycle lane ingests durable findings, enforces a closed
lifecycle-state transition graph, and emits paired finding_lifecycle.action
slog records through a bounded action enum. Records land in slog and a
process-scoped ring buffer (capacity 16).
Package layout, emit helpers, and enum extension rules are maintainer-owned —
see the
Finding lifecycle topic
and the
Finding lifecycle recipe block.
| Action | Severity | Meaning |
|---|---|---|
ingest_skipped | info | Diagnostic batch was non-authoritative; no rows ingested. |
ingest_failed | warn | Ingest pipeline failed (validation or store). |
ingest_scope_failed | warn | Workspace scope resolution failed. |
ingest_pool_failed | warn | Worker-pool start failed; ingest disabled. |
ingest_worker_submit_failed | warn | Worker-pool submit rejected the ingest job. |
link_persist_failed | warn | Cross-finding link batch failed to persist. |
validation_findings_ingested | info | Verify pass ingested operation findings. |
validation_findings_resolved | info | Verify pass resolved one or more findings. |
finding_lifecycle.action records carry feature=finding_lifecycle,
the bounded action enum, and metadata-only attributes
(session_id, finding_count, lifecycle_status, reason, error,
debounced, count). Bodies and diagnostic source ranges never
enter the ring.
Lifecycle states
Section titled “Lifecycle states”| State | Meaning |
|---|---|
open | Default on first ingest; visible to read paths. |
resolved | Closed verdict; remains visible. |
suppressed | Hidden from the default read path. |
accepted_risk | Visible with an explicit risk badge. |
Illegal transitions are rejected at apply time; the graph is closed and property-tested (maintainer topic above).
Operator surfaces
Section titled “Operator surfaces”/findings (TUI)
Section titled “/findings (TUI)”Aliases: /finding-lifecycle, /finding_lifecycle. Read-only.
Renders ingest counters (InFlight, FailureCount, last success /
failure timestamps), the most-recent ring entries, and a one-line
summary the sidebar reuses.
bmo config show-finding-lifecycle (CLI)
Section titled “bmo config show-finding-lifecycle (CLI)”Same snapshot as /findings for the current process. CLI and TUI
share the ring only when run in the same process (the ring is
process-global, not persisted). A regression test enforces byte-equal
JSON parity between the CLI and the App-level snapshot.
Sidebar
Section titled “Sidebar”The TUI sidebar shows Findings: with one of:
- empty — no in-flight ingest and no recent warn/error events.
ingest x<n>—<n>ingest passes are currently in flight.warn: <action>— the most recent ring entry was warn or error severity.
Permissive session filter: events without a session id are included alongside the current session, so cross-session worker failures still surface.
Agent tool
Section titled “Agent tool”list_recent_finding_lifecycle_events returns JSON
{ "ring_capacity", "returned", "events" } with snake_case event
fields. Optional session_id filter and limit (1..16, default
ring capacity). The same data drives /findings and
bmo config show-finding-lifecycle.
list_findings (read-only, ExposeFirst) lists the per-session
finding rows the read path exposes after the trust matrix filter.
MCP tool twins
Section titled “MCP tool twins”bmo_list_findings and bmo_list_recent_finding_lifecycle_events
expose the same JSON contracts to MCP-host agents (Cursor, Claude
Desktop, etc.). Parity with the in-process agent tools is covered by the
maintainer MCP tool test lane (see
docs/topics/quality/ci.md).
Failure modes the lane catches
Section titled “Failure modes the lane catches”- Pool start failure on app boot.
ingest_pool_failed(Warn) at startup; subsequent ingest calls become no-ops. The sidebar carries the warn label until the next session start. - Cross-finding link persistence failure.
link_persist_failed(Warn) when a batch flush hits the store; ingest continues for new findings. - Diagnostic ingest failed mid-flight.
ingest_failed(Warn) withsession_id,finding_count,debounced,error. Pair the precedingingest_skipped(Info) records bysession_idto see whether the same session was previously short-circuited by the trust gate.
Privacy posture
Section titled “Privacy posture”Lifecycle records carry only metadata. Specifically out of scope:
- Finding body text and operator notes
- Diagnostic source-range contents
- File paths beyond the workspace-scoped session id
The ring truncates error_summary and lifecycle_status to keep
cardinality predictable. If a future arm needs a structured signal,
add it as a new LifecycleAction enum constant — do not stuff data
into free-text attributes.
