Skip to content

fix(server): keep provider notification consumers alive past startSession - #6538

Merged
juliusmarminge merged 1 commit into
pingdotgg:mainfrom
tsouth89:fix/provider-notification-consumer-scope
Aug 15, 2026
Merged

fix(server): keep provider notification consumers alive past startSession#6538
juliusmarminge merged 1 commit into
pingdotgg:mainfrom
tsouth89:fix/provider-notification-consumer-scope

Conversation

@tsouth89

@tsouth89 tsouth89 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #5781.

What Changed

CursorAdapter, GrokAdapter and CodexAdapter fork their notification consumer with Effect.forkChild inside startSession. That makes the consumer a child of the calling fiber, and Effect interrupts a fiber's children when it completes, so the consumer dies the moment startSession returns and every notification the provider streams afterwards is dropped.

Each adapter switches to Effect.forkIn with the session scope it already has:

File
CursorAdapter.ts:877 Effect.forkIn(ctx.scope)
GrokAdapter.ts:879 Effect.forkIn(ctx.scope)
CodexAdapter.ts:1733 Effect.forkIn(sessionScope)

Why

The user-visible symptom is a thread that sits on "Working" forever: the request succeeds, the provider streams its whole turn, and nothing is projected.

Nothing else needed to change. Cursor and Grok already create a session scope, store it on the session context, and close it during teardown; Codex does the same with a local sessionScope. Only the fork target was wrong. CodexSessionRuntime one layer down already uses Effect.forkIn(runtimeScope), so this makes the adapters consistent with the pattern the codebase already follows.

On the tests

The existing tests cannot catch this, which is worth being explicit about. They call startSession directly from the test fiber, and that fiber never completes, so the consumer stays alive and the bug is invisible. Any test written that way passes with or without the fix.

Each new test instead runs startSession in a fiber and joins it, which is what production does, then asserts that a notification arriving afterwards is still projected.

Verified by mutation, one adapter at a time:

Grok     with fix: pass 488ms   forkChild restored: fail 10.4s
Cursor   with fix: pass         forkChild restored: fail 10.4s
Codex    with fix: pass         forkChild restored: fail 10.0s

They also run on the live clock. Under the default test clock the timeouts wait on virtual time that never advances, so a regression would hang until the suite timeout instead of failing at the assertion. Without TestClock.withLive the failing case took 60s instead of 10s.

Verification

Run on Linux, since the mock ACP wrapper is a POSIX shell script and these suites do not run on Windows:

  • apps/server/src/provider/Layers/ — 350 tests pass
  • server typecheck exits 0
  • lint clean

UI Changes

None.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes (n/a)
  • I included a video for animation/interaction changes (n/a)

Note

Medium Risk
Touches core provider session lifecycle and fiber scoping for Codex, Cursor, and Grok; wrong scope/fork behavior could drop events or leak fibers, but the change is narrow and covered by new regression tests.

Overview
Fixes threads stuck on Working after startSession returns: provider streams kept running but nothing reached the UI because the runtime notification consumers were forked with Effect.forkChild on the startSession fiber. Effect interrupts children when that fiber finishes, so consumers died as soon as the request completed.

Codex, Cursor, and Grok adapters now fork event/notification consumers with Effect.forkIn on the existing session scope (sessionScope / ctx.scope), so they outlive the caller and keep projecting deltas and turn lifecycle until teardown.

Each adapter gets a regression test that runs startSession in a short-lived fiber (matching production), then asserts post-start streaming still works; tests use TestClock.withLive so timeouts fail fast on regression instead of hanging the suite.

Reviewed by Cursor Bugbot for commit 8c0bc32. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix provider notification consumers to persist past startSession completion

In CodexAdapter, CursorAdapter, and GrokAdapter, the notification/event consumer fibers were forked as children of the startSession caller fiber, causing them to be interrupted when startSession returned. This meant runtime events and session notifications stopped flowing to the adapter event stream after session startup.

  • Replaces Effect.forkChild with Effect.forkIn(sessionScope) (or ctx.scope) in each adapter's startSession flow, tying the consumer fiber lifetime to the session scope instead of the calling fiber.
  • Adds regression tests to each adapter verifying that events continue to be emitted after the startSession fiber completes.

Macroscope summarized 8c0bc32.

…sion

CursorAdapter, GrokAdapter and CodexAdapter fork their notification consumer
with Effect.forkChild inside startSession. forkChild makes it a child of the
calling fiber, and Effect interrupts a fiber's children when that fiber
completes, so the consumer dies the moment startSession returns and every
notification the provider streams afterwards is dropped.

The user-visible symptom is a thread that sits on "Working" forever: the
request succeeds, the provider streams its whole turn, and nothing is
projected.

Each adapter already builds the scope this belongs in. Cursor and Grok create
a session scope, store it on the session context and close it during teardown;
Codex does the same with a local sessionScope. Only the fork target was wrong.
CodexSessionRuntime one layer down already uses Effect.forkIn(runtimeScope), so
this makes the adapters consistent with the pattern the codebase already
follows.

The existing tests could not catch this. They call startSession directly from
the test fiber, which never completes, so the consumer stays alive and the bug
is invisible. Each new test runs startSession in a fiber it then joins, which
is what production does, and asserts a notification arriving afterwards is
still projected. Verified by mutation: restoring forkChild in any one adapter
fails that adapter's test in about 10 seconds, and the suites pass with the
fix.

The tests run on the live clock. Under the default test clock the timeouts wait
on virtual time that never advances, so a regression would hang until the suite
timeout instead of failing where the assertion is.

Fixes pingdotgg#5781
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: caf290c0-e470-4cbc-9708-b98c5afcc6b2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 13, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 8c0bc32

Straightforward bug fix changing Effect.forkChild to Effect.forkIn(sessionScope) in three adapter files to prevent notification consumers from being interrupted when the startSession fiber completes. The production changes are minimal single-line fixes, with most additions being comprehensive regression tests.

You can customize Macroscope's approvability policy. Learn more.

@juliusmarminge
juliusmarminge merged commit afca73d into pingdotgg:main Aug 15, 2026
17 checks passed
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 15, 2026
## What's Changed
* fix(desktop): app zoom no longer zooms the preview browser by @juliusmarminge in pingdotgg/t3code#6649
* fix(server): keep provider notification consumers alive past startSession by @tsouth89 in pingdotgg/t3code#6538
* fix(server): treat removed Bitbucket permissions endpoint as unknown, not blocking by @lnieuwenhuis in pingdotgg/t3code#6525
* fix(ssh): let cold remote servers finish starting by @gbarros-dev in pingdotgg/t3code#6168
* fix(web): preserve Claude insight line breaks by @nateEc in pingdotgg/t3code#4344
* feat(web): accept file drops across the chat workspace by @dbalders in pingdotgg/t3code#6636
* fix(web): widen ordered-list marker gutter for 3+ digit item numbers by @lnieuwenhuis in pingdotgg/t3code#6527
* fix(server): bound thread activity hydration by @t3-code[bot] in pingdotgg/t3code#6153
* fix(web): restore the Archive action in the default sidebar thread menu by @lnieuwenhuis in pingdotgg/t3code#6526
* fix(web): open diff files from nested projects by @gbarros-dev in pingdotgg/t3code#6174
* fix(mobile): use tryOpenExternalUrl for markdown links in ThreadFeed by @mohamedmastouri-hue in pingdotgg/t3code#5872
* fix(web): open the file a bare filename reference names by @Brechard in pingdotgg/t3code#6297
* fix(server): stop the provider title mirror from overwriting real thread titles by @repparw in pingdotgg/t3code#5941
* fix(shared): match source-control providers by DNS label by @gbarros-dev in pingdotgg/t3code#6175
* feat(desktop): Chrome-style hold-to-quit by @Bil0000 in pingdotgg/t3code#5508
* fix(gitlab): submit review comments on context lines by @tarik02 in pingdotgg/t3code#6348
* fix(marketing): keep Grok mark clear of mobile hero copy by @NicL9923 in pingdotgg/t3code#4542
* fix(mobile): recover the QR pairing scanner when camera access is denied by @hey-jj in pingdotgg/t3code#6487
* fix(web): keep a long path from running under the folder picker button by @Sy-D in pingdotgg/t3code#4823
* fix(terminal): right-click paste works in the terminal by @StiensWout in pingdotgg/t3code#5240
* fix(mobile): explain iOS-only settings on Android by @danvernon in pingdotgg/t3code#4981
* fix(web): stop counting a workflow coordinator as a working agent by @Rishet11 in pingdotgg/t3code#6672
* fix(web): keep floating preview anchored after panel closes by @AksharP5 in pingdotgg/t3code#6547
* fix(web): unstick /connect after in-modal sign-in by redirecting to the authorize endpoint by @TorbenWetter in pingdotgg/t3code#5133
* fix(web): keep send reachable while a turn is running on mobile by @AMohamedAakhil in pingdotgg/t3code#4781
* fix(web): reject unsupported composer image types at attach time by @mdshzb04 in pingdotgg/t3code#6574
* Make ClaudeTextGeneration tests hermetic on Windows by @mihneaptu in pingdotgg/t3code#4508
* fix(web): show command output in work log by @LikoKiko in pingdotgg/t3code#4083
* fix(web): reserve sibling column width when resizing the right panel by @Lasdw6 in pingdotgg/t3code#6279
* fix(web): replace whitespace in new ref names with dashes by @jorj-pineda in pingdotgg/t3code#6270
* fix(client-runtime): branch list no longer resets while paging through refs by @a20hek in pingdotgg/t3code#5858
* fix(web): support Shift+Insert terminal paste by @aoright in pingdotgg/t3code#5982
* fix(web): keep the composer glass aligned with the context strip at any interface font size by @Williawar in pingdotgg/t3code#5703
* fix(codex): keep background memory out of chats by @AksharP5 in pingdotgg/t3code#5468
* fix(server): treat a missing Codex rollout as a recoverable resume error by @Rishet11 in pingdotgg/t3code#6671
* fix(web): hide provider Update toast action while an update is running by @mrmg in pingdotgg/t3code#6544
* fix(desktop): agent shells inherit a UTF-8 locale on macOS by @Linus-Boehm in pingdotgg/t3code#6236
* fix(server): ignore Claude command lifecycle messages by @naveed949 in pingdotgg/t3code#6606
* docs: mention Bitbucket user read scope needed by auth probe by @thamrx in pingdotgg/t3code#6291
* fix(server): return valid preview action results by @duncan-vc in pingdotgg/t3code#5966
* fix(claude): make "Always allow for session" stick, and only for the session by @kakismash in pingdotgg/t3code#5041
* fix(ssh): surface a failed remote t3 install instead of a silent 0-byte server.log by @TorbenWetter in pingdotgg/t3code#5132
* perf(server): persist the wire projection for streaming tool.updated data by @mInrOz in pingdotgg/t3code#6675
* fix(web): stop wrapping partial code block selections in markdown fences by @JoeJoeflyn in pingdotgg/t3code#5069
* fix(web): hide T3 Connect toggle in web app settings by @JoeJoeflyn in pingdotgg/t3code#5068
* fix(web): show provider account accent badge in sidebar rows and hover card by @vitalyiegorov in pingdotgg/t3code#5980
* fix(server): wait for concurrent SQLite writers instead of failing with SQLITE_BUSY by @ostapondo in pingdotgg/t3code#5134
* fix(web): reject oversized prompts before provider turn start by @naveed949 in pingdotgg/t3code#6602
* feat(web): collapse the question prompt from its header by @Jardo-51 in pingdotgg/t3code#6773
* fix(shared): degrade an unknown system time zone to UTC in usage windows by @Rishet11 in pingdotgg/t3code#6670
* fix(claude): discover repo-local .agents/skills in skill discovery by @RoshanMhatre in pingdotgg/t3code#5488
* fix(server): let slow provider CLIs raise their discovery probe budget by @CDVolvik in pingdotgg/t3code#6223
* fix(web): retain terminal PR badges after checkout switch by @sebbonit in pingdotgg/t3code#4755
* fix(web): show selected model in context window tooltip by @nqrwhal in pingdotgg/t3code#4772
* fix(web): scale command details with code font by @Serendeep in pingdotgg/t3code#6510
* fix(web): preserve XML-like tags in user messages by @0utsights in pingdotgg/t3code#4133

## New Contributors
* @mohamedmastouri-hue made their first contribution in pingdotgg/t3code#5872
* @NicL9923 made their first contribution in pingdotgg/t3code#4542
* @hey-jj made their first contribution in pingdotgg/t3code#6487
* @danvernon made their first contribution in pingdotgg/t3code#4981
* @Rishet11 made their first contribution in pingdotgg/t3code#6672
* @AksharP5 made their first contribution in pingdotgg/t3code#6547
* @TorbenWetter made their first contribution in pingdotgg/t3code#5133
* @AMohamedAakhil made their first contribution in pingdotgg/t3code#4781
* @mdshzb04 made their first contribution in pingdotgg/t3code#6574
* @mihneaptu made their first contribution in pingdotgg/t3code#4508
* @LikoKiko made their first contribution in pingdotgg/t3code#4083
* @Lasdw6 made their first contribution in pingdotgg/t3code#6279
* @jorj-pineda made their first contribution in pingdotgg/t3code#6270
* @a20hek made their first contribution in pingdotgg/t3code#5858
* @aoright made their first contribution in pingdotgg/t3code#5982
* @Williawar made their first contribution in pingdotgg/t3code#5703
* @mrmg made their first contribution in pingdotgg/t3code#6544
* @Linus-Boehm made their first contribution in pingdotgg/t3code#6236
* @naveed949 made their first contribution in pingdotgg/t3code#6606
* @thamrx made their first contribution in pingdotgg/t3code#6291
* @duncan-vc made their first contribution in pingdotgg/t3code#5966
* @kakismash made their first contribution in pingdotgg/t3code#5041
* @mInrOz made their first contribution in pingdotgg/t3code#6675
* @JoeJoeflyn made their first contribution in pingdotgg/t3code#5069
* @vitalyiegorov made their first contribution in pingdotgg/t3code#5980
* @ostapondo made their first contribution in pingdotgg/t3code#5134
* @Jardo-51 made their first contribution in pingdotgg/t3code#6773
* @RoshanMhatre made their first contribution in pingdotgg/t3code#5488
* @CDVolvik made their first contribution in pingdotgg/t3code#6223
* @sebbonit made their first contribution in pingdotgg/t3code#4755
* @nqrwhal made their first contribution in pingdotgg/t3code#4772
* @Serendeep made their first contribution in pingdotgg/t3code#6510
* @0utsights made their first contribution in pingdotgg/t3code#4133

**Full Changelog**: pingdotgg/t3code@v0.0.34-nightly.20260815.1100...v0.0.34-nightly.20260815.1101

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.34-nightly.20260815.1101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provider notification consumers are interrupted when startSession returns (Cursor, Grok, Codex)

2 participants