The first unattended run, and the bookkeeping that broke it
A cron job writes and ships a weekly post in two languages. The writing worked first time, and the script around it broke four ways.
The brief was a post every week without sitting down to write it, in English
and Portuguese, shipped the way every other change ships. A pool of 24 ideas,
each with the reader’s question in both languages. A headless Claude writes
both files and may use no figure that is not in the live llms.txt, a
report’s JSON or a route board. Then a script branches, runs bin/check, opens
the PR, merges it, waits for the deploy and fetches both URLs. It refuses to
ship if the agent touched anything outside content/.
Setting up the server found the first failure. Cron starts a bare shell, so
claude: command not found: its directory is added to PATH only by an
interactive shell, and rvm only switches Ruby on an interactive cd. The
script now sets both itself, with the error trap off around rvm’s own scripts,
which return non-zero.
The first real run wrote both posts, then died reading the English title.
YAML.load refuses a bare date in front matter, so it now uses safe_load
with Date permitted. That one failure opened two GitHub issues, one second
apart: set -E runs the error trap inside the $(...) subshell and again in
the parent. It now reports only from the main shell.
The last one was quieter. The sync step resets the clone to main, and when main has changed the script, that rewrites the file bash is still reading line by line. It now copies itself somewhere else and runs the copy.
The rerun shipped a post about flying home for Christmas. Nothing in it had been the hard part.