Skip to content
Mahdi Talal

Case study

employment

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

Illustration: an isometric 3D software factory where cobalt code blocks ride a conveyor through three scanner gates, past a small robotic arm, to a final station topped by a glowing marigold approval light
Role
Architect and sole builder
Organization
Empire Media logoEmpire Media
Period
May 2026 – Present
Stack
  • Claude Code
  • Claude API
  • GitHub Actions
  • Python
  • TypeScript
  • Model Context Protocol
  • Playwright

By 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

FlowFrom labelled issue to merged pull request
  1. A person labels a GitHub issue ai-build
  2. Read-only scout and planner subagents map the code and write a plan with testable criteria
  3. The refined spec is posted on the issue; a vague issue gets a question instead of code
  4. One writer session writes failing tests, then the implementation
  5. An adversarial verifier tries to break the change before the pull request opens
  6. Required checks gate it: tests, AI review, AI security review and a frontend build
  7. A juror filters the findings, and a bounded fix loop repairs the upheld ones
  8. 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 or Architecture B
Architecture A: VPS multi-agent orchestratorArchitecture B: GitHub-native (chosen)
IntakeSpreadsheet rows polled by a dispatcherA GitHub issue with the ai-build label
Authoring7 sequential agent subprocesses with a state databaseOne Claude session inside GitHub Actions
Test disciplineA tester agent scores tests after the code existsA PreToolUse hook blocks implementation code until a matching test exists
Merge gateCustom verdict parsingRequired checks, branch protection and CODEOWNERS
Audit trailCustom Postgres tablesGitHub Actions run history
OperationsA server, database, admin UI and backupsNo servers; templates updated by pull request
Estimated build10+ working days6 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.

  1. employment

    Empire Media · Sole developer · Jun 2026 – Present

    Google Workspace MCP Server

    One Claude connector that gives each employee isolated access to their own Google accounts.

    186Tools across 12 Google services

    Read the Google Workspace MCP Server case study

Contact

Let's build

Available for freelance AI projects, and open to the right full-time role