Skip to content

anchor — the v2 node model #957

Description

@softmarshmallow

Status — 2026-07-11

Tracking issue for the anchor v2 node model and its authored, file-first projection: Grida XML Draft 0 (.grida.xml).

The current proof lives on model-v2-anchor, with the language RFD under docs/wg/format. This branch changes no production runtime under crates/grida and does not change the packed .grida archive schema. It proves the model, source language, resolver, display list, and raster materialization before promotion.

Current thesis

A node is an anchored box with typed content. The file stores authored intent; resolution derives boxes, transforms, layout, text lines, paint geometry, and visual bounds.

The model decisions that remain locked are:

  • declared, measured, and derived box sources stay distinct;
  • the box is the sole layout negotiation surface;
  • rotation and flips are visual-only post-layout intent;
  • no stored general matrix exists outside the quarantined lens operation list;
  • primitives are parametric and may own ordinary children in their local box, so <rect><text>…</text></rect> needs no combined shape-with-text node;
  • free positioning and flex layout preserve authored bindings instead of baking one viewport's coordinates;
  • reads materialize resolved geometry while writes retarget or reject typed intent;
  • derivable state is not serialized.

Grida XML Draft 0

.grida.xml is the inspectable, diffable authored source. .grida remains the packed archive; conversion between them is explicit.

The root contract is:

<grida version="0">
  <container width="960" height="540">
    <!-- one authored scene tree -->
  </container>
</grida>

Draft 0 uses direct semantic elements:

  • container, rect, ellipse, line, text, group, and lens;
  • no separate circle; equal ellipse extents express a circle;
  • no render-node kind; <shape> is reserved for a future custom-shape design;
  • long, stable property names such as width, min-width, and aspect-ratio;
  • strict applicability and typed errors for unknown, contradictory, or out-of-context syntax.

Every property marked Draft 0 in the property registry is implemented by the proof reader/writer. Placeholder and Design rows remain deliberately invalid syntax.

Ordered paint, projected from Paint / Paints

The existing Grida paint variants and ordered Paints semantics are the source of truth. XML does not introduce a parallel style abstraction.

<rect width="320" height="180">
  <fill>
    <solid color="#101828"/>
    <gradient kind="linear" from="0 0" to="1 1" opacity="0.8">
      <stop offset="0" color="#7C3AED"/>
      <stop offset="1" color="#2563EB"/>
    </gradient>
    <image src="./noise.png" fit="cover" opacity="0.15"/>
  </fill>
  <text>Example</text>
</rect>
  • <fill> owns an ordered paint channel; first paint is bottommost, last is topmost.
  • Typed values are solid, gradient kind="linear|radial|sweep|diamond", and contextual image paint.
  • visible, opacity, and blend-mode belong to each typed paint.
  • Stops remain structured and ordered.
  • fill="#RRGGBB" is not legacy syntax: it is the canonical compact form for the common one-solid case and is mutually exclusive with <fill>.
  • <fill/> explicitly suppresses a primitive/text default.
  • Inline paint definitions are canonical; reusable definitions/references are future work.

The accepted syntax alternatives are recorded in the RFD: direct typed children are accepted; a generic paint envelope is deferred; CSS-like functional paint attributes are rejected as canonical.

Native multiple strokes

stroke is repeatable. Each occurrence is an independent geometry with its own ordered Paints:

<rect width="320" height="180" fill="#101828">
  <stroke width="12" align="outside" join="round">
    <solid color="#111827"/>
  </stroke>
  <stroke width="3" align="inside">
    <solid color="#2563EB"/>
    <gradient kind="linear">
      <stop offset="0" color="#A78BFA"/>
      <stop offset="1" color="#60A5FA"/>
    </gradient>
  </stroke>
</rect>

This directly represents road casings, double borders, keylines, focus rings, and decorative outlines without duplicated scene geometry, nested wrappers, pseudo-elements, or shadow approximations. Strokes paint in source order after the node's children; each stroke's paints independently paint bottom to top.

This is the one intentional model extension: the proof owns Vec<Stroke>, while the current production scene/archive contract owns one stroke geometry plus one Paints. Production must adopt ordered stroke applications before claiming lossless Draft 0 support.

Materialized proof

The model-v2 proof now includes:

  • a strict parser and fallible, self-validating canonical writer;
  • semantic round-trip independent of arena IDs, with dead/cyclic/mis-parented tree rejection;
  • direct primitive composition, responsive bindings, size constraints, free/flex layout, padding, gap, grow, alignment, clipping, opacity, flips, rotation, and lens operations;
  • all existing solid, linear, radial, sweep, diamond, and image paint variants where Draft 0 can spell their state;
  • ordered fill paints and repeated stroke geometries with alignment, caps, joins, miter limits, and dashes;
  • per-paint visibility, opacity, and blend execution in painter order;
  • node-local unit paint coordinates with the defined one-pixel fallback for a degenerate axis;
  • structured text-line topology shared by measurement, fill, and repeated text strokes;
  • host-relative image RID loading with authored/resolved-path diagnostics;
  • conservative stroke-aware visual bounds and descendant-only container clipping;
  • a thin .grida.xml → PNG host using the same resolve → drawlist → paint pipeline.

The flagship source is source-becomes-surface.grida.xml. It exercises every Draft 0 element/property family, all four gradients, image paints, clipping, responsive bindings, nested text, lenses, and native multi-stroke composition.

cd model-v2/engine
cargo run --bin grida_xml_render -- \
  rig/examples/source-becomes-surface.grida.xml \
  target/grida-xml-source-becomes-surface.png 1600 1000

Evidence

All reviewed proof suites are green:

(cd model-v2/a/lab && cargo test)             # 195 tests
(cd model-v2/engine && cargo test)            # 59 tests
(cd model-v2/a/spike-canvas && cargo test)    # 1 regression test

The example corpus is parsed, canonicalized, resource-resolved, resolved without ignored/error reports, and actually rendered in tests. XML well-formedness, Rust formatting, docs formatting, the repository pre-commit formatter, and root-workspace clippy also pass.

The legacy re-host screenshot oracle has not been silently re-blessed. Its old goldens encode implicit frame borders and the earlier parent-stroke order; reviewing/rebaselining those four screenshots is an explicit follow-up after the new semantics are accepted.

Compatibility boundary

Surface Current status
Existing Grida Paint variants / ordered Paints Projected directly; no parallel XML paint model
Model-v2 lab/engine Parses, canonicalizes, resolves, and renders Draft 0
Multiple stroke geometry Implemented in the proof; incompatible with the current production single-geometry shape
crates/grida production runtime Not yet a .grida.xml consumer
Packed .grida archive Not extended for Draft 0 or repeated strokes
Image resources Strict file host preflights relative RIDs; the low-level paint executor expects an already-populated resource context
Text Topology is deterministic, but the proof metric is still synthetic; production promotion needs one real shared shaper/font contract

No production model incompatibility was hidden by inventing XML-only state. Unrepresentable model values fail the writer; unsupported source syntax fails the reader.

Landed commits

  1. 0cf5b29b8docs(format): specify Grida XML Draft 0
  2. 6e3e7c4a8feat(model-v2): ingest and render Grida XML
  3. eb22e7ec4test(model-v2): add canonical Grida XML showcase

Canonical reading order

  1. Grida XML Draft 0 RFD
  2. Grida XML property registry
  3. Anchor model statement
  4. Model-v2 engine guide
  5. Lab parser/writer boundary

Remaining promotion work

  • Lock the anchor model and visual-only transform framing.
  • Specify .grida.xml Draft 0 and its property applicability.
  • Prove strict parse/canonical write, resolution, ordered paint, repeated strokes, resources, and raster output.
  • Add canonical examples and an all-properties showcase.
  • Decide and migrate production nodes/archive to ordered stroke applications.
  • Promote the parser/materializer out of the model-v2 proof without coupling the engine to XML I/O.
  • Add .grida.xml loading/saving and file association to the product surface.
  • Replace the synthetic text metric with the production shaping/font-resource contract.
  • Define portable resource declarations/packaging and network policy.
  • Reconcile scene/archive gaps before enabling additional registry placeholders.
  • Review and intentionally rebaseline the legacy screenshot oracle.

The issue remains open through production and archive promotion. Effects, editable vector networks, attributed text, components/reuse, durable IDs, scene images, masks, and animation remain later, versioned design work—not implicit Draft 0 syntax.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions