Skip to content

fix(sync): stop retrying permanently missing threads#4222

Open
StiensWout wants to merge 7 commits into
pingdotgg:mainfrom
StiensWout:fix/missing-thread-hydration-loop
Open

fix(sync): stop retrying permanently missing threads#4222
StiensWout wants to merge 7 commits into
pingdotgg:mainfrom
StiensWout:fix/missing-thread-hydration-loop

Conversation

@StiensWout

@StiensWout StiensWout commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • model genuinely missing thread subscriptions with a dedicated typed error
  • use synchronized shell membership to make permanent shell absence terminal
  • remove stale cached detail and stop retry/foreground resubscription after deletion

Root cause

Thread subscription failures were all retried every 250 ms. A permanent not-found failure therefore rebuilt subscription input forever, repeatedly running cold HTTP hydration, while deleted state did not stop later resubscriptions.

Impact

Stale references that receive a typed not-found response and are absent from the authoritative shell now settle in the existing unavailable/deleted state after one cold HTTP attempt and one WebSocket attempt. Shell-present and shell-unknown failures retain the existing retry behavior so creation/projection races can still recover; changing or bounding that retry policy is outside this fix.

Validation

  • vp test run packages/client-runtime/src/rpc/client.test.ts packages/client-runtime/src/state/threads-sync.test.ts packages/client-runtime/src/state/shell-sync.test.ts packages/client-runtime/src/state/shellMembership.test.ts (31 tests passed)
  • vp test run apps/server/src/server.test.ts -t "tags a missing.*thread subscription as not found" (2 focused tests passed)
  • targeted typechecks passed for contracts, client runtime, server, web, and mobile
  • targeted formatter, linter, and git diff --check passed
  • web/desktop integrated verification: after the shell settled, a fresh shell-absent thread ID produced exactly one HTTP snapshot request and one subscribeThread request, with no repeats over more than two seconds
  • representative mobile integrated verification was unavailable on the Windows host because no Android SDK/ADB was installed and iOS Simulator is not available on Windows

Refs #2761

Preserves the creation-race recovery reported in #4127.


Note

Medium Risk
Changes real-time sync and WebSocket error contracts for thread subscriptions; behavior is heavily tested but affects orchestration client/server coordination and deleted-thread UX.

Overview
Stops infinite 250ms retries when a thread is permanently missing by introducing OrchestrationThreadNotFoundError and wiring it through subscribeThread (cold and resumed paths). Resumed subscriptions now use hasThreadById so archived threads still exist; missing threads get the typed error instead of a generic snapshot failure.

On the client, EnvironmentShellMembership tracks whether a thread is unknown, present, or absent from the authoritative shell snapshot, with revision guards so stale shell writes cannot restore authority after disconnect. Thread sync treats not-found as terminal when membership is absent (clears cache, stops subscribing); unknown or present keeps the existing retry path for projection/creation races.

Shell sync adopts subscribeDynamicWithContext so membership revisions stay bound to the subscription session; web/mobile runtimes include environmentShellMembershipLayer. RPC client gains the shared subscribeDynamicStream helper plus subscribeDynamicWithContext.

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

Note

Stop retrying permanently missing thread subscriptions by marking them deleted via shell membership

  • Adds OrchestrationThreadNotFoundError to the subscribeThread WS RPC, returned when a thread is missing on initial load or when hasThreadById returns false for resumed subscriptions.
  • Introduces EnvironmentShellMembership service to track per-environment thread membership (unknown/present/absent) with revision-guarded updates.
  • Thread sync now marks a thread as deleted and stops retrying when shell membership is absent; missing-thread errors are retried only when membership is unknown or present.
  • Adds hasThreadById to ProjectionSnapshotQuery to check non-deleted thread existence (including archived) without loading full thread details.
  • Adds subscribeDynamicWithContext to the RPC client to propagate per-session context (membership revision) alongside stream values across session switches.

Macroscope summarized 1ce1c25.

@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

Run ID: f84a52a3-22cf-4e78-b71a-a5d5a44c04e9

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 size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Jul 21, 2026
Comment thread packages/client-runtime/src/state/shell.ts

@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 Conventions — 1 issue found.

The new reason: "not-found" discriminator on OrchestrationGetSnapshotError is used to select caller control flow, which the conventions say should be modeled as a distinct error class rather than a single-value literal on an existing tag. See the inline comment.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/orchestration.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new infrastructure (EnvironmentShellMembership service, hasThreadById query, OrchestrationThreadNotFoundError) and changes sync subscription retry behavior across client and server. While framed as a fix, it's functionally a new feature that affects runtime behavior of the sync system.

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

Comment thread packages/client-runtime/src/state/shell.ts Outdated
Comment thread packages/client-runtime/src/state/shell.ts Outdated
Comment thread packages/client-runtime/src/state/threads.ts
Comment thread packages/client-runtime/src/state/shell.ts
Comment thread apps/server/src/ws.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 2 potential issues.

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 d479716. Configure here.

Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/server/src/ws.ts Outdated
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