Skip to content

Treat missing thread snapshots as terminal#4235

Open
Quicksaver wants to merge 17 commits into
pingdotgg:mainfrom
Quicksaver:fix/thread-not-found-subscription-loop
Open

Treat missing thread snapshots as terminal#4235
Quicksaver wants to merge 17 commits into
pingdotgg:mainfrom
Quicksaver:fix/thread-not-found-subscription-loop

Conversation

@Quicksaver

@Quicksaver Quicksaver commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Treat authoritative missing-thread snapshot responses as terminal client state instead of falling through to a repeatedly failing WebSocket subscription, including after session replacement or application-foreground resubscription. Transient HTTP failures still retain the existing socket-snapshot fallback.

Delay detail synchronization for known local drafts across every detail consumer until the server shell observes thread creation, preserving the legitimate pre-creation flow while preventing a missing-resource response from deleting the draft.

What Changed

  • Preserve decoded HTTP thread_not_found errors through the thread snapshot loader while mapping transient failures to socket fallback.
  • Clear persisted thread detail, mark the client state deleted, emit structured diagnostics, and terminate before opening or retrying the WebSocket subscription for an authoritative miss or later resubscription signal.
  • Share the local-draft detail gate between merged thread reads and the server-thread route so every known draft waits for its thread shell.
  • Add direct HTTP-loader, thread synchronization, and web hook lifecycle regression coverage.
  • Document the branch-owned synchronization contract in BRANCH_DETAILS.md.

Why

While investigating renderer memory growth from roughly 300 MB after launch to nearly 2 GB after extended use and idle time, stale thread details were observed being prewarmed after their server resources no longer existed. Each HTTP lookup returned authoritative thread_not_found, but the client treated that as a transient miss, opened a WebSocket subscription that also failed, and retried the cycle every 250 ms. Multiple stale subscriptions made this a concrete source of sustained allocation, retained state, and background CPU pressure, though it should not be assumed to explain every byte of renderer memory growth by itself.

The terminal handling stops that loop, evicts the stale persisted detail, and prevents later session or foreground events from reopening the socket. Because a brand-new local draft also legitimately has no server thread before its first send, every draft detail consumer now waits for the shell's creation signal so the fix does not turn that expected pre-creation 404 into a deleted draft.

Validation

  • 17 focused client-runtime tests passed across thread synchronization and HTTP snapshot loading.
  • 4 focused web entity-hook lifecycle tests passed.
  • Client-runtime typecheck passed.
  • Web typecheck passed.
  • Targeted formatting and lint checks passed.
  • Integrated Playwright verification confirmed that a local draft issued no pre-creation detail request, promoted on first send, then requested only its created server thread and completed the response.

Proof

No additional proof artifacts are required; the focused automated coverage and integrated browser flow exercise the affected lifecycle directly.


Note

Medium Risk
Changes core thread sync and deletion semantics across HTTP and WebSocket paths; incorrect 404 handling or draft gating could wrongly delete threads or block legitimate sync, though behavior is covered by new tests.

Overview
Authoritative HTTP thread_not_found responses now end thread-detail sync instead of being treated like transient misses that fall through to a WebSocket subscription and retry loop.

ThreadSnapshotLoader propagates EnvironmentResourceNotFoundError on 404 while other HTTP failures still map to socket fallback. The thread state machine marks the thread deleted, clears persisted detail, and interrupts before opening or resubscribing the WebSocket stream (including on application-foreground wakeups).

Local drafts no longer subscribe to server detail until a server shell exists: shouldSubscribeToThreadDetail, useThreadDetailWhenReady, and useThreadStatusWhenReady gate subscriptions so pre-creation 404s cannot delete a draft. The chat thread route and useThread use this gate.

Adds regression tests for the HTTP loader, sync lifecycle, and web hook gating, plus BRANCH_DETAILS.md documenting the contract.

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

Note

Treat missing thread snapshots as terminal by marking 404 responses as deleted

  • ThreadSnapshotLoader.load in threadSnapshotHttp.ts now propagates EnvironmentResourceNotFoundError (404 thread_not_found) as a failure instead of mapping it to Option.none.
  • threads.ts catches this error during the HTTP snapshot phase, marks the thread state as deleted, and interrupts the subscription — preventing socket subscription and any retry on wakeup.
  • New useThreadDetailWhenReady and useThreadStatusWhenReady hooks in entities.ts gate detail/status subscriptions for draft threads until a server shell exists.
  • Behavioral Change: deleted threads no longer initiate or resume socket subscriptions across session replacement or app-active wakeups; previously a 404 would fall through to socket subscription via Option.none.

Macroscope summarized 2d25d20.

- Preserve authoritative thread-not-found snapshot failures
- Mark missing threads deleted before opening socket subscriptions
- Cover terminal missing-thread handling with sync tests
- Document thread detail subscription reliability
- Delay draft detail loading until the server shell appears
- Cover draft and normal thread subscription decisions
- Document pre-creation draft synchronization behavior
- Verify decoded 404 and transient HTTP loader behavior
- Cover draft detail subscription activation through the hook
- Add structured metadata for terminal missing-thread cleanup
@coderabbitai

coderabbitai Bot commented Jul 21, 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: 271ef083-c92d-4e13-88a0-a294d5f02cde

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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:L 100-499 changed lines (additions + deletions). labels Jul 21, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect service review: one convention violation found. The new HTTP thread_not_found handling uses Effect.catchTag, but the conventions require Effect.catchTags for statically known tagged failures — including when handling a single tag.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/client-runtime/src/state/threads.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR changes subscription termination behavior for missing threads, which is a significant runtime behavior modification. Additionally, an unresolved review comment identifies a potential hydration race condition that could cause draft threads to be incorrectly marked as deleted.

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

Comment thread apps/web/src/state/entities.ts Outdated
Comment thread packages/client-runtime/src/state/threads.ts
- Gate direct route detail reads until local drafts have a shell
- Interrupt subscribe input creation once thread state is deleted
- Cover foreground resubscription and direct draft consumers
…d-subscription-loop

# Conflicts:
#	apps/web/src/routes/_chat.$environmentId.$threadId.tsx
#	apps/web/src/state/entities.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2d25d20. Configure here.

Comment thread apps/web/src/routes/_chat.$environmentId.$threadId.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 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.

1 participant