docflow

The docflow methodology

A formal definition of the documentation-led, ADR-driven method that docflow installs and maintains — the artefacts, the conventions that govern them, a normative specification precise enough to implement against, why the method is useful, and where it is weak.

1. Definition

docflow is a documentation-led, ADR-driven way of running a repository. Every consequential decision is recorded as an Architecture Decision Record (ADR) — a small, numbered, immutable-by-default document that states one decision, why it was made, and how to tell whether the system actually satisfies it. The ADR catalogue is the source of truth: code is written to match the ADRs, not the other way round, and the running system is expected to conform to its ADRs.

The method constrains form and lifecycle, never content. An ADR body is free-form prose under a fixed skeleton, so the same machinery records a regulatory control, a training-module decision, a software-architecture choice, a brownfield retrofit, or a firmware timing constraint without modification. It has been applied in production across regulated compliance, training-material authoring, application tooling (an issue-tracker), brownfield retrofits of existing services and apps, and embedded firmware (see §6).

The method is deliberately small. It is a set of canonical files plus a handful of rules strict enough that both a human and a coding agent can pick up a cold repository and act correctly with no oral handover.

2. The artefacts

Artefact Role
adr/NNNN-<slug>.md One decision per file, contiguously numbered. The catalogue.
INDEX.md A table regenerated from ADR metadata. Derived, never hand-edited.
plan/todo/ · plan/done/ The work queue. Together with git history, plan/done/ is the shipped-work record.
CONVENTIONS.md The authoring rules every contributor reads first.
AGENTS.md (+ CLAUDE.md) The hard-rules entry point coding agents load.
_agent/ The agent operating contract — who writes what, the one real mutex, how an unattended run behaves. Optional; per-mode members, and the run prompt only where a verify gate and the plan/todo/ queue it walks both exist.

3. The conventions

C1 — One decision per ADR. An ADR captures exactly one decision. When a decision splits or changes, you supersede the original with a new ADR rather than expanding the old one.

C2 — A fixed shape. ADRs follow a fixed section order, so they are scannable and machine-checkable (§4.3).

C3 — A status lifecycle. Every ADR moves through a defined state machine (§4.4). Status is not decorative: it drives the work queue.

C4 — Testable, numbered acceptance criteria. Each ADR states numbered, observable criteria. “Done” means the criteria hold — not that code was written.

C5 — Contiguous identity. ADRs are numbered contiguously with no gaps and no reuse, so the number is a stable, citable key.

C6 — Derived indices. Anything derived (INDEX.md, a roll-up) is treated as derived — regenerated, never the source of truth.

C7 — The plan queue is the ledger of work. A unit of work is queued before it starts, naming its owning ADR(s), scope, and exit criteria. When it ships, git history and plan/done/ are the shipped record.

C8 — Audit trail and approvals. Substantive ADR changes append a Revision History row; an Approvals table is populated when an ADR is accepted.

C9 — ADRs are internal. ADR numbers, titles, and the existence of the catalogue never appear in any surface an end user sees.

C10 — An explicit git contract. Conventional Commits, a mandatory rationale footer on ADR-touching changes, and one declared integration model so “shipped” is unambiguous.

C11 — Enforcement by audit. None of the above is left to goodwill; an audit routine checks it mechanically.

4. Normative specification

This section is the precise definition. It uses the keywords MUST, MUST NOT, SHOULD, and MAY in the sense of RFC 2119: MUST is a hard requirement an audit can fail; SHOULD is a strong default a team may override with a recorded reason; MAY is optional.

4.1 Defined terms

4.2 Conformance

A repository conforms when every applicable invariant in §4.5 holds and its audit reports clean. An ADR conforms when it carries the metadata and sections of §4.3 and its status is reachable in the §4.4 state machine. A tool implementing the method MUST refuse to mark a unit of work complete unless its owning ADR’s acceptance criteria are met.

4.3 The ADR record — schema

Each ADR is a Markdown file named NNNN-<kebab-slug>.md (NNNN zero-padded to four digits) with a frontmatter block and a fixed body.

Frontmatter fields:

Field Type Required Meaning
adr integer MUST The number; MUST equal the filename’s NNNN.
title string MUST Sentence-case decision title.
status enum MUST One of the §4.4 states.
date ISO date MUST Authoring date.
owner string MUST Accountable agent or human.
shape enum MAY capability or technology. Only repositories that distinguish the two shapes carry it; an absent field means capability.
supersedes id / empty MAY ADR(s) this replaces.
superseded-by id / empty MAY ADR that replaced this one.
depends-on list of ids SHOULD ADRs this decision builds on.
tags list MAY Free-form classification.

Body — a capability ADR MUST contain these sections in order: Context → Capability statement → User stories / scenarios → Acceptance criteria → Out of scope → Open questions → References → Revision History → Approvals. A technology ADR replaces “Capability statement / User stories” with “Decision → Rationale → Consequences”, and its Rationale MUST name the alternatives considered with specific rejection reasons. Acceptance criteria MUST be numbered and testable (§4.5 INV-4).

Where both shapes are in use, the shape is declared in the shape: field, never inferred from the number: a repository keeps one contiguous sequence for both shapes, ships one template per shape (each numbered 0000, neither a decision), and its index MUST carry a Shape column. A repository using one shape omits the field entirely and its index carries no such column. (A federation roll-up MAY add the column when any member declares two shapes — §5.)

Earlier revisions of this method encoded the shape as a number range — capability records below a recorded cutoff, technology records at or above, the technology template numbered at the boundary. That encoding is superseded, for three reasons: it overflows (a capability block reaching the cutoff cannot grow, because the technology block already holds the numbers above it, and widening the boundary collides with real records); it forces an exception, since the record that adopts the method is itself a technology-shaped decision that must be first in the sequence; and it is per-shape numbering, which the flat-identity rule (§4.6) already rejects for domains. A repository still on it remains conformant — it is checked by its own range rules — and MAY migrate: the technology records take the numbers after the last capability one in their original order, capability numbers are unchanged, shape: is written on every record, and every in-repository reference follows. The migration MUST be confirmed against a shown old-to-new map before any file is written, and MUST land as one commit whose message lists every pair — outside the repository, history is not rewritten, so that list is the only bridge between the two numberings.

4.4 Status — the state machine

States: Proposed, Accepted, Implemented, Superseded, Deprecated.

From To Guard (MUST hold to transition)
Proposed Accepted Open questions resolved (section empty); Approvals populated.
Accepted Implemented The owning plan item(s) reached the completion event. In a federation, every per-repo plan item shipped.
any non-terminal Superseded A successor ADR exists and the supersedes/superseded-by links are set symmetrically.
any non-terminal Deprecated The decision is retired with no successor.

Superseded and Deprecated are terminal and reachable from any prior state. There are no other transitions; an audit MUST flag any status outside this set or any transition lacking its guard.

4.5 Invariants

4.6 Numbering at scale, and alternatives considered

The identifier is an integer, rendered four-digit zero-padded by convention. Tooling sorts ADRs numerically, not lexically, so a catalogue is not capped at 9999: widen the padding to five digits if a repository ever approaches it (none in practice does). The padding is cosmetic; the integer is the identity.

Two richer identifier schemes are deliberately not the default:

4.7 Grouping ADRs by domain

A repository may add domains/<slug>/README.md files (e.g. domains/auth/, domains/billing/) — each a curated index of the ADRs belonging to one area. It is an optional but first-class organisational layer: a navigation view over the single flat catalogue, enabled when a repository spans distinct areas or its numbered list grows large enough to be hard to scan.

The grouping is organisational only. An ADR keeps its flat contiguous number and its INDEX.md row; a domain README never renames, renumbers, or namespaces — the number stays the single identity. new-adr maintains the grouping: it records each new ADR under its owning domain’s README and offers to create domains/<slug>/README.md when an ADR is filed under a domain that does not exist yet.

Two adjacent mechanisms are deliberately distinct: it is not per-domain numbering (auth/0001, billing/0001) — rejected in §4.6; identity stays the flat number — and it is not the federation (§5), which serves genuinely independent sequences across separate repositories. Domain grouping is one repository’s one catalogue, viewed by area.

4.8 Capturing work done outside the process

The model assumes decisions are recorded as ADRs and work is queued as plan items — but real development sometimes runs ahead of the process: a large development lands with no ADR and no plan, recorded only in version control. The method’s answer is reconstruction, not exception: such a development is treated as an ADR not yet written. It is captured the same way an existing codebase is adopted (§ backfill) — recover the decision(s) it embodies as ADR(s) drafted at Implemented, each Revision History row citing the implementing commits and noting it was recorded after the fact, with matching plan/done entries so the Implemented⇒plan-coverage invariant (INV-3) holds. The audit carries a coverage check — a deliberately conservative, doc-centric heuristic — that flags substantial behaviour with no owning ADR, so an escaped development is surfaced for capture rather than silently accumulating. Trivial or mechanical changes are not in scope here: they belong to the commit history, not the catalogue.

5. Scaling to many repositories

A single product spread across several repositories runs as a federation, applying §4 unchanged:

The federation rules are one principle applied repeatedly: no tool writes across a repository boundary; consistency is declared at the edges and enforced by audit, not by remote control.

6. Domain independence

Because the method fixes form and lifecycle but never content (§1), the same primitives carry very different subject matter. The ADR body is prose; domain-specific rigour is added as optional hard rules layered on top (for example a regulated-evidence posture — attribution, retention, e-signatures — or a mandatory persona set), leaving the core untouched.

It has been used successfully across:

The breadth is the point: the method is a container for decisions, not a template for any one kind of system.

7. Why it is useful

8. Weaknesses and limits

An honest method states where it costs more than it returns.

9. Lineage

The method is a strict, operationalised descendant of the classic lightweight ADR (Michael Nygard, 2011; the doc/adr convention popularised by Martin Fowler and the ADR community). It keeps the core idea — short, immutable, numbered decision records — and adds the parts a running, agent-driven, possibly multi-repository project needs: a status-driven work queue, an enforcement audit, an explicit privacy boundary, and a federation model.


See the examples for the method applied, or the source on GitHub.

Live coordination state belongs to each queued item: Claimed by, Blockers and Stopped. Historical completion is derived from git and the done footer. Final results and persisted reports use Status at a glance (This run, Overall, Yet to do), distinguishing prepared PR work from completed integration.