Skip to content
Developer Blog

I Typed a Ticket Number and a Feature Came Out

Bogi Bordas

Software Engineer

5738e30b-a5e2-4f72-8521-bdcd0a6f1109

Part one of three. Three months of running an AI agent pipeline on real tickets.

I spent several months running an AI agent pipeline on JIRA tickets. This is the first of three posts about how that operated, including the details of building a pipeline where agents do the work and the engineer keeps control of the decisions that matter.

Most AI-generated code gets the architecture wrong. It produces something plausible, something that compiles, something a tired reviewer might approve on a Friday afternoon. What it doesn't usually do is read your actual codebase and follow the patterns already in it. I expected the same.

So I gave the pipeline a ticket number, /feature XST-228, and watched it try anyway.

The agent read the ticket. Then it read the codebase. Then, without being told, it picked out the exact Angular component pattern the team had settled on three months earlier and built the whole plan around it.

That's what this post is about. Not "we use AI now." Not "productivity is up X percent." Something more specific: what it looks like to build a pipeline where agents do the work and the engineer keeps the decisions that matter, and what you learn about your own workflow in the process.

What the pipeline does

The whole pipeline hangs off a single slash command in Claude Code, Anthropic's AI coding agent: /feature. The app it builds features for is a browser-based PDF viewer and form editor, an Angular front end over a rendering backend. Give it a Jira ticket key or a written description and it runs a chain of specialised agents until the feature lands as a commit.

The chain:

  1. An architect reads the codebase and writes an implementation plan.
  2. An implementer codes against the plan while a second agent drafts end-to-end (e2e) browser tests in parallel, against a test-hook contract the plan defines.
  3. A QA agent writes and runs unit and integration tests.
  4. A reviewer checks the result, code and tests together, against a defined checklist.
  5. The drafted e2e specs get validated against the real feature before anything is committed.

The words workflow and agent get used interchangeably in most writing about this, and the difference matters. A workflow is a pipeline whose steps are fixed in advance. An agent decides what to do next. This pipeline is a hybrid of the two. Its order is a workflow: the steps live in code, not in anyone's judgment. Inside each step is an agent: the architect decides how to read the codebase, the reviewer decides what to flag, the implementer decides how to write the code. Fixed structure where structure is enough, reasoning only where nothing else will do.

Two things are worth noting upfront. The pipeline takes either a Jira ticket or a plain written description (it’s a spec runner, not a Jira integration). And the engineer never leaves the loop: the plan requires approval before any code is written, and the review output is read before anything is committed. The agents handle the mechanical work. The judgment calls stay human.

The plan file

Before the implementer writes a single line, the architect produces a document, a plan file saved to disk, and every agent after it reads that file instead of starting cold. Skip this and each agent works from nothing: the implementer doesn't know what the architect decided or why, the reviewer doesn't know what it's checking against. What you get is a chain of prompts that happen to line up, but only by luck.

The shape:

 
# Plan: XST-711 — Context menus: infrastructure
_Generated: 2026-07-02_

## Pipeline State    ← branch, step counter — the resume point for interrupted runs
## Specification     ← the acceptance criteria, verbatim
## Architect Plan    ← the approved plan, including the E2e Hooks contract
## Changes           ← appended as the run progresses: implementer discoveries,
                       reviewer findings

 

The plan becomes the run's shared memory, and because the pipeline writes its own state into the file after every step, a run that gets interrupted resumes instead of restarting.

Before it is finalised, the architect asks questions. A missing payload shape, an edge case nobody specified, a constraint that could reasonably go either way: instead of guessing, it surfaces the gap and waits. The template asked for those questions from the first version in April; what took another month was the step that reads them back and waits for answers before the plan can be approved at all. The alternative is an assumption, a feature built against it, and the mismatch turning up in manual testing after the PR is open. Catching a gap before approval costs a minute; catching it after the PR is open costs an afternoon.

It isn't always a single exchange, either. When the right approach isn't known upfront, the architect and the developer work it out together: read the same files, compare two competing approaches, reason through the trade-off out loud. What gets written into the plan afterward is a decision that was made, not a guess nobody questioned. That conversation used to happen in a Slack thread or a design doc, somewhere apart from the code. Now it happens in the same document the implementation reads from.

Answering those questions has a cost of its own. The developer must be able to answer them, which means holding the shape of the system in their head rather than handing that off too. Some of the architect's questions are ones the developer hadn't thought to ask themselves. Those are the useful ones, the moment the two of you discover a gap in the same conversation, and the engineer learns that corner of the app before any code exists, not after a bug report finds it.

The retry loop

A blocker from the reviewer sends the fix to whichever agent owns it (test coverage to QA, everything else to the implementer), and the reviewer runs again. One retry. Still broken after that, and it goes to the engineer.

The first time it fired for real, the reviewer flagged a subscription leak in a file the ticket never touched: a missing takeUntilDestroyed that had been quietly leaking for months. The implementer hadn't put it there. The reviewer blocked the commit anyway. Nobody asked it to do that. It just did.

This is the Friday-afternoon problem from the top of this post: code that looks finished before it is. The retry loop exists because of it. Left alone, that kind of blocker doesn't stay contained. Merge it anyway and the next ticket's implementer builds on top of it without knowing, and the one after that does too, until the debt surfaces on its own schedule instead of the reviewer's. The retry loop is what stops a merged blocker from quietly becoming the foundation the next feature stands on.

That is the machine: five steps, one plan file, one retry, and a person at both ends of it. Standing up a chain like that takes an afternoon. What took three months was working out which of the steps deserved to exist, which had to be taken back out again, and which of the rules I had written down were rules at all. Those are the next two parts.

The pipeline itself was built and iterated on with Claude Code over several months. The numbers and commit history behind this post were checked with its help.