Architecture decision records
An ADR is a short note recording one decision: what we chose, why, and what it costs us. One file, numbered, never deleted. If a decision is reversed later, the old record stays and is marked Superseded by ADR-XXXX, so the reasoning is still readable.
The point is not ceremony. It is that in two years somebody — possibly one of us — will ask "why on earth is it done this way?", and the answer should take one minute to find rather than an afternoon of archaeology. It is also the main defence against the bus factor that left RESTest 1.x hard to maintain.
Format
Section titled “Format”# ADR-NNNN: <decision, as a short statement>
**Status:** Proposed | Accepted | Superseded by ADR-NNNN**Date:** YYYY-MM-DD
## ContextWhat forced a decision. Facts, constraints, measurements.
## DecisionWhat we are doing. Present tense, unambiguous.
## ConsequencesWhat this buys us, what it costs us, and what it rules out.
## Alternatives consideredWhat else was on the table and why it lost.When to write one
Section titled “When to write one”Write an ADR when a choice has more than one defensible answer and would be expensive to reverse: a dependency that will spread through the codebase, a boundary between modules, a file format, a licence. Do not write one for ordinary implementation choices — those belong in the pull request.
Records that cite a plan
Section titled “Records that cite a plan”Records written while 2.0 was built cite its plan and the files that described its process: milestone
codes such as M1.9 in their status lines and amendments, rows of ROADMAP.md, and CLAUDE.md or
the reviewer's checklist as they were then. Those files are kept, as they were:
- for records written while 2.0 was built, at the tag
history/2.0-development; - for records written after it, such as the later amendments of ADR-0025, at commit
0b17e7b7, the last in which this repository held them.
Plans are no longer kept in this repository (ADR-0030), so records written from now on cite none.
| # | Decision | Status |
|---|---|---|
| 0001 | Record architecture decisions | Accepted |
| 0002 | Rewrite rather than refactor; Apache-2.0; no 1.x source | Accepted |
| 0003 | Every module targets Java 21; toolchain on 25 | Accepted, amended at M0.2 |
| 0004 | Multi-module structure and inward dependencies | Accepted, amended at M0.2 and on 14 and 30 October 2026 |
| 0005 | Test cases are data, not generated source code | Accepted, amended at M1.1b, M2.2, M10.3 and M11.3, and on 10 and 17 October 2026 |
| 0006 | One event stream; every interaction persisted | Accepted, amended at M1.4, M1.6, M1.7, M11.1, M9.1 and M11.3, and in #325, #327 and #334, and on 10, 19 and 25 October 2026 |
| 0007 | The parser sits behind our own interface; OAS 2.0, 3.0.x and 3.1.x | Accepted, amended at M0.2, in #303 and in #328, reversed at M1.2 |
| 0008 | Extension points, and no AI-specific abstractions | Accepted |
| 0009 | The engine never blocks; idle time replaces a throughput floor | Accepted, amended twice on 29 October 2026 |
| 0010 | IDL: relicensed assets, ANTLR4 parser, solver behind an interface | Accepted, not yet built (14 October 2026) |
| 0011 | Evaluation harness in evaluation/, outside the Maven build — amended at M1.9: in a repository of its own, so evaluation/ was never created |
Accepted, amended at M1.9, on 9 October 2026 and on 3 November 2026 |
| 0012 | The canonical model: immutable records, sealed types, recorded gaps | Accepted, amended at M1.8, M2.1a and M2.1b, and on 17, 26 and 29 October 2026 |
| 0013 | Where input values come from, and how a campaign is put together | Accepted, amended at M1.11, M2.7a, M2.4, M2.10a, M9.1, M9.3, M10.1 and M10.3, and on 10, 12, 13 and 25 October 2026 |
| 0014 | A reply is judged against the specification document itself | Accepted, amended at M1.8 and on 10, 17, 19 and 22 October 2026 |
| 0015 | One command, a budget spent in full, and what an exit code means | Accepted, amended at M1.7, M1.8, M2.7a, M2.10a, M11.1, M9.1, M11.3, M12.1a, M12.1c and M12.1b, in #314 and #344, and on 1, 11, 12, 19, 22, 25 and 28 October 2026 |
| 0016 | A fault: catalogue number as identity, our words as description, classified twice | Accepted, amended at M1.10 and on 10, 12, 15, 17, 22 and 29 October 2026 |
| 0017 | What we take from the tool that won the 2026 competition, and what we refuse | Proposed, amended at M9.1, M9.4 and M10.1 |
| 0018 | A choice between shapes is one more shape | Accepted, amended on 10 October 2026 |
| 0019 | A document's own sample values are read, sent as written, and named in the record | Accepted |
| 0020 | A dictionary is a named list of values with one key, and the plan decides what each list is for | Accepted, amended at M2.7c, M2.4, M2.10a and M8.3, on 1 October 2026, at 2.1, and on 12 and 13 October 2026 |
| 0021 | A request body is one more value, and what the API returns is reused leaf by leaf | Accepted, amended at M2.5b, M9.2, M9.5, M9.7, M9.8 and M9.9, at 2.1, and on 12 October 2026 |
| 0022 | The characters a value is made of are read where its length already is, and the regular expressions are somebody else's | Accepted, amended at M9.6, at 2.1, and on 12 October 2026 |
| 0023 | A plan is an ordered list of named sources, and the order is the preference | Accepted, amended at M9.1, M10.1 and M10.3, and on 1, 12 and 13 October 2026; its §5 superseded by ADR-0034 |
| 0024 | v2.0 is the version submitted to the 2027 competition, and what that leaves for 2.1 | Accepted, amended on 3, 4, 5, 11 and 12 October 2026 |
| 0025 | The numbers somebody decided live in one settings object, layered from four sources, and never in the plan | Accepted, amended at M11.1, M9.1, M10.1, M10.3, M8.5, M11.2, M12.1b, M9.5, M9.6, M9.7, M10.4-M10.7 and M9.8-M9.9, at 2.1, and on 1, 12, 13, 14, 15, 17, 22, 25, 28 and 29 October 2026 |
| 0026 | A run opens with every operation once, in five steps, and one part of the tool owns the clock | Accepted, amended at M10.3 and on 28 October 2026 |
| 0027 | A request the API accepted is sent again with one thing changed, and says what it broke | Accepted, amended at M10.2, M8.5 and M10.4-M10.7, and on 12, 13, 15 and 17 October 2026 |
| 0028 | A creation may start a short series about the thing it made, and each series asks one question | Accepted, amended at M11.2 and on 12 and 13 October 2026 |
| 0029 | A key is handed over by the person running the tool, sent where the document says, and hidden in everything the run writes | Accepted, amended at M12.1c and on 10 and 25 October 2026 |
| 0030 | The process RESTest is built with lives outside the repository, and AGENTS.md is the contract it relies on | Accepted, amended on 3 November 2026 |
| 0031 | The manual is the Markdown GitHub shows, and a release carries no PDF of it | Accepted, amended on 5 November 2026 |
| 0032 | A release is a draft until its image and archives are checked, and RESTest starts through the archive's launcher | Accepted, amended on 13, 23, 27 and 30 October and 1 November 2026 |
| 0033 | A dictionary file is written by operation and place | Accepted, amended on 13 October 2026 |
| 0034 | Version 2 of the plan's format: a strategy says its kind, a source has two forms, a group is a map of weights | Accepted |
| 0035 | The dictionary and the plan have published JSON Schemas, held to their readers | Accepted |
| 0036 | Kinds of fault RESTest numbers itself: F920 and F921, in the range the catalogue leaves to tools |
Accepted |
| 0037 | Every fault is an error or a warning, and a run fails on its errors | Accepted |
| 0038 | report.json says how a run went over the time it was given | Accepted, amended on 25 October 2026 |
| 0039 | The HTML report: one page beside report.json, drawn in the browser from the whole of it, fetching nothing | Accepted |
| 0040 | RESTest is installed with Homebrew from a tap of its own, by a formula that brings its Java and is written into the tap once a release is published | Accepted |
| 0041 | A run stops sending when the API answers 401 to every request of its opening lap, and ends with 5 |
Accepted |
| 0042 | An operation the API asks to be left alone is left alone, and the whole API once a second one is | Accepted, amended on 29 October 2026 |
| 0043 | A run can be given a rate to keep to, in one line, and keeps to it evenly where it sends | Accepted |
| 0044 | RESTest's modules are on Maven Central as es.us.isa.restest, uploaded with a release and published there by a person; their POMs name exactly what the archive carries, and their Java code is no part of the promise |
Accepted |
| 0045 | RESTest is run with jbang from a catalog of the ISA group's own, whose alias names RESTest on Maven Central and is written once a release is published | Accepted |
| 0046 | A skill in the open format of agent skills teaches a coding agent to use RESTest; it ships with every release as the JSON Schemas do, ends the page for coding agents word for word, and is held to the tool by a test | Accepted |
| 0047 | The documentation is a website, built from a released version's files as they are by a repository of its own, and moved on to each release once it is published; a test holds the pages to the shape the site reads | Accepted |