If the same fix fails four times, you're fixing the wrong thing
Four safe attempts failed at the same boundary, but the breakthrough came from asking what another retry could still teach us—and replacing the design when the answer became: nothing useful.
Roughwise had a narrow promise: once an idea passed its approval gate, the desktop app would use an AI coding agent to turn it into a development-ready private GitHub repository, which meant the automation had to send one exact, reviewed Git history to one destination without borrowing settings, hooks, credentials, or remotes from anywhere else on my machine.
Four authorized pushes failed at the same remote-update step, but the repository stayed empty each time because the safety boundary worked: every attempt required fresh permission, failures could not trigger automatic retries, credentials stayed isolated, temporary files were removed, and unrelated projects were never touched.
That sounds responsible, and it was; however, by the fourth push I was spending another approval to learn almost nothing new, while “nothing broke” had become a poor standard for a debugging loop that kept asking the same question.
The problem was no longer whether failure was safe. It was whether another attempt deserved to run.
A retry should buy an answer
The attempts were not identical because each exposed a little more of the operation. Local object packing, authentication, and a dry run all worked, but the real update still failed with the same unhelpful exit code.
The diagnostic labels improved, but the information never became strong enough to change the decision.
- Attempt 1 — prove the operation fails closed
- Attempt 2 — expose the exact failing boundary
- Attempt 3 — eliminate common authentication and packing failures
- Attempt 4 — confirm the same transport still fails during the real update
- Stop — another retry cannot justify itself
Approval-gated systems make this trap easy to miss because the workflow can remain perfectly compliant while the engineering becomes careless; each confirmation feels like a responsible checkpoint, so another attempt feels cheap.
It is not cheap, though, because the user has to interrupt their work, understand the request, trust the boundary, approve it, wait for the result, and then decide whether the next attempt is meaningfully different. Human attention is part of the cost of the experiment.
Before asking for attempt five, the system needed to answer a harder question: what new fact would this attempt establish?
The honest answer was: probably none.
The useful question changed
The turning point was not another error category, but a direct challenge to the direction of the work: would redesigning the push mechanism be better than continuing with these “blindish” attempts?
That reframed the problem because the goal was never to rescue a particular Git trick; it was to preserve a narrow authority boundary while reliably sending an approved commit.
The original mechanism tried to isolate Git through a specially arranged object-directory setup, and its intention was good: do not consult the generated project's mutable remotes, hooks, includes, credential settings, or object store. However, the mechanism was difficult to inspect and failed only during the most expensive part of the test—the real remote update.
So the design changed.
Instead of asking Git to operate through the unusual object arrangement, the replacement created a self-contained temporary bare repository, copied the approved commit and its objects into that snapshot, repacked them, checked their integrity with Git's own tools, and verified that the exact approved commit was present before any network operation began.
The boundary worth keeping
- One approval for one attempt
- One exact private destination
- One immutable commit and branch
- No ambient Git configuration
- No automatic retry
The design worth replacing
- Unusual object-directory transport
- Failure visible only during the live push
- More diagnostic wrappers after every miss
- Shrinking information from each approval
- A mechanism harder to trust than the goal required
Git then ran from a held directory descriptor with minimal fixed configuration, pushed that immutable commit to the approved branch, and removed the temporary snapshot through the same identity-bound cleanup path.
The simpler concept did not mean casual implementation, though. Independent review found race-condition risks in the first redesign: creation, execution, and cleanup still had to stay attached to held directory identities rather than reusable paths. Those problems were fixed offline, where repeated validation was cheap and did not consume another live approval.
Only then did one new authorized attempt make sense, and it succeeded.
Stop improving the explanation of a bad design
There is a difference between an unsuccessful experiment and an uninformative one: the first can be valuable when it rules something out, narrows the cause, or changes the next decision, while the second repeats the same mechanism with slightly nicer logging and hopes reality will become more cooperative.
The mistake here was not keeping the safety boundary, but allowing that good boundary to protect a weakening technical strategy from a more fundamental question.
The completed lifecycle left Roughwise with one retained private integration proof. As of this Note's publication date, the broader product has not been publicly released.
The fifth attempt did not succeed because persistence finally paid off, but because the work stopped trying to save the thing that had failed four times.