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 — <title>: [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 `<dir-of-spec>/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 <path> -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 (`<ID>`): <title>." 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: <url>` if not already present. Preserve the `<!-- tech-plan-step: <ID> -->` 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 `<!-- tech-plan-step: <ID> -->` 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 → `<new status>`. Next pending: Step M — <title>." +- 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 `<!-- tech-plan-step: -->` 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-<slug>`). 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: + +``` +<!-- tech-plan-step: <ID> --> +``` + +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] <step title>` + - **Description:** `<!-- tech-plan-step: <ID> -->` 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 `<!-- tech-plan-step: -->` 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 (<ID>) — <title> → <ticket URL> [created | updated | unchanged | skipped-terminal | failed: <reason>]` +- 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 `<!-- tech-plan-step: -->` 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 `<dir-of-spec>/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-<slug>` *(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/<slug>-01-<short>` +- **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 <feature>`. 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 <step.Branch>` +> - `stacked`: branch off the previous step's `Branch` field, not `main` +> - `worktree-per-step`: `git worktree add <path> -b <step.Branch> <step.Base>` +> +> 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/<initiative>/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 <spec-dir> -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.