From 6ea8b469f03b9f406041d526f86298ef174439eb Mon Sep 17 00:00:00 2001 From: 777genius Date: Thu, 23 Apr 2026 04:22:58 +0300 Subject: [PATCH] fix(team): map suffixed runtime metadata to launch statuses --- .../services/team/TeamProvisioningService.ts | 16 +++++++- .../team/TeamProvisioningService.test.ts | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index cb5f8494..ee5bf673 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -11265,7 +11265,19 @@ export class TeamProvisioningService { const runtimeByMember = await this.getLiveTeamAgentRuntimeMetadata(teamName); const nextStatuses = { ...statuses }; for (const [memberName, metadata] of runtimeByMember.entries()) { - const current = nextStatuses[memberName]; + const resolvedStatusKey = + nextStatuses[memberName] != null + ? memberName + : (() => { + const matches = Object.keys(nextStatuses).filter((candidateName) => + matchesTeamMemberIdentity(candidateName, memberName) + ); + return matches.length === 1 ? matches[0] : null; + })(); + if (!resolvedStatusKey) { + continue; + } + const current = nextStatuses[resolvedStatusKey]; if (!current) { continue; } @@ -11288,7 +11300,7 @@ export class TeamProvisioningService { nextEntry.livenessSource = current.bootstrapConfirmed ? current.livenessSource : 'process'; nextEntry.launchState = deriveMemberLaunchState(nextEntry); } - nextStatuses[memberName] = nextEntry; + nextStatuses[resolvedStatusKey] = nextEntry; } return nextStatuses; } diff --git a/test/main/services/team/TeamProvisioningService.test.ts b/test/main/services/team/TeamProvisioningService.test.ts index f8fc404d..879d7882 100644 --- a/test/main/services/team/TeamProvisioningService.test.ts +++ b/test/main/services/team/TeamProvisioningService.test.ts @@ -5554,6 +5554,43 @@ describe('TeamProvisioningService', () => { }); }); + it('maps suffixed live runtime metadata keys back onto canonical spawn statuses', async () => { + const svc = new TeamProvisioningService(); + (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn( + async () => + new Map([ + [ + 'bob-2', + { + alive: true, + model: 'gpt-5.2', + }, + ], + ]) + ); + + const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', { + bob: createMemberSpawnStatusEntry({ + status: 'error', + launchState: 'failed_to_start', + error: 'Teammate did not join within the launch grace window.', + hardFailure: true, + hardFailureReason: 'Teammate did not join within the launch grace window.', + }), + }); + + expect(result.bob).toMatchObject({ + status: 'online', + launchState: 'runtime_pending_bootstrap', + runtimeAlive: true, + hardFailure: false, + hardFailureReason: undefined, + error: undefined, + runtimeModel: 'gpt-5.2', + livenessSource: 'process', + }); + }); + it('does not clear an explicit restart failure just because the old runtime is still alive', async () => { const svc = new TeamProvisioningService(); (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(