Releases: quillmark-org/quillmark
v0.90.0
v0.89.1
-
chore(release): v0.89.1-rc.1 (#714)
-
feat(wasm)!: 0.90 canonical API — engine-free Quill, single root export, typed errors; Python parity (#713)
-
Proposal: WASM bindings split (core + render) via backend-decoupled Quill (#710)
-
Add version selector matching and mismatch warnings (#708)
-
docs: density-optimization pass on user-facing docs (#703)
-
Remove role annotation from root block metadata header (#707)
-
canon: audit and correct all prose/canon/ docs (#704)
-
Fix makeCard fields/body typed as required in WASM .d.ts (#702)
-
Update CLAUDE.md
-
feat(wasm)!: 0.90 canonical API — engine-free Quill, single root export, typed errors; Python parity (#713)
-
Proposal: WASM bindings split (core + render) via backend-decoupled Quill (#710)
-
Add version selector matching and mismatch warnings (#708)
-
docs: density-optimization pass on user-facing docs (#703)
-
Remove role annotation from root block metadata header (#707)
-
canon: audit and correct all prose/canon/ docs (#704)
-
Fix makeCard fields/body typed as required in WASM .d.ts (#702)
-
Update CLAUDE.md
v0.89.1-rc.1
- feat(wasm)!: 0.90 canonical API — engine-free Quill, single root export, typed errors; Python parity (#713)
- Proposal: WASM bindings split (core + render) via backend-decoupled Quill (#710)
- Add version selector matching and mismatch warnings (#708)
- docs: density-optimization pass on user-facing docs (#703)
- Remove role annotation from root block metadata header (#707)
- canon: audit and correct all prose/canon/ docs (#704)
- Fix makeCard fields/body typed as required in WASM .d.ts (#702)
- Update CLAUDE.md
v0.88.0
- Breaking (bindings + Rust API): a single canonical
Cardwire shape now
flows in both directions. Core owns it asquillmark_core::CardWire(with
From<&Card>/TryFrom<CardWire>); the WASM/Python bindings serialize and
deserialize it instead of hand-rolling their own per-card translation. The
flatCardInput { kind, fields?, body? }input type is removed:
Document.pushCard/insertCard(push_card/insert_card) now accept the
sameCardshape they return ({ kind, payloadItems, … }), so a card from
cards/removeCard/quill.seedCardfeeds straight back in. Build a fresh
card from a flat field map with the newDocument.makeCard/
Document.make_cardhelper. A stale{ kind, fields }object is now a loud
error (deny_unknown_fields), not a silently-empty card. The seeded per-card
gettersquill.seedMain/quill.seedCard(seed_main/seed_card) are
exposed on both bindings, mirroring the RustQuill::seed_main/seed_card. - Breaking (Rust API):
Document::push_cardnow returns
Result<(), EditError>and, withinsert_card, validates that the card's
$kindis a valid, non-reserved composable kind — the cards-list invariant is
enforced at the edit op rather than incidentally atCard::new. - Breaking (bindings + Rust API): the schema-aware form view is removed.
Quill::form/Quill::blank_main/Quill::blank_card(and the
quill.form/blankMain/blankCardbindings) are gone, along with the
Form/FormCard/FormFieldValue/FormFieldSourcetypes. Validation
diagnostics now flow throughQuill::validate(&Document) -> Vec<Diagnostic>
(quill.validate(doc)in WASM/Python), which forwards the canonical
validation::*diagnostics and keeps the non-fatalvalidation::field_absent
completeness signal thatrenderdemotes. Field values/defaults/order are a
Document×quill.schemajoin the consumer performs directly. See
docs/migrations/0.87-to-0.88.md. - Breaking (diagnostics): the validation code
validation::must_fill_absent
is renamedvalidation::field_absent. "Must-fill" is now scoped to the
blueprint communication surface (the<must-fill>sentinel and the fatal
validation::must_fill_sentinel); an absent field is a non-fatal
completeness signal, not a fill requirement, since the render floor
zero-fills it. The schema cell axis is renamed accordingly: the no-default:
cell is Unendorsed (was "Must Fill"), the antonym of Endorsed —
consumers routing on the old code or label must update. Internally
ValidationError::MustFillUnset { source }splits intoFieldAbsentand
MustFillSentineland theMustFillSourceenum is removed. - Breaking (bindings + Rust API): the
examplereference document is
removed.QuillConfig::example()and theQuill.example(WASM) /
Quill.example(Python) getters are gone. Its "show me a filled-out one"
role is served by seeding —Quill::seed_document()/Quill.seedDocument()
/Quill.seed_document()— which returns a committedDocumentrather than
an annotated string. The CLIrenderwith no input file now renders the
seeded document. Nothing consumed the example document's annotations (the
authoring surface isblueprint()), so the projection collapses into the
seed: internally theFillSourcefork in blueprint emission is gone and the
blueprint always rendersdefault:else the<must-fill>sentinel. - wasm: lower the npm package
engines.nodefloor from>=24to>=22.
The runtime never required 24 —--weak-refsneeds only Node 14.6+, and the
usingsugar that motivated the 24 floor is optional (atry/finally
fallback covers Node 22). The aggressive floor hard-blocked installs on Node
22 CI/dev images underengine-strict. - wasm:
Document.makeCard(kind, fields?, body?)now typesfieldsas
optional in the generated.d.ts(was required, contradicting its docs);
omitting it yields an empty field map, as before. - docs: fix the
Quill.schemagetter doc — the returned schema includes
uihints (it never stripped them); the stale "ui hints stripped" wording is
corrected. The 0.87→0.88 migration guide now documents thefillflag's
!fill-placeholder semantics and clarifies that seeding is example-filled,
not a blank-form replacement. - blueprint: flatten
group_fieldsand drop the unused group label (#697). - docs: document seeding (example → absent), fix a block-scalar prescan
bug, and add commitment-ladder docs (#691). - docs(canon): dedup field-resolution semantics into SCHEMAS (#692); note
that released migration guides are era-accurate and immutable (#695); prune
evolutionary information from comments and canon docs (#700).
v0.88.0-rc.1
- blueprint: flatten group_fields, drop unused group label (#697)
- Update CLAUDE.md
- Expose seedMain/seedCard to WASM + Python bindings (#696)
- docs: note that released migration guides are era-accurate and immutable (#695)
- Remove form-view projection; add Quill::validate (#694)
- Remove example() reference document, fold into seeding (#693)
- Document seeding (example → absent) + block-scalar prescan fix + commitment-ladder docs (#691)
- docs(canon): dedup field-resolution semantics into SCHEMAS (#692)
v0.87.3
- Complete and consolidate the $ext mutator surface (#689)
- Complete the
$extmutator matrix with namespace-scoped removal and
card-indexed namespace ops:remove_ext_namespace(RustCard,
removeExtNamespaceWASM,remove_ext_namespacePython) plus
setCardExtNamespace/removeCardExtNamespace. Deleting a sub-namespace
is now the preferred way to clear$extstate — it preserves sibling
consumers' slots and drops$extentirely once empty, whereremoveExt
remains a blunt clear-everything escape hatch. - Breaking (bindings): the whole-map card mutator
updateCardExt/
update_card_extis renamedsetCardExt/set_card_extfor naming
consistency withsetExton the main card.
v0.87.2
v0.87.1
v0.87.0
Arrays become first-class typed fields via a required items element
schema, datetime is unified under a single type: datetime accepting the
full YAML-1.1-style timestamp range (FieldType::Date is gone), and object
zero values are now shape-valid. This release tightens schema-load
validation in several places — empty properties maps and deeper array
nesting are now rejected — and consolidates the example/default conformance
checks behind one shared primitive. Documentation now ships from GitHub
Pages instead of Read the Docs.
Breaking changes
These are schema-load cutovers for Quill.yaml authors; full before/after
steps are in docs/migrations/0.86-to-0.87.md.
-
Array fields now require an
itemselement schema (#672). Arrays
previously carried a single untypedArraytype; scalar arrays were
never coerced or validated element-wise and were always annotated
array<string>. Every array field must now declareitems, and schema
load rejects arrays without it. The bare-properties-on-an-array form
(the old "typed table") is removed in favor of
items: { type: object, properties: … }. Migration for a typed table:# before rows: type: array properties: { name: { type: string }, qty: { type: integer } } # after rows: type: array items: type: object properties: { name: { type: string }, qty: { type: integer } }
A scalar array adds
itemsdirectly, e.g.
counts: { type: array, items: { type: integer } }. Elements now coerce
and validate againstitems(failing at the indexed path, e.g.
counts[1]), and blueprint annotations reflect the element type
(array<integer>,array<markdown>, …). Bundled quills and the
usaf_memogolden schema are migrated. -
FieldType::Dateremoved; usetype: datetime(#679).type: date
no longer exists.type: datetimenow accepts the full range from a bare
YYYY-MM-DDdate through RFC 3339 with offset (seconds optional,Tor
space separator). Datetime values gain calendar validation (e.g. Feb 30
is now rejected), and JSON Schema output emitsformat: date-timefor
all datetime fields. The WASMFieldTypeunion drops"date". The
blueprint hint is nowdatetime<YYYY-MM-DD[Thh:mm:ss]>. -
Empty
properties: {}on an object field is rejected (#678). An
empty properties map carries no information (the only conforming value is
{}) and is almost always a mistake. It is now treated like a missing
propertieskey and surfacesquill::object_empty_properties. -
Deeper array nesting is rejected (#673). The documented "one level of
nesting" contract is now enforced in a single recursive pass, closing a
gap wherearray<object<array>>andobject<array>were silently
accepted. A typed table row and a typed dictionary may carry scalar
columns/properties only; deeper shapes fail with
quill::nested_array_not_supported.
Behavioral changes
- Object zero values are now shape-valid (#677).
zero_valuereturned
a bare{}for every object field, which failed validation on any object
withproperties(each absent property reported asMustFillUnset), so
the zero-filled render path broke for object fields. An object with
propertiesnow recurses, zero-filling each property to its own
type-empty leaf.{}remains the zero only for the property-less edge
case. example:values are now validated (#680). The conformance check for
example/defaultliterals recurses into array items and object
properties and validates datetime format — capabilities the old
load-time path lacked, so previously-unvalidatedexample:values are
now caught.
Documentation & infrastructure
- Docs hosting moved from Read the Docs to GitHub Pages (#671). A new
docs.ymlworkflow builds MkDocs (strict build as a PR check) and
deploys to Pages on a published release; RCs are skipped..readthedocs.yaml
is removed and homepage/User Guide links point at the Pages URL. - Canon + docs: partial documents are first-class citizens (#670). The
docs and binding READMEs no longer claim Must Fill fields must be supplied
before shipping. The only hard render gate is well-formedness (values
coerce, no surviving<must-fill>sentinel); completeness is a hint
surfaced by the form view. Theformat-designer/docs tree is renamed to
quills/. - A Migration section overview page was added and wired into the nav (#674).
Internal
- Example/default validation is consolidated behind a single
validate_schema_literalconformance core shared byquillmark-core
config loading and the CLIvalidatecommand, with author-friendly
diagnostics preserved (#680). - Array and markdown handling collapse into recursive passes over the
schema in both schema-shape validation and the Typst markdown transform
(#673). - Doc/comment fixes from the array-items review (#675).
v0.86.0
Documents now render even when incomplete, the canonical card-yaml fence
becomes a bare ~~~, and the way placeholder/illustrative values are
produced is reworked. This release also fixes two markdown→Typst
conversion bugs and stamps a PDF /Producer field.
Breaking changes
- Bare
~~~is now the canonical card-yaml fence (was~~~card-yaml)
(#662). Existing~~~card-yamldocuments still parse, butto_markdown
re-emits the bare~~~form, so a document's canonical bytes change on
its first re-emit (relevant if you content-hash or byte-compare emitted
markdown, or store blueprint goldens). A side effect: a column-zero
~~~fence in a prose body is now read as a card-yaml block — use a
backtick fence or a non-card-yamlinfo string (e.g.~~~rust) for a
literal code block. Full details and corpus-migration steps:
docs/migrations/0.85-to-0.86.md. fill_blueprint()removed fromquillmark_coreandquillmark,
along with its re-exports (#657, #665). Callers no longer post-process a
blueprint string: fillable/illustrative documents come from
QuillConfig::example(), and the render path fills placeholders itself
(see below).
Behavioral changes
- Incomplete documents render instead of erroring (#665). An absent
Must Fill field is no longer a render error. On the render path each
schema field resolves to its authored value, else itsdefault:, else a
type-empty zero value — applied to the plate projection only, never
persisted to the document. Only malformed input stays fatal: a surviving
<must-fill>sentinel, or a value that won't coerce/validate.
quill.form(doc)still reports completeness independently of the render
gate. defaultvsexampleclarified (#665, #663, #658).defaultis the
value most authors want and is interpolated when a field is omitted (an
authored value always wins);exampledocuments a field's shape only and
never renders into output. Preview and illustrative fills now draw from a
field'sexample:when present, falling back to the leanest type-valid
value ("",0,false,[],{}, first enum variant, empty body).
Markdown → Typst fixes (#661)
- Code is now emitted as
#raw(...)with a string literal instead of a
backtick fence. This fixes fenced or inline code whose content contained
a run of three-or-more backticks, which previously closed the block early
and rendered as markup. - Ordered-list start numbers are preserved — a list written
3./4.now
renders starting at 3 instead of restarting at 1.
New API
QuillConfig::example(), plusexamplegetters on the Python and WASM
bindings (#665).quillmark_core::zero_value— the single source of truth for a field's
type-minimal value, shared by blueprint emission and the render path
(#665).RenderOptions.produceron the core, WASM, and Python render APIs (#656)
— overrides the PDF/Info/Producerstring, which now defaults to
Quillmark <version>on every Typst-rendered PDF.
Other fixes
- PDF rendering folds the
/Producerstamp and the signature-field
AcroForm injection into a single incremental-update pass, preserving
Typst's/Creator(#656). usaf_memo: the signature widget is now overlaid at the 4.5in signature
block (AFH 33-337) instead of the 1in left margin, and no longer consumes
layout flow that could push the block out of position (#660); empty
signature fields no longer carry theAPPEND_ONLYflag (#654).