How to Build an AI Coding Loop That Turns GitHub Issues Into Pull Requests
In the previous article, I argued that developers should spend less time writing individual prompts and more time designing loops that generate the right prompt from live context.
This article builds one.
By the end, you will have an AI coding workflow that can run with either Codex or Claude Code and can:
- Wake up on a schedule.
- Read open GitHub issues with an eligibility label.
- Select one issue that is safe and testable.
- Turn the issue and repository state into a working task.
- Implement the change on a branch.
- Run tests, lint, and type checking.
- Use failures as context for another attempt.
- Prepare a pull request for human review.
The loop will not merge code, deploy anything, or make product decisions. That boundary is deliberate. We want useful autonomy with an obvious review point.
What We Are Building
Our example repository is a small TypeScript API called account-api.
It contains this fictional issue:
Issue 184: Usernames with spaces return a server error
Creating a user with a username that begins or ends with spaces returns a server error. Trim the username before validation. If the trimmed username is empty, return a validation error. Add regression tests.
The issue has the label agent ready.
Every weekday morning, the coding agent will inspect issues with that label. If issue 184 is the best safe candidate, the agent will reproduce it, create a branch, implement the fix, run the repository checks, and prepare a pull request.
The architecture is intentionally small:
Scheduled automation
↓
GitHub issue triage skill
↓
Selected issue plus required proof
↓
Maintainer loop skill
↓
Implement → verify → inspect → retry or stop
↓
Pull request ready for review
There are three durable inputs:
- A repository instruction file describes how to work inside this repository.
- The triage skill decides which issues qualify.
- The maintainer skill controls execution, verification, retries, and stopping conditions.
The automation prompt stays short because the durable policy lives in those files.
Prerequisites
You need:
- A GitHub repository cloned locally.
- GitHub CLI installed and authenticated.
- Codex or Claude Code with access to the repository.
- A test command that works locally.
- Permission to create branches and pull requests.
Confirm GitHub CLI access:
gh auth status
gh repo view --json nameWithOwner,url
Confirm the repository is healthy before adding automation:
git status --short --branch
npm ci
npm test
npm run lint
npm run typecheck
If the repository does not pass its normal checks before the loop starts, fix that first or document the known failure precisely. An agent cannot reliably prove it caused no regression when the baseline is already ambiguous.
Step 1: Create the Eligibility Label
Create a label that means an issue may be considered by the loop:
gh label create "agent ready" \
--description "Eligible for autonomous triage and implementation" \
--color "1D76DB"
The label is an admission signal, not permission to do anything the issue requests.
The triage skill will still reject work involving security policy, credentials, billing, destructive migrations, unclear product behaviour, or missing verification.
That distinction matters. GitHub issue text is untrusted task input. It can be mistaken, incomplete, stale, or malicious. The loop must inspect the repository and apply its own rules before acting.
Step 2: Add Portable Repository Instructions
Create an AGENTS.md file at the repository root:
# Repository Instructions
## Setup
Install dependencies with `npm ci`.
## Verification
Run focused tests for the changed area first.
Before preparing a pull request, run:
1. `npm test`
2. `npm run lint`
3. `npm run typecheck`
Do not weaken, delete, or skip an existing test to make a change pass.
## Implementation Rules
Reproduce reported bugs before changing code when feasible.
Prefer the smallest maintainable change that addresses the verified root cause.
Add regression coverage for bug fixes.
Do not change authentication policy, authorization rules, billing, database schemas, public API contracts, or infrastructure without owner approval.
## GitHub Rules
Work on one issue at a time.
Create a dedicated agent branch. Use `codex/issue-<number>-<short-name>` with Codex or `claude/issue-<number>-<short-name>` with Claude Code.
Pull requests must include the issue link, root cause, implementation summary, exact verification commands, results, and remaining risks.
Never merge automatically.
AGENTS.md is repository policy. It should contain commands and conventions that apply to all coding work, not just this automation.
Claude Code reads CLAUDE.md rather than AGENTS.md. Do not maintain two copies of the same rules. Add this small CLAUDE.md beside AGENTS.md:
@AGENTS.md
## Claude Code
Project skills live under `.claude/skills/`.
Claude Code supports importing another instruction file with the @path syntax. This lets Codex and Claude Code share the same repository policy while keeping tool specific configuration small. See Claude Code project memory.
Run these instructions manually once before proceeding:
Read AGENTS.md. Inspect the repository and tell me whether every documented setup and verification command currently works. Make no changes.
Correct any stale command now. A scheduled loop is the wrong place to discover that the repository renamed typecheck six months ago.
Step 3: Add the Triage Skill

Create the skill in the location used by your coding agent:
# Codex
.agents/
skills/
github-issue-triage/
SKILL.md
# Claude Code
.claude/
skills/
github-issue-triage/
SKILL.md
The SKILL.md content is identical in both locations. Claude Code follows the Agent Skills open standard and loads project skills from .claude/skills/. See Claude Code skills.
Add the following content:
---
name: github-issue-triage
description: Select one safe, bounded, and verifiable GitHub issue for autonomous work.
---
# GitHub Issue Triage
Use the current GitHub repository.
## Readiness Gate
Before triage, inspect `AGENTS.md`, the current branch, and repository status.
List open issues labelled `agent ready`:
`gh issue list --state open --label "agent ready" --limit 30 --json number,title,url,labels,updatedAt`
For each plausible candidate, read its body and comments:
`gh issue view <number> --json number,title,body,comments,labels,url`
Search for related open and recently closed issues and pull requests before selecting work.
## Classification
Classify every inspected issue as one of:
* `Autonomous`
* `Needs owner`
* `Defer`
An autonomous issue must have all of the following:
* Bounded scope
* Clear expected behaviour
* A plausible reproduction path
* A usable verification path
* No unresolved product decision
* No required secret, privileged account, or destructive action
Issues involving security policy, privacy, authentication policy, authorization, billing, destructive migrations, broad public behaviour, unavailable live proof, or unclear acceptance criteria need the owner.
Prefer reproducible bugs, documentation corrections, tests, and narrow internal improvements.
Select no more than one issue.
## Output Contract
Return:
* Repository name
* Selected issue number, title, and URL
* Classification and confidence
* Why the issue qualifies
* Expected behaviour
* Required reproduction
* Required verification
* Material risks
* Conditions that require stopping
If no issue qualifies, return `No work` and explain why. Make no repository changes.
This skill does not implement anything. It converts a queue of loosely written reports into one structured work item with explicit proof requirements.
The design is adapted from Peter Steinberger's more comprehensive GitHub Project Triage skill. The production skill covers broad repository discovery, contributor trust, pull request repair, CI, live proof, and maintainer operations. Our version keeps only what this single repository loop needs.
Step 4: Add the Maintainer Loop Skill
Create the second skill beside the first one:
# Codex
.agents/
skills/
issue-to-pr-loop/
SKILL.md
# Claude Code
.claude/
skills/
issue-to-pr-loop/
SKILL.md
Add:
---
name: issue-to-pr-loop
description: Take one approved GitHub issue through reproduction, implementation, verification, and pull request preparation.
---
# Issue to Pull Request Loop
Process exactly one issue at a time.
## 1. Protect Repository State
Record:
* `git status --short --branch`
* `git branch --show-current`
* `git rev-parse HEAD`
* `git remote -v`
If the worktree is dirty, the current branch contains unrelated work, the default branch is diverged, or the checkout cannot be updated safely, stop with `Blocked`.
Never stash, reset, clean, overwrite, or delete existing work.
On a clean default branch, fetch and update with fast forward only.
## 2. Confirm the Assignment
Read the selected issue, all comments, related issues, related pull requests, `AGENTS.md`, and relevant source and tests.
Treat the issue's proposed diagnosis as a hypothesis.
If the requested behaviour is unclear or conflicts with current tests or documented policy, stop with `Needs owner`.
## 3. Reproduce Before Editing
Reproduce the reported behaviour on the current default branch when feasible.
Prefer a focused failing test. Record the command and the important failure output.
If the issue cannot be reproduced, investigate whether it is already fixed, environment specific, incomplete, or incorrect. Do not implement the requested patch blindly.
## 4. Create an Isolated Branch
Create an agent branch only after the issue is confirmed. Use the prefix permitted by the active platform: `codex/` for Codex or `claude/` for Claude Code.
Do not reuse a branch that contains unrelated changes.
## 5. Implement
Identify the root cause and implement the smallest maintainable fix.
Add regression tests that fail before the fix and pass after it.
Do not weaken tests or broaden behaviour to make verification pass.
## 6. Verify
Run focused tests first, then every verification command in `AGENTS.md`.
Inspect `git diff --check`, `git diff --stat`, and the full diff.
Confirm the diff contains no unrelated files, secrets, generated noise, debug logging, or accidental dependency changes.
## 7. Retry With Evidence
When a check fails, use the command, exit code, and relevant output as context for the next attempt.
Retry only when the failure provides a concrete next action and the work remains within the approved scope.
Stop after three failed implementation attempts or sooner if progress stops. Return `Blocked` with the evidence gathered.
Never delete or weaken a failing test merely to reach green.
## 8. Prepare the Pull Request
Only after all required checks pass:
* Commit the scoped change.
* Push the branch.
* Create a pull request.
* Link the issue with `Closes #<number>`.
* Include root cause, implementation, exact checks, results, and remaining risk.
Never merge the pull request.
## Terminal States
Every run must end as exactly one of:
* `Ready for review`
* `Needs owner`
* `Blocked`
* `Rejected`
* `No work`
Report the issue URL, branch, changed files, verification evidence, pull request URL when created, and the exact next human action.
This skill is the state machine. It determines what must be true before each transition.
It is adapted from Steinberger's Maintainer Orchestrator skill, which manages a much larger portfolio, worker ownership, recovery, CI, releases, and owner decisions. Starting with that entire production system would be overkill. The useful pattern is to preserve state, serialize work, demand proof, and escalate decisions instead of guessing.
Step 5: Test Triage Without Allowing Changes
Do not schedule the loop yet.
Open the repository in your coding agent and run:
Use the github issue triage skill.
Inspect open issues labelled agent ready in the current repository. Select no more than one autonomous candidate and return the complete triage output contract.
Do not edit files, create branches, comment on GitHub, or create a pull request.
For issue 184, a useful result should resemble:
Selected: Issue 184, Usernames with spaces return a server error
Classification: Autonomous
Confidence: High
Why it qualifies: The behaviour is narrow, acceptance criteria are explicit, and user creation has focused tests.
Required reproduction: Add or run a request using leading spaces, trailing spaces, and whitespace only input. Confirm the current branch returns the reported server error.
Required verification: Focused user creation tests, full test suite, lint, type checking, and diff review.
Stop if: Existing tests show whitespace is intentionally preserved, normalization affects authentication identity, or the fix requires a database migration.
If the triage output does not mention proof or stopping conditions, improve the skill before granting write access.
Step 6: Run the Full Loop Manually

Once read only triage is reliable, run one issue manually:
Use the github issue triage skill to select one open issue labelled agent ready.
If and only if it is classified Autonomous, use the issue to PR loop skill to reproduce, implement, verify, and prepare a pull request.
Follow AGENTS.md. Work on one issue only. Never merge. Stop on product, security, privacy, credential, destructive action, or repository state uncertainty.
Watch the first run closely.
The most important moment is not when the model writes the code. It is when something fails.
Suppose the focused test passes, but the full suite fails because an account import test expects surrounding spaces to remain unchanged.
A weak agent may delete the old expectation or normalize every code path.
The loop should instead reason from the evidence:
Full verification failed in the account import suite.
The selected issue covers interactive user creation. Import behaviour is a separate existing contract.
Inspect whether normalization can remain inside the user creation path. Do not change import behaviour or weaken its test. If a shared helper would necessarily change both paths, stop with Needs owner and explain the choice.
This is the core loop:
attempt
→ observe result
→ classify result
→ generate next instruction from evidence
→ attempt again or stop
The failed command becomes part of the next prompt automatically.
Step 7: Turn the Working Loop Into an Automation
Only schedule the workflow after it succeeds manually and produces a pull request you would be comfortable reviewing.
Use this automation instruction:
Every weekday at 8:00 AM, run the issue to pull request workflow in the current repository.
First use the github issue triage skill to inspect open issues labelled agent ready. Select no more than one issue.
If no issue qualifies, return No work and make no changes.
If one issue is classified Autonomous, use the issue to PR loop skill to reproduce it, implement the smallest maintainable fix, add regression coverage, run every verification command in AGENTS.md, inspect the final diff, and prepare a pull request.
Never process more than one issue per run. Never merge. Never deploy. Never modify an existing dirty checkout. Never weaken tests to make a check pass.
Stop with Needs owner for product, security, privacy, authentication policy, authorization, billing, destructive migration, credential, or unavailable live proof decisions.
Stop with Blocked when repository state is unsafe, required tooling is unavailable, verification cannot run, or three evidence based implementation attempts fail.
End with exactly one terminal state and include the issue URL, branch, changed files, verification commands and results, pull request URL when created, residual risk, and the next human action.
The prompt is portable. What changes is the scheduler that invokes it.
Run It With Codex
Create a Codex automation from the working repository thread and use the prompt above. Set it to run every weekday at 8:00 AM.
Codex automations can run recurring tasks on a schedule and return the result for review. OpenAI recommends making them specific, repeatable, and easy to review. For local runs, the computer must be awake and Codex must be running. See Codex Automations.
Codex reads the repository rules from AGENTS.md and the two skills from .agents/skills/.
Run It With Claude Code
Claude Code offers three useful scheduling surfaces.
The closest equivalent is a cloud Routine. Open Claude Code and run:
/schedule weekdays at 8am, run the issue to pull request workflow for this repository
During setup, select the repository and use the full automation prompt above. Claude Code Routines run on Anthropic managed cloud infrastructure, so the laptop does not need to remain awake. Each run starts from a fresh clone, can load skills committed under .claude/skills/, and creates a reviewable session. By default, branch pushes are limited to names beginning with claude/. Routines are currently a research preview. See Claude Code Routines.
If the workflow needs local files or tools, use a Claude Code Desktop scheduled task instead. If it belongs in CI, use Claude Code GitHub Actions with a scheduled trigger. For quick polling inside an open terminal session, Claude also provides /loop, but those session tasks expire and require the session to remain available. See Claude Code scheduling options.
Claude reads the shared rules through CLAUDE.md, which imports AGENTS.md, and reads the same skill content from .claude/skills/.
The schedule does not make the workflow intelligent. It only wakes it up. The skills, repository rules, live issue, source code, and verification output create the task context.
Step 8: Review the First Scheduled Runs
For the first several runs, check:
- Did it select only labelled issues?
- Did it inspect comments and related work?
- Did it preserve repository state?
- Did it reproduce before editing?
- Did the new test fail for the expected reason?
- Did it run every repository check?
- Did it inspect the final diff?
- Did it stop when the scope changed?
- Did the pull request state exactly what was proven?
- Did it avoid merging?
When you correct the same behaviour twice, move the correction into the right durable layer:
- Repository command or convention belongs in
AGENTS.md. - Issue selection policy belongs in the triage skill.
- Retry, verification, or escalation policy belongs in the maintainer skill.
- Timing and run limits belong in the automation.
Do not keep fixing durable workflow problems with longer one time prompts.
What the Generated Worker Task Looks Like
The reader never writes this prompt manually, but the loop effectively assembles it:
Work on issue 184 in owner/account-api.
Reported behaviour: User creation returns a server error when a username contains surrounding spaces.
Expected behaviour: Trim before validation. Reject an empty normalized value with the existing validation response.
Triage: Autonomous, high confidence. The user creation path has focused tests. Scope is limited to interactive user creation.
Required proof: Reproduce on the current default branch. Add regression cases for leading spaces, trailing spaces, and whitespace only input. Run focused user tests, npm test, npm run lint, npm run typecheck, git diff --check, and full diff inspection.
Repository rules: Follow AGENTS.md. Preserve unexpected work. Do not change import behaviour, authentication identity policy, database schemas, or public contracts.
Terminal action: Prepare a pull request with Closes #184 after all checks pass. Never merge.
That is a strong prompt because it contains the symptom, expected behaviour, risk classification, local policy, exact proof, boundaries, and terminal action.
No person copied those pieces into a chat box. The loop composed them from current state.
Common Failure Modes
The Loop Chooses Vague Issues
Tighten the admission gate. Require a reproduction path and acceptance criteria before applying agent ready.
It Starts Coding Before Reproducing
Make reproduction a transition gate in the maintainer skill. Require recorded evidence before branch creation.
It Changes Too Much
Add diff size and file scope checks. Treat changes to authentication, migrations, dependencies, generated files, or public contracts as escalation triggers.
It Keeps Retrying Forever
Set an attempt limit and require concrete progress between attempts. A failure with no new evidence should stop the run.
It Declares Success Without Running Everything
Put exact commands in AGENTS.md. Require commands, exit results, and gaps in the pull request body.
It Damages Existing Work
Never let the loop clean, reset, or stash an unexpected checkout. Dirty state is a terminal blocker, not an inconvenience.
It Creates Pull Requests Nobody Trusts
Improve the proof section. A useful autonomous pull request should show the reproduction, root cause, regression test, full verification, diff scope, and residual risk.
How Far Should You Extend It?
Once this loop is predictable, you can add:
- CI monitoring after the pull request opens.
- Automatic repair for clearly attributable CI failures.
- Duplicate issue detection.
- Pull request review feedback as another loop input.
- Multiple repositories with one serial worker per repository.
- A release gate after explicit human approval.
Do not start with automatic merging.
The first useful maturity level is not code shipping without humans. It is a system that can turn well formed issues into reviewable, verified pull requests without a human repeatedly translating context into prompts.
The Finished System
You now have five connected pieces:
GitHub label
decides what may enter
AGENTS.md
defines repository rules and proof commands
Triage skill
selects one safe issue and defines required evidence
Maintainer skill
controls state, implementation, retries, and terminal conditions
Codex automation or Claude Code Routine
wakes the loop on a schedule
The model still reasons. The prompts still exist. You simply stopped writing each prompt by hand.
You wrote the system that produces them.
References
- Codex Automations
- Codex use cases
- Claude Code skills
- Claude Code Routines
- Claude Code scheduling options
- Claude Code project memory
- GitHub Project Triage skill
- Maintainer Orchestrator skill
- Stop Prompting AI. Start Engineering Loops.
Written by Ali Hamie, software engineer at Meta and founder of Bits & Bonds.
Keep reading
Useful next steps
Stop Prompting AI. Start Engineering Loops.
The next evolution of AI assisted engineering is not writing better prompts. It is building loops that discover work, gather context, verify results, and generate the next prompt for you.
Read nextTechBC Has 20 Parking Apps. That's Not a Feature. That's a Failure.
BC drivers juggle up to 20 different parking apps, pay junk fees, and got hit with a QR code scam in 2025. This is a tech governance failure. Here's what a real fix looks like.
Read nextTechI Built an App for the FIFA World Cup in One Night. Here's Exactly How.
I had an idea, a plan, and one night. Here's how I built a full PWA for FIFA World Cup 2026 fans in Vancouver and Toronto using AI, NotebookLM research, and zero CMS.
Read nextNewsletter
Get new posts in your inbox.
Finance and tech insights for Canadians — no spam, unsubscribe any time.
