Skip to content

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.

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.

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:

Terminal window
uvx roadkeep adopt docs/ROADMAP.md # measure it: how many lines would a limit refuse?
uvx roadkeep declare roadmap docs/ROADMAP.md
uvx roadkeep lint # every finding, each naming the command that closes it
uvx roadkeep lint --fix # the derived half, repaired mechanically

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

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.

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

KeyTypeDefaultThis repository
blocksarray[]
budgetsno defaultdeclared
claimsno defaultdeclared
criteriano defaultdeclared
filesno defaultdeclared
grammarno default
headingsno defaultdeclared
id_sourcesarray[]["agents.md"]
idsno default
installno default
ledgerno default
limitsno defaultdeclared
markersno defaultdeclared
non_goalsno defaultdeclared
prefixstring"RK""RK"
priorityarray[]
readsno defaultdeclared
ref_schemestring"id""id"
refsno default
reportno defaultdeclared
requirementsno default
reserved_idsarray[]
rulesno default
toolsno defaultdeclared

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

KeyTypeDefaultThis repository
changelogno default"docs/CHANGELOG.md"
decisionsno default"docs/DECISIONS.md"
deferredno default
improvementsno default"docs/IMPROVEMENTS.md"
roadmapno default"docs/ROADMAP.md"
strategyno default

[refs]

KeyTypeDefaultThis repository
decisionsno default
improvementsno default
strategyno 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.

KeyTypeDefaultThis repository
padinteger1
suffixbooleanfalse

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

KeyTypeDefaultThis repository
permanentbooleanfalsetrue
wordstring"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.

KeyTypeDefaultThis repository
enforcedbooleanfalse
pinnedbooleanfalse
wiredstring""

[markers]

KeyTypeDefaultThis repository
deferredstring"⏸"
openarray["📋", "💭", "⏳", "🛠"]["📋", "💭", "⏳", "🛠"]
retiredstring"🗑""🗑"
shippedstring"✅""✅"
undesignedarray["💭"]["💭"]

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

KeyTypeDefaultThis repository
markerbooleantrue
symptombooleantrue

[limits]

KeyTypeDefaultThis repository
lineinteger320320
partinteger40
proseinteger8888
sectioninteger250at 2 addresses
symptominteger120120
whyinteger200200

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

KeyTypeDefaultThis repository
one_sentencebooleantrue
refbooleantrue
terminatorbooleantrue

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

KeyTypeDefaultThis repository
leadinteger8060
whyinteger320200

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

KeyTypeDefaultThis repository
leadinteger8060
whyinteger320200

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

KeyTypeDefaultThis repository
heldinteger6060

[requirements]

KeyTypeDefaultThis repository
declaredarray[]

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

KeyTypeDefaultThis repository
upstreamno default"alegauss/roadkeep"

[budgets.<path>]

KeyTypeDefaultThis repository
bytesno defaultat 2 addresses
linesno defaultat 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.

KeyTypeDefaultThis repository
droparray[]
extendsstring"roadmap"
markersarray[]

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

KeyTypeDefaultThis repository
charactersno default2850
sessionno default66660

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

KeyTypeDefaultThis repository
briefno default3300

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/word at 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).