Every code change in this repo moves through a fixed pipeline. Each item has its own file under graphitron-rewrite/roadmap/, with YAML front-matter naming its current status:. The rolled-up roadmap/README.md is generated from those files by graphitron-roadmap-tool and never edited by hand.

States and transitions

stateDiagram-v2
    direction LR
    state "In Progress" as InProgress
    state "In Review" as InReview
    [*] --> Backlog
    Backlog --> Spec        : pick / draft plan; status: Spec
    Spec --> Spec           : revise [reviewer ≠ last committer]
    Spec --> Ready          : sign off [reviewer ≠ last committer]
    Ready --> Spec          : reopen; status: Spec
    Ready --> InProgress    : start work; status: In Progress
    InProgress --> InReview : land implementation; status: In Review
    InReview --> Ready      : rework [reviewer ≠ implementer]
    InReview --> Done       : approve [reviewer ≠ implementer] / delete file
    Done --> [*]
    Backlog --> Discarded   : abandon; delete file
    Spec --> Discarded      : abandon; delete file
    Ready --> Discarded     : abandon; delete file
    InProgress --> Discarded: abandon; delete file
    InReview --> Discarded  : abandon; delete file
    Discarded --> [*]

Reopening Ready: Ready → Spec is unguarded. Either the implementer (after starting work and discovering the plan needs substantive redesign) or any other session may flip a Ready item back to Spec when the existing sign-off no longer covers the shape of the work. The next Spec → Ready transition re-engages the reviewer-rule guard against the session that lands the spec revision, so a fresh independent sign-off is required before implementation resumes. Use this rather than carrying an in-flight Ready forward through implementation when the spec body has materially diverged from what the original reviewer approved. A minor in-flight tweak that does not change the design is a normal In Progress plan edit, not a reopen.

Reviewer rule: the guards on Spec → Ready and In Review → Done require a different Claude Code session from the one that last changed the artifact. The session ID is the identifier the gate compares against, not the git author or the human at the keyboard; the human user behind both sessions is typically the same person, and that’s fine. The point is fresh context: a reviewer session with no prior reasoning trail on the work spots design problems the authoring or implementing session has already rationalized away. Each session’s ID is recorded on the commits it produces as the https://claude.ai/code/session_<id>; trailer in the message body, so the disqualified party can be resolved from git log on the artifact (spec file, or implementation commits referencing the item). A reviewer session that lands substantive edits on the artifact disqualifies that session from approving the resulting revision; another session must sign off.

User-facing-doc check. Before approving In Review → Done, the reviewer skims any docs/ changes in the item for roadmap-internal markers leaking into user-facing prose: R<n>, Phase <n>, under construction, forthcoming chapter, TODO comments, and references to plans by slug. The user-manual chapter is read by people who arrived from search and have no context on our process; project-management vocabulary undermines the chapter’s promise. Items with no docs/ changes skip this check.

Discarded: terminal state for plans that won’t ship; the action is to delete the file in the same commit that records the decision. Use when a plan is superseded wholesale (the successor captures everything worth keeping; e.g. R41 superseded by R53 before shipping), found infeasible, or abandoned for any other reason. Like Done, Discarded does not appear as a persistent status: value in front-matter; it names the transition, and the file removal is the observable. Reach for Discarded rather than leaving a stale plan in Backlog with prose explaining why nobody will pick it up.

A small adjacent pattern, Backlog tombstone, applies when a plan’s execution merges into another in-flight item (e.g. R20’s coverage folded into R50’s test surface). The plan stays as a Backlog item with a body explaining the deferral; the file deletes when the absorbing item reaches Done. Use this only when the file genuinely serves as a redirect during the absorbing item’s lifecycle. If the supersession is total and the successor’s spec already captures the predecessor by reference, prefer Discarded.

Item file conventions

  • Location: graphitron-rewrite/roadmap/<slug>.md. Slug describes the work, not the phase (variant-coverage-meta-test.md, not phase-2.md). No plan- prefix; backlog items live in the same directory and use the same shape.

  • Each item carries an id: of the form R<n> (literal R plus a positive integer). IDs are monotonic across the whole roadmap and never reused: when an item ships and its file is deleted on Done, the number stays a gap so historical references in changelog.md and commit messages keep their meaning. Refer to items by R<n> (or R<n>: <slug> when slug context helps); the rendered README shows the ID in a leading column.

  • The tool stamps created: and last-updated: (ISO YYYY-MM-DD) on every item it touches. The create subcommand writes both at filing; the status subcommand writes a fresh last-updated: on every transition and leaves created: strictly untouched. Hand-editing the file is physically possible but bypasses the stamp; for the stamp to be accurate, use the subcommand. Pre-R143 items have no created: value and will not get one even on their next transition (the status subcommand never invents one); the rendered table shows last-updated: only for them, which is itself a readable signal that the item predates the convention.

  • First lines are YAML front-matter, delimited by ---:

    ---
    id: R<n>                                  # allocated by `roadmap-tool create`
    title: "Human-readable title"
    status: Backlog | Spec | Ready | In Progress | In Review
    bucket: architecture | stubs | cleanup    # backlog items only
    priority: 5                                # ordering hint, lower first
    deferred: true                             # optional, e.g. Ready (deferred)
    ---

    GitHub renders this block as a table at the top of the file. The roll-up roadmap/README.md is regenerated from these fields by mvn -pl :graphitron-roadmap-tool exec:java (or mise r roadmap) and CI verifies it stays in sync via the tool’s verify mode.

  • Allocate a new ID atomically with the tool rather than guessing. From the repo root:

    mvn -f graphitron-rewrite/pom.xml -pl roadmap-tool exec:java -q \
      -Dexec.args='create graphitron-rewrite/roadmap <slug> --title "<title>" \
                   --bucket <bucket> --priority <n> --theme <theme>'

    The create subcommand picks the next free R<n>, writes the file with the ID baked in, and refreshes the roll-up README. next-id is also available as a read-only allocator if you need the number without writing a file yet. Hand-creating an item file without an ID will fail the build.

  • Plans may be multi-phase. When a phase ships, the implementation commit updates the plan to mark that phase done (typically by collapsing its section into a one-line "shipped at <sha>`" note and capturing any learnings). The overall plan’s status tracks what’s next: if more phases remain, status stays `Ready; if only the just-shipped phase is pending review, status is In Review.

  • Plans describe what to do, not how many commits to land it in. Implementation commit structure is the implementer’s judgment: split when the seams add review value, keep unified when they don’t.

  • Default plan shape is flat sections (## Implementation, ## Tests, ## Roadmap entries), not numbered steps. Reach for "Step 1, Step 2, …​" only when the numbering reflects a real seam: separate PRs, a feature-flagged rollout, or sequencing where intermediate states are observable. If every step has to land before the next one compiles, the numbering is bookkeeping; collapse to a flat file-by-file list under ## Implementation so the actual diff shape is obvious. Numbering implies "stop and verify between steps"; don’t imply it when there’s nothing to verify between them.

  • A plan deleted on Done has its file removed outright. Git history preserves it; leaving a tombstone file encourages staleness.

  • Plans with a user-visible surface (a new Mojo goal, a new directive, a new output format, a wire-protocol change) include a draft of the user docs as the first client of the design. If the docs do not read simply, the design is wrong and must change before implementation. The draft lives inside the plan and moves into its real home (getting-started.adoc, the relevant README) when the feature ships. The LSP plan’s ## User documentation (first-client check) section is the canonical example. Internal refactors with no user surface are exempt.

Roadmap rendering

The per-item front-matter is the source of truth. The roll-up README is derived. Use Done only for milestones worth keeping as history (capture the landing commit in changelog.md); routine completions disappear entirely when their item file is deleted.

Publishing

"Publish" = commit + push. A change that lives only in your working copy doesn’t exist for the rest of the workflow. The trunk-push rule from CLAUDE.md applies: any push to your branch must be followed by a fast-forward to claude/graphitron-rewrite.

Adding to the roadmap

Any session can add items at any time: drop a new <slug>.md under graphitron-rewrite/roadmap/ with status: Backlog and the appropriate bucket:, then regenerate the README. Opportunities spotted during review, implementation, or unrelated work all land that way. The expectation is that they’re substantive enough to justify eventual planning, not every passing thought.

Canonical path

Taking a feature from idea to Done. Minimum four commits by at least two parties; typical paths are five to six when reviews involve iteration:

  1. Author picks a Backlog item, expands its file with a real plan body, updates status: to Spec, regenerates README.

  2. Reviewer (≠ author) reads the plan, revises if needed (stays Spec), then signs off by flipping status: to Ready.

  3. Implementer writes code, updates the plan (remove shipped, keep pending), flips status: to In Review.

  4. Reviewer (≠ implementer) approves (delete the item file; entry in changelog.md if worth keeping) or requests more work (flip back to Ready, new cycle).

For a recent worked example, see the computed-field-with-reference entry in changelog.md: an end-to-end @externalFieldComputedField lift covering classification, validation, generator emit, and execution-tier coverage in one cycle.