Skip to content

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.

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.

ActionSeverityMeaning
ingest_skippedinfoDiagnostic batch was non-authoritative; no rows ingested.
ingest_failedwarnIngest pipeline failed (validation or store).
ingest_scope_failedwarnWorkspace scope resolution failed.
ingest_pool_failedwarnWorker-pool start failed; ingest disabled.
ingest_worker_submit_failedwarnWorker-pool submit rejected the ingest job.
link_persist_failedwarnCross-finding link batch failed to persist.
validation_findings_ingestedinfoVerify pass ingested operation findings.
validation_findings_resolvedinfoVerify 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.

StateMeaning
openDefault on first ingest; visible to read paths.
resolvedClosed verdict; remains visible.
suppressedHidden from the default read path.
accepted_riskVisible with an explicit risk badge.

Illegal transitions are rejected at apply time; the graph is closed and property-tested (maintainer topic above).

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.

Finding lifecycle status readout
Live BMO TUI: `/findings` reports ingest counters (in-flight, failure count, link flush failures), the process-scoped 16-event `finding_lifecycle.action` ring, and tracing filter hints — the same bounded snapshot as `bmo config show-finding-lifecycle` and `get_finding_lifecycle_status`.

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.

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.

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.

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).

  • 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) with session_id, finding_count, debounced, error. Pair the preceding ingest_skipped (Info) records by session_id to see whether the same session was previously short-circuited by the trust gate.

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.