agentsclimarketplace

Plan issue

Skill bendyworks/claude-skills/skills/plan-issue

Plan a story end-to-end -- interview the user about a tracker issue (Linear, Shortcut, or GitHub Issues), research the code, propose a plan, optionally challenge the plan from a fresh perspective, record it as a markdown file under `.claude/plans/` and start working through the to-dos, then finish by confirming the work shipped and cleaning up the local branch. Use when the user says "let's plan a new issue", "let's work on a new issue", "plan this story", "challenge the plan", "record the plan", "write up the plan", "finish up the plan", "we shipped X, clean it up", or similar.From its SKILL.md

Install
npx -y skills add bendyworks/claude-skills --skill plan-issue

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

51.1 KB, as published. Nobody here has run it

Plan an issue

This skill drives the create -> challenge -> record -> finish arc that spans the whole life of a story. It has four phases. Pick the phase that matches the user's request:

  • create (default) -- start a new plan for an issue. Triggered by "let's work on a new issue", "let's plan ...", or a tracker issue URL (Linear, Shortcut, or GitHub) with no other context.
  • challenge -- critique the current plan. Triggered by "challenge the plan", "poke holes in the plan", or after the user says the plan looks done.
  • record -- write the agreed plan to a file and start executing. Triggered by "record the plan", "write up the plan", "start working through it".
  • finish -- after the work has shipped (the PR merged AND the change live in production, or just the merge in Deploy-on-Merge Mode), confirm the plan is wrapped up and delete the local working branch. Triggered by "finish up the plan", "we shipped X, clean it up", "we're done with X", or similar.

If the user invokes the skill without specifying a phase, default to create. After create finishes, do NOT auto-advance into challenge or record -- wait for the user. Likewise, do NOT auto-advance from record into finish: finish runs only once the user says the work is wrapped up. On a project where merge and production deploy are distinct events, it also waits for the deploy.

Rules the user's CLAUDE.md files may already cover -- do NOT restate them in the plan

The user's global and project CLAUDE.md files may already require:

  • TDD by default (failing spec first for production code changes).
  • Small PRs (<300-400 lines), incremental commits.
  • Clean and green: lint + tests must pass before commit/push/PR.
  • Open - [ ] checkboxes for incomplete items, - [x] for completed. Never use the green checkmark to mean "todo".
  • Use the bundled linear CLI for Linear access rather than raw GraphQL curl calls.
  • No emdashes in user-facing prose.
  • Conventional Commits shape with a Title Case outcome description (type(scope): Title Case Outcome Description).

The plan should behave consistently with these rules but should not duplicate the rules themselves. Keep the plan focused on this specific issue's why, what, and how.

Deploy-on-Merge Mode (project-declared)

On some projects there is no gap between merging and deploying: merging to the default branch is the production deploy. A project says so by declaring the mode in its checked-in CLAUDE.md or a rules file, in wording like:

This project uses Deploy-on-Merge Mode: merging to the default branch is the production deploy, with no later step that could still fail or be skipped.

Recognize the declaration by meaning rather than by exact string, but hold a floor: it must be an explicit statement of the fact, naming the mode or saying unambiguously that merging to the default branch is the deploy. Prose that merely describes how the project happens to work ("we deploy on every merge", "merged means shipped") is a shape observation, not a declaration -- treat that project as undeclared and ask the user rather than deciding for them.

A project that declares nothing is in the default, where merge and production deploy are distinct events and every gate below applies as written -- never infer the mode from a project's shape (no version file, auto-merge enabled, a deploy workflow present). A silent inference is exactly how a real gate gets dropped. A project whose files say nothing on the subject is never prompted about the mode; the one case worth a question is the project above, whose files gesture at the mode without declaring it.

Establish the mode from the project's checked-in files at every site that consumes it, and say which answer came back. Both the ship tail in create Step 6 and the production precondition in finish Step 1 depend on the answer, and finish is a standalone entry point that never runs create. The declaration may live in a rules file rather than an auto-loaded CLAUDE.md, so it is not safe to assume it is already in front of you -- and a session that half-remembers the mode from conversation rather than from the project's files is the one most likely to apply it where it does not hold. A conversational memory of the mode does not count as establishing it.

Verify the premise before declaring the mode, before recommending it, and before acting on a declaration someone else wrote. The premise is that nothing after the merge can still fail or be skipped. The mis-declaration to rule out by name: a merge that triggers an automatic deploy pipeline which can itself go red is not Deploy-on-Merge -- there is a gap and it can fail. Declaring the mode there fails silently, because the housekeeping pass would delete the branch and close the issue while the pipeline is red.

Two things that look like disqualifying steps but are not. A post-merge job that only verifies the merged commit -- a test or lint run that reports on what users already have and cannot withhold it from them -- is not a deploy step, so it does not break the premise. And availability is not adoption: for a versionless package or plugin, merging makes the new code available and that is the whole deploy; downstream installers pulling it later is not a step that can fail.

The declaration states the fact, not its consequences. Each site that consumes the mode says what it does differently, so there is one authoritative answer per behavior instead of two that can disagree.

What Deploy-on-Merge Mode does NOT change

  • The finish phase stays user-triggered. Never auto-advance from record into finish. That rule's justification is the gap between "shipped" and "we're sure nothing else is outstanding," which the mode leaves entirely intact.
  • Housekeeping still runs in full, and still stops on genuinely unfinished work.
  • Review, CI, and the draft-PR flow are untouched; the mode says nothing about what has to be true before a merge.
  • Existing plans need no rewriting. A plan written before the declaration keeps its confirm-shipped item; the housekeeping skill (bundled in this plugin) owns what satisfies it.

One canonical title; three artifacts share it by default

A single succinct title is chosen up front and used as the base for all three artifacts:

  1. The tracker issue title (Linear, Shortcut, or GitHub).
  2. The plan filename under .claude/plans/.
  3. The git branch name.

Target roughly 40 characters or fewer for the title -- a short verb-led phrase with one concept. Detail belongs in the description, not the title. Always pull the canonical branch name from the issue tracker rather than inventing a slug -- this guarantees the tracker can auto-link the branch/PR to the story.

  • Shortcut: if a Shortcut MCP server is configured, call its stories-get-branch-name tool (the mcp__shortcut__stories-get-branch-name action) for the story's public ID; it returns the valid, tracker-recognized slug, e.g. sc-30818-fix-flakey-specs. Otherwise derive the branch name from the story's public ID and title, or ask the user. Use that exact string for the git branch and the plan filename. Do this immediately after the story is created (or as soon as its ID is known) and before creating the branch.
  • Linear: use the "Copy git branch name" action, which produces the slugified form (e.g. abc-525-fix-pdf-uploads); prefer that exact slug.
  • GitHub Issues: GitHub generates no slug to copy, so this is the one tracker where the slug is composed rather than pulled: build it from the agreed (possibly renamed) title in GitHub's own NNN-title-slug shape, shortened to roughly 40 characters (e.g. 525-fix-pdf-uploads), and confirm it with the user along with the title. Then, at branch-creation time -- only after the title and slug are locked -- run gh issue develop NNN --name <slug> --checkout, the CLI form of GitHub's "create a branch for this issue" web action. It creates a linked branch (visible in the issue's Development section) and checks it out with upstream tracking already set. Three caveats: it creates the branch on the remote immediately (never run it just to preview a name); it creates it on whatever repo gh resolves as the default -- in a fork clone normally the parent, so confirm gh repo view --json nameWithOwner names the intended repo when more than one remote exists; and a PR opened from a linked branch AUTO-CLOSES the issue at merge regardless of PR-body keywords (see the ship-tail steps for when that matters and how to prevent it). If any caveat is unwanted, or you lack write access to the repo, fall back to a local git checkout -b with the same slug.

Never hand-roll a slug like fix-flakey-specs when a tracker issue exists -- the SC-/ABC- style tracker prefix is what lets Shortcut/Linear detect the PR, and the NNN- prefix keeps a GitHub branch recognizably tied to its issue.

If the existing issue title is too long or restates a whole sentence, propose a rename before creating the plan file or branch so all three artifacts can match. Do not silently use a different name across artifacts.

When deviation is allowed

Occasionally a single tracker issue spawns multiple plan files (separate up-front refactor + main work, or a follow-up cluster). In that case, the plan filename and the corresponding git branch are allowed to diverge from the original story title -- they should still share a base name with each other (the plan and its branch), but they do not have to match the parent story. When this happens, mention the parent story ID inside the plan file and the branch name when practical (e.g. abc-502-followup-report-cleanup.md paired with branch abc-502-followup-report-cleanup).

Rename the Claude Code session to match the slug

As soon as the canonical slug is locked in (after the issue title is agreed and the branch name is known, but before creating files or the branch), rename the current Claude Code session to match. This makes the session discoverable later from a glance at the session list.

The Claude Code built-in /rename slash command is a UI command that cannot be invoked from a tool call. So: prompt the user to run /rename themselves. Pick the slug:

  • If a tracker issue exists (or has just been created) and the canonical git branch slug has been chosen, use <branch-slug> (e.g. abc-525-fix-pdf-uploads, 525-fix-pdf-uploads).
  • If no issue exists and none will be created (planning-only exercise, exploratory spike, pure-refactor plan with no tracker), use the same slug that will be the plan filename under .claude/plans/.

Then ask the user to run:

/rename <slug>

Do this exactly once per plan, immediately after the slug is chosen. If the slug changes later (e.g., a parent-story plan spawns a follow-up plan with its own slug per the deviation rule above), ask the user to /rename again with the new slug.


Phase: create

Step 1 -- Interview for the issue

Ask the user:

  1. Is there an existing tracker issue (Linear, Shortcut, or GitHub)? If yes, get the URL or ID.
  2. Is this work going to use the canonical tracker title for the plan file and branch (the default), or is this a follow-up plan that needs its own deviating name?
  3. Are we already on a clean copy of the right branch and good to go, or do we need to create/checkout/rebase first? (Before answering this from the trunk's state, resolve the project remote and fetch it -- see Step 3.)

Pull the suggested branch name from Linear's "Copy git branch name" action or the Shortcut equivalent; for GitHub Issues, compose the NNN-title-slug form per the canonical-title section -- do NOT run gh issue develop during the interview, since it creates the remote branch immediately and the title may still be renamed. Do not invent a slug from scratch when the issue tracker already has one.

If the user has no issue yet, offer to create one in the project's tracker before going further. Do not proceed without an issue to anchor the plan.

When creating any Linear issue, ask the user which project it belongs to rather than guessing from a heuristic (e.g. "bug -> maintenance"). The same work can be classified as contracted maintenance or as billable requested-additional-work depending on context only the user knows, and that choice has billing consequences. Offer the likely candidates (e.g. the maintenance project vs. the requested-additional-work project) and let the user pick; do not silently default. This applies to spun-off / follow-up issues too.

When creating a GitHub issue (gh issue create), there is no required project or team field; labels and milestone fill that interview slot. Offer the repo's existing labels and milestones rather than inventing new ones, and skip cleanly when the repo uses neither.

If the existing issue title is overly long or sentence-shaped, propose a rename now so the plan filename and branch can share the base name.

Step 2 -- Read the issue

  • Linear URL or identifier (linear.app/... or ABC-NNN): use the linear CLI bundled with this plugin (on PATH when the plugin is installed; requires Ruby and a LINEAR_API_TOKEN env var) -- not raw curl.
    linear get ABC-NNN --full      # title, state, description, project, comments, parent, children
    linear comments ABC-NNN        # just the comment thread
    
    Skim the description, comments, parent, and children for context. If the user invoked /plan-issue ABC-NNN (or just ABC-NNN), run linear get ABC-NNN --full as the first action of this step. If a subcommand is missing, suggest an improvement PR to the plugin repo.
  • Shortcut URL (app.shortcut.com/...): use the Shortcut REST API or ask the user for the story body if no token is configured. Refer to Shortcut stories as "Shortcut", never "Linear".
  • GitHub issue URL or #NNN (github.com/<org>/<repo>/issues/NNN): use the gh CLI.
    gh issue view NNN --json title,body,state,labels,comments
    
    A bare #NNN means GitHub Issues only when the repo actually tracks work there -- issues enabled on the repo, the project's CLAUDE.md naming GitHub as the tracker, or existing plan-file slugs carrying the NNN- prefix. If the project could plausibly have two trackers in play, ask rather than assume. Refer to GitHub issues as "issues", never "stories".

Read the title, description, comments, and any linked parent or sibling issues. Note the current workflow state.

Step 3 -- Research the code

Fetch before reasoning about remote state. Before drawing any conclusion that depends on what is on a remote branch -- "is ABC-NNN merged?", "does the trunk already contain X?", "where should this branch from?", "how many commits is this branch ahead/behind?" -- resolve the project remote and its trunk, then fetch, as the first action of this step. Neither half of origin/main is a constant: the remote name is a per-clone fact, and the default branch is the project's choice.

  • The remote is the one this work's pull request will target: the sole remote in a single-remote clone, whatever it is called; upstream over origin when both exist (a fork clone -- origin is the fork, whose trunk goes stale); origin whenever it exists without upstream, however many other remotes sit alongside it. A recorded gh repo set-default answer (git config --get-regexp 'gh-resolved') and the user's word both beat that name heuristic. When no rule applies (several remotes, none named origin; no remotes at all), ask the user.
  • The trunk is read, never assumed. Refresh the symref first with git remote set-head <remote> --auto -- fetch never updates it, so after a default-branch rename it stays stale with no error -- then read it: git symbolic-ref --short refs/remotes/<remote>/HEAD. That prints <remote>/<branch>; <trunk> is the bare branch name after the <remote>/ prefix (main, not origin/main). When the symref cannot be resolved (normal for a hand-added remote), git ls-remote --symref <remote> HEAD names the default branch in its ref: refs/heads/<branch> line -- again take the bare <branch>. Both the refresh and ls-remote need the network; offline, proceed with the existing symref when it exists.

Then git fetch <remote> -- the whole remote, not just the trunk: "is ABC-NNN merged?" and "ahead/behind?" read other branches' remote-tracking refs too, and a trunk-only fetch leaves those stale. The local remote-tracking refs and the session-start git snapshot are both point-in-time and go stale the moment someone else pushes; trusting them without fetching has produced confidently-wrong branching decisions. After fetching, query <remote>/<trunk> (e.g. git show <remote>/<trunk>:path, git rev-list --count HEAD..<remote>/<trunk>), never a bare local ref you have not refreshed this session. The targeted-specs skill (bundled in this plugin) carries the canonical, fuller statement of this resolution rule (offline behavior, rename edge cases); when the two ever disagree on how to identify the remote or the trunk, defer to it. The whole-remote fetch above is deliberately this skill's own -- its questions read more refs than the trunk.

Then learn what you can from the repo:

  • Find the models, controllers, services, jobs, views, and specs that the issue touches.
  • Identify existing patterns for similar work (look at recent merged PRs on similar features if possible).
  • Note where tests for affected code already live.
  • For bug fixes, locate the suspected source and any related code paths.

For broad exploration use the Explore agent; for targeted lookups use grep/find directly.

Step 4 -- Frame the end-user story first (before implementation)

Before discussing how to build anything, pin down what changes for the end user -- the person or client who will actually use the result. State the change as a user story plus acceptance criteria written entirely in terms of what the user sees, does, inputs, and gets back. Keep implementation out of it: no models, no controllers, no "we'll add a column." If a non-technical stakeholder (the client stakeholder who requested it) read this story, it should make plain sense and they should recognize their own request in it. This step exists because it is easy to leap straight to a mechanism and end up with a technically correct change that does not match what the user actually wanted; the story is the contract the implementation then serves.

Produce:

  1. A user story in the form "As a <role>, I want <capability>, so that <outcome>." Use the real persona (an admin user, a customer user, ...), not a generic "user" when a specific role applies.
  2. Acceptance criteria as a short walkthrough of the interaction: what the user sees today, the action they take, the inputs they provide, and the observable outputs -- including the downstream artifacts they rely on (the receipt, the report, the email, the next screen), not just the screen in front of them. "Done" is when every line of this is observably true from the user's seat.
  3. The user-facing decisions that define "done" -- the product/UX choices the story cannot be built without, surfaced as questions in end-user terms and explicitly separated from implementation questions. Typical shapes: does the affected item disappear after the action (consumption / idempotency)? whole-amount or partial? what is the guard when an input is out of range? which screens carry the new affordance? what does the user see on failure? Propose a sensible default for each, and say which genuinely need the requesting stakeholder's input versus which you can settle from existing behavior.

Confirm this story with the user (and, where the answer is theirs to give, flag what should be confirmed with the requesting client or stakeholder) before moving to the implementation interview.

Add the agreed user story and acceptance criteria to the tracker issue, and carry them into the plan file (Step 6) so the end-user definition of done travels with the work. On GitHub, any body edit is a read-modify-write (gh issue edit --body-file REPLACES the entire issue body), so never hand-roll it: use the gh-issue-sync CLI bundled with this plugin (on PATH when the plugin is installed; requires Ruby, like the linear CLI):

gh-issue-sync section NNN --file <md> --slug user-story

The helper owns the mechanics every GitHub body edit in this skill needs: it fetches fresh immediately before writing (GitHub has no compare-and-swap and body edits notify no one, so writing from a stale copy silently clobbers a concurrent human edit), keeps the content in its own marker-delimited section separated by a blank line, round-trips the body exactly, normalizes CRLF from web-UI edits, and enforces GitHub's body-length limit. The write is idempotent: re-running replaces the section in place, so amending the story is just editing the file and re-running the command. The file is the source of truth -- hand-edits inside the marked section are overwritten. To remove a section outright, run the same command with --delete in place of --file. (Transition note: a block written by the long-retired append subcommand is marker-less -- --delete has no slug to target it by, so delete it from the body by hand once before the first section run, or it will be duplicated.) In the multi-plan deviation case (one issue, several plans), prefix the slug with the plan slug (e.g. --slug <plan-slug>-user-story) and make sure the file's own heading names the plan -- the markers are invisible on GitHub, so only the content distinguishes the sections for a reader. For the rare body edit the helper doesn't cover (editing body text outside any section, removing a marker-less append-era block, or repairing marker damage that section --delete itself refuses to touch -- orphaned, duplicated, or interleaved marker pairs, an out-of-charset slug), fetch fresh with gh issue view NNN --json body parsed as JSON (never -q .body, which grows a trailing newline per cycle), edit, and write back immediately. Without write access to the repo, post the addition as a comment via gh issue comment instead (comments are one-shot: a re-posted comment duplicates, it never updates in place).

When this step is light or N/A: for changes with no end-user- observable surface -- pure refactors, infra, dev tooling, internal performance -- the "user" is the developer or operator. State the story in their terms (what an engineer or operator can now do, or no longer has to worry about) in a sentence or two, or note explicitly that the change is internal with no user-facing surface, and move on. Do not manufacture ceremony where there is no user-visible change.

Step 5 -- Interview the user in depth (implementation)

With the end-user story from Step 4 agreed, dig into how to build it. Ask non-obvious questions across these dimensions, one or two at a time (do not blast a numbered list of 12 questions in one message):

  • Technical implementation -- approach, data model changes, service boundaries, async vs sync, transactional boundaries.
  • UI / UX -- form layout, empty states, error states, loading states, permission visibility differences.
  • Edge cases & failure modes -- what happens on partial failure, retries, race conditions, concurrent edits.
  • Authorization -- which roles can do what; double-check Pundit policies for the affected resources.
  • Backwards compatibility / migration -- existing rows, in-flight jobs, deployed sessions, partial rollouts.
  • Tradeoffs -- what alternatives did you consider, why this one.
  • Out of scope -- what is NOT being done here, to prevent scope drift later.

Strongly prefer the design that is best to live with for years -- then get creative about reaching it cheaply. When the work has more than one viable shape -- a new model vs. extending an existing one, a stored field vs. a derived one, unifying two concepts vs. keeping them separate -- lean hard toward the option that is cleanest to maintain over the long run, even when it is the bigger diff. Heuristics for "cleaner to maintain": a preserved, locally-true invariant beats an overloaded one; a value derived from a single source of truth beats a duplicated field that can drift; modeling distinct concepts distinctly beats conflating them behind a sign or a flag.

But naming the long-term winner is only half the job. Also tell the user, plainly, the implementation risk, cost, and blast radius of that option. The user will almost always still choose the best end-state -- but knowing its cost is what lets them make a creative call, so don't hide it to make the recommendation look cleaner. The highest-value move is usually to brainstorm a lower-risk path to the same long-term destination: an incremental migration, a transitional coexistence that cleanly collapses once a backfill runs, a clean seam a later effort can converge onto, or phasing the work across PRs. High cost or risk is a reason to get creative about the route, not a reason to settle for a worse end-state.

Resist speculative generalization in the other direction, too -- unify or abstract only when a real, present need demands it, not for a future that may never arrive. And when the user pushes on "which is cleaner to maintain?", answer honestly even if it argues against the option you first proposed.

Skip questions whose answers are obvious from the issue or the code. Continue until the picture is clear; do not stop after a single round.

When the interview is done, write the resulting specification into the tracker issue description (on GitHub, via the bundled helper -- gh-issue-sync section NNN --file <md> --slug spec, or --slug <plan-slug>-spec in the multi-plan deviation case, per Step 4), so the source of truth for what we agreed on lives there too.

Step 6 -- Propose the plan

Draft a plan with:

  1. Why + user story -- a single opening paragraph that states the user-visible motivation, paired with the end-user story and acceptance criteria agreed in Step 4 (verbatim or lightly edited). This is the most easily-overlooked section when heads-down on details, so lead with it: the plan should open with the user-facing definition of done, and it must make sense to someone who has not read the tracker issue.

  2. Approach -- the strategy in plain language, not a file list.

  3. Optional up-front refactor -- if the existing code is shaped awkwardly for the change, propose a refactoring to do first (either in the same PR, or as its own PR before this one). Frame it as optional and let the user decide.

  4. TDD-first steps for any production code change: write the failing spec before the production change. (Skip the failing spec only for config-only changes that aren't covered by regression tests.)

  5. Ripple work:

    • For new features: explicitly list the parts of the app that need to be updated to account for the new feature (other controllers, reports, exports, mailers, JS, fixtures, seeds).
    • For bug fixes: include a step to search for sibling bugs of the same shape elsewhere in the codebase, beyond just the reported one.
  6. Out of scope -- a short list of things not being done, mirroring the interview.

  7. To-dos as numbered - [ ] checkboxes, in execution order, each carrying an explicit stable number the user can see and refer to, e.g. - [ ] **1.** Identify the example payment. The numbers are permanent handles: never renumber when an item completes (it just becomes - [x] **1.**), and append newly-discovered work as the next unused number rather than reflowing the list. This is what lets the user say "do task 13" and lets you cite "Task 13" against something they can actually find in the file. The standard tail of the list -- the "ship the work" steps -- runs in the order below, which exists for a wall-clock reason. Several items are conditional -- step 2 can be opted out of, step 4 collapses to nothing on a tracker with no review state, step 5 applies only to stakeholder-visible changes, and step 6 is absent in Deploy-on-Merge Mode -- but their relative order does not change. The numbers below index this list, not the plan's to-dos: a tail that omits an item still numbers the plan's to-dos consecutively, leaving no gap.

    1. Run the project's full lint+test suite once, capturing complete output to a uniquely-named log under /tmp to grep for follow-ups -- never re-run just to re-read output (use the project's suite-runner skill if it provides one; one that already captures this way satisfies the capture rule). This is the slow step in Full Verification Mode. In Targeted Spec Verification Mode (declared in the project's CLAUDE.md or rules files), run the targeted-specs skill (bundled in this plugin) instead and act on its verdict line; if the final per-to-do checkpoint already ran on a tree unchanged since (apart from plan and doc edits), its verdict stands -- don't repeat the run. The capture rule and both modes are defined in the clean-and-green guidance, where a team imports it.

    2. Run the gauntlet skill (bundled in this plugin) via the Skill tool. The gauntlet is a multi-front quality pass that requires clean-and-green as its starting state -- it dispatches parallel sub-agents to audit cruft, idioms, RSpec quality, validation bypass, and security, then triages findings into a punch list. Run it after step 1 because it expects specs and lint to already pass; run it before step 3 because any findings the user accepts should be in the PR before it leaves draft for human review. Skip only if the user explicitly opts out for this issue.

    3. Open the draft PR. Its body leads with the plan's opening Why paragraph (just the motivation, not the paired user story and acceptance criteria), before any What/mechanism content; the pull-requests guidance, where the team imports it, carries the full rule. Opening the PR kicks off GitHub CI checks and any automated reviewer (e.g. claude-code-action) that runs on ready-for-review or synchronize events. On GitHub-tracked repos, put Closes #NNN (or Fixes #NNN) in the PR body when this PR fully resolves the issue; when the issue outlives the PR (a multi-PR epic), use a plain #NNN reference instead. Two traps: auto-close fires at merge to the default branch, which on a project outside Deploy-on-Merge Mode is BEFORE any production deploy; and a plain reference is NOT enough to prevent it when the branch came from gh issue develop -- a PR from a linked branch auto-closes the issue at merge with no keyword at all. A team that wants the issue open past merge (a project outside Deploy-on-Merge Mode, or any multi-PR epic) should disable the repo setting "Auto-close issues with merged linked pull requests" (Settings > General > Issues), or unlink the PR from the issue's Development section before merging, and close the issue manually in the finish phase.

      That setting is repo-wide, so it belongs to the team's predominant issue shape rather than to any one issue; the per-issue lever is unlinking. In Deploy-on-Merge Mode auto-close lands a single-PR issue in its terminal state at close to the right moment, which is why such a team usually leaves it on -- but name the tradeoff rather than treating it as free. It closes the issue before the finish phase can STOP on unfinished work, so a story with an outstanding to-do ends up closed with no board signal; and it permanently spends one of the three signals housekeeping uses to detect a half-run pass. A multi-PR epic on such a team still needs unlinking, or its first merge closes it.

    4. Hand the PR off for review and merge, then move the issue to PR Review. The hand-off happens on every tracker, including the ones where the state move below is a no-op: say plainly what the PR is waiting on rather than offering to merge it.

      Who merges depends on a declaration, so establish it from the project's checked-in files here and say which answer came back. By default the merge and the review are both the human's, and arming auto-merge is merging -- the pull-requests guidance, where the team imports it, owns that rule. A project that has declared Session-Merge Mode has authorized the merge itself; recognize only an explicit declaration and never infer it from the repo's shape, exactly as with Deploy-on-Merge Mode above. On a project that declares both, merging is deploying, which is the larger claim of the two. In Targeted Spec Verification Mode, CI's full-suite run on the PR is the full gate: confirm it is green before marking the PR ready for review or moving the issue, and own a red run exactly like a red local full run (see the clean-and-green guidance). A MISSING full-suite CI run -- a new repo, a deleted or narrowed job -- is owned the same way: fall back to a local full gate rather than proceeding on no evidence. (GitHub Issues has no such state. If the repo visibly runs status off labels or a Projects board, update those the same way record Step 5 chose to; otherwise the linked PR going ready-for-review is the visible signal and there is nothing extra to do.)

    5. Stakeholder change-highlights (conditional). When the change alters something a client stakeholder visibly relies on -- a report, receipt, statement, mailer, or screen -- and the project provides a change-highlights-style skill for stakeholder-facing before/after summaries, add a step to build one and communicate it to the stakeholder so they can review each change visually with a clear, explained example. Skip only for purely internal changes with no stakeholder-visible surface (refactors, infra, dev tooling).

    6. Confirm shipped (PR merged, live in production). The finish phase's entry gate, listed as its own to-do so post-ship work is visibly pending instead of remembered informally. Use that short wording verbatim. Never auto-advance into this item -- it waits for the user to trigger the finish phase, whose Step 1 checks are what complete it; do not run separate merge or deploy checks just for the checkbox.

      Omit this item entirely in Deploy-on-Merge Mode. There the merge that step 4 hands off is the deploy, so the item could never be independently true or false -- it would only restate step 4. The tail then ends with the run-housekeeping item below, which still keeps the post-ship work visibly pending.

    7. Run finished-issue-housekeeping (finish phase, user-triggered). The final ship-tail entry; use that wording verbatim. Newly-discovered work may append after it by number -- its role, not its list position, is what marks it. The housekeeping pass checks off this item and the confirm-shipped item above when the plan carries one.

      If the issue auto-closes at merge (see the traps in step 3), it closes with the tail's remaining items still unchecked -- correct, not a bug; the finish phase's reconcile edits the closed issue's body later.

Present the plan inline for the user to discuss and refine. Do not write it to disk yet -- that happens in the record phase.


Phase: challenge

The goal of this phase is an honest second opinion on the current plan, not a rationalization of it.

Strongly prefer dispatching this to a fresh subagent so the critique is not anchored to the same conversation that produced the plan. Use Agent with subagent_type: "general-purpose" (or Plan), and pass the plan text plus the issue description in the prompt. Tell the agent to read it cold and report back.

The critique must consider:

  • Is the plan creating unnecessary complexity? Is there a simpler, more elegant path?
  • Where does it deviate from team or community conventions for this stack (Rails idioms, Pundit policies, RSpec / shoulda-matchers / FactoryBot patterns, project conventions in CLAUDE.md)?
  • Does it sufficiently emphasize a TDD spec that will prove the fix or feature continues to work into the future? A spec that only passes incidentally is not enough.
  • For bug fixes: does it search for sibling bugs of the same shape?
  • For features: does it cover the ripple effects across affected parts of the app?
  • Are there assumptions baked in that we should validate before building?

Value code quality and ease of maintenance over saving development time. Only propose improvements -- do not assume the plan will change. The user is asking to stress-test the plan, not to redo it.


Phase: record

Step 1 -- Locate the plans directory

The plans directory convention is <project>/.claude/plans/. In any established project it already exists, so do NOT probe for it with ls/test/mkdir -- that check is pure waste on every plan after the first. Just write the plan file directly in Step 3. Only if that Write fails because the directory is genuinely missing (a brand-new repo that has never had a plan) do you mkdir -p .claude/plans once and retry the Write. This pushes the one-time setup cost onto the first-ever plan in a fresh repo and keeps the common path zero-overhead.

Step 2 -- Pick the filename

By default the plan filename matches the canonical tracker title slug -- the same slug used for the git branch. If Linear's "Copy git branch name" produces abc-525-fix-pdf-uploads, the plan file is abc-525-fix-pdf-uploads.md; if gh issue develop named the branch 525-fix-pdf-uploads, the plan file is 525-fix-pdf-uploads.md.

The only time the plan filename should diverge is when this plan is one of multiple spawned by a single story (a follow-up cluster, an up-front refactor split out from the main work). In that case use a descriptive slug that still references the parent issue ID, e.g. abc-502-followup-report-cleanup.md. The git branch should share the plan's slug, not the parent story's title.

Do not restate the entire issue title in the filename. Aim for ~40 characters or fewer.

Step 3 -- Write the plan verbatim

Write the agreed plan -- including the Why paragraph, approach, to-dos, and any agreed refinements from the challenge phase -- to that file. Use - [ ] for incomplete to-dos.

Step 4 -- Load the to-dos into the Task tracker

Immediately after writing the plan file, mirror its to-do list into the Claude Code Task tracker via TaskCreate. This gives the user a live, toggleable view of progress (Ctrl-T) alongside the markdown plan file.

  • Create one task per - [ ] checkbox in the plan, in the same execution order. Keep task titles short and faithful to the plan's wording -- do not paraphrase, summarize, or merge items.

  • One task = one verifiable deliverable. Never merge distinct steps into one task, even when they always run together. Smell test: if a task title needs "and", "+", "then", or a comma to join separate deliverables, split it. "Full test suite and gauntlet and draft PR" is three tasks, not one -- each is independently substantial and independently checkable.

  • The trailing ship-the-work steps are each their own task, not a single bundled one: the suite gate (full or targeted per the project's mode), the gauntlet skill, open draft PR, move to PR Review, and run finished-issue-housekeeping are separate tasks so the toggle view shows the whole arc and each completes on its own. Outside Deploy-on-Merge Mode the confirm-shipped item is one too. Mirror whatever tail the plan carries; do not add a task the plan omits. The finish-tail tasks stay pending until the user triggers the finish phase; the housekeeping pass completes whichever of them the plan carries, mirroring its plan-file flips via TaskUpdate before its task-cleanup step deletes them. When the change is stakeholder-visible (a report, receipt, statement, mailer, or screen a client stakeholder relies on) and the project provides a change-highlights-style skill, add a further own task for the before/after summary and its communication to the stakeholder, ordered before the finish-tail items as in the ship tail.

  • The markdown plan file remains the source of truth for the why and the approach. The Task tracker is a fast-access view of the what's next. Keep them in sync: when a to-do is checked off in the markdown, mark the corresponding task complete via TaskUpdate; when new work is discovered, append it to both (and, on GitHub-tracked repos, to the issue-body checklist at its next sync -- next bullet).

  • GitHub-tracked repos get a third surface: the issue-body checklist. Mirror the plan's numbered to-dos into the issue body with the bundled helper:

    gh-issue-sync checklist NNN --plan .claude/plans/<slug>.md
    

    Progress becomes publicly visible on the issue, with GitHub's own "x of y tasks" progress bar. The helper regenerates a marker-delimited section from the plan file (creating it when absent, adopting a pre-helper ## To-dos heading rather than duplicating it), so every sync covers both directions of change at once: ticked boxes land AND newly-discovered to-dos appear, keeping their plan numbers. When several plans share one issue (the deviation case above), each plan file's basename (minus .md) keys its own section; pass --heading <slug> to also name the plan in the visible heading. A box someone ticks directly on GitHub that the plan lacks gets overwritten with a warning -- absorb real ticks into the plan file, the single source of truth. The finish-tail items are the exception: a premature GitHub-side tick on one of those is surfaced to the user ("the finish phase completes this one"), never absorbed. Without write access to the repo, skip this surface entirely -- the comment fallback suits a one-shot addition, not a per-commit sync, and the plan file and Task tracker remain the two surfaces. The checklist is a projection, not a peer: it syncs at each commit/push boundary (Step 6), not per to-do. This bullet owns the cadence. The finish phase's reconcile (gh-issue-sync reconcile) is stronger than a sync: it refuses to run while the plan still has bare unchecked - [ ] items, so the checklist ends as an exact mirror of the finalized plan (every item - [x] **N.** <text>, deferred ones with a trailing (deferred to #NNN) note; the finish-tail items are checked off by the housekeeping pass itself) -- the public surface may drift mid-flight but never ends stale.

  • Always show the task number next to each task whenever you surface the task list to the user (e.g. 1. ..., 2. ...). The user refers to tasks by number, so a bare bulleted list is not enough -- every rendered task list must carry its numbers so they map back to the tracker.

  • When you reference a single task in prose, cite both its number and its short name -- "Task 13 (Move ABC-616 to PR Review)", never a bare "Task 13". The bare number forces the user to go count; the number plus name is unambiguous whether they are looking at the tracker, the plan file, or just your message. Keep the numbers identical across the plan markdown, the Task tracker, and your prose.

Step 5 -- Move the issue to In Progress (assigned)

Before starting the work, transition the tracker issue out of Todo and into the active state -- and assign it in the same call. A started state (In Progress) without an owner is the error this step exists to prevent.

For Linear:

linear update ABC-NNN --state "In Progress" --assignee me

--assignee me resolves to the token's own user; pass an explicit email (e.g. --assignee [email protected]) if the work belongs to someone else. The CLI rejects a move into a started state with no assignee, so never strip the --assignee flag to get past that error -- supply the owner instead.

For Shortcut, use the configured Shortcut MCP server's workflow-state update if one is available; otherwise ask the user to move and assign the story in the Shortcut UI.

For GitHub Issues there is no started state to move to -- an issue is only open or closed. Assign instead:

gh issue edit NNN --add-assignee "@me"

If the repo visibly runs status off labels (in progress, pr review, ...) or a Projects board, ask the user which to update rather than silently doing nothing; do not invent labels the repo does not already use.

Skip this step only for planning-only exercises with no tracker issue.

Step 6 -- Show the to-do list and start

Surface the plan's to-do list and start working through it. After each phase or to-do (your judgment on grouping):

  1. Run rubocop (or standardrb) and fix all failures.
  2. If production code changed: run the project's full lint+test suite once, capturing complete output to a uniquely-named log under /tmp to grep for follow-ups -- never re-run just to re-read output, per the clean-and-green guidance's capture rule, where a team imports it (use the project's suite-runner skill if it provides one; one that already captures this way satisfies the rule; in Targeted Spec Verification Mode, a run of the targeted-specs skill (bundled in this plugin) stands in here -- act on its verdict line). If only tests changed: just the affected specs are fine. You may bundle multiple to-dos into a single suite run (full or targeted) when the time saved is worth the lower granularity.
  3. Commit and push that work. Default to the Conventional Commits shape with a Title Case outcome description (type(scope): Title Case Outcome Description), unless the project's conventions say otherwise.
  4. Update the plan markdown: change - [ ] to - [x] for completed items, add any newly-discovered work. Mirror the change in the Task tracker via TaskUpdate so the Ctrl-T view stays accurate. On a GitHub-tracked repo, also sync the issue-body checklist now (record Step 4 owns the cadence): gh-issue-sync checklist NNN --plan .claude/plans/<slug>.md.
  5. Show the user the updated to-do list, with each task's number shown next to it (the user refers to tasks by number), unless there's a clear reason not to, e.g. a single-line trailing checkoff.
  6. Tell the user the current PR size in lines changed and how far it is over (or under) the 400-line easy-review threshold.

If at any point the PR size or scope is drifting, surface it and offer to split.


Phase: finish

The work isn't done when the code ships -- it's done when the user has confirmed nothing is outstanding. On most projects shipping means production is running the merged code; in Deploy-on-Merge Mode the merge itself is that event. Either way, this phase's role is to gate the handoff to the housekeeping skill: confirm preconditions, then delegate. The finished-issue-housekeeping skill (bundled in this plugin) owns the actual cleanup work -- see that skill's steps.

Do NOT auto-advance into this phase from record. The gap between "shipped" and "we're sure nothing else is needed" is real, and no mode closes it -- wait for the user to ask. On a project outside Deploy-on-Merge Mode there is a second gap to wait out first, between "PR merged to the default branch" and "shipped to production".

Step 1 -- Confirm preconditions

First establish whether the project is in Deploy-on-Merge Mode, by reading its checked-in CLAUDE.md and rules files now -- check 2 depends on the answer, and this phase is a standalone entry point, so nothing earlier in this session necessarily established it. A conversational memory of the mode does not count. The Deploy-on-Merge Mode section above carries the recognition floor, the never-infer rule, and the default for a project that declares nothing.

Then walk through these checks (the housekeeping skill will re-verify, but catching a "no" here lets you exit early before invoking it):

  1. PR is merged to the default branch. Verify with gh pr view <PR#> --json state,mergedAt,mergeCommit (or whichever forge the project uses).

  2. The merged code is live in production. Before taking any shortcut, confirm the premise: nothing after the merge can still fail or be skipped. A project whose merge triggers a deploy that can go red does not qualify however its rules read.

    With that premise confirmed, in Deploy-on-Merge Mode check 1 satisfies this one, because the merge is the deploy. Otherwise it is a project-specific check:

    • Heroku-deployed apps: heroku releases -a <prod-app> and confirm a release after the merge commit.
    • Other deploy targets: ask the user, or look at the deploy log / dashboard.
    • When in doubt, ask the user to confirm rather than guess.
  3. The user explicitly confirms the plan is wrapped up.

If any of these is "no", stop. The branch may still be needed -- for a hotfix on top of the same code, for cherry-picking, for a follow-up PR that branches from it. Don't proceed with cleanup on assumption.

Step 2 -- Invoke the finished-issue-housekeeping skill

Invoke the finished-issue-housekeeping skill (bundled in this plugin) via the Skill tool. It will:

  • Re-verify preconditions (idempotent with Step 1).
  • Classify each unchecked plan-file item and STOP if any genuinely unfinished work surfaces (the plan's own finish-tail items don't trigger STOP -- the pass completes them itself).
  • Add the Shipment section to the plan file.
  • On GitHub-tracked repos, reconcile the issue-body checklist (gh-issue-sync reconcile) so the public surface does not end stale.
  • Delete the local working branch with -d safety.
  • Add a Done entry to MEMORY.md and remove the issue from Active Work if it was there.
  • Move the tracker issue to its terminal Done state (on GitHub, verify the auto-close or close manually -- outside Deploy-on-Merge Mode the manual close happens here; in the mode the auto-close at merge has usually already done it, so this is normally a verification).
  • Ask whether anything is worth saving as a tech-note memory or a new skill, and create it if so.
  • Verify sibling-audit follow-ups got filed.
  • Clear completed tasks from the conversation task list.
  • Run a permission-prompt sweep (via the /fewer-permission-prompts built-in, when available), gating any settings-file write on the user's approval.

Relay the housekeeping skill's summary to the user when it finishes.


Project-specific overrides

Projects may place a .claude/skills/plan-issue/SKILL.md in their own repo to override this global default (for example, to specify a different plans directory, a different test command, or a different branch-naming convention). When such a file exists, it wins entirely -- do not try to merge.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.