fix(native-chat): degrade instead of throwing on a null tab title

A stacked branch can publish title: null when a conversation name is
cleared. The wire type says string, so this consumer trusted it and
threw inside the store patch that applies the snapshot. Fall back to the
placeholder — the producer bug is fixed separately, but a consumer of
wire data should not crash on a contract violation.
This commit is contained in:
Merge Sim
2026-09-06 21:02:41 -07:00
parent cf456eb601
commit 154b1bb333
2 changed files with 11 additions and 1 deletions
@@ -67,6 +67,14 @@ describe('buildMirroredAgentTabs', () => {
expect(tab.customLabel).toBeNull()
})
it('degrades to the placeholder when the host violates the string contract', () => {
const snapshot = snapshotWith('claude', 'Named')
// The wire type says `string`, but a host clearing a name can send null.
;(snapshot.tabs[0] as { title: unknown }).title = null
expect(() => build(snapshot)).not.toThrow()
expect(build(snapshot).label).toBe('Claude Chat')
})
it('names an agent this build does not know after itself, not Codex', () => {
const snapshot = snapshotWith('codex', '')
// Cast: the wire union is claude|codex today, but Tab.agentSessionAgent is
@@ -74,7 +74,9 @@ export function buildMirroredAgentTabs(
worktreeId: snapshot.worktree,
contentType: 'agent-session',
agentSessionAgent: tab.agent,
label: tab.title.trim() || defaultAgentChatLabel(tab.agent),
// Why: `title` is wire data typed `string`; a host that violates that must
// degrade to the placeholder, not throw inside the snapshot patch.
label: tab.title?.trim() || defaultAgentChatLabel(tab.agent),
// Why: a manual rename lives only on the client; re-nulling it here made
// every host snapshot silently discard the user's title.
customLabel: existing?.customLabel ?? null,