0.96 → 0.97 — Document.get → getStored; $id becomes the unique card handle¶
Two changes. On the WASM surface, the quill-free transport read
Document.get becomes Document.getStored; the interpreted schema-plane
read quill.reader(doc).get is unchanged, and Python is unaffected (it has no
quill-free field read). In the document model, a card's $id is now unique
per document — parse repairs a violation, mutators and storage reject one.
If your host never calls doc.get(...) and never stamps the same $id onto
two cards, there is nothing to do.
Why¶
The write surface separates verbatim from typed on two axes — receiver and
verb: verbatim writes are store_field / store_fields on Document; typed
writes are set / set_all on quill.writer(doc). "The verb carries the lane."
The read surface used to separate the same two lanes on the receiver alone —
verbatim Document.get vs. interpreted reader.get, one get doing both jobs.
The two read rows in the bindings parity table read as near-duplicates, and the
only thing distinguishing a total, returns-undefined-on-a-typo read from a
schema-checked, throws-on-a-typo read was which object you called it on.
getStored restores the write side's discipline to the read side: the verbatim
read is the read echo of store, and reader.get stays the reader/writer twin of
set. The lane now lives in the verb both ways.
getStored's semantics are byte-for-byte what get did — verbatim value for a
field (a content object for a richtext field, a scalar/array/object otherwise),
body content when addr.field is absent, undefined for an absent field, and a
throw only for an out-of-range addr.card. Only the name changed.
Migrate: rename the call sites¶
Every doc.get(...) transport read becomes doc.getStored(...). The argument is
the same Addr | string (a bare string is { field } shorthand).
// 0.96
doc.get('qty') // 3
doc.get('missing') // undefined
doc.get({ card: 2, field: 'qty' }) // a composable card's field
doc.get({}) // main body content
// 0.97
doc.getStored('qty') // 3
doc.getStored('missing') // undefined
doc.getStored({ card: 2, field: 'qty' }) // a composable card's field
doc.getStored({}) // main body content
Leave reader.get alone — it was already the right name, and it is the read you
want whenever a quill is in hand:
quill.reader(doc).get('subject') // richtext → markdown; unchanged
quill.reader(doc).card(0).get('body') // card field by its $kind; unchanged
getMarkdown (the body markdown read), getExt / getExtNamespace, and isFill
keep their names.
Unaffected¶
- Python.
quillmark(PyO3) has no quill-free field read; interpreted field reads already go throughquill.reader(doc).get. No change. - Core (Rust). The transport read is the map-idiomatic
payload().get, already lexically distinct fromreader.get— no collision, no rename.
$id is now unique per document¶
$id graduates from free-form metadata to the durable card handle (canon:
DOCUMENT_STORAGE.md § Card-id identity). One rule — unique across a
document's composable cards, never empty — enforced per boundary:
- Parse repairs. A duplicate
$idkeeps the first card's entry and drops it from later cards under a warning (parse::card_id_duplicate); an empty$idis dropped too (parse::card_id_empty). The document still loads, and one save converges on a valid file. - Mutators reject. Core
push_card/insert_cardand WASMinsertCardthrowedit::card_id_collision/edit::empty_card_idfor a card whose$idis taken or empty. The guarded setterDocument::set_card_id(re-)stamps a placed card;remove_card_idretires a handle. Python'sadd_cardstamps no$id, so it cannot collide. - Storage rejects. A stored JSON blob carrying a duplicate or empty
composable-card
$idfails to load as malformed. No writer up to 0.96 minted a$idat all, so existing rows are unaffected.
find_card / cardIndexById are unchanged for valid documents — there is
now at most one match, so "first match" stops being a caveat. A $id on the
main card is outside the scope (main is addressed structurally, never by id)
and is preserved verbatim.
Migrate: nothing, unless you deliberately stamped one $id onto several
cards. Re-stamp each card with a distinct id and key any shared grouping off
a user field or an $ext namespace instead.