The devil is in the details. A Detail Devil document is a single Markdown file that tracks the details of a project as questions, records their answers, and declares which questions depend on which others.
A question is resolved when it has an answer and every question it depends on is also resolved. That one rule is the whole format. It surfaces the state that ordinary documents hide: an answer that rests on something still open.
A project that adopts this format keeps a DEVILS.md file at its root, so that anyone familiar with the standard recognizes it on sight.
cat the file, you can read it. If you can git clone the repo, you can ship it.Version 0.1 does not define: multi-file workspaces, access control, question ownership or assignment, due dates, answer staleness, comment threads, or any wire protocol. Tools MAY provide these; the format says nothing about them.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.
? (§4).[[...]] span naming a question (§7).A document is a UTF-8 Markdown file. Its recommended name is DEVILS.md.
A document MAY begin with a YAML frontmatter block delimited by ---. All frontmatter keys are optional:
| Key | Meaning |
|---|---|
devils_version | The specification version the document targets, e.g. "0.1" |
title | A human name for the document |
Producers MAY include any additional keys. Consumers MUST NOT reject a document for unknown keys, and SHOULD preserve them when round-tripping.
Content before the first heading is prose and has no defined meaning.
A document SHOULD open that prose slot with a preamble telling a reader who has never seen this format how to read the file.
Without one, a first-time reader — increasingly, an agent reading a codebase cold — sees sensible questions with answers under them and concludes the project is further along than it is. Three of this format's rules are invisible to them: that nesting means dependency, that [[...]] means dependency, and that an answer alone does not resolve anything. Every one of those misreadings runs the same direction, toward more settled than it is, which is the failure the rest of this specification is built to prevent (§1.3, principle 3). The frontmatter cannot help: devils_version names a specification without saying where to find it.
The recommended wording, which a producer MAY copy verbatim:
> **What this file is.** The open questions on this project, and what depends on
> what. A heading ending in `?` is a question; the block marked `Answer:` beneath
> it is its answer.
>
> **The part that isn't obvious.** A question also depends on every question
> nested beneath it, and on any question it names in `[[double brackets]]`. It
> counts as resolved only when it has an answer *and* everything it depends on is
> resolved too. So an answered question sitting on unanswered ones is an
> assumption, not a decision — making those visible is the point of the file.
>
> Format spec: https://devils.md
Standard wording is worth more than good wording here: a reader who has seen one preamble recognizes every later document at a glance.
The preamble MUST precede the first heading, where it belongs to no question and is inert — a [[...]] span written in it declares nothing. Placed after a heading it would become that question's notes, and its example reference would declare a phantom question.
Consumers MUST NOT parse the preamble for meaning. It is written for people, and its absence is not an error.
A Markdown ATX heading (# through ######) whose text, after removing trailing tags (§9) and surrounding whitespace, ends with ? is a question. The question's text is the heading text with tags removed.
## Who pays for infra?
Requiring the question mark is deliberate: it needs no new syntax, it is self-evident to a reader, and it enforces the discipline the format depends on — details stated as questions rather than as topics.
Only ATX headings are recognized. Setext headings, underlined with = or -, are not, since a line of hyphens is already the frontmatter delimiter.
Fenced code blocks are content, not structure. A # line inside one is never a heading, so a document may quote a shell session or a Ruby file without those comments becoming sections and questions.
Any heading that is not a question is a section. Sections carry no state and are never resolved. They exist so that a document can be organized without that organization being mistaken for meaning.
One section text is special: a section named Answer answers the question it sits directly under (§5).
Every question has a slug, derived from its text by:
What's our hosting budget? yields whats-our-hosting-budget. This closely follows the anchor slugs generated by common Markdown renderers, so a question's slug and its in-page anchor generally coincide. Step 4 makes punctuation between words immaterial, so that [[a - b]], [[a-b]], and [[a — b]] all name the same question.
A slug MUST NOT be empty. A heading that slugs to nothing — one made only of punctuation, such as ## ? — has no identity for a reference to name, so it is not a question however it is punctuated. Consumers SHOULD report it.
Two headings in a document SHOULD NOT slug alike. Where they do they are one question: the first occurrence gives its text, and the answers and dependencies of every occurrence merge into it. Consumers SHOULD report the repetition.
Merging rather than discarding the later occurrences is deliberate. Dropping them would silently discard the questions nested beneath them, removing dependencies from the graph and so making the merged question more likely to resolve — the wrong direction to fail in (§1.3, principle 3).
A question is answered if either form is present. Both mean the same thing.
An answer paragraph in the question's block: a paragraph whose text begins with the word Answer followed by a colon, case-insensitively, optionally wrapped in emphasis or strong emphasis.
## Which email provider?
Answer: Resend on port 2587 — DigitalOcean blocks 587.
An answer section: a section (§4.2) whose text is Answer, case-insensitively, and whose nearest ancestor heading is this question.
## Which email provider?
### Answer
Resend on port 2587 — DigitalOcean blocks 587.
**Answer:**, answer:, and ### Answer are equally valid. The paragraph form suits an answer written in place in a sentence; the section form suits a long or structured one, and is what many authors reach for out of habit. Where more than one is present the question is answered once, and its content is all of them in document order.
An answer section must sit directly under its question — nearest ancestor heading, not nearest ancestor question. It may be a sibling of other sections, but a heading in between severs it:
## Can we ship?
### Discussion notes
#### Answer <- does not answer "Can we ship?"
Were the looser rule used, an Answer heading buried at any depth beneath a section about something else would silently resolve the question above it — a failure toward resolved (§1.3, principle 3).
Everything else in a question's block is notes and does not answer it. This distinction is the reason for requiring a marker at all. Without one, a note such as "Blocked on the budget, need to ask Dan" would register as an answer, and the document would report a question resolved that no one has resolved — the exact failure this format exists to prevent (§1.3, principle 3).
A question may become irrelevant — overtaken by a decision elsewhere, or out of scope after all. This format defines no special construct for that case. State it as the answer:
## Do we need a cookie banner?
Answer: Moot — we have no EU traffic this year.
Then remove the question's dependencies, because a question that genuinely does not matter does not have any: its sub-questions were details of something that turned out not to be asked, so they are deleted along with it, and any [[reference]] it made to an assumption is no longer an assumption of anything.
Keep the question itself. It records why the matter is closed, and any reference elsewhere in the document still resolves to it. Deleting it instead would be worse than useless: under §7.1 a surviving reference would silently re-declare it as an unanswered question with no explanation attached.
A question depends on:
[[...]] reference appearing anywhere in its block, or in the block of one of its answer sections (§5). An assumption recorded while answering is a dependency regardless of which answer form the author used.The two rules produce edges of the same kind. There is one kind of dependency in this format; a question's dependencies are the union of both sets, and no consumer distinguishes an edge's origin. The rules differ only in what they can express.
Rule 1 can express a tree and nothing more: a heading occupies one position, so it has exactly one ancestor question. In exchange it costs no syntax — an author who writes a plain outline of nested questions has already written a working document.
Rule 2 expresses every edge a tree cannot: between siblings, to a question with several dependents, or backward to one already answered. It is also the mechanism by which an author records "I am assuming this" in the middle of writing an answer, without leaving the sentence.
A question MUST NOT depend on itself; consumers MUST discard self-edges.
Duplicate edges between the same pair of questions are a single dependency, including when one arises from nesting and the other from a reference.
A reference belongs to the question it was written under — the one whose block or answer section contains it — and not to that question's ancestors. A nested question's references are its own, and its ancestors depend on them only transitively, through it.
A reference is a [[...]] span. Its target is found by normalizing the span's content with the slug algorithm of §4.3 and matching it against question slugs. Because both sides are normalized identically, [[Who pays for infra?]] and [[who-pays-for-infra]] name the same question.
Consumers SHOULD ignore references appearing inside inline code spans or fenced code blocks.
A reference that matches no question in the document declares one. The implicitly declared question has the reference's content as its text, no answer, and no dependencies — and is therefore Ready.
This is what makes references cheap enough to use. An author writing an answer can name an assumption mid-sentence without breaking off to declare anything, and the document remains correct: the assumption exists, and it is open.
References match questions only. A reference naming a section does not point at it; the section is not a question, so the reference declares a new question that merely happens to share its text. The ? rule of §4.1 governs headings and does not apply here, so an implicitly declared question need not end in one — but a reference that reads as a topic rather than a question is usually a mistake, which is why consumers report unmatched references (§10).
Consumers MUST NOT treat an unmatched reference as an error.
Let answered(q) be true if q is answered in either form (§5), and deps(q) be q's dependencies (§6). A question is resolved when:
resolved(q) = answered(q) AND all(resolved(d) for d in deps(q))
Resolution propagates upward from questions that have no dependencies. Where the rule is self-referential — a question that, by way of its dependencies, depends on itself — it is not satisfied, and such questions are never resolved (§8.2).
The rule tests two facts about a question, so every question is in exactly one of four states:
| all dependencies resolved | some dependency unresolved | |
|---|---|---|
| answered | Resolved | Provisional |
| unanswered | Ready | Blocked |
Provisional is the state this format exists to expose, and the one it is named for: an answer standing on ground that is still open. Consumers SHOULD make it prominent.
Ready is the set of questions that can usefully be worked on right now — nothing is in their way, and nobody has answered them.
Cycles are a reality of real projects — scope depends on budget, budget depends on scope — and this format MUST NOT prohibit them. A format that rejects an honest cycle only teaches its authors to write dishonest documents.
A cycle is not a state. Every question in one is already described correctly by §8.1: unanswered members are Blocked, answered members are Provisional, and none of them can ever resolve. What a cycle adds is the reason, which is worth reporting because it is the one form of blockage that no amount of answering will clear.
Consumers MUST detect cycles and MUST report each one as a whole set — every question in the strongly connected component together — since a cycle can only be broken by deciding its members jointly or by removing one of the dependencies between them.
A question that merely depends on a cycle is Blocked for the ordinary reason; its trace leads into the cycle.
Consumers MAY compute further views. Two are recommended:
A heading MAY end with one or more #tag words. Tags are removed from the heading text before it becomes a question's text or slug (§4.1, §4.3).
No tag has defined meaning in this version. Consumers MUST ignore tags when computing resolution and MAY expose them for filtering.
The stripping rule exists despite the absence of semantics, for two reasons. Without it, a trailing tag would leave the heading not ending in ?, silently demoting a question to a section and dropping it out of the graph. And defining the rule now means a future version can give tags meaning without changing the slug any existing document's headings produce.
A conforming consumer:
devils_version it does not recognize. It SHOULD report these and continue.A conforming producer:
Versions are <major>.<minor>. A minor bump adds backward-compatible constructs, such as a new reserved tag or an optional frontmatter key. A major bump changes how existing documents are read.
A document MAY declare devils_version in its frontmatter. A consumer encountering an unknown version SHOULD attempt best-effort consumption rather than refusing the document.
Each version of this specification is published frozen at https://devils.md/spec/v<major>.<minor>/ — this document is https://devils.md/spec/v0.1/ — with its source Markdown beside it, and https://devils.md/spec/ serves the latest version. A published URL never changes which version it serves, so a citation of v0.1 keeps meaning v0.1.
---
devils_version: "0.1"
title: coordination.tools v1.0 launch
---
# Can we launch v1.0 publicly?
## Which email provider?
Answer: Resend on port 2587 — DigitalOcean blocks 587.
Assumes [[who pays for infra?]] lands under $20/mo.
## Who pays for infra?
Need to ask before committing to a paid tier.
## Should sign-in confirm the email address?
Blocked on [[do we show contact emails to other participants?]] —
if addresses are private, an unverified typo is harmless.
## Do we show contact emails to other participants?
### Answer
Yes, on ride cards, to anyone signed in. Drivers and riders need a way to
reach each other once a seat is claimed, and we have no in-app messaging.
## What is the launch scope?
Depends on [[how much time do we have?]].
## How much time do we have?
Depends on [[what is the launch scope?]].
## Compliance
### Do we need a cookie banner?
Answer: Moot — we have no EU traffic this year.
Computed state, none of which appears in the document:
| Question | State | Why |
|---|---|---|
| Who pays for infra? | Ready | No answer, no dependencies |
| Which email provider? | Provisional | Answered, but its assumption is open |
| Should sign-in confirm the email address? | Ready | Its reference resolved; the note is not an answer |
| Do we show contact emails to other participants? | Resolved | Answered by a section, no dependencies |
| What is the launch scope? | Blocked | In a cycle with "How much time do we have?" |
| How much time do we have? | Blocked | Same cycle |
| Do we need a cookie banner? | Resolved | Answered, and its details were deleted with it (§5.1) |
| Can we launch v1.0 publicly? | Blocked | Seven nested questions, five unresolved |
Alongside these states a consumer reports one cycle, {what is the launch scope, how much time do we have} (§8.2). Both members are Blocked like any other blocked question; the cycle report is what says answering around them will never help.
Note the third row. "Blocked on ..." is a note, not an answer, so the question correctly stays open once its reference resolves rather than turning green on its own.
By leverage, "Who pays for infra?" is the highest-value question in the document: it is Ready, and it blocks both an existing answer and the root.
The document uses both answer forms, which are interchangeable: "Which email provider?" answers with a paragraph, "Do we show contact emails?" with an ### Answer section (§5).
The "Compliance" heading is a section, not a question (§4.2). It groups the cookie-banner question for a reader without appearing in the graph — and per §6, the root still depends on what it contains.