Claude Fable is relentlessly proactive

Cloud Fable is constantly active

11 June 2026

After two days of experience with CloudFable 5 I think the best way to describe it is constantly active. It knows a lot of tricks and will use almost any of them to reach its goal.

I will explain it with an example. I was hacking the dataset agent today when I noticed a glitch: a horizontal scrollbar that shouldn’t be in the jump menu chat prompt. I took this screenshot:

Screenshot of a modal dialog displaying the scrollbar bug. There is a focused search input with a blue outline and placeholder at the top "jump to..."There is an X close button to the right of it. Below, a caption reads "Start a new agent chat" Above text area with placeholder "Ask a question about your data..." - Bug: A thick gray horizontal scrollbar is incorrectly displayed at almost its full width, at the bottom edge of the empty text area, next to the resize handles. Below the text field: "Press Enter to begin. Shift+Enter adds a new line." then blue "start chat" button.

then i started again claude session in my datasette-agent Checkout, grab a screenshot and tell:

Look at dependencies to help figure out why there is a horizontal scrollbar here

I had a hunch that the cause was a dependency of the dataset agent (possibly the dataset itself) and I knew that Fable was good at digging up dependency code, either by inspecting the files installed in its own virtual environment. site-packages Or by referencing a local checkout on disk. Asking it to start dependently seemed like a good bet.

I got distracted by a household task and walked away from my computer.

After a few minutes when I came back I saw my machine open a browser window in my regular firefox and then Navigate to the dialog in question. I didn’t tell Cloud Code to use any browser automation, and I was pretty sure it wasn’t possible for him to trigger mouse movement or keyboard shortcuts within the window, so how was he doing it?

I watched it in fascination as it continued its search, then I noticed it opening a Safari window instead of Firefox. I also took this snapshot from Cloud Terminal:

fable bash pyobjc

what was it doing there uv run --with pyobjc-framework-Quartz?

It turns out that Fable had hacked his own pattern for taking screenshots of browser windows. This was using Python to iterate through all available windows on my machine, then filtering for Safari windows with the expected strings "textarea" In window name. This is used to find their window number – an integer like 153551 – which can then be used with screencapture CLI tool to get PNG.

Ok, this is a good way to take screenshots. But what was it taking a screenshot of?

Turns out it was writing my own from-scratch HTML page to try and recreate the bug, then opening Safari and taking screenshots.

Here’s the /tmp/textarea-scrollbar-test.html page he created, and the screenshot he took with it screencapture -x -o -l 153551 /tmp/safari-cases.png: :

Screenshot of the Safari browser window showing the TextArea Scrollbar test page at file:///private/tmp/textarea-scrollbar-test.html. Page text reads: scrollbar thickness: 17px | UA: Mozilla/5.0 (Macintosh; Intel Mac OS Devicepixel ratio: 2. Comes with four numbered test cases, each containing a text area with a placeholder "Ask a question about your data...": 1. Precise plugin CSS (resize: vertical, default overflow), 2. Plugin CSS + overflow-x: hidden, 3. Plugin CSS + resize: none, and 4. Bare default textarea, which is a very small box wrapping over two lines with a placeholder.
(I have a lot of open tabs!)

OK, so I can see how it was opening the test page and taking the screenshot, but how was it triggering the modal dialog that was supposed to be tested? It’s only available via a single click or keyboard shortcut, and I couldn’t see any mechanism to run it in Safari.

I finally found out what it did.

The cloud was running in a folder that contained the source code for the application. It knows enough about the dataset to be able to run a local development server. It turned out that it was editing the dataset’s own templates to add JavaScript that would trigger the correct keyboard shortcut as soon as the window opened, adding code like this:

<script>
window.addEventListener("load", function () {
  setTimeout(function () {
    document.dispatchEvent(new KeyboardEvent("keydown", {key: "https://simonwillison.net/", bubbles: true}));
  }, 1200);
});
script>

1.2 seconds after the window opens, this code triggers a simulated / Key, which is the keyboard shortcut to open a modal dialog.

There was one challenge left. To understand what was going on, the cloud itself needed to run JavaScript on the page to take measurements.

It wrote my own custom web application to get information through CORS, then ran it as a local server and opened a page with JavaScript that would post directly to it!

Here is a Python web app written using the standard library http.server package:

from http.server import HTTPServer, BaseHTTPRequestHandler

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length", 0))
        open("/tmp/diag.json", "w").write(self.rfile.read(n).decode())
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Headers", "*")
        self.end_headers()
    def log_message(self, *a):  # quiet
        pass

HTTPServer(("127.0.0.1", 9999), H).serve_forever()

All it takes is a POST request filled with JSON and writing it out /tmp/diag.json file. it sends Access-Control-Allow-Origin: * header (including from) OPTIONS request) so that code running on another domain can still communicate back to it.

The cloud then injected this code into the template it was loading into the browser:

const host = document.querySelector("navigation-search");
const ta   = host.shadowRoot.querySelector("textarea");
const cs   = getComputedStyle(ta);
fetch("http://127.0.0.1:9999/diag", {
  method: "POST",
  body: JSON.stringify({
    dpr: window.devicePixelRatio,
    scrollWidth: ta.scrollWidth, clientWidth: ta.clientWidth,
    whiteSpace: cs.whiteSpace, width: cs.width,
  }),
});

measured from