# Detail Devil Format — v0.1

## 1. Purpose

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.

### 1.1 Goals

- **Readable without tooling.** If you can `cat` the file, you can read it.
  If you can `git clone` the repo, you can ship it.
- **Cheap to contribute to.** Raising a question must cost one line of typing.
- **Honest.** The document records only what a human asserted. Every notion of
  progress is computed from those assertions at read time.

### 1.2 Non-goals

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.

### 1.3 Design principles

1. **Assertions in, state computed.** A conforming document MUST NOT record
   whether a question is resolved. Resolution is derived (§8). A document
   therefore cannot misreport its own state.
2. **Git is the provenance layer.** The format defines no author, created, or
   modified fields. The substrate already has them.
3. **Fail toward unresolved.** Where a construct is ambiguous, the reading
   that leaves a question *open* is the correct one. A tracker that wrongly
   reports "resolved" is worse than useless.

## 2. Terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be
interpreted as described in RFC 2119.

- **Document** — one Markdown file conforming to this specification.
- **Question** — a heading whose text ends in `?` (§4).
- **Section** — any other heading. Organizational only (§4.2).
- **Block** — the content following a heading, up to the next heading of any
  level. A question's block excludes its nested headings and their content.
- **Answer** — an explicitly marked paragraph or section asserting what a
  question's answer is (§5).
- **Dependency** — a directed edge from a question to another question that
  must be resolved before it can be (§6).
- **Reference** — a `[[...]]` span naming a question (§7).
- **Consumer** — software that reads a document and reports on it.
- **Producer** — software or a person that writes a document.

## 3. Document structure

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.

### 3.1 Preamble

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:

```markdown
> **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.

## 4. Questions and sections

### 4.1 Questions

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.

```markdown
## 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.

### 4.2 Sections

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

### 4.3 Identity

Every question has a **slug**, derived from its text by:

1. Converting to lowercase.
2. Removing every character that is not a letter, digit, space, or hyphen.
3. Replacing each run of whitespace with a single hyphen.
4. Replacing each run of hyphens with a single hyphen.
5. Removing leading and trailing hyphens.

`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).

## 5. Answers

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.

```markdown
## 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.

```markdown
## 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:

```markdown
## 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).

### 5.1 Questions that turn out not to matter

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:

```markdown
## 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.

## 6. Dependencies

A question depends on:

1. **Its nested questions.** Every question whose nearest ancestor question
   heading is this question. Sections are transparent for this purpose: a
   question nested under a section inherits that section's nearest ancestor
   question.
2. **Its references.** Every question named by a `[[...]]` 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.

## 7. References

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.

### 7.1 Implicit questions

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.

## 8. Resolution

### 8.1 States

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.

### 8.2 Circular dependencies

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.

### 8.3 Derived reporting

Consumers MAY compute further views. Two are recommended:

- **Trace.** For any unresolved question, the unresolved questions reachable by
  following its dependencies — the answer to "why isn't this resolved?"
- **Leverage.** For any unanswered question, the number of questions that
  depend on it, directly or transitively. Ranking the Ready set by leverage
  produces a work order.

## 9. Tags

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.

## 10. Conformance

A conforming consumer:

1. MUST identify questions by the heading rule of §4.1, answers by the marker
   rule of §5, and dependencies by both clauses of §6.
2. MUST compute resolution and states as specified in §8, including cycle
   detection.
3. MUST NOT reject a document because of unknown frontmatter keys, tags,
   unmatched references, repeated questions, or a declared `devils_version` it
   does not recognize. It SHOULD report these and continue.
4. MUST report an unterminated code fence. Every heading after one is read as
   content (§4.1), which deletes questions an ancestor depended on and can
   flip that ancestor to Resolved. It is the one parse condition that a
   consumer must never pass over in silence.

A conforming producer:

1. MUST write a document that a plain Markdown renderer displays sensibly.
2. MUST NOT write computed state into the document.

## 11. Versioning

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.

## Appendix A — Worked example

```markdown
---
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.
