ADR015 MarkdownInDescribe
Title: Markdown in describe
- Status: Accepted
- Decided: D2026-08-23
- Followed by: ADR017
LinksInDescribe - Revised by: ADR016
UsingMarkdown - Affects:
Describe,Parser - Pairings:
Document— While markdown in describe was rejected as a feature, there may be a prose-native way to accomplish the same tasks that could normally be done in markdown.
Context
Markdown in the describe block was wanted for as long as it was resisted. The want is easy to see. Describe is where an author writes, and markdown is how a lot of writing already works: emphasis, lists, headings, links, fenced code. The resistance is older than this record. The record itself was written long after the events it describes.
A describe block is not a document sitting on disk. The compiler folds it into a JSON manifest, next to identities, edges, and spans. Putting a second language into that field would have meant one of two things: parse the markdown and store a tree, or store the source and make every consumer a markdown parser. The first turns a structured graph into a mixed document. The second is a tax on every tool that only wanted the words.
The deeper objection is what sprig is for. Prose is a field guide, not a documentation site. Describe exists to say what a thing is and why it matters. Full markdown is an unstructured surface that can hold anything a page can hold, and that is a different product. Once describe can be a README, the graph is decoration around a markdown file.
A narrower attempt was made anyway. The original UI tried to display lists when they were obviously lists, and stopped there: not headings, not code fences, not a markdown grammar. What remained open was whether a markdown-lite — emphasis, strong emphasis, and lists, the marks that still read as prose if you ignore them — belonged in describe as editorial syntax.
Separately, there was already more to say than editorial marks can carry.
This universe is supposed to be able to house the sprig and prose docs. Docs
need code, and describe has no honest way to show some. That overflow is
what a document block is for: a layer above describe, still Candidate, at
syntax Document in universe/prose.prose.
Decision
Describe does not support markdown. The language does not parse it, the manifest does not carry a markdown document, and a consumer is not required to speak markdown to read a universe.
Full markdown in describe is refused. Anything that is more than editorial prose — code samples, embedded concepts, the shape of a documentation page — belongs in a document block if that block is added, not in describe.
Editorial syntax (emphasis, lists) was left open rather than adopted. That opening is what 016 takes up.
Consequences
Authoring. A describe block is words. An author who wants a bullet list or a bold phrase has no language-level way to ask for one, and an author who wants a code sample has nowhere in describe to put it.
Cost. The documentation loop this universe is aimed at cannot close inside describe. Housing the language's own docs in prose needs a way to show syntax, and refusing markdown is what makes that a hole rather than a solved problem. The hole is accepted because filling it in describe would replace the graph with a page.
Consumers. A describe field that is a string of prose is readable by anything that can print a string. A describe field that is markdown is a second language. The original reasons still hold: the manifest stays structured, and tools that do not care about formatting are not drafted into a markdown ecosystem.
What this does not do. It does not stop a consumer from noticing that some describe text looks like a list. That attempt existed, and 016 is the record that says a consumer may format what it is given. It does not create the document block; it names the block as the place the overflow goes. It does not decide markdown-lite as a grammar. Leaving that open is the part 016 revises.
Alternatives considered
Full markdown in describe, parsed into the manifest. Store an AST alongside the graph so consumers receive structure rather than source. Rejected. The manifest would then hold two kinds of document, and every compiler, linter, and UI would have to agree on a markdown dialect. Describe stops being a statement about a concept and becomes a page.
Store markdown source in the JSON string and require consumers to parse it. Rejected. This is the cheaper implementation of the same decision, and it is the one that spreads the cost: a receipt scanner, a CLI printer, and a plain-text export all become markdown tools, or they show source to the reader. 016 later allows the source to be stored without making that requirement. The requirement is what this record refused.
Markdown-lite as grammar: emphasis, strong emphasis, and lists. The marks that still read as prose. Not adopted as a language feature. Teaching a small markdown is still teaching markdown, and the boundary after lists and emphasis is where every later request stands. Left as a consideration rather than a ruling; 016 moves it to consumers instead of taking it into the language.
Keep special-casing obvious lists in the UI, with no language change. This is what was tried. It is also not a substitute for a decision: a UI heuristic is not a contract, and a second renderer is free to ignore it. The heuristic is evidence that the want is real. It is not a reason to put markdown in the language.
Let describe become the documentation surface, and keep the graph for navigation. Rejected. That is a markdown wiki with a concept index. Sprig would be competing at the thing it is not, and the structured world would be optional.
Evidence
The compiler stores describe as a string. normalizeDescribe in
packages/prose-parser/src/normalizer.js returns { describe: string }.
There is no markdown tree in the manifest, which is the original worry
about mixing a document language into a structured graph, still visible in
every compile.
The original UI's attempt to display lists is still in
packages/sprig-ui/src/lib/components/Prose.svelte, as parseMarkdownLite.
It recognises unordered and ordered lists and otherwise leaves the text
alone. It does not implement emphasis, strong emphasis, links, or fences, so
the "stopped at lists" half of the source is exact. The other half — that
the UI stopped treating lists specially after the handwritten parser — is
not exact about that file. The original UI still defaults to lists mode.
The beta field guide does not: ProseBlock.svelte splits paragraphs and
renders them as text. Two consumers, two choices, no language feature. That
split is the fact 016 then records.
syntax Document in universe/prose.prose is Candidate. Its describe names
code snippets and embedded concepts as the things describe cannot honestly
hold. The relates between this record and Prose.Document is the pointer:
markdown-in-describe was refused as a feature, and a prose-native block is
the remaining way to do the work markdown would have done.