I can confirm the getShellSnapshot part of this issue on the October 22 nightly, using a component reproduction against a consistent copy of my database. In the captured writer read, the delay was inside the synchronous query rather than waiting to acquire the connection. This validates the blocking mechanism; it doesn't reproduce an actual T3 client disconnect or cover getThreadShell and getThreadRecords.
What I saw live
On 0.1.2-nightly.20261022.3526 (f054d1b), macOS 27.0.1, M4 Pro with 24 GiB RAM and a 12.1 GB statev2.sqlite, the server trace showed a transaction containing readShellSnapshotRows running on db.connection = writer for 9,841 ms with db.lock_wait_ms = 0.001, and the event-loop monitor reported a 9,133 ms max delay for the 30 s window containing it. The writer is a DatabaseSync on the main thread (nodeSqliteClient.ts#L851-L854), reads only leave it when wrapped in readOnly (#L1072-L1081), and getShellSnapshot isn't (ProjectionStore.ts#L6406-L6409). While the synchronous statement runs, the main thread cannot handle incoming RPCs, pings or queued writes. That delay needn't appear as connection lock wait. The same trace records MCP listThreads → getShellSnapshot on writer too.
How the reproduction works
This runs the installed SQL and reader-worker source in isolation, not the full T3 service or its WebSocket client.
- Consistent snapshot. The copy used SQLite's online backup API from a
mode=ro+query_onlyconnection holding one read transaction throughout. It passedquick_checkand matched the pinned source's schema hash, page count, row counts and max event sequence: 12,125,323,264 bytes, 3,798 threads, 5,874 runs, 268,415 turn items, 56,083 messages, and 3,773 threads returned by the shell query. An earlier inconsistent copy was discarded. The snapshot monitor was corrected to use a separate process, and only the verified replacement was benchmarked. - Exact SQL and runtime. The harness extracted SQL from the installed bundle and ran it in
readShellSnapshotRowsorder under the app's Electron 44.9.3 / Node 24.22.0 / SQLite 3.53.4. It didn't yield to the event loop between statements, so it represents the worst case for Effect scheduling; result decoding was excluded. - Writer vs reader. Writer cells use a main-thread
DatabaseSyncwith the installed pragmas andBEGIN IMMEDIATE. Reader cells run the installed reader-worker source verbatim withBEGIN; that's where #23689 sendsgetShellSnapshot. - Probes. A separate process sends loopback HTTP pings in place of WebSocket RPC, the server process records event-loop delay, and the writer connection inserts into a harness-owned sandbox table every 250 ms.
- Indexes. The comparison applied #23009's migration 068 SQL at
7180469verbatim to a clone, withsqlite_stat1left empty as in the live database. - Cache state. Each cell starts on a fresh APFS clone ("first read") and repeats in-process; page-cache residency wasn't measured, so first read isn't guaranteed to be empty-cache.
Results
For the full shell, each configuration had three first-read samples and two repeat samples. Query times are medians; event-loop figures are medians of each run's maximum delay. Probe RTT and queued-write delays are the maxima across the three first reads.
- Installed routing (writer): The query took 13,163 ms (range 13,135–13,496), with 13,170 ms of event-loop delay. The worst probe RTT was 13,419 ms and the worst queued-write delay was 13,433 ms. Repeat reads took 271 ms.
- Reader worker: The query still took 13,374 ms, but event-loop delay stayed at 12.7 ms. The worst probe RTT was 1.5 ms and the worst queued-write delay was 11.7 ms. Between 52 and 54 sandbox writes committed during each first read, compared with zero on the writer path. Repeat reads took 287 ms.
- #23009 indexes, writer: The query fell to 2,700 ms, but still blocked the event loop for 2,707 ms. The worst probe RTT was 2,909 ms and the worst queued-write delay was 2,919 ms. Repeat reads took 150 ms.
- #23009 indexes, reader: The query took 2,702 ms with 12.7 ms of event-loop delay. The worst probe RTT was 0.7 ms and the worst queued-write delay was 26 ms. Repeat reads took 159 ms.
Row counts and per-statement SHA-256 matched within each query shape across all 34 benchmark runs (20 full shell, 8 active, 6 project + active), covering writer vs reader, original vs indexed and first vs repeat. That's result equivalence, not a correctness review of either PR. The indexes change three probes in selectShellThreadRows (the blocked-run candidate drops its temp B-tree sort, both stop-request lookups become covering) and no other plans.
Limits
- Reader routing fixes main-loop blocking, not read latency. The reader is one worker behind a single lock, so a long shell read delays other
readOnlyreads. The live traces also contain a 14.4 sShellStreamreader transaction, with overlapping reader operations recording 14.2 s and 9.0 s of connection lock wait. - I started on this because app.t3.codes showed disconnected/reconnecting, which I didn't reproduce with the real client. The installed client pings every 5 s and times out after 15 s without a frame, so an idle connection needs a block over about 10 s; a simulation of that rule timed out in 14 of 25 idle and 0 of 25 active cases across the five real-data first-read writer blocks (11.9–13.5 s), and the 9.8 s live block is too short by itself under that model. These are timing-phase tests, not a measured browser disconnect rate.
- The host was shared with the live app. Benchmark I/O could affect both workloads, and the main first-read configurations have only three samples each.
- #23689's
ProjectionStore.tshunks apply cleanly tof054d1b, but I didn't build or run the patched app.
Takeaway
Routing getShellSnapshot through the reader, as #23689 does, keeps the main loop and the writer free during the read with identical results; event-loop delay stays near its ~13 ms idle level instead of matching the 13 s statement. The #23009 indexes address query cost separately (about 4.9x faster on first read, 1.8x on repeat), but alone they still leave the writer path blocked for about 2.7 s.
Raw results and the harness are retained locally; the snapshot holds private data.
Note
Reproduction run by Claude Opus 5.5 (xhigh) in T3 Code; Codex reviewed the artifacts.