fix(team): trust persisted permission launch state

This commit is contained in:
777genius 2026-04-23 02:31:56 +03:00
parent 842a929a83
commit 34dd669d88
2 changed files with 76 additions and 3 deletions

View file

@ -11855,9 +11855,12 @@ export class TeamProvisioningService {
confirmedCount: number;
pendingCount: number;
runtimeAlivePendingCount: number;
}
},
snapshot?: PersistedTeamLaunchSnapshot | null
): string {
const permissionPendingCount = this.countRunPermissionPendingMembers(run);
const permissionPendingCount = snapshot
? this.countSnapshotPermissionPendingMembers(snapshot)
: this.countRunPermissionPendingMembers(run);
if (
launchSummary.pendingCount > 0 &&
permissionPendingCount > 0 &&
@ -11901,7 +11904,7 @@ export class TeamProvisioningService {
): string {
const mixedSecondaryLanes = run.mixedSecondaryLanes ?? [];
if (!snapshot || mixedSecondaryLanes.length === 0) {
return this.buildPendingBootstrapStatusMessage(prefix, run, launchSummary);
return this.buildPendingBootstrapStatusMessage(prefix, run, launchSummary, snapshot);
}
const allPendingMembers = snapshot.expectedMembers.filter((memberName) => {
@ -11970,6 +11973,23 @@ export class TeamProvisioningService {
return count;
}
private countSnapshotPermissionPendingMembers(snapshot: PersistedTeamLaunchSnapshot): number {
let count = 0;
for (const memberName of snapshot.expectedMembers) {
const member = snapshot.members[memberName];
if (!member) {
continue;
}
if (
member.launchState === 'runtime_pending_permission' ||
(member.pendingPermissionRequestIds?.length ?? 0) > 0
) {
count += 1;
}
}
return count;
}
private hasPendingLaunchMembers(
run: ProvisioningRun,
launchSummary: {

View file

@ -2121,6 +2121,59 @@ describe('TeamProvisioningService', () => {
);
});
it('trusts persisted snapshot permission state for pure teams when live run statuses are absent', () => {
const svc = new TeamProvisioningService();
const run = createMemberSpawnRun({
teamName: 'pure-team',
expectedMembers: ['alice'],
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,
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>) => ({