Cyclium.Findings (Cyclium v0.1.7)

Copy Markdown View Source

Context for finding queries and lifecycle operations.

Read-path: strategies query active findings during init/2. Write-path: post-converge persists raise/update/clear actions.

Subject queries use denormalized subject_kind and subject_id columns instead of JSON operators for SQL Server 2017 compatibility. Upserts use transactions (read-then-write) instead of ON CONFLICT for SQL Server compatibility.

Summary

Functions

Query active findings with flexible filters.

Mode-aware query: automatically prefixes :actor and :finding_key filters when the episode is a dry run with persist_findings enabled.

Walks the causal chain upward from a finding, returning ancestors.

Returns active child findings caused by the given finding key.

Count active findings matching the given filters. Accepts same opts as active_for/2.

Persist a finding action from a ConvergeResult.

Returns true if an active finding with the given key was updated within window_ms milliseconds.

Returns the root cause finding for the given finding key.

Functions

active_for(filters, opts \\ [])

Query active findings with flexible filters.

Examples

Cyclium.Findings.active_for(actor: :po_status, class: "po_stalled")
Cyclium.Findings.active_for(subject: %{kind: "po", id: "PO-1955"})
Cyclium.Findings.active_for(finding_key: "po_stalled:PO-1955")
Cyclium.Findings.active_for(class: "non_responsive")

Options

  • :limit — max rows to return
  • :offset — rows to skip (default: 0)
  • :exclude_archived — when true, excludes findings with a non-nil archived_at (default: false)
  • :env — override the env scope (defaults to Cyclium.Env.current()). Pass a string to read another env's findings (demo/staging inspection or backfill), or nil to target the unset/default env explicitly.

active_for_mode(filters, episode, opts \\ [])

Mode-aware query: automatically prefixes :actor and :finding_key filters when the episode is a dry run with persist_findings enabled.

In live mode (or dry runs without persist_findings), behaves identically to active_for/2. In dry run mode with a persist prefix, rewrites filter keys so strategies can transparently read their own dry run findings.

Examples

# In a strategy's init/2, pass the episode to get mode-aware results:
Cyclium.Findings.active_for_mode([actor: "po_monitor"], episode)

# Dry run with persist_findings: true → queries actor: "dry_run:po_monitor"
# Live mode → queries actor: "po_monitor" (unchanged)

causal_chain(finding_key, opts \\ [])

Walks the causal chain upward from a finding, returning ancestors.

Starting from the given finding, follows caused_by_key links up to max_depth levels. Returns a list of findings from immediate parent to root (oldest ancestor).

Options

  • :max_depth — maximum chain depth (default: 10)

caused_by(finding_key)

Returns active child findings caused by the given finding key.

count_active(filters \\ [], opts \\ [])

Count active findings matching the given filters. Accepts same opts as active_for/2.

get(id)

get!(id)

persist_finding(arg, episode)

Persist a finding action from a ConvergeResult.

Actions

  • {:raise, params} — upsert active finding (last writer wins on mutable fields)
  • {:update, key, changes} — update mutable fields on an active finding
  • {:clear, key} — idempotent clear (set status to :cleared)
  • {:clear, key, reason} — clear with reason stored in evidence_refs

recent?(finding_key, window_ms)

Returns true if an active finding with the given key was updated within window_ms milliseconds.

Useful for per-subject dedup in strategies — skip re-runs when a recent finding already exists.

Examples

Cyclium.Findings.recent?("client:advisor:123", :timer.minutes(5))

root_cause(finding_key, opts \\ [])

Returns the root cause finding for the given finding key.

Walks up the causal chain and returns the first finding with no caused_by_key. Returns nil if the finding itself is not found.