diff --git a/commands/tech-plan-doc.md b/commands/tech-plan-doc.md
new file mode 100644
index 0000000..e2bc692
--- /dev/null
+++ b/commands/tech-plan-doc.md
@@ -0,0 +1,107 @@
+---
+description: Consolidate a completed tech-plan's learnings and decisions into a Slite page for end users and future engineers.
+argument-hint: [optional: path to tech-plan.md]
+---
+
+# /tech-plan-doc — Slite Write-Up
+
+Run after **all** steps in a `tech-plan.md` are done. Consolidates the plan's `Decisions & corrections`, `Notes / learnings`, and PR links into a Slite page aimed at end users and future engineers.
+
+## Inputs
+
+`$ARGUMENTS` — optional path to a specific `tech-plan.md`. The plan can live anywhere — in this docs repo, another repo, or an arbitrary path. If empty, search starting from cwd, then `specs/`, `docs/`, repo root, and `~/dev/dam-creation-specifications/specs/projects/`. If multiple exist, ask which one. If none found, ask the user for the path.
+
+---
+
+## Phase 1: VALIDATE
+
+Read the plan. Check:
+- **Plan status is `In progress`.** Reject `Draft`, `Reviewing`, `Approved`, `Done`, `Superseded`. (Edge case: a plan that skipped tracker sync may be in `Synced` instead — accept with explicit user confirmation.)
+- **All steps are in a terminal state** — `merged`, `skipped`, or `superseded`. Steps in `pending`, `in_progress`, `pr_open`, or `blocked` block the write-up. List any non-terminal steps and ask the user to resolve them before continuing.
+- Every `merged` step has a `PR:` URL.
+- `Decisions & corrections` and `Notes / learnings` are non-empty (otherwise there's not much to write up — confirm the user still wants to proceed).
+
+If anything is missing, list the gaps and ask whether to proceed anyway, fix the plan first, or abort.
+
+---
+
+## Phase 2: DRAFT
+
+Build a Slite-flavored markdown writeup. **Audience: end users and future engineers**, not the team that just shipped it. Lead with what changed for users, not the technical journey.
+
+Structure:
+
+```markdown
+# [Initiative Title]
+
+**Shipped:** YYYY-MM (latest PR merge month)
+**Spec:** [link to .spec.md on GitHub]
+**Tech plan:** [link to tech-plan.md on GitHub]
+
+## What changed
+
+[2–4 sentences. User-visible behavior. Plain language. No jargon. Pull from the functional spec's Goal + acceptance criteria.]
+
+## How it works now
+
+[Brief description of the new behavior. If this was a migration, describe the new shape — not the old one. If a flag was introduced, name it and where it lives.]
+
+## Migration approach (for engineers)
+
+[One paragraph. The expand/contract / flag-ramp / additive shape we used. Why. Pulled from the plan's Migration strategy section.]
+
+## Key decisions
+
+[Bulleted, from Decisions & corrections. Each bullet: the decision + one-line why.]
+
+## Gotchas and learnings
+
+[The meaty section. From Notes / learnings. Things that surprised us, things future engineers should know, edge cases discovered during implementation.]
+
+## Reference
+
+- Functional spec: [link]
+- Tech plan: [link]
+- PRs:
+ - Step 1 —
: [PR link]
+ - Step 2 — : [PR link]
+ - ...
+- Tracker: [parent ticket link]
+```
+
+Show the draft inline. Ask the user to review and request edits. Iterate until they approve.
+
+---
+
+## Phase 3: PUBLISH
+
+**Always ask the user where in Slite the page should go.** Do not assume a default space or parent doc.
+
+Ask:
+- Which **Slite space** (e.g. Engineering, Product Docs, a team-specific space)?
+- Which **parent doc/folder** within that space, if any?
+- Final **page title** (default to the initiative title)?
+
+Then create the page via the Slite MCP tool (`mcp__slite__` or `mcp__claude_ai_Slite__` — authenticate if needed). Capture the resulting page URL.
+
+If creation fails, save the draft to `/slite-draft.md` and tell the user to publish manually.
+
+---
+
+## Phase 4: WIRE BACK
+
+After publishing:
+- Update `tech-plan.md` — add a `**Slite write-up:**` line near the top with the page URL.
+- **Transition plan status: `In progress → Done`.** Validate against the plan's State machine — if not currently in `In progress`, refuse and surface the inconsistency to the user.
+- Update `Last updated:` to today.
+
+Print the Slite URL. Done.
+
+---
+
+## Don'ts
+
+- Don't publish without showing the draft first.
+- Don't pick a Slite location without asking.
+- Don't include code-level detail unless it's a genuine gotcha — the audience is broader than the implementing team.
+- Don't duplicate the functional spec — link to it.
\ No newline at end of file
diff --git a/commands/tech-plan-progress.md b/commands/tech-plan-progress.md
new file mode 100644
index 0000000..19f74b6
--- /dev/null
+++ b/commands/tech-plan-progress.md
@@ -0,0 +1,138 @@
+---
+description: Sync a tech-plan step's status with its PR (opened/merged/closed), update the tracker ticket, and capture learnings.
+argument-hint: [optional: PR url or path to tech-plan.md]
+---
+
+# /tech-plan-progress — Sync Step Status
+
+Run after opening, merging, or closing a PR for a step in `tech-plan.md`. Updates the step's status to match the PR state (`pr_open` / `merged` / `in_progress`), syncs the tracker ticket to a matching status, and captures any learnings.
+
+Idempotent — re-run it across a step's lifecycle (after opening the PR, after merging, after a force-close, etc.).
+
+## Inputs
+
+`$ARGUMENTS` — optional. Either:
+- A PR URL (`https://github.com/.../pull/123`), or
+- A path to a specific `tech-plan.md` (when multiple are active)
+
+If empty, infer everything from current branch and `gh`.
+
+---
+
+## Phase 1: LOCATE THE PLAN AND STEP
+
+1. **Find the active `tech-plan.md`:**
+ - If `$ARGUMENTS` is a path ending in `tech-plan.md`, use it.
+ - Else, the plan can live anywhere (this docs repo, another repo, an arbitrary path). Search broadly: start at cwd, then check common locations like `specs/`, `docs/`, the repo root, and `~/dev/dam-creation-specifications/specs/projects/`. Use `find -name tech-plan.md -not -path '*/node_modules/*'`.
+ - If multiple exist, match by current branch name to a step's `Branch:` field. If still ambiguous, ask the user which one.
+ - If none found, ask the user for the path.
+
+2. **Identify the step in progress.** Try in order, stopping at the first unique match:
+ - **PR URL match** (when `$ARGUMENTS` is a URL or `gh pr view` returned one): match the URL against each step's `PR:` field. Useful when the user has already switched off the branch (e.g. marking a step `merged` from `main`).
+ - **Branch match:** `git rev-parse --abbrev-ref HEAD` against each step's `Branch:` field.
+ - **Step ID prompt:** ask the user which Step `ID:` (e.g. `step-03-backfill`) corresponds to this PR. Step IDs are stable across renames and reorders, so this is the reliable fallback when branches have drifted.
+
+3. **Read the step.** Confirm to the user: "Syncing Step N (``): ." Wait for one-line confirmation if anything looks off; otherwise proceed.
+
+---
+
+## Phase 2: GET THE PR
+
+1. If `$ARGUMENTS` is a URL, use it.
+2. Else, run from the code repo with the PR's branch checked out (typically `~/dev/app-server/` or `~/dev/web-app/`):
+ ```
+ gh pr view --json url,number,title,state --jq '.'
+ ```
+3. If no PR exists yet, tell the user and stop — this command is for **after** a PR is opened.
+4. Note: the plan and the code may live in different repos. Don't assume cwd contains the plan.
+
+---
+
+## Phase 3: UPDATE THE PLAN
+
+Determine the new step status from the PR's `state` field returned by `gh pr view`:
+- `OPEN` → step status becomes `pr_open`
+- `MERGED` → step status becomes `merged`
+- `CLOSED` (without merge) → step status becomes `in_progress` (work resumes)
+
+**Validate the transition against the plan's State machine.** Allowed paths from each current state:
+- `pending → in_progress → pr_open → merged`
+- `pr_open → in_progress` (PR closed without merge)
+- `blocked` requires user action — leave alone unless user explicitly resolves it
+
+If the current step status doesn't allow the target directly (e.g. plan still says `pending` but the PR is already `MERGED`), apply intermediate transitions in order (`pending → in_progress → pr_open → merged`) so the audit trail is consistent.
+
+**Dependency check (when transitioning out of `pending`):** If the step has a `Depends on:` field referencing other steps, look up each dependency's `Status:`. If any dependency isn't in a terminal state (`merged` / `skipped` / `superseded`), warn the user and ask whether to proceed anyway. Some teams allow parallel work despite stated dependencies; the warning lets them make that call explicitly.
+
+**Validation hint (when transitioning to `pr_open`):** Surface the step's `Validation commands:` from the plan and ask the user to confirm they ran (or that CI will run them on the PR). Soft check — don't block — but don't skip the prompt either, since validation gaps are a common source of regressions.
+
+In `tech-plan.md`:
+- Update the step's `Status:` to the new value.
+- Set `PR:` to the PR URL.
+- Update the top-of-file `Last updated:` to today's date.
+
+**Plan-status transition:**
+- If plan is `Synced` and any step is now non-`pending`, set plan to `In progress`.
+- This command never sets plan to `Done` — that transition is owned by `/tech-plan-doc`.
+
+Then ask the user **one** question:
+> Any learnings worth capturing for the final write-up? (Gotchas, surprises, things future engineers should know. Press enter to skip.)
+
+If the user provides text, append a bullet under `## Notes / learnings` with the date and the learning. If they reference a decision that emerged during this step, add it under `## Decisions & corrections` instead.
+
+---
+
+## Phase 4: UPDATE THE TICKET
+
+Read the step's `Tracker ticket:` field. Use the parent plan's `Tracker:` field to choose the integration.
+
+### Step status → tracker status mapping
+
+| Step status | Suggested Linear status | Suggested ClickUp status |
+| --- | --- | --- |
+| `in_progress` | "In Progress" / "Started" | "in progress" |
+| `pr_open` | "In Review" | "in review" / "review" |
+| `merged` | "Done" / "Closed" | "complete" / "closed" |
+| `blocked` | "Blocked" (or add a comment if no such status) | "blocked" (or add a comment) |
+| `skipped` / `superseded` | "Cancelled" / "Won't do" | "cancelled" / "archived" |
+
+Each team's workflow has its own status names. **List the available statuses and pick the closest match**; if uncertain, surface the candidates and ask the user.
+
+### Linear
+- Authenticate via `mcp__claude_ai_Linear__` if needed.
+- Update the issue:
+ - **Status:** the mapped value (see table above; ask if your team's workflow uses different names).
+ - **Description:** append `PR: ` if not already present. Preserve the `` marker block written by `/tech-plan-sync`.
+- Confirm the update succeeded.
+
+### ClickUp
+- Authenticate via `mcp__clickup__` if needed.
+- Update the task:
+ - **Status:** the mapped value (see table above).
+ - Add a comment with the PR URL, or set a custom field if one exists for "PR link". Preserve the `` marker.
+- Confirm.
+
+If the ticket update fails, log the error and continue — the plan update is the source of truth; the ticket can be reconciled manually.
+
+---
+
+## Phase 5: NEXT
+
+Print a short status line:
+- "Step N → ``. Next pending: Step M — ."
+- Or, if all steps are in terminal states (`merged`, `skipped`, `superseded`): "All steps complete. Run `/tech-plan-doc` to write up learnings and transition the plan to `Done`."
+
+Ask whether to proceed to the next step, hand off, or pause. Do **not** auto-start the next step.
+
+---
+
+## Don'ts
+
+- Don't mark a step `merged` without a real PR URL whose state is `MERGED`.
+- Don't skip intermediate step transitions — even if the PR is already merged, walk the state through `in_progress → pr_open → merged` so the audit trail is consistent.
+- Don't transition the plan to `Done` — that's `/tech-plan-doc`'s job.
+- Don't assign `blocked` / `skipped` / `superseded` automatically — only when the user explicitly says so.
+- Don't leave the tracker on "In Review" once the PR has merged — re-run progress to map `merged` → "Done".
+- Don't strip the `` marker from ticket descriptions — `/tech-plan-sync` relies on it for idempotent re-syncs.
+- Don't merge unrelated edits into `tech-plan.md` — only the step status, PR link, learnings, plan status, and `Last updated`.
+- Don't auto-progress to the next step.
\ No newline at end of file
diff --git a/commands/tech-plan-sync.md b/commands/tech-plan-sync.md
new file mode 100644
index 0000000..6c7bff4
--- /dev/null
+++ b/commands/tech-plan-sync.md
@@ -0,0 +1,106 @@
+---
+description: Push an approved tech-plan.md to Linear or ClickUp, creating one child ticket per step under a parent.
+argument-hint: [optional: path to tech-plan.md]
+---
+
+# /tech-plan-sync — Sync Plan to Tracker
+
+Creates tracker tickets (Linear or ClickUp) from an approved `tech-plan.md`. One child ticket per step, parented under a single epic / work package, assigned to the current user.
+
+**Sync must be idempotent. Each tracker child must include a stable Step ID from `tech-plan.md`. On repeated sync, update existing children rather than creating duplicates.**
+
+This is **separate from `/tech-plan`** so the planning artifact and the tracker sync stay decoupled. You can iterate on the plan without spamming the tracker; you can re-sync after adding/splitting steps.
+
+## Inputs
+
+`$ARGUMENTS` — optional path to a specific `tech-plan.md`. The plan can live anywhere — in this docs repo, another repo, or an arbitrary path. If empty, search starting from cwd, then `specs/`, `docs/`, repo root, and `~/dev/dam-creation-specifications/specs/projects/`. If multiple exist, ask which one. If none found, ask for the path.
+
+---
+
+## Phase 1: VALIDATE
+
+1. Read the plan.
+2. **Plan-status guard.** Plan status must be one of `Approved`, `Synced`, `In progress`.
+ - `Draft` / `Reviewing` → reject. Tell the user to approve via `/tech-plan` first.
+ - `Done` / `Superseded` → reject. Confirm explicitly with the user before continuing (they may be re-syncing a closed plan intentionally).
+ - Any other value → treat as malformed; ask the user to fix the header.
+3. **Verify every step has a stable `ID:` field** (e.g. `step-01-`). If any step is missing one — typically because the plan was authored before this rule existed — assign IDs (matching their current numeric order) and persist them to the plan before going further. IDs must never change after first sync.
+4. **Skip terminal-state steps.** Steps in `merged`, `skipped`, or `superseded` are not synced or updated — they're frozen. Tell the user the count of skipped-due-to-terminal-state steps.
+5. **Classify each remaining step:**
+ - **New** — has an `ID:` but no `Tracker ticket:` and no existing child found with that Step ID under the parent.
+ - **Existing-cached** — has both `ID:` and `Tracker ticket:`. Re-fetch by URL to confirm the ticket still exists and still encodes the same Step ID.
+ - **Existing-orphan** — has `ID:` but no `Tracker ticket:`, yet a search under the parent finds a child whose description encodes that Step ID. Reattach the URL to the plan; treat as existing.
+ Tell the user the count per category before creating or updating anything.
+
+---
+
+## Phase 2: TRACKER CHOICE
+
+If the plan's `Tracker:` field is already set (e.g. `Linear` or `ClickUp` from a prior sync), use that. Otherwise ask:
+
+- **Linear** or **ClickUp**? (Different teams use different ones.)
+
+Then ask for the parent:
+
+- **Linear:** the parent issue or project ID/URL.
+- **ClickUp:** the **work package** (parent task) ID/URL.
+
+Confirm assignee defaults to the current authenticated user unless the user says otherwise.
+
+Persist these choices into the plan's header (`Tracker:`, `Parent ticket:`) before creating any children. That way a re-run picks up where you left off.
+
+---
+
+## Phase 3: CREATE OR UPDATE CHILD TICKETS
+
+For each step, decide based on its classification from Phase 1.4: **create** (New) or **update** (Existing-cached / Existing-orphan).
+
+### Step ID embedding
+
+Every child ticket — created or updated — must carry the Step ID in a machine-readable line at the top of its description, so future sync runs can find it without relying on the cached URL alone:
+
+```
+
+```
+
+This is the source of truth for matching on re-sync. Do not remove or alter it.
+
+### Linear
+- Authenticate via `mcp__claude_ai_Linear__` if needed.
+- **Create** (New step):
+ - **Title:** `[Step N] `
+ - **Description:** `` line, then Step's Scope + Acceptance + Backward-compat guarantee + link to `tech-plan.md` + link to functional spec.
+ - **Parent:** the parent issue.
+ - **Assignee:** current user.
+ - **Labels/team:** match the parent's team.
+- **Update** (Existing step): rewrite **title** (step number may have changed) and **description** (scope/acceptance may have changed) to match the current plan. **Preserve** status, comments, custom fields, and the `` marker. Do not change the assignee unless the user asks.
+
+### ClickUp
+- Authenticate via `mcp__clickup__` if needed.
+- **Create** (New step): same shape as Linear above — title, description with Step ID marker, parent = work package, assignee = current user.
+- **Update** (Existing step): rewrite title and description to match the current plan; preserve status, comments, custom fields, and the Step ID marker.
+
+After each successful create/update, write the ticket URL into the matching step's `Tracker ticket:` field in `tech-plan.md`. Save after each step (incremental persistence) so a partial run isn't lost.
+
+---
+
+## Phase 4: REPORT
+
+Print a summary:
+- Tracker + parent link
+- Per-step result: `Step N () — → [created | updated | unchanged | skipped-terminal | failed: ]`
+- Update `Last updated:` in the plan.
+- **Plan-status transition:** if the plan was `Approved`, set to `Synced`. If already `Synced` or `In progress`, leave unchanged — re-syncs do not roll the plan back. Validate against the plan's State machine before writing.
+
+If any creations failed, ask whether to retry the failures, fix them manually, or accept and move on.
+
+---
+
+## Don'ts
+
+- Don't create duplicate tickets for steps that already exist on the tracker — match by Step ID, not just by `Tracker ticket:` URL presence.
+- Don't change a step's `ID:` once assigned. Renames, reorders, and splits keep the original ID; new steps get a new ID. Changing IDs breaks idempotency.
+- Don't strip the `` marker from ticket descriptions. It is the matching anchor.
+- Don't overwrite tracker status, comments, or custom fields during sync. Status belongs to `/tech-plan-progress`.
+- Don't change the plan's step ordering or content during sync. This command writes only `Tracker:`, `Parent ticket:`, per-step `Tracker ticket:`, missing `ID:` backfills, and `Last updated:`.
+- Don't pick a tracker without asking, unless the plan already has `Tracker:` set.
diff --git a/commands/tech-plan.md b/commands/tech-plan.md
new file mode 100644
index 0000000..3bc6b94
--- /dev/null
+++ b/commands/tech-plan.md
@@ -0,0 +1,343 @@
+---
+description: Generate a backward-compatible PR-by-PR technical plan from a functional spec, then sync to Linear or ClickUp.
+argument-hint: [path to functional spec]
+---
+
+# /tech-plan - Technical Plan Generator
+
+Generate a self-contained, PR-by-PR technical plan from a functional spec.
+
+The plan must:
+- map every acceptance criterion to implementation and validation steps
+- split work into independently deployable, backward-compatible PRs
+- describe migration, rollout, rollback, and observability strategy
+- embed enough codebase context for a fresh executor to implement one step without reading the chat history
+- remain tracker-agnostic until explicitly synced by `/tech-plan-sync`
+
+This command plans only. It does not create tracker tickets, execute code changes, or open PRs.
+
+## Inputs
+
+- `$ARGUMENTS` - path to the functional spec. Can be relative to cwd or absolute. Spec format is flexible - this command extracts intent, not specific section names.
+- If empty or invalid, ask the user for it before proceeding.
+
+---
+
+## Phase 1: UNDERSTAND
+
+**Before step 1, run the [Pre-flight detection](#pre-flight-detect-prior-planning)** - there may already be a `tech-plan.md`, a `*technical_plan*/` directory, or ad-hoc `plan*.md` files at the spec's location. Re-planning from scratch when prior planning exists wastes work and conflicts with the existing breakdown.
+
+Do not write the plan yet. First, build a complete picture.
+
+1. **Read the functional spec.** Read `$ARGUMENTS` in full. The spec may live anywhere - in this docs repo, in another repo, or in an arbitrary path the user provides. Extract: goal, in-scope, out-of-scope, acceptance criteria, target behavior, open questions, and any decisions already made. Adapt to whatever section names the spec uses. If the spec carries a status field that signals it isn't finalised (e.g. `Research`, `Draft`, `WIP`), warn the user and ask whether to proceed.
+
+2. **Identify affected surfaces.** From the spec + appendices, list which sibling repos under `~/dev/` are touched. Common surfaces: `~/dev/app-server/` (backend), `~/dev/web-app/` (frontend), `~/dev/testing-suite-playwright/` (E2E). Per the repo's `CLAUDE.md`, **analyse backend and frontend separately**, then consolidate.
+
+3. **Read repo conventions, then map the current state in code.** For each affected surface, do these in order:
+ - **First, read the repo's convention docs.** Look at the surface's root `CLAUDE.md` and/or `AGENTS.md` - they document directory layout, conventions, testing patterns, feature-flag mechanics, and common gotchas. These are the planner's shortcut to context that would otherwise take many greps to assemble. If neither file exists, fall back to `README.md`. Capture the bits that affect this change (e.g. "feature flags via X", "migrations via Y") for use in the plan's `### Conventions to follow` section.
+ - **Then, map the code.** Find the key files/classes/methods/fields/APIs implicated by the spec. For large surface area, dispatch parallel `Explore` subagents (one per repo) - feed each the spec excerpt and the relevant convention notes. For small surface area, grep/read directly.
+
+4. **Map acceptance criteria to implementation surfaces.** For each acceptance criterion in the spec, identify the backend / frontend / testing / docs surface that proves it is satisfied. If any criterion has no implementation or validation path - or maps to a surface not yet identified in step 2 - **flag it before drafting**. Either expand the surface list, push back to the user (criterion is untestable as written), or escalate as a blocking question in step 6.
+
+5. **Identify the migration shape.** Pick the pattern that fits this change:
+ - **Field/data shape change** → expand/contract: `read-both → write-new → backfill → read-new only → drop-old`
+ - **API change** → side-by-side versions: `add-new-version → migrate-callers → deprecate-old → remove-old`
+ - **Behavior change behind a flag** → `flag-off default → ramp → flag-on default → remove-flag`
+ - **Pure additive (new endpoint/UI)** → 1 - 2 PRs, no migration phases
+ - **Mixed** → describe explicitly
+
+6. **Ask any clarifying questions in one batch.** Group them; do not trickle. **When in doubt, ask.** Don't plan around an unstated assumption - the user would rather answer a quick question than relitigate the plan after seeing it. **Do not ask tracker questions here** - those belong in `/tech-plan-sync`, after the plan is approved.
+
+ Cover, when relevant:
+ - Anything ambiguous in the spec that affects implementation
+ - The migration shape if not obvious from the spec
+ - Acceptance criteria without a clear validation path (from step 4)
+ - **Branching strategy** - `branch-per-step` (each branch off `main`), `stacked` (each branch off the previous), or `worktree-per-step` (parallel checkouts)
+ - **Handoff style** after approval - continue this session, sub-agent, or fresh Claude instance
+ - **PR sizing** - typically one logical concern per PR; ask if the change suggests otherwise
+
+ If a question can be answered by reading the codebase or the spec more carefully, do that instead of asking.
+
+ **Exception:** if the user explicitly says "and sync to Linear/ClickUp now" when invoking the command, also ask the tracker questions here so you can chain straight into sync after approval. Otherwise leave it for `/tech-plan-sync`.
+
+7. **Summarize and confirm.** Give the user:
+ - One-paragraph restatement of the goal
+ - The chosen migration shape
+ - The proposed PR breakdown as a numbered one-liner list (no detail yet)
+
+ Wait for explicit "yes, write the plan" before Phase 2.
+
+---
+
+## Phase 2: WRITE THE PLAN
+
+Write `/tech-plan.md` co-located with the functional spec, wherever it lives. If writing alongside the spec isn't appropriate (e.g. the spec lives in a read-only location), ask the user where to put the plan. Use the template below. The plan **must** be self-contained - embed enough codebase context, conventions, and gotchas that a fresh Claude (with no chat history) can execute step N from a cold start.
+
+### Plan template
+
+```markdown
+# Tech Plan: [Initiative Title]
+
+**Status:** Draft | Reviewing | Approved | Synced | In progress | Done | Superseded *(state machine governed by /tech-plan)*
+**Functional spec:** [relative link to .spec.md]
+**Tracker:** [unset until /tech-plan-sync runs - Linear | ClickUp]
+**Parent ticket:** [filled by /tech-plan-sync]
+**Branching strategy:** branch-per-step | stacked | worktree-per-step
+**Supersedes:** [path to prior plan dir or file consumed during pre-flight, if any]
+**Superseded by:** [link to successor plan, if `Status: Superseded`]
+**Last updated:** YYYY-MM-DD
+
+## Goal
+
+[One paragraph from the spec's Goal, plus a sentence on the technical approach.]
+
+## Affected surfaces
+
+- Backend: `~/dev/app-server/` - [areas/modules]
+- Frontend: `~/dev/web-app/` - [areas/modules]
+- Other: [if relevant]
+
+## Codebase context
+
+For each surface, list the key files/classes the executor needs to know about. Use `class::method` or `file:concept` form so it survives line-number drift.
+
+### Backend (`~/dev/app-server/`)
+- `application/.../FooService::doX` - [what it does, why it matters here]
+- `domain/.../Foo` - current shape: [field A, B, C]
+
+### Frontend (`~/dev/web-app/`)
+- `src/.../FooBar.tsx` - reads `foo.fieldA`
+- API hook: `useFoo()` in `src/api/foo.ts`
+
+### Conventions to follow
+*(Pulled from each surface's `CLAUDE.md` / `AGENTS.md`. Cite the file and section so the executor can verify.)*
+- [House style for the kind of change at hand - e.g. "Feature flags via X (`~/dev/app-server/CLAUDE.md` § Feature flags)", "Migrations via Y", "GraphQL schema versioning policy".]
+
+## Migration strategy
+
+[Narrative: what's the old shape, what's the new shape, how do we get there safely. Reference the chosen pattern: expand/contract, side-by-side, flag ramp, etc.]
+
+## Steps
+
+Each step is one PR. Each step leaves the codebase compilable, tested, deployable, and **backward-compatible** with the previous deployed state.
+
+### Step 1: [Short imperative title]
+- **ID:** `step-01-` *(stable identifier - never change once assigned, even if the step is renamed, reordered, or split. Used by `/tech-plan-sync` to match tracker tickets across re-runs.)*
+- **Branch:** `feat/-01-`
+- **Base:** `main` (or, for `stacked`: the previous step's branch; for `worktree-per-step`: include worktree path)
+- **Tracker ticket:** [filled by /tech-plan-sync]
+- **Depends on:** Step N / none
+- **Phase:** expand | dual-write | backfill | contract | additive | flag-on | flag-off | cleanup
+- **Feature flag state:** off | partial | on | removed | n/a - [name the flag if any]
+- **Scope:** [What this PR changes - be specific. Include both backend and frontend if both touched.]
+- **Files likely touched:**
+ - Backend: [...]
+ - Frontend: [...]
+- **Backward-compat guarantee:** [Why deploying just this PR is safe in isolation. What old clients/data still works.]
+- **Rollback:** [How to undo or safely disable this step. For flag-gated steps: flip flag off. For migrations: explicit reverse migration or "no-op - step is forward-compatible". Must be concrete.]
+- **Acceptance:** [Testable bullets. What new behavior? What unchanged?]
+- **Test approach:** [Unit/integration/E2E touch points. Existing tests to update.]
+- **Validation commands:** [Concrete commands the executor runs before opening a PR - e.g. `composer test`, `pnpm typecheck`, `pnpm test src/foo`, `playwright test --grep `. Must be copy-pasteable.]
+- **Status:** pending *(one of: `pending | in_progress | blocked | pr_open | merged | skipped | superseded` - state machine governed by /tech-plan)*
+- **PR:** -
+
+### Step 2: ...
+[same shape]
+
+## Decisions & corrections
+
+- [YYYY-MM-DD] - [Decision and why. Updated by /tech-plan-progress as decisions emerge during execution.]
+
+## Notes / learnings
+
+(For end users / future engineers. Appended to during execution. Consumed by /tech-plan-doc at the end.)
+
+- [Gotcha or learning]
+
+## Handoff
+
+**Mode:** continue-here | sub-agent | fresh-claude
+**Next step to execute:** Step N
+**Executor instructions:**
+> Read this file. Pick the next step where Status is `[ ] pending`. Honour the **Branching strategy** at the top of this file:
+> - `branch-per-step`: `git checkout main && git pull && git checkout -b `
+> - `stacked`: branch off the previous step's `Branch` field, not `main`
+> - `worktree-per-step`: `git worktree add -b `
+>
+> Implement only what's in scope. Verify type-checks/tests pass. Open a PR. Then run `/tech-plan-progress` from this repo to mark the step done and update the tracker.
+```
+
+### Rules for writing the plan
+
+- **One logical concern per step.** Not "fix everything in module X". A step has a single, articulable purpose.
+- **Each step is independently deployable and backward-compatible.** No step requires another to be deployed simultaneously.
+- **Order matters.** Earlier steps don't depend on later ones. Reads expand before writes change. Writes change before old reads are removed. Use the `Depends on:` field to make any non-obvious ordering explicit.
+- **Validation commands must be runnable as-is.** No placeholders like "run the tests" - name the actual command(s) the executor should paste into a terminal before opening the PR. If the surface has no test command yet, mark it `n/a` and explain in `Test approach`.
+- **Rollback must be concrete.** "Revert PR" is not enough - name the flag to flip, the reverse migration, or explicitly state "forward-only / no rollback needed (step is additive)".
+- **Embed enough context.** A fresh Claude reading only `tech-plan.md` should know which files to touch and which conventions apply. Don't make the executor re-discover the codebase.
+- **Ask when in doubt.** If a decision affects the plan and you're not confident, ask in Phase 1 step 6. Don't paper over uncertainty with a silent default.
+- **Code-pointer hygiene.** Use `Class::method` or `file:concept`, not raw line numbers (they rot). Per-repo `CLAUDE.md` is the source of truth for conventions - reference it, don't duplicate.
+- **Do not include full implementation code.** Allow short schema sketches, payload examples, pseudocode, or migration examples when they clarify compatibility or acceptance criteria. The bar: just enough to make the contract or transformation unambiguous, not enough to substitute for the actual implementation.
+- **Size sanity check.** If a step's scope reads like more than a few days of focused work, propose splitting before finalizing.
+
+### Self-review before presenting
+
+Before showing the draft to the user, run through this checklist. Fix anything that fails - don't ship a known-incomplete plan to review.
+
+- **Is every acceptance criterion covered?** Cross-reference Phase 1 step 4's criterion-to-surface map against the steps. Each criterion should land in at least one step's Acceptance.
+- **Is every step deployable on its own?** No step should require another to be deployed simultaneously. If two steps are coupled, either merge them or restructure.
+- **Does every step have a rollback or disablement path?** Concrete: flag flip, reverse migration, or explicit "forward-only / additive". "Revert PR" alone doesn't count.
+- **Are old/new client/server/data combinations safe?** Walk through the deploy sequence: after step 1 deploys but before step 2, do old clients still work against new servers? Old data against new code? Etc.
+- **Are validation commands concrete?** Copy-pasteable terminal commands, no "run the tests" placeholders.
+- **Are steps small enough for review?** A reviewer should be able to understand a step's PR in isolation. If a step's scope reads like more than a few days of focused work, propose splitting before presenting.
+
+If any check fails, fix the plan and re-run the checklist.
+
+### Present
+
+Present the draft plan inline to the user. Wait for approval. The user may ask to merge, split, reorder, or remove steps. Apply changes, **re-run the self-review** if anything structural changed, re-present, await final approval.
+
+---
+
+## Phase 3: APPROVAL & NEXT STEPS
+
+Drive the plan through its state machine. Valid plan transitions in this command:
+- Initial: `Draft`
+- When you present the draft for review (Phase 2): `Draft → Reviewing` *(skip if the user gives instant approval - `Draft → Approved` is also valid)*
+- On revision request: `Reviewing → Draft`
+- On explicit user approval: `Reviewing → Approved` (or `Draft → Approved` if you skipped Reviewing)
+
+After the plan reaches `Approved`:
+
+1. Update the plan's `Status:` to `Approved` and bump `Last updated:` to today.
+2. Tell the user the plan is ready and offer next steps:
+ - **Sync to tracker** → run `/tech-plan-sync` (asks Linear vs ClickUp, parent ticket, work package; creates child tickets per step). Skip if the user wants informal tracking.
+ - **Start executing** → see Handoff below.
+3. **Exception:** if the user said "sync now" when invoking this command (and you collected tracker info in Phase 1), chain straight into `/tech-plan-sync` logic without asking again.
+
+Do not create tracker tickets in this command's default path. Do not transition the plan past `Approved` here - `Synced` is owned by `/tech-plan-sync`.
+
+---
+
+## Phase 4: HANDOFF
+
+Based on the handoff style chosen in Phase 1:
+
+- **continue-here:** Tell the user the plan is ready and ask whether to start Step 1 now (in `/stack`-style: one step, one approval, one PR). Do **not** start without explicit go-ahead.
+- **sub-agent:** Confirm before spawning. Use `Agent` with `subagent_type: general-purpose`. The agent's prompt must be self-contained: path to `tech-plan.md`, the executor instructions block from the plan, and explicit "execute exactly one step, open the PR, then return - do not loop". Run in foreground unless the user prefers background.
+- **fresh-claude:** Print a copy-pasteable instruction the user can give to a new Claude Code session, e.g.:
+ ```
+ Read specs/projects//tech-plan.md and execute the next pending step per its Handoff section.
+ ```
+
+---
+
+## Pre-flight: detect prior planning
+
+Run this **before Phase 1 step 1**. Check the spec's directory and any sibling locations for prior planning artifacts. Tech plans authored before `/tech-plan` existed (or by other tools) often live as multi-file directories - re-planning from scratch destroys that work.
+
+### What to look for
+
+In the spec's directory (and one level deep), search for:
+
+1. **`tech-plan.md`** - produced by a prior `/tech-plan` run. State-machine format.
+2. **Sibling tech-plan directories** matching `*technical_plan*` / `*tech_plan*` / `*tech-plan*`. Typically contain `overview.md`, `plan1-*.md`, `plan2-*.md`, etc.
+3. **Ad-hoc `plan*.md` files** co-located with the spec, excluding any handled in case 1.
+
+Use:
+```
+find -maxdepth 2 \( -name "tech-plan.md" -o -name "plan*.md" -o -type d -name "*technical_plan*" -o -type d -name "*tech_plan*" -o -type d -name "*tech-plan*" \)
+```
+
+**Filter by spec stem.** The find above casts wide and will match plan dirs for sibling specs in the same directory. After running it, **keep only results whose name starts with the spec's filename stem** (e.g. spec `h7_filter_mechanics.spec.md` → keep `h7_filter_mechanics_technical_plan/`, drop `h9_selection_limit_technical_plan/`). If the filter leaves zero results, treat as nothing-found rather than guessing.
+
+### How to handle each case
+
+**Case 1 - `tech-plan.md` exists:**
+1. Read it.
+2. Show the user the status snapshot (steps done, in progress, pending - per the State machine).
+3. Ask: continue (next pending step), revise plan, or start over (overwrite - confirm twice).
+
+**Case 2 - sibling tech-plan directory exists:**
+1. List the directory contents to the user.
+2. Ask which mode to use:
+ - **Consume** *(default lean)* - Read every plan file in the directory. In Phase 2, translate the existing breakdown into a single `tech-plan.md` in the state-machine format, preserving step intent, ordering, and dependencies. Add a `Supersedes:` note in the plan header pointing at the source directory. Leave the source files in place for traceability.
+ - **Replace** - Start fresh. Leave the existing directory untouched but mark the resulting `tech-plan.md` as the canonical plan.
+ - **Abort** - Stop. Let the user reconcile manually.
+3. Default lean is **Consume** because the existing breakdown usually reflects domain knowledge that's expensive to recreate.
+
+**Case 3 - ad-hoc `plan*.md` files (and no case 1 / case 2 hits):**
+Same prompt as Case 2 - consume / replace / abort.
+
+**Nothing found:** proceed to Phase 1 normally.
+
+### Don't
+
+- Don't silently overwrite or ignore prior planning. Always surface it and ask.
+- Don't merge a sibling tech-plan directory's files in-place. Always produce a single `tech-plan.md` and leave the source dir for history.
+
+---
+
+## State machine
+
+`tech-plan.md` is a state machine. Both the plan and each step have explicit, enumerated states. Transitions follow the rules below - every command in this pipeline enforces them.
+
+### Plan status
+
+| Value | Meaning |
+| --- | --- |
+| `Draft` | Initial planning. Not yet shared for approval. |
+| `Reviewing` | Shared for feedback. May return to `Draft` for revisions. *(Optional - `Draft → Approved` is also valid if user gives instant approval.)* |
+| `Approved` | User has explicitly approved the plan. Ready to sync. |
+| `Synced` | `/tech-plan-sync` has created/updated tracker tickets. Ready to execute. |
+| `In progress` | At least one step has left `pending`. |
+| `Done` | All steps in a terminal state and `/tech-plan-doc` has published the write-up. |
+| `Superseded` | Replaced by a successor plan. Do not execute. Record successor in `Superseded by:` header. |
+
+**Allowed plan transitions:**
+- `Draft → Reviewing → Approved → Synced → In progress → Done`
+- `Draft → Approved` (when Reviewing is skipped)
+- `Reviewing → Draft` (revisions)
+- Any state → `Superseded`
+
+### Step status
+
+| Value | Meaning |
+| --- | --- |
+| `pending` | Not yet started. |
+| `in_progress` | Work has begun (branch checked out, code being written). |
+| `blocked` | Cannot proceed. Record reason in `## Decisions & corrections`. |
+| `pr_open` | PR is open for review. |
+| `merged` | PR merged. Terminal. |
+| `skipped` | Step decided against. Terminal. Record reason. |
+| `superseded` | Step replaced by another in a re-plan. Terminal. Reference successor step's `ID`. |
+
+**Allowed step transitions:**
+- `pending → in_progress → pr_open → merged`
+- `pending ↔ blocked`, `in_progress ↔ blocked`
+- `pr_open → in_progress` (PR closed without merge - work resumes)
+- `pending → skipped`, `pending → superseded` (terminal exits without execution)
+
+Non-happy-path transitions (`blocked`, `skipped`, `superseded`) require a dated entry in `## Decisions & corrections` with the reason.
+
+### Who writes which transitions
+
+| Command | Plan transitions | Step transitions |
+| --- | --- | --- |
+| `/tech-plan` | sets `Draft`; `Draft → Reviewing → Approved` (or `Draft → Approved`) | initialises steps as `pending` |
+| `/tech-plan-sync` | `Approved → Synced` (refuses unless `Approved` / `Synced` / `In progress`) | none |
+| `/tech-plan-progress` | `Synced → In progress` on first non-`pending` step | `pending → in_progress`, `in_progress → pr_open`, `pr_open → merged`, `pr_open → in_progress` |
+| `/tech-plan-doc` | `In progress → Done` after publishing | none (validates terminal states only) |
+
+`blocked`, `skipped`, `superseded` (plan and step) are **user-initiated** - commands accept them when found but never assign them automatically.
+
+---
+
+## Don'ts
+
+- Don't auto-start execution. Approval gates everything.
+- Don't skip the codebase analysis phase. The plan's value is the embedded context.
+- Don't ask tracker questions during discovery. Tracker setup is `/tech-plan-sync`'s job - keep this command focused on the planning artifact.
+- Don't create tracker tickets in this command unless the user explicitly opted into "sync now" up front.
+- Don't combine backend and frontend analysis - separate passes per `CLAUDE.md`.
+- Don't write the plan as a generic checklist. Be specific to this spec, this codebase, this migration.