fix(team): use persisted launch member union
This commit is contained in:
parent
82d4f094f4
commit
02419ec8ee
2 changed files with 67 additions and 5 deletions
|
|
@ -11858,7 +11858,9 @@ export class TeamProvisioningService {
|
||||||
},
|
},
|
||||||
snapshot?: PersistedTeamLaunchSnapshot | null
|
snapshot?: PersistedTeamLaunchSnapshot | null
|
||||||
): string {
|
): string {
|
||||||
const expectedTeammateCount = snapshot?.expectedMembers.length ?? run.expectedMembers.length;
|
const expectedTeammateCount = snapshot
|
||||||
|
? this.getPersistedLaunchMemberNames(snapshot).length
|
||||||
|
: run.expectedMembers.length;
|
||||||
const permissionPendingCount = snapshot
|
const permissionPendingCount = snapshot
|
||||||
? this.countSnapshotPermissionPendingMembers(snapshot)
|
? this.countSnapshotPermissionPendingMembers(snapshot)
|
||||||
: this.countRunPermissionPendingMembers(run);
|
: this.countRunPermissionPendingMembers(run);
|
||||||
|
|
@ -11908,7 +11910,8 @@ export class TeamProvisioningService {
|
||||||
return this.buildPendingBootstrapStatusMessage(prefix, run, launchSummary, snapshot);
|
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];
|
const member = snapshot.members[memberName];
|
||||||
if (!member) {
|
if (!member) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -11935,7 +11938,7 @@ export class TeamProvisioningService {
|
||||||
const primaryExpectedMembers = new Set(
|
const primaryExpectedMembers = new Set(
|
||||||
snapshot.bootstrapExpectedMembers ?? run.expectedMembers
|
snapshot.bootstrapExpectedMembers ?? run.expectedMembers
|
||||||
);
|
);
|
||||||
const secondaryPendingMembers = snapshot.expectedMembers.filter((memberName) => {
|
const secondaryPendingMembers = persistedMemberNames.filter((memberName) => {
|
||||||
if (primaryExpectedMembers.has(memberName)) {
|
if (primaryExpectedMembers.has(memberName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -11976,7 +11979,7 @@ export class TeamProvisioningService {
|
||||||
|
|
||||||
private countSnapshotPermissionPendingMembers(snapshot: PersistedTeamLaunchSnapshot): number {
|
private countSnapshotPermissionPendingMembers(snapshot: PersistedTeamLaunchSnapshot): number {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const memberName of snapshot.expectedMembers) {
|
for (const memberName of this.getPersistedLaunchMemberNames(snapshot)) {
|
||||||
const member = snapshot.members[memberName];
|
const member = snapshot.members[memberName];
|
||||||
if (!member) {
|
if (!member) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -11998,10 +12001,16 @@ export class TeamProvisioningService {
|
||||||
},
|
},
|
||||||
snapshot?: PersistedTeamLaunchSnapshot | null
|
snapshot?: PersistedTeamLaunchSnapshot | null
|
||||||
): boolean {
|
): 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;
|
return launchSummary.pendingCount > 0 && expectedCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getPersistedLaunchMemberNames(snapshot: PersistedTeamLaunchSnapshot): string[] {
|
||||||
|
return Array.from(new Set([...snapshot.expectedMembers, ...Object.keys(snapshot.members)]));
|
||||||
|
}
|
||||||
|
|
||||||
private buildLiveLaunchSnapshotForRun(
|
private buildLiveLaunchSnapshotForRun(
|
||||||
run: ProvisioningRun,
|
run: ProvisioningRun,
|
||||||
launchPhase: PersistedTeamLaunchPhase = run.provisioningComplete ? 'finished' : 'active'
|
launchPhase: PersistedTeamLaunchPhase = run.provisioningComplete ? 'finished' : 'active'
|
||||||
|
|
|
||||||
|
|
@ -2227,6 +2227,59 @@ describe('TeamProvisioningService', () => {
|
||||||
expect(message).not.toContain('/0');
|
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 () => {
|
it('launches the OpenCode secondary lane with side-lane provider and member runtime identity', async () => {
|
||||||
const svc = new TeamProvisioningService();
|
const svc = new TeamProvisioningService();
|
||||||
const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
|
const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue