The publish-time checks a host runs over a chart before it lets that chart reach a route (ADR-0005, decision 7).
This package has no publish step and gains none here. A host calls
these two functions from its own - when it saves a revision, when it
compiles a document in CI, when an author presses publish - and decides
for itself whether a finding blocks the save or only warns. Both are
pure and total: they read a compiled Statifier.Machine.t/0 and a
resolved StatifierRouter.Config.t/0, touch no process, no clock and
no database, and add no Statifier.Validator finding. The engine's
equivalent check refuses to add one for its own half (st-ADR-0069,
decision 3) and this one keeps that posture.
The two halves, and why they are two
A <send> meant for this package writes the host's processor in type
and the route name in target (ADR-0005, decision 1). Those two
slots fail independently, and nothing in the engine can check the
second.
unsupported_types/2answers the type half by composingStatifier.Send.Types.unsupported_sends/2over the snapshot this configuration hands statifier_persistence. A type the host never registered is 6.2.5'serror.executionat run time.unregistered/2answers the route name half, and it is this package's to build because the engine cannot build it. For a registered type the engine never parsestarget-Statifier.Send.Processor's moduledoc calls it "the processor's own opaque route string: the library never parses it" - so a chart that names its type correctly and then sends to a route name the host never registered gets nothing at all from the engine's check. At run time that send misses the registry, is recorded as a refusal and reported as an error, and the step still commits (ADR-0005, section 7). This function is what finds it before a chart ships.
What cannot be checked
Only a literal attribute can be checked. A targetexpr or a
typeexpr compiles to {:compiled, _, _} and resolves against the
datamodel at execute time, so no publish-time pass can know what it
will name. Such a send is never reported as a finding and never
silently dropped: unregistered/2 returns it under :unchecked, with
which attribute deferred it, so a host can surface the gap it cannot
close. The core's own check at execute time stays the backstop for
both, exactly as it is for the engine's function.
The reserved name
StatifierRouter.SendHandler.execution_target/0 is not a route and is
never reported. A send that writes it names another durable execution
(ADR-0006, section 1), and StatifierRouter.Config.new/1 refuses a
registry entry under it, so it is registered by reservation rather than
by the host.
An example
The impression-and-click join's outbound half writes two route names (ADR-0005). A host that registered only one of them learns which before the chart ships:
iex> {:ok, config} =
...> StatifierRouter.Config.new(
...> repo: MyApp.Repo,
...> delivery: MyApp.Delivery,
...> send_type: "myapp:sink",
...> route_adapters: %{"joined_records" => {StatifierRouter.RecordingRoute, %{}}}
...> )
iex> {:ok, machine} = Statifier.compile(StatifierRouter.DeliveryFixtures.join_sends())
iex> %{unregistered: findings, unchecked: []} =
...> StatifierRouter.Routes.unregistered(config, machine)
iex> Enum.map(findings, & &1.route)
["dead_letter"]
Summary
Types
One <send> of the configuration's own type whose literal target
names no registered route: the route name, and the <send> element's
location. route is nil when the send writes no target at all,
which names no route and so can never resolve.
What unregistered/2 answers, each list in c_index order.
One <send> this pass could not judge, and which attribute deferred
it: :typeexpr when the type is an expression, so whether the send is
this configuration's at all is unknown; :targetexpr when the type is
the configuration's and the route name is an expression.
Functions
Every <send> in machine whose literal type is this
configuration's :send_type and whose literal target names no route
in its :route_adapters, with the sends this pass could not judge
beside them. Both lists are in c_index order, which is document
order.
Every <send> in machine whose literal type is outside the set
this configuration registers, with the <send> element's location, in
c_index order.
Types
@type finding() :: %{route: String.t() | nil, location: Statifier.Parser.Location.t()}
One <send> of the configuration's own type whose literal target
names no registered route: the route name, and the <send> element's
location. route is nil when the send writes no target at all,
which names no route and so can never resolve.
What unregistered/2 answers, each list in c_index order.
@type unchecked() :: %{ reason: :typeexpr | :targetexpr, location: Statifier.Parser.Location.t() }
One <send> this pass could not judge, and which attribute deferred
it: :typeexpr when the type is an expression, so whether the send is
this configuration's at all is unknown; :targetexpr when the type is
the configuration's and the route name is an expression.
Functions
@spec unregistered(StatifierRouter.Config.t(), Statifier.Machine.t()) :: report()
Every <send> in machine whose literal type is this
configuration's :send_type and whose literal target names no route
in its :route_adapters, with the sends this pass could not judge
beside them. Both lists are in c_index order, which is document
order.
:send_type rather than the whole registered set is deliberate: it is
what StatifierRouter.SendHandler answers to, so it is exactly the set
of sends that will reach this package's registry at run time. A
configuration with no :send_type claims no send, and every list is
empty.
The reserved StatifierRouter.SendHandler.execution_target/0 is never
a finding (ADR-0006, section 1). An expression in target or type is
never a finding either; see the moduledoc.
@spec unsupported_types(StatifierRouter.Config.t(), Statifier.Machine.t()) :: [ Statifier.Send.Types.unsupported_send() ]
Every <send> in machine whose literal type is outside the set
this configuration registers, with the <send> element's location, in
c_index order.
This is Statifier.Send.Types.unsupported_sends/2 composed over the
Statifier.Send.Types snapshot on this configuration's
:persistence_options - the same snapshot every create and every step
of every delivery carries, so what this pass judges is what the
execution will be started with (ADR-0005, decision 6). That snapshot
holds the configuration's :send_type and every type its
:send_handlers names (ADR-0005, the Amendment of 2026-09-26). A
configuration carrying no snapshot is judged as "no declaration", under
which every non-built-in type is unsupported.
It cannot see a typeexpr, and says so in its own @doc; that half is
unregistered/2's :unchecked list.