Claude Code writes the plan, Gemini writes the code

Problem
I pay for two AI subscriptions and I was using them in the worst possible ratio.
The first is Claude Pro, and I hit its limit. Not occasionally — routinely, all the way to 100% of the weekly allowance. I watch the usage indicator the way you watch a fuel gauge on a long drive, and I have stopped Claude mid-task more than once because I wanted to keep something in the tank for the evening.
The second is Google AI Pro, and I did not really buy it for the AI. I was on Google One Plus for the 2 TB of storage, ran out of space, and moved up to the 5 TB tier — which happens to be the one Google attaches its AI to. I paid for a year upfront and told myself the assistant would be a pleasant bonus.
It was a disappointment. Gemini was fine when the prompt was exact, and confidently wrong when it was not. It would fill gaps in an underspecified request with invented specifics, which in a repository full of generated data is the expensive kind of wrong. Once I had spent a while with Opus 5, I stopped going back.
So the situation was: one model I pay for and do not use, and one model I use until the meter runs out.
Solution
Claude Code stopped doing the small work itself and started subcontracting it.
The order of operations is the whole idea:
- I describe the task in one or two sentences.
- Claude writes a plan — named files, named functions, acceptance criteria expressed as commands with expected output, and a list of prohibitions.
- That plan file is handed to Gemini through the Antigravity CLI as the entire prompt. No conversation history, no context I happen to have in my head.
- Gemini edits the working tree and reports back.
- Claude reads the diff — not the report — runs the acceptance criteria itself, fixes what needs fixing, and commits.
This lives in the repository as a skill called gemini, next to an older one called review-gemini. The second one is the fossil of how I used to do this, and the difference between them is the point of this post.

The repository, for context: it is a private one for the house we are building. Site documentation, cost estimates, and a 3D model of the house and its surroundings that is generated by scripts rather than edited by hand — greenery counted off an orthophoto, HV pylons placed from a cadastral register, room dimensions read from the architect’s drawing. It is an unusually good test case for delegation, because almost every number in it either comes from a measurement or comes from nowhere, and it is obvious afterwards which one you got.
Technical details & puzzles
The prompt is a file
Gemini runs through agy, the Antigravity CLI, logged in on the machine. Claude writes the plan into the session scratchpad and pipes it in:
agy -p "$(cat "$SCRATCH/plan-gemini.md")" \
--model gemini-3.1-pro-high \
--dangerously-skip-permissions \
--output-format json --print-timeout 25m \
> "$SCRATCH/gemini-1.json"
Three of those flags are load-bearing and none of them are obvious:
--dangerously-skip-permissionsis not optional. Without it,-pmode never gets consent to touch files, so Gemini comes back with a beautifully written description of the change and an unmodified working tree. That took a while to work out, because nothing fails — you just get prose.--print-timeout 25m, because the default is five minutes and a real coding turn can exceed it. The call also has to run in the background: Claude Code’s own Bash tool cuts off at 600 seconds.--output-format json, for theconversation_id. The convenient-cflag resumes “the last conversation”, and I run my own Antigravity sessions on the same machine, so-cis a coin flip. The correction round has to use--conversation <id>explicitly.
Gemini also reads GEMINI.md from the repository root on its own, which is where the project-wide rules live — units, which scripts are off limits, how to check ES module syntax. The plan does not repeat any of that. It only states what this task changes or overrides.
Acceptance criteria, or the report means nothing
This is the part that makes the whole thing work, and I would not bother with any of it otherwise.
A plan without acceptance criteria is worthless, because without them Gemini reports success on the grounds that nothing exploded. So every plan ends with commands and expected results:
## Acceptance criteria (run these and show the output)
- `python3 -c "import zielen_orto"` → no error
- `python3 -m zielen_orto --stats` → 412 trees, 188 shrubs
- `git status --porcelain` → exactly two files changed
And a list of prohibitions, which is mostly about keeping the blast radius small:
- no
git commit,push,add,checkoutorstash— changes stay in the working tree, I commit after review; - no running the full model build (Blender, xvfb, gltfpack) — that is my step;
- no touching binary files;
- no touching files outside the list above;
- if something cannot be done as planned, say so and stop. Do not improvise a workaround.
That last one is the most valuable line in the file, and the anecdote further down is entirely about it.
Reading the diff, not the report
Claude reviews with git diff, and the checklist is ordered by how often each thing actually went wrong:
- Did it touch only what it was told to? Any file outside the plan is an automatic correction round, however innocent the change looks.
- Did it commit anyway?
git log <base>..HEADhas to be empty. - Run the acceptance criteria yourself. “It passed” is not evidence.
- Did it write numbers from memory instead of deriving them from the data? In this repository that is the single most expensive habit, and it is the reason the whole delegation is structured around measured values being supplied rather than requested.
Three correction rounds is the ceiling. After that Claude does git checkout -- on the files Gemini touched, does the task itself, and tells me in one sentence where it fell over. That budget has not been used yet.
Two models, one git identity
Both models commit as Mateusz Kotlarz <rampler@gmail.com>, so %an cannot tell them apart. What separates them is a trailer: Claude’s commits end with Co-Authored-By: Claude Opus 5, and commits from my own Antigravity sessions have nothing.
That one convention is what makes review-gemini able to find its own work queue:
git log --invert-grep --grep='Co-Authored-By: Claude Opus 5' "$LAST_REVIEW"..HEAD
It matters more than it sounds, because under the new arrangement Claude commits Gemini’s work under Claude’s trailer — it has reviewed it, so it owns it. The queue stays correct: the review skill still only picks up the commits nobody has looked at.
What I do not hand over
The split is not “small tasks to Gemini, big tasks to Claude”. It is whether the plan can be written down to the file and the function, and the result checked with a command.
Fine to delegate: text on a page, small CSS or HTML, moving a constant, adding or removing an entry in a data file, mechanical refactors, adding an exclusion polygon to an algorithm that already has one.
Not delegated, ever: anything where a number comes from a measurement — the architect’s drawing, the orthophoto, the site plan — plus new geometry, colour work that has to survive two different render engines, and the export pipeline. When I ask for one of those anyway, the skill’s instruction is to say so in one sentence and propose a split: the measuring is mine, Gemini gets finished numbers to type in.
That division is not a guess. It is what the review-gemini skill’s checklist was built out of, and that checklist is a list of things that really happened: a script that was a plain SyntaxError, so every rebuild of that part of the model threw and the committed binary could only be reproduced from memory. A max(0.0, u) “fix” on texture coordinates that ate the half of the solid sitting in negative x, because the tiles are wrapped and negative u was correct all along. A surface normal compared against a coordinate in centimetres — a condition that is false at every angle. A hand-typed elevation that drifted the moment the object it was attached to changed size. None of those are stupid mistakes. They are all what happens when a model is asked to do something without being told what the answer has to look like.
The one where Gemini was right and I was not
The best moment so far was a failure that wasn’t one.
The task was thickening the tree cover along a stream: three thresholds, and four acceptance criteria with exact expected counts. Gemini made the change, ran the criteria — and the species breakdown did not match.
It did not match because my criterion was wrong. I had computed the expected numbers with one function, and told it to verify with another — and the second one additionally cuts greenery under the 110 kV line down to shrubs. Three solids of difference.
What Gemini did with that: reported the discrepancy, printed both numbers, and stopped. It did not nudge the thresholds until the output matched the number I had written down.
That is the best available outcome. It was the fifth task in a row with no correction round, and it caught an error in my plan on the way through.
Note the shape of it, though. Gemini did not need to be clever to get that right — it needed a plan precise enough that a mismatch was visible. The thing I had written off the model for was that it filled in gaps by inventing plausible specifics. Give it a document with no gaps in it and that failure mode has nowhere to happen. Claude produces documents with no gaps in them as a side effect of the way it plans.
Which is the actual finding here: the plan is the product. Typing the code was never the expensive part.
The journal
Next to the skill there is a dziennik.md — a table with one row per delegated task: date, task, model, correction rounds, outcome. Claude appends to it after every job.
It costs nothing and it is the only part of this that will still be useful in six months, because it is the only part that accumulates evidence about what not to hand over. Right now it reads as seven wins in a row, which tells me approximately nothing except that I have been picking easy tasks.
Result
Two days, seven delegated tasks, and the numbers from the journal:
- Zero correction rounds. All seven landed on the first attempt.
- 63 to 135 seconds per task, wall clock, including Gemini reading the repository.
- My own edits afterwards: a reworded comment, and a long line wrapped. That is the complete list.
- One bug found in my own plan, by the subcontractor.
For comparison, the old arrangement — me driving agy over SSH, later from the phone once Antigravity got remote control, and asking Claude for a review afterwards — ran at three to five rounds per task, and Claude still had substantial work to do on top.
And the cheaper part is not the one I expected. I assumed the saving was “Claude does not write the code”. The real saving is that Claude no longer has to reverse-engineer intent out of a bad diff. Reviewing a diff that came from a plan you wrote is a fraction of the work of reviewing a diff that came from somewhere else, and it is a much smaller fraction than I would have guessed.
I have not measured any of this in tokens. I have no numbers on the saving and I am not going to invent them — what I have is a usage indicator that stopped being the thing I think about first.
What I do not know yet
This is two days old. Everything above is true and none of it is settled.
- Seven tasks is not a sample. It is one repository, one kind of work — Python that generates data — and tasks I chose partly because I thought they would work.
- The tasks may simply have been too small. Seven for seven is exactly the result you would get from a system that is genuinely good and from one that has not been tested yet. I cannot tell those apart from here.
- The three-round budget has never been spent, so I have no idea whether “give up and do it yourself” actually triggers cleanly when it needs to.
- Nothing has broken. No timeouts, no wrong conversation resumed, no commits made in defiance of the prohibitions, nothing damaged by
--dangerously-skip-permissions. I keep expecting one of those and it keeps not happening, which I am treating as a small sample rather than as robustness.
So the next step is deliberately handing it something it should struggle with, and I will write up what happens either way. That post will be the interesting one.
Story
The honest version of how I got here is not a plan, it is a sequence of small annoyances.
I upgraded Google One because I ran out of disk space. The AI came attached to the storage, and I did try it — for a while I genuinely thought I would end up using both models for different things. Then Gemini invented a set of dimensions for me with total confidence, and I went back to Claude and stopped thinking about it. Money already spent, for a year, on something sitting idle.
Meanwhile Claude Pro and I developed a relationship with its usage indicator. Hitting 100% of a weekly limit changes how you work — not always for the worse, since it does make you write better prompts — but it definitely means you start noticing which tasks are worth the budget. Regenerating a data file after a threshold change is not worth the budget. It is also not work I want to do by hand.
The first attempt at fixing that was the obvious one: let Gemini do the work in its own sessions, and afterwards ask Claude to review its commits. That is what the review-gemini skill is. It worked, in the sense that the model eventually got there, and it cost three to five rounds of me relaying complaints between two chat windows, and then Claude found the real problems anyway.
The realisation was embarrassingly simple, and it arrived — like a suspicious share of this blog’s infrastructure decisions — nowhere near a desk: if Claude is going to end up reviewing the work regardless, it should be the one writing the brief. Not because Gemini needs supervision, but because the brief is where all the actual thinking lives, and reviewing against a brief you wrote yourself is a completely different job from reviewing against nothing.
So now the model I had written off does five tasks in a row without a single correction, and the reason is not that it got better. It is that it finally gets told what “done” means. The model I stopped trusting because it hallucinated on vague prompts turns out to be perfectly good at following a specification — I had just never written one.
The whole thing runs on the same Oracle Cloud VM that used to host this blog, which by now has a habit of quietly building things that replace some part of my workflow. The blog itself moved off it to Cloudflare Workers a few weeks ago. The VM does not seem to mind.
Diagram made with Claude Code. The plan for it, unsurprisingly, was the long part.