Skip to content

fix: host-authored session state for every client - #608

Merged
Tryanks merged 8 commits into
mainfrom
fix/host-authored-session-state
Oct 6, 2026
Merged

Tryanks merged 8 commits into
mainfrom
fix/host-authored-session-state

Conversation

@Tryanks

@Tryanks Tryanks commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Summary

Every fact a client shows about a thread is now computed once on the host and read by every client, so the desktop, a phone and the browser agree with each other and with what the host will accept.

 host
-  IndexSummary.activity: {id → (working, approval, input, background)}   # residents only
+  IndexSummary.activity: {id → SessionActivity}                           # every unarchived thread
+    working · turn_running · background_only · waiting_* · unread · fork
+  IndexSummary.worktree_shared · archived_revision
   SessionStatus
-    working · turn_running
+    activity · native_rewind_blocked · checkout_blocked · conversation_read_only
+    terminal_limit_reached · terminal_split_available · effective_approval_mode
+    native_approval_modes_enabled · usage (full fold) · context_window (meter capacity)
+    queued_messages[].editable
+  Topic::SessionPlan → SessionPlan { proposed, steps }                    # window-independent

 client store
-  selected row: re-derive flags from SessionStatus; other rows: host tuple
-  background_session_flags, status_background_only, text-equality send adoption
+  sidebar rows read only IndexSummary.activity (selected row included)
+  composer / chat read only SessionStatus + SessionPlan
+  send adoption by delivery_key / record id only

Each predicate has one owner on the host and is shared between command validation and the status projection: session_activity, session_fork_availability, native_rewind_blocked, checkout_blocked, queued_message_editable, terminal_*, session_approval_policy, WorktreeSharing. CheckoutBranch now gates on turn_in_flight (a provider that dies without closing its timeline turn no longer blocks checkout forever).

UI changes that fall out of this: the phone thread row shows Background (muted) distinct from Working; queue rows in delivery have their edit/drop/steer actions disabled; rewind conversation modes use the absolute turn index; the plan panel and Implement/Refine read the plan replica so a phone with a partial history window sees the same plan as the desktop; the archived page's permanent delete uses the host's worktree-sharing fact and the list reloads on archived_revision.

PROTOCOL_VERSION stays at 9 with an Unreleased note (Principle 9). Wire changes: SessionStatus.working/turn_running move into activity; archived_worktree_branches removed; ArchivedSessions response carries worktree_shared and revision.

Evidence

  • Before: 21 client-side derivations audited; confirmed disagreements included fork offered/refused differently from fork_thread, rewind enabled while the host rejected (background tasks), queue edit enabled on the in-flight delivery, orphan-worktree prompt wrong for archived cwd-only forks, phone row showing "Working" for background-only threads, and an acknowledged send retired by an unrelated same-text record.
    After: each has a host owner; negative controls (old checkout predicate, background_only without the delivery guard, text-equality adoption, pre-Ack adoption ignored, old scheduled send-now guard) made the new contract tests fail for the intended reason before the fix was restored.
  • Checks on the merged branch: cargo fmt --all --check, cargo clippy --workspace --all-targets --locked -- -D warnings, RUSTFLAGS=-D warnings cargo check -p tcode-web --target wasm32-unknown-unknown, iOS sim check, cargo machete; cargo nextest run --workspace --locked: 937 passed, 11 skipped. Android NDK not available locally.
  • Visual: phone (393×852) and desktop widths, light and dark: Background row muted vs Working blue; disabled queue actions; rewind sheet offering conversation modes at window-local turn 0; disabled branch button; host descriptor label in the traits chip; meter using host capacity; identical plan panel on phone and desktop with the plan absent from the loaded history.

Merge Danger

Door: two-way

Revert is a clean git revert of the merge; no data migration. Between releases the wire may change freely (Principle 9), so mixed-commit host/client pairs are not a supported state.

Blast Radius: protocol-wide

Every client surface that shows thread state (sidebar, composer, chat, plan panel, terminal drawer, archived page) now reads different fields. IndexSummary now carries an entry per unarchived thread and is re-sent whenever any of them changes; snapshot construction is one pass but larger indexes send more bytes per summary replacement.

@Tryanks
Tryanks merged commit d48b809 into main Oct 6, 2026
7 checks passed
@Tryanks
Tryanks deleted the fix/host-authored-session-state branch October 6, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant