2. Overview and goals
Every change, from the editor, the ud command, a usefigma script or an agent, is one or more events appended to the WAL.
A design file has two parts:
- the tree, the document as one JSON object, pretty printed, rewritten only by consolidation;
- the WAL, a list of events appended one per line, each line starting with a comma.
Every change, from the editor, the ud command, a use_figma script or an agent, is one or more events
appended to the WAL. Opening a file reads the tree and folds the events into it in file order. When the list
gets long, consolidation folds it into a new tree and empties it.
2.1 Goals#
| Goal | How the format meets it |
|---|---|
| Git-mergeable (COL-4, NFR-2) | Every change appends exactly one line per event and touches no other line. A line that is on disk is never rewritten, not even to repair it (seq included, 5.1); only consolidation and a migration rewrite the file. *.design merge=union keeps the lines of both branches. Nodes live in a flat map with fractional indexes, so concurrent inserts and moves never touch the same text. Folding is per property: edits of different properties of one node on two branches both survive. |
| Agent-readable | Plain JSON with Figma Plugin API property names, so an agent that knows Figma reads the file without a new vocabulary. A $comment at the top says how to change the file. Every event is a self-contained line that can be written with any JSON library. |
| Small (NFR-3) | No binary data in the file: images and fonts are files in the assets folder, named by content hash and stored once. Properties equal to their default are not written. Derived values are not written. A 10 000-node file is under 15 MB after consolidation. |
| Reviewable | A change is a readable diff: added lines in the WAL, each naming its actor, time, operation and target. |
| Robust (NFR-6) | Readers repair what they can and report it; unknown fields and operations survive a round trip; a file broken by a bad manual merge can be recovered line by line (section 9). |
| Deterministic | An event carries every value it generated (ids, times, the result of a computation). Folding the same file twice gives the same document on every surface. |
2.2 Figma Plugin API names#
Property names, enumeration values and value shapes are those of the Figma Plugin API wherever that is
sensible. layoutMode, itemSpacing, paddingLeft, primaryAxisAlignItems, layoutSizingHorizontal,
cornerRadius, fills, strokes, effects, constraints, characters, fontName, boundVariables,
componentProperties, reactions, exportSettings mean what they mean in Figma and take the same values.
This is a design goal, not a convenience. use_figma runs scripts written against the Plugin API (CMD-11),
and its layer maps a node property to a stored property one to one (CMD-15). ud inspect output reads like
Figma. An agent that learned Figma’s vocabulary can read and write this file.
The format departs from Figma only where git or determinism requires it. Each departure is stated where it occurs; the main ones are:
| Figma | This format | Why |
|---|---|---|
children arrays |
a flat node map with parent and a fractional index (4.3) |
Concurrent inserts on two branches must not edit the same line. |
PAGE nodes under DOCUMENT |
tree.pages, a map with index (4.2) |
Same reason. |
modes, variableIds arrays |
maps with index (4.10) |
Same reason. |
Instance sublayers stored with I…;… ids |
derived, not stored; overrides keyed by an override path (4.8) | A main component change must not rewrite every instance. |
x, y of auto layout children, hug sizes |
derived (4.5) | A text edit must not rewrite its siblings (LAY-9). |
relativeTransform |
x, y, rotation, flipX, flipY (4.4.1) |
Separately mergeable properties. |
SHA-1 imageHash |
sha256-<hex> (8.2) |
One hash for file names and references. |
The Plugin API layer converts between the two in each case.