The docs examples loop
Every example in this reference is a hand-authored, unzipped IDML package that CI assembles into a real .idml and runs through the pinned renderer on each build — a structural parse-and-build gate that breaks the docs if the renderer ever stops accepting it.
Every example in this reference is validated against the real renderer on every build — so a page can never describe something the renderer rejects.
In short: The examples you see embedded throughout this reference are not
illustrations typed into the prose. Each one lives in this repository as a
hand-authored, unzipped IDML package — readable XML parts you can review part by
part. On every build, CI zips each package into a real .idml (reproducing the
exact byte rules the renderer expects) and runs it through paged-inspect, the
renderer's introspection tool, built from a pinned commit of the engine. If the
renderer stops accepting an example, the docs build breaks in the same run. That is
the living-docs guarantee: the reference and the renderer cannot quietly drift
apart.
Examples are stored unzipped
An IDML file is a ZIP archive of XML parts (see
Package anatomy). We could store each example as a finished
.idml, but a binary ZIP is opaque in review — you cannot see in a pull request
what changed, and you cannot tell at a glance whether a part is honestly
hand-written or copied. So examples are stored the other way round: unzipped,
one directory per example under examples/, with the package parts as plain files
and an example.json manifest beside them.
Storing them unzipped buys three things. The parts are diff-able in review, so the clean-room rule that examples are ours — authored, not transcribed from the spec — is auditable line by line. The chapter-relevant part (the story, the spread, whatever the page is teaching) can be imported as text straight into the MDX view the reader sees. And each example stays a minimum viable fragment: the smallest package that demonstrates one concept, with the complete-but-uninteresting skeleton kept present-but-quiet around it.
The parts are authored clean-room, grounded in our own parser
(core/crates/paged-parse) and the paged-gen generators — never copied from the
IDML specification. smallest-valid/ is the worked reference: a one-page,
one-story package that the renderer reports as exactly one page, one frame, one
story, one paragraph, one run.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="20.0">
<Story Self="ustory">
<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/[No paragraph style]">
<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
<Content>Hello, paged media.</Content>
</CharacterStyleRange>
</ParagraphStyleRange>
</Story>
</idPkg:Story>
Assembling to a real .idml
A pile of XML files is not yet an IDML — the format has byte-level rules about how
the ZIP is laid out, and a renderer that is strict about them (as ours is) will
reject a naively zipped package. So the docs carry an assembler
(scripts/examples/assemble.ts) that reproduces those rules exactly, mirroring the
generator's own packaging logic in the engine:
| Rule | What the assembler does |
|---|---|
| mimetype first, stored | The mimetype part is the first entry in the archive and is stored uncompressed, with no trailing newline. |
| everything else deflated | All other parts are compressed normally. |
| canonical entry order | Parts are emitted in a fixed order — mimetype, designmap, then META-INF, Resources, XML, MasterSpreads, Spreads, Stories. |
| fixed timestamps | Every entry uses a fixed 1980-01-01 timestamp, so the same package always assembles to byte-identical output. |
The same assembler feeds two consumers, and that is deliberate: it feeds the CI validator today, and it is wired to feed the WebGPU live preview later. Because both get bytes from the same function, the package CI validates and the package the reader will eventually see rendered are byte-for-byte identical — there is no "works in the test, differs in the preview" gap.
Validating against the pinned renderer
With a real .idml in hand, the validator (scripts/examples/validate.ts) runs it
through paged-inspect --json and checks the result against the expect counts in
the example's manifest — pages, frames, stories, paragraphs, runs. If the renderer
reports different totals than the manifest claims, or refuses the package outright,
the example fails and the build fails with it. An example can also be marked
pathological, which inverts the check: it is expected to make the renderer
surface a diagnostic, and it fails if the renderer accepts it.
The renderer used is not "whatever is latest." It is built from a specific commit
of the engine, pinned in this repo's core.pin. This is what ties the docs
version to the renderer's version: a given state of the docs validates against a
known renderer, the pin moves deliberately by pull request, and a weekly drift job
re-validates against the engine's main to surface breakage early — without
silently changing what the published docs were tested against. See
The renderer for what paged-inspect is introspecting.
A structural gate, not a visual one
Here is the honest boundary, and it is the most important thing on this page. The
examples gate is structural, not pixel-level. paged-inspect opens the package
and builds the renderer's internal model — it parses the XML, resolves the
document, builds the display list — and reports the structure it found. It does
not rasterise. It needs no GPU and no fonts.
That makes the examples gate the mirror image of the fidelity gate. The fidelity gate is pixel-deep but narrow — it proves a handful of generated fixtures match InDesign, down to the colour of individual pixels. The examples gate is broad but shallow — it touches every documented construct that has an example and proves the renderer still accepts and models it, but it never checks that the result looks right. An example can be green here and still have a rendering bug, because nothing in this loop ever paints it. We say so plainly rather than letting a green check imply more than it proves.
Why this makes the docs living
Documentation rots when it describes a system that has moved on underneath it. The examples loop is the mechanism that stops that here. Because every example is a real package run through the real renderer on every build, a page cannot keep claiming the renderer accepts a construct after the renderer has stopped — the example that backs the claim would fail first, and the build would go red. The reference is not asserted to match the renderer; it is tested to, continuously. That is the guarantee these docs are built on, and it is worth knowing exactly how far it reaches: it guarantees the renderer still takes the package, not that the page looks the way the prose says it should.
Frequently asked questions
Why store examples unzipped instead of as finished .idml files?
For reviewability and reuse. Unzipped, the XML parts are diff-able in a pull
request, so a reviewer can confirm each example is hand-authored clean-room rather
than copied — and the chapter-relevant part can be imported straight into the page
as readable text. A committed binary ZIP would be opaque on both counts. The .idml
is a build artifact, assembled on demand, never committed.
What exactly breaks the build when an example "fails"?
Either the renderer refuses the assembled package (a parse or build-document
error), or paged-inspect reports structural counts — pages, frames, stories,
paragraphs, runs — that disagree with the example's manifest. For an example marked
pathological, it is the opposite: failing means the renderer accepted something
that was supposed to surface a diagnostic. Any of these returns a non-zero exit and
fails the docs build.
Does a green examples gate mean the example renders correctly? No. The gate is structural: it proves the renderer parses the package and builds its internal model with the expected shape. It does not rasterise, so it says nothing about pixels — colour, glyph placement, layout fidelity. Visual correctness is the job of the fidelity loop, which runs over the generated corpus and diffs against InDesign. The two gates are complementary, and neither stands in for the other.
How we test
The fidelity loop — a deterministic IDML generator emits fixtures, InDesign exports matching reference PDFs, and a per-page ΔE2000 plus SSIM image diff gates the renderer in CI on the CPU backend, with thresholds that only ever tighten.
Cookbook
Task recipes for authoring IDML by hand — build a table, apply a gradient, place an image — each grounded in a validated example and cross-linked to the reference.