$ cat how-i-ship.md
I Stopped Writing Most of My Code. Here's the Pipeline That Replaced It.
Most of the code I ship at work these days is written by Claude Code agents. That doesn't make it vibe coding. Every change has to prove itself twice in a real browser before a human (me) even looks at it, and the process is strict enough that I trust it on a multi-tenant compliance platform, where a wrong permission check is an audit finding, not a bug report.
This post is the pipeline: what each stage does, why it exists, and what kept going wrong before I added it.
1. One command per ticket
Everything starts with /ticket KEY. It's a Claude Code skill: a markdown file of rules the agent follows. It:
- reads the Jira ticket, its parent, every QA comment, and downloads the screenshots so it can actually look at them;
- turns all of that into a numbered acceptance checklist (
AC-1,AC-2, …), where each line is something a user can observe; - finds the right deploy branch and cuts a branch named after the ticket key.
Why: early on, agents fixed what the description said rather than what QA's latest bounce-back comment said. Writing the checklist first, from all the sources, closed that gap. QA's newest comment is now binding.
2. Red: prove it's broken first
Before the agent writes a line of code, it has to watch the bug happen: log in to the local stack in a headless browser, drive the real UI and reproduce it. On a feature ticket, it shows each acceptance criterion failing or missing.
"I understand it from the code" is not reproduction. "The AC is obviously not met yet" is not a red.
Why: an agent that hasn't seen the bug will happily fix a different one. It reads the code, finds something that looks wrong, fixes it, and the reported bug is still there. Making it watch the failure first is the cheapest guard I know.
3. Fix the cause, not the symptom
Then it root-causes and builds, following the repo's own guidelines plus a Laravel best-practices skill. If the fix needs a decision the ticket doesn't make, like relaxing a rule, a seeder that rewrites live records, or narrowing who may act beyond what was asked, the agent stops and asks. Those questions are written to a per-ticket state file, not just the terminal, for reasons that come up below.
4. Green: prove it's fixed, against the database
The agent re-runs the same browser flow, and it passes. Then it checks the outcome in the database, because a UI can show a success toast while nothing was written.
5. Draft PR first, then the review gate
As soon as the work is green, the agent commits, pushes and opens a draft PR. The PR exists before any review starts, so every review round reviews real, pushed code and every fix lands on that PR.
Then the gate:
- one automated audit pass over the diff, with real findings fixed;
- a fresh-context reviewer, an agent that has never seen the implementation conversation, re-reviews until clean;
- browser re-verification after every fix round;
- a hard cap of three rounds. Anything left after that comes back to me as a draft with the open findings listed in its body.
Why fresh context: an agent reviewing its own work in the same conversation defends its decisions. A fresh one just reads the diff.
6. I review and merge
On a clean gate, the PR is marked ready. Handoff to code review and QA is the one step agents never run. That's mine.
The part nobody talks about: supervising many agents
One agent is a toy. The leverage comes from running several in parallel, and that brings a new problem: knowing when one needs you.
- AgentPane is a Tauri desktop app I built to run them. Each agent gets its own git worktree, its own PTY session that survives an app restart, and a live status.
- watch-agents is a systemd user service that watches every session, my PRs and my Jira. It raises a popup, plus a phone push when I'm away, when an agent asks a question, gets blocked or stalls, or when a PR fails checks, hits a merge conflict, or a review comes back "with fixes". When a new ticket is assigned to me, it starts an agent on
/ticketby itself. - An agent's questions and progress live in a per-ticket state file. A restarted agent resumes where it stopped instead of redoing finished steps, and the watcher can show me the question even if I never open that pane.
Closing the loop: /reflect
At the end of a session, /reflect runs three reviewer agents over the transcript (judgment, tooling, and a "what would you have done differently" lens) and a synthesizer that proposes concrete edits to the skills themselves. I approve the ones worth keeping. The pipeline gets a little better every day it's used.
What it costs, and what it doesn't replace
- Time moves, it doesn't vanish. I spend less of my day typing and more of it reading diffs, answering agents' questions and tightening the rules they follow.
- Setup is real work. The skills, the watcher and AgentPane are software in their own right, with their own bugs and tests. They pay off only because I use them every day.
- Tokens aren't free. Browser runs and repeated review rounds are the expensive part, and the three-round cap exists partly for that reason.
- It doesn't replace judgment. Deciding what to build, making the calls a ticket leaves open, and the final read of every diff are still mine.
Takeaways
- Make agents prove things, not claim them. Red, then green, in a real browser.
- Review with fresh eyes, literally a fresh context.
- Invest in supervision, not just generation. The bottleneck moves to you.
- Write the rules down as skills and let every session improve them.