diff --git a/src/main/services/team/provisioning/TeamProvisioningLaunchFailurePolicy.ts b/src/main/services/team/provisioning/TeamProvisioningLaunchFailurePolicy.ts index be073191..8d99b5a6 100644 --- a/src/main/services/team/provisioning/TeamProvisioningLaunchFailurePolicy.ts +++ b/src/main/services/team/provisioning/TeamProvisioningLaunchFailurePolicy.ts @@ -1,7 +1,4 @@ -import { - isProvisionedButNotAliveFailureReason, - stripProcessTableUnavailableDiagnosticSuffix, -} from '@shared/utils/teamLaunchFailureReason'; +import { stripProcessTableUnavailableDiagnosticSuffix } from '@shared/utils/teamLaunchFailureReason'; import { mentionsProcessTableUnavailable } from './TeamProvisioningLaunchDiagnostics'; import { isBootstrapInstructionPrompt } from './TeamProvisioningPromptBuilders'; @@ -72,7 +69,20 @@ export function isBootstrapMcpResourceReadFailureReason(reason?: string): boolea } export function isBootstrapCheckInTimeoutFailureReason(reason?: string): boolean { - return reason?.trim() === 'Teammate was registered but did not bootstrap-confirm before timeout.'; + const text = reason?.trim(); + if (!text) { + return false; + } + if (text === 'Teammate was registered but did not bootstrap-confirm before timeout.') { + return true; + } + const normalized = text.toLowerCase(); + return ( + normalized.includes('bootstrap prompt was submitted') && + normalized.includes('did not bootstrap-confirm') && + normalized.includes('submitted-confirmation timeout') && + normalized.includes('last transport stage: bootstrap_submitted') + ); } export function isBootstrapInstructionPromptFailureReason(reason?: string): boolean { diff --git a/test/main/services/team/TeamProvisioningLaunchFailurePolicy.test.ts b/test/main/services/team/TeamProvisioningLaunchFailurePolicy.test.ts index 99a94de7..cb705220 100644 --- a/test/main/services/team/TeamProvisioningLaunchFailurePolicy.test.ts +++ b/test/main/services/team/TeamProvisioningLaunchFailurePolicy.test.ts @@ -3,8 +3,8 @@ import { isAutoClearableLaunchFailureReason, isBootstrapCheckInTimeoutFailureReason, isBootstrapInstructionPromptFailureReason, - isCliProvisionedButNotAliveFailureReason, isBootstrapMcpResourceReadFailureReason, + isCliProvisionedButNotAliveFailureReason, isConfigRegistrationFailureReason, isLaunchCleanupBootstrapIncompleteFailureReason, isLaunchGraceWindowFailureReason, @@ -68,6 +68,16 @@ describe('TeamProvisioningLaunchFailurePolicy', () => { 'Teammate was registered but did not bootstrap-confirm before timeout.' ) ).toBe(true); + expect( + isBootstrapCheckInTimeoutFailureReason( + 'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before submitted-confirmation timeout (3m). Last transport stage: bootstrap_submitted' + ) + ).toBe(true); + expect( + isBootstrapCheckInTimeoutFailureReason( + 'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before timeout.' + ) + ).toBe(false); expect( isBootstrapInstructionPromptFailureReason( 'You are bootstrapping into team atlas. Your first action is to call the MCP tool member_briefing.' @@ -107,6 +117,16 @@ describe('TeamProvisioningLaunchFailurePolicy', () => { 'Teammate did not join within the launch grace window.; process table unavailable' ) ).toBe(true); + expect( + isAutoClearableLaunchFailureReason( + 'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before submitted-confirmation timeout (3m). Last transport stage: bootstrap_submitted' + ) + ).toBe(true); + expect( + isAutoClearableLaunchFailureReason( + 'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before timeout.' + ) + ).toBe(false); expect( isAutoClearableLaunchFailureReason( 'CLI process exited (code 1) \u2014 team provisioned but not alive' diff --git a/test/main/services/team/TeamProvisioningService.test.ts b/test/main/services/team/TeamProvisioningService.test.ts index 0554258f..85381cd7 100644 --- a/test/main/services/team/TeamProvisioningService.test.ts +++ b/test/main/services/team/TeamProvisioningService.test.ts @@ -21421,7 +21421,8 @@ describe('TeamProvisioningService', () => { status: 'failed', lastAttemptAt: Date.parse(acceptedAt), lastObservedAt: Date.parse(failureAt), - failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.', + failureReason: + 'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before submitted-confirmation timeout (3m). Last transport stage: bootstrap_submitted', }, ], failureAt