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: falseWhat 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.
Summary
Functions
Raises where: why unless condition holds. Compiles to nothing in production.
Whether the strict checks are compiled in. False in a release.
Every way a document can be internally inconsistent, in one predicate.
Raises when a document is internally inconsistent. Compiles to nothing in production.
Raises outright — for a branch that should be unreachable. Compiles to nothing in production.
Functions
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.
@spec enabled?() :: boolean()
Whether the strict checks are compiled in. False in a release.
@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.
Raises when a document is internally inconsistent. Compiles to nothing in production.
Raises outright — for a branch that should be unreachable. Compiles to nothing in production.