fix(team): use persisted launch member union

This commit is contained in:
777genius 2026-04-23 02:40:29 +03:00
parent 82d4f094f4
commit 02419ec8ee
2 changed files with 67 additions and 5 deletions

View file

@ -11858,7 +11858,9 @@ export class TeamProvisioningService {
},
snapshot?: PersistedTeamLaunchSnapshot | null
): string {
const expectedTeammateCount = snapshot?.expectedMembers.length ?? run.expectedMembers.length;
const expectedTeammateCount = snapshot
? this.getPersistedLaunchMemberNames(snapshot).length
: run.expectedMembers.length;
const permissionPendingCount = snapshot
? this.countSnapshotPermissionPendingMembers(snapshot)
: this.countRunPermissionPendingMembers(run);
@ -11908,7 +11910,8 @@ export class TeamProvisioningService {
return this.buildPendingBootstrapStatusMessage(prefix, run, launchSummary, snapshot);
}
const allPendingMembers = snapshot.expectedMembers.filter((memberName) => {
const persistedMemberNames = this.getPersistedLaunchMemberNames(snapshot);
const allPendingMembers = persistedMemberNames.filter((memberName) => {
const member = snapshot.members[memberName];
if (!member) {
return false;
@ -11935,7 +11938,7 @@ export class TeamProvisioningService {
const primaryExpectedMembers = new Set(
snapshot.bootstrapExpectedMembers ?? run.expectedMembers
);
const secondaryPendingMembers = snapshot.expectedMembers.filter((memberName) => {
const secondaryPendingMembers = persistedMemberNames.filter((memberName) => {
if (primaryExpectedMembers.has(memberName)) {
return false;
}
@ -11976,7 +11979,7 @@ export class TeamProvisioningService {
private countSnapshotPermissionPendingMembers(snapshot: PersistedTeamLaunchSnapshot): number {
let count = 0;
for (const memberName of snapshot.expectedMembers) {
for (const memberName of this.getPersistedLaunchMemberNames(snapshot)) {
const member = snapshot.members[memberName];
if (!member) {
continue;
@ -11998,10 +12001,16 @@ export class TeamProvisioningService {
},
snapshot?: PersistedTeamLaunchSnapshot | null
): boolean {
const expectedCount = snapshot?.expectedMembers.length ?? run.expectedMembers?.length ?? 0;
const expectedCount = snapshot
? this.getPersistedLaunchMemberNames(snapshot).length
: (run.expectedMembers?.length ?? 0);
return launchSummary.pendingCount > 0 && expectedCount > 0;
}
private getPersistedLaunchMemberNames(snapshot: PersistedTeamLaunchSnapshot): string[] {
return Array.from(new Set([...snapshot.expectedMembers, ...Object.keys(snapshot.members)]));
}
private buildLiveLaunchSnapshotForRun(
run: ProvisioningRun,
launchPhase: PersistedTeamLaunchPhase = run.provisioningComplete ? 'finished' : 'active'

View file

@ -2227,6 +2227,59 @@ describe('TeamProvisioningService', () => {
expect(message).not.toContain('/0');
});
it('uses the union of persisted expected members and persisted member entries for pending 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: [],
bootstrapExpectedMembers: [],
members: {
alice: {
name: 'alice',
providerId: 'opencode',
laneId: 'primary',
laneKind: 'primary',
laneOwnerProviderId: 'opencode',
launchState: 'runtime_pending_permission',
agentToolAccepted: true,
runtimeAlive: true,
bootstrapConfirmed: false,
hardFailure: false,
pendingPermissionRequestIds: ['perm-1'],
lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
},
},
summary: {
confirmedCount: 0,
pendingCount: 1,
failedCount: 0,
runtimeAlivePendingCount: 1,
},
teamLaunchState: 'partial_pending',
}
);
expect(message).toBe('Finishing launch — 1 teammate awaiting permission approval');
});
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<string, unknown>) => ({