skills/finish/SKILL.md
Finish a Linear issue — check off requirements, add completion comment, commit/push, mark Ready For Release. Use when the user says 'finish issue', 'done with this issue', 'complete PL-XX', or invokes /finish.
npx skillsauth add alienfast/claude finishInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Automates the post-completion workflow for a Linear issue using the linear CLI. The mechanical steps (worktree-mode detection, issue-ID resolution, Linear posts, git commit/push) are delegated to scripts in ~/.claude/scripts/; this skill is the orchestrator + LLM-judgment surface (reading the description, composing the completion comment).
PL-12) — optional, auto-detected from branch/commitno push / don't push / skip push — optional, skips the git push step (commit still happens)merge — only meaningful inside a /start wt worktree. Default when in a worktree. Merge the worktree branch back into its recorded source branch, then remove the worktree.pr — open a pull request for the current branch (works from any branch). Inside a /start wt worktree the base is the recorded source branch and the worktree is left in place; otherwise the base is the repo's GitHub default branch (in-place — no worktree touched). Optionally apply labels with with label X / label X, Y. The issue stays In Progress (the PR is open, not yet shipped).Examples: /finish, /finish PL-12, /finish no push, /finish PL-12 no push, /finish merge, /finish pr PL-12, /finish pr with label pr-deploy
pnpm check must pass before committing or pushing code. Check failures are always CRITICAL — never "pre-existing", never "out of scope", never deferred. Fix them before proceeding. Turborepo caching makes repeated runs cheap.
If the session is in plan mode when /finish is invoked, call ExitPlanMode before any other step. Every step from Step 0 onward needs Bash, Write, or Edit — all blocked in plan mode — so finish-detect-mode.sh would fail on its first call otherwise.
Detection. Use the harness's plan-mode indicator visible at skill entry (the same signal that was gating tool calls just before this skill loaded). If that indicator is ambiguous or unavailable, attempt Step 0; if finish-detect-mode.sh fails with a plan-mode block, return here, call ExitPlanMode, then retry Step 0. Do NOT speculatively call ExitPlanMode when plan mode is not active — it raises a spurious approval prompt the user must dismiss.
Plan body. Pass a one-line plan summarizing what /finish is about to do for the resolved issue. There is nothing to design — /finish is a fixed mechanical workflow — but ExitPlanMode is the only way to leave plan mode and it requires a plan body. For the <ISSUE-ID> substitution: only inline a user-supplied token if it matches ^[A-Z]+-[0-9]+$ (case-insensitive, uppercase it before substituting); otherwise use the current branch's issue. This keeps malformed tokens (e.g., PL13, stray merge/pr keywords) out of the plan body and out of any rejection-terminator that echoes the same value.
ExitPlanMode failed for a non-user-cancel reason): surface the error verbatim and stop with BLOCKED-ON-REVIEW: <ISSUE-ID or "current branch"> — ExitPlanMode failed: <first line of error>. No state change. Do NOT continue to Step 0; plan mode is still active and Step 0 will compound the failure.On user rejection via the approval UI, treat as an abort and stop with:
BLOCKED-ON-REVIEW: <ISSUE-ID or "current branch"> — user rejected /finish at the plan-mode preflight. No state change.
Do not retry, do not re-prompt, do not run any subsequent step. Skip this preflight only when plan mode is NOT active — /finish is normally invoked from a non-plan session after /start or manual development, in which case this section is a no-op.
Normalize the user's args before calling the script:
merge and pr tokens (case-insensitive, position-agnostic) — pass through whichever is present (if both, the script errors).no push / don't push / skip push — translate to --no-push for the script.with label X, label X, Y, --label X) — collect them into a list and carry it forward to Step 9 (pr mode only). Labels are NOT passed to the script — its arg contract is merge|pr|--no-push only. If labels were requested but the resolved ACTION is not pr (i.e., merge or the standard flow), labels have no PR to attach to — warn the user once (Labels apply only to /finish pr; ignoring: <list>.) rather than silently dropping them.~/.claude/scripts/finish-detect-mode.sh [merge|pr] [--no-push]
The script probes worktree state, validates incompatible argument combinations, and emits six KEY=value lines on stdout: ACTION, SOURCE_BRANCH, WORKTREE_BRANCH, WT_DIR, REPO_ROOT, NO_PUSH. Read those values and carry them forward — Step 9 substitutes them into bash commands as literal strings (each Bash tool call is a fresh shell).
Exit codes:
merge + pr, or pr + no push). Surface the error and stop.merge requested outside a /start wt worktree. Surface and stop. (pr is not rejected here — it works from any branch; see below.)When SOURCE_BRANCH is set (we're in a worktree), the script defaults ACTION to merge; /finish pr is the way to opt into the PR flow with base = SOURCE_BRANCH. Outside a worktree, pr is allowed (it emits ACTION=pr with an empty SOURCE_BRANCH, and Step 9 targets the repo's default branch), while merge is rejected (exit 2).
If both SOURCE_BRANCH and ACTION are empty, this is the standard /finish flow.
~/.claude/scripts/detect-issue-id.sh [--input <USER-SUPPLIED-ID>]
The script tries --input → current branch → latest commit subject, in that order. Pass --input only when the user typed an explicit ID (e.g., /finish PL-12). On exit 1, ask the user for the identifier explicitly.
Cross-worktree sanity check (standard-flow only). After the issue ID is resolved, if the standard flow was detected in Step 0 (ACTION empty — no worktree config and no explicit pr) but the issue's branch exists in a known linked worktree of this repo (git worktree list shows a path whose basename or branch contains <issue-id-lowercased>, e.g., pl-13), warn the user before continuing:
Issue
<ISSUE-ID>appears to live in worktree<path>. Are you running/finishfrom the wrong cwd? Replyyesto proceed here anyway, orabortandcdinto the worktree first.
Continue only on explicit yes. This catches the case where /start wt created a worktree, the user opened a fresh terminal in the main checkout, and ran /finish PL-13 from there — which would otherwise push/commit on the wrong branch. Skip the check entirely when Step 0 detected a worktree (in which case SOURCE_BRANCH is set and we're already in the right place), when ACTION is pr (an explicit /finish pr is a deliberate choice to open a PR for the current branch — never a wrong-cwd accident), or when no issue ID was resolved (nothing to check against).
~/.claude/scripts/finish-read-verdict.sh PL-12
Emits seven KEY=value lines: VERDICT_FILE, VERDICT, CYCLES, SUB_ISSUES, SUB_ISSUES_ERROR, VERDICT_STALE, VERDICT_STALE_REASON. Read those values and carry them forward — Step 4 embeds them in the completion comment, Step 8 gates the Ready For Release transition on VERDICT and VERDICT_STALE.
VERDICT is one of:
passed-clean / passed-after-fixes — /quality-review converged cleanly. Step 8 proceeds without prompting.terminated-with-open-items / escalated-to-architect — non-passing. Step 8 hard-refuses by default (override prompt; see Step 8).malformed — verdict file exists but cannot be parsed (no Verdict: line, the line contains the pipe-separated schema example, or the value is not one of the four recognized enums). Step 8 hard-refuses; the user clearly ran /quality-review but the handoff is broken, so silently passing the gate would defeat the safety check.none-found — no verdict file exists at either the current worktree's tmp/ or the main checkout's tmp/. /quality-review was either never run for this issue or was run from a different repo. Step 8 warns and proceeds.SUB_ISSUES is the parent issue's current children array from Linear — i.e., every sub-issue that exists under this parent right now, not necessarily ones filed by this /quality-review run. Step 4 surfaces this list as context (labeled accordingly), not as a "filed this run" claim.
SUB_ISSUES_ERROR is populated only if linear i get failed (CLI unauthenticated, missing issue, network blip). Step 1.5 does NOT abort on this — linear auth login is offered in Step 2's error handling if needed, and the rest of /finish can proceed without sub-issue context. Surface the warning text in chat once when populated.
VERDICT_STALE=1 means the verdict file's mtime predates HEAD's commit time — additional commits landed AFTER /quality-review ran, so the verdict does not reflect current code. Step 8 escalates passing-but-stale to refuse-with-override (same shape as malformed), preventing the gate from sailing through on an out-of-date verdict. The VERDICT_STALE_REASON field carries diagnostic text for the override prompt.
linear issues get PL-12 --format full
Read the description carefully. Note:
- [ ] items)linear issues get PL-12 --output json
Identify each - [ ] checkbox and decide which were completed this session. Don't post anything yet — Step 5 sends the updated description and the completion comment together.
Write a markdown comment summarizing the work. Every <...> token below is a substitution site — replace each one with the resolved value before posting; never emit a literal <placeholder> to Linear. Template:
## Implementation Complete
Branch: `<actual branch name>`
### What was done
- Bullet points of key changes (files created/modified, features implemented)
### Design decisions
- Key technical choices and why they were made
### Verification
- What was verified (type checks, tests, dev server, etc.)
### Adversarial review
- Verdict: <VERDICT value from Step 1.5> (cycles: <CYCLES value from Step 1.5>)
- Sub-issues (current children of this issue): <comma-list of SUB_ISSUES from Step 1.5, or the bare word `none` (no quotes) when empty>
- Open items: <text extracted from VERDICT_FILE's Open items: section, only when VERDICT=terminated-with-open-items, escalated-to-architect, or malformed>
### Notes
- Any unchecked items with explanation of why
- Any follow-up work identified
Omit sections that have no content (e.g., skip "Notes" if everything was completed). Omit the Adversarial review section entirely when VERDICT=none-found (no /quality-review ran). When the verdict is passing, drop the Open items bullet but keep the other two.
Write both files:
tmp/linear-description-<issue-id-lowercased>.md (e.g., tmp/linear-description-pl-12.md) — full description with - [ ] flipped to - [x] for completed items. Preserve everything else exactly.tmp/linear-comment-<issue-id-lowercased>.md (e.g., tmp/linear-comment-pl-12.md) — completion-comment body from Step 4.Then post both in one call:
~/.claude/scripts/finish-post-update.sh PL-12 tmp/linear-description-pl-12.md tmp/linear-comment-pl-12.md
Exit codes: 1 (validation — missing/empty files), 2 (Linear API failure).
Run pnpm check as a hard gate before committing:
pnpm check
If it fails: this is CRITICAL. Do not commit or push. Fix the failures first, then re-run until it passes.
If it passes: proceed to commit.
Stage relevant files by name (git add <files>). Never git add -A / git add . (per CLAUDE.md).
Write the commit message to tmp/finish-commit-<issue-id-lowercased>.md (e.g., tmp/finish-commit-pl-13.md). The issue ID must appear in the message (the script enforces it for Linear auto-linking):
PL-13: <short imperative summary>
<optional body explaining the why>
Run the commit script:
~/.claude/scripts/finish-commit.sh PL-13 tmp/finish-commit-pl-13.md [--no-push]
--no-push is required in TWO cases — easy to miss the second:
no push / don't push / skip push (Step 0 translates these to NO_PUSH=1).ACTION=merge — the temp branch is about to be merged into source and deleted locally; pushing it pollutes origin with abandoned branches. The merge commit reaches origin later via the source branch.If either condition holds, pass --no-push. The script does NOT enforce this rule (it has no awareness of ACTION), so the orchestrator MUST gate on NO_PUSH=1 OR ACTION=merge.
The script handles all three states: pre-staged changes (commit + push), already-committed-but-ahead (push only), already-synced (no-op). If staging is missing for an unstaged-only state, it errors with exit 2 — go back and git add the files.
Skip when ACTION == "pr". In PR mode, the work is not yet shipped — review and merge are still pending. Leave the issue in In Progress; the transition to Ready For Release happens after the PR merges (manually, or via a follow-up /finish once the worktree branch is merged into source).
In all other cases (no worktree, or ACTION == "merge"), gate the transition on the VERDICT from Step 1.5. Every <...> token in the prompt and comment bodies below is a substitution site — replace each with the resolved value before emitting; never write a literal <placeholder> to chat or to Linear. The Step 4 substitution rule applies here too.
Step 8 termination contract — applies to ALL branches below. Per standards/lifecycle-tags.md, every terminal path of /finish Step 8 ends with exactly one tagged final line. (The Preflight has its own independent terminator — BLOCKED-ON-REVIEW on plan-mode rejection or ExitPlanMode tool failure — and never reaches Step 8.) Mechanical mapping (do not skip):
linear issues update --state "Ready For Release" AND ACTION from Step 0 is empty (standard flow, no Step 9 to follow) → emit RELEASED: <ISSUE-ID> — <one-line summary> as the last LLM-authored line.ACTION == "merge" → do NOT emit a tag here; Step 9 owns the terminal line (SHIPPED-MERGE:). (ACTION == "pr" never reaches a state update — Step 8 is skipped for it — so it isn't in these bullets; Step 9 owns SHIPPED-PR:. Discriminate on ACTION, not SOURCE_BRANCH: a non-worktree pr has an empty SOURCE_BRANCH yet still flows to Step 9.)abort or re-run at the gate prompt → emit BLOCKED-ON-REVIEW: <ISSUE-ID> — <one-line reason> as the last LLM-authored line. State was NOT changed.none-found) → same as the first/second bullets depending on ACTION, unless linear issues update itself fails, in which case bullet 5 supersedes.linear issues update itself failed (API error, auth dropped mid-session, team's terminal state name differs from Ready For Release) → see the State-update failure section below for the recovery + terminator rule. This bullet supersedes bullets 1, 2, and 4 whenever the state update doesn't succeed — never emit RELEASED: on a failed update.The per-branch instructions below indicate which terminator each branch uses; trust the contract above for the literal tag wording.
State-update failure recovery (applies to every branch that attempts linear issues update --state "Ready For Release"). If the call exits non-zero:
/start Step 8.5's CANCELED/ABANDONED fallback and /quality-review sub-step 6's fallback:
PL-13 → team PL). Then probe: linear teams states PL./^ready[ _-]?for[ _-]?(release|deploy|ship)$/i (exact match — NOT a prefix match — to avoid latching onto Ready For Review; the [ _-]? separator class matches Ready For Release, Ready_For_Release, Ready-For-Release, ReadyForRelease).linear issues update <ISSUE-ID> --state "<matched-name>". If it succeeds, proceed with the branch's stated terminator per the Step 8 termination contract — RELEASED: for the standard flow (ACTION empty), or no tag here for ACTION == "merge" (fall through to Step 9, which emits SHIPPED-MERGE:). Do NOT hardcode RELEASED: for a merge branch.Ready: the regex deliberately requires Ready For <release|deploy|ship> and does NOT match a bare Ready state. A team's Ready state is too ambiguous (could mean ready-for-review, ready-for-QA, etc.) to auto-route into — the issue falls through to step 2's BLOCKED-ON-REVIEW. To use bare Ready as a release state, rename it to Ready For Release or add canonical config.BLOCKED-ON-REVIEW: <ISSUE-ID> — linear issues update failed: <reason>. State NOT changed; this issue remains In Progress. as the terminator. Distill <reason> to a single line: if the CLI returned a multi-line error (stack trace, JSON error body), take the first informative line (typically the error message) and drop the rest — the tag must fit on one line so the agents-list parser picks it up correctly. Do NOT silently emit RELEASED: (it would lie about the state) and do NOT continue to Step 9 (worktree flow can't ship an issue whose state didn't transition).passed-clean / passed-after-fixes — proceed, BUT first check VERDICT_STALE:
VERDICT_STALE=0 → proceed with the state update:
linear issues update PL-12 --state "Ready For Release"
Terminator: RELEASED: (non-worktree) or none (worktree; Step 9 emits).
VERDICT_STALE=1 → refuse with override. The verdict says "passing" but was produced before the latest commits — it may not reflect current code. Prompt the user:
Quality-review verdict is
<VERDICT>but is stale:<VERDICT_STALE_REASON>. Additional commits landed after/quality-reviewran, so the verdict may not reflect current code.Mark
Ready For Releaseanyway? Replyyesto override (the prior verdict was passing and you've verified the new commits don't introduce findings),re-runto invoke/quality-reviewand produce a fresh verdict for current HEAD, orabortto stop here.
On yes: proceed with the state update AND post an override comment: Override: marked Ready For Release on stale verdict <VERDICT> (additional commits since /quality-review ran). User-acknowledged. Terminator: RELEASED: (non-worktree) or none (worktree).
On re-run: stop with Re-run /quality-review <ISSUE-ID> to produce a fresh verdict for current HEAD, then retry /finish. Terminator: BLOCKED-ON-REVIEW: <ISSUE-ID> — stale verdict, user opted to re-run /quality-review against current HEAD before /finish.
On abort: stop with no state change. Terminator: BLOCKED-ON-REVIEW: <ISSUE-ID> — stale verdict, user aborted at /finish gate. No state change.
terminated-with-open-items / escalated-to-architect — refuse by default. The implementation has known unresolved findings per /quality-review. Before composing the prompt, Read the file at VERDICT_FILE and extract the Open items: line (and any continuation lines, if Open items: is followed by an indented bullet list). Substitute that text into the prompt below — never emit the literal placeholder <open items list from VERDICT_FILE>. If VERDICT_STALE=1, prepend a staleness note to the prompt so the user knows the open-items list may not reflect current code (recent commits may have resolved some). Then prompt the user (single message, wait for reply):
Quality-review verdict is
<VERDICT>with open items:
<text extracted from VERDICT_FILE's Open items: section>[If
VERDICT_STALE=1, include:Note: the verdict is stale — <VERDICT_STALE_REASON>. The open items above may have already been resolved by the more recent commits.]Mark
Ready For Releaseanyway? Replyyesto override,re-runto invoke/quality-reviewand try to converge, orabortto stop here.
On yes: proceed with the state update AND post an additional Linear comment recording the override — body: Override: marked Ready For Release despite verdict <VERDICT>. Open items at override time: <list>. User-acknowledged. (If VERDICT_STALE=1, append: Verdict was stale; user explicitly accepted the risk of overriding without a fresh /quality-review.) Use ~/.claude/scripts/linear-post.sh to post. Terminator: RELEASED: (non-worktree) or none (worktree).
On re-run: stop /finish with the message Re-run /quality-review <ISSUE-ID> to address open items, then retry /finish. Do not change state. Terminator: BLOCKED-ON-REVIEW: <ISSUE-ID> — verdict <VERDICT>, user opted to re-run /quality-review before /finish.
On abort: stop with no state change and no further output. Terminator: BLOCKED-ON-REVIEW: <ISSUE-ID> — verdict <VERDICT>, user aborted at /finish gate. No state change.
malformed — verdict file exists at VERDICT_FILE but cannot be parsed (no Verdict: line, or value is the pipe-separated schema example, or value is not one of the four recognized enums). Refuse with the same prompt as the non-passing path above, but with this preamble instead of the open-items list:
Quality-review verdict file exists at
<VERDICT_FILE>but is malformed (no recognized verdict value). The user clearly ran /quality-review, but the handoff is broken — silently marking Ready For Release would defeat the safety check.Mark
Ready For Releaseanyway? Replyyesto override (consider inspecting the file first),re-runto invoke/quality-reviewand produce a fresh artifact, orabortto stop here.
Same response handling as the non-passing path: yes posts an override comment (body: Override: marked Ready For Release despite malformed /quality-review verdict file. User-acknowledged. — do NOT include VERDICT_FILE's absolute path in the comment body, since Linear comments are not necessarily private and the path leaks the user's home directory and project layout); re-run stops with the re-run suggestion; abort stops. Terminators: yes → RELEASED: (non-worktree) or none (worktree); re-run → BLOCKED-ON-REVIEW: <ISSUE-ID> — malformed verdict, user opted to re-run /quality-review.; abort → BLOCKED-ON-REVIEW: <ISSUE-ID> — malformed verdict, user aborted at /finish gate. No state change.
none-found — no verdict file located. Warn once: No /quality-review artifact found for this issue. Proceeding without gate. Consider running /quality-review before /finish next time. Then proceed with the state update. Terminator: RELEASED: (non-worktree) or none (worktree). (Backward compatibility for issues finished before this gate existed.)
Any other value (defense in depth — shouldn't happen since the script normalizes everything else to malformed) → treat as malformed. Do NOT proceed silently. Terminators: same as malformed.
ACTION is merge or pr)Runs only when ACTION is merge or pr. Skip entirely when ACTION is empty (the standard flow ended at Step 8). merge always implies a worktree (SOURCE_BRANCH set); pr runs with or without one.
Step 9 is the terminal step of this session — for all modes. After the merge (or gh pr create) completes, present the closing message and stop. Don't run further bash commands.
Substitute the values captured from Step 0 (SOURCE_BRANCH, WORKTREE_BRANCH, WT_DIR, REPO_ROOT) into the bash commands below as literal strings.
If ACTION == "merge":
The script brings the worktree branch up to source's tip inside the worktree (where this session can edit even under bgIsolation, and where no lock is needed because the worktree is private to this session), then advances source to it — by git merge --ff-only when the main checkout is on source, or by an atomic ref update (git update-ref, compare-and-swap) when it's on another branch. Either way the advance never merges in the main checkout, never leaves it mid-merge, and never switches its HEAD. The common case — worktree branch one commit ahead, source unmoved — collapses to a single PL-XXX: <summary> line in git log with no merge commit. Only when source moved during the worktree's life is a merge commit created (with the prepared one-line Merge PL-XXX subject, avoiding the verbose default boilerplate); any conflicts from that are resolved in the worktree, never the main checkout. This is what makes the merge safe to run concurrently and from a background session.
Write the merge-commit message to <WT_DIR>/tmp/git-merge-msg-<issue-id-lowercased>.md (e.g., <WT_DIR>/tmp/git-merge-msg-pl-13.md; substitute the actual WT_DIR value from Step 0). Use the Write tool — it requires an absolute path. A single line is all that's needed; it's only used in the rare divergent-merge case (or during conflict resolution), and the issue ID is what Linear auto-links on:
Merge PL-13
Run the merge in a single Bash tool call — cd to the main checkout (the script removes the worktree on success, so cwd must not be inside it), then call finish-merge.sh:
cd '<REPO_ROOT from Step 0>'
~/.claude/scripts/finish-merge.sh '<WT_DIR>' '<SOURCE_BRANCH>' '<WORKTREE_BRANCH>' '<WT_DIR>/tmp/git-merge-msg-pl-13.md'
The script self-serializes against other /finish merge sessions targeting the same parent repo. If another session holds the merge slot, the Bash call will print [finish-queue] waiting for <REPO_ROOT> ... on stderr and block until its turn — surface that output to the user as-is and wait. The lock is released by the OS when the script exits (success, error, or crash); no manual cleanup is needed.
Exit codes:
0 (success) — surface the script's output and present the closing message. The tagged final line (per standards/lifecycle-tags.md) MUST be the last LLM-authored output:
This agent-view session is done — close it and dispatch a new session for the next issue.
SHIPPED-MERGE: <ISSUE-ID> — <WORKTREE_BRANCH> merged into <SOURCE_BRANCH>, worktree removed, Ready For Release.
Do not run further bash commands.
1 (precondition failure) — surface the script's output and stop. Don't attempt recovery; precondition errors are setup issues (dirty checkout, missing branch, mid-merge state) that the user needs to resolve.
2 (merge conflict — resolve in the worktree) — the script merged <SOURCE_BRANCH> into the worktree branch inside <WT_DIR> and hit conflicts. The main checkout is untouched and clean; the conflict lives in the worktree, which this session owns (edits there are permitted even under bgIsolation) and which is private (no lock needed — do not wrap these in with-repo-lock.py). Conflicted files are listed on the script's stderr as worktree-relative paths.
<WT_DIR>/<path>, understand both sides of the conflict, apply the resolution. When one side clearly subsumes the other (e.g., the worktree branch removed code the source side modified), take the subsuming side. Ask the user only when the right answer is genuinely ambiguous.git -C '<WT_DIR>' add <resolved-files>pnpm check from <WT_DIR> — must be green before committing. If it fails: the conflict resolution introduced a regression. Per the Working Application Contract, do not commit and do not re-invoke. Surface the failing output to the user; let them decide between fixing the resolution further, aborting the merge (git -C '<WT_DIR>' merge --abort), or escalating to architect. The mid-merge state is preserved in the worktree for inspection.git -C '<WT_DIR>' commit -F '<WT_DIR>/tmp/git-merge-msg-<issue-id-lowercased>.md' — reuse the prepared merge-commit message (e.g., <WT_DIR>/tmp/git-merge-msg-pl-13.md).finish-merge.sh line. It re-acquires the lock and fast-forwards the main checkout, then removes the worktree and branch. If source advanced during your resolution, it re-merges the new delta — which may return 2 again with a fresh conflict on that delta; if so, repeat steps 1–5. This is expected under concurrent /finish merge sessions and converges (each round reconciles only the latest source delta).If ACTION == "pr":
The branch was pushed in Step 7 (the no push + pr combination was rejected in Step 0). Open a PR for the current branch — this works whether or not we're in a worktree.
Resolve the base branch:
SOURCE_BRANCH is set (we're in a /start wt worktree) → base = SOURCE_BRANCH.
SOURCE_BRANCH is empty (plain-branch PR) → base = the repo's GitHub default branch:
gh repo view --json defaultBranchRef -q .defaultBranchRef.name
If this fails (no GitHub remote, gh unauthenticated), surface the error and stop — there's nowhere to open the PR. Don't guess a base. The base is the default branch by design even when the branch was forked off another branch; the user can retarget the PR in GitHub if it should land elsewhere.
Verify any requested labels exist (only if Step 0 collected labels). Check each label with an exact-equality jq filter (a substring like deploy must NOT match an existing pr-deploy) — the command prints the label name if it exists and nothing if it doesn't:
gh label list --limit 200 --json name -q '.[].name | select(. == "<label>")'
If a requested label is missing (empty output), do NOT silently drop it. Surface it and ask the user: proceed without that label / create it (gh label create '<label>') / abort. Apply only labels that exist (or that the user just created).
Generate the PR title and body from the actual diff — do NOT use --fill (it only echoes the commit message). Apply the pr-update skill's methodology directly: read ~/.claude/skills/pr-update/SKILL.md and follow its Analysis Process (§2–6), PR Title Formats, and Description Structure. Skip pr-update's own base-resolution (its §2 if [[ -n "$pr_info" ]]; … BASE=… prologue) and substitute the <BASE> you already resolved in sub-step 1 wherever its commands reference $BASE — in a worktree that base is SOURCE_BRANCH, not the default branch, and letting pr-update re-resolve $BASE would diff the worktree PR against the wrong base. This is a by-reference use of that methodology — read its sections and apply them here — not a Skill-tool dispatch of pr-update: /finish must stay the authority over the base, labels, and the terminal SHIPPED-PR tag, and must not trigger pr-update's own interactive PR-state prompts. The branch is already pushed (Step 7), so no push here. (pr-update's own empty-$BASE guard lives in the §2 prologue you're skipping; sub-step 1 above is the backstop for this path — it already stops if it cannot resolve a base, so the substituted <BASE> is non-empty by the time you reach here.)
Write the body to <WT_DIR>/tmp/pr-body-<issue-id-lowercased>.md (worktree mode) or <REPO_ROOT>/tmp/pr-body-<issue-id-lowercased>.md (in-place) using the Write tool (it requires an absolute path) — the same pattern as the merge-commit message file in the merge branch above.
Create the PR — pass --base explicitly, the generated title, the body file, and one --label per verified label:
gh pr create --base '<BASE>' --head '<WORKTREE_BRANCH>' --title '<generated title>' --body-file '<path from step 4>' [--label '<label>' ...]
After the PR is created, present the closing message. The tagged final line (per standards/lifecycle-tags.md) MUST be the last LLM-authored output. Substitute the actual resolved <BASE>, branch, and label list (or the bare word none when no labels were applied). The message branches on whether a worktree is involved:
SOURCE_BRANCH set (worktree PR) — leave the worktree in place; it's the lifecycle boundary. The leading sentence and the tagged line should NOT duplicate the cleanup hint:
This agent-view session is done. The worktree stays in place until the PR merges.
SHIPPED-PR: <ISSUE-ID> — PR opened (base=<BASE>, head=<WORKTREE_BRANCH>), labels: <list|none>. After merge, run `git worktree remove .claude/worktrees/<issue-id-lowercased>` from the main checkout.
After the PR merges, the user removes the worktree manually from the main repo checkout:
# cd to the main repo checkout (parent of .claude/worktrees/), then:
git worktree remove .claude/worktrees/<issue-id-lowercased>
SOURCE_BRANCH empty (plain-branch PR) — there is no worktree to clean up; omit the worktree hint entirely:
This agent-view session is done. The PR is open against <BASE> — review and merge it there.
SHIPPED-PR: <ISSUE-ID> — PR opened (base=<BASE>, head=<WORKTREE_BRANCH>), labels: <list|none>. Review/merge the PR.
linear CLI is not authenticated, prompt: linear auth logintesting
End-to-end Linear issue macro — runs /start then /finish in sequence, gated on the /quality-review verdict. Worktree mode is opt-in via the `wt` token, mirroring /start. Pauses only for plan approval and the deferred-items filing decision; otherwise autonomous. Use when the user says 'full PL-XX', 'ship PL-XX end-to-end', or invokes /full.
development
Adversarial implementation review with triage and fix loop. Hard-gates on `pnpm check`, delegates to the quality-reviewer agent for categorized findings (Critical/High/Medium/Nice-to-Have/Approved), then triages and fixes findings via the developer agent. Loops until a re-review surfaces no new Critical/High/Medium findings (convergence), with a soft ceiling of 5 cycles before asking the user how to proceed; option 3 of that prompt terminates with verdict `escalated-to-architect`. Use when the user says 'review my work', 'check this implementation', 'adversarial review', 'quality review', or invokes /quality-review.
testing
Triage and prioritize Linear backlog. Analyzes issues for staleness, blockers, and suggests priorities based on dependencies and capacity.
testing
Start working on a Linear issue — check blockers, assign, move to In Progress, create branch, plan implementation, execute with checkpoint updates, review and triage findings. Use when the user says 'start issue', 'work on PL-XX', 'begin PL-XX', or invokes /start.