Migration Guides¶
A release that breaks the document syntax, the plate-JSON wire format, or a public API ships a guide for that version step. Most breaks are hard cutovers — the old form stops parsing or compiling — so the guide is the path forward, not an optional read.
Each guide covers one step. To cross several versions, work through them in order; each states its own breaks in full.
0.93 was never separately published
The 0.93 milestone folded into 0.94.0 — no 0.93.x release was tagged. Upgrading from 0.92.1 means following 0.92 → 0.93 and 0.93 → 0.94 in sequence.
Guides¶
| Step | What changes |
|---|---|
| 0.101 → 0.102 | The pre-1.0 vocabulary reset. install* → overwrite*, writer setBody → reviseBody (now returning the receipt), LiveSession.apply → update, getMarkdown / reader getBody → bodyMarkdown, store/removeSeedNamespace → *SeedOverlay, schemaVersionOf → storageVersionOf. The edit::* codes for content-field failures rename and gain a codec arg, so one code covers both codecs; richtext::not_inline becomes validation::not_inline. Documents and stored blobs are unaffected. Separately, island-id minting splits by case: restoring a deleted island re-lands the id it was deleted under, and the island channel states two contracts it previously left to the implementation. @quillmark/wasm ships --target web: await init() is required before first use, and the bundler plugins the package used to demand are gone. |
| 0.100 → 0.101 | Two Rust constructors leave the published surface: Document::from_main_and_cards becomes crate-internal (its invariants were debug_asserts feeding a release-build panic), and QuillConfig::from_yaml is removed for from_yaml_with_warnings, which keeps the structured diagnostics the boxed string destroyed. The change-bundle verbs take one ChangeBundle struct in place of three positional op arguments, because the bundle gains an island channel: IslandOp edits an island's payload or inserts one with its slot, so a table or image edit keeps the field's identity anchors instead of falling back to a whole-field install. On the bindings the channel is additive (applyChange's new optional islandOps); documents and stored blobs are unaffected. |
| 0.99 → 0.100 | Card $id is removed: the reserved key, find_card / set_card_id / remove_card_id, the collision errors, and cardIndexById / card_index_by_id plus the projected id on both bindings. Per-card consumer keys move to $ext under a namespace you own, with no uniqueness guarantee. Content, Line, Mark, and Island take #[non_exhaustive], the four the 0.99 sweep missed, so their literals give way to new plus with_* setters. A string-valued plaintext field reads through the literal codec instead of markdown, and reader.getContent returns a content field's corpus whichever lane built the document. Load conforms content fields to one resting form per codec (Quill::parse / Quill::conform, the bound door), and Diagnostic.args carries the facts a message interpolates. |
| 0.98 → 0.99 | The Rust API opens ahead of 1.0.0: #[non_exhaustive] across the published crates, so an exhaustive match needs a _ arm and a struct literal gives way to new plus with_* setters. attrs beside a built-in discriminator is rejected where a host authors it, instead of resolving to the built-in and dropping the payload in silence. Island loss opens; a handle from a second copy of @quillmark/wasm is refused. |
| 0.97 → 0.98 | The block vocabulary opens — an unrecognized line kind or container round-trips opaque instead of failing the load. OutputFormat::Txt retires on every surface. |
| 0.96 → 0.97 | The WASM transport read renames to Document.getStored. A card $id becomes unique per document and never empty. |
| 0.95 → 0.96 | One address grammar (DocPath) on every boundary; mutator failures gain namespaced edit::* codes; view() renames to reader(). |
| 0.94 → 0.95 | One insertion verb (insertCard(card, at?)) and one parse entry (Document::parse); the typed writer becomes the one schema-bound door; datetime splits into strict date and datetime. |
| 0.93 → 0.94 | type: richtext(inline) retires for inline: true; typed field writes land; ui.order is removed — declaration order is the ordering contract. |
| 0.92 → 0.93 | The blueprint placeholder splits into value and marker axes: the !must_fill tag replaces the <must-fill> string sentinel, and a bare null falls back to default/zero. |
| 0.91 → 0.92 | !fill renames to !must_fill with no alias — a stale !fill silently loses placeholder status. $seed carries per-card-kind seed overlays. |
| 0.90 → 0.91 | A card-yaml closing ~~~ must sit at column zero. Data-field names and the 100-level nesting limit are enforced on every input path. |
| 0.89 → 0.90 | Quill becomes engine-free data — the engine takes the quill per call. The WASM package collapses to a single @quillmark/wasm root import. |
| 0.88 → 0.89 | A $quill name or version mismatch fails the render instead of warning. |
| 0.87 → 0.88 | The schema-aware form view gives way to quill.validate(doc); one Card shape flows both in and out. |
| 0.86 → 0.87 | Array fields require an items element schema; type: date folds into a unified type: datetime. |
| 0.85 → 0.86 | A partial document renders without error; the canonical card-yaml fence becomes a bare ~~~. |
| 0.83 → 0.84 | The Must Fill / Endorsed schema model replaces required:. |
| 0.82 → 0.83 | The $-prefixed plate JSON wire format retires the legacy uppercase reserved keys. |
| 0.81 → 0.82 | card-yaml metadata replaces the --- / QUILL: frontmatter and fenced cards. |
@quillmark/wasm 0.77 → 0.80 |
WASM consumers crossing the card-syntax release. |
Related¶
For how Quills themselves are versioned and how authors target a version, see Quill Versioning.