Skip to content

Tickets in, a game I can play out: the orchestrated build-and-feedback loop

I am a Python and C++ developer. I cannot write the React for this project's screens, and I am not going to learn to in order to ship it. And yet I drive the product: I decide what gets built, I play the result on a real phone and a real TV, and I say what is wrong. The thing that makes that possible is a loop -- tickets go in, reviewed code comes out, I run the actual app and form an opinion, and that opinion becomes the next tickets. This post is about what that loop feels like from the builder's seat.

This is the third and closing post of a builder's-eye series about building an app with AI. The first, From an idea in a chat window to a repo the robot can build, is the on-ramp: a vague idea becomes planning docs in a git repo with a workflow orchestrator wired up. The second, Describe the screen, don't draw it: a prompt playbook for UI specs without a design tool, covers how a screen you can only describe in a sentence becomes a frozen, buildable UI spec. By the time you are standing where this post starts, you have both: planning docs that say what the thing is, and a UI spec that says what the screens are. The question this post answers is the one the whole series was building toward -- now what? You have the contracts. How does a feature actually get built, and how do you, the builder who cannot read the diff, stay in charge of the product?

The honest answer is a loop with four moves. I will walk each one from the builder's seat, and then I will tell you where the loop leaks, because it does.

Tickets are how you talk to the builder

The first thing that changes is that you stop asking for things in chat.

When I want a feature -- or, more often, when I have just played the game and noticed something is wrong -- I do not open a conversation and say "hey, can you fix the lobby screen." I get a ticket written: a markdown file under the backlog. The ticket is authored by the AI, drawing on exactly the two artifacts the earlier posts produced -- the planning docs and the UI spec -- so it lands as a real contract instead of a vibe.

What a ticket carries, at a high level, is the whole reason this works:

  • A goal. What this change is for, in one or two sentences. Not "fix the lobby" but "the join code is below the fold on a phone; it should be the first thing a player sees."
  • Scope. What the change is allowed to touch, and -- just as important -- what it is not. A ticket that touches the wrong files is a ticket that picked a fight with three other in-flight changes.
  • Acceptance criteria. A checklist of what "done" means, concrete enough that both the AI and I can tell whether it was met. "The join code renders above the GAME settings on a phone viewport" is checkable. "Make the lobby better" is not.

That structure is not bureaucracy. It is the thing that makes autonomous building possible at all. An AI can build from a contract; it cannot build from a wish. The discipline of turning "this screen feels off" into a ticket with a goal, a scope, and an acceptance bar is the discipline of deciding what I actually mean -- which is work only I can do, and which the ticket forces me to do before a single line of code is written. The ticket is the interface. Everything downstream runs on it.

The orchestrator turns a ticket into reviewed code

Once a ticket exists in the backlog, I hand it to the orchestrator and it comes back as something mergeable.

From my seat this is almost anticlimactic, and that is the point. I point the orchestrator -- the dispatcher at the orchestrator dispatcher -- at the backlog. It spawns a throwaway clone, an AI agent implements the ticket on a branch, a battery of review gates runs against the result, and what I get back is code that has already passed the project's bar. I did not copy-paste anything out of a chat. I did not paste an error back in. The typing, the running, the re-running -- all of it happened inside the pipeline.

Two things are worth naming here, precisely because I am not going to go deep on them.

There are review gates. Before anything is allowed to merge, the orchestrator runs a preflight: lint, tests, and a set of project-specific rules that encode the things this codebase cares about. The AI cannot ship code that fails the gate. That is the line between "the AI wrote something" and "the AI shipped something that clears the bar."

And there are run records. Every ticket leaves a sibling .run.md file next to it, recording what actually happened during the build -- the surprises, the dead ends, the judgment calls the agent had to make. Six weeks later, when I wonder why a thing was built the way it was, the answer is on disk instead of in a chat I closed.

That is genuinely all you need to know about the machinery to use the loop. How the gates are built, what each check does, how a ticket moves through its lifecycle from phase to retro -- that is a level down, and it has its own home. If you want the internals, the series has a separate two-post deep-dive: The orchestration lifecycle: from phase to ticket to retro, which follows a single unit of work all the way through, and Review surfaces and the AI-first guardrails, which opens up the gate battery and the rules that make AI output reviewable. Those posts own the mechanics. This one deliberately does not -- the whole reason the series splits into a builder's-eye layer and a deep-dive layer is so this post can stay about the loop and let those two be about the machine. So: there are gates, there are run records, and when you want to see how they work, follow those two links.

What I care about, in the builder's seat, is that the thing I get back is not a diff to read. It is a working app to play.

You test the running app, not a diff

This is the move the whole loop is built around, and it is the one that makes it possible for a non-web developer to drive a web product.

I do not review the React. I could squint at it, but I would be pretending, and pretending is worse than useless because it manufactures false confidence. What I do instead is run the actual software. From the repo root:

The concrete command lives in the private project wrapper for this workflow.

That brings up the real backend and the real client together -- the same server, the same WebSocket protocol, the same role-filtered views the eventual players will hit. Then I do the thing the code is for: I open the TV view on an actual TV, I scan the join QR with an actual phone, I play the actual game. I am not evaluating whether the code is elegant. I am evaluating whether the product is right -- whether the join flow makes sense, whether the turn feels good, whether the screen shows what it should and hides what it must.

(One precision the series keeps insisting on, because it bit me once: the project dev wrapper is the project dev wrapper -- dev-up, dev-down, lint, test. The orchestrator dispatcher is a different file, the orchestrator dispatcher, and it owns the ticket lifecycle verbs. Two wrappers, two jobs; I run the app with the first and drive the build loop with the second.)

This is the moment the loop pays off. Every earlier step -- the planning docs, the frozen UI spec, the ticket, the gated build -- exists so that this step is possible: that I, who cannot write the frontend, can still give feedback on the software, because I am holding the software, not reading the code. The diff is the AI's concern. The running game is mine. That division of labor is the entire trick, and dev-up is where it becomes real.

Feedback becomes the next tickets

And then the loop closes, because the things I notice while playing do not stay in my head. They become the next round of tickets.

I play a few rounds of Memory Match on a phone and the back-button does something surprising. The recap screen flashes before it redirects. A player's private hand is a beat slow to render after a reconnect. None of that is a code review -- it is a play review, the kind of feedback only the running app can produce. Each observation gets captured the same way the first feature did: as a backlog ticket, often citing the exact section of the UI spec from the second post that the behavior is supposed to match. "The recap redirect should be immediate per gameplay_ui.md section 4" is a ticket. So is "the back button on the player view should return to the lobby, not exit the room."

This is where the run records earn their keep a second time. When a build surprised the agent -- when it hit a dead end, or made a judgment call the ticket did not anticipate -- that is written down in the .run.md, and it often becomes the seed of a follow-up ticket too. The loop is not only fed by what I notice playing; it is fed by what the build itself learned. Either way, the output is the same shape as the input: a ticket in the backlog, ready for the orchestrator. Feedback in, tickets out, and the loop turns again.

That is the rhythm of building this way. Ticket -> reviewed code -> run the app -> feedback -> next ticket. I spend my time deciding what the product should do and judging whether it does it, which is the part I am actually good at. The AI spends its time turning those decisions into code that clears the bar. Neither of us is doing the other's job.

The honest edges

I would be selling you something if I stopped there, so here is where the loop leaks.

A ticket can encode the wrong premise. The orchestrator will faithfully build exactly what the ticket says, which is wonderful when the ticket is right and a problem when it is not. If I describe the wrong fix -- if I diagnose the recap flash as a redirect-timing bug when it is really a render-order bug -- the loop will hand me back a clean, gated, well-recorded implementation of the wrong thing. The pipeline guarantees the code matches the ticket. It cannot guarantee the ticket matches reality. That judgment is still mine, and the only way I catch a wrong-premise ticket is by playing the result and noticing it did not actually fix what I felt.

The gates catch a lot, but not everything. Review preflight is very good at the things that can be checked mechanically -- a lint rule, a failing test, a private value leaking onto a public screen. It is not good at "this turn feels sluggish" or "the join flow is confusing for a first-time player." Those are not gate failures; they are taste, and taste does not lint. The gates keep the bad code out. They do not put the right product in. That part still comes from a human playing the game.

You still have to play it, and form a judgment. This is the one the genre always skips. The loop removes the typing -- I genuinely do not write the React, and I genuinely do not copy-paste errors around. It does not remove the thinking. I still have to bring up the app, play it with attention, and decide what is wrong and what to do about it. If I stop doing that -- if I let tickets flow without ever actually playing the result -- the loop will happily build a tall stack of plausible, gated, well-documented code that drifts further and further from a game anyone wants to play. The orchestrator removes the labor. It does not remove the responsibility.

None of these are reasons not to build this way. They are the shape of the deal. You are buying a loop that lets someone who cannot write the frontend still own the product -- and you are paying for it by staying the person who decides what is right, writes the ticket that says so, and plays the result to find out whether it worked.


That is the series. You plan the idea in a chat and land it as docs in a repo with an orchestrator (From an idea in a chat window to a repo the robot can build). You turn a screen you can only describe into a frozen, buildable spec (Describe the screen, don't draw it). And then you run the loop in this post: tickets in, reviewed code out, the real app under your thumb, feedback back into the backlog.

If you want to see how the machine under that loop is actually built -- how a unit of work moves from phase to ticket to retro, and what every one of those review gates is really checking -- that is the separate two-post orchestration deep-dive: The orchestration lifecycle: from phase to ticket to retro and Review surfaces and the AI-first guardrails. This post handed you the loop; those two open up the engine. And if you are a backend developer still skeptical that you can really drive a frontend you cannot write, the closest neighbor is Shipping a React client I have not read -- the honest boundary behind doing exactly that.

You do not have to be able to write the app to build the app. You have to be able to say what it should do, and then play it to find out if it did.


Or: subscribe for the two-post orchestration deep-dive that opens up the machine under this loop.