An agent with an undo button

An agent cleans up a supplier master of 48,000 records. Every step says what it will do and to how many records before you press. Undo one and whatever depended on it comes back with it. I built it. Go break it.

Supplier masterProduction
AP West
All suppliers
Select
Connecting…

Run the first three steps. Then undo the merge and watch what comes back with it.

Built for a laptop, where the plan sits beside the table. On a phone it sits under it.

What I decided

  1. 1

    A step says what it will do and to how many before it runs.

    The count comes from the records as they are: run the merge and the countries step has fewer blanks to fill, and its card says so. The step in focus marks the columns it will change in the grid’s header and can put the rows it would touch in the grid.

  2. 2

    A step is a diff with its inverse.

    Each change is a cell, what it held and what it became. A step keeps every one it makes and undo replays them backwards, so it is exact at any size.

  3. 3

    Undo follows dependence, not time.

    Each step declares what it reads. Undo one and only a later step that read or changed what it changed comes back with it. The card says so before you press.

  4. 4

    Reversible and irreversible do not look alike.

    The one with no undo is square with an orange edge. It asks first, and once it has run it stays run.

  5. 5

    The database is a worker, and the grid does not know.

    The records sit in a thread of their own and the grid pages through them the way it would page through a server. A real database behind an API could take the worker’s place without a change to the grid.

  6. 6

    Claude writes, the engine has the last word.

    Pick a goal and Claude writes a plan in the same small language as my five steps. The engine runs it against all 48,212 suppliers before you see it and sends back what is wrong, three tries at most. Every count on a card is the engine’s.

  7. 7

    A limit comes before the model.

    Five plans an hour for each visitor and a dollar a day for the site. Past either the page keeps my plan and says why.

How it is built

The records live in a worker thread in your browser, generated from one seed in about 150 milliseconds, so every visitor gets the same 48,212 suppliers and no record is stored or sent anywhere. The grid draws only the rows in view and asks the worker for a page of a hundred as each comes into reach. Sorting and search run in the worker too. A step is a function from the records to a list of cell diffs, applied in slices with a beat between so the sweep can be watched. The status bar prints what each page cost.

Steps are written in a small plan language: merge, rewrite, fill in, mark, set and email, each with its conditions. My plan is five of them. Pick a goal and only the goal goes to a Cloudflare Worker at ai.voigt.work, where Claude Haiku 5.5 writes a plan in the same language. A Durable Object there holds the same 48,212 suppliers and runs the plan through the same engine. A step that touches nothing, reaches past a quarter of the master or sends an email before a fix it depends on goes back to the model, three tries at most.

I picked the model with an eval: each goal four times, graded by code. Haiku 5.5 and Sonnet 5.5 both passed 12 of 12. Haiku did it for a tenth of a cent a plan against nearly a cent. The eval also caught me out. I had made it fail any audit plan that didn’t chase missing tax ids. In these records every one of them is half of a duplicate. The merge fills them all and the engine had already told the model so.

Every record is generated from a seed. The phone numbers are in ranges reserved for fiction and the addresses end in .example. No record leaves your browser, and when you ask Claude for a plan only the goal you picked is sent. The five steps you start with are mine. A plan you ask for is Claude’s, checked by the engine. None of this is anyone’s product.

Ask Alex

What would you like to see?

Type / for the kinds of work.