# `Surfex.Golden`
[🔗](https://github.com/dcoai/surfex/blob/v0.6.2/lib/surfex/golden.ex#L1)

The ONE renderer for a project's surface goldens.

A **surface golden** is a document generated from a source scan, listing what the code
declares, gated so that it cannot disagree with the code: CI regenerates it and
byte-compares, and a difference fails the build. The document therefore cannot lie —
which is the only property that makes a generated document worth reading three weeks
later.

Each scanner builds a plain data spec and hands it here. **The renderer IS the
convention**: notation lives in one place, so per-file dialects cannot form — and, now
that more than one project renders goldens, per-*project* dialects cannot either.

## The shape a golden always has

  1. **Header block** — line 1 `# <NAME>`; line 2 the one-line `:purpose`; line 3 the
     machine-parseable attribution `Generated by `mix <task> --write` · gate
     `<gate>` (HARD|ADVISORY) — do not edit; a drift FAILS the gate`. Domain
     `:prose` (semantics, class ladders) FOLLOWS the block, unconstrained.
  2. **Stats line(s)** — each `**<lead>** · <label> <n> · …` (see `stat_line/2`); every
     golden carries at least a row count.
  3. **Tables** — first column = the catalogued item (backticked); last column
     conventionally `Locus`. Cells are TYPED (`t:cell/0`) so the renderer — not each
     caller — owns the backtick/atom/locus/`—` notation. Optional grouping:
     `## <heading>` with per-group tables.

**No timestamps or VCS data are ever emitted.** A golden is a stable function of source;
a date makes it drift against itself on a quiet day, and a gate that cries wolf gets
disabled. Dates belong in a report, never here. This is a lesson paid for once already in
the project this came from, and it is the one rule most likely to be re-broken.

## What this does NOT know

The **evidence ladder** — whether a thing is `model_checked`, `static_gate`, `property`,
`unit_test` or NOTHING — is the calling project's vocabulary, and differs between them.
So does what a surface *is*. This renders typed cells; it never interprets them.

## Determinism

`render/1` is a pure function of its spec and is invariant under the input order of a row
map's keys (cells are pulled in `:columns` order) and under the input order of the row
list (rows are sorted by `:sort`, default the first column's natural key). So two specs
that differ only in such orderings render identical bytes — **the property every drift
gate rests on**, and the reason it is asserted in this package's tests rather than
assumed.

> Extracted from `agentronic`'s `Maatronic.SourceScan.Golden`, where ten goldens have
> used it in production. Behaviour is unchanged; only the prose was rewritten for a home
> that serves more than one project.

# `cell`

```elixir
@type cell() ::
  {:code, iodata()}
  | {:atom, atom()}
  | {:locus, String.t() | nil, pos_integer() | nil}
  | {:version, String.t() | nil}
  | {:raw, iodata()}
  | :absent
  | nil
  | binary()
```

A typed table cell — the renderer formats it, so notation is central, not per-caller:

  * `{:code, io}` → `` `io` `` (the backticked-item rule)
  * `{:atom, a}` → `` `:a` `` (atoms always backticked, e.g. an effect class `:local`)
  * `{:locus, file, line}` → `` `file:line` `` (a `nil` file ⇒ `—`; a `nil` line drops the
    `:line`, so a hashed-locus surface passes `{:locus, file, nil}` for a bare-path Locus)
  * `{:version, hash}` → `` `hash` `` (a content version — `Surfex.SourceScan.definition_hash/1`'s
    8-hex output; a `nil` hash ⇒ `—`). Named rather than a bare `{:code, …}` so the
    content-version notation lives in one place across every golden's `Version` column.
  * `{:raw, io}` → verbatim (escape hatch for a composite cell like `` `unit_test` · tagged``)
  * `:absent` / `nil` → `—` (the absent-value glyph)
  * a plain binary → verbatim

# `group`

```elixir
@type group() :: %{
  :heading =&gt; iodata(),
  :rows =&gt; [row()],
  optional(:columns) =&gt; [String.t()]
}
```

A `## <heading>` grouped table. `:columns` overrides the spec's top-level columns for this group —
for a golden whose sub-tables have different shapes (e.g. AUTHZ's boundary ops vs runtime seams).

# `row`

```elixir
@type row() :: %{optional(String.t()) =&gt; cell()}
```

One table row: column-name => cell. Order-independent (cells pulled in `:columns` order).

# `spec`

```elixir
@type spec() :: %{
  :name =&gt; String.t(),
  :purpose =&gt; iodata(),
  :task =&gt; String.t(),
  :gate =&gt; String.t(),
  :hardness =&gt; :hard | :advisory,
  :columns =&gt; [String.t()],
  optional(:prose) =&gt; iodata(),
  optional(:stats) =&gt; [stat()],
  optional(:notes) =&gt; iodata(),
  optional(:groups) =&gt; [group()],
  optional(:rows) =&gt; [row()],
  optional(:sort) =&gt; (row() -&gt; term())
}
```

A surface-golden spec. `:groups` and `:rows` are mutually exclusive — supply `:groups` for a
grouped golden (`SPEC_COVERAGE`), `:rows` for a flat one. `:sort` is a row → sortable key (rows
are sorted within each group); it defaults to the first column's natural key.

# `stat`

```elixir
@type stat() :: %{
  lead: iodata(),
  dims: [{iodata(), term()} | iodata()],
  text: iodata()
}
```

One stats line as DATA: the bold `:lead`, its `:dims` (`{label, value}` pairs / bare flags), and
the rendered `:text`. Built by `stat/2` so the renderer AND any cross-surface report read the
same counts — the report never parses the markdown.

# `natural_key`

```elixir
@spec natural_key(String.t()) :: [String.t() | {0, integer()}]
```

A natural sort key for an identifier-ish string: alternating text / integer chunks, so `Foo.9`
sorts before `Foo.10`. The renderer's default row order when a spec supplies no `:sort`.

# `render`

```elixir
@spec render(spec()) :: String.t()
```

Render `spec` to golden markdown. Pure; order-invariant per the moduledoc.

# `stat`

```elixir
@spec stat(iodata(), [{iodata(), term()} | iodata()]) :: stat()
```

A stats line as DATA (`t:stat/0`): `lead` + `dims` + the rendered `text`. Each golden
passes these in `spec.stats`; a cross-surface report reads `lead`/`dims` from the same value, so the
report's counts round-trip the golden's stats line by construction.

# `stat_line`

```elixir
@spec stat_line(iodata(), [{iodata(), term()} | iodata()]) :: iodata()
```

Build the stats line STRING: `**<lead>** · <label> <value> · …`. `lead` is the pre-bold
summary (`"963 sections"`, or a label like `"Freshness (of tagged validations)"`); `dims` is a list
of `{label, value}` (a bare `label` is a flag). Prefer `stat/2` in a spec so the counts stay data.

---

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