A cell's live verdict: the one-word answer, rolled up from its rows' statuses.
This is the engine piece a host used to hand-write (the portal's
ModelEval.Verdict); it's domain-neutral, so it lives here and the host keeps
only its own addressing sugar (for_guarantee, for_control, typed detail
joins) on top.
A verdict is:
%{status: :green | :findings | :pending | :unknown,
failing: non_neg_integer(), pending: non_neg_integer(), sample: [key]}computed from the cell's status histogram plus a failing-key sample, both read
from the node's own resource through ReactiveDag.Node.Rows.
Scoping to a subset
This used to read the coordination tuple, which offered a :key_scope
selector — prefix and glob shapes over the serialized cell key — because a
key was all the tuple had to filter on. A resource has columns, so scoping is
now an ordinary Ash read: filter the rows yourself and hand the result to
rollup/2.
MyApp.CategoryHealth
|> Ash.Query.filter(tenant_id == ^tenant)
|> Ash.read!()
|> Enum.frequencies_by(& &1.status)
|> ReactiveDag.Verdict.rollup(sample)That is more expressive than the selector was — any column, not just a key prefix — and it honors policies, which a raw tuple query never did.
Summary
Functions
The live verdict for cell. Options
Roll a MAP of per-cell verdicts into a single status (e.g. a control over its
guarantees): findings if any has findings, else pending if any pending, else
green if any is green, else unknown. The dual of for_cell at the group level.
Roll a %{status => count} histogram + a failing sample into a verdict — the
status decision, exposed so a host can roll up counts it gathered another way.
Types
@type verdict() :: %{ status: :green | :findings | :pending | :unknown, failing: non_neg_integer(), pending: non_neg_integer(), sample: [String.t()] }
Functions
@spec for_cell( ReactiveDag.Cell.t(), keyword() ) :: verdict()
The live verdict for cell. Options:
:sample_limit— cap on the failing-key sample (default 5).
total == 0 → :unknown (nothing evaluated — distinct from green). Any failing
→ :findings; else any pending/stale → :pending; else :green.
Takes a %ReactiveDag.Cell{} rather than a cell id: the rows live in the
node's resource, and the cell is what knows which resource that is. Get one
from plan.cells[id].
@spec roll_group([verdict()]) :: :green | :findings | :pending | :unknown
Roll a MAP of per-cell verdicts into a single status (e.g. a control over its
guarantees): findings if any has findings, else pending if any pending, else
green if any is green, else unknown. The dual of for_cell at the group level.
@spec rollup(%{required(String.t() | nil) => non_neg_integer()}, [String.t()]) :: verdict()
Roll a %{status => count} histogram + a failing sample into a verdict — the
status decision, exposed so a host can roll up counts it gathered another way.