Markdown To

markdownto.ai v0.1 draft Specification family

Three formats,
one grammar

Each specification defines only what is particular to it and inherits everything else from one shared document. So a person who has learned [email protected] has already learned most of [email protected], and an agent that can repair one file can repair all three.

Status
v0.1 drafts · written in the open, will change
Base grammar
GitHub Flavored Markdown a strict superset of CommonMark
Shared rules
specs/conventions.md normative
Conformance
fixtures from day one valid + invalid + expected diagnostics

§ 01

The catalogue

Three small formats. Each one ships an agent-facing specification document, a JSON Schema for its intermediate representation, conformance fixtures, and its own verb vocabulary — and each one names itself in the file, so the envelope and never the filename decides what a file is.

Exactly one spec per file

The envelope names one spec and the whole file is interpreted under it. Files do not compose — there is no “kanban section inside a todo file” — because composition makes conformance untestable, makes diagnostics ambiguous, and makes the patch engine’s job undecidable. A project that needs two specs uses two files. This is enforced mechanically: a markdownto value that is a sequence or a mapping rather than a string is MDTO004.

§ 02

Shared conventions

One normative document defines the base grammar, the envelope, item identity, inline metadata, tags, the preservation guarantees, the diagnostic code scheme, and the conformance-fixture contract. Every rule in it is subordinate to a single sentence.

A conforming file must be a joy to hand-author.

Where a design choice traded machine convenience against human comfort, human comfort won. There is no HTML-comment metadata, no required identifiers, no generated preamble, no field a person would not willingly type. Where syntax was needed we adopted vocabulary that already exists in the Obsidian and Dataview ecosystem rather than inventing our own — humans may already know it, agents certainly do, and conforming files open meaningfully in Obsidian for free.

Two consequences implementers have to internalize. One: a file a person wrote by hand, with no tooling ever having touched it, is fully conforming — tooling adds nothing on read, and the minimum on write. Two: nothing in a conforming file exists for the machine’s benefit alone. If a construct would only ever be produced by a tool and never typed by a person, it does not belong in a Markdown To spec.

  1. The envelope

    A conforming file begins with a YAML frontmatter block whose markdownto key names exactly one spec, in the form [email protected]. title and id are reserved and mean the same thing everywhere; a spec may define more (kanban defines columns). Everything else is an unknown key: parsed, preserved, written back verbatim.

    That last rule is what lets a Markdown To envelope share one frontmatter block with another system’s — a page carrying description: and agentsfs_role: alongside markdownto: loses nothing on round-trip. Filename suffixes such as .kanban.md aid discovery and are advisory only: the envelope, never the filename, is authoritative.

    ---
    markdownto: [email protected]
    title: Markdown To launch
    ---
  2. Lazy identifiers

    No item carries an identifier until something needs one. An untouched item is addressed by its content and position, so a hand-written file contains zero identifier noise — the single most visible expression of the governing principle. When tooling first needs a durable handle it pins an Obsidian-style block identifier as the final token of the line.

    Identifiers are stable once pinned: tooling must never renumber, normalize or garbage-collect them, and must never pin one on an item it did not touch. A token at end of line that looks like an identifier but is malformed is an error (MDTO010), not literal text — silently treating it as prose would let tooling mint a colliding identifier later.

    - [ ] Publish conformance fixtures
    - [ ] Build the patch engine ^t3

    Exactly one line carries an identifier, because exactly one has been touched by tooling.

  3. Inline fields

    Typed metadata uses Dataview-style inline fields: a key, ::, a value, in brackets. Keys are matched case-insensitively and lowercased in the IR. Fields may appear anywhere in the item’s text — position is never meaningful in any Markdown To spec — though putting them at the end is conventional and is what tooling produces.

    Metadata sitting next to the thing it describes survives copy-paste, reordering and moving an item between files, and it reads acceptably in a plain viewer. A lookup table keyed by identifier would have forced identifiers onto everything, which is exactly what the governing principle rejects.

    - [ ] Ship fixtures [priority:: high] [due:: 2026-08-20]
    the shared vocabulary
    priority (low/medium/high), due (a real YYYY-MM-DD calendar date), owner (free text; a leading @ is preserved). Recognized by the specs that use the item model — not by [email protected], which has no items.
    unknown fields
    Not an error, not a warning. Carried as a string, written back verbatim. Specs extend by promoting an unknown field to a known one, which is an additive minor version change.
    near misses
    A bracketed span that looks like a field but is not one — a single colon, a triple colon, a missing close — is MDTO020, a warning, and is treated as literal text. Hand-authors write [due: 2026-08-15] constantly, and a silent no-op would be the worst possible outcome.
    the empty value
    [owner:: ] is MDTO021. Omit the field instead. A key repeated on one item is MDTO023: two values with no defined precedence is ambiguity, and Markdown To does not guess.
  4. Tags and wikilinks

    A tag is # followed by word characters, and it must contain at least one non-digit — so #1234 in “Fix issue #1234” is literal text, because issue references are extremely common in hand-written task lists. / denotes hierarchy, matching Obsidian. Tags are stored without the leading sigil: the # marks the tag, it is not part of it. That has behavioural weight — audio narrates the word, never the hash.

    [[Name]] and [[Name#^blockid]] are the shared cross-reference syntax, path-independent so that reorganizing files never breaks links. Wikilinks are inert in the core: recognized and surfaced with source positions, carrying no validation obligation and no behaviour, so that spec-owned relations and link-aware tools can build on them. A spec that assigns meaning to a wikilink defines that meaning, including whether an unresolvable target is a diagnostic.

    Tags and wikilinks both stay part of the item’s rendered text. They read as part of the sentence; fields do not.

    - [ ] Reorganize the garage #home/errands
    - [ ] Fix issue #1234 — see [[Weekly notes]]
  5. Fenced code is data

    Inside inline code spans and fenced or indented code blocks, nothing is scanned — no fields, no tags, no identifiers, no headings. This is what makes it possible to write documentation about Markdown To in a Markdown To file, and it is why nothing in a conforming file can cause a tool to execute anything.

  6. Everything carries a source position

    Each spec ships a JSON Schema for its canonical intermediate representation, and every node in it carries a position with 1-based line and column points, matching mdast conventions. Positions are what make line-addressed diagnostics and minimal patches possible, so they are required rather than optional.

    Renderers, verbs and downstream tools consume the IR; they never re-parse source and never guess. [email protected] does not restate the item model — its schema $refs todo’s definition directly, so a card and a todo item are the same object by construction rather than by intention.

Read specs/conventions.md — the normative text

§ 03

What is guaranteed

These are the promises that make the file, and not the tool, the source of truth. An implementation that cannot keep them is not conforming, whatever else it does.

identity round-trip
For any conforming file, parse then serialize reproduces the input byte for byte. There is no partial credit for losing one blank line.
unknown data survives
Unknown frontmatter keys, unknown inline fields, unrecognized tags, prose, blockquotes, tables, images, code fences, HTML blocks, and blank-line placement all survive a parse and serialize cycle unchanged.
minimal diffs
A semantic mutation produces the smallest source edit that achieves it. Unrelated lines are never reformatted, re-wrapped, re-indented or reordered. Markdown To tools do not normalize files.
invalid data survives too
A value that fails validation is reported, not deleted. Diagnostics are advice; the file remains the author’s.
conflicts surface
A tool holding a file open must detect that the source changed underneath it, via a source hash, and surface the conflict rather than overwrite.
3.1

Determinism and safety

deterministic
Compilation and serialization produce the same output from the same input, on every machine, forever.
no model in the loop
No LLM is involved in conforming compilation. A renderer never infers what a heading “means”.
no execution
Fenced code is data. Nothing in a conforming file can cause a tool to run it.
no network
None during validation, rendering or mutation. Links are text. (The one exception is mdto audio voices, which asks a provider for its catalogue and is not part of compiling anything.)
safe on hostile input
Rendering a conforming file is safe whatever the file contains: raw HTML in the source is escaped or stripped by the HTML renderer, never passed through.
Versioning

The major version changes on any breaking change, and an implementation must reject a file whose major it does not implement (MDTO006). The minor changes only additively: a file valid under [email protected] stays valid under [email protected] for later M. An implementation reading a newer minor than it implements must accept it, must preserve constructs it does not understand, and should warn with MDTO008.

The file declares the version it was authored against, and implementations must not rewrite it as a side effect of an unrelated edit.

§ 04

Diagnostics and conformance

A diagnostic is a code, a severity, a 1-based line, and a message. Only error makes a file non-conforming — warnings and info are advice. Codes are stable forever: never reused, never renumbered, never repurposed. Adding one is an additive change; changing what one means is breaking.

MDTO001–099 Shared — envelope, item identity, inline fields, item structure. Owned by conventions.md.
MDTO100–199 [email protected] — heading levels, section names, metadata on a heading.
MDTO200–299 [email protected] — columns, the declared column set, cards before the first column.
MDTO300–399 [email protected] — document direction, chapters, block direction, empty manuscripts.
MDTO400+ Allocated to future specs, one hundred per spec, in registration order.
Recovery is a single pass

Validation reports everything it can find rather than stopping at the first problem, because an agent repairing a file should get the whole list. Two rules make that deterministic. Envelope errors halt validation: if the envelope is missing, unparseable, or names a spec or major version the implementation cannot handle, no spec is known, so exactly one diagnostic is reported. Everything else recovers locally: a construct that fails validation is reported, kept in the IR, and does not suppress checks on its neighbours. Each spec states what its own invalid constructs recover to.

4.1

Fixtures are the operational definition

Every spec publishes conformance fixtures from day one. They are what “adheres to the format” actually means: an independent implementation is conforming if and only if it agrees with them.

valid/<case>.md
Must parse with no error-severity diagnostic, and must round-trip byte for byte. Warnings and info are permitted.
invalid/<case>.md
Has a sibling .diagnostics.json. An implementation must produce every listed diagnostic, matching on code, severity and line, and must produce no error-severity diagnostic that is not listed. Message wording is informative and need not match.
unfixtured codes
Three, deliberately: unknown spec name, newer-minor-version warning, and unknown voice. What each reports depends on an implementation-local registry rather than on the file alone. Every other code has at least one fixture.
Fixture style

Valid fixtures are also documentation, so they have to look like something a person would happily write by hand: real content, no foo and bar, no metadata a human would not type, no identifier on an item tooling has not touched. A valid fixture that reads like machine output is a bug in the fixture. Every Markdown example on the three sheets is one of them, pasted in byte for byte.

§ 05

Every spec documents itself

Each spec ships an agent-facing document at specs/<name>/SPEC.md, versioned with the spec and consultable at any time. Its shape is fixed, in this order: purpose, grammar, rationale, examples (valid files, plus invalid files with the exact diagnostic each produces), and the verb reference. The document is part of the spec’s definition of done, alongside the schema and the fixtures.

mdto — working name in progress
$ mdto spec kanban
[email protected] — agent-facing specification
  1. Purpose   what the format is for
  2. Grammar   heading = column, item = card
  3. Rationale why it is shaped that way
  4. Examples  valid + invalid + diagnostics
  5. Verbs     add · move · done · rm · edit

$ mdto validate launch.kanban.md
ok conforms to [email protected] · 7 cards, 4 columns

$ mdto spec --list
[email protected]     add · done · undone · rm · move
[email protected]   add · move · done · rm · edit
[email protected]    estimate · produce · preview · voices
universal    validate · render · spec
  • Verb vocabularies are spec-owned

    There is no universal mutation vocabulary. todo and kanban overlap because they share an item model — a coincidence of domain, not a platform contract — and audio’s verbs share nothing with either. A new spec invents whatever its domain needs. Only validate, render and spec are universal.

    universal validaterenderspec

  • Rationale is not decoration

    Agents repair and extend files far better when they understand intent, and won’t “fix” things in the wrong direction. Every spec’s rationale section opens with the same line — read this before repairing a file; most bad repairs come from guessing at intent — and names its deliberate non-features so a deferred feature is never mistaken for an oversight.

  • Fetchable, not scrapeable

    The same documents live at stable URLs beside an llms.txt so an agent never has to scrape a human documentation site to author a conforming file. They also stay in the repository, versioned with the code that implements them.

    fetch markdownto.ai/llms.txt