# `MishkaCmsCore.Runtime.PageBuilder.Strict`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/strict.ex#L5)

What the editor refuses to let past in development, and forgives in production.

## Why this exists

Nine bugs were found in the editor's history over one stretch of work, and eight of them were
silent: a filter comparing against a pinned nil, which Postgres reads as `= NULL` and never
matches; a relationship that was never preloaded, so every row claimed to be by "System
administrator"; a capped list used as a "did anything change?" signal, which saturated and stopped
the editor recording anything at all. None raised. Each degraded to something that looks exactly
like a legitimate empty answer, and each was found by somebody noticing that a screen looked wrong.

The forgiving behaviour is right in production — an editor that takes itself down because one
plugin returned the wrong shape is worse than one that carries on. It is wrong everywhere else,
because during development an empty answer that should have been impossible is the single most
valuable thing you can be told about.

So the two are separated. Every check here raises in `:dev` and `:test` and compiles to nothing in
`:prod` — `check!/2` and its friends are macros, so a release contains no call, no closure and no
branch.

    config :mishka_cms_core, builder_strict: false

## What to check with it

Conditions that are impossible unless something is already broken. Not user error, not a refused
op, not a document that failed to parse — those are answers. A dangling child id in a document, a
write to a table that is not there, a history entry pointing at nothing: those are bugs, and every
one of them used to travel silently to somewhere far away from its cause.

# `check!`
*macro* 

Raises `where: why` unless `condition` holds. Compiles to nothing in production.

The condition is not evaluated at all in a release, so an expensive check costs a developer time
and costs a deployment nothing.

# `enabled?`

```elixir
@spec enabled?() :: boolean()
```

Whether the strict checks are compiled in. False in a release.

# `inspect_tree`

```elixir
@spec inspect_tree(MishkaCmsCore.Runtime.PageBuilder.Tree.t()) ::
  :ok | {:error, String.t()}
```

Every way a document can be internally inconsistent, in one predicate.

A `children` list naming an id that is not in `nodes` is the one that matters most: the serializer
is deliberately total, so it skips what it cannot find — and a save writes what the serializer
produced. A subtree that vanished this way leaves markup that parses perfectly, so the save's own
"is this readable?" check cannot see it. The document is simply smaller than it was, silently.

Returns `:ok` or `{:error, reason}`; `tree!/2` is the raising form the mutation funnel uses.

# `tree!`
*macro* 

Raises when a document is internally inconsistent. Compiles to nothing in production.

# `violation!`
*macro* 

Raises outright — for a branch that should be unreachable. Compiles to nothing in production.

---

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