ADR-030: Plan Lifecycle and Status Vocabulary
On this page
Context
docs/modules/ROOT/pages/plans/*.adoc holds CRAIG’s planning corpus (~30 plans active or archived). Nothing currently enforces Status-table upkeep, archival of completed plans, or a single vocabulary for the Status column. Three drift patterns surfaced during the 2026-05-15 Plan K Step 7 review:
-
Status-table drift. Plans Step-1 rows often remain "Not started" after the plan body itself ships (e.g. Plans G/K Step 1 entries were "Not started" until 2026-05-16 even though the plan bodies merged via !307). Code-level work proceeds; the Status table is the last cell anyone updates.
-
"Potential Improvements" as idea graveyard. The current precommit Q6 ("Can this feature be improved? Document in the plan’s 'potential improvements' section") routes improvement ideas to plan prose, where they accumulate without owners, titles, labels, or scheduled follow-up. The section normalizes dumping ideas into a document nobody re-reads after merge.
-
Errata as first-resort for deviation. The current precommit Q4 ("Have you deviated from the plan? Document why in errata") incentivizes adding an errata line rather than fixing the plan in place. Deviations should update the plan’s Design/Scope section so the plan-body diff against shipped code stays at zero; errata is for genuinely post-hoc corrections (typos, citation errors).
Sibling project Canopy filed an equivalent ADR-013 (Plan Lifecycle and Status Vocabulary, Accepted 2026-04-23) addressing the same three patterns. CRAIG’s ADR-013 slot is taken (web-session-strategy); this ADR occupies the next free slot (Plans E/F’s ADRs are 028/029). The canonical-vocabulary tokens are designed to match the words CRAIG plans already use, so the lint lands with minimal grandfathered drift.
Decision
1. Canonical Status vocabulary
Every Status-table row’s Status column MUST begin with one of the following tokens (case-insensitive first-token match, whitespace-preserved):
| Token | Meaning |
|---|---|
|
Default state for rows added to a new plan. |
|
Step is being actively worked in an open MR. |
|
Step shipped; date + freeform detail required. Optional: reference the delivering MR ( |
|
Step explicitly descoped; reason required in parentheses. |
|
Step cannot proceed; blocker (external partner, spec pending, dependent plan) required. |
|
Row added to the table for structural reasons but doesn’t apply. |
Anything else (bare "Complete", "✓", "done", uppercase variants like "COMPLETE") is a lint violation. The canonical tokens match what most existing CRAIG plans already use; residual variants will migrate as plans are touched.
Grandfather sentinel. Plans completed before ADR-030 sometimes have no recoverable date — the original cell carried Complete with no MR or commit reference, and the date isn’t in CHANGELOG. For those entries, Done (pre-ADR-030) is the accepted sentinel and passes the lint. New entries (anything written after 2026-05-16) MUST use the Done (YYYY-MM-DD) form; the sentinel is closed to new use. CRAIG’s one-time cleanup MR (#460) used the sentinel for ~180 cells that had no derivable date.
2. Plan archival
Plans whose Status tables contain only Done / N/A rows MUST move to docs/modules/ROOT/pages/plans/archive/. Moving is a single operation — the file is relocated, the content is unchanged, the Antora xrefs break. Cross-references to archived plans update to ….
Archive is for audit trail, not discovery. A plan in archive/ is immutable in spirit: editing it should trigger asking "do we actually need a new plan?" rather than amending historical record.
3. Precommit protocol Q4 + Q6 amendments
The CRAIG pre-commit hook (.githooks/pre-commit) embeds a token-gated 8-question reflection checklist. Two questions currently incentivize the drift in §Context:
Q4 (was): "Have you deviated from the plan? If yes and the reason is legitimate, document it in the plan’s Errata section."
Q4 (now):
Have you deviated from the plan? If so:
UPDATE the plan’s Design/Scope section to reflect what was built — plans are living specs, not immutable records.
File a GitLab issue for any follow-up work.
Errata is for post-hoc corrections only (typos, citation errors), not a dumping ground for "I built it differently" (see ADR-030).
Q6 (was): "Can this feature be improved? Document in the plan’s 'Potential Improvements' section."
Q6 (now):
Can this feature be improved in any way? If so, FILE A GITLAB ISSUE and link it. Plans are specifications, not backlogs — do NOT add ideas to a plan’s "Potential Improvements" section (see ADR-030).
Existing plans retain their "Potential Improvements" sections — no retroactive migration. New plans omit the section; implementers who find improvements during execution file issues instead.
4. Enforcement tooling + scope guidance
Enforcement (Plan K F-047 + F-048):
-
cargo xtask docs plan-lintvalidates every file underdocs/modules/ROOT/pages/plans/(excludingarchive/). Every| <Step> | <Description> | <Status>row’s Status cell starts with one of the canonical tokens. File-level report lists violations with plan filename + row description (so output is actionable without opening each file). Exit non-zero on any violation. Wire intocargo xtask validatewithallow_failure: trueat first (grandfathers existing drift); promote to blocking in a follow-up once the cleanup MR lands. -
cargo xtask docs plan-archiveis a developer-invoked one-shot: scans plans for all-Done Status tables andgit mv`s them into `archive/. Not run in CI.
Scope guidance for when to write a plan:
-
Plans cover features, services, or cross-cutting initiatives. If the work fits in a single MR and touches a single service, extend the parent domain plan (or skip the plan entirely — a clear MR description is often enough).
-
Tiny sweeps that arise from a prior plan belong as that plan’s follow-up issue, not as new plans.
-
This is reviewer guidance, not a lint. No CI enforcement.
Consequences
Positive:
-
Drift visible. Status-table drift fails CI (eventually blocking; advisory at first via
allow_failure: true). The "completed-plan-stale-status" pattern is caught at the commit level rather than discovered months later during audits. -
Ideas get owners. Q6 edit routes improvement ideas to GitLab issues, where they have titles, labels, and assignees rather than rotting in plan prose.
-
Plans stay honest specifications. Q4 edit says "update the plan" before "add errata" — plans reflect reality.
-
Archive improves discovery. The
plans/index shrinks to in-flight work. Archived plans remain searchable but out of the way. -
No mass migration required. Canonical vocabulary matches the repo’s existing convention; the lint lands with grandfathered
allow_failureso existing violations don’t block work.
Negative:
-
Dual-home for improvements during transition. Existing plans still contain "Potential Improvements" sections. New work files issues. Reviewers must tolerate this asymmetry until retroactive cleanup (deferred, not in this ADR’s scope).
-
Hook drift risk. The hook’s Q4/Q6 text diverges from any upstream template that doesn’t track this edit. Tier 1 docs auto-sync via
cargo xtask check-docs --fix; if the upstream template later mandates the same wording, CRAIG’s hook stays consistent. Otherwise CRAIG’s local hook is the source of truth. -
plan-lintfalse positives on unusual Status text. Plans that use novel Status vocabulary need to adopt one of the 6 canonical tokens. Small friction, catches drift.
Constraints:
-
Vocabulary is closed-set. Adding a new token (e.g.,
Approved,Cancelled) requires updating this ADR + the lint’s allow-list. Intentional friction. -
Archive directory is flat. No nesting by year / program / phase. Simplicity over organization —
archive/is an audit trail, not a browsing surface. CRAIG’s existingarchive.adocindex page provides a browsable summary.
Not addressed by this ADR:
-
Retroactive cleanup of existing "Potential Improvements" sections. A small number of CRAIG plans have them. Sweeping into GitLab issues is a separate effort, tracked if / when someone decides to do it.
-
Retroactive cleanup of existing Errata sections. Same story.
-
Automatic archival.
plan-archiveis developer-invoked. A future CI step could move plans automatically when all rows becomeDone, but not in this ADR.
References
-
Canopy ADR-013 (Plan Lifecycle and Status Vocabulary, Accepted 2026-04-23) — the reference impl this ADR adapts.
-
Plan K (
plans/archive/canopy-xtask-backports.adoc) Steps 7-9 — implementation tooling (plan-lint,plan-archive). -
The
coding-conventionsstandard, §Plan Lifecycle — companion doctrine doc.