113 lines
4.5 KiB
TypeScript
113 lines
4.5 KiB
TypeScript
import {
|
|
deriveMemberLaunchState,
|
|
isAutoClearableLaunchFailureReason,
|
|
isBootstrapCheckInTimeoutFailureReason,
|
|
isBootstrapInstructionPromptFailureReason,
|
|
isBootstrapMcpResourceReadFailureReason,
|
|
isConfigRegistrationFailureReason,
|
|
isLaunchCleanupBootstrapIncompleteFailureReason,
|
|
isLaunchGraceWindowFailureReason,
|
|
isNeverSpawnedDuringLaunchReason,
|
|
isOpenCodeBridgeLaunchFailureReason,
|
|
isProcessTableUnavailableFailureReason,
|
|
isRegisteredRuntimeMetadataFailureReason,
|
|
stripProcessTableUnavailableDiagnosticSuffix,
|
|
} from '@main/services/team/provisioning/TeamProvisioningLaunchFailurePolicy';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
describe('TeamProvisioningLaunchFailurePolicy', () => {
|
|
it('recognizes exact launch failure reasons that are safe to auto-clear', () => {
|
|
expect(isNeverSpawnedDuringLaunchReason(' Teammate was never spawned during launch. ')).toBe(
|
|
true
|
|
);
|
|
expect(
|
|
isLaunchGraceWindowFailureReason('Teammate did not join within the launch grace window.')
|
|
).toBe(true);
|
|
expect(
|
|
isConfigRegistrationFailureReason(
|
|
'Teammate was not registered in config.json during launch. Persistent spawn failed.'
|
|
)
|
|
).toBe(true);
|
|
expect(isOpenCodeBridgeLaunchFailureReason('OpenCode bridge reported member launch failure')).toBe(
|
|
true
|
|
);
|
|
expect(
|
|
isRegisteredRuntimeMetadataFailureReason('registered runtime metadata without live process')
|
|
).toBe(true);
|
|
});
|
|
|
|
it('recognizes bootstrap-specific failure reasons without accepting unrelated text', () => {
|
|
expect(
|
|
isBootstrapMcpResourceReadFailureReason(
|
|
'resources/read failed for member_briefing: MCP error method not found'
|
|
)
|
|
).toBe(true);
|
|
expect(isBootstrapMcpResourceReadFailureReason('resources/read failed for other resource')).toBe(
|
|
false
|
|
);
|
|
expect(
|
|
isBootstrapCheckInTimeoutFailureReason(
|
|
'Teammate was registered but did not bootstrap-confirm before timeout.'
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
isBootstrapInstructionPromptFailureReason(
|
|
'You are bootstrapping into team atlas. Your first action is to call the MCP tool member_briefing.'
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
isLaunchCleanupBootstrapIncompleteFailureReason(
|
|
'Launch ended before teammate bootstrap completed. Runtime process was alive after bootstrap failure'
|
|
)
|
|
).toBe(true);
|
|
});
|
|
|
|
it('handles process-table unavailable reasons and suffixes conservatively', () => {
|
|
expect(isProcessTableUnavailableFailureReason('process table unavailable')).toBe(true);
|
|
expect(
|
|
isProcessTableUnavailableFailureReason(
|
|
'runtime pid could not be verified because process table is unavailable'
|
|
)
|
|
).toBe(true);
|
|
expect(isProcessTableUnavailableFailureReason('runtime failed; process table unavailable')).toBe(
|
|
false
|
|
);
|
|
expect(
|
|
stripProcessTableUnavailableDiagnosticSuffix(
|
|
'Teammate did not join within the launch grace window.; process table unavailable'
|
|
)
|
|
).toBe('Teammate did not join within the launch grace window.');
|
|
});
|
|
|
|
it('keeps auto-clear policy narrow but accepts known recoverable suffixes', () => {
|
|
expect(
|
|
isAutoClearableLaunchFailureReason('Teammate was never spawned during launch.')
|
|
).toBe(true);
|
|
expect(isAutoClearableLaunchFailureReason('process table is unavailable')).toBe(true);
|
|
expect(
|
|
isAutoClearableLaunchFailureReason(
|
|
'Teammate did not join within the launch grace window.; process table unavailable'
|
|
)
|
|
).toBe(true);
|
|
expect(isAutoClearableLaunchFailureReason('model not found')).toBe(false);
|
|
expect(isAutoClearableLaunchFailureReason(undefined)).toBe(false);
|
|
});
|
|
|
|
it('derives member launch state by the existing precedence order', () => {
|
|
expect(deriveMemberLaunchState({ skippedForLaunch: true, hardFailure: true })).toBe(
|
|
'skipped_for_launch'
|
|
);
|
|
expect(deriveMemberLaunchState({ hardFailure: true, bootstrapConfirmed: true })).toBe(
|
|
'failed_to_start'
|
|
);
|
|
expect(deriveMemberLaunchState({ bootstrapConfirmed: true })).toBe('confirmed_alive');
|
|
expect(deriveMemberLaunchState({ pendingPermissionRequestIds: ['req-1'] })).toBe(
|
|
'runtime_pending_permission'
|
|
);
|
|
expect(deriveMemberLaunchState({ runtimeAlive: true })).toBe('runtime_pending_bootstrap');
|
|
expect(deriveMemberLaunchState({ agentToolAccepted: true })).toBe(
|
|
'runtime_pending_bootstrap'
|
|
);
|
|
expect(deriveMemberLaunchState({})).toBe('starting');
|
|
});
|
|
});
|