Skip to content

fix(client-runtime): port turn-keyed settling — mobile/Windows stop flipping Running→Done→Running - #454

Merged
SergeSerb2 merged 4 commits into
mainfrom
surgecode/client-runtime-turn-keyed
Aug 3, 2026
Merged

fix(client-runtime): port turn-keyed settling — mobile/Windows stop flipping Running→Done→Running#454
SergeSerb2 merged 4 commits into
mainfrom
surgecode/client-runtime-turn-keyed

Conversation

@SergeSerb2

Copy link
Copy Markdown
Owner

Summary

#442 fixed turn settling on the server and mac; mobile and Windows consume packages/client-runtime directly and never got the fix — every session start/restart/steer/reconnect still flipped threads Running→Done→Running with a phantom completion push. Ported the same rule (mirrors apps/server/src/orchestration/projector.ts:46-105 exactly): non-terminal statuses settle nothing; thread.session-set settles from event.payload.settledTurn; a running session-set naming a settled turn re-opens it.

Also, while auditing the same subscription/command code paths:

  • Stuck command lane (finding 251): a stalled unary RPC on a wedged relay hop blocked every later command for that thread — including Stop and approval answers, the worst possible stuck state. Dispatch now carries a 30s timeout; Stop/approval/user-input got their own concurrency lane so they never queue behind a wedged send.
  • Zombie thread loop (finding 249): a deleted thread caused an unbounded ~4 req/s subscribe+snapshot retry loop with a bogus "Could not synchronize" banner. Exponential backoff (capped 30s, jittered) replaces the flat 250ms retry; "thread not found" is now classified terminal (settles as deleted, stops retrying).
  • Silently frozen stream (finding 250): a dead durable stream on a live socket kept serving frozen data labeled "live" with no indication. Transport failure now emits a synchronizing transition and resubscribes with backoff instead of draining silently.
  • Stale shell fields after resume (finding 247): autoReviewPhase/settledOverride/settledAt/scenery now come from the shell in mergeEnvironmentThread, so an event-only resume can't leave them frozen at cached values.
  • Finding 213 (revoked token) was re-checked and found already correctly handled on the current code path — no change, existing test cited in commit body.

Area

  • apps/mac
  • apps/windows — Windows desktop app
  • apps/mobile — iPhone companion app
  • apps/server
  • Shared packages (packages/client-runtime)
  • Build, CI, or release tooling
  • Docs

Release size

  • size:XS
  • size:S
  • size:M
  • size:L
  • size:XL

Verification

  • pnpm run verify --all after merging current main: 5/5 steps, exit 0
  • Full client-runtime suite (40 files / 484 tests) green; 27 related files re-verified in the scoped gate

🤖 Generated with Claude Code

SergeSerb2 and others added 4 commits August 2, 2026 16:45
…le/ready

The thread detail reducer folded every `idle`/`ready` session status into a
`completed` turn. Sessions bounce through `ready` mid-turn on start, restart,
steer, and reconnect, so mobile and Windows flipped Running -> Done -> Running
on each of those and pushed phantom completions.

Port the turn-keyed settling PR #442 gave the mac (see
apps/server/src/orchestration/projector.ts): non-terminal statuses settle
nothing, an explicit `settledTurn` marker settles exactly the turn it names,
a marker with `turnId: null` sweeps every still-running turn, and terminal
statuses (error/interrupted/stopped) still sweep on their own.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l lane

A dispatched command rode an unary RPC with no deadline, and thread commands
share one serial per-thread lane. A server that accepted the frame and never
answered therefore parked the lane forever: every later command for that
thread — including Stop and approval answers — queued behind it with no way
out short of a reconnect.

Two changes: dispatch now fails with a TimeoutError after 30s, releasing the
lane; and the commands whose whole point is to reach a busy thread (interrupt,
task stop, session stop, approval and user-input responses) run in their own
per-thread lane so they never queue behind an in-flight or wedged send.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ptions

Two failure modes of the same code path, so one change.

A subscription that failed for a reason the server would keep repeating —
a deleted or never-created thread — was retried after a flat 250ms, forever:
roughly four subscribe+snapshot requests a second against a thread that will
never exist, each one painting "Could not synchronize the thread." The retry
delay now doubles from 250ms up to a 30s cap with jitter (so a fleet
recovering from one incident does not resubscribe in lockstep), and a caller
can classify a failure as terminal. A thread the server reports as not found
is terminal: the state settles as deleted and stops resubscribing.

A durable stream that died with a transport error was logged and silently
drained to wait for the next session. When only that one multiplexed stream
died the session never changed, so the atom kept serving frozen data labelled
"live". Transport failures now run an `onTransportFailure` hook — thread and
shell state mark themselves stale — and resubscribe on the same session under
the same backoff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…shell

The shell/detail merge copied workspace metadata from the shell but left
autoReviewPhase, settledOverride, settledAt, and scenery on whatever the
detail carried. A detail that resumes from cache by events alone never
rewrites those fields, so a thread the shell already reports as settled (or
past its review phase, or rebranded) kept rendering the cached values until a
full snapshot arrived.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SergeSerb2 SergeSerb2 added the size:M Normal feature or meaningful behavior change label Aug 3, 2026
@SergeSerb2
SergeSerb2 merged commit 750f89c into main Aug 3, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M Normal feature or meaningful behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant