Configuration
roadkeep.toml is the whole of the sixth law: configuration, not convention. The prefix,
the id shape, which files hold which role, the markers, the limits, the budgets, the claims and
the reference scheme are all decided per project, because a tool that hard-coded any of them
would be one that only fits the repository it was written in.
The table below is generated from roadkeep config, which is the command that answers this
question from inside a checkout. It is here because the reader who most needs it is the one who
has not got a checkout yet: deciding whether to adopt comes before there is anything to
configure, and the description of the keys was behind the adoption it would decide.
What a number is for
Section titled “What a number is for”Two tables hold judgements rather than settings, and the difference matters more than any individual value.
[limits] bounds what a line may say and [budgets] bounds what a file may cost. Both are
measured against a corpus — the lines that already read well — and not chosen because they
sound reasonable. govern is the verb that writes one, and it refuses a limit this corpus
already breaks, because a number whose first act is a finding is one somebody lowers, reads
the report and quietly raises again.
govern --because "…" is where the argument goes: your sentence, wrapped into comments above
the key and stacked on whatever argued it before. That is the half worth keeping. A number
without its reasoning is one the next person changes on instinct, and the reasoning is the
expensive part.
So this page does not recommend a value, and nothing here should be copied as a default. Run
roadkeep adopt against the file you already have; it measures how many of your lines a given
limit would refuse, which turns the decision into a number rather than a hope.
Adopting with a backlog already in it
Section titled “Adopting with a backlog already in it”The interesting case is not an empty repository. It is one with a ROADMAP.md somebody has
been keeping by hand for two years, which is exactly the repository worth adopting and the one
init is wrong for.
The order that works:
uvx roadkeep adopt docs/ROADMAP.md # measure it: how many lines would a limit refuse?uvx roadkeep declare roadmap docs/ROADMAP.mduvx roadkeep lint # every finding, each naming the command that closes ituvx roadkeep lint --fix # the derived half, repaired mechanicallydeclare retrofits one role onto a file that already exists, rather than scaffolding a new
one. What lint then reports is a work list, and the findings that remain after --fix are
the ones needing a judgement — each carrying the command that closes it, and each with
a page here explaining the class.
Nothing forces you to declare every role at once. An absent role is absent, not empty: a project can govern its roadmap for a month before it declares a changelog.
The worked example
Section titled “The worked example”This repository’s own roadkeep.toml
is the one configuration that is provably valid — its docs/ are the format’s conformance
fixture, and lint passing on them is a gate on every push. It is one fetch away rather than
transcribed here, so it cannot go stale.
The This repository column below is that same file, read back: what roadkeep declares for itself, beside what it would use if it declared nothing.
Every table
Section titled “Every table”Read from roadkeep 0.2.125 against roadkeep.toml. What is listed is what that build accepts — which is how a key your copy does not have is told from a typo.
the top level
| Key | Type | Default | This repository |
|---|---|---|---|
blocks | array | [] | — |
budgets | — | no default | declared |
claims | — | no default | declared |
criteria | — | no default | declared |
files | — | no default | declared |
grammar | — | no default | — |
headings | — | no default | declared |
id_sources | array | [] | ["agents.md"] |
ids | — | no default | — |
install | — | no default | — |
ledger | — | no default | — |
limits | — | no default | declared |
markers | — | no default | declared |
non_goals | — | no default | declared |
prefix | string | "RK" | "RK" |
priority | array | [] | — |
reads | — | no default | declared |
ref_scheme | string | "id" | "id" |
refs | — | no default | — |
report | — | no default | declared |
requirements | — | no default | — |
reserved_ids | array | [] | — |
rules | — | no default | — |
tools | — | no default | declared |
[files]
The governed files. A project declares the ones it has; the rest are absent, not empty — `strategy` missing means Shio, not a Shio with an empty strategy. The same is true of `deferred` (RK96), which is why it is not in :data:`DEFAULT_PATHS`: a project that never pauses anything has no store, rather than an empty one. `decisions` is the sixth and the one adopters ask for by name (RK1269): read as this format an ADR is the pair already written here — an id, a marker, one falsifiable claim and a reason — and the only difference is the departure. A roadmap line leaves by three doors and a decision leaves by one, being superseded, so nothing in that file is ever deleted and it grows only by decisions somebody actually made. **A named role and not an open tuple**: a role no machinery knows is a file with no schema, which is the convention this tool replaces.
| Key | Type | Default | This repository |
|---|---|---|---|
changelog | — | no default | "docs/CHANGELOG.md" |
decisions | — | no default | "docs/DECISIONS.md" |
deferred | — | no default | — |
improvements | — | no default | "docs/IMPROVEMENTS.md" |
roadmap | — | no default | "docs/ROADMAP.md" |
strategy | — | no default | — |
[refs]
| Key | Type | Default | This repository |
|---|---|---|---|
decisions | — | no default | — |
improvements | — | no default | — |
strategy | — | no default | — |
[ids]
`[ids]` — the shape of an id, where a project already spells one the format refused (RK106). Its own table for the reason `[headings]` is: the spelling has more than one part, and a bare `pad` beside `prefix` would read as one of the limits.
| Key | Type | Default | This repository |
|---|---|---|---|
pad | integer | 1 | — |
suffix | boolean | false | — |
[headings]
`[headings]` — the word a project files work under (RK75). Its own table and not a top key, because the heading is a shape with more than one part and the next question about it (a sub-block that carries no word at all) belongs under the same heading.
| Key | Type | Default | This repository |
|---|---|---|---|
permanent | boolean | false | true |
word | string | "Block" | — |
[install]
`[install]` — whether this project holds its wired launcher, hook and skill at the version they are (RK1192). Its own table and not a `[rules]` entry, because every key there is a prose rule one governed *file* is not held to, and this is about the harness around them.
| Key | Type | Default | This repository |
|---|---|---|---|
enforced | boolean | false | — |
pinned | boolean | false | — |
wired | string | "" | — |
[markers]
| Key | Type | Default | This repository |
|---|---|---|---|
deferred | string | "⏸" | — |
open | array | ["📋", "💭", "⏳", "🛠"] | ["📋", "💭", "⏳", "🛠"] |
retired | string | "🗑" | "🗑" |
shipped | string | "✅" | "✅" |
undesigned | array | ["💭"] | ["💭"] |
[ledger]
Which slots the ledger's lines carry (RK43, RK48). Its own table because the shape of a file is one decision with two parts, and `markers.ledger` put half of it under a heading that cannot name the other half: a symptom is not a marker.
| Key | Type | Default | This repository |
|---|---|---|---|
marker | boolean | true | — |
symptom | boolean | true | — |
[limits]
| Key | Type | Default | This repository |
|---|---|---|---|
line | integer | 320 | 320 |
part | integer | 40 | — |
prose | integer | 88 | 88 |
section | integer | 250 | at 2 addresses |
symptom | integer | 120 | 120 |
why | integer | 200 | 200 |
[rules.<role>]
The rules a role may switch off (`[rules.<role>]`, RK52). Not limits, because they are not numbers, and not `[ledger]`, because that table says which *slots* a line has. `ref` joins them for RK66's reason: whether a line must point at a rationale section is a convention, not a fact about the format, and a project that documents the opposite one gets a finding for obeying itself.
| Key | Type | Default | This repository |
|---|---|---|---|
one_sentence | boolean | true | — |
ref | boolean | true | — |
terminator | boolean | true | — |
[non_goals]
`[non_goals]` — the two fields the roadmap's other bullet has (RK70). Opt-in for RK66's reason: two live corpora wrote theirs as free prose, and a default that reported findings on the first run is a gate that gets bypassed instead of adopted.
| Key | Type | Default | This repository |
|---|---|---|---|
lead | integer | 80 | 60 |
why | integer | 320 | 200 |
[criteria]
`[criteria]` — the same two numbers about the **positive twin** (RK1265): what must be true for a block to be finished, where a non-goal says what is not built. Opt-in for the reason that one is, and separately: a project may govern one list and not the other, and a criterion that inherited the non-goals' limits would be judged by numbers measured on a different corpus.
| Key | Type | Default | This repository |
|---|---|---|---|
lead | integer | 80 | 60 |
why | integer | 320 | 200 |
[claims]
`[claims]` — how long a claim on a line reads as held (RK151). Its own table for the reason `[headings]` has one: a bare `held` beside `prefix` would read as one of the limits, and it is not a limit on any field — it is the one number in the claim mechanism that is a judgement about how long work takes.
| Key | Type | Default | This repository |
|---|---|---|---|
held | integer | 60 | 60 |
[requirements]
| Key | Type | Default | This repository |
|---|---|---|---|
declared | array | [] | — |
[report]
`[report]` — one key, and refused like every other. A table with room for a token or a URL is a table somebody puts a token in; the only thing declarable here is *where* a defect in this tool would be filed, and filing it is still a command a person types.
| Key | Type | Default | This repository |
|---|---|---|---|
upstream | — | no default | "alegauss/roadkeep" |
[budgets.<path>]
| Key | Type | Default | This repository |
|---|---|---|---|
bytes | — | no default | at 2 addresses |
lines | — | no default | at 2 addresses |
[grammar.<role>]
`[grammar.<role>]` — the shape of a role's records, which L6 declared everything about except (RK1064). Three keys and no fourth: what a record starts from, which markers it may carry, and which slots it does without. `states` is not among them — whether a file *is* a status is a fact about the tool's own roles, so a project may reshape a line and never invent a state for which no verb exists.
| Key | Type | Default | This repository |
|---|---|---|---|
drop | array | [] | — |
extends | string | "roadmap" | — |
markers | array | [] | — |
[tools]
`[tools]` — what one served tool may cost a session (RK1059). Its own table and not a `[budgets]` entry, because every key there is a **path** and this cost is not a file: it is composed per session from the parser, the config and the `TOOLS` table, so an entry under a name no file has would break the one thing that table's refusals can say. Per tool and not per list, which is the decision RK464 deliberately left open. A ceiling on the total fails on whichever tool is added last and names nothing; a per-tool one is refused by the tool that grew, which is the tool whose description somebody just edited — and `cost --tools` already ranks them, so the read that composes the fix exists.
| Key | Type | Default | This repository |
|---|---|---|---|
characters | — | no default | 2850 |
session | — | no default | 66660 |
[reads]
`[reads] brief` — what the one read that replaces reading the file may cost (RK1286). Its own table and not a `[budgets]` entry, for `[tools]`' reason exactly: every key there is a **path**, and a brief is composed per call from the line, its design, the deps, the non-goals and four allowances. Not `[limits]` either — that table is the widths of the fields a line carries, and this is the size of an answer about one.
| Key | Type | Default | This repository |
|---|---|---|---|
brief | — | no default | 3300 |
Fixed by the build, and not yours to declare
The boundary of the question above. These are numbers this build measures from a corpus rather than accepts from a file, so there is no key for them.
chars/wordat 6.6 — the first round number above this corpus's percentile, so a word aim clears the character gate about nineteen times in twenty (read at 6.53 over 1992 samples, 95th percentile).