Sixteen Approvals for a Copy
A coding agent planned it, an AI architect made it more rigorous, and I approved a decision every four minutes. A copy between private repositories got a process built for an irreversible change.
Moving a folder from one private repository to another took a 700-line plan, five revisions, two AI agents and sixteen decisions from me in 66 minutes. About one every four minutes.
Our coding agent wrote the plan. Our architect, also an AI agent, reviewed it and asked for more rigor. I carried each review from one to the other and approved what came back. Nobody asked whether a copy needed any of it.
The one real failure came out of the process itself. A safeguard line in our deployment configuration took the public glossary off our development site, and the simplest check in the plan was the one that caught it.
This is the log of a change with one irreversible risk, governed as though every part of it were irreversible, and of what that cost. Not time. Human attention.
The job
The GetSentience.ai glossary is maintained as a registry: one file per entry, with the public text above a marker and internal notes below it. Around the registry sit immutable exports, release records, five small Python tools, grounding scripts and a specification. Until October 8 all of it lived in our private internal repository. The website, a separate private repository, read only a copy of one approved export.
I asked our coding agent whether the glossary should live beside the site it ships to. It should. The job was to move 308 files from one private repository to another and stop editing the old copy.
Three facts bounded the risk:
- Both repositories are private, and git history keeps every version on both sides.
- The site’s glossary input, the copied export, did not change at all.
- Nothing required a production deployment.
One risk was not bounded by any of them. The repository changes were reversible. Accidental disclosure of internal notes would not have been. If the private folder reached a deployed site, reverting git history would not take it back. That justified a packaging boundary and a deployment check. It did not justify a 700-line migration protocol.
What we built instead
The agent offered to write the move up as a formal migration plan, and I took it to our architect for review. The architect chose the rigorous path: its first review found five issues, including a requirement for byte-for-byte export verification and rollback procedures. The plan went through revisions 2, 3, 3.1, 3.2 and 3.3, each one reviewed by the architect, carried by me, and answered by the coding agent. By the end, with its verification records, it ran past 700 lines. It contained:
- A verification matrix of 21 named checks across integrity, publication isolation and validation.
- An occurrence ledger of 61 places where internal marker words already appeared legitimately elsewhere in the site, so that any new appearance could be reviewed.
- A pinned toolchain. The machine had a newer Node than the repository pins, so the agent downloaded the official Node 22 build to make baseline builds match.
- Build normalization. Astro writes a random identifier into some components on every build, so the agent normalized it before comparing builds byte for byte.
- New safeguards: a separation test suite, a credential scanner and changes to pre-push hooks in both repositories.
- A two-step ownership switch, with the new location’s README reading
PENDING CUTOVERand thenCANONICAL, under a freeze across both repositories with recovery checkpoints.
From my question to the commit that transferred ownership took 66 minutes. Once the plan existed, the work stopped sixteen times for a decision from me, either an approval or a direct answer to the agent’s question: about one every four minutes. There were three deployments to the development environment and none to production.
It did not take long. That is part of the problem. An agent can write a 700-line plan in less time than a person needs to read one carefully. The cost did not show up as hours. It showed up as attention, mine, spent approving process the change did not need.
The defect
The private root glossary/ folder should never reach the deployed site, because its entries carry internal notes. Our agent added an unanchored exclusion pattern, glossary/, to Railway’s deployment configuration in .railwayignore. Railway applied it as configured, excluding both the private glossary source and the public glossary routes.
The mechanism is gitignore syntax, which .railwayignore follows: a pattern without a leading slash matches a folder of that name at any depth. So glossary/ matched the private root folder, as intended, and also astro/src/pages/glossary/, the two files that define the public glossary routes. The development build had no glossary pages.
The checks missed it for three reasons:
- The agent’s pre-deploy check of the upload looked only at paths at the repository root.
- The separation test checked that the ignore line existed, not what it did.
- Every local build used the full checkout, not the set of files that would actually be uploaded.
What caught it was the simplest check in the plan: deploy to development and load the pages. The failing deployment served for about five minutes before we replaced it with a clean upload of the previous code. The fix was a leading slash, /glossary/, and tests that ask git’s own matcher which files an upload would leave out, including one that reproduces the original failure. Production was never touched, and an inspection of the failed development deployment found no internal material in it.
The one real defect came from a safeguard, not from the change.
What actually mattered
Four things carried real weight:
- Copy from a fixed commit and compare hashes. Cheap, and it proves nothing changed in transit.
- One line keeping the private folder out of the upload. This guarded the only irreversible risk in the job. One line, and we got it wrong the first time.
- One development deploy, then open the pages. This found the only real defect.
- A pointer README in the old location, and a pre-push guard that blocks re-adding glossary files there. This is what actually stops two editable copies from drifting apart.
The rest was true, and none of it changed a decision.
Why it happened
Over-engineering. The code changes were reversible, and the one irreversible risk, disclosure, needed a single boundary and a single check. The agent built a process for a change that was high-stakes and irreversible throughout.
Task expansion. “Move the glossary” grew into a test suite, a credential scanner, hook changes, an occurrence ledger, a toolchain download and an ownership state machine. Each addition was defensible on its own. Together they became a project.
Process by familiarity. The agent knows how to run a careful migration, so it ran one. Knowing a process is not a reason to apply it. The question should have been “what does this task need?”, not “what is the most thorough way I know to do this?”
Two agents ratcheted each other. The coding agent proposed a plan. The AI architect reviewed it and asked for more rigor. The coding agent supplied what was asked, and then more. Each round asked whether the plan was rigorous enough, and each answer added a layer. Neither agent asked whether it was too much. Both were doing what they are good at: one at thorough execution, the other at thorough review. Two agents that each optimize for thoroughness do not balance each other. They compound.
That last one should have been familiar. In August we published The Fix That Grew, about a plan that passed two good reviews and was still the wrong plan. We wrote then that a review checks whether the thing you built is correct, and does not ask whether you should have built it. Seven weeks later we did it again. Having published the lesson did not stop the pattern.
The question, and when it got asked
Fifty-four minutes in, after the failed deployment, I wrote: “I want to simplify the remaining migration, not add more process.” And: “Do not introduce additional architecture or verification layers.” The remaining work, merging the copy, deprecating the old source and transferring ownership, took twelve minutes.
I should have asked at minute one: why is this so complex? Why not copy the files, deprecate the old folder, and be done? Three parties could have asked it, and none did.
- The coding agent had the information. It could see that both repositories were private, that the code changes were reversible, and that the only irreversible risk was disclosure. And the simple version was in its first reply: copy the files over and leave a pointer. In the same reply it added a byte-for-byte regeneration proof and offered a formal plan for review. The simple path was on the table, but it was not the default. The reply should have said: this is a copy, one packaging boundary, one development deploy and a pointer README; here is the five-step version; do you want more than that?
- The AI architect chose the complex path. Given a choice between a copy and a migration, it reviewed for a migration, and every review made the plan more rigorous. A reviewer that only asks “is this rigorous enough?” cannot catch a plan that is too rigorous.
- I failed to notice. I carried every review from the architect to the coding agent and approved every revision that came back, and I did not see that the reviewer had picked the complex path when the simple one had already been offered. I was in the loop the whole time. I was not reviewing the loop.
The agent proposing the work owns sizing it, and the reviewer owns asking whether it should exist. The operator’s question is the backstop. Here the backstop failed too, and the reason is the next section.
Approval is a finite resource
Sixteen decisions in 66 minutes is one every four minutes. That does not work. At that rate a human in the loop stops reviewing and starts relaying: reading enough to see that the work looks careful, then approving it. That is what I did. Each approval was real. The attention behind it was not equal to it.
The speed made it worse. Two agents can write and review a 700-line plan faster than one person can read it, and every gate they add lands on the same person’s attention. Adding approvals did not add oversight. It spread a fixed amount of human attention more thinly across more decisions, until the one question that mattered, whether any of this was needed, had no attention left.
Between plan revisions, the coding agent drafted a glossary entry on high-consequence operations, which had not yet been published. Among its open problems, the draft says that “an approval step that fires too often becomes a reflex.” The agent then brought me a decision roughly every four minutes, most of them about changes git could undo.
The principle is not new. NIST’s AI Risk Management Framework leaves risk tolerance to each organization and asks that attention and resources go where assessed risk and potential impact are highest. In this migration that place was narrow: the packaging boundary between private notes and a deployed site. Everything else could be undone with git.
Sentience Governor was not recording any of this. It evaluates agent actions against declared governance context and records governance evidence without blocking execution, and it is not configured in the website repository where this session ran. The connection is a design premise, not a product claim. In Governor, the operator names the high-consequence operations in a governance profile, and everything else passes without ceremony, because consequence is a judgment about an action in its context, made by someone who knows the context. We build around that premise. On October 8 we did not apply it to our own work.
What we should have done
- Copy the glossary and its tools into the website repository from a fixed commit, and check that the hashes match.
- Point the tools at the new layout, and add the anchored
/glossary/line to.railwayignore. - Run the registry check and one build, deploy to development, open the glossary pages, and confirm the private folder is not in the upload.
- Merge.
- In the internal repository, replace the old folder with a pointer README and add the re-add guard.
That is two or three decisions, not sixteen, for the same safety outcome. Step 2 is the one that needed care, and step 3 is the check that proves it.
What we changed
One rule. Before planning any change, the agent answers three questions in one line:
- What could go wrong?
- Can it be undone?
- Does it cross a consequential boundary?
Production is one such boundary, not the only one. Private material can also leave through CI artifacts, development deployments and external integrations. Our development site is publicly reachable, so the development deploy in this migration crossed one, which is exactly why the packaging check mattered.
If the change can be undone and crosses no consequential boundary, the default is the simple version, and heavier process is offered only if asked for. If it crosses one, the care goes to that boundary, not to everything around it.
It is a written rule, not a mechanism, and the August article shows what a written lesson is worth on its own. The next migration will tell us whether this one holds.
A test for your own agents
Take the last change your coding agent planned for you. Before reading the plan, write down what could go wrong, whether it can be undone, and which consequential boundaries it crosses. Then count the decisions the plan asks of you. If the count is out of proportion to those three answers, the plan is governing the agent’s thoroughness, not the change’s risk.