Claude Fable discovered that the best way to diagnose a horizontal scrollbar bug was to write its own CORS web server, inject JavaScript into a template, and hijack Safari's windows via pyobjc. Simon Willison documented Fable's relentless approach after a two-day session hacking on Datasette Agent—and it's the most telling autonomous debugging behavior I've seen.
The Glitch That Triggered a Multi-Tool Investigation
Willison noticed a horizontal scrollbar in the jump menu chat prompt and snapped a screenshot. He started a fresh Claude session in his datasette-agent checkout, dragged in the screenshot, and asked Fable to "Look at dependencies to help figure out why there is a horizontal scrollbar here." He walked away. When he returned, his machine had opened a browser window and navigated to the dialog in question. He hadn't told Claude to use browser automation.
Fable had hacked up its own screenshot pipeline using uv run --with pyobjc-framework-Quartz. It used Python to iterate through all open windows, filtering for Safari windows with expected strings like "textarea" in the name. It grabbed the window number—an integer like 153551—and used the screencapture -x -o -l CLI tool to capture a PNG.
How Fable Built a Diagnostics Server and Hijacked the Browser
One tool wasn't enough—Fable wanted to reproduce the bug and measure the textarea inside a Web Component. It wrote an HTML test page to /tmp/textarea-scrollbar-test.html, opened it in Safari, and took screenshots. But it still needed to trigger the modal dialog—only available via a click or keyboard shortcut. So it edited Datasette's templates to inject JavaScript that would dispatch a keyboard shortcut 1.2 seconds after page load:
setTimeout(function() {
document.dispatchEvent(new KeyboardEvent("keydown", {key:"/", bubbles:true}));
}, 1200);
Then it needed to read diagnostic data from the browser. Fable wrote a custom web server using Python's http.server that listened on 127.0.0.1:9999. The server accepted POST requests with JSON data, wrote them to /tmp/diag.json, and sent Access-Control-Allow-Origin: * headers. It then injected a script into the template that fetched http://127.0.0.1:9999/diag with measurements like devicePixelRatio, scrollWidth, clientWidth, and whiteSpace. All data flowed back to disk where Fable could read it.
When Fable Hit a Guardrail, Opus Took Over
Fable's autonomous debugging hit some invisible guardrail and downgraded itself to Opus. Opus had the full transcript and continued using the tricks Fable had pioneered. Shortly afterwards, it found, tested, and verified the fix. Willison prompted Opus to write a report to /tmp/automatio.
What Fable did in those few minutes—write a CORS server, inject JS into a live template, control Safari via pyobjc and screencapture—required composing tools most engineers wouldn't think to chain together. If Fable's tenacity is any indication, the next generation of AI agents will treat every bug as a puzzle worth solving with whatever tools it can assemble.
Source: Claude Fable is relentlessly proactive
Domain: simonwillison.net
Comments load interactively on the live page.