fix(team): keep launch failure copy without live details

This commit is contained in:
777genius 2026-04-23 00:43:35 +03:00
parent 1cb9af3fc7
commit 065ec81466
2 changed files with 124 additions and 3 deletions

View file

@ -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',

View file

@ -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: {