A table spec plus rows in, a struct with per-cell outcomes, first-match-wins selection and expectation status out.
Why expected is compared against selection, not raw truth
core.branch (lib/statifier_blocks/core/branch.ex, ADR-0002 decision 10)
stores an ordered arms list and tries them in order: the first arm whose
condition holds runs, and every later arm is skipped even if its own
condition would also hold. That ordering is a config-level fact, not an
implementation detail - the branch's moduledoc says arms are tried in order
and slots/1 returns them in that order followed by otherwise.
So a truth table over a branch has to model the same thing a branch does:
which arm is taken, not which arms are individually true. A cell here
carries both readings. outcome is the raw {:ok, boolean()} | {:error, reason} from Predicates.evaluate/2 - what the expression alone says.
selected? is true | false | :undecidable, from one ordered
first-match-wins pass over the row's columns, mirroring what core.branch
would actually run. expected, a row's declared answer for one column, is
compared against selected? rather than outcome, because "does this
fixture behave like the branch it describes" is a question about which arm
wins, not about which conditions are independently true. Two columns can
both raw-evaluate true in the same row; at most one of them is selected.
The otherwise column
At most one column may carry source: nil - the otherwise column - and
it is never evaluated as an expression (outcome: nil). It must be the
last column if present, because it is the branch's fallback arm and a
fallback that could still be overridden by a later real arm would not be a
fallback. build/2 rejects a spec that breaks either rule before touching
any row.
The five-value status
status is deliberately wider than a true | false | unset tri-state,
because "the row's expectation was contradicted" and "this cell's answer is
unknowable" are different findings that a renderer, and an author reading
one, need to tell apart:
:match-selected?is a boolean and equalsexpected.:mismatch-selected?is a boolean and differs fromexpected.:unchecked-selected?is a boolean but the row declares noexpectedvalue for this column.:error- this column's ownoutcomeis an error; nothing about selection can be said until the expression itself is fixed.:undecidable- this column's ownoutcomeis fine, but an earlier column in the row errored, so whether this column would even have been reached is unknown. Reportingfalsehere would claim "no arm before this one matched", which is a guess this package does not make.
A row's bindings can also fail to build a context at all (an undefined
variable, a binding conflict). Then the whole row carries error: reason
and cells: [] rather than guessing at any column - a renderer checks
row.error first.
Summary
Functions
Builds a %TruthTable{} from a table spec and a list of row specs.
Flattens every cell's status across every row, in row-then-column order,
so a caller can assert a whole table's shape in one pattern match.
Types
@type t() :: %StatifierBlocks.Predicates.TruthTable{ columns: [StatifierBlocks.Predicates.TruthTable.Column.t()], description: String.t() | nil, name: String.t(), paths: [String.t()], rows: [StatifierBlocks.Predicates.TruthTable.Row.t()] }
Functions
@spec build(spec :: map(), rows :: [map()]) :: {:ok, t()} | {:error, build_error()}
Builds a %TruthTable{} from a table spec and a list of row specs.
Validates the spec first - column keys must be unique
({:error, {:duplicate_column, key}}), at most one column may have
source: nil and it must be last ({:error, {:otherwise_not_last, key}}),
and every column's source must be a binary or nil
({:error, {:invalid_source, key}}). A malformed spec is an error return;
a failing cell is data, because showing which cells fail is the point of
the table - rows still build and render around a bad cell or a bad row.
@spec statuses(t()) :: [StatifierBlocks.Predicates.TruthTable.Cell.status()]
Flattens every cell's status across every row, in row-then-column order,
so a caller can assert a whole table's shape in one pattern match.