# End to End: From Issue to Merged Pull Request
You have an issue sitting in your GitHub repo, and by the end of this loop you want a merged pull request that closes it, with a human reviewing the diff as the final gate. That is the whole job, and Codex is built to run most of it while you stay in control.
This lesson walks that loop concretely: pick up the issue, let Codex branch and implement, run the tests, open the PR, review, and merge. The discipline that makes it work is not fancy: branch hygiene, small diffs, and treating review as a real checkpoint, not a rubber stamp.
Codex is OpenAI's software engineering agent. It runs in an isolated cloud sandbox, reads your repo, edits files, runs commands, and pushes branches. You reachreachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → it from the Codex sidebar in ChatGPT and from the Codex CLI in your terminal, and it connects to GitHub through the Codex GitHub integration so it can open and update PRs directly.
The mental model: Codex is a teammate who can do the mechanical work of a change fast, but who never merges its own code. You assign scope, it produces a diff, you review and merge. GitHub's branch protection is what enforces that boundary, so set it up first.
On the repo, enable branch protection on your default branch:
Now even an agent that pushes code cannot land it without a green build and a human approval. This is the single most important setting for letting an agent work in a real repo.
Start from a real, well-scoped issue. Codex works best when the issue reads like a spec, not a vibe. Compare:
Weak: "Login is flaky."
Strong: "Users with emails containing a + alias (e.g. sam+test@x.com) get rejected at signup. Expected: RFC-compliant addresses with + are accepted. See validateEmail in src/auth/validate.ts. Add a failing test first."
The strong version names the file, the expected behavior, and asks for a test. That constraint alone tends to cut the diff in half.
In the Codex sidebar, point it at the repo and paste the task. If your repo has a codex.md or an AGENTS.md file at the root, Codex reads it as standing instructions: how to run tests, code style, what not to touch. Treat that file as the durable version of your custom instructions for this codebase.
# AGENTS.md
## Commands
- Install: `pnpm install`
- Test: `pnpm test`
- Lint: `pnpm lint`
## Conventions
- TypeScript strict mode. No `any`.
- One logical change per PR. Keep diffs under ~150 lines.
- Never edit files under `src/generated/`.Do not let Codex work on main. Tell it to create a branch with a clear name tied to the issue:
codex "Fix issue #214: accept + aliases in validateEmail.
Branch: fix/214-email-plus-alias.
Write a failing test first, then make it pass."Good branch names are boring and traceable: fix/214-email-plus-alias, feat/187-csv-export. The issue number in the branch name means anyone can trace code back to the conversation that created it. This is branch hygiene: one branch, one intent, easy to delete after merge.
Codex spins up its sandbox, clones the branch, and starts working. You will see it read files, form a plan, and run commands. Let it run, but read the plan it proposes before it writes much. If the plan already smells too big (it wants to refactor three modules for a one-line bug), stop it and narrow the scope now, not at review time.
The order matters, and it is worth being explicit with Codex: failing test first, then the fix. This is the agent version of test-driven development, and it does two things. It proves the bug exists, and it proves your fix actually addresses it rather than something adjacent.
A clean loop for our example looks like this:
# tests/test_validate.py
import pytest
from src.auth.validate import validate_email
@pytest.mark.parametrize("email", [
"sam+test@example.com",
"sam+news+promo@example.com",
])
def test_plus_alias_is_accepted(email):
assert validate_email(email) is True
def test_bare_plus_still_rejected():
assert validate_email("+@example.com") is FalseCodex adds the test, runs it, watches it fail, then edits validate_email until the suite is green. Because it runs commands in its sandbox, you get the real output, not a guess about whether the code works. If a test fails in a way it did not expect, it iterates. Watch that it does not "fix" the test to match broken behavior. That is a classic agent failure mode, and it is why you asked for the failing test up front: you can read the test and confirm it encodes the behavior you actually want.
When it finishes, Codex reports what changed and why. Read that summary, but do not trust it in place of the diff.
Have Codex open the PR from its branch into main. A good agent-authored PR body is structured so a reviewer can approve in minutes:
## What
Accept email addresses with `+` aliases in `validateEmail`.
## Why
Closes #214. Aliased addresses are RFC 5321 valid; we were rejecting them.
## Changes
- `src/auth/validate.ts`: widened local-part regex to allow `+`.
- `tests/test_validate.py`: added alias-accept and bare-plus-reject cases.
## Testing
- `pnpm test` passes (2 new cases).Note Closes #214. That keyword auto-links the PR to the issue and closes the issue when the PR merges. It keeps your board honest with zero extra clicks.
The PR triggers your CI. Because you required status checks, the merge button stays disabled until the build passes. This is your first automated gate doing its job before a human even looks.
This is the step you never delegate. Open the Files changed tab and read every line as if a junior engineer wrote it, because functionally one did.
What to look for specifically in agent-generated diffs:
+ and also accept garbage. Read the actual pattern. This is where subtle security and correctness bugs live.Small diffs make this possible. A 40-line PR gets a real review; a 600-line PR gets skimmed and waved through, which defeats the entire point of the human gate. If Codex produced something large, split it: ask it to open the change as two or three sequential PRs, each independently reviewable.
You can also leave review comments directly on the PR and ask Codex to address them. It pushes new commits to the same branch, CI re-runs, and you re-review the delta. That comment-driven loop is where most of the real collaboration happens.
Knowledge check
1. Why is enabling GitHub branch protection described as the single most important setting before letting Codex work in a real repo?
2. What is the core mental model for how Codex fits into the issue-to-merge workflow?
3. Why does the lesson stress writing an issue that 'reads like a spec, not a vibe'?
4. Select ALL correct answers about the guardrails recommended for the default branch before starting.
Select all the correct answers.
5. Select ALL correct answers about the discipline that makes the issue-to-merge loop work.
Select all the correct answers.
Once CI is green and you have approved, merge. Prefer Squash and merge for agent work: the branch may contain a dozen tiny iteration commits ("fix test", "oops", "lint"), and squashing collapses them into one clean commit on main with your PR title as the message. History stays readable.
After merge, delete the branch. GitHub offers this as a button right after merging, and you can turn on auto-delete in repo settings so stale agent branches never pile up. The issue closes automatically because of your Closes #214 line. The loop is complete: issue in, reviewed and tested change out, clean history.
You will move between two surfaces, and they suit different tasks:
Both open PRs; both respect your AGENTS.md. The read on platform.openai.com/docs and the developer docs will always be more current than any tutorial, so bookmark them.
The reason this works at team scale is that Codex slots into a process that already exists. You are not inventing a new merge policy for AI. You are pointing an agent at the same branch protection, CI, and review culture your humans use, and the agent inherits those guardrails. The failing-test-first habit, small diffs, and mandatory human approval are exactly the practices that make human PRs safe. They make agent PRs safe for the same reasons.
The failure mode to avoid is speed pressure: Codex produces diffs fast, and it is tempting to approve fast to match. Resist that. Your throughput bottleneck should be review quality, not agent speed, because review is the only step that catches the plausible-but-wrong change.
Closes #NNN in the PR, keep history and your board clean automatically.