Letting an AI Write Code That Can Delete Things — Safely
Letting an AI Write Code That Can Delete Things — Safely
I asked my AI setup to do something small and boring: clean up the leftover processes left running on my machine after a day of work. The obvious way to do that job would have been a quiet disaster.
A day of building leaves debris — a development server still bound to a port, a headless browser that never shut down, a script that outlived its purpose. Reaping that debris is exactly the kind of tedious task you want to automate. But "kill the leftover processes" is a trap, because the leftovers and the things you must never kill look almost identical from the outside.
Everything looks like a leftover
On my machine, a naive "find the stray node, python, and browser processes and kill them" sweep would have taken down, in no particular order:
- the helper processes of a live coding session I still had open in another window, and
- my actual web browser.
Neither is a leftover. And a broader sweep — one run with more privilege — would have reached further, into a container quietly doing real work that happened to be spared only because it runs as a different system user. Every one of these presents to the operating system as a generic node, python, or chrome process — the same shape as the junk I wanted gone. The two things I actually wanted killed — one stray dev server, one abandoned browser daemon — were a small, nameable set. Everything else was a live dependency wearing the same costume.
This is the general shape of every destructive operation an AI might write for you: the blast radius is defined by what the code decides to spare, and "what to spare" is easy to get catastrophically wrong.
Rule 1: gate it deterministically — don't trust the model to decide
The tempting fix is to make the AI smarter about telling a leftover from a live process. That is the wrong instinct. "Smarter" means the model re-makes a fuzzy judgement every time it runs, and one bad call is irreversible.
So the cleanup does not let the model decide what to kill. It is built around an explicit, mechanical gate:
- a denylist of things it may never touch — live sessions, the tools doing the orchestrating, background file-sync, system services, anything inside a container;
- a short allowlist of things it may kill — the specific dev servers and daemons that are genuinely disposable;
- and for everything else: do nothing.
An unknown process is not a kill candidate. It is spared by default. You have to earn a kill by being on the allowlist and absent from the denylist. The safety lives in the lists — which I wrote and can read — not in how sharp the model is on a given afternoon.
The test of whether you built this right: could a weaker, cheaper model run it safely? If the answer is no, your safety was never in the code — it was in hoping the model stayed clever. With the judgement moved into an explicit gate, the runtime model is just executing a deterministic check.
Rule 2: give the unattended path a smaller blast radius
There are two ways this cleanup runs: when I explicitly ask for it, and automatically when a session ends. Those are not the same risk.
When I ask for it, I am watching. When it fires on its own — including when I simply reset my workspace mid-task, while I am still working — nobody is. So the automatic path is deliberately more timid: it only reaps processes that have been genuinely abandoned (their parent is gone), and it refuses to run at all on the events most likely to fire while I am still working. Same tool, two safety levels, matched to whether a human is in the loop.
An operation that runs unattended should never have the same reach as one you personally trigger. That is not paranoia; it is matching the blast radius to the amount of supervision.
Rule 3: have an adversary try to break it before it runs
Here is the part that actually saved me.
The agent that wrote the cleanup script also wrote a full set of tests, ran them, and reported success: everything protected was still alive, everything disposable was gone. Green across the board.
Then I did something different. I gave the finished script to a second, independent agent with one instruction: don't confirm this works — try to make it kill the wrong thing.
It found three real holes, none of which the passing tests had caught:
- One kill path skipped the safety list entirely. A special case for cleaning up browser processes matched on a raw text pattern, bypassing the denylist — which meant that under the right conditions it could have matched and killed my real browser, not the disposable one.
- A shared flag could kill a live session. When two browser-automation sessions were running — one abandoned, one live — a single shared setting would have marked the live one for death too. The exact session it was supposed to protect.
- A pattern was too greedy. The rule meant to catch a throwaway static-file server also matched
ollama serve— the command that runs a local AI model. Reaping "leftovers" would have shut down a running model server.
Every one of those is the difference between a useful tool and an incident. And every one got through the implementer's own green tests, because a test written by the author checks the cases the author thought of. An adversary checks the cases they didn't.
This is not exotic. It is the same principle as asking someone to prove a claim false instead of confirm it's true — you get completely different, and far more useful, answers from the same model, on the same code, just by pointing it the other way.
What "owning your AI" actually means
There is a lot of talk about whether you should trust AI with real access to your systems. I think that framing misses it. The question isn't trust — it's structure.
You get the productivity by letting the AI write the tedious, error-prone operational code you would rather not write by hand. You get the safety by refusing to let its output run on trust: you gate the destructive parts behind rules you control, you give the unattended paths less reach than the supervised ones, and you send anything irreversible through an adversarial review before it ever executes.
That is what owning an AI agent looks like in practice — not renting a black box and hoping, but running one you can inspect, constrain, and supervise. The agent does the work. You keep the keys.
This post is about engineering practice, not a product pitch — but it is also exactly how we build the automation we hand to clients: the AI does the labour, deterministic gates and human review hold the line. If that is the kind of AI you want in your business — one you own and supervise rather than rent and trust — that's the conversation we have.
Frequently Asked Questions
Can't I just tell the AI to 'be careful' and not delete the wrong thing?
No, and this is the core mistake. 'Be careful' is a judgement the model re-makes every single time it runs, on whatever it happens to see that day. The safety of a destructive operation should not depend on the model getting a fuzzy decision right under pressure. It should depend on an explicit list of what may never be touched — a list you wrote, that the code checks mechanically. The model's cleverness stops being load-bearing.
What is the difference between a denylist and an allowlist here, and why use both?
The allowlist is the short set of things the cleanup is permitted to kill (a specific kind of dev server, a specific browser daemon). The denylist is the set it may never touch under any circumstances (live sessions, the orchestration tools, background sync, anything inside a container, system services). Everything that matches neither list is simply left alone — not killed. The default is 'do nothing', and you have to earn a kill by being on the allowlist while not being on the denylist. That ordering is what makes it safe: an unknown process is spared, never reaped.
Why have a second AI review the first AI's code? Isn't that just more of the same?
Because an implementer testing its own work is grading its own homework. The agent that wrote my cleanup script produced a full set of passing tests and reported that nothing protected was harmed. A second agent, told explicitly to break it rather than confirm it, found three real ways it could kill the wrong process — none of which the passing tests caught. The value is not 'two models are smarter than one'. It is that a verifier looks for reasons to say yes and a refuter looks for reasons to say no, and for anything irreversible you want the one looking for reasons to say no.
Does this mean AI shouldn't be allowed near infrastructure at all?
The opposite. AI is genuinely good at this kind of tedious operational work. The point is that 'good at writing it' and 'safe to run unsupervised' are different properties. You get the productivity by letting the AI write the code, and you get the safety by gating it with rules you control and reviewing the destructive parts adversarially before they ever run. That combination — use the agent, but own and supervise it — is the whole game.
Can a cheaper or smaller model run this safely once it is built?
Yes, and that is a useful test of whether you built it right. Once the judgement about what may never be killed lives in an explicit list inside the script, the model at runtime is just executing a deterministic gate. It barely matters how clever it is, because it is not the thing deciding what is safe. If your 'safety' would break under a weaker model, your safety was never in the code — it was in hoping the model stayed sharp.
Ready to automate your business?
We build AI tools and automation systems for European SMEs — from rapid MVPs to production systems, always GDPR-compliant.
it's human stuff
Weekly AI insights for European SMEs. No hype, just what works.
Keep Reading
An ICML Paper Won the Benchmark by Returning Nothing
Researchers built a deliberately useless method that beats the standard score everyone reports. I re-ran it on my own CPU in eight seconds and got their number back. Then I found their published code doesn't reproduce their own paper. Here is what that means when a vendor quotes you a benchmark.
How Our Local AI Agent Does Real Research — With No US Cloud in the Loop
Most 'AI research agent' demos are a thin wrapper around a frontier cloud API. Ours isn't. Here is the architecture of a local agent that searches, reads and verifies entirely on hardware we own — the runtime, the self-hosted tools, the subagents, and the proof it does real work.