When an agent writes a one-off script, it skips your safety rails
Two agent sessions wrote small operations scripts within a minute of each other. I had already asked the team to use the shared command line, but both agents took the shortest local path: write a script that does the job.
The scripts looked efficient because each was narrow. The problem was that the commands they replaced were not just wrappers around shell operations. The existing disk tools recorded delete approvals, checked the host registry, and refused production targets. A bespoke script that calls the same underlying APIs does not inherit those controls. It creates a second policy implementation, often without the audit record or production guard that made the original command safe.
I checked the actual CLI surface before reviewing the scripts. The operations already existed as disk planning, reclaim, and fleet commands. The repeated implementation was the signal: our paved road had a missing capability, but the agents had no reason to know whether they should extend it or route around it.
A script can be locally correct and operationally unsafe
A one-off script is easy to test against its happy path. It is harder to notice what it bypasses: approval capture, host classification, dry-run behavior, safe retries, locks, and the other callers that depend on the same contract. Those protections live at the command boundary because that is where users and agents enter the operation. A parallel script creates another entrance.
This is especially risky for destructive actions. The same request might be safe on an isolated development machine and forbidden on a production host. The shared CLI knows the host registry and the recorded approval path. A script that directly invokes deletion can be functionally correct while violating the operating contract. No code comment or prompt reminder restores those missing checks.
I also learned that “just add a script” often hides a product gap in the internal platform. In this case the agents wanted options that the common reclaim command did not yet expose. That was a real need. The right fix was to add the missing options to the existing command, add tests for them, and keep the approval and host checks on the shared path. Once the CLI covered the task, the temporary scripts were removed.
Make the safe route easier than inventing one
My rule now starts with discovery: check the existing CLI and framework before writing a new operational entry point. If the capability is missing, extend the established command or library. Put the behavior behind the same approval recorder, host registry, locks and validation that govern its neighboring operations. Then test the behavior through the public command, including the forbidden target and approval paths.
The rule needs a platform consequence. If an agent repeatedly writes a wrapper, I treat that as evidence that the paved road is missing a useful option or discoverability. I update the CLI help and usage examples, not just the instruction file. A short instruction helps, but the architecture is the real control: the agent should have one obvious command that owns the policy.
I also keep one-off scripts for read-only analysis in a different risk category from scripts that mutate hosts or data. But “temporary” does not make a destructive script safe. If it will be used to make a change, it needs the same approval, target validation and evidence as the maintained tool.
The practical lesson is simple: every operational script is another capability surface. Before accepting an agent-generated utility, compare it with the existing CLI and ask which guards it duplicates or omits. If the tool is missing a feature, add that feature where the policy already lives. What safety checks does your shared command enforce that a copied script might silently skip?
Treat the CLI as a policy boundary
The shared command is more than a short spelling for a shell operation. It knows how to identify the target host, display a plan, request the required approval, serialize concurrent work, write an audit record and stop before a production target when the scope is wrong. Each of those behaviors is part of the operation’s contract. A new script that calls the same underlying service can omit all of them and still pass a happy-path test.
When an agent needs a missing capability, the right improvement is usually to add that capability to the shared CLI and its policy surface. The narrow script may be a useful prototype, but the production path should centralize target classification, approval capture, dry-run output, retries and logs. Then every caller inherits the same refusal and evidence behavior. If there is a reason for a one-off command, name its owner, scope and expiry, and keep it from becoming a hidden second implementation.
Make the paved road the capability boundary
I now review an operational script as a new interface, even if it is only a few lines long. I ask which identity it runs as, how it resolves its target, where approval is captured, what happens on retry, whether concurrent calls can race, and what receipt proves the effect. Those questions expose missing policy more reliably than counting lines or deciding whether the script “looks harmless.” A small wrapper can inherit a safe tool’s guarantees only if it actually calls that tool and preserves its contract.
The common CLI should make safe behavior discoverable at the moment an agent needs it. That means help text names the supported option, examples show the ordinary route, and an unsupported request fails with a next step that points back to the maintained command. If every agent has to inspect source to learn how to perform a routine operation, the platform has left too much policy implicit.
I distinguish a disposable read-only diagnostic from a command that mutates hosts, credentials or data. Read-only exploration can be useful for learning. Once a script can perform a side effect, it needs the same target checks, approval recording, serialization and evidence as the maintained path. The right regression test should invoke the public CLI, exercise one allowed target, and include one forbidden target with a negative control proving the refusal is active. That tests the contract callers actually use, not a private helper.
Keep delegated work on the same entry point
A command boundary can still be bypassed if an agent can ask a helper or delegated worker to perform the underlying operation directly. I map the full call graph from the request to the side effect and keep target validation and approval at the service that actually owns the mutation. The CLI is the normal entry point, but the deeper service also checks its invariants so a second caller cannot silently become a privileged route. This is the difference between one convenient interface and one actual policy boundary. When a new caller is legitimate, it should supply the same typed request and receive the same refusal and receipt behavior as the CLI.
The same review applies to permission inheritance. A helper that receives a broad token may be able to perform more than the CLI operation appears to allow, even if its caller validated the target first. I check the identity and scope at the effect boundary, and I keep the approval receipt linked to the exact target and operation. That way, a wrapper cannot transform a narrow approved request into a broader call after validation. When I cannot preserve that contract, I do not ship the wrapper as a temporary convenience.
I review this as a route map. Start from the requested operation and enumerate every route that can perform it: command-line tool, helper script, hosted worker, delegated agent and direct API. For each route, prove the same safety decision happens before the effect. A warning in the manual does not make an unguarded route equivalent. Which ad hoc script in your environment can perform an operation that the official tool would refuse?
How this was verified
I checked the existing command surface, the change that moved the missing options into the shared CLI, and the associated ticket. This article omits assistant-reported test and review counts that were not pinned to a durable receipt.
