---
name: tackle-issue
description: Conductor for the issue-tackle workflow. Triage a GitHub issue or PR, scan for open tasks, propose a resolution plan, create an isolated worktree, guide execution through the appropriate workflow stages, and open a PR. Triggers when the user says "tackle issue #N", "resolve issue #N", "work on issue #N", "fix issue #N", "tackle PR #N", or "resolve PR #N", or invokes /issue:tackle <N>.
argument-hint: <issue-or-pr-number>
---

# Issue-tackle conductor

Triage a GitHub issue or PR, find open work, guide it to a shipped PR.

Shared rules (gating, escalation, inputs gate, conductor constraints): `.claude/skills/_shared/conductor-pattern.md`.

## Triggers

Natural language (auto-trigger):
- "tackle issue #N" / "resolve issue #N" / "work on issue #N"
- "fix issue #N" / "handle issue #N"
- "tackle PR #N" / "resolve PR #N" / "work on PR #N"

Explicit: `/issue:tackle <N>` where N is an issue or PR number.

## Pre-flight

1. `gh auth status` — fail closed if not authenticated.
2. Determine target type: issue or PR.
3. **Worktree idempotency.** If `.worktrees/issue-<N>-*` exists, surface its path + branch and exit — do not clobber a prior in-flight run.

## Step 1 — Inputs gate

List `inputs/` non-recursively. If any items exist, surface them in a single `AskUserQuestion` (multi-select): "I see N items in `inputs/`. Which are relevant?" If empty, print one line and proceed.

## Step 2 — Gather issue/PR data

Check sources in order:

**Source 1 — local issue mirror (always first):** Glob `issues/<N>-*.md` (canonical mirror per [ADR-0031](../../../docs/adr/0031-adopt-issues-folder-for-canonical-issue-tracking.md)). If found, read frontmatter: `spec_slug`, `roadmap_status`, `stage`, `title`, `labels`, `url`. `spec_slug` directly names the linked `specs/<slug>/` — use it in Step 4 to skip discovery. Note any `roadmap_status` vs GitHub `state` divergence (drift signal). If absent, after tackle completes the user may want `/spec:start` or `npm run sync:issues`.

**Source 2 — GitHub API:**

```bash
# issues
gh issue view <N> --json number,title,body,labels,state,url,assignees,milestone,comments
# PRs
gh pr view <N> --json number,title,body,labels,state,url,baseRefName,headRefName,comments,reviews,author,isCrossRepository,headRepositoryOwner
```

Merge: local frontmatter wins for `spec_slug` / `roadmap_status`; GitHub wins for `body`, `labels`, `assignees`, `state`, `comments`.

If issue is `CLOSED` or PR is `MERGED` / `CLOSED`, warn and prompt via `AskUserQuestion`. Exit if user declines.

## Step 2b — Open review threads (PRs only)

Skip for issues. Use GraphQL — REST review-comment endpoints do **not** expose thread `isResolved`:

```bash
gh api graphql \
  -f owner="$(gh repo view --json owner --jq '.owner.login')" \
  -f repo="$(gh repo view --json name --jq '.name')" \
  -F number=<N> \
  -f query='query($owner:String!,$repo:String!,$number:Int!,$endCursor:String){repository(owner:$owner,name:$repo){pullRequest(number:$number){reviewThreads(first:100,after:$endCursor){pageInfo{hasNextPage endCursor}nodes{isResolved path line comments(first:1){nodes{body author{login}}}}}}}}' \
  --jq '.data.repository.pullRequest.reviewThreads | {pageInfo, unresolved: [.nodes[] | select(.isResolved == false) | {path, line, body: .comments.nodes[0].body[:120], author: .comments.nodes[0].author.login}]}'
```

`isResolved == false` = true GitHub state regardless of replies. If `.pageInfo.hasNextPage`, re-run with `-F endCursor=<endCursor>` and merge `.unresolved`.

If unresolved findings exist:
- Print "N unresolved review comment(s) on PR #<N>" + each entry's path, line, first 120 chars, author.
- Add `**Open reviews:** N comments` to the triage summary.
- Step 5 surfaces a `Review-fix path` option (first when reviews are the primary work).

## Step 3 — Triage

Classify from labels, title, body. Produce a one-paragraph summary covering:

| Field | What to assess |
|---|---|
| **Type** | `bug`, `feature`, `enhancement`, `docs`, `chore`, `question`, `security`, `performance` |
| **Priority** | `critical`, `high`, `normal`, `low` — from `priority:*` labels or body severity |
| **Scope** | Narrow (one file/function) vs broad (cross-cutting, new API) |
| **Clarity** | Is expected behaviour defined? Reproducer present (bugs)? |
| **Spec linkage** | Body links `specs/<slug>/`? `spec:*` label? |
| **Blocker status** | Blocking another issue, release, or SLA? |

Present summary **before** proceeding. Confirm classification via `AskUserQuestion` (or let user correct). Batch with Step 5's path-proposal call when triage is unambiguous.

## Step 4 — Scan for open tasks

Parallel:

- **Source A — body checkboxes:** parse `- [ ] …` lines.
- **Source B — linked spec:** if spec linkage found, read `specs/<slug>/workflow-state.md` (`current_stage`, `status`, per-artifact statuses) and `specs/<slug>/tasks.md` (open tasks not marked `done`).

## Step 5 — Classify situation, propose path

Pick one of four cases via a single `AskUserQuestion`:

### Case A — Spec exists, open tasks in `tasks.md`
Show open task IDs + descriptions. Propose: "Continue spec — set up worktree and run `/spec:implement` on next open task."

### Case B — Spec exists, tasks complete, workflow continuing
Read `current_stage`. Map (stage values ≠ command names 1:1):

| `current_stage` | Command |
|---|---|
| `idea` | `/spec:idea` |
| `research` | `/spec:research` |
| `requirements` | `/spec:requirements` |
| `design` | `/spec:design` |
| `specification` | `/spec:specify` |
| `tasks` | `/spec:tasks` |
| `implementation` | `/spec:implement` |
| `testing` | `/spec:test` |
| `review` | `/spec:review` |
| `release` | `/spec:release` |
| `learning` | `/spec:retro` |

Propose: "Resume at stage `<current_stage>` — set up worktree and run `<mapped-command>`."

### Case C — Body checkboxes, no linked spec
Display checkboxes. Two sub-options:
- `Quick path` — work directly in worktree, no formal spec (small well-defined tasks).
- `Full workflow` — `/spec:start` then run lifecycle.

### Case D — No tasks, no spec
Fresh-work case. Recommend per triage type:

| Type | Recommended approach |
|---|---|
| `bug` | Isolate → failing test → fix → PR (no spec for narrow bugs) |
| `feature` / `enhancement` | `/spec:start` → Lean or Standard depth |
| `docs` | Edit docs in worktree → PR |
| `chore` | Direct change in worktree → PR |
| `question` | Answer in issue comment — no code change |
| `security` | Isolate in worktree → fix → PR (do **not** disclose publicly; flag) |
| `performance` | Profile → benchmark test → fix → PR |
| `unclear` | One clarifying question first (per conductor pattern) |

`AskUserQuestion` options: recommended approach (label "Recommended"), alternative full spec workflow, "Other".

## Step 6 — Confirm worktree details

**Skip entirely if path is `question`** — go to Step 8.

Surface before creating:
- **Worktree:** `.worktrees/issue-<N>-<slug>`
- **Branch:** `<prefix>/issue-<N>-<slug>` — prefix `fix` (bugs/security), `feat` (features/enhancements), `docs` (docs), `chore` (chores)
- **Base:** issue → integration branch (Step 7; usually `develop` or `main`); PR → `headRefName` from Step 2.

Batch into Step 5's `AskUserQuestion` if information is available before the gate.

## Step 7 — Set up worktree

**Skip entirely if path is `question`** — go to Step 8. Setup differs by target type.

**Issue target — new branch from integration branch:**

```bash
integration_branch=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||')
if [ -z "$integration_branch" ]; then
  for candidate in develop main master; do
    git show-ref --quiet "refs/remotes/origin/$candidate" && integration_branch="$candidate" && break
  done
fi
if [ -z "$integration_branch" ]; then
  echo "Cannot resolve integration branch — set origin/HEAD or pass the branch explicitly."; exit 1
fi
git fetch origin
git worktree add .worktrees/issue-<N>-<slug> \
  -b <prefix>/issue-<N>-<slug> "origin/${integration_branch}"
```

**PR target (same-repo)** — `isCrossRepository == false` — check out the PR's existing head branch so Step 10's push fast-forwards:

```bash
git fetch origin <headRefName>
git worktree add .worktrees/issue-<N>-<slug> \
  -b <headRefName> "origin/<headRefName>"
```

Step 10 then `git push origin <headRefName>` (plain fast-forward — no force).

**PR target (fork)** — `isCrossRepository == true`. Head branch is in contributor's fork, not `origin`. Add fork as temp remote, fetch, warn user about push access:

```bash
fork_remote="fork-pr-<N>"
fork_url=$(gh pr view <N> --json headRepository --jq '.headRepository.url')
if git remote get-url "$fork_remote" > /dev/null 2>&1; then
  git remote set-url "$fork_remote" "$fork_url"
else
  git remote add "$fork_remote" "$fork_url"
fi
git fetch "$fork_remote" <headRefName>
git worktree add .worktrees/issue-<N>-<slug> \
  -b <headRefName> "${fork_remote}/<headRefName>"
```

Warn: "Fork PR — Step 10 pushes to `<headRepositoryOwner.login>/<headRefName>`; you need write access. If not, coordinate with the PR author." Step 10 push: `git push "$fork_remote" <headRefName>`.

If the local branch already exists (retry after partial run), refuse `-B` (force). Surface the conflict and ask: delete + recreate, or reuse?

Bootstrap deps (e.g. `npm ci`) — verify assumes deps installed.

## Step 8 — Execute

Hand off to the right mechanism:

**Review-fix path (PR with unresolved threads):** do **not** dispatch `/review-fix` — it creates a separate worktree+PR per finding (wrong tool for updating an existing PR). Address each thread inline in the current worktree (already on `headRefName`), commit, push to existing PR branch in Step 10. Sequential: read comment → targeted change → stage → continue.

**Case A/B (spec workflow):** dispatch the mapped `/spec:*` command in the worktree. Stage subagent owns the work.

**Case C Quick / Case D narrow fix:** follow [`tdd-cycle`](../tdd-cycle/SKILL.md) — failing test → fix → refactor on green → `npm run verify`.

**Case C Full / Case D feature:** `/spec:start <slug> [AREA]` then `/orchestrate`.

**Case D docs/chore:** work directly in worktree. `npm run verify` before push.

**Case D question:** post answer as issue comment and exit:
```bash
gh issue comment <N> --body "<answer>"
```

## Step 9 — Verify

```bash
# inside .worktrees/issue-<N>-<slug>/
npm run verify
```

Fix failures. Do not push with failing verify.

## Step 10 — Commit and open PR

Map triage type to a CI-allowed Conventional Commits type. PR-title gate (`feat|fix|chore|docs|refactor|perf|test|build|ci|revert`) rejects raw triage labels:

| Triage type | CC type |
|---|---|
| `bug` | `fix` |
| `feature` / `enhancement` | `feat` |
| `docs` | `docs` |
| `chore` | `chore` |
| `security` | `fix` |
| `performance` | `perf` |
| `question` | — (comment only — see Step 8) |
| `unclear` | `chore` (or `fix`/`feat` after clarification) |

`#` is invalid in scope — PR-title gate rejects it.

**Issue target commit:**
```
<cc-type>: <concise imperative subject>

Closes #<N>
```

**PR target commit** — omit `Closes` (closes an issue, not a PR):
```
<cc-type>: <concise imperative subject>
```

**Issue target — push and open new draft PR:**
```bash
git push -u origin <prefix>/issue-<N>-<slug>
gh pr create \
  --title "<cc-type>: <subject>" \
  --body "$(cat <<'EOF'
## Summary
<1-3 bullet points>

## Triage
- **Type:** <triage-type>
- **Priority:** <priority>

Closes #<N>

🤖 Resolved via `/issue:tackle <N>`
EOF
)" \
  --draft
```

**PR target — push to existing PR branch (fast-forward from Step 7); no new PR:**
```bash
# Same-repo PR:
git push origin <headRefName>
# Fork PR:
git push fork-pr-<N> <headRefName>
```

Report PR URL (existing for PR target, new for issue target).

## Step 11 — Post-tackle cleanup (on user request)

```bash
git worktree remove .worktrees/issue-<N>-<slug>
# Issue target — delete topic branch:
git branch -d <prefix>/issue-<N>-<slug>
# PR target (same-repo or fork) — delete local tracking branch:
git branch -d <headRefName>
# Fork only — remove temporary remote:
git remote remove fork-pr-<N>
git worktree prune
```

Do not clean up automatically.

## Reporting

After Step 10:

```
Tackled issue #<N>:
  triage:   <type> / <priority>
  worktree: .worktrees/issue-<N>-<slug>
  branch:   <prefix>/issue-<N>-<slug>
  PR:       <url>

Next: review the draft PR, then merge when CI is green.
```

## Do not

- Do **not** force-create the branch (`-B`). If it exists, the prior fix is in flight.
- Do **not** push to `main`, `develop`, or `demo`. Only the topic branch.
- Do **not** skip triage — classification drives every downstream decision.
- Do **not** run `--no-verify`.
- Do **not** merge automatically — see [`feedback_autonomous_merge.md`](../../memory/feedback_autonomous_merge.md).
- Do **not** open more than one PR — see [`feedback_pr_hygiene.md`](../../memory/feedback_pr_hygiene.md).
- Do **not** write to `specs/<slug>/` directly — dispatch the `/spec:*` command and let the stage subagent own the artifact.

## References

- Methodology: `docs/issue-tackle-track.md`
- Conductor pattern: `.claude/skills/_shared/conductor-pattern.md`
- Worktrees / branching / verify: `docs/worktrees.md`, `docs/branching.md`, `docs/verify-gate.md`
- Issues mirror: `issues/README.md` (per ADR-0031)
- TDD cycle / orchestrate / review-fix: `.claude/skills/{tdd-cycle,orchestrate,review-fix}/SKILL.md`
- Companion tracks: `docs/issue-breakdown-track.md`, `docs/issue-draft-track.md`
