fix(agent-rows): preserve same-tab child labels

This commit is contained in:
Brennan Benson
2026-07-22 21:47:38 -07:00
parent 628fb0dc20
commit 515feb71cc
3 changed files with 54 additions and 7 deletions
@@ -56,6 +56,44 @@ describe('useAgentRowConversationName', () => {
expect(useAgentRowConversationName(makeAgent({ rowSource: 'subagent' }))).toBeNull()
})
it('does not inherit a same-tab lineage parent conversation name', () => {
const tabsByWorktree = new Proxy(
{},
{
get: () => {
throw new Error('same-tab child rows must not read the parent tab')
}
}
)
storeState.current = { settings: {}, tabsByWorktree }
expect(
useAgentRowConversationName(
makeAgent({
entry: {
prompt: 'child prompt',
orchestration: {
parentPaneKey: 'tab-1:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
}
},
lineage: { depth: 1, isFirstSibling: true, isLastSibling: true, childCount: 0 }
} as Partial<DashboardAgentRow>)
)
).toBeNull()
})
it('uses a lineage child conversation name when it owns a separate tab', () => {
const agent = makeAgent({
entry: {
prompt: 'child prompt',
orchestration: {
parentPaneKey: 'parent-tab:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
}
},
lineage: { depth: 1, isFirstSibling: true, isLastSibling: true, childCount: 0 }
} as Partial<DashboardAgentRow>)
expect(useAgentRowConversationName(agent)).toBe('Patient sync spike')
})
it('indexes one immutable tab array once across rows', () => {
let tabReads = 0
const tabs = new Proxy(
@@ -1,4 +1,5 @@
import { getAgentRowConversationName } from '../../../../shared/agent-row-conversation-name'
import { parsePaneKey } from '../../../../shared/stable-pane-id'
import { useAppStore } from '@/store'
import type { AppState } from '@/store/types'
import type { DashboardAgentRow } from './useDashboardData'
@@ -24,15 +25,22 @@ function getIndexedTab(
/** The row's conversation name, or null when nothing usable exists. */
export function useAgentRowConversationName(agent: DashboardAgentRow): string | null {
const isSubagent = agent.rowSource === 'subagent'
const parentPaneKey = agent.entry.orchestration?.parentPaneKey
const usesParentTab =
agent.lineage?.depth === 1 &&
parentPaneKey !== undefined &&
parsePaneKey(parentPaneKey)?.tabId === agent.tab.id
const cannotOwnTabName = agent.rowSource === 'subagent' || usesParentTab
const generatedTitlesEnabled = useAppStore(
(s) => !isSubagent && s.settings?.tabAutoGenerateTitle === true
(s) => !cannotOwnTabName && s.settings?.tabAutoGenerateTitle === true
)
// Why: child rows describe the child, so the parent tab would mislabel them.
const liveTab = useAppStore((s) =>
isSubagent ? undefined : getIndexedTab(s.tabsByWorktree[agent.tab.worktreeId], agent.tab.id)
cannotOwnTabName
? undefined
: getIndexedTab(s.tabsByWorktree[agent.tab.worktreeId], agent.tab.id)
)
if (isSubagent) {
// Why: synthetic and same-tab child rows do not own the parent tab's name.
if (cannotOwnTabName) {
return null
}
// Why: retained row snapshots need a fallback after their live tab disappears.
@@ -474,8 +474,9 @@ describe('WorktreeCard agent-list <-> child-worktrees expansion coupling', () =>
setAgentLineageState({ agentActivityDisplayMode: 'full' })
const { container } = await renderWorktreeList()
// Sanity: real agent rows rendered (parent + its lineage child agent).
expect(container.textContent).toContain('PARENT_AGENT_PROMPT')
// Same-tab lineage children must not inherit the parent's conversation name.
expect(container.textContent).toContain('Parent Terminal')
expect(container.textContent).not.toContain('PARENT_AGENT_PROMPT')
expect(container.textContent).toContain('CHILD_AGENT_PROMPT')
// Both controls exist and are independent DOM elements.