Conversation
…t produced it The auto-generated tab title comes from one pane's agent prompt but is stored tab-wide, so every agent row in a split tab showed it. Record the pane that produced the title and let only that pane's rows use it: workspace-card and dashboard rows, the board snapshot, and the Agents list. Titles saved before the source was recorded keep today's behavior.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (17)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe change adds Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The pane-scoped title behavior is covered across persistence, synchronization, dashboard, agent-row, and activity-title paths. No actionable merge risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
ℹ️ No critical issues — one scope question and a small test nitpick.
Reviewed changes
- Source pane recorded on the tab —
TerminalTab.generatedTitlePaneKeyis written alongsidegeneratedTitleinapplyGeneratedTabTitleUpdates, persisted throughterminalTabSchemawith.catch(undefined), and compared byterminalTabEqual. - Single ownership rule —
isGeneratedTabTitleOwnedByPaneinshared/agent-tab-title.tsgates the generated title on the row's pane key, falling back to tab-wide for untagged titles or callers that pass no pane. - Applied at both row producers —
getAgentRowConversationName(worktree-card rows, dashboard rows, board snapshot) andgetActivityThreadTaskTitle(Agents list, Activity page) now pass the row'spaneKey. - Paired web client —
buildMirroredTerminalTabscarries the client-local source pane only when the existing tab id matches the mirrored local id, andterminalTabEqualcompares the new field.
I verified the tests actually pin the behavior: all 7 changed test files pass (85 tests) with the change, and reverting the 10 non-test files makes 8 new assertions fail across 7 files. tc:web passes, and the related activity / web-session-tabs-sync / generated-title / dashboard suites pass (256 tests).
ℹ️ Headless mobile projection still stamps the tab's generated title on every leaf
The PR body says "the host transport never carried generatedTitle", but the headless path does carry it. buildHeadlessMobileSessionTerminalTabs emits one surface per leaf and sets each leaf's title to tab.generatedTitle, so a headless-host split tab still shows one pane's generated title on every leaf — the same class of mislabel this PR fixes for rows. The renderer publication path deliberately scopes the tab-wide title to the launch-agent leaf only, so the two paths disagree.
Technical details
# Headless mobile projection applies the tab's generated title to every leaf
## Affected sites
- `src/main/runtime/mobile-session-terminal-projection.ts:26-31` — `title = customTitle || generatedTitle || title || defaultTitle` is computed per leaf, so all leaves of a split tab get the tab's `generatedTitle`.
- Contrast `src/renderer/src/runtime/sync-runtime-graph/mobile-session-terminal-tabs.ts:78-86` — the renderer path gates the tab-wide title on `tabWideFallbackSafe` (`launchAgentLeafId === leafId`), so only the launch-agent leaf receives it.
## Required outcome
- Decide whether a headless-host mobile surface should follow the same pane ownership as the desktop rows. If yes, the projection needs the leaf id (`tab.id::leafId` already carries it) compared against `generatedTitlePaneKey`; if no, the PR description's "host transport never carried generatedTitle" claim should be corrected.
## Open questions for the human
- Is the mobile per-leaf surface intentionally tab-wide (mirroring the tab strip), or should it match the row behavior this PR establishes?ℹ️ Nitpicks
- The second assertion in
mirrored-generated-title.test.ts("drops a source pane recorded under another tab id so the title is not hidden") passes without the production change — when the field does not exist, no pane key is ever carried. It still guards against an unconditional copy, but the PR's "every new assertion fails without the production change" claim does not hold for it.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Thanks for the review. On the headless projection: On the nitpick: agreed, that guard passes without the change. I've reworded the testing note. |

ELI5
With "Auto-generate tab titles" on, Orca names a tab after the first agent prompt it sees. When a tab is split and each pane runs its own agent, every agent row for that tab shows that one name, so the second agent is listed under the first agent's task. Orca now remembers which pane the name came from, and only that pane's rows use it.
What Changed
TerminalTab.generatedTitlePaneKey(optional) records the pane whose prompt producedgeneratedTitle.applyGeneratedTabTitleUpdatessets it together with the title..catch(undefined), so a malformed value can't drop the tab on restore.isGeneratedTabTitleOwnedByPane(tab, paneKey)inshared/agent-tab-title.tsis the single rule:getAgentRowConversationNametakespaneKey.useAgentRowConversationNameandrowConversationNamepass the row's pane key, which covers worktree-card rows, dashboard rows and the board snapshot.getActivityThreadTaskTitlereadsentry.paneKey, which covers the Agents list and the Activity page.terminalTabEqualcompares the new field.Why
generatedTitleis stored once per tab but derived from one pane's prompt.Known limits (out of scope):
Linked Issue
Fixes #20856
Visual Proof
Headless Electron e2e build, two Codex agents,
tabAutoGenerateTitleon.The Agents list matches the worktree-card rows in each case.
Worktree card rows (split tab, both agents start)
Agents list (same scenario)
Left pane names the tab first, then split, then the right agent starts (after)
Testing
agent-generated-tab-title-pane.test.ts: the writer records the source, a later sibling doesn't take it, and a dispatch replacement moves it.workspace-session-schema.test.ts: the field persists, and a malformed value keeps the tab.agent-row-conversation-name.test.ts,use-agent-row-conversation-name.test.ts,dashboard-card-labels.test.ts: the producer keeps the title, a sibling shows its own name, and an omitted pane keeps today's behavior.activity-thread-display.test.ts: the Agents-list title.mirrored-generated-title.test.ts: the web client keeps the source only under the same tab id.pnpm lint,pnpm typecheckandpnpm run build:desktoppass locally.src/shared,lib,store,runtime,hooksandcomponents/{dashboard,activity,sidebar}: 22,846 tests pass.AI Disclosure
I used Claude Code (Claude Opus 5) to help investigate the bug, write the change and tests, and run adversarial reviews of the diff. I reviewed every change myself and verified it locally, including the e2e scenarios above.
Review
generatedTitle. Old sessions and old clients keep today's behavior. There is no RPC or stream change.conversationNamekeeps its shape and meaning. Mobile per-leaf surface titles are not changed here; the headless-host projection (buildHeadlessMobileSessionTerminalTabs) still uses the tab-level title.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)