# `Statifier.Effect.Trace.CondsEvaluated`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.6.0/lib/statifier/effect/trace/conds_evaluated.ex#L1)

Trace payload for `{:trace, %__MODULE__{}}` - emitted once per selection
round in which at least one *written* `cond` was evaluated through
`Statifier.Evaluator`, by both `select_transitions/2` and
`select_eventless_transitions/1`.

This is the guard seam. Predicator is deliberately telemetry-silent
(predicator's ADR-0016), so an evaluation that fails, or one that quietly
disables a transition a chart author expected to fire, leaves no trace of
its own anywhere in the family. This payload is the engine-side answer:
`evaluations` is the ordered list of the round's guard outcomes, in walk
order - the same order `Statifier.Interpreter.Selection` raises
`error.execution` in - one entry per evaluation the round performed.

Each entry is a plain map, never a `%Statifier.Machine.Transition{}`
(`docs/observability.md` constraint 3):

- `t_index` - the transition whose `cond` was evaluated. Its
  `cond_location` resolves through `Statifier.Machine.transition/2`.
- `outcome` - `:enabled` (`{:ok, true}`), `:disabled` (`{:ok, false}`), or
  `:error` (an evaluation error *or* a non-boolean result, which spec
  5.9.1 joins into one case - see
  `Statifier.Interpreter.Selection.condition_match/2`).
- `reason` - the `{:error, reason}` term for an `:error` outcome, `nil`
  otherwise. The same term the round's `error.execution` carries as its
  `data`, so a consumer can join the two without re-deriving either.

## Why a transition with no `cond` is absent, and why an empty round emits nothing

`Trace.TransitionsSelected` is emitted on every selection round including
the empty one, because a round always *selects* - the empty set is a
result. A round with no written `cond` on any candidate transition
performs no evaluation at all, so there is no result to report and this
effect is not emitted; and a `nil` `cond` short-circuits to `{:ok, true}`
ahead of `Statifier.Evaluator` (`Selection.condition_match/2`), so it is
not an evaluation either and gets no entry. The commitment is one entry
per Predicator call, and no effect when the round made none.

Built with `new/2`, never a struct literal, so
`macrostep`/`microstep`/`round` are always stamped from the
`Statifier.MachineState` at hand.

# `evaluation`

```elixir
@type evaluation() :: %{
  t_index: non_neg_integer(),
  outcome: :enabled | :disabled | :error,
  reason: term() | nil
}
```

One guard evaluation: the transition's `t_index`, what the evaluation
answered, and the failure term when it failed.

# `t`

```elixir
@type t() :: %Statifier.Effect.Trace.CondsEvaluated{
  evaluations: [evaluation()],
  macrostep: non_neg_integer(),
  microstep: non_neg_integer(),
  round: non_neg_integer()
}
```

# `new`

```elixir
@spec new(machine_state :: Statifier.MachineState.t(), fields :: keyword()) :: t()
```

Stamps `macrostep`/`microstep`/`round` from `machine_state` and sets
`fields` (`:evaluations`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
