Day 149: The Sandbox Accused the Agent
If you ask an agent to remember something, it should still know it tomorrow. Mine didn’t. A user would teach an agent a rule, which collective account a certain kind of invoice posts to, and the next day it would get it wrong again, having apparently forgotten. Yesterday’s work sat next to the reason it couldn’t read memory back: semantic search was dead because the embedding index defaulted to a provider Pinchy has no key for, so I bundled a small offline embedding model that runs with no network and no API key. Verified in the real image with networking cut off entirely, a German paraphrase of a question now recalls the stored English answer. But reading was only half the memory system, and today the other half fell over.
An attack accusation for a folder that didn’t exist
The reading fix uncovered that agents also couldn’t reliably write memory. What they got instead was a security error: “Access denied: write target escapes the sandbox via a symlink.” That is a scary message. It says the agent tried to break out of its jail through a crafted link. It was completely false. The real story, in PR #762, is duller and more embarrassing: Pinchy’s config granted every agent write access to a memory/ directory, but nothing in the setup ever created that directory. So the sandbox check, resolving a path inside a folder that didn’t exist, failed in a way the code interpreted as a symlink escape, and reported an attack. The agent hadn’t attacked anything. It had been handed a key to a room that was never built, and told it was picking the lock.
You can see the agents coping with it in production, which is its own kind of sad. Denied their real memory path, some improvised, writing their notes into a workbench/MEMORY.md that nothing reads. They kept diaries in a drawer no one opens, because the drawer they were assigned wouldn’t unlock. PR #761 fixed the misdiagnosis, so a missing write root is reported as a missing write root, with the actual allowlist in the message, instead of a false symlink accusation. PR #763 made pinchy_write create parent directories first, but carefully, after the access checks, so a rejected write can never leave a directory behind as a side effect. And PR #762 creates the memory/ folder the config always promised, including a retrofit pass for agents already running in the field.
The fix I deliberately didn’t make
Here’s the one I keep thinking about. Having decided to create the missing directory, the obvious next step is to also create an empty MEMORY.md inside it, so the whole path exists and is ready. I didn’t, on purpose. Pinchy runs an audit watcher that notices when an agent’s memory file changes and records agent.memory_changed, attributed to the agent. If Pinchy itself created an empty MEMORY.md during setup, that watcher would fire and write an audit row saying the agent changed its memory, when the agent did no such thing. In a trail I spent Day 143 hardening against exactly this, a convenience that forges an entry blaming an agent for something it didn’t do is not a convenience, it’s a corruption. So the file stays uncreated until an agent actually writes to it, and the audit trail keeps telling the truth. A test caught me when I first got this wrong: it went red on a zero-byte file, and it was right.
Day 149
Two bugs, one shape, and it’s the shape this whole month keeps returning to. Software fails, and then it says something about the failure, and the something is often a confident, specific, wrong accusation. Here the wrong accusation pointed at the agent: you tried to escape the sandbox. The truth pointed at me: I granted access to a folder I forgot to create. An error message is a claim, and a claim can be false with total conviction. The discipline is to make the failing code accuse the right party, and to refuse the tidy fix that would make your own audit trail lie on your behalf. Legible failure isn’t just showing an error. It’s showing the true one, even when the convenient false one is right there.