+190 XP

End to end: from issue to merged pull request

# 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.

Where Codex lives in this workflow

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 reach 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.

Set the guardrails before you start

On the repo, enable branch protection on your default branch:

  • Require a pull request before merging.
  • Require at least one approving review.
  • Require status checks (your CI) to pass.

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.

Step 1: Pick up the issue

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.

markdown
# 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/`.

Step 2: let codex branch

Do not let Codex work on main. Tell it to create a branch with a clear name tied to the issue:

bash
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.

Step 3: Implement and test the change

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:

python
# 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 False

Codex 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.

Codex: From Issue to Pull Request

Watch on YouTube

Step 4: Open the pull request

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:

markdown
## 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.

Step 5: Review the diff (the real gate)

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:

  • Scope creep. Did it only touch what the issue needed? Reformatted files, renamed unrelated variables, and "while I was here" edits inflate the diff and hide the real change. Ask Codex to revert anything off-topic.
  • The regex or logic that "fixes" it. A widened regex can accept + and also accept garbage. Read the actual pattern. This is where subtle security and correctness bugs live.
  • Test quality. Does the test fail without the fix? Does it assert the right thing? A test that passes even with the bug present is worse than no test.
  • Secrets and config. Confirm nothing hardcoded a key or changed an env default.

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.

Vérification des acquis

1. What is the core mental model the lesson uses to describe Codex's role in the issue-to-merge workflow?

2. Why does the lesson call branch protection 'the single most important setting' when letting an agent work in a real repo?

3. According to the lesson, why does a strongly-scoped issue produce better results than a vague one like 'Login is flaky'?

CHOIX MULTIPLES

4. Select ALL of the branch protection rules the lesson recommends enabling on the default branch.

Sélectionnez toutes les réponses correctes.

CHOIX MULTIPLES

5. Select ALL statements that accurately reflect the disciplines and practices the lesson emphasizes.

Sélectionnez toutes les réponses correctes.

Step 6: Merge and clean up

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.

A note on the two ways to run Codex

You will move between two surfaces, and they suit different tasks:

  • Cloud (ChatGPT sidebar / GitHub integration): best for asynchronous, self-contained tasks you kick off and check later. Assign it, walk away, review the PR when it pings you.
  • Codex CLI (local): best when you want to watch and steer in real time, run it against uncommitted local state, or work in a repo with local-only tooling.

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.

Why this loop scales

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.

Key Takeaways

  • Set branch protection before you let Codex touch the repo. Require a PR, a review, and passing checks so an agent physically cannot merge unreviewed code.
  • Write issues like specs. Name the file, the expected behavior, and ask for a failing test first. Tight input produces tight, reviewable diffs.
  • Keep one branch per intent and squash on merge. Traceable branch names with the issue number, plus Closes #NNN in the PR, keep history and your board clean automatically.
  • Never delegate the diff review. Read every line for scope creep, sketchy logic, and weak tests. Small diffs are what make honest review possible.
  • Match the surface to the task: cloud Codex for fire-and-forget PRs, the Codex CLI when you want to watch and steer live.

À faire, tiré de cette leçon

Ces actions sont compilées dans le plan d'action du rôle.

  • Set branch protection requiring PR, review, and passing checks before Codex runs
  • Run autonomous loops only with a machine-checkable done condition and hard caps
  • Review every diff for the deleted-failing-test cheat and scope creep
Voir le plan d'action complet