fix(team): keep launch summary projection aligned
This commit is contained in:
parent
f036cf0386
commit
33f51b68a7
2 changed files with 56 additions and 3 deletions
|
|
@ -30,6 +30,10 @@ export interface PersistedTeamLaunchSummaryProjection extends LaunchStateSummary
|
||||||
mixedAware?: true;
|
mixedAware?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPersistedLaunchMemberNames(snapshot: PersistedTeamLaunchSnapshot): string[] {
|
||||||
|
return Array.from(new Set([...snapshot.expectedMembers, ...Object.keys(snapshot.members)]));
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeIsoDate(value: unknown): string | null {
|
function normalizeIsoDate(value: unknown): string | null {
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -48,7 +52,8 @@ function toMillis(value: string | undefined | null): number {
|
||||||
export function createLaunchStateSummary(
|
export function createLaunchStateSummary(
|
||||||
snapshot: PersistedTeamLaunchSnapshot
|
snapshot: PersistedTeamLaunchSnapshot
|
||||||
): LaunchStateSummary {
|
): LaunchStateSummary {
|
||||||
const missingMembers = snapshot.expectedMembers.filter((name) => {
|
const persistedMemberNames = getPersistedLaunchMemberNames(snapshot);
|
||||||
|
const missingMembers = persistedMemberNames.filter((name) => {
|
||||||
const member = snapshot.members[name];
|
const member = snapshot.members[name];
|
||||||
return member?.launchState === 'failed_to_start';
|
return member?.launchState === 'failed_to_start';
|
||||||
});
|
});
|
||||||
|
|
@ -57,8 +62,8 @@ export function createLaunchStateSummary(
|
||||||
...(snapshot.teamLaunchState === 'partial_failure'
|
...(snapshot.teamLaunchState === 'partial_failure'
|
||||||
? { partialLaunchFailure: true as const }
|
? { partialLaunchFailure: true as const }
|
||||||
: {}),
|
: {}),
|
||||||
...(snapshot.expectedMembers.length > 0
|
...(persistedMemberNames.length > 0
|
||||||
? { expectedMemberCount: snapshot.expectedMembers.length }
|
? { expectedMemberCount: persistedMemberNames.length }
|
||||||
: {}),
|
: {}),
|
||||||
...(snapshot.summary.confirmedCount > 0
|
...(snapshot.summary.confirmedCount > 0
|
||||||
? { confirmedMemberCount: snapshot.summary.confirmedCount }
|
? { confirmedMemberCount: snapshot.summary.confirmedCount }
|
||||||
|
|
|
||||||
|
|
@ -174,4 +174,52 @@ describe('TeamLaunchSummaryProjection', () => {
|
||||||
})
|
})
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses the union of expectedMembers and persisted members for summary projection', () => {
|
||||||
|
const summary = createPersistedLaunchSummaryProjection({
|
||||||
|
version: 2,
|
||||||
|
teamName: 'mixed-team',
|
||||||
|
updatedAt: '2026-04-22T12:00:00.000Z',
|
||||||
|
launchPhase: 'finished',
|
||||||
|
expectedMembers: ['alice'],
|
||||||
|
members: {
|
||||||
|
alice: {
|
||||||
|
name: 'alice',
|
||||||
|
providerId: 'codex',
|
||||||
|
launchState: 'confirmed_alive',
|
||||||
|
agentToolAccepted: true,
|
||||||
|
runtimeAlive: true,
|
||||||
|
bootstrapConfirmed: true,
|
||||||
|
hardFailure: false,
|
||||||
|
lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
bob: {
|
||||||
|
name: 'bob',
|
||||||
|
providerId: 'opencode',
|
||||||
|
launchState: 'failed_to_start',
|
||||||
|
agentToolAccepted: true,
|
||||||
|
runtimeAlive: false,
|
||||||
|
bootstrapConfirmed: false,
|
||||||
|
hardFailure: true,
|
||||||
|
hardFailureReason: 'Side lane failed',
|
||||||
|
lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
confirmedCount: 1,
|
||||||
|
pendingCount: 0,
|
||||||
|
failedCount: 1,
|
||||||
|
runtimeAlivePendingCount: 0,
|
||||||
|
},
|
||||||
|
teamLaunchState: 'partial_failure',
|
||||||
|
} as never);
|
||||||
|
|
||||||
|
expect(summary).toMatchObject({
|
||||||
|
expectedMemberCount: 2,
|
||||||
|
confirmedMemberCount: 1,
|
||||||
|
missingMembers: ['bob'],
|
||||||
|
failedCount: 1,
|
||||||
|
teamLaunchState: 'partial_failure',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue