card-yaml Blocks¶
Quillmark documents carry structured metadata in card-yaml blocks — explicitly delimited blocks that isolate YAML data from the surrounding Markdown prose. The first such block (the root block) names the format used to render the document; later blocks are composable cards.
~~~
$quill: my_format
$kind: main
title: My Document
author: Jane Doe
date: 2025-01-15
tags: ["important", "draft"]
~~~
# Document content starts here
Block Structure¶
A card-yaml block has three parts, in order:
- Opening fence — a bare
~~~(three tildes, no info string). No leading indentation. The legacy~~~card-yamlinfo string is still accepted on input but is non-canonical; it parses identically and re-emits as a bare~~~. - YAML payload — a standard YAML mapping. The reserved keys
$quill,$kind,$id, and$extcarry system metadata (see below); every other key is a user-defined data field. - Closing fence — a tilde run at least as long as the opener. The canonical opener and closer are both
~~~; a longer opener (e.g.~~~~) requires an equally long closer.
The unstructured Markdown body begins immediately after the closing ~~~
fence and runs to the next opening fence or the end of the document.
A blank line is required immediately above every ~~~ opener,
except when the opener is the very first line of the document. A
~~~ line without a blank line above it is not an opener — it is
treated as an ordinary code block.
Because every column-zero ~~~ block is a card-yaml block, writing a literal
fenced code block in prose requires the escape hatch: use a backtick fence
(or a ~~~ fence carrying a language info string, e.g. ~~~rust). Adding more
tildes does not escape — a ~~~~ block is still a card (its closer must just
be at least as long). A ~~~ fence whose info string is anything other than
the legacy card-yaml stays an ordinary code block.
System Metadata ($)¶
The block's YAML payload may contain four reserved $-prefixed keys.
After parsing, these keys are extracted from the user field set and exposed
on the block's typed metadata.
$quill: <name>@<version>names the format used to render the document. The root block (the first block, identified by position) must declare$quill— it is the only required$entry. If the root block is missing$quill, parsing fails.$kind: <kind>identifies a card's kind. The root block's kind ismainby position;$kind: mainmay be omitted or declared explicitly — any other value is a parse error. Every composable card must declare a kind matching[a-z_][a-z0-9_]*other thanmain.$id: <value>is an opaque, optional identifier — plain metadata with no validation or uniqueness requirement, carried through the round-trip.$ext: <mapping>is an opaque YAML mapping reserved for out-of-band extension data — UI editor state, agent annotations, anything bespoke to a consumer that should not reach the rendered output. Round-trips through Markdown and the storage DTO; never appears in the plate JSON consumed by backends. The value must be a mapping (scalars and sequences are parse errors); an empty$ext: {}is preserved as a distinct, explicit declaration.
$ metadata entries may appear anywhere in the block's payload (the
canonical emission puts them first, in the order $quill, $kind,
$id, $ext). Any other $-prefixed key is a parse error — the set is
closed.
Version Selectors¶
Pin a specific version with @version syntax on the $quill line:
| Syntax | Meaning |
|---|---|
format |
Latest version (default) |
format@latest |
Latest version (explicit) |
format@2 |
Latest 2.x.x |
format@2.1 |
Latest 2.1.x |
format@2.1.0 |
Exact version 2.1.0 |
Quill names must match [a-z_][a-z0-9_]* (lowercase letters, digits, and
underscores; must start with a lowercase letter or underscore).
Payload Data Types¶
The data payload (everything in the YAML mapping except the $-prefixed
metadata keys) is standard YAML.
Strings:
title: Simple String
quoted: "String with special chars: $%^"
multiline: |
This is a
multiline string
Numbers:
Booleans:
Arrays:
Objects:
Object-valued fields must be schematized in Quill.yaml with type: object
and a properties: map; array-valued fields with type: array and an
items: element schema (e.g. items: { type: string }, or items: { type:
object, properties: … } for a list of objects). Nesting beyond one level is
not supported. See
Quill.yaml Reference: Field Types.
Field names must match [a-z_][a-z0-9_]*.
Comments¶
YAML comments are supported in the payload and round-trip through
toMarkdown — both own-line comments and inline comments:
Comments adjacent to $ metadata keys — own-line or inline — round-trip
identically to comments on data fields.
Placeholder Fields (!fill)¶
A top-level field tagged !fill marks the value as a placeholder awaiting
input. The tag round-trips through parsing and emit, so editors and bindings
can detect and update placeholders without losing them.
!fill is valid on scalars (string, number, bool, null) and sequences. It is
rejected on mappings. Other custom YAML tags (!include, !env, …) are
dropped with a warning.
Field-name Rules¶
User field names match [a-z_][a-z0-9_]*. Uppercase, hyphens, and the
$ sigil are not allowed — the $ prefix is reserved for system
metadata ($quill, $kind, $id, $ext, plus the plate-JSON keys
$body and $cards).
Card Blocks¶
Every block after the root is a card — see Cards for syntax and structural rules.
Emission¶
toMarkdown always emits the canonical block form — a bare ~~~
opener, the $ metadata lines in the canonical order $quill, $kind,
$id, $ext, the remaining data fields, and a ~~~ closer. The root
block emits both $quill and $kind: main; composable cards emit
$kind: <kind> plus any $id / $ext they declared. Fence markers,
key ordering, and YAML quoting are normalised; !fill tags and YAML
comments (own-line and inline trailing, including those adjacent to $
lines) survive the round-trip.
The payload is coerced and validated against the schema declared in the
Quill's Quill.yaml (main.fields). See the
Quill.yaml Reference for field
types and constraints.