Happened again just now. Neither the iOS app nor the desktop had been restarted, so the restart in the original report doesn't explain it. The thread list sat on syncing for over 6 s after the app came back to the foreground.
These are the server's spans for the phone's requests (desktop server.trace.ndjson, times in UTC, phone = T3Code/164 CFNetwork/3860.700.2 Darwin/25.6.0 / T3Code/2.1.0 (iPhone; iOS 26.6.2) over the T3 relay, iPhone Air):
| 1 | 01:49:58.832 POST /api/auth/websocket-ticket 11ms |
| 2 | 01:49:59.013 GET /api/auth/session 10ms |
| 3 | 01:49:59.647 GET /api/auth/session 450ms |
| 4 | 01:49:59.661 GET /api/orchestration/shell 10022ms <- ends 01:50:09.683 |
| 5 | 01:50:00.093 http.orchestration.loadShellSnapshot 2228ms <- ends 01:50:02.321 |
| 6 | sql.transaction > ProjectionStore.readShellSnapshotRows 2129ms |
| 7 | sqlite SELECT t.thread_id, t.payload_json, CASE WHEN json_extract(...forkedFrom.type)... 499ms |
| 8 | sqlite SELECT counts.thread_id, SUM(CASE WHEN counts.run_id IS NULL OR r.status <> 'rolled_back' ... 1605ms |
| 9 | ProjectionStore.decodeShellSnapshot 92ms |
| 10 | (no child spans after 01:50:02.321; the GET stays open another 7.36 s) |
| 11 | 01:50:10.308 ws.orchestrationV2.subscribeShell 911ms |
| 12 | 01:50:34.751 GET /api/orchestration/threads/<id>/bounded?compactTurnItems=1 165ms (loadThreadSnapshotWindow 42ms) |
So for this sync:
- ~7.4 s went after the handler had built the shell snapshot, with no server work traced. It looks like serializing the response and sending it through the relay. This machine has 2,157 non-deleted threads (their
payload_jsoninorchestration_v2_projection_threadstotals 2.7 MB), and as far as I can tell the phone downloads the whole shell again on every foreground/reconnect. - ~2.1 s went building the snapshot, 1.6 s of it in the per-thread item-count aggregate query (
statev2.sqliteis ~21 GB here). - The thread itself was fast this time (165 ms for the bounded window), so the blank wait comes from shell sync blocking the UI, not from the thread fetch.
Suggestions, beyond the ones in the report:
- Make reconnect sync incremental (resume from the last seen sequence, or send only threads changed since the client's cached shell) instead of re-sending the full shell.
- Page or limit the shell to the threads the phone shows (active/recent), and fetch settled/archived ones on demand.
- Don't block opening a cached thread on the shell sync finishing.
I couldn't get the iOS app's own logs off the phone, so this is the server side only. I'm happy to attach client logs if the app has a way to export them.