0.86.0 → 0.87.0 — Typed arrays via items, unified datetime, stricter schema load¶
0.87.0 reshapes how array and datetime fields are declared in a
Quill.yaml schema. The changes are hard cutovers at schema-load time:
a schema that uses the old forms no longer loads, and the load diagnostics
name the exact field and fix. The typed accessors, plate-JSON wire format,
and document syntax are unchanged.
If you only render documents against the bundled quills, no action is
needed — they are already migrated. This guide is for anyone maintaining
their own Quill.yaml, a stored golden schema, or a generator that emits
schemas.
Array fields now require items¶
Arrays previously carried a single untyped Array type. Scalar arrays were
never coerced or validated element-wise and were always annotated
array<string> regardless of contents; element typing was only possible for
objects, via a bare properties: map directly on the array (the "typed
table"). 0.87.0 makes every array carry an items element schema, and
items is required.
Scalar arrays¶
Add items with the element's type:
Elements now coerce and validate against items, failing at the indexed
path (e.g. counts[1]). The blueprint annotation reflects the element type
(array<integer>), not a blanket array<string>.
Typed tables (the bare-properties-on-array form is removed)¶
A typed table used to be an array with properties: set directly on it.
That form is gone; wrap the object shape in items instead:
rows:
type: array
- properties:
- name: { type: string }
- qty: { type: integer }
+ items:
+ type: object
+ properties:
+ name: { type: string }
+ qty: { type: integer }
markdown[] and other element types¶
The same items shape applies to any element type. A markdown list becomes:
markdown[] elements are converted to Typst markup on render, and the
schema emits contentMediaType: text/markdown on the items recursively.
What the loader now rejects¶
| Diagnostic | Cause | Fix |
|---|---|---|
quill::array_missing_items |
type: array with no items |
add an items element schema |
quill::array_properties_not_supported |
properties: set directly on an array |
move it under items: { type: object, properties: … } |
quill::items_not_supported |
items on a non-array field |
remove items, or change type to array |
type: date is gone — use type: datetime¶
FieldType::Date is removed. There is now one temporal type,
datetime, and it accepts the full range from a bare calendar date through
a full timestamp with offset:
YYYY-MM-DD(bare date)YYYY-MM-DDThh:mm:ssand the space-separated form- RFC 3339 with timezone offset; seconds and fractional seconds optional
Knock-on effects:
- Calendar validation — values are now parsed, so an impossible date
such as
2026-02-30is rejected (it was previously accepted by the format check). - JSON Schema output — every datetime field emits
format: date-time. - WASM
FieldTypeunion — the"date"member is removed; use"datetime". - Blueprint hint — now
datetime<YYYY-MM-DD[Thh:mm:ss]>.
A value that was a valid date (YYYY-MM-DD) is still valid under
datetime, so existing document values do not need editing — only the
schema's type: keyword changes.
Empty properties: {} is rejected¶
An object field with 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 properties key and reported as
quill::object_empty_properties. Either give the object real properties or
drop the field.
Deeper array nesting is rejected¶
The documented "one level of nesting" contract is now enforced in a single
recursive pass, closing a gap where deeper shapes were silently accepted. A
typed table row (array<object>) and a typed dictionary (object) may
carry scalar columns/properties only. Shapes like array<object<array>>
or object<array> now fail with quill::nested_array_not_supported.
If you relied on a deeper shape, flatten it — e.g. lift the inner array to a top-level field, or model the data as a separate card kind.
example: values are now validated¶
The conformance check for example: and default: literals now recurses
into array items and object properties and validates datetime format —
capabilities the old load-time path lacked. An example: value that does
not match its field's type, enum, or datetime grammar is now caught at load
(quill::example_type_mismatch, quill::example_not_in_enum,
quill::default_type_mismatch). This surfaces latent mistakes in example:
blocks that previously went unchecked; correct the literal to match the
declared schema.
Migration checklist¶
- Add
itemsto every array field. Scalar arrays getitems: { type: <elem> }; typed tables moveproperties:underitems: { type: object, properties: … }. - Replace
type: datewithtype: datetime. Document values need no change; verify no value is an impossible calendar date. - Remove empty
properties: {}maps. - Flatten any array nested more than one level deep.
- Re-baseline stored golden schemas and blueprint goldens — array
annotations (
array<integer>,array<markdown>, …), the typed-table shape, and datetimeformat: date-timeoutput all change. - WASM consumers: drop
"date"from any code that branches on theFieldTypeunion.
Load each migrated quill (Quill::from_path / the CLI validate command);
the diagnostics above name the offending field and the corrective action
directly.