<?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: Vlad</title>
    <description>The latest articles on DEV Community by Vlad (@mrvlad).</description>
    <link>https://dev.to/mrvlad</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%2F3977904%2F70e34f00-e03a-483d-9785-166f364b76f7.png</url>
      <title>DEV Community: Vlad</title>
      <link>https://dev.to/mrvlad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrvlad"/>
    <language>en</language>
    <item>
      <title>Two AI agents, one Postgres row: the bug your version check won't catch</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:26:11 +0000</pubDate>
      <link>https://dev.to/mrvlad/two-ai-agents-one-postgres-row-the-bug-your-version-check-wont-catch-4i21</link>
      <guid>https://dev.to/mrvlad/two-ai-agents-one-postgres-row-the-bug-your-version-check-wont-catch-4i21</guid>
      <description>&lt;p&gt;If you have two AI agents writing the same Postgres row, you have probably already added a conditional write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a peer moved the version first, you get &lt;code&gt;rowcount = 0&lt;/code&gt; and reject the write. That is real, it works, and you should keep it. It closes half the problem.&lt;/p&gt;

&lt;p&gt;This post is about the other half, which is quieter and shows up as a bug you will blame on the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure
&lt;/h2&gt;

&lt;p&gt;Here is the timeline that bit me.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent A reads the row at version 7 and starts a multi-step edit. Call it two minutes of reasoning.&lt;/li&gt;
&lt;li&gt;Agent B commits version 8.&lt;/li&gt;
&lt;li&gt;A finally writes. The conditional write comes back &lt;code&gt;rowcount = 0&lt;/code&gt; and A's write is rejected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The row is fine. That is exactly what the CAS is for.&lt;/p&gt;

&lt;p&gt;But look at what happened in step 2. For those two minutes, A was deriving work from a value that was already dead. The plan it wrote, the summary it saved, the tool call it fired. None of that went through the row's CAS, so none of it was rejected. It all landed normally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The row survived. The work is contaminated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A version check protects the write. It does not protect the reader.&lt;/p&gt;

&lt;p&gt;This is specific to agents in a way it is not for ordinary CRUD. A normal service reads and writes in the same breath, so the window is milliseconds. An agent reads, reasons for a long time, then acts. The window is the whole reasoning pass, and everything it emits in that window escapes before the CAS ever fires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;The demos are offline and need no credentials, so you can watch this happen in about a minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Cohexa-ai/agent-coherence
&lt;span class="nb"&gt;cd &lt;/span&gt;agent-coherence
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[coherent-row]"&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; examples.coherent_row.main &lt;span class="nt"&gt;--baseline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--baseline&lt;/code&gt; arm runs the un-coordinated agent first, so you see it act on its stale cache, and then runs the guarded version. The deny gets measured against its absence rather than just asserted at you. Exit code is &lt;code&gt;0&lt;/code&gt; only if the contract held.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, in code
&lt;/h2&gt;

&lt;p&gt;The piece that was missing is telling A that its cached view died, before A acts. That is what the binding adds. Condensed from &lt;code&gt;examples/coherent_row/main.py&lt;/code&gt;:&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;ccs.adapters.coherent_row&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoherentRow&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ccs.adapters.substrate&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoordinatedSubstrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SubstrateCoordinatorSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ccs.core.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StaleView&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SubstrateCoordinatorSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;managed&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="n"&gt;agent_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoordinatedSubstrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CoherentRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;profiles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dsn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DSN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ... A reasons for a while. Meanwhile B commits a new version ...
&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;agent_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;edited&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;StaleView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fresh_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reacquire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fresh_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;redecide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The detail I care most about is what does &lt;em&gt;not&lt;/em&gt; happen. When &lt;code&gt;StaleView&lt;/code&gt; is raised, the write never reaches Postgres. The binding denies it at the coordinator before the substrate is touched, and the demo asserts exactly that by checking the CAS was never called. You are not catching a rejection after the fact. You are stopping the act.&lt;/p&gt;

&lt;p&gt;Recovery is one verb, &lt;code&gt;reacquire()&lt;/code&gt;, and it is the same verb whether the state is a row, an S3 object, or a file on disk. &lt;code&gt;CoherentObject&lt;/code&gt; gives you the identical story against S3 with &lt;code&gt;If-Match&lt;/code&gt; underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your bytes stay in your database
&lt;/h2&gt;

&lt;p&gt;The thing that would make me suspicious of a library like this is whether it quietly becomes a second store. It does not, and the design makes that checkable.&lt;/p&gt;

&lt;p&gt;The coordinator holds a monotonic version, per-agent state, and a fixed-width content hash. The binding declares &lt;code&gt;SENDS_CONTENT_TO_COORDINATOR = False&lt;/code&gt;, the conformance kit asserts it, and composition refuses any binding that sets it &lt;code&gt;True&lt;/code&gt;. The row body never leaves Postgres. Your database keeps its backups, permissions, and durability story, and stays the system of record. Remove the library tomorrow and your data is exactly where it always was.&lt;/p&gt;

&lt;p&gt;Nobody should migrate production state to get a correctness guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you do not need this
&lt;/h2&gt;

&lt;p&gt;Worth saying plainly, because it is a real answer for some of you.&lt;/p&gt;

&lt;p&gt;If you wrap every read and write in a &lt;code&gt;pg_advisory_lock&lt;/code&gt; and never cache a read between them, the bare CAS is enough and you can stop here. The binding is for the agent that reads, reasons, then acts. If your window between read and write is short and you never derive anything inside it, you do not have this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest scope
&lt;/h2&gt;

&lt;p&gt;The limits matter more than the pitch, so here they are.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single host only.&lt;/strong&gt; Two writers on two machines are not covered by a shipped guarantee. The cross-host transport is a demo and is not production fencing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bindings are cooperative.&lt;/strong&gt; A writer that goes straight to Postgres without the binding is not caught at write time. The coordinator notices the divergence on the next mediated read from the content hash, and detection after the fact is not prevention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The demo models the row in memory&lt;/strong&gt; so it runs offline. The real Postgres CAS is exercised against actual Postgres in the conformance kit's &lt;code&gt;real_substrate&lt;/code&gt; arm. In production you point the same binding at a real DSN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No read-generation fence in this binding.&lt;/strong&gt; The v1 writers ride admit-on-absent plus the version CAS. The binding surfaces invalidation. The fence is a documented roadmap item, not shipped behaviour here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not make a weak substrate strong.&lt;/strong&gt; Each binding declares a capability tier derived from what the substrate can actually enforce, and the conformance kit checks the declared tier against observed behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"agent-coherence[coherent-row]"&lt;/span&gt;    &lt;span class="c"&gt;# Postgres&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"agent-coherence[coherent-object]"&lt;/span&gt; &lt;span class="c"&gt;# S3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/Cohexa-ai/agent-coherence" rel="noopener noreferrer"&gt;https://github.com/Cohexa-ai/agent-coherence&lt;/a&gt;&lt;br&gt;
Full write-up: &lt;a href="https://agent-coherence.dev/blog/shipped-byo-substrate/" rel="noopener noreferrer"&gt;https://agent-coherence.dev/blog/shipped-byo-substrate/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have hit the read-reason-act window in your own system, I would like to hear how it surfaced for you. It almost never gets reported as a concurrency bug.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>mlops</category>
      <category>agentengineering</category>
    </item>
    <item>
      <title>Write returned success. The file was never there.</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:47:57 +0000</pubDate>
      <link>https://dev.to/mrvlad/write-returned-success-the-file-was-never-there-12h4</link>
      <guid>https://dev.to/mrvlad/write-returned-success-the-file-was-never-there-12h4</guid>
      <description>&lt;p&gt;Four issues filed in the past week describe the same failure: an agent writes to persistent storage, the write API returns without error, and the data is gone. No exception, no log entry, no indication that anything went wrong until something tries to read what was written.&lt;/p&gt;

&lt;p&gt;The symptoms vary. In one case, a Write tool call reports success while a concurrent disk check from a separate process shows nothing written. In another, 28 concurrent agent workflows report &lt;code&gt;started=1, result=0&lt;/code&gt; in their journals with no abort marker. In a third, two processes writing to the same data directory produce 157 GB of growth and a kernel panic. The corruption accumulated silently over days before the system failed. In a fourth, a memory layer agent skips writes entirely or writes partial records. The store fills with fragments no future session can act on.&lt;/p&gt;

&lt;p&gt;The failure is structural. A write that looks atomic to the caller is not atomic to the filesystem when multiple processes share state. The write API returns when the calling process hands off to the OS or a downstream layer, not when durability is confirmed across all concurrent writers. If two writers race on the same file, one loses. If a shared runtime dies mid-flight, in-progress writes evaporate. The caller gets no signal either way.&lt;/p&gt;

&lt;p&gt;What makes this hard to debug is where the evidence lands. The write site looks clean. The gap shows up at the read site: a future session, a downstream consumer, or a human checking disk from outside the agent's process. By then, the causal chain is several hops from where the failure occurred.&lt;/p&gt;

&lt;p&gt;Closing this class requires three things.&lt;/p&gt;

&lt;p&gt;Writes to shared state need to go through a coordination layer that enforces at-most-one-writer semantics. File locks, atomic renames, or a mediating coordinator all work. The mechanism matters less than the invariant: concurrent writes to the same artifact are serialized, not raced.&lt;/p&gt;

&lt;p&gt;That coordination layer needs to sit in the critical path of the write. If the agent can bypass it, the invariant breaks under concurrent load.&lt;/p&gt;

&lt;p&gt;And failures need to surface at the write site, not the read site. A write that cannot be confirmed as durable should return an error to the caller. A write that silently succeeds but leaves nothing behind is a lie the next session has to investigate.&lt;/p&gt;

&lt;p&gt;None of this is new. Distributed databases and cache coherence protocols solved this class decades ago. What's changed is that multi-agent systems are hitting it at the filesystem and plugin layer, where the coordination primitives are still thin.&lt;/p&gt;

&lt;p&gt;We built agent-coherence to address this for the AI agent case. The coordinator enforces single-writer invariants across concurrent sessions and surfaces write failures at the call site instead of the read site. &lt;/p&gt;

&lt;p&gt;Library at github.com/hipvlady/agent-coherence, with adapters for LangGraph, CrewAI, and Claude Code workflows.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>rag</category>
    </item>
    <item>
      <title>The write your agents lost — and why nothing errored</title>
      <dc:creator>Vlad</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:56:21 +0000</pubDate>
      <link>https://dev.to/mrvlad/the-write-your-agents-lost-and-why-nothing-errored-k1n</link>
      <guid>https://dev.to/mrvlad/the-write-your-agents-lost-and-why-nothing-errored-k1n</guid>
      <description>&lt;h3&gt;
  
  
  Three ways an agent fleet loses work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario one: the parallel sessions.&lt;/strong&gt; &lt;br&gt;
Two coding agents work the same repository — one refactoring, one writing tests, both reading and updating the shared &lt;code&gt;plan.md&lt;/code&gt;. Session B commits a revised plan. Session A, which read the plan twenty minutes ago, finishes its task and writes its version back. B's revision is gone. No exception, no conflict marker, no log line. The next agent to read the plan builds on the wrong one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario two: the orchestrator fleet.&lt;/strong&gt; &lt;br&gt;
A planner dispatches six workers; each appends its result to a shared decisions document or store key. Two workers finish in the same instant. Both writes "succeed." One of them isn't there afterward. With humans this is the oldest concurrency bug in the book; with agents it's worse, because nobody re-reads the document with suspicion — the next prompt just inherits whatever survived.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario three: the overnight agent.&lt;/strong&gt; &lt;br&gt;
A long-running agent stalls mid-task while holding the write lock. Your recovery logic — correctly — reclaims the lock so the rest of the fleet isn't blocked. Hours later the stalled process wakes up and completes its write. Here's the trap: if nothing else changed the artifact in between, the version number still matches. Every version check passes. The zombie's stale commit lands on top of a state the system has long since moved past.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why agents make this worse than microservices
&lt;/h3&gt;

&lt;p&gt;Distributed systems have had these bugs for fifty years. What's new is the failure &lt;em&gt;presentation&lt;/em&gt;. A service that reads stale state usually crashes or returns something visibly wrong. An agent that reads stale state confabulates continuity — it produces fluent, confident output built on the wrong version, and the error surfaces three steps downstream as "the model hallucinated" or "the agent forgot."&lt;/p&gt;

&lt;p&gt;So teams debug the model. They rewrite prompts, swap providers, add retries. But the bug isn't in the model — it's in the write path. Until the state layer can refuse a stale write, every layer above it inherits silent corruption.&lt;/p&gt;
&lt;h3&gt;
  
  
  What "enforcement" can fix it?
&lt;/h3&gt;

&lt;p&gt;agent-coherence started as a coherence protocol: MESI-style ownership and invalidation over shared artifacts, so a write from a stale view is denied fail-closed and the writer must re-read before it can land anything. That covers scenario one — the sequential stale-read-then-write.&lt;/p&gt;

&lt;p&gt;In the recent version (out now on PyPI), it completes the picture with enforcement for the two cases ownership alone can't catch:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent writers — optimistic commit-CAS.&lt;/strong&gt; &lt;br&gt;
&lt;code&gt;write_cas&lt;/code&gt; commits only if the artifact version still equals the version the writer read. Two agents racing the same key resolve to exactly one winner; the loser receives a typed conflict and a bounded retry path — read fresh, recompute, commit again. Scenario two stops being a coin flip and becomes a protocol. The invariant has a name: &lt;code&gt;NoLostUpdate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crash-reclaimed writers — the read-generation fence.&lt;/strong&gt; &lt;br&gt;
Every reclamation bumps the artifact's ownership epoch; every claim captures the epoch it was made under; commit checks them atomically with the version persist. The overnight zombie from scenario three is rejected even though the version number never moved — with a typed, retryable reason, not a silent overwrite. The invariant: &lt;code&gt;NoStaleApply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And the piece that makes reclamation safe to run at all: a crashed agent holding EXCLUSIVE forever would block the fleet, so a heartbeat/TTL sweep reclaims stale grants automatically — on by default since the previous version.&lt;/p&gt;
&lt;h3&gt;
  
  
  The rigor part
&lt;/h3&gt;

&lt;p&gt;Every guarantee above is a safety invariant model-checked with TLA+/TLC. Four specs — the MESI protocol, crash recovery, optimistic concurrency, and the fence — run in CI on every push. Each spec carries a documented mutant (remove the guard, weaken the check) that must turn the model checker red; if the mutant passes, the invariant isn't load-bearing and the build fails the&lt;br&gt;
review. The fence itself is server-side by design: no public write API accepts&lt;br&gt;
a generation or fence argument, and a CI signature guard enforces that&lt;br&gt;
boundary.&lt;/p&gt;

&lt;p&gt;This is the difference between "we added locking" and "here is the state&lt;br&gt;
machine, here is the invariant, here is the checker run that explores every&lt;br&gt;
interleaving up to the model bounds."&lt;/p&gt;
&lt;h3&gt;
  
  
  Scope, honestly
&lt;/h3&gt;

&lt;p&gt;The guarantees hold for writers that go through the coordinator, under a&lt;br&gt;
single coordinator — one host. Concurrent same-key writers on one host are&lt;br&gt;
covered. Cross-host fencing is on the roadmap and demand-gated: if your fleet&lt;br&gt;
spans machines and you need it, open an issue — that's the signal that pulls&lt;br&gt;
it forward.&lt;/p&gt;
&lt;h3&gt;
  
  
  The economics come along for free
&lt;/h3&gt;

&lt;p&gt;Correctness is the wedge, but the same protocol is why the token bill drops:&lt;br&gt;
writes publish ~12-token invalidation signals instead of rebroadcasting full&lt;br&gt;
artifacts, so read-heavy fleets stop re-paying for state they already hold.&lt;br&gt;
Measured on real LangGraph graphs: 69% savings on a read-heavy planning&lt;br&gt;
workload, 47% on moderate code review, 29% on high-churn writes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Try it in five minutes
&lt;/h3&gt;

&lt;p&gt;LangGraph — one import change, no node code changes:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from ccs.adapters import CCSStore
store = CCSStore(strategy="lazy")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Plain files shared across processes — no framework required:&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;ccs.adapters.coherent_volume&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CoherentVolume&lt;/span&gt;
&lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoherentVolume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workspace_root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;managed&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;plans/**&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plans/plan.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plans/plan.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;revised_plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# stale view? denied, fail-closed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CrewAI, AutoGen, and the OpenAI Agents SDK ship as adapters on the same protocol; the runnable lost-update demo is in the repo&lt;br&gt;
(&lt;code&gt;python -m examples.coherent_volume.main&lt;/code&gt;), and the formal protocol + verification story is on arXiv (2603.15183).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;agent-coherence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The ask
&lt;/h3&gt;

&lt;p&gt;If you're running a fleet that shares state — parallel coding sessions, an orchestrator with workers, agents with shared memory — I'm looking for early design partners, and the first conversation is me listening to how your system fails. Repo: &lt;a href="https://github.com/hipvlady/agent-coherence" rel="noopener noreferrer"&gt;https://github.com/hipvlady/agent-coherence&lt;/a&gt; — or message me here.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>langchain</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
