From 065ec81466a62fd89c89eda17a5a0252911c9604 Mon Sep 17 00:00:00 2001 From: 777genius Date: Thu, 23 Apr 2026 00:43:35 +0300 Subject: [PATCH] fix(team): keep launch failure copy without live details --- .../utils/teamProvisioningPresentation.ts | 25 ++++- .../teamProvisioningPresentation.test.ts | 102 ++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/src/renderer/utils/teamProvisioningPresentation.ts b/src/renderer/utils/teamProvisioningPresentation.ts index 17d32f53..dd6e90bf 100644 --- a/src/renderer/utils/teamProvisioningPresentation.ts +++ b/src/renderer/utils/teamProvisioningPresentation.ts @@ -108,6 +108,19 @@ function buildFailedSpawnCompactDetail( return `${failedSpawnDetails.length} teammates failed to start`; } +function buildGenericFailedSpawnPanelMessage( + failedSpawnCount: number, + expectedTeammateCount: number +): string | null { + if (failedSpawnCount <= 0) { + return null; + } + if (failedSpawnCount === 1) { + return '1 teammate failed to start'; + } + return `${failedSpawnCount}/${Math.max(expectedTeammateCount, failedSpawnCount)} teammates failed to start`; +} + export interface TeamProvisioningPresentation { progress: TeamProvisioningProgress; isActive: boolean; @@ -184,6 +197,10 @@ export function buildTeamProvisioningPresentation({ const failedSpawnDetails = getFailedSpawnDetails(memberSpawnStatuses); const failedSpawnPanelMessage = buildFailedSpawnPanelMessage(failedSpawnDetails); const failedSpawnCompactDetail = buildFailedSpawnCompactDetail(failedSpawnDetails); + const genericFailedSpawnPanelMessage = buildGenericFailedSpawnPanelMessage( + failedSpawnCount, + expectedTeammateCount + ); const { allTeammatesConfirmedAlive, hasMembersStillJoining, remainingJoinCount } = getLaunchJoinState({ @@ -220,7 +237,7 @@ export function buildTeamProvisioningPresentation({ hasMembersStillJoining, remainingJoinCount, panelTitle: 'Launch failed', - panelMessage: progress.error ?? failedSpawnPanelMessage ?? null, + panelMessage: progress.error ?? failedSpawnPanelMessage ?? genericFailedSpawnPanelMessage, panelTone: 'error', defaultLiveOutputOpen: true, compactTitle: 'Launch failed', @@ -245,7 +262,7 @@ export function buildTeamProvisioningPresentation({ : `All ${expectedTeammateCount} teammates joined`; const readyDetailMessage = failedSpawnCount > 0 - ? (failedSpawnPanelMessage ?? progress.message) + ? (failedSpawnPanelMessage ?? genericFailedSpawnPanelMessage ?? progress.message) : expectedTeammateCount === 0 ? 'Team provisioned - lead online' : allTeammatesConfirmedAlive @@ -316,7 +333,9 @@ export function buildTeamProvisioningPresentation({ remainingJoinCount, panelTitle: 'Launching team', panelMessage: - failedSpawnCount > 0 ? (failedSpawnPanelMessage ?? progress.message) : progress.message, + failedSpawnCount > 0 + ? (failedSpawnPanelMessage ?? genericFailedSpawnPanelMessage ?? progress.message) + : progress.message, panelMessageSeverity: failedSpawnCount > 0 ? 'warning' : progress.messageSeverity, defaultLiveOutputOpen: false, compactTitle: 'Launching team', diff --git a/test/renderer/utils/teamProvisioningPresentation.test.ts b/test/renderer/utils/teamProvisioningPresentation.test.ts index fb6dae4a..afedb507 100644 --- a/test/renderer/utils/teamProvisioningPresentation.test.ts +++ b/test/renderer/utils/teamProvisioningPresentation.test.ts @@ -155,6 +155,57 @@ describe('buildTeamProvisioningPresentation', () => { expect(presentation?.compactDetail).toBe('jack failed to start'); }); + it('keeps a generic failed teammate message when only persisted failure counts remain', () => { + const presentation = buildTeamProvisioningPresentation({ + progress: { + runId: 'run-3b', + teamName: 'codex-team', + state: 'ready', + startedAt: '2026-04-13T10:00:00.000Z', + updatedAt: '2026-04-13T10:00:08.000Z', + message: 'Launch completed', + messageSeverity: undefined, + pid: 4321, + cliLogsTail: '', + assistantOutput: '', + }, + members: [ + { + name: 'team-lead', + agentType: 'team-lead', + status: 'active', + currentTaskId: null, + taskCount: 0, + lastActiveAt: null, + messageCount: 0, + }, + { + name: 'jack', + agentType: 'engineer', + status: 'unknown', + currentTaskId: null, + taskCount: 0, + lastActiveAt: null, + messageCount: 0, + }, + ], + memberSpawnStatuses: {}, + memberSpawnSnapshot: { + expectedMembers: ['jack'], + summary: { + confirmedCount: 0, + pendingCount: 0, + failedCount: 1, + runtimeAlivePendingCount: 0, + }, + }, + }); + + expect(presentation?.successMessage).toBe('Launch finished with errors - 1/1 teammates failed to start'); + expect(presentation?.panelMessage).toBe('1 teammate failed to start'); + expect(presentation?.compactDetail).toBe('1 teammate failed to start'); + }); + it('prefers live member spawn statuses over a stale persisted launch summary', () => { const presentation = buildTeamProvisioningPresentation({ progress: { @@ -218,6 +269,57 @@ describe('buildTeamProvisioningPresentation', () => { expect(presentation?.panelMessage).toBe('1 teammate still joining'); }); + it('keeps a generic failed teammate message while launch is still active if only persisted failure counts remain', () => { + const presentation = buildTeamProvisioningPresentation({ + progress: { + runId: 'run-4b', + teamName: 'codex-team', + state: 'assembling', + startedAt: '2026-04-13T10:00:00.000Z', + updatedAt: '2026-04-13T10:00:05.000Z', + message: 'Finalizing launch...', + messageSeverity: undefined, + pid: 4321, + cliLogsTail: '', + assistantOutput: '', + }, + members: [ + { + name: 'team-lead', + agentType: 'team-lead', + status: 'active', + currentTaskId: null, + taskCount: 0, + lastActiveAt: null, + messageCount: 0, + }, + { + name: 'jack', + agentType: 'engineer', + status: 'unknown', + currentTaskId: null, + taskCount: 0, + lastActiveAt: null, + messageCount: 0, + }, + ], + memberSpawnStatuses: {}, + memberSpawnSnapshot: { + expectedMembers: ['jack'], + summary: { + confirmedCount: 0, + pendingCount: 0, + failedCount: 1, + runtimeAlivePendingCount: 0, + }, + }, + }); + + expect(presentation?.panelMessage).toBe('1 teammate failed to start'); + expect(presentation?.compactDetail).toBe('1 teammate failed to start'); + expect(presentation?.compactTone).toBe('warning'); + }); + it('prefers live confirmed teammates over a stale persisted launch summary', () => { const presentation = buildTeamProvisioningPresentation({ progress: {