Case study
employmentAutonomous AI Coding Pipeline
An autonomous AI coding pipeline built on GitHub Actions and Claude Code that has opened 200+ pull requests across four company repositories, with TDD guardrails, bounded retry loops and gated review before every merge.
- Role
- Architect and sole builder
- Organization
Empire Media
- Period
- May 2026 – Present
- Stack
- Claude Code
- Claude API
- GitHub Actions
- Python
- TypeScript
- Model Context Protocol
- Playwright
By Mahdi Talal Published Updated
The Autonomous AI Coding Pipeline is a GitHub-native system I designed and built at Empire Media. A person labels an issue; a Claude agent plans the change, writes failing tests, implements it and opens a pull request, which AI reviewers and CI gate before a person merges. It has opened 200+ pull requests across four company repositories (pipeline GitHub history, Sep 2026), and most of the engineering went into the guardrails that make that safe on real client code.
- Pull requests opened
- 200+
- Source: My count from the pipeline's GitHub history · Sep 2026
The problem
Empire Media ships code into several client products and in-house tools, each on its own stack. In May 2026 every pull request was written by hand, so a senior developer was the bottleneck on every client simultaneously. My PRD put it plainly: a senior on vacation halts authoring on every client at once (PRD v2.2, May 2026). Planning was implicit, tests were optional and security review came after the code.
I wanted well-written issues to become tested, reviewable pull requests on any of those repositories, without a new platform to operate.
Constraints
- A tiny team. One AI engineer (me) and one DevOps lead (PRD v2.2, May 2026). Anything that needed its own operator was out.
- Different stacks. Node and TypeScript, Next.js, Python and FastAPI. Stack, test commands and danger zones had to live in one committed file per repository.
- The agent can't be trusted with its own rules. It must not edit its hooks, workflows or schemas, push to protected branches or send credentials anywhere.
- GitHub's limits. The Claude Code GitHub Action can't approve a pull request (PRD v2.2, May 2026), so AI verdicts had to land as required checks. The gate is branch protection with required checks, plus CODEOWNERS.
- Real client work. A bad merge reaches a client's production, so every loop needs a hard limit and a hand-back to a person.
Architecture
- A person labels a GitHub issue ai-build
- Read-only scout and planner subagents map the code and write a plan with testable criteria
- The refined spec is posted on the issue; a vague issue gets a question instead of code
- One writer session writes failing tests, then the implementation
- An adversarial verifier tries to break the change before the pull request opens
- Required checks gate it: tests, AI review, AI security review and a frontend build
- A juror filters the findings, and a bounded fix loop repairs the upheld ones
- A person merges, and a weekly retro turns lessons into memory pull requests
Everything runs inside GitHub Actions, and every Claude Code session runs through anthropics/claude-code-action; there is no orchestration server to keep alive.
Planning. A Haiku classifier picks the authoring model. A read-only scout reads the repository's memory index and the relevant code, and a read-only planner returns a schema-validated JSON plan: at most 5 sub-tasks, each with positive and negative criteria, boundary criteria where a limit exists and a safety criterion for danger zones (architecture spec, Jun 2026). A deterministic validator rejects happy-path-only plans, and the spec is posted on the issue before any code exists. A vague issue gets questions for up to 2 rounds, then a blocked status instead of a guess (installer release notes, Jun 2026).
One writer, many readers. Exactly one session holds the Edit and Write tools. The scout, planner, verifier and juror are read-only subagents with restricted tools.
Guardrails at the tool-call layer. Claude Code hooks run before tool calls, and the agent can't edit them: a TDD guard, a block on pushes to production and integration branches, a sensitive-path guard that protects the pipeline's own hooks, workflows and schemas, a credential-egress guard, and a migration guard that refuses to open a pull request when a data-model change has no migration. The TDD guard, in illustrative pseudo-code:
before Write or Edit on a path:
if the path matches the repository's exemptions (tests, docs, generated files): allow
if no matching test file exists: deny "write a failing test first"
otherwise: allow
The agent reads the denial and writes the test first. That's a stronger guarantee than a tester agent grading code that already exists.
Bounded loops. Every loop has a limit and a recorded escalation: up to 5 iterations per sub-task, a forced change of approach after 3 identical errors, and up to 3 rounds with the adversarial verifier (architecture spec, Jun 2026). After the gates, fixes get 3 attempts on Sonnet, then 2 on Opus, before the pull request is labelled needs-human (installer release notes v2.2.0, Jul 2026).
Gates and the juror. Every pull request needs the Tests, claude-review and claude-security checks; full-stack repositories add a path-aware Frontend build with Playwright visual regression (installer release notes, Jun 2026). When a gate requests changes, a Haiku juror rules on each finding with file-and-line evidence and a confidence score. Findings below 8 out of 10 become advisory, and the most severe security findings are never demoted (architecture spec, Jun 2026), so false positives don't use up the fix attempts.
Memory with two trust channels. Each repository commits a memory folder: an index, feature files, conventions, gotchas and decisions. Code facts change in the same pull request as the code; lessons arrive only through a weekly retro that opens a memory pull request for a person to review. The agent proposes; it never rewrites its own rules.
A portable installer. One command copies the proven files into a repository; the one required choice is the integration branch. It never commits, pushes, sets secrets or applies branch protection itself.
Key decisions
Architecture A or Architecture B
In May 2026 I prototyped Architecture A: a deterministic TypeScript harness driving a chain of Claude Agent SDK agents from a spreadsheet queue. The next PRD compared it with a GitHub-native design, and I chose B.
| Architecture A: VPS multi-agent orchestrator | Architecture B: GitHub-native (chosen) | |
|---|---|---|
| Intake | Spreadsheet rows polled by a dispatcher | A GitHub issue with the ai-build label |
| Authoring | 7 sequential agent subprocesses with a state database | One Claude session inside GitHub Actions |
| Test discipline | A tester agent scores tests after the code exists | A PreToolUse hook blocks implementation code until a matching test exists |
| Merge gate | Custom verdict parsing | Required checks, branch protection and CODEOWNERS |
| Audit trail | Custom Postgres tables | GitHub Actions run history |
| Operations | A server, database, admin UI and backups | No servers; templates updated by pull request |
| Estimated build | 10+ working days | 6 working days |
The table's figures come from the PRD (PRD v2.2, May 2026). Everything A would have built already exists in GitHub, and B's failure modes are GitHub's, documented and status-paged, not mine to discover at night.
One writer instead of an agent team
The research behind the architecture spec was consistent: agent teams that all write code lose context between agents and make conflicting edits. More agents only helped where they were read-only.
Hooks, not instructions, for what must never happen
A prompt can ask the agent to stay off production; a hook refuses the tool call. Production pushes, guardrail edits, credential egress, missing migrations and untested code are all hook-enforced. The prompt carries conventions.
Results
- 200+ pull requests opened across four Empire Media repositories (pipeline GitHub history, reported by me, Sep 2026).
- Installed in four company repositories: ApplyBoost's job-application platform, an e-commerce repricing engine, an e-commerce client's portal sandbox and an internal dashboard, between the first install on 25 May and the v2.2 back-port on 23 July 2026 (repository histories).
- A dashboard written largely through it. Much of the internal Spend Tracking Dashboard was written through the pipeline from its PRD. On its first day, 18 June 2026, 20 pipeline pull requests were squash-merged, covering the scaffold, schema, ingestion and first views; by September its main branch held 144 commits from the pipeline's bot account (dashboard git log, Sep 2026).
What didn't work
- The agent edited CSS blind. Until June 2026 the gates were backend-only, so the agent changed styles it never saw; one scrollbar took 7 pull requests to get right (pipeline v2 update notes, Jun 2026). The fix was a fourth required check with Playwright visual regression in a pinned container, plus skills that render the change in a browser before the pull request opens.
- Letting the pipeline merge its own work. At the start of the dashboard build I added an auto-merge step for pull requests with all three gates green, and it was active during the first-day merges above. I removed it on 25 June 2026, and a person has merged since (dashboard repository history, Jun 2026). Gates are good filters, not a replacement for a person who owns the result.
- Template drift. The installer's workflow copies drifted from the live pipeline by hundreds of lines, and one placeholder left a wrong branch fact in a repository's memory for 3 weeks (installer release notes v2.1.0, Jul 2026). The same release added a version stamp and a drift report.
- Quiet failures. Fresh installs silently ignored the documented TDD exemptions because of a missing marker (installer release notes v2.2.0, Jul 2026). The pipeline's own rails need loud failures as much as the code it writes.
- Thin outcome data. I stand behind the pull-request count, but I haven't published measured first-attempt pass rates or issue-to-PR times.
Stack
- Orchestration: GitHub Actions,
anthropics/claude-code-action, branch protection, required checks, CODEOWNERS - Models: Claude Opus and Sonnet for authoring and fixes; Claude Haiku for classification and the juror
- Agent layer: Claude Code subagents, skills and hooks; MCP servers for library docs (Context7) and browser checks (Playwright)
- Rails: Python and Bash hooks and scripts, JSON Schema structured outputs
- Frontend gate: Playwright visual regression in a pinned container
Questions
How do you stop a coding agent from breaking production?
Don't rely on the prompt. Here, hooks that run before tool calls block pushes to production branches, edits to the pipeline's own guardrails and credential egress, and the agent can't modify those hooks. Every change still passes required checks, and a person merges it.
Why use one writing agent instead of a team of agents?
Agents writing in parallel lose context between each other and make conflicting edits. One session gets write access; read-only subagents scout, plan, verify and judge review findings. That keeps one coherent context across the test, implement and fix loop.
How do you enforce test-driven development on an AI coding agent?
With a hook, not an instruction. Before any write to an implementation file, a PreToolUse hook checks that a matching test file exists and denies the write if there isn't one. The agent writes the test first; its instructions then require it to see the test fail before implementing.
What happens when AI reviewers reject the agent's pull request?
A juror subagent rules on each finding; low-confidence findings become advisory, and the most severe security findings are never dismissed. Upheld findings go to a fix session with 3 attempts on Sonnet, then 2 on Opus (installer release notes v2.2.0, Jul 2026), and after that the pull request is labelled needs-human.
Can an autonomous coding pipeline merge its own pull requests?
This one doesn't. I ran an auto-merge step for the first week of one build in June 2026 and removed it; merging is a human decision in every repository the pipeline runs in.
Links
- How I built the multi-tenant Google Workspace MCP server, a remote connector with its own OAuth 2.1 server
- The anthropics/claude-code-action repository on GitHub, the action the pipeline's Claude Code sessions run in
- The Claude Code hooks reference, which documents the PreToolUse hooks the guardrails are built on
- GitHub's guide to protected branches and its guide to code owners, the merge gate described above
- All projects and case studies