| name | issue-to-pr |
| description | Use when implementing a fix for a known GitHub issue end-to-end on the Breeze repo — "work on issue |
Issue → PR
Overview
Execution runbook that carries one already-chosen GitHub issue from claim to
a reviewed, open PR. Triage already happened (see gh-queue); this skill runs
after the decision to work an issue is made.
Repo: LanternOps/breeze. Two hard boundaries: the worker never merges and
the worker never closes the issue — those stay the user's call.
When to use vs. not
- Use: "fix #1234", "take #1234 to a PR", an
issue-fixer agent picking up a number.
- Don't use for triage ("what's waiting on me", "any new issues") → that's
gh-queue.
- Don't redefine etiquette — comment style, never-self-close, and lifecycle
live in the
github-issues skill. Follow it; don't restate it.
The lifecycle
digraph issue_to_pr {
"Read full issue + comments" [shape=box];
"Eligible?" [shape=diamond];
"ABORT — report back" [shape=box];
"Assign to self" [shape=box];
"Worktree off fresh main" [shape=box];
"Fix (debug/brainstorm)" [shape=box];
"Verify: tests + typecheck" [shape=box];
"Commit / push / PR (Closes #N)" [shape=box];
"/pr-review-toolkit:review-pr" [shape=box];
"Findings?" [shape=diamond];
"Comment: what the review ran" [shape=box];
"STOP — hand off (no merge, no close)" [shape=doublecircle];
"Read full issue + comments" -> "Eligible?";
"Eligible?" -> "ABORT — report back" [label="no"];
"Eligible?" -> "Assign to self" [label="yes"];
"Assign to self" -> "Worktree off fresh main";
"Worktree off fresh main" -> "Fix (debug/brainstorm)";
"Fix (debug/brainstorm)" -> "Verify: tests + typecheck";
"Verify: tests + typecheck" -> "Commit / push / PR (Closes #N)";
"Commit / push / PR (Closes #N)" -> "/pr-review-toolkit:review-pr";
"/pr-review-toolkit:review-pr" -> "Findings?";
"Findings?" -> "Fix (debug/brainstorm)" [label="yes, address"];
"Findings?" -> "Comment: what the review ran" [label="clean"];
"Comment: what the review ran" -> "STOP — hand off (no merge, no close)";
}
0. Select an issue (when you weren't handed a number)
If invoked with a specific issue number, skip to step 1. If asked to "find
something to work on" / "grab an issue off the backlog", select one yourself:
gh issue list --repo LanternOps/breeze --state open --limit 60 \
--json number,title,assignees,labels,createdAt,comments
Rank candidates, best first. Prefer an issue that is:
- Unassigned (or assigned to you — see the note on "you" in step 1).
- Actionable without design — a clear repro, root cause, or file pointers in
the body/comments. A precise root-cause comment from a maintainer is gold.
- Bounded — one component, a contained fix.
Skip (don't even open a worktree):
- Anything labeled
tracking/epic/meta, needs-design/question/discussion/RFC.
- Infra firefighting (
ci-red, broad "X is broken on main") unless explicitly asked.
- Anything that needs a product/pricing/policy decision.
Then run the step 1 guard on your top candidate — that's the real gate
(it catches already-shipped fixes and in-flight PRs that ranking can't see). If
the top candidate aborts on the guard, move to the next candidate; repeat until
one passes or the list is exhausted. Report which issues you considered and
why you skipped each — don't silently pick one and hide the rest.
When selecting several to fan out, cap the count and state the cap.
1. Read & guard (do this before touching anything)
gh issue view N --repo LanternOps/breeze --comments \
--json number,title,body,state,assignees,labels,comments
gh pr list --repo LanternOps/breeze --state all --search "N in:body" \
--json number,title,state,headRefName,mergedAt
Read the whole issue and every comment. ABORT and report back (do
not start work) if any of these is true:
- Issue is closed.
- Already assigned to someone else (not you, not unassigned). "You" is the
operator's GitHub account — the
@me login, resolved with
gh api user --jq .login — not a separate agent identity. An issue
assigned to that account is "assigned to you" and is eligible (work it);
only an issue assigned to a different login is "someone else" → abort. If
you're a subagent, you have no GitHub identity of your own, so never read the
operator's own assigned issues as belonging to a third party.
- A PR already references it. An open PR → work is in flight, don't
duplicate. A merged/closed PR (or a member comment / commit on
main
saying "fixed in #NNN" / "merged to main") → the fix likely already
shipped and the issue is open only awaiting reporter confirmation. Verify
against origin/main before concluding either way — a gh pr list --state open
miss does NOT mean no fix exists. Don't open a duplicate no-op PR.
- It's too ambiguous or too large to fix without design — needs a spec or a
product decision first. Say so; don't guess a fix.
Aborting is a success, not a failure. Report why so the orchestrator/user can
decide. Guessing past a guard wastes a worktree and a review cycle.
2. Claim
gh issue edit N --repo LanternOps/breeze --add-assignee @me
3. Isolated worktree
REQUIRED SUB-SKILL: Use superpowers:using-git-worktrees. The main working
copy at /Users/toddhebebrand/breeze is shared across sessions and drifts —
verify your base is fresh main before branching (a stale base has nearly
shipped dozens of unrelated commits in a PR).
Spawn-location trap (read this): When an orchestrator fans you out, your
CWD is very likely already inside an existing worktree under
.claude/worktrees/ that belongs to a different task. That is NOT your
workspace. Do not edit there, and do not reach back into the shared
/Users/toddhebebrand/breeze checkout (it may be on a stale branch). Always
git fetch origin main, then create your own new worktree off
origin/main, cd into it, and confirm git rev-parse HEAD equals
origin/main before you branch. Editing the spawn worktree or the shared
checkout has silently reverted other in-flight PRs' files.
Branch naming (AGENTS.md — no codex/ / claude/ prefixes):
fix/N-short-slug, feat/N-short-slug, docs/short-slug, chore/short-slug.
Fresh worktrees need pnpm install and the gitignored .env.test symlink —
without it RLS forge tests pass vacuously on a BYPASSRLS connection. Prefix
node-pinned commands: PATH=$HOME/.nvm/versions/node/v22.20.0/bin:$PATH.
4. Fix
- Bug → REQUIRED:
superpowers:systematic-debugging (find root cause before patching).
- Feature/behavior change →
superpowers:brainstorming first if the issue left design open.
- Touching tenant tables / migrations → follow the RLS shapes + migration rules
in
CLAUDE.md (policies in the same migration, idempotent, never edit a
shipped migration). Touching the Go agent → write to agent/, not apps/agent/.
5. Verify (evidence before "done")
REQUIRED SUB-SKILL: breeze-testing for what to cover.
- Run the affected test files single-fork (the full API suite is flaky in
parallel — don't trust or trigger a full red run):
pnpm exec vitest run <path>.
- Type-check touched areas:
npx tsc --noEmit (and astro check if .astro
changed — plain tsc skips Astro files).
- Go changes:
cd agent && go test -race ./internal/<pkg>/....
Do not proceed to the ready comment on red tests. REQUIRED SUB-SKILL:
superpowers:verification-before-completion.
6. Commit / push / PR
Use commit-commands:commit-push-pr (or do it by hand). The PR body must
include Closes #N. PR title follows fix(scope): summary (#N) /
feat(scope): summary (#N). Required trailers:
- Commit messages end with:
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PR body ends with the
🤖 Generated with [Claude Code]… line.
7. Review
Run /pr-review-toolkit:review-pr on the PR. Address every real finding (loop
back to step 4 → re-verify). Re-run review if you made non-trivial changes.
8. Ready signal — comment reports what the last review ran
Assignee stays you. Post a comment on the PR (not the issue) in the
bold-section style (github-issues / comment-style conventions). The comment's
job is to record what the last review run was and its outcome — NOT a
generic "ready for review" banner.
**Review run:** /pr-review-toolkit:review-pr (code-reviewer, silent-failure-hunter, pr-test-analyzer)
**Findings:** 2 raised → both addressed in <sha>; 0 outstanding.
**Tests:** apps/api affected suite green (`vitest run routes/foo.test.ts`, 14 passed); `tsc --noEmit` clean.
**Status:** review-clean, awaiting maintainer merge.
If review surfaced nothing, say that explicitly ("0 findings") rather than
omitting the line — a missing line reads as "didn't run it."
9. Hand off — STOP
Report the PR number + a one-line summary to the orchestrator/user. Then stop.
The worker does NOT merge and does NOT close the issue. Merge (--admin,
gated on green required checks) and closure (after the reporter/user verifies)
are the user's judgment calls — see the merge/hold rules. The issue stays
open and assigned to you until then.
Red flags — STOP if you catch yourself
- "It's obviously fixed, I'll just close the issue." → Never. Hand off open.
- "Checks are green, I'll merge it." → Never. Merge is the user's call.
- "The issue's a bit vague but I'll guess what they meant." → Abort & ask (step 1).
- "Someone's assigned but they seem stalled, I'll take it." → Abort & report — don't poach. (But an issue assigned to the operator's own
@me account is not poaching — work it; see step 1.)
- "No open PR, so nobody's fixed it." → Check merged/closed PRs + commits on
main too. Open-only is a blind spot.
- "I'll skip the worktree, the main copy is fine." → No. Stale base + shared copy = wrong-commit PR.
- "I'm already inside a worktree, I'll just use this one." → No — it's another task's worktree. Make your own off fresh
origin/main.
- "Affected tests pass, skip the rest / skip typecheck." → Run typecheck (and astro check) too.
- "Posting 'ready for review' is enough." → No. Record which review ran and what it found.
| Rationalization | Reality |
|---|
| "A commit means it's done, close it." | A commit isn't verification. Reporter/user closes. |
| "Green CI means I can merge." | Merge is gated on the user's hold/judgment rules, not just CI. |
| "Aborting wastes the dispatch." | Aborting on a guard is the correct, cheap outcome. Guessing is expensive. |
| "Full suite is red so my change is broken." | The full API suite is flaky in parallel — verify via affected files single-fork. |
Orchestrating multiple issues
To work several issues at once, the in-session agent acts as orchestrator:
-
Decide the issue numbers BEFORE dispatch — never let multiple workers
self-select in parallel. Each worker's step-0 selection ranks the backlog
the same way and claims aren't visible at selection time, so parallel
self-selecting agents converge on the same top issue and open duplicate PRs
(observed: 3 agents → PRs #1917/#1918/#1919, all on #1896). Get a distinct set
one of two ways:
- The user named explicit numbers / a label filter → use that list.
- The user asked you to "find a few" with no numbers → you (the
orchestrator) do the selection: scan the backlog once, rank with the step-0
criteria, pick N distinct eligible candidates, run the step-1 guard on
each, and claim each up front (
gh issue edit <N> --add-assignee @me)
so the set is reserved. Only then dispatch.
Any orchestrator-level eligibility pre-check has the same blind spot as the
worker's: gh pr list --state open won't reveal an already-merged fix, so
don't pre-declare an issue "eligible" on that basis — the worker's full guard
(comments + --state all + origin/main) is the real gate.
-
REQUIRED SUB-SKILL: superpowers:dispatching-parallel-agents — spawn one
issue-fixer agent per already-chosen number, each in its own worktree
(isolation prevents branch/DB collisions; see the worktree skill). Every agent
gets a concrete number — none self-selects.
-
Each agent runs this runbook independently and returns its PR number (or its
abort reason). A worker that finds its handed number already assigned to @me
(because the orchestrator pre-claimed it) treats that as eligible — that's the
reservation working, not a poach.
-
Collect the results into one summary table for the user. The orchestrator
does not merge or close either — same boundary applies.
Do not auto-expand the list beyond what the user named. If you self-selected
from a label, log/state the cap and which issues were dropped.
Anti-pattern (do not do this): dispatching 2+ issue-fixer agents that
each run step-0 selection, trusting a "skip already-claimed issues" instruction
to keep them apart. Selection precedes any visible claim, so the guard can't
see a sibling's pick — they collide. Distinctness is the orchestrator's job,
settled before dispatch.