# `Noizu.Tessera.Component.Extension`
[🔗](https://github.com/the-robot-lives/tessera/blob/v0.2.0/lib/noizu/tessera/component/extension.ex#L1)

The shared Elixir derivation layer behind `use Vendor.Element, tag: …`
(EXTENSION-CONTRACT.md §5). Runtime-agnostic on purpose: Tier 1 (compiled to
Lit) uses it now, and Tier 2 (Hologram-in-element, brief 3f) reuses the same
merge + dispatcher generation — the spec is the single interface between
definition and runtime (§9).

Responsibilities:

  * `derive!/3` — the expansion of the base module's generated `__using__/1`
    macro: option validation (`tag:` / `:inherit` / `priority:` /
    `extension_api:`), the double-`use` and cycle guards, and the AST that
    re-emits the inherited Hologram props and internally `use`s
    `Noizu.Tessera.Component` in derivation mode;
  * `merge_spec!/3` — deep-copy the base spec's declarations and layer the
    derived module's deltas: additive new names append in declaration order,
    override-by-name replaces wholesale, removal is forbidden, `required:`
    may relax not tighten, and the collision/duplicate/round-trip/reserved
    checks re-run over the MERGED spec (the caller runs `validate_all!`);
  * `check_pin!/3` — the §8 extension-api semver pin (major mismatch raises,
    minor-range mismatch warns);
  * chain construction (§5.9, brief 2e) — `takeover?/1`, `chain_depth/1`,
    `chain_order/1` (dependency depth → `priority:` → module name,
    innermost first — the mirror of the JS registry comparator),
    `record_takeover_layer!/2` (the app-wide sibling registry + the §5.7
    chain-conflict warning), and `artifact_stem/1` so sibling takeover
    layers never clobber each other's base-tag-keyed artifacts (§5.8);
  * dispatcher generation — `base_init/2,3` (init hooks) and the
    `base_action/3` scaffolding Tier 2 consumes. Hologram's IR has no
    `super`, so the Elixir down-call is an injected dispatcher, never
    language-level `super`. Tier-1 fn-actions never compile to the BEAM;
    their down-call is real ES `super` in the lowered JS (see
    `Noizu.Tessera.Component.Expr`), so no BEAM `base_action/3` is generated
    for them.

Inherited entries in a merged spec carry `inherited: true`; the derived
module's own (new or overriding) entries do not. The Lit lowering uses that
marker to emit only new/overridden members and inherit the rest through the
ES class chain.

# `__using__`
*macro* 

The canonical Tier-2 element macro, invoked by a vendor module as
`use Noizu.Tessera.Component.Extension, …` (D4: the vendor module IS the
macro — the `__using__` its extenders consume is generated here, not
hand-rolled). Options (compile-time literals):

  * `tag:` (required) — the vendor's element tag;
  * `actions:` — snake atoms in declaration order, the §6.1 OPEN surface
    (the runtime bodies are the vendor's own `def action/3` clauses —
    Hologram-transpilable Elixir, never fn-AST DSL bodies);
  * `props:` — `name: [type: :, default: :]` triples mirroring the
    vendor's Hologram `prop/2,3` declarations (re-emitted into extenders);
  * `regions:` — atoms naming the arity-1 template helper functions the
    markup calls (e.g. `[:row_label]`) — extenders get delegating
    `def name(arg), do: Vendor.name(arg)` + `defoverridable`;
  * `template:` — the markup string (the same markup the vendor's own
    `def template` renders) — rides `spec.template.raw` so extenders
    re-emit it and the T1-4/T2-7 reference-graph guard can walk it;
  * `extension_api:` — the §8 semver declaration (defaults to 1.0.0).

The expansion splices the CONTRACT scaffold onto the vendor:
`__tessera_spec__/0` + `__tessera_manifest__/0` (the minimal Tier-2 spec —
the interface extenders merge against), the per-action literal
`__tessera_action__/3` entry clauses (static calls into the vendor's own
`action/3`; unknown names raise the boxed FunctionClauseError the §6.3
unknown-action classification expects), and the generated `__using__/1`
that drives `derive_tier2!/3` for every extender.

# `action_dispatcher_asts`

```elixir
@spec action_dispatcher_asts(module(), [atom()]) :: [Macro.t()]
```

Tier-2 `base_action/3` dispatcher scaffolding (brief 3f): one clause per
inherited action, dispatching to the base module's `__tessera_action__/3`
entry point. Generated ONLY when the base actually exports that entry point
(a Hologram-runtime component). Tier-1 fn-actions never compile to the BEAM
and have no `__tessera_action__/3`, so this returns `[]` for them — their
down-call is real ES `super` in the lowered JS, not a BEAM dispatcher.

# `artifact_stem`

```elixir
@spec artifact_stem(Noizu.Tessera.Spec.t()) :: String.t()
```

The artifact filename STEM for a spec. Fresh and fresh-tag-derived specs
keep the plain tag (byte-stable goldens). A takeover layer registers under
the BASE tag (§5.8), and sibling layers would otherwise clobber each other's
base-tag-keyed element/contract files — so takeover artifacts carry the
declaring module: `<base-tag>__<module_snake>`.

# `chain_depth`

```elixir
@spec chain_depth(Noizu.Tessera.Spec.t()) :: non_neg_integer()
```

Layers above the vendor base along the `derived_from` spine (§5.9 dependency
order): `0` for a fresh component, `1` for the first `:inherit` layer, and so
on. A cycle cannot reach here — `guard_cycle!/2` rejects it at expansion.

# `chain_order`

```elixir
@spec chain_order([map()]) :: [map()]
```

Sorts takeover-chain layer descriptors INNERMOST → OUTERMOST per §5.9:
dependency `depth` first, then `priority:` (higher sits further out), then
module name ascending (alphabetically-first sits closer to the base). The
outermost layer runs first and wins conflicts. Entries are maps with
`:depth`, `:priority` and `:module` (string). This is the compile-time
mirror of `priv/js/src/extend.mjs`'s comparator (which adds a
first-registered-outermost fallback for unlabelled hand-written mixins).

# `check_pin!`

```elixir
@spec check_pin!(String.t() | nil, String.t() | nil, Macro.Env.t()) :: :ok
```

Checks a derived `extension_api:` pin against the base's declared version.
Unsatisfied MAJOR raises `CompileError`; an unsatisfied minor range warns
(host policy decides whether to fail). `nil` requirement is a no-op. A base
that declares no version is treated as the first stable `1.0.0`.

# `derive!`

```elixir
@spec derive!(Noizu.Tessera.Spec.t(), keyword(), Macro.Env.t()) :: Macro.t()
```

Builds the AST spliced into a deriving module by the base module's generated
`__using__/1`. `base_spec` is the base module's compiled spec; `opts` are the
raw `use Base, …` options (AST); `caller` is the deriving module's env.

Validates at expansion time (pointed `CompileError`s, §5.7) and returns code
that flags the module as derived, internally `use`s
`Noizu.Tessera.Component`, and re-emits the inherited Hologram props so the
adapter's `@__props__` accumulator is complete before Hologram's
`__before_compile__` runs.

# `derive_tier2!`

```elixir
@spec derive_tier2!(module(), keyword(), Macro.Env.t()) :: Macro.t()
```

The expansion of a Tier-2 vendor's generated `__using__/1`
(`use Vendor, tag: …` — §5.3, D4). Runs the SAME shared layer Tier 1 uses —
option guards, the §5.9 chain machinery, `merge_spec!/3` (hence the §5.5
merge rules + the T1-4/T2-7 reference-graph type-change guard) and the §8
pin check — then splices the Tier-2 lowering: a Hologram component whose
inherited surface is reachable through the injected `base_action/3`
dispatcher + `defoverridable` (Hologram's IR has no `super`; D4 forbids
promising Elixir-level super).

Multi-level chaining (T2-9 gap fix): the scaffold records the PARENT
(the module the extender `use`d) and links through it — per-action
`base_action/3` clauses dispatch to the parent's `__tessera_action__/3`
entry, and the dynamic-name fallback dispatches the parent's `action/3`
(whose clauses/catch-all continue down-chain), so A→B→C never re-includes
the whole stack.

# `init_dispatcher_asts`

```elixir
@spec init_dispatcher_asts(module()) :: [Macro.t()]
```

Injected init-hook dispatchers for a deriving module: `base_init/2` and
`base_init/3` reach the base module's (possibly overridden)
`__tessera_init__/2,3`. The derived module's own `__tessera_init__` defaults
dispatch through these, so an unoverridden hook still runs the base's init
(§5.3). Names mirror `base_action/3` (T1-2 resolved).

# `merge_spec!`

```elixir
@spec merge_spec!(Noizu.Tessera.Spec.t(), map(), Macro.Env.t()) :: map()
```

Deep-copies the base spec's declarations and layers the derived module's
`deltas` (a map of raw author entries keyed by kind). Returns a merged
declaration map the caller feeds through `validate_all!` + the element
translator. Inherited entries carry `inherited: true`.

Rules (§5.5): additive new names append in declaration order; override-by-name
replaces wholesale (events merge detail, keeping inherited keys); removal is
forbidden; `required:` may relax not tighten; retyping an inherited
attr/prop/state key the base READS is rejected (the type-change
reference-graph guard). Duplicate AUTHOR declarations raise here (the merge
would otherwise silently last-wins them).

Event renames (§5.6, brief 2d): `event "new", renames: "inherited"` replaces
the inherited name in place (detail inherited keys kept, `renamed_from`
marker). The rename is then propagated over the merged emission points —
inherited action/lifecycle JS that still emits the OLD event name is
rewritten to the new one and marked `re_emit: true` so the derived class
re-emits the method and shadows the base's (which would otherwise emit the
dead name).

# `record_takeover_layer!`

```elixir
@spec record_takeover_layer!(Noizu.Tessera.Spec.t(), Macro.Env.t()) :: :ok
```

Records a compiled `tag: :inherit` layer in the app-wide sibling registry
and emits the §5.7 CHAIN-CONFLICT warning for every same-depth sibling that
overrides an action body or a template region this layer also overrides.
Called from `Component.__before_compile__/1` once the merged spec exists:
whichever sibling compiles SECOND sees the first and warns (compile order
never hides the conflict). The chain itself needs no re-ordering here —
depth/priority ride each spec + the JS registry entries, and the runtime
composition IS `chain_order/1` at define time.

The registry lives in `:persistent_term` keyed by the BASE TAG: it must
outlive any single module's compile (siblings expand in separate rounds)
and one re-compiling module never warns against itself.

# `snake_stem`

```elixir
@spec snake_stem(Noizu.Tessera.Spec.t()) :: String.t()
```

snake form of `artifact_stem/1` (contract filenames).

# `takeover?`

```elixir
@spec takeover?(Noizu.Tessera.Spec.t()) :: boolean()
```

True when `spec` is a `tag: :inherit` tag-takeover: it derives from another
module AND registers under the BASE tag. Fresh tags MUST differ from the
base tag (`resolve_tag!/3`), so `derived_from` + tag equality is exactly the
takeover condition — no extra spec field, and goldens/manifests for
fresh-tag specs are untouched.

---

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