# `Statifier.Validator.Error`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/validator/error.ex#L1)

The validator's own error shape: never raised, always collected into a
list. Mirrors `Statifier.Lowering.Error`'s shape (`reason`, `message`,
`location`) with character-identical field names, so a future common
diagnostic protocol can adopt both without either layer's reason union
leaking into the other's. The two layers' error lists are never observed
together - `Statifier.Validator.validate/2` only ever receives a document
lowering already accepted - so sharing the *shape* rather than the type is
enough.

`reason` is a closed tagged-tuple union, declared once in full here even
though initially only `:duplicate_id` has a constructor. Later additions
add a constructor per reason rather than reopening the type, following
`Statifier.Lowering.Error`'s own precedent.

`code/1` returns the reason tuple's tag atom - the stable error code the
bead's acceptance criteria ask for - while `reason` itself keeps carrying
the offending ids as data, the way the rest of the repo does.

# `owner`

```elixir
@type owner() :: {:initial, String.t() | nil} | {:history, String.t() | nil}
```

Which slot a shared transition-shape violation (`:transition_count`,
`:transition_missing_target`, `:transition_forbidden_attribute`) came
from: a state's `<initial>` element, or a `:history` state's own default
transition. The id is `nil` exactly when the owning state's own `id` is.

# `reason`

```elixir
@type reason() ::
  {:duplicate_id, id :: binary()}
  | {:empty_id}
  | {:unresolved_target, id :: binary()}
  | {:unresolved_initial, id :: binary()}
  | {:initial_not_descendant, id :: binary(), parent_id :: binary()}
  | {:initial_on_atomic_state, id :: binary()}
  | {:initial_attribute_and_element, id :: binary()}
  | {:transition_count, owner :: owner(), count :: non_neg_integer()}
  | {:transition_missing_target, owner :: owner()}
  | {:transition_forbidden_attribute, owner :: owner(), attr :: :event | :cond}
  | {:history_bad_parent, id :: binary(), parent_kind :: atom()}
  | {:history_bad_type, raw :: binary()}
  | {:transition_bad_type, raw :: binary()}
  | {:scxml_bad_binding, raw :: binary()}
  | {:final_has_states, id :: binary()}
  | {:final_has_transitions, id :: binary() | nil}
  | {:final_parent_missing_id, final_id :: binary() | nil}
  | {:default_entry_not_enterable, id :: binary(), child_kind :: atom()}
  | {:donedata_not_on_final, id :: binary()}
  | {:donedata_content_and_params, id :: binary() | nil}
  | {:content_expr_and_text, expr :: binary()}
  | {:param_expr_and_location, name :: binary()}
  | {:param_no_value, name :: binary()}
  | {:bad_namespace, uri :: binary() | nil}
  | {:bad_version, version :: binary() | nil}
  | {:scxml_bad_datamodel, raw :: binary()}
  | {:data_expr_and_src, id :: binary()}
  | {:data_value_and_children, id :: binary()}
  | {:data_reserved_id, id :: binary()}
  | {:datamodel_bad_parent, kind :: atom()}
  | {:assign_expr_and_text, expr :: binary()}
  | {:if_elseif_after_else}
  | {:if_duplicate_else}
  | {:script_no_src_or_text}
  | {:invoke_type_and_typeexpr}
  | {:invoke_src_and_srcexpr}
  | {:invoke_src_and_content}
  | {:invoke_id_and_idlocation}
  | {:invoke_namelist_and_param}
  | {:invoke_bad_autoforward, raw :: binary()}
  | {:send_event_and_eventexpr}
  | {:send_target_and_targetexpr}
  | {:send_type_and_typeexpr}
  | {:send_id_and_idlocation}
  | {:send_delay_and_delayexpr}
  | {:send_delay_and_internal_target}
  | {:send_namelist_and_content}
  | {:send_param_and_content}
  | {:cancel_sendid_and_sendidexpr}
  | {:cancel_no_sendid}
```

# `t`

```elixir
@type t() :: %Statifier.Validator.Error{
  location: Statifier.Parser.Location.t(),
  message: binary(),
  reason: reason()
}
```

# `assign_expr_and_text`

```elixir
@spec assign_expr_and_text(
  expr :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Spec 5.4.2: an `<assign>` element carries both an `expr` attribute and
non-blank child content - "A conformant SCXML document MUST specify
either 'expr' or children of `<assign>`, but not both." `expr` is the
attribute's value as lowered, so the message quotes the expression rather
than the text, which has no span of its own. Reported at the `<assign>`'s
own element span (`Statifier.Document.Assign.node_location`, not the
SCXML `location` *attribute* that struct also carries -
`Statifier.Document.Assign`'s moduledoc names the two spans apart).

# `bad_namespace`

```elixir
@spec bad_namespace(uri :: binary() | nil, location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 9 (spec 3.2.1): the root element's resolved namespace,
`document.namespace`, is not the SCXML namespace. `uri` is
`document.namespace` itself - `nil` for a fragment that declares no
namespace at all, the boilerplate-free case that relaxed parsing left
representable.

# `bad_version`

```elixir
@spec bad_version(
  version :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 10 (spec 3.2.1): the root element's `version` is not `"1.0"`.
`version` is `nil` when the attribute is absent.

# `cancel_no_sendid`

```elixir
@spec cancel_no_sendid(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.3.1: "A conformant SCXML document MUST specify exactly one of
sendid or sendidexpr." A `<cancel>` carrying neither. Reported at the
`<cancel>` element's own `location`.

# `cancel_sendid_and_sendidexpr`

```elixir
@spec cancel_sendid_and_sendidexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.3.1: "sendid ... Must not occur with sendidexpr." A `<cancel>`
carrying both. Reported at the `<cancel>` element's own `location`.

# `code`

```elixir
@spec code(reason :: reason()) :: atom()
```

The reason tuple's tag - the stable error code.

# `content_expr_and_text`

```elixir
@spec content_expr_and_text(
  expr :: binary(),
  location :: Statifier.Parser.Location.t(),
  payload :: :text | :markup
) :: t()
```

Spec 5.6: a `<content>` element carries both an `expr` attribute and
inline content, whether that content is text or markup (ADR-0041) - the
spec's "MUST NOT specify both" that `lib/statifier/document/content.ex`
deliberately leaves representable so this check can report it. `expr` is
the attribute's value as lowered, and `payload` names which inline form
was actually present, `:text` or `:markup` - markup wins when both are
somehow present, since it is the more specific payload. The `reason` tuple
stays `{:content_expr_and_text, expr}` regardless of `payload`; only the
message varies. For `:text`, the message quotes the expression rather than
the text, which has no span of its own (`Statifier.Document.Content`'s
moduledoc); for `:markup`, the message quotes the expression and names
markup by kind, since markup DOES have a span of its own
(`markup_location`) even though this diagnostic does not use it. Reported
at the `<content>` element's own `location`, the only span covering both
halves of the conflict.

# `data_expr_and_src`

```elixir
@spec data_expr_and_src(id :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 13 (spec 5.3.2): a `<data>` carries both an `expr` and a `src`
attribute - "MAY have either a 'src' or an 'expr' attribute, but MUST NOT
have both". `id` is the offending `<data>`'s own id.

# `data_reserved_id`

```elixir
@spec data_reserved_id(id :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 13 (spec 5.10): a `<data id="...">` begins with `_` - "A conformant
SCXML document MUST NOT contain ids beginning with '_' in the `<data>`
element".

# `data_value_and_children`

```elixir
@spec data_value_and_children(
  id :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 13 (spec 5.3.2): a `<data>` carries an `expr` or `src` attribute
**and** non-blank child text - "if either attribute is present, the
element MUST NOT have any children". Whitespace-only text does not count
(`Checks.Data.blank?/1`).

# `datamodel_bad_parent`

```elixir
@spec datamodel_bad_parent(kind :: atom(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 13 (spec 3.2.2 / 3.3.2 / 3.4.2): a `<datamodel>` sits on a `:final`
or `:history` state - legal only under `<scxml>`, `<state>`, and
`<parallel>`. `kind` is the offending state's own kind.

# `default_entry_not_enterable`

```elixir
@spec default_entry_not_enterable(
  id :: binary() | nil,
  child_kind :: atom(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 7 (spec 3.3): `id` names a compound state with no
`initial` attribute and no `<initial>` element, whose first child in
document order has `child_kind` - a `:history` pseudo-state cannot be
entered by the spec 3.3 default-entry fallback. Reported at the first
child's own `location`.

# `donedata_content_and_params`

```elixir
@spec donedata_content_and_params(
  id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) ::
  t()
```

Spec 5.5: a `<donedata>` carries both a `<content>` child and one or more
`<param>` children - "either a single `<content>` element or one or more
`<param>` elements as children of `<donedata>`, but not both". `id` is the
owning `:final` state's own id (`nil` when unwritten, the same shape
`final_parent_missing_id/2` allows). Reported at the `<donedata>`'s own
`location`, the only span covering both halves of the conflict.

# `donedata_not_on_final`

```elixir
@spec donedata_not_on_final(
  id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 8 (spec 3.7, 5.7): `id` names a non-`:final` state carrying a
non-nil `donedata` - `<donedata>` is only legal on `:final`. Reported at
the `donedata`'s own `location`.

# `duplicate_id`

```elixir
@spec duplicate_id(id :: binary(), location :: Statifier.Parser.Location.t()) :: t()
```

Check 1 (spec 3.14): `id` names more than one state in the document. The
location is the offending (non-first) occurrence's own span, never the
canonical one.

# `empty_id`

```elixir
@spec empty_id(location :: Statifier.Parser.Location.t()) :: t()
```

Check 1 (spec 3.14): a state was written with `id=""`. `id` is
typed as an XML Schema ID, whose lexical space is an XML `Name` and
therefore excludes the empty string, so an empty id is not a name a
transition could ever target.

This is a different case from an absent `id`, which is legal and reported
by nothing (`lib/statifier/document/state.ex`): the two are told apart by
the `attribute_locations[:id]` key, never by the value alone. The reason
carries no payload - the offending value is the empty string, and the
location is the span the author wrote it at.

# `final_has_states`

```elixir
@spec final_has_states(
  id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 6 (spec 3.7): `id` names a state child of a `:final` - only
`onentry`, `onexit`, and `donedata` are legal there. Reported once per
offending child, at the **child's own** `location` (not the `<final>`'s),
so the caret lands on the element that should not be there.

# `final_has_transitions`

```elixir
@spec final_has_transitions(
  id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 6 (spec 3.7): `id` names a `:final` carrying a `<transition>`. A
`<final>` is where a region stops, so it has no outgoing transitions to
take - spec 3.7's content model is `onentry`, `onexit`, and `donedata`
only. Reported once per offending transition, at the **transition's own**
`location` rather than the `<final>`'s, matching `final_has_states/2`.

# `final_parent_missing_id`

```elixir
@spec final_parent_missing_id(
  final_id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) ::
  t()
```

Check 12 (spec 3.7, Appendix D `enterStates`): a `<final>` state's parent
carries no `id`. Entering a non-top-level `<final>` raises
`done.state.{parent.id}`, so a parent with no written id has a completion
event the spec names after a name it does not have - and no transition
could match `"done.state."` even if it were raised. `final_id` is the
offending `<final>` child's own id, which names the completion the
document loses; it rides in `reason` and stays **out** of `message`,
because it is itself optionally `nil` and interpolating that reads as a
bug ("containing <final> nil") in the one case the check exists for. The
location is the **parent's** span, since the parent is the element that
has to change.

# `history_bad_parent`

```elixir
@spec history_bad_parent(
  id :: binary() | nil,
  parent_kind :: atom(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 5 (spec 3.10): a `:history` state `id` is a child of `parent_kind`,
which is not a compound `<state>` or `<parallel>` - the only two legal
parents for a history pseudo-state. `parent_kind` is `:scxml` for the
document root itself.

# `history_bad_type`

```elixir
@spec history_bad_type(raw :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 5 (spec 3.10): a `<history type="...">` value that is neither
`"shallow"` nor `"deep"`. `raw` is the source text as written
(`Location.slice/2`), so the message quotes what the author
actually typed rather than the default it silently lowered to.

# `if_duplicate_else`

```elixir
@spec if_duplicate_else(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 4.3.2: an `<if>` with more than one `<else>` branch (each an
attribute-less, `cond`-less partition per `Statifier.Document.If.Branch`).
4.3 caps `<else>` at "0 or 1 times", so a second one is malformed
regardless of position. Reported at the **second** `<else>`'s own
`location`, never the first's.

# `if_elseif_after_else`

```elixir
@spec if_elseif_after_else(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 4.3.2: "In a conformant SCXML document, `<else>` MUST occur after all
`<elseif>` tags." An `<elseif>` branch that follows the `<if>`'s `<else>`
branch violates that ordering. Reported at the offending `<elseif>`'s own
`location` (`Statifier.Document.If.Branch.location`), not the enclosing
`<if>`'s.

# `initial_attribute_and_element`

```elixir
@spec initial_attribute_and_element(
  id :: binary() | nil,
  location :: Statifier.Parser.Location.t()
) ::
  t()
```

Check 4 (spec 3.3, 3.6): a state carries both an `initial` attribute and
an `<initial>` element - at most one is legal. Reported at the
`<initial>` element's own span.

# `initial_not_descendant`

```elixir
@spec initial_not_descendant(
  id :: binary(),
  parent_id :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 3 (spec 3.3, 3.6): a resolved initial target `id` is not a
descendant of the state it initializes, `parent_id` - ancestry, not
direct-child membership (v1's bug).

# `initial_on_atomic_state`

```elixir
@spec initial_on_atomic_state(
  id :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Check 3 (spec 3.3): `id` names a state carrying an `initial`
attribute or `<initial>` element, and the spec MUST NOT: an atomic state
(no `states` children, or a `:parallel`, `:final`, or `:history` kind) has
nothing to default into. Fires ahead of, and suppresses, the descendancy
check for the same state.

# `invoke_bad_autoforward`

```elixir
@spec invoke_bad_autoforward(
  raw :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Spec 6.4.1: an `<invoke autoforward="...">` value that is neither
`"true"` nor `"false"`. Same slice-back substrate as
`transition_bad_type/2` and `Statifier.Validator.Checks.Enums`'s other
three enumerated attributes - `Statifier.Lowering.Attributes.atom/4` maps
an unrecognised value onto the `false` default, so the atom alone cannot
tell `autoforward="false"` from `autoforward="sideways"`. `raw` is the
source text as written (`Location.slice/2`).

# `invoke_id_and_idlocation`

```elixir
@spec invoke_id_and_idlocation(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.4.1: "id: Must not occur with the 'idlocation' attribute." An
`<invoke>` carrying both. Reported at the `<invoke>` element's own
`location`.

# `invoke_namelist_and_param`

```elixir
@spec invoke_namelist_and_param(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.4.1: "namelist: Must not occur with the `<param>` element." An
`<invoke>` carrying a written `namelist` alongside one or more `<param>`
children. Reported at the `<invoke>` element's own `location`.

# `invoke_src_and_content`

```elixir
@spec invoke_src_and_content(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.4.1: the other half of `src`'s "Must not occur with ... the
`<content>` element" constraint - an `<invoke>` carrying a `<content>`
child alongside `src` or `srcexpr`. Reported at the `<invoke>` element's
own `location`.

# `invoke_src_and_srcexpr`

```elixir
@spec invoke_src_and_srcexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.4.1: "src: Must not occur with the 'srcexpr' attribute or the
`<content>` element." The `src`/`srcexpr` half of that constraint; the
`<content>` half is `invoke_src_and_content/1`. An `<invoke>` carrying both
`src` and `srcexpr`. Reported at the `<invoke>` element's own `location`.

# `invoke_type_and_typeexpr`

```elixir
@spec invoke_type_and_typeexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.4.1: "type: Must not occur with the 'typeexpr' attribute." An
`<invoke>` carrying both. Reported at the `<invoke>` element's own
`location` - the reason carries no payload, matching `{:if_duplicate_else}`
and `{:script_no_src_or_text}`'s own nullary shape for a violation with no
natural id to name it by.

# `param_expr_and_location`

```elixir
@spec param_expr_and_location(
  name :: binary(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Spec 5.7: a `<param>` element carries both an `expr` attribute and a
`location` attribute - "A conformant SCXML document MUST specify either
the 'expr' attribute of `<param>` or the 'location' attribute, but MUST
NOT specify both." `name` is the offending `<param>`'s own `name`
attribute. Reported at the `<param>` element's own `location`.

# `param_no_value`

```elixir
@spec param_no_value(name :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Spec 5.7: a `<param>` element carries neither an `expr` attribute nor a
`location` attribute - the same "MUST specify either ... but MUST NOT
specify both" rule as `param_expr_and_location/2`, its other half. `name`
is the offending `<param>`'s own `name` attribute. Reported at the
`<param>` element's own `location`.

# `script_no_src_or_text`

```elixir
@spec script_no_src_or_text(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 5.8.2: "A conformant SCXML document MUST specify either the 'src'
attribute or child content, but not both." `src` is unreachable in a
struct lowering ever builds (`Statifier.Lowering.Builders.build_script/2`
already refuses to build one when `src` is written - ADR-0026 decision
2), so the only way this document-layer check can still observe the
MUST's violation is the other missing half: a `<script>` with neither.
The reason carries no payload - a `<script>` has no id and no
attribute-value to name it by, matching `{:empty_id}` and
`{:if_duplicate_else}`'s own nullary shape.

# `scxml_bad_binding`

```elixir
@spec scxml_bad_binding(raw :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Spec 3.2.1: an `<scxml binding="...">` value that is neither
`"early"` nor `"late"`. Same slice-back substrate as
`transition_bad_type/2`, against the `:early` default.

# `scxml_bad_datamodel`

```elixir
@spec scxml_bad_datamodel(raw :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 13 (spec 3.2.1): a `datamodel="..."` value outside
`["predicator", "elixir", "null", "ecmascript", "xpath"]` - the spec's own
three named values (`"null"`, `"ecmascript"`, `"xpath"`) plus this
platform's two (`"predicator"`, its default; `"elixir"`, an alias for
v1-converted documents). Accepting `"ecmascript"` and `"xpath"` does not
claim this engine implements either datamodel - spec 3.2.1's Valid Values
clause names "other platform-defined values" as legal too, and Appendix
B's intro states no rule for what a processor does with a datamodel it
does not implement. What this check actually catches is a typo
(`datamodel="predicater"`, `datamodel="javascript"`); which datamodel this
engine runs is `docs/datamodel.md`'s answer, not this attribute's. `raw`
is the source text as written (`Location.slice/2`), the same substrate as
`scxml_bad_binding/2`.

# `send_delay_and_delayexpr`

```elixir
@spec send_delay_and_delayexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: "delay ... Must not occur with 'delayexpr' or when the
attribute 'target' has the value "_internal"." The `delay`/`delayexpr`
half of that constraint; the `target="_internal"` half is
`send_delay_and_internal_target/1`. A `<send>` carrying both `delay` and
`delayexpr`. Reported at the `<send>` element's own `location`.

# `send_delay_and_internal_target`

```elixir
@spec send_delay_and_internal_target(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: the other half of `delay`'s "Must not occur with ... when the
attribute 'target' has the value "_internal"" constraint - a `<send>`
carrying `delay` or `delayexpr` alongside a literal `target="_internal"`.
Detectable only when `target` is a literal attribute; with `targetexpr`
the value is not known until execute time, so this check stays silent
there rather than guessing (`Statifier.Validator.Checks.Send`'s
moduledoc). Reported at the `<send>` element's own `location`.

# `send_event_and_eventexpr`

```elixir
@spec send_event_and_eventexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: "event ... Must not occur with 'eventexpr'." A `<send>`
carrying both. Reported at the `<send>` element's own `location`.

# `send_id_and_idlocation`

```elixir
@spec send_id_and_idlocation(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: "id ... Must not occur with 'idlocation'." A `<send>`
carrying both. Reported at the `<send>` element's own `location`.

# `send_namelist_and_content`

```elixir
@spec send_namelist_and_content(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.3: "A conformant document MUST NOT specify 'namelist' or
`<param>` with `<content>`." The `namelist` half of that constraint; the
`<param>` half is `send_param_and_content/1`. A `<send>` carrying a
written `namelist` alongside a `<content>` child. Reported at the
`<send>` element's own `location`.

# `send_param_and_content`

```elixir
@spec send_param_and_content(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.3: the other half of "A conformant document MUST NOT specify
'namelist' or `<param>` with `<content>`" - a `<send>` carrying a
`<param>` child alongside a `<content>` child. Reported at the `<send>`
element's own `location`.

# `send_target_and_targetexpr`

```elixir
@spec send_target_and_targetexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: "target ... Must not occur with 'targetexpr'." A `<send>`
carrying both. Reported at the `<send>` element's own `location`.

# `send_type_and_typeexpr`

```elixir
@spec send_type_and_typeexpr(location :: Statifier.Parser.Location.t()) :: t()
```

Spec 6.2.2: "type ... Must not occur with 'typeexpr'." A `<send>`
carrying both. Reported at the `<send>` element's own `location`.

# `transition_bad_type`

```elixir
@spec transition_bad_type(raw :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Spec 3.5: a `<transition type="...">` value that is neither
`"internal"` nor `"external"`. `raw` is the source text as written
(`Location.slice/2`) - lowering maps any out-of-range value
onto the `:external` default, so the atom alone cannot tell
`type="external"` from `type="sideways"`.

# `transition_count`

```elixir
@spec transition_count(
  owner :: owner(),
  count :: non_neg_integer(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Checks 4 and 5 (spec 3.6, 3.10): `owner`'s content model is
exactly one `<transition>`, and it holds `count` instead. Fires for both
zero (nothing to default into) and two-or-more (which one wins is
undefined).

# `transition_forbidden_attribute`

```elixir
@spec transition_forbidden_attribute(
  owner :: owner(),
  attr :: :event | :cond,
  location :: Statifier.Parser.Location.t()
) :: t()
```

Checks 4 and 5 (spec 3.6, 3.10): `owner`'s one transition carries `attr`
(`:event` or `:cond`), which neither an `<initial>` nor a `<history>`
default transition may specify.

# `transition_missing_target`

```elixir
@spec transition_missing_target(
  owner :: owner(),
  location :: Statifier.Parser.Location.t()
) :: t()
```

Checks 4 and 5 (spec 3.6, 3.10): `owner`'s one transition
carries no usable `target` - `target=""` and an absent `target` are the
same failure here.

# `unresolved_initial`

```elixir
@spec unresolved_initial(id :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 3 (spec 3.3, 3.6): an `initial` attribute or `Document.initial`
names `id`, and no state in the document carries that id. An `<initial>`
element's own transition targets are never reported by this constructor -
check 2 already owns their existence.

# `unresolved_target`

```elixir
@spec unresolved_target(id :: binary(), location :: Statifier.Parser.Location.t()) ::
  t()
```

Check 2 (spec 3.5): a `<transition>`'s `target` names `id`, and no state
in the document carries that id. Reported once per unresolved id, at the
transition's own `target` span (or the transition's, when unwritten).

---

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