| name | ir:exec |
| description | End-to-end execution of a GitHub issue via `/ir:exec [mode] <N>` — open a worktree, investigate, then either present a visual HTML plan for approval or proceed straight to implementation (mode-dependent), open a PR, review it with /code-review, fix findings, simplify, hand back the PR link with a test-or-merge recommendation, and land the merge on request. `mode` defaults to `auto`, which picks `plan` or `full` from the issue's `/ir:triage` signals. Triggers on "/ir:exec", "exec issue", "implement issue", "plan issue", or when the user gives an issue number/URL and asks to plan, implement, or land it. |
Execute a GitHub Issue, End to End
Take an issue from a number to a review-clean, landed PR:
/ir:exec [mode] <N>
Four modes share one flow; mode defaults to auto when omitted.
ir:exec always works in its own worktree (Phase 1), so there's no separate
"runs in a worktree" naming convention to invoke — the mode is just an
argument.
/ir:exec [mode] <N>
→ worktree → investigate → (auto resolves to plan or full here)
→ plan: HTML + ⛔ approval, or full: inline summary → assign issue
→ implement → verify
→ PR → /code-review (effort scaled to diff) → fix → /simplify (or inline) → recommendation [plan / full stop here]
→ land: confirm mergeable → squash-merge → remove worktree [close]
Modes
| Mode | Invocation | Gate? | Stops after |
|---|
auto (default) | /ir:exec <N> or /ir:exec auto <N> | Decided from the issue's /ir:triage signals — see Auto mode below | Whatever the resolved mode (plan or full) stops after |
plan | /ir:exec plan <N> | Yes — render HTML plan, end turn, wait for explicit approval | Phase 6 (hand-back), once approved |
full | /ir:exec full <N> | No — skip the wait | Phase 6 (hand-back) |
close | /ir:exec close [N] (or replying "merge" to Phase 6) | n/a | Phase 7 (land) |
full still follows every other rule in this skill — worktree isolation, one
branch/PR per issue, AGENTS.md conventions — only the approval wait and the
HTML plan artifact are dropped.
Auto mode
auto never invents a new strategy — it picks between plan and full using
signals /ir:triage already computes, then proceeds exactly as that mode
would from there on:
gh issue view <N> --json labels -q '.labels[].name'
gh issue view <N> --comments | grep -o '\*\*Complexity:\*\*[^—]*' | tail -1
| Signal | Resolves to |
|---|
needs-info label present | Refuse. Don't open a worktree — report the blockers (from the triage comment if present) and point at /ir:triage <N> or manual clarification. |
No readiness label and no /ir:triage comment at all | plan — safest default when nothing has assessed the issue. |
ready-for-agent label + Complexity Low or Medium | full |
ready-for-agent label + Complexity High, or any value that doesn't cleanly parse as Low/Medium (e.g. a hybrid like "Medium-to-High") | plan — a visual plan is still worth a human's eyes before multi-package work starts, even though the label says ready. An ambiguous read of the signal should never resolve toward skipping the gate. |
If the issue was never triaged and has no readiness label, make the same
Low/Medium/High call yourself during Phase 2's investigation, reusing
/ir:triage's calibration (one file/one concern = Low; 2–4 files one slice =
Medium; multi-package/schema/cross-adapter/multi-phase = High) rather than a
new one.
auto only ever resolves to plan or full — never to close (landing is a
step only a human would ask for directly) — and it must never itself invoke
the Workflow tool no matter how high the detected complexity; that AGENTS.md
rule is unconditional.
Inputs
<N> is the issue number — from what the user typed, or from the branch/
worktree name. close additionally resolves it from pwd / git status -sb /
gh pr view if omitted (see Phase 7). If unresolvable, ask before continuing.
Phase 1 — Worktree
- Resolve the issue number and a short kebab slug from its title.
- Open a dedicated worktree off the latest
main (skip if already in a clean,
issue-matching worktree — run pwd + git status -sb to check):
git -C <main-repo> fetch origin main
git -C <main-repo> worktree add -b feat/<N>-<slug> .claude/worktrees/<N>-<slug> origin/main
.claude/worktrees/ is gitignored. Do all work via the worktree path — editing
the main checkout's files by absolute path touches the wrong tree.
Phase 2 — Investigate & plan
- Fetch the issue and its comments (comments often hold the real spec):
gh issue view <N> --comments
- Investigate the code. Delegate to one or more
Explore subagents (thoroughness
"medium") with a prompt naming the issue's key terms — file names, symbols, error
strings, feature names — asking where it lives, what touches it, current behavior.
Don't grep manually first; the subagent protects the main context.
- Synthesize the plan: Problem (one sentence), Approach/Design (the chosen
direction, naming files/functions), Steps (ordered, concrete, one logical change
each), Files touched (new/mod/del), Risks/unknowns. For a user-facing feature,
identify which frontends this repo ships (
platforms/macos/ Swift app,
platforms/web/ dashboard) and scope Approach + Steps to implement it in every
frontend the capability applies to — not just whichever is easiest to reach
first. If one is deliberately excluded, say so explicitly under Risks/unknowns
with the reason; never let a single-frontend implementation land silently as if
it shipped everywhere. (Real incident: the Activity Matrix History chart
landed web-only, its changelog entry didn't say so — unlike the DORA entry
right above it, which explicitly said "on both macOS and web" — and a later
from-scratch QA pass on the macOS app couldn't find the feature at all.)
- If invoked as
auto, resolve the signal per the Auto mode table above and
continue as whichever of plan/full it names — everything from here on follows
that mode's path.
Phase 3 — Present the plan (branches by mode)
plan mode (gated)
-
Render the plan to HTML. Read templates/plan.html (next to this file). Copy it
to /tmp/ir-exec-plan-<N>.html and fill it in. The page reads outside-in — a
stranger to the codebase should understand the top half:
Section roster (order in the template):
- TL;DR —
{{TLDR}}, 2–3 sentences: the problem + the intent. The most-read line.
- High-level design —
{{HLD_INTRO}} + REPEAT:hld bullets. Code-free: no
file or function names here (those belong to the technical Approach below).
- Visual — pick exactly one archetype (see next bullet), or delete all three.
- Approach & Design (technical) — the file/function-level direction (
REPEAT:approach).
- Steps —
REPEAT:step; card = title + one-line summary + one or more chip
spans for the file(s) it touches; the deep rationale/edge-cases go in the step's
<template> (click-to-reveal).
- Files Touched —
REPEAT:file, badge class new|mod|del; per-file "what
changes and why" in its <template>.
- Risks & Unknowns —
REPEAT:risk (native <details>).
Fill primitives: replace every {{TOKEN}}; duplicate each
REPEAT:x…/REPEAT:x region per item and delete the leftover example; delete any
unused OPTIONAL:x region whole. Then strip every REPEAT:/OPTIONAL: marker
comment from the file — they are scaffolding, not output (a kept marker is
harmless but clutters the source).
Pick ONE visual archetype matching the dominant kind of work (delete the others;
delete all three if no visual adds signal — don't ship an empty box):
| Issue kind | Keep block | What to author |
|---|
| Frontend / UI | OPTIONAL:ui | a real screenshot ("Today") + a hand-authored SVG wireframe ("Proposed"); mark each new region <g data-detail="ui-N"> |
| Data processing | OPTIONAL:dataflow | node-and-arrow flow; each node data-detail reveals its transform |
| Vertical slice / many components | OPTIONAL:components | impact-map nodes, data-impact = changed|adjacent|untouched — show the blast radius AND what's left alone |
UI screenshot policy (the ui archetype embeds the real current UI). Obtain the
capture before rendering — never ship the page with an unfilled
{{UI_SCREENSHOT_DATA_URI}}:
- Web UI (a URL exists): use the
claude-in-chrome tools to open the running UI
and screenshot the relevant viewport (not a giant full-page capture). The capture
comes back as a file path — base64-encode it and embed as the "Today"
<img src="data:image/png;base64,…">. Never put a file://, http(s)://, or raw
path in src (that is an external/broken reference, not self-contained).
- Non-URL UI (macOS app, CLI) or no clean capture: there's no reliable capture at
plan time — either ask the user for a screenshot (and wait for it before
rendering), hand-model the current screen as SVG in place of the
<img>, or
delete the OPTIONAL:ui block. Never invent a UI you haven't seen, and never
render with the image token still unfilled.
Interactivity: to make anything click-to-reveal, give it data-detail="<id>"
(the id must be unique within the page) and
put the detail in a sibling <template data-detail-body="<id>">. Do not write event
handlers — the template's inline engine handles it.
Self-containment & hazards:
- Self-contained = no EXTERNAL resources (no URLs, CDN scripts, or web fonts).
The inline
<style> block and the inline <script> engine are part of the
template — keep both byte-for-byte, add no others. data: URIs (the screenshot)
are fine.
- Never write a comment-close sequence inside a comment's text, and never write a
closing
</template> or </script> inside a detail body.
- Before presenting, verify no
{{TOKEN}} is left behind (the page also shows a
warning banner at load time if any slip through).
- One visual archetype max; Steps ≤ 8–10.
-
Present the link, then end your turn. Give the user the
file:///tmp/ir-exec-plan-<N>.html link plus a 2–3 line summary, and stop the
response there — do not present the link and keep working in the same turn. The
next user message is the gate: treat only an explicit approval as go. An ambiguous or
partial reply ("looks good, but…") is a change request — revise the plan + HTML and
re-present. Do not edit a single implementation file until the user approves.
full mode (gate-skipped)
Nobody is gating on the plan, so skip the HTML artifact and the wait entirely:
- Post a short inline plan summary in the response text — Problem, Approach,
Steps, Files touched, each 1 line — no separate render step, no
/tmp file.
- Proceed straight into Phase 4 in the same turn. No stop, no waiting for a reply.
Phase 4 — Implement
- Assign the issue — a gated precondition of starting Phase 4, not a step to
fire-and-forget:
gh issue edit <N> --add-assignee @me
gh issue view <N> --json assignees -q '.assignees | length'
If the count comes back 0, retry the edit once and re-check; if it's still
0, surface that and pause rather than silently proceeding unassigned — the
same idiom Phase 7 uses for an unmergeable PR.
- Push through the implementation in the worktree.
- If the work is complex/multi-part, break it into tasks with
TaskCreate and work
them in order (as you naturally would). For a small change, just implement it.
- Follow the repo's conventions (AGENTS.md): surgical changes, match surrounding
style, three-state model, hexagonal layering, etc.
- Verify before declaring done: run the test suites relevant to what you touched
(per AGENTS.md —
go test ./core/... -race -count=1, the factory/web suites, replay
fixtures, swift build/swift test, as applicable). Fix what you broke. For a
UI-facing change, also confirm it's actually reachable in every frontend the plan
scoped it to — open the macOS app (/ir:test-mac) and/or the web dashboard and
look. A passing test suite in the same area is not evidence the specific feature
exists on a specific platform — name the exact test that covers the claim, or look.
Phase 5 — PR, review, simplify
Calibrate the depth of steps 13–14 to the diff you just produced — a
one-line string edit and a multi-package refactor must not get identical
scrutiny (spending four /simplify subagents on a doc-string is the failure
this guards against). The diff exists as soon as Phase 4 is done; measure it
cheaply first:
git fetch origin main
git diff --shortstat origin/main...HEAD
…then glance at what changed (docs/strings/tests only? new control flow? how
many packages?) and read it into one of four tiers. These are calibration
anchors, not hard gates — use judgment at the boundaries:
| Diff tier | Looks like | Step 13 review | Step 14 simplify |
|---|
| Trivial | docs / comments / string-constants / config only, or ≤~30 non-test lines in 1 file | low | skip the fan-out — do an inline reuse/simplification/efficiency/altitude glance and say so |
| Small | 1–3 files, one concern, no new logic, <~150 lines | low | inline glance, no 4-agent fan-out |
| Medium | 2–5 files / one slice / some new logic | medium | run /simplify (fan-out is fine) |
| Large / risky | multi-package, schema, cross-adapter, logic-heavy, >~400 lines | high | run /simplify (fan-out) |
Guardrails (unconditional): never auto-select /code-review ultra (the
cloud, billed, human-only path) and never use the Workflow tool for either
step. The auto range is bounded low↔high; max/ultra stay
human-triggered.
- Open the PR against
main:
git push -u origin feat/<N>-<slug>
gh pr create --base main --fill
End the PR body with the 🤖 Generated with [Claude Code] line.
- Review the diff at the calibrated effort (
low for trivial/small,
up to high for large/risky — never ultra/Workflow). Run the
/code-review skill with the explicit base origin/main...HEAD (e.g.
/code-review low origin/main...HEAD), then fix every finding it surfaces
in the worktree and push the fixes. A single review pass, not a fan-out.
Pass the base explicitly because in this worktree both of the skill's own
defaults mislead: the local main ref is stale (never updated when other
PRs merge, so main...HEAD drags in already-merged hunks) and after step
12's git push -u, @{upstream}...HEAD is ~empty (upstream now points at
the pushed branch, so it reviews nothing).
- Simplify per the tier. For Medium/Large diffs run the
/simplify
skill with the same explicit base (/simplify origin/main...HEAD, for the
reason in step 13); for Trivial/Small diffs skip its 4-agent fan-out
and do the reuse/simplification/efficiency/altitude review inline, stating
what you checked. Push any cleanup.
Phase 6 — Hand back
- Present the final PR link and ask whether the user wants to test or merge.
Make a recommendation, and let your confidence decide which you lead with:
- Lean merge when:
/code-review came back clean (no unresolved findings), all
relevant suites are green, and the diff is small/low-risk and fully covered by
tests. Suggested: proceed to Phase 7 (land), or /ir:exec close.
- Lean test-first when: review raised non-trivial findings, tests are
failing/flaky/absent for the behavior, the diff is large or risky, or the change
is user-visible and only confirmable by running it. Point at
/verify, or
/ir:test-mac for macOS-app changes.
State the recommendation in one line with the reason; the merge decision is the
user's. A reply of "merge" (or /ir:exec close) moves into Phase 7.
Phase 7 — Land (close mode)
Self-sufficient: this phase resolves the issue/PR itself rather than assuming
continuity from earlier phases, so it works standalone or as a continuation of
Phase 6.
- Resolve the worktree, branch, and PR if not already known from context —
pwd / git status -sb for the worktree and branch, gh pr view for the PR —
the same way Phase 1 resolves the issue number.
- Confirm the worktree is clean and pushed:
git status -sb shows nothing
outstanding and the branch is up to date with its remote.
- Confirm the PR is mergeable:
gh pr view <N> --json mergeable,state. If
checks are pending or failing, surface that and pause rather than forcing
the merge.
- Merge:
gh pr merge --squash (no --delete-branch — keep the remote
branch, per existing repo convention).
- Clean up the local worktree:
git -C <main-repo> worktree remove <path>,
and move the session back to the main repo.
- Confirm final state (
git worktree list) and report the merged PR link.
Notes
- The approval gate (
plan mode) is real — never start editing before the user
accepts.
- Keep the plan tight: if Steps run past ~8–10 entries, you're over-planning; collapse.
- If the issue is ambiguous, surface it under Risks/unknowns in the plan rather than
guessing — that's what the approval gate is for.
- One worktree + one branch + one PR per issue.
- Scale Phase 5 to the diff (the tier table there): trivial changes get a
low
review and an inline simplify glance, not a high review and a four-agent
fan-out. Depth follows the change, and never auto-escalates to ultra/Workflow.
- User-facing features ship on every applicable frontend (macOS + web), or the plan
says explicitly why not (Phase 2). Verify each one directly rather than trusting an
adjacent green test suite (Phase 4) — see the Activity Matrix incident above.