# roadkeep > A CLI that owns the writes to a project's `ROADMAP.md`, `CHANGELOG.md`, > `IMPROVEMENTS.md`, `STRATEGY.md`, `DEFERRED.md` and `DECISIONS.md`, so the format is > a schema at the point of insertion instead of a convention an author is asked to > remember. It refuses an over-length field before the sentence is composed, rather > than reporting on it after the tokens are spent. Python >=3.11, zero runtime > dependencies (`argparse` and `tomllib`), Markdown-only store. Shipped as a Claude > Code plugin, because the author to constrain is usually an agent. Status: the model, the write paths, the query surface, the gate, the Claude Code plugin and the editor surface are all done, and `roadkeep.toml` itself is now governed by a verb rather than hand-edited. It is on PyPI, so `uvx roadkeep lint` runs the gate with nothing installed and nothing checked out, and `pip install roadkeep` pulls the package alone — zero runtime dependencies means there is nothing else to resolve. Or take the plugin, which carries the same engine: `claude plugin marketplace add alegauss/roadkeep`. No count is stated here on purpose. A restatement of a backlog goes stale on the first ship — this file said "9 of 36" through twenty-five of them — and the ROADMAP and CHANGELOG linked below are one fetch away and always right. This file is deliberately short for the same reason. A tool whose thesis is that prose expands to fill whatever room it is given cannot ship a 2000-word summary of itself; the links below are the detail, and each one is fetchable Markdown. ## Why it exists Three files in a real production repository each declared a format and none enforced it: a roadmap whose rule was one sentence per task averaged 142 words across 92 lines (worst line 1406 characters), an `agents.md` meant to be an index reached 186 KB (~46k tokens) while being loaded every turn, and an `IMPROVEMENTS.md` scoped to unshipped work reached 539 KB. Six of the eight worst lines were written in the session that then diagnosed the problem — so the drift is invited by the process, not caused by inattention. A linter cannot fix this, because it reports after the prose exists. The saving is the analysis, not the characters. ## The six laws A change that breaks one is wrong even if requested. - **L1** — the format is a schema, enforced where the text is created; `lint` is only the backstop. - **L2** — the store is the repository: Markdown, greppable, diffable, no database and no service. - **L3** — round-trip or don't write: parse then render must be byte-identical, or the file is refused. - **L4** — the tool never writes prose; it validates and renders. - **L5** — query instead of read: every question is a command, so answering costs no context. - **L6** — configuration, not convention: prefix, paths, markers and limits are per project. ## What it does A whole task is four calls, which is the shape rather than the reference — `--help` and the repository carry the flags. - `roadkeep brief` — what to work on and everything it costs to start: the line, its rationale, the resolved deps, the blocker chain, what shipping it unblocks, the binding non-goals. - `roadkeep add` — compose a task line from validated fields; refuses an over-length field before the sentence is composed to fill it, and writes nothing when it does. - `roadkeep ship ` — the ledger entry, the roadmap line removed and the rationale section dropped, as one transaction: all three or none. - `roadkeep lint` — exit 1 when any governed line drifted, a pointer resolves to nothing, or an always-loaded file outgrew its declared budget. The exit code is the whole contract. Around them, eighty-odd more in six families — authoring, shipping, sections, querying, linting and adoption: `status`/`amend`/`restate`/`renumber`, `defer`/`resume`, `section`/`block`/`non-goal`/`criterion`/`priority`, `retire`, `record`, `list`/`stats`/`audit`, `show`, `pick`, `deps`, `gaps`, `origin`, `weight`, `remaining`, `budget`, `export`, `merge`, `repair`, and `init`/`adopt`/`declare`/`install` for a repository that already has a backlog — `adopt` measures one and reports what adopting would cost before anything is changed, and `declare ` adds a governed file to a project already configured. The complete list, with every argument, is generated from the parser rather than written beside it: one page per family under https://alegauss.github.io/roadkeep/docs/reference/, each with a plain-text twin at `index.md`. Every command takes `--json`, which carries provenance (which file and which line the answer came from), because an answer an agent cannot audit gets verified by reading the file — the cost the command existed to remove. Four things worth knowing about separately: - **The decision outlives its rationale.** `ship` deletes the design section, which is why `IMPROVEMENTS.md` does not grow monotonically. `ship --decides "…"` writes the verdict inside it to `DECISIONS.md` in the same transaction, and `supersede --by ` marks one replaced without deleting it. `reversals` reads back what was decided and undone. - **The config file is governed too.** `config` prints every table, key, type and default `roadkeep.toml` may carry; `govern ` takes the reading and the number in one call and **refuses a limit the corpus already breaks**. `govern --because "…"` places your argument in comments above the number, and the read hands it back. - **More than one session on one tree.** `claim --path …` declares what a commit owns and `claim ` reads it back against the working tree; every write prints the `git add --` line for exactly what it wrote. `merge` is git's driver for a governed file, merging entries by id so two branches appending under one heading is two additions. - **A defect in the tool is an input, not a paragraph.** Every failure prints the `report` line that files it with the argv, the config and the governed files; `replay` re-runs that capture against whatever tree is there later. Surfaces: a CLI, an MCP server whose tool list and input schemas are derived from your `roadkeep.toml` — `maxLength` is that project's field limits, `enum` its markers, `pattern` its id shape, and a project declaring three roles is served fewer tools than one declaring every role — a `PreToolUse` hook that denies a hand-edit and names the command that replaces it, a trigger-loaded skill, four slash commands, a GitHub Action, a pre-commit hook, and a VS Code extension showing the backlog as a tree. One engine behind all of them. ## Non-goals These are binding; check them before proposing work. - No web UI and no server — files and a CLI. - No issue-tracker sync — a backlog that lives in a service is one an agent cannot `grep`. - No model and no prompts inside the tool — it never writes the symptom or the rationale. - No dates, quarters or estimates — a marker is maturity, not a schedule. ## Sponsor Sponsored by **Viglet** (https://www.viglet.org). Open source search and content tools for organisations with a lot to publish — run on your own servers, with no per-user licence. - **Viglet Turing ES** (https://turing.viglet.org) — Search for sites with a lot of content. People ask in their own words — “evening management course”, “engineering at the north campus” — and find it, with filters that match how they actually choose. AI answers using only your own published content, so it never invents a price or a deadline. Runs on your servers, your data stays in house, and there is no per-user licence. - **Viglet Shio CMS** (https://shio.viglet.org) — A content system that takes the page out of the development queue: AI builds it, your communications team reviews and approves it, seeing exactly how it will look before it goes live. Every version is kept, so you can see what changed and roll back. Runs on your servers, no per-user licence. - **Viglet Dumont** (https://www.viglet.org) — The same item of information usually lives in several places at once — a portal, a back-office system, a PDF, an old departmental site. This gathers it automatically from all of them and feeds it to the search, with nobody copying, pasting or keeping a spreadsheet up to date. ## Links - [Website](https://alegauss.github.io/roadkeep/): project landing page - [The landing page as plain text](https://alegauss.github.io/roadkeep/index.md): the same page, converted from the same render, so it cannot disagree with what a person reads - [Documentation area](https://alegauss.github.io/roadkeep/docs/llms.txt): the index of every page in the documentation area, each with a plain-text twin at its own address plus `index.md` — the reference is generated from the parser, the finding codes from the gate's own table, and the adoption walkthrough from a real run - [GitHub repository](https://github.com/alegauss/roadkeep): source code - [README](https://raw.githubusercontent.com/alegauss/roadkeep/main/README.md): the problem measured, the comparison against linters and task-file tools, install - [agents.md](https://raw.githubusercontent.com/alegauss/roadkeep/main/agents.md): the development guide an agent is expected to follow in this repository - [ROADMAP.md](https://raw.githubusercontent.com/alegauss/roadkeep/main/docs/ROADMAP.md): the active backlog, one line per task, and the binding non-goals - [CHANGELOG.md](https://raw.githubusercontent.com/alegauss/roadkeep/main/docs/CHANGELOG.md): the shipped ledger, indexed by block - [IMPROVEMENTS.md](https://raw.githubusercontent.com/alegauss/roadkeep/main/docs/IMPROVEMENTS.md): design rationale for unshipped work, and the authoritative statement of the six laws - [DECISIONS.md](https://raw.githubusercontent.com/alegauss/roadkeep/main/docs/DECISIONS.md): one line per decision, written by the `ship` that took it; a superseded row is marked and never deleted - [roadkeep.toml](https://raw.githubusercontent.com/alegauss/roadkeep/main/roadkeep.toml): this project's own configuration, which the tool reads — the repository is its own conformance fixture - [Sponsor](https://www.viglet.org): Viglet, open source search and content management