<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: anassBld</title>
    <description>The latest articles on DEV Community by anassBld (@anasbuilds997).</description>
    <link>https://dev.to/anasbuilds997</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079476%2Ff38b0751-2949-421a-81d7-74c78bced305.jpg</url>
      <title>DEV Community: anassBld</title>
      <link>https://dev.to/anasbuilds997</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anasbuilds997"/>
    <language>en</language>
    <item>
      <title>How to handle scope creep without ruining the client relationship</title>
      <dc:creator>anassBld</dc:creator>
      <pubDate>Sun, 23 Aug 2026 04:34:06 +0000</pubDate>
      <link>https://dev.to/anasbuilds997/how-to-handle-scope-creep-without-ruining-the-client-relationship-427f</link>
      <guid>https://dev.to/anasbuilds997/how-to-handle-scope-creep-without-ruining-the-client-relationship-427f</guid>
      <description>&lt;p&gt;If you build websites for clients, you know the feeling. The project is 95% done, and you get the email: &lt;em&gt;"Could we just add a quick careers page? Oh, and maybe a small integration with our CRM? Should be quick!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's never quick. But saying "No" feels confrontational, and saying "Yes" eats into your margin until you're working for below minimum wage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "One More Thing" Trap
&lt;/h3&gt;

&lt;p&gt;The problem isn't the client asking. The problem is that freelance web designers usually respond with a paragraph of text explaining why it's hard. &lt;/p&gt;

&lt;p&gt;Clients don't read paragraphs. They read prices and timelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to standardize scope changes
&lt;/h3&gt;

&lt;p&gt;Next time a client asks for something out of scope, don't write an essay. Put the request through a structured filter and offer them one of five explicit choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Included:&lt;/strong&gt; It's genuinely tiny. Do it, but &lt;em&gt;document&lt;/em&gt; that you did it for free so it holds value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price:&lt;/strong&gt; "Yes, we can add that CRM integration. It will add $850 to the budget and push launch back by 4 days. Let me know if you want to proceed."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swap:&lt;/strong&gt; "We can add the careers page, but to keep the launch date and budget the same, we'll need to drop the custom team-member illustrations."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Park:&lt;/strong&gt; "Great idea. Let's put this in the 'Phase 2' backlog and tackle it right after we launch Phase 1."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decline:&lt;/strong&gt; "This requires a custom backend we don't specialize in. I recommend bringing in a dedicated backend dev for this."&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Standardize your response
&lt;/h3&gt;

&lt;p&gt;When you remove the emotion and present choices, clients respect the boundary. They usually choose "Park" or happily pay the "Price".&lt;/p&gt;

&lt;p&gt;If you struggle with wording these emails or estimating the impact of a request, I've put together a structured workbook and offline calculator to handle exactly this. &lt;/p&gt;

&lt;p&gt;You can grab the &lt;strong&gt;Scope Change Control Kit for Web Designers&lt;/strong&gt; on my Gumroad here: &lt;a href="https://gridquill.gumroad.com/l/scope-change-control-kit-web-designers" rel="noopener noreferrer"&gt;https://gridquill.gumroad.com/l/scope-change-control-kit-web-designers&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It includes the offline calculator, copy-ready client messages for all five scenarios, and a reusable request log. Stop working for free, and start getting paid for your expertise.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>freelancing</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Your AI Agent shouldn't own its own memory. Here's why.</title>
      <dc:creator>anassBld</dc:creator>
      <pubDate>Sat, 22 Aug 2026 10:45:12 +0000</pubDate>
      <link>https://dev.to/anasbuilds997/your-ai-agent-shouldnt-own-its-own-memory-heres-why-1206</link>
      <guid>https://dev.to/anasbuilds997/your-ai-agent-shouldnt-own-its-own-memory-heres-why-1206</guid>
      <description>&lt;p&gt;When building AI agents, the standard tutorial approach is to pass a sliding window of chat history directly back to the LLM on every turn. The LLM acts as both the reasoning engine and the state manager. &lt;/p&gt;

&lt;p&gt;For simple chat bots, this is fine. For production agents taking actions (API calls, database writes), this architecture is a ticking time bomb.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Probabilistic State
&lt;/h3&gt;

&lt;p&gt;LLMs are probabilistic. If an agent executes a tool call (e.g., &lt;code&gt;charge_credit_card&lt;/code&gt;), and the API times out, the LLM has to &lt;em&gt;guess&lt;/em&gt; what happened based on the error string. Did the charge go through and the connection drop? Or did it fail immediately?&lt;/p&gt;

&lt;p&gt;If the LLM owns the state, it might hallucinate a successful charge, or worse, hallucinate a failure and retry the action, double-charging the customer. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Deterministic Outer Loops
&lt;/h3&gt;

&lt;p&gt;The reasoning engine (the LLM) should be completely decoupled from the state machine. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The LLM outputs intent:&lt;/strong&gt; "I want to call &lt;code&gt;charge_credit_card&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Outer Loop executes it:&lt;/strong&gt; A deterministic, traditional state machine (written in Python/TS) intercepts the intent, logs it to a database as &lt;code&gt;pending&lt;/code&gt;, and executes the call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out-of-band Verification:&lt;/strong&gt; If the call times out, the outer loop &lt;em&gt;does not&lt;/em&gt; blindly ask the LLM what to do. The outer loop pauses the agent, runs an out-of-band verification (e.g., checking the Stripe ledger), updates the exact deterministic outcome (&lt;code&gt;success&lt;/code&gt; or &lt;code&gt;failed&lt;/code&gt;), and &lt;em&gt;only then&lt;/em&gt; hands the state back to the LLM.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stop letting your LLMs manage their own state. Treat them as pure transformation functions: &lt;code&gt;Context -&amp;gt; Intent&lt;/code&gt;. Your deterministic harness must handle the rest.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Timeout Is Not Failure: The State Your AI Agent Is Missing</title>
      <dc:creator>anassBld</dc:creator>
      <pubDate>Tue, 18 Aug 2026 19:56:57 +0000</pubDate>
      <link>https://dev.to/anasbuilds997/timeout-is-not-failure-the-state-your-ai-agent-is-missing-1fml</link>
      <guid>https://dev.to/anasbuilds997/timeout-is-not-failure-the-state-your-ai-agent-is-missing-1fml</guid>
      <description>&lt;p&gt;When an AI agent's HTTP request or browser tool call times out, what does your system record?&lt;/p&gt;

&lt;p&gt;If it records &lt;code&gt;failed&lt;/code&gt;, the agent has a blind spot. A network timeout does not mean the operation failed on the remote server; it means the connection closed before the client received the answer. If the server processed the mutation, retrying the call blindly will create a duplicate artifact: a double payment, a duplicate ticket, a repeat email, or a redundant article.&lt;/p&gt;

&lt;p&gt;If it records &lt;code&gt;succeeded&lt;/code&gt;, it is hallucinating certainty.&lt;/p&gt;

&lt;p&gt;The missing state is &lt;strong&gt;&lt;code&gt;outcome_unknown&lt;/code&gt;&lt;/strong&gt;—a first-class operational status that halts automatic retries, records the unconfirmed mutation, and hands off execution to an explicit reconciliation loop.&lt;/p&gt;

&lt;p&gt;In our previous post, we discussed why agents need &lt;a href="https://dev.to/anasbuilds997/your-ai-agent-doesnt-need-more-memory-it-needs-receipts-1e3m"&gt;action receipts&lt;/a&gt; rather than purely semantic memory. Following valuable discussions with practitioners on distributed systems and memory boundaries, this article turns that concept into a concrete, testable state machine you can drop into any production agent framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Line: Pre-Send Failure vs. Post-Send Ambiguity
&lt;/h2&gt;

&lt;p&gt;Not all exceptions are created equal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Intent Recorded]
       |
       v
[Attempting Transport] ---&amp;gt; (DNS / Local Socket / Auth error) ---&amp;gt; [REJECTED / SAFE_TO_RETRY]
       |
  (Bytes sent)
       |
       v
[Awaiting Response]   ---&amp;gt; (Connection Timeout / Drop / 504)  ---&amp;gt; [OUTCOME_UNKNOWN]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Send Failures&lt;/strong&gt;: If the DNS lookup fails, credentials are missing locally, or the connection is refused before a single byte leaves the socket, the world hasn't changed. The action is deterministically unexecuted and safe to retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Send Ambiguity&lt;/strong&gt;: The moment bytes cross the wire, transport failure ceases to be an indicator of server state. The server may have committed the mutation and crashed during response serialization, or an intermediate proxy timed out after 30 seconds while the backend worker finished the job.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Treating post-send ambiguity as a failure is the root cause of automated duplicate storms.&lt;/p&gt;




&lt;h2&gt;
  
  
  The State Machine
&lt;/h2&gt;

&lt;p&gt;Here is the complete lifecycle of a guarded agent action:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Allowed Next Transitions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;planned&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;Intent recorded locally with safe payload fingerprint.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;submitted&lt;/code&gt;, &lt;code&gt;rejected&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;submitted&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;Bytes sent to remote endpoint; awaiting response.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;succeeded&lt;/code&gt;, &lt;code&gt;rejected&lt;/code&gt;, &lt;code&gt;outcome_unknown&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;outcome_unknown&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suspended&lt;/td&gt;
&lt;td&gt;Network dropped or timed out after submission. Retries blocked.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reconciling&lt;/code&gt;, &lt;code&gt;manual_review&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;reconciling&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;Querying external system for proof of effect.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;succeeded&lt;/code&gt;, &lt;code&gt;safe_to_retry&lt;/code&gt;, &lt;code&gt;manual_review&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;succeeded&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;External ID verified via response or readback.&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;safe_to_retry&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;Absence of effect proven via authoritative readback.&lt;/td&gt;
&lt;td&gt;None (new intent required)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;rejected&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;Server returned deterministic client error (4xx).&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;manual_review&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;Absence/presence cannot be proven programmatically.&lt;/td&gt;
&lt;td&gt;Human intervention&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Idempotency Keys vs. Intent Fingerprints
&lt;/h2&gt;

&lt;p&gt;In payment engineering, distributed consensus is achieved through at-least-once delivery paired with a server-side deduplication key (an &lt;strong&gt;Idempotency Key&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;When an external platform natively supports idempotency headers (such as &lt;code&gt;Idempotency-Key: &amp;lt;uuid&amp;gt;&lt;/code&gt; in Stripe or GitHub GraphQL mutation keys), reconciliation is straightforward: if you time out, you re-send with the exact same key.&lt;/p&gt;

&lt;p&gt;However, the vast majority of web APIs, CRUD services, and browser-driven surfaces &lt;strong&gt;do not support native idempotency keys&lt;/strong&gt;. In those environments, the caller must carry the burden:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Normalized Intent Fingerprint&lt;/strong&gt;: Before sending, compute a canonical cryptographic hash of the semantic payload (mutation type, resource target, normalized body fields).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-After-Write Reconciliation&lt;/strong&gt;: When &lt;code&gt;outcome_unknown&lt;/code&gt; triggers, the agent queries the read API (or search endpoint) for resources created by the agent's account within a bounded timestamp window matching the intent fingerprint.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_intent_fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;separators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sha256:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Retaining the Ambiguity Audit Trail
&lt;/h2&gt;

&lt;p&gt;A subtle trap in state machine design is &lt;strong&gt;destructive in-place updates&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If an action goes &lt;code&gt;submitted -&amp;gt; outcome_unknown -&amp;gt; reconciling -&amp;gt; succeeded&lt;/code&gt;, and you simply overwrite the state to &lt;code&gt;succeeded&lt;/code&gt;, you destroy the historical record that the action lived in an indeterminate state for hours.&lt;/p&gt;

&lt;p&gt;During post-mortems (or when auditing race conditions where another worker observed missing state during that window), knowing &lt;em&gt;how&lt;/em&gt; an action reached success is as critical as the final state.&lt;/p&gt;

&lt;p&gt;A robust action receipt preserves the full transition trajectory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20260818T190000Z-a1b2c3d4e5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"articles.create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"succeeded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent_fingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:4d8a..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state_history"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"planned"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recorded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:00:00Z"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submitted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recorded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:00:01Z"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"outcome_unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"recorded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:00:31Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gateway Timeout 504"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reconciling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"recorded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:05:00Z"&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"succeeded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"recorded_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-18T19:05:02Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reconciliation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Readback from /api/articles matched title fingerprint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"external_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4407310&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Minimal Working Implementation
&lt;/h2&gt;

&lt;p&gt;Here is a Python implementation of the guarded execution and reconciliation pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;operation_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;planned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;state_history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recorded_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_guarded_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ActionReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ActionReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;operation_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m%dT%H%M%SZ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;compute_intent_fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;planned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Record intent persistently before touching network
&lt;/span&gt;    &lt;span class="nf"&gt;persist_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;submitted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;persist_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;succeeded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TimeoutError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Crucial: DO NOT retry. Mark as ambiguous.
&lt;/span&gt;        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outcome_unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;persist_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reconcile_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ActionReceipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read_fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ActionReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outcome_unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;

    &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reconciling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;persist_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;matched_item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matched_item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matched_item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;succeeded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Matched on readback query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If absence is authoritatively proven, mark safe for a fresh attempt
&lt;/span&gt;        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;safe_to_retry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authoritative readback showed 0 records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;persist_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Limitations &amp;amp; Eventual Consistency
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Eventual Consistency Lag&lt;/strong&gt;: In distributed databases, a newly created resource might not be immediately visible on read replicas. A reconciliation loop must account for propagation delay with bounded backoff rather than immediately concluding absence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blind Mutation Endpoints&lt;/strong&gt;: If an API allows mutations but exposes no listing, search, or readback endpoints, reconciliation cannot be automated. Such actions must transition to &lt;code&gt;manual_review&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destructive Operations&lt;/strong&gt;: Deletion operations (&lt;code&gt;DELETE&lt;/code&gt;) are inherently trickier to reconcile because absence is the intended target state. A missing item could mean either the delete succeeded or the item never existed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;When building autonomous agents interacting with external APIs or browser surfaces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which external write in your systems is hardest to reconcile after an unexpected timeout, and how do you prevent duplicate execution?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Your AI Agent Doesn't Need More Memory. It Needs Receipts.</title>
      <dc:creator>anassBld</dc:creator>
      <pubDate>Sun, 16 Aug 2026 01:15:21 +0000</pubDate>
      <link>https://dev.to/anasbuilds997/your-ai-agent-doesnt-need-more-memory-it-needs-receipts-1e3m</link>
      <guid>https://dev.to/anasbuilds997/your-ai-agent-doesnt-need-more-memory-it-needs-receipts-1e3m</guid>
      <description>&lt;p&gt;An AI agent can remember a 30-page conversation and still perform the same action twice.&lt;/p&gt;

&lt;p&gt;It sends a request. The connection times out. The agent remembers the goal, the plan, and the tool&lt;br&gt;
call—but not whether the outside system changed. So it tries again.&lt;/p&gt;

&lt;p&gt;That is not a vector-memory problem. It is an action-receipt problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  The missing memory layer
&lt;/h2&gt;

&lt;p&gt;“Agent memory” often means conversation history, retrieved documents, or durable project knowledge.&lt;br&gt;
Those are useful, but they answer questions about what the agent knew—not what happened in another&lt;br&gt;
system.&lt;/p&gt;

&lt;p&gt;I find it useful to separate four layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Typical retention&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;What did the agent know?&lt;/td&gt;
&lt;td&gt;Task-scoped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan&lt;/td&gt;
&lt;td&gt;What did it intend to do?&lt;/td&gt;
&lt;td&gt;Until the task is reviewed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attempt&lt;/td&gt;
&lt;td&gt;What request did it submit?&lt;/td&gt;
&lt;td&gt;Until reconciled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect&lt;/td&gt;
&lt;td&gt;What external change was verified?&lt;/td&gt;
&lt;td&gt;Durable audit record&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first two help reasoning. The last two prevent duplicate emails, repeated publications,&lt;br&gt;
double-created listings, and other expensive “helpful” retries.&lt;/p&gt;

&lt;p&gt;More context does not close this gap. A model can recall the exact request and still not know&lt;br&gt;
whether a server committed it before the connection disappeared.&lt;/p&gt;
&lt;h2&gt;
  
  
  Timeout is not failure
&lt;/h2&gt;

&lt;p&gt;Before submission, failure is simple: nothing was sent, so retrying may be safe.&lt;/p&gt;

&lt;p&gt;After submission, failure is ambiguous. A timeout, connection reset, or unreadable response can&lt;br&gt;
mean either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the platform never received the request; or&lt;/li&gt;
&lt;li&gt;the platform completed it, but the response never reached the agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treating both cases as “failed” converts a transport problem into a duplicate-action bug.&lt;/p&gt;

&lt;p&gt;The operation therefore needs a state that most happy-path workflows omit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;planned -&amp;gt; submitted -&amp;gt; succeeded
                    \-&amp;gt; rejected
                    \-&amp;gt; outcome_unknown -&amp;gt; reconciling
                                         \-&amp;gt; succeeded
                                         \-&amp;gt; safe_to_retry
                                         \-&amp;gt; manual_review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;outcome_unknown&lt;/code&gt; is not an error message to hide. It is durable knowledge about the limit of what&lt;br&gt;
the system can currently prove.&lt;/p&gt;
&lt;h2&gt;
  
  
  What an action receipt records
&lt;/h2&gt;

&lt;p&gt;A receipt should be written before the external request. Otherwise the precise failure that makes&lt;br&gt;
it valuable can also prevent it from existing.&lt;/p&gt;

&lt;p&gt;A small receipt can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20260816T012030Z-1fd54b31a2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"articles.create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/articles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submitted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent_fingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submitted_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-16T01:20:30Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"external_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authentication_recorded"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fingerprint should be calculated from an allowlisted or redacted representation of the intent,&lt;br&gt;
not from secrets. The receipt needs enough identity to recognize the effect later; it does not need&lt;br&gt;
to become a second credentials store.&lt;/p&gt;

&lt;p&gt;This is also different from a log line. Logs describe events. A receipt is an operation record with&lt;br&gt;
a lifecycle. The system updates the same record as its knowledge changes.&lt;/p&gt;
&lt;h2&gt;
  
  
  I added this to a publishing CLI
&lt;/h2&gt;

&lt;p&gt;I tested the pattern in a small CLI that writes to the DEV API. The CLI already previewed mutations,&lt;br&gt;
required explicit confirmation, wrote a private intent file, and never retried a write after a&lt;br&gt;
network failure.&lt;/p&gt;

&lt;p&gt;But the intent file stayed an intent file forever. A successful response did not advance it to&lt;br&gt;
&lt;code&gt;succeeded&lt;/code&gt;, and an ambiguous transport failure did not advance it to &lt;code&gt;outcome_unknown&lt;/code&gt;. The safety&lt;br&gt;
rule existed in the client, while the durable state lagged behind it.&lt;/p&gt;

&lt;p&gt;The corrected shape is deliberately boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;record_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sanitized_request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;submitted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submitted_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;send_once&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ExplicitRejection&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TransportFailure&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outcome_unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;succeeded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;completed_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is intentionally no retry in that exception path. Tests cover success, explicit rejection,&lt;br&gt;
and ambiguous failure as different receipt states.&lt;/p&gt;

&lt;p&gt;One implementation detail mattered more than I expected: state updates should be atomic. Replacing&lt;br&gt;
the receipt through a private temporary file avoids turning a process interruption into half a JSON&lt;br&gt;
document—the audit system creating its own ambiguous evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconcile the world, not the agent's story
&lt;/h2&gt;

&lt;p&gt;An unknown outcome is resolved with a read, not another write.&lt;/p&gt;

&lt;p&gt;The reconciler should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query the external system by its idempotency key or returned identifier when one exists.&lt;/li&gt;
&lt;li&gt;Otherwise perform a bounded search and match the smallest safe intent fingerprint.&lt;/li&gt;
&lt;li&gt;Mark the operation &lt;code&gt;succeeded&lt;/code&gt; if the intended effect exists.&lt;/li&gt;
&lt;li&gt;Mark it &lt;code&gt;safe_to_retry&lt;/code&gt; only when absence is actually provable and the operation permits retry.&lt;/li&gt;
&lt;li&gt;Send every remaining case to &lt;code&gt;manual_review&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where API design changes the safety envelope. A platform with idempotency keys and exact&lt;br&gt;
read-after-write lookup is much easier to automate safely than one with neither. When the platform&lt;br&gt;
offers no reliable way to prove absence, “I cannot tell” is the correct answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Receipts are evidence, not truth
&lt;/h2&gt;

&lt;p&gt;Receipts solve one narrow problem: what request was attempted, what the transport reported, and&lt;br&gt;
what external effect was later observed.&lt;/p&gt;

&lt;p&gt;They do not prove that the request was wise, that the payload was semantically correct, or that the&lt;br&gt;
verification query inspected the right thing. A perfectly maintained receipt can preserve a bad&lt;br&gt;
decision with excellent fidelity.&lt;/p&gt;

&lt;p&gt;That means receipts belong beside—not instead of—policy checks, human approval for consequential&lt;br&gt;
actions, semantic validation, and negative controls for the verifier itself.&lt;/p&gt;

&lt;p&gt;There are other limits too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eventual consistency can make a successful effect temporarily invisible;&lt;/li&gt;
&lt;li&gt;two similar operations may not have a unique fingerprint;&lt;/li&gt;
&lt;li&gt;some APIs expose no idempotency key or stable lookup;&lt;/li&gt;
&lt;li&gt;a crashed process can leave a submitted operation that still needs recovery;&lt;/li&gt;
&lt;li&gt;retention and redaction rules must match the sensitivity of the action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those limits are exactly why &lt;code&gt;manual_review&lt;/code&gt; belongs in the state machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should an agent be allowed to forget?
&lt;/h2&gt;

&lt;p&gt;Scratch context can expire. Old plans can be archived. Failed approaches can become history.&lt;/p&gt;

&lt;p&gt;But an externally visible attempt with an unknown outcome should not be forgotten or summarized&lt;br&gt;
away. Keep it until the world has been reconciled with the agent's intent.&lt;/p&gt;

&lt;p&gt;The practical question is not only, “What does the agent remember?”&lt;/p&gt;

&lt;p&gt;It is: &lt;strong&gt;What can the system prove happened before the agent acts again?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which external write in your system is hardest to reconcile after a timeout?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
