| name | issue-to-pr |
| description | Take one or more GitHub issues and drive each to a review-ready pull request, holding one belief throughout: an issue is a hypothesis, not a spec. The pipeline VERIFIES BEFORE IT TRUSTS and runs TWO ADVERSARIES before it commits: multi-agent investigation of the real cause (flagging any "landmine" — a cited function or file:line that does not exist) and the blast radius, a plan whose test must go RED BEFORE GREEN, two adversarial gates on that plan (a self/ethos checklist + a pure codex adversarial pass), implementation expressed as a forged `/goal` (via goal-forge) whose verify loop runs the otbox acceptance journey, a green suite + docs sync, a PR that `Closes #<n>`, and a FINAL codex adversarial review of the diff. Honesty, not theater: it reports unmet acceptance criteria and invalid issues rather than faking a fix. The human merges. Everything happens in a per-issue git worktree; multiple issues each get their own worktree + branch + PR. Use when the user says "issue-to-pr", "solve issue", "fix issue #N", "take issue N to a PR", "investigate and fix #N", or hands over one or more issue numbers/URLs. For a plan-only pass use /new-plan; for docs sync use /docs-update; this is the full issue→PR pipeline.
|
issue-to-pr
Distilled from the issue-#85 run. The load-bearing belief: an issue is a hypothesis, not a spec. Issues drift from the code, cite functions that no longer exist, and assert causes that aren't the real cause. So the pipeline verifies before it trusts, and runs two independent adversarial passes before committing to a plan — because on #85 both the codex pass and the self red-team independently caught the same real defects that a single reviewer missed.
Leading words (leitwörter)
A handful of leading words carry this skill. Use them by name — in your plan, your commits, your PR body, and your own reasoning. Each compresses a whole behavior into one token you can repeat to keep yourself honest, and each is borrowed from a discipline whose instincts you want to borrow with it. They recur on purpose.
- An issue is a hypothesis (science) — never trust the issue's claims; reproduce the cause and check every assertion against the current tree before you believe it. When you catch yourself about to trust a line number, say the words and go verify.
- Landmine (the refuted claim) — a cited file:line / function / API that does not exist or is wrong. Name landmines out loud and route around them; never build on a landmine (#85 cited two functions that did not exist).
- Blast radius (incident response) — everything a fix could disturb: callers, tests, frozen
opentraces.*.v1 contracts, the schema, docs. Map the blast radius before you touch code.
- Red before green (TDD) — the new test must FAIL on the buggy code before it passes on the fix. A test that has never been red is theater, not evidence.
- Acceptance is an otbox journey (opentraces' own UAT substrate) — unit and subprocess tests prove the mechanism; a user- or agent-observable change is not accepted until an otbox journey drives the real CLI/agent in an isolated box and asserts on real state (the way #85 shipped with 8 journeys). Unit-test-only is not acceptance. If a fix is genuinely not user/agent-observable, say so and justify it — never skip the journey silently.
- Two adversaries, then commit (red-teaming) — the self/ethos pass AND codex each try to BREAK the plan; when two independent adversaries converge on the same defect, believe it. Adversarial before commit, not after.
- Honesty, not theater (the house rule) — report residue, partial failures as errors, unmet acceptance criteria, and invalid issues. A green checkmark that proves nothing is theater.
- The simplest change that closes the issue (pragmatic minimalism) — extend what exists; resist the speculative refactor riding along.
- The human merges (the gate) — assume forward and surface every decision in the PR, not in a question; but never merge — the merge is theirs.
Anti-patterns (the failure modes these words name)
State the leitwort, then catch yourself in its opposite:
- Building on a landmine — taking the issue's file:line / function name as truth, so the fix targets something that doesn't exist or the wrong place. → an issue is a hypothesis.
- Green theater — a test that passes on the unfixed code, so it proves nothing. → red before green.
- Silent no-op — a reverser/handler that returns ok without doing (or undoing) anything; a skip with no reason; a partial failure dressed as success. → honesty, not theater.
- One-reviewer confidence — shipping a plan a single pass approved. → two adversaries, then commit.
- The interview spiral — stalling on questions the PR could answer. → assume forward; the human merges.
- Scope bloom — a refactor the issue never asked for. → the simplest change that closes the issue.
- Unit-test-only acceptance — proving the mechanism in a unit test but never showing a user or agent experience the change end-to-end. → acceptance is an otbox journey.
This skill is the main-loop spine: it owns judgment, the worktree/PR lifecycle, the codex MCP calls, the human gate, and multi-issue orchestration. It delegates the bounded, read-only parallel fan-out to the Workflow tool (Stage 1), and expresses the implementation as a forged /goal (Stage 4) whose verify loop runs the otbox acceptance journey — so the implement↔verify and otbox-verify loops are one. Implementation is sequential in one worktree — a deliberate v1 choice: parallel file-editing across worktrees is conflict-prone and rarely pays off for a typical issue, so parallelism is confined to the phases where it is free and safe (investigation + multi-lens review).
Effort policy
Reasoning depth is a lever — spend it where judgment matters. Max for planning and adversarial testing; xhigh for normal development.
| Phase | Effort | Why |
|---|
| Plan (Stage 2) · both adversarial gates (3a self/ethos, 3b + 6 codex) · final synthesis | max | judgment and adversarial reasoning is where depth pays off — two adversaries, then commit |
| Implementation (Stage 4) and other normal dev work | xhigh | thorough, but mechanical |
| Investigation (Stage 1, read-only fan-out) | high | bounded evidence-gathering; reasoning needn't be maxed for grep-and-report |
Apply it through whatever lever the phase uses:
- Main-loop stages (you — planning, self/ethos, implementing): run at the tier above — set
/effort max before the plan + adversarial stages and /effort xhigh for implementation, or simply reason at that depth.
- Workflow
agent() calls: pass effort: 'high' (investigation) or effort: 'max' (any adversarial red-team fan-out — e.g. the codex-unavailable fallback).
- codex MCP (
mcp__codex__codex): config: { model_reasoning_effort: "high" } — "high" is codex's top reasoning tier (the Claude-side max/xhigh names don't apply to codex; high is the adversarial max there).
Model policy
Opus across the board. Run the main loop — planning, the adversarial gates, implementation, synthesis — on the latest Opus (the session default; don't downgrade it). The only exception is cheap, high-volume, mechanical work, where the latest Sonnet is appropriate: the Stage-1 investigation lenses (read-only grep-and-verify fan-out — N per issue, the highest fan-out in the pipeline, especially across multiple issues) run on model: 'sonnet', while their judgment-bearing synthesis runs on Opus. codex (Stages 3b/6) is GPT — Claude model selection doesn't apply there.
Inputs
/issue-to-pr <n> or /issue-to-pr <n> <m> ... — accepts issue numbers, #n, or full GitHub issue URLs. The repo is the current repo (gh repo view). No issue argument → ask which issue(s).
Multi-issue orchestration
- Run Stage 1 (investigation) for ALL issues in parallel — it's read-only and cheap.
- Then take issues one at a time through Stages 2→7 (plan → gate → implement → PR). Each issue gets its own worktree + branch + PR. Sequential keeps the human gate legible and avoids cross-issue worktree thrash.
- If the user explicitly asks to background whole-issue pipelines (truly independent issues), you may, but default to sequential.
- Report a per-issue status line at the end (PR URL or terminal-outcome reason).
Per-issue pipeline
Stage 0 — Intake & worktree (preflight; fail fast and honestly)
- Resolve the issue:
gh issue view <n> --json number,title,body,state,labels,comments. If it's not OPEN, or not a real/actionable issue (discussion, duplicate, "wontfix"), STOP for this issue and report why — do not invent work.
- Preflight the environment (each is a terminal outcome for this issue if it fails, with a clear message — never half-start):
gh auth status succeeds (else: "gh not authenticated — run gh auth login").
- The base branch (
main) is clean enough to branch from (uncommitted changes on the base are fine; you branch off the committed base).
- No branch/worktree collision: if
fix/issue-<n>-* or the target worktree path already exists, reuse it only if it's clearly a prior run of THIS issue; otherwise pick a suffixed name and say so.
- Create the worktree off a FRESH base — fetch first, branch off the remote-tracking ref, so the fix never starts from a stale local
main:
base=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
git fetch origin "$base"
git worktree add -b fix/issue-<n>-<slug> ../<repo>-issue-<n> "origin/$base"
(feat/… if the issue is labeled feature/enhancement.) Build/reuse a venv inside it (python3 -m venv .venv + pip install -e packages/opentraces-schema + pip install -e ".[dev]") — run it in the background while you investigate. The #85 lesson: in-process tests can't isolate import-bound $HOME paths, so the worktree + a fresh venv + $HOME-isolated / subprocess tests are the reliable substrate.
- All file work for this issue happens in that worktree from here on.
Stage 1 — Investigate (multi-agent, read-only) → Workflow · effort: high
Run the bundled investigation workflow, scoped to this issue:
Workflow({
scriptPath: ".claude/skills/issue-to-pr/issue-investigate.workflow.js",
args: { issue: <n>, title: "<title>", body: "<issue body>", repoRoot: "<worktree path>" }
})
Pass args as a real JSON object (the workflow also defensively parses a JSON-string form). Keep body to the issue's substance — the workflow re-reads the repo itself, so a multi-KB body isn't needed and risks the stringified-args footgun.
It fans out read-only Explore agents to produce three things (see references/stage-contracts.md for the exact return schema). Throughout, hold the line: an issue is a hypothesis — the workflow's job is to confirm, correct, or refute it against the current tree.
- Cause — the real mechanism that produces the behavior, with file:line evidence. An issue is a hypothesis; find the actual cause, not the issue's asserted one.
- Claim verification — every file:line / function / API the issue cites, checked against the CURRENT tree:
confirmed / drifted (corrected file:line) / refuted (a landmine — the issue is wrong; #85 cited two functions that did not exist) / not-found.
- Blast radius — callers, tests, docs, and frozen consumer contracts (
opentraces.*.v1 envelopes, schema, otbox journeys) the fix could disturb.
Read the consolidated report. If investigation shows the issue is invalid / already-fixed / misdiagnosed, that is a terminal outcome: report it (with evidence) and stop — a correct "this isn't a bug, here's why" beats a fabricated fix.
Stage 2 — Plan · effort: max
Create the plan at <worktree>/kb/plans/issue-<n>.md (mkdir -p <worktree>/kb/plans first — kb/ is gitignored, so a fresh worktree has no kb/; the plan is a working doc that won't be committed, which is correct). Use the absolute worktree path everywhere downstream (the forged goal references it). It MUST contain (template in references/stage-contracts.md):
- Root cause — from Stage 1, with file:line.
- Fix approach — the simplest change that closes the issue, grounded in verified (never landmine) file:line and existing patterns (extend a registry/verb, additive + reversible schema evolution, data-safe by default, honesty not theater). Prefer modifying what exists over siloing something new; leave no scope bloom.
- Test plan — two layers, both named explicitly:
- Mechanism (unit /
$HOME-isolated subprocess e2e): proves the fix works at the code level; carries the red-before-green proof below.
- Acceptance — the otbox journey (required for any user/agent-observable change): which new or extended
tests/otbox/catalogue/journeys/<name>.toml drives the real CLI/agent in an isolated box and asserts on real state, its tier (0 = runs in default CI; 1 = opt-in, needs captured-session checkpoints / real daemons), the seed/checkpoint, the [[steps]], and the exact assertion kinds. Acceptance is an otbox journey — name the journey here. If the change is genuinely not user/agent-observable (pure internal refactor with no behavior change, a perf budget, docs-only), state that explicitly and justify why no journey applies — do not skip silently.
- Test-completeness proof (executable, not aspirational) — exactly how you will PROVE the test exercises the fix: a captured red-before-green output (the new test fails on the pre-fix code) OR a temporary reintroduce-the-bug patch that makes the new test fail for the specific verified cause. A test that passes on the buggy code is not evidence and does not count. The otbox acceptance journey must likewise fail on the unfixed code and pass on the fix (or, for tier-1 journeys that don't run in default CI, be runnable on demand and verified by hand).
- Acceptance map — map to the issue's ACs; call out any AC that cannot be met (e.g. it depends on a refuted/fictional surface) rather than papering over it.
Stage 3 — Dual adversarial gate on the plan · effort: max
Two adversaries, then commit. Two independent passes each try to BREAK the plan; fold both in before any code. Run this stage at max effort (and codex at its top tier) — adversarial reasoning is exactly where depth earns its cost.
3a — Self / ethos (main-loop checklist). Score the plan against references/ethos-checklist.md (the product-direction rubric derived from CLAUDE.md "Key Decisions"). Resolve every ❌. This is your own adversarial pass — be harsh.
3b — Codex pure adversarial (Plan Reviewer). Delegate read-only to a different model:
- Locate the reviewer prompt (do not assume
$CLAUDE_PLUGIN_ROOT is set — it usually isn't): p=$(find ~/.claude -path '*/prompts/plan-reviewer.md' 2>/dev/null | head -1) (it lives under the delegator plugin). Inject its contents as developer-instructions. If find returns nothing, write an equivalent inline instruction: "You are a ruthless plan reviewer; APPROVE/REJECT with file:line evidence; reject any plan that relies on a function/API that doesn't exist."
- Call
mcp__codex__codex with sandbox: "read-only", cwd: "<worktree path>", config: {model_reasoning_effort: "high"}, and a 7-section prompt that points codex at the plan file + the real source and tells it to BREAK the plan (missed surfaces, wrong API shapes, fictional refs, data-loss, test gaps).
- Codex-unavailable fallback (MCP tool absent — headless/cron): run the bundled red-team workflow as the external adversary —
Workflow({scriptPath: ".claude/skills/issue-to-pr/redteam.workflow.js", args: {kind: "plan", target: "<abs plan path>", repoRoot: "<worktree>"}}) — and note in the plan that the codex gate was substituted.
Fold both passes into the plan. Convergence between 3a and 3b on the same defect is the strongest signal. Re-run a pass only if a blocker materially reshaped the plan.
Stage 4 — Implement as a forged /goal, with otbox verify inside the goal loop · effort: xhigh
Implementation is not ad-hoc coding — it is a persistent goal whose verification surface includes the otbox acceptance journey, so the implement↔verify loop and the otbox verify loop are the same loop. The goal is not DONE until the journeys are green. (goal-forge's rule: the evaluator judges only what lands in the transcript — so the verification surface must be commands that run and report, which is precisely what folds the otbox run into the goal's completion check.)
4a — Forge the implementer goal with /goal-forge --claude --fast. Supply the six slots from the hardened plan so goal-forge drafts without interviewing (template in references/stage-contracts.md):
- Outcome — the plan at
<worktree>/kb/plans/issue-<n>.md is implemented in the worktree; the mechanism tests AND the named otbox acceptance journey(s) are GREEN; the red-before-green proof is captured.
- Verification surface (each must emit its summary line into the transcript):
.venv/bin/python -m pytest <focused mechanism tests> -q → then the acceptance journey: tier-0 .venv/bin/python -m pytest tests/otbox/test_otbox_slice.py -k "<journey>", or tier-1 OT_OTBOX_TIER1=1 ./otbox matrix --journey '<journey>' → then the broad regression .venv/bin/python -m pytest tests/cli tests/core tests/capture -q.
- Constraints — don't regress the suite; preserve frozen
opentraces.*.v1 envelopes + the schema; data-safe; honesty, not theater.
- Boundaries — only the files in the plan's ownership, inside the worktree
<path>; never merge; don't touch unrelated modules.
- Iteration logging —
<worktree>/runs/issue-<n>/log.md; per attempt record the diff, the test/journey output observed, and the decision.
- Blocked stop condition — append a
BLOCKED: entry with attempted paths + the input that would unlock; stop after the same failure survives ~3 attempts rather than thrash.
4b — The goal contract (the loop):
- DONE when: the plan is implemented, the mechanism tests pass, the otbox acceptance journey(s) pass (tier-0 in CI / tier-1 hand-verified), and the broad regression is green — every claim backed by command output in the transcript.
- NOT DONE (loop again) when: any of those is red. Triage, fix in source, re-run the same verification surface — including the otbox journey — every iteration. A green unit suite with a red journey is NOT done; acceptance is an otbox journey.
- BLOCKED (stop + report) when: a fix needs a decision you don't have, the same failure survives ~3 attempts, or a new landmine invalidates the plan. Log
BLOCKED: and surface it — don't loop forever.
4c — Run it. Run the forged Goal as the session /goal (Claude Code /goal, v2.1.139+ — needs workspace trust + hooks enabled; see goal-forge's harness notes) and work it to DONE; or, when the session's single goal slot is needed elsewhere or issues run in parallel, hand the forged prompt to an implementer subagent for an isolated run. If /goal is unavailable (no trust / hooks disabled), run the SAME forged contract by hand — the DONE / NOT-DONE / BLOCKED conditions + the run log still drive the loop; only the Stop-hook evaluator is missing. Either way the loop is the same: implement → run mechanism tests + the otbox journey(s) → triage → repeat. Match surrounding code; reuse the reversers/registries the investigation surfaced. otbox authoring gotchas (#85): path_exists doesn't expand ~ (use {opentraces_dir} / {project}); stdout_json_contains is substring (use stdout_json exact / stdout_json_set_contains for arrays); a shell step expected to exit non-zero needs expect_returncode = N; run the catalogue lint; a new CLI verb/flag/envelope trips the surface-sweep / snapshot tests (update GLOBAL_JSON_ONLY + regenerate json_envelope_shapes.json — part of the work).
Stage 5 — Tests green + docs + PR
/docs-update FIRST — always a step (standing directive). It self-scopes: if the change touches no doc surface it's a clean no-op; if it adds/changes a CLI verb, flag, schema field, or install/uninstall flow, it syncs README / installation / commands / SKILL.md / llms.txt. Never hand-edit llms.txt — regenerate it.
- Then re-confirm the relevant suite + the acceptance journey are green — docs-update may have mutated tracked files (llms.txt, docs), so verify after it, not before, or you commit an unverified state.
- Commit (conventional-commit subject; end the message with the
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> trailer).
- Push the branch; open the PR:
gh pr create --base <base> --head fix/issue-<n>-<slug> --title "..." --body-file <body>
The body MUST contain Closes #<n> (so the merge auto-closes the issue), a cause→fix→tests narrative, the test-completeness evidence, the otbox acceptance journey(s) added + their tier and CI status (acceptance is an otbox journey), and any unmet ACs (honesty). If gh pr create fails (no remote / perms), report the pushed branch + the exact command for the user to run.
Stage 6 — Final codex adversarial pass at the PR · effort: max (codex: high)
- Locate the reviewer prompt (
p=$(find ~/.claude -path '*/prompts/code-reviewer.md' 2>/dev/null | head -1); inline an equivalent if absent), inject it as developer-instructions, and call mcp__codex__codex (sandbox: "read-only", cwd: "<worktree>", config: {model_reasoning_effort: "high"}) on git diff <base>...HEAD. Tell it to hunt correctness / data-safety / regression holes and to try to BREAK the change, not nitpick.
- Incorporate the feedback: apply judgment (codex can be wrong), fix the real findings (each with a regression test), re-run tests, push. Run a focused confirm round on any PARTIAL findings — the #85 loop converged in three rounds.
- Post a PR comment documenting the adversarial loop (findings → resolution) so the human reviewer sees the gate closed.
- Codex-unavailable fallback: run the bundled red-team on the diff —
Workflow({scriptPath: ".claude/skills/issue-to-pr/redteam.workflow.js", args: {kind: "diff", target: "git diff <base>...HEAD", repoRoot: "<worktree>", base: "<base>"}}) — and say so in the PR comment.
Stage 7 — Present (human gate — never merge)
- Confirm CI is green (
gh pr checks <pr>); if red, fix until green or surface the failure.
- Present the PR for review-before-merge: PR URL, cause→fix→test→evidence summary, CI status, any unmet ACs, and (if the worktree should be cleaned up) the cleanup command. The merge is the human's decision — do not merge.
Worktree lifecycle & cleanup
- One worktree + branch + venv per issue.
- On a successfully-presented PR, offer to remove the worktree (
git worktree remove <path>); keep it on failure and report its path + branch so the user can inspect.
- Don't auto-
git worktree prune other people's worktrees — this repo accumulates them; only clean up the ones this run created.
What this skill does NOT do
- It does not merge — ever. The human merges.
- It does not fabricate a fix for an invalid issue, paper over an unmet AC, or trust an issue's file:line / API claims without verifying them.
- It does not parallelize file-editing across worktrees in v1 (parallelism is confined to read-only investigation + review).
- It does not count a test that passes on the buggy code as evidence.
Failure / terminal outcomes (report, don't force)
issue-not-actionable (closed/discussion/duplicate) · issue-invalid (investigation refutes it) · gh-unauthenticated · dirty/colliding-worktree (resolve or rename) · tests-cannot-run (env broken) · codex-unavailable (substitute a Workflow red-team) · pr-create-failed (report branch + command). Each ends THIS issue's pipeline cleanly and moves to the next issue.