Your Agents Already Picked CP or AP — You Just Never Got a Vote
Day 7: Your Agents Already Chose CP or AP — You Just Didn't Get a Vote
The second two of your agents can write to the same repo, plan, or task queue without checking in with each other first, you're not running an AI feature anymore — you're running a distributed system. And distributed systems don't ask permission before they make you pick a side of CAP. They just wait until something breaks, then show you which side you were already on.
The clobber
Two agents, same repo, same feature branch. 14:02:03 — Agent A reads shared_state.json to pull the current task list, kicks off a 40-second refactor, and keeps its own in-memory copy of that file while it works. 14:02:11 — Agent B, running a completely different subtask in parallel, reads the same file, flips its subtask to complete, and commits. 14:02:43 — Agent A finishes, writes its in-memory copy straight back over shared_state.json, and commits. Agent B's completion flag is just gone. Not merged. Not conflicted. Gone — because as far as Agent A knew, it never existed. CI stays green, because nothing about the file's schema is broken; it's just missing a fact nobody wrote a check for. No error fires. The plan quietly settles into a state that never actually existed — not any single agent's view of the world at any point in time, but the file everyone downstream now treats as truth.
This is Brewer's CAP, not a framework bug
CAP says a distributed data store can't give you all three of Consistency (every read sees the latest write), Availability (every request gets a response), and Partition tolerance (the system keeps running when nodes can't talk to each other) at once. Pick two — and since partitions are just a fact of networked life, you're really choosing between C and A whenever one hits. Map that straight onto the mess above: the shared repo, or plan, or task queue, is the data store. Agent-to-agent communication — including something as indirect as two agents reading and writing the same file — is the network. A partition here isn't a dropped connection; it's Agent A sitting on a stale in-memory read while Agent B's write slots in underneath it. That's a partition by CAP's own definition: two nodes making decisions with no shared view of current state. The clobber wasn't a bug in how Agent A merged. It was the system resolving a partition in favor of Availability — both agents kept working, neither one blocked on the other — and Consistency paid for it.
The reveal: your framework already decided
Nearly every popular multi-agent orchestration pattern defaults to AP, and it does it so quietly that most teams never clock a choice got made at all. Fire-and-forget parallel workers — spin up N agents, let each chase a subtask, reconcile at the end — are AP by construction: nobody blocks, so nobody's consistent in the meantime. Shared-memory and shared-scratchpad designs — LangGraph's shared state channels, an AutoGen group chat, a CrewAI crew reading and writing one common context object — are AP too, unless someone bothers to bolt on locking, versioning, or a single writer. The tell is almost always some config default nobody reads twice: a 'merge strategy' set to last-write-wins, a state store with no optimistic-concurrency check, a task queue where 'append your result' quietly means 'overwrite the field.'
# The invisible AP decision, made by omission:
# shared_state.json
{
"tasks": [...],
"last_updated_by": "agent_b" # overwritten on every write
}
# Agent A's write:
state = read("shared_state.json") # stale snapshot, t0
... 40s of work ...
write("shared_state.json", state) # last write wins, t0+40s
# Agent B's write from t0+8s is gone. No error. No conflict flag.
# This IS eventual consistency's failure mode with no reconciliation step —
# which is just "unresolved concurrent writes" wearing a nicer name.Why CP is sometimes the wrong panic response
The reflex fix after a clobber like this is to lock everything down: every agent grabs a mutex on the shared file before it reads, waits its turn, writes, releases. That's CP — you've bought consistency by giving up availability during partitions (here, 'partition' just means 'another agent is mid-write'). It works. It's also often the wrong call. Picture five agents running independent research subtasks, each one occasionally appending a short summary to a shared notes file. Serialize every append behind a lock and you've got four agents sitting idle, waiting on one slow agent's write — to protect an operation, an append, that never conflicts in the first place. You paid the full CP tax to guard an invariant that was never actually at risk, because the real operation underneath was commutative. The lesson isn't 'CP good, AP bad.' It's that locking has a cost, and that cost is only worth paying when the thing you're protecting can actually be broken by concurrent access.
Three questions before you wire up shared state
- ▹What's the invariant you actually can't afford to violate? Not 'the file should be correct' — name the specific thing: 'no task gets marked complete and then un-marked,' 'two agents never both claim the same ticket.' If you can't name it, you don't know what you're protecting, and locking everything is just a guess wearing a lab coat.
- ▹What's your merge or read-repair strategy when two agents step on each other? 'Last write wins' is a strategy — usually the wrong one. A real strategy names who wins (timestamp, agent priority, a semantic merge like CRDT-style set-union for append-only logs) and, just as important, surfaces the conflict instead of burying it.
- ▹Is 'eventual consistency' here a real reconciliation step, or just silence dressed up nicely? If nothing in your system ever looks at divergent state and repairs it — no periodic reconciliation pass, no conflict log, no re-read before a critical decision — you don't have eventual consistency. You have unresolved concurrent writes that just haven't hurt you yet.
Where this goes next
Naming the CAP tradeoff out loud turns an accident into a decision: you choose AP for the research-notes case and CP for the ticket-claiming case, on purpose, instead of finding out your orchestration framework's default the hard way, in a postmortem. But naming the tradeoff is step one. Once you decide a given invariant needs CP, you still have to build it — and 'just add a lock' stops scaling the moment you're past a handful of agents. That's the real problem waiting underneath this: how do a bunch of autonomous agents agree on one order of operations without a single bottleneck choking everything? That's consensus — Raft, Paxos, leader election, the protocols that let distributed systems get C and P without giving up on A entirely. Day 7 was about noticing you're in a CAP tradeoff at all. What's next is how systems actually engineer their way to CP without grinding to a halt.
Extend your knowledge
- ▹Read Brewer's original 2000 PODC keynote that first laid out the CAP conjecture, his own 2012 look-back "CAP Twelve Years Later: How the 'Rules' Have Changed" (IEEE Computer), and Gilbert & Lynch's formal proof ("Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services") — the agent-system mapping in this lesson is a direct translation of that paper's model.
- ▹Go look at how your current orchestration framework — LangGraph, AutoGen, CrewAI, or whatever custom queue you're running — actually resolves concurrent writes to shared state today. Read the source of whatever function commits state. Don't trust the docs.
- ▹Reproduce the clobber on purpose: two async workers reading and writing the same JSON file, with a sleep injected into one of them, so you see the failure with your own eyes before you trust any framework's default again.
- ▹Preview of what's coming: read up on Raft leader election, the simplest real consensus protocol — it's the mechanism Day 8 builds on for coordinating agents without a global lock.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.