So I spent the weekend at Graph Hacks 2026 (WeMakeDevs × FalkorDB) and ended up building something around a problem I'd honestly never thought much about before.
When a release goes bad, you roll back. Pretty standard. The old version comes back up, the health checks pass, and you assume you're fine. But the new version was running for a while before you rolled back, and it was saving data. The old version has never seen that data. So sometimes it starts up totally fine and then falls over the first time it tries to read something the new version wrote.
That's what I built RollbackWitness for. It checks whether the old versions can actually read the data that's already there, before anyone approves the rollback.
The example I used
I made a small fake online shop, based on the OpenTelemetry Astronomy Shop. Checkout saves orders, accounting keeps the books, and fraud detection looks through the orders.
In my newer release, checkout started handling euros, pounds and yen instead of only dollars. Accounting and fraud detection were updated to understand them.
Putting everything back on the old version sounds reasonable. You're undoing the change that introduced those currencies. Except the orders people already placed are still sitting there, and the old software doesn't know what to do with them.
Then I made the example a little messier. Accounting also saves its own records based on those orders. Now the newer data has made its way into another part of the shop.
I asked the agent to roll back accounting and fraud detection, but leave checkout alone.
Looking at accounting's saved records, you can see that accounting created them. Follow where their contents came from, though, and you end up at checkout. The service I'd specifically told it not to touch.
Accounting wrote the row. Checkout wrote the order behind it. That's the connection I wanted the tool to follow.
Where I got stuck
A lot of the work ended up being about getting the agent to use that information properly.
At one point it kept suggesting I roll back the currency service. Which sounds plausible, given the problem. But in my fake shop, that wasn't the part that had changed how the orders were stored. I had to make the release notes clearer about what had actually changed, rather than letting the service's name do the explaining.
Another problem was in my own code. The checker had found problems in both accounting and fraud detection, but I was shortening the failure summary before sending it to the agent. A whole service's failures got left out.
So the information existed. I'd just cut it out of the bit the model was reading.
I fixed the summary and included where the troublesome records came from. The useful question eventually became: “You told me to leave checkout alone. Can I change it?”
That's much closer to what I wanted. If the request needs changing, ask me.
I told it to turn off checkout's local-currency setting and keep the newer accounting and fraud readers. That stops new orders being written that way, while keeping the software that understands the orders already placed.
It doesn't repair the old orders or prove I've fixed the wrong totals. It gives me a way forward that doesn't break access to the data while I investigate.
Same saved data, different proposal. This screenshot uses a saved agent conversation; the data checks run again.
I wanted to see what it was following
I added an interactive graph because a paragraph telling me the rollback was risky wasn't enough.
You can click a group of saved records, see the problems associated with them, and open an individual one to find out what happened when the older code tried to read it. You can also follow the connection back to the service that produced its source data.
The actual error from trying to read one saved accounting record. Click to enlarge.
FalkorDB holds those connections: the proposed software versions, what data they read, and where that data came from. Following the connections determines what gets checked, and the result goes back into the agent's next step.
I also connected it to Claude Code over MCP, so I could start an investigation from there and open the review screen when I wanted to inspect the details.
Did the graph actually matter?
I wanted to be honest about this, so I tried taking the relationships away while keeping the data.
Remove the link between an accounting record and its original order, and the tool can still find that the old software can't read it. What it loses is the trail back to checkout.
Remove the links telling it which software reads which store, and it can't work out what needs checking. It asks for more information instead of deciding everything is fine.
I also tried ordinary SQL joins over the same facts. They found the same things to check. So no, a graph isn't doing anything magic here.
What it gives me is a straightforward way to follow a problem across services, use that connection in the investigation, and show someone else where it led.
What I took away
I went in thinking about old code reading new data. I also ended up dealing with an agent making decisions from an incomplete summary that I'd given it myself.
That was probably the most useful part of building this. Having the information somewhere doesn't help much if the next step never sees it.
Also, having a “can't confirm” answer turned out to be more useful than I expected. I'd rather be told something is missing than get a reassuring answer built on half the picture.
It's all fake data, with small Python models standing in for the shop's services. A passing result only means they can read the saved data I checked. Approval stays with a person, and applying a proposal is just a local simulation. Nothing gets deployed.
Code's here if you want to poke at it.
Demo Video -




Top comments (0)