I'm a marketer who builds software, or to be more exact, I say what I want and AI agents write the code. I can't judge code, or at least I couldn't. I was talking about it with friends, and we figured out it feels like my children, who speak French without anyone having taught them. They got there just by listening and watching, again and again, and code feels the same to me! Sometimes I even wonder if I should apply as a developer for some gig, or at least as a website engineer or something like that. Anyway!
This year alone, I put in production software used by almost 100,000 people, for my clients. Some of it has super complex rules, like a payroll engine. And sometimes I even open pull requests to fix bugs in my clients' own code :)
So somehow I ended up with a workflow that works, and this note is how it goes.
The big picture
5 steps, top to bottom. Define the problem, with an agent, until a direction holds. Draw it in Paper: the screens, before any code. Say what I want: 4 lines in a file. Build, slice by slice: one model builds, another reviews. My check, then it's merged and pushed.
The first 2 steps are about finding what to build, and the last 3 are about building it without me reading any code. I'll take them one by one below, in that order.
How it starts
It always starts with a problem that I describe to an agent, and we iterate until we find a direction. For anything big, I hand that direction to the other model and ask it to challenge it, and nothing gets built before that.
Always draw it first
Before any code, I draw the screens in Paper, a design tool. The Paper connector is insane: the agent draws right in my design file, I move things around, I say what's wrong, and it redraws. We settle what the thing looks like while changing it still costs nothing.

What's also good about Paper is that I can copy an element and paste it into Claude or Codex. What lands in the chat is the reference of that element, something like "Node IDs: SOX-0", so it's super easy to point at one thing and iterate on it.
And going from Paper to code is super cheap, because of how each tool stores a design. In a tool like Figma, a design is a drawing, saved in the tool's own format. To build it, someone has to turn that drawing into code, and some things get lost on the way. In Paper, the canvas is made of the same stuff as a web page: real HTML and CSS. So the agent doesn't look at a picture and guess. It reads the exact structure and the exact values, and it writes the code from them.
That's also why the designs in my setup come straight out of my Paper files.
The review method, in short
I found that models are very good at reviewing each other's work. I can't judge the code, but I can judge a finding, and that's the trick, I guess. When a reviewer says "this would mark your mail as read without you", I get it, and I can check it. So the model that builds asks another model to review what it did, slice by slice. I ask it to only apply the fixes that move the needle, meaning the ones that block what I asked for. And at the end there is always a human check, mine: I don't read the code, I use the result like a user would and I read a 4-line report.
In practice, it all happens inside one Ghostty tab (see my setup). If I'm in Claude Code, Claude calls Codex directly for the review. If I'm in Codex, it calls Claude. I tell it once, at the start. I don't copy and paste between them anymore, and it's super, super helpful.
3 boxes in a row. Me, on the left, asks the agent I'm in, which builds. That agent sends the work to the other model with the words review this. The other model reviews and changes nothing, and answers ship, fix or ask.
Why a second model, and why another one
A model that just wrote something tends to find it fine. It made choices, so it reads its own work through them, a bit like grading your own homework.
A second model starts fresh, and it only sees what I asked for and what was built. And I take it from another company on purpose: Claude is made by Anthropic, Codex by OpenAI. They were trained differently, so they don't have the same blind spots. What one lets through, the other often catches.
The loop
3 steps in a row: lock the direction, done by me. Then slice by slice, done without me. Then my review, followed by merge and push. Below, the middle step is opened up into a loop of 4 steps: build, by one model. Review, by the other one. Fix what matters. Commit. Then an arrow goes back to build for the next slice.
- I say what I want, and for anything big it gets written down
- The agent builds one slice
- It asks the other model to review that slice, in the background
- It fixes what moves the needle and asks again, until the verdict is ship
- It saves that slice and moves on to the next one
- At the end, I check the result. Then it gets merged and pushed
Steps 2 to 5 run without me. The agent tells me when it's done, or when it has a question only I can answer. Until my check, the work sits on a separate copy of the project. Merging adds it to the main version, and pushing sends it up to GitHub.
Saying what I want
You don't need to write like an engineer for this. It's OK to talk to an LLM the way you want, in your own words, and to ask it to output this structure, which you then paste in a new session:
For anything big, that's what I do. I describe what I want, the agent turns it into those 4 lines, and I fix them until they're right. Each of the 4 lines is there for a reason:
- Outcome, because an agent that knows the goal makes better small choices than one that only knows the task
- Done when, because "it works" doesn't really mean anything. A check anyone can observe can't be argued with
- Must preserve, because agents break things next to the thing they touch
- Out of scope, because agents love to do more than what was asked
It says what I want, never how to do it. The agent knows how better than I do.
Why a file and not just a message? Because the reviewer gets it too, and checks the work against what I asked for, not against what the builder remembers. My mail client's repository has about 50 of these files, even if I skip it for small things.
Slice by slice
A slice is a piece that's useful on its own, and small enough to review in one go.
I cut the work that way for 2 reasons. A reviewer does a much better job on a small change than on a whole day of work in one block. And if a slice goes wrong, I lose an hour, not a day.
The latest redesign of my mail client was cut into 8 slices. The agent proposed the cut, and I approved it before it started. The first one was the window frame, the list row that every screen shares, and the status line, and all 8 were built and reviewed in one day.
On the left, a pile of slices, one on top of the other: slice 1, slice 2, slice 3, and so on to slice 8. Each one is marked built, reviewed, saved. Under the pile: one model builds a slice, the other reviews it, then the next one starts. An arrow goes from the pile to a box on the right, My check, I use the result, and from there down to a last box, Merged and pushed.
The review
When a slice is done, the agent writes a short brief and calls the other model. From Claude Code, the call looks like this:
codex exec -m gpt-6.1-sol \
-c 'model_reasoning_effort="high"' \
-s read-only -C <worktree> \
-o review.md - < prompt.md
Line by line, in plain words:
codex execruns Codex once, with no chat window. It does the job and then stops- The next 2 parts pick the model and tell it to think hard
-s read-onlylets it read every file and change none. I want one writer at a time. A reviewer that also edits leaves me with 2 versions to sort out-Csays which folder to look at-o review.mdsays where to write the answer- The end hands it the brief
Claude starts that command in the background and keeps working. It's told when the answer is there.
The brief says what I asked for, where the changes are, and how to answer. The builder writes that brief, so the reviewer doesn't take its word for anything. It opens the task file and the changes itself. This is what comes back:

The first word is always ship, fix or ask, so the builder knows what to do next in one word. Ask means the question is for me.
A blocker can only be one of 4 things, and the brief says so: the work doesn't do what I asked, it breaks something that worked, it's a real security, privacy or data risk, or it locks me into something costly. No nitpicking, no other ways to do it, no ideas for later.
The list is that narrow because a reviewer always finds something. If every remark gets applied, the 2 models go back and forth on details and the real work stops moving, which I've lived, see below. So the reviewer looks at every slice, but the builder doesn't apply everything it says. My instruction was: "you'll implement what moves the needle and iterate back and forth until it seems reasonable to commit and move forward (no overengineering)".

The builder can leave out a remark that isn't a blocker. For a blocker, it has 2 choices: fix it, or answer with a fact. The loop ends when the reviewer says ship. If the 2 of them can't settle it, it comes to me as one question, in plain words, with a recommendation.
The reviewer now also runs the tests itself. It copies the project into a folder of its own and runs them there, so what it says rests on something it ran and not only on what it read, and I like that a lot.
What it caught
All of these come from my mail client:
- An Undo button that moved under my pointer. After I undid one thing, another Undo slid into the place of the button I had just clicked, so one more click would have undone something else
- A command made for me that an extension could also have run on its own. It would have opened an email on my screen and marked it as read without me
- 3 claims in a document that went further than the evidence: "19 seconds" was an estimate, "Every page check passed" was written while one check hadn't run yet, and 1.3 seconds was given as measured when it was a target
- A record that was deleted and rewritten in the same millisecond got its old timestamp back. The screen showed the old record, while the Enter key acted on the new one
- A privacy problem when the pictures in an email are hidden again. It took 2 rounds of fixes before the verdict was ship
Honestly, I wouldn't have found any of these by reading the code. Each one got fixed, and the next review checked the fix.
What it missed, and what it costs
The review reads the code, and it doesn't see what happens around it.
One week, the agent installed 3 builds of my mail client with a settings file missing. A build is the app put together from the code, ready to install. That file is what lets the app renew its access to my mailboxes, so nothing broke at first. 35 minutes later, when the access ran out, every mailbox stopped syncing, and yeah, that one still stings. No review caught that, because nothing was wrong in the code itself. What fixed it was a rule: nothing gets installed over the app I use every day without my yes. It's written in the project's rules file, the one the agents load at the start of every session. A rule is not a lock, but it's what I've got.
It also takes rounds: the 8 slices above needed 17 reviews, so about 2 each, which is fine. When I was still carrying the reviews by hand, a small change to the notifications of my mail client took 3 of them: hold, hold again, then ship. On the work just before, 3 pieces out of 7 needed 6 rounds each. I don't know yet if rounds 4, 5 and 6 were worth it.
And I've seen it go wrong before. With an older version of this, I once wrote to my agents about my saved prompts, which I call snippets: "it feels my snippets are broken. Codex + Claude overengineering reviews / feedbacks. There are tons of back and forths for nothing, tiny details, that prevent us to focus on the main outcome of each task / project."
How I got here
- First, I tried to automate the passing between the 2 models with tools of my own: 6 attempts between January and May, one of which has 118 commits. I dropped them all, because each time the automation turned into a project of its own
- So I went back to copy and paste. One model built, the other reviewed, and I carried the text between them. I had a few saved prompts on keyboard shortcuts, like "review this as my CTO" or "act on this review". Over 30 days, I copied text out of Claude 1,159 times, and I had turned into a copy-paste operator
- Then I rewrote those prompts and set myself a rule: the 2 models never call each other. The copy-paste was the moment I read and decided
- A bit later I cut the prompts from 6 to 4, because they were too rigid and some only worked if I'd used another one first
- A few days later I broke my own rule: on one task, I told Claude to ask Codex itself and to act on the review, and the next day I made that the normal way of working on that project. Then I let it handle the merge and the push too, while my check still comes first
2 things changed: the models need a lot less steering than a few months ago, and I was done being the cable.
Where I stay in
- What I want, before anything is built
- The last stage, where my check is mandatory
- Anything that can't be undone: installing over the app I use every day, touching real mail, a product or a money decision
Every report ends with the same 4 lines, so I can check without rereading the whole session: Asked, Changed, Verified, Needs me. The left side of the review screenshot above is one of them. The reviewer has already checked what those lines claim, and that's why I can lean on them.
On a product I build that has other users, every change goes through a pull request, which is a page on GitHub that shows the change before it's accepted. 311 have been merged since the end of March. On my own mail client, I mostly skip that and merge after my check.
What I don't know yet
- If this still works in 3 months
- How many review rounds are too many
- What it misses that a senior developer would see. I have no way to measure that from where I sit
I'll update this note when the loop changes. At the current pace, that's in about 2 weeks.