Inspiration

What it does

How we built it

INSPIRATION

I used to sell digital vinyl. We had forgery constantly, refunds constantly, Stripe and Crossmint underneath it all. That was hard enough with people doing the work.

Now companies are handing that same job to AI agents. 66% of service organisations run at least one, up from 39% a year ago. And 53% of the 445 organisations surveyed by the Cloud Security Alliance have already had an agent exceed the permissions it was given.

So I asked a simple question. When an agent acts on something false and sends money, what takes it back?

Nothing does. You can delete the memory. The refund still went out, and nothing in the system knows the refund was ever that memory's idea.


WHAT IT DOES

RETRACT is shared memory for agent fleets. Three things happen when a belief turns out to be wrong:

Everything built on it is marked wrong. Not just the note — every belief derived from it, walked through a derivation graph.

Anything queued and not yet run is cancelled. That effect never happens. This is real state change, not a log entry.

Anything already executed is reached and recorded as owed. A reversal request is written once, in the same transaction, under an idempotency key derived from the original.

And unrelated beliefs are left alone. The blast radius is asserted in the tests, because a cascade that retracts everything is as wrong as one that retracts nothing.


THE NUMBER

Same scenario, same cluster, run twice. $1,329 had already been spent when the passport came back forged.

Without RETRACT: three derived beliefs stay active, one pending effect still runs, and nothing reaches any of the $1,329. Nobody is even told.

With RETRACT: all three retracted, the pending effect cancelled, both executed effects reached. $1,240 gets a reversal recorded under one idempotency key. $89 is named as unreachable. $0 settles.

Run it yourself: uv run python experiments/reach_eval.py

The control arm is a model of a naive agent memory, not a competitor product, and it is the generous version — it marks the wrong belief retracted and keeps the audit trail. n=1, architectural rather than statistical.


HOW WE BUILT IT

CockroachDB, three features. A distributed vector index (C-SPANN) carries every read — remove it and the READ phase has nothing to read. Serializable transactions with durable locking hold the claim lock, because unreplicated FOR UPDATE locks are documented as best-effort and correctness cannot rest on them. And the Cloud Managed MCP Server serves the receipt over an endpoint with no write surface, so the agent that shows you what happened is structurally incapable of changing it.

AWS Bedrock, two services. Titan Text Embeddings V2 at 512 dimensions generates every vector CockroachDB indexes. Claude Sonnet 4.5 adjudicates every contradiction the live demo raises.

A turn is three phases, because a transaction cannot be held open across model inference. READ does an ANN search at a recorded snapshot. THINK happens entirely outside any transaction. COMMIT is a short serializable transaction that locks the claim key, reads what is already believed, and either writes or raises a contradiction.


WHAT WE MEASURED AND DIDN'T ASSUME

Identity cannot come from the embedding. On two independent production embedding models, a paraphrase and a flat negation sit 0.001 apart, and a different customer can be nearer than the same fact reworded. So identity comes from a canonical claim key and vectors do retrieval, the job they are good at. Data in FINDINGS.md.

A published negative result. We tested whether the vector index alone serialises concurrent adjacent writes, which would have let us drop the explicit lock. It does not — 13% against an 8% control, p = 0.15, and the effect shrank as power increased. Published, and the lock built explicitly instead.

The adjudicator scores 7 of 8, not 8. Claude passes a case the heuristic stand-in failed and fails one it passed. The eval's exit code says so. We publish the case it misses.


WHAT IT DOES NOT DO

No money moves. $0 settles. There is no HTTP client, no payment SDK and no provider credential in the repository. RETRACT records that a reversal is owed. Saying "the money came back" is one database query from being caught.

One charge stays red on purpose. card_charge, $89, has no registered compensation handler. RETRACT reaches it, cannot reverse it, and says so. Silently greening it would be the exact dishonesty this project argues against.

The database does not guarantee the verdict is right. It guarantees exactly one agent adjudicates a claim at a time and that the outcome is atomic. A model decides.

Scope is not authentication. It proves a caller was granted a scope, not who they are.


WHAT'S NEXT

One adapter. The reversal is already recorded as owed inside the transaction, with its own idempotency key; dispatching it to a payment provider and recording the provider's identifier against the row is a separable piece of work. Until an identifier arrives that this process did not generate, nothing here claims the money moved.

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for Retract

Built With

Share this project:

Updates