Skip to content

The Plan stage

Plan is the quiet, technical stage between the design you approved and the code that gets built. /c-plan takes your approved design and writes a plan folder, and a plan is a very different kind of document from a design.

A design explains why. A plan states what and in what order, and nothing else. It’s written for the machine that will execute it, so it’s exact paths, exact commands, and explicit ordering, with either the full code or the exact decisions the code must honor (see below). There is deliberately no narrative in a plan: the story already lives in the design, and repeating it here would just be a place for the two to drift apart.

plan.style picks what a plan’s steps contain. A full-code plan (the default) writes out the code for every step, and the implementing agent puts it in place. A decision plan writes down only what the agent can’t work out alone (which file, each function’s exact name, inputs, and return type, which tests prove it and what they check, and the exact values the design fixes) and lets the agent write the code. Decision plans should be much shorter and cheaper to write, and they keep the planner from building the whole feature while it plans.

A decision-plan task looks like this:

### Task 2.3: Parse retry budget from config
**Reads:** [`src/config/loader.py`, `tests/config/test_loader.py`]
**Touches:** [`src/config/retry.py`, `tests/config/test_retry.py`]
**Depends:** [2.1]
**Interfaces:**
- Consumes: `load_section(name: str) -> dict` (Task 2.1)
- Produces: `retry_budget(cfg: dict) -> RetryBudget` where `RetryBudget(max_attempts: int, backoff_s: float)`
- [ ] **Step 1: Write failing tests** in `tests/config/test_retry.py`
`test_defaults_when_absent`: `retry_budget({})` == `RetryBudget(3, 0.5)`
`test_rejects_zero_attempts`: `retry_budget({"max_attempts": 0})` raises `ValueError("max_attempts must be >= 1")`
- [ ] **Step 2: Run, expect FAIL**
`pytest tests/config/test_retry.py -v` → Expected: FAIL, `ImportError: cannot import name 'retry_budget'`
- [ ] **Step 3: Implement `retry_budget(cfg: dict) -> RetryBudget` and the `RetryBudget` dataclass in `src/config/retry.py`**
Defaults 3 and 0.5 come from the design's retry section.
- [ ] **Step 4: Run, expect PASS**
`pytest tests/config/test_retry.py -v` → Expected: 2 passed
- [ ] **Step 5: Commit**
`git add src/config/retry.py tests/config/test_retry.py && git commit -m "feat(config): parse retry budget"`

Every plan, of either style, also carries two short sections: Global Constraints (rules every task must follow, like a minimum runtime version or “no new dependencies”) and Review Focus (up to five risky inputs the design implies but no test covers, each with a check added to the task that owns the code).

A design always becomes exactly one plan folder, never several. When work is large, the plan splits it into numbered phase files (01-…, 02-…) inside that single folder, and the execution engine runs those phases in parallel where it safely can. There’s no “should we split this into multiple plans?” question, because the answer is always no: phase files handle decomposition.

Bidirectional linkage: the no-drift mechanism

Section titled “Bidirectional linkage: the no-drift mechanism”

This is the part worth internalizing. When /c-plan writes the plan, it records a two-way link: the plan’s overview names its source design (linked_design:), and the design’s overview is updated to name its plan (linked_plan:).

That linkage is how Cadence prevents silent drift between what you approved and what gets built. The design and plan know about each other. A design only ever becomes completed once its linked plan has been validated, and /c-validate flips both at once. The two move together, by design, never independently.

Exact usage: see the /c-plan reference for the plan folder layout, task structure, and linkage rules.

Next stage: The Execute stage.

Built by Sentasity MIT licensed