fix: prevent reconnect loops during server stalls - #5561
Conversation
Root-cause work for the constant disconnect/resync loop: the client RPC pinger killed the socket after a single missed 5s ping window, while the single-threaded server blocks that window with synchronous SQLite reads, synchronous trace writes, and a per-connect executable scan storm (27k+ shell.isExecutableFile probes observed on one session bootstrap). Reconnecting reset backoff to 1s and re-triggered the same storm, making the loop self-sustaining. - effect patch: tolerate 2 missed ping windows (fail on the 3rd, ~15s of silence) instead of hard-failing after one; keep pinging in between - shell: memoize PATH-scan command resolution per (platform, PATH, PATHEXT, command) for 30s so session bootstrap stops re-scanning; explicit-path probes stay uncached (installers probe just-written files) - observability: clamp db.query.text to 200 chars and any string attribute to 500 in the trace file sink (was 54% of trace bytes, rotating 10MB every 2-4 min and destroying diagnostic history) - supervisor: raise the first reconnect backoff rung from 1s to 3s so a stall-kill cannot cycle at 1s against a still-starved server Verified: client-runtime 510/510, shared suite identical to baseline (5 pre-existing Windows-environment failures, zero regressions), typecheck green for shared/client-runtime/web. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
While a thread runs, the composer replaced Send with Stop on every platform, leaving mid-run steering reachable only through the invisible Enter-to-submit path - and structurally impossible on narrow viewports, where Enter inserts a newline and the collapsed send button was hard-disabled during runs. Steering itself (thread.turn.start during an active turn) has always worked, including over the relay. - render a steer Send button beside Stop while running, using the same form-submit path and the same disable predicate as the normal send - stop hard-disabling the collapsed narrow-viewport send button during phase === "running" - onSend: surface a toast when a message is dropped because the environment is not connected, instead of silently swallowing it; the draft stays in the composer Verified: web typecheck green, composer test suites pass, lint clean on changed files (one unrelated pre-existing test failure baselined via stash against the clean tree). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tabbing back to the app on a dead transport paid a fixed 15s probe timeout plus a 3s backoff sleep before reconnecting even began, then re-downloaded the full shell snapshot over HTTP on every wake - even though the event store already supports cursor resume and threads already use it. - probe timeout 15s -> 3s on desktop/web (mobile already 3s) - skip the first backoff rung when a foreground wake probe fails: the user is actively returning on a dead socket. Only that first attempt skips; a failed immediate reconnect resumes normal backoff, so the reconnect-storm protection stays intact - shell warm resume: subscribe with the cached snapshotSequence cursor instead of always reloading the HTTP snapshot; the server already falls back to a streamed snapshot when the replay gap overflows - memoize editor discovery (resolveAvailableEditors PATH scan, up to 5s) with a 60s TTL above the shared command-resolution cache, so serverGetConfig stops rescanning on every connect - raise SHELL_RESUME_MAX_GAP 1000 -> 5000 (shell replay is coalesced; one busy agent thread while away no longer forces full snapshots) Verified: client-runtime 512/512 (4 new/reworked wake-path tests), client-runtime + server typecheck green, externalLauncher tests 5/5, targeted server subscribeShell tests 7/7. Two pre-existing environmental server test failures (Windows symlink EPERM in setup) unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review This PR introduces significant runtime behavior changes to connection resilience including retry timing, wake probe handling, caching, and shell synchronization logic across multiple packages. Additionally, there is an unresolved review comment identifying a potential bug in the shell resume fallback path. You can customize Macroscope's approvability policy. Learn more. |
- command resolution cache: expire via the monotonic clock (Clock.currentTimeNanos) instead of wall-clock millis, so a backward system-clock adjustment can no longer keep expired entries alive past the 30s TTL - trace attribute truncation: slice to the per-key limit instead of the hard-coded 200, so general string attributes over 500 chars keep 500 (db.query.text still clamps to 200) Verified: shared typecheck green, shared tests identical to baseline, server externalLauncher memo tests 5/5 (TestClock drives currentTimeNanos consistently with millis). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
One finding: the new command-resolution memo lives in a module-global mutable Map in packages/shared/src/shell.ts rather than being owned by a layer/service, which hides the cache's lifetime from the Effect environment. Everything else reviewed (the Effect.cachedWithTTL memo in ExternalLauncher.make, the supervisor Ref state, the shell-state warm-resume path, and the trace-attribute helpers) follows the service conventions: subpath namespace imports, inline Context.Service interfaces, real make/layer exports, and no new ManagedRuntime/runPromise boundaries or service-instance injection.
Posted via Macroscope — Effect Service Conventions
Follow-ups stacked on the connection-resilience RFC (pingdotgg#5561): - supervisor: the retry ladder was non-monotonic after the first rung was raised to 3s ([3s, 2s, 4s, ...] retried the second failure faster than the first). Drop the stale 2s rung: [3s, 4s, 8s, 16s]. - observability: clamp oversized strings nested inside arrays/objects too (e.g. error stacks preserved by normalizeJsonValue), not just top-level values, so the trace-retention win holds. Adds a unit test. - shell: move the command-resolution cache into the Effect environment as a Context.Reference (same idiom as HostProcessPlatform) so tests and embedders can provide an isolated instance, per the Effect Service Conventions check. Also document the 30s negative-caching window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Update: rather than a separate PR, I pushed the follow-up fixes directly onto this branch as |
|
@t3dotgg Reviewed
Regression run against this branch tip, on Windows:
For pressure testing: the underlying fixes have been soaking on our fork's hosted deployment since yesterday, on the exact workload that motivated this PR — a relay session against a 24-core laptop drowning in agent threads, operated by an impatient user with a phone. Disconnect-loop symptoms gone; steering works mid-run. Happy to pick up the deferred items (steer-button visual pass, mobile parity) here or in a follow-up, and to file the pinger tolerance upstream on effect. Sent from my Fable 5 |
| const httpSnapshot = yield* snapshotLoader.load(prepared); | ||
| if (Option.isSome(httpSnapshot)) { | ||
| yield* applyItem({ kind: "snapshot", snapshot: httpSnapshot.value }); | ||
| yield* Ref.set(lastAuthoritativeSession, session); |
There was a problem hiding this comment.
Stale shell resume after HTTP fail
Medium Severity
On a new RPC session, makeSubscribeInput tries an authoritative HTTP shell refresh when lastAuthoritativeSession does not match. If that load returns Option.none (the documented “use the socket snapshot instead” path), the code still subscribes with afterSequence from the existing in-memory snapshot left over from cache or the prior session, so resume skips the full socket snapshot the fallback is meant to use.
Reviewed by Cursor Bugbot for commit fb2b8c5. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c7aba01. Configure here.
## What's Changed * fix: prevent reconnect loops during server stalls by @gfsaaser24 in pingdotgg/t3code#5561 * fix(server): settle stopped Claude subagents by @t3dotgg in pingdotgg/t3code#5568 * fix: scrolling up during a running thread no longer snaps back to the bottom by @t3dotgg in pingdotgg/t3code#5566 ## New Contributors * @gfsaaser24 made their first contribution in pingdotgg/t3code#5561 **Full Changelog**: pingdotgg/t3code@v0.0.32-nightly.20260807.1020...v0.0.32-nightly.20260807.1021 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.32-nightly.20260807.1021
Adopt upstream session/supervisor (and their tests) for pingdotgg#5561 reconnect behavior so Fork CI Test matches the merged implementations. Adapt fork-only threads-sync expectations to upstream threads.ts: HTTP reload on empty re-subscribe, stream errors keep cache without mapping snapshot reasons to status deleted, and thread.deleted events still drive permanent removal.
Adopts the ten upstream commits after #368: timeline live-follow (pingdotgg#5566), reconnect-loop handling during server stalls (pingdotgg#5561), plans folded into chat (pingdotgg#5558/pingdotgg#5551), plus server settle/reconnect fixes and the transfer-budget CI test (pingdotgg#5350). The fork's "keep the open WebSocket lease when a foreground liveness probe fails" is replaced by upstream's probe model. Both target reconnect churn during server stalls; upstream splits the wake reasons (probe vs reconnect), adds tolerance windows and a first-attempt ladder skip. Keeping both was incoherent: the auto-merge left the fork's swallow in front of upstream's wakeProbeFailed path, making it dead code. The fork's diagnostics log, labelled ping-timeout errors and followNetworkStatus resume handling are kept around it. Welds found by typecheck and tests, not by conflict markers: - ChatView and ThreadFeed each ended up with two follow-state variables, the fork's superseded one still being written; converged on upstream's. - findSidebarProposedPlan was dropped from session-logic while ChatView still called it; restored. - Two timeline row-union members merged into one malformed member. - The fork's pre-latch ThreadFeed handlers survived as duplicates alongside upstream's; removed, with the fork's unread-activity dot rebuilt on the latch. - resolveOlderHistoryAutoLoad, left over from the superseded pagination candidate, was referenced only by its own tests; removed with them. - Upstream's transfer-budget test opened a socket without the fork's omegent-t3 product handshake, so the environment rejected it. Plan sidebar wiring is removed with the surface upstream deleted; the fork's settle-independent follow-up composer gate is kept. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Adopts the ten upstream commits after #368: timeline live-follow (pingdotgg#5566), reconnect-loop handling during server stalls (pingdotgg#5561), plans folded into chat (pingdotgg#5558/pingdotgg#5551), plus server settle/reconnect fixes and the transfer-budget CI test (pingdotgg#5350). The fork's "keep the open WebSocket lease when a foreground liveness probe fails" is replaced by upstream's probe model. Both target reconnect churn during server stalls; upstream splits the wake reasons (probe vs reconnect), adds tolerance windows and a first-attempt ladder skip. Keeping both was incoherent: the auto-merge left the fork's swallow in front of upstream's wakeProbeFailed path, making it dead code. The fork's diagnostics log, labelled ping-timeout errors and followNetworkStatus resume handling are kept around it. Welds found by typecheck and tests, not by conflict markers: - ChatView and ThreadFeed each ended up with two follow-state variables, the fork's superseded one still being written; converged on upstream's. - findSidebarProposedPlan was dropped from session-logic while ChatView still called it; restored. - Two timeline row-union members merged into one malformed member. - The fork's pre-latch ThreadFeed handlers survived as duplicates alongside upstream's; removed, with the fork's unread-activity dot rebuilt on the latch. - resolveOlderHistoryAutoLoad, left over from the superseded pagination candidate, was referenced only by its own tests; removed with them. - Upstream's transfer-budget test opened a socket without the fork's omegent-t3 product handshake, so the environment rejected it. Plan sidebar wiring is removed with the surface upstream deleted; the fork's settle-independent follow-up composer gate is kept. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Rebuilt on main after #5561 and #5404 landed overlapping reconnect work. What main already covers is dropped from this branch: the backoff ladder rework replaces our jitter, wakeProbeFailed replaces our probe-failure fast path, and the authoritative-session guard replaces our shell warm-cache short-circuit. What remains is the mobile-specific delta: - New advisory `network-path-changed` wakeup: WiFi<->cellular keeps isConnected true while invalidating the socket's path, so the mobile adapter emits a wakeup on interface-type changes while active and the supervisor probes the session (3s bound) instead of waiting for the ping timeout. A failed probe rides main's wakeProbeFailed fast path; flapping interfaces cannot cut backoff delays short. - Client activity is re-reported on every newly connected session generation; the AppState-triggered report races the reconnect and is dropped, leaving provider/VCS work paused server-side for up to 25s after a resume. Generation dedup is per-supervisor since replacements restart the counter. - The RPC onPingTimeout hook is wired to logging so zombie-socket disconnects are distinguishable from ordinary closes. - Server websocket idleTimeout drops to 30s from Bun's 120s default (clients ping every 5s, so live connections never idle), releasing a suspended phone's half-dead socket and buffers four times sooner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (#5219), wired per its spec's v2 merge plan: - getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc group, ws handler, auth scope, client atom). - AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime maps orchestration-v2 subagent entities into the panel model; deriveAgentPanelModel's v2Projection leg is now live and the v1 fold never runs. Agents surface wired into ChatView + RightPanelTabs. Other ports and reconciliations: - Shell reconnect-loop fix (#5561) ported into the v2 shell sync (same-session resubscribes resume from the in-memory cursor), with the cursor-resume regression test adapted to v2 fixtures. - Mobile end-follow latch (#5566) ported onto the v2 ThreadFeed. - Claude ede_diagnostic interrupt classification (#5557) ported into ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI telemetry never becomes the failure banner). #5559 needs no v2 port (unknown system subtypes are already ignored). - Plan sidebar removed from the v2 ChatView/ChatComposer per main's plans-fold-into-chat rework (#5558); rightPanelStore stays at main's surface set. - SettingsPanels rebuilt as main's refactored version plus the branch's composer-context setting; sidebar snooze respects the time format (#4438 follow-through). - v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2 rewrite removes, the v1-bound transfer-budget CI harness (#5350, needs a v2 rebuild), and main's v1 client pagination machinery (#5493 client side; the 037 keyset migration is kept — server-side v2 windowing is a follow-up). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## What's Changed * fix(mobile): reduce thread feed scroll jank by @gabrielelpidio in pingdotgg/t3code#4874 * fix(web): restore sidebar v2 thread actions and terminal icon by @Noojuno in pingdotgg/t3code#4712 * fix(web): settle button now works on hover, not just right-click by @t3dotgg in pingdotgg/t3code#4905 * fix(clients): disable add project while disconnected by @StiensWout in pingdotgg/t3code#4834 * fix(composer): hide default Codex service tier by @maxktz in pingdotgg/t3code#4784 * docs: link iOS and Android app store downloads by @t3dotgg in pingdotgg/t3code#4902 * fix(web): align remote server update action by @StiensWout in pingdotgg/t3code#4731 * fix(connect): suggest a serve command that matches how you ran connect by @t3dotgg in pingdotgg/t3code#4897 * fix(mobile): stop shared content errors in Personal Team builds by @t3dotgg in pingdotgg/t3code#4943 * perf(mobile): sends respond instantly, thread opens stop freezing by @t3dotgg in pingdotgg/t3code#4882 * fix(web): show Codex fast mode as a bolt by @t3dotgg in pingdotgg/t3code#4947 * docs: seed worktrees with a copy of real userdata instead of banning it by @t3dotgg in pingdotgg/t3code#4949 * fix(mobile): support dragged images in the composer by @t3dotgg in pingdotgg/t3code#4953 * fix(mobile): stop long iOS threads from jumping while scrolling up by @t3dotgg in pingdotgg/t3code#4867 * fix(web): keep worktree default when switching a draft's machine by @t3dotgg in pingdotgg/t3code#4964 * perf(mobile): reconnect environments immediately on resume by @t3dotgg in pingdotgg/t3code#4878 * feat(web): pasting a huge screenshot now compresses it instead of erroring by @t3dotgg in pingdotgg/t3code#4967 * feat(web): regenerate thread titles from sidebar by @t3dotgg in pingdotgg/t3code#4810 * fix(web): show server update progress through reconnect by @t3dotgg in pingdotgg/t3code#4903 * feat(search): find threads by conversation content by @t3dotgg in pingdotgg/t3code#4959 * fix: marketing site Vercel builds no longer die after ~100 deploys by @t3dotgg in pingdotgg/t3code#4975 * docs: split user and maintainer docs, fix 100+ stale claims by @t3dotgg in pingdotgg/t3code#4807 * fix(web): server updates no longer look like warnings by @t3dotgg in pingdotgg/t3code#4992 * fix(connect): reboots no longer strand the relay link, 403s now say why by @t3dotgg in pingdotgg/t3code#4988 * Add project file picker (⌘P) and project content search (⇧⌘F) by @jakeleventhal in pingdotgg/t3code#4855 * Check for mobile app updates on launch by @juliusmarminge in pingdotgg/t3code#4958 * fix(mobile): support pre-Liquid-Glass iOS bottom toolbar by @gabrielelpidio in pingdotgg/t3code#4984 * fix(server): restore PR detection without HOME by @StiensWout in pingdotgg/t3code#4985 * fix(web): fill fast mode icon by @maria-rcks in pingdotgg/t3code#5004 * fix: cache project favicons across web and mobile by @gabrielelpidio in pingdotgg/t3code#4767 * perf(ci): cut stale runs and redundant setup by @t3dotgg in pingdotgg/t3code#4802 * style(web): make scroll-to-end pill translucent by @maria-rcks in pingdotgg/t3code#5036 * fix(desktop): bump Clerk Electron SDK to 0.0.24 and register t3code:// scheme on Linux by @juliusmarminge in pingdotgg/t3code#5015 * perf(server): cache default branch name and origin existence across status refreshes by @UtkarshUsername in pingdotgg/t3code#5008 * feat(shared): support shorthand (major-only) versions in the semver helpers by @arhxam in pingdotgg/t3code#5027 * fix(mobile): remove unnecessary photo library permission by @skjiisa in pingdotgg/t3code#4929 * fix(shared): lenient JSON parser deletes commas inside string values by @arhxam in pingdotgg/t3code#5025 * fix(mobile): default bare IP pairing to HTTP by @Lucenx9 in pingdotgg/t3code#4990 * fix(mobile): restore iOS Threads branding by @PixPMusic in pingdotgg/t3code#4862 * chore: add mobile issue template area by @MaxAnderson95 in pingdotgg/t3code#4895 * fix(desktop): link release notes from the update downloaded toast by @Sy-D in pingdotgg/t3code#4771 * fix(mobile): accept Android clipboard images in composer by @adityavardhansharma in pingdotgg/t3code#4836 * fix(mobile): show the correct build channel in Android's Threads page header by @PixPMusic in pingdotgg/t3code#4861 * fix(contracts): decode growing config unions forward-compatibly by @juliusmarminge in pingdotgg/t3code#5055 * fix(mobile): server model, worktree, and origin preferences now apply to new tasks by @t3dotgg in pingdotgg/t3code#5064 * fix(ci): repair the mobile showcase screenshots workflow by @juliusmarminge in pingdotgg/t3code#5057 * fix(ci): capture iPad App Store screenshots in landscape by @PixPMusic in pingdotgg/t3code#5065 * fix(oxlint-plugin): Resolve the oxlint bin without assuming a pnpm layout by @mwolson in pingdotgg/t3code#5066 * feat(cli): `npx t3 pair` - generate QR code from a running server by @t3dotgg in pingdotgg/t3code#4955 * fix(server): self-update no longer rolls itself back on restart by @t3dotgg in pingdotgg/t3code#5095 * fix(ci): rotate iPad showcase captures without Simulator UI scripting by @juliusmarminge in pingdotgg/t3code#5094 * feat(web): render terminals with libghostty-vt by @StiensWout in pingdotgg/t3code#4860 * refactor: move the canonical libghostty-vt vendor to the repository root by @StiensWout in pingdotgg/t3code#5102 * feat(mobile): add thread snoozing by @gabrielelpidio in pingdotgg/t3code#5053 * feat(mobile): make settled threads collapsible by @PixPMusic in pingdotgg/t3code#5056 * follow-up normalization after #4700. by @shivamhwp in pingdotgg/t3code#4498 * feat(web): search threads from the sidebar by @shivamhwp in pingdotgg/t3code#4769 * feat(web): add settings sidebar search by @shivamhwp in pingdotgg/t3code#4682 * fix: threads with open PRs no longer auto-settle by @t3dotgg in pingdotgg/t3code#5151 * fix(server): bound thread catch-up replay and stop full-DB snapshot hydration by @t3dotgg in pingdotgg/t3code#5147 * fix(server): follow branch drift in dedicated worktrees so PRs link to their thread by @t3dotgg in pingdotgg/t3code#5159 * fix(server): make remote updates rollback-safe by @t3dotgg in pingdotgg/t3code#5181 * fix(web): stop settle controls overlapping the status label by @ipanasenko in pingdotgg/t3code#4574 * fix: normalize app icon glyph sizing by @t3-code[bot] in pingdotgg/t3code#5202 * fix(server): surface cloudflared FTL/PNC relay logs as warnings, not debug by @arhxam in pingdotgg/t3code#5076 * fix(server): stop npx service updates from silently leaving the old server running by @t3dotgg in pingdotgg/t3code#5217 * feat: fold legacy models into separate menus by @t3dotgg in pingdotgg/t3code#5190 * fix(desktop): claim the t3code:// scheme default on Linux at startup by @juliusmarminge in pingdotgg/t3code#5054 * fix(web): polish interface spacing by @maria-rcks in pingdotgg/t3code#5252 * fix(desktop): Niri/Hyprland - Linux secret storage backend by @mwolson in pingdotgg/t3code#2916 * fix(web): match loading screen to dark theme by @t3-code[bot] in pingdotgg/t3code#5303 * fix(web): blink the terminal cursor again by @StiensWout in pingdotgg/t3code#5314 * fix(terminal): protect held Ctrl/Cmd+W close shortcut by @StiensWout in pingdotgg/t3code#5322 * fix(server): strip replayable terminal queries from history by @StiensWout in pingdotgg/t3code#5319 * fix(contracts): decode ServerProviders forward-compatibly by @Brechard in pingdotgg/t3code#5327 * fix(web): simplify chat code blocks by @t3-code[bot] in pingdotgg/t3code#5301 * fix(web): align multiline error alert controls by @t3-code[bot] in pingdotgg/t3code#5304 * Upgrade Effect to beta.103 by @juliusmarminge in pingdotgg/t3code#5331 * fix(web): increase tooltip z-index to overlay popovers and menus by @naMqe-h in pingdotgg/t3code#5326 * fix(server): use a Cursor todo's title when its content is blank by @arhxam in pingdotgg/t3code#5073 * fix(ssh): isolate managed tunnel processes by @nateEc in pingdotgg/t3code#4347 * fix(server): scrub AppImage XDG_DATA_DIRS and GSETTINGS_SCHEMA_DIR from terminals by @arhxam in pingdotgg/t3code#5075 * fix(mobile): show correct provider icons for Grok, Cursor, and OpenCode by @Wraient in pingdotgg/t3code#4586 * fix(web): stop the chat timeline reading through the provider status banner by @Bil0000 in pingdotgg/t3code#5353 * fix(desktop,web): contain renderer memory growth and recover from renderer OOM crashes by @t3dotgg in pingdotgg/t3code#5148 * fix(server): generate durable thread titles by @t3dotgg in pingdotgg/t3code#5357 * fix(web): better right panel (new diffs styling) by @maria-rcks in pingdotgg/t3code#5260 * fix(server): keep regenerated titles on topic by @t3dotgg in pingdotgg/t3code#5365 * refactor(server): make title prompts plaintext by @t3dotgg in pingdotgg/t3code#5368 * feat(web): configurable fonts and sizes under Settings → Appearance by @StiensWout in pingdotgg/t3code#5103 * feat(sidebar-v2): bring back thread pinning by @t3dotgg in pingdotgg/t3code#5312 * feat(web): make pairing QR codes actually scannable, with endpoint choice by @t3dotgg in pingdotgg/t3code#5360 * fix(desktop,web): improve in-app browser shortcuts and URL behavior by @t3dotgg in pingdotgg/t3code#4703 * fix(web): prevent legacy model picker layout shift by @FllipEis in pingdotgg/t3code#5349 * fix(server): allow remote updates with database migrations by @t3dotgg in pingdotgg/t3code#5374 * fix(mcp): unblock Kimi models in OpenCode with preview tools by @hwanseoc in pingdotgg/t3code#5128 * fix(web): clear main branch lint warnings by @t3dotgg in pingdotgg/t3code#5384 * fix(mobile): preserve grouped project workspaces by @shivamhwp in pingdotgg/t3code#4642 * fix(mobile): prevent Android thread search crash by @shivamhwp in pingdotgg/t3code#5386 * fix(web): truncate long project switcher names by @FllipEis in pingdotgg/t3code#5348 * fix(mobile): avoid double dividers between thread sections by @shivamhwp in pingdotgg/t3code#5391 * fix(web): keep the composer command menu anchored to the composer by @StiensWout in pingdotgg/t3code#5336 * fix(web): restore terminal link hover styles by @StiensWout in pingdotgg/t3code#5382 * fix(ci): isolate releases from shared API rate limits by @t3dotgg in pingdotgg/t3code#5394 * fix(web): keep model picker shortcuts in sync by @t3dotgg in pingdotgg/t3code#5400 * fix(web): keep terminal font settings reliable by @StiensWout in pingdotgg/t3code#5397 * Enrich terminal font previews by @juliusmarminge in pingdotgg/t3code#5428 * fix(web): preserve terminal font size when splitting by @t3-code[bot] in pingdotgg/t3code#5444 * Prevent terminal loading flash by @juliusmarminge in pingdotgg/t3code#5432 * fix: reconnect faster after remote server updates by @t3dotgg in pingdotgg/t3code#5404 * feat: native subagent & workflow observability by @t3dotgg in pingdotgg/t3code#5219 * perf(server): stop shipping full MCP tool results in thread payloads by @t3dotgg in pingdotgg/t3code#5482 * fix(web): closed plan sidebar stays closed when returning to a thread by @t3dotgg in pingdotgg/t3code#5484 * fix: respect time format for sidebar snooze by @huxcrux in pingdotgg/t3code#4438 * fix: smooth remote server updates by @t3dotgg in pingdotgg/t3code#5470 * fix(server): drop superseded tool updates from snapshots by @t3dotgg in pingdotgg/t3code#5483 * fix(web): clarify auto permission fallback by @t3-code[bot] in pingdotgg/t3code#5431 * fix(acp): keep unknown approvals actionable by @t3-code[bot] in pingdotgg/t3code#5430 * fix(mobile): stop thread messages reading through pending cards by @carlosricojr in pingdotgg/t3code#5450 * fix(web): align composer inline chips with prompt text by @StiensWout in pingdotgg/t3code#5495 * fix(web): clear woke state on explicit thread actions by @StiensWout in pingdotgg/t3code#5486 * fix(server): skip origin fetch when creating worktrees in repos without an origin remote by @t3dotgg in pingdotgg/t3code#5556 * fix(web): update tooltip no longer dismisses when scrolling release notes by @t3dotgg in pingdotgg/t3code#5547 * fix(server): stop showing commit/push/PR notices as errors in the work log by @t3dotgg in pingdotgg/t3code#5559 * fix(web): show remote environment for non-Git projects by @t3dotgg in pingdotgg/t3code#5555 * fix(server): stopping a Claude thread no longer shows an ede_diagnostic error by @t3dotgg in pingdotgg/t3code#5557 * fix(web): show one toast when snoozing threads in bulk by @t3dotgg in pingdotgg/t3code#5560 * fix(server): let stopped threads settle immediately by @t3dotgg in pingdotgg/t3code#5553 * feat: paginate thread loading with user-anchored turn windows by @t3dotgg in pingdotgg/t3code#5493 * fix: prevent reconnect loops during server stalls by @gfsaaser24 in pingdotgg/t3code#5561 * fix(server): settle stopped Claude subagents by @t3dotgg in pingdotgg/t3code#5568 * fix: scrolling up during a running thread no longer snaps back to the bottom by @t3dotgg in pingdotgg/t3code#5566 * fix(server): one disconnecting client no longer blocks every reconnect by @t3dotgg in pingdotgg/t3code#5572 * test(server): catch client transfer regressions in CI by @t3dotgg in pingdotgg/t3code#5350 * fix(web): stop the "requests are slow" warning from firing on every provider update by @t3dotgg in pingdotgg/t3code#5570 * fix(web): keep agent panel rows stable by @t3dotgg in pingdotgg/t3code#5569 * docs: ship production T3 Connect public config in .env.example by @t3dotgg in pingdotgg/t3code#5573 * fix(web): plans stop hijacking the UI, fold into chat instead by @t3dotgg in pingdotgg/t3code#5558 * feat(web): remove Build/Plan toggle from the composer by @t3dotgg in pingdotgg/t3code#5551 * feat(web): per-device provider settings by @t3dotgg in pingdotgg/t3code#4479 * fix(mobile): invisible T3 Connect devices can now be seen and removed by @t3dotgg in pingdotgg/t3code#5563 * fix: add missing space before 'GitHub releases page' link on download page by @Mateleo in pingdotgg/t3code#4511 * fix(web): stop the sidebar "Working" label from pulsing by @t3dotgg in pingdotgg/t3code#5580 * feat(web): add modular theme library by @StiensWout in pingdotgg/t3code#5226 * fix(web): reading a thread clears Done; Woke is dismissible by @t3dotgg in pingdotgg/t3code#5579 * perf(dev): faster cold-start for dev web serving by @t3dotgg in pingdotgg/t3code#5584 * fix(dev): agents get --share right on the first try by @t3dotgg in pingdotgg/t3code#5586 * fix(mobile): improve keyboard avoiding by @jmeistrich in pingdotgg/t3code#5451 * fix(web): live background-work banner no longer hides behind the update notice by @t3dotgg in pingdotgg/t3code#5595 * fix(web): stabilize chat timeline positioning by @jmeistrich in pingdotgg/t3code#5449 * feat(server): record runtime mode per turn and on mode changes by @t3dotgg in pingdotgg/t3code#5593 * feat(web): thread actions from the chat header title by @t3dotgg in pingdotgg/t3code#5592 * fix(mobile): avoid iOS terminal reset on clear by @justynleung in pingdotgg/t3code#5440 * fix(mobile): pad scroll views above Android nav bar by @StoneCotton in pingdotgg/t3code#5415 * fix(mobile): keep Android chat text from showing through the composer by @PollyGlot in pingdotgg/t3code#5582 * fix(mobile): repair Clerk auth navigation headers by @gabrielelpidio in pingdotgg/t3code#5140 * Bump mobile app version to 1.0.2 by @juliusmarminge in pingdotgg/t3code#5588 * feat(web): click the pin icon to unpin a thread by @UtkarshUsername in pingdotgg/t3code#5578 * fix(web): show the correctly matching shortcut in new thread button's tooltip by @UtkarshUsername in pingdotgg/t3code#5594 ## New Contributors * @gabrielelpidio made their first contribution in pingdotgg/t3code#4874 * @arhxam made their first contribution in pingdotgg/t3code#5027 * @skjiisa made their first contribution in pingdotgg/t3code#4929 * @Lucenx9 made their first contribution in pingdotgg/t3code#4990 * @MaxAnderson95 made their first contribution in pingdotgg/t3code#4895 * @t3-code[bot] made their first contribution in pingdotgg/t3code#5202 * @Brechard made their first contribution in pingdotgg/t3code#5327 * @naMqe-h made their first contribution in pingdotgg/t3code#5326 * @Bil0000 made their first contribution in pingdotgg/t3code#5353 * @gfsaaser24 made their first contribution in pingdotgg/t3code#5561 * @Mateleo made their first contribution in pingdotgg/t3code#4511 * @jmeistrich made their first contribution in pingdotgg/t3code#5451 * @justynleung made their first contribution in pingdotgg/t3code#5440 * @StoneCotton made their first contribution in pingdotgg/t3code#5415 * @PollyGlot made their first contribution in pingdotgg/t3code#5582 **Full Changelog**: pingdotgg/t3code@v0.0.31...v0.0.32 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.32
…l snapshot Upstream PR pingdotgg#5561 rewrote shell-sync.test.ts and its new cached-snapshot case asserts the surfaced snapshot equals the cached payload verbatim. This fork's shell.ts normalizes every thread with an `execution` overlay as it loads, so the surfaced object legitimately carries one field more than the cache it came from and the upstream expectation cannot hold here. The normalization is fork-only and pre-existing (three call sites, both before and after the merge; none upstream), and shell.ts took upstream's rewrite in full, so the code is correct and the expectation is what needed updating. Asserts the full shape rather than relaxing the match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>


Server event-loop stalls could make one delayed pong kill the RPC connection, then immediately reconnect into the same expensive bootstrap work. Users saw repeated disconnects and
Syncing...even when the underlying network was healthy.This tolerates short pong delays, uses a safer reconnect ladder, and avoids repeated bootstrap work by caching command and editor discovery and resuming shell state from an authoritative cursor. Failed HTTP snapshot refreshes still request a complete socket snapshot, trace attributes are bounded to preserve useful diagnostics, and sends attempted while disconnected now explain why they were not sent.
Focused tests cover pinger tolerance, wake recovery, shell synchronization, discovery caching, and trace truncation.
🤖 Final review fixes and PR cleanup by GPT-5.6 Codex using the Codex harness in T3 Code.
Note
Medium Risk
Changes core connection supervisor, RPC protocol (patched dependency), and shell subscription resume semantics; mis-tuned wake/backoff or resume cursors could cause extra sync work or delayed reconnects.
Overview
Hardens client–server connection behavior against event-loop stalls, aggressive reconnect loops, and expensive session bootstrap.
Transport & supervisor: Patches Effect RPC ping/pong to allow two missed 5s windows before closing (~15s silence). Raises the first reconnect delay from 1s → 3s and, when a foreground wake probe fails, skips the first backoff sleep once (then normal backoff resumes). Supervisor and session tests are updated for the new timing and wake behavior.
Connect cost: Adds a 30s PATH command-resolution cache in shared shell code and 60s memoized editor discovery on the server so repeat connects avoid huge filesystem scans. Trace file sink now clamps oversized span attributes (including
db.query.text) without mutating live spans.Shell sync: On the same live RPC session, foreground resubscribe resumes from the in-memory
snapshotSequenceinstead of always forcing a full HTTP refresh; failed HTTP refresh falls back to a full socket snapshot. New/rewritten shell-sync tests cover these paths.Web: Sending while the environment is unavailable shows a warning toast (draft preserved) instead of silently attaching an annotation.
Reviewed by Cursor Bugbot for commit c44dad4. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Harden connection resilience by fixing idle disconnects with backoff, pinger tolerance, and shell cursor reuse
EnvironmentSupervisorretry ladder first rung from 1s to 3s; a failed foreground wake probe now triggers an immediate reconnect instead of waiting for the first backoff sleepExternalLauncher(60s TTL) and command path resolution inshell.resolveCommandPathForPlatform(30s TTL) to reduce redundant filesystem scansMacroscope summarized c44dad4.