A field note from the autonomous Claude Code agent I run every day on one Windows PC. The numbers come from its own ledgers, not from memory.
My book has a free sample PDF. It ends by saying where to read the rest and gives the address of the full bundle. That line is the whole point of a free sample.
My agent counted the link annotations in that PDF. There were zero. The address was printed as plain text. On a phone, a reader would have had to copy it by hand.
Why it was text
The PDF is built from Markdown, and the line ended with a bare URL. Python-Markdown does not turn a bare URL into a link. Wrapping it in angle brackets (<https://...>) makes it an autolink. The rebuilt sample had exactly the same body text as the old one, 6,667 characters, and link annotations went from 0 to 1.
Replacing it on the first store
The first store has an option to replace a product's files. The agent replaced the file, then downloaded it back and compared the bytes. They matched.
The independent auditor still rejected it. The change was in which file spec the replace function uses, and the test covered only the new helper that picks the spec. The call line inside the replace function was not tested. With the old call put back, the error was caught by an except and the function quietly returned False. Every test still passed. A test that goes through that call line, with a fake browser and a fake ledger, now fails if it is reverted.
Replacing it on the second store
The second store, a Korean one, had a harder problem. The existing update tool rewrites the title, the price and the description along with the file. That is too much to change just to swap a file.
And the store's edit screen shows only the file's name. No size, no address. If the agent uploaded a file with the same name, reading the screen back could not tell whether anything had changed.
So the agent built a separate path that touches only the file field:
- Check the file first: it exists, it is a PDF, it has text, and it contains no banned material.
- Upload a copy whose name ends with the date.
- Go through confirm, next, next, save. If any step fails, don't save.
- Open the edit screen again. Count it as a success only if the new name is shown.
The real run reported success after the read-back. The public page still had the same title and the same free-download button.
There is one limit, and it is written in the handoff notes: two uploads on the same day get the same name, and the read-back can't tell them apart.
The auditor found one more gap. The runner passes a product ID to the replace function, and the test called it only with the free sample's ID. Hardcoding that ID would still pass, and replacing a different product would then change the free sample. A second test now passes a different product's ID.
The rules
- Count what the reader can do, not what the file contains. The address was in the PDF. It just couldn't be tapped.
- If a screen can't show a change, make the change visible. A dated file name turns "did it upload?" into a question the screen can answer.
- Test the line that calls the new code, not only the new code. An
exceptthat swallows errors will hide a revert. - Pass an unusual value in tests. If the only value tested is the common one, a hardcoded value passes too.
Where this comes from. Every post here comes from one setup I run daily: a CLAUDE.md, memory files the agent reads before it touches anything, and a separate auditor agent that returns PASS or FAIL. The first 3 chapters of the book that walks through it are free as a PDF: https://dbsoul.gumroad.com/l/autonomous-ai-agents-claude-code-free-sample
The full edition is 11 chapters plus 4 ready-to-use templates (CLAUDE.md starter, memory files, auditor checklist, measurement guide) and a hands-on section for every chapter, $19 as a PDF: https://dbsoul.gumroad.com/l/autonomous-ai-agents-claude-code
Questions about the setup are welcome in the comments — I'll answer with what actually happened, not theory.