Skip to content

fix(client-runtime): report terminated thread loads - #10216

Merged
juliusmarminge merged 7 commits into
mainfrom
audit/thread-load-errors-9414
Sep 6, 2026
Merged

fix(client-runtime): report terminated thread loads#10216
juliusmarminge merged 7 commits into
mainfrom
audit/thread-load-errors-9414

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 5, 2026

Copy link
Copy Markdown
Member

Related to #9414 and #10206.

A thread snapshot load can terminate on a protocol or programming defect while shared state still says synchronizing with no error. Without cached detail, mobile keeps showing Loading messages.

Report defects from the complete subscription attempt, including input construction and initial snapshot loading, through the existing generic synchronization diagnostic. Preserve it through connection-only updates and buffered data from the failed attempt; confirmed deletion still wins. Clear it when a real attempt begins. Existing transport recovery, domain retries and fatal-defect behavior are unchanged. Raw decoder messages do not become mobile error copy.

Verification on 3ae0fad3:

  • The orchestrator reviewed all four changed files and independently passed all 56 focused RPC/thread-state/atom tests. Exact-main before controls fail 12 times and pass 44. The final loader test waits for the actual atom error; the preserved negative fixture observes the missing error without waiting indefinitely.
  • Scoped client-runtime typecheck, changed-file lint and formatting pass. Public Check, Test, server shards, Release Smoke and Rust pass. Effect Conventions cleared both production files. Correctness reviewed the preceding production-identical head; the final formatting-only test change was skipped. Both review findings are resolved.
  • Actual-client before/after uses latest main f12d3935, one disposable backend, synthetic history and disabled providers. A private wrapper changes only snapshotSequence for two test thread IDs. Real HTTP encoding fails, the loader falls back, and the real WebSocket encoder emits a request-scoped defect. The healthy control loads before and after the failure.

iOS before and after

The same retained iPhone simulator and compatible development binary load the exact two baseline modules, then the exact two candidate modules. Other retained patches are preserved. This is verification of the changed client path, not a full current-main native rebuild or the reporter's original phone payload. Both captures are after the matching HTTP and WebSocket failure receipts. The candidate shows generic error text instead of indefinite loading.

Before, main After, candidate
Before: iOS remains at Loading messages after the controlled load failed After: iOS shows Could not load conversation with a generic synchronization error

Web scope check

Web and desktop do not consume this shared error for their loading presentation. The isolated browser confirms web still shows loading on both revisions. This PR does not claim to fix that UI or cached-mobile error presentation.

Before, main After, unchanged web presentation
Before: web remains at Loading messages after failure After: web still shows Loading messages and is not claimed fixed

The controlled error-state lifecycle is verified. The original phone's unknown payload and attachment codec remain unverified, so this PR does not automatically close that report. No real provider calls, live database writes, schema changes or native rebuilds were needed.

Prepared by GPT 6 Astra via Codex in T3 Code.

Note

Preserve terminated thread load diagnostics in makeEnvironmentThreadState

  • Thread state now keeps an existing error across synchronizing and ready connection notifications instead of clearing it, so terminated loads retain their diagnostic during connection updates.
  • Defect causes (die or RpcClientDefect) report a generic "Could not synchronize the thread." message via a new onDefect callback on SubscriptionOptions, while ordinary domain failures keep their formatted cause message.
  • Buffered thread values arriving after a failed attempt are applied with cached status, and the diagnostic is cleared only when an actual subscription retry begins (e.g., foreground resubscription), not on connection notifications alone.
  • In subscribeDynamicMapped, cause observation and recovery handling move outside the session stream so makeInput failures are also covered by onDefect.
  • Risk: callers relying on setStreamError to clear errors on any connection transition will see diagnostics persist longer; verify consumers of threads-atoms state read the error field rather than assuming a synchronizing transition clears it.
  • Adds extensive parameterized test coverage in client.ts and threads-atoms.test.ts for snapshot-loader defects, protocol/fatal/transport/domain failures, foreground retries, and buffered outcomes.

Macroscope summarized 3ae0fad.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.5 KiB 13.5 KiB +25 B (+0.2%) 15.1 KiB
Codex Thread snapshot wire 7.0 KiB 7.0 KiB +7 B (+0.1%) 7.3 KiB
Codex Live turn WebSocket wire 6.5 KiB 6.5 KiB +18 B (+0.3%) 7.8 KiB
Codex Live turn WebSocket decoded 57.0 KiB 57.0 KiB 0 B (0.0%) 66.4 KiB
Codex Live turn messages 8 8 0 (0.0%) 21
Claude Total thread wire 13.5 KiB 13.5 KiB −3 B (−0.0%) 15.1 KiB
Claude Thread snapshot wire 7.0 KiB 7.0 KiB +4 B (+0.1%) 7.3 KiB
Claude Live turn WebSocket wire 6.5 KiB 6.5 KiB −7 B (−0.1%) 7.8 KiB
Claude Live turn WebSocket decoded 57.8 KiB 57.8 KiB −44 B (−0.1%) 66.4 KiB
Claude Live turn messages 9 8 −1 (−11.1%) 21

Baseline: 585ce2c · PR result: 3ae0fad · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment thread packages/client-runtime/src/state/threads.ts Outdated
Comment thread packages/client-runtime/src/rpc/client.ts Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review September 6, 2026 00:10
@macroscopeapp

macroscopeapp Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 3ae0fad

Macroscope's review found this PR approvable — The PR is a focused client-runtime bug fix that reports terminated thread loads without changing transport recovery or ordinary domain retries. Its production changes are confined to thread synchronization/error state, with extensive regression coverage for defects, retries, buffered data, and deletion.

Notes:

  • No code objects were reviewed. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant