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.516 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[]—
budgets—no defaultdeclared
claims—no defaultdeclared
criteria—no defaultdeclared
files—no defaultdeclared
grammar—no default—
headings—no defaultdeclared
history—no defaultdeclared
id_sourcesarray[]["agents.md"]
ids—no default—
install—no default—
ledger—no default—
limits—no defaultdeclared
markers—no defaultdeclared
non_goals—no defaultdeclared
prefixstring"RK""RK"
priorityarray[]—
project—no defaultdeclared
reads—no defaultdeclared
ref_schemestring"id""id"
refs—no default—
report—no defaultdeclared
requirements—no defaultdeclared
reserved_idsarray[]—
rules—no default—
tools—no defaultdeclared
validation—no defaultdeclared

[files]

KeyTypeDefaultThis repository
changelog—no default"docs/CHANGELOG.md"
decisions—no default"docs/DECISIONS.md"
deferred—no default—
dismissed—no default—
improvements—no default"docs/IMPROVEMENTS.md"
roadmap—no default"docs/ROADMAP.md"
strategy—no default—

[refs]

KeyTypeDefaultThis repository
decisions—no default—
improvements—no default—
strategy—no default—

[ids]

KeyTypeDefaultThis repository
padinteger1—
suffixbooleanfalse—

[headings]

KeyTypeDefaultThis repository
permanentbooleanfalsetrue
wordstring"Block"—

[install]

KeyTypeDefaultThis repository
enforcedbooleanfalse—
pinnedbooleanfalse—
wiredstring""—

[markers]

KeyTypeDefaultThis repository
deferredstring"⏸"—
dismissedstring"🚫"—
openarray["📋", "💭", "⏳", "🛠"]["📋", "💭", "⏳", "🛠"]
partialstring"⏳"—
retiredstring"🗑""🗑"
shippedstring"✅""✅"
undesignedarray["💭"]["💭"]
workingstring"🛠"—

[ledger]

KeyTypeDefaultThis repository
markerbooleantrue—
symptombooleantrue—

[limits]

KeyTypeDefaultThis repository
descriptioninteger200—
iconinteger16—
lineinteger320320
nameinteger60—
partinteger40—
proseinteger8888
sectioninteger250at 2 addresses
symptominteger120120
whyinteger200200

[rules.<role>]

KeyTypeDefaultThis repository
one_sentencebooleantrue—
refbooleantrue—
terminatorbooleantrue—

[project]

KeyTypeDefaultThis repository
description—no default"Owns the writes to a project's roadmap and changelog, so the format is a schema."
icon—no default"🛣️"
logo—no default—
name—no default"roadkeep"

[non_goals]

KeyTypeDefaultThis repository
leadinteger8060
whyinteger320200

[criteria]

KeyTypeDefaultThis repository
leadinteger8060
whyinteger320200

[claims]

KeyTypeDefaultThis repository
heldinteger6060

[requirements]

KeyTypeDefaultThis repository
declaredarray[]{'decision': 'a choice a person makes between written designs; no code in this tree settles it'}

[report]

KeyTypeDefaultThis repository
upstream—no default"alegauss/roadkeep"

[history]

KeyTypeDefaultThis repository
incidentalarray[]["plugin/src/roadkeep/__init__.py", "plugin/.claude-plugin/plugin.json", "editor/package.json", "gui/package.json"]

[budgets.<path>]

KeyTypeDefaultThis repository
bytes—no defaultat 2 addresses
lines—no defaultat 2 addresses

[grammar.<role>]

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

[tools]

KeyTypeDefaultThis repository
characters—no default2850
session—no default69665

[reads]

KeyTypeDefaultThis repository
brief—no default3300
list—no default40000
show—no default28000

[validation]

KeyTypeDefaultThis repository
from—no default—

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.5 over 2422 samples, 95th percentile).