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

One SCXML subtree, structurally (ADR-0004 decision 4).

`emit/2` returns one of these, never a string. A block type building XML
text would own escaping, namespace handling and attribute-value
normalization, all easy to get subtly wrong, none of them a block-type
author's business, and all of them - per st-ADR-0052's whitespace
sensitivity - able to change chart identity by accident. The compiler
serializes the whole tree once, at the end, through
`StatifierBlocks.Compiler.Serializer`.

## Attributes are a sorted list, not a map

`attributes` is a list of `{name, value}` pairs kept in sorted order by
`element/3`, because ADR-0004 decision 6 forbids iterating a bare map
anywhere in the pipeline: two maps that are `==` can enumerate in
different orders, and the serializer's output is identity-bearing.
Sorting at construction rather than at serialization means the sort
happens once, where the pairs are known.

## Children, and the child placeholder

A parent never receives its children's emitted SCXML (decision 4: a
parent that could read it would be a parent that could depend on it).
What it receives is a summary - block id, state id, done event - and what
it emits in a child's place is `child_ref/1`, a placeholder naming that
child's block id. The compiler splices each child's own emission in
afterwards.

That is what buys decision 6's per-block byte stability: a parent's own
bytes are a function of its config and its children's *ids*, never of
their contents, so an unedited subtree compiles to unchanged bytes even
when a sibling changes.

## Provenance hints (ADR-0004 decision 5)

Two of the three fields below carry no bytes at all. They are hints the
compiler reads while building the provenance map, and the serializer
never writes them:

  * `owner` - an **attribution judgment**. By default every element a
    block emits belongs to that block, which is right almost everywhere.
    ADR-0004 decision 5 names the exceptions: the `done.state` transition
    a sequence emits belongs to *the child it leaves*, because "what
    happens after the authorize step" is the fact an author would recognise.
    `attributed_to/2` records that. `from_config/2` records the other
    half - an element written out of one config field, so a finding
    against it is the author's rather than a bug.
  * `attribute_owners` - the same, one attribute value at a time. A
    `cond` built verbatim from an author's `:expression` field is the
    motivating case: the *element* is the block type's, the *attribute
    value* is the author's, and only the second should carry a config
    key. `attribute_from_config/3` records it.

A hint is never required. A block type that sets none gets the default
attribution, which is what every leaf wants.

# `hint`

```elixir
@type hint() :: %{
  block_id: StatifierBlocks.Block.id() | nil,
  role: String.t() | nil,
  config_key: String.t() | nil
}
```

An attribution hint, and - once
`StatifierBlocks.Compiler.Attribution.stamp/3` has run over the tree -
the resolved `t:StatifierBlocks.Provenance.owner/0` itself, which is
this type with `block_id` known.

`block_id` is `nil` for "the block that emitted this", which is the
default and the common case. `role` is never set by a block type: the
compiler derives it from the state ids the block minted, because a block
type naming its own role twice - once in an id and once in a hint - is a
place for the two to disagree.

# `node_t`

```elixir
@type node_t() :: t() | {:child, StatifierBlocks.Block.id()}
```

A child of an element: another element, or a placeholder for a compiled child block.

# `t`

```elixir
@type t() :: %StatifierBlocks.Emission{
  attribute_owners: [{String.t(), String.t()}],
  attributes: [{String.t(), String.t()}],
  children: [node_t()],
  name: String.t(),
  owner: hint() | nil
}
```

# `attribute_from_config`

```elixir
@spec attribute_from_config(t(), String.t(), String.t()) :: t()
```

Records that one attribute's **value** came verbatim from the config
field `config_key`, leaving the element itself attributed as it was.

This is the finer of the two grains, and the one the fault split
(ADR-0004 decision 9) actually turns on: an upstream finding whose
location falls inside the value is the author's typo, while one against
the element around it is not. A `config_key` for an attribute the
element does not carry is dropped, so an optional attribute can be
annotated unconditionally.

# `attributed_to`

```elixir
@spec attributed_to(t(), StatifierBlocks.Block.id()) :: t()
```

Attributes `emission` and everything under it to `block_id` rather than
to the block that emitted it (ADR-0004 decision 5).

Reserved for the cases the record names, where the block an author would
recognise is not the block whose `emit/2` wrote the element. Attributing
an element to a block that is not in this document at all is a bug in the
block type, and the compiler reports it as an Emit finding rather than
writing an owner nothing can resolve.

# `child_ref`

```elixir
@spec child_ref(StatifierBlocks.Block.id()) :: {:child, StatifierBlocks.Block.id()}
```

A placeholder for the compiled emission of the child block `block_id`.

The compiler replaces it with that child's own subtree; a placeholder
naming a block the compiler did not compile in this slot is a compiler
bug, not a document error, and `StatifierBlocks.Compiler` reports it as
one rather than emitting a hole.

# `element`

```elixir
@spec element(String.t(), [{String.t(), String.t() | nil}], [node_t()]) :: t()
```

Builds an element.

`attributes` is a list of `{name, value}` pairs; it is sorted by name
here, and an attribute whose value is `nil` is dropped, so a caller can
write an optional attribute without a conditional around it.

# `from_config`

```elixir
@spec from_config(t(), String.t()) :: t()
```

Records that `emission` was written out of the config field `config_key`,
so a finding landing inside it is the author's rather than a bug.

---

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