The Rage Detector Caught Me Working
Three open items in my dashboard’s backlog were filed by the dashboard. Each said the same thing: rage taps on a control, with a selector and a timestamp, please investigate. The usage log had caught a burst of three taps on one element inside a second, and the routine that turns that log into usability bugs had done its job.
I went to the timestamps. All three bursts were one afternoon in October, on the desktop, inside four minutes. The first was on the button that confirms a decision card: seven cards armed and confirmed in fourteen seconds, and the decision log holds seven rows stamped to the exact taps. The second and third were on the two buttons that step a concurrency cap down. The server journal shows one accepted write per tap, four to three to two on one lane, eight down to one on the other, after which I paused that lane. Every tap had been answered. I was not angry at anything. I was clearing a queue I had let pile up, on a page where I know where the buttons are.
The detector could not have known. It read only the human’s side of the exchange: how many times, how fast, on what. Rage was defined as a tempo. A tempo is also what competence looks like on a page you use every day.
The fix was to give the detector the page’s side. Before any handler runs, each tap reads the control it landed on and takes a signature of its answer: label, class, disabled state, the aria and data attributes, and for a stepper the text of its group, which is where the value lives. The next tap compares. A control whose signature changed between taps answered, and the count restarts. A control that looks identical after three fast taps is one the page ignored, and that is still filed, so a dead button is still caught. Rage is now three taps the page left unanswered, not three taps.
It was the third time the usage log had been wrong about its own page. In September an off-canvas drawer, parked at the right edge of a phone screen with no visible width, counted as seen on every load, because the observer that counts sightings treats an edge-adjacent box as intersecting. In early October a button on the landing view read as four steps deep, because depth was measured as the control’s position among every step of the session rather than as hops from where the session landed, and the sync filed “surface it one tap from landing” for a button that was already there.
Each fix added the one thing the metric had left out: how much of the drawer is on screen, where the session started, whether the page answered. A usage metric that reads only the user’s motion will file the user’s fluency as a defect, and it will be very precise about the timestamp.