---
name: plan
description: Use when you have a spec or requirements for a multi-step task, before touching code
---

# Writing Plans

Audience: AI coding agents first. Optimize for activation, not aesthetics.

MUST follow `/home/user/Projects/0 DOCS/GIT_FATIGUE.md` §12. Plans MUST NOT create user-facing git/CI/publication decisions. Valid current-tree receipt/log replaces repeated broad checks.

Write implementation plans for an implementer that **reads the repo itself** (cursor-agent, north, or a Claude subagent). Plan pins the **contract + acceptance**, not the body. Document: files to touch, the seam (signatures/types/IO shapes), behavior, how to verify. Bite-sized tasks. DRY. YAGNI. TDD. Frequent commits.

## Contract-level default — the one rule that governs every task

**Default: write the CONTRACT, never the implementation body.** The implementer writes the body for free by reading the repo; an Opus-authored body in the plan is paid-for work the implementer then re-derives and the gate re-reviews — triple cost. Pin the seam; let the body be written downstream.

**Collapse to literal code ONLY when `impl_LOC ≤ contract_LOC`** — when the code is shorter than the prose contract that would describe it (a one-line guard, a constant, a typo fix), writing a contract is pure overhead. Then paste the literal code AND tag it `→ apply inline (LOC≤LOP), no dispatch` so the executor edits it directly instead of dispatching an implementer.

```
for each task:
  impl_LOC ≤ contract_LOC ?
    ── yes → literal code in plan, tagged "apply inline, no dispatch"
    ── no  → contract only (seam + behavior + acceptance); implementer writes body
```

This is the same LOC≤LOP economics the orchestrators apply at execution — applied here, at authoring, where the cost is actually sunk. Never write a substantial body into a plan and then hand it to an implementer to retype.

**Save to:** `docs/plans/YYYY-MM-DD-<PLAN_SLUG>.md`
(User preference overrides default)

`PLAN_SLUG` = kebab key. If invoked by brainstorm, use the slug it passed (the plan doc shares the stem with the session file `docs/plans/YYYY-MM-DD-<PLAN_SLUG>.jsonl`). Invoked standalone → derive a kebab slug from the feature. The slug is the resume key for `run-plan`.

## Scope Check

Spec covers multiple independent subsystems → suggest breaking into sub-plans, one per subsystem. Each plan produces working, testable software standalone.

## Dependency Analysis & Wave Planning

Determine parallel vs sequential before writing tasks.

**Task blocks another when:**
- Task B imports/uses symbols Task A creates
- Task B modifies file Task A also modifies
- Task B tests behavior Task A implements

**Tasks parallelize (same wave) when:**
- Zero semantic dependency
- Zero file overlap (check both create and modify)

Assign each task a wave via topological sort. Same-wave tasks run simultaneously.

**EVERY plan MUST include Wave Plan table immediately after header:**

```markdown
## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1, Task 2 | src/types.ts, src/parser.ts | ✅ no overlap |
| 2 | Task 3 | src/codec.ts | single task |
| 3 | Task 4, Task 5 | src/cli.ts, src/server.ts | ✅ no overlap |
```

Wave has file overlap or semantic dependency → split into separate waves. NEVER put two tasks touching same file in same wave.

NEVER serialize independent file-disjoint tasks into separate waves "to be safe" — that buries parallelism the executor is meant to exploit. Real dependency → later wave + deps edge. No dependency + disjoint files → SAME wave, runs concurrently.

No scheduler field to stamp: the harness engine runs each wave's tasks concurrently (up to the run's `--concurrency N`, a launch-time flag, not a plan-authored field) and waves strictly in order. A wave with only one task simply has no parallelism to exploit that round — nothing else to decide at plan time.

## Decision-Enumeration Pass

After waves assigned, BEFORE saving the plan: for EVERY task ask "would this need a human?"

**Source of truth — what needs a human:**
1. Project's `## Pre-flight gate policy` section in its `CLAUDE.md` (read it first if present). Additive on top of baseline.
2. Absent ⇒ baseline categories: `irreversible | fork | input | policy | architecture`.

Baseline is the floor. Project policy can broaden scope; it can NEVER silence an irreversible destructive op — always gate it.

**Per-task decision:** author a `gated` record; link it from the task via `requires_decision`.
- MUST include: `status:"OPEN"`, `source:"author"`, `category`, `needs`, `why`, `options`.
- `blast_radius` MANDATORY when `category:"irreversible"`.
- **No-go sentinel — go/no-go `options` MUST be exactly `["proceed","abort"]`.** `"abort"` is the ONLY value the engine treats as stop; EVERY other answer PROCEEDS. NEVER use `skip`/`no`/`cancel`/`stop` as the no-go word — the engine rejects a non-`abort`/`proceed` go/no-go at load (fail-closed). There is NO skip-this-task-but-continue; that is a graph concern resolved at plan time (re-plan), not a gate answer. Fork is the sole exception (its options are branch values, all proceed-semantics).
- Field shapes: [[brainstorm]] SKILL.md "Session File" `gated` schema — cross-reference, do NOT re-inline.

**Run-level publication mechanism** (push-vs-PR, base branch, auto-merge): delivery controller resolves internally from project policy. NEVER author a user gate for source-control/CI mechanics. Product-impact deployment target remains a product decision.

**Fork binding — pick exact kind:**
- Param-only fork (answer changes a value; task graph unchanged) → ONE task with `requires_decision`. Answer injected as input to that task at run time. NEVER author branch tasks or a second graph.
- Graph-changing fork (answer would produce different tasks/deps) → resolve HERE, at plan time (ask the user during `plan`). NEVER defer a graph-changing fork to the pre-flight gate — the run needs a frozen task graph at launch.

NEVER save the plan with an unresolved graph-changing fork. NEVER author branch tasks for a param-only fork.

## File Structure

Before tasks, map which files create/modify and each file's responsibility.

- One clear responsibility per file. Prefer smaller, focused files over large ones that do too much.
- Files that change together → live together; split by responsibility not layer
- In existing codebases: follow established patterns; don't unilaterally restructure

Structure informs task decomposition. Each task → self-contained changes.

## Bite-Sized Task Granularity

**Each step = one action (2-5 min):**
- "Write the failing test" — step
- "Run it to verify it fails" — step
- "Implement minimal code to pass" — step
- "Run tests, verify pass" — step
- "Commit" — step

## Plan Document Header

**EVERY plan MUST start with:**

```markdown
# [Feature Name] Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use /ship (recommended) or /executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** [One sentence describing what this builds]

**Architecture:** [2-3 sentences about approach]

**Tech Stack:** [Key technologies/libraries]

---
```

## Task Structure (contract task — the default)

Pin the seam, state behavior, give one executable acceptance check. The implementer writes the test bodies and the implementation. Do NOT write either for it.

````markdown
### Task N: [Component Name]

**Wave:** N
**Blocks:** Task X, Task Y (or —)
**Blocked by:** Task X (or —)

**Files:**
- Create: `exact/path/to/file.py` — one-line responsibility
- Modify: `exact/path/to/existing.py:123-145` — what changes
- Test: `tests/exact/path/to/test.py`

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Signature: `def function(input: InputType) -> ResultType`
- Types: `InputType = {...}`, `ResultType = {...}` (or point to where defined)
- IO / route / schema shape; any spec-mandated literal string verbatim

**Behavior:** what it must do; edge cases; error conditions (bullets, not code)

**Acceptance (one executable check):**
- Run: `pytest tests/path/test.py::test_name -v`
- Expected: PASS — `function(sample) == expected_value`

- [ ] Write tests covering the behavior above (implementer writes the test code)
- [ ] Implement to satisfy the contract + acceptance (implementer writes the body)
- [ ] Run minimal failing acceptance check during TDD; otherwise consume valid current-tree receipt
- [ ] Commit: `git add <exact paths> && git commit -m "feat: <description>"`
````

### Literal-code task (only when `impl_LOC ≤ contract_LOC`)

When the change is shorter than its contract would be — a guard, a constant, a rename, a typo — paste the literal code and tag it. No contract prose, no dispatch.

````markdown
### Task N: [Component Name]
**Files:** Modify `src/path/file.py:42`
**Literal — apply inline (LOC≤LOP), no dispatch:**
```python
if user.id != resource.owner_id:
    raise Forbidden()
```
- [ ] Apply edit, run `pytest tests/path/test_auth.py`, commit.
````

## Precise contract, not vague prose

Contract-level ≠ vague. Pin the seam exactly; leave only the body to the implementer. A precise contract is more robust than handing code — it ships an executable target instead of inviting transcription drift.

**Vagueness failures — NEVER write:**
- "TBD", "TODO", "implement later", "fill in details"
- "Add appropriate error handling" / "add validation" / "handle edge cases" → name the exact errors, validations, edge cases in Behavior
- "Write tests for the above" with no acceptance criterion → state the executable acceptance check (command + expected output)
- "Similar to Task N" → restate the contract; tasks read out of order
- References to types/functions/methods whose signature appears in no task's Contract

**Body-bloat failures (the new waste) — NEVER write:**
- A substantial implementation body for a task that will be dispatched (`impl_LOC > contract_LOC`) — pin the contract, let the implementer write it
- Full unit-test bodies — give acceptance criteria + behavior; the implementer writes the test code
- Literal code without the `apply inline (LOC≤LOP), no dispatch` tag when `impl_LOC ≤ contract_LOC` — the executor must know not to dispatch it

## Remember
- Exact file paths always
- Exact commands with expected output
- DRY, YAGNI, TDD, frequent commits
- **Commit steps:** only stage paths in project git allow-list. NEVER `git add tests/`, docs, specs, or anything outside allow-list
- **Required output formats:** spec mandates exact strings/formats → paste verbatim in task step. NEVER say "similar to spec" or leave format inference to implementer

## Self-Review

After complete plan, check spec with fresh eyes. Run yourself — not a subagent dispatch.

**1. Spec coverage:** Skim each spec requirement. Can you point to a task implementing it? List gaps.

**2. Vagueness + body-bloat scan:** Search for red flags from "Precise contract, not vague prose" above — both lists. Any dispatched task carrying a substantial body → strip to contract. Any literal-code task missing the `apply inline` tag → add it. Any task whose Behavior says "handle edge cases" without naming them → name them.

**3. Contract/seam consistency:** Every Contract's signatures/types/property names match across tasks and match any symbol another task references? `clearLayers()` in Task 3 but `clearFullLayers()` in Task 7 = bug. Every type a task's Contract uses is defined in some task's Contract or already in the repo.

**4. Wave plan check:** Every task has Wave/Blocks/Blocked-by? Table present and consistent? Same-wave tasks have zero file overlap — check each pair against Files lists. Task B uses Task A output → B must be later wave.

Find issues → fix inline. No re-review. Missing task for spec requirement → add it.

## Execution Handoff

After saving plan, announce and immediately proceed:

**"Plan complete and saved to `docs/plans/<filename>.md`. Proceeding with Subagent-Driven execution."**

**REQUIRED SUB-SKILL:** Use ship — fresh subagent per task + two-stage review.

(Use executing-plans only if user explicitly requests inline execution.)

## Learned Rules

### i18n-mirror-pages-same-task | fired:1 | 2026-05-25
Task 8 listed only `src/pages/portfolio/index.astro` — omitted `src/pages/he/portfolio/index.astro`. Hebrew mirror retained old gradient overlay, required separate fix commit caught by final reviewer.
Prevent: when any task touches `src/pages/<path>`, immediately check for `src/pages/he/<path>` mirror. If it exists, add it to the same task's Files list. EN+HE mirrors always change together.

### plan-code-snippets-must-use-tokens | fired:1 | 2026-05-25
Task 8 code snippet included `style="height: 55vh; min-height: 320px;"` — raw values violating project Hard Rule 1 (tokens everywhere outside `:root`). Quality reviewer caught it; required extra global.css token additions.
Prevent: before embedding any inline-style snippet in a plan task, read project CLAUDE.md Hard Rules. Raw px/vh/rem values → add named tokens to global.css (or equivalent token file) in that task's Files list and use `var(--token)` in the snippet.

### sweep-task-priority-vs-exhaustive | fired:1 | 2026-06-01
Plan labeled V7 raw-button task as "Priority files:" — agents treated list as exhaustive, left 16 raw buttons in unlisted files. Required Wave 6 extra pass. User intent was "all UI consistent", not "fix these specific files".
Prevent: when spec says "priority files" for a sweep task, plan must restate as "ALL files matching the pattern in features/ (run `grep -rln '<button' src/features` to get complete list). Add no-exceptions note except for documented exclusions (drag handles, design-system demos)."

### fontsource-variable-preload-url-mismatch | fired:1 | 2026-05-25
Plan specified copy woff2 to `public/fonts/` + `<link rel="preload" href="/fonts/*.woff2">` while keeping `@import '@fontsource-variable/...'` in CSS. Vite hashes fontsource files into `/_astro/[hash].woff2` — preload pointed to `/fonts/` but CSS loaded `/_astro/` paths. Preloaded bytes wasted; final reviewer flagged FINAL_FAIL, required extra fix commit dropping @import + adding direct `@font-face`.
Prevent: before planning font preloads, check if CSS loads fonts via a Vite-processed `@import` (fontsource, npm font packages). If yes, preload `/fonts/` path won't match hashed CSS request. Either plan to self-host: drop the `@import`, add `@font-face { src: url('/fonts/file.woff2') }` so CSS and preload share the same stable URL — OR omit the preload entirely.

### probe-self-heal-needs-query-all-enabled-states | fired:1 | 2026-06-05
Plan specified `listModels(db, task)` for health probe that claims self-healing re-enable. `listModels` filters `WHERE enabled = 1` — probe never sees disabled models → can never re-enable them. Task 4 implementer had to add `listAllModels` to unblock self-healing.
Prevent: when plan specifies a probe/checker that writes `setEnabled(1)` on success, verify the query function used does NOT filter by `enabled = 1`. Probe must query ALL rows regardless of state — call it `listAllX` or pass `includeDisabled: true`. Filtering by enabled=1 makes self-healing structurally impossible.

### fixture-violations-blocked-by-edit-hooks | fired:1 | 2026-06-10
Plan task wrote a lint-canary fixture containing deliberate rule violations; the session's PostToolUse edit hook (slopgate) blocked the Write with exit 2. Implementer had to patch the global hook script unplanned mid-wave.
Prevent: when a plan task writes files with DELIBERATE violations (fixtures, canaries, negative test data), check active edit/commit hooks in .claude/settings.json first. If a hook lints writes, add a plan step whitelisting the fixture path in the hook BEFORE the fixture-writing task.
