import { buildTeamRuntimeDisplayRows } from '@renderer/components/team/teamRuntimeDisplayRows'; import { describe, expect, it } from 'vitest'; import type { MemberSpawnStatusEntry, TeamAgentRuntimeEntry, TeamAgentRuntimeSnapshot, } from '@shared/types'; const members = [{ name: 'alice' }, { name: 'bob' }]; function createRuntimeEntry(overrides: Partial = {}): TeamAgentRuntimeEntry { return { memberName: 'alice', alive: true, restartable: true, updatedAt: '2026-05-03T10:00:00.000Z', ...overrides, }; } function createRuntimeSnapshot( membersByName: Record ): TeamAgentRuntimeSnapshot { return { teamName: 'my-team', updatedAt: '2026-05-03T10:00:00.000Z', runId: 'run-1', members: membersByName, }; } function createSpawnStatus(overrides: Partial = {}): MemberSpawnStatusEntry { return { status: 'spawning', launchState: 'starting', updatedAt: '2026-05-03T10:00:00.000Z', ...overrides, }; } describe('buildTeamRuntimeDisplayRows', () => { it('maps alive runtime entries to running display rows', () => { const rows = buildTeamRuntimeDisplayRows({ members, runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ runtimeModel: 'claude-sonnet-4.5', runtimePid: 1234 }), }), }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'running', source: 'runtime', runtimeModel: 'claude-sonnet-4.5', pidLabel: 'runtime pid 1234', actionsAllowed: false, }); expect(rows[1]).toMatchObject({ memberName: 'bob', state: 'unknown', actionsAllowed: false, }); }); it('does not treat historical bootstrap as running when runtime is not alive', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ alive: false, historicalBootstrapConfirmed: true, runtimeDiagnostic: 'Runtime heartbeat is stale', }), }), spawnStatuses: { alice: createSpawnStatus({ status: 'online', launchState: 'confirmed_alive', bootstrapConfirmed: true, }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'stopped', source: 'mixed', stateReason: 'Runtime heartbeat is stale', actionsAllowed: false, }); }); it('does not show bootstrap-only runtime evidence as running when spawn says runtime is stopped', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ alive: true, livenessKind: 'confirmed_bootstrap', runtimeDiagnostic: 'bootstrap confirmed', runtimeDiagnosticSeverity: 'info', }), }), spawnStatuses: { alice: createSpawnStatus({ status: 'online', launchState: 'confirmed_alive', bootstrapConfirmed: true, runtimeAlive: false, runtimeDiagnostic: 'persisted runtime pid is not alive', runtimeDiagnosticSeverity: 'warning', }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'stopped', source: 'mixed', stateReason: 'persisted runtime pid is not alive', diagnosticSeverity: 'warning', actionsAllowed: false, }); }); it('keeps spawn degradation stronger than stopped evidence for mixed rows', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ alive: true, livenessKind: 'confirmed_bootstrap', runtimeDiagnostic: 'bootstrap confirmed', }), }), spawnStatuses: { alice: createSpawnStatus({ status: 'online', launchState: 'runtime_pending_permission', bootstrapConfirmed: true, runtimeAlive: false, pendingPermissionRequestIds: ['perm-1'], runtimeDiagnostic: 'Runtime is waiting for permission approval', runtimeDiagnosticSeverity: 'warning', }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'degraded', source: 'mixed', stateReason: 'Runtime is waiting for permission approval', diagnosticSeverity: 'warning', actionsAllowed: false, }); }); it('does not show spawn-only confirmed bootstrap as running when spawn says runtime is stopped', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], spawnStatuses: { alice: createSpawnStatus({ status: 'online', launchState: 'confirmed_alive', bootstrapConfirmed: true, runtimeAlive: false, }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'stopped', source: 'spawn-status', stateReason: 'Spawn status reports runtime is not alive', diagnosticSeverity: 'warning', actionsAllowed: false, }); }); it('treats confirmed spawn bootstrap as running even if stale status is still waiting', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], spawnStatuses: { alice: createSpawnStatus({ status: 'waiting', launchState: 'confirmed_alive', bootstrapConfirmed: true, runtimeAlive: true, livenessKind: 'registered_only', }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'running', source: 'spawn-status', stateReason: 'Bootstrap confirmed', actionsAllowed: false, }); }); it('maps a non-alive runtime with error diagnostics to degraded', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ alive: false, runtimeDiagnostic: 'Runtime process crashed', runtimeDiagnosticSeverity: 'error', }), }), }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'degraded', stateReason: 'Runtime process crashed', actionsAllowed: false, }); }); it('degrades mixed rows when runtime is alive but spawn evidence has failed', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ alice: createRuntimeEntry({ alive: true, runtimeDiagnostic: 'Runtime heartbeat is alive', }), }), spawnStatuses: { alice: createSpawnStatus({ status: 'error', launchState: 'failed_to_start', hardFailure: true, hardFailureReason: 'Bootstrap command failed', }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'degraded', source: 'mixed', stateReason: 'Bootstrap command failed. Process is still alive.', actionsAllowed: false, }); }); it('degrades spawn-only rows when online process evidence has stalled bootstrap', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], spawnStatuses: { alice: createSpawnStatus({ status: 'online', launchState: 'runtime_pending_bootstrap', runtimeAlive: true, bootstrapStalled: true, runtimeDiagnostic: 'Runtime is alive, but bootstrap did not confirm', }), }, }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'degraded', source: 'spawn-status', stateReason: 'Runtime is alive, but bootstrap did not confirm', actionsAllowed: false, }); }); it('uses explicit spawn status handling without promoting unknown statuses to running', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }, { name: 'bob' }, { name: 'carol' }], spawnStatuses: { alice: createSpawnStatus({ status: 'spawning' }), bob: createSpawnStatus({ status: 'online', launchState: 'confirmed_alive', runtimeAlive: true, }), carol: createSpawnStatus({ status: 'surprising-new-status' as never }), }, }); expect(rows.map((row) => [row.memberName, row.state])).toEqual([ ['alice', 'starting'], ['bob', 'running'], ['carol', 'unknown'], ]); }); it('chooses the latest runtime entry when multiple lanes map to one member', () => { const rows = buildTeamRuntimeDisplayRows({ members: [{ name: 'alice' }], runtimeSnapshot: createRuntimeSnapshot({ 'alice-primary': createRuntimeEntry({ memberName: 'alice', alive: false, laneKind: 'primary', updatedAt: '2026-05-03T10:00:00.000Z', }), 'alice-secondary': createRuntimeEntry({ memberName: 'alice', alive: true, laneKind: 'secondary', updatedAt: '2026-05-03T10:01:00.000Z', }), }), }); expect(rows[0]).toMatchObject({ memberName: 'alice', state: 'running', laneKind: 'secondary', actionsAllowed: false, }); }); });