# `StatifierBlocks.SlotValidation`
[🔗](https://github.com/riddler/statifier_blocks/blob/v0.44.1/lib/statifier_blocks/slot_validation.ex#L1)

ADR-0002 decision 6's two properties, checked over a whole document
against a palette: declared slots are the complete set a block may carry
(`:undeclared_slot`), and a slot's child count must satisfy the arity
its type declares for it (`:slot_arity_violated`).

One implementation, consulted by both the editor and the compiler,
the same way `StatifierBlocks.Assignability` is - both are
passed the same palette and the same document, and neither owns a
private copy of either rule.

## Ordering

`validate/2` walks `Document.blocks/1` (pre-order, root first) and
concatenates each block's findings, so the result is already in the
order a caller wants to report them. Within one block, arity findings
come first, in `slots/1`'s own declaration order; undeclared-slot
findings come after, in UTF-8-sorted slot-name order (matching
`Document.blocks/1`'s own sorted slot traversal).

## Degradation

A block whose type fails `Palette.resolve/2` contributes no findings of
either kind - the same permissive default `Assignability.produces/4`
uses, because unresolvability is ADR-0002 decision 3's own finding,
reported by the walk that owns it, not this one.

## Totality, and where the `slots/1` precondition holds

`module.slots(resolved.config)` is called with no rescue, exactly as
`Assignability.valid_targets/4` calls `module.slots/1`: this package's
rule is that nothing is rescued to a default. ADR-0002 decision 6
guarantees `slots/1` returns without raising only for a config
`validate_config/1` accepts, so `validate/2` is total over any document
and palette whose block types honour that guarantee for the configs
they are handed. In the compiler this precondition holds by
construction - the Config stage runs `validate_config/1` over every
block and stops the pipeline before Structure runs - but a caller
outside the compiler (an editor, mid-edit) wanting the same guarantee
runs `validate_config/1` first.

# `finding`

```elixir
@type finding() ::
  {:slot_arity_violated, StatifierBlocks.Block.id(),
   StatifierBlocks.Block.slot_name(), StatifierBlocks.BlockType.slot_arity(),
   non_neg_integer()}
  | {:undeclared_slot, StatifierBlocks.Block.id(),
     StatifierBlocks.Block.slot_name(), non_neg_integer()}
```

# `arity_satisfied?`

```elixir
@spec arity_satisfied?(StatifierBlocks.BlockType.slot_arity(), non_neg_integer()) ::
  boolean()
```

Whether `count` children satisfies `arity`, per ADR-0002 decision 6's
four-row table: `:any` admits any count; `:at_least_one` needs `count >=
1`; `:exactly_one` needs `count == 1`; `:zero_or_one` needs `count <= 1`.

# `validate`

```elixir
@spec validate(StatifierBlocks.Palette.t(), StatifierBlocks.Document.t()) ::
  :ok | {:error, [finding()]}
```

Every slot-arity and undeclared-slot finding in `document` against
`palette`, in `Document.blocks/1` pre-order. `:ok` when the list is
empty.

Per block: `Palette.resolve(palette, block)`. On `{:error, _}` the
block contributes nothing. On `{:ok, module, resolved}`,
`module.slots(resolved.config)` is the declaration list checked -
`resolved.config`, not `block.config`, because the migrated config is
what the type's own declarations are read against (ADR-0002 decision
8), and it is what the compiler's Resolve stage already uses.

---

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