How to delegate work to an AI agent without it editing the wrong repo
Most advice about delegating to an AI agent is about the prompt. Be specific. Give it context. Tell it what good looks like.
That advice is fine and it is not where the failures come from. The failures come from the handoff being confidently, traceably wrong about something nobody thought to check — and the agent then executing it perfectly.
Here is one of mine, in full, because a real one is worth more than a principle.
The line
authoritative page file: ~/workspace/public/fund.html on main
I wrote that. It went into a kickoff note at the end of a session, so that the next session could pick up where I left off. It names a repo, a path and a branch. It is unambiguous. It has a paper trail leading back to the work that produced it.
It was wrong about the repo, and I would not have found that out by reading it again.
What the job was
We run a live fundraiser page. It had been reading zero — not low, zero — on a page with real backers behind it.
The cause was in the database: a production row held a zero where a reconciled figure belonged. That got corrected through the database’s own console, and the endpoint came back agreeing with reality:
total_cents: 31000 gofundme_cents: 31000 stale: false
(Values from the incident record. Not a current figure — the fund’s live total is on the page itself, which is the only place it is ever quoted from.)
total_cents there is derived rather than stored, which is why there was exactly one
number to write.
So the hard half was done. What remained was cosmetic-but-real. The page carries
data-fallback-* attributes so that if the endpoint ever blips, the page degrades to the
last known figure instead of back to the zero it had just been. Those attributes still
said zero.
Three attributes. One file. One commit.
This is the shape of task you delegate without thinking hard about it, which is exactly why it is the shape of task that bites you.
The first step was a read
The handoff’s first instruction was not the edit. It was a read: confirm the branch and diff the page’s bytes against live before touching anything.
$ git -C ~/workspace show main:public/fund.html
fatal: path 'public/fund.html' does not exist in 'main'
Then the other branch — same fatal. Then the history:
$ git -C ~/workspace log -1 -- public/fund.html
(no output, on either branch)
No commit had ever touched that path. Then, to settle it by content rather than by path:
$ git -C ~/workspace grep -l 'data-fallback-total' feat/fund-totals-endpoint
No matches found
The attributes I had been sent to edit did not exist anywhere in that repository.
Why the read mattered more than the finding
Stop on that fatal for a second, because it is the whole point.
A missing file is a loud failure on a read and a silent success on a write.
git show on a path that is not there errors. An editor told to open a path that is not
there does not error — it creates it. If the first step of that handoff had been the edit
instead of the read, the sequence would have been: create a file, make three
well-formed changes, stage it, commit it with an accurate-sounding message, republish.
A clean commit. A successful deploy. And the live page still reading zero, with nothing anywhere to say why.
That is not an agent failing. Every individual step would have been correct. The agent would have done exactly what it was told, and produced an artifact that looks in every respect like success. It is the most expensive failure mode in delegated work, because nothing about it looks like a failure.
What was actually true
~/workspace was the platform repo — the one that serves the API we had just fixed.
It held the endpoint code and the branch the endpoint was built on.
The fund page is a separate static-site Repl. Its own deploy surface, its own repo,
and the place public/fund.html actually lives.
Both of them are “the fund.” Only one of them serves the URL. The kickoff note had conflated an API surface and a page surface because they share a word — and named a path generic enough to sound correct in either.
A filesystem path does not identify a deploy surface.
The tell I had already read past
The evidence had been in front of me for a while.
Two copies of that page disagreed with each other. A stale snapshot pointed its
data-endpoint attribute at one hostname, which does not resolve. The live-served bytes
pointed at a different one, which returns 200.
Two copies of the same file, contradicting each other about the single most load-bearing attribute on the page. I had looked at that and moved on, because both looked like “the file.”
The only thing that breaks that tie is the live URL. Not the repo copy, not the snapshot,
not whichever file opens when you type the name and hit tab. curl what the world
actually receives, and grep that.
Every copy is a claim. The served bytes are the evidence.
Standing in the right repo
In the static Repl, the same content grep, now expecting the opposite result:
$ git -C ~/workspace grep -l 'data-fallback-total' HEAD
HEAD:public/fund.html
HEAD:public/fund.html.bak … .bak6
A filename back. Right surface, confirmed by content, in about a second.
And a second finding for free: six copies of that page in the repo, five of them
backups. Which means the commit names that one file exactly — never a glob, never
git add ., or five stale backups ride into the deploy alongside the fix.
One more thing happened worth keeping. The edit went in through the editor, and the check
between staging and committing came back with nothing staged: the edit had silently not
persisted. On an edit-then-commit habit, that ships as a commit containing no change and
a message asserting the change was made. Instead it got caught, and the change went in
deterministically — sed against anchored strings, then read back — and committed as one
named file: three insertions, three deletions.
The proof was taken from the bytes the URL actually serves:
data-fallback-total="31000" data-fallback-gofundme="31000" others 0
endpoint=brand-builder.replit.app
(Again: values from the incident record, not a current figure.)
A commit is a claim that something is live. Served bytes are the evidence that it is.
The fix, which is four lines
This generalizes into one cheap addition at the top of any handoff that will edit a deployed file. It runs before branch checks, tree checks, or any write:
## Step 0 — surface identity (read-only, run FIRST)
`git -C ~/workspace grep -l '<distinctive live-page string>' HEAD`
Expect the target file's path back (e.g. public/fund.html).
If empty → WRONG REPO. Stop and report; do not create the file.
Use the exact path it returns everywhere below.
Grep a distinctive string from the live page against the repo you think serves that URL. A filename comes back — you are standing in the right place, and you use the exact path it returned for everything after. Nothing comes back — you are in the wrong repo, and you stop rather than creating the file.
It costs one read.
And change what the handoff names
The smaller half, which matters more.
Before: file: ~/workspace/public/fund.html on main
After: Surface: example.com/fund (the static Repl that serves it)
Path: confirmed by Step 0, not asserted here
Paths repeat across repos. public/fund.html can be tracked in two of them
simultaneously. The URL plus the repo behind it is unique.
Name the path and an agent can walk confidently into the wrong one. Name the surface and it cannot, because the identifying fact is now actually identifying.
The three rules underneath this
1. The first step of a handoff is a read, not a write. Reads fail loudly where writes fail silently. That asymmetry is the entire value of the gate, and it is free.
2. Verify by content, not by path. A path is a guess about where something sits. A distinctive string is an assertion about what something is. When they disagree, the string is right.
3. Name the serving surface. The live URL and the repo behind it. Never just a path.
None of this is about a model being unreliable. It survives any agent and any model, because it is not a claim about agents at all — it is a claim about references. An inherited reference with a paper trail feels confirmed, so nobody checks it, and a delegated worker of any kind will execute it faithfully into the wrong place.
The handoff was the defect. A handoff is a thing you can fix.
The spec I use
Step 0 above is yours. Paste it into your handoffs today, whether or not you ever buy anything from me.
If you want the rest of it, The Agent Handoff Spec is $39: the template this came out of, plus three domain variants, plus the verification-gate structure underneath it. It is tool-agnostic — nothing in it depends on a particular agent or model.
It is the operating discipline I use to run my own work with AI agents, and this post is one of the sessions that shaped it.
Sources for every factual claim in this post are in the claims sheet filed with it. The incident is a first-party Zipline record. No client is named because none is involved.
Want this done for your business?
Most advice about delegating to an AI agent is about the prompt. Be specific. Give it context. Tell it what good looks like.