The Name Came Later — From Jira Ticket to PR: The Story of Building an AI Development Loop

Ever since Claude Code launched in early 2025, the way I work as a developer has been steadily changing and improving.
Then early this year, in 2026, I realized there was a repeating pattern in how I worked — and I started wondering what would happen if I turned that pattern itself into a system. So back in March, I began building my own AI agent system, piece by piece. Four months later, it's producing some genuinely meaningful results.
This post is about how my AI agent journey — which started in early 2025 — branched off in a new direction earlier this year, along with my own reflections on where it landed and what I make of it. I'll also touch on how closely the recent direction of AI agents lines up with my own thinking.
1. A Year with Claude Code
Claude Code launched in beta in February 2025. I started paying for it in March.
I still remember installing Claude Code for the first time and making a tiny code change over a few turns of conversation. I'd just give instructions in plain language, and Claude Code did the actual editing. I also remember being genuinely shocked at how easy and how powerful it was.
After that, I leaned into this way of working. At first I only handed the AI bug fixes and simple feature additions, keeping the slightly complex logic for myself. But as the Claude models got dramatically better, the ratio of human-written to AI-written code slowly flipped — and today (as of July 2026), the AI writes close to 99% of the code.
After working this way for about a year, I started noticing a recurring pattern. Every task, obviously enough, starts with me — with my prompt. I open a new Claude Code session and calmly start typing out the problem to solve. Sometimes I type everything out as text myself; other times I just hand it a Jira ticket number and tell it to pull the details through the Atlassian MCP.
Then I hit enter, and Claude Code spins its own internal loop trying to solve the problem. Over dozens or hundreds of internal turns, it edits or writes code, and when it gets stuck, it turns around and asks me a question. Before the problem is finally solved, the back-and-forth between me and the AI repeats anywhere from once to a few dozen times.
Human input → AI action → Human verification → Human input again (repeat)
Like a turn-based game, the work advances one turn at a time — the human, then the AI. And at the very end, the moment the human decides the problem is solved, the loop ends. (Though sometimes, of course, it ends without the problem being solved at all.)
As the Claude models kept getting better, the output of this approach kept getting better too — no argument there. But I couldn't shake a strange feeling: the AI was doing the actual work, while I, the human, was just hauling fuel for it. The ticket changed, but the sequence never did. Open Jira, read the ticket, figure out which module in which repo to fix, assemble the context and hand it over, check the result, and make the commit and PR. The most important job — reviewing the AI's output — was still mine, sure. But in practice, I was spending more of my time hauling fuel than reviewing.
I didn't want to pour my time into this repetitive work. I'm human, so I make mistakes — and above all, this kind of repetition was just mind-numbingly boring.
2. Symphony — When the Problem Gained Structure
In March 2026, I stumbled on an unfamiliar repo on GitHub. It was OpenAI's Symphony.
Symphony is a Codex orchestration spec that OpenAI put out. At first only the spec document was public — a simple project that just laid out the spec so you could implement it yourself with Codex or Claude Code.
How it works is simple. It watches a Linear board, and when it spots an open ticket, it attaches an agent to that ticket — and that agent keeps running until the work is done. Instead of a human opening an agent session and feeding it work, the board supplies the work and the system opens the sessions.
Reading through the intro and SPEC.md, I thought: this is it. It wasn't that I'd found a new problem. It was the moment a problem I'd lived with all year but never managed to put into words — that fuel I hauled every day — finally gained structure.
What I took from Symphony was three perspectives.
First, the basic unit of work is the ticket, not the coding session. Until then, I'd worked in units of sessions. Open a session, transfer the ticket's contents into it, check the result, close the session. Symphony flips this around. The ticket is the unit of execution, and the session is just a disposable tool for processing that ticket.
Second, the project management board can be the coding agent's control plane. The board is already where all the company's work collects. Priorities, statuses, assignees, requirements — they're all right there. So instead of building a new interface for handing work to the agent, you just use the board itself. The human's role shifts from managing agent sessions to writing good tickets on the board and reviewing the output.
Third, a human doesn't have to be the one who starts the work. Sometimes a person carefully writes the ticket, but another system can create it automatically too. In other words, not just people but systems can kick off work on their own.
That said, I couldn't just take Symphony and use it as-is. Symphony assumes you're on Linear and Codex, and OpenAI itself said it was a reference they had no plans to maintain as a product. My company's work, on the other hand, lives in Jira, and our repo structure and build setup are completely different. So I decided to take the perspectives above — not the code — and build a system suited to my company's environment from scratch.
3. The Skeleton of the Loop, Built in a Month
I named the new system jira-autopilot. Since my company uses Atlassian Jira rather than Linear, I made Jira the issue tracker, and I picked TypeScript as the implementation language because it's what I'm comfortable with. I also put Claude Code in charge of the AI agent role instead of Codex — my company only subscribes to Claude Code.
As I started building jira-autopilot, I set exactly one guiding rule.
Start as small as possible, then flesh it out bit by bit.
I decided not to worry about things like parallelism or token-usage management up front. Rather than nailing down a perfect design before writing any code, I focused on getting a Hello world! version working first.
I actually already knew what I needed to build. The sequence I'd repeated every day for a year — open Jira, read the ticket, decide which module to fix, assemble the context and hand it over, check the result, and make the commit and PR — was itself the system's spec. I wrote the code by watching myself.
A month later, the finished skeleton worked like this.
Find a ticket → gather context → decide the module → change the code → verify the build → commit → open a PR → record the result → next ticket (repeat)

A long-running Orchestrator polls Jira every 30 seconds. When it finds a ticket that meets the criteria, it spins up a single disposable Docker container per ticket, and inside it Claude Code runs in headless (non-interactive) mode. The agent reads the ticket body, decides on its own which module in which repo to fix, changes the code, verifies it with a build, makes the commit and PR, leaves the result as a Jira comment, and disappears.
Each component takes over one piece of the repetition I used to do. The polling replaces me constantly opening Jira to check; the module decision replaces me wondering "which repo does this belong to?"; the prompt assembly replaces me copy-pasting ticket contents; the build verification and PR creation replace me checking results and wrapping things up. The list of fuel I hauled in section 1 had become, more or less, the system's feature list.
I built as little new infrastructure as I could. No separate work queue, no state-management system. Labeling Jira tickets gave me the queue, the state machine, and the locking all at once. A ticket being processed gets an in-progress label so no other worker picks it up, and done/failed labels mark the outcome. This is the second perspective — the board is the control plane — implemented directly.
I settled on a single isolation rule too. 1 ticket = 1 container = 1 branch = 1 PR. Each ticket's execution is completely separated from every other, so if one fails, they don't contaminate each other. The container vanishes entirely once the work is done, leaving no residue. I also locked the agent inside a disposable container and controlled even its outbound network traffic — but how I caged it is something I'll cover in detail in part 2 of this series.
And so the human who'd been inside the loop stepped outside of it. The human who used to type a prompt and check the result every turn moved to the two ends of the loop. What the human does boils down to two things: writing clear tickets, and reviewing PRs. There's no auto-merge. Opening a PR isn't the completion of the work — it's the step that prepares output a human can review.
In early April, jira-autopilot opened a PR on its own for the first time. It was the first PR that got made without me hitting enter.
Then on April 27, OpenAI announced Symphony on its official blog.
Manage work instead of supervising coding agents.
That's the tagline on the Symphony repo. By the time I saw this announcement, jira-autopilot had already been running on my company's Jira for weeks.
4. Three Months of Expansion — In Three Directions
I didn't hand it heavy work from the start. The tickets I gave it early on were things with clear completion criteria and clear ways to verify them — simple bug fixes, minor behavior improvements, config changes. I wasn't aiming for full automation from day one, either. I'd hand it a small task, watch where it failed, reinforce the verification, and slowly widen the scope of what I trusted it with.
Looking back, this expansion happened in three directions. The kinds of work I delegated broadened, the work I delegated got heavier, and the safeguards that made all of it possible grew alongside.
4.1. The Kinds of Work Broadened
It started with code. Things like "change this field's default value" or "fix the guidance text on this screen" — work where, as long as the ticket was clear, everything through verification wrapped up in one go. And autopilot handled these tickets reliably, all the way through committing and opening the PR without a hitch.
Once code changes were running reliably, one thing became clear. What this loop does, at its core, isn't fixing code — it's reading a ticket, gathering the context it needs, making a judgment, and leaving behind a result. The result didn't have to be a PR.
So the next thing I handed it was analyzing CS (customer support) tickets. Handling a CS ticket is mostly investigation work — digging up similar past tickets, checking the DB schema, opening the actual code to narrow down the cause. I handed that investigation to the agent. When a new CS ticket comes in, the agent digs through the archive of past tickets, the schema, and the code, then leaves a root-cause analysis as a comment on the ticket. This workflow doesn't change a single line of code. The output is a comment, not a PR, and the human just verifies the analysis and decides on next steps.
Going one step further, I also handed off assigning CS tickets. The agent reads a new ticket, judges who it should go to, assigns it, and leaves its reasoning as a comment. But I hammered one principle into it: if you're not confident, don't assign. A wrong assignment costs more than no assignment. The agent has to be able to stand in front of an ambiguous ticket, say "I don't know," and back off.
Here are the three things I handed to autopilot.
| Work delegated | Output | Boundary to respect | The human's part |
|---|---|---|---|
| Code changes | Commit and PR | No auto-merge | PR review |
| CS ticket analysis | Root-cause comment | No code changes | Verifying the analysis and next steps |
| CS ticket assignment | Assignment + reasoning comment | No confidence → no assignment | Judging ambiguous tickets |
And so a loop that wrote code became a loop that handled work. What I learned at this point was that the condition for putting work into the loop isn't "work that can be solved with code" but "work that can be defined as a ticket and whose result can be verified."
4.2. The Work Got Heavier
Once simple tickets were being handled reliably, a natural next question came up. Just how much could I hand off?
When I fed it a heavy ticket — feature work that spanned several modules, with requirements that ran past ten lines — it did produce something. But it was rare for a single run to satisfy the entire spec. There'd be a requirement it missed, or something that worked but left something to be desired on quality.
Looking back, one of the biggest advantages of working in back-and-forth conversation with Claude Code was exactly here. The human reviews the AI's output and gives feedback, and the AI picks up that feedback and reworks it. This cycle of review and rework pushed the quality of the output up. I wanted to bake this cycle into the system too.
So I built a reviewer workflow. When a code change succeeds, a review-only agent now runs automatically in a separate container. This reviewer has a principle of independence. It can't access the logs or intentions of the worker that wrote the code; it sees only the original ticket and the PR's diff, and reviews it as if seeing it for the first time. Its review has a single criterion: how well the code meets the ticket's spec.
The problems the reviewer finds — the findings — get registered as subtasks of the original ticket. Not all of them, of course. Trivial nitpicks get filtered out and left as plain comments; only the ones worth acting on become subtasks. Then the subtask workflow detects these and, one by one, creates fix PRs on top of the parent ticket's branch. A verification loop attaches behind the execution loop, and that verification spawns execution again — the loops had started to compose.

Let me show one late-April example of how this structure worked in practice. It was a ticket for a bulk data upload feature spanning the backend and frontend. I filed the ticket in the evening. From it, autopilot made one PR on each side, and the reviewer flagged 15 findings against the spec across those two PRs. Eleven of them were registered as subtasks (the remaining four were minor and left as comments only), and overnight, each of those 11 subtasks produced its own fix PR. What was waiting for me the next morning was 13 PRs. The total cost came to about $26.
The caliber of the findings was impressive, too. For example, things like "the validation-failure error message is hardcoded in English instead of using the i18n message keys — a violation of the spec's acceptance criteria." These weren't style nitpicks; they were the result of cross-checking the code against the acceptance criteria written in the ticket.
Of course, 13 PRs doesn't mean 13 things done. Each one has to go through human review, and some get rejected in that review. What autopilot changed isn't the bar for "done" — it's that by the time a human starts reviewing, the implementation, the verification, and the follow-up fixes are already prepared.
Here's the thing worth noticing. At the point I could start delegating heavy features, the model hadn't suddenly gotten smarter. What changed was the structure of the loop. The verification loop I'd originally built to protect quality had become a decomposition device — one that splits big work into small work and feeds it back into the loop. The expansion in capability didn't come from the model. It came from the structure of the loop.
4.3. What Made All of It Possible
One direction remained. While the work I delegated kept broadening and getting heavier, an unease lingered in the back of my mind.
Have I given the AI agent way too much power? What if this leads to a security incident?
jira-autopilot's agents run with no approval gate. In an interactive session, the agent would wait for a human's approval before running a dangerous command — but in headless execution inside a container, there's no human there to approve. In effect, I'd given the agent maximum freedom. So from the start I set one principle: give maximum freedom, but pair it with maximum constraint. If the "boundaries to respect" in the 4.1 table are the rules of the work, beneath them lies a more physical layer of constraint. Disposable containers, read-only mounts, control over outbound network traffic. And each worker is born with only the minimum permissions its own job needs. The worker that fixes code has no Jira token; the worker that analyzes CS tickets has no GitHub token.
The key is that these constraints aren't requests written into a prompt — they're physical boundaries. Saying "please don't" and making something impossible in the first place are problems on entirely different levels. The agent's good intentions are not a line of defense. The freedom in what I could delegate was able to grow because these boundaries grew first. How I caged the agent, and what those constraints were, is enough material for a whole post of its own, so I'll cover it in detail in part 2 of this series.
5. Three Months, in Numbers
Here's how much this loop actually ran over three months, copied straight from the operational database.
| Metric | Value |
|---|---|
| Operating period | ~3 months (April to early July 2026) |
| Total runs | ~470 |
| PRs created | 313 |
| Code-change success rate | 90.7% (351 succeeded / 36 failed, excluding cancellations) |
| Cost per run | median **5.85) |
| Time per run | median 4.1 min (p90 12.8 min) |
Roughly speaking, one PR comes out for a dollar or two, in about five minutes. But there's a caveat when reading these numbers. The denominator is tickets a human judged "worth handing to autopilot." That means these numbers carry selection bias — and at the same time, that's exactly the right way to use this tool. It's not a tool you throw any ticket at; it's a tool you feed well-defined tickets.
Let me lay out the other side of that success rate, too.
First, over the three months, 19 runs ended with zero PRs because the agent couldn't figure out which module in which repo to fix. When a ticket points somewhere vague, the loop stops at the very first step.
The auto-review approval rate is 55%. Put another way, nearly half of the output from first runs got the reviewer asking for changes. As we saw in 4.2, the subtask loop fills that gap — but the data doesn't allow for any fantasy of "a perfect result in one shot."
And one pattern stands out as the most consistent. A vague ticket becomes a vague PR. Tickets with clear requirements get handled reliably, but the more room a ticket leaves for interpretation, the stranger the output can get. The conclusion I reached after three months of running this is that what determines the quality of this system is the quality of the tickets as much as the performance of the model. Writing good tickets didn't get automated. That's still — maybe more than before — the human's job.
6. June: The Name Arrived
In June 2026, with jira-autopilot three months into running, an unfamiliar phrase started catching my eye in the developer community. Loop engineering.
The idea is to call it a loop when an agent repeats its work cycle until it reaches a stopping condition, and to call the work of designing, operating, and improving that loop Loop engineering. Used sporadically here and there, the phrase came together in late June in a post from the Claude Code team. That post sorts loops into four kinds.
| Type | What starts the loop | Where the human sits |
|---|---|---|
| Turn-based loop | A human's prompt | Checks each turn's result and enters the next instruction |
| Goal-based loop | A goal defined by a human | Defines the success criteria and leaves the rest to the loop until it's reached |
| Time-based loop | A fixed schedule | Defines the schedule and the work |
| Proactive loop | An event or a schedule — no real-time intervention | Designs the loop and reviews the results |
The moment I read the first row, the way of working I described in section 1 came to mind. One turn for the human, one for the AI. That rhythm we'd traded back and forth like a turn-based game had a name after all. Turn-based loop. The way I'd worked for a year.

And the last row of the table: Proactive loop. A loop that, without a human intervening in real time, starts work on its own according to an event or a schedule and carries it through to completion. I recognized jira-autopilot in that definition. Judging only by the behavior of checking Jira every 30 seconds, it looks Time-based — but what actually starts the loop isn't time; it's the event that "an executable ticket exists." This was the moment the third perspective I'd taken from Symphony in section 2 — that a human doesn't have to be the one who starts the work — got a name: Proactive loop.
To be clear, I didn't start building jira-autopilot after reading this post. By the time it came out, the system had already been running on my company's Jira for three months and had made close to 300 PRs. I didn't build a system after seeing a concept — the name arrived later, for a system I'd already built.
At first it felt strange. But another thought quickly followed. The fact that this term spread so fast means I wasn't the only one who'd had this idea. Developers all over the world, each worn down by the same repetition, had been arriving at a similar structure. Convergence is a signal that the direction is right. For me, the arrival of the term Loop engineering wasn't the start of a trend — it was a confirmation of the direction I'd been heading.
7. Closing — The Person Who Designs the Loop
Let me close by introducing the workflow I'm fondest of in this system these days. It's the workflow where autopilot fixes autopilot.
Even now, autopilot still mishandles tickets sometimes. When I spot a failure like that, I turn it into an improvement ticket for the pipeline itself. Then autopilot picks up that ticket — with the exact same loop it uses for any other ticket — and modifies its own repo's code to make a PR. Two PRs made this way have actually gone through human review and been merged. They were changes to its own verification logic and prompts.
Of course, there are two human gates here as well. A human approves the improvement ticket for execution, and a human reviews and merges the final PR. It's not an infinite self-modification loop; it's a controlled improvement loop.
The reason I'm fond of this workflow is its symbolism. At first, the loop took over the repetition I used to do. Next, the loop verified the loop's own output. And now, the loop fixes the loop. Through all of it, my own position kept shifting — from a person who performs individual tasks directly, to a person who designs, observes, and improves a loop that can keep performing good work. And even that improvement, in part, is now something the loop helps with.
Early in this post, I said I was tired of hauling fuel for the work. After three months of running this, one thing is certain. What jira-autopilot eliminated wasn't the developer's work — it was the hauling of fuel. Writing clear tickets and reviewing the output — every place that needs judgment remains exactly as it was, perhaps having grown even more important than before.
There are two stories I've deferred in this post. How I caged an agent that runs with no approval gate, and how — under the principle that "a prompt is not a means of enforcement" — I paired LLM freedom with deterministic control. I'll cover them in parts 2 and 3 of this series, respectively.
