# `Statifier.Machine.Identity`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/machine/identity.ex#L1)

A chart's identity: a content hash taken over the SCXML source
`Statifier.compile/2` received, plus an optional embedder-supplied name and
version. `Statifier.compile/2` stamps one onto every `Statifier.Machine.t()`
it produces (`Statifier.Machine.identity/1`).

The hash is taken over the source **binary**, not the compiled
`%Statifier.Machine{}` term: the full argument for that choice lives in
ADR-0052 (a term-level hash would vary with compiler internals - numbering
order, expression-compilation output - that carry no meaning to a host
comparing chart revisions, while two byte-identical documents must always
agree regardless of what the compiler does with them).

Two `t()` values are the same chart only when `matches?/2` says so -
`==/2` on the struct is deliberately not the public comparison, since a
future field addition here should not silently change what "the same
chart" means at every existing call site.

# `t`

```elixir
@type t() :: %Statifier.Machine.Identity{
  content_hash: String.t(),
  name: String.t() | nil,
  version: String.t() | nil
}
```

# `format_version`

```elixir
@spec format_version() :: pos_integer()
```

The version tag `to_binary/1` writes and `from_binary/1` checks. A bare
integer, so a future format change is a version bump here rather than an
inference from the blob's shape.

# `from_binary`

```elixir
@spec from_binary(blob :: binary()) ::
  {:ok, t()}
  | {:error, :not_a_statifier_blob}
  | {:error, {:unsupported_format_version, term()}}
```

Decodes a `to_binary/1` envelope back into a `t()`.

Returns `{:error, :not_a_statifier_blob}` for anything that is not this
module's tagged envelope - a foreign `term_to_binary` blob, garbage bytes,
or a well-formed envelope whose payload is not a `t()` - and
`{:error, {:unsupported_format_version, version}}` when the envelope is
recognizably this module's own but carries a format version this build
does not know how to read.

# `matches?`

```elixir
@spec matches?(a :: t() | nil, b :: t() | nil) :: boolean()
```

Whether `a` and `b` name the same chart identity. Total: `nil` on either
side answers `false`, never `true` - two unidentified charts are not the
same chart, and treating `nil == nil` as a match would be exactly the
silent misread this module exists to prevent.

# `of_source`

```elixir
@spec of_source(source :: binary(), opts :: keyword()) :: t()
```

Hashes `source` and carries `opts[:chart_name]`/`opts[:chart_version]`
alongside the hash.

`opts` is `Statifier.compile/2`'s whole option keyword list, passed through
unfiltered - this function reads only its own two keys and ignores every
other one (`invoke_content_markup: true` included). That is intentional:
narrowing this to a filtered keyword list would just move the coupling to
this call site instead of removing it.

# `to_binary`

```elixir
@spec to_binary(identity :: t()) :: binary()
```

Encodes `identity` as a tagged, versioned binary envelope, so a host can
store an identity beside the source it retained for it.

---

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