Your AI Agent Doesn't Need More Memory. It Needs Receipts. A developer argues that AI agents' duplicate-action failures stem from missing action receipts, not insufficient memory. The post outlines a four-layer model separating context, plan, attempt, and effect, and proposes a receipt-based state machine to track external operations. The pattern was tested in a CLI writing to the DEV API, where intent files were upgraded to durable receipts that advance through states like submitted, outcome_unknown, and succeeded. An AI agent can remember a 30-page conversation and still perform the same action twice. It sends a request. The connection times out. The agent remembers the goal, the plan, and the tool call—but not whether the outside system changed. So it tries again. That is not a vector-memory problem. It is an action-receipt problem. “Agent memory” often means conversation history, retrieved documents, or durable project knowledge. Those are useful, but they answer questions about what the agent knew—not what happened in another system. I find it useful to separate four layers: | Layer | Question it answers | Typical retention | |---|---|---| | Context | What did the agent know? | Task-scoped | | Plan | What did it intend to do? | Until the task is reviewed | | Attempt | What request did it submit? | Until reconciled | | Effect | What external change was verified? | Durable audit record | The first two help reasoning. The last two prevent duplicate emails, repeated publications, double-created listings, and other expensive “helpful” retries. More context does not close this gap. A model can recall the exact request and still not know whether a server committed it before the connection disappeared. Before submission, failure is simple: nothing was sent, so retrying may be safe. After submission, failure is ambiguous. A timeout, connection reset, or unreadable response can mean either: Treating both cases as “failed” converts a transport problem into a duplicate-action bug. The operation therefore needs a state that most happy-path workflows omit: php planned - submitted - succeeded \- rejected \- outcome unknown - reconciling \- succeeded \- safe to retry \- manual review outcome unknown is not an error message to hide. It is durable knowledge about the limit of what the system can currently prove. A receipt should be written before the external request. Otherwise the precise failure that makes it valuable can also prevent it from existing. A small receipt can contain: { "operation id": "20260816T012030Z-1fd54b31a2", "operation": "articles.create", "target": "/api/articles", "state": "submitted", "intent fingerprint": "sha256:…", "submitted at": "2026-08-16T01:20:30Z", "external id": null, "authentication recorded": false } The fingerprint should be calculated from an allowlisted or redacted representation of the intent, not from secrets. The receipt needs enough identity to recognize the effect later; it does not need to become a second credentials store. This is also different from a log line. Logs describe events. A receipt is an operation record with a lifecycle. The system updates the same record as its knowledge changes. I tested the pattern in a small CLI that writes to the DEV API. The CLI already previewed mutations, required explicit confirmation, wrote a private intent file, and never retried a write after a network failure. But the intent file stayed an intent file forever. A successful response did not advance it to succeeded , and an ambiguous transport failure did not advance it to outcome unknown . The safety rule existed in the client, while the durable state lagged behind it. The corrected shape is deliberately boring: receipt = record intent operation, sanitized request update receipt, state="submitted", submitted at=now try: response = send once except ExplicitRejection as error: update receipt, state="rejected", error=error.code raise except TransportFailure as error: update receipt, state="outcome unknown", error=error.code raise else: update receipt, state="succeeded", external id=response.id, completed at=now , There is intentionally no retry in that exception path. Tests cover success, explicit rejection, and ambiguous failure as different receipt states. One implementation detail mattered more than I expected: state updates should be atomic. Replacing the receipt through a private temporary file avoids turning a process interruption into half a JSON document—the audit system creating its own ambiguous evidence. An unknown outcome is resolved with a read, not another write. The reconciler should: succeeded if the intended effect exists. safe to retry only when absence is actually provable and the operation permits retry. manual review .This is where API design changes the safety envelope. A platform with idempotency keys and exact read-after-write lookup is much easier to automate safely than one with neither. When the platform offers no reliable way to prove absence, “I cannot tell” is the correct answer. Receipts solve one narrow problem: what request was attempted, what the transport reported, and what external effect was later observed. They do not prove that the request was wise, that the payload was semantically correct, or that the verification query inspected the right thing. A perfectly maintained receipt can preserve a bad decision with excellent fidelity. That means receipts belong beside—not instead of—policy checks, human approval for consequential actions, semantic validation, and negative controls for the verifier itself. There are other limits too: Those limits are exactly why manual review belongs in the state machine. Scratch context can expire. Old plans can be archived. Failed approaches can become history. But an externally visible attempt with an unknown outcome should not be forgotten or summarized away. Keep it until the world has been reconciled with the agent's intent. The practical question is not only, “What does the agent remember?” It is: What can the system prove happened before the agent acts again? Which external write in your system is hardest to reconcile after a timeout?