The Agent Rewrote the Shell Script in Python and Dropped the Exit Code
The build script was 40 lines of bash. It ran npm test, then npm run build, then rsync the dist folder to a server, then paged us on Slack.
It worked. Nobody loved it. One morning I asked the agent to port it to Python so it would be readable and testable.
The output looked great. Same steps, same order, same messages. The linter was happy. I merged it.
The first week, CI went green on a build that had actually failed. npm test returned non-zero. The Python script kept going, built from stale artifacts, rsynced them, and posted a green Slack message.
The bug was not in any single line. It was in the failure mode the shell version carried implicitly:
set -euo pipefailThat one line made the script stop on the first failed command. In Python, the agent called subprocess.run(["npm", "test"]) without checking the return code, without check=True, and without sys.exit(1) on failure. The happy path was identical. The unhappy path had been quietly deleted.
This is the porting tax that doesn't show up in a side-by-side diff. You compare the commands and they match. You run the script and it works on the day everything works. The failure modes are what the old runtime gave you for free — set -e, pipefail, unbound variable checks, exit codes propagating through pipes — and the agent has no reason to re-create them unless you ask.
After that I started asking for port rewrites with one extra instruction: list the ways the old script could fail, and make sure each one still fails the new script the same way. It is not a thing the agent does on its own. It will produce cleaner code and call it a day.
The same trap shows up when an agent ports a GitHub Actions step to a Makefile, or a Makefile to a justfile, or a curl pipeline into an SDK client. The commands survive. The guarantees do not.