Skip to content

Open chats move to the account you switch to, before their next message - #30955

Merged
brennanb2025 merged 51 commits into
mainfrom
brennanb2025/chat-account-switch
Oct 31, 2026
Merged

brennanb2025 merged 51 commits into
mainfrom
brennanb2025/chat-account-switch

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Switching Claude accounts changed new chats, but an existing native chat could keep using the previous account. This change makes a chat's next eligible ordinary message use the selected account while continuing the same conversation. A known account still receives the exact worker-result response owed to it before a later ordinary message can move. A chat whose selected account is still being set up now waits for that setup or shows the existing account error on that message, instead of quietly answering from the previous account.

Tracked in STA-11211 and STA-11281.

Behavior and compatibility

  • The host running the chat resolves the selected Claude account, including switches from Settings, the status bar, CLI and paired clients. Older hosts keep their supported saved-account launch path; no new wire field or minimum server version is introduced.
  • Active replies and protected work finish first. A known actual account remains responsible for its exact pending native result response through process replacement or graceful host restart. Completing or otherwise canonically disposing that exact response ends the obligation; accepting or acknowledging a result alone does not. The next eligible ordinary Claude message then follows the current selection.
  • The comparison that decides whether a running chat must move uses the selected account's identity, not whether its folder exists yet. If the selected account's folder is still being created, has failed to set up, or was removed, an ordinary message no longer continues on the previous account: the running process is stopped once the chat has no protected work, the start waits for the account's setup, and if setup fails that message fails with the existing account-setup error. A later message after the account is repaired starts on it. A chat whose own selected account folder disappears keeps its working process. Model lists, new-chat creation and command discovery keep their existing availability checks, so a missing folder still reads as unknown there rather than as another account.
  • Only an older chat whose receiving process is gone and whose actual acquired account was genuinely never recorded can recover through the selected account. This does not claim to recover the original account, or turn a known account into an unknown one.
  • If the selected account cannot read a known Claude history, the existing refusal asks the user to switch back. Returning to the original account continues the same conversation, including a deferred rewind/fork.
  • Session History details show the account a Claude chat was last launched under when the host recorded it, and the chat's founding account otherwise. The chat's original saved account remains its creation/replay identity; nothing new is saved and no transcript, search or resume identity changes.
  • Codex and other agents that pin a conversation to its starting account remain pinned. Codex follow behavior is separate from this PR, after STA-11187; OpenCode movement is tracked in STA-11769.
  • Terminal restart behavior remains separate, as in feat(accounts): offer to restart running Claude sessions on the new account after a switch #29898. Ask / Restart / Keep running and the account-switch toast now explicitly describe terminals. There is no new chat indicator, automatic quota transfer, or provider-login change.

The implementation uses the same receiving-account decision for existing-process replacement and new-process construction. A successful launch records its actual account privately on the host; original conversation identity and saved model, effort and permission intent remain separate. Account changes join the existing retirement reasons under the existing work/wake protections from #34031, incorporating #30914 without a second account guard. An unchanged delivery attempt parks until legitimate work wakes it; a failed start settles its message once and does not retry on its own. The account resolver carries an explicit purpose: the replacement comparison and the start's pre-read ask for the selected identity, while catalog, create and probe reads keep the directory check. This follows the common pattern for applying a turn's selected environment and failing that turn when it cannot be prepared, adapted to Orca's host-managed account folders and owed worker results.

Model catalogs use the actual live account, or that same receiving policy at rest, with the existing host/folder/invocation scope. Ordinary literal choices do not depend on asynchronous catalog discovery. Unknown models remain unknown rather than becoming a guessed default. Older clients retain their command, no-choice observation and replay paths. Related catalog work: #30107.

Review and verification

Review covered running-chat replacement, account preparation, pending worker results, restart and recovery, older clients, and Session History. An independent comparison with the common pattern, performance checks and the readiness checklist found no remaining blocking defect. The final account-routing fix is 46a325f; later test and fixture repairs (7ea925b, 9ec20b2, bb726c9, e0659ca4) preserve the shipped behavior, so the shipping code reviewed and run under QA is the code in bb726c9. Two main merges follow. 8960978 merges main 1f504e95; its only conflict was the worker-listing file, resolved by keeping main's owner-routed worktree visibility together with this branch's account-aware chat listing readers, with one unchanged three-line repository-list method moved to the file's existing dependency base as main itself had done for a sibling method. 8d1ba5dd merges main 2cc33b8d; its two conflicts were in files main had reorganized. In the journal database, the writable-open step now runs main's new outcome-table setup and this branch's successor-index creation one after the other, after the command-receipts table, so neither side's startup work is lost. Main split the at-rest RPC test suite into four smaller suites; every original case still runs exactly once across them, this branch's three no-choice replay cases are kept verbatim in the first suite, and the shared request helper's optional client parameter moved with the helper into main's new test fixture. One list-of-loaded-chats member that both sides had added identically was kept once. No chat, account, history or journal-reading mechanism file changed in either merge, but main 2cc33b8d also replaced the journal's incremental change feed: after a chat's first reopened write, each later change is read back as a keyed outcome record naming the touched entries instead of a typed row. CI on 8d1ba5dd caught that this branch's receiving-account index still understood only typed rows (six receiving-account suites failed, with the matching type error) and one Mobile test mock out of step with main. 20786375 adapts the index to the new feed: the in-process read row now also names touched queue cards and repeats the keys a sequence deleted; the index re-reads each touched queue card, submission and item at its current value, retires a notice whose carrying send a rewind removed, and rebuilds its recovery view only when an item was deleted, so Stop marks are still excluded. Three production files changed (+75/−28) with no saved field, schema, wire operation or new guard, and two added cases pin the new row facts and the rewind retirement. Sixteen named suites (168 cases, the six failing ones among them) passed on the committed bytes; nine named suites (149 cases) had passed on the merge itself, and the two hand-off cases that now also exist in main's split suites remain as a duplicate to tidy later.

Differences from the common pattern

Each is labelled; the comparison was made against the references as whole mechanisms, not by name.

  • Intended. Account changes join the existing reasons for replacing a process under the unchanged no-open-work and no-wake-debt gate. Separately, the known actual account remains responsible for its exact pending worker-result response; there is no blanket worker-mail hold on all retirement reasons. The common pattern applies a new turn's environment directly. Reason: Orca's exact result obligation and restart requirement (Keep Claude approval choices across model changes without freezing delivery #34031; Brennan's 10-27 ruling on known and historical accounts).
  • Intended. Account preparation runs inside the existing start path, outside the comparison, while the common pattern constructs the new session at the comparison point. Reason: Orca's managed account folders are set up asynchronously for the whole host, and joining that setup inside the serialized comparison would hold sends and Stop.
  • Intended. No in-chat note is shown when a chat moves accounts; the toast describes terminals only. Reason: the binding rulings that chat status comes from its latest turn and that the account-switch toast and policy describe terminals (the account-switch product ruling).
  • Intended. Model lists, new-chat creation, prewarm and command discovery keep the folder availability check, while the replacement comparison and start pre-read use the selected identity. Reason: the accepted catalog ruling that a missing selected folder reads as unknown and must not start probes under an unprepared folder.
  • Intended (Orca-only surface). Session History shows the account a chat was last launched under; the references have no per-row account display. Reason: the approved History account section design, with the founding account as the fallback.
  • Temporary. Custom Claude folders that cannot see shared history still require switching back: STA-11300. Resting Claude slash commands still use the saved historical account: STA-11298.
  • Out of scope, not differences: Codex following the selection (STA-11187) and OpenCode movement (STA-11769).

Named test runs through the test gate on the committed bytes: the production change ran fifteen suites, 194 cases (six of them failing before the fix and passing after), including host-level tests that drive the real delivery loop, a real account router on disk and real account preparation: a live chat on A with B still setting up stops A, fails each message once if setup fails, parks, and starts the next message on B once ready; a chat whose own folder vanished keeps its process; a resting chat starts through preparation instead of a read refusal. The History projection has an operation-count contract at 10 and 100 owned rows (one roster read per projection, one record lookup per owned row). Subsequent test-only commits ran their named suites (56, 24, 77 and 68 cases). Scripted external IO in tests does not claim live-provider results; counts establish bounds on measured axes, not platform latency.

CI on e0659ca4 passed: PR checks, mobile, headless and WSL (the WSL Fedora job passed on the workflow's own one-time retry after a hosted runner froze). CI on the merge commit 8960978: PASS: PR checks, mobile, headless, and WSL. The resulting-head watch exited 0; all three WSL distributions passed. CI on the merge commit 8d1ba5dd failed in the six receiving-account suites and in Mobile verify for the reasons described above; that failure is what 20786375 repairs. CI on the repaired head 20786375: PASS: PR checks, mobile, headless and WSL. The single resulting-head watch exited 0; static analysis and typecheck, all twelve unit shards, the cross-version checks, Mobile verify and all three WSL distributions passed. Earlier heads' CI runs are not represented as a later head's result.

Completed runtime and visual evidence below carries within its original scope. The old-native lifecycle row and the account-setup slice ran on a host built from bb726c9, whose shipping code equals e0659ca4 (test-only commits in between). The 8960978 and 8d1ba5dd main merges changed no account or lifecycle mechanism file, but 20786375 changes how the receiving-account index consumes the journal's change feed, which is the code behind the live owed-result disposition those captures prove. That one flow was therefore run again on a host built from 20786375; its results and limits are in "Targeted run on the repaired head" below. Every other flow's mechanism is untouched by the repair and its evidence carries. The earlier lifecycle captures used source 92d9 with retained shipping 506dc desktop/CLI outputs; appearance and older historical captures retain their separately stated heads. The later production change touches only the account comparison and start pre-read for a selected account whose folder is unavailable, and the History account display; it does not change the toast copy, settings, history refusal or owed-result flows those captures prove. Only a changed or proven-broken run-row flow needs another targeted run. Historical images remain explicitly labeled.

Targeted run on the repaired head

Because 20786375 changes how a chat's receiving-account index reads the journal's change feed, the one flow that code decides, the owed worker-result response and the next ordinary message, was run again on a host built from 20786375 after CI passed, with the same synthetic folder accounts and real load-balancer turns. Each capture is the whole window at its original size.

  • Claude: known account A keeps its owed result, the next message follows B. With A active, a parent chat printed its own account folder (A) and created one supervised native child that answered READY and left its task pending. B was then selected. A message sent while the result was still pending still answered from folder A. The child reported its unique result through the real worker_done; the parent woke on the notice with no further input, printed folder A before the consuming check that returned exactly that result with the matching Task and Dispatch, and completed. The next ordinary message printed folder B and recalled the original marker. On the unrepaired merge that last message would have stayed on A, because the index never advanced on an incremental read.
Automatic result turn on A Next ordinary message on B
Claude automatic turn: folder A printed before the check that returned the exact result Claude ordinary message: folder B and the recalled marker
  • Codex stays pinned. The same lifecycle on a Codex parent: the founding turn, the message sent while the result was pending, the automatic result turn (own home printed before the check that returned the exact result with matching Task and Dispatch) and the ordinary message all printed the same pinned Codex home. Disclosed: the ordinary reply gave the requested nonce but omitted the remembered marker. The session, home and result were unchanged, the earlier reply in the same session had recalled the marker, and a later direct question on the same chat, after the host had been restored (below), answered with the exact marker without being given it; that reply is model output, not a routing change. The child's temporary tab was kept open by the test operator, a supported action, so the worker ended retained instead of auto-released.
Automatic result turn, pinned home Ordinary message, pinned home Later context question, restored chat
Codex automatic turn: pinned home printed before the check that returned the exact result Codex ordinary message: pinned home and the nonce, marker omitted Codex answers the exact remembered marker on the restored chat
  • Queue-card withdrawal attempt (Claude). The run tried to park the result notice as a queued card by keeping a second Claude parent busy on a 150-second tool while its child reported. The agent ran the long command as a background task and ended its turn after 8 seconds, so the notice arrived as a new automatic turn instead: folder A was printed before the check that returned the exact result, the background command later finished on its own, and the ordinary message printed folder B with both markers. No queued card and no Delete control appeared, so withdrawal was not exercised live. It remains covered by the production tests that withdraw a pointer card against the real journal feed (structured-agent-session-receiving-account-queue.test.ts, agent-session-history-batches.test.ts); the two captures below are labelled as that fallback, not as a card.
Fallback: automatic turn on A, no card Fallback: ordinary message on B
Claude fallback: automatic turn printed folder A before the check; no queued card Claude fallback: ordinary message printed folder B with both markers

Qualifications for this run: the QA host application on the shared machine was lost twice between the Codex run and the queue-card attempt, during shared M4Air events (factory investigating) that affected many sessions; the final stage ran on a fresh launch of the same build and data after the installed preflight passed, so the Codex context question ran on a restored chat. The final stop was graceful with no surviving owned processes and the run's port released; a neighbouring listener on the machine was not touched. The machine's real Claude configuration file hash changed twice during the day (bytes only, contents not read, cause not established) and was unchanged across the final stage and its cleanup. Delivery ids, acknowledgment times, consumer generation and turn ids are not exposed by the read-only surfaces used; ordering comes from the expanded tool lists in the originals and the read-only inbox, where the consuming check marks the returned message handled.

Old-native lifecycle row — completed on this build, with one disclosed gap

The exact older native app 0.0.51 (its own old JS, signed for the simulator) was paired to the final-head desktop host through the normal pairing screen; the pairing token survived a full app restart without re-pairing. On the phone, /clear in a Claude chat and in a Codex chat each showed "Context cleared" and the next message ran a real tool in the chat's own account folder and replied with its marker exactly once; on the desktop the post-clear conversation is separate from the founding history entry. After one completed-turn graceful host restart (same homes and data, no surviving processes), the phone reconnected to the same chats and transcripts. Codex model, effort and permission choices matched the pre-restart capture; Claude model and permission matched. Claude effort medium was saved afterward, but its pre-restart provenance was not established. Both agents then answered post-restart contextual messages from the phone: they recalled the marker from before the restart, ran a real tool in their own folder and replied with the requested text.

On an unsent Claude chat with the Claude command temporarily pointed at a nonexistent path, the phone's picker offered "Model not reported"; tapping it was accepted as a no-choice operation (empty result, nothing written: the chat's record still held only its permission choice), the command and arguments were restored exactly, and after a graceful restart the chat still had no model or effort recorded and no provider handle. A later send into that chat by mistake, after those captures, is the reason its record now carries a model; the captures above predate it.

Second message during startup: in all five phone attempts (two agents, non-conflicting prompts) both messages were accepted once, delivered in order once, both dispatches accepted, and the agents replied; but the host handed the first message to the agent within half a second while typing the second on the phone took three to six seconds, so no attempt landed while the chat was still starting. That ordering is covered by production-loop tests (a second send accepted while the first starts the child is held and handed over after it, with its own captured choices). The one Codex attempt that mentioned a conflicting "reply with exactly" instruction, and one retry whose visible reply omitted one requested word, occurred on accepted deliveries. The non-conflicting first attempt produced both requested replies; the retry’s rendering boundary was not exported, so the omitted word is qualified as model output rather than evidence of a host drop.

Phone /clear — Claude before Phone /clear — Claude completed Phone /clear — Codex reply
old-clear-claude-before.png old-clear-claude-completed.png old-clear-codex-reply-one.png
Desktop before restart — Claude Desktop after restart — Claude
d4-claude-restart-before.png d5b-claude-restart-after.png
Desktop before restart — Codex Desktop after restart — Codex
d4-codex-restart-before.png d5b-codex-restart-after.png

nochoice-claude-picker.png

nochoice-restored-after-restart.png overlap-claude-attempt1.png

overlap-codex-attempt2.png

Account-setup slice — completed

In a separate retained QA home with two managed Claude accounts: a fresh chat on System default ran a real tool and printed the System default folder (ACCT-A). The managed account B was then selected through Settings with no send, and B's profile folder was moved aside. The next ordinary message started on B: the shipped preparation recreated the folder, the tool printed B's profile folder, the reply was ACCT-B1, and nothing came from the previous account and no error appeared. The original B folder was then restored identical (the recreated folder was set aside, not deleted) and the next message again printed B's folder and replied ACCT-B2. The chat's founding account stays System default in its record, as designed; three sends, two provider handles. Account labels are synthetic folder-routing identities with real provider turns through the load balancers; no OAuth, principal or quota claim.

d10a-a-reply.png d10b-b1-result.png d10c-b2-result.png

For Brennan’s review

Completed settings and actual toast appearance

On 506dc, the French, Spanish, Japanese, Korean and Simplified Chinese terminal-policy title/description/Ask control and Claude search/navigation checks passed. These are full-window rendered captures; search shows the filtered Accounts category and Claude policy content. Navigation events are documented by the driver, rather than inferred from a still image. No account-switch toast or provider/lifecycle result is shown here.

Locale Terminal policy Claude search
French French terminal policy French Claude search
Spanish Spanish terminal policy Spanish Claude search
Japanese Japanese terminal policy Japanese Claude search
Korean Korean terminal policy Korean Claude search
Simplified Chinese Chinese terminal policy Chinese Claude search

Actual terminal account-switch toasts

On 92d9, all six full-window actual toasts passed visual inspection: readable localized title/body, actual A/B email interpolation, terminal scope, and no clipping or unresolved placeholders. These captures prove appearance only.

Locale Actual toast
English English terminal account-switch toast
French French terminal account-switch toast
Spanish Spanish terminal account-switch toast
Japanese Japanese terminal account-switch toast
Korean Korean terminal account-switch toast
Simplified Chinese Simplified Chinese terminal account-switch toast

Live and known-account restart results

Claude and Codex completed the live receiving-result flow and a distinct later-result flow across graceful host loss. Original task, dispatch and current recipient remained linked; actual home tools ran before result check/ack, and the exact automatic provider response completed successfully. Known Claude A handled the automatic response, then the first eligible ordinary contextual reply used selected B. Codex used its pinned home for both. Independent record audits and parent inspection of all relevant original whole-window images support these narrow results.

Live result and next ordinary reply — Claude and Codex

receiving-auto-A-claude-home-before-check.png

receiving-ordinary-claude.png

receiving-auto-A-codex.png

receiving-ordinary-codex.png

Later result after graceful restart, then ordinary reply — Claude and Codex

receiving-later-auto-claude.png

receiving-later-auto-claude-home-before-check.png

receiving-later-ordinary-claude.png

receiving-later-auto-codex.png

receiving-later-auto-codex-home-readable.png

receiving-later-ordinary-codex.png

Genuine older deferred history and materialized custom-home recovery

The older producer created two real contextual turns, rewound the second user row and stopped gracefully before any subsequent send. After the same-data upgrade, selected B received the history refusal without losing the deferred source/rewind. Restoring A forked from the genuine source and completed a contextual reply. A later ordinary refusal on the now-materialized target, followed by restoring A, resumed that same target and context. These are separate source-fork and materialized-target checks.

Deferred source history: refusal Same conversation: source-fork recovery
fi1-history-refusal.png fi1-source-recovery-reply.png
Materialized target: refusal Same target: contextual recovery
c1-history-refusal.png c1-source-recovery-reply.png

The original source remained unchanged. The target grew 952 bytes at ordinary retirement and 954 bytes across shutdown; causes and contents are unknown. An older receipt sentence claiming unchanged target stats is incorrect and is not used as evidence. No target-stat equality, captured runtime call stack, or unobserved provider-process identity is claimed.

Genuinely unknown historical receiving account

A real older cohort naturally lacked a saved Claude acquisition account; no newer receipt was deleted or fabricated. After graceful owner loss before a distinct later result, the same original child/task and recipient resumed. Claude’s automatic response and subsequent ordinary contextual reply used selected B; Codex’s known pinned home stayed A. This is the narrow historical exception, not recovery of A. Claude’s owner had already been gracefully lost at an earlier checkpoint, rather than being live immediately before the later stop.

Historical pending state and selected account

receiving-historical-selected-claude.png

receiving-historical-selected-codex.png

account-selected-b.png

Claude automatic selected B Codex automatic pinned A
receiving-historical-auto-claude.png receiving-historical-auto-codex.png
Claude ordinary contextual reply Codex ordinary contextual reply
receiving-historical-ordinary-claude.png receiving-historical-ordinary-codex.png

Pinned-main comparison: known account, graceful loss, distinct later result

Independent data created on pinned main 1dec1ebc35c0cfea84189345f77c325287c11d73 established actual A before selection of B and graceful owner loss. The original child’s distinct later result automatically resumed the original recipient. Main used B before result check/ack and completed that exact response; its next ordinary contextual reply also used B. The branch instead reacquired known A for the exact automatic response, then used B for the next eligible ordinary reply. Both ordinary replies recalled their original context without the remembered marker being supplied again. This compares the measured pinned builds, not the newest main or a Codex-main run.

Pinned main: automatic B Branch: automatic known A
main-reacquisition-auto-claude.png receiving-later-auto-claude-home-before-check.png

main-auto-result-terminal.png

Pinned main: ordinary B with context Branch: ordinary B with context
main-reacquisition-ordinary-claude.png receiving-later-ordinary-claude.png

Judgment locations

These three links are verified on the repaired head 20786375 (the replacement and History files are byte-identical to the reviewed e0659ca4; the disposition file carries the 20786375 change-feed adaptation elsewhere in the same file, and the linked line is unchanged).

Evidence qualifications and follow-ups

  • Account labels are synthetic owned folder-routing identities; provider turns are real requests through the existing load balancers. They establish neither distinct provider principals nor OAuth, subscription-plan, quota or provider-login failure behavior. Recorded authentication output supplies a test contract; synthetic controls remain tests. A genuine terminal quota event was not captured/exercised. No forced exhaustion or fake runtime pass is claimed.
  • Direct child-composer actions genuinely produced user takeover and retained children (user_owned / retained / processAction:none). They do not prove exclusive parent custody, worker termination or automatic reclamation. No assisting parent input completed the automatic responses. Claude’s two disclosed tokens in one worker result are one completion, not two. Natural Codex waits, the corrected observational decoder, and the old Codex Resume clarification before founding success remain disclosed; delay causes are unknown.
  • Canonical notice, accepted provider alias, exact turn and terminal-success joins establish completion; acknowledgment, an empty mailbox, screenshot prose or driver exit0 alone do not. Images establish visible state; records establish hidden lineage and exact disposition. Some original raw state/driver traces were intentionally excluded from safe exports, so exported summaries and containment receipts were not an independent replay of every underlying command. Original full-window images were inspected during the review.
  • Containment qualifications remain: generated driver sessions differed from generic brief wording; some identity timestamps were reused within the accepted freshness interval; the first main phase combined one account click/read under one guard; a supplemental visual phase omitted the requested command prefix and initially wrote own checkout screenshot-tool artifacts outside its narrower phase folder. The ordinary supplemental image was byte-identical to the already complete original, adding no evidence. Corrected scanners/projections and original mistakes remain preserved. These are disclosed rig limits, not policy waivers or invented product failures.
  • Graceful cleanup/config-hash/protected-artifact receipts describe the recorded boundaries, not a fresh liveness survey. Some config hashes changed at earlier checkpoints; contents and causes were not inspected. Final restore preserved the reviewed source/output distinction and accepted cohorts. Existing shared dependency-store references were retained, not rebuilt into a self-contained bundle or copied between checkouts. Its bounded audit rehashed 34 local files and verified five large inventories by named-file metadata only; it did not independently reconstruct full live dependency inventories. Auxiliary output continuity rests on the reviewed renames, not an invented historical full hash baseline. The final cleanup receipt confirms all 150 recorded owned process tuples stopped, the owned simulator shut down, and both owned remote terminals closed; the unrelated listener was left untouched.
  • Custom folders that cannot see shared Claude history still require switching back; sharing those folders is tracked in STA-11300. Resting Claude slash commands retain their historical-account behavior: STA-11298.
  • A new chat created while the selected account's folder is unavailable still shows the existing account-folder error; that behavior predates this PR and is unchanged here.
  • QA-rig notes: the phone's typing latency is why the startup instant was not reached by hand; the QA machine's Codex configuration file hash changed during the run (hash-only observation, cause not established by the cleanup receipt; Claude files unchanged); all owned processes were stopped gracefully and nothing was deleted.
  • Deferred performance work: on a total history lookup miss, the saved-home subtree can be traversed twice. Existing counted contracts record that finite unnecessary work; no measured latency or responsiveness claim is made. Deferred future-reader concern: optional internal saved-catalog dependencies remain a future-caller concern; installed current callers are covered, with no current failing input demonstrated. Both retain their terminal deferred dispositions.
  • An inherited already-handed-over message that ends without a reply after update/restart is excluded. This PR adds no permanent hold for it and makes no new indicator, automatic quota or Codex-follow claim.
Retained historical QA and before/after screenshots — not current-head lifecycle certification

Real Claude/Codex runs at d95126414d5b against main e01a5392d42d, followed by a narrow run at a9d0cba264ff, established ordinary Claude A→B→A/context, busy reply completion, normal custom-home refusal and same-chat recovery, model picker, terminal offer/Keep and pinned Codex. Those unaffected subflows carry within their recorded limits; later shared receiving changes are now covered by the live and known-account restart checks above; the genuinely unknown historical slice is covered separately above. Separate historical evidence also covers predecessor continuity and a second graceful restart, completed owned-worker subflows, truthful-unknown desktop/native appearance and older accepted-close behavior. The old-native clear/restart proof and account-setup slice are recorded separately above.

The earlier Claude automatic owed-A result response did not pass; its archived cause remains unknown. Earlier Codex automatic A/ack evidence is historical and does not certify the later shared changes. The initial idle-model comparison differed between builds; matched-model busy and explicit-model controls supported the original comparison. Historical Codex saved-auto request/report attribution is not claimed as a passed acquisition result. These runs did not certify every remote/platform topology or acceptance of terminal Restart. Transcript byte preservation was proven only by the source/filesystem reproduction, not by a real CLI that writes transcripts during normal turns.

Original comparison: main keeps A Original branch: B with context
Historical main keeps A Historical branch uses B
Original custom-home refusal Original same-chat recovery
Historical switch-back refusal Historical System-default recovery
a9d0cba264ff: B with context a9d0cba264ff: back to A
Historical next turn B Historical return A
a9d0cba264ff: custom-home refusal a9d0cba264ff: recovery
Historical plain refusal Historical CHERRY recovery

Agent skill upstream boundary

  • This change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments or documentation.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:47
@brennanb2025
brennanb2025 marked this pull request as draft October 20, 2026 01:50
@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:50
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review.

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5dd4e731-f290-4a18-b03d-64280008f668
📥 Commits

Reviewing files that changed from the base of the PR and between 59c6bcc and 1454518.

📒 Files selected for processing: 5
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.

Walkthrough

The change tracks account-home metadata from Claude session acquisition through provider-child attachment. Runtime account selection can resolve the account home for a launch, and child retirement compares it with the account used to launch the child. Model catalog operations use the live account when available and otherwise use the next-launch account for reads. Option handling also changes model-less reads and empty-model request recording and replay.

Merge Risk: Minimal · up to 14545

No actionable merge-blocking risk was found in the localized account-switch messaging.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary behavior change: open chats use the newly selected account before their next message.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description is detailed and directly covers the user-facing behavior, implementation, rationale, linked issues, visual evidence, testing, compatibility, limitations, and upstream-boundary checklis…
Full details: Description check

Explanation

The description is detailed and directly covers the user-facing behavior, implementation, rationale, linked issues, visual evidence, testing, compatibility, limitations, and upstream-boundary checklist. It does not use every template heading exactly, and it omits the general checklist and AI Disclosure section, but these omissions do not prevent the description from being mostly complete.

Finishing Touches
Resolve merge conflicts
  • Resolve merge conflict in branch brennanb2025/chat-account-switch
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: eba12076-fbbc-4648-b565-ce58e12f4bd0
📥 Commits

Reviewing files that changed from the base of the PR and between 907694d and 8ef8f4441049846f84f2e23eb50db05d3d428d08.

📒 Files selected for processing: 24
  • src/main/claude/claude-structured-launch-account-routing.test.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/claude/claude-structured-session-publication.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-fingerprint.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-prewarm.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-running-account.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-acquisition.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-flow.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-orchestration.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-publish.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-delivery.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-outgrown-child.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-outgrown-child.ts
  • src/main/runtime/session-tabs/structured-session-account-home.ts
  • src/main/runtime/structured-agent-model-catalog-wiring.ts
  • src/main/runtime/structured-agent-runtime-metadata.ts
  • src/main/runtime/structured-agent-runtime-registration-types.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/workspaces/workspace-process-listing.ts
  • src/shared/agent-session-account-home.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

@brennanb2025
brennanb2025 marked this pull request as draft October 20, 2026 01:53
@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:53
…ore its next message

A running chat's agent read its account only when it started, and nothing compared it with the
account a start would use now, so open chats kept answering on the old account after a switch.

The agent a chat runs now records the account its launch used, and the host's replace-before-the-
next-send check replaces an idle one whose account is no longer what a start would use. A chat
mid-reply finishes on the old account first. Claude chats follow; agents whose chats keep the
account they were created on are never compared.

A chat's model-list reads and saves now use the account it runs on, so a moved chat no longer
writes its new account's list under its first account (STA-11281).

Tracked in STA-11211.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/chat-account-switch branch from 8ef8f44 to 1f8beb9 Compare October 20, 2026 01:57
brennanb2025 and others added 14 commits October 29, 2026 19:09
…eceiving fixtures

Main now resolves a worker start lazily through bindRun with objective and task references, and creating a Run no longer unbinds a coordinator's earlier Runs; run-use does. The receiving fixtures follow the same production owners so every original scenario, input and bound is exercised unchanged. The receiving runtime subclass moves verbatim into its own support module.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A live Claude chat compared its account against the selected one only after that account's folder passed an availability check, so an account still being set up (or whose setup failed) left the chat silently answering on the previous account, and a resting chat refused its start before preparation could run. The retirement comparison and the start pre-read now ask the router for the selected route as identity, through the existing resolver purposes; catalog, create and probe reads keep checking availability. Preparation, typed launch failure and parking are unchanged.

Session History details show the account a chat was last launched under when the host recorded one, else its founding home.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sertion

The History account case now reads the fixture receipt through the typed acquisition reader instead of spreading the record field through a cast; the three assertions and their inputs are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The account-setup cases spread an optional link, which typed its id as possibly undefined; the rig always links a handle, so a guard that cannot fire narrows it. No assertion or input changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… the History account projection

Chat admission no longer reads an experimental host setting, so the continuation-context test drops the spy main dropped in its own fixtures. The two History consumers that install a minimal chat host give their fake store the record accessor and host id the real store has, since the projection now reads the acquisition receipt. An operation-count contract holds the production History projection to one roster read and one record lookup per owned row at 10 and 100 rows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…port

The declared-effort fixture builds a catalog service without the capture member the options reader asks every catalog for, which failed its desktop, catalog and phone consumers; it now supplies the inert test operation like the other fakes. The History projection contract also drops an unused map parameter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@brennanb2025

brennanb2025 commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor Author

Open Claude chats could keep using the previous account after the selection changed. This change makes the next eligible ordinary message follow the selected account while keeping a known account responsible for its exact pending worker-result response. A chat whose selected account is still being set up now waits for that setup or shows the existing account error on that message, instead of answering from the previous account. Session History shows the account a chat was last launched under. An older chat with a genuinely never-recorded actual account and a gone process may recover through selection; this does not claim recovery of its former account. Codex stays pinned. No new indicator or quota routing is added.

What was fixed. A review comment on this PR showed that an ordinary message sent while the selected account's folder was unavailable (being set up, failed, or removed) left a running chat answering from the previous account. The settled fix compares the selected account's identity rather than its folder's existence when deciding whether a running chat must move; the start then waits for the account's setup or fails that one message with the existing account-setup error, and a later message after repair uses the new account. Model lists, new-chat creation and command discovery keep their directory checks. Session History details now show the last launched account when the host recorded one, else the founding home. This is production commit 46a325f; every later commit (7ea925b, 9ec20b2, bb726c9, e0659ca4) changes only tests, fixtures and test support, so the shipping code QA ran against (bb726c9) is the code reviewed. Two main merges follow. 8960978 merges main 1f504e95 to clear a conflict in the worker-listing file: main's owner-routed worktree visibility and this branch's account-aware chat listing readers are both kept, and one unchanged three-line repository-list method moved to the file's existing dependency base as main had done for a sibling. 8d1ba5dd merges main 2cc33b8d to clear two more: the journal database's writable-open step now runs main's new outcome-table setup and this branch's successor-index creation one after the other, and main's split of the at-rest RPC test suite keeps every original case once with this branch's three no-choice replay cases kept verbatim and the shared request helper's optional client parameter moved into main's new fixture; one identically added loaded-chats member was kept once. No chat, account, history or journal-reading mechanism file changed in either merge, but main 2cc33b8d also replaced the journal's incremental change feed with keyed outcome records, and CI on 8d1ba5dd caught that this branch's receiving-account index still understood only the old typed rows (six suites failed) plus one Mobile test mock out of step with main. 20786375 adapts the index: the read row also names touched queue cards and deleted keys, each touched card, submission and item is re-read at its current value, a notice whose send a rewind removed is retired, and the recovery view rebuilds only when an item was deleted. Three production files, +75/−28, no saved field, schema, wire or new guard; sixteen named suites (168 cases) passed on the committed bytes. Independent certifications accepted the source, the common-pattern comparison, performance and readiness with no new proven defect, checked each main integration against both parents, and verified the repair against main's own consumers of the same feed.

Verification. Named suites through the test gate on the committed bytes: the production change ran fifteen suites, 194 cases, six failing before the fix, including host-level tests that drive the real delivery loop, a real account router on disk and real account preparation (live A with B setting up stops A, fails each message once if setup fails, parks, and starts the next message on B once ready; a chat whose own folder vanished keeps its process; a resting chat starts through preparation). The History projection has an operation-count contract at 10 and 100 owned rows. Later test-only commits ran 56, 24, 77 and 68 cases. The final main integration also passed fourteen unchanged receiving-account and account-location contract cases. CI on e0659ca4 passed: PR checks, mobile, headless and WSL (WSL Fedora passed on the workflow's own one-time retry after a hosted runner froze). CI on the merge commit 8960978: PASS: PR checks, mobile, headless, and WSL. The resulting-head watch exited 0; all three WSL distributions passed. CI on the merge commit 8d1ba5dd failed in the six receiving-account suites and Mobile verify for the reasons above; 20786375 repairs that. CI on 20786375: PASS: PR checks, mobile, headless and WSL. The single resulting-head watch exited 0; static analysis and typecheck, all twelve unit shards, the cross-version checks, Mobile verify and all three WSL distributions passed. Differences from the common pattern are labelled intended or temporary in the PR description.

Accepted real Claude/Codex evidence covers live and known-account restart results: actual A before result check/ack, exact completed automatic response, then eligible Claude B/context; Codex stays pinned. Genuine older deferred-source refusal/fork recovery and later materialized-target refusal/resume passed. The genuinely unknown historical Claude cohort used selected B; Codex remained A. Independent pinned-main data measured automatic B→ordinary B versus branch automatic known A→ordinary B, with real replies and original context. The later production change does not touch the flows those captures prove, and the main merges and test-only commits stale nothing. 20786375 does change the code behind the live owed-result disposition, so that one flow was run again on a host built from it with synthetic folder accounts and real load-balancer turns. Claude: own folder A printed before the consuming check returned the exact worker result, the turn completed with no further input, and the next ordinary message printed folder B with the original marker (on the unrepaired merge it would have stayed on A). Codex: the pinned home on every turn, including the automatic result turn; its ordinary reply omitted the remembered marker while the session, home and result were unchanged, and a later question on the same restored chat returned the exact marker (disclosed in the description). Queue-card withdrawal was not reached live: the agent backgrounded the long tool, so the notice arrived as a new turn (folder A before the check, then ordinary B); withdrawal stays covered by the production tests against the real feed. The QA host application was lost once between stages during a machine-wide event on the shared machine; the final stage ran on a fresh launch of the same build after preflight, and cleanup was graceful with no owned survivors.

Old-native row and account-setup slice (final head). The exact old native app 0.0.51 paired to the final-head host through the normal pairing screen and kept its token across an app restart. /clear in Claude and Codex chats cleared context and the next message ran a real tool in the chat's own folder with its marker once; after one completed-turn graceful host restart, Codex model, effort and permission matched, and Claude model and permission matched (Claude effort medium was saved afterward; its pre-restart provenance is unknown), and both agents answered post-restart contextual messages with recalled markers and own-home tools. No-choice: on an unsent Claude chat with the command temporarily missing, tapping "Model not reported" was accepted as an empty operation that wrote nothing; command and arguments were restored exactly and a graceful restart left the chat with no model/effort and no provider handle (a later mistaken send into that chat postdates these captures). Account-setup slice: System default chat answered on its own folder; B was selected and its profile folder moved aside; the next message started on B through the shipped preparation (folder recreated, B path printed, ACCT-B1, nothing from A, no error); the original folder was restored identical and the next message answered on B again.

Disclosed gap: a second message sent while the chat was still starting was not reached by hand in five phone attempts (the host hands the first message to the agent within half a second; typing the second took three to six seconds); both messages in each attempt were accepted once and delivered in order; contextual replies were observed with both agents. That ordering is covered by production-loop tests. One Codex attempt with a conflicting "reply with exactly" instruction and one retry whose visible reply omitted one requested word occurred on accepted deliveries. The non-conflicting first attempt produced both requested replies; the retry’s rendering boundary was not exported, so the omitted word is qualified as model output rather than evidence of a host drop.

Folder-routing labels are synthetic owned identities with real load-balancer turns, not OAuth/distinct-principal/plan/quota proof. The custom-home target growth of 952/954 bytes has unknown cause; unchanged-target-stat wording is false. Direct child actions retained user-owned children with processAction:none; no parent input assisted automatic completion. Two Claude tokens were one result. Natural waits/decoder corrections, safe-export/process-image limits and parent original-image acceptance remain in the PR evidence. Containment is qualified: generated sessions, reused freshness timestamps, one first-main compound click/read, and a supplemental prefix/write-boundary deviation are disclosed, not waived; its duplicate image added no proof. Earlier config-hash changes have unknown cause. Cleanup is historical; restore retained existing shared-store references with five large inventories checked as metadata only. No OAuth/quota failure, whole-machine census or fresh runtime claim follows.

Deferred: P2-PERF1 is finite duplicate saved-home work on a total miss, without a latency claim. R6-N1 is a future optional-reader concern without a current failing input. A new chat created while the selected account's folder is unavailable keeps its pre-existing account-folder error. Custom-folder sharing remains STA-11300; resting slash-command routing remains STA-11298. The inherited already-handed-over/no-reply restart case remains excluded.

For Brennan’s review

Completed flow Proof screenshots
Repaired head 20786375, targeted run Claude automatic A · Claude ordinary B · Codex automatic, pinned · Codex ordinary, pinned, marker omitted · Codex context on restored chat · Claude fallback automatic A, no card · Claude fallback ordinary B
Live result / next ordinary Claude A · Claude ordinary B · Codex automatic · Codex ordinary
Known-account graceful restart Claude automatic A · Claude ordinary B · Codex automatic · Codex ordinary
Deferred source / materialized target source refusal · source recovery · target refusal · target recovery
Unknown historical / pinned Codex Claude auto B · Claude ordinary B · Codex auto A · Codex ordinary A
Pinned main versus branch, automatic main B / exact main completion beside branch A
Pinned main versus branch, ordinary main B/context beside branch B/context
French policy/search French terminal policy · French Claude search
Spanish policy/search Spanish terminal policy · Spanish Claude search
Japanese policy/search Japanese terminal policy · Japanese Claude search
Korean policy/search Korean terminal policy · Korean Claude search
Simplified Chinese policy/search Chinese terminal policy · Chinese Claude search
English actual toast English terminal account-switch toast
French actual toast French terminal account-switch toast
Spanish actual toast Spanish terminal account-switch toast
Japanese actual toast Japanese terminal account-switch toast
Korean actual toast Korean terminal account-switch toast
Simplified Chinese actual toast Simplified Chinese terminal account-switch toast
Old-native /clear (phone) Claude before · Claude completed · Codex reply
Graceful restart, desktop before / after Claude before · Claude after · Codex before · Codex after
Phone post-restart replies, no-choice picker, account-setup slice A/B1/B2 overlap-claude-attempt1.png · overlap-codex-attempt2.png · nochoice-claude-picker.png · nochoice-restored-after-restart.png · d10a-a-reply.png · d10b-b1-result.png · d10c-b2-result.png

Screenshots show visible state; accepted canonical records establish exact completion/lineage. Full historical images and limitations remain in the PR body.

Three judgment locations, verified on the repaired head 20786375 (the replacement and History files are byte-identical to the reviewed e0659ca4; the disposition file carries the 20786375 change-feed adaptation elsewhere in the same file, and the linked line is unchanged):

brennanb2025 and others added 2 commits October 31, 2026 01:03
…split at-rest suites

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Main's keyed journal records deliver each change after an epoch's frozen prefix as an
outcome sequence naming touched keys instead of typed rows. The receiving-account index
now re-reads those keys at their current values: queue cards by id, a deleted submission
retires its exact notice, and a deleted item drives the existing recovery rebuild, with
Stop marks still excluded. The in-process read row exposes the already-persisted
queue.cards keys and per-key deletion facts; itemIds and submissionIds keep listing every
touched key. The mobile unknown-consumer test mocks MobileSwitch as main's picker test does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@brennanb2025
brennanb2025 merged commit 002e975 into main Oct 31, 2026
62 of 63 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/chat-account-switch branch October 31, 2026 11:40
brennanb2025 added a commit that referenced this pull request Oct 31, 2026
Resolves six conflicts with main's account moves for open chats (#30955):
the owner proof keeps the switch's last check inside its write, acquisition
keeps the switch's pre-spawn checks with main's account recipe, and the
switch's chat delegates join main's moved mutation delegates.
brz-anderson added a commit to brz-anderson/brza-studio that referenced this pull request Nov 2, 2026
v1.4.246 takes Chat UI out of Experimental (stablyai#36110). 1554 upstream
commits over the fork's last sync (c98a2d6); 404 files conflicted.

Resolution
- Rebrand-only hunks (1393 in 195 files): upstream's text with the same
  Orca -> BrzA Studio rewrite the fork had applied there.
- Locale catalogs: merged key by key; where both sides changed a key,
  upstream's text. The repo-wide rebrand of new upstream copy follows in
  its own commit.
- Upstream-deleted files the fork had only rebranded: deleted. The
  coexistence cases of the deleted identity tests move to brza/ tests;
  the account-switch notice cases to native-chat/brza/.
- stablyai#34824 (agent message card), cherry-picked earlier: upstream's version;
  the fork's Claude Code peer reader, hand-back heading and sender click
  ride on top of it.

Fork features against new upstream
- Continue in another account (P40) gives way to upstream's
  NativeChatUsageLimitActions (stablyai#31241); Resume automatically stays.
- The per-chat account switch (P36) stays beside upstream's global switch
  (stablyai#30955): a pinned chat launches, and lists models, on its pinned
  account, and the switch drops the old account's acquisition receipt.
- Match Terminal follows what the terminal shows, the chat theme when the
  terminal follows it; plugin terminal themes are offered as chat themes.
- Islands Dark becomes useMonacoTheme's fallback for every surface.
- New Orca identity copies: Windows protocol registration uses the brand
  scheme; brza-studio://plugins links are accepted beside orca://.
- The Windows uninstaller no longer removes agent hooks: BrzA's app GUID
  hides an installed upstream Orca from ORCA_OTHER_INSTALL_STATE, and the
  hooks under ~/.orca are shared with it.
- Files pushed past max-lines split into brza/ or sibling modules.

Committed without the pre-commit hook: lint-staged over the merge's 18k
files is killed. The lint and rebrand fixes follow in their own commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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