diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index 284845a6..c6f414ba 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -11858,6 +11858,7 @@ export class TeamProvisioningService { }, snapshot?: PersistedTeamLaunchSnapshot | null ): string { + const expectedTeammateCount = snapshot?.expectedMembers.length ?? run.expectedMembers.length; const permissionPendingCount = snapshot ? this.countSnapshotPermissionPendingMembers(snapshot) : this.countRunPermissionPendingMembers(run); @@ -11880,15 +11881,15 @@ export class TeamProvisioningService { if (launchSummary.confirmedCount === 0) { const allRuntimeAlive = launchSummary.runtimeAlivePendingCount > 0 && - launchSummary.runtimeAlivePendingCount === run.expectedMembers.length; + launchSummary.runtimeAlivePendingCount === expectedTeammateCount; return allRuntimeAlive ? `${prefix} — teammates online` : launchSummary.runtimeAlivePendingCount > 0 - ? `${prefix} — ${launchSummary.runtimeAlivePendingCount}/${run.expectedMembers.length} teammate${launchSummary.runtimeAlivePendingCount === 1 ? '' : 's'} online${stillStartingCount > 0 ? `, ${stillStartingCount} still starting` : ''}` + ? `${prefix} — ${launchSummary.runtimeAlivePendingCount}/${expectedTeammateCount} teammate${launchSummary.runtimeAlivePendingCount === 1 ? '' : 's'} online${stillStartingCount > 0 ? `, ${stillStartingCount} still starting` : ''}` : `${prefix} — teammates are still starting`; } - return `${prefix} — ${launchSummary.confirmedCount}/${run.expectedMembers.length} teammates made contact${launchSummary.runtimeAlivePendingCount > 0 ? `, ${launchSummary.runtimeAlivePendingCount} teammate${launchSummary.runtimeAlivePendingCount === 1 ? '' : 's'} online` : ''}${stillStartingCount > 0 ? `${launchSummary.runtimeAlivePendingCount > 0 ? ', ' : ', '}${stillStartingCount} still joining` : ''}`; + return `${prefix} — ${launchSummary.confirmedCount}/${expectedTeammateCount} teammates made contact${launchSummary.runtimeAlivePendingCount > 0 ? `, ${launchSummary.runtimeAlivePendingCount} teammate${launchSummary.runtimeAlivePendingCount === 1 ? '' : 's'} online` : ''}${stillStartingCount > 0 ? `${launchSummary.runtimeAlivePendingCount > 0 ? ', ' : ', '}${stillStartingCount} still joining` : ''}`; } private buildAggregatePendingLaunchMessage( diff --git a/test/main/services/team/TeamProvisioningService.test.ts b/test/main/services/team/TeamProvisioningService.test.ts index aa3ee3af..be9b54c2 100644 --- a/test/main/services/team/TeamProvisioningService.test.ts +++ b/test/main/services/team/TeamProvisioningService.test.ts @@ -2174,6 +2174,59 @@ describe('TeamProvisioningService', () => { expect(message).toBe('Finishing launch — 1 teammate awaiting permission approval'); }); + it('uses persisted expected member count instead of stale run expected members for pure launch copy', () => { + const svc = new TeamProvisioningService(); + const run = createMemberSpawnRun({ + teamName: 'pure-team', + expectedMembers: [], + memberSpawnStatuses: new Map(), + }); + + const message = (svc as any).buildAggregatePendingLaunchMessage( + 'Finishing launch', + run, + { + confirmedCount: 0, + pendingCount: 1, + failedCount: 0, + runtimeAlivePendingCount: 1, + }, + { + version: 2, + teamName: 'pure-team', + updatedAt: '2026-04-22T12:00:00.000Z', + launchPhase: 'active', + expectedMembers: ['alice'], + bootstrapExpectedMembers: ['alice'], + members: { + alice: { + name: 'alice', + providerId: 'opencode', + laneId: 'primary', + laneKind: 'primary', + laneOwnerProviderId: 'opencode', + launchState: 'runtime_pending_bootstrap', + agentToolAccepted: true, + runtimeAlive: true, + bootstrapConfirmed: false, + hardFailure: false, + lastEvaluatedAt: '2026-04-22T12:00:00.000Z', + }, + }, + summary: { + confirmedCount: 0, + pendingCount: 1, + failedCount: 0, + runtimeAlivePendingCount: 1, + }, + teamLaunchState: 'partial_pending', + } + ); + + expect(message).toBe('Finishing launch — teammates online'); + expect(message).not.toContain('/0'); + }); + it('launches the OpenCode secondary lane with side-lane provider and member runtime identity', async () => { const svc = new TeamProvisioningService(); const adapterLaunch = vi.fn(async (input: Record) => ({