Skip to content

Every tab Orca copies from a host closes through one record and one set of rules - #27561

Draft
brennanb2025 wants to merge 12 commits into
mainfrom
brennanb2025/tab-close-ledger
Draft

brennanb2025 wants to merge 12 commits into
mainfrom
brennanb2025/tab-close-ledger

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2316 $\color{#cf222e}{\Huge{\mathbf{−}}}$​112 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2204
Prod 36 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1479 $\color{#cf222e}{\Huge{\mathbf{−}}}$​480 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​999

ELI5

Closing a host-backed tab hides it in the window while the host finishes the request. A stale host list could put it back at the right end. #27205 fixed that race for native chat, but different tab kinds still disagreed on failed and unanswered closes. Losing contact could leave a closed tab missing indefinitely.

Desktop tabs copied from a host now share one close record and answer policy. A refusal or failure restores current host content at the captured position; an unanswered close restores after 15 seconds. Commands stay with the original host and pairing, including when pairing changes during the request.

What Changed

  • One pending-close record covers paired terminal, browser, editor and chat tabs, plus local native chat. Stale host lists cannot undo an unfinished close. Accepted and absent answers wait for authoritative omission, subject to existing abandonment limits.
  • The existing host-input record retains its latest accepted list in memory. The close operation captures the exact window tab occurrence, group and surviving neighbors. Recovery uses that list and the normal layout reconciler, preserving newer content and moves.
  • Store-dependent placement reading is registered after store creation. Background recovery loading cannot delay settlement, the close result or the independent reread. The first restoring host frame has the placement information even when that module is cold.

Why

One operation over one accepted host source replaces repeated close policies. The operation keeps the window placement the host cannot reconstruct; the existing reconciler applies it in its normal store update. This avoids whole-store rollback, a second layout writer and another persistent obligation.

Differences from the common pattern:

Linked Issue

Follow-up to #27205; base for #27563 and #27586. Host membership corrections are reviewed separately in #27565.

Visual Proof

Remote M4Air, hidden paired apps, real native Claude, and genuine host-owned mirrors. Both comparisons used the correct chat close button and a chat bracketed by two terminals. Main fb5d016 is the main snapshot used by final integration. Both main and this branch pass ordinary busy-chat close; these images show continuity of #27205 behavior, rather than a new main regression.

Final a269460: before closing the busy chat, then 16 seconds later

Final a269460 before: busy Claude chat between Terminal 1 and Terminal 2

Final a269460 after 16 seconds: chat absent, both terminals remain in order and workspace active

Main fb5d016: same busy-chat comparison

Main fb5d016 before: busy Claude chat between two terminals

Main fb5d016 after 16 seconds: chat absent and both terminals preserved

Unanswered close: cached restoration while host contact is lost

Main did not restore the chat while its host was paused through 18 seconds. This earlier branch capture is 85b253d, where restoration occurred at 15.062 seconds while the host remained paused. All 57 reviewed paths are byte-identical on final a269460; this image is labelled with its actual revision. The connection indicator still shows contact loss.

Reviewed 85b253d after the unanswered-close bound: Claude Chat restored while Connecting

Testing

  • Targeted automated tests added and updated
  • Rendered checks completed on remote macOS; limitations below

The fresh independent seventh review passed 130 tests across 13 suites, production store initialization, all five placement-reader kinds and 32 real-store recovery cases. The reviewed correction passed 215 tests in 20 suites; the resolved integration passed 223 tests in 22 suites plus 31 terminal-action cases. Both required #27205 regression test bodies retain their actions and assertions. Counts overlap.

CI exposed two stale paired-host bookkeeping expectations. The actual four-case fixture reproduced both failures; the correction types its mock from the real API and checks the exact captured pairing options, preserving all other actions and assertions. The fixture and existing owner/revision contract suites pass 24 tests in three suites. Renderer typechecking passes. All 57 reviewed hashes remain unchanged after final integration.

The full merged layout gate and all 22 source checks pass. Local typecheck:ci has the documented Bun 1.3 limitation; supported final CI static analysis and typechecking pass. Final CI is 18 successful, 21 skipped, zero failed or pending on a26946013e1ea2fb7c825ec178c63d56e46177b3, including all five Node shards. One explicitly authorized Windows-only retry passed after an unchanged native-terminal warmup timed out. A passing retry does not establish the original timeout's cause.

Remote macOS rendered checks used hidden, isolated apps and real native Claude. Local terminal, browser, editor and busy Claude closes passed; genuine paired host-owned terminal/browser/editor/Claude closes passed. Final-head editor and bracketed busy Claude checks retained siblings and active workspace through 16 seconds. Main fb5d016 also passes ordinary busy Claude close, consistent with #27205.

With the original host paused, main hid the chat through 18 seconds; reviewed 85b253d restored it from cached state at 15.062 seconds while the host remained paused. All 57 reviewed paths are byte-identical on final a269460; the earlier capture is labelled with its actual revision. A final-head injected RPC refusal, limited to one close response in the QA renderer, restored a real host-owned terminal at its original position at 277 ms; the actual host retained all three terminals. This tests refusal handling, not a genuine host refusal decision.

Not verified: native Codex was blocked by the isolated app's sign-in verdict; real SSH and Windows native UI QA were blocked by available tooling/login/usage. No login or credential workaround was used. Paired macOS is not SSH coverage. Skipped CI jobs remain untested.

Review

Seven rounds, including Brennan's explicitly approved seventh round for initialization and layout ownership. Final scoped verdict: architecture KEEP, correctness PASS, all eight readiness items checked. The loop corrected original-host routing, exact tab identity/placement, layout ownership, store initialization and cold recovery ordering. Most later findings came from recovery changes made during this review.

End integration was explicitly authorized. A second authorized main merge picked up main's own #28085 line-limit fix; it changed none of the 57 reviewed paths and introduced no new PR policy. The PR remains a draft.

Agent skill upstream boundary

  • Not applicable; no upstream skill source or installer data copied.

Notes

Deferred P2: after host restart, an empty partial list that does not yet describe browser pages can end suppression. A later list can append the browser at the end. This was reproduced with the actual store; closing again is a workaround. No content loss was demonstrated. It remains outside the approved seventh-round correction.

Older-host boundary: a live refusal without a republish fact can look like intentional omission. The host correction belongs to #27565; the client cannot infer the missing fact.

No required RPC field, stream operation, schema or persisted ordinary-close state is introduced. Reload loses the in-memory source and unfinished close. Accepted suppression has its existing 20-second abandonment backstop; expiry is not omission evidence. SSH contact loss is unverifiable, never proof of exit.

Checklist

  • Desktop host-tab close scope, mechanism and alternatives explained
  • Independent review/readiness and meaningful automated checks complete
  • Security, performance, platforms, folder workspaces, SSH and mixed peers considered
  • Inspected screenshots attached and remote QA limits recorded
  • CI green

…and one policy

A failed close of a paired terminal, browser or editor tab now brings the tab back
at once, as chats already did; an unanswered close (including a local chat close,
which had no timeout) fails after 15 s; re-pairing one host no longer drops the
close records of another.
- a chat the host answers 'no such tab' for may be listed again once the host
  restores its list, so it is hidden only for the settled limit
- an answer that lands by the call's own limit always settles the record
- a failed close no longer waits on the re-read before reporting
- a chat close the window gave up on is aborted rather than sent late
- one re-read for every mirror; consistent close-intent names; dead code removed
- a chat close the window gave up on is skipped, not sent over a different transport
- the session-tab call budget is the shared close bound, so the two cannot drift
@brennanb2025

Copy link
Copy Markdown
Contributor Author

A host-backed tab disappears immediately when closed, but stale host lists can still name it. #27205 fixed the native-chat flash; different tab kinds still disagreed on failed and unanswered closes. This PR gives desktop host-backed tabs one close record and policy: stale lists cannot undo an unfinished close, failure restores current host content at the original window position, and an unanswered close restores after 15 seconds.

Seven review rounds completed, including the explicitly approved seventh-round initialization/layout correction. Fixed original-host routing, tab occurrence identity, restoration order, store initialization and cold recovery ordering. Final scoped verdict: architecture KEEP, correctness PASS, eight readiness items checked. Most later findings came from recovery changes made during this loop. The final integration preserves all 57 reviewed file hashes.

Verified: final a26946013e1ea2fb7c825ec178c63d56e46177b3 CI is green: 18 successful checks, 21 skipped, none failed/pending; all five Node shards pass. The one authorized Windows-only retry passed; its original warmup timeout's cause remains unproven. Renderer typechecking and all 22 source checks pass, with the documented local Bun version limitation covered by supported CI. Independent review ran 130 tests in 13 suites; integration ran 223 in 22 suites plus 31 terminal cases, and the final fixture correction passed 24 in three suites. Counts overlap.

Remote macOS hidden-renderer QA passed local and genuine paired terminal/browser/editor/busy Claude closes. Final-head bracketed Claude and editor closes stayed absent through 16 seconds, with siblings and workspace intact. Main also passes ordinary busy Claude close. In the paused-host comparison, main remained hidden through 18 seconds; reviewed branch code restored from cache at 15.062 seconds. A labelled final-head injected RPC refusal restored the middle terminal at its original position in 277 ms; the real host retained its tabs. Screenshots are attached in the PR body.

Deferred P2: a partial empty browser list after host restart can end suppression; a later list can append the browser at the end. Actual-store reproduction showed no content loss; closing again is a workaround. This remains outside the approved correction. An older host's live refusal without a republish fact remains a compatibility boundary handled separately in #27565.

Not verified: native Codex (isolated-app sign-in verdict), real SSH, and Windows native UI (tooling/login/usage blockers). No authentication workaround was used. Paired macOS does not establish SSH coverage. The remote qa-pr-27561 worktree and owned QA processes were cleaned up after evidence preservation.

PR remains DRAFT. No merge or ready action was performed.

This branch has not been deployed

No deployments
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