DEV Community

Cover image for "I built a multiverse for AI agents: they rehearse every action in hundreds of graph copies before touching anything"
Tarun Mhanta
Tarun Mhanta

Posted on

"I built a multiverse for AI agents: they rehearse every action in hundreds of graph copies before touching anything"

home

AI agents are starting to act on real systems. They clean up databases, rotate keys, move cron jobs, scale services. The action itself is rarely the dangerous part. The danger is everything that silently depends on the thing being changed.

So for Graph Hacks (WeMakeDevs × FalkorDB) I built Multiverse: before an agent does anything, it rehearses the action in hundreds of copies of the company, counts how often something important breaks, and only lets it through if the answer is clearly safe.


The problem: "nobody uses this table"

Here is the scenario the whole project is built around.

A cleanup agent looks at orders_archive. No service declares a dependency on it. No config file mentions it. By every static check, it is safe to delete.

But the access log knows something the catalog doesn't: tax_filing_job read the archive in 4 of its last 5 runs. Delete it, and the next quarterly tax filing breaks.

Real companies are full of links like this. Some are certain (a Kubernetes deployment is configured with another service's address). Many are only probably real: a job that used to read a table, a script from an old runbook that may still use a secret. A yes/no dependency check can't handle "probably". So I stopped asking "does anything depend on it?" and started asking "in how many possible futures does something important break?"

The idea: rehearse, don't guess

Multiverse treats the company as a graph in FalkorDB, and every uncertain link as a coin with a known bias.

To rehearse a plan:

  1. Snapshot the company graph with GRAPH.COPY.
  2. For each future, make a fresh copy of the snapshot.
  3. In that copy, roll the dice for every uncertain link: is it real in this future? Is the hand-run job used during the window?
  4. Apply the change and let the failure ripple outward, one hop at a time, in Cypher.
  5. Read back what broke, then delete the copy.

Each future becomes one square on screen. Orange means something business-critical broke. After enough futures, Multiverse knows the chance of breaking something critical, and if it's 5% or more, the plan is blocked.

A blocked rehearsal: every square is one copy of the company

Building the graph from evidence, not opinions

The company in the demo is Boutique Inc. Its 12 services and Redis come straight from Google's real Online Boutique Kubernetes manifests, unmodified. Every *_SERVICE_ADDR environment variable becomes a CALLS edge. The back office (billing, tax and analytics jobs, databases, secrets, owners and a 13-month access log) is synthetic, and labelled as synthetic everywhere in the app.

The rule I held myself to: no confidence number is typed in by hand. Every link gets a rule and a confidence computed from its evidence:

Rule Example Confidence
k8s frontend has CART_SERVICE_ADDR=cartservice:7070 100%
verified declared in the catalog and signed off by the owner 100%
logs tax_filing_job read the archive in 4 of its last 5 runs 48%
declared an old runbook says refund_script uses the payment key 50%

The 48% is checkable by hand. Recent runs count more: a run's weight halves every 90 days. The five runs weigh 0.05, 0.11, 0.22, 0.44 and 0.88. The four that read the archive add up to 0.81 out of 1.70, and 0.81 ÷ 1.70 = 48%. In the app you can click that link and see every run, its weight, and the exact file lines it came from.

The evidence behind one link

The Cypher that does the work

The heart of each future is a hop-by-hop ripple, run inside that future's own copy:

UNWIND $frontier AS f
MATCH (x:Asset)-[r:CALLS|READS|WRITES|USES_SECRET|DEPENDS_ON|FED_BY]->(y:Asset {name: f})
WHERE NOT r.key IN $inactive
RETURN x.name, type(r), r.soft, r.key, y.name
Enter fullscreen mode Exit fullscreen mode

Before the ripple, links the dice said are not real in this future are simply deleted from the copy:

MATCH ()-[r]->() WHERE r.key IN $keys DELETE r
Enter fullscreen mode Exit fullscreen mode

Hard links break the dependent asset, soft links only degrade it, and data feeds go stale. The statuses are written back into the copy, read out, and the copy is dropped.

Launch day uses the same graph differently. The load on every service is the traffic at the front door multiplied along every path, which turns out to be a single query:

MATCH p = (e:Asset {name: $entry})-[:CALLS|READS|WRITES*0..8]->(y:Asset)
WHERE all(r IN relationships(p) WHERE r.calls_per_request IS NOT NULL)
RETURN y.name, sum(reduce(f = 1.0, r IN relationships(p) | f * r.calls_per_request))
Enter fullscreen mode Exit fullscreen mode

Divide capacity by that and you get each service's breaking point, for example "cartservice runs out at 3.5× normal traffic".

Knowing when to stop

Running a fixed 1,000 futures would be slow and pointless. Multiverse adds futures 25 at a time and computes a 95% Wilson interval after each batch. As soon as the whole interval is clearly above or below the 5% safety line, it stops. A clear BLOCK usually takes 25 to 50 futures.

And if nothing uncertain can reach the plan's effects, every future would be identical, so it plays exactly one and says so. "Exact" is a real answer, not a shortcut.

Replay: no black boxes

Every future is reproducible. The same plan with the same replay code rolls the same dice in the same order. Press Replay on any square and it runs again in a fresh copy, with every FalkorDB command shown as it executes and the failure spreading across the graph in step with the real query results.

Replaying one future, command by command

Memory: ask once, never guess again

When a verdict hinges on an uncertain link, Multiverse asks the team that owns it. Here, Finance gets "Does tax_filing_job still read orders_archive?".

The answer goes into a second graph, the memory (boutique_memory), which survives rebuilds and sessions:

(:Question)-[:ASKS]->(:Team)
(:Fact)-[:ANSWERS]->(:Question)
(:Rehearsal)-[:HAS_FUTURE]->(:Future)
(:Plan)-[:CONFLICTS_WITH]->(:Plan)
Enter fullscreen mode Exit fullscreen mode

Every later rehearsal uses the answer instead of rolling dice. In the demo, once Finance and Data answer, the same delete becomes exact and safe, and only then does execute let it through. Execution itself re-runs a fresh rehearsal first, so a stale SAFE can't sneak past.

Two agents, one company

This is the part I find most interesting. A finance agent moves billing to read from the replica: safe alone. A platform agent takes the replica offline on the 1st from 01:00 to 04:00 for maintenance: also safe alone.

Together, billing runs at 02:00 against a database that is down, and payouts break.

Agents in Multiverse propose plans instead of acting. Each proposal is rehearsed alone and together with every other active plan that overlaps it in time and in the graph. The second plan is held, and Multiverse looks for the earliest safe slot.

Checking 168 hourly start times naively means 168 rehearsals. But start times that see the same running jobs and the same overlapping plans produce identical futures, so Multiverse groups them and rehearses each group once: 168 start times, 3 rehearsals.

Agents: holds, the schedule and the safe-slot heatmap

A company brain you can question

The same graph answers questions about the company itself, using FalkorDB's built-in algorithms:

  • algo.betweenness finds chokepoints (checkoutservice, which most dependency paths run through)
  • algo.pageRank finds foundations, the assets everything ultimately leans on
  • algo.WCC finds islands, groups connected to nothing else, which feed a cleanup finder that then proves each candidate with a real rehearsal
  • shortestPath explains why one asset depends on another
  • a full-text index powers the Ctrl K search
  • "Ask the graph" turns a plain-English question into Cypher and runs it with GRAPH.RO_QUERY and a timeout, so the model physically cannot write

Real agents, through MCP

Multiverse exposes 13 MCP tools (rehearse, propose_plan, execute_approved_plan, find_safe_window, evidence, recall and more). Claude Desktop or any MCP client can use them, so a real AI agent rehearses before it acts. Every tool call is logged into the memory graph, which is also how the web console shows agent activity live: the memory graph doubles as a shared bus between the console, the MCP server and the command line.

What I learned about FalkorDB

  • GRAPH.COPY is the whole trick. Giving every future its own complete, writable copy of the company made the engine simple. A future is just "copy, delete some edges, ripple, read, drop". No undo logic, no risk to the real graph. A fingerprint check before and after every rehearsal proves the real company is untouched.
  • Copies are sequential. FalkorDB forks to copy, so futures run one after another, about 0.1 s each on my laptop, most of it the copy itself. That is exactly why adaptive stopping and "exact" mode mattered so much.
  • Two graphs beat one. Keeping the company (rebuildable from evidence) separate from memory (permanent) made resets safe and made "remembered across sessions" literally true.
  • Read-only queries are a safety feature. GRAPH.RO_QUERY is what made it reasonable to run LLM-written Cypher at all.

Proving it

A self-test (python selftest.py) runs 17 checks against its own graphs. For example:

  • 300 futures land within 3 standard errors of what the evidence predicts
  • 12 replayed futures match their recordings exactly
  • adding one edge changes the answer, so nothing is precomputed
  • fixing a dependency turns BLOCK into SAFE
  • two plans that are each safe alone are blocked together
  • every rehearsal leaves the real graph unchanged and deletes all of its copies

Honest limits

  • The back office is synthetic. Only the Online Boutique part is real.
  • The ripple models hard, soft and stale dependencies, not retries, timeouts or "either A or B is enough" redundancy.
  • "Ask the graph" is only as good as the Cypher the model writes, so the Cypher is always shown.

Try it

  • Live: https://multiverse-cqzs.onrender.com. Press Take the 2-minute tour: it runs the real engine and explains each step.
  • Locally: clone the repo, then run.ps1 on Windows or bash run.sh on macOS/Linux. It starts FalkorDB in Docker, builds the graph and opens the console on localhost:8765.

Multiverse

Rehearse what an AI agent is about to do, in hundreds of copies of your company, before it touches the real one.

Multiverse is a rehearsal layer for AI agents, built on FalkorDB. Before an agent deletes, migrates, rotates or launches anything, Multiverse forks the company graph with GRAPH.COPY, plays the change in every copy (rolling dice for every link nobody is sure about), counts how often something important breaks, and returns a verdict: SAFE, REVIEW or BLOCK The real company is never touched, and every number on screen is counted from real futures.

Multiverse home page

Demo video youtu.be/xh497r3bZTY
Live demo multiverse-cqzs.onrender.com (free server: the first visit can take up to a minute to wake up)
Tracks entered 01 Agents That Act on Connected Data · 02 Agent Memory and Coordination · 03 Company Brain
Built for Graph Hacks by WeMakeDevs × FalkorDB, 15–18 October 2026

The problem

AI…

Built solo for Graph Hacks 2026 with FalkorDB, Python, vanilla JavaScript, SVG and three.js. I used an AI coding assistant for parts of the code and docs; the idea, the decisions and the testing are mine.

If you're building agents that touch real systems, I'd love to hear how you're handling the "nobody uses this table" problem. Let me know in the comments.

Top comments (0)