Raxol. Harness. Surface. Parity
(Raxol v2.6.1)
View Source
Multi-surface parity matrix: renders each fixture session through all four surface projections and pins each one, so "one TEA module renders to terminal, browser, SSH, and MCP" is a checked claim rather than a README sentence.
Raxol.Harness.Surface.Golden pins the raw bytes of ONE surface (the
inline/degradation terminal ladder) across three render modes. This module
pins ONE render across four SURFACES, and additionally asserts they agree
with each other. A regression that teaches the LiveView encoder to drop a
wide character, or the ANSI writer to emit SGR before the cursor move, is
invisible to a single-surface golden and lands here as both a drift and a
parity break.
The shared pivot
Every projection derives from ONE render of ONE fold, so a divergence is always the projection's fault and never the input's:
session (.jsonl)
-> Raxol.Harness.Projection.project/2 (pure fold, T7)
-> [Block.t()] -> Block.render/2 (pure view tree)
-> LayoutEngine.apply_layout/2 (positioned elements)
-> UIRenderer.render_to_cells/2 (the cell grid)and from the cell grid:
| surface | projection | derived from |
|---|---|---|
:cells | canonical row-major cell dump | the grid |
:liveview_dom | TerminalBridge.buffer_to_html/2, normalized | the grid |
:ssh_ansi | Core.Renderer.render_diff/2 + apply_diff/1, escaped | the grid |
:structured_json | MCP.StructuredScreenshot.from_view_tree/2 | the view tree |
:structured_json is deliberately taken from the view tree rather than the
grid: that IS the MCP surface's input, and pinning it from the grid would
test a pipeline nothing runs.
What parity means, per surface
:cells, :liveview_dom, and :ssh_ansi are three encodings of the same
grid, so their visible text must be character-for-character identical --
parity/1 asserts exactly that, and it is the check with teeth: each
encoder walks the grid independently.
:structured_json comes from the pre-layout tree, so its text is NOT
grid-identical (layout wraps, truncates, and pads). Its parity property is
containment, checked at the word level: a word the MCP surface reports must
be visible on screen. Claiming more would be false precision.
Determinism
Same closure as Raxol.Harness.Surface.Golden's audit, plus:
- fixed geometry (
60x24), so layout never reads a live terminal; - an explicitly constructed theme, so the hash never depends on the global
theme registry (which other tests in the suite mutate) -- the same
reason
Raxol.RATEpasses one; render_diff/2is taken against a BLANK buffer, making the ANSI stream a full repaint rather than a function of whatever was on screen before;- cells are sorted row-major before serialization, so emission order cannot leak into the hash.
Summary
Functions
On-disk path for a fixture x surface artifact.
Every projectable fixture in test/fixtures/harness/sessions, sorted.
Fixed render geometry, shared by every surface.
Checks cross-surface agreement for fixture.
Directory holding the checked-in per-surface artifacts.
Project a rendered fixture onto one surface. Returns the artifact text.
Path of the committed hash refs file.
Renders fixture once and returns %{tree: view_tree, cells: cells} --
the single pivot every projection is taken from.
Blesses (or with check: true, verifies) every fixture x surface artifact
and the hash refs file.
The four surface projections this matrix covers.
The visible text of a fixture on each grid-derived surface, as
%{surface => [line]}. The three lists must be equal -- see parity/1.
Types
@type result() :: %{ name: String.t(), fixture: String.t(), surface: surface(), status: :current | :created | :overwritten, path: Path.t(), bytes: non_neg_integer() }
@type surface() :: :cells | :liveview_dom | :ssh_ansi | :structured_json
Functions
On-disk path for a fixture x surface artifact.
@spec fixtures() :: [String.t()]
Every projectable fixture in test/fixtures/harness/sessions, sorted.
Membership is decided by Raxol.Harness.Fixture.Session.golden?/1 -- the
same predicate Raxol.Harness.Fixture.Bless uses, so the two corpora cannot
drift apart. An kind: "adversarial" fixture exists to be REJECTED by the
loader, so there is no render to pin.
Note this is a header field, not a filename convention: a .notes.md
sidecar is documentation and says nothing about whether a fixture renders
(projection-panels is hand-authored, documented, and golden).
@spec geometry() :: %{width: pos_integer(), height: pos_integer()}
Fixed render geometry, shared by every surface.
Checks cross-surface agreement for fixture.
Returns :ok, or {:error, diagnostics} naming the first disagreeing line
per surface pair plus any MCP-reported word that is not on screen.
@spec parity_dir() :: Path.t()
Directory holding the checked-in per-surface artifacts.
Project a rendered fixture onto one surface. Returns the artifact text.
@spec refs_path() :: Path.t()
Path of the committed hash refs file.
Renders fixture once and returns %{tree: view_tree, cells: cells} --
the single pivot every projection is taken from.
Blesses (or with check: true, verifies) every fixture x surface artifact
and the hash refs file.
Returns {:ok, results} or {:error, {:drift, names}}.
@spec surfaces() :: [surface()]
The four surface projections this matrix covers.
The visible text of a fixture on each grid-derived surface, as
%{surface => [line]}. The three lists must be equal -- see parity/1.