Open the interface and the live panes draw themselves. Both of them resolved their browser through ready, and ready starts what it resolves, so drawing a pane for a conversation nobody had asked anything launched an engine. Measured on 0.38.0 with an empty home and no instruction given: 9 firefox processes before, 16 after, roughly 800 MB and seven seconds. /live/frame then answered 503, so the engine the look had started was not even used for the look.
The guard that was supposed to prevent this read whether the conversation had ever called anything. The page polls /live/browsers first, browser_list is the one tool chosen precisely because it starts nothing, and it armed the guard: the very call that cannot cost an engine is what let the next one do it. Both views carried the same guard and both did it.
Underneath was a belief written down twice, in link.py and in the test that pinned it: "over MCP there is no way to ask is one running without starting one". There is. registry.peek answers exactly that, its docstring has said so since the day it was written - "for callers that want to know whether a browser is up, such as a live view" - and it had no callers.
So looking sits beside ready, and the two tools a view uses take it. session_list_pages answers the empty list for a browser that is not running; browser_watch refuses. Nothing else changes: a command aimed at a sleeping browser still starts it as the same person, which is the promise browser_list makes to a model, and that is asserted rather than assumed. The remembered flag is gone with the workaround it existed for.
Fixing it in the interface instead was written, measured and thrown away, and the numbers are why:
| median | |
|---|---|
browser_list alone, which is the guard
| 30.3 ms |
/live/frame with that guard in it
| 31.3 ms |
/live/frame as it ships here
| 9.2 ms |
The free question is free of engines, not of time: it asks every running browser for its tabs, which is a round trip each. Paying it per frame put the live pane at 32 frames a second to answer a question whose answer is almost always yes, and the pump asks for 25. Measured in separate phases rather than interleaved, because the two routes share the link's lock and alternating them made each wait for the other - interleaved they read 33.7 and 33.6 ms, which measured the contention and not the routes.
Re-verified on the running product with the bench that found the defect: three sequences, zero processes started, and /live/frame answers 204 instead of 503. With a browser actually running the panes draw exactly as before.
The gate is tests/mcp_server/test_a_look_starts_nothing.py, and it asserts the event one layer up from the engine: whether the registry ends up holding a session it did not hold. Every existing test of these tools builds a registry whose factory launches nothing, which is not carelessness - it is what makes them fast and isolated - and it is also why none of them could see this: starting a browser was not an unobserved event, it was an impossible one. Two of the four tests are the other half, because a guard that refuses everything would satisfy the first two: a browser that IS running is still looked at, and a command still starts a sleeping one.
Seven known-bad versions, six killed. The seventh renames the shared refusal, which one place defines and three read, so nothing breaks - that mutation confirms the single source rather than accusing the gate. Two of the seven found a hole in the fix rather than in the old code: /live/frame had to learn that a refusal is not always a failure, since the same shape carries "the capture is broken" (a sentence somebody reads, 503) and "there is nothing to look at" (the idle pane, 204), and nothing was watching that branch.
554 tests pass.