The side door
Lab note · 2026-08-27 · Reserve — an AI-native studio.
Yesterday five agents swept every screen of our app against six classes of interface defect and came back with seventy-nine findings, each pinned to a file and a line. A few hours later a founder, testing by hand, found a real one the sweep had missed — on a surface the sweep had explicitly covered.
The bug: during an event, your identity is frozen. The identity picker knows this — it pre-selects your frozen persona and greys out all the others. But the same picker has one more row: create a new persona. That row only checks whether you have slots left. Create one, and the picker happily selects it and sends it to the server. A frozen identity, bypassed through the front door’s neighbour.
Why did the sweep miss it? Because it audited controls, not outcomes. Every check was of the form “does this widget enforce the rule?” — and every widget did. The greyed-out rows enforced it. The hidden switch button enforced it. The audit walked the room checking that each door was locked, and never asked the only question that mattered: how many doors does this room have?The “create new” row wasn’t on the list of doors, so nobody checked it.
The fix to the method is now a standing rule in our scanning playbook: bind every invariant to the outcome, never to the action. Not “the switch button must be disabled” but “no path may result in a different persona entering this event.” Then enumerate the paths to that outcome — including the ones that create their objects on the way in.
There was a second lesson hiding underneath. Before touching any interface code we wrote a red test against a local copy of the production rules: send the server every wrong persona we could construct — an existing one, a freshly created one, a null. Eleven checks out of eleven, the server silently swapped the frozen identity back. Nobody else would ever have seen the new face. The privacy hole we feared didn’t exist; what existed was a display layer promising something the server would quietly refuse.
That distinction — is the rule broken, or is the interface lying about the rule? — decided the severity, the fix, and who needed to be woken up. We only knew which world we were in because we tested the invariant, not the widget.
← All lab notes