Skip to content

Your first runnable line: clone it, run it, play it, file a ticket, watch it merge

The conceptual posts can take you right up to the keyboard and then leave you there. From an idea in a chat window to a repo the robot can build is the clearest example: it walks the whole on-ramp -- plan in chat, capture the decisions as docs, move them into a git repo, and drop in a workflow orchestrator so the AI builds from tickets instead of from vibes -- and it is deliberately honest that the last step has real setup cost. But read it closely and you will notice it never shows you a single command. It stops at the shape of the on-ramp. It tells you to add an orchestrator; it does not tell you what to run once you have one.

This post is the missing line. It is the hands-on companion to that conceptual one: the literal sequence I actually type to go from a fresh clone of this project -- a multi-device board game platform -- to a change I noticed while playing, written as a ticket, built by the orchestrator, and merged. No invented steps. Every command below resolves to a real verb in this repo at the time of writing, and I will tell you exactly which file owns it.

There are five moves, in order, and then the honest edge.

The on-ramp the conceptual post leaves blank

It is worth being precise about the gap, because it is a real one and naming it is the whole reason this post exists.

A reader can finish the mindset posts -- the on-ramp, the read-before-you-write habits, the "tickets are how you talk to the builder" loop -- and come away genuinely convinced and completely unable to start. They know the AI should build from a ticket. They do not know how to make a ticket run. They know the orchestrator gates every change. They have never watched it gate one. The conceptual layer hands you a mental model; it does not hand you a terminal.

So here is the terminal. Clone the repo, then walk these five moves once, in order. After you have done it once, the mental model from the other posts snaps into place, because you have seen the thing the model was describing.

The first runnable line, in order

One: set up. From the repo root, the first command hydrates everything -- the Python virtualenv and dependencies for the backend, and the client's dependencies for the React frontend -- in a single repeatable step:

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

This is idempotent. Run it on a fresh clone, run it again after a dependency changes; it is the canonical "make this checkout buildable" entry point. You do not hunt around for a requirements.txt here and an npm install there. One command, both halves of the stack.

Two: run the app. With dependencies in place, bring up the backend and the client together:

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

That starts the FastAPI backend on port 8001 and the Vite client on port 3001, and it records the process IDs under .runtime/ so the stack can be cleanly stopped later. (It also writes the server and client output to .runtime/backend.log and .runtime/client.log, which is where you look when something misbehaves.) When you are done, the matching teardown is the project dev wrapper, and the project dev wrapper is just the two in sequence.

Three: play it on a real device. This is the move that distinguishes this project from a localhost demo, and it is the one most people skip. Do not just poke at the client in a browser tab on your laptop. This is a multi-device platform -- a TV shows the shared table and a room code, phones join as players by scanning a QR, and the views are role-filtered so a player's private hand never leaks to the shared screen. The point of the product only becomes legible when you hold it the way a player will. Open the table view on an actual TV (or a second monitor standing in for one), scan the join QR with an actual phone on the same LAN, and play a round. You are not evaluating the code. You are evaluating the product -- whether the join flow makes sense, whether the turn feels right, whether each screen shows what it should and hides what it must.

You will notice something. You always do. The join code is below the fold on your phone. The recap screen flashes before it redirects. The back button does something surprising. Hold onto that -- it is the input to the next move.

Four: file a backlog ticket. Here is the move newcomers misread. You do open a chat for the thing you noticed -- on your own clone of main, the authoring copy where you plan and hand-edit, not a throwaway. What you do not do is ask it to "go fix the lobby." You ask it to draft a ticket: a markdown file under the backlog that says what to change, what it is allowed to touch, what "done" means, and how to verify it. The chat is the authoring surface; what comes out of it is a ticket file, not an edit to the code. And a ticket has more shape than a freeform note -- authoring follows real contracts under the workflow docs (start with the ticket-authoring contract): required pre-reading, YAML frontmatter, a declared edit scope (## Touches), a dependency graph (## Depends on), acceptance criteria with verification commands, and extra rigor when the ticket is a bug fix. You do not memorize any of that; the chat session reads the contracts and drafts to the template. That structure is not bureaucracy -- it is the act of deciding what you actually mean before a single line of code is written, which is the part only you can do. "The join code should render above the GAME settings on a phone viewport" is a ticket. "Make the lobby better" is a wish, and the orchestrator cannot build from a wish.

Five: run the ticket and watch it merge. Now you hand the ticket to the orchestrator. This is a different dispatcher from the one you used to run the app (more on why in a moment), and it takes the ticket's name:

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

From your seat this is almost anticlimactic, and that is the point. The dispatcher spawns a throwaway clone, an AI agent implements the ticket on a branch, a battery of review gates runs against the result, and -- if it clears the bar -- it ships and merges. You did not copy code out of a chat or paste an error back in. You can watch in-flight and recent runs with the orchestrator dispatcher, and the same verbs are surfaced as /dev-* slash commands (/dev-ticket, /dev-status) if you drive the orchestrator from inside an editor agent rather than the shell. When it lands, the ticket leaves a sibling .run.md run record next to it on disk -- the surprises, the dead ends, the judgment calls the agent made -- so six weeks later the answer to "why was this built this way" is a file, not a chat you closed.

That is the first runnable line, end to end: setup -> dev-up -> play on a real device -> write a ticket under the backlog -> the orchestrator dispatcher -> merged. Once you have run it once, the orchestrated build-and-feedback loop -- tickets in, reviewed code out, the real app under your thumb, feedback back into the backlog -- stops being an abstraction and becomes the rhythm you are already standing inside.

Two wrappers, one launcher

There is one thing in that sequence that trips up everyone the first time, and it is worth slowing down on, because reaching for the wrong tool here produces confusing failures.

You used the project dev wrapper to run the app and the orchestrator dispatcher to run the ticket. Those are two separate files with two separate jobs, and they are not interchangeable:

  • the project dev wrapper is the project dev wrapper. It owns the verbs you use to work on the code: setup, dev-up, dev-down, dev-restart, test, lint, docs-build. If you are building or running the software, you are here.
  • the orchestrator dispatcher is the orchestrator dispatcher. It owns the ticket lifecycle: ticket, phase, review, ship, status. If you are driving the build loop, you are here.

Do not cross-call them. the orchestrator dispatcher is not a thing (the orchestrator has no project-dev verbs), and the project dev wrapper is not a thing (the project wrapper does not orchestrate). When in doubt: running or testing the app is the left-hand file; moving a ticket through its lifecycle is the right-hand file.

The launcher half of this rule is load-bearing enough that the project treats it as hard law: the local app is started only through dev-up / dev-down / dev-restart, never with a raw uvicorn server.main:app or a bare npm run dev. The reason is concrete, not stylistic. dev-up records its process IDs under .runtime/ so dev-down can find and stop exactly those processes. A server you start by hand is invisible to that bookkeeping -- it survives a dev-down, keeps holding the port, and keeps serving stale state across what you think was a clean restart. The single most baffling "I fixed it but the browser still shows the old behavior" session you can have on this project is a hand-started server that dev-down never knew to kill. Use the wrapper and that entire class of ghost never appears.

The honest edge

Everything above is the happy path, and I would be selling you something if I stopped there.

The five moves describe a clean run. Real runs are messier. When you hand a ticket to the orchestrator, the most common outcome is not an instant merge. The review preflight -- lint, the test suite, and a battery of project-specific rules -- gates every change, and it fails tickets routinely: a lint rule tripped, a test broken, a rule about role-filtered views that the implementation did not honor. That is the gate doing its job. A ticket can come back needing a re-run, or it can escalate -- stop and ask for a human decision -- when the agent hits something the ticket did not anticipate. None of that is failure; it is the system being governance with a human in the loop rather than push-button autonomy. If your first ticket clears every gate on the first try, enjoy it, but do not calibrate on it.

The orchestrator builds exactly what the ticket says. That is wonderful when the ticket is right and a problem when it is not. If you diagnose the recap flash as a redirect-timing bug when it is really a render-order bug, you will get back a clean, gated, well-recorded implementation of the wrong fix. The pipeline guarantees the code matches the ticket. It cannot guarantee the ticket matches reality -- and the only way you catch a wrong-premise ticket is by going back to move three and playing the result.

You still have to be able to judge the output. This project's owner is a Python and C++ developer who cannot honestly claim to write or source-review the React client -- the boundary named in Shipping a React client I have not read. The loop does not remove the need to understand what came back; it makes the output reviewable, which is not the same thing. It removes the typing. It does not remove the thinking.

None of these are reasons not to work this way. They are the shape of the deal. The first runnable line buys you a governed build loop that someone who cannot hand-write the frontend can still drive -- and you pay for it by staying the person who plays the result, decides what is wrong, and writes the ticket that says so.

So: clone it. setup. dev-up. Play it on a real phone. Notice one thing. Write the ticket. Run it. Watch it merge. That is the first runnable line, and once you have typed it once, every conceptual post about working this way is describing a room you are already standing in.


Or: subscribe for the rest of the mindset track.