# R33: a queued action that starts late reports `busy` briefly, then the tab recovers

Before = main 4c924ff4e6 + capture instrumentation (before.patch). After = fix/issue-32849 4052b2f7d0 + the same instrumentation (after.patch). Each side ran its own dev server from its own checkout with `T3CODE_DEV_SEED=0` (fresh database, synthetic data), driven through the same MCP tool path by the same driver (`driver.mjs <side> R33`). The instrumentation only logs request delivery, operation start/settle and host connections; it changes no behaviour. No live model calls: the seed step starts one scripted fixture turn only to obtain MCP credentials.

Environment: Ubuntu 24.04.4, Linux 6.8.0 x86_64, Node v26.12.1, Playwright 1.60.0, Google Chrome for Testing 157.0.8103.38 (headless server browser).

## Script (identical MCP inputs on both sides)

| t (ms from A) | Step |
|---|---|
| 0 | A: `preview_evaluate` on tab T, a promise that resolves after 2500 ms (default timeout 15000 ms) |
| 200 | W: `preview_wait_for` on T, selector `#never-r33`, `timeoutMs` 4000. Queues behind A |
| ~2500 | A settles; W leaves the queue and its own 4000 ms Playwright timer starts (~2300 ms after W was delivered) |
| 4200 | W's request deadline (4000 ms after delivery) passes while W's operation is still running |
| ~4800 | Probe: `preview_evaluate` `"follow-up ran"` on T (~4600 ms after W delivery) |
| ~6500 | W's own operation times out and settles |
| ~8200 | Recovery: `preview_status`, then `preview_evaluate` on T, then `t3_preview_close` |

## Results (verbatim MCP results)

| Call | Before (main 4c924ff4e6) | After (4052b2f7d0) |
|---|---|---|
| A `preview_evaluate` | `{"value":"A settled"}` | `{"value":"A settled"}` |
| W `preview_wait_for` | `Preview automation waitFor timed out after 4000ms.` (answered 5068 ms after send) | `Preview automation waitFor timed out after 4000ms.` (answered 4511 ms after send, i.e. timeoutMs + ~500 ms) |
| Probe `preview_evaluate` | `Preview automation client server-browser disconnected during evaluate.` (465 ms) | `This browser tab is busy with an earlier action that timed out. Open a new tab with preview_open and reuseExistingTab=false, or close this one with t3_preview_close.` (10 ms) |
| Recovery `preview_status` | status JSON, tab present | status JSON, tab present |
| Recovery `preview_evaluate` | `{"value":"ok"}` | `{"value":"ok"}` |
| `t3_preview_close` | `{}` | `{}` |

## Server timeline (instrumentation log, host connection ids shortened)

Before:
```
20.983 request.delivered waitFor 4000        conn 26f55250
23.245 operation.start   waitFor             conn 26f55250   (started 2262 ms after delivery)
25.538 request.delivered evaluate (probe)    conn 26f55250
26.985 host.connected                        conn c6ace5c9   <- broker evicted the host and it reconnected
27.248 operation.error   waitFor "Waited 4000ms without every condition holding."
27.248 operation.start   evaluate (probe)    <- the probe still ran on the tab after its caller got "disconnected"
28.942 operation.start   evaluate (recovery) conn c6ace5c9
```

After:
```
21.228 request.delivered waitFor 4000        conn 3ca58c05
23.532 operation.start   waitFor             conn 3ca58c05   (started 2304 ms after delivery)
25.829 request.delivered evaluate (probe)    conn 3ca58c05   -> refused at once with busy, never queued
27.535 operation.error   waitFor "Waited 4000ms without every condition holding."
29.242 operation.start   evaluate (recovery) conn 3ca58c05   <- same host connection throughout
```

## Reading

- Before: the probe queued behind the late-started wait. The broker then evicted the shared server-browser host (a new connection id appears), and the agent got "client server-browser disconnected", although the probe still ran on the tab afterwards.
- After: from W's request deadline until W's own operation settled (~2.3 s here), the probe on T got `busy` at once. Once W settled, the same tab served the recovery call on the same host connection.
- The `busy` is transient and bounded by the late-started operation's own timeout. Its recovery advice (new tab or close) is stronger than needed in this case; waiting also works.

What else changed between the sides: W's timeout answer arrives ~560 ms earlier after the fix (the server answers at timeoutMs + 500 ms); nothing else differs.
