Skip to main content

A document has many roots

DocumentVersion.roots is an ordered readonly string[]. A marketing page is a stack of sections — hero, features, testimonial, footer — and no block contains them, so no single id could name where the page starts. Artifact.tree was already an array; it is now what roots denormalizes to, in that order, rather than an array the compiler only ever filled with one entry.

validateStructure refuses a document naming no entry element, seeds cycle detection and reachability from each one, and names the entry that matches no element. A page with one entry is roots: ["stack"] — the same graph, the same walk order, the same content address — so adopting this rewrites no composition and moves no artifact. A container block stays legal: it becomes one shape a page may take rather than the shape every page must take.

Requiring a container block was rejected

The demo does this today with SectionStack, and it is cheap exactly once. Every adopter then writes the same empty block, it reaches the palette as something an author can nest inside itself to no effect, and it owns a DOM element belonging to the consumer — SectionStack emits <main>, so a host layout emitting its own produces two landmarks in the consumer's application. That is a WCAG failure caused here and visible only in a rendered page, which no gate in this repository can see. It shipped: every studio preview route carried nested <main> until #472.

A one-element tuple for Artifact.tree was rejected

Narrowing the array to a tuple is the container block with the type made honest, so it inherits every cost above and adds one. A page whose top level holds two blocks stops being expressible at all, rather than merely being awkward to author — and that page is the case this decision exists to serve.

Document-level named slots wait

slots: Record<string, readonly string[]> on the document is the better long-run shape, and this widens into it without a migration: an ordered list is one named slot with the naming left out. It waits on two things. Slot names on a document have to agree with the layout's, and how a layout and a page reconcile a slot they both fill remains unsettled. Naming them now would decide that silently in the shape of the data. No Layout type exists yet for such a rule to check against. Named slots also turn Artifact.tree into a map, which moves the address of every artifact ever published.