diff --git a/src/renderer/utils/memberLaunchDiagnostics.ts b/src/renderer/utils/memberLaunchDiagnostics.ts index 2ea06e17..208082cf 100644 --- a/src/renderer/utils/memberLaunchDiagnostics.ts +++ b/src/renderer/utils/memberLaunchDiagnostics.ts @@ -381,11 +381,6 @@ function buildDiagnosticHints(input: { 'Bootstrap submit was rejected because local prompt/bash command queue was not empty.' ); } - if (textIncludesAny(text, ['no stdin data received in 3s'])) { - hints.push( - 'CLI read empty stdin before bootstrap submit; verify headless teammate runtime flag/env and startup input handling.' - ); - } if ( textIncludesAny(text, ['bootstrap_submit_rejected', 'submit rejected by local prompt handler']) ) { @@ -401,6 +396,11 @@ function buildDiagnosticHints(input: { ) { hints.push('Parent process timed out waiting for durable bootstrap_submitted evidence.'); } + if (textIncludesAny(text, ['no stdin data received in 3s'])) { + hints.push( + 'CLI read empty stdin before bootstrap submit; verify headless teammate runtime flag/env and startup input handling.' + ); + } if ( input.livenessKind === 'stale_metadata' || textIncludesAny(text, ['persisted runtime pid is not alive']) diff --git a/test/renderer/utils/memberLaunchDiagnostics.test.ts b/test/renderer/utils/memberLaunchDiagnostics.test.ts index 32e57b57..ea7fb2e0 100644 --- a/test/renderer/utils/memberLaunchDiagnostics.test.ts +++ b/test/renderer/utils/memberLaunchDiagnostics.test.ts @@ -1,12 +1,11 @@ -import { describe, expect, it } from 'vitest'; - import { buildMemberLaunchDiagnosticsPayload, formatMemberLaunchDiagnosticsPayload, + getMemberLaunchDiagnosticsErrorMessage, hasMemberLaunchDiagnosticsDetails, hasMemberLaunchDiagnosticsError, - getMemberLaunchDiagnosticsErrorMessage, } from '@renderer/utils/memberLaunchDiagnostics'; +import { describe, expect, it } from 'vitest'; describe('member launch diagnostics', () => { it('builds a bounded copy payload from spawn and runtime evidence', () => { @@ -1180,4 +1179,59 @@ describe('member launch diagnostics', () => { 'Anthropic API error. resolved_behavior_changed:old->new' ); }); + + it('prioritizes durable bootstrap timeout over no-stdin stderr noise', () => { + const payload = buildMemberLaunchDiagnosticsPayload({ + teamName: 'signal-ops', + runId: 'run-mailbox-written-no-submit', + memberName: 'atlas', + spawnEntry: { + status: 'error', + launchState: 'failed_to_start', + hardFailure: true, + error: + 'Teammate process atlas@signal-ops did not submit bootstrap prompt: timed out waiting for bootstrap_submitted; last transport stage: mailbox_bootstrap_written: messageId=bootstrap-atlas-1 Last stderr: Warning: no stdin data received in 3s, proceeding without it.', + livenessKind: 'stale_metadata', + runtimeDiagnostic: 'persisted runtime pid is not alive', + runtimeDiagnosticSeverity: 'warning', + updatedAt: '2026-05-19T13:53:36.668Z', + }, + }); + + expect(payload.probableCause).toBe( + 'Parent process timed out waiting for durable bootstrap_submitted evidence.' + ); + expect(payload.diagnosticHints?.[0]).toBe( + 'Parent process timed out waiting for durable bootstrap_submitted evidence.' + ); + expect(payload.diagnosticHints).toContain( + 'CLI read empty stdin before bootstrap submit; verify headless teammate runtime flag/env and startup input handling.' + ); + }); + + it('prioritizes bootstrap submit rejection over no-stdin stderr noise', () => { + const payload = buildMemberLaunchDiagnosticsPayload({ + teamName: 'signal-ops', + runId: 'run-submit-rejected-no-stdin', + memberName: 'bob', + spawnEntry: { + status: 'error', + launchState: 'failed_to_start', + hardFailure: true, + error: + 'Teammate process bob@signal-ops did not submit bootstrap prompt: timed out waiting for bootstrap_submitted; last transport stage: bootstrap_submit_rejected: submit rejected by local prompt handler retryable=true Last stderr: Warning: no stdin data received in 3s, proceeding without it.', + updatedAt: '2026-05-19T13:53:36.668Z', + }, + }); + + expect(payload.probableCause).toBe( + 'The teammate process observed bootstrap mail, but local prompt submission did not accept the bootstrap turn.' + ); + expect(payload.diagnosticHints?.[0]).toBe( + 'The teammate process observed bootstrap mail, but local prompt submission did not accept the bootstrap turn.' + ); + expect(payload.diagnosticHints).toContain( + 'CLI read empty stdin before bootstrap submit; verify headless teammate runtime flag/env and startup input handling.' + ); + }); });