Inspiration
We kept seeing multi-agent frameworks treat memory as a storage problem. Put the embeddings in a vector DB, put the rows in Postgres, wrap the writes in a transaction, done.
Then we wrote down a case that breaks it. Agent A records "check-in is September 14." Agent B records "confirmed check-in is September 15." Two INSERTs, two different rows, no constraint violated. Both commit cleanly under SERIALIZABLE. Every anomaly counter reads zero — and the swarm now holds memory that is internally inconsistent, so the next agent books a hotel for a night nobody is in the city.
Transactions solve write conflicts. Nothing in the stack has an opinion about whether two facts can both be true. That gap is Quorum.
What it does
Quorum is a memory consistency layer for multi-agent systems, built on CockroachDB. It detects semantic contradiction inside the transaction that commits the write, resolves it under an explicit authority policy, and refuses to let a downstream action run against contested memory.
The same workload runs in three modes, and the only variable is the memory layer:
| mode | storage | txn | semantic layer | outcome |
|---|---|---|---|---|
naive |
separate vector + row store | no | no | contradictory memory, wrong booking |
txn_only |
CockroachDB, SERIALIZABLE | yes | no | zero anomalies — and the same wrong booking |
quorum |
CockroachDB, SERIALIZABLE | yes | yes | contradiction caught, resolved or contested, action blocked |
txn_only is the column that matters. It is CockroachDB used correctly —
serializable, zero lost updates, zero dirty reads, zero write skew — and it still
books the wrong night, because the contradiction lives across two structurally
unrelated rows that no isolation level will ever flag.
How we built it
The write path is split in two. Embedding and LLM adjudication are network calls with unbounded tail latency, and holding a serializable transaction open across them causes contention collapse. So a prepare phase runs outside the transaction — embed the claim, probe the semantic neighbourhood, classify — and produces a decision. The commit phase then re-runs the neighbourhood search inside the transaction, where it is authoritative. If a concurrent writer changed the neighbourhood between probe and commit, either the in-transaction read sees it, or the transaction fails with 40001 and retries. There is no window where two contradictory facts both slip through.
Detection is two-tier. Tier 1 is deterministic structural comparison on a
normalized subject_key — free, instant, no model in the loop, and it carries
most of the load. Tier 2 is a bounded LLM adjudicator for pairs that contradict
without sharing a key, and it fails closed to CONTEST on timeout or parse
failure. A false contest is visible and safe; a missed contradiction is the exact
failure the project exists to prevent.
Resolution is four ordered rules, not a model: authority tier, evidence count, recency within a tier, and otherwise contest. Rule-based because it has to be explainable to the human who gets escalated to.
The action gate turns memory consistency into a safety property: an action declaring the subject keys it depends on is refused if any of them is contested, missing, or ambiguous.
Memory is append-only. Nothing is deleted, only closed out with valid_to and
superseded_by, which is what makes the AS OF SYSTEM TIME forensic replay work.
Challenges we ran into
Two invariants that contradicted each other. No network calls inside a transaction, and the neighbourhood read and the write must be in the same transaction. Both are non-negotiable and they appear mutually exclusive. The prepared-decision pattern above is the resolution: the pre-transaction probe is advisory, the in-transaction read is authoritative.
Our own policy engine made its best feature unreachable. The recency rule as originally specified fires whenever two confidences are equal — which made the contest path dead code, and contest is the strongest safety story we have. We changed R3 to require the newer claim to be materially more confident. Now S4 (0.80 vs 0.60) supersedes on recency and S3 (0.70 vs 0.70) falls through to CONTEST, which is what the scenario is for.
Bedrock authenticates but will not invoke. Credentials work and 122 models
enumerate, but every single one returns Operation not allowed — 22 chat models
and 8 embedding models, across seven providers. It is an account entitlement
issue, not a code one. Rather than let that silently degrade, we made provider
selection prove reachability with a real invocation before claiming a provider,
and every run report records which provider produced it. Embeddings now run on a
local ONNX model (bge-small-en-v1.5, zero-padded 384→1024 so cosine is preserved
exactly) — a genuine semantic space, which is why the tier-2 scenario passes
rather than being reported untested.
Keeping the baseline fair. naive had to be what a competent engineer would
actually build with a separate vector store — not a strawman with deliberate
bugs. If the baseline is unfair the whole comparison is worthless, and a sharp
judge catches it in seconds.
Accomplishments that we're proud of
- The
txn_onlycolumn exists at all. Most entries would show "vector store bad, CockroachDB good." Building the mode that makes our own database look insufficient is what turns this from configuration into engineering. - The claim is measured, not asserted. The concurrent race ran 200 times per
mode:
naiveproduced contradictory memory 200/200,txn_only200/200,quorum0/200 with 200 counted 40001 retries. A control run with the artificial race window removed entirely produced identical outcomes — disclosed, because volunteering it is worth more than being asked. - The flagship test, 100 consecutive races: zero single-active outcomes, 100 contested pairs, 100 × 40001 retries, zero violations. The thesis expressed as code.
- Availability and consistency together. Node-kill chaos on a local 3-node cluster: 16 writes completed while a node was down, 32 retries, 0 give-ups, and contradictory pairs stayed at zero.
- Append-only enforced as a database grant, not a convention. The writer role
has
UPDATEon exactly five columns and physically cannot rewrite what a claim said. The auditor role is refused by the database itself —InsufficientPrivilege— on camera.
What we learned
Isolation is necessary and it is not sufficient. Serializability constrains the order of operations. It says nothing about the meaning of the data they write. Two INSERTs of contradictory facts touch different rows and violate no constraint, so they are trivially serializable in either order. The gap isn't a bug in CockroachDB — it's a layer that doesn't exist yet.
Tier 1 should carry the load. If the LLM adjudicator fires on everything,
your key normalization is broken. Getting subject_key right made the system
fast, cheap, and demonstrably not "just ask a model."
40001 is the system working. Retries are the database refusing to break serializability. We count them, bound them, and put them on screen rather than hiding them.
Provenance beats polish. Every run report records which embedding provider and which adjudicator produced it, so a result can never be mistaken for something it wasn't. That discipline cost us a nicer-sounding demo and bought something worth more.
What's next for Quorum
- Attach the Managed MCP Server end to end. The read-only role it binds to is built and verified; the client attach is documented but wasn't wired for this submission.
- Bedrock, once the account is entitled. Every code path is in place and provider selection will pick it up automatically the moment it can invoke.
- Erasure through the provenance graph — delete a root claim and watch every inference derived from it die with it. The edges are already modeled.
- Tune ANN
kagainst recall. Higherkfinds more conflicts and costs latency; that tradeoff deserves a measured curve, not a default. - A second domain. Travel made the failure legible on camera, but the contradiction pattern is domain-agnostic — incident response and clinical handoff are the obvious next tests.
Built With
- amazon-bedrock
- amazon-cloudwatch
- amazon-web-services
- aws-lambda
- boto3
- cockroachdb
- fastapi
- fastembed
- javascript
- next.js
- node.js
- onnx
- psycopg3
- pydantic
- pytest
- python
- react
- structlog
- uvicorn
Log in or sign up for Devpost to join the conversation.