Late at night, I handed Claude Code the chore of clearing the build caches that had piled up across several of my app repositories.
DerivedData, CocoaPods' Pods, the React Native node_modules, .build. When you carry several apps in parallel as an indie developer, these intermediate artifacts quietly eat away at your disk.
Deleting them by hand, one repository at a time, is dull work. That is exactly why it seemed right for an agent.
Partway through, the terminal paused for a moment. The command about to run looked like this.
rm -rf "${CACHE_ROOT}"/*CACHE_ROOT was empty. Expanded, that command becomes rm -rf /*. My hands went a little cold.
Why the variable came up empty
The cause was ordinary.
The cleanup script was designed to place each repository's cache location into an environment variable before running. But one of the targets was an app whose directory layout I had reorganized only recently.
The path I expected no longer existed, so nothing was assigned when CACHE_ROOT was built. The shell silently treats an undefined variable as an empty string. No error, no warning.
The intent behind rm -rf "${CACHE_ROOT}"/* was "delete only this app's cache." Yet the moment it collapsed into an empty string, the meaning became something else entirely.
In solo development, I am the only tester and the only reviewer. There is usually no second pair of eyes to catch a mix-up like this.
Auto mode was watching for "variables it could not resolve from context"
What stopped it was Claude Code's auto mode.
In the July 2026 update, auto mode added several safe-by-default behaviors. One of them is that an rm -rf containing a variable it cannot resolve from context now pauses for confirmation before running.
The agent could not tie CACHE_ROOT to a reliable value. So instead of running silently, it handed the decision back to me.
What matters here, I think, is that this is not a blunt "stop every destructive command." That would produce so many prompts that you would end up approving without reading.
What it watches for is whether the value can be resolved from context. It surfaces only the deletions it cannot back with a real value. That line felt like a kindness toward the attention of the person doing the delegating.
Auto mode also now blocks tampering with the session record itself. The trace of what was executed cannot be rewritten afterward, and the more unattended the work, the more that line earns its place.
What it felt like to hand a long cleanup to Opus 4.8
That same day, Claude Opus 4.8 became generally available. It is a model with a lift in coding, agentic work, and consistency across long sequences of steps.
This cleanup was a quietly long job whose steps simply repeated for each repository. Check the cache location, judge what is safe to remove, remove it. Having it carry that back-and-forth to the end while holding context was, plainly, a relief.
That said, I did not leave it fully on its own.
I asked it to always print the list of deletion targets as text before executing. That is a step I added to the instructions. The smarter the model gets, the more it pays off to write down my own "shape of confirmation."
Here is the target check before deletion, in pseudocode.
# Always visualize targets and approve before deleting
for repo in "${REPOS[@]}"; do
cache="${repo}/build-cache"
# Never pass a path we cannot back with a value into the delete loop
if [ -z "${cache}" ] || [ ! -d "${cache}" ]; then
echo "skip: ${repo} (cache unconfirmed)"
continue
fi
echo "delete candidate: ${cache}"
doneThat single line, [ -z "${cache}" ], is the wall that turns away the empty variable of that night. Auto mode's preflight, plus my own script-side defense. With both in place, I can stay calm even while delegating overnight.
The confirmation rules I settled into
After this incident, I put a few promises into words for whenever I hand a cleanup-type job to an agent.
| Situation | What I decided |
|---|---|
| Just before deletion | Always print the absolute paths of targets as text, and approve only after reading them |
| Handling variables | Any variable that builds a path must be empty-checked; if empty, skip that target |
| Permissions | Never disable auto mode's preflight for steps that include rm -rf |
| Records | Keep execution logs on the assumption they cannot be altered, so the next morning I can trace what was removed |
None of this is special. But on the ground of solo development, whether you can hold these obvious things as a system is what decides whether you can let go with peace of mind.
Unlike AdMob settings or an App Store build, cache cleanup is dull work whose results are hard to see. That is precisely why it turns sloppy, and why accidents happen there.
What I plan to work on next
Next, I am thinking of building a "dry run" into the cleanup script itself as standard. First print only the list of what is about to be removed; once I approve, run the same steps again for real. I want to double up auto mode's preflight on my own tooling side as well.
The range you can hand to a capable agent has certainly grown. At the same time, the responsibility of the one delegating to decide in advance "where I want you to stop" is, quietly, growing too.
I do not think I will forget those cold hands for a while. I am simply grateful for the preflight that took a breath before deleting. If it helps even a little as a small safeguard for someone who also entrusts work to the night, I would be glad. Thank you for reading.