Skip to content

anchor — the v2 node model #9

Description

@softmarshmallow

Status — 2026-07-11

Tracking issue for the anchor v2 node model and its authored, file-first projection: n0 XML Draft 0 (.n0.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.

n0 XML Draft 0

.n0.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 .n0.xml → PNG host using the same resolve → drawlist → paint pipeline.

The flagship source is source-becomes-surface.n0.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 -p n0 --bin n0_xml_render -- \
  fixtures/test-n0-xml/examples/source-becomes-surface.n0.xml \
  target/n0-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 .n0.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 n0 XML Draft 0
  2. 6e3e7c4a8feat(model-v2): ingest and render n0 XML
  3. eb22e7ec4test(model-v2): add canonical n0 XML showcase

Canonical reading order

  1. n0 XML Draft 0 RFD
  2. n0 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 .n0.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 .n0.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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions