Your AI Agent Doesn't Need More Memory. It Needs Receipts.
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. The missing memory layer “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. Timeout is not failure 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: the platform never received the request; or the platform completed it, but the response never reached the agent. 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: 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. What an action receipt records 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 added this to a publishing CLI 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. Reconcile the world, not the agent's story An unknown outcome is resolved with a read, not another write. The reconciler should: Query the external system by its idempotency key or returned identifier when one exists. Otherwise perform a bounded search and match the smallest safe intent fingerprint. Mark the operation succeeded if the intended effect exists. Mark it safe_to_retry only when absence is actually provable and the operation permits retry. Send every remaining case to 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 are evidence, not truth 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: eventual consistency can make a successful effect temporarily invisible; two similar operations may not have a unique fingerprint; some APIs expose no idempotency key or stable lookup; a crashed process can leave a submitted operation that still needs recovery; retention and redaction rules must match the sensitivity of the action. Those limits are exactly why manual_review belongs in the state machine. What should an agent be allowed to forget? 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?
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to