
My team and I are building a mobile app in a private repo. Like any real app, bugs do not arrive politely one at a time. They come in clusters: a UI edge case here, a state bug there, a data mismatch somewhere else, and a vague "this flow feels broken" report that might be a real product problem or might just need better reproduction steps.
If we treat every issue as a separate emergency, two bad things happen.
First, we lose time to context switching. Every developer or AI coding session has to re-read the repo, understand the app architecture, figure out which files matter, and rediscover which issues overlap.
Second, the codebase starts collecting cleanup debt. A rushed bug fix might solve the visible symptom but leave duplicated logic, unclear ownership, missing tests, or a hidden second bug in the same path.
That is what the issue-pipeline repo is built to prevent.
It is not a magic bug-fixing bot. It is a small set of Claude Code slash commands plus a deterministic shell library that turns a pile of GitHub issues into scoped, reviewable, parallel work streams.
The main idea is simple: diagnose before changing code.
The workflow has three main commands:
There is also a parallel version of the first step:
The flow looks like this:
Each stream gets its own branch, worktree, terminal, prompt, and PR. That isolation is the part that keeps the work from turning into one giant risky branch.
/issue-phase-0 is read-only. No code edits. No worktrees. No "while I am here" cleanup.
It starts with a precheck through the shell library:
That verifies the session is running from the main checkout, on the default branch, with a clean working tree. That sounds basic, but it matters. A dirty tree at the start of a bug batch is how unrelated local changes accidentally get folded into bug-fix PRs.
Then the pipeline asks which issues to work on. We can give it specific issue numbers, a gh issue list filter, or something like "all open bugs." It fetches the issues, filters out non-actionable items, and asks for confirmation before proceeding.
The useful part is the diagnosis report. Phase 0 writes:
That report captures:
This is where tech debt gets stopped early. We are not just asking "how do I patch bug #142?" We are asking "does bug #142 share files, state, or root cause with #138 and #145, and should those land together or separately?"
That is the difference between bug fixing and codebase maintenance.
For bigger batches, /issue-phase-0-parallel fans out one investigator subagent per issue.
Each investigator is read-only and has one job: inspect a single issue and write a JSON report with the title, diagnosis, likely files touched, dependencies, prefix, scope, confidence, and notes.
The orchestrator then aggregates those reports and builds the stream graph:
That last point is important. The pipeline does not pretend that every AI investigation succeeded. If an issue is unclear, malformed, or under-investigated, the report says so. That prevents false confidence from becoming production code.
After reviewing the Phase 0 report, we run:
This is setup only. It does not fix anything.
It parses the diagnostic report, pulls the latest default branch, and creates one worktree per stream:
Each worktree gets a branch with a conventional prefix:
The shell library validates those prefixes. That small constraint gives us predictable branches and commit history. Bugs become fix/..., cleanup work becomes refactor/..., test-only work becomes test/..., and so on.
The setup command also copies local environment files into the worktree, detects the package manager from the lockfile, installs dependencies, creates a .claude directory, and writes a stream-specific prompt:
That prompt contains the stream's issues, branch, file scope, ordered tasks, constraints, and stop condition.
The result is not "Claude, go fix some bugs." It is "Claude, in this worktree, on this branch, for these issues, touch this scope, run these checks, commit each task, open a PR, and stop."
That level of specificity is what keeps AI-assisted development from turning into random walk refactoring.
Inside each worktree, we run:
The command verifies that it is actually running inside a recognized stream worktree. If someone accidentally runs it from the main checkout, it stops.
Then the stream session reads .claude/STREAM_PROMPT.md and follows the workflow:
gh issue view.Closes #N.That stop condition matters. The stream does not self-merge. It does not manually close issues. It does not keep wandering after the PR is open.
Human review stays in the loop.
The pipeline helps because it turns implicit engineering discipline into explicit steps.
Clean starting point. The precheck rejects dirty working trees and non-default branches before investigation begins.
No code before diagnosis. Phase 0 produces a report before anyone edits files. That gives us a shared map of root causes, file overlap, and merge risk.
Parallel work is based on file scope. Streams are grouped by actual code paths, not by gut feel. If two issues touch the same files, they land in the same stream or get serialized.
Every stream has boundaries. The stream prompt names the files in scope and tells the agent not to edit outside them.
Small commits are expected. The stream workflow asks for one commit per task or cohesive group, each referencing the relevant issue.
PRs close issues, not agents. The PR body uses Closes #N, so GitHub closes issues only when the reviewed code lands.
Low-confidence work is surfaced. The parallel phase has explicit buckets for unanalyzed issues, suspected duplicates, and low-confidence investigations.
Deterministic operations are not left to the LLM. The shell library owns project discovery, status moves, worktree creation, package manager detection, and stream prompt writes.
That last point is the design center. The LLM should diagnose, group, and explain. The script should do the repeatable plumbing.
issue-pipeline-lib.sh is the boring part, which is exactly why it is valuable.
It provides subcommands for:
precheckdetect-pmdiscover-projectconfig-loadmove-issuecreate-worktreewrite-stream-promptin-worktree-checkThose commands handle the details that should not depend on prompt quality: checking git state, finding the GitHub Project, moving issues to "Investigating" or "In Progress", creating branches and worktrees, and verifying the current session is in the right place.
The repo also has a two-tier test harness:
Tier 1 covers pure bash logic like package-manager detection, worktree checks, branch-prefix validation, and prompt writes. Tier 2 stubs GitHub CLI paths for project discovery and issue status moves. In the local copy I inspected, Tier 1 has 38 assertions and Tier 2 has 20.
For a workflow that is supposed to keep bugs controlled, testing the workflow itself is not optional.
This pipeline is useful, but it is not a replacement for judgment.
The .env copy step is a plain copy. That is convenient for local development, but it means we need to be careful about what lives in those files and where worktrees are created.
The cached GitHub Project config is scoped to one repo. That is fine for our private mobile app repo, but it is not a multi-repo portfolio scheduler.
The tests cover the shell library, not live GitHub end-to-end behavior. That is the right tradeoff for fast local confidence, but it means we still pay attention during first use in a real repo.
And most importantly, the pipeline stops at PR creation. Review is still review. If the implementation looks too broad, misses tests, or drifts from the stream prompt, we fix that before merge.
The most valuable thing about issue-pipeline is not that it lets us run several AI coding sessions at once. Parallelism is nice, but uncontrolled parallelism just creates merge conflicts faster.
The valuable thing is that it forces structure:
That is how we keep our mobile app moving without letting every bug fix become a little pile of hidden debt.
AI can write a lot of code quickly. The hard part is making sure the code lands in the right shape. For us, the issue pipeline is the layer that turns a messy issue queue into bounded engineering work.