Skip to content

Record chat tab opens and closes in main (dark, layout-owner build only) - #30019

Merged
Jinwoo-H merged 4 commits into
mainfrom
e2-5-chat-tabs
Oct 17, 2026
Merged

Jinwoo-H merged 4 commits into
mainfrom
e2-5-chat-tabs

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​518 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​518
Prod 11 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​242 $\color{#cf222e}{\Huge{\mathbf{−}}}$​31 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​211

ELI5

In the test-only build where the app's background process owns the tab layout, chat tabs now tell the background process when they open or close. Before, it only learned about them later, from the window's periodic save. Shipped builds don't change.

What Changed

Applies only with the layout-owner build switch on, which is off in every shipped build.

  • Window opens or closes a chat tab: the window still draws the tab first. It then records the change in main under the same tab id, in the same group and position, with the same creation time. Main uses its existing open-chat-tab and close-tab commands.
  • This machine's host lists a new chat, or drops one: for example, a chat started from the CLI or a phone. The window still adds or removes the tab, then records it in main the same way. It also records the host's tab color and pin, and records new chats in tab order.
  • Main's open-chat-tab command now also takes: the window's position and creation time, and a new split group for open-to-the-side. Sending the same open twice changes nothing.
  • Paired-server workspaces send nothing: they stay mirrored from that server.

Why

Main is taking over the tab layout from the window. Until every change reaches main as a command, main learns some of them only from the window's saves, and the save-divergence detector logs each of those saves. Chat tabs were one of the remaining sources.

Why one rule can't serve both callers today. Main already uses this command for a host with no window open. In shipped builds, that path saves a chat tab even when another tab already holds its id, which leaves a duplicate id. A window-sent open must instead refuse a taken id. So refusing a taken id is gated on the build switch. The gate goes away when the switch ships on. The duplicate id is a minor shipped bug that turning the switch on removes, so there is no separate fix.

The windowless caller now passes its remembered active group only when the workspace still holds that group. Otherwise it gets a freshly made group, exactly as it does today.

Linked Issue

N/A (internal refactor step E2-5).

Visual Proof

N/A. Nothing changes with the switch off, and with it on, the tab bar looks the same; only who records the change differs.

Testing

  • Unit tests, all of which failed before the fix:

    • with the switch on, the window's save no longer differs from main's copy after a chat opens, closes, or is added or removed by a host listing;
    • main's command takes the window's place, time and split;
    • the windowless open → close → reopen still gets a freshly made group;
    • with the switch off, a duplicate chat tab id is still saved (kept legacy behaviour);
    • a listing's new chats are recorded in tab order.
  • Switch-off unminified bundle diff: renderer call sites are gone. The recorder functions remain as dead exports because two callers sit in lazy-loaded files. The main-bundle changes give the same results as before on every switch-off path.

  • Save-divergence detector, in a switch-on app (layout oracle, 22 specs, plus a chat evidence spec):

    • chat tab added/removed: 6 → 0;
    • group tab-order changes: 5 → 2;
    • the one remaining report is a pre-existing group-id mismatch from the workspace's first terminal tab.
  • The chat evidence spec, not committed, uses a fake Claude CLI. It creates two chats on this machine's host (as the CLI or a phone would), closes one from the window and one on the host, then opens a chat from the New tab menu and closes it. test(e2e): open and close structured chat tabs with an isolated Claude stand-in #30011 adds a durable chat e2e.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

  • Fresh adversarial review: merge with fixes. It found two switch-off changes in the windowless path (a zero-group workspace reusing a remembered group id, and a taken chat tab id being refused and dropped). Both are restored to today's behaviour, each with a test that fails first.
  • Delta review: it judged a per-sender flag on the command to be debt. That flag is replaced by the caller-side group rule plus one build-switch gate on the taken-id check, as described above.

Agent skill upstream boundary

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

Notes

Folder workspaces take the local path. SSH hosts cannot run structured chats. Paired servers are skipped. The shipped build is unchanged.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…yout-owner build only)

A chat tab the window opens, closes, or adds and removes from a host
listing of this machine's chats now reaches main as openAgentSessionTab,
closeTabs or setTabProps under the window's ids, so the window's save no
longer teaches main a chat tab. openAgentSessionTab takes the window's
place, creation time and new split group, and the same open again
changes nothing. Switch-off builds are unchanged.
…nd synced chat opens in tab order (dark)

Only a window's openAgentSessionTab (main marks it drawn on layout.apply)
takes the new rules: a repeat changes nothing, a taken id is refused, and
a workspace with no group takes the window's. A windowless host's own
saves mint a fresh group and keep a chat whose id another tab holds, as
before. A host listing's new chats are recorded in tab order, so each
open's anchor already reached main.
…ller names only a group it holds (dark)

Drops the per-sender drawn flag. The windowless save passes its
remembered active group only when the workspace still holds it, so it
mints a fresh group as before. Refusing a taken chat tab id is gated on
the build switch: shipped builds still save the duplicate id.
@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 97b88078-195d-48a3-9ccb-0914b26b3bd2
📥 Commits

Reviewing files that changed from the base of the PR and between 824ca95 and cefe5f7.

📒 Files selected for processing: 15
  • src/main/runtime/agent-session-tab-layout.test.ts
  • src/main/runtime/agent-session-tab-layout.ts
  • src/main/runtime/rpc/methods/workspace-layout-apply.test.ts
  • src/renderer/src/lib/structured-agent-session-provisional-tab.ts
  • src/renderer/src/runtime/agent-session-tab-layout-owner.test.ts
  • src/renderer/src/runtime/agent-session-tab-layout-owner.ts
  • src/renderer/src/runtime/layout-apply-send.ts
  • src/renderer/src/runtime/local-structured-session-tabs-sync/snapshot-apply.ts
  • src/renderer/src/store/slices/tabs/tabs-close-actions.ts
  • src/shared/rpc-contract/workspace-layout-params.ts
  • src/shared/workspace-layout/workspace-layout-agent-session-tab.test.ts
  • src/shared/workspace-layout/workspace-layout-command-types.ts
  • src/shared/workspace-layout/workspace-layout-commands.ts
  • src/shared/workspace-layout/workspace-layout-content-commands.ts
  • src/shared/workspace-layout/workspace-layout-group-commands.ts

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 adds openAgentSessionTab to workspace layout commands and records agent-session tab opens, closes, and synchronized changes in the main layout owner. It also updates group placement and duplicate tab ID handling. The main runtime uses a remembered active group only when that group exists in the target workspace.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to cefe5

No identified issue remains that should prevent merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 15 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the main change: recording chat-tab opens and closes in the main process for the layout-owner build.
Description check Passed The description is complete and aligned with the template. It explains the user impact, implementation, rationale, testing, scope, compatibility considerations, and checklist status. The linked issue …
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.
Full details: Description check

Explanation

The description is complete and aligned with the template. It explains the user impact, implementation, rationale, testing, scope, compatibility considerations, and checklist status. The linked issue is marked N/A for an internal refactor step, which is a minor deviation from the template.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • 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

Comment @coderabbitai help to get the list of available commands.

@Jinwoo-H
Jinwoo-H merged commit fa962dd into main Oct 17, 2026
45 checks passed
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