Work Plan — branch-scoped working memory
One line: the branch is the unit of work; one committed plan per branch is its
memory. Plans hold volatile state (what we're doing, where we stopped). Durable
truth — specs, glossary, decisions — lives in _docs/ and is owned by
rjv-spec-driven. The plan links to durable docs, never duplicates them.
.plans/<name>.md= working memory for ONE branch (one plan per branch). Committed, so any agent on any machine that checks out the branch resumes from it.- Each plan declares
Branch: <name>in its header. The file itself can be named anything readable (a topic name is fine); the current branch is the key, and resume finds the plan whoseBranch:matchesgit branch --show-current. No index to maintain — git branches ARE the active-work index, and the branch you have checked out is which plan, without being told. - A plan is never deleted. It ships with the code: on merge it moves to
.plans/shipped/with a final status and dates..plans/*.md(top level) = work in flight;.plans/shipped/= the delivery record. The lifecycle runs past merge into maintenance — seeLIFECYCLE.md.
Tool-agnostic: same files serve Claude Code, Codex, any agent (reference this from AGENTS.md so every agent follows it).
Authorship hygiene — no AI signatures
Never add AI authorship or generator credit unless the human explicitly asks.
This applies to commit messages and trailers, PR/issue bodies, comments, plans,
specs, ADRs, source comments, and generated files. Forbidden additions include
Generated by Claude, Generated by Codex, Co-Authored-By for an AI, model
names, badges, emojis, or equivalent signature/footer text. Git already records
the human-controlled author identity; agent involvement is workflow detail, not
artifact content. Preserve attribution a human deliberately wrote—do not silently
remove or rewrite it.
Entry point — resume or start
On any new conversation about the current work, hydrate in order — no code before step 4:
- Find this branch's plan:
b=$(git branch --show-current)thengrep -l "^Branch: $b\$" .plans/*.md.- 1 match → that's the plan.
- 0 matches → branch isn't scoped yet; create a plan (format below), set its
Branch:+ goal +Started:, before touching code. If it's a follow-up on shipped work,grep -l "<topic>" .plans/shipped/*.mdfirst and link that plan. - >1 match → violates one-plan-per-branch; ask the human, or take the most-recently-modified and flag the others as stragglers.
grep -A6 ">>> RESUME HERE <<<" <that-plan>— land on the resume block, then read the whole plan.- Read every durable doc the plan's Source of Truth section links (specs via
rjv-spec-driven, glossary, ADRs). - Reconcile-on-open (below). Only then act.
- Report to the human: current state, drift found, next step about to be taken.
Plan format
# Plan: <topic or branch>
Branch: <branch-name> ← the deterministic key; resume matches on this line
Build mode: simple | spec-driven | gated + spec-driven
Status: brainstorm | approved | in-progress | blocked | shipped | maintenance | closed
Started: <date> ← stamped at creation
Shipped: <date | —> ← stamped when it lands on main / hits prod
Last reconciled: <date> — <matches reality? what drifted?>
## Goal ← what this branch delivers (+ one-line intent if too small to spec)
## Cast ← who builds this: agents, models, approver (see below)
## Decisions ← locked choices + why (crystallized brainstorm; promote hard ones to ADRs)
## Open Questions ← still-live brainstorm (resolve → Decisions or ADR)
## Current State ← VERIFIED ground truth now, not assumed
## Next Steps ← ordered resume point; carries the RESUME HERE block
## Regression Guard← how to avoid breaking existing behaviour
## Out of Scope
## Source of Truth ← links to _docs/ spec, glossary, ADRs, key file:line
Task lists, test cases, data models slot under Next Steps / Current State. One-line, actionable, agent-register (terse facts, file:line). The plan holds ONLY volatile state — anything settled and durable promotes out in real time (see below).
Status is the lifecycle, and it runs past merge:
brainstorm → approved → in-progress ⇄ blocked → shipped → maintenance → closed
shipped = merged and live. maintenance = live and being watched — hotfixes,
follow-ups, prod findings land against it. closed = settled, nothing outstanding;
read-only history. The status must be true at all times — a stale in-progress
on a merged plan corrupts the timeline, so stamp it in the same commit as the event.
Next Steps always carries the literal marker block:
## Next Steps
>>> RESUME HERE <<<
Step: <id> — <status>
Do next: <one imperative — the exact next action>
Must-read first: <file:line, …>
<<< END RESUME >>>
1. …ordered steps after the current one…
Cast section — the agent lineup is a locked decision, recorded at plan creation; every resume plays its role without re-negotiating:
## Cast
Orchestrator: claude-code @ fable ← holds this plan, integrates
Author: claude (main session) ← or: codex · qwen3.6:35b via rjv-codex-ollama-subagents
Reviewer: codex via codex:rescue ← explicit APPROVED gates each step (gated builds)
Subagents: haiku = sweeps/forwarders · sonnet = routine code
Cost rule: flagship = judgment only; recon/mechanical/boilerplate/summaries → cheapest capable tier
Human gates: spec sign-off · USER-flagged decisions · live/prod switches
Recasting mid-build is allowed but is a logged Decision (with why), not a drift.
Build-mode gate — classify before code
Do not leave “substantial enough” to memory. At plan creation and every reconcile,
record exactly one Build mode:
simple
spec-driven
gated + spec-driven
Use spec-driven when ANY item is true:
- real feature or behaviour change with more than one meaningful implementation slice;
- architecture/foundation, public SDK/API/contract, state machine, durable data, or migration;
- multiple apps, actors, journeys, providers, callbacks, jobs, or embedded Features;
- the branch needs acceptance criteria, a glossary, or a hard decision record;
- a review discovers that prose intent and implementation can diverge.
Use gated + spec-driven when ANY item is true:
- money, financial correctness, production safety, security, destructive work, or high blast radius;
- multiple author/reviewer agents or an explicit independent approval loop;
- zero-debt, human QA, or phase-by-phase ship gates;
- repeated rejection/rework shows one agent's self-review is insufficient.
simple is only for one-slice, low-risk work whose complete intent fits in one plan
sentence. Uncertainty chooses the stricter mode. Once a branch qualifies for a stricter
mode, do not downgrade it to avoid the gate.
Mode consequences:
spec-driven→ loadrjv-spec-driven; create/link the durable spec beforeWORK.gated + spec-driven→ also loadrjv-gated-build; grill the spec before code and require independent approval per slice.- The plan's
RESUME HEREnames active criterion IDs, not a prose interpretation.
Cost-routing is a hard rule on EVERY branch, not just gated builds. Reserve the flagship (top tier) for judgment — design, review, synthesis. Route recon, file-reads, mechanical edits, boilerplate, test-writing, and summarization to the cheapest capable tier, and set each subagent's model explicitly (never default-inherit the expensive parent — the most common leak).
Decide per task, and revisit. The Cast is a starting default, not a fixed
lineup. For each task ask "cheapest tier that clears this bar?" and route
accordingly — cheap hands through a bulk/mechanical phase, flagship when judgment
dominates. When the mix of work shifts, recast (a logged Decision in the plan,
with why — not a silent drift). Don't route out a task whose spec+review overhead
exceeds the saving. Full two-ladder split (repo-tool work vs self-contained text) +
break-even detail in rjv-gated-build's MODEL-ECONOMY.md.
Ceiling — the plan stays thin
Hard ceiling ~400 lines / ~20KB. A plan is re-read on every resume — an unbounded plan is a recurring token tax that compounds each turn. It stays thin by construction:
- Git holds history, so the plan doesn't. Never keep a log "in case" —
git log .plans/<name>.mdis the log. The plan is a current-state surface. - Real-time promotion (below) drains settled facts out continuously.
- If it's over the ceiling at reconcile, promote durable facts to
_docs/and compress BEFORE acting.
Real-time promotion
Settled facts leave the plan the instant they crystallize — written straight to
their durable home, never parked here for "later". Because promoted docs are branch
commits, they travel through the same PR and land on main exactly when the code
does. No drift, no batch-at-merge.
rjv-spec-driven owns the rest: which facts leave, where each one goes, the
mutation test that decides plan-state from durable truth, and the artifact formats
(spec / glossary / ADR). Load it before promoting anything.
Reconcile-on-open — never stale
The resume guarantee is a cheap ritual, not "the agent remembers":
read plan → VERIFY each "done" claim against real code/db/tests → note drift in
Current State → rewrite Next Steps → stamp Last reconciled →
if over the ~400-line ceiling, promote + compress → then act
Never trust a checkbox; a plan whose "done" you haven't verified is a rumor.
During work: update the plan in the same turn as the change, never batched.
On stop/handoff: rewrite the >>> RESUME HERE <<< block to the exact resume
point; no done that isn't.
Resume mechanism — deterministic, do not reinvent
The >>> RESUME HERE <<< / <<< END RESUME >>> strings are literal — never
paraphrase them, or the grep breaks. A fixed string is a deterministic landing
(grep finds it every time, survives header drift); a semantic "find the Next Steps
section" is something each agent re-locates and each session re-invents.
b=$(git branch --show-current) # current branch = the key
grep -l "^Branch: $b\$" .plans/*.md # → the plan that declares it
grep -A6 ">>> RESUME HERE <<<" <that-plan> # land on the block
→ reconcile-on-open (verify done-claims) → act
→ at END of every step: rewrite the block
There is NO RESUME.md — git branches are the active-work index. Concurrent work =
concurrent branches (or worktrees), each with its own committed plan.
Plan vocabulary stays in the plan
Step: <id>, Status: shipped, RESUME HERE, .plans/shipped/…, criterion IDs are
the file's nouns. They are not the user's. The user does not have the plan open and is
not tracking step numbers, so an answer built on a coordinate they'd have to go read is
an answer they can't use.
When reporting in chat, translate. Say what the step is and what state it's in: "the old components were never deleted", rather than "step 6 is open". Cite the plan path once, at the end, and only if they'd need to open it.
Merge, maintenance, timeline, roadmap → LIFECYCLE.md
A plan is archived, never deleted: git mv .plans/<name>.md .plans/shipped/<YYYY-MM-DD>-<name>.md, status stamped in the same commit. Read
LIFECYCLE.md when a branch is landing, when work arrives against
something already live, or when someone asks what shipped and when — it holds the
merge ritual, the CI-guardable invariants, the maintenance phase, how to read the
delivery timeline back, and the durable roadmap.
Brainstorm in the plan
The plan is where thinking out loud lives. Keep it from rotting: resolved → one-line Decision with the why (promote hard-to-reverse ones to an ADR); unresolved → Open Questions; loose musing either crystallizes or dies.
With rjv-spec-driven and rjv-gated-build
rjv-spec-driven— mandatory forBuild mode: spec-drivenandgated + spec-driven. It owns the durable acceptance criteria, glossary, and ADRs.WORK, handoff, review, and QA refer to permanent criterion IDs.rjv-gated-build— mandatory forBuild mode: gated + spec-driven. The plan is the compressed operational anchor: cast, active criterion IDs, evidence, grill trail, tombstones, and resume point. Durable acceptance criteria stay inspec.md. Multiple concurrent gated builds remain separate branches/plans.
Provenance: production workflow from a live fintech monorepo — multiple concurrent
branches, two agents (Claude Code, Codex) sharing committed plans + _docs/.