Skip to content

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:

  1. Opening fence — a bare ~~~ (three tildes, no info string). No leading indentation. The legacy ~~~card-yaml info string is still accepted on input but is non-canonical; it parses identically and re-emits as a bare ~~~.
  2. YAML payload — a standard YAML mapping. The reserved keys $quill, $kind, $id, and $ext carry system metadata (see below); every other key is a user-defined data field.
  3. 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 is main by position; $kind: main may 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 than main.
  • $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:

~~~
$quill: my_format@2.1
$kind: main
title: Document Title
~~~
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:

count: 42
price: 19.99

Booleans:

published: true
draft: false

Arrays:

tags: ["tech", "tutorial"]
# or
authors:
  - Alice
  - Bob

Objects:

author:
  name: John Doe
  email: john@example.com

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:

# An own-line comment.
title: My Document  # an inline comment

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.

recipient: !fill
department: !fill Department Here
tags: !fill []

!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.