009 — Legal numbering for ROADMAP.md (#32)
Status: shipped (v0.6.0)
Why
A project roadmap numbers its items legally (1, 1.6, 1.6.2) so a
changelog, an ADR or a commit can cite a story by number. The numbers are
kept by hand, and on gobank's roadmap they drifted: a stage inserted
before a shipped one took a letter (1.6a), three stages were pushed back
by renumbering, ticked items lacked the version stamp that says when they
shipped, and every cross-reference in the prose had to be found by eye.
Nothing checks any of it. The convention is cross-project, like the
changelog format and md2html, so the check belongs in task-plus and in
tp check, which tp release runs.
The rule the check enforces is that a legal number is positional, so an insertion either renumbers or takes a letter, and which one depends on whether the number has been cited yet: a frozen item (ticked and stamped, or with a stamped descendant) keeps its number for good, an insertion before a frozen number takes a letter, and unfrozen items are renumbered freely.
What changes
tp roadmap check: readsROADMAP.md, finds every numbered item (a list line starting- **N**or- **N Title**, whereNis dot-separated segments, each digits with an optional trailing letter, optionally followed by[x]and a stamp in parentheses) and reports every rule broken, with the line number. Exit 1 on any finding. A roadmap with no numbered item passes. The**N Title**form is how gobank writes its top-level items; without it every1.xthere would be an orphan.tp roadmap renumber [--dry-run]: rewrites the headings of unfrozen items so siblings are consecutive after the last frozen one, carrying each change down to the item's descendants, and prints every other occurrence of a changed number in the file for the author to fix by hand; it never changes a frozen number.--dry-runprints the plan.tp checkrunstp roadmap checkwhenROADMAP.mdexists.docs/roadmap.md: the grammar, the rules and the frozen-number convention, linked fromREADME.md.
Acceptance
| Items in file order | Frozen? | tp roadmap check |
|---|---|---|
1.6, 1.7, 1.8 |
any | passes |
1.7.1 with no 1.7 |
— | orphan: parent missing |
1.9 before 1.8 |
neither | out of order |
1.8 twice |
— | duplicate |
1.7, 1.9 |
neither | gap: 1.8 missing (renumber makes 1.9 → 1.8) |
1.6 then 1.8 |
1.6 |
gap: 1.8 → 1.7 |
- **1.5** [x] Core into packages |
— | ticked without a stamp |
- **1.5** [x] (v0.18.0) …, (go-luca v0.4.0), (unreleased) |
— | stamp accepted |
1.6a with 1.6a.2 stamped |
1.6a |
passes: a letter on a frozen item |
1.6b with nothing stamped under it |
no | letter on an unfrozen item (renumber gives it the next number) |
1.6a and 1.6a.2 stamped, then 1.7 |
1.6a |
passes: 1.6a sits between 1.6 and 1.7 |
| no numbered item | — | passes, nothing checked |
tp check with ROADMAP.md present |
— | runs the check; a finding fails the check |
tp roadmap renumber on |
Result |
|---|---|
1.6 frozen, 1.6a frozen, 1.7, 1.9 with 1.9.1 |
1.9 → 1.8, 1.9.1 → 1.8.1; 1.6, 1.6a untouched |
| prose elsewhere in the file says "after 1.9" | printed with its line number, not changed |
--dry-run |
plan printed, file unchanged |
| nothing to do | says so, exit 0 |
Out of scope
- Ticking, stamping or archiving items: completion is a judgement, left to the person (or the roadmap skill that proposes it).
- Numbers in
CHANGELOG.mdor ADRs: they cite roadmap numbers and are why frozen numbers never move. - Rewriting cross-references in prose: too easy to hit a version number or an ADR stage; the command lists them instead.
- Section structure (phases,
Now/Next/Later): the check reads numbered items wherever they are.
Decisions
The frozen-number convention is recorded in docs/roadmap.md; no ADR,
since no fork was resolved against an alternative worth keeping.
Done when
- tests express the acceptance tables and pass
-
docs/roadmap.mdwritten and linked - roadmap ticked and stamped; changelog entry
- issue closed with a link to the release (v0.6.0)