Files

1 line
67 KiB
JSON

{"repo": "777genius/agent-teams-ai", "files": {"test/renderer/utils/memberHelpers.test.ts": {"2544602890ac60d0f7e933fa6498ca27d0cf8684": {"t": 1785596392.3274708, "n": 66802, "h": "3662b480239ff11f", "c": "import {\n buildMemberLaunchPresentation,\n getLaunchAwarePresenceLabel,\n getMemberRuntimeAdvisoryLabel,\n getMemberRuntimeAdvisoryTitle,\n getMemberRuntimeAdvisoryTone,\n getSpawnAwareDotClass,\n getSpawnAwarePresenceLabel,\n getSpawnCardClass,\n isOpenCodeRelaunchActionable,\n shouldDisplayMemberCurrentTask,\n} from '@renderer/utils/memberHelpers';\n\nimport type {\n MemberSpawnStatusEntry,\n ResolvedTeamMember,\n TeamAgentRuntimeEntry,\n} from '@shared/types';\n\nconst member: ResolvedTeamMember = {\n name: 'alice',\n status: 'unknown',\n taskCount: 0,\n currentTaskId: null,\n lastActiveAt: null,\n messageCount: 0,\n color: 'blue',\n agentType: 'reviewer',\n role: 'Reviewer',\n providerId: 'gemini',\n removedAt: undefined,\n};\n\nconst provisionedButNotAliveSpawn: MemberSpawnStatusEntry = {\n status: 'error',\n launchState: 'failed_to_start',\n updatedAt: '2026-05-25T20:14:02.147Z',\n runtimeAlive: false,\n bootstrapConfirmed: true,\n hardFailure: true,\n hardFailureReason: 'CLI process exited (code 1) \\u2014 team provisioned but not alive',\n livenessKind: 'confirmed_bootstrap',\n};\n\nconst processTableUnavailableRuntime: TeamAgentRuntimeEntry = {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'anthropic',\n livenessKind: 'confirmed_bootstrap',\n runtimeDiagnostic: 'runtime pid could not be verified because process table is unavailable',\n runtimeDiagnosticSeverity: 'warning',\n updatedAt: '2026-05-25T20:14:05.411Z',\n};\n\ndescribe('memberHelpers spawn-aware presence', () => {\n it('does not display current task labels for offline or terminal launch states', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: false,\n })\n ).toBe(false);\n\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'offline',\n spawnLaunchState: 'confirmed_alive',\n spawnRuntimeAlive: false,\n })\n ).toBe(false);\n\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnRuntimeAlive: false,\n })\n ).toBe(false);\n\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'error',\n spawnLaunchState: 'failed_to_start',\n })\n ).toBe(false);\n });\n\n it('does not display current task labels for runtime entries without a live agent runtime', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'stale_metadata',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n })\n ).toBe(false);\n\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n })\n ).toBe(false);\n });\n\n it('keeps current task labels for confirmed online members', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnRuntimeAlive: true,\n runtimeEntry: {\n memberName: 'alice',\n alive: true,\n restartable: true,\n providerId: 'gemini',\n livenessKind: 'confirmed_bootstrap',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n })\n ).toBe(true);\n });\n\n it('treats bootstrap-confirmed provisioned-but-not-alive entries as active for task display', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnEntry: provisionedButNotAliveSpawn,\n runtimeEntry: processTableUnavailableRuntime,\n })\n ).toBe(true);\n });\n\n it('treats spawn-only bootstrap-confirmed provisioned-but-not-alive entries as active for task display', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnEntry: provisionedButNotAliveSpawn,\n })\n ).toBe(true);\n });\n\n it('does not show task activity for provisioned-but-not-alive entries with runtime errors', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnEntry: provisionedButNotAliveSpawn,\n runtimeEntry: {\n ...processTableUnavailableRuntime,\n runtimeDiagnostic: 'Runtime process crashed',\n runtimeDiagnosticSeverity: 'error',\n },\n })\n ).toBe(false);\n\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnEntry: {\n ...provisionedButNotAliveSpawn,\n runtimeDiagnostic: 'Runtime process crashed',\n runtimeDiagnosticSeverity: 'error',\n },\n runtimeEntry: processTableUnavailableRuntime,\n })\n ).toBe(false);\n });\n\n it('does not show task activity for unsafe provisioned-but-not-alive runtime candidates', () => {\n expect(\n shouldDisplayMemberCurrentTask({\n member: { ...member, currentTaskId: 'task-1' },\n isTeamAlive: true,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnRuntimeAlive: true,\n spawnEntry: {\n ...provisionedButNotAliveSpawn,\n status: 'online',\n launchState: 'confirmed_alive',\n runtimeAlive: true,\n },\n runtimeEntry: {\n ...processTableUnavailableRuntime,\n alive: false,\n livenessKind: 'runtime_process_candidate',\n runtimeDiagnostic:\n 'OpenCode runtime process detected, but teammate bootstrap is not confirmed',\n },\n })\n ).toBe(false);\n });\n\n it('shows process-online teammates as online with a green dot', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n 'process',\n true,\n false,\n true,\n false,\n undefined\n )\n ).toBe('online');\n\n expect(\n getSpawnAwareDotClass(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n true,\n false,\n true,\n false,\n undefined\n )\n ).toContain('bg-emerald-400');\n expect(\n getSpawnAwareDotClass(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n true,\n false,\n true,\n false,\n undefined\n )\n ).toContain('animate-pulse');\n });\n\n it('keeps accepted-but-not-yet-online teammates in starting state', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'waiting',\n 'starting',\n undefined,\n false,\n false,\n true,\n false,\n undefined\n )\n ).toBe('starting');\n });\n\n it('labels queued OpenCode lanes separately from active startup', () => {\n const openCodeMember: ResolvedTeamMember = { ...member, providerId: 'opencode' };\n\n expect(\n buildMemberLaunchPresentation({\n member: openCodeMember,\n spawnStatus: 'waiting',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeAdvisory: undefined,\n isLaunchSettling: true,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'queued',\n launchVisualState: 'queued',\n launchStatusLabel: 'queued',\n dotClass: expect.stringContaining('bg-zinc-400'),\n });\n });\n\n it('does not label non-OpenCode waiting lanes as queued', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'waiting',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeAdvisory: undefined,\n isLaunchSettling: true,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'starting',\n launchVisualState: 'waiting',\n launchStatusLabel: 'waiting to start',\n });\n });\n\n it('marks long-running starting states as stale without making them failed', () => {\n const presentation = buildMemberLaunchPresentation({\n member,\n spawnStatus: 'waiting',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n spawnUpdatedAt: '2026-05-08T12:00:00.000Z',\n runtimeAdvisory: undefined,\n isLaunchSettling: true,\n isTeamAlive: true,\n isTeamProvisioning: false,\n nowMs: Date.parse('2026-05-08T12:03:00.000Z'),\n });\n\n expect(presentation.presenceLabel).toBe('starting stale');\n expect(presentation.launchVisualState).toBe('starting_stale');\n expect(presentation.launchStatusLabel).toBe('starting stale');\n expect(presentation.dotClass).toContain('bg-amber-400');\n expect(presentation.dotClass).not.toContain('animate-pulse');\n expect(presentation.cardClass).not.toContain('member-waiting-shimmer');\n expect(presentation.spawnBadgeLabel).toBe('starting stale');\n });\n\n it('keeps OpenCode runtime evidence states more specific than queued', () => {\n const openCodeMember: ResolvedTeamMember = { ...member, providerId: 'opencode' };\n\n expect(\n buildMemberLaunchPresentation({\n member: openCodeMember,\n spawnStatus: 'waiting',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'registered_only',\n runtimeDiagnostic: 'registered runtime metadata without live process',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: true,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'registered',\n launchVisualState: 'registered_only',\n launchStatusLabel: 'registered',\n });\n });\n\n it('keeps starting visuals after provisioning already transitioned out of active state', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'spawning',\n 'starting',\n undefined,\n false,\n false,\n true,\n false,\n undefined\n )\n ).toBe('starting');\n\n expect(\n getSpawnAwareDotClass(member, 'spawning', 'starting', false, false, true, false, undefined)\n ).toContain('bg-amber-400');\n\n expect(getSpawnCardClass('spawning', 'starting', false, false)).toContain(\n 'member-waiting-shimmer'\n );\n });\n\n it('shows offline instead of stale starting visuals when the team is offline', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'spawning',\n 'starting',\n undefined,\n false,\n false,\n false,\n false,\n undefined\n )\n ).toBe('offline');\n\n expect(\n getSpawnAwareDotClass(member, 'spawning', 'starting', false, false, false, false, undefined)\n ).toContain('bg-red-400');\n\n expect(getSpawnCardClass('spawning', 'starting', false, false, false, false)).toBe('');\n });\n\n it('keeps runtime-pending teammates in starting state while launch is still settling', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n 'process',\n true,\n true,\n true,\n false,\n undefined\n )\n ).toBe('starting');\n\n expect(\n getSpawnAwareDotClass(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n true,\n true,\n true,\n false,\n undefined\n )\n ).toContain('bg-zinc-400');\n\n expect(\n getSpawnCardClass('online', 'runtime_pending_bootstrap', true, true, true, false)\n ).toContain('member-waiting-shimmer');\n });\n\n it('shows confirmed teammates as ready instead of idle while launch is still settling', () => {\n expect(\n getSpawnAwarePresenceLabel(\n member,\n 'online',\n 'confirmed_alive',\n 'heartbeat',\n true,\n true,\n true,\n false,\n undefined\n )\n ).toBe('ready');\n });\n\n it('derives runtime-pending and settling visual states from the same launch inputs', () => {\n const runtimePending = buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'runtime_pending_bootstrap',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n const settling = buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'heartbeat',\n spawnRuntimeAlive: true,\n runtimeAdvisory: undefined,\n isLaunchSettling: true,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(runtimePending.launchVisualState).toBe('runtime_pending');\n expect(runtimePending.launchStatusLabel).toBe('waiting for bootstrap');\n expect(settling.launchVisualState).toBe('settling');\n expect(settling.launchStatusLabel).toBe('joining team');\n });\n\n it('surfaces permission-blocked teammates as awaiting permission instead of generic starting', () => {\n const permissionPending = buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'runtime_pending_permission',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(permissionPending.presenceLabel).toBe('awaiting permission');\n expect(permissionPending.launchVisualState).toBe('permission_pending');\n expect(permissionPending.launchStatusLabel).toBe('awaiting permission');\n expect(permissionPending.dotClass).toContain('bg-amber-400');\n expect(permissionPending.cardClass).toContain('member-waiting-shimmer');\n });\n\n it('surfaces bootstrap-stalled OpenCode teammates as actionable pending state', () => {\n const bootstrapStalled = buildMemberLaunchPresentation({\n member: { ...member, providerId: 'opencode' },\n spawnStatus: 'waiting',\n spawnLaunchState: 'runtime_pending_bootstrap',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: true,\n spawnBootstrapStalled: true,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(bootstrapStalled.presenceLabel).toBe('bootstrap stalled');\n expect(bootstrapStalled.launchVisualState).toBe('bootstrap_stalled');\n expect(bootstrapStalled.launchStatusLabel).toBe('bootstrap stalled');\n expect(bootstrapStalled.dotClass).toContain('bg-amber-400');\n });\n\n it('surfaces strict runtime liveness diagnostics as launch labels', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'waiting',\n spawnLaunchState: 'runtime_pending_bootstrap',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n livenessKind: 'shell_only',\n pidSource: 'tmux_pane',\n runtimeDiagnostic: 'tmux pane foreground command is zsh',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'shell only',\n launchVisualState: 'shell_only',\n launchStatusLabel: 'shell only',\n });\n\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'runtime_pending_bootstrap',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n livenessKind: 'runtime_process_candidate',\n runtimeDiagnostic: 'OpenCode runtime process detected, but bootstrap is not confirmed',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'bootstrap unconfirmed',\n launchVisualState: 'runtime_candidate',\n launchStatusLabel: 'bootstrap unconfirmed',\n dotClass: expect.stringContaining('bg-amber-400'),\n });\n\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n spawnBootstrapConfirmed: true,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n livenessKind: 'registered_only',\n runtimeDiagnostic: 'registered runtime metadata without live process',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'registered',\n launchVisualState: 'registered_only',\n launchStatusLabel: 'registered',\n dotClass: expect.stringContaining('bg-red-400'),\n });\n\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'waiting',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n spawnBootstrapConfirmed: true,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n livenessKind: 'registered_only',\n runtimeDiagnostic: 'registered runtime metadata without live process',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'registered',\n launchVisualState: 'registered_only',\n launchStatusLabel: 'registered',\n dotClass: expect.stringContaining('bg-red-400'),\n });\n });\n\n it('marks confirmed members offline when spawn runtime liveness is false', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: false,\n spawnBootstrapConfirmed: true,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'stale runtime',\n launchVisualState: 'stale_runtime',\n launchStatusLabel: 'stale runtime',\n dotClass: expect.stringContaining('bg-red-400'),\n });\n });\n\n it('marks dead confirmed runtime entries as stale runtime', () => {\n for (const livenessKind of ['runtime_process', 'confirmed_bootstrap'] as const) {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n spawnBootstrapConfirmed: true,\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n livenessKind,\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'stale runtime',\n launchVisualState: 'stale_runtime',\n launchStatusLabel: 'stale runtime',\n dotClass: expect.stringContaining('bg-red-400'),\n });\n }\n });\n\n it('marks stuck OpenCode launch states as manually relaunchable', () => {\n const openCodeMember: ResolvedTeamMember = { ...member, providerId: 'opencode' };\n\n expect(\n isOpenCodeRelaunchActionable({\n member: openCodeMember,\n spawnEntry: {\n status: 'online',\n launchState: 'confirmed_alive',\n runtimeAlive: false,\n bootstrapConfirmed: false,\n livenessKind: 'registered_only',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'registered_only',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n })\n ).toBe(true);\n\n expect(\n isOpenCodeRelaunchActionable({\n member: openCodeMember,\n spawnEntry: {\n status: 'online',\n launchState: 'runtime_pending_bootstrap',\n runtimeAlive: true,\n bootstrapConfirmed: false,\n livenessKind: 'runtime_process_candidate',\n firstSpawnAcceptedAt: '2026-04-24T12:00:00.000Z',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeEntry: {\n memberName: 'alice',\n alive: true,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'runtime_process_candidate',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n nowMs: Date.parse('2026-04-24T12:06:00.000Z'),\n })\n ).toBe(true);\n });\n\n it('marks unsafe provisioned-but-not-alive OpenCode entries as relaunchable', () => {\n expect(\n isOpenCodeRelaunchActionable({\n member: { ...member, providerId: 'opencode' },\n spawnEntry: {\n status: 'error',\n launchState: 'failed_to_start',\n runtimeAlive: false,\n bootstrapConfirmed: true,\n hardFailure: true,\n hardFailureReason: 'CLI process exited (code 1) - team provisioned but not alive',\n livenessKind: 'not_found',\n runtimeDiagnostic: 'Runtime is no longer registered',\n runtimeDiagnosticSeverity: 'warning',\n updatedAt: '2026-05-25T20:14:02.147Z',\n },\n })\n ).toBe(true);\n\n expect(\n isOpenCodeRelaunchActionable({\n member: { ...member, providerId: 'opencode' },\n spawnEntry: {\n status: 'error',\n launchState: 'failed_to_start',\n runtimeAlive: false,\n bootstrapConfirmed: true,\n hardFailure: true,\n hardFailureReason: 'CLI process exited (code 1) - team provisioned but not alive',\n livenessKind: 'confirmed_bootstrap',\n runtimeDiagnostic: 'Runtime process crashed',\n runtimeDiagnosticSeverity: 'error',\n updatedAt: '2026-05-25T20:14:02.147Z',\n },\n })\n ).toBe(true);\n });\n\n it('does not mark fresh OpenCode runtime candidates as relaunchable', () => {\n expect(\n isOpenCodeRelaunchActionable({\n member: { ...member, providerId: 'opencode' },\n spawnEntry: {\n status: 'online',\n launchState: 'runtime_pending_bootstrap',\n runtimeAlive: true,\n bootstrapConfirmed: false,\n livenessKind: 'runtime_process_candidate',\n firstSpawnAcceptedAt: '2026-04-24T12:00:00.000Z',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeEntry: {\n memberName: 'alice',\n alive: true,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'runtime_process_candidate',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n nowMs: Date.parse('2026-04-24T12:01:00.000Z'),\n })\n ).toBe(false);\n });\n\n it('does not mark fresh OpenCode not-found checks as relaunchable', () => {\n expect(\n isOpenCodeRelaunchActionable({\n member: { ...member, providerId: 'opencode' },\n spawnEntry: {\n status: 'waiting',\n launchState: 'runtime_pending_bootstrap',\n runtimeAlive: false,\n bootstrapConfirmed: false,\n livenessKind: 'not_found',\n firstSpawnAcceptedAt: '2026-04-24T12:00:00.000Z',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'not_found',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n nowMs: Date.parse('2026-04-24T12:01:00.000Z'),\n })\n ).toBe(false);\n\n expect(\n isOpenCodeRelaunchActionable({\n member: { ...member, providerId: 'opencode' },\n runtimeEntry: {\n memberName: 'alice',\n alive: false,\n restartable: true,\n providerId: 'opencode',\n livenessKind: 'not_found',\n updatedAt: '2026-04-24T12:00:00.000Z',\n },\n nowMs: Date.parse('2026-04-24T12:01:00.000Z'),\n })\n ).toBe(false);\n });\n\n it('returns shared launch status labels without changing generic presence labels', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'waiting',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'starting',\n launchVisualState: 'waiting',\n launchStatusLabel: 'waiting to start',\n });\n\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'spawning',\n spawnLaunchState: 'starting',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'starting',\n launchVisualState: 'spawning',\n launchStatusLabel: 'starting',\n });\n\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: 'error',\n spawnLaunchState: 'failed_to_start',\n spawnLivenessSource: undefined,\n spawnRuntimeAlive: false,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'spawn failed',\n launchVisualState: 'error',\n launchStatusLabel: 'failed',\n });\n });\n\n it('does not render bootstrap-confirmed provisioned-but-not-alive entries as failed or stale', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n runtimeEntry: processTableUnavailableRuntime,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'idle',\n launchVisualState: null,\n launchStatusLabel: null,\n spawnBadgeLabel: null,\n });\n });\n\n it('does not render spawn-only bootstrap-confirmed provisioned-but-not-alive entries as failed or stale', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'idle',\n launchVisualState: null,\n launchStatusLabel: null,\n spawnBadgeLabel: null,\n });\n });\n\n it('does not leak safe process-table liveness into healed member visuals', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n runtimeEntry: {\n ...processTableUnavailableRuntime,\n livenessKind: 'registered_only',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'idle',\n launchVisualState: null,\n launchStatusLabel: null,\n spawnBadgeLabel: null,\n });\n });\n\n it('recognizes provisioned-but-not-alive when the reason is only in runtime diagnostics', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnRuntimeDiagnostic: provisionedButNotAliveSpawn.hardFailureReason,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n runtimeEntry: processTableUnavailableRuntime,\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'idle',\n launchVisualState: null,\n launchStatusLabel: null,\n spawnBadgeLabel: null,\n });\n });\n\n it('keeps runtime errors visible for bootstrap-confirmed provisioned-but-not-alive entries', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n spawnRuntimeDiagnosticSeverity: provisionedButNotAliveSpawn.runtimeDiagnosticSeverity,\n runtimeEntry: {\n ...processTableUnavailableRuntime,\n runtimeDiagnostic: 'Runtime process crashed',\n runtimeDiagnosticSeverity: 'error',\n },\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'spawn failed',\n launchVisualState: 'error',\n launchStatusLabel: 'failed',\n spawnBadgeLabel: 'error',\n });\n });\n\n it('keeps spawn diagnostic errors visible for bootstrap-confirmed provisioned-but-not-alive entries', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: provisionedButNotAliveSpawn.livenessKind,\n spawnRuntimeDiagnosticSeverity: 'error',\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'spawn failed',\n launchVisualState: 'error',\n launchStatusLabel: 'failed',\n });\n });\n\n it('keeps stopped runtime evidence failed for bootstrap-confirmed provisioned-but-not-alive entries', () => {\n expect(\n buildMemberLaunchPresentation({\n member,\n spawnStatus: provisionedButNotAliveSpawn.status,\n spawnLaunchState: provisionedButNotAliveSpawn.launchState,\n spawnLivenessSource: provisionedButNotAliveSpawn.livenessSource,\n spawnRuntimeAlive: provisionedButNotAliveSpawn.runtimeAlive,\n spawnBootstrapConfirmed: provisionedButNotAliveSpawn.bootstrapConfirmed,\n spawnBootstrapStalled: provisionedButNotAliveSpawn.bootstrapStalled,\n spawnAgentToolAccepted: provisionedButNotAliveSpawn.agentToolAccepted,\n spawnHardFailure: provisionedButNotAliveSpawn.hardFailure,\n spawnHardFailureReason: provisionedButNotAliveSpawn.hardFailureReason,\n spawnError: provisionedButNotAliveSpawn.error,\n spawnLivenessKind: 'not_found',\n spawnRuntimeDiagnosticSeverity: 'warning',\n runtimeAdvisory: undefined,\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n })\n ).toMatchObject({\n presenceLabel: 'spawn failed',\n launchVisualState: 'error',\n launchStatusLabel: 'failed',\n spawnBadgeLabel: 'error',\n });\n });\n\n it('renders unified retry advisory labels for provider retries', () => {\n expect(\n getMemberRuntimeAdvisoryLabel(\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2026-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'quota_exhausted',\n message: 'Gemini cli backend error: capacity exceeded.',\n },\n 'gemini',\n Date.parse('2026-04-07T09:00:00.000Z')\n )\n ).toBe('Gemini quota retry · 45s');\n\n expect(\n getMemberRuntimeAdvisoryTitle(\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2026-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'rate_limited',\n message: 'Gemini cli backend error: rate limit 429.',\n },\n 'gemini'\n )\n ).toContain('Gemini rate limited the request');\n });\n\n it('keeps network advisories provider-neutral and appends raw details to the title', () => {\n expect(\n getMemberRuntimeAdvisoryLabel(\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2026-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'network_error',\n message: 'Connection timed out while contacting provider.',\n },\n 'gemini',\n Date.parse('2026-04-07T09:00:00.000Z')\n )\n ).toBe('Network retry · 45s');\n\n expect(\n getMemberRuntimeAdvisoryTitle(\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2026-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'network_error',\n message: 'Connection timed out while contacting provider.',\n },\n 'gemini'\n )\n ).toContain('Connection timed out while contacting provider.');\n });\n\n it('renders local filesystem advisories as disk space errors', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'filesystem_error' as const,\n message: 'Local disk is full (ENOSPC). Free disk space and retry OpenCode delivery.',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('Disk space error');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'opencode')).toContain(\n 'Local disk is full or unavailable.'\n );\n });\n\n it('renders terminal API errors as errors instead of retrying status', () => {\n expect(\n getMemberRuntimeAdvisoryLabel(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'auth_error',\n statusCode: 500,\n message: 'API Error: 500 {\"error\":{\"message\":\"auth_unavailable: no auth available\"}}',\n },\n 'anthropic',\n Date.parse('2026-04-07T09:00:00.000Z')\n )\n ).toBe('Anthropic auth error');\n\n expect(\n getMemberRuntimeAdvisoryTitle(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'auth_error',\n statusCode: 500,\n message: 'auth_unavailable: no auth available',\n },\n 'anthropic'\n )\n ).toContain('Anthropic authentication error');\n });\n\n it('renders timed OpenCode quota errors with retry/reset context', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-17T21:44:34.000Z',\n retryUntil: '2026-05-18T00:00:00.502Z',\n retryDelayMs: 8_126_502,\n reasonCode: 'quota_exhausted' as const,\n message:\n 'OpenCode session status retry - attempt=1 - Free usage exceeded, subscribe to Go https://opencode.ai/go - next=2026-05-18T00:00:00.502Z',\n };\n\n expect(\n getMemberRuntimeAdvisoryLabel(advisory, 'opencode', Date.parse('2026-05-17T21:45:00.000Z'))\n ).toBe('OpenCode quota error · retry 2h 15m');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode quota exhausted.');\n expect(title).toContain('Waiting for OpenCode retry or quota reset around 00:00 UTC.');\n expect(title).toContain('Free usage exceeded');\n });\n\n it('formats raw OpenCode protocol advisory reasons before showing them in titles', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'protocol_proof_missing' as const,\n message: 'visible_reply_still_required',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode proof missing');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('warning');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n\n expect(title).toContain('OpenCode delivery completed without required visible/progress proof.');\n expect(title).toContain('OpenCode responded, but did not create a visible message_send reply.');\n expect(title).not.toContain('visible_reply_still_required');\n });\n\n it('hides internal OpenCode bootstrap MCP diagnostics from advisory titles', () => {\n const title = getMemberRuntimeAdvisoryTitle(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'backend_error',\n message:\n 'OpenCode bootstrap MCP did not complete required tools before assistant response: runtime_bootstrap_checkin, member_briefing',\n },\n 'opencode'\n );\n\n expect(title).toContain('OpenCode runtime delivery did not complete.');\n expect(title).not.toContain('runtime_bootstrap_checkin');\n });\n\n it('formats unknown OpenCode bridge outcome timeouts as delivery advisory text', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'backend_error' as const,\n message: 'opencode_prompt_acceptance_unknown_after_bridge_timeout',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode delivery error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n\n expect(title).toContain('OpenCode runtime delivery error.');\n expect(title).toContain('OpenCode bridge outcome unknown after timeout, retrying/observing.');\n expect(title).not.toContain('Network or connectivity error');\n expect(title).not.toContain('opencode_prompt_acceptance_unknown_after_bridge_timeout');\n });\n\n it.each([\n 'session_stale',\n 'resolved_behavior_changed:old->new',\n '(resolved_behavior_changed:old->new)',\n 'OpenCode API error: resolved_behavior_changed:old->new',\n 'resolved_behavior_changed:old.hash/1=abc->new.hash/2=def.',\n 'resolved_behavior_changed:tool_error->session_error',\n 'resolved_behavior_changed:responded_non_visible_tool->pending',\n 'resolved_behavior_changed:permission_blocked->pending',\n 'resolved_behavior_changed:old->new opencode_app_mcp_transport_changed:a->b',\n 'OpenCode session is stale (resolved_behavior_changed:old->new); reading historical messages for log projection only',\n 'opencode_app_mcp_transport_changed:old->new',\n 'opencode_prompt_delivery_session_refresh_scheduled',\n 'OpenCode API error. opencode_prompt_delivery_session_refresh_scheduled',\n 'OpenCode API error. opencode_prompt_delivery_session_refresh_scheduled.',\n 'OpenCode session refresh scheduled after resolved behavior changed',\n 'opencode_session_refresh_scheduled_after_resolved_behavior_changed',\n 'OpenCode API error. opencode_session_stale_observe_scheduled_after_accepted_prompt',\n ])('renders recoverable OpenCode session refresh advisory %s as a warning', (message) => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message,\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode session refresh');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('warning');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toBe('OpenCode session changed; refreshing the session before retry.');\n expect(title).not.toContain('OpenCode API error');\n expect(title).not.toContain(message);\n });\n\n it('renders legacy OpenCode session refresh advisories without a reason code as warnings', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n message: 'session_stale',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode session refresh');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('warning');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'opencode')).toBe(\n 'OpenCode session changed; refreshing the session before retry.'\n );\n });\n\n it('does not hide real OpenCode API errors that merely mention a refresh marker', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'OpenCode API error. resolved_behavior_changed:old->new permission denied',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('permission denied');\n });\n\n it('does not strip a generic OpenCode API error prefix without a separator', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'OpenCode API errorresolved_behavior_changed:old->new',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'opencode')).toContain(\n 'OpenCode API errorresolved_behavior_changed:old->new'\n );\n });\n\n it('does not format a refresh-prefixed message with extra failure details as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new permission denied',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('resolved_behavior_changed:old->new permission denied');\n expect(title).not.toBe('OpenCode session changed; refreshing the session before retry.');\n });\n\n it('does not format refresh markers with unknown extra text as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new unexpected detail',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('unexpected detail');\n });\n\n it('does not format colon-suffixed refresh failure details as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new:permission_denied',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('resolved_behavior_changed:old->new:permission_denied');\n expect(title).not.toBe('OpenCode session changed; refreshing the session before retry.');\n });\n\n it('does not format semicolon-attached failure details as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new;permission_denied',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('permission_denied');\n });\n\n it.each([\n 'permission_denied',\n 'error',\n 'failed',\n 'failure',\n 'aborted',\n 'canceled',\n 'cancelled',\n 'interrupted',\n 'enospc',\n ])('does not let refresh pattern consume directly attached failure token _%s', (suffix) => {\n const message = `resolved_behavior_changed:old->new_${suffix}`;\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message,\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain(message);\n });\n\n it.each([\n 'resolved_behavior_changed:old->new/auth_unavailable',\n 'resolved_behavior_changed:old->new permission denied',\n 'resolved_behavior_changed:old->new permission_blocked',\n 'resolved_behavior_changed:old->new login required',\n 'resolved_behavior_changed:old->new not logged in',\n 'resolved_behavior_changed:old->new missing credentials',\n 'resolved_behavior_changed:old->new access denied',\n 'resolved_behavior_changed:old->new 401',\n 'resolved_behavior_changed:old->new;key limit exceeded',\n 'resolved_behavior_changed:old->new-network_timeout',\n 'resolved_behavior_changed:old->new interrupted',\n 'resolved_behavior_changed:old->new(non_visible_tool_without_task_progress)',\n 'opencode_app_mcp_transport_changed:old->new/permission_denied',\n 'opencode_app_mcp_transport_changed:old->new;visible_reply_missing_task_refs',\n ])('keeps separator-attached failure detail as an OpenCode API error for %s', (message) => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message,\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain(message);\n });\n\n it('still formats clean refresh markers after direct suffix checks', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode session refresh');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('warning');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'opencode')).toBe(\n 'OpenCode session changed; refreshing the session before retry.'\n );\n });\n\n it('does not format refresh markers with network failures as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new network timeout',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('network timeout');\n });\n\n it('does not format refresh markers with auth failures as a clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new auth_unavailable',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('auth_unavailable');\n });\n\n it.each([\n 'OpenCode session is stale (resolved_behavior_changed:old->new); Key limit exceeded (total limit)',\n 'OpenCode session is stale (resolved_behavior_changed:old->new); 429 too many requests',\n 'OpenCode session is stale (resolved_behavior_changed:old->new); Free usage exceeded, subscribe to Go',\n ])(\n 'does not format stale refresh text with quota/rate failures as clean refresh: %s',\n (message) => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message,\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain(message);\n }\n );\n\n it('does not format stale refresh text with unknown extra text as clean refresh', () => {\n const message =\n 'OpenCode session is stale (resolved_behavior_changed:old->new); unexpected detail';\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message,\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'opencode')).toContain(message);\n });\n\n it('does not format stale log-projection text with protocol failures as clean session refresh', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message:\n 'OpenCode session is stale (resolved_behavior_changed:old->new); visible_reply_missing_task_refs',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode API error.');\n expect(title).toContain('visible_reply_missing_task_refs');\n });\n\n it('does not downgrade action-required OpenCode errors with refresh-looking messages', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'quota_exhausted' as const,\n message: 'resolved_behavior_changed:old->new',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'opencode')).toBe('OpenCode quota error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'opencode')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'opencode');\n expect(title).toContain('OpenCode quota exhausted.');\n });\n\n it('does not downgrade non-OpenCode backend errors that reuse OpenCode refresh-looking text', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error' as const,\n message: 'resolved_behavior_changed:old->new',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'anthropic')).toBe('Anthropic API error');\n expect(getMemberRuntimeAdvisoryTone(advisory, 'anthropic')).toBe('error');\n\n const title = getMemberRuntimeAdvisoryTitle(advisory, 'anthropic');\n expect(title).toContain('Anthropic API error.');\n expect(title).toContain('resolved_behavior_changed:old->new');\n expect(title).not.toContain('OpenCode session changed');\n });\n\n it('formats non-visible tool progress advisory reasons before showing them in titles', () => {\n const title = getMemberRuntimeAdvisoryTitle(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'protocol_proof_missing',\n message: 'non_visible_tool_without_task_progress',\n },\n 'opencode'\n );\n\n expect(\n getMemberRuntimeAdvisoryLabel(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'protocol_proof_missing',\n message: 'non_visible_tool_without_task_progress',\n },\n 'opencode'\n )\n ).toBe('OpenCode proof missing');\n expect(\n getMemberRuntimeAdvisoryTone({\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'protocol_proof_missing',\n message: 'non_visible_tool_without_task_progress',\n })\n ).toBe('warning');\n expect(title).toContain(\n 'OpenCode used tools, but did not create a visible reply or task progress proof.'\n );\n expect(title).not.toContain('non_visible_tool_without_task_progress');\n });\n\n it('formats missing taskRefs advisory reasons before showing them in titles', () => {\n const title = getMemberRuntimeAdvisoryTitle(\n {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'protocol_proof_missing',\n message: 'visible_reply_missing_task_refs',\n },\n 'opencode'\n );\n\n expect(title).toContain('OpenCode created a reply without the required taskRefs metadata.');\n expect(title).not.toContain('visible_reply_missing_task_refs');\n });\n\n it('renders Codex native timeout separately from network errors', () => {\n const advisory = {\n kind: 'api_error' as const,\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'codex_native_timeout' as const,\n message: 'Codex native exec timed out after 120000ms.',\n };\n\n expect(getMemberRuntimeAdvisoryLabel(advisory, 'codex')).toBe('Codex native timeout');\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'codex')).toContain(\n 'Codex native mailbox turn timed out'\n );\n expect(getMemberRuntimeAdvisoryTitle(advisory, 'codex')).toContain(\n 'Codex native exec timed out after 120000ms.'\n );\n });\n\n it('marks launch presentation as an error when the runtime has a terminal API error', () => {\n const presentation = buildMemberLaunchPresentation({\n member: { ...member, providerId: 'anthropic' },\n spawnStatus: 'online',\n spawnLaunchState: 'runtime_pending_bootstrap',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n runtimeAdvisory: {\n kind: 'api_error',\n observedAt: '2026-04-07T09:00:00.000Z',\n reasonCode: 'auth_error',\n statusCode: 500,\n message: 'auth_unavailable: no auth available',\n },\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(presentation.presenceLabel).toBe('Anthropic auth error');\n expect(presentation.runtimeAdvisoryTone).toBe('error');\n expect(presentation.dotClass).toContain('bg-red-400');\n });\n\n it('keeps recoverable OpenCode session refresh presentation out of the terminal error state', () => {\n const presentation = buildMemberLaunchPresentation({\n member: { ...member, providerId: 'opencode' },\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'process',\n spawnRuntimeAlive: true,\n runtimeAdvisory: {\n kind: 'api_error',\n observedAt: '2026-05-18T08:31:46.075Z',\n reasonCode: 'backend_error',\n message: 'opencode_app_mcp_transport_changed:old->new',\n },\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(presentation.presenceLabel).toBe('OpenCode session refresh');\n expect(presentation.runtimeAdvisoryLabel).toBe('OpenCode session refresh');\n expect(presentation.runtimeAdvisoryTone).toBe('warning');\n expect(presentation.dotClass).not.toContain('bg-red-400');\n });\n\n it('keeps recovered OpenCode App MCP connectivity advisory out of the terminal presentation', () => {\n const presentation = buildMemberLaunchPresentation({\n member: { ...member, providerId: 'opencode' },\n spawnStatus: 'online',\n spawnLaunchState: 'confirmed_alive',\n spawnLivenessSource: 'heartbeat',\n spawnRuntimeAlive: true,\n spawnBootstrapConfirmed: true,\n spawnAgentToolAccepted: true,\n spawnHardFailure: false,\n spawnLivenessKind: 'runtime_process',\n runtimeEntry: {\n memberName: 'alice',\n providerId: 'opencode',\n alive: true,\n restartable: false,\n livenessKind: 'runtime_process',\n updatedAt: '2026-05-18T17:21:24.498Z',\n },\n runtimeAdvisory: {\n kind: 'api_error',\n observedAt: '2026-05-18T17:20:36.681Z',\n reasonCode: 'network_error',\n message:\n 'OpenCode app MCP was not connected before message delivery (status=attach_failed, connected=null). OpenCode app MCP readiness check failed: Unable to connect.',\n },\n isLaunchSettling: false,\n isTeamAlive: true,\n isTeamProvisioning: false,\n });\n\n expect(presentation.presenceLabel).not.toContain('OpenCode API error');\n expect(presentation.runtimeAdvisoryLabel).toBeNull();\n expect(presentation.runtimeAdvisoryTone).toBeNull();\n expect(presentation.dotClass).not.toContain('bg-red-400');\n });\n\n it('falls back to the existing generic retry wording when no structured reason is present', () => {\n expect(\n getMemberRuntimeAdvisoryLabel(\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2026-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n message: 'Gemini cli backend error: capacity exceeded.',\n },\n 'gemini',\n Date.parse('2026-04-07T09:00:00.000Z')\n )\n ).toBe('retrying now · 45s');\n });\n\n it('surfaces retry advisory text instead of plain online while bootstrap contact is still pending', () => {\n expect(\n getLaunchAwarePresenceLabel(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n 'process',\n true,\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2099-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'quota_exhausted',\n message: 'Gemini cli backend error: capacity exceeded.',\n },\n false,\n true,\n false,\n undefined\n )\n ).toContain('Gemini quota retry');\n\n expect(\n getLaunchAwarePresenceLabel(\n member,\n 'online',\n 'runtime_pending_bootstrap',\n 'process',\n false,\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2099-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'quota_exhausted',\n message: 'Gemini cli backend error: capacity exceeded.',\n },\n false,\n true,\n false,\n undefined\n )\n ).toBe('starting');\n });\n\n it('keeps retry advisory visible after contact when the teammate is otherwise just idle or ready', () => {\n expect(\n getLaunchAwarePresenceLabel(\n member,\n 'online',\n 'confirmed_alive',\n 'heartbeat',\n true,\n {\n kind: 'sdk_retrying',\n observedAt: '2026-04-07T09:00:00.000Z',\n retryUntil: '2099-04-07T09:00:45.000Z',\n retryDelayMs: 45_000,\n reasonCode: 'quota_exhausted',\n message: 'Gemini cli backend error: capacity exceeded.',\n },\n false,\n true,\n false,\n undefined\n )\n ).toContain('Gemini quota retry');\n });\n});\n"}}}}