import {
  buildWorkspaceTrustPathCandidates,
  type WorkspaceTrustWorkspace,
} from '@features/workspace-trust/main';
import { EventEmitter } from 'events';
import * as fs from 'fs';
import { promises as fsPromises } from 'fs';
import * as os from 'os';
import * as path from 'path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import type { ChildProcess } from 'child_process';

const hoisted = vi.hoisted(() => ({
  paths: {
    claudeRoot: '',
    teamsBase: '',
    tasksBase: '',
    projectsBase: '',
  },
}));

let tempClaudeRoot = '';
let tempTeamsBase = '';
let tempTasksBase = '';
let tempProjectsBase = '';

vi.mock('@main/services/team/ClaudeBinaryResolver', () => ({
  ClaudeBinaryResolver: { resolve: vi.fn() },
}));

vi.mock('@features/tmux-installer/main', () => ({
  killTmuxPaneForCurrentPlatformSync: vi.fn(),
  listRuntimeProcessTableForCurrentPlatform: vi.fn(async () => []),
  listTmuxPanePidsForCurrentPlatform: vi.fn(async () => new Map()),
  listTmuxPaneRuntimeInfoForCurrentPlatform: vi.fn(async () => new Map()),
  sendKeysToTmuxPaneForCurrentPlatform: vi.fn(async () => undefined),
  isTmuxRuntimeReadyForCurrentPlatform: vi.fn(async () => true),
}));

vi.mock('pidusage', () => {
  const pidusageMock = vi.fn();
  return {
    default: pidusageMock,
  };
});

vi.mock('@main/services/team/TeamTaskReader', () => ({
  TeamTaskReader: class {
    async getTasks() {
      return [];
    }
  },
}));

vi.mock('@main/utils/childProcess', () => ({
  execCli: vi.fn(async (_binaryPath: string | null, args: string[]) => {
    if (args[0] === '-e' && args[1]?.includes('process.execPath')) {
      return {
        stdout: JSON.stringify({ execPath: process.execPath, version: process.versions.node }),
        stderr: '',
      };
    }

    if (args[0] === 'model') {
      return {
        stdout: JSON.stringify({
          schemaVersion: 1,
          providers: {
            anthropic: {
              defaultModel: 'opus[1m]',
              models: [
                { id: 'opus', label: 'Opus 4.7', description: 'Anthropic default family alias' },
                {
                  id: 'opus[1m]',
                  label: 'Opus 4.7 (1M)',
                  description: 'Anthropic long-context default',
                },
              ],
            },
            codex: {
              defaultModel: 'gpt-5.4',
              models: [{ id: 'gpt-5.4', label: 'GPT-5.4', description: 'Codex default' }],
            },
            gemini: {
              defaultModel: 'gemini-2.5-pro',
              models: [{ id: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', description: 'Default' }],
            },
          },
        }),
        stderr: '',
      };
    }
    if (args[0] === 'runtime') {
      return {
        stdout: JSON.stringify({
          providers: {
            codex: {
              runtimeCapabilities: {
                modelCatalog: { dynamic: false, source: 'runtime' },
                reasoningEffort: {
                  supported: true,
                  values: ['low', 'medium', 'high'],
                  configPassthrough: false,
                },
              },
            },
          },
        }),
        stderr: '',
      };
    }
    return { stdout: '', stderr: '' };
  }),
  spawnCli: vi.fn(),
  killProcessTree: vi.fn(),
}));

vi.mock('@main/utils/processKill', () => ({
  killProcessByPid: vi.fn(),
}));

vi.mock('@main/utils/windowsProcessTable', () => ({
  listWindowsProcessTable: vi.fn(async () => []),
  listWindowsProcessTableSync: vi.fn(() => []),
}));

vi.mock('@main/utils/pathDecoder', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@main/utils/pathDecoder')>();
  return {
    ...actual,
    getAutoDetectedClaudeBasePath: () => hoisted.paths.claudeRoot,
    getClaudeBasePath: () => hoisted.paths.claudeRoot,
    getHomeDir: () => hoisted.paths.claudeRoot,
    getProjectsBasePath: () => hoisted.paths.projectsBase,
    getTasksBasePath: () => hoisted.paths.tasksBase,
    getTeamsBasePath: () => hoisted.paths.teamsBase,
  };
});

import {
  killTmuxPaneForCurrentPlatformSync,
  listRuntimeProcessTableForCurrentPlatform,
  listTmuxPanePidsForCurrentPlatform,
  listTmuxPaneRuntimeInfoForCurrentPlatform,
  sendKeysToTmuxPaneForCurrentPlatform,
  type RuntimeProcessTableRow,
} from '@features/tmux-installer/main';
import { agentTeamsMcpHttpServer } from '@main/services/team/AgentTeamsMcpHttpServer';
import {
  clearAutoResumeService,
  getAutoResumeService,
  initializeAutoResumeService,
} from '@main/services/team/AutoResumeService';
import { ClaudeBinaryResolver } from '@main/services/team/ClaudeBinaryResolver';
import { OPEN_CODE_BRIDGE_SCHEMA_VERSION } from '@main/services/team/opencode/bridge/OpenCodeBridgeCommandContract';
import { OpenCodeReadinessBridge } from '@main/services/team/opencode/bridge/OpenCodeReadinessBridge';
import {
  getOpenCodeLaneScopedRuntimeFilePath,
  getOpenCodeRuntimeManifestPath,
  OpenCodeRuntimeManifestEvidenceReader,
  readCommittedOpenCodeBootstrapSessionEvidence,
  readOpenCodeRuntimeLaneIndex,
  setOpenCodeRuntimeActiveRunManifest,
  upsertOpenCodeRuntimeLaneIndexEntry,
} from '@main/services/team/opencode/store/OpenCodeRuntimeManifestEvidenceReader';
import {
  createDefaultRuntimeStoreManifest,
  createRuntimeStoreManifestStore,
  createRuntimeStoreReceiptStore,
  OPENCODE_RUNTIME_STORE_DESCRIPTORS,
  RuntimeStoreBatchWriter,
} from '@main/services/team/opencode/store/RuntimeStoreManifest';
import { OpenCodeTeamRuntimeAdapter } from '@main/services/team/runtime/OpenCodeTeamRuntimeAdapter';
import { TeamRuntimeAdapterRegistry } from '@main/services/team/runtime/TeamRuntimeAdapter';
import { getTeamBootstrapStatePath } from '@main/services/team/TeamBootstrapStateReader';
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
import {
  createPersistedLaunchSnapshot,
  snapshotFromRuntimeMemberStatuses,
} from '@main/services/team/TeamLaunchStateEvaluator';
import {
  getTeamLaunchStatePath,
  getTeamLaunchSummaryPath,
} from '@main/services/team/TeamLaunchStateStore';
import {
  getMixedLaunchFallbackRecoveryError,
  TeamProvisioningService,
} from '@main/services/team/TeamProvisioningService';
import { TeamTaskActivityIntervalService } from '@main/services/team/TeamTaskActivityIntervalService';
import { spawnCli } from '@main/utils/childProcess';
import { encodePath } from '@main/utils/pathDecoder';
import { killProcessByPid } from '@main/utils/processKill';
import {
  listWindowsProcessTable,
  listWindowsProcessTableSync,
} from '@main/utils/windowsProcessTable';
import {
  AGENT_TEAMS_NAMESPACED_LEAD_BOOTSTRAP_TOOL_NAMES,
  AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES,
} from 'agent-teams-controller';
import pidusage from 'pidusage';

const EXPECTED_RUNTIME_PIDUSAGE_OPTIONS =
  process.platform === 'win32' ? { maxage: 10_000 } : { maxage: 0 };
const ORIGINAL_RUNTIME_PIDUSAGE_ENABLED = process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;

type RuntimeTelemetryProcessTableRow = RuntimeProcessTableRow & {
  runtimeTelemetrySource?: 'native' | 'wsl' | 'windows-host';
};

type LeadWorkSyncTestTaskRef = { taskId: string; displayId?: string; teamName?: string };
type LeadWorkSyncTestInboxMessage = {
  from: string;
  to?: string;
  text: string;
  timestamp: string;
  messageId: string;
  read: boolean;
  messageKind?: string;
  taskRefs?: LeadWorkSyncTestTaskRef[];
};
type LeadRelayPriorityTestInboxMessage = LeadWorkSyncTestInboxMessage & {
  source?: string;
  workSyncIntent?: string;
};
type LeadRelayPriorityTestRun = ReturnType<typeof createMemberSpawnRun> & {
  leadRelayCapture?: { resolveOnce(text: string): void } | null;
};
type LeadRelayPriorityServiceHarness = {
  runs: Map<string, LeadRelayPriorityTestRun>;
  aliveRunByTeam: Map<string, string>;
  configReader: {
    getConfig(teamName: string): Promise<Record<string, unknown>>;
  };
  inboxReader: {
    getMessagesFor(
      teamName: string,
      inboxName: string
    ): Promise<LeadRelayPriorityTestInboxMessage[]>;
  };
  confirmSameTeamNativeMatches(input: unknown): Promise<{
    nativeMatchedMessageIds: Set<string>;
    persisted: boolean;
  }>;
  markInboxMessagesRead(
    teamName: string,
    inboxName: string,
    messages: LeadRelayPriorityTestInboxMessage[]
  ): Promise<void>;
  resolveControlApiBaseUrl(): Promise<string | null>;
  scheduleLeadInboxFollowUpRelay(teamName: string): void;
  sendMessageToRun(run: LeadRelayPriorityTestRun, message: string): Promise<void>;
};
type LeadWorkSyncReadCommitTestHarness = {
  hasAcceptedLeadWorkSyncReport(input: { teamName: string; leadName: string }): Promise<boolean>;
  getLeadRelayReadCommitBatch(input: {
    teamName: string;
    leadName: string;
    batch: LeadWorkSyncTestInboxMessage[];
  }): Promise<LeadWorkSyncTestInboxMessage[]>;
};

function leadWorkSyncReadCommitHarness(
  svc: TeamProvisioningService
): LeadWorkSyncReadCommitTestHarness {
  return svc as unknown as LeadWorkSyncReadCommitTestHarness;
}

function restoreRuntimePidusageTelemetryEnv() {
  if (ORIGINAL_RUNTIME_PIDUSAGE_ENABLED === undefined) {
    delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
    return;
  }
  process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED = ORIGINAL_RUNTIME_PIDUSAGE_ENABLED;
}

function withRuntimePidusageTelemetryEnv(
  value: string | undefined,
  callback: () => Promise<void>
): Promise<void> {
  const previous = process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
  if (value === undefined) {
    delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
  } else {
    process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED = value;
  }
  return callback().finally(() => {
    if (previous === undefined) {
      delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
    } else {
      process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED = previous;
    }
  });
}

function allowConsoleLogs() {
  vi.spyOn(console, 'error').mockImplementation(() => {});
  vi.spyOn(console, 'warn').mockImplementation(() => {});
}

function createFakeChild(exitCode: number): ChildProcess {
  const child = Object.assign(new EventEmitter(), {
    stdout: null,
    stderr: null,
    stdin: null,
  }) as unknown as ChildProcess;
  setImmediate(() => child.emit('close', exitCode));
  return child;
}

function createRunningChild() {
  return Object.assign(new EventEmitter(), {
    pid: 12345,
    stdin: {
      writable: true,
      write: vi.fn(() => true),
      end: vi.fn(),
    },
    stdout: new EventEmitter(),
    stderr: new EventEmitter(),
    kill: vi.fn(),
  });
}

function createPidusageStat(pid: number, memory: number, cpu = 0) {
  return {
    cpu,
    memory,
    ppid: 1,
    pid,
    ctime: 0,
    elapsed: 0,
    timestamp: Date.now(),
  };
}

function createDeferred<T>() {
  let resolve!: (value: T | PromiseLike<T>) => void;
  let reject!: (reason?: unknown) => void;
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
}

function writeLaunchConfig(
  teamName: string,
  projectPath: string,
  leadSessionId: string,
  members: string[]
): void {
  const teamDir = path.join(tempTeamsBase, teamName);
  fs.mkdirSync(teamDir, { recursive: true });
  fs.writeFileSync(
    path.join(teamDir, 'config.json'),
    JSON.stringify({
      name: teamName,
      projectPath,
      leadSessionId,
      members: [
        { name: 'team-lead', agentType: 'team-lead' },
        ...members.map((name) => ({ name })),
      ],
    }),
    'utf8'
  );
}

async function startDeterministicLaunchCloseHarness(options?: {
  teamName?: string;
  leadSessionId?: string;
  members?: string[];
}) {
  const teamName = options?.teamName ?? `launch-close-${Date.now()}`;
  const leadSessionId = options?.leadSessionId ?? `lead-session-${teamName}`;
  const members = options?.members ?? ['alice'];
  writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, members);

  vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
  const child = createRunningChild();
  vi.mocked(spawnCli).mockReturnValue(child as any);

  const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
    writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
    removeConfigFile: vi.fn(async () => {}),
  } as any);
  (svc as any).buildProvisioningEnv = vi.fn(async () => ({
    env: { CODEX_API_KEY: 'test' },
    authSource: 'codex_runtime',
  }));
  (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
    members: members.map((name) => ({ name })),
    source: 'members-meta',
    warning: undefined,
  }));
  (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
  (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
  (svc as any).updateConfigProjectPath = vi.fn(async () => {});
  (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
  (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
  (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
  (svc as any).startFilesystemMonitor = vi.fn();
  (svc as any).waitForValidConfig = vi.fn(async () => ({ ok: false }));
  (svc as any).pathExists = vi.fn(async (targetPath: string) =>
    targetPath.endsWith(`${leadSessionId}.jsonl`)
  );

  const progressUpdates: any[] = [];
  const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, (progress) => {
    progressUpdates.push(progress);
  });
  const run = (svc as any).runs.get(runId);
  expect(run).toBeTruthy();

  return { child, members, progressUpdates, run, runId, svc, teamName };
}

function writeLaunchState(
  teamName: string,
  leadSessionId: string,
  members: Record<string, Record<string, unknown>>,
  options?: {
    launchPhase?: 'active' | 'finished';
    updatedAt?: string;
  }
): void {
  const snapshot = createPersistedLaunchSnapshot({
    teamName,
    leadSessionId,
    launchPhase: options?.launchPhase ?? 'finished',
    expectedMembers: Object.keys(members),
    members: Object.fromEntries(
      Object.entries(members).map(([name, member]) => [
        name,
        {
          name,
          launchState: 'failed_to_start',
          agentToolAccepted: false,
          runtimeAlive: false,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: 'Teammate was never spawned during launch.',
          lastEvaluatedAt: new Date().toISOString(),
          ...member,
        },
      ])
    ) as any,
    ...(options?.updatedAt ? { updatedAt: options.updatedAt } : {}),
  });
  fs.writeFileSync(
    getTeamLaunchStatePath(teamName),
    `${JSON.stringify(snapshot, null, 2)}\n`,
    'utf8'
  );
}

function writeBootstrapState(
  teamName: string,
  members: {
    name: string;
    status: string;
    lastAttemptAt?: number;
    lastObservedAt?: number;
    failureReason?: string;
  }[],
  updatedAt = new Date().toISOString(),
  options?: { runId?: string }
): void {
  fs.writeFileSync(
    getTeamBootstrapStatePath(teamName),
    `${JSON.stringify(
      {
        version: 1,
        ...(options?.runId ? { runId: options.runId } : {}),
        teamName,
        updatedAt,
        phase: 'completed',
        members,
      },
      null,
      2
    )}\n`,
    'utf8'
  );
}

function writeMemberBootstrapRunId(teamName: string, memberName: string, runId: string): void {
  const configPath = path.join(tempTeamsBase, teamName, 'config.json');
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
    members?: Array<Record<string, unknown>>;
  };
  config.members = (config.members ?? []).map((member) =>
    member.name === memberName ? { ...member, bootstrapRunId: runId } : member
  );
  fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
}

function writeAliveProcessRegistry(teamName: string): void {
  const teamDir = path.join(tempTeamsBase, teamName);
  fs.mkdirSync(teamDir, { recursive: true });
  fs.writeFileSync(
    path.join(teamDir, 'processes.json'),
    `${JSON.stringify(
      [
        {
          id: 'lead-process',
          label: 'Team Lead',
          pid: process.pid,
          registeredAt: '2026-04-23T10:00:00.000Z',
        },
      ],
      null,
      2
    )}\n`,
    'utf8'
  );
}

function writeTeamMeta(teamName: string, overrides: Record<string, unknown> = {}): void {
  const teamDir = path.join(tempTeamsBase, teamName);
  fs.mkdirSync(teamDir, { recursive: true });
  fs.writeFileSync(
    path.join(teamDir, 'team.meta.json'),
    `${JSON.stringify(
      {
        version: 1,
        cwd: '/Users/test/proj',
        providerId: 'codex',
        providerBackendId: 'codex-native',
        model: 'gpt-5.4',
        effort: 'medium',
        createdAt: Date.now(),
        ...overrides,
      },
      null,
      2
    )}\n`,
    'utf8'
  );
}

function writeMembersMeta(
  teamName: string,
  members: Record<string, unknown>[],
  providerBackendId = 'codex-native'
): void {
  const teamDir = path.join(tempTeamsBase, teamName);
  fs.mkdirSync(teamDir, { recursive: true });
  fs.writeFileSync(
    path.join(teamDir, 'members.meta.json'),
    `${JSON.stringify(
      {
        version: 1,
        providerBackendId,
        members,
      },
      null,
      2
    )}\n`,
    'utf8'
  );
}

async function writeCommittedOpenCodeSessionStore(input: {
  teamName: string;
  laneId: string;
  runId: string;
  batchKey?: string;
  sessions: unknown[];
}): Promise<void> {
  const descriptor = OPENCODE_RUNTIME_STORE_DESCRIPTORS.find(
    (candidate) => candidate.schemaName === 'opencode.sessionStore'
  );
  if (!descriptor) throw new Error('session descriptor missing');
  const manifestPath = getOpenCodeRuntimeManifestPath(tempTeamsBase, input.teamName, input.laneId);
  const runtimeDirectory = path.dirname(manifestPath);
  await fsPromises.mkdir(runtimeDirectory, { recursive: true });
  const writer = new RuntimeStoreBatchWriter(
    runtimeDirectory,
    createRuntimeStoreManifestStore({ filePath: manifestPath, teamName: input.teamName }),
    createRuntimeStoreReceiptStore({
      filePath: path.join(runtimeDirectory, 'opencode-runtime-receipts.json'),
    }),
    {
      clock: () => new Date('2026-04-22T12:00:00.000Z'),
      batchIdFactory: () => `batch-${input.runId}${input.batchKey ? `-${input.batchKey}` : ''}`,
      receiptIdFactory: () => `receipt-${input.runId}${input.batchKey ? `-${input.batchKey}` : ''}`,
    }
  );
  await writer.writeBatch({
    teamName: input.teamName,
    runId: input.runId,
    capabilitySnapshotId: null,
    behaviorFingerprint: null,
    reason: 'launch_checkpoint',
    writes: [{ descriptor, data: { sessions: input.sessions } }],
  });
}

async function writeDefaultBobOpenCodeBootstrapEvidence(): Promise<void> {
  await writeCommittedOpenCodeSessionStore({
    teamName: 'team-a',
    laneId: 'secondary:opencode:bob',
    runId: 'opencode-run-bob',
    sessions: [
      {
        id: 'oc-session-bob',
        teamName: 'team-a',
        memberName: 'bob',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        source: 'runtime_bootstrap_checkin',
      },
    ],
  });
}

async function configureOpenCodeBobDeliveryService(input: {
  svc: TeamProvisioningService;
  sendMessageToMember: ReturnType<typeof vi.fn>;
  observeMessageDelivery?: ReturnType<typeof vi.fn>;
  memberModel?: string;
}): Promise<void> {
  const registry = new TeamRuntimeAdapterRegistry([
    {
      providerId: 'opencode',
      prepare: vi.fn(),
      launch: vi.fn(),
      reconcile: vi.fn(),
      stop: vi.fn(),
      sendMessageToMember: input.sendMessageToMember,
      observeMessageDelivery: input.observeMessageDelivery ?? vi.fn(),
    } as any,
  ]);
  input.svc.setRuntimeAdapterRegistry(registry);

  (input.svc as any).getTrackedRunId = vi.fn(() => 'run-1');
  (input.svc as any).provisioningRunByTeam.set('team-a', 'run-1');
  (input.svc as any).setSecondaryRuntimeRun({
    teamName: 'team-a',
    runId: 'opencode-run-bob',
    providerId: 'opencode',
    laneId: 'secondary:opencode:bob',
    memberName: 'bob',
    cwd: '/repo',
  });
  await writeDefaultBobOpenCodeBootstrapEvidence();
  (input.svc as any).configReader = {
    getConfig: vi.fn(async () => ({
      projectPath: '/repo',
      members: [
        { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
        {
          name: 'bob',
          providerId: 'opencode',
          model: input.memberModel ?? 'minimax-m2.5-free',
        },
      ],
    })),
  };
  (input.svc as any).teamMetaStore = {
    getMeta: vi.fn(async () => ({
      launchIdentity: { providerId: 'codex' },
      providerId: 'codex',
    })),
  };
  (input.svc as any).membersMetaStore = {
    getMembers: vi.fn(async () => [
      {
        name: 'bob',
        providerId: 'opencode',
        model: input.memberModel ?? 'opencode/minimax-m2.5-free',
      },
    ]),
  };
}

function createMemberSpawnStatusEntry(
  overrides: Record<string, unknown> = {}
): Record<string, unknown> {
  return {
    status: 'waiting',
    launchState: 'runtime_pending_bootstrap',
    error: undefined,
    updatedAt: new Date().toISOString(),
    runtimeAlive: false,
    livenessSource: undefined,
    bootstrapConfirmed: false,
    hardFailure: false,
    agentToolAccepted: true,
    firstSpawnAcceptedAt: new Date().toISOString(),
    lastHeartbeatAt: undefined,
    ...overrides,
  };
}

type TeamProvisioningServicePrivateHarness = {
  getLiveTeamAgentRuntimeMetadata: (
    teamName: string
  ) => Promise<Map<string, Record<string, unknown>>>;
  attachLiveRuntimeMetadataToStatuses: (
    teamName: string,
    statuses: Record<string, Record<string, unknown>>,
    options?: Record<string, unknown>
  ) => Promise<Record<string, Record<string, unknown>>>;
  applyBootstrapTranscriptEvidenceOverlay: (
    snapshot: ReturnType<typeof createPersistedLaunchSnapshot> | null
  ) => Promise<ReturnType<typeof createPersistedLaunchSnapshot> | null>;
  applyProcessBootstrapTransportOverlay: (
    input: Record<string, unknown>
  ) => Record<string, unknown>;
  reconcilePersistedLaunchState: (teamName: string) => Promise<{
    snapshot: null;
    statuses: Record<string, never>;
  }>;
  readProcessUsageStatsByPid: (
    pids: readonly number[]
  ) => Promise<Map<number, { rssBytes?: number; cpuPercent?: number }>>;
  readRuntimeProcessRowsForUsageSnapshot: (teamName: string) => Promise<unknown[] | null>;
  readCachedRuntimeProcessRowsForLiveRuntimeMetadata: (
    teamName: string,
    runId: string | null
  ) => { rows: RuntimeTelemetryProcessTableRow[] | null } | null;
  runtimeProcessRowsForUsageSnapshotByTeam: Map<
    string,
    {
      expiresAtMs: number;
      generation: number;
      runId: string | null;
      sampledAtMs: number;
      rows: RuntimeTelemetryProcessTableRow[] | null;
      includesWindowsHostRows: boolean;
    }
  >;
  getRuntimeSnapshotCacheGeneration: (teamName: string) => number;
  invalidateRuntimeSnapshotCaches: (teamName: string) => void;
  aliveRunByTeam: Map<string, string>;
  readRecentBootstrapTranscriptOutcome: (
    filePath: string,
    sinceMs: number | null,
    memberName: string,
    teamName: string,
    options?: { allowAnonymousFailure?: boolean; contextMemberNames?: readonly string[] }
  ) => Promise<{ kind: string; observedAt: string; source?: string; reason?: string } | null>;
  readPersistedRuntimeMembers: (teamName: string) => Array<Record<string, unknown>>;
  readPersistedTeamProjectPath: (teamName: string) => string | null;
};

function privateHarness(svc: TeamProvisioningService): TeamProvisioningServicePrivateHarness {
  return svc as unknown as TeamProvisioningServicePrivateHarness;
}

function createMemberSpawnRun(params?: {
  runId?: string;
  teamName?: string;
  startedAt?: string;
  expectedMembers?: string[];
  memberSpawnStatuses?: Map<string, Record<string, unknown>>;
  memberSpawnLeadInboxCursorByMember?: Map<string, { timestamp: string; messageId: string }>;
  mixedSecondaryLanes?: Array<{ providerId: string; member: { name: string } }>;
}) {
  const teamName = params?.teamName ?? 'member-spawn-team';
  const expectedMembers = params?.expectedMembers ?? ['alice'];
  const memberSpawnStatuses =
    params?.memberSpawnStatuses ??
    new Map([
      [
        expectedMembers[0]!,
        createMemberSpawnStatusEntry({
          firstSpawnAcceptedAt: new Date(Date.now() - 5_000).toISOString(),
        }),
      ],
    ]);

  return {
    runId: params?.runId ?? 'run-member-spawn-1',
    teamName,
    startedAt: params?.startedAt ?? new Date(Date.now() - 60_000).toISOString(),
    request: {
      members: [],
    },
    mixedSecondaryLanes: params?.mixedSecondaryLanes ?? [],
    expectedMembers,
    memberSpawnStatuses,
    memberSpawnToolUseIds: new Map(),
    pendingMemberRestarts: new Map(),
    memberSpawnLeadInboxCursorByMember: params?.memberSpawnLeadInboxCursorByMember ?? new Map(),
    provisioningOutputParts: [],
    activeToolCalls: new Map(),
    isLaunch: false,
    provisioningComplete: false,
  } as any;
}

type LeadActivityTestState = 'active' | 'idle' | 'offline';

interface LeadActivityTestRun {
  runId: string;
  teamName: string;
  leadActivityState: LeadActivityTestState;
  request: {
    members: { name: string; role: string }[];
  };
}

interface LeadActivityServiceInternals {
  runs: Map<string, LeadActivityTestRun>;
  aliveRunByTeam: Map<string, string>;
  runtimeAdapterProgressByRunId: Map<string, unknown>;
  runtimeAdapterRunByTeam: Map<string, unknown>;
  setLeadActivity(run: LeadActivityTestRun, state: LeadActivityTestState): void;
}

function toLeadActivityTestRun(
  params: Parameters<typeof createMemberSpawnRun>[0],
  leadActivityState: LeadActivityTestState,
  leadName: string
): LeadActivityTestRun {
  return {
    ...createMemberSpawnRun(params),
    leadActivityState,
    request: {
      members: [{ name: leadName, role: 'Team Lead' }],
    },
  };
}

const TEST_OPENCODE_APP_MANAGED_BOOTSTRAP_PROMPT = [
  'AGENT_TEAMS_APP_MANAGED_BOOTSTRAP_V1',
  '<agent_teams_app_managed_briefing_source>',
  'Test app-managed member briefing.',
  '</agent_teams_app_managed_briefing_source>',
].join('\n');

function stubOpenCodeAppManagedLaunchPrompt(svc: TeamProvisioningService) {
  return vi
    .spyOn(svc as any, 'buildOpenCodeSecondaryAppManagedLaunchPrompt')
    .mockImplementation(async (_run: unknown, lane: unknown) => {
      const memberName =
        lane &&
        typeof lane === 'object' &&
        'member' in lane &&
        lane.member &&
        typeof lane.member === 'object' &&
        'name' in lane.member &&
        typeof lane.member.name === 'string'
          ? lane.member.name
          : 'unknown';
      return `${TEST_OPENCODE_APP_MANAGED_BOOTSTRAP_PROMPT}\nmember=${memberName}`;
    });
}

function createClaudeLogsRun(overrides: Record<string, unknown> = {}) {
  return {
    runId: 'run-logs-1',
    teamName: 'logs-team',
    startedAt: '2026-04-19T10:00:00.000Z',
    isLaunch: false,
    provisioningComplete: true,
    processKilled: false,
    cancelRequested: false,
    timeoutHandle: null,
    fsMonitorHandle: null,
    stallCheckHandle: null,
    silentUserDmForwardClearHandle: null,
    child: null,
    leadActivityState: 'idle',
    activeToolCalls: new Map(),
    pendingDirectCrossTeamSendRefresh: false,
    memberSpawnStatuses: new Map(),
    activeCrossTeamReplyHints: [],
    pendingInboxRelayCandidates: [],
    pendingApprovals: new Map(),
    mcpConfigPath: null,
    bootstrapSpecPath: null,
    bootstrapUserPromptPath: null,
    claudeLogLines: ['[stdout]', 'first line', '[stderr]', 'boom'],
    claudeLogsUpdatedAt: '2026-04-19T10:00:01.000Z',
    progress: {
      updatedAt: '2026-04-19T10:00:01.000Z',
      state: 'ready',
    },
    ...overrides,
  } as any;
}

async function waitForFile(filePath: string, timeoutMs = 2_000): Promise<void> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    if (fs.existsSync(filePath)) {
      return;
    }
    await new Promise((resolve) => setTimeout(resolve, 25));
  }
  throw new Error(`Timed out waiting for file: ${filePath}`);
}

describe('TeamProvisioningService', () => {
  beforeEach(() => {
    process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED = '1';
    vi.clearAllMocks();
    vi.mocked(pidusage).mockReset();
    vi.mocked(killTmuxPaneForCurrentPlatformSync).mockReset();
    vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockReset();
    vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([]);
    vi.mocked(listWindowsProcessTable).mockReset();
    vi.mocked(listWindowsProcessTable).mockResolvedValue([]);
    vi.mocked(listWindowsProcessTableSync).mockReset();
    vi.mocked(listWindowsProcessTableSync).mockReturnValue([]);
    vi.mocked(listTmuxPanePidsForCurrentPlatform).mockReset();
    vi.mocked(listTmuxPanePidsForCurrentPlatform).mockResolvedValue(new Map());
    vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockReset();
    vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValue(new Map());
    vi.mocked(sendKeysToTmuxPaneForCurrentPlatform).mockReset();
    vi.mocked(sendKeysToTmuxPaneForCurrentPlatform).mockResolvedValue(undefined);
    tempClaudeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-team-provisioning-'));
    tempTeamsBase = path.join(tempClaudeRoot, 'teams');
    tempTasksBase = path.join(tempClaudeRoot, 'tasks');
    tempProjectsBase = path.join(tempClaudeRoot, 'projects');
    hoisted.paths.claudeRoot = tempClaudeRoot;
    hoisted.paths.teamsBase = tempTeamsBase;
    hoisted.paths.tasksBase = tempTasksBase;
    hoisted.paths.projectsBase = tempProjectsBase;
    fs.mkdirSync(tempTeamsBase, { recursive: true });
    fs.mkdirSync(tempTasksBase, { recursive: true });
    fs.mkdirSync(tempProjectsBase, { recursive: true });
    writeAliveProcessRegistry('team-a');
  });

  afterEach(() => {
    restoreRuntimePidusageTelemetryEnv();
    clearAutoResumeService();
    vi.useRealTimers();
    try {
      fs.rmSync(tempClaudeRoot, { recursive: true, force: true });
    } catch {
      // ignore temp cleanup failures
    }
    hoisted.paths.claudeRoot = '';
    hoisted.paths.teamsBase = '';
    hoisted.paths.tasksBase = '';
    hoisted.paths.projectsBase = '';
  });

  describe('warmup', () => {
    it('does not throw when spawnCli rejects', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('C:\\path\\claude');
      let callCount = 0;
      vi.mocked(spawnCli).mockImplementation(() => {
        callCount++;
        if (callCount === 1) {
          throw new Error('spawn EINVAL');
        }
        return createFakeChild(0);
      });

      const svc = new TeamProvisioningService();
      await expect(svc.warmup()).resolves.not.toThrow();
      expect(spawnCli).toHaveBeenCalled();
    });
  });

  describe('persisted team config cache', () => {
    it('returns defensive runtime member copies and refreshes when config changes', () => {
      const teamName = 'persisted-config-cache-team';
      const teamDir = path.join(tempTeamsBase, teamName);
      const configPath = path.join(teamDir, 'config.json');
      fs.mkdirSync(teamDir, { recursive: true });
      fs.writeFileSync(
        configPath,
        JSON.stringify({
          projectPath: '/repo-one',
          members: [{ name: 'alice', agentId: 'agent-alice' }],
        }),
        'utf8'
      );

      const svc = new TeamProvisioningService();
      const internals = privateHarness(svc);
      const firstMembers = internals.readPersistedRuntimeMembers(teamName);
      firstMembers[0]!.name = 'mutated';

      expect(internals.readPersistedRuntimeMembers(teamName)[0]).toMatchObject({
        name: 'alice',
        agentId: 'agent-alice',
      });
      expect(internals.readPersistedTeamProjectPath(teamName)).toBe('/repo-one');

      fs.writeFileSync(
        configPath,
        JSON.stringify({
          projectPath: '/repo-two',
          members: [{ name: 'bob', agentId: 'agent-bob' }],
        }),
        'utf8'
      );
      const refreshedAt = new Date(Date.now() + 5_000);
      fs.utimesSync(configPath, refreshedAt, refreshedAt);

      expect(internals.readPersistedRuntimeMembers(teamName)[0]).toMatchObject({
        name: 'bob',
        agentId: 'agent-bob',
      });
      expect(internals.readPersistedTeamProjectPath(teamName)).toBe('/repo-two');
    });
  });

  describe('live lead messages', () => {
    it('updates one live message for Codex synthetic text chunks', () => {
      const svc = new TeamProvisioningService();
      const internals = svc as unknown as {
        pushLiveLeadTextMessage: (
          run: object,
          cleanText: string,
          stableMessageId?: string,
          messageTimestamp?: string,
          options?: { coalesceStreamChunk?: boolean }
        ) => void;
      };
      const run = {
        teamName: 'my-team',
        runId: 'run-1',
        request: {
          members: [{ name: 'team-lead', role: 'Team Lead' }],
        },
        leadMsgSeq: 0,
        liveLeadTextBuffer: null,
        pendingToolCalls: [],
        lastLeadTextEmitMs: 0,
      };

      internals.pushLiveLeadTextMessage(run, 'Соз', undefined, '2026-04-17T12:00:00.000Z', {
        coalesceStreamChunk: true,
      });
      internals.pushLiveLeadTextMessage(run, 'дал', undefined, '2026-04-17T12:00:00.010Z', {
        coalesceStreamChunk: true,
      });
      internals.pushLiveLeadTextMessage(
        run,
        ' стартовую задачу',
        undefined,
        '2026-04-17T12:00:00.020Z',
        { coalesceStreamChunk: true }
      );

      const messages = svc.getLiveLeadProcessMessages('my-team');
      expect(messages).toHaveLength(1);
      expect(messages[0]).toMatchObject({
        messageId: 'lead-turn-run-1-1',
        text: 'Создал стартовую задачу',
        timestamp: '2026-04-17T12:00:00.000Z',
        source: 'lead_process',
      });
    });
  });

  describe('OpenCode runtime delivery user-visible impact', () => {
    it('treats policy none as authoritative over raw failed delivery facts', () => {
      const svc = new TeamProvisioningService();

      expect(
        svc.buildOpenCodeRuntimeDeliveryUserVisibleImpact({
          delivered: false,
          responsePending: false,
          ledgerStatus: 'failed_terminal',
          reason: 'empty_assistant_turn',
          diagnostics: ['empty_assistant_turn'],
          policyImpact: { state: 'none' },
        })
      ).toEqual({ state: 'none' });
    });

    it('schedules one bounded recovery retry before terminalizing no-assistant OpenCode delivery', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      const record = {
        id: 'opencode-prompt:test',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-1',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: null,
        replyRecipient: 'team-lead',
        actionMode: null,
        taskRefs: [],
        payloadHash: 'sha256:test',
        status: 'accepted',
        responseState: 'prompt_delivered_no_assistant_message',
        attempts: 3,
        maxAttempts: 3,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: null,
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: null,
        observedAssistantPreview: null,
        observedToolCallNames: [],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'prompt_delivered_no_assistant_message',
        diagnostics: ['prompt_delivered_no_assistant_message'],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(),
        markNextAttemptScheduled: vi.fn(async (input: any) => ({
          ...record,
          status: input.status,
          nextAttemptAt: input.nextAttemptAt,
          lastReason: input.reason,
          updatedAt: input.scheduledAt,
        })),
      };

      const nextRecord = await (svc as any).scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'prompt_delivered_no_assistant_message',
      });

      expect(ledger.markFailedTerminal).not.toHaveBeenCalled();
      expect(ledger.markNextAttemptScheduled).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          status: 'retry_scheduled',
          reason: 'prompt_delivered_no_assistant_message',
        })
      );
      expect(nextRecord.status).toBe('retry_scheduled');
    });

    it('emits a terminal failure event when exhausted work-sync proof retries fail', async () => {
      const svc = new TeamProvisioningService();
      const taskRefs = [{ taskId: 'task-1', displayId: 'task-1', teamName: 'team-a' }];
      const record = {
        id: 'opencode-prompt:work-sync-proof-missing',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-work-sync-proof-missing',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: 'member_work_sync_nudge',
        workSyncIntent: 'agenda_sync',
        replyRecipient: 'team-lead',
        actionMode: 'do',
        taskRefs,
        payloadHash: 'sha256:work-sync',
        status: 'retry_scheduled',
        responseState: 'responded_non_visible_tool',
        attempts: 3,
        maxAttempts: 3,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: '2026-05-18T08:31:45.000Z',
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: 'assistant-1',
        observedAssistantPreview: null,
        observedToolCallNames: ['member_work_sync_status'],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'member_work_sync_report_required',
        diagnostics: ['member_work_sync_report_required'],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const failedRecord = {
        ...record,
        status: 'failed_terminal',
        failedAt: '2026-05-18T08:32:00.000Z',
        updatedAt: '2026-05-18T08:32:00.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(async () => failedRecord),
        markNextAttemptScheduled: vi.fn(),
      };
      const harness = svc as unknown as {
        scheduleOpenCodePromptDeliveryWatchdog: ReturnType<typeof vi.fn>;
        logOpenCodePromptDeliveryEvent: ReturnType<typeof vi.fn>;
        scheduleOpenCodePromptLedgerFollowUp(input: {
          ledger: typeof ledger;
          ledgerRecord: typeof record;
          teamName: string;
          memberName: string;
          retry: boolean;
          reason: string;
        }): Promise<typeof failedRecord>;
      };
      harness.scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      harness.logOpenCodePromptDeliveryEvent = vi.fn();

      const nextRecord = await harness.scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'member_work_sync_report_required',
      });

      expect(nextRecord).toBe(failedRecord);
      expect(ledger.markFailedTerminal).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          reason: 'member_work_sync_report_required',
        })
      );
      expect(harness.logOpenCodePromptDeliveryEvent).toHaveBeenCalledWith(
        'opencode_prompt_delivery_terminal_failure',
        failedRecord,
        expect.objectContaining({
          reason: 'member_work_sync_report_required',
          retry: true,
        })
      );
    });

    it('uses stamped OpenCode session-refresh evidence instead of stale historical diagnostics', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      const record = {
        id: 'opencode-prompt:session-refresh',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-1',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: null,
        replyRecipient: 'team-lead',
        actionMode: null,
        taskRefs: [],
        payloadHash: 'sha256:test',
        status: 'accepted',
        responseState: 'session_stale',
        attempts: 1,
        maxAttempts: 3,
        sessionRefreshAttempts: 0,
        maxSessionRefreshAttempts: 5,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: null,
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: null,
        observedAssistantPreview: null,
        observedToolCallNames: [],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'resolved_behavior_changed:old->new',
        lastSessionRefreshReason: 'resolved_behavior_changed:old->new',
        diagnostics: ['network timeout', 'resolved_behavior_changed:old->new'],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(),
        markNextAttemptScheduled: vi.fn(),
        markSessionRefreshScheduled: vi.fn(async (input: any) => ({
          ...record,
          status: 'retry_scheduled',
          responseState: 'session_stale',
          nextAttemptAt: input.nextAttemptAt,
          sessionRefreshAttempts: 1,
          lastSessionRefreshReason: input.reason,
          lastReason: input.reason,
          updatedAt: input.scheduledAt,
        })),
      };

      const nextRecord = await (svc as any).scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'resolved_behavior_changed:old->new',
      });

      expect(ledger.markFailedTerminal).not.toHaveBeenCalled();
      expect(ledger.markNextAttemptScheduled).not.toHaveBeenCalled();
      expect(ledger.markSessionRefreshScheduled).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          reason: 'resolved_behavior_changed:old->new',
          maxSessionRefreshAttempts: 5,
        })
      );
      expect(nextRecord).toMatchObject({
        status: 'retry_scheduled',
        sessionRefreshAttempts: 1,
      });
    });

    it('does not reuse stamped OpenCode session-refresh evidence for current action-required stale sessions', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      const record = {
        id: 'opencode-prompt:session-stale-auth',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-1',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: null,
        replyRecipient: 'team-lead',
        actionMode: null,
        taskRefs: [],
        payloadHash: 'sha256:test',
        status: 'accepted',
        responseState: 'session_stale',
        attempts: 1,
        maxAttempts: 3,
        sessionRefreshAttempts: 1,
        maxSessionRefreshAttempts: 5,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: null,
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: null,
        observedAssistantPreview: null,
        observedToolCallNames: [],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'authentication_failed: invalid api key',
        lastSessionRefreshReason: 'resolved_behavior_changed:old->new',
        diagnostics: [
          'resolved_behavior_changed:old->new',
          'authentication_failed: invalid api key',
        ],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(),
        markSessionRefreshScheduled: vi.fn(),
        markNextAttemptScheduled: vi.fn(async (input: any) => ({
          ...record,
          status: input.status,
          nextAttemptAt: input.nextAttemptAt,
          lastReason: input.reason,
          updatedAt: input.scheduledAt,
        })),
      };

      const nextRecord = await (svc as any).scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'authentication_failed: invalid api key',
      });

      expect(ledger.markSessionRefreshScheduled).not.toHaveBeenCalled();
      expect(ledger.markNextAttemptScheduled).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          status: 'retry_scheduled',
          reason: 'authentication_failed: invalid api key',
        })
      );
      expect(nextRecord.status).toBe('retry_scheduled');
    });

    it('does not let generic session-refresh stamps bypass current action-required diagnostics', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      const record = {
        id: 'opencode-prompt:session-stale-generic-auth',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-1',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: null,
        replyRecipient: 'team-lead',
        actionMode: null,
        taskRefs: [],
        payloadHash: 'sha256:test',
        status: 'accepted',
        responseState: 'session_stale',
        attempts: 1,
        maxAttempts: 3,
        sessionRefreshAttempts: 1,
        maxSessionRefreshAttempts: 5,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: null,
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: null,
        observedAssistantPreview: null,
        observedToolCallNames: [],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'OpenCode API error',
        lastSessionRefreshReason: 'OpenCode API error',
        diagnostics: ['OpenCode API error', 'permission_blocked'],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(),
        markSessionRefreshScheduled: vi.fn(),
        markNextAttemptScheduled: vi.fn(async (input: any) => ({
          ...record,
          status: input.status,
          nextAttemptAt: input.nextAttemptAt,
          lastReason: input.reason,
          updatedAt: input.scheduledAt,
        })),
      };

      const nextRecord = await (svc as any).scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'OpenCode API error',
      });

      expect(ledger.markSessionRefreshScheduled).not.toHaveBeenCalled();
      expect(ledger.markNextAttemptScheduled).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          status: 'retry_scheduled',
          reason: 'OpenCode API error',
        })
      );
      expect(nextRecord.status).toBe('retry_scheduled');
      expect(
        (svc as any).isOpenCodeSessionRefreshRetryRecord(
          {
            ...record,
            id: 'opencode-prompt:session-stale-display-auth',
            lastReason: 'OpenCode session changed; refreshing the session before retry.',
            lastSessionRefreshReason:
              'OpenCode session changed; refreshing the session before retry.',
          },
          'OpenCode session changed; refreshing the session before retry.'
        )
      ).toBe(false);
    });

    it('does not reuse stale session-refresh stamps for later non-session-stale retries', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).scheduleOpenCodePromptDeliveryWatchdog = vi.fn();
      const record = {
        id: 'opencode-prompt:no-assistant-after-refresh',
        teamName: 'team-a',
        memberName: 'atlas',
        laneId: 'secondary:opencode:atlas',
        runId: 'run-1',
        runtimeSessionId: 'ses-1',
        inboxMessageId: 'msg-1',
        inboxTimestamp: '2026-05-18T08:31:00.000Z',
        source: 'watcher',
        messageKind: null,
        replyRecipient: 'team-lead',
        actionMode: null,
        taskRefs: [],
        payloadHash: 'sha256:test',
        status: 'accepted',
        responseState: 'prompt_delivered_no_assistant_message',
        attempts: 3,
        maxAttempts: 3,
        sessionRefreshAttempts: 1,
        maxSessionRefreshAttempts: 5,
        acceptanceUnknown: false,
        nextAttemptAt: null,
        lastAttemptAt: '2026-05-18T08:31:30.000Z',
        lastObservedAt: '2026-05-18T08:31:45.000Z',
        acceptedAt: '2026-05-18T08:31:30.000Z',
        respondedAt: null,
        failedAt: null,
        inboxReadCommittedAt: null,
        inboxReadCommitError: null,
        prePromptCursor: null,
        postPromptCursor: null,
        deliveredUserMessageId: 'delivered-1',
        observedAssistantMessageId: null,
        observedAssistantPreview: null,
        observedToolCallNames: [],
        observedVisibleMessageId: null,
        visibleReplyMessageId: null,
        visibleReplyInbox: null,
        visibleReplyCorrelation: null,
        lastReason: 'prompt_delivered_no_assistant_message',
        lastSessionRefreshReason: 'resolved_behavior_changed:old->new',
        diagnostics: [
          'resolved_behavior_changed:old->new',
          'prompt_delivered_no_assistant_message',
        ],
        createdAt: '2026-05-18T08:31:00.000Z',
        updatedAt: '2026-05-18T08:31:45.000Z',
      };
      const ledger = {
        markFailedTerminal: vi.fn(),
        markSessionRefreshScheduled: vi.fn(),
        markNextAttemptScheduled: vi.fn(async (input: any) => ({
          ...record,
          status: input.status,
          nextAttemptAt: input.nextAttemptAt,
          lastReason: input.reason,
          updatedAt: input.scheduledAt,
        })),
      };

      const nextRecord = await (svc as any).scheduleOpenCodePromptLedgerFollowUp({
        ledger,
        ledgerRecord: record,
        teamName: 'team-a',
        memberName: 'atlas',
        retry: true,
        reason: 'prompt_delivered_no_assistant_message',
      });

      expect(ledger.markSessionRefreshScheduled).not.toHaveBeenCalled();
      expect(ledger.markNextAttemptScheduled).toHaveBeenCalledWith(
        expect.objectContaining({
          id: record.id,
          status: 'retry_scheduled',
          reason: 'prompt_delivered_no_assistant_message',
        })
      );
      expect(nextRecord.status).toBe('retry_scheduled');
    });

    it('does not requeue terminal no-assistant delivery after the bounded recovery retry is exhausted', async () => {
      const svc = new TeamProvisioningService();
      const record = {
        status: 'failed_terminal',
        responseState: 'prompt_delivered_no_assistant_message',
        attempts: 4,
        maxAttempts: 3,
        inboxReadCommittedAt: null,
        lastReason: 'prompt_delivered_no_assistant_message',
        diagnostics: ['prompt_delivered_no_assistant_message'],
      };
      const ledger = {
        markNextAttemptScheduled: vi.fn(),
      };

      const nextRecord = await (svc as any).requeueOpenCodeNoAssistantTerminalDeliveryIfNeeded({
        ledger,
        ledgerRecord: record,
      });

      expect(nextRecord).toBe(record);
      expect(ledger.markNextAttemptScheduled).not.toHaveBeenCalled();
    });

    it('does not requeue terminal no-assistant delivery when diagnostics contain action-required provider errors', async () => {
      const svc = new TeamProvisioningService();
      const record = {
        status: 'failed_terminal',
        responseState: 'empty_assistant_turn',
        attempts: 3,
        maxAttempts: 3,
        inboxReadCommittedAt: null,
        lastReason: 'empty_assistant_turn',
        diagnostics: [
          'Insufficient credits. Add more using https://openrouter.ai/settings/credits',
        ],
      };
      const ledger = {
        markNextAttemptScheduled: vi.fn(),
      };

      const nextRecord = await (svc as any).requeueOpenCodeNoAssistantTerminalDeliveryIfNeeded({
        ledger,
        ledgerRecord: record,
      });

      expect(nextRecord).toBe(record);
      expect(ledger.markNextAttemptScheduled).not.toHaveBeenCalled();
    });
  });

  describe('team launch notifications', () => {
    it('fires team launched when the last pending teammate joins after ready', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-late-all-joined',
          teamName: 'late-all-joined-team',
          isLaunch: true,
          provisioningComplete: true,
          processKilled: false,
          cancelRequested: false,
          progress: { state: 'ready' },
          request: {
            cwd: tempClaudeRoot,
            displayName: 'late-all-joined-team',
          },
          expectedMembers: ['alice', 'bob'],
          allEffectiveMembers: [{ name: 'alice' }, { name: 'bob' }],
          teamLaunchedNotificationFired: undefined as boolean | undefined,
          memberSpawnStatuses: new Map([
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
            [
              'bob',
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: true,
                bootstrapConfirmed: false,
              }),
            ],
          ]),
        };
        (svc as any).runs.set(run.runId, run);
        (svc as any).aliveRunByTeam.set(run.teamName, run.runId);

        (svc as any).emitMemberSpawnChange(run, 'bob');
        expect(addTeamNotification).not.toHaveBeenCalled();

        run.memberSpawnStatuses.set(
          'bob',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
          })
        );

        (svc as any).emitMemberSpawnChange(run, 'bob');
        await Promise.resolve();

        expect(addTeamNotification).toHaveBeenCalledTimes(1);
        expect(addTeamNotification).toHaveBeenCalledWith(
          expect.objectContaining({
            teamEventType: 'team_launched',
            teamName: 'late-all-joined-team',
            dedupeKey: 'team_launched:late-all-joined-team:run-late-all-joined',
            body: 'Team "late-all-joined-team" has been launched - all 2 teammates joined and are ready for tasks.',
          })
        );

        (svc as any).emitMemberSpawnChange(run, 'bob');
        await Promise.resolve();
        expect(addTeamNotification).toHaveBeenCalledTimes(1);
      } finally {
        NotificationManager.resetInstance();
      }
    });

    it('does not latch the launched notification flag when called before all teammates join', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-early-launch-toast',
          teamName: 'early-launch-toast-team',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'early-launch-toast-team',
          },
          expectedMembers: ['alice', 'bob'],
          allEffectiveMembers: [{ name: 'alice' }, { name: 'bob' }],
          teamLaunchedNotificationFired: undefined as boolean | undefined,
          memberSpawnStatuses: new Map([
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
            [
              'bob',
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: true,
                bootstrapConfirmed: false,
              }),
            ],
          ]),
        };
        const internals = svc as unknown as {
          fireTeamLaunchedNotification(targetRun: typeof run): Promise<void>;
        };

        await internals.fireTeamLaunchedNotification(run);

        expect(addTeamNotification).not.toHaveBeenCalled();
        expect(run.teamLaunchedNotificationFired).toBeUndefined();

        run.memberSpawnStatuses.set(
          'bob',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
          })
        );

        await internals.fireTeamLaunchedNotification(run);

        expect(addTeamNotification).toHaveBeenCalledTimes(1);
      } finally {
        NotificationManager.resetInstance();
      }
    });

    it('waits for current mixed secondary lane evidence before firing team launched', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const jackLane: {
          laneId: string;
          providerId: 'opencode';
          member: { name: string; providerId: 'opencode' };
          runId: string;
          state: 'queued' | 'launching' | 'finished';
          result: null | {
            runId: string;
            teamName: string;
            launchPhase: 'finished';
            teamLaunchState: 'clean_success';
            members: Record<
              string,
              {
                memberName: string;
                providerId: 'opencode';
                launchState: 'confirmed_alive';
                agentToolAccepted: boolean;
                runtimeAlive: boolean;
                bootstrapConfirmed: boolean;
                hardFailure: boolean;
              }
            >;
            warnings: string[];
            diagnostics: string[];
          };
          warnings: string[];
          diagnostics: string[];
        } = {
          laneId: 'secondary:opencode:jack',
          providerId: 'opencode',
          member: { name: 'jack', providerId: 'opencode' },
          runId: 'opencode-run-jack-current',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        };
        const run = {
          runId: 'run-mixed-lane-race',
          teamName: 'mixed-lane-race-team',
          isLaunch: true,
          provisioningComplete: true,
          processKilled: false,
          cancelRequested: false,
          progress: { state: 'ready' },
          request: {
            cwd: tempClaudeRoot,
            displayName: 'mixed-lane-race-team',
          },
          expectedMembers: ['alice', 'jack'],
          allEffectiveMembers: [{ name: 'alice' }, { name: 'jack', providerId: 'opencode' }],
          mixedSecondaryLanes: [jackLane],
          memberSpawnStatuses: new Map([
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
            [
              'jack',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
          ]),
        };
        const internals = svc as unknown as {
          runs: Map<string, typeof run>;
          aliveRunByTeam: Map<string, string>;
          emitMemberSpawnChange(targetRun: typeof run, memberName: string): void;
        };
        internals.runs.set(run.runId, run);
        internals.aliveRunByTeam.set(run.teamName, run.runId);

        internals.emitMemberSpawnChange(run, 'jack');
        await Promise.resolve();

        expect(addTeamNotification).not.toHaveBeenCalled();

        jackLane.state = 'finished';
        jackLane.result = {
          runId: 'opencode-run-jack-current',
          teamName: 'mixed-lane-race-team',
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            jack: {
              memberName: 'jack',
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
            },
          },
          warnings: [],
          diagnostics: [],
        };

        internals.emitMemberSpawnChange(run, 'jack');
        await Promise.resolve();

        expect(addTeamNotification).toHaveBeenCalledTimes(1);
        expect(addTeamNotification).toHaveBeenCalledWith(
          expect.objectContaining({
            teamEventType: 'team_launched',
            teamName: 'mixed-lane-race-team',
            body: 'Team "mixed-lane-race-team" has been launched - all 2 teammates joined and are ready for tasks.',
          })
        );
      } finally {
        NotificationManager.resetInstance();
      }
    });

    it('does not fire incomplete notification for pending-only teammates still joining', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-beacon-desk-15',
          teamName: 'beacon-desk-15',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'beacon-desk-15',
          },
          expectedMembers: ['alice', 'bob', 'jack', 'tom'],
          allEffectiveMembers: [
            { name: 'alice' },
            { name: 'bob' },
            { name: 'jack' },
            { name: 'tom' },
          ],
          memberSpawnStatuses: new Map(
            ['alice', 'bob', 'jack', 'tom'].map((name) => [
              name,
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: true,
                bootstrapConfirmed: false,
                hardFailure: false,
              }),
            ])
          ),
        };
        const pendingSnapshot = {
          expectedMembers: ['alice', 'bob', 'jack', 'tom'],
          members: Object.fromEntries(
            ['alice', 'bob', 'jack', 'tom'].map((name) => [
              name,
              {
                name,
                launchState: 'runtime_pending_bootstrap',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: false,
                hardFailure: false,
                lastEvaluatedAt: '2026-04-13T10:00:00.000Z',
              },
            ])
          ),
          summary: {
            confirmedCount: 0,
            pendingCount: 4,
            failedCount: 0,
            runtimeAlivePendingCount: 4,
          },
        };

        await (svc as any).fireTeamLaunchIncompleteNotification(
          run,
          [],
          pendingSnapshot.summary,
          pendingSnapshot
        );
      } finally {
        NotificationManager.resetInstance();
      }

      expect(addTeamNotification).not.toHaveBeenCalled();
    });

    it('ignores stale failed summary without concrete failed member evidence', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-stale-summary',
          teamName: 'stale-summary-team',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'stale-summary-team',
          },
          expectedMembers: ['alice'],
          allEffectiveMembers: [{ name: 'alice' }],
          memberSpawnStatuses: new Map([
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: true,
                bootstrapConfirmed: false,
                hardFailure: false,
              }),
            ],
          ]),
        };
        const staleSnapshot = {
          expectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-13T10:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 0,
            pendingCount: 0,
            failedCount: 1,
            runtimeAlivePendingCount: 0,
          },
        };

        await (svc as any).fireTeamLaunchIncompleteNotification(
          run,
          [],
          staleSnapshot.summary,
          staleSnapshot
        );
      } finally {
        NotificationManager.resetInstance();
      }

      expect(addTeamNotification).not.toHaveBeenCalled();
    });

    it('prefers live confirmed evidence over stale persisted failed member evidence', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-live-confirmed',
          teamName: 'live-confirmed-team',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'live-confirmed-team',
          },
          expectedMembers: ['alice'],
          allEffectiveMembers: [{ name: 'alice' }],
          memberSpawnStatuses: new Map([
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
                hardFailure: false,
              }),
            ],
          ]),
        };
        const staleSnapshot = {
          expectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'stale failure',
              lastEvaluatedAt: '2026-04-13T10:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 0,
            pendingCount: 0,
            failedCount: 1,
            runtimeAlivePendingCount: 0,
          },
        };

        await (svc as any).fireTeamLaunchIncompleteNotification(
          run,
          [],
          staleSnapshot.summary,
          staleSnapshot
        );
      } finally {
        NotificationManager.resetInstance();
      }

      expect(addTeamNotification).not.toHaveBeenCalled();
    });

    it('uses live member evidence instead of stale summary for incomplete launch copy', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-relay-works-18',
          teamName: 'relay-works-18',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'relay-works-18',
          },
          expectedMembers: ['bob', 'jack', 'alice', 'tom'],
          allEffectiveMembers: [
            { name: 'bob' },
            { name: 'jack' },
            { name: 'alice' },
            { name: 'tom' },
          ],
          memberSpawnStatuses: new Map([
            [
              'bob',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
            [
              'jack',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'error',
                launchState: 'failed_to_start',
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: true,
                hardFailureReason: 'Insufficient credits',
              }),
            ],
            [
              'tom',
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: true,
                bootstrapConfirmed: false,
              }),
            ],
          ]),
        };
        const staleSnapshot = {
          expectedMembers: ['bob', 'jack', 'alice', 'tom'],
          members: Object.fromEntries(
            ['bob', 'jack', 'alice', 'tom'].map((name) => [
              name,
              {
                name,
                launchState: 'runtime_pending_bootstrap',
                agentToolAccepted: true,
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: false,
                lastEvaluatedAt: '2026-04-13T10:00:00.000Z',
              },
            ])
          ),
          summary: {
            confirmedCount: 0,
            pendingCount: 4,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
        };

        await (svc as any).fireTeamLaunchIncompleteNotification(
          run,
          [{ name: 'alice' }],
          staleSnapshot.summary,
          staleSnapshot
        );
      } finally {
        NotificationManager.resetInstance();
      }

      expect(addTeamNotification).toHaveBeenCalledTimes(1);
      const payload = addTeamNotification.mock.calls[0]![0] as { body: string };
      expect(payload.body).toBe('2/4 joined · failed: @alice · still joining: @tom');
      expect(payload.body).not.toContain('0/4');
      expect(payload.body).not.toContain('did not join');
    });

    it('does not report persisted bootstrap-confirmed primary members as failed from a stale failed list', async () => {
      const { NotificationManager } =
        await import('@main/services/infrastructure/NotificationManager');
      const addTeamNotification = vi.fn(async (_payload: unknown) => undefined);
      NotificationManager.setInstance({ addTeamNotification } as never);

      try {
        const svc = new TeamProvisioningService();
        const run = {
          runId: 'run-forge-labs-15',
          teamName: 'forge-labs-15',
          isLaunch: true,
          request: {
            cwd: tempClaudeRoot,
            displayName: 'forge-labs-15',
          },
          expectedMembers: ['bob', 'jack', 'alice', 'tom'],
          allEffectiveMembers: [
            { name: 'bob' },
            { name: 'jack' },
            { name: 'alice' },
            { name: 'tom' },
          ],
          memberSpawnStatuses: new Map([
            [
              'bob',
              createMemberSpawnStatusEntry({
                status: 'error',
                launchState: 'failed_to_start',
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: true,
                hardFailureReason: 'Teammate was never spawned during launch.',
              }),
            ],
            [
              'jack',
              createMemberSpawnStatusEntry({
                status: 'error',
                launchState: 'failed_to_start',
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: true,
                hardFailureReason: 'Teammate was never spawned during launch.',
              }),
            ],
            [
              'alice',
              createMemberSpawnStatusEntry({
                status: 'waiting',
                launchState: 'runtime_pending_bootstrap',
                runtimeAlive: false,
                bootstrapConfirmed: false,
              }),
            ],
            [
              'tom',
              createMemberSpawnStatusEntry({
                status: 'online',
                launchState: 'confirmed_alive',
                runtimeAlive: true,
                bootstrapConfirmed: true,
              }),
            ],
          ]),
        };
        const reconciledSnapshot = {
          expectedMembers: ['bob', 'jack', 'alice', 'tom'],
          members: {
            bob: {
              name: 'bob',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-05-04T19:32:37.000Z',
            },
            jack: {
              name: 'jack',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-05-04T19:32:30.000Z',
            },
            alice: {
              name: 'alice',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-05-04T19:35:49.000Z',
            },
            tom: {
              name: 'tom',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-05-04T19:35:49.000Z',
            },
          },
          summary: {
            confirmedCount: 3,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
        };

        await (svc as any).fireTeamLaunchIncompleteNotification(
          run,
          [{ name: 'bob' }, { name: 'jack' }],
          reconciledSnapshot.summary,
          reconciledSnapshot
        );
      } finally {
        NotificationManager.resetInstance();
      }

      expect(addTeamNotification).not.toHaveBeenCalled();
    });
  });

  describe('getClaudeLogs', () => {
    it('retains the last logs after cleanupRun removes the live run', async () => {
      const svc = new TeamProvisioningService();
      const run = createClaudeLogsRun();

      (svc as any).runs.set(run.runId, run);
      (svc as any).aliveRunByTeam.set(run.teamName, run.runId);

      await expect(svc.getClaudeLogs(run.teamName)).resolves.toEqual({
        lines: ['boom', '[stderr]', 'first line', '[stdout]'],
        total: 4,
        hasMore: false,
        updatedAt: '2026-04-19T10:00:01.000Z',
      });

      (svc as any).cleanupRun(run);

      await expect(svc.getClaudeLogs(run.teamName)).resolves.toEqual({
        lines: ['boom', '[stderr]', 'first line', '[stdout]'],
        total: 4,
        hasMore: false,
        updatedAt: '2026-04-19T10:00:01.000Z',
      });
    });

    it('writes a launch failure artifact pack when cleanup finalizes failed launch state', async () => {
      allowConsoleLogs();
      const svc = new TeamProvisioningService();
      const teamName = 'launch-artifact-cleanup-team';
      const runId = 'run-launch-artifact-cleanup';
      const startedAt = '2026-05-09T00:25:00.000Z';
      const run = createClaudeLogsRun({
        runId,
        teamName,
        startedAt,
        isLaunch: true,
        provisioningComplete: false,
        cancelRequested: false,
        deterministicBootstrap: true,
        expectedMembers: ['bob'],
        allEffectiveMembers: [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'anthropic',
            model: 'opus',
          },
        ],
        request: {
          cwd: '/repo',
          providerId: 'anthropic',
          model: 'opus',
          members: [
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'anthropic',
              model: 'opus',
            },
          ],
        },
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'spawning',
              launchState: 'runtime_pending_bootstrap',
              runtimeAlive: true,
              firstSpawnAcceptedAt: '2026-05-09T00:25:05.000Z',
              updatedAt: '2026-05-09T00:25:05.000Z',
            }),
          ],
        ]),
        progress: {
          runId,
          teamName,
          state: 'failed',
          message: 'Launch failed',
          startedAt,
          updatedAt: '2026-05-09T00:26:00.000Z',
          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.',
        },
        claudeLogLines: [
          '[stderr]',
          'Warning: no stdin data received in 3s, proceeding without it.',
        ],
        provisioningOutputParts: [],
      });

      (svc as any).runs.set(run.runId, run);
      (svc as any).aliveRunByTeam.set(run.teamName, run.runId);
      (svc as any).cleanupRun(run);

      const latestPath = path.join(
        tempTeamsBase,
        teamName,
        'launch-failure-artifacts',
        'latest.json'
      );
      await waitForFile(latestPath);
      const latest = JSON.parse(fs.readFileSync(latestPath, 'utf8')) as { manifestPath: string };
      const manifest = JSON.parse(fs.readFileSync(latest.manifestPath, 'utf8')) as {
        reason: string;
        classification: { code: string };
        bootstrapTransportBreadcrumb: {
          submitRejected: boolean;
          noStdinWarning: boolean;
          retryable: boolean | null;
        };
      };

      expect(manifest.reason).toBe('launch_progress_failed');
      expect(manifest.classification.code).toBe('transport_rejected');
      expect(manifest.bootstrapTransportBreadcrumb).toMatchObject({
        submitRejected: true,
        noStdinWarning: true,
        retryable: true,
      });
    });

    it('falls back to the persisted lead transcript when no live run exists', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'offline-logs-team';
      const projectPath = '/tmp/offline-logs-project';
      const leadSessionId = 'lead-session-1';
      const projectDir = path.join(tempProjectsBase, encodePath(projectPath));

      writeLaunchConfig(teamName, projectPath, leadSessionId, []);
      fs.mkdirSync(projectDir, { recursive: true });
      fs.writeFileSync(
        path.join(projectDir, `${leadSessionId}.jsonl`),
        [
          '{"type":"user","message":{"role":"user","content":"first"}}',
          '{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"second"}]}}',
          '{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"third"}]}}',
        ].join('\n') + '\n',
        'utf8'
      );

      await expect(svc.getClaudeLogs(teamName)).resolves.toEqual({
        lines: [
          '{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"third"}]}}',
          '{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"second"}]}}',
          '{"type":"user","message":{"role":"user","content":"first"}}',
        ],
        total: 3,
        hasMore: false,
        updatedAt: expect.any(String),
      });
    });

    it('clears retained logs when a new run starts for the same team', async () => {
      const svc = new TeamProvisioningService();

      (svc as any).retainedClaudeLogsByTeam.set('logs-team', {
        lines: ['[stdout]', 'stale line'],
        updatedAt: '2026-04-19T10:00:01.000Z',
      });

      (svc as any).resetTeamScopedTransientStateForNewRun('logs-team');

      await expect(svc.getClaudeLogs('logs-team')).resolves.toEqual({
        lines: [],
        total: 0,
        hasMore: false,
      });
    });
  });

  describe('provisioning status', () => {
    it('retains final progress after cleanupRun removes the live run', async () => {
      const svc = new TeamProvisioningService();
      const run = createClaudeLogsRun({
        runId: 'run-retained-progress',
        teamName: 'retained-progress-team',
        provisioningComplete: false,
        progress: {
          runId: 'run-retained-progress',
          teamName: 'retained-progress-team',
          state: 'failed',
          message: 'CLI exited quickly',
          startedAt: '2026-04-19T10:00:00.000Z',
          updatedAt: '2026-04-19T10:00:01.000Z',
          error: 'bootstrap failed',
          warnings: ['retry is safe'],
        },
      });

      (svc as any).runs.set(run.runId, run);
      (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

      (svc as any).cleanupRun(run);

      expect((svc as any).runs.has(run.runId)).toBe(false);
      await expect(svc.getProvisioningStatus(run.runId)).resolves.toMatchObject({
        runId: run.runId,
        teamName: run.teamName,
        state: 'failed',
        message: 'CLI exited quickly',
        error: 'bootstrap failed',
        warnings: ['retry is safe'],
      });
    });

    it('treats result.success as a fallback provisioning completion signal', () => {
      const svc = new TeamProvisioningService();
      const run = createClaudeLogsRun({
        runId: 'run-success-fallback',
        teamName: 'success-fallback-team',
        provisioningComplete: false,
        progress: {
          runId: 'run-success-fallback',
          teamName: 'success-fallback-team',
          state: 'configuring',
          message: 'Waiting for CLI result',
          startedAt: '2026-04-19T10:00:00.000Z',
          updatedAt: '2026-04-19T10:00:01.000Z',
        },
      });
      const complete = vi
        .spyOn(svc as any, 'handleProvisioningTurnComplete')
        .mockResolvedValue(undefined);

      (svc as any).handleStreamJsonMessage(run, { type: 'result', subtype: 'success' });

      expect(complete).toHaveBeenCalledWith(run);
    });

    it('finalizes unconfirmed launch members as failed before cleanup removes the run', () => {
      allowConsoleLogs();
      const svc = new TeamProvisioningService();
      const run = createClaudeLogsRun({
        runId: 'run-cleanup-finalizes-launch',
        teamName: 'cleanup-finalizes-launch-team',
        isLaunch: true,
        provisioningComplete: false,
        cancelRequested: false,
        expectedMembers: ['alice', 'bob'],
        provisioningOutputParts: [],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              livenessSource: 'process',
            }),
          ],
        ]),
      });
      const persist = vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);

      (svc as any).runs.set(run.runId, run);
      (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

      (svc as any).cleanupRun(run);

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: true,
      });
      expect((svc as any).buildLiveLaunchSnapshotForRun(run, 'finished')).toMatchObject({
        teamLaunchState: 'partial_failure',
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 1,
        },
      });
      expect(persist).toHaveBeenCalledWith(run, 'finished');
    });

    it('preserves specific member launch failures when cleanup applies its fallback reason', () => {
      allowConsoleLogs();
      const svc = new TeamProvisioningService();
      const timeoutReason = 'Teammate was registered but did not bootstrap-confirm before timeout.';
      const specificReason = 'OpenCode bridge reported member launch failure';
      const run = createClaudeLogsRun({
        runId: 'run-cleanup-preserves-specific-launch-failure',
        teamName: 'cleanup-preserves-specific-launch-failure-team',
        isLaunch: true,
        provisioningComplete: false,
        cancelRequested: false,
        expectedMembers: ['bob', 'carol'],
        provisioningOutputParts: [],
        progress: {
          runId: 'run-cleanup-preserves-specific-launch-failure',
          teamName: 'cleanup-preserves-specific-launch-failure-team',
          state: 'failed',
          message: 'Deterministic bootstrap failed',
          startedAt: '2026-04-19T10:00:00.000Z',
          updatedAt: '2026-04-19T10:00:01.000Z',
          error: timeoutReason,
        },
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'error',
              launchState: 'failed_to_start',
              error: specificReason,
              hardFailure: true,
              hardFailureReason: specificReason,
              bootstrapConfirmed: false,
              runtimeAlive: true,
              livenessSource: 'process',
            }),
          ],
          [
            'carol',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
            }),
          ],
        ]),
      });
      vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);

      (svc as any).runs.set(run.runId, run);
      (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

      (svc as any).cleanupRun(run);

      const bobStatus = run.memberSpawnStatuses.get('bob');
      expect(bobStatus).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        hardFailureReason: specificReason,
        runtimeAlive: false,
        runtimeDiagnostic:
          'Bootstrap failed before teammate check-in; launch-owned runtime cleanup requested.',
        runtimeDiagnosticSeverity: 'warning',
      });
      expect(bobStatus?.livenessSource).toBeUndefined();
      expect(run.memberSpawnStatuses.get('carol')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        hardFailureReason: timeoutReason,
      });
    });

    it('does not treat non-terminal error text as an existing launch failure during cleanup', () => {
      allowConsoleLogs();
      const svc = new TeamProvisioningService();
      const timeoutReason = 'Teammate was registered but did not bootstrap-confirm before timeout.';
      const run = createClaudeLogsRun({
        runId: 'run-cleanup-non-terminal-error-text',
        teamName: 'cleanup-non-terminal-error-text-team',
        isLaunch: true,
        provisioningComplete: false,
        cancelRequested: false,
        expectedMembers: ['bob'],
        provisioningOutputParts: [],
        progress: {
          runId: 'run-cleanup-non-terminal-error-text',
          teamName: 'cleanup-non-terminal-error-text-team',
          state: 'failed',
          message: 'Deterministic bootstrap failed',
          startedAt: '2026-04-19T10:00:00.000Z',
          updatedAt: '2026-04-19T10:00:01.000Z',
          error: timeoutReason,
        },
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              error: 'Transient runtime diagnostic, not terminal launch failure',
              agentToolAccepted: true,
              bootstrapConfirmed: false,
            }),
          ],
        ]),
      });
      vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);

      (svc as any).runs.set(run.runId, run);
      (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

      (svc as any).cleanupRun(run);

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        hardFailure: true,
        hardFailureReason: timeoutReason,
        error: timeoutReason,
      });
    });
  });

  describe('lead activity task intervals', () => {
    it('reports runtime adapter teams as idle instead of offline when no legacy run exists', () => {
      const svc = new TeamProvisioningService();
      const internals = svc as unknown as LeadActivityServiceInternals;
      const teamName = 'opencode-runtime-adapter-lead-activity-team';
      const runId = 'opencode-runtime-adapter-run';

      internals.aliveRunByTeam.set(teamName, runId);
      internals.runtimeAdapterRunByTeam.set(teamName, {
        runId,
        providerId: 'opencode',
        cwd: '/tmp/opencode-runtime-adapter-lead-activity-team',
        members: {},
      });
      internals.runtimeAdapterProgressByRunId.set(runId, {
        runId,
        teamName,
        state: 'ready',
        message: 'OpenCode team launch is waiting for runtime evidence or permissions',
        startedAt: '2026-05-02T10:00:00.000Z',
        updatedAt: '2026-05-02T10:00:05.000Z',
      });

      expect(svc.isTeamAlive(teamName)).toBe(true);
      expect(svc.getLeadActivityState(teamName)).toEqual({
        state: 'idle',
        runId,
      });
    });

    it('keeps terminal runtime adapter progress offline without a legacy run', () => {
      const svc = new TeamProvisioningService();
      const internals = svc as unknown as LeadActivityServiceInternals;
      const teamName = 'opencode-runtime-adapter-terminal-lead-activity-team';
      const runId = 'opencode-runtime-adapter-terminal-run';

      internals.aliveRunByTeam.set(teamName, runId);
      internals.runtimeAdapterRunByTeam.set(teamName, {
        runId,
        providerId: 'opencode',
        cwd: '/tmp/opencode-runtime-adapter-terminal-lead-activity-team',
        members: {},
      });
      internals.runtimeAdapterProgressByRunId.set(runId, {
        runId,
        teamName,
        state: 'failed',
        message: 'OpenCode team launch failed readiness gate',
        startedAt: '2026-05-02T10:00:00.000Z',
        updatedAt: '2026-05-02T10:00:05.000Z',
      });

      expect(svc.getLeadActivityState(teamName)).toEqual({
        state: 'offline',
        runId: null,
      });
    });

    it('read-repairs active lead task intervals once when lead activity is polled', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:00.000Z'));
      const resumeSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'resumeActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 1 });
      try {
        const svc = new TeamProvisioningService();
        const internals = svc as unknown as LeadActivityServiceInternals;
        const teamName = 'lead-activity-read-repair-team';
        const run = toLeadActivityTestRun(
          {
            runId: 'run-lead-read-repair',
            teamName,
            expectedMembers: ['alice'],
          },
          'active',
          'lead'
        );
        internals.runs.set(run.runId, run);
        internals.aliveRunByTeam.set(teamName, run.runId);

        expect(svc.getLeadActivityState(teamName)).toEqual({
          state: 'active',
          runId: run.runId,
        });
        expect(svc.getLeadActivityState(teamName)).toEqual({
          state: 'active',
          runId: run.runId,
        });

        expect(resumeSpy).toHaveBeenCalledTimes(1);
        expect(resumeSpy).toHaveBeenCalledWith(teamName, 'lead', '2026-05-02T10:00:00.000Z');
      } finally {
        resumeSpy.mockRestore();
      }
    });

    it('syncs lead task intervals only for the currently tracked run', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const resumeSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'resumeActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 1 });
      const pauseSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'pauseActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 1 });
      try {
        const svc = new TeamProvisioningService();
        const internals = svc as unknown as LeadActivityServiceInternals;
        const teamName = 'lead-activity-current-run-team';
        const run = toLeadActivityTestRun(
          {
            runId: 'run-current-lead-activity',
            teamName,
            expectedMembers: ['alice'],
          },
          'idle',
          'team-lead'
        );
        internals.runs.set(run.runId, run);
        internals.aliveRunByTeam.set(teamName, run.runId);

        internals.setLeadActivity(run, 'active');
        internals.setLeadActivity(run, 'active');
        internals.setLeadActivity(run, 'idle');

        expect(resumeSpy).toHaveBeenCalledTimes(1);
        expect(resumeSpy).toHaveBeenCalledWith(teamName, 'team-lead', '2026-05-02T10:05:00.000Z');
        expect(pauseSpy).toHaveBeenCalledTimes(1);
        expect(pauseSpy).toHaveBeenCalledWith(teamName, 'team-lead', '2026-05-02T10:05:00.000Z');

        const staleRun = toLeadActivityTestRun(
          {
            runId: 'run-stale-lead-activity',
            teamName,
            expectedMembers: ['alice'],
          },
          'active',
          'team-lead'
        );
        internals.runs.set(staleRun.runId, staleRun);

        internals.setLeadActivity(staleRun, 'offline');

        expect(pauseSpy).toHaveBeenCalledTimes(1);
      } finally {
        resumeSpy.mockRestore();
        pauseSpy.mockRestore();
      }
    });
  });

  describe('member spawn status launch reads', () => {
    it('coalesces concurrent active launch status reads and serves a short cached follow-up', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-cache-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
      });
      run.isLaunch = true;
      run.progress = {
        teamName,
        state: 'launching',
        message: 'Launching',
        updatedAt: '2026-05-02T10:00:00.000Z',
      };
      run.onProgress = vi.fn();
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      const refreshDeferred = createDeferred<void>();
      const refreshLeadInbox = vi
        .spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox')
        .mockImplementation(async () => refreshDeferred.promise);
      const auditStatuses = vi
        .spyOn(svc as any, 'maybeAuditMemberSpawnStatuses')
        .mockResolvedValue(undefined);
      const persistSnapshot = vi
        .spyOn(svc as any, 'persistLaunchStateSnapshot')
        .mockResolvedValue(null);

      const first = svc.getMemberSpawnStatuses(teamName);
      const second = svc.getMemberSpawnStatuses(teamName);
      await Promise.resolve();

      expect(refreshLeadInbox).toHaveBeenCalledTimes(1);
      refreshDeferred.resolve();
      const [firstSnapshot, secondSnapshot] = await Promise.all([first, second]);

      expect(firstSnapshot).toEqual(secondSnapshot);
      expect(auditStatuses).toHaveBeenCalledTimes(1);
      expect(persistSnapshot).toHaveBeenCalledTimes(1);

      await svc.getMemberSpawnStatuses(teamName);

      expect(refreshLeadInbox).toHaveBeenCalledTimes(1);
      expect(auditStatuses).toHaveBeenCalledTimes(1);
      expect(persistSnapshot).toHaveBeenCalledTimes(1);
    });

    it('invalidates the short status cache when a real member-spawn change is emitted', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-cache-invalidated-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
      });
      run.isLaunch = true;
      run.progress = {
        teamName,
        state: 'launching',
        message: 'Launching',
        updatedAt: '2026-05-02T10:00:00.000Z',
      };
      run.onProgress = vi.fn();
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      const refreshLeadInbox = vi
        .spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox')
        .mockResolvedValue(undefined);
      vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);
      vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);

      await svc.getMemberSpawnStatuses(teamName);
      expect(refreshLeadInbox).toHaveBeenCalledTimes(1);

      (svc as any).setMemberSpawnStatus(
        run,
        'alice',
        'online',
        undefined,
        'heartbeat',
        '2026-05-02T10:00:05.000Z'
      );
      await svc.getMemberSpawnStatuses(teamName);

      expect(refreshLeadInbox).toHaveBeenCalledTimes(2);
    });

    it('pauses member task intervals at last runtime evidence plus grace when runtime goes offline', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const pauseSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'pauseActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 0 });
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-runtime-offline-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            {
              status: 'online',
              launchState: 'confirmed_alive',
              updatedAt: '2026-05-02T10:00:02.000Z',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              livenessSource: 'heartbeat',
              lastHeartbeatAt: '2026-05-02T10:00:00.000Z',
              livenessLastCheckedAt: '2026-05-02T10:00:01.000Z',
            },
          ],
        ]),
      });

      (svc as any).setMemberSpawnStatus(run, 'alice', 'offline');

      expect(pauseSpy).toHaveBeenCalledWith(teamName, 'alice', '2026-05-02T10:00:06.000Z');
    });

    it('pauses member task intervals when snapshot sync observes runtime loss', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const pauseSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'pauseActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 0 });
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-runtime-snapshot-offline-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            {
              status: 'online',
              launchState: 'confirmed_alive',
              updatedAt: '2026-05-02T10:00:02.000Z',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              livenessSource: 'heartbeat',
              lastHeartbeatAt: '2026-05-02T10:00:00.000Z',
              livenessLastCheckedAt: '2026-05-02T10:00:01.000Z',
            },
          ],
        ]),
      });
      const snapshot = createPersistedLaunchSnapshot({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'finished',
        members: {
          alice: {
            name: 'alice',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'Runtime disappeared before finalization.',
            lastEvaluatedAt: '2026-05-02T10:04:00.000Z',
          },
        },
      });

      (svc as any).syncRunMemberSpawnStatusesFromSnapshot(run, snapshot);

      expect(pauseSpy).toHaveBeenCalledWith(teamName, 'alice', '2026-05-02T10:00:06.000Z');
      expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
        status: 'error',
        runtimeAlive: false,
        launchState: 'failed_to_start',
      });
    });

    it('resumes member task intervals at the heartbeat evidence time when runtime comes online', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const resumeSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'resumeActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 0 });
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-runtime-online-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            {
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              updatedAt: '2026-05-02T09:59:00.000Z',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
            },
          ],
        ]),
      });

      (svc as any).setMemberSpawnStatus(
        run,
        'alice',
        'online',
        undefined,
        'heartbeat',
        '2026-05-02T10:00:00.000Z'
      );

      expect(resumeSpy).toHaveBeenCalledWith(teamName, 'alice', '2026-05-02T10:00:00.000Z');
    });

    it('does not resume member task intervals from a stale heartbeat older than offline status', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const resumeSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'resumeActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 0 });
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-runtime-stale-heartbeat-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            {
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              updatedAt: '2026-05-02T10:04:00.000Z',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastHeartbeatAt: '2026-05-02T10:00:00.000Z',
            },
          ],
        ]),
      });

      (svc as any).setMemberSpawnStatus(
        run,
        'alice',
        'online',
        undefined,
        'heartbeat',
        '2026-05-02T10:00:30.000Z'
      );

      expect(resumeSpy).toHaveBeenCalledWith(teamName, 'alice', '2026-05-02T10:05:00.000Z');
    });

    it('does not resume member task intervals from stale direct runtime evidence', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:05:00.000Z'));
      const resumeSpy = vi
        .spyOn(TeamTaskActivityIntervalService.prototype, 'resumeActiveIntervalsForMember')
        .mockReturnValue({ changedTasks: 0 });
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-runtime-stale-direct-evidence-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
      });
      const previous = {
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        updatedAt: '2026-05-02T10:04:00.000Z',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
      };
      const next = {
        ...previous,
        status: 'online',
        launchState: 'confirmed_alive',
        updatedAt: '2026-05-02T10:03:00.000Z',
        runtimeAlive: true,
        bootstrapConfirmed: true,
      };

      (svc as any).syncMemberTaskActivityForRuntimeTransition(
        run,
        'alice',
        previous,
        next,
        '2026-05-02T10:00:30.000Z'
      );

      expect(resumeSpy).toHaveBeenCalledWith(teamName, 'alice', '2026-05-02T10:05:00.000Z');
    });

    it('retries the owner status request when a member-spawn change lands while it is building', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'spawn-cache-owner-retry-team';
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
      });
      run.isLaunch = true;
      run.progress = {
        teamName,
        state: 'launching',
        message: 'Launching',
        updatedAt: '2026-05-02T10:00:00.000Z',
      };
      run.onProgress = vi.fn();
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      const firstRefresh = createDeferred<void>();
      const refreshLeadInbox = vi
        .spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox')
        .mockImplementationOnce(async () => firstRefresh.promise)
        .mockResolvedValue(undefined);
      vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);
      vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);

      const pending = svc.getMemberSpawnStatuses(teamName);
      await Promise.resolve();
      expect(refreshLeadInbox).toHaveBeenCalledTimes(1);

      (svc as any).setMemberSpawnStatus(
        run,
        'alice',
        'online',
        undefined,
        'heartbeat',
        '2026-05-02T10:00:05.000Z'
      );
      firstRefresh.resolve();
      const result = await pending;

      expect(refreshLeadInbox).toHaveBeenCalledTimes(2);
      expect(result.statuses.alice).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
        bootstrapConfirmed: true,
      });
    });
  });

  describe('launch-state no-op persistence guard', () => {
    it('does not clear persisted launch state for an expected run after tracking is gone', () => {
      const svc = new TeamProvisioningService();

      expect(
        (svc as any).canClearPersistedLaunchStateForRun(
          'workspace-trust-stale-clear-team',
          'run-stale'
        )
      ).toBe(false);
    });

    it('invalidates runtime cache when launch-state is cleared', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-clear-invalidates-runtime-cache';
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      await (svc as any).clearPersistedLaunchState(teamName);

      expect((svc as any).launchStateStore.clear).toHaveBeenCalledWith(teamName);
      expect(invalidateRuntime).toHaveBeenCalledTimes(1);
    });

    it('does not rewrite launch-state or invalidate runtime cache for a recent semantic no-op', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:05.000Z'));
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-noop-team';
      const status = createMemberSpawnStatusEntry({
        updatedAt: '2026-05-02T10:00:00.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
      });
      const previousSnapshot = snapshotFromRuntimeMemberStatuses({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'active',
        updatedAt: '2026-05-02T10:00:02.000Z',
        statuses: { alice: status as any },
      });
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([['alice', status]]),
      });
      run.isLaunch = true;
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).launchStateWrittenRunIdByTeam.set(teamName, run.runId);
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      const result = await (svc as any).persistLaunchStateSnapshotNow(run, 'active');

      expect(result).toBe(previousSnapshot);
      expect((svc as any).launchStateStore.write).not.toHaveBeenCalled();
      expect(invalidateRuntime).not.toHaveBeenCalled();
    });

    it('keeps a bounded launch-state heartbeat for unchanged active snapshots', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:20.000Z'));
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-heartbeat-team';
      const status = createMemberSpawnStatusEntry({
        updatedAt: '2026-05-02T10:00:00.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
      });
      const previousSnapshot = snapshotFromRuntimeMemberStatuses({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'active',
        updatedAt: '2026-05-02T10:00:00.000Z',
        statuses: { alice: status as any },
      });
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([['alice', status]]),
      });
      run.isLaunch = true;
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      const result = await (svc as any).persistLaunchStateSnapshotNow(run, 'active');

      expect(result.updatedAt).toBe('2026-05-02T10:00:20.000Z');
      expect((svc as any).launchStateStore.write).toHaveBeenCalledTimes(1);
      expect(invalidateRuntime).toHaveBeenCalledTimes(1);
    });

    it('does not skip the first service-owned launch-state write for an existing snapshot', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:05.000Z'));
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-first-write-team';
      const status = createMemberSpawnStatusEntry({
        updatedAt: '2026-05-02T10:00:00.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
      });
      const previousSnapshot = snapshotFromRuntimeMemberStatuses({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'active',
        updatedAt: '2026-05-02T10:00:02.000Z',
        statuses: { alice: status as any },
      });
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([['alice', status]]),
      });
      run.isLaunch = true;
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      await (svc as any).persistLaunchStateSnapshotNow(run, 'active');

      expect((svc as any).launchStateStore.write).toHaveBeenCalledTimes(1);
      expect(invalidateRuntime).toHaveBeenCalledTimes(1);
    });

    it('does not skip the first write for a new run even when the previous snapshot is semantically equal', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:05.000Z'));
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-new-run-team';
      const status = createMemberSpawnStatusEntry({
        updatedAt: '2026-05-02T10:00:00.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
      });
      const previousSnapshot = snapshotFromRuntimeMemberStatuses({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'active',
        updatedAt: '2026-05-02T10:00:02.000Z',
        statuses: { alice: status as any },
      });
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([['alice', status]]),
      });
      run.isLaunch = true;
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).launchStateWrittenRunIdByTeam.set(teamName, 'previous-run-id');
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      const result = await (svc as any).persistLaunchStateSnapshotNow(run, 'active');

      expect(result.updatedAt).toBe('2026-05-02T10:00:05.000Z');
      expect((svc as any).launchStateStore.write).toHaveBeenCalledTimes(1);
      expect(invalidateRuntime).toHaveBeenCalledTimes(1);
    });

    it('writes and invalidates runtime cache when launch-state semantics change', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-02T10:00:05.000Z'));
      const svc = new TeamProvisioningService();
      const teamName = 'launch-state-change-team';
      const previousStatus = createMemberSpawnStatusEntry({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        updatedAt: '2026-05-02T10:00:00.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
      });
      const nextStatus = createMemberSpawnStatusEntry({
        status: 'online',
        launchState: 'confirmed_alive',
        runtimeAlive: true,
        bootstrapConfirmed: true,
        hardFailure: false,
        livenessSource: 'heartbeat',
        updatedAt: '2026-05-02T10:00:05.000Z',
        firstSpawnAcceptedAt: '2026-05-02T10:00:00.000Z',
        lastHeartbeatAt: '2026-05-02T10:00:05.000Z',
      });
      const previousSnapshot = snapshotFromRuntimeMemberStatuses({
        teamName,
        expectedMembers: ['alice'],
        launchPhase: 'active',
        updatedAt: '2026-05-02T10:00:02.000Z',
        statuses: { alice: previousStatus as any },
      });
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([['alice', nextStatus]]),
      });
      run.isLaunch = true;
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      const invalidateRuntime = vi.spyOn(svc as any, 'invalidateRuntimeSnapshotCaches');

      const result = await (svc as any).persistLaunchStateSnapshotNow(run, 'active');

      expect(result.members.alice?.launchState).toBe('confirmed_alive');
      expect((svc as any).launchStateStore.write).toHaveBeenCalledTimes(1);
      expect(invalidateRuntime).toHaveBeenCalledTimes(1);
    });
  });

  describe('getTeamAgentRuntimeSnapshot', () => {
    it('dedupes concurrent runtime snapshot probes for the same team', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: '%1',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      const paneInfo = createDeferred<Map<string, { paneId: string; panePid: number }>>();
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockReturnValueOnce(
        paneInfo.promise as ReturnType<typeof listTmuxPaneRuntimeInfoForCurrentPlatform>
      );
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '222': createPidusageStat(222, 456_000_000),
      } as any);

      const first = svc.getTeamAgentRuntimeSnapshot('runtime-team');
      const second = svc.getTeamAgentRuntimeSnapshot('runtime-team');
      paneInfo.resolve(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 222,
            },
          ],
        ])
      );
      const [firstSnapshot, secondSnapshot] = await Promise.all([first, second]);

      expect(listTmuxPaneRuntimeInfoForCurrentPlatform).toHaveBeenCalledTimes(1);
      expect(pidusage).toHaveBeenCalledTimes(1);
      expect(firstSnapshot.members.alice?.pid).toBe(222);
      expect(secondSnapshot.members.alice?.pid).toBe(222);
    });

    it('does not cache live runtime metadata when invalidated while the probe is in flight', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      const processRows =
        createDeferred<Awaited<ReturnType<typeof listRuntimeProcessTableForCurrentPlatform>>>();
      vi.mocked(listRuntimeProcessTableForCurrentPlatform)
        .mockReturnValueOnce(processRows.promise)
        .mockResolvedValueOnce([]);

      const first = (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team') as Promise<
        Map<string, unknown>
      >;
      (svc as any).invalidateRuntimeSnapshotCaches('runtime-team');
      processRows.resolve([]);
      await first;

      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
    });

    it('returns cloned live runtime metadata maps from cache', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([]);

      const first = (await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team')) as Map<
        string,
        unknown
      >;
      expect(first.has('alice')).toBe(true);
      first.delete('alice');

      const second = (await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team')) as Map<
        string,
        unknown
      >;

      expect(second.has('alice')).toBe(true);
      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
    });

    it('skips live process table reads when runtime metadata has no verifiable handle', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };

      const metadata = (await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team')) as Map<
        string,
        unknown
      >;

      expect(metadata.has('alice')).toBe(true);
      expect(listRuntimeProcessTableForCurrentPlatform).not.toHaveBeenCalled();
    });

    it('uses a longer live runtime metadata cache for persisted teams without a tracked run', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([]);

      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:03.000Z'));
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z'));
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);

      vi.setSystemTime(new Date('2026-05-03T12:00:11.000Z'));
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
    });

    it('reuses process rows through the short liveness cache for tracked runs', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([]);

      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:03.000Z'));
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);

      vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z'));
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
    });

    it('clears runtime probe caches when starting a new run for the team', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      vi.mocked(listRuntimeProcessTableForCurrentPlatform)
        .mockResolvedValueOnce([])
        .mockResolvedValueOnce([]);

      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
      (svc as any).resetTeamScopedTransientStateForNewRun('runtime-team');
      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
    });

    it('does not cache a probe that started before runtime adapter evidence was installed', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            {
              name: 'alice',
              providerId: 'opencode',
              model: 'gpt-5.4-mini',
              agentId: 'alice@runtime-team',
            },
          ],
        })),
      };
      (svc as any).provisioningRunByTeam.set('runtime-team', 'run-1');
      const processRows =
        createDeferred<Awaited<ReturnType<typeof listRuntimeProcessTableForCurrentPlatform>>>();
      vi.mocked(listRuntimeProcessTableForCurrentPlatform)
        .mockReturnValueOnce(processRows.promise)
        .mockResolvedValueOnce([]);

      const first = (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team') as Promise<
        Map<string, unknown>
      >;
      (svc as any).runtimeAdapterRunByTeam.set('runtime-team', {
        runId: 'run-1',
        providerId: 'opencode',
        cwd: '/tmp/runtime-project',
        members: {
          alice: {
            providerId: 'opencode',
            runtimeAlive: true,
            bootstrapConfirmed: false,
            runtimePid: 333,
            livenessKind: 'runtime_process',
            pidSource: 'agent_process_table',
          },
        },
      });
      (svc as any).invalidateRuntimeSnapshotCaches('runtime-team');
      processRows.resolve([]);
      await first;

      await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
    });

    it('uses batched pidusage rss values for lead and teammates', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: '%1',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 222,
              currentCommand: 'codex',
            },
          ],
        ])
      );

      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '222': createPidusageStat(222, 456_000_000),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        rssBytes: 123_000_000,
        runtimeModel: 'gpt-5.4',
      });
      expect(snapshot.members.alice).toMatchObject({
        pid: 222,
        rssBytes: 456_000_000,
        runtimeModel: 'gpt-5.4-mini',
      });
    });

    it('uses process table CPU and RSS values before falling back to pidusage', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: '%1',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 222,
              currentCommand: 'codex',
            },
          ],
        ])
      );
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        {
          pid: 111,
          ppid: 1,
          command: '/usr/bin/node lead.js',
          cpuPercent: 3.5,
          rssBytes: 123_000_000,
        },
        {
          pid: 222,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id alice@runtime-team --agent-name alice --team-name runtime-team --model gpt-5.4-mini',
          cpuPercent: 7,
          rssBytes: 456_000_000,
        },
      ]);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).not.toHaveBeenCalled();
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        cpuPercent: 3.5,
        rssBytes: 123_000_000,
      });
      expect(snapshot.members.alice).toMatchObject({
        pid: 222,
        cpuPercent: 7,
        rssBytes: 456_000_000,
      });
    });

    it('keeps cached runtime resource process rows across snapshot invalidations', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
      const svc = new TeamProvisioningService();
      const harness = privateHarness(svc);
      harness.aliveRunByTeam.set('runtime-team', 'run-1');
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: 111,
          ppid: 1,
          command: '/usr/bin/node lead.js',
          cpuPercent: 3.5,
          rssBytes: 123_000_000,
        },
      ]);

      const firstRows = await harness.readRuntimeProcessRowsForUsageSnapshot('runtime-team');
      harness.invalidateRuntimeSnapshotCaches('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:05.000Z'));
      const secondRows = await harness.readRuntimeProcessRowsForUsageSnapshot('runtime-team');

      expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
      expect(secondRows).toEqual(firstRows);
      vi.useRealTimers();
    });

    it('keeps fresh live runtime process rows across snapshot invalidations', () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
      const svc = new TeamProvisioningService();
      const harness = privateHarness(svc);
      const rows: RuntimeTelemetryProcessTableRow[] = [
        {
          pid: 111,
          ppid: 1,
          command: '/usr/bin/node lead.js',
          cpuPercent: 3.5,
          rssBytes: 123_000_000,
          runtimeTelemetrySource: 'native',
        },
      ];
      harness.runtimeProcessRowsForUsageSnapshotByTeam.set('runtime-team', {
        expiresAtMs: Date.now() + 60_000,
        generation: harness.getRuntimeSnapshotCacheGeneration('runtime-team'),
        runId: 'run-1',
        sampledAtMs: Date.now(),
        rows,
        includesWindowsHostRows: false,
      });

      harness.invalidateRuntimeSnapshotCaches('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:04.000Z'));

      const cached = harness.readCachedRuntimeProcessRowsForLiveRuntimeMetadata(
        'runtime-team',
        'run-1'
      );
      expect(cached).toEqual({ rows });
      vi.useRealTimers();
    });

    it('skips pidusage by default when process table metrics are missing', async () => {
      await withRuntimePidusageTelemetryEnv(undefined, async () => {
        const svc = new TeamProvisioningService();
        (svc as any).configReader = {
          getConfig: vi.fn(async () => ({
            members: [
              { name: 'team-lead', agentType: 'team-lead' },
              { name: 'alice', model: 'gpt-5.4-mini' },
            ],
          })),
        };
        (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
          {
            name: 'alice',
            agentId: 'alice@runtime-team',
            tmuxPaneId: '%1',
            backendType: 'tmux',
          },
        ]);
        (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
        (svc as any).runs.set('run-1', {
          runId: 'run-1',
          child: { pid: 111 },
          request: { model: 'gpt-5.4' },
          processKilled: false,
          cancelRequested: false,
          spawnContext: null,
        });
        vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
          new Map([
            [
              '%1',
              {
                paneId: '%1',
                panePid: 222,
                currentCommand: 'codex',
              },
            ],
          ])
        );
        vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
          {
            pid: 999,
            ppid: 1,
            command: '/usr/bin/node unrelated.js',
            cpuPercent: 1.5,
            rssBytes: 12_000_000,
          },
        ]);

        const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

        expect(pidusage).not.toHaveBeenCalled();
        expect(snapshot.members['team-lead']).toMatchObject({ pid: 111 });
        expect(snapshot.members['team-lead']?.rssBytes).toBeUndefined();
        expect(snapshot.members.alice).toMatchObject({ pid: 222 });
        expect(snapshot.members.alice?.rssBytes).toBeUndefined();
      });
    });

    it('falls back to pidusage for root pids missing from an otherwise available process table', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: '%1',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 222,
              currentCommand: 'codex',
            },
          ],
        ])
      );
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        {
          pid: 999,
          ppid: 1,
          command: '/usr/bin/node unrelated.js',
          cpuPercent: 1.5,
          rssBytes: 12_000_000,
        },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '222': createPidusageStat(222, 456_000_000),
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        rssBytes: 123_000_000,
      });
      expect(snapshot.members.alice).toMatchObject({
        pid: 222,
        rssBytes: 456_000_000,
      });
    });

    it('captures CPU and memory history on runtime snapshots', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000, 3.5),
      } as any);

      const firstSnapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(firstSnapshot.members['team-lead']).toMatchObject({
        pid: 111,
        pidSource: 'lead_process',
        cpuPercent: 3.5,
        rssBytes: 123_000_000,
        primaryCpuPercent: 3.5,
        primaryRssBytes: 123_000_000,
        processCount: 1,
        runtimeLoadScope: 'single-process',
      });
      expect(firstSnapshot.members['team-lead']?.resourceHistory).toEqual([
        expect.objectContaining({
          cpuPercent: 3.5,
          rssBytes: 123_000_000,
          primaryCpuPercent: 3.5,
          primaryRssBytes: 123_000_000,
          processCount: 1,
          runtimeLoadScope: 'single-process',
          pidSource: 'lead_process',
          pid: 111,
        }),
      ]);

      (svc as any).invalidateRuntimeSnapshotCaches('runtime-team');
      vi.setSystemTime(new Date('2026-05-03T12:00:31.000Z'));
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 130_000_000, 18),
      } as any);

      const secondSnapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(secondSnapshot.members['team-lead']).toMatchObject({
        cpuPercent: 18,
        rssBytes: 130_000_000,
        primaryCpuPercent: 18,
        primaryRssBytes: 130_000_000,
        processCount: 1,
        runtimeLoadScope: 'single-process',
      });
      expect(secondSnapshot.members['team-lead']?.resourceHistory).toEqual([
        expect.objectContaining({
          cpuPercent: 3.5,
          rssBytes: 123_000_000,
          pidSource: 'lead_process',
          pid: 111,
        }),
        expect.objectContaining({
          cpuPercent: 18,
          rssBytes: 130_000_000,
          primaryCpuPercent: 18,
          primaryRssBytes: 130_000_000,
          processCount: 1,
          runtimeLoadScope: 'single-process',
          pidSource: 'lead_process',
          pid: 111,
        }),
      ]);
    });

    it('aggregates CPU and memory across the runtime process tree', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 111, ppid: 1, command: 'claude' },
        { pid: 222, ppid: 111, command: 'node tool.js' },
        { pid: 333, ppid: 222, command: 'python worker.py' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
        '222': createPidusageStat(222, 30_000_000, 5),
        '333': createPidusageStat(333, 20_000_000, 7),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 222, 333], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        cpuPercent: 14,
        rssBytes: 150_000_000,
        primaryCpuPercent: 2,
        primaryRssBytes: 100_000_000,
        childCpuPercent: 12,
        childRssBytes: 50_000_000,
        processCount: 3,
        runtimeLoadScope: 'process-tree',
      });
      expect(snapshot.members['team-lead']?.resourceHistory).toEqual([
        expect.objectContaining({
          cpuPercent: 14,
          rssBytes: 150_000_000,
          primaryCpuPercent: 2,
          primaryRssBytes: 100_000_000,
          childCpuPercent: 12,
          childRssBytes: 50_000_000,
          processCount: 3,
          runtimeLoadScope: 'process-tree',
        }),
      ]);
    });

    it('does not count another teammate root subtree inside lead runtime telemetry', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'codex', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'alice',
              {
                alive: true,
                backendType: 'process',
                providerId: 'codex',
                pid: 333,
                pidSource: 'agent_process_table',
                model: 'gpt-5.4-mini',
              },
            ],
          ])
      );
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 111, ppid: 1, command: 'lead' },
        { pid: 222, ppid: 111, command: 'lead-tool' },
        { pid: 333, ppid: 111, command: 'alice-runtime' },
        { pid: 444, ppid: 333, command: 'alice-tool' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
        '222': createPidusageStat(222, 10_000_000, 1),
        '333': createPidusageStat(333, 80_000_000, 3),
        '444': createPidusageStat(444, 25_000_000, 9),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith(
        [111, 222, 333, 444],
        EXPECTED_RUNTIME_PIDUSAGE_OPTIONS
      );
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        cpuPercent: 3,
        rssBytes: 110_000_000,
        childCpuPercent: 1,
        childRssBytes: 10_000_000,
        processCount: 2,
        runtimeLoadScope: 'process-tree',
      });
      expect(snapshot.members.alice).toMatchObject({
        pid: 333,
        cpuPercent: 12,
        rssBytes: 105_000_000,
        childCpuPercent: 9,
        childRssBytes: 25_000_000,
        processCount: 2,
        runtimeLoadScope: 'process-tree',
      });
    });

    it('keeps command-identified teammate roots out of lead runtime telemetry before liveness catches up', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'jack', providerId: 'anthropic', model: 'haiku-4.5' },
          ],
        })),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 111, ppid: 1, command: 'lead' },
        { pid: 222, ppid: 111, command: 'lead-tool' },
        {
          pid: 333,
          ppid: 111,
          command:
            'bun cli.js --teammate-runtime headless --agent-id jack@runtime-team --agent-name jack --team-name runtime-team',
        },
        { pid: 444, ppid: 333, command: 'jack-tool' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
        '222': createPidusageStat(222, 10_000_000, 1),
        '333': createPidusageStat(333, 400_000_000, 40),
        '444': createPidusageStat(444, 200_000_000, 80),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        cpuPercent: 3,
        rssBytes: 110_000_000,
        childCpuPercent: 1,
        childRssBytes: 10_000_000,
        processCount: 2,
        runtimeLoadScope: 'process-tree',
      });
      expect(snapshot.members.jack?.cpuPercent).toBeUndefined();
      expect(snapshot.members.jack?.rssBytes).toBeUndefined();
    });

    it('does not cut a same-member metrics root out of that member process tree', () => {
      const svc = new TeamProvisioningService();
      const rows = [
        { pid: 111, ppid: 1, command: 'alice-runtime' },
        { pid: 222, ppid: 111, command: 'alice-metrics-runtime' },
        { pid: 333, ppid: 222, command: 'alice-tool' },
        { pid: 444, ppid: 111, command: 'bob-runtime' },
        { pid: 555, ppid: 444, command: 'bob-tool' },
      ];
      const ownersByPid = new Map<number, ReadonlySet<string>>([
        [111, new Set(['alice'])],
        [222, new Set(['alice'])],
        [444, new Set(['bob'])],
      ]);

      const trees = (svc as any).buildRuntimeUsageProcessTrees([111, 222, 444], rows, ownersByPid);

      expect(trees.get(111)).toEqual({ pids: [111, 222, 333], truncated: false });
      expect(trees.get(222)).toEqual({ pids: [222, 333], truncated: false });
      expect(trees.get(444)).toEqual({ pids: [444, 555], truncated: false });
    });

    it('aggregates CPU and memory for non-tmux process backend members', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'alice',
              {
                alive: true,
                backendType: 'process',
                providerId: 'opencode',
                pid: 333,
                pidSource: 'agent_process_table',
                model: 'opencode/minimax-m2.5-free',
              },
            ],
          ])
      );
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        {
          pid: 333,
          ppid: 1,
          command:
            '/Users/test/.bun/bin/bun cli.js --team-name runtime-team --agent-id alice@runtime-team',
        },
        { pid: 444, ppid: 333, command: 'node mcp-server.js' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '333': createPidusageStat(333, 80_000_000, 3),
        '444': createPidusageStat(444, 25_000_000, 9),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([333, 444], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members.alice).toMatchObject({
        backendType: 'process',
        providerId: 'opencode',
        pid: 333,
        pidSource: 'agent_process_table',
        cpuPercent: 12,
        rssBytes: 105_000_000,
        primaryCpuPercent: 3,
        primaryRssBytes: 80_000_000,
        childCpuPercent: 9,
        childRssBytes: 25_000_000,
        processCount: 2,
        runtimeLoadScope: 'process-tree',
      });
    });

    it('marks shared OpenCode host runtime load as non-exclusive', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'bob', providerId: 'opencode', model: 'opencode/big-pickle' },
          ],
        })),
      };
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'bob',
              {
                alive: true,
                backendType: 'process',
                providerId: 'opencode',
                metricsPid: 555,
                model: 'opencode/big-pickle',
              },
            ],
          ])
      );
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 555, ppid: 1, command: 'opencode serve' },
        { pid: 556, ppid: 555, command: 'node openrouter-worker.js' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '555': createPidusageStat(555, 90_000_000, 4),
        '556': createPidusageStat(556, 40_000_000, 6),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members.bob).toMatchObject({
        providerId: 'opencode',
        pid: 555,
        restartable: false,
        cpuPercent: 10,
        rssBytes: 130_000_000,
        primaryCpuPercent: 4,
        childCpuPercent: 6,
        processCount: 2,
        runtimeLoadScope: 'shared-host',
      });
      expect(snapshot.members.bob?.resourceHistory).toEqual([
        expect.objectContaining({
          cpuPercent: 10,
          rssBytes: 130_000_000,
          runtimeLoadScope: 'shared-host',
        }),
      ]);
    });

    it('builds aggregate runtime stats from sampled processes when a child exits mid-snapshot', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 111, ppid: 1, command: 'claude' },
        { pid: 222, ppid: 111, command: 'node finished-tool.js' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']).toMatchObject({
        cpuPercent: 2,
        rssBytes: 100_000_000,
        primaryCpuPercent: 2,
        primaryRssBytes: 100_000_000,
        processCount: 1,
        runtimeLoadScope: 'single-process',
      });
      expect(snapshot.members['team-lead']?.childCpuPercent).toBeUndefined();
      expect(snapshot.members['team-lead']?.childRssBytes).toBeUndefined();
    });

    it('continues runtime snapshot when telemetry tree building fails', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      (svc as any).buildRuntimeUsageProcessTrees = vi.fn(() => {
        throw new Error('tree broke');
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members['team-lead']).toMatchObject({
        alive: true,
        pid: 111,
        runtimeModel: 'gpt-5.4',
      });
      expect(snapshot.members['team-lead']?.cpuPercent).toBeUndefined();
      expect(snapshot.members['team-lead']?.rssBytes).toBeUndefined();
    });

    it('continues runtime snapshot when aggregate load stats fail for a member', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
      } as any);
      (svc as any).buildRuntimeProcessLoadStats = vi.fn(() => {
        throw new Error('aggregate broke');
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members['team-lead']).toMatchObject({
        alive: true,
        pid: 111,
      });
      expect(snapshot.members['team-lead']?.cpuPercent).toBeUndefined();
      expect(snapshot.members['team-lead']?.resourceHistory).toBeUndefined();
    });

    it('keeps current runtime metrics when telemetry history recording fails', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
      } as any);
      (svc as any).recordAgentRuntimeResourceSample = vi.fn(() => {
        throw new Error('history broke');
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members['team-lead']).toMatchObject({
        cpuPercent: 2,
        rssBytes: 100_000_000,
        primaryCpuPercent: 2,
        primaryRssBytes: 100_000_000,
      });
      expect(snapshot.members['team-lead']?.resourceHistory).toBeUndefined();
    });

    it('keeps runtime snapshot when telemetry history pruning fails', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
      } as any);
      (svc as any).pruneAgentRuntimeResourceHistory = vi.fn(() => {
        throw new Error('prune broke');
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members['team-lead']).toMatchObject({
        cpuPercent: 2,
        rssBytes: 100_000_000,
      });
    });

    it('caps oversized runtime process trees without blocking the snapshot', () => {
      const svc = new TeamProvisioningService();
      const rows = [
        { pid: 111, ppid: 1, command: 'claude' },
        ...Array.from({ length: 70 }, (_, index) => ({
          pid: 200 + index,
          ppid: index === 0 ? 111 : 199 + index,
          command: `child-${index}`,
        })),
      ];

      const trees = (svc as any).buildRuntimeUsageProcessTrees([111], rows);
      const tree = trees.get(111);

      expect(tree?.pids).toHaveLength(64);
      expect(tree?.pids[0]).toBe(111);
      expect(tree?.truncated).toBe(true);
    });

    it('combines WSL and Windows host process tables for telemetry on Windows', async () => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', {
        value: 'win32',
        configurable: true,
      });
      try {
        const svc = new TeamProvisioningService();
        vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
          { pid: 111, ppid: 1, command: 'wsl-runtime' },
        ]);
        vi.mocked(listWindowsProcessTable).mockResolvedValueOnce([
          { pid: 333, ppid: 1, command: 'opencode.exe serve' },
          { pid: 444, ppid: 333, command: 'node.exe tool.js' },
        ]);

        const rows = await (svc as any).readRuntimeProcessRowsForUsageSnapshot('runtime-team', {
          includeWindowsHostRows: true,
        });

        expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
        expect(listWindowsProcessTable).toHaveBeenCalledWith(1_500);
        expect(rows).toEqual([
          { pid: 111, ppid: 1, command: 'wsl-runtime', runtimeTelemetrySource: 'wsl' },
          {
            pid: 333,
            ppid: 1,
            command: 'opencode.exe serve',
            runtimeTelemetrySource: 'windows-host',
          },
          {
            pid: 444,
            ppid: 333,
            command: 'node.exe tool.js',
            runtimeTelemetrySource: 'windows-host',
          },
        ]);
      } finally {
        Object.defineProperty(process, 'platform', {
          value: originalPlatform,
          configurable: true,
        });
      }
    });

    it('keeps WSL process rows when Windows host process table lookup times out', async () => {
      const originalPlatform = process.platform;
      const originalWindowsTimeout = (TeamProvisioningService as any)
        .RUNTIME_WINDOWS_PROCESS_TABLE_TIMEOUT_MS;
      Object.defineProperty(process, 'platform', {
        value: 'win32',
        configurable: true,
      });
      (TeamProvisioningService as any).RUNTIME_WINDOWS_PROCESS_TABLE_TIMEOUT_MS = 5;
      try {
        const svc = new TeamProvisioningService();
        vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
          { pid: 111, ppid: 1, command: 'wsl-runtime' },
        ]);
        vi.mocked(listWindowsProcessTable).mockImplementationOnce(
          () => new Promise(() => {}) as any
        );

        const rows = await (svc as any).readRuntimeProcessRowsForUsageSnapshot('runtime-team', {
          includeWindowsHostRows: true,
        });

        expect(rows).toEqual([
          { pid: 111, ppid: 1, command: 'wsl-runtime', runtimeTelemetrySource: 'wsl' },
        ]);
        expect(listWindowsProcessTable).toHaveBeenCalledWith(5);
      } finally {
        (TeamProvisioningService as any).RUNTIME_WINDOWS_PROCESS_TABLE_TIMEOUT_MS =
          originalWindowsTimeout;
        Object.defineProperty(process, 'platform', {
          value: originalPlatform,
          configurable: true,
        });
      }
    });

    it('does not mix WSL and Windows PID namespaces when building telemetry trees on Windows', () => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', {
        value: 'win32',
        configurable: true,
      });
      try {
        const svc = new TeamProvisioningService();
        const rows = [
          { pid: 111, ppid: 1, command: 'lead.exe', runtimeTelemetrySource: 'windows-host' },
          {
            pid: 222,
            ppid: 111,
            command: 'node.exe tool.js',
            runtimeTelemetrySource: 'windows-host',
          },
          { pid: 333, ppid: 111, command: 'wsl-child-collision', runtimeTelemetrySource: 'wsl' },
          { pid: 444, ppid: 333, command: 'wsl-grandchild', runtimeTelemetrySource: 'wsl' },
        ];

        const trees = (svc as any).buildRuntimeUsageProcessTrees([111, 333], rows);

        expect(trees.get(111)).toEqual({ pids: [111, 222], truncated: false });
        expect(trees.get(333)).toEqual({ pids: [], truncated: false });
      } finally {
        Object.defineProperty(process, 'platform', {
          value: originalPlatform,
          configurable: true,
        });
      }
    });

    it('treats an empty telemetry process tree as explicitly unsampled', () => {
      const svc = new TeamProvisioningService();

      const stats = (svc as any).buildRuntimeProcessLoadStats({
        rootPid: 333,
        usageStatsByPid: new Map([[333, { rssBytes: 999_000_000, cpuPercent: 88 }]]),
        processTree: { pids: [], truncated: false },
      });

      expect(stats).toBeUndefined();
    });

    it('does not sample Windows WSL tmux runtime pids when the process table is unavailable', async () => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', {
        value: 'win32',
        configurable: true,
      });
      try {
        const svc = new TeamProvisioningService();
        (svc as any).configReader = {
          getConfig: vi.fn(async () => ({
            members: [
              { name: 'team-lead', agentType: 'team-lead' },
              { name: 'alice', model: 'gpt-5.4-mini' },
            ],
          })),
        };
        (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
          async () =>
            new Map([
              [
                'alice',
                {
                  alive: false,
                  backendType: 'tmux',
                  pid: 222,
                  pidSource: 'tmux_child',
                  tmuxPaneId: '%1',
                  model: 'gpt-5.4-mini',
                },
              ],
            ])
        );
        (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
        (svc as any).runs.set('run-1', {
          runId: 'run-1',
          child: { pid: 111 },
          request: { model: 'gpt-5.4' },
          processKilled: false,
          cancelRequested: false,
          spawnContext: null,
        });
        vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockRejectedValueOnce(
          new Error('wsl process table unavailable')
        );
        vi.mocked(pidusage).mockResolvedValueOnce({
          '111': createPidusageStat(111, 100_000_000, 2),
        } as any);

        const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

        expect(pidusage).toHaveBeenCalledWith([111], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
        expect(snapshot.members['team-lead']).toMatchObject({
          pid: 111,
          rssBytes: 100_000_000,
          cpuPercent: 2,
        });
        expect(snapshot.members.alice).toMatchObject({
          pid: 222,
          pidSource: 'tmux_child',
        });
        expect(snapshot.members.alice?.rssBytes).toBeUndefined();
        expect(snapshot.members.alice?.cpuPercent).toBeUndefined();
      } finally {
        Object.defineProperty(process, 'platform', {
          value: originalPlatform,
          configurable: true,
        });
      }
    });

    it('ignores malformed process table rows before building runtime telemetry trees', async () => {
      const svc = new TeamProvisioningService();
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        { pid: 111, ppid: 1, command: 'claude' },
        null,
        { pid: 0, ppid: 1, command: 'bad-pid' },
        { pid: 222, ppid: 111, command: '   ' },
        { pid: 333, ppid: 111, command: 'node tool.js' },
        { pid: '444', ppid: '333', command: 'python worker.py' },
      ] as any);

      const rows = await (svc as any).readRuntimeProcessRowsForUsageSnapshot('runtime-team');
      const trees = (svc as any).buildRuntimeUsageProcessTrees([111], rows);

      expect(rows).toEqual([
        { pid: 111, ppid: 1, command: 'claude', runtimeTelemetrySource: 'native' },
        { pid: 333, ppid: 111, command: 'node tool.js', runtimeTelemetrySource: 'native' },
        {
          pid: 444,
          ppid: 333,
          command: 'python worker.py',
          runtimeTelemetrySource: 'native',
        },
      ]);
      expect(trees.get(111)).toEqual({ pids: [111, 333, 444], truncated: false });
    });

    it('fails soft when runtime process table lookup times out', async () => {
      const svc = new TeamProvisioningService();
      const originalProcessTableTimeout = (TeamProvisioningService as any)
        .RUNTIME_PROCESS_TABLE_TIMEOUT_MS;
      (TeamProvisioningService as any).RUNTIME_PROCESS_TABLE_TIMEOUT_MS = 5;
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockImplementationOnce(
        () => new Promise(() => {}) as any
      );

      try {
        const rows = await (svc as any).readRuntimeProcessRowsForUsageSnapshot('runtime-team');

        expect(rows).toBeNull();
        expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
      } finally {
        (TeamProvisioningService as any).RUNTIME_PROCESS_TABLE_TIMEOUT_MS =
          originalProcessTableTimeout;
      }
    });

    it('keeps runtime snapshot when live process table lookup times out', async () => {
      const svc = new TeamProvisioningService();
      const originalProcessTableTimeout = (TeamProvisioningService as any)
        .RUNTIME_PROCESS_TABLE_TIMEOUT_MS;
      (TeamProvisioningService as any).RUNTIME_PROCESS_TABLE_TIMEOUT_MS = 5;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini', agentId: 'alice@runtime-team' },
          ],
        })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockImplementationOnce(
        () => new Promise(() => {}) as any
      );
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 100_000_000, 2),
      } as any);

      try {
        const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

        expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
        expect(snapshot.members['team-lead']).toMatchObject({
          alive: true,
          pid: 111,
          cpuPercent: 2,
          rssBytes: 100_000_000,
        });
      } finally {
        (TeamProvisioningService as any).RUNTIME_PROCESS_TABLE_TIMEOUT_MS =
          originalProcessTableTimeout;
      }
    });

    it('caps runtime resource history per member and pid', () => {
      const svc = new TeamProvisioningService();
      let history: unknown[] | undefined;
      for (let index = 0; index < 70; index += 1) {
        history = (svc as any).recordAgentRuntimeResourceSample({
          teamName: 'runtime-team',
          memberName: 'alice',
          timestamp: `2026-04-24T12:${String(index).padStart(2, '0')}:00.000Z`,
          cpuPercent: index,
          rssBytes: 100_000_000 + index,
          pidSource: 'tmux_child',
          pid: 222,
        });
      }

      expect(history).toHaveLength(60);
      expect(history?.[0]).toMatchObject({
        cpuPercent: 10,
        rssBytes: 100_000_010,
        pidSource: 'tmux_child',
        pid: 222,
      });
      expect(history?.[59]).toMatchObject({
        cpuPercent: 69,
        rssBytes: 100_000_069,
      });
    });

    it('does not send legacy process backend pane markers to tmux liveness lookup', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: 'process:4242',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
      } as any);

      await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(listTmuxPaneRuntimeInfoForCurrentPlatform).not.toHaveBeenCalled();
      expect(listTmuxPanePidsForCurrentPlatform).not.toHaveBeenCalled();
    });

    it('exposes providerBackendId from the live run request when available', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead', providerId: 'codex' }],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({ providerId: 'codex', providerBackendId: 'adapter' })),
      };
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { providerId: 'codex', model: 'gpt-5.4', providerBackendId: 'codex-native' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.providerBackendId).toBe('codex-native');
    });

    it('falls back to persisted team meta backend when no live run exists', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead', providerId: 'codex' }],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({ providerId: 'codex', providerBackendId: 'codex-native' })),
      };

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.providerBackendId).toBe('codex-native');
    });

    it('drops stale Codex backend metadata for Anthropic runtime snapshots', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead', providerId: 'anthropic' }],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          providerId: 'anthropic',
          providerBackendId: 'codex-native',
        })),
      };

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.providerBackendId).toBeUndefined();
    });

    it('uses launch identity instead of stale root provider metadata for runtime snapshots', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead', providerId: 'anthropic' }],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          providerId: 'codex',
          providerBackendId: 'codex-native',
          fastMode: 'on',
          launchIdentity: {
            providerId: 'anthropic',
            providerBackendId: null,
            selectedFastMode: 'inherit',
          },
        })),
      };

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.providerBackendId).toBeUndefined();
      expect(snapshot.fastMode).toBe('inherit');
    });

    it('falls back to per-pid pidusage reads when batched sampling fails', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          tmuxPaneId: '%1',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 222,
              currentCommand: 'codex',
            },
          ],
        ])
      );

      vi.mocked(pidusage)
        .mockRejectedValueOnce(new Error('ps: process exited'))
        .mockResolvedValueOnce(createPidusageStat(111, 123_000_000) as any)
        .mockResolvedValueOnce(createPidusageStat(222, 456_000_000) as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenNthCalledWith(1, [111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(pidusage).toHaveBeenNthCalledWith(2, 111, EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(pidusage).toHaveBeenNthCalledWith(3, 222, EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members['team-lead']?.rssBytes).toBe(123_000_000);
      expect(snapshot.members.alice?.rssBytes).toBe(456_000_000);
    });

    it('fails soft when batched pidusage sampling times out', async () => {
      const svc = new TeamProvisioningService();
      const originalBatchTimeout = (TeamProvisioningService as any)
        .RUNTIME_PIDUSAGE_BATCH_TIMEOUT_MS;
      (TeamProvisioningService as any).RUNTIME_PIDUSAGE_BATCH_TIMEOUT_MS = 5;
      vi.mocked(pidusage).mockImplementation(() => new Promise(() => {}) as any);

      try {
        const stats = await (svc as any).readProcessUsageStatsByPid([111, 222]);

        expect(stats.size).toBe(0);
        expect(pidusage).toHaveBeenCalledTimes(1);
        expect(pidusage).toHaveBeenCalledWith([111, 222], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      } finally {
        (TeamProvisioningService as any).RUNTIME_PIDUSAGE_BATCH_TIMEOUT_MS = originalBatchTimeout;
      }
    });

    it('ignores malformed pidusage results while keeping valid runtime stats', async () => {
      const svc = new TeamProvisioningService();
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': null,
        '222': { memory: 'bad', cpu: Number.NaN },
        '333': { memory: '123000000', cpu: '7' },
      } as any);

      const stats = await (svc as any).readProcessUsageStatsByPid([111, 222, 333]);

      expect(stats.size).toBe(1);
      expect(stats.get(333)).toEqual({ rssBytes: 123_000_000, cpuPercent: 7 });
    });

    it('caches runtime process usage stats for repeated reads', async () => {
      const svc = new TeamProvisioningService();
      const usageByPid: Record<string, ReturnType<typeof createPidusageStat>> = {
        '111': createPidusageStat(111, 123_000_000, 7),
      };
      vi.mocked(pidusage).mockResolvedValueOnce(usageByPid);

      const harness = privateHarness(svc);
      const first = await harness.readProcessUsageStatsByPid([111]);
      const second = await harness.readProcessUsageStatsByPid([111]);

      expect(pidusage).toHaveBeenCalledTimes(1);
      expect(pidusage).toHaveBeenCalledWith([111], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(first.get(111)).toEqual({ rssBytes: 123_000_000, cpuPercent: 7 });
      expect(second.get(111)).toEqual({ rssBytes: 123_000_000, cpuPercent: 7 });
    });

    it('bounds runtime process usage cache entries', async () => {
      const svc = new TeamProvisioningService();
      const maxEntries = (TeamProvisioningService as unknown as Record<string, number>)
        .RUNTIME_PROCESS_USAGE_CACHE_MAX_ENTRIES;
      const pids = Array.from({ length: maxEntries + 2 }, (_value, index) => 10_000 + index);
      const firstPid = pids[0]!;
      const newestPid = pids[pids.length - 1]!;
      const usageByPid = Object.fromEntries(
        pids.map((pid, index) => [String(pid), createPidusageStat(pid, 100_000_000 + index, 1)])
      );
      vi.mocked(pidusage).mockResolvedValueOnce(usageByPid);

      await privateHarness(svc).readProcessUsageStatsByPid(pids);

      const cache = (
        svc as unknown as {
          runtimeProcessUsageStatsCacheByPid: Map<number, unknown>;
        }
      ).runtimeProcessUsageStatsCacheByPid;
      expect(cache.size).toBe(maxEntries);
      expect(cache.has(firstPid)).toBe(false);
      expect(cache.has(newestPid)).toBe(true);
    });

    it('falls back to direct agent process lookup when tmux pane pid lookup is unavailable', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.2' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@nice-team',
          tmuxPaneId: '%0',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('nice-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: 333,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id alice@nice-team --agent-name alice --team-name nice-team --model gpt-5.2',
        },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '333': createPidusageStat(333, 456_000_000),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('nice-team');

      expect(snapshot.members['team-lead']).toMatchObject({
        pid: 111,
        rssBytes: 123_000_000,
      });
      expect(snapshot.members.alice).toMatchObject({
        pid: 333,
        rssBytes: 456_000_000,
        runtimeModel: 'gpt-5.2',
      });
    });

    it('keeps RSS visible for bootstrap-confirmed Anthropic teammates with a verified process', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'anthropic', model: 'claude-sonnet-4-6' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@nice-team',
          backendType: 'tmux',
        },
      ]);
      const run = createMemberSpawnRun({
        teamName: 'nice-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
              lastHeartbeatAt: '2026-04-24T12:00:00.000Z',
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.request = { model: 'claude-opus-4-6' };
      run.processKilled = false;
      run.cancelRequested = false;
      (svc as any).aliveRunByTeam.set('nice-team', run.runId);
      (svc as any).runs.set(run.runId, run);
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: 333,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id alice@nice-team --agent-name alice --team-name nice-team --model claude-sonnet-4-6',
        },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '333': createPidusageStat(333, 456_000_000),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('nice-team');

      expect(pidusage).toHaveBeenCalledWith([111, 333], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members.alice).toMatchObject({
        alive: true,
        providerId: 'anthropic',
        pid: 333,
        pidSource: 'agent_process_table',
        rssBytes: 456_000_000,
        runtimeModel: 'claude-sonnet-4-6',
      });
    });

    it('prefers the newest matching agent pid when multiple processes match the same teammate', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', model: 'gpt-5.2' },
          ],
        })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@nice-team',
          tmuxPaneId: '%0',
          backendType: 'tmux',
        },
      ]);
      (svc as any).aliveRunByTeam.set('nice-team', 'run-1');
      (svc as any).runs.set('run-1', {
        runId: 'run-1',
        child: { pid: 111 },
        request: { model: 'gpt-5.4' },
        processKilled: false,
        cancelRequested: false,
        spawnContext: null,
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: 222,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id alice@nice-team --agent-name alice --team-name nice-team --model gpt-5.2',
        },
        {
          pid: 333,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --team-name nice-team --agent-id alice@nice-team --agent-name alice --model gpt-5.2',
        },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '111': createPidusageStat(111, 123_000_000),
        '333': createPidusageStat(333, 456_000_000),
      } as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('nice-team');

      expect(snapshot.members.alice).toMatchObject({
        pid: 333,
        rssBytes: 456_000_000,
      });
    });

    it('excludes removed meta members from runtime snapshot candidate members', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            removedAt: Date.now(),
          },
        ]),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => null),
      };
      vi.mocked(pidusage).mockResolvedValueOnce({} as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members.alice).toBeUndefined();
    });

    it('keeps historical bootstrap separate from current runtime liveness', async () => {
      const teamName = 'pure-opencode-runtime-team-strict';
      const projectPath = '/Users/test/project';
      writeLaunchConfig(teamName, projectPath, 'lead-session', ['alice']);
      writeLaunchState(teamName, 'lead-session', {
        alice: {
          providerId: 'opencode',
          model: 'opencode/big-pickle',
          launchState: 'confirmed_alive',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: true,
          hardFailure: false,
          hardFailureReason: undefined,
        },
      });

      const svc = new TeamProvisioningService();
      (svc as any).runtimeAdapterRunByTeam.set(teamName, {
        runId: 'opencode-runtime-run',
        providerId: 'opencode',
        cwd: projectPath,
      });
      (svc as any).aliveRunByTeam.set(teamName, 'opencode-runtime-run');

      const snapshot = await svc.getTeamAgentRuntimeSnapshot(teamName);

      expect(snapshot.members.alice).toMatchObject({
        alive: false,
        historicalBootstrapConfirmed: true,
        providerId: 'opencode',
        runtimeModel: 'opencode/big-pickle',
      });
    });

    it('reconciles persisted launch state before building runtime snapshot metadata', async () => {
      const teamName = 'zz-runtime-snapshot-reconciles-before-live-metadata';
      const leadSessionId = 'lead-session';
      const projectPath = '/Users/test/proj';
      const bootstrapAttemptAt = '2026-05-24T09:25:33.388Z';
      const bootstrapConfirmedAt = '2026-05-24T09:25:42.904Z';
      const appAcceptedAt = '2026-05-24T09:25:45.178Z';
      const staleRefreshAt = '2026-05-24T11:36:58.278Z';
      const runtimePid = 97_255;
      const bootstrapRunId = 'run-runtime-snapshot-reconcile-first';
      const staleDiagnostic = 'persisted runtime pid is not alive';

      writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
      writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
      writeLaunchState(
        teamName,
        leadSessionId,
        {
          tom: {
            providerId: 'anthropic',
            model: 'haiku',
            laneId: 'primary',
            laneKind: 'primary',
            laneOwnerProviderId: 'codex',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            runtimePid,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason:
              'runtime pid could not be verified because process table is unavailable',
            livenessKind: 'stale_metadata',
            runtimeDiagnostic: staleDiagnostic,
            runtimeDiagnosticSeverity: 'warning',
            firstSpawnAcceptedAt: appAcceptedAt,
            runtimeLastSeenAt: staleRefreshAt,
            lastEvaluatedAt: staleRefreshAt,
          },
        },
        { launchPhase: 'finished', updatedAt: staleRefreshAt }
      );
      writeBootstrapState(
        teamName,
        [
          {
            name: 'tom',
            status: 'bootstrap_confirmed',
            lastAttemptAt: Date.parse(bootstrapAttemptAt),
            lastObservedAt: Date.parse(bootstrapConfirmedAt),
          },
        ],
        '2026-05-24T09:26:08.090Z',
        { runId: bootstrapRunId }
      );

      const svc = new TeamProvisioningService();

      const snapshot = await svc.getTeamAgentRuntimeSnapshot(teamName);
      const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath(teamName), 'utf8'));

      expect(snapshot.members.tom).toMatchObject({
        alive: true,
        livenessKind: 'confirmed_bootstrap',
        runtimeDiagnostic: 'bootstrap confirmed',
        runtimeDiagnosticSeverity: 'info',
      });
      expect(snapshot.members.tom?.runtimeDiagnostic).not.toBe(staleDiagnostic);
      expect(persisted.members.tom).toMatchObject({
        launchState: 'confirmed_alive',
        bootstrapConfirmed: true,
        hardFailure: false,
      });
      expect(persisted.members.tom?.runtimeDiagnostic).not.toBe(staleDiagnostic);
    });

    it('exposes confirmed runtime snapshot after CLI provisioned-but-not-alive launch cleanup', async () => {
      const teamName = 'zz-runtime-snapshot-cli-provisioned-not-alive-heals';
      const leadSessionId = 'lead-session';
      const projectPath = '/Users/test/proj';
      const bootstrapRunId = 'run-runtime-snapshot-cli-exit-after-bootstrap';
      const reason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
      writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
      writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
      writeLaunchState(
        teamName,
        leadSessionId,
        {
          tom: {
            providerId: 'anthropic',
            model: 'sonnet',
            laneId: 'primary',
            laneKind: 'primary',
            laneOwnerProviderId: 'anthropic',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            runtimePid: 27_036,
            bootstrapConfirmed: true,
            hardFailure: true,
            hardFailureReason: reason,
            livenessKind: 'confirmed_bootstrap',
            pidSource: 'persisted_metadata',
            runtimeDiagnostic:
              'runtime pid could not be verified because process table is unavailable',
            runtimeDiagnosticSeverity: 'warning',
            firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
            lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
            runtimeLastSeenAt: '2026-05-25T20:13:46.326Z',
            lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
          },
        },
        { launchPhase: 'finished', updatedAt: '2026-05-25T20:14:05.411Z' }
      );
      writeBootstrapState(
        teamName,
        [
          {
            name: 'tom',
            status: 'bootstrap_confirmed',
            lastAttemptAt: Date.parse('2026-05-25T20:13:46.326Z'),
            lastObservedAt: Date.parse('2026-05-25T20:13:56.110Z'),
          },
        ],
        '2026-05-25T20:14:03.317Z',
        { runId: bootstrapRunId }
      );

      const svc = new TeamProvisioningService();
      const snapshot = await svc.getTeamAgentRuntimeSnapshot(teamName);
      const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath(teamName), 'utf8'));

      expect(snapshot.members.tom).toMatchObject({
        alive: true,
        providerId: 'anthropic',
        runtimeModel: 'sonnet',
        livenessKind: 'confirmed_bootstrap',
        historicalBootstrapConfirmed: true,
        runtimeDiagnostic: 'bootstrap confirmed',
        runtimeDiagnosticSeverity: 'info',
      });
      expect(persisted.members.tom).toMatchObject({
        launchState: 'confirmed_alive',
        bootstrapConfirmed: true,
        hardFailure: false,
      });
      expect(persisted.members.tom?.hardFailureReason).toBeUndefined();
    });

    it('does not treat a reused OpenCode runtime pid as live', async () => {
      const teamName = 'pure-opencode-reused-pid-team';
      const projectPath = '/Users/test/project';
      writeLaunchConfig(teamName, projectPath, 'lead-session', ['alice']);
      writeLaunchState(teamName, 'lead-session', {
        alice: {
          providerId: 'opencode',
          model: 'opencode/big-pickle',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          runtimePid: 333,
          runtimeSessionId: 'session-alice',
        },
      });
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 333, ppid: 1, command: 'node unrelated-worker.js' },
      ]);
      vi.mocked(pidusage).mockResolvedValueOnce({
        '333': createPidusageStat(333, 456_000_000),
      } as any);

      const svc = new TeamProvisioningService();
      (svc as any).runtimeAdapterRunByTeam.set(teamName, {
        runId: 'opencode-runtime-run',
        providerId: 'opencode',
        cwd: projectPath,
      });
      (svc as any).aliveRunByTeam.set(teamName, 'opencode-runtime-run');

      const snapshot = await svc.getTeamAgentRuntimeSnapshot(teamName);

      expect(snapshot.members.alice).toMatchObject({
        alive: false,
        livenessKind: 'runtime_process_candidate',
        pidSource: 'opencode_bridge',
        runtimeDiagnostic: 'OpenCode runtime pid is alive, but process identity is unverified',
        pid: 333,
        providerId: 'opencode',
      });
    });

    it('does not carry stale persisted runtimeAlive through launch-state reconcile', async () => {
      const teamName = 'persisted-stale-runtime-status-team';
      const projectPath = '/Users/test/project';
      const acceptedAt = new Date(Date.now() - 220_000).toISOString();
      writeLaunchConfig(teamName, projectPath, 'lead-session', ['alice']);
      writeLaunchState(teamName, 'lead-session', {
        alice: {
          providerId: 'codex',
          model: 'gpt-5.4',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          firstSpawnAcceptedAt: acceptedAt,
          runtimePid: 333,
          livenessKind: 'runtime_process',
          pidSource: 'agent_process_table',
        },
      });

      const svc = new TeamProvisioningService();

      const result = await svc.getMemberSpawnStatuses(teamName);
      const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath(teamName), 'utf8'));

      expect(result.statuses.alice).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: false,
        livenessSource: undefined,
        livenessKind: 'stale_metadata',
        hardFailure: true,
        hardFailureReason: 'Teammate did not join within the launch grace window.',
      });
      expect(result.summary).toMatchObject({
        failedCount: 1,
        runtimeAlivePendingCount: 0,
      });
      expect(persisted.members.alice.runtimeAlive).toBe(false);
      expect(persisted.members.alice.sources?.processAlive).toBeUndefined();
    });

    it('excludes removed meta members from live runtime metadata resolution', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            agentId: 'alice@runtime-team',
            removedAt: Date.now(),
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'alice',
          agentId: 'alice@runtime-team',
          backendType: 'tmux',
          tmuxPaneId: '%1',
        },
      ]);

      const metadata = await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');

      expect(metadata.has('alice')).toBe(false);
    });

    it('uses config runtime identity to detect live codex teammates when no persisted launch snapshot exists', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            {
              name: 'alice',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
              agentId: 'alice@signal-ops-6',
              backendType: 'tmux',
              tmuxPaneId: '%0',
            },
            {
              name: 'atlas',
              providerId: 'codex',
              model: 'gpt-5.3-codex',
              agentId: 'atlas@signal-ops-6',
              backendType: 'tmux',
              tmuxPaneId: '%1',
            },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
          },
          {
            name: 'atlas',
            providerId: 'codex',
            model: 'gpt-5.3-codex',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: 17527,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id alice@signal-ops-6 --agent-name alice --team-name signal-ops-6 --model gpt-5.4-mini',
        },
        {
          pid: 17528,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --agent-id atlas@signal-ops-6 --agent-name atlas --team-name signal-ops-6 --model gpt-5.3-codex',
        },
      ]);

      const metadata = await (svc as any).getLiveTeamAgentRuntimeMetadata('signal-ops-6');

      expect(metadata.get('alice')).toMatchObject({
        alive: true,
        agentId: 'alice@signal-ops-6',
        backendType: 'tmux',
        tmuxPaneId: '%0',
        pid: 17527,
        model: 'gpt-5.4-mini',
      });
      expect(metadata.get('atlas')).toMatchObject({
        alive: true,
        agentId: 'atlas@signal-ops-6',
        backendType: 'tmux',
        tmuxPaneId: '%1',
        pid: 17528,
        model: 'gpt-5.3-codex',
      });
    });

    it('does not let removed base member metadata hide an active suffixed member', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice-2', providerId: 'codex', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            removedAt: Date.now(),
          },
        ]),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => null),
      };
      vi.mocked(pidusage).mockResolvedValueOnce({} as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members['alice-2']).toMatchObject({
        memberName: 'alice-2',
        runtimeModel: 'gpt-5.4-mini',
      });
      expect(snapshot.members.alice).toBeUndefined();
    });

    it('includes persisted launch members that only exist in launchSnapshot.members when expectedMembers is stale', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => []),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => null),
      };
      (svc as any).launchStateStore = {
        read: vi.fn(async () =>
          createPersistedLaunchSnapshot({
            teamName: 'runtime-team',
            leadSessionId: 'lead-session',
            launchPhase: 'active',
            expectedMembers: ['alice'],
            members: {
              bob: {
                name: 'bob',
                providerId: 'codex',
                providerBackendId: 'codex-native',
                model: 'gpt-5.4-mini',
                effort: 'high',
                launchState: 'runtime_pending_bootstrap',
                agentToolAccepted: true,
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: false,
                lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
              },
            },
            updatedAt: '2026-04-23T10:00:00.000Z',
          })
        ),
      };
      vi.mocked(pidusage).mockResolvedValueOnce({} as any);

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(snapshot.members.bob).toMatchObject({
        memberName: 'bob',
        runtimeModel: 'gpt-5.4-mini',
        providerBackendId: 'codex-native',
      });
    });

    it('shows RSS for OpenCode secondary lane host pids without treating pre-bootstrap runtime as alive', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'alice', providerId: 'codex', model: 'gpt-5.4-mini' },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      const run = createMemberSpawnRun({
        runId: 'run-1',
        teamName: 'runtime-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.request = { providerId: 'codex', model: 'gpt-5.4', members: [] };
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
          runId: 'secondary-run-1',
          state: 'finished',
          result: {
            runId: 'secondary-run-1',
            teamName: 'runtime-team',
            launchPhase: 'active',
            teamLaunchState: 'partial_pending',
            members: {
              bob: {
                memberName: 'bob',
                providerId: 'opencode',
                launchState: 'runtime_pending_bootstrap',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: false,
                hardFailure: false,
                runtimePid: 333,
                diagnostics: [],
              },
            },
            warnings: [],
            diagnostics: [],
          },
          warnings: [],
          diagnostics: [],
        },
      ];
      (svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
      (svc as any).runs.set('run-1', run);
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 333, ppid: 1, command: 'opencode runtime host' },
      ]);
      vi.mocked(pidusage).mockReset();
      vi.mocked(pidusage).mockImplementation(
        async (target: number | string | Array<number | string>) => {
          if (Array.isArray(target)) {
            if (target.length === 1 && Number(target[0]) === 333) {
              return {
                '333': createPidusageStat(333, 456_000_000),
              } as any;
            }
            return {
              '111': createPidusageStat(111, 123_000_000),
            } as any;
          }
          if (target === 333) {
            return createPidusageStat(333, 456_000_000) as any;
          }
          if (target === 111) {
            return createPidusageStat(111, 123_000_000) as any;
          }
          throw new Error(`Unexpected pidusage target: ${String(target)}`);
        }
      );

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([111, 333], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(pidusage).toHaveBeenCalledWith([333], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members.bob).toMatchObject({
        memberName: 'bob',
        alive: false,
        restartable: false,
        pid: 333,
        runtimeModel: 'opencode/minimax-m2.5-free',
        rssBytes: 456_000_000,
        livenessKind: 'runtime_process_candidate',
      });
    });

    it('shows RSS for persisted OpenCode secondary lane host pids without treating historical bootstrap as live', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      (svc as any).launchStateStore = {
        read: vi.fn(async () =>
          createPersistedLaunchSnapshot({
            teamName: 'runtime-team',
            expectedMembers: ['bob'],
            launchPhase: 'finished',
            members: {
              bob: {
                name: 'bob',
                providerId: 'opencode',
                model: 'opencode/minimax-m2.5-free',
                laneId: 'secondary:opencode:bob',
                laneKind: 'secondary',
                laneOwnerProviderId: 'opencode',
                launchState: 'confirmed_alive',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: true,
                hardFailure: false,
                runtimePid: 333,
                lastEvaluatedAt: '2026-04-23T12:26:31.563Z',
              },
            },
            updatedAt: '2026-04-23T12:26:31.563Z',
          })
        ),
      };
      vi.mocked(pidusage).mockReset();
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        { pid: 333, ppid: 1, command: 'opencode runtime host' },
      ]);
      vi.mocked(pidusage).mockImplementation(
        async (target: number | string | Array<number | string>) => {
          if (Array.isArray(target)) {
            return {
              '333': createPidusageStat(333, 456_000_000),
            } as any;
          }
          if (target === 333) {
            return createPidusageStat(333, 456_000_000) as any;
          }
          throw new Error(`Unexpected pidusage target: ${String(target)}`);
        }
      );

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');

      expect(pidusage).toHaveBeenCalledWith([333], EXPECTED_RUNTIME_PIDUSAGE_OPTIONS);
      expect(snapshot.members.bob).toMatchObject({
        memberName: 'bob',
        alive: false,
        restartable: false,
        pid: 333,
        providerId: 'opencode',
        runtimeModel: 'opencode/minimax-m2.5-free',
        rssBytes: 456_000_000,
        historicalBootstrapConfirmed: true,
        livenessKind: 'runtime_process_candidate',
      });
    });
  });

  describe('restartMember', () => {
    it('uses members meta runtime settings when config members are stale or absent', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'edited-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              livenessSource: 'heartbeat',
              firstSpawnAcceptedAt: new Date().toISOString(),
              lastHeartbeatAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Edited Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            role: 'Reviewer',
            workflow: 'Use checklist',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
            effort: 'high',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('edited-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('edited-team', 'alice');

      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
      const restartCall = sendMessageToRun.mock.calls[0] as unknown as
        | [unknown, string]
        | undefined;
      const restartMessage = restartCall?.[1] ?? '';
      expect(restartMessage).toContain('provider="codex"');
      expect(restartMessage).toContain('model="gpt-5.4-mini"');
      expect(restartMessage).toContain('effort="high"');
      expect(restartMessage).toContain('with role "Reviewer"');
      expect(restartMessage).toContain('Their workflow: Use checklist');
    });

    it('re-reads teammate runtime settings immediately before respawn so stale edit snapshots are not reused', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'edited-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              livenessSource: 'heartbeat',
              firstSpawnAcceptedAt: new Date().toISOString(),
              lastHeartbeatAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      const getConfig = vi.fn().mockResolvedValue({
        name: 'Edited Team',
        members: [{ name: 'team-lead', agentType: 'team-lead' }],
      });
      const getMembers = vi
        .fn()
        .mockResolvedValueOnce([
          {
            name: 'alice',
            role: 'Reviewer',
            workflow: 'Use checklist',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
            effort: 'high',
            agentType: 'general-purpose',
          },
        ])
        .mockResolvedValueOnce([
          {
            name: 'alice',
            role: 'Approver',
            workflow: 'Use the updated checklist',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]);

      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = { getConfig };
      (svc as any).membersMetaStore = { getMembers };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('edited-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('edited-team', 'alice');

      expect(getMembers).toHaveBeenCalledTimes(2);
      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
      const restartCall = sendMessageToRun.mock.calls[0] as unknown as
        | [unknown, string]
        | undefined;
      const restartMessage = restartCall?.[1] ?? '';
      expect(restartMessage).toContain('provider="codex"');
      expect(restartMessage).toContain('model="gpt-5.4"');
      expect(restartMessage).toContain('effort="medium"');
      expect(restartMessage).toContain('with role "Approver"');
      expect(restartMessage).toContain('Their workflow: Use the updated checklist');
    });

    it('retries a failed teammate without live runtime by resetting spawn status to spawning', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'error',
              launchState: 'failed_to_start',
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate "bob" failed to start: spawn failed',
              error: 'Teammate "bob" failed to start: spawn failed',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: undefined,
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('codex-team', 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        error: undefined,
        agentToolAccepted: false,
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(true);
      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
      expect(sendMessageToRun).toHaveBeenCalledWith(
        run,
        expect.stringContaining('Teammate "bob" with role "Developer" was restarted from the UI.')
      );
    });

    it('projects a pending restart as bootstrap-pending in finished launch snapshots without mutating live state', () => {
      const requestedAt = new Date().toISOString();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'spawning',
              launchState: 'starting',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: requestedAt,
              runtimeDiagnostic: undefined,
              runtimeDiagnosticSeverity: undefined,
            }),
          ],
        ]),
      });
      run.isLaunch = true;
      run.provisioningComplete = true;
      run.pendingMemberRestarts.set('bob', {
        requestedAt,
        desired: {
          name: 'bob',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'medium',
        },
      });
      const svc = new TeamProvisioningService();

      const projected = (svc as any).buildRuntimeSpawnStatusRecord(run);

      expect(projected.bob).toMatchObject({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        error: undefined,
        runtimeDiagnostic: 'Manual restart is already in progress; waiting for teammate bootstrap.',
        runtimeDiagnosticSeverity: 'info',
      });
      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
        agentToolAccepted: false,
        hardFailure: false,
      });
    });

    it('does not sync a stale never-spawned launch snapshot over a pending restart', () => {
      const requestedAt = new Date().toISOString();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'spawning',
              launchState: 'starting',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: requestedAt,
              hardFailure: false,
              hardFailureReason: undefined,
              error: undefined,
            }),
          ],
        ]),
      });
      run.pendingMemberRestarts.set('bob', {
        requestedAt,
        desired: {
          name: 'bob',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'medium',
        },
      });
      const snapshot = createPersistedLaunchSnapshot({
        teamName: run.teamName,
        expectedMembers: ['bob'],
        launchPhase: 'finished',
        members: {
          bob: {
            name: 'bob',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'Teammate was never spawned during launch.',
            lastEvaluatedAt: new Date().toISOString(),
          },
        },
      });
      const svc = new TeamProvisioningService();

      (svc as any).syncRunMemberSpawnStatusesFromSnapshot(run, snapshot);

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
        agentToolAccepted: false,
        hardFailure: false,
        hardFailureReason: undefined,
        error: undefined,
      });
    });

    it('does not mark a pending restart as failed during bootstrap cleanup projection', () => {
      const requestedAt = new Date().toISOString();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['alice', 'bob'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
            }),
          ],
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'spawning',
              launchState: 'starting',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: requestedAt,
              hardFailure: false,
              hardFailureReason: undefined,
              error: undefined,
            }),
          ],
        ]),
      });
      run.pendingMemberRestarts.set('bob', {
        requestedAt,
        desired: {
          name: 'bob',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'medium',
        },
      });
      const svc = new TeamProvisioningService();

      (svc as any).markUnconfirmedBootstrapMembersFailed(run, 'launch cleanup requested', {
        cleanupRequested: true,
      });

      expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        hardFailure: true,
        hardFailureReason: 'launch cleanup requested',
      });
      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
        agentToolAccepted: false,
        hardFailure: false,
        hardFailureReason: undefined,
        error: undefined,
      });
    });

    it('restarts a tmux teammate directly in its shell-only pane after the runtime process disappeared', async () => {
      const teamName = 'forge-labs-10';
      const teamDir = path.join(tempTeamsBase, teamName);
      const projectPath = path.join(tempClaudeRoot, 'forge-project');
      fs.mkdirSync(teamDir, { recursive: true });
      fs.mkdirSync(projectPath, { recursive: true });
      fs.writeFileSync(
        path.join(teamDir, 'config.json'),
        JSON.stringify(
          {
            name: 'Forge Labs 10',
            projectPath,
            leadSessionId: 'lead-session-1',
            members: [
              { name: 'team-lead', agentType: 'team-lead' },
              {
                name: 'bob',
                role: 'Developer',
                providerId: 'codex',
                model: 'gpt-5.4',
                effort: 'high',
                agentType: 'general-purpose',
                tmuxPaneId: '%1',
                backendType: 'tmux',
              },
            ],
          },
          null,
          2
        ),
        'utf8'
      );

      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValue(
        new Map([
          [
            '%1',
            {
              paneId: '%1',
              panePid: 4242,
              currentCommand: 'zsh',
              currentPath: projectPath,
            },
          ],
        ])
      );

      const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
        writeConfigFile: vi.fn(async () => '/mock/mcp-config.json'),
      } as any);
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'error',
              launchState: 'failed_to_start',
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              error: 'Teammate was never spawned during launch.',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: undefined,
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.detectedSessionId = 'lead-session-1';
      run.request = { providerId: 'codex', skipPermissions: true };

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).buildProvisioningEnv = vi.fn(async () => ({
        env: { OPENAI_API_KEY: 'test-openai-key' },
        authSource: 'openai_api_key',
        providerArgs: [],
      }));
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Forge Labs 10',
          projectPath,
          leadSessionId: 'lead-session-1',
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'codex',
              model: 'gpt-5.4',
              effort: 'high',
            },
          ],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'high',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'bob',
          agentId: 'bob@forge-labs-10',
          backendType: 'tmux',
          tmuxPaneId: '%1',
          cwd: projectPath,
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember(teamName, 'bob');

      expect(killTmuxPaneForCurrentPlatformSync).not.toHaveBeenCalled();
      expect(sendMessageToRun).not.toHaveBeenCalled();
      expect(sendKeysToTmuxPaneForCurrentPlatform).toHaveBeenCalledTimes(1);
      const [paneId, command] = vi.mocked(sendKeysToTmuxPaneForCurrentPlatform).mock.calls[0] ?? [];
      expect(paneId).toBe('%1');
      expect(command).toContain("cd '");
      expect(command).toContain(projectPath);
      expect(command).toContain("'/mock/claude'");
      expect(command).toContain("'--agent-id' 'bob@forge-labs-10'");
      expect(command).toContain("'--team-name' 'forge-labs-10'");
      expect(command).toContain("'--parent-session-id' 'lead-session-1'");
      expect(command).toContain("'--setting-sources' 'user,project,local'");
      expect(command).toContain("'--mcp-config' '/mock/mcp-config.json'");
      expect(command).not.toContain('--strict-mcp-config');
      expect(command).toContain("'--model' 'gpt-5.4'");
      expect(command).toContain("'--effort' 'high'");
      expect(command).toContain('__CLAUDE_TEAMMATE_EXIT__');
      expect(run.pendingMemberRestarts.has('bob')).toBe(true);
      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        hardFailure: false,
      });

      const updatedConfig = JSON.parse(
        fs.readFileSync(path.join(teamDir, 'config.json'), 'utf8')
      ) as { members: Array<Record<string, unknown>> };
      expect(updatedConfig.members.find((member) => member.name === 'bob')).toMatchObject({
        agentId: 'bob@forge-labs-10',
        tmuxPaneId: '%1',
        backendType: 'tmux',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
      });
      const inbox = JSON.parse(
        fs.readFileSync(path.join(teamDir, 'inboxes', 'bob.json'), 'utf8')
      ) as Array<Record<string, unknown>>;
      expect(inbox.at(-1)).toMatchObject({
        from: 'team-lead',
        to: 'bob',
        source: 'system_notification',
        leadSessionId: 'lead-session-1',
      });
    });

    it('skips a failed teammate for the current launch without marking it alive', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'error',
              launchState: 'failed_to_start',
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate "bob" failed to start: spawn failed',
              error: 'Teammate "bob" failed to start: spawn failed',
              agentToolAccepted: false,
              firstSpawnAcceptedAt: undefined,
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.isLaunch = true;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.skipMemberForLaunch('codex-team', 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'skipped',
        launchState: 'skipped_for_launch',
        skippedForLaunch: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        error: undefined,
        agentToolAccepted: false,
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(false);
      expect(sendMessageToRun).toHaveBeenCalledWith(
        run,
        expect.stringContaining('Teammate "bob" was skipped for this launch')
      );
    });

    it('rejects skipping a failed teammate while a retry is already in progress', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'error',
              launchState: 'failed_to_start',
              hardFailure: true,
              hardFailureReason: 'spawn failed',
              error: 'spawn failed',
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.pendingMemberRestarts.set('bob', {
        requestedAt: new Date().toISOString(),
        desired: { name: 'bob', role: 'Developer' },
      });

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [
            { name: 'team-lead', agentType: 'team-lead' },
            { name: 'bob', role: 'Developer' },
          ],
        })),
      };
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await expect(svc.skipMemberForLaunch('codex-team', 'bob')).rejects.toThrow(
        'already in progress'
      );
    });

    it('does not let removed base-member metadata override a suffixed teammate during restart', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'edited-team',
        expectedMembers: ['alice-2'],
        memberSpawnStatuses: new Map([
          [
            'alice-2',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              livenessSource: 'heartbeat',
              firstSpawnAcceptedAt: new Date().toISOString(),
              lastHeartbeatAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Edited Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            providerId: 'opencode',
            model: 'nemotron-3-super-free',
            removedAt: Date.now(),
          },
          {
            name: 'alice-2',
            role: 'Reviewer',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
            effort: 'high',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('edited-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('edited-team', 'alice-2');

      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
      const restartCall = sendMessageToRun.mock.calls[0] as unknown as
        | [unknown, string]
        | undefined;
      const restartMessage = restartCall?.[1] ?? '';
      expect(restartMessage).toContain('provider="codex"');
      expect(restartMessage).toContain('model="gpt-5.4-mini"');
      expect(restartMessage).toContain('effort="high"');
      expect(restartMessage).not.toContain('nemotron-3-super-free');
    });

    it('requires the OpenCode runtime adapter before restarting a secondary-lane teammate', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:alice',
          providerId: 'opencode',
          member: {
            name: 'alice',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: 'opencode-run-1',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Mixed Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({ providerId: 'codex' })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('mixed-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await expect(svc.restartMember('mixed-team', 'alice')).rejects.toThrow(
        'OpenCode runtime adapter is not available for controlled lane reattach.'
      );
    });

    it('restarts a pure OpenCode member through the app-owned runtime adapter without a tracked lead run', async () => {
      const svc = new TeamProvisioningService();
      const adapterLaunch = vi.fn();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      (svc as any).runtimeAdapterRunByTeam.set('pure-opencode-team', {
        runId: 'opencode-run-1',
        providerId: 'opencode',
        cwd: '/repo',
        members: {},
      });
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Pure OpenCode Team',
          projectPath: '/repo',
          members: [
            { name: 'team-lead', agentType: 'team-lead', providerId: 'opencode' },
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'opencode',
              model: 'openai/gpt-5.4-mini',
              agentType: 'general-purpose',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'openai/gpt-5.4-mini',
              agentType: 'general-purpose',
            },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          version: 1,
          cwd: '/repo',
          providerId: 'opencode',
          model: 'openai/gpt-5.4-mini',
          createdAt: Date.now(),
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            role: 'Reviewer',
            providerId: 'opencode',
            model: 'openai/gpt-5.4-mini',
            agentType: 'general-purpose',
          },
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'openai/gpt-5.4-mini',
            agentType: 'general-purpose',
          },
        ]),
      };
      vi.spyOn(svc as any, 'resolveOpenCodeMemberWorkspacesForRuntime').mockImplementation(
        async (input: any) =>
          (input.members as Array<Record<string, unknown>>).map((member) => ({
            ...member,
            cwd: '/repo',
          }))
      );
      const persistRestartMessage = vi
        .spyOn(svc as any, 'persistOpenCodeMemberRestartSystemMessage')
        .mockImplementation(() => undefined);
      const runtimeRelaunch = vi
        .spyOn(svc as any, 'runOpenCodeTeamRuntimeAdapterLaunch')
        .mockResolvedValue({ runId: 'opencode-run-2' });

      await svc.restartMember('pure-opencode-team', 'alice');

      expect(runtimeRelaunch).toHaveBeenCalledTimes(1);
      const relaunchInput = runtimeRelaunch.mock.calls[0]?.[0] as any;
      expect(relaunchInput.request).toMatchObject({
        teamName: 'pure-opencode-team',
        cwd: '/repo',
        providerId: 'opencode',
        model: 'openai/gpt-5.4-mini',
      });
      expect(relaunchInput.members.map((member: { name: string }) => member.name).sort()).toEqual([
        'alice',
        'bob',
      ]);
      expect(relaunchInput.sourceWarning).toContain('OpenCode-only member restart refreshes');
      expect(persistRestartMessage).toHaveBeenCalledWith(
        expect.objectContaining({
          teamName: 'pure-opencode-team',
          leadName: 'team-lead',
          member: expect.objectContaining({ name: 'alice' }),
          reason: 'manual_restart',
        })
      );
    });

    it('still allows restarting a primary-lane teammate when another mixed secondary lane exists', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: 'opencode-run-1',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Mixed Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'alice',
            role: 'Reviewer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'high',
            agentType: 'general-purpose',
          },
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({ providerId: 'codex' })),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('mixed-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('mixed-team', 'alice');

      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
      expect(run.pendingMemberRestarts.has('alice')).toBe(true);
    });

    it('aborts restart if the teammate is removed before respawn is requested', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'edited-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              livenessSource: 'heartbeat',
              firstSpawnAcceptedAt: new Date().toISOString(),
              lastHeartbeatAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      const getConfig = vi.fn().mockResolvedValue({
        name: 'Edited Team',
        members: [{ name: 'team-lead', agentType: 'team-lead' }],
      });
      const getMembers = vi
        .fn()
        .mockResolvedValueOnce([
          {
            name: 'alice',
            role: 'Reviewer',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
            effort: 'high',
            agentType: 'general-purpose',
          },
        ])
        .mockResolvedValueOnce([
          {
            name: 'alice',
            role: 'Reviewer',
            providerId: 'codex',
            model: 'gpt-5.4-mini',
            effort: 'high',
            agentType: 'general-purpose',
            removedAt: new Date().toISOString(),
          },
        ]);

      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = { getConfig };
      (svc as any).membersMetaStore = { getMembers };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('edited-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await expect(svc.restartMember('edited-team', 'alice')).rejects.toThrow(
        'Member "alice" was removed while restart was in progress'
      );

      expect(sendMessageToRun).not.toHaveBeenCalled();
      expect(run.pendingMemberRestarts.has('alice')).toBe(false);
      expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
        status: 'offline',
        launchState: 'starting',
        runtimeAlive: false,
      });
    });

    it('aborts restart if team config disappears before respawn is requested', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'edited-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              livenessSource: 'heartbeat',
              firstSpawnAcceptedAt: new Date().toISOString(),
              lastHeartbeatAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      const getConfig = vi
        .fn()
        .mockResolvedValueOnce({
          name: 'Edited Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })
        .mockResolvedValueOnce(null);
      const getMembers = vi.fn(async () => [
        {
          name: 'alice',
          role: 'Reviewer',
          providerId: 'codex',
          model: 'gpt-5.4-mini',
          effort: 'high',
          agentType: 'general-purpose',
        },
      ]);

      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = { getConfig };
      (svc as any).membersMetaStore = { getMembers };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('edited-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await expect(svc.restartMember('edited-team', 'alice')).rejects.toThrow(
        'Team "edited-team" configuration disappeared while restart was in progress'
      );

      expect(sendMessageToRun).not.toHaveBeenCalled();
      expect(run.pendingMemberRestarts.has('alice')).toBe(false);
      expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
        status: 'offline',
        launchState: 'starting',
        runtimeAlive: false,
      });
    });

    it('treats duplicate_skipped already_running as a failed codex restart because the old runtime is still active', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('codex-team', 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
      });
      expect(sendMessageToRun).toHaveBeenCalledWith(
        run,
        expect.stringContaining('provider="codex", model="gpt-5.2", effort="medium"')
      );

      run.activeToolCalls.set('tool-agent-1', {
        memberName: 'bob',
        toolUseId: 'tool-agent-1',
        toolName: 'Agent',
        preview: 'Spawn teammate bob',
        startedAt: new Date().toISOString(),
        state: 'running',
        source: 'runtime',
      });
      run.memberSpawnToolUseIds.set('tool-agent-1', 'bob');

      (svc as any).finishRuntimeToolActivity(
        run,
        'tool-agent-1',
        [
          {
            type: 'text',
            text: 'status: duplicate_skipped\nreason: already_running\nname: bob\nteam_name: codex-team',
          },
        ],
        false
      );

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: false,
        hardFailure: true,
        hardFailureReason:
          'Restart for teammate "bob" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(false);
    });

    it('keeps a codex teammate restart pending instead of failed when lead reports duplicate_skipped bootstrap_pending', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      (svc as any).sendMessageToRun = vi.fn(async () => {});
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('codex-team', 'bob');

      run.activeToolCalls.set('tool-agent-1', {
        memberName: 'bob',
        toolUseId: 'tool-agent-1',
        toolName: 'Agent',
        preview: 'Spawn teammate bob',
        startedAt: new Date().toISOString(),
        state: 'running',
        source: 'runtime',
      });
      run.memberSpawnToolUseIds.set('tool-agent-1', 'bob');

      (svc as any).finishRuntimeToolActivity(
        run,
        'tool-agent-1',
        [
          {
            type: 'text',
            text: 'status: duplicate_skipped\nreason: bootstrap_pending\nname: bob\nteam_name: codex-team',
          },
        ],
        false
      );

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        runtimeAlive: false,
        agentToolAccepted: true,
        hardFailure: false,
        hardFailureReason: undefined,
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(true);
    });

    it('fails a codex teammate restart immediately when Agent returns duplicate_skipped without a reason', async () => {
      allowConsoleLogs();
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['jack'],
        memberSpawnStatuses: new Map([
          [
            'jack',
            createMemberSpawnStatusEntry({
              launchState: 'failed_to_start',
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              error: 'Teammate was never spawned during launch.',
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      (svc as any).sendMessageToRun = vi.fn(async () => {});
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'jack',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('codex-team', 'jack');

      run.activeToolCalls.set('tool-agent-1', {
        memberName: 'jack',
        toolUseId: 'tool-agent-1',
        toolName: 'Agent',
        preview: 'Spawn teammate jack',
        startedAt: new Date().toISOString(),
        state: 'running',
        source: 'runtime',
      });
      run.memberSpawnToolUseIds.set('tool-agent-1', 'jack');

      (svc as any).finishRuntimeToolActivity(
        run,
        'tool-agent-1',
        [
          {
            type: 'text',
            text: 'status: duplicate_skipped\nname: jack\nteam_name: codex-team',
          },
        ],
        false
      );

      expect(run.pendingMemberRestarts.has('jack')).toBe(false);
      expect(run.memberSpawnStatuses.get('jack')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: false,
        hardFailure: true,
        hardFailureReason:
          'Restart for teammate "jack" could not be confirmed and may not have applied. Agent returned duplicate_skipped without a reason.',
      });
    });

    it('waits for a killed tmux pane to disappear before sending a restart request', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'tmux-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Tmux Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@tmux-team',
          backendType: 'tmux',
          tmuxPaneId: '%2',
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('tmux-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      vi.mocked(listTmuxPanePidsForCurrentPlatform)
        .mockResolvedValueOnce(new Map([['%2', 999]]))
        .mockResolvedValueOnce(new Map());

      const restartPromise = svc.restartMember('tmux-team', 'forge');
      await Promise.resolve();

      expect(sendMessageToRun).not.toHaveBeenCalled();

      await vi.advanceTimersByTimeAsync(100);
      await restartPromise;

      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
    });

    it('uses secondary-lane pending copy instead of bootstrap-only pending copy for mixed teams', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
        ]),
      });
      run.isLaunch = true;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: 'opencode-run-1',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 1,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        {
          version: 2,
          teamName: 'mixed-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'starting',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 1,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch - waiting for secondary runtime lane: bob');
    });

    it('treats missing secondary-lane snapshot members as still pending', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
        ]),
      });
      run.isLaunch = true;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: 'opencode-run-1',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 1,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        {
          version: 2,
          teamName: 'mixed-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 1,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch - waiting for secondary runtime lane: bob');
    });

    it('uses permission-pending copy when the remaining mixed-team member is awaiting approval', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
        ]),
      });
      run.isLaunch = true;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: 'opencode-run-1',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 1,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
          runtimeProcessPendingCount: 1,
        },
        {
          version: 2,
          teamName: 'mixed-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'runtime_pending_permission',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              pendingPermissionRequestIds: ['perm-1'],
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 1,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 1,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch — 1 teammate awaiting permission approval');
    });

    it('keeps launch pending when the only remaining teammate is permission-blocked but already online', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'runtime_pending_permission',
              runtimeAlive: true,
              agentToolAccepted: true,
              bootstrapConfirmed: false,
              pendingPermissionRequestIds: ['perm-1'],
            }),
          ],
        ]),
      });
      const launchSummary = (svc as any).getMemberLaunchSummary(run);

      expect((svc as any).hasPendingLaunchMembers(run, launchSummary, null)).toBe(true);
      expect(
        (svc as any).buildPendingBootstrapStatusMessage('Finishing launch', run, launchSummary)
      ).toBe('Finishing launch — 1 teammate awaiting permission approval');
    });

    it('counts registered-only liveness as no-runtime pending in launch summaries', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              livenessKind: 'registered_only',
              runtimeDiagnostic: 'registered runtime metadata without live process',
            }),
          ],
        ]),
      });

      const launchSummary = (svc as any).getMemberLaunchSummary(run);

      expect(launchSummary).toMatchObject({
        pendingCount: 1,
        noRuntimePendingCount: 1,
      });
      expect(
        (svc as any).buildPendingBootstrapStatusMessage('Finishing launch', run, launchSummary)
      ).toContain('1 waiting for runtime');
    });

    it('trusts persisted snapshot permission state for pure teams when live run statuses are absent', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'pure-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map(),
      });

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 0,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
          runtimeProcessPendingCount: 1,
        },
        {
          version: 2,
          teamName: 'pure-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'opencode',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              pendingPermissionRequestIds: ['perm-1'],
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 0,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 1,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch — 1 teammate awaiting permission approval');
    });

    it('uses persisted expected member count instead of stale run expected members for pure launch copy', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'pure-team',
        expectedMembers: [],
        memberSpawnStatuses: new Map(),
      });

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 0,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
          runtimeProcessPendingCount: 1,
        },
        {
          version: 2,
          teamName: 'pure-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'opencode',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              livenessKind: 'runtime_process',
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 0,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 1,
            runtimeProcessPendingCount: 1,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch — teammates online');
      expect(message).not.toContain('/0');
    });

    it('does not use legacy runtimeAlivePendingCount as online launch copy evidence', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'pure-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map(),
      });

      const message = (svc as any).buildAggregatePendingLaunchMessage('Finishing launch', run, {
        confirmedCount: 0,
        pendingCount: 1,
        failedCount: 0,
        runtimeAlivePendingCount: 1,
      });

      expect(message).toBe('Finishing launch — teammates are still starting');
    });

    it('uses the union of persisted expected members and persisted member entries for pending launch copy', () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'pure-team',
        expectedMembers: [],
        memberSpawnStatuses: new Map(),
      });

      const message = (svc as any).buildAggregatePendingLaunchMessage(
        'Finishing launch',
        run,
        {
          confirmedCount: 0,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
        },
        {
          version: 2,
          teamName: 'pure-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: [],
          bootstrapExpectedMembers: [],
          members: {
            alice: {
              name: 'alice',
              providerId: 'opencode',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'opencode',
              launchState: 'runtime_pending_permission',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              pendingPermissionRequestIds: ['perm-1'],
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 0,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 1,
          },
          teamLaunchState: 'partial_pending',
        }
      );

      expect(message).toBe('Finishing launch — 1 teammate awaiting permission approval');
    });

    it('launches the OpenCode secondary lane with side-lane provider and member runtime identity', async () => {
      const svc = new TeamProvisioningService();
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const teamName = String(input.teamName);
        const laneId = String(input.laneId);
        const runId = String(input.runId);
        await writeCommittedOpenCodeSessionStore({
          teamName,
          laneId,
          runId,
          sessions: [
            {
              id: 'oc-session-bob',
              teamName,
              memberName: 'bob',
              laneId,
              runId,
              source: 'runtime_bootstrap_checkin',
            },
          ],
        });
        return {
          runId,
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            bob: {
              memberName: 'bob',
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              diagnostics: [],
            },
          },
          warnings: [],
          diagnostics: [],
        };
      });

      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: adapterLaunch,
          reconcile: vi.fn(),
          stop: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);
      stubOpenCodeAppManagedLaunchPrompt(svc);

      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };

      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
        memberSpawnStatuses: new Map([
          [
            'alice',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
            }),
          ],
        ]),
      });
      run.isLaunch = true;
      run.request = {
        teamName: 'mixed-team',
        cwd: '/tmp/mixed-team',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
        skipPermissions: true,
      };
      run.effectiveMembers = [
        {
          name: 'alice',
          role: 'Reviewer',
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'high',
        },
      ];
      run.detectedSessionId = 'lead-session-1';
      run.launchIdentity = null;
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await run.mixedSecondaryLaneLaunchQueue;

      expect(adapterLaunch).toHaveBeenCalledTimes(1);
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          model: 'minimax-m2.5-free',
          effort: 'medium',
          runtimeOnly: true,
          prompt: expect.stringContaining('AGENT_TEAMS_APP_MANAGED_BOOTSTRAP_V1'),
          cwd: '/tmp/mixed-team',
          expectedMembers: [
            expect.objectContaining({
              name: 'bob',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
              effort: 'medium',
              cwd: '/tmp/mixed-team',
            }),
          ],
        })
      );
      expect(adapterLaunch.mock.calls[0]?.[0]).not.toHaveProperty('skipReadinessPreflight');
    });

    it('does not trust OpenCode secondary bootstrap success without committed lane evidence', async () => {
      const svc = new TeamProvisioningService();
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName: 'mixed-team-no-committed-evidence',
        laneId: 'secondary:opencode:bob',
        state: 'active',
      });
      await setOpenCodeRuntimeActiveRunManifest({
        teamsBasePath: tempTeamsBase,
        teamName: 'mixed-team-no-committed-evidence',
        laneId: 'secondary:opencode:bob',
        runId: 'lane-run-bob',
      });

      const result = await (svc as any).guardCommittedOpenCodeSecondaryLaneEvidence({
        teamName: 'mixed-team-no-committed-evidence',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        result: {
          runId: 'lane-run-bob',
          teamName: 'mixed-team-no-committed-evidence',
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            bob: {
              memberName: 'bob',
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              diagnostics: [],
            },
          },
          warnings: [],
          diagnostics: [],
        },
      });

      expect(result.teamLaunchState).toBe('partial_pending');
      expect(result.launchPhase).toBe('active');
      expect(result.members.bob).toMatchObject({
        launchState: 'starting',
        agentToolAccepted: false,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        livenessKind: 'registered_only',
        runtimeDiagnostic:
          'OpenCode bootstrap confirmation was not committed to lane runtime evidence.',
      });
      expect(result.members.bob.diagnostics).toContain(
        'OpenCode bridge reported bootstrap confirmation, but no lane runtime evidence was committed.'
      );
    });

    it('delivers direct messages to OpenCode secondary lanes with the lane run id', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_direct_lane',
        runtimePid: 456,
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toEqual({
        delivered: true,
        diagnostics: [],
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-bob',
          teamName: 'team-a',
          laneId: 'secondary:opencode:bob',
          memberName: 'bob',
          cwd: '/repo',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      );
    });

    it('does not deliver direct OpenCode messages when recovery leaves the lane inactive', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_direct_lane',
        runtimePid: 456,
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      (svc as any).deleteSecondaryRuntimeRun('team-a', 'secondary:opencode:bob');
      vi.spyOn(svc as any, 'tryRecoverOpenCodeRuntimeLaneBeforeDelivery').mockResolvedValue(false);
      const committedRecoverySpy = vi
        .spyOn(svc as any, 'tryRecoverOpenCodeRuntimeLaneFromCommittedSessionBeforeDelivery')
        .mockResolvedValue(true);

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toMatchObject({
        delivered: false,
        reason: 'opencode_runtime_not_active',
      });

      expect(committedRecoverySpy).toHaveBeenCalled();
      expect(sendMessageToMember).not.toHaveBeenCalled();
    });

    it('persists verified OpenCode bridge runtime pids so member cards can show memory', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_verified_pid',
        runtimePid: 456,
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);
      vi.spyOn(svc as any, 'readProcessCommandByPid').mockReturnValue(
        '/opt/homebrew/bin/opencode serve --hostname 127.0.0.1 --port 45678'
      );

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      writeLaunchState('team-a', 'lead-session', {
        bob: {
          providerId: 'opencode',
          model: 'opencode/minimax-m2.5-free',
          laneId: 'secondary:opencode:bob',
          laneKind: 'secondary',
          laneOwnerProviderId: 'opencode',
          launchState: 'confirmed_alive',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: true,
          hardFailure: false,
          runtimeRunId: 'opencode-run-bob',
          runtimeSessionId: 'oc-session-bob',
          livenessKind: 'confirmed_bootstrap',
        },
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
        {
          pid: 456,
          ppid: 1,
          command: '/opt/homebrew/bin/opencode serve --hostname 127.0.0.1 --port 45678',
        },
      ]);
      vi.mocked(pidusage).mockReset();
      vi.mocked(pidusage).mockImplementation(
        async (target: number | string | Array<number | string>) => {
          if (Array.isArray(target)) {
            return {
              '456': createPidusageStat(456, 456_000_000),
            } as any;
          }
          if (target === 456) {
            return createPidusageStat(456, 456_000_000) as any;
          }
          throw new Error(`Unexpected pidusage target: ${String(target)}`);
        }
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toMatchObject({ delivered: true });

      const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath('team-a'), 'utf8'));
      expect(persisted.members.bob).toMatchObject({
        runtimePid: 456,
        pidSource: 'opencode_bridge',
        runtimeSessionId: 'oc-session-bob',
      });

      const snapshot = await svc.getTeamAgentRuntimeSnapshot('team-a');
      expect(snapshot.members.bob).toMatchObject({
        memberName: 'bob',
        providerId: 'opencode',
        pid: 456,
        runtimePid: 456,
        rssBytes: 456_000_000,
        livenessKind: 'runtime_process_candidate',
      });
    });

    it('does not persist OpenCode bridge runtime pids when process identity is not verified', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_unverified_pid',
        runtimePid: 456,
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);
      vi.spyOn(svc as any, 'readProcessCommandByPid').mockReturnValue('/usr/bin/yes');

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      writeLaunchState('team-a', 'lead-session', {
        bob: {
          providerId: 'opencode',
          model: 'opencode/minimax-m2.5-free',
          laneId: 'secondary:opencode:bob',
          laneKind: 'secondary',
          laneOwnerProviderId: 'opencode',
          launchState: 'confirmed_alive',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: true,
          hardFailure: false,
          runtimeRunId: 'opencode-run-bob',
          runtimeSessionId: 'oc-session-bob',
          livenessKind: 'confirmed_bootstrap',
        },
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toMatchObject({ delivered: true });

      const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath('team-a'), 'utf8'));
      expect(persisted.members.bob.runtimePid).toBeUndefined();
      expect(persisted.members.bob.pidSource).toBeUndefined();
    });

    it('uses snapshot config reads for OpenCode member delivery routing', async () => {
      const getConfig = vi.fn(async () => {
        throw new Error('verified config read should not be used for delivery routing');
      });
      const getConfigSnapshot = vi.fn(async () => ({
        projectPath: '/repo',
        members: [
          { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
          { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
        ],
      }));
      const svc = new TeamProvisioningService({
        getConfig,
        getConfigSnapshot,
      } as any);
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_snapshot_config',
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ])
      );
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          { name: 'bob', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
        ]),
      };
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toMatchObject({ delivered: true });

      expect(getConfigSnapshot).toHaveBeenCalledWith('team-a');
      expect(getConfig).not.toHaveBeenCalled();
    });

    it('resolves OpenCode runtime lane members from one snapshot directory read', async () => {
      const getConfig = vi.fn(async () => {
        throw new Error('verified config read should not be used for lane member resolution');
      });
      const getConfigSnapshot = vi.fn(async () => ({
        projectPath: '/repo',
        members: [
          { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
          { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          { name: 'alice', providerId: 'codex', model: 'gpt-5.4' },
        ],
      }));
      const svc = new TeamProvisioningService({
        getConfig,
        getConfigSnapshot,
      } as any);
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          { name: 'bob', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
        ]),
      };

      await expect(
        (svc as any).resolveOpenCodeMembersForRuntimeLane('team-a', 'secondary:opencode:bob')
      ).resolves.toEqual(['bob']);

      expect(getConfigSnapshot).toHaveBeenCalledTimes(1);
      expect(getConfig).not.toHaveBeenCalled();
    });

    it('delivers OpenCode secondary-lane messages to the member worktree cwd after restart', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_worktree_cwd',
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ])
      );

      (svc as any).getTrackedRunId = vi.fn(() => null);
      (svc as any).canDeliverToOpenCodeRuntimeForTeam = vi.fn(() => true);
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo/.agent-team-worktrees/bob',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).resolveCurrentOpenCodeRuntimeRunId = vi.fn(async () => 'opencode-run-bob');
      (svc as any).isOpenCodeRuntimeLaneIndexActive = vi.fn(async () => true);
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
            isolation: 'worktree',
            cwd: '/repo/.agent-team-worktrees/bob',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-1',
        })
      ).resolves.toMatchObject({ delivered: true });

      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-bob',
          laneId: 'secondary:opencode:bob',
          memberName: 'bob',
          cwd: '/repo/.agent-team-worktrees/bob',
        })
      );
    });

    it('observes accepted OpenCode prompt delivery before sending the same inbox row again', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-1',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'responded_plain_text',
          deliveredUserMessageId: 'oc-user-1',
          assistantMessageId: 'oc-assistant-1',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: 'Answer after observe.',
          reason: null,
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-ledger-1',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'pending',
      });
      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{ nextAttemptAt: string | null }>;
      };
      ledgerEnvelope.data[0].nextAttemptAt = '2000-01-01T00:00:00.000Z';
      await fsPromises.writeFile(ledgerPath, JSON.stringify(ledgerEnvelope, null, 2), 'utf8');

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-ledger-1',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: false,
        responseState: 'responded_plain_text',
        visibleReplyCorrelation: 'plain_assistant_text',
      });

      const userInbox = JSON.parse(
        await fsPromises.readFile(
          path.join(tempTeamsBase, 'team-a', 'inboxes', 'user.json'),
          'utf8'
        )
      ) as Array<Record<string, unknown>>;
      expect(userInbox).toHaveLength(1);
      expect(userInbox[0]).toMatchObject({
        from: 'bob',
        to: 'user',
        text: 'Answer after observe.',
        relayOfMessageId: 'msg-ledger-1',
        source: 'runtime_delivery',
      });

      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledWith(
        expect.objectContaining({
          messageId: 'msg-ledger-1',
          prePromptCursor: 'cursor-before',
        })
      );
    });

    it('emits a narrow task-log signal when OpenCode prompt delivery records exact session evidence', async () => {
      const svc = new TeamProvisioningService();
      const emitter = vi.fn();
      svc.setTeamChangeEmitter(emitter);
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-1',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-ledger-session',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
          taskRefs: [
            {
              taskId: 'task-a',
              displayId: 'task-a',
              teamName: 'team-a',
            },
          ],
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'pending',
      });

      expect(emitter).toHaveBeenCalledWith(
        expect.objectContaining({
          type: 'task-log-change',
          teamName: 'team-a',
          runId: 'opencode-run-bob',
          taskId: 'task-a',
          detail: 'opencode-prompt-delivery-session-evidence',
          taskSignalKind: 'log',
        })
      );
    });

    it('observes due stale OpenCode sessions without duplicating accepted prompts', async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date('2026-04-25T10:00:00.000Z'));
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: `msg_prompt_${sendMessageToMember.mock.calls.length}`,
        prePromptCursor: `cursor-${sendMessageToMember.mock.calls.length}`,
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: `oc-user-${sendMessageToMember.mock.calls.length}`,
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'session_stale',
          deliveredUserMessageId: null,
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'resolved_behavior_changed:old->new',
        },
        diagnostics: ['OpenCode session reconcile skipped because the stored session is stale'],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
            observeMessageDelivery,
          } as any,
        ])
      );

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-stale-session',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'pending',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{
          status: string;
          responseState: string;
          nextAttemptAt: string | null;
          lastReason: string | null;
        }>;
      };
      Object.assign(ledgerEnvelope.data[0], {
        status: 'accepted',
        responseState: 'session_stale',
        nextAttemptAt: '2000-01-01T00:00:00.000Z',
        lastReason: 'resolved_behavior_changed:old->new',
      });
      await fsPromises.writeFile(ledgerPath, JSON.stringify(ledgerEnvelope, null, 2), 'utf8');

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-stale-session',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'session_stale',
        ledgerStatus: 'accepted',
      });

      expect(observeMessageDelivery).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledWith(
        expect.objectContaining({
          sessionId: 'oc-session-bob',
          runtimePromptMessageId: 'msg_prompt_1',
          prePromptCursor: 'cursor-1',
        })
      );
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);

      const scheduledEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{
          status?: string;
          nextAttemptAt: string | null;
          diagnostics?: string[];
          lastReason?: string | null;
          lastSessionRefreshReason?: string | null;
          sessionRefreshAttempts?: number;
          attempts: number;
          maxAttempts: number;
        }>;
      };
      expect(scheduledEnvelope.data[0]).toMatchObject({
        attempts: 1,
        maxAttempts: 3,
        sessionRefreshAttempts: 1,
      });
      scheduledEnvelope.data[0].status = 'retry_scheduled';
      scheduledEnvelope.data[0].nextAttemptAt = '2000-01-01T00:00:00.000Z';
      scheduledEnvelope.data[0].lastReason = 'resolved_behavior_changed:old->new';
      scheduledEnvelope.data[0].lastSessionRefreshReason = 'resolved_behavior_changed:old->new';
      scheduledEnvelope.data[0].diagnostics = [
        'network timeout',
        'resolved_behavior_changed:old->new',
        'opencode_session_refresh_scheduled_after_resolved_behavior_changed',
      ];
      await fsPromises.writeFile(ledgerPath, JSON.stringify(scheduledEnvelope, null, 2), 'utf8');

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-stale-session',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'session_stale',
        ledgerStatus: 'accepted',
      });

      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(2);
      expect(sendMessageToMember).not.toHaveBeenCalledWith(
        expect.objectContaining({
          deliveryAttemptId: expect.stringContaining(':refresh'),
          forceSessionRefreshReason: 'resolved_behavior_changed:old->new',
        })
      );
      const rescheduledEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<Record<string, unknown>>;
      };
      expect(rescheduledEnvelope.data[0]).toMatchObject({
        status: 'accepted',
        responseState: 'session_stale',
        sessionRefreshAttempts: 2,
        nextAttemptAt: '2026-04-25T10:00:15.000Z',
      });
    });

    it('forces an OpenCode session refresh before send when app MCP transport evidence changed', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob-refreshed',
        runtimePromptMessageId: 'msg_prompt_refreshed',
        prePromptCursor: 'cursor-refreshed',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-refreshed',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'transport-refresh',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'old-transport-hash',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43123,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43123/mcp',
        urlHash: 'current-transport-hash',
        generation: 2,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43123 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello refreshed bob',
            messageId: 'msg-refresh-transport',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          responsePending: true,
          responseState: 'pending',
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(sendMessageToMember.mock.calls[0]?.[0]).toMatchObject({
        runId: 'opencode-run-bob',
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        forceSessionRefreshReason:
          'opencode_app_mcp_transport_changed:old-transport-hash->current-transport-hash',
      });

      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob-refreshed',
        appMcpTransportHash: 'current-transport-hash',
      });
    });

    it('routes app MCP transport mismatch through the production OpenCode adapter and bridge command', async () => {
      const svc = new TeamProvisioningService();
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember: vi.fn() });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'production-adapter-refresh',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'old-production-transport-hash',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43124,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43124/mcp',
        urlHash: 'current-production-transport-hash',
        generation: 3,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43124 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);
      const directBridgeExecute = vi.fn(async () => {
        throw new Error('direct OpenCode bridge executor should not be used for acceptance send');
      });
      const stateChangingExecute = vi.fn(
        async (input: { command: string; body: Record<string, unknown> }) => ({
          ok: true as const,
          schemaVersion: OPEN_CODE_BRIDGE_SCHEMA_VERSION,
          requestId: 'send-refresh-command',
          command: input.command as any,
          completedAt: '2026-04-25T10:00:01.000Z',
          durationMs: 10,
          runtime: {
            providerId: 'opencode' as const,
            binaryPath: '/opt/homebrew/bin/opencode',
            binaryFingerprint: 'test-opencode-binary',
            version: '1.0.0',
            capabilitySnapshotId: 'test-capability-snapshot',
          },
          diagnostics: [],
          data: {
            accepted: true,
            memberName: 'bob',
            sessionId: 'oc-session-bob-production-refresh',
            runtimePid: 456,
            runtimePromptMessageId: 'msg_prompt_production_refresh',
            prePromptCursor: 'cursor-production-refresh',
            responseObservation: {
              state: 'pending',
              deliveredUserMessageId: 'oc-user-production-refresh',
              assistantMessageId: null,
              toolCallNames: [],
              visibleMessageToolCallId: null,
              visibleReplyMessageId: null,
              visibleReplyCorrelation: null,
              latestAssistantPreview: null,
              reason: 'assistant_response_pending',
            },
            diagnostics: [],
          },
        })
      );
      const productionBridge = new OpenCodeReadinessBridge(
        { execute: directBridgeExecute },
        { stateChangingCommands: { execute: stateChangingExecute as any } }
      );
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([new OpenCodeTeamRuntimeAdapter(productionBridge)])
      );

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello production bob',
            messageId: 'msg-production-refresh-transport',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          responsePending: true,
          responseState: 'pending',
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(directBridgeExecute).not.toHaveBeenCalled();
      expect(stateChangingExecute).toHaveBeenCalledTimes(1);
      const commandInput = stateChangingExecute.mock.calls[0]?.[0] as {
        command: string;
        teamName: string;
        laneId: string;
        runId: string;
        cwd: string;
        body: Record<string, unknown>;
      };
      expect(commandInput).toMatchObject({
        command: 'opencode.sendMessage',
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        cwd: '/repo',
      });
      expect(commandInput.body).toMatchObject({
        runId: 'opencode-run-bob',
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        projectPath: '/repo',
        memberName: 'bob',
        messageId: 'msg-production-refresh-transport',
        settlementMode: 'acceptance',
        forceSessionRefreshReason:
          'opencode_app_mcp_transport_changed:old-production-transport-hash->current-production-transport-hash',
      });
      expect(commandInput.body.payloadHash).toEqual(expect.any(String));
      expect(commandInput.body.deliveryAttemptId).toEqual(expect.any(String));
      expect(String(commandInput.body.text)).toContain('hello production bob');

      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob-production-refresh',
        appMcpTransportHash: 'current-production-transport-hash',
      });
    });

    it('stamps current transport evidence when forced refresh succeeds with the same OpenCode session id', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_same_session_refresh',
        prePromptCursor: 'cursor-same-session-refresh',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-same-session-refresh',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'same-session-transport-refresh',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'old-same-session-transport-hash',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43128,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43128/mcp',
        urlHash: 'current-same-session-transport-hash',
        generation: 7,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43128 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello same session refresh bob',
            messageId: 'msg-same-session-refresh-transport',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          responsePending: true,
          responseState: 'pending',
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          forceSessionRefreshReason:
            'opencode_app_mcp_transport_changed:old-same-session-transport-hash->current-same-session-transport-hash',
        })
      );
      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob',
        appMcpTransportHash: 'current-same-session-transport-hash',
      });
    });

    it('stamps legacy OpenCode session transport evidence after a successful send without forcing refresh', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_legacy_transport_stamp',
        prePromptCursor: 'cursor-legacy-transport-stamp',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-legacy-transport-stamp',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'legacy-transport-stamp',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43129,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43129/mcp',
        urlHash: 'current-legacy-transport-hash',
        generation: 8,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43129 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello legacy transport stamp bob',
            messageId: 'msg-legacy-transport-stamp',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          responsePending: true,
          responseState: 'pending',
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.not.objectContaining({
          forceSessionRefreshReason: expect.any(String),
        })
      );
      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions).toHaveLength(1);
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob',
        appMcpTransportHash: 'current-legacy-transport-hash',
      });
    });

    it('dedupes stale and refreshed OpenCode session evidence when stamping a new transport hash', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob-refreshed',
        runtimePromptMessageId: 'msg_prompt_deduped_refresh',
        prePromptCursor: 'cursor-deduped-refresh',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-deduped-refresh',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'dedupe-transport-refresh',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'old-deduped-transport-hash',
          },
          {
            id: 'oc-session-bob-refreshed',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'older-duplicate-transport-hash',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43130,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43130/mcp',
        urlHash: 'current-deduped-transport-hash',
        generation: 9,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43130 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello deduped refresh bob',
            messageId: 'msg-deduped-refresh-transport',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          responsePending: true,
          responseState: 'pending',
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          forceSessionRefreshReason:
            'opencode_app_mcp_transport_changed:old-deduped-transport-hash->current-deduped-transport-hash',
        })
      );
      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions).toHaveLength(1);
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob-refreshed',
        appMcpTransportHash: 'current-deduped-transport-hash',
      });
    });

    it('fails closed through the delivery ledger when forced refresh reaches an old OpenCode bridge contract', async () => {
      const svc = new TeamProvisioningService();
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember: vi.fn() });
      await writeCommittedOpenCodeSessionStore({
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        runId: 'opencode-run-bob',
        batchKey: 'production-adapter-refresh-contract-missing',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName: 'team-a',
            memberName: 'bob',
            laneId: 'secondary:opencode:bob',
            runId: 'opencode-run-bob',
            source: 'app_managed_bootstrap',
            appMcpTransportHash: 'old-contract-transport-hash',
          },
        ],
      });

      const currentTransportEvidence = {
        schemaVersion: 1,
        transport: 'httpStream',
        host: '127.0.0.1',
        port: 43127,
        endpoint: '/mcp',
        url: 'http://127.0.0.1:43127/mcp',
        urlHash: 'current-contract-transport-hash',
        generation: 6,
        observedAt: '2026-04-25T10:00:00.000Z',
      };
      const transportSpy = vi.spyOn(agentTeamsMcpHttpServer, 'getCurrentHandle').mockReturnValue({
        url: currentTransportEvidence.url,
        port: currentTransportEvidence.port,
        child: { pid: 43127 },
        generation: currentTransportEvidence.generation,
        urlHash: currentTransportEvidence.urlHash,
        transportEvidence: currentTransportEvidence,
        diagnostics: [],
      } as any);
      const directBridgeExecute = vi.fn(async () => {
        throw new Error('direct OpenCode bridge executor should not be used for acceptance send');
      });
      const stateChangingExecute = vi.fn(async (input: { command: string }) => ({
        ok: false as const,
        schemaVersion: OPEN_CODE_BRIDGE_SCHEMA_VERSION,
        requestId: 'send-refresh-contract-missing',
        command: input.command as any,
        completedAt: '2026-04-25T10:00:01.000Z',
        durationMs: 10,
        error: {
          kind: 'contract_violation' as const,
          message:
            'OpenCode delivery acceptance mode is required, but the orchestrator does not advertise contract version 2.',
          retryable: false,
        },
        diagnostics: [],
      }));
      const productionBridge = new OpenCodeReadinessBridge(
        { execute: directBridgeExecute },
        { stateChangingCommands: { execute: stateChangingExecute as any } }
      );
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([new OpenCodeTeamRuntimeAdapter(productionBridge)])
      );

      try {
        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'hello old contract bob',
            messageId: 'msg-production-refresh-contract-missing',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: false,
          accepted: false,
          responsePending: false,
          responseState: 'session_stale',
          ledgerStatus: 'retry_scheduled',
          diagnostics: expect.arrayContaining([
            expect.stringContaining(
              'OpenCode forced session refresh requires delivery acceptance contract version 2'
            ),
            'opencode_session_refresh_scheduled_after_resolved_behavior_changed',
          ]),
        });
      } finally {
        transportSpy.mockRestore();
      }

      expect(directBridgeExecute).not.toHaveBeenCalled();
      expect(stateChangingExecute).toHaveBeenCalledTimes(1);
      const evidence = await readCommittedOpenCodeBootstrapSessionEvidence({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
      });
      expect(evidence.sessions[0]).toMatchObject({
        id: 'oc-session-bob',
        appMcpTransportHash: 'old-contract-transport-hash',
      });
    });

    it.each([
      {
        label: 'resolved behavior changes',
        staleReason: 'resolved_behavior_changed:old->new',
        staleDiagnostics: [
          'OpenCode session reconcile skipped because the stored session is stale',
        ],
      },
      {
        label: 'action-required reasons',
        staleReason: 'permission denied',
        staleDiagnostics: ['permission denied'],
      },
    ])('bounds accepted OpenCode session-stale observations for $label', async (scenario) => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (deliveryInput: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(deliveryInput.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_refresh_cap',
        prePromptCursor: 'cursor-refresh-cap',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-refresh-cap',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (deliveryInput: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(deliveryInput.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'session_stale',
          deliveredUserMessageId: null,
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: scenario.staleReason,
        },
        diagnostics: scenario.staleDiagnostics,
      }));
      await configureOpenCodeBobDeliveryService({
        svc,
        sendMessageToMember,
        observeMessageDelivery,
      });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-stale-session-cap',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'pending',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<Record<string, unknown>>;
      };
      Object.assign(ledgerEnvelope.data[0], {
        status: 'accepted',
        responseState: 'session_stale',
        nextAttemptAt: '2000-01-01T00:00:00.000Z',
        lastReason: scenario.staleReason,
        sessionRefreshAttempts: 5,
        maxSessionRefreshAttempts: 5,
      });
      await fsPromises.writeFile(ledgerPath, JSON.stringify(ledgerEnvelope, null, 2), 'utf8');

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'hello bob',
          messageId: 'msg-stale-session-cap',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: false,
        accepted: true,
        responsePending: false,
        responseState: 'session_stale',
        ledgerStatus: 'failed_terminal',
        reason: 'opencode_session_stale_observe_loop_after_accepted_prompt',
      });

      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(1);
      const terminalEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<Record<string, unknown>>;
      };
      expect(terminalEnvelope.data[0]).toMatchObject({
        status: 'failed_terminal',
        attempts: 1,
        sessionRefreshAttempts: 5,
        maxSessionRefreshAttempts: 5,
        lastReason: 'opencode_session_stale_observe_loop_after_accepted_prompt',
      });
    });

    it('keeps OpenCode ack-only plain-text responses pending instead of committing read', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_plain_text',
          deliveredUserMessageId: 'oc-user-ack',
          assistantMessageId: 'oc-assistant-ack',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: 'Понял',
          reason: null,
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer directly.',
          messageId: 'msg-ack-only',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'responded_plain_text',
        reason: 'plain_text_ack_only_still_requires_answer',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{ lastReason: string | null; nextAttemptAt: string | null }>;
      };
      expect(ledgerEnvelope.data[0]).toMatchObject({
        lastReason: 'plain_text_ack_only_still_requires_answer',
      });
      expect(ledgerEnvelope.data[0].nextAttemptAt).toBeTruthy();
    });

    it('materializes plain-text fallback after OpenCode message_send tool errors', async () => {
      const svc = new TeamProvisioningService();
      const taskRef = {
        taskId: 'task-tool-error-fallback',
        displayId: 'toolerr1',
        teamName: 'team-a',
      };
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'tool_error',
          deliveredUserMessageId: 'oc-user-tool-error',
          assistantMessageId: 'oc-assistant-tool-error',
          toolCallNames: ['agent-teams_message_send'],
          visibleMessageToolCallId: 'call-message-send',
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: 'GAUNTLET_CONCURRENT_TOM_OK_1',
          reason: 'message_send_tool_error_without_visible_reply_proof',
        },
        diagnostics: ['OpenCode tool failed without output'],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Concurrent check. Reply to user with GAUNTLET_CONCURRENT_TOM_OK_1.',
          messageId: 'msg-tool-error-fallback',
          replyRecipient: 'user',
          actionMode: 'ask',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_plain_text',
        visibleReplyCorrelation: 'plain_assistant_text',
        diagnostics: expect.arrayContaining([
          'opencode_message_send_tool_error_plain_text_reply_materialized',
          'opencode_plain_text_reply_materialized_to_user_inbox',
        ]),
      });

      const userInbox = JSON.parse(
        await fsPromises.readFile(
          path.join(tempTeamsBase, 'team-a', 'inboxes', 'user.json'),
          'utf8'
        )
      ) as Array<Record<string, unknown>>;
      expect(userInbox).toHaveLength(1);
      expect(userInbox[0]).toMatchObject({
        from: 'bob',
        to: 'user',
        text: 'GAUNTLET_CONCURRENT_TOM_OK_1',
        relayOfMessageId: 'msg-tool-error-fallback',
        source: 'runtime_delivery',
        taskRefs: [taskRef],
      });
    });

    it('waits through delayed OpenCode message_send tool-error fallback inline', async () => {
      const svc = new TeamProvisioningService();
      const taskRef = {
        taskId: 'task-tool-error-observe-first',
        displayId: 'obsfirst',
        teamName: 'team-a',
      };
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before-tool-error',
        responseObservation: {
          state: 'tool_error',
          deliveredUserMessageId: 'oc-user-tool-error-observe',
          assistantMessageId: 'oc-assistant-tool-error-observe',
          toolCallNames: ['agent-teams_message_send'],
          visibleMessageToolCallId: 'call-message-send-observe',
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'message_send_tool_error_without_visible_reply_proof',
        },
        diagnostics: ['OpenCode tool failed without output'],
      }));
      let observeAttempts = 0;
      const opencodeAdapter = {
        providerId: 'opencode',
        prepare: vi.fn(),
        launch: vi.fn(),
        reconcile: vi.fn(),
        stop: vi.fn(),
        sendMessageToMember,
        observeMessageDelivery: vi.fn(async function (
          this: unknown,
          input: Record<string, unknown>
        ) {
          expect(this).toBe(opencodeAdapter);
          observeAttempts += 1;
          return {
            ok: true,
            providerId: 'opencode',
            memberName: String(input.memberName),
            sessionId: 'oc-session-bob',
            responseObservation:
              observeAttempts === 1
                ? {
                    state: 'pending',
                    deliveredUserMessageId: 'oc-user-tool-error-observe',
                    assistantMessageId: null,
                    toolCallNames: ['agent-teams_message_send'],
                    visibleMessageToolCallId: 'call-message-send-observe',
                    visibleReplyMessageId: null,
                    visibleReplyCorrelation: null,
                    latestAssistantPreview: null,
                    reason: 'assistant_reply_not_visible_yet',
                  }
                : {
                    state: 'responded_plain_text',
                    deliveredUserMessageId: 'oc-user-tool-error-observe',
                    assistantMessageId: 'oc-assistant-plain-fallback',
                    toolCallNames: ['agent-teams_message_send'],
                    visibleMessageToolCallId: 'call-message-send-observe',
                    visibleReplyMessageId: null,
                    visibleReplyCorrelation: 'plain_assistant_text',
                    latestAssistantPreview: 'GAUNTLET_OBSERVE_FIRST_OK_1',
                    reason: 'assistant_replied_with_plain_text',
                  },
            diagnostics: [
              observeAttempts === 1
                ? 'OpenCode assistant reply not visible yet'
                : 'Observed OpenCode plain-text fallback after message_send tool error',
            ],
          };
        }),
      } as any;
      const observeMessageDelivery = opencodeAdapter.observeMessageDelivery;
      svc.setRuntimeAdapterRegistry(new TeamRuntimeAdapterRegistry([opencodeAdapter]));

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Reply to user with GAUNTLET_OBSERVE_FIRST_OK_1.',
          messageId: 'msg-tool-error-observe-first',
          replyRecipient: 'user',
          actionMode: 'ask',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_plain_text',
        visibleReplyCorrelation: 'plain_assistant_text',
        diagnostics: expect.arrayContaining([
          'opencode_message_send_tool_error_inline_observe',
          'opencode_direct_user_delivery_inline_observe_attempt_2',
          'opencode_plain_text_reply_materialized_to_user_inbox',
        ]),
      });

      const userInbox = JSON.parse(
        await fsPromises.readFile(
          path.join(tempTeamsBase, 'team-a', 'inboxes', 'user.json'),
          'utf8'
        )
      ) as Array<Record<string, unknown>>;
      expect(userInbox).toHaveLength(1);
      expect(userInbox[0]).toMatchObject({
        from: 'bob',
        to: 'user',
        text: 'GAUNTLET_OBSERVE_FIRST_OK_1',
        relayOfMessageId: 'msg-tool-error-observe-first',
        source: 'runtime_delivery',
        taskRefs: [taskRef],
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(2);
      expect(observeMessageDelivery).toHaveBeenCalledWith(
        expect.objectContaining({
          messageId: 'msg-tool-error-observe-first',
          prePromptCursor: 'cursor-before-tool-error',
        })
      );
    }, 15_000);

    it('keeps accepted OpenCode delivery retryable when inline observe throws', async () => {
      const svc = new TeamProvisioningService();
      const taskRef = {
        taskId: 'task-tool-error-observe-throws',
        displayId: 'obsthrow',
        teamName: 'team-a',
      };
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before-observe-throws',
        responseObservation: {
          state: 'tool_error',
          deliveredUserMessageId: 'oc-user-observe-throws',
          assistantMessageId: 'oc-assistant-observe-throws',
          toolCallNames: ['agent-teams_message_send'],
          visibleMessageToolCallId: 'call-message-send-observe-throws',
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'message_send_tool_error_without_visible_reply_proof',
        },
        diagnostics: ['OpenCode tool failed without output'],
      }));
      const observeMessageDelivery = vi.fn(async () => {
        throw new Error('observe bridge fs write failed');
      });
      await configureOpenCodeBobDeliveryService({
        svc,
        sendMessageToMember,
        observeMessageDelivery,
      });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Reply to user with GAUNTLET_OBSERVE_THROW_OK_1.',
          messageId: 'msg-tool-error-observe-throws',
          replyRecipient: 'user',
          actionMode: 'ask',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'reconcile_failed',
        ledgerStatus: 'retry_scheduled',
        reason: expect.stringContaining('opencode_direct_user_delivery_inline_observe_failed'),
        diagnostics: expect.arrayContaining([
          'opencode_direct_user_delivery_inline_observe_attempt_1',
          expect.stringContaining('observe bridge fs write failed'),
        ]),
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(1);
    }, 10_000);

    it('resolves stored attachment payloads for OpenCode inbox relay before delivery', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'responded_plain_text',
          deliveredUserMessageId: 'oc-user-attachment',
          assistantMessageId: 'oc-assistant-attachment',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: 'I reviewed the attached image and can proceed.',
          reason: 'assistant_replied_with_plain_text',
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({
        svc,
        sendMessageToMember,
        memberModel: 'openai/gpt-5.4-mini',
      });
      await (svc as any).attachmentStore.saveAttachments('team-a', 'msg-image-attachment', [
        {
          id: 'att-1',
          filename: 'diagram.png',
          mimeType: 'image/png',
          size: 5,
          data: 'aW1nMQ==',
        },
      ]);
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'bob.json'),
        `${JSON.stringify(
          [
            {
              from: 'team-lead',
              to: 'bob',
              text: 'Review this image.',
              timestamp: '2026-04-25T10:00:00.000Z',
              read: false,
              messageId: 'msg-image-attachment',
              attachments: [
                {
                  id: 'att-1',
                  filename: 'diagram.png',
                  mimeType: 'image/png',
                  size: 5,
                },
              ],
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.relayOpenCodeMemberInboxMessages('team-a', 'bob', {
          onlyMessageId: 'msg-image-attachment',
        })
      ).resolves.toMatchObject({
        attempted: 1,
        delivered: 1,
        failed: 0,
        relayed: 1,
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          messageId: 'msg-image-attachment',
          fileParts: [
            {
              type: 'file',
              mime: 'image/png',
              url: 'data:image/png;base64,aW1nMQ==',
              filename: 'diagram.png',
            },
          ],
        })
      );
    });

    it('keeps OpenCode inbox relay unread and surfaces a clear reason when the model is not vision-capable', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn();
      await configureOpenCodeBobDeliveryService({
        svc,
        sendMessageToMember,
        memberModel: 'openrouter/z-ai/glm-5.1',
      });
      await (svc as any).attachmentStore.saveAttachments('team-a', 'msg-unsupported-image-model', [
        {
          id: 'att-unsupported-model',
          filename: 'diagram.png',
          mimeType: 'image/png',
          size: 5,
          data: 'aW1nMQ==',
        },
      ]);
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'bob.json'),
        `${JSON.stringify(
          [
            {
              from: 'team-lead',
              to: 'bob',
              text: 'Review this image.',
              timestamp: '2026-04-25T10:00:00.000Z',
              read: false,
              messageId: 'msg-unsupported-image-model',
              attachments: [
                {
                  id: 'att-unsupported-model',
                  filename: 'diagram.png',
                  mimeType: 'image/png',
                  size: 5,
                },
              ],
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const relay = await svc.relayOpenCodeMemberInboxMessages('team-a', 'bob', {
        onlyMessageId: 'msg-unsupported-image-model',
      });

      expect(relay).toMatchObject({
        attempted: 1,
        delivered: 0,
        failed: 1,
        relayed: 0,
        lastDelivery: {
          delivered: false,
          reason: 'attachment_model_unsupported',
          userVisibleImpact: {
            state: 'error',
            reasonCode: 'backend_error',
            message:
              'This OpenCode model is not verified for image attachments. Choose a vision-capable model or remove the image.',
          },
        },
      });
      expect(relay.diagnostics?.join('\n')).toContain(
        'opencode_attachment_delivery_prepare_failed: This OpenCode model is not verified for image attachments. Choose a vision-capable model or remove the image.'
      );
      expect(sendMessageToMember).not.toHaveBeenCalled();
      const rows = JSON.parse(
        await fsPromises.readFile(path.join(inboxDir, 'bob.json'), 'utf8')
      ) as Array<{ read?: boolean }>;
      expect(rows[0]?.read).toBe(false);
    });

    it('keeps OpenCode inbox relay unread when attachment payload data is unavailable', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn();
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'bob.json'),
        `${JSON.stringify(
          [
            {
              from: 'team-lead',
              to: 'bob',
              text: 'Review this image.',
              timestamp: '2026-04-25T10:00:00.000Z',
              read: false,
              messageId: 'msg-missing-attachment',
              attachments: [
                {
                  id: 'missing-att',
                  filename: 'missing.png',
                  mimeType: 'image/png',
                  size: 5,
                },
              ],
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.relayOpenCodeMemberInboxMessages('team-a', 'bob', {
          onlyMessageId: 'msg-missing-attachment',
        })
      ).resolves.toMatchObject({
        attempted: 1,
        delivered: 0,
        failed: 1,
        relayed: 0,
        lastDelivery: {
          delivered: false,
          reason: 'opencode_inbox_attachment_payload_unavailable: missing-att',
        },
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();

      const inbox = JSON.parse(await fsPromises.readFile(path.join(inboxDir, 'bob.json'), 'utf8'));
      expect(inbox[0]).toMatchObject({
        messageId: 'msg-missing-attachment',
        read: false,
      });
    });

    it('treats OpenCode send bridge timeouts as acceptance-unknown observe-first records', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: false,
        providerId: 'opencode',
        memberName: String(input.memberName),
        diagnostics: ['OpenCode message bridge failed: OpenCode bridge command timed out'],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please handle this.',
          messageId: 'msg-timeout-unknown',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: false,
        responsePending: true,
        acceptanceUnknown: true,
        reason: 'opencode_prompt_acceptance_unknown_after_bridge_timeout',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{
          acceptanceUnknown: boolean;
          status: string;
          lastReason: string | null;
          nextAttemptAt: string | null;
        }>;
      };
      expect(ledgerEnvelope.data[0]).toMatchObject({
        acceptanceUnknown: true,
        status: 'failed_retryable',
        lastReason: 'opencode_prompt_acceptance_unknown_after_bridge_timeout',
      });
      expect(ledgerEnvelope.data[0].nextAttemptAt).toBeTruthy();
    });

    it('keeps accepted OpenCode responses without exact prompt identity acceptance-unknown', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please handle this.',
          messageId: 'msg-accepted-missing-prompt-id',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: false,
        responsePending: true,
        acceptanceUnknown: true,
        reason: 'opencode_prompt_acceptance_unknown_missing_runtime_prompt_id',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{
          acceptanceUnknown: boolean;
          status: string;
          runtimePromptMessageId: string | null;
          lastReason: string | null;
          diagnostics: string[];
        }>;
      };
      expect(ledgerEnvelope.data[0]).toMatchObject({
        acceptanceUnknown: true,
        status: 'failed_retryable',
        runtimePromptMessageId: null,
        lastReason: 'opencode_prompt_acceptance_unknown_missing_runtime_prompt_id',
      });
      expect(ledgerEnvelope.data[0].diagnostics).toContain(
        'opencode_prompt_acceptance_missing_runtime_prompt_id'
      );
    });

    it('marks OpenCode payload hash mismatch terminal without sending a duplicate prompt', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'pending',
          deliveredUserMessageId: 'oc-user-payload',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Original text.',
          messageId: 'msg-payload-mismatch',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
      });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Changed text under the same message id.',
          messageId: 'msg-payload-mismatch',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: false,
        responsePending: false,
        reason: 'opencode_prompt_delivery_payload_mismatch',
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
    });

    it('accepts visible OpenCode replies written to the configured lead inbox for lead aliases', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn();
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'team-lead.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'team-lead',
              text: 'Here is the concrete answer.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-lead-1',
              relayOfMessageId: 'msg-lead-alias',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer the lead.',
          messageId: 'msg-lead-alias',
          replyRecipient: 'lead',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        visibleReplyMessageId: 'reply-lead-1',
        visibleReplyCorrelation: 'relayOfMessageId',
        diagnostics: [],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
    });

    it('inherits taskRefs from the OpenCode delivery ledger for exact visible replies', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn();
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      const taskRef = { taskId: 'task-1', displayId: 'abcd1234', teamName: 'team-a' };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Here is the concrete answer for #abcd1234.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-task-1',
              relayOfMessageId: 'msg-task-refs-1',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer for #abcd1234.',
          messageId: 'msg-task-refs-1',
          replyRecipient: 'user',
          actionMode: 'ask',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        visibleReplyMessageId: 'reply-user-task-1',
        visibleReplyCorrelation: 'relayOfMessageId',
      });

      const userInbox = JSON.parse(
        await fsPromises.readFile(path.join(inboxDir, 'user.json'), 'utf8')
      ) as Array<Record<string, unknown>>;
      expect(userInbox[0]).toMatchObject({
        messageId: 'reply-user-task-1',
        taskRefs: [taskRef],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
    });

    it('repairs OpenCode visible replies that used a wrong relayOfMessageId but returned a messageId', async () => {
      const svc = new TeamProvisioningService();
      const taskRef = { taskId: 'task-1', displayId: 'abcd1234', teamName: 'team-a' };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => {
        await fsPromises.mkdir(inboxDir, { recursive: true });
        await fsPromises.writeFile(
          path.join(inboxDir, 'user.json'),
          `${JSON.stringify(
            [
              {
                from: 'bob',
                to: 'user',
                text: 'Here is the concrete answer for #abcd1234.',
                timestamp: '2026-04-25T10:00:03.000Z',
                read: false,
                messageId: 'reply-wrong-relay-1',
                relayOfMessageId: 'hallucinated-inbound-id',
                source: 'runtime_delivery',
                taskRefs: [taskRef],
              },
            ],
            null,
            2
          )}\n`,
          'utf8'
        );
        return {
          ok: true,
          providerId: 'opencode',
          memberName: String(input.memberName),
          sessionId: 'oc-session-bob',
          prePromptCursor: 'cursor-before',
          responseObservation: {
            state: 'responded_visible_message',
            deliveredUserMessageId: 'oc-user-1',
            assistantMessageId: 'oc-assistant-1',
            toolCallNames: ['message_send'],
            visibleMessageToolCallId: 'call-1',
            visibleReplyMessageId: 'reply-wrong-relay-1',
            visibleReplyCorrelation: 'direct_child_message_send',
            visibleReplyMissingRelayOfMessageId: true,
            latestAssistantPreview: null,
            reason: 'visible_reply_missing_relayOfMessageId',
          },
          diagnostics: [],
        };
      });
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer for #abcd1234.',
          messageId: 'msg-wrong-relay-1',
          replyRecipient: 'user',
          actionMode: 'ask',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        visibleReplyMessageId: 'reply-wrong-relay-1',
        visibleReplyCorrelation: 'relayOfMessageId',
        diagnostics: expect.arrayContaining([
          'opencode_visible_reply_recovered_by_observed_message_id',
          'opencode_visible_reply_relayOfMessageId_repaired',
        ]),
      });

      const userInbox = JSON.parse(
        await fsPromises.readFile(path.join(inboxDir, 'user.json'), 'utf8')
      ) as Array<Record<string, unknown>>;
      expect(userInbox).toHaveLength(1);
      expect(userInbox[0]).toMatchObject({
        messageId: 'reply-wrong-relay-1',
        relayOfMessageId: 'msg-wrong-relay-1',
        taskRefs: [taskRef],
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
    });

    it('accepts observed visible OpenCode user replies for lead-delegated inbox messages', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_visible_message',
          deliveredUserMessageId: 'oc-user-1',
          assistantMessageId: 'oc-assistant-1',
          toolCallNames: ['message_send'],
          visibleMessageToolCallId: 'call-1',
          visibleReplyMessageId: 'reply-user-1',
          visibleReplyCorrelation: 'relayOfMessageId',
          latestAssistantPreview: null,
          reason: 'visible_message_sent',
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Here is the concrete answer for the user.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-1',
              relayOfMessageId: 'msg-lead-delegated',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer the user.',
          messageId: 'msg-lead-delegated',
          replyRecipient: 'team-lead',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        visibleReplyMessageId: 'reply-user-1',
        visibleReplyCorrelation: 'relayOfMessageId',
        diagnostics: ['visible_message_sent'],
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          replyRecipient: 'team-lead',
          messageId: 'msg-lead-delegated',
        })
      );
    });

    it('accepts exact observed OpenCode user replies for custom configured lead recipients', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'captain', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Old reply with the same relay id must not be accepted.',
              timestamp: '2026-04-25T10:00:02.000Z',
              read: false,
              messageId: 'reply-user-stale',
              relayOfMessageId: 'msg-custom-lead',
              source: 'runtime_delivery',
            },
            {
              from: 'bob',
              to: 'user',
              text: 'Here is the observed answer for the user.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-custom',
              relayOfMessageId: 'msg-custom-lead',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'captain',
        from: 'bob',
        relayOfMessageId: 'msg-custom-lead',
        expectedMessageId: 'reply-user-custom',
      });

      expect(proof).toMatchObject({
        inboxName: 'user',
        message: {
          messageId: 'reply-user-custom',
          relayOfMessageId: 'msg-custom-lead',
          from: 'bob',
          to: 'user',
        },
        missingRuntimeDeliverySource: false,
      });
    });

    it('uses the exact observed message id for direct OpenCode user replies', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Old duplicate for the same delivery.',
              timestamp: '2026-04-25T10:00:02.000Z',
              read: false,
              messageId: 'reply-user-stale',
              relayOfMessageId: 'msg-direct-user',
              source: 'runtime_delivery',
            },
            {
              from: 'bob',
              to: 'user',
              text: 'Current observed reply.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-current',
              relayOfMessageId: 'msg-direct-user',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'user',
        from: 'bob',
        relayOfMessageId: 'msg-direct-user',
        expectedMessageId: 'reply-user-current',
      });

      expect(proof).toMatchObject({
        inboxName: 'user',
        message: {
          messageId: 'reply-user-current',
          relayOfMessageId: 'msg-direct-user',
          from: 'bob',
          to: 'user',
        },
      });
    });

    it('accepts a unique OpenCode user fallback reply when relay correlation has no exact id', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'captain', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'alice',
              to: 'user',
              text: 'Different sender should not affect Bob proof.',
              timestamp: '2026-04-25T10:00:01.000Z',
              read: false,
              messageId: 'reply-user-alice',
              relayOfMessageId: 'msg-custom-lead-no-id',
              source: 'runtime_delivery',
            },
            {
              from: 'bob',
              to: 'user',
              text: 'Here is the only Bob reply for this relay.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: ' reply-user-single ',
              relayOfMessageId: 'msg-custom-lead-no-id',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'captain',
        from: 'bob',
        relayOfMessageId: 'msg-custom-lead-no-id',
        allowUserFallbackForLeadRecipient: true,
      });

      expect(proof).toMatchObject({
        inboxName: 'user',
        message: {
          messageId: 'reply-user-single',
          relayOfMessageId: 'msg-custom-lead-no-id',
          from: 'bob',
          to: 'user',
        },
        missingRuntimeDeliverySource: false,
      });
    });

    it('does not use OpenCode user fallback for lead recipients without confirmed relay correlation', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'captain', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'This exists, but the caller did not confirm relay correlation.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-single',
              relayOfMessageId: 'msg-custom-lead-no-correlation',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'captain',
        from: 'bob',
        relayOfMessageId: 'msg-custom-lead-no-correlation',
      });

      expect(proof).toBeNull();
    });

    it('rejects ambiguous OpenCode user fallback replies when relay correlation has no exact id', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'captain', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'First candidate.',
              timestamp: '2026-04-25T10:00:02.000Z',
              read: false,
              messageId: 'reply-user-1',
              relayOfMessageId: 'msg-custom-lead-ambiguous',
              source: 'runtime_delivery',
            },
            {
              from: 'bob',
              to: 'user',
              text: 'Second candidate.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-2',
              relayOfMessageId: 'msg-custom-lead-ambiguous',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'captain',
        from: 'bob',
        relayOfMessageId: 'msg-custom-lead-ambiguous',
        allowUserFallbackForLeadRecipient: true,
      });

      expect(proof).toBeNull();
    });

    it('rejects custom lead user fallback replies without the exact observed message id', async () => {
      const svc = new TeamProvisioningService();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'captain', providerId: 'codex', agentType: 'team-lead', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'This is not the observed reply for the current delivery.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-user-stale',
              relayOfMessageId: 'msg-custom-lead',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      const proof = await (svc as any).findOpenCodeVisibleReplyByRelayOfMessageId({
        teamName: 'team-a',
        replyRecipient: 'captain',
        from: 'bob',
        relayOfMessageId: 'msg-custom-lead',
        expectedMessageId: 'reply-user-expected',
      });

      expect(proof).toBeNull();
    });

    it('uses legacy OpenCode prompt acceptance semantics when the watchdog is disabled', async () => {
      const previous = process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG;
      process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG = '0';
      try {
        const svc = new TeamProvisioningService();
        const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
          ok: true,
          providerId: 'opencode',
          memberName: String(input.memberName),
          sessionId: 'oc-session-bob',
          responseObservation: {
            state: 'pending',
            deliveredUserMessageId: 'oc-user-disabled',
            assistantMessageId: null,
            toolCallNames: [],
            visibleMessageToolCallId: null,
            visibleReplyMessageId: null,
            visibleReplyCorrelation: null,
            latestAssistantPreview: null,
            reason: 'assistant_response_pending',
          },
          diagnostics: [],
        }));
        const registry = new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ]);
        svc.setRuntimeAdapterRegistry(registry);

        (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
        (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
        (svc as any).setSecondaryRuntimeRun({
          teamName: 'team-a',
          runId: 'opencode-run-bob',
          providerId: 'opencode',
          laneId: 'secondary:opencode:bob',
          memberName: 'bob',
          cwd: '/repo',
        });
        await writeDefaultBobOpenCodeBootstrapEvidence();
        (svc as any).configReader = {
          getConfig: vi.fn(async () => ({
            projectPath: '/repo',
            members: [
              { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
              { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
            ],
          })),
        };
        (svc as any).teamMetaStore = {
          getMeta: vi.fn(async () => ({
            launchIdentity: { providerId: 'codex' },
            providerId: 'codex',
          })),
        };
        (svc as any).membersMetaStore = {
          getMembers: vi.fn(async () => [
            {
              name: 'bob',
              providerId: 'opencode',
              model: 'opencode/minimax-m2.5-free',
            },
          ]),
        };

        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'Please answer eventually.',
            messageId: 'msg-watchdog-disabled',
            replyRecipient: 'user',
            actionMode: 'ask',
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          accepted: true,
          responsePending: false,
          responseState: 'pending',
          diagnostics: [],
        });
        expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      } finally {
        if (previous === undefined) {
          delete process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG;
        } else {
          process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG = previous;
        }
      }
    });

    it('keeps legacy OpenCode work-sync delivery pending without accepted report proof', async () => {
      const previous = process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG;
      process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG = '0';
      try {
        const svc = new TeamProvisioningService();
        const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
          ok: true,
          providerId: 'opencode',
          memberName: String(input.memberName),
          sessionId: 'oc-session-bob',
          responseObservation: {
            state: 'responded_non_visible_tool' as const,
            deliveredUserMessageId: 'oc-user-legacy-work-sync',
            assistantMessageId: 'oc-assistant-legacy-work-sync',
            toolCallNames: ['member_work_sync_status', 'member_work_sync_report'],
            visibleMessageToolCallId: null,
            visibleReplyMessageId: null,
            visibleReplyCorrelation: null,
            latestAssistantPreview: null,
            reason: null,
          },
          diagnostics: [],
        }));
        await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
        svc.setMemberWorkSyncAcceptedReportChecker(async () => false);

        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'Work sync check for #task-1.',
            messageId: 'msg-legacy-work-sync-report',
            replyRecipient: 'team-lead',
            actionMode: 'do',
            messageKind: 'member_work_sync_nudge',
            workSyncIntent: 'agenda_sync',
            taskRefs: [
              {
                taskId: 'task-1',
                displayId: 'task-1',
                teamName: 'team-a',
              },
            ],
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          accepted: true,
          responsePending: true,
          responseState: 'responded_non_visible_tool',
          reason: 'member_work_sync_report_required',
        });
      } finally {
        if (previous === undefined) {
          delete process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG;
        } else {
          process.env.CLAUDE_TEAM_OPENCODE_PROMPT_DELIVERY_WATCHDOG = previous;
        }
      }
    });

    it('prioritizes OpenCode work-sync nudges over older ordinary inbox rows', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: `runtime-${String(input.messageId)}`,
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: `oc-user-${String(input.messageId)}`,
          assistantMessageId: `oc-assistant-${String(input.messageId)}`,
          toolCallNames:
            input.messageKind === 'member_work_sync_nudge'
              ? ['member_work_sync_status', 'member_work_sync_report']
              : ['task_get'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      svc.setMemberWorkSyncAcceptedReportChecker(async () => true);

      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'bob.json'),
        `${JSON.stringify(
          [
            {
              from: 'team-lead',
              to: 'bob',
              text: 'Older ordinary follow-up.',
              timestamp: '2026-04-25T09:00:00.000Z',
              read: false,
              messageId: 'msg-ordinary-old',
            },
            {
              from: 'system',
              to: 'bob',
              text: 'Work sync check for #task-1.',
              timestamp: '2026-04-25T10:00:00.000Z',
              read: false,
              messageId: 'msg-work-sync-priority',
              source: 'system_notification',
              messageKind: 'member_work_sync_nudge',
              workSyncIntent: 'agenda_sync',
              taskRefs: [
                {
                  taskId: 'task-1',
                  displayId: 'task-1',
                  teamName: 'team-a',
                },
              ],
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(svc.relayOpenCodeMemberInboxMessages('team-a', 'bob')).resolves.toMatchObject({
        attempted: 1,
        delivered: 1,
        failed: 0,
        relayed: 1,
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          messageId: 'msg-work-sync-priority',
          messageKind: 'member_work_sync_nudge',
        })
      );
    });

    it('retries OpenCode direct asks after non-visible tool activity with an explicit retry header', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state:
            sendMessageToMember.mock.calls.length === 1 ? 'responded_non_visible_tool' : 'pending',
          deliveredUserMessageId: 'oc-user-ask',
          assistantMessageId:
            sendMessageToMember.mock.calls.length === 1 ? 'oc-assistant-read' : null,
          toolCallNames: sendMessageToMember.mock.calls.length === 1 ? ['read'] : [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: sendMessageToMember.mock.calls.length === 1 ? null : 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'responded_non_visible_tool',
          deliveredUserMessageId: 'oc-user-ask',
          assistantMessageId: 'oc-assistant-read',
          toolCallNames: ['read'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'What did you find?',
          messageId: 'msg-visible-required',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'responded_non_visible_tool',
        reason: 'visible_reply_still_required',
      });

      const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
        teamsBasePath: tempTeamsBase,
        teamName: 'team-a',
        laneId: 'secondary:opencode:bob',
        fileName: 'opencode-prompt-delivery-ledger.json',
      });
      const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
        data: Array<{ nextAttemptAt: string | null }>;
      };
      ledgerEnvelope.data[0].nextAttemptAt = '2000-01-01T00:00:00.000Z';
      await fsPromises.writeFile(ledgerPath, JSON.stringify(ledgerEnvelope, null, 2), 'utf8');

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'What did you find?',
          messageId: 'msg-visible-required',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
      });

      expect(observeMessageDelivery).toHaveBeenCalledTimes(1);
      expect(sendMessageToMember).toHaveBeenCalledTimes(2);
      expect(sendMessageToMember.mock.calls[1]?.[0]).toMatchObject({
        messageId: 'msg-visible-required',
        text: expect.stringContaining('<opencode_delivery_retry>'),
      });
      const retryText = String(sendMessageToMember.mock.calls[1]?.[0].text ?? '');
      expect(retryText).toContain('relayOfMessageId="msg-visible-required"');
      expect(retryText).toContain('agent-teams_message_send');
      expect(retryText).toContain('What did you find?');
    });

    it('keeps OpenCode task delivery pending after read-only non-visible tool activity', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-task',
          assistantMessageId: 'oc-assistant-read',
          toolCallNames: ['read', 'bash'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Start task #task-1 now.',
          messageId: 'msg-task-read-only',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'responded_non_visible_tool',
        ledgerStatus: 'retry_scheduled',
        reason: 'non_visible_tool_without_task_progress',
      });
    });

    it('accepts member work sync report as OpenCode delivery response proof', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-work-sync',
          assistantMessageId: 'oc-assistant-work-sync-report',
          toolCallNames: ['member_work_sync_status', 'member_work_sync_report'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      svc.setControlApiBaseUrlResolver(async () => 'http://127.0.0.1:43123');
      svc.setMemberWorkSyncAcceptedReportChecker(async () => true);

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Work sync check for #task-1.',
          messageId: 'msg-work-sync-report',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_non_visible_tool',
        ledgerStatus: 'responded',
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          controlUrl: 'http://127.0.0.1:43123',
        })
      );
    });

    it('accepts member work sync report proof even when OpenCode also sends a visible reply', async () => {
      const svc = new TeamProvisioningService();
      const taskRef = {
        taskId: 'task-1',
        displayId: 'task-1',
        teamName: 'team-a',
      };
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_visible_message' as const,
          deliveredUserMessageId: 'oc-user-work-sync-report-visible',
          assistantMessageId: 'oc-assistant-work-sync-report-visible',
          toolCallNames: ['member_work_sync_status', 'member_work_sync_report', 'message_send'],
          visibleMessageToolCallId: 'call-visible-work-sync-report',
          visibleReplyMessageId: 'visible-work-sync-report-reply',
          visibleReplyCorrelation: 'relayOfMessageId' as const,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: {
          state: 'responded_visible_message' as const,
          deliveredUserMessageId: 'oc-user-work-sync-report-visible',
          assistantMessageId: 'oc-assistant-work-sync-report-visible',
          toolCallNames: ['member_work_sync_status', 'member_work_sync_report', 'message_send'],
          visibleMessageToolCallId: 'call-visible-work-sync-report',
          visibleReplyMessageId: 'visible-work-sync-report-reply',
          visibleReplyCorrelation: 'relayOfMessageId' as const,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({
        svc,
        sendMessageToMember,
        observeMessageDelivery,
      });
      svc.setMemberWorkSyncAcceptedReportChecker(async () => true);
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'team-lead.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'team-lead',
              text: 'I reported that I am still working on task-1.',
              timestamp: '2026-04-25T10:00:01.000Z',
              read: false,
              messageId: 'visible-work-sync-report-reply',
              relayOfMessageId: 'msg-work-sync-report-visible',
              source: 'runtime_delivery',
              taskRefs: [taskRef],
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Work sync check for #task-1.',
          messageId: 'msg-work-sync-report-visible',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          workSyncIntent: 'agenda_sync',
          taskRefs: [taskRef],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        ledgerStatus: 'responded',
      });
    });

    it('keeps OpenCode member work sync report pending until the report is accepted', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-work-sync-rejected-report',
          assistantMessageId: 'oc-assistant-work-sync-rejected-report',
          toolCallNames: ['member_work_sync_status', 'member_work_sync_report'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });
      svc.setMemberWorkSyncAcceptedReportChecker(async () => false);

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Work sync check for #task-1.',
          messageId: 'msg-work-sync-rejected-report',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          workSyncIntent: 'agenda_sync',
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'responded_non_visible_tool',
        ledgerStatus: 'retry_scheduled',
        reason: 'member_work_sync_report_required',
      });
    });

    it('accepts review workflow tools as review pickup delivery response proof', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-review-pickup',
          assistantMessageId: 'oc-assistant-review-start',
          toolCallNames: ['review_start'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Review pickup required for #task-1.',
          messageId: 'msg-review-pickup-start',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          workSyncIntent: 'review_pickup',
          workSyncReviewRequestEventIds: ['evt-review-request'],
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'member-work-sync-review-pickup',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_non_visible_tool',
        ledgerStatus: 'responded',
      });
    });

    it.each([
      {
        name: 'plain text',
        responseObservation: {
          state: 'responded_plain_text' as const,
          deliveredUserMessageId: 'oc-user-work-sync-plain',
          assistantMessageId: 'oc-assistant-work-sync-plain',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: 'I am still working on task-1 and will continue now.',
          reason: null,
        },
      },
      {
        name: 'visible message',
        seedVisibleReply: true,
        responseObservation: {
          state: 'responded_visible_message' as const,
          deliveredUserMessageId: 'oc-user-work-sync-visible',
          assistantMessageId: 'oc-assistant-work-sync-visible',
          toolCallNames: ['agent-teams_message_send'],
          visibleMessageToolCallId: 'call-visible-work-sync',
          visibleReplyMessageId: 'visible-work-sync-reply',
          visibleReplyCorrelation: 'relayOfMessageId' as const,
          latestAssistantPreview: null,
          reason: null,
        },
      },
      {
        name: 'task tool',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-work-sync-task-tool',
          assistantMessageId: 'oc-assistant-work-sync-task-tool',
          toolCallNames: ['task_start'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
      },
      {
        name: 'agenda-sync review tool',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-work-sync-review-tool',
          assistantMessageId: 'oc-assistant-work-sync-review-tool',
          toolCallNames: ['review_start'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
      },
    ])(
      'keeps member work sync $name OpenCode deliveries pending without report proof',
      async ({ responseObservation, seedVisibleReply }) => {
        const svc = new TeamProvisioningService();
        const taskRef = {
          taskId: 'task-1',
          displayId: 'task-1',
          teamName: 'team-a',
        };
        const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
          ok: true,
          providerId: 'opencode',
          memberName: String(input.memberName),
          sessionId: 'oc-session-bob',
          prePromptCursor: 'cursor-before',
          responseObservation,
          diagnostics: [],
        }));
        const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
          ok: true,
          providerId: 'opencode',
          memberName: String(input.memberName),
          sessionId: 'oc-session-bob',
          responseObservation,
          diagnostics: [],
        }));
        await configureOpenCodeBobDeliveryService({
          svc,
          sendMessageToMember,
          observeMessageDelivery,
        });
        if (seedVisibleReply) {
          const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
          await fsPromises.mkdir(inboxDir, { recursive: true });
          await fsPromises.writeFile(
            path.join(inboxDir, 'team-lead.json'),
            `${JSON.stringify(
              [
                {
                  from: 'bob',
                  to: 'team-lead',
                  text: 'I am still working on task-1 and will continue now.',
                  timestamp: '2026-04-25T10:00:01.000Z',
                  read: false,
                  messageId: 'visible-work-sync-reply',
                  relayOfMessageId: 'msg-work-sync-without-report-proof',
                  source: 'runtime_delivery',
                  taskRefs: [taskRef],
                },
              ],
              null,
              2
            )}\n`,
            'utf8'
          );
        }

        await expect(
          svc.deliverOpenCodeMemberMessage('team-a', {
            memberName: 'bob',
            text: 'Work sync check for #task-1.',
            messageId: 'msg-work-sync-without-report-proof',
            replyRecipient: 'team-lead',
            actionMode: 'do',
            messageKind: 'member_work_sync_nudge',
            workSyncIntent: 'agenda_sync',
            taskRefs: [taskRef],
            source: 'watcher',
            inboxTimestamp: '2026-04-25T10:00:00.000Z',
          })
        ).resolves.toMatchObject({
          delivered: true,
          accepted: true,
          responsePending: true,
          responseState: responseObservation.state,
          ledgerStatus: 'retry_scheduled',
          reason: 'member_work_sync_report_required',
        });
      }
    );

    it('keeps member work sync status-only OpenCode deliveries pending', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'responded_non_visible_tool' as const,
          deliveredUserMessageId: 'oc-user-work-sync-status',
          assistantMessageId: 'oc-assistant-work-sync-status',
          toolCallNames: ['member_work_sync_status'],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: null,
        },
        diagnostics: [],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Work sync check for #task-1.',
          messageId: 'msg-work-sync-status-only',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'responded_non_visible_tool',
        ledgerStatus: 'retry_scheduled',
        reason: 'member_work_sync_report_required',
      });
    });

    it('treats OpenCode empty assistant turns with prompt proof as pending delivery', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: false,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'empty_assistant_turn' as const,
          deliveredUserMessageId: 'oc-user-empty',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'empty_assistant_turn',
        },
        diagnostics: ['empty_assistant_turn'],
      }));
      await configureOpenCodeBobDeliveryService({ svc, sendMessageToMember });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Work sync check for #task-1.',
          messageId: 'msg-empty-assistant-pending',
          replyRecipient: 'team-lead',
          actionMode: 'do',
          messageKind: 'member_work_sync_nudge',
          taskRefs: [
            {
              taskId: 'task-1',
              displayId: 'task-1',
              teamName: 'team-a',
            },
          ],
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'prompt_delivered_no_assistant_message',
        ledgerStatus: 'retry_scheduled',
        reason: 'member_work_sync_report_required',
      });
    });

    it('marks OpenCode delivery terminal after bounded recovery instead of leaving it pending', async () => {
      const svc = new TeamProvisioningService();
      const emptyResponseObservation = {
        state: 'empty_assistant_turn' as const,
        deliveredUserMessageId: 'oc-user-empty',
        assistantMessageId: 'oc-assistant-empty',
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: null,
        reason: 'empty_assistant_turn',
      };
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: emptyResponseObservation,
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        responseObservation: emptyResponseObservation,
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
          observeMessageDelivery,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      const deliver = () =>
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Please answer.',
          messageId: 'msg-max-attempts',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        });
      const forceDue = async () => {
        const ledgerPath = getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName: 'team-a',
          laneId: 'secondary:opencode:bob',
          fileName: 'opencode-prompt-delivery-ledger.json',
        });
        const ledgerEnvelope = JSON.parse(await fsPromises.readFile(ledgerPath, 'utf8')) as {
          data: Array<{ nextAttemptAt: string | null }>;
        };
        ledgerEnvelope.data[0].nextAttemptAt = '2000-01-01T00:00:00.000Z';
        await fsPromises.writeFile(ledgerPath, JSON.stringify(ledgerEnvelope, null, 2), 'utf8');
      };

      await expect(deliver()).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'empty_assistant_turn',
      });
      await forceDue();
      await expect(deliver()).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'empty_assistant_turn',
      });
      await forceDue();
      await expect(deliver()).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'empty_assistant_turn',
        ledgerStatus: 'retry_scheduled',
        reason: 'empty_assistant_turn',
      });
      await forceDue();
      await expect(deliver()).resolves.toMatchObject({
        delivered: false,
        accepted: true,
        responsePending: false,
        responseState: 'empty_assistant_turn',
        ledgerStatus: 'failed_terminal',
        reason: 'empty_assistant_turn',
      });
      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Late but valid answer.',
              timestamp: '2026-04-25T10:00:04.000Z',
              read: false,
              messageId: 'reply-after-terminal',
              relayOfMessageId: 'msg-max-attempts',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );
      await expect(deliver()).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: false,
        responseState: 'responded_visible_message',
        ledgerStatus: 'responded',
        visibleReplyMessageId: 'reply-after-terminal',
        visibleReplyCorrelation: 'relayOfMessageId',
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(4);
      expect(observeMessageDelivery).toHaveBeenCalledTimes(3);
    });

    it('queues newer OpenCode deliveries behind one active unresolved member delivery', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'pending' as const,
          deliveredUserMessageId: 'oc-user-pending',
          assistantMessageId: null,
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'assistant_response_pending',
        },
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'First prompt.',
          messageId: 'msg-active-old',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        responsePending: true,
        responseState: 'pending',
      });

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Second prompt.',
          messageId: 'msg-active-new',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:05.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: false,
        responsePending: true,
        queuedBehindMessageId: 'msg-active-old',
        reason: 'opencode_delivery_response_pending',
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(1);
    });

    it('unblocks newer OpenCode deliveries when the previous pending delivery now has visible proof', async () => {
      const svc = new TeamProvisioningService();
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        prePromptCursor: 'cursor-before',
        responseObservation: {
          state: 'empty_assistant_turn' as const,
          deliveredUserMessageId: 'oc-user-empty',
          assistantMessageId: 'oc-assistant-empty',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'empty_assistant_turn',
        },
        diagnostics: [],
      }));
      const observeMessageDelivery = vi.fn(async () => ({
        ok: true,
        providerId: 'opencode',
        memberName: 'bob',
        responseObservation: {
          state: 'empty_assistant_turn' as const,
          deliveredUserMessageId: 'oc-user-empty',
          assistantMessageId: 'oc-assistant-empty',
          toolCallNames: [],
          visibleMessageToolCallId: null,
          visibleReplyMessageId: null,
          visibleReplyCorrelation: null,
          latestAssistantPreview: null,
          reason: 'empty_assistant_turn',
        },
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
            observeMessageDelivery,
          } as any,
        ])
      );

      (svc as any).getTrackedRunId = vi.fn(() => 'run-1');
      (svc as any).provisioningRunByTeam.set('team-a', 'run-1');
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'team-a',
        runId: 'opencode-run-bob',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/repo',
      });
      await writeDefaultBobOpenCodeBootstrapEvidence();
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'First prompt.',
          messageId: 'msg-active-old',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:00.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'empty_assistant_turn',
      });

      const inboxDir = path.join(tempTeamsBase, 'team-a', 'inboxes');
      await fsPromises.mkdir(inboxDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(inboxDir, 'user.json'),
        `${JSON.stringify(
          [
            {
              from: 'bob',
              to: 'user',
              text: 'Delayed but sufficient answer.',
              timestamp: '2026-04-25T10:00:03.000Z',
              read: false,
              messageId: 'reply-old-1',
              relayOfMessageId: 'msg-active-old',
              source: 'runtime_delivery',
            },
          ],
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage('team-a', {
          memberName: 'bob',
          text: 'Second prompt.',
          messageId: 'msg-active-new',
          replyRecipient: 'user',
          actionMode: 'ask',
          source: 'watcher',
          inboxTimestamp: '2026-04-25T10:00:05.000Z',
        })
      ).resolves.toMatchObject({
        delivered: true,
        accepted: true,
        responsePending: true,
        responseState: 'empty_assistant_turn',
      });
      expect(sendMessageToMember).toHaveBeenCalledTimes(2);
      expect(observeMessageDelivery).not.toHaveBeenCalled();
    });

    it('uses lane-scoped manifest activeRunId for OpenCode member delivery after restart', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_after_restart',
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        state: 'active',
      });
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'opencode-run-durable',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName,
            memberName: 'bob',
            laneId,
            runId: 'opencode-run-durable',
            source: 'runtime_bootstrap_checkin',
          },
        ],
      });

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'hello after restart',
          messageId: 'msg-after-restart',
        })
      ).resolves.toEqual({
        delivered: true,
        diagnostics: [],
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-durable',
          teamName,
          laneId,
          memberName: 'bob',
          cwd: '/repo',
          text: 'hello after restart',
          messageId: 'msg-after-restart',
        })
      );
    });

    it('prefers live secondary lane runId over the primary tracked runId for OpenCode member delivery', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_live_lane',
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ])
      );

      (svc as any).aliveRunByTeam.set(teamName, 'primary-run');
      (svc as any).runs.set('primary-run', {
        runId: 'primary-run',
        teamName,
        processKilled: false,
        cancelRequested: false,
        progress: { state: 'ready' },
        request: { providerId: 'codex', cwd: '/repo' },
        mixedSecondaryLanes: [
          {
            laneId,
            providerId: 'opencode',
            member: { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
            runId: 'opencode-run-live',
            state: 'finished',
            result: {
              members: {
                bob: {
                  bootstrapConfirmed: true,
                  launchState: 'confirmed_alive',
                  sessionId: 'oc-session-bob',
                },
              },
            },
            warnings: [],
            diagnostics: [],
          },
        ],
      });
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'opencode-run-live',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName,
            memberName: 'bob',
            laneId,
            runId: 'opencode-run-live',
            source: 'app_managed_bootstrap',
          },
        ],
      });

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'hello live lane',
          messageId: 'msg-live-lane',
        })
      ).resolves.toMatchObject({
        delivered: true,
        diagnostics: [],
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-live',
          teamName,
          laneId,
          memberName: 'bob',
        })
      );
      expect(sendMessageToMember).not.toHaveBeenCalledWith(
        expect.objectContaining({ runId: 'primary-run' })
      );
    });

    it('does not trust live OpenCode secondary bootstrap state without committed evidence', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async () => ({
        ok: true,
        providerId: 'opencode',
        memberName: 'bob',
        sessionId: 'oc-session-bob',
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ])
      );
      (svc as any).aliveRunByTeam.set(teamName, 'primary-run');
      (svc as any).runs.set('primary-run', {
        runId: 'primary-run',
        teamName,
        processKilled: false,
        cancelRequested: false,
        progress: { state: 'ready' },
        request: { providerId: 'codex', cwd: '/repo' },
        mixedSecondaryLanes: [
          {
            laneId,
            providerId: 'opencode',
            member: { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
            runId: 'opencode-run-live',
            state: 'finished',
            result: {
              members: {
                bob: {
                  bootstrapConfirmed: true,
                  launchState: 'confirmed_alive',
                  sessionId: 'oc-session-bob',
                },
              },
            },
            warnings: [],
            diagnostics: [],
          },
        ],
      });
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'must wait for committed bootstrap evidence',
          messageId: 'msg-live-lane-without-committed-evidence',
        })
      ).resolves.toMatchObject({
        delivered: false,
        reason: 'opencode_runtime_not_active',
        diagnostics: [
          expect.stringContaining('OpenCode runtime bootstrap is not confirmed for bob'),
        ],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
    });

    it('blocks OpenCode secondary delivery when runtime session exists but bootstrap did not check in', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        diagnostics: [],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: vi.fn(),
            reconcile: vi.fn(),
            stop: vi.fn(),
            sendMessageToMember,
          } as any,
        ])
      );
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          { name: 'bob', providerId: 'opencode', model: 'opencode/minimax-m2.5-free' },
        ]),
      };
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        state: 'active',
      });
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'opencode-run-pending-bootstrap',
        sessions: [],
      });

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'must wait for bootstrap',
          messageId: 'msg-before-bootstrap-checkin',
        })
      ).resolves.toMatchObject({
        delivered: false,
        reason: 'opencode_runtime_not_active',
        diagnostics: [
          expect.stringContaining('OpenCode runtime bootstrap is not confirmed for bob'),
        ],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {
          [laneId]: {
            state: 'active',
          },
        },
      });
    });

    it('rejects stale active lane manifest without runtime evidence before delivery', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        state: 'active',
      });
      const manifestPath = getOpenCodeRuntimeManifestPath(tempTeamsBase, teamName, laneId);
      await fsPromises.mkdir(path.dirname(manifestPath), { recursive: true });
      await fsPromises.writeFile(
        manifestPath,
        `${JSON.stringify(
          {
            ...createDefaultRuntimeStoreManifest(teamName, '2026-04-22T12:00:00.000Z'),
            activeRunId: 'opencode-run-stale-empty',
          },
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'must not deliver to empty durable lane',
          messageId: 'msg-stale-empty-manifest',
        })
      ).resolves.toMatchObject({
        delivered: false,
        reason: 'opencode_runtime_not_active',
        diagnostics: [
          expect.stringContaining('runtime manifest has no committed runtime evidence'),
        ],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {
          [laneId]: {
            state: 'degraded',
          },
        },
      });
    });

    it('waits for OpenCode runtime evidence before delivering to a fresh active secondary lane', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        state: 'active',
      });
      const now = new Date().toISOString();
      const manifestPath = getOpenCodeRuntimeManifestPath(tempTeamsBase, teamName, laneId);
      await fsPromises.mkdir(path.dirname(manifestPath), { recursive: true });
      await fsPromises.writeFile(
        manifestPath,
        `${JSON.stringify(
          {
            ...createDefaultRuntimeStoreManifest(teamName, now),
            activeRunId: 'opencode-run-starting-empty',
          },
          null,
          2
        )}\n`,
        'utf8'
      );

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'wait until runtime check-in',
          messageId: 'msg-fresh-empty-manifest',
        })
      ).resolves.toMatchObject({
        delivered: false,
        reason: 'opencode_runtime_not_active',
        diagnostics: [
          expect.stringContaining('OpenCode runtime bootstrap evidence is not ready for bob'),
        ],
      });
      expect(sendMessageToMember).not.toHaveBeenCalled();
      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {
          [laneId]: {
            state: 'active',
          },
        },
      });
    });

    it('falls back to lane manifest when a tracked primary run lacks the secondary lane snapshot', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'team-a';
      const laneId = 'secondary:opencode:bob';
      const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
        ok: true,
        providerId: 'opencode',
        memberName: String(input.memberName),
        sessionId: 'oc-session-bob',
        runtimePromptMessageId: 'msg_prompt_manifest_fallback',
        diagnostics: [],
      }));
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      (svc as any).resolveDeliverableTrackedRuntimeRunId = vi.fn(() => 'run-1');
      (svc as any).runs.set('run-1', {
        mixedSecondaryLanes: [],
      });
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          projectPath: '/repo',
          members: [
            { name: 'team-lead', providerId: 'codex', model: 'gpt-5.4' },
            { name: 'bob', providerId: 'opencode', model: 'minimax-m2.5-free' },
          ],
        })),
      };
      (svc as any).teamMetaStore = {
        getMeta: vi.fn(async () => ({
          launchIdentity: { providerId: 'codex' },
          providerId: 'codex',
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            providerId: 'opencode',
            model: 'opencode/minimax-m2.5-free',
          },
        ]),
      };
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        state: 'active',
      });
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'opencode-run-from-manifest',
        sessions: [
          {
            id: 'oc-session-bob',
            teamName,
            memberName: 'bob',
            laneId,
            runId: 'opencode-run-from-manifest',
            source: 'runtime_bootstrap_checkin',
          },
        ],
      });

      await expect(
        svc.deliverOpenCodeMemberMessage(teamName, {
          memberName: 'bob',
          text: 'hello via manifest fallback',
          messageId: 'msg-manifest-fallback',
        })
      ).resolves.toEqual({
        delivered: true,
        diagnostics: [],
      });
      expect(sendMessageToMember).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-from-manifest',
          teamName,
          laneId,
          memberName: 'bob',
          cwd: '/repo',
          text: 'hello via manifest fallback',
          messageId: 'msg-manifest-fallback',
        })
      );
    });

    it('marks an OpenCode secondary lane degraded when readiness fails before runtime materializes', async () => {
      const teamName = 'mixed-prelaunch-failure';
      const svc = new TeamProvisioningService();
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
        runId: String(input.runId),
        teamName: String(input.teamName),
        launchPhase: 'finished',
        teamLaunchState: 'partial_failure',
        members: {
          bob: {
            memberName: 'bob',
            providerId: 'opencode',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'unknown_error',
            diagnostics: [
              'OpenCode readiness bridge failed: timeout: OpenCode bridge command timed out',
              'opencode_bridge_unknown_outcome: OpenCode bridge command timed out',
            ],
          },
        },
        warnings: [],
        diagnostics: [
          'OpenCode readiness bridge failed: timeout: OpenCode bridge command timed out',
        ],
      }));

      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: adapterLaunch,
          reconcile: vi.fn(),
          stop: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);
      stubOpenCodeAppManagedLaunchPrompt(svc);

      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };

      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['alice'],
      });
      run.isLaunch = true;
      run.request = {
        teamName,
        cwd: '/tmp/mixed-prelaunch-failure',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
        skipPermissions: true,
      };
      run.effectiveMembers = [
        {
          name: 'alice',
          role: 'Reviewer',
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'high',
        },
      ];
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];
      const manifestPath = getOpenCodeRuntimeManifestPath(
        tempTeamsBase,
        teamName,
        'secondary:opencode:bob'
      );
      await fsPromises.mkdir(path.dirname(manifestPath), { recursive: true });
      await fsPromises.writeFile(
        manifestPath,
        `${JSON.stringify(
          {
            ...createDefaultRuntimeStoreManifest(teamName, '2026-04-22T10:00:00.000Z'),
            activeRunId: 'stale-run',
            highWatermark: 2,
          },
          null,
          2
        )}\n`,
        'utf8'
      );

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(
        async () => {
          expect(adapterLaunch).toHaveBeenCalledTimes(1);
          const launchInput = adapterLaunch.mock.calls[0]?.[0] as { runId?: string } | undefined;
          expect(launchInput?.runId).toEqual(expect.any(String));
          await expect(
            new OpenCodeRuntimeManifestEvidenceReader({ teamsBasePath: tempTeamsBase }).read(
              teamName,
              'secondary:opencode:bob'
            )
          ).resolves.toMatchObject({
            activeRunId: launchInput?.runId,
            highWatermark: 0,
          });
          await expect(
            readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)
          ).resolves.toMatchObject({
            lanes: {
              'secondary:opencode:bob': {
                state: 'degraded',
                diagnostics: expect.arrayContaining([
                  'OpenCode readiness bridge failed: timeout: OpenCode bridge command timed out',
                  expect.stringMatching(
                    /^OpenCode secondary lane timing: member=bob queueWaitMs=\d+ms launchMs=\d+ms totalMs=\d+ms$/
                  ),
                ]),
              },
            },
          });
        },
        { timeout: 5000 }
      );
    });

    it('does not keep an OpenCode secondary lane active from prompt acceptance without runtime evidence', async () => {
      const teamName = 'mixed-accepted-without-runtime-evidence';
      const svc = new TeamProvisioningService();
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
        runId: String(input.runId),
        teamName: String(input.teamName),
        launchPhase: 'finished',
        teamLaunchState: 'partial_failure',
        members: {
          tom: {
            memberName: 'tom',
            providerId: 'opencode',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'OpenCode bridge reported member launch failure',
            diagnostics: ['runtime_bootstrap_checkin failed: MCP Not connected'],
          },
        },
        warnings: [],
        diagnostics: ['OpenCode bridge reported member launch failure'],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );
      stubOpenCodeAppManagedLaunchPrompt(svc);
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };

      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['bob'],
      });
      run.isLaunch = true;
      run.request = {
        teamName,
        cwd: '/tmp/mixed-accepted-without-runtime-evidence',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
        skipPermissions: true,
      };
      run.effectiveMembers = [
        {
          name: 'bob',
          role: 'Developer',
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'high',
        },
      ];
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:tom',
          providerId: 'opencode',
          member: {
            name: 'tom',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(
        async () => {
          await expect(
            readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)
          ).resolves.toMatchObject({
            lanes: {
              'secondary:opencode:tom': {
                state: 'degraded',
                diagnostics: expect.not.arrayContaining([
                  'opencode_bootstrap_pending_after_materialized_session',
                ]),
              },
            },
          });
          expect(run.mixedSecondaryLanes?.[0]?.result?.members.tom).toMatchObject({
            launchState: 'failed_to_start',
            hardFailure: true,
          });
        },
        { timeout: 5000 }
      );
    });

    it('keeps an OpenCode secondary lane active when bootstrap is pending after runtime materializes', async () => {
      const teamName = 'mixed-runtime-materialized-failure';
      const svc = new TeamProvisioningService();
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
        runId: String(input.runId),
        teamName: String(input.teamName),
        launchPhase: 'active',
        teamLaunchState: 'partial_failure',
        members: {
          tom: {
            memberName: 'tom',
            providerId: 'opencode',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'OpenCode bridge reported member launch failure',
            sessionId: 'ses_tom_materialized_without_bootstrap',
            runtimePid: 71388,
            livenessKind: 'runtime_process',
            diagnostics: [
              'OpenCode bootstrap MCP did not complete required tools before assistant response: runtime_bootstrap_checkin, member_briefing',
            ],
          },
        },
        warnings: [],
        diagnostics: ['OpenCode bridge reported member launch failure'],
      }));

      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );
      stubOpenCodeAppManagedLaunchPrompt(svc);
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
        clear: vi.fn(async () => {}),
      };

      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['bob'],
      });
      run.isLaunch = true;
      run.request = {
        teamName,
        cwd: '/tmp/mixed-runtime-materialized-failure',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
        skipPermissions: true,
      };
      run.effectiveMembers = [
        {
          name: 'bob',
          role: 'Developer',
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'high',
        },
      ];
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:tom',
          providerId: 'opencode',
          member: {
            name: 'tom',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(
        async () => {
          expect(adapterLaunch).toHaveBeenCalledTimes(1);
          const launchInput = adapterLaunch.mock.calls[0]?.[0] as { runId?: string } | undefined;
          await expect(
            new OpenCodeRuntimeManifestEvidenceReader({ teamsBasePath: tempTeamsBase }).read(
              teamName,
              'secondary:opencode:tom'
            )
          ).resolves.toMatchObject({
            activeRunId: launchInput?.runId,
          });
          await expect(
            readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)
          ).resolves.toMatchObject({
            lanes: {
              'secondary:opencode:tom': {
                state: 'active',
                diagnostics: expect.arrayContaining([
                  'OpenCode bridge reported member launch failure',
                  'OpenCode bootstrap MCP did not complete required tools before assistant response: runtime_bootstrap_checkin, member_briefing',
                  'opencode_bootstrap_pending_after_materialized_session',
                ]),
              },
            },
          });
          expect(run.mixedSecondaryLanes?.[0]?.result?.members.tom).toMatchObject({
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            livenessKind: 'runtime_process',
          });
          expect(run.mixedSecondaryLanes?.[0]?.result?.teamLaunchState).toBe('partial_pending');
        },
        { timeout: 5000 }
      );
    });

    it('starts queued OpenCode secondary lanes sequentially without blocking launch progress', async () => {
      const svc = new TeamProvisioningService();
      const registry = new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
        } as any,
      ]);
      svc.setRuntimeAdapterRegistry(registry);

      const persistLaunchStateSnapshot = vi
        .spyOn(svc as any, 'persistLaunchStateSnapshot')
        .mockResolvedValue(null);

      let resolveFirstLaunch: () => void = () => {};
      const firstLaunch = new Promise<void>((resolve) => {
        resolveFirstLaunch = resolve;
      });
      const launchSingleMixedSecondaryLane = vi
        .spyOn(svc as any, 'launchSingleMixedSecondaryLane')
        .mockImplementationOnce(async () => {
          await firstLaunch;
        })
        .mockResolvedValueOnce(undefined)
        .mockResolvedValueOnce(undefined);

      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
      });
      run.isLaunch = true;
      run.request = {
        teamName: 'mixed-team',
        cwd: '/tmp/mixed-team',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'high',
        skipPermissions: true,
      };
      run.effectiveMembers = [
        {
          name: 'alice',
          role: 'Reviewer',
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'high',
        },
      ];
      run.mixedSecondaryLanes = [
        {
          laneId: 'secondary:opencode:tom',
          providerId: 'opencode',
          member: {
            name: 'tom',
            role: 'Developer',
            providerId: 'opencode',
            model: 'nemotron-3-super-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
        {
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          member: {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
        {
          laneId: 'secondary:opencode:jack',
          providerId: 'opencode',
          member: {
            name: 'jack',
            role: 'Developer',
            providerId: 'opencode',
            model: 'ling-2.6-flash-free',
            effort: 'medium',
          },
          runId: null,
          state: 'queued',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ];

      const resultPromise = (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await Promise.resolve();
      await Promise.resolve();

      expect(launchSingleMixedSecondaryLane).toHaveBeenCalledTimes(1);
      expect(run.mixedSecondaryLanes.map((lane: { state: string }) => lane.state)).toEqual([
        'launching',
        'queued',
        'queued',
      ]);

      await expect(resultPromise).resolves.toBeNull();
      expect(persistLaunchStateSnapshot).toHaveBeenCalledTimes(1);

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await Promise.resolve();
      expect(launchSingleMixedSecondaryLane).toHaveBeenCalledTimes(1);
      expect(persistLaunchStateSnapshot).toHaveBeenCalledTimes(2);

      resolveFirstLaunch();
      await Promise.resolve();
      await vi.waitFor(() => expect(launchSingleMixedSecondaryLane).toHaveBeenCalledTimes(3));
    });

    it('preserves mixed lane metadata when OpenCode runtime liveness updates a secondary lane member', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['alice', 'bob'],
        bootstrapExpectedMembers: ['alice'],
        members: {
          alice: {
            name: 'alice',
            providerId: 'codex' as const,
            laneId: 'primary',
            laneKind: 'primary' as const,
            laneOwnerProviderId: 'codex' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            model: 'minimax-m2.5-free',
            effort: 'medium' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchIdentity: {
              providerId: 'opencode' as const,
              providerBackendId: null,
              selectedModel: 'minimax-m2.5-free',
              selectedModelKind: 'explicit' as const,
              resolvedLaunchModel: 'minimax-m2.5-free',
              catalogId: 'minimax-m2.5-free',
              catalogSource: 'runtime' as const,
              catalogFetchedAt: '2026-04-22T12:00:00.000Z',
              selectedEffort: 'medium' as const,
              resolvedEffort: 'medium' as const,
              selectedFastMode: null,
              resolvedFastMode: null,
              fastResolutionReason: null,
            },
            launchState: 'runtime_pending_bootstrap' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
        },
        teamLaunchState: 'partial_pending' as const,
      };
      const write = vi.fn(async () => {});

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write,
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'run-member-spawn-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: ['native heartbeat'],
        reason: 'OpenCode runtime heartbeat accepted',
      });

      expect(write).toHaveBeenCalledTimes(1);
      const writtenSnapshot = (
        write.mock.calls[0] as unknown as [string, Record<string, unknown>] | undefined
      )?.[1] as { members?: Record<string, unknown> } | undefined;
      expect(writtenSnapshot?.members?.bob).toMatchObject({
        name: 'bob',
        providerId: 'opencode',
        model: 'minimax-m2.5-free',
        effort: 'medium',
        laneId: 'secondary:opencode:bob',
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchIdentity: {
          providerId: 'opencode',
          selectedModel: 'minimax-m2.5-free',
          resolvedLaunchModel: 'minimax-m2.5-free',
        },
        launchState: 'confirmed_alive',
        runtimeAlive: true,
        bootstrapConfirmed: true,
        runtimeRunId: 'run-member-spawn-1',
        runtimeSessionId: 'session-bob',
      });
    });

    it('persists sanitized runtime tool metadata diagnostics on OpenCode liveness updates', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'runtime_pending_bootstrap' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            diagnostics: ['existing diagnostic'],
          },
        },
        summary: {
          confirmedCount: 0,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
        },
        teamLaunchState: 'partial_pending' as const,
      };
      const write = vi.fn(async () => {});

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write,
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'run-member-spawn-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: ['native heartbeat'],
        metadata: {
          runtimePid: 4321,
          processCommand: 'opencode runtime --token super-secret --safe ok',
          runtimeVersion: '1.2.3',
          hostPid: 987,
          cwd: '/tmp/project',
        },
        reason: 'OpenCode runtime heartbeat accepted',
      });

      expect(write).toHaveBeenCalledTimes(1);
      const writtenSnapshot = (
        write.mock.calls[0] as unknown as [string, Record<string, unknown>] | undefined
      )?.[1] as { members?: Record<string, { diagnostics?: string[] }> } | undefined;
      const diagnostics = writtenSnapshot?.members?.bob?.diagnostics ?? [];
      expect(diagnostics).toEqual(
        expect.arrayContaining([
          'existing diagnostic',
          'native heartbeat',
          'runtime pid: 4321',
          'runtime process command: opencode runtime --token [redacted] --safe ok',
          'runtime version: 1.2.3',
          'runtime host pid: 987',
          'runtime cwd: /tmp/project',
          'OpenCode runtime heartbeat accepted',
        ])
      );
      expect(diagnostics.join('\n')).not.toContain('super-secret');
    });

    it('emits member-spawn when OpenCode runtime liveness first confirms a pending member', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'runtime_pending_bootstrap' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            runtimeRunId: 'run-member-spawn-1',
            runtimeSessionId: 'session-bob',
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 0,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
        },
        teamLaunchState: 'partial_pending' as const,
      };
      const events: Array<{ type: string; teamName: string; runId?: string; detail?: string }> = [];

      svc.setTeamChangeEmitter((event) => {
        events.push(event);
      });
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'run-member-spawn-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: ['native heartbeat'],
        metadata: { runtimePid: 4321 },
        reason: 'OpenCode runtime heartbeat accepted',
      });

      expect(events).toEqual([
        {
          type: 'member-spawn',
          teamName: 'mixed-team',
          runId: 'run-member-spawn-1',
          detail: 'bob',
        },
      ]);
    });

    it('does not emit member-spawn for routine OpenCode heartbeat from the same live session', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimePid: 4321,
            runtimeRunId: 'run-member-spawn-1',
            runtimeSessionId: 'session-bob',
            livenessKind: 'confirmed_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const events: Array<{ type: string; teamName: string; runId?: string; detail?: string }> = [];
      const write = vi.fn(async () => {});

      svc.setTeamChangeEmitter((event) => {
        events.push(event);
      });
      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write,
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'run-member-spawn-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: ['native heartbeat'],
        metadata: { runtimePid: 4321 },
        reason: 'OpenCode runtime heartbeat accepted',
      });

      expect(write).toHaveBeenCalledTimes(1);
      expect(events).toEqual([]);
    });

    it('does not carry a stale OpenCode runtime pid into a fresh runtime run check-in', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimePid: 1111,
            runtimeRunId: 'opencode-run-old',
            runtimeSessionId: 'session-bob-old',
            livenessKind: 'confirmed_bootstrap' as const,
            pidSource: 'runtime_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const write = vi.fn(async () => {});

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write,
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'opencode-run-new',
        memberName: 'bob',
        runtimeSessionId: 'session-bob-new',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: [],
        reason: 'OpenCode runtime bootstrap check-in accepted',
      });

      const writtenSnapshot = (
        write.mock.calls[0] as unknown as [string, Record<string, unknown>] | undefined
      )?.[1] as { members?: Record<string, Record<string, unknown>> } | undefined;
      expect(writtenSnapshot?.members?.bob).toMatchObject({
        runtimeRunId: 'opencode-run-new',
        runtimeSessionId: 'session-bob-new',
        launchState: 'confirmed_alive',
      });
      expect(writtenSnapshot?.members?.bob?.runtimePid).toBeUndefined();
      expect(writtenSnapshot?.members?.bob?.pidSource).toBeUndefined();
    });

    it('preserves richer persisted expectedMembers when OpenCode runtime liveness updates a stale snapshot', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          alice: {
            name: 'alice',
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'runtime_pending_bootstrap' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 1,
          failedCount: 0,
          runtimeAlivePendingCount: 1,
        },
        teamLaunchState: 'partial_pending' as const,
      };
      const write = vi.fn(async () => {});

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write,
      };

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName: 'mixed-team',
        runId: 'run-member-spawn-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: ['native heartbeat'],
        reason: 'OpenCode runtime heartbeat accepted',
      });

      expect(write).toHaveBeenCalledTimes(1);
      const writtenSnapshot = (
        write.mock.calls[0] as unknown as [string, Record<string, unknown>] | undefined
      )?.[1] as { expectedMembers?: string[] } | undefined;
      expect(writtenSnapshot?.expectedMembers).toEqual(['bob', 'alice']);
    });

    it('accepts duplicate OpenCode bootstrap check-ins for the same runtime session and refreshes liveness', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimeRunId: 'opencode-run-1',
            runtimeSessionId: 'session-bob',
            livenessKind: 'confirmed_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const updateLiveness = vi.spyOn(svc as any, 'updateOpenCodeRuntimeMemberLiveness');

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
      };
      (svc as any).resolveOpenCodeRuntimeLaneId = vi.fn(async () => 'secondary:opencode:bob');
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async () => {});

      const ack = await svc.recordOpenCodeRuntimeBootstrapCheckin({
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
        memberName: 'bob',
        runtimeSessionId: 'session-bob',
        observedAt: '2026-04-22T12:05:00.000Z',
      });

      expect(ack).toMatchObject({
        ok: true,
        state: 'accepted',
        diagnostics: ['opencode_bootstrap_checkin_duplicate_accepted'],
        runtimeSessionId: 'session-bob',
      });
      expect(updateLiveness).toHaveBeenCalledWith(
        expect.objectContaining({
          teamName: 'mixed-team',
          runId: 'opencode-run-1',
          memberName: 'bob',
          runtimeSessionId: 'session-bob',
        })
      );
    });

    it('keeps OpenCode bootstrap check-in allowlist on verified config reads', async () => {
      const getConfig = vi.fn(async () => ({
        teamName: 'mixed-team',
        members: [{ name: 'bob', providerId: 'opencode' }],
      }));
      const getConfigSnapshot = vi.fn(async () => {
        throw new Error('snapshot config read should not be used for bootstrap check-in guards');
      });
      const svc = new TeamProvisioningService({
        getConfig,
        getConfigSnapshot,
      } as any);
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => []),
      };

      await expect(
        (svc as any).assertOpenCodeRuntimeMemberCheckinAllowed({
          teamName: 'mixed-team',
          memberName: 'bob',
        })
      ).resolves.toBeUndefined();

      expect(getConfig).toHaveBeenCalledWith('mixed-team');
      expect(getConfigSnapshot).not.toHaveBeenCalled();
    });

    it('rejects duplicate OpenCode bootstrap check-ins for members removed after the first check-in', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimeRunId: 'opencode-run-1',
            runtimeSessionId: 'session-bob',
            livenessKind: 'confirmed_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const updateLiveness = vi.spyOn(svc as any, 'updateOpenCodeRuntimeMemberLiveness');

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
      };
      (svc as any).resolveOpenCodeRuntimeLaneId = vi.fn(async () => 'secondary:opencode:bob');
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async () => {});
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          teamName: 'mixed-team',
          members: [{ name: 'bob', providerId: 'opencode', removedAt: 123 }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => []),
      };

      await expect(
        svc.recordOpenCodeRuntimeBootstrapCheckin({
          teamName: 'mixed-team',
          runId: 'opencode-run-1',
          memberName: 'bob',
          runtimeSessionId: 'session-bob',
          observedAt: '2026-04-22T12:05:00.000Z',
        })
      ).rejects.toMatchObject({
        name: 'RuntimeStaleEvidenceError',
      });
      expect(updateLiveness).not.toHaveBeenCalled();
    });

    it('rejects conflicting OpenCode bootstrap check-ins for an already confirmed runtime session', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimeRunId: 'opencode-run-1',
            runtimeSessionId: 'session-bob-1',
            livenessKind: 'confirmed_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const updateLiveness = vi.spyOn(svc as any, 'updateOpenCodeRuntimeMemberLiveness');

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
      };
      (svc as any).resolveOpenCodeRuntimeLaneId = vi.fn(async () => 'secondary:opencode:bob');
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async () => {});

      await expect(
        svc.recordOpenCodeRuntimeBootstrapCheckin({
          teamName: 'mixed-team',
          runId: 'opencode-run-1',
          memberName: 'bob',
          runtimeSessionId: 'session-bob-2',
          observedAt: '2026-04-22T12:05:00.000Z',
        })
      ).rejects.toMatchObject({
        name: 'RuntimeStaleEvidenceError',
        message: expect.stringContaining('opencode_bootstrap_checkin_session_conflict'),
      });
      expect(updateLiveness).not.toHaveBeenCalled();
    });

    it('does not let stale confirmed OpenCode evidence from an older run block a fresh check-in', async () => {
      const svc = new TeamProvisioningService();
      const previousSnapshot = {
        version: 2 as const,
        teamName: 'mixed-team',
        updatedAt: '2026-04-22T12:00:00.000Z',
        launchPhase: 'active' as const,
        expectedMembers: ['bob'],
        members: {
          bob: {
            name: 'bob',
            providerId: 'opencode' as const,
            laneId: 'secondary:opencode:bob',
            laneKind: 'secondary' as const,
            laneOwnerProviderId: 'opencode' as const,
            launchState: 'confirmed_alive' as const,
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: true,
            hardFailure: false,
            runtimeRunId: 'opencode-run-old',
            runtimeSessionId: 'session-bob-old',
            livenessKind: 'confirmed_bootstrap' as const,
            lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
          },
        },
        summary: {
          confirmedCount: 1,
          pendingCount: 0,
          failedCount: 0,
          runtimeAlivePendingCount: 0,
        },
        teamLaunchState: 'ready' as const,
      };
      const updateLiveness = vi
        .spyOn(svc as any, 'updateOpenCodeRuntimeMemberLiveness')
        .mockResolvedValue(undefined);

      (svc as any).launchStateStore = {
        read: vi.fn(async () => previousSnapshot),
        write: vi.fn(async () => {}),
      };
      (svc as any).resolveOpenCodeRuntimeLaneId = vi.fn(async () => 'secondary:opencode:bob');
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async () => {});
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          teamName: 'mixed-team',
          members: [{ name: 'bob', providerId: 'opencode' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => []),
      };

      await expect(
        svc.recordOpenCodeRuntimeBootstrapCheckin({
          teamName: 'mixed-team',
          runId: 'opencode-run-new',
          memberName: 'bob',
          runtimeSessionId: 'session-bob-new',
          observedAt: '2026-04-22T12:05:00.000Z',
        })
      ).resolves.toMatchObject({
        ok: true,
        state: 'accepted',
        runtimeSessionId: 'session-bob-new',
      });
      expect(updateLiveness).toHaveBeenCalledWith(
        expect.objectContaining({
          runId: 'opencode-run-new',
          runtimeSessionId: 'session-bob-new',
        })
      );
    });

    it('rejects OpenCode bootstrap check-ins for removed members before writing runtime evidence', async () => {
      const svc = new TeamProvisioningService();
      const updateLiveness = vi.spyOn(svc as any, 'updateOpenCodeRuntimeMemberLiveness');

      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
        write: vi.fn(async () => {}),
      };
      (svc as any).resolveOpenCodeRuntimeLaneId = vi.fn(async () => 'secondary:opencode:bob');
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async () => {});
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          teamName: 'mixed-team',
          members: [{ name: 'bob', providerId: 'opencode', removedAt: 123 }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => []),
      };

      await expect(
        svc.recordOpenCodeRuntimeBootstrapCheckin({
          teamName: 'mixed-team',
          runId: 'opencode-run-1',
          memberName: 'bob',
          runtimeSessionId: 'session-bob',
        })
      ).rejects.toMatchObject({
        name: 'RuntimeStaleEvidenceError',
      });
      expect(updateLiveness).not.toHaveBeenCalled();
    });

    it('accepts secondary OpenCode lane evidence using the lane run id instead of the lead run id', async () => {
      const svc = new TeamProvisioningService();

      (svc as any).aliveRunByTeam.set('mixed-team', 'lead-run');
      (svc as any).runs.set('lead-run', {
        runId: 'lead-run',
        teamName: 'mixed-team',
        request: {
          providerId: 'codex',
        },
      });
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/tmp/mixed-team',
      });

      await expect(
        (svc as any).assertOpenCodeRuntimeEvidenceAccepted({
          teamName: 'mixed-team',
          runId: 'opencode-run-1',
          laneId: 'secondary:opencode:bob',
          evidenceKind: 'heartbeat',
        })
      ).resolves.toBeUndefined();
    });

    it('commits lane-scoped OpenCode session evidence when bootstrap check-in is accepted', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'mixed-team';
      const laneId = 'secondary:opencode:bob';
      const runId = 'opencode-run-1';
      const teamDir = path.join(tempTeamsBase, teamName);
      await fsPromises.mkdir(teamDir, { recursive: true });
      await fsPromises.writeFile(
        path.join(teamDir, 'config.json'),
        `${JSON.stringify({
          name: teamName,
          projectPath: '/tmp/mixed-team',
          members: [
            { name: 'team-lead', providerId: 'codex' },
            { name: 'bob', providerId: 'opencode' },
          ],
        })}\n`,
        'utf8'
      );

      (svc as any).aliveRunByTeam.set(teamName, 'lead-run');
      (svc as any).runs.set('lead-run', {
        runId: 'lead-run',
        teamName,
        request: {
          providerId: 'codex',
        },
      });
      (svc as any).setSecondaryRuntimeRun({
        teamName,
        runId,
        providerId: 'opencode',
        laneId,
        memberName: 'bob',
        cwd: '/tmp/mixed-team',
      });

      await expect(
        svc.recordOpenCodeRuntimeBootstrapCheckin({
          teamName,
          runId,
          memberName: 'bob',
          runtimeSessionId: 'session-bob',
          observedAt: '2026-04-22T12:05:00.000Z',
        })
      ).resolves.toMatchObject({
        ok: true,
        state: 'accepted',
        runtimeSessionId: 'session-bob',
      });

      const manifestPath = getOpenCodeRuntimeManifestPath(tempTeamsBase, teamName, laneId);
      const manifest = JSON.parse(await fsPromises.readFile(manifestPath, 'utf8')) as {
        data: { entries: Array<{ schemaName: string; relativePath: string; runId: string }> };
      };
      expect(manifest.data.entries).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            schemaName: 'opencode.sessionStore',
            relativePath: 'opencode-sessions.json',
            runId,
          }),
        ])
      );
      const sessionStore = JSON.parse(
        await fsPromises.readFile(
          path.join(path.dirname(manifestPath), 'opencode-sessions.json'),
          'utf8'
        )
      ) as {
        data: {
          sessions: Array<{ id: string; memberName: string; runId: string; laneId: string }>;
        };
      };
      expect(sessionStore.data.sessions).toEqual([
        expect.objectContaining({
          id: 'session-bob',
          memberName: 'bob',
          runId,
          laneId,
        }),
      ]);
    });

    it('updates the live mixed OpenCode lane when bootstrap check-in arrives after launch command completion', async () => {
      const svc = new TeamProvisioningService();
      const persistLaunchStateSnapshot = vi.spyOn(svc as any, 'persistLaunchStateSnapshot');
      const teamName = 'mixed-live-checkin-team';
      const laneId = 'secondary:opencode:tom';
      const runId = 'opencode-run-tom';
      const run = createMemberSpawnRun({
        runId: 'lead-run',
        teamName,
        expectedMembers: ['bob', 'tom'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'confirmed_alive',
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
            }),
          ],
          [
            'tom',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
            }),
          ],
        ]),
      });
      Object.assign(run, {
        isLaunch: true,
        request: { providerId: 'codex', members: [] },
        effectiveMembers: [{ name: 'bob', providerId: 'codex', model: 'gpt-5.5' }],
        allEffectiveMembers: [
          { name: 'bob', providerId: 'codex', model: 'gpt-5.5' },
          { name: 'tom', providerId: 'opencode', model: 'openrouter/minimax/minimax-m2.5' },
        ],
        mixedSecondaryLanes: [
          {
            laneId,
            providerId: 'opencode',
            member: {
              name: 'tom',
              providerId: 'opencode',
              model: 'openrouter/minimax/minimax-m2.5',
            },
            runId,
            state: 'finished',
            result: {
              runId,
              teamName,
              launchPhase: 'active',
              teamLaunchState: 'partial_pending',
              members: {
                tom: {
                  memberName: 'tom',
                  providerId: 'opencode',
                  launchState: 'runtime_pending_bootstrap',
                  agentToolAccepted: true,
                  runtimeAlive: false,
                  bootstrapConfirmed: false,
                  hardFailure: false,
                  livenessKind: 'registered_only',
                  diagnostics: ['registered runtime metadata without live process'],
                },
              },
              warnings: [],
              diagnostics: ['registered runtime metadata without live process'],
            },
            warnings: [],
            diagnostics: ['registered runtime metadata without live process'],
          },
        ],
      });
      (svc as any).aliveRunByTeam.set(teamName, 'lead-run');
      (svc as any).runs.set('lead-run', run);

      await (svc as any).updateOpenCodeRuntimeMemberLiveness({
        teamName,
        runId,
        memberName: 'tom',
        runtimeSessionId: 'ses_tom_live',
        observedAt: '2026-04-22T12:05:00.000Z',
        diagnostics: undefined,
        metadata: undefined,
        reason: 'OpenCode runtime bootstrap check-in accepted',
      });

      expect(run.memberSpawnStatuses.get('tom')).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
        runtimeAlive: true,
        bootstrapConfirmed: true,
        hardFailure: false,
        livenessKind: 'confirmed_bootstrap',
      });
      expect(run.mixedSecondaryLanes[0]?.result?.members.tom).toMatchObject({
        launchState: 'confirmed_alive',
        bootstrapConfirmed: true,
        sessionId: 'ses_tom_live',
      });
      expect(persistLaunchStateSnapshot).toHaveBeenCalledWith(run, 'finished');
    });

    it('uses the secondary lane run id for OpenCode runtime delivery journal acceptance', async () => {
      const svc = new TeamProvisioningService();
      const delivered = new Map<
        string,
        { kind: 'member_inbox'; teamName: string; memberName: string; messageId: string }
      >();

      (svc as any).aliveRunByTeam.set('mixed-team', 'lead-run');
      (svc as any).runs.set('lead-run', {
        runId: 'lead-run',
        teamName: 'mixed-team',
        request: {
          providerId: 'codex',
        },
      });
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/tmp/mixed-team',
      });
      (svc as any).createOpenCodeRuntimeDeliveryPorts = vi.fn(() => [
        {
          kind: 'member_inbox',
          write: vi.fn(async ({ envelope, destinationMessageId }) => {
            const location = {
              kind: 'member_inbox' as const,
              teamName: envelope.teamName,
              memberName:
                typeof envelope.to === 'object' && 'memberName' in envelope.to
                  ? envelope.to.memberName
                  : 'unknown',
              messageId: destinationMessageId,
            };
            delivered.set(destinationMessageId, location);
            return location;
          }),
          verify: vi.fn(async ({ destinationMessageId }) => {
            const location = delivered.get(destinationMessageId) ?? null;
            return {
              found: location !== null,
              location,
              diagnostics: [],
            };
          }),
          buildChangeEvent: vi.fn(() => null),
        },
      ]);

      const delivery = (svc as any).createOpenCodeRuntimeDeliveryService(
        'mixed-team',
        'secondary:opencode:bob'
      );
      const ack = await delivery.deliver({
        idempotencyKey: 'delivery-1',
        runId: 'opencode-run-1',
        teamName: 'mixed-team',
        fromMemberName: 'bob',
        providerId: 'opencode',
        runtimeSessionId: 'session-bob',
        to: { memberName: 'alice' },
        text: 'hi',
        createdAt: '2026-04-22T12:05:00.000Z',
      });

      expect(ack).toMatchObject({
        ok: true,
        delivered: true,
        reason: null,
      });
    });

    it('maps runtime delivery local data.detail to public TeamChangeEvent.detail', async () => {
      const svc = new TeamProvisioningService();
      const emitted: Array<Record<string, unknown>> = [];
      const delivered = new Map<
        string,
        {
          kind: 'member_inbox';
          teamName: string;
          memberName: string;
          messageId: string;
        }
      >();

      svc.setTeamChangeEmitter((event) => {
        emitted.push(event as unknown as Record<string, unknown>);
      });
      (svc as any).setSecondaryRuntimeRun({
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/tmp/mixed-team',
      });
      (svc as any).createOpenCodeRuntimeDeliveryPorts = vi.fn(() => [
        {
          kind: 'member_inbox',
          write: vi.fn(async ({ envelope, destinationMessageId }) => {
            const location = {
              kind: 'member_inbox' as const,
              teamName: envelope.teamName,
              memberName:
                typeof envelope.to === 'object' && 'memberName' in envelope.to
                  ? envelope.to.memberName
                  : 'unknown',
              messageId: destinationMessageId,
            };
            delivered.set(destinationMessageId, location);
            return location;
          }),
          verify: vi.fn(async ({ destinationMessageId }) => {
            const location = delivered.get(destinationMessageId) ?? null;
            return {
              found: location !== null,
              location,
              diagnostics: [],
            };
          }),
          buildChangeEvent: vi.fn(({ teamName, location }) => ({
            type: 'inbox',
            teamName,
            data: {
              detail:
                location.kind === 'member_inbox'
                  ? `inboxes/${location.memberName}.json`
                  : 'inboxes',
            },
          })),
        },
      ]);

      const delivery = (svc as any).createOpenCodeRuntimeDeliveryService(
        'mixed-team',
        'secondary:opencode:bob'
      );
      const ack = await delivery.deliver({
        idempotencyKey: 'delivery-event-shape-1',
        runId: 'opencode-run-1',
        teamName: 'mixed-team',
        fromMemberName: 'bob',
        providerId: 'opencode',
        runtimeSessionId: 'session-bob',
        to: { memberName: 'alice' },
        text: 'hi',
        createdAt: '2026-04-22T12:05:00.000Z',
      });

      expect(ack).toMatchObject({ ok: true, delivered: true });
      expect(emitted).toContainEqual(
        expect.objectContaining({
          type: 'inbox',
          teamName: 'mixed-team',
          detail: 'inboxes/alice.json',
        })
      );
      expect(emitted[0]).not.toHaveProperty('data');
    });

    it('recovers OpenCode delivery journals from canonical launch snapshot when lane index is missing', async () => {
      const svc = new TeamProvisioningService();

      (svc as any).launchStateStore = {
        read: vi.fn(async () => ({
          version: 2,
          teamName: 'mixed-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob', 'tom'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
            tom: {
              name: 'tom',
              providerId: 'opencode',
              laneId: 'secondary:opencode:tom',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 2,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 1,
          },
          teamLaunchState: 'partial_pending',
        })),
      };

      await expect(
        (svc as any).getOpenCodeRuntimeRecoveryLaneIds('mixed-team', {})
      ).resolves.toEqual(['secondary:opencode:bob', 'secondary:opencode:tom']);
    });

    it('routes runtime deliveries to the persisted secondary OpenCode lane after in-memory tracking is lost', async () => {
      const svc = new TeamProvisioningService();
      const observedLaneIds: string[] = [];

      (svc as any).launchStateStore = {
        read: vi.fn(async () => ({
          version: 2,
          teamName: 'mixed-team',
          updatedAt: '2026-04-22T12:00:00.000Z',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob'],
          bootstrapExpectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-22T12:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 2,
            pendingCount: 0,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
          teamLaunchState: 'ready',
        })),
      };
      (svc as any).assertOpenCodeRuntimeEvidenceAccepted = vi.fn(async ({ laneId }) => {
        observedLaneIds.push(`evidence:${laneId}`);
      });
      (svc as any).createOpenCodeRuntimeDeliveryService = vi.fn((_teamName, laneId) => {
        observedLaneIds.push(`delivery:${laneId}`);
        return {
          deliver: vi.fn(async () => ({
            ok: true,
            delivered: true,
            idempotencyKey: 'delivery-1',
            location: {
              kind: 'member_inbox' as const,
              teamName: 'mixed-team',
              memberName: 'alice',
              messageId: 'msg-1',
            },
            reason: null,
          })),
        };
      });

      const ack = await svc.deliverOpenCodeRuntimeMessage({
        idempotencyKey: 'delivery-1',
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
        fromMemberName: 'bob',
        runtimeSessionId: 'session-bob',
        to: { memberName: 'alice' },
        text: 'hi',
        createdAt: '2026-04-22T12:05:00.000Z',
      });

      expect(ack).toMatchObject({
        ok: true,
        state: 'delivered',
        teamName: 'mixed-team',
        runId: 'opencode-run-1',
      });
      expect(observedLaneIds).toEqual([
        'evidence:secondary:opencode:bob',
        'delivery:secondary:opencode:bob',
      ]);
    });

    it('removes lane index entries when mixed secondary lanes are stopped without an OpenCode adapter', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'mixed-team';

      (svc as any).setSecondaryRuntimeRun({
        teamName,
        runId: 'opencode-run-1',
        providerId: 'opencode',
        laneId: 'secondary:opencode:bob',
        memberName: 'bob',
        cwd: '/tmp/mixed-team',
      });
      (svc as any).setSecondaryRuntimeRun({
        teamName,
        runId: 'opencode-run-2',
        providerId: 'opencode',
        laneId: 'secondary:opencode:tom',
        memberName: 'tom',
        cwd: '/tmp/mixed-team',
      });

      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId: 'secondary:opencode:bob',
        state: 'active',
      });
      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId: 'secondary:opencode:tom',
        state: 'active',
      });
      await fsPromises.mkdir(
        path.dirname(
          getOpenCodeLaneScopedRuntimeFilePath({
            teamsBasePath: tempTeamsBase,
            teamName,
            laneId: 'secondary:opencode:bob',
            fileName: 'opencode-delivery-journal.json',
          })
        ),
        { recursive: true }
      );
      await fsPromises.writeFile(
        getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName,
          laneId: 'secondary:opencode:bob',
          fileName: 'opencode-delivery-journal.json',
        }),
        '{"records":[]}\n',
        'utf8'
      );

      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
      };

      await (svc as any).stopMixedSecondaryRuntimeLanes(teamName);

      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {},
      });
      await expect(
        fsPromises.stat(
          path.dirname(
            getOpenCodeLaneScopedRuntimeFilePath({
              teamsBasePath: tempTeamsBase,
              teamName,
              laneId: 'secondary:opencode:bob',
              fileName: 'opencode-delivery-journal.json',
            })
          )
        )
      ).rejects.toThrow();
    });

    it('clears provider-local lane storage when a single mixed secondary lane is stopped during controlled reattach', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'mixed-team',
        expectedMembers: ['alice'],
      });
      run.request = {
        providerId: 'codex',
        cwd: '/tmp/mixed-team',
        members: [],
      };
      const lane = {
        laneId: 'secondary:opencode:bob',
        providerId: 'opencode' as const,
        member: {
          name: 'bob',
          providerId: 'opencode',
          model: 'minimax-m2.5-free',
        },
        runId: 'opencode-run-1',
        state: 'active',
        result: null,
        warnings: [],
        diagnostics: [],
      };

      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName: run.teamName,
        laneId: lane.laneId,
        state: 'active',
      });
      await fsPromises.mkdir(
        path.dirname(
          getOpenCodeLaneScopedRuntimeFilePath({
            teamsBasePath: tempTeamsBase,
            teamName: run.teamName,
            laneId: lane.laneId,
            fileName: 'opencode-permissions.json',
          })
        ),
        { recursive: true }
      );
      await fsPromises.writeFile(
        getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName: run.teamName,
          laneId: lane.laneId,
          fileName: 'opencode-permissions.json',
        }),
        '{"requests":[]}\n',
        'utf8'
      );

      await (svc as any).stopSingleMixedSecondaryRuntimeLane(run, lane, 'relaunch');

      await expect(
        readOpenCodeRuntimeLaneIndex(tempTeamsBase, run.teamName)
      ).resolves.toMatchObject({
        lanes: {},
      });
      await expect(
        fsPromises.stat(
          path.dirname(
            getOpenCodeLaneScopedRuntimeFilePath({
              teamsBasePath: tempTeamsBase,
              teamName: run.teamName,
              laneId: lane.laneId,
              fileName: 'opencode-permissions.json',
            })
          )
        )
      ).rejects.toThrow();
      expect(lane.runId).toBeNull();
      expect(lane.state).toBe('finished');
    });

    it('removes the primary lane index entry when a pure OpenCode team is stopped without an adapter', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'opencode-team';

      (svc as any).runtimeAdapterRunByTeam.set(teamName, {
        runId: 'opencode-run-1',
        providerId: 'opencode',
        cwd: '/tmp/opencode-team',
      });
      (svc as any).aliveRunByTeam.set(teamName, 'opencode-run-1');
      (svc as any).provisioningRunByTeam.set(teamName, 'opencode-run-1');
      (svc as any).launchStateStore = {
        read: vi.fn(async () => null),
      };

      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId: 'primary',
        state: 'active',
      });
      await fsPromises.mkdir(
        path.dirname(
          getOpenCodeLaneScopedRuntimeFilePath({
            teamsBasePath: tempTeamsBase,
            teamName,
            laneId: 'primary',
            fileName: 'opencode-delivery-journal.json',
          })
        ),
        { recursive: true }
      );
      await fsPromises.writeFile(
        getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName,
          laneId: 'primary',
          fileName: 'opencode-delivery-journal.json',
        }),
        '{"records":[]}\n',
        'utf8'
      );

      await (svc as any).stopOpenCodeRuntimeAdapterTeam(teamName, 'opencode-run-1');

      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {},
      });
      await expect(
        fsPromises.stat(
          path.dirname(
            getOpenCodeLaneScopedRuntimeFilePath({
              teamsBasePath: tempTeamsBase,
              teamName,
              laneId: 'primary',
              fileName: 'opencode-delivery-journal.json',
            })
          )
        )
      ).rejects.toThrow();
      expect((svc as any).runtimeAdapterRunByTeam.has(teamName)).toBe(false);
      expect((svc as any).aliveRunByTeam.has(teamName)).toBe(false);
      expect((svc as any).provisioningRunByTeam.has(teamName)).toBe(false);
    });

    it('clears primary lane storage when OpenCode runtime adapter launch fails', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'opencode-team';
      const adapterLaunch = vi.fn(async () => {
        throw new Error('launch boom');
      });
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId: 'primary',
        state: 'active',
      });
      await fsPromises.mkdir(
        path.dirname(
          getOpenCodeLaneScopedRuntimeFilePath({
            teamsBasePath: tempTeamsBase,
            teamName,
            laneId: 'primary',
            fileName: 'opencode-launch-transaction.json',
          })
        ),
        { recursive: true }
      );
      await fsPromises.writeFile(
        getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName,
          laneId: 'primary',
          fileName: 'opencode-launch-transaction.json',
        }),
        '{"transactionId":"tx-1"}\n',
        'utf8'
      );

      await expect(
        (svc as any).runOpenCodeTeamRuntimeAdapterLaunch({
          request: {
            teamName,
            cwd: '/tmp/opencode-team',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
            skipPermissions: true,
          },
          members: [
            {
              name: 'alice',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
              effort: 'medium',
            },
          ],
          prompt: 'Launch team',
          onProgress: vi.fn(),
        })
      ).rejects.toThrow('launch boom');

      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {},
      });
      await expect(
        fsPromises.stat(
          path.dirname(
            getOpenCodeLaneScopedRuntimeFilePath({
              teamsBasePath: tempTeamsBase,
              teamName,
              laneId: 'primary',
              fileName: 'opencode-launch-transaction.json',
            })
          )
        )
      ).rejects.toThrow();
      expect((svc as any).provisioningRunByTeam.has(teamName)).toBe(false);
    });

    it('does not keep a pure OpenCode team alive when the runtime adapter returns partial_failure', async () => {
      const svc = new TeamProvisioningService();
      const teamName = 'opencode-team';
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => ({
        runId: String(input.runId),
        teamName,
        launchPhase: 'finished',
        teamLaunchState: 'partial_failure',
        members: {
          alice: {
            memberName: 'alice',
            providerId: 'opencode',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            diagnostics: ['launch failed'],
          },
        },
        warnings: [],
        diagnostics: ['launch failed'],
      }));
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      await upsertOpenCodeRuntimeLaneIndexEntry({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId: 'primary',
        state: 'active',
      });
      await fsPromises.mkdir(
        path.dirname(
          getOpenCodeLaneScopedRuntimeFilePath({
            teamsBasePath: tempTeamsBase,
            teamName,
            laneId: 'primary',
            fileName: 'opencode-diagnostics.json',
          })
        ),
        { recursive: true }
      );
      await fsPromises.writeFile(
        getOpenCodeLaneScopedRuntimeFilePath({
          teamsBasePath: tempTeamsBase,
          teamName,
          laneId: 'primary',
          fileName: 'opencode-diagnostics.json',
        }),
        '{"events":[]}\n',
        'utf8'
      );

      const response = await (svc as any).runOpenCodeTeamRuntimeAdapterLaunch({
        request: {
          teamName,
          cwd: '/tmp/opencode-team',
          providerId: 'opencode',
          model: 'minimax-m2.5-free',
          effort: 'medium',
          skipPermissions: true,
        },
        members: [
          {
            name: 'alice',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
            effort: 'medium',
          },
        ],
        prompt: 'Launch team',
        onProgress: vi.fn(),
      });

      expect(response).toMatchObject({
        runId: expect.any(String),
      });
      expect((svc as any).runtimeAdapterRunByTeam.has(teamName)).toBe(false);
      expect((svc as any).aliveRunByTeam.has(teamName)).toBe(false);
      expect((svc as any).provisioningRunByTeam.has(teamName)).toBe(false);
      await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
        lanes: {},
      });
      await expect(
        fsPromises.stat(
          path.dirname(
            getOpenCodeLaneScopedRuntimeFilePath({
              teamsBasePath: tempTeamsBase,
              teamName,
              laneId: 'primary',
              fileName: 'opencode-diagnostics.json',
            })
          )
        )
      ).rejects.toThrow();
    });

    it('preserves pending permission request ids for pure OpenCode launch-state members', () => {
      const svc = new TeamProvisioningService();

      const member = (svc as any).toOpenCodePersistedLaunchMember(
        {
          name: 'alice',
          providerId: 'opencode',
          model: 'minimax-m2.5-free',
          effort: 'medium',
        },
        {
          launchState: 'runtime_pending_permission',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          pendingPermissionRequestIds: [
            'opencode:run-1:perm-1',
            'opencode:run-1:perm-1',
            'opencode:run-1:perm-2',
          ],
          diagnostics: ['waiting for permission approval'],
        }
      );

      expect(member).toMatchObject({
        name: 'alice',
        providerId: 'opencode',
        launchState: 'runtime_pending_permission',
        pendingPermissionRequestIds: ['opencode:run-1:perm-1', 'opencode:run-1:perm-2'],
        diagnostics: ['waiting for permission approval'],
      });
    });

    it('fails early when the previous tmux pane does not exit before restart', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'tmux-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Tmux Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@tmux-team',
          backendType: 'tmux',
          tmuxPaneId: '%2',
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('tmux-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      vi.mocked(listTmuxPanePidsForCurrentPlatform).mockImplementation(
        async () => new Map([['%2', 999]])
      );

      const restartPromise = expect(svc.restartMember('tmux-team', 'forge')).rejects.toThrow(
        'Restart for teammate "forge" is still waiting for the previous tmux pane to exit (%2).'
      );
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('still verifies tmux pane exit when pane kill throws, and blocks restart if the pane remains alive', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'tmux-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Tmux Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@tmux-team',
          backendType: 'tmux',
          tmuxPaneId: '%2',
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('tmux-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      vi.mocked(killTmuxPaneForCurrentPlatformSync).mockImplementation(() => {
        throw new Error('pane kill failed');
      });
      vi.mocked(listTmuxPanePidsForCurrentPlatform).mockImplementation(
        async () => new Map([['%2', 999]])
      );

      const restartPromise = expect(svc.restartMember('tmux-team', 'forge')).rejects.toThrow(
        'Restart for teammate "forge" is still waiting for the previous tmux pane to exit (%2).'
      );
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('does not treat tmux pane lookup failures as a successful restart precondition', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'tmux-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Tmux Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@tmux-team',
          backendType: 'tmux',
          tmuxPaneId: '%2',
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('tmux-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      vi.mocked(listTmuxPanePidsForCurrentPlatform).mockRejectedValue(
        new Error('tmux list-panes failed')
      );

      const restartPromise = expect(svc.restartMember('tmux-team', 'forge')).rejects.toThrow(
        'Restart for teammate "forge" could not verify that the previous tmux pane exited: tmux list-panes failed'
      );
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('treats a dead tmux server as successful pane exit verification after kill', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'tmux-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Tmux Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@tmux-team',
          backendType: 'tmux',
          tmuxPaneId: '%2',
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('tmux-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      vi.mocked(listTmuxPanePidsForCurrentPlatform).mockRejectedValue(
        new Error('error connecting to /private/tmp/tmux-501/default (No such file or directory)')
      );

      await svc.restartMember('tmux-team', 'forge');

      expect(sendMessageToRun).toHaveBeenCalledTimes(1);
    });

    it('fails early when the previous process backend runtime does not exit before restart', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'process-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Process Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'forge',
              {
                alive: true,
                backendType: 'process',
                pid: process.pid,
                agentId: 'forge@process-team',
              },
            ],
          ])
      );
      (svc as any).aliveRunByTeam.set('process-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      const restartPromise = expect(svc.restartMember('process-team', 'forge')).rejects.toThrow(
        `Restart for teammate "forge" is still waiting for the previous process to exit (${process.pid}).`
      );
      await vi.waitFor(() => {
        expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      });
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('bypasses stale live runtime metadata cache before restarting a process backend teammate', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'process-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Process Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@process-team',
          backendType: 'process',
        },
      ]);
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: process.pid,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --team-name process-team --agent-id forge@process-team --agent-name forge --model gpt-5.4',
        },
      ]);
      (svc as any).liveTeamAgentRuntimeMetadataCache.set('process-team', {
        expiresAtMs: Date.now() + 60_000,
        metadata: new Map([
          [
            'forge',
            {
              alive: false,
              backendType: 'process',
              agentId: 'forge@process-team',
            },
          ],
        ]),
      });
      (svc as any).aliveRunByTeam.set('process-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      const restartPromise = expect(svc.restartMember('process-team', 'forge')).rejects.toThrow(
        `Restart for teammate "forge" is still waiting for the previous process to exit (${process.pid}).`
      );
      await vi.waitFor(() => {
        expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      });
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('uses members.meta agentId to detect a live process backend teammate when config runtime identity is stale', async () => {
      vi.useFakeTimers();

      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'process-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Process Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
            agentId: 'forge@process-team',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValueOnce([
        {
          pid: process.pid,
          ppid: 1,
          command:
            '/Users/belief/.bun/bin/bun cli.js --team-name process-team --agent-id forge@process-team --agent-name forge --model gpt-5.4',
        },
      ]);
      (svc as any).aliveRunByTeam.set('process-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      const restartPromise = expect(svc.restartMember('process-team', 'forge')).rejects.toThrow(
        `Restart for teammate "forge" is still waiting for the previous process to exit (${process.pid}).`
      );
      await vi.waitFor(() => {
        expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      });
      await vi.advanceTimersByTimeAsync(1_500);
      await restartPromise;

      expect(vi.mocked(killProcessByPid)).toHaveBeenCalledWith(process.pid);
      expect(sendMessageToRun).not.toHaveBeenCalled();
    });

    it('restarts a process backend teammate directly without asking the lead to respawn it', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'process-team',
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;

      const sendMessageToRun = vi.fn(async () => {});
      const directProcessRestart = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).launchDirectProcessMemberRestart = directProcessRestart;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Process Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.4',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => [
        {
          name: 'forge',
          agentId: 'forge@process-team',
          backendType: 'process',
          tmuxPaneId: 'process:1234',
          runtimePid: 1234,
        },
      ]);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('process-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('process-team', 'forge');

      expect(directProcessRestart).toHaveBeenCalledTimes(1);
      expect(sendMessageToRun).not.toHaveBeenCalled();
      expect(run.pendingMemberRestarts.has('forge')).toBe(true);
    });

    it.each([
      ['anthropic', undefined, 'anthropic'],
      ['codex', undefined, 'codex'],
      ['gemini', undefined, 'gemini'],
      ['codex', 'anthropic', 'anthropic'],
      ['anthropic', 'codex', 'codex'],
      ['codex', 'gemini', 'gemini'],
    ] as const)(
      'attaches live primary-owned %s/%s teammate through direct process lifecycle',
      async (leadProviderId, memberProviderId, expectedProviderId) => {
        const teamName = `direct-live-${leadProviderId}-${memberProviderId ?? 'inherit'}`;
        const svc = new TeamProvisioningService();
        const run = createMemberSpawnRun({
          teamName,
          expectedMembers: [],
          memberSpawnStatuses: new Map(),
        });
        run.child = { pid: 111 };
        run.processKilled = false;
        run.cancelRequested = false;
        run.request = { providerId: leadProviderId };
        (svc as any).aliveRunByTeam.set(teamName, run.runId);
        (svc as any).runs.set(run.runId, run);

        const directProcessLaunch = vi.fn(async (input) => {
          const memberSpec = (svc as any).buildPrimaryOwnedMemberSpecForRuntime({
            configuredMember: input.configuredMember,
            run: input.run,
          });
          expect(memberSpec.providerId).toBe(expectedProviderId);
        });
        const opencodeReattach = vi.fn(async () => {});
        (svc as any).launchDirectProcessMemberRestart = directProcessLaunch;
        (svc as any).reattachOpenCodeOwnedMemberLaneUnlocked = opencodeReattach;
        (svc as any).readConfigForStrictDecision = vi.fn(async () => ({
          name: 'Direct Live Team',
          members: [
            { name: 'team-lead', agentType: 'team-lead', providerId: leadProviderId },
            {
              name: 'forge',
              role: 'Developer',
              ...(memberProviderId ? { providerId: memberProviderId } : {}),
              agentType: 'general-purpose',
            },
          ],
        }));
        (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };
        (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
        (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());

        await svc.attachLiveRosterMember(teamName, 'forge', { reason: 'member_added' });

        expect(directProcessLaunch).toHaveBeenCalledWith(
          expect.objectContaining({
            teamName,
            memberName: 'forge',
            operation: 'member_added',
            configuredMember: expect.objectContaining({
              name: 'forge',
            }),
          })
        );
        expect(opencodeReattach).not.toHaveBeenCalled();
      }
    );

    it('routes live OpenCode teammates through the OpenCode lane lifecycle', async () => {
      const teamName = 'direct-live-opencode-member';
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: [],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.request = { providerId: 'codex' };
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);

      const directProcessLaunch = vi.fn(async () => {});
      const opencodeReattach = vi.fn(async () => {});
      (svc as any).launchDirectProcessMemberRestart = directProcessLaunch;
      (svc as any).reattachOpenCodeOwnedMemberLaneUnlocked = opencodeReattach;
      (svc as any).readConfigForStrictDecision = vi.fn(async () => ({
        name: 'Direct Live OpenCode Team',
        members: [
          { name: 'team-lead', agentType: 'team-lead', providerId: 'codex' },
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'opencode',
            agentType: 'general-purpose',
          },
        ],
      }));
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };

      await svc.attachLiveRosterMember(teamName, 'forge', { reason: 'member_added' });

      expect(opencodeReattach).toHaveBeenCalledWith(teamName, 'forge', {
        reason: 'member_added',
      });
      expect(directProcessLaunch).not.toHaveBeenCalled();
    });

    it('blocks live primary-owned teammate attach for OpenCode-led teams', async () => {
      const teamName = 'opencode-led-live-primary-member';
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: [],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.request = { providerId: 'opencode' };
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);

      const directProcessLaunch = vi.fn(async () => {});
      const opencodeReattach = vi.fn(async () => {});
      (svc as any).launchDirectProcessMemberRestart = directProcessLaunch;
      (svc as any).reattachOpenCodeOwnedMemberLaneUnlocked = opencodeReattach;
      (svc as any).readConfigForStrictDecision = vi.fn(async () => ({
        name: 'OpenCode Led Live Team',
        members: [
          { name: 'team-lead', agentType: 'team-lead', providerId: 'opencode' },
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            agentType: 'general-purpose',
          },
        ],
      }));
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };

      await expect(
        svc.attachLiveRosterMember(teamName, 'forge', { reason: 'member_added' })
      ).rejects.toThrow('OpenCode-led mixed teams are not supported');

      expect(directProcessLaunch).not.toHaveBeenCalled();
      expect(opencodeReattach).not.toHaveBeenCalled();
    });

    it('blocks live primary-owned teammate detach for OpenCode-led teams', async () => {
      const teamName = 'opencode-led-live-primary-detach';
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.request = { providerId: 'opencode' };
      (svc as any).aliveRunByTeam.set(teamName, run.runId);
      (svc as any).runs.set(run.runId, run);

      const opencodeDetach = vi.fn(async () => {});
      const stopPrimaryRuntime = vi.fn(async () => {});
      (svc as any).detachOpenCodeOwnedMemberLaneUnlocked = opencodeDetach;
      (svc as any).stopPrimaryOwnedRosterRuntime = stopPrimaryRuntime;
      (svc as any).readConfigForStrictDecision = vi.fn(async () => ({
        name: 'OpenCode Led Live Team',
        members: [
          { name: 'team-lead', agentType: 'team-lead', providerId: 'opencode' },
          {
            name: 'forge',
            role: 'Developer',
            providerId: 'codex',
            agentType: 'general-purpose',
          },
        ],
      }));
      (svc as any).membersMetaStore = { getMembers: vi.fn(async () => []) };

      await expect(svc.detachLiveRosterMember(teamName, 'forge')).rejects.toThrow(
        'OpenCode-led mixed teams are not supported'
      );

      expect(opencodeDetach).not.toHaveBeenCalled();
      expect(stopPrimaryRuntime).not.toHaveBeenCalled();
    });

    it('launches direct process teammate restarts with normal MCP settings inheritance', async () => {
      const teamName = 'process-flags-team';
      const projectPath = path.join(tempProjectsBase, 'process-flags-project');
      fs.mkdirSync(projectPath, { recursive: true });

      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      const child = Object.assign(new EventEmitter(), {
        pid: 4567,
        stdin: { on: vi.fn(), unref: vi.fn() },
        stdout: { pipe: vi.fn(), unref: vi.fn() },
        stderr: { pipe: vi.fn(), unref: vi.fn() },
        unref: vi.fn(),
      });
      vi.mocked(spawnCli).mockReturnValue(child as any);

      const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
        writeConfigFile: vi.fn(async () => '/mock/mcp-config.json'),
      } as any);
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['atlas'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.request = { providerId: 'codex', skipPermissions: true, fastMode: 'on' };
      run.detectedSessionId = 'lead-session-1';
      const configuredMember = {
        name: 'forge',
        role: 'Developer',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'medium',
        agentType: 'general-purpose',
      };
      const config = {
        name: 'Process Flags Team',
        projectPath,
        leadSessionId: 'lead-session-1',
        members: [{ name: 'team-lead', agentType: 'team-lead' }, configuredMember],
      };

      (svc as any).buildProvisioningEnv = vi.fn(async () => ({
        env: { CODEX_API_KEY: 'test-openai-key' },
        authSource: 'openai_api_key',
        providerArgs: [],
      }));
      const launchIdentity = {
        providerId: 'codex',
        providerBackendId: 'native',
        selectedFastMode: 'on',
        resolvedFastMode: true,
      };
      (svc as any).resolveDirectMemberLaunchIdentity = vi.fn(async (input) => {
        expect(input.memberSpec.fastMode).toBe('on');
        return launchIdentity;
      });
      (svc as any).buildTeamRuntimeLaunchArgsPlan = vi.fn(async (input) => ({
        fastModeArgs: input.launchIdentity === launchIdentity ? ['--test-codex-fast-mode'] : [],
        runtimeTurnSettledHookArgs: [],
        providerArgs: [],
        settingsArgs: [],
        extraArgs: [],
        inheritedProviderArgs: [],
        appManagedSettingsPath: null,
      }));
      (svc as any).materializeDirectProcessNativeBootstrapContext = vi.fn(async () => ({}));
      (svc as any).updateDirectTmuxRestartMemberConfig = vi.fn(async () => {});
      (svc as any).enqueueDirectRestartPrompt = vi.fn();
      (svc as any).appendDirectProcessRuntimeEvent = vi.fn(async () => {});

      await (svc as any).launchDirectProcessMemberRestart({
        run,
        teamName,
        displayName: 'Process Flags Team',
        leadName: 'team-lead',
        memberName: 'forge',
        config,
        configuredMember,
        persistedRuntimeMembers: [],
        operation: 'member_added',
      });

      child.emit('close', 0, null);
      await new Promise((resolve) => setTimeout(resolve, 25));

      expect((svc as any).resolveDirectMemberLaunchIdentity).toHaveBeenCalledWith(
        expect.objectContaining({
          memberSpec: expect.objectContaining({ fastMode: 'on' }),
        })
      );
      expect((svc as any).buildTeamRuntimeLaunchArgsPlan).toHaveBeenCalledWith(
        expect.objectContaining({ launchIdentity })
      );
      expect(run.expectedMembers).toEqual(['atlas', 'forge']);
      expect(run.effectiveMembers).toEqual([
        expect.objectContaining({
          name: 'forge',
          providerId: 'codex',
          fastMode: 'on',
        }),
      ]);
      expect(run.allEffectiveMembers).toEqual([
        expect.objectContaining({
          name: 'forge',
          providerId: 'codex',
          fastMode: 'on',
        }),
      ]);
      const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      expect(launchArgs).toEqual(
        expect.arrayContaining([
          '--teammate-runtime',
          'headless',
          '--setting-sources',
          'user,project,local',
          '--mcp-config',
          '/mock/mcp-config.json',
          '--test-codex-fast-mode',
        ])
      );
      expect(launchArgs).not.toContain('--strict-mcp-config');
    });

    it('stops a direct process teammate when post-spawn runtime event persistence fails', async () => {
      const teamName = 'process-event-failure-team';
      const projectPath = path.join(tempProjectsBase, 'process-event-failure-project');
      fs.mkdirSync(projectPath, { recursive: true });

      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      const child = Object.assign(new EventEmitter(), {
        pid: 5678,
        stdin: { on: vi.fn(), unref: vi.fn() },
        stdout: { pipe: vi.fn(), unref: vi.fn() },
        stderr: { pipe: vi.fn(), unref: vi.fn() },
        unref: vi.fn(),
      });
      vi.mocked(spawnCli).mockReturnValue(child as any);

      const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
        writeConfigFile: vi.fn(async () => '/mock/mcp-config.json'),
      } as any);
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.request = { providerId: 'codex', skipPermissions: true };
      run.detectedSessionId = 'lead-session-1';
      const configuredMember = {
        name: 'forge',
        role: 'Developer',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'medium',
        agentType: 'general-purpose',
      };
      const config = {
        name: 'Process Event Failure Team',
        projectPath,
        leadSessionId: 'lead-session-1',
        members: [{ name: 'team-lead', agentType: 'team-lead' }, configuredMember],
      };

      (svc as any).buildProvisioningEnv = vi.fn(async () => ({
        env: { CODEX_API_KEY: 'test-openai-key' },
        authSource: 'openai_api_key',
        providerArgs: [],
      }));
      (svc as any).resolveDirectMemberLaunchIdentity = vi.fn(async () => ({ providerId: 'codex' }));
      (svc as any).buildTeamRuntimeLaunchArgsPlan = vi.fn(async () => ({
        fastModeArgs: [],
        runtimeTurnSettledHookArgs: [],
        providerArgs: [],
        settingsArgs: [],
        extraArgs: [],
        inheritedProviderArgs: [],
        appManagedSettingsPath: null,
      }));
      (svc as any).materializeDirectProcessNativeBootstrapContext = vi.fn(async () => ({}));
      (svc as any).updateDirectTmuxRestartMemberConfig = vi.fn(async () => {});
      (svc as any).enqueueDirectRestartPrompt = vi.fn();
      (svc as any).appendDirectProcessRuntimeEvent = vi
        .fn()
        .mockRejectedValueOnce(new Error('event write failed'));

      await expect(
        (svc as any).launchDirectProcessMemberRestart({
          run,
          teamName,
          displayName: 'Process Event Failure Team',
          leadName: 'team-lead',
          memberName: 'forge',
          config,
          configuredMember,
          persistedRuntimeMembers: [],
          operation: 'member_added',
        })
      ).rejects.toThrow('event write failed');

      expect(killProcessByPid).toHaveBeenCalledWith(5678);
      expect((svc as any).updateDirectTmuxRestartMemberConfig).not.toHaveBeenCalled();
      expect(run.allEffectiveMembers ?? []).toEqual([]);
      await new Promise((resolve) => setTimeout(resolve, 25));
    });

    it('launches direct process teammate restarts with strict per-member MCP policy', async () => {
      const teamName = 'process-strict-mcp-team';
      const projectPath = path.join(tempProjectsBase, 'process-strict-mcp-project');
      fs.mkdirSync(projectPath, { recursive: true });

      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      const child = Object.assign(new EventEmitter(), {
        pid: 4568,
        stdin: { on: vi.fn(), unref: vi.fn() },
        stdout: { pipe: vi.fn(), unref: vi.fn() },
        stderr: { pipe: vi.fn(), unref: vi.fn() },
        unref: vi.fn(),
      });
      vi.mocked(spawnCli).mockReturnValue(child as any);

      const mcpConfigBuilder = {
        writeConfigFile: vi.fn(async () => '/mock/strict-mcp-config.json'),
      };
      const svc = new TeamProvisioningService(
        undefined,
        undefined,
        undefined,
        undefined,
        mcpConfigBuilder as any
      );
      const run = createMemberSpawnRun({
        teamName,
        expectedMembers: ['forge'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.request = { providerId: 'codex', skipPermissions: true };
      run.detectedSessionId = 'lead-session-1';
      const configuredMember = {
        name: 'forge',
        role: 'Developer',
        providerId: 'codex',
        model: 'gpt-5.4',
        effort: 'medium',
        agentType: 'general-purpose',
        mcpPolicy: {
          mode: 'strictAllowlist' as const,
          scopes: { user: true, project: true, local: false },
          serverNames: ['github'],
        },
      };
      const config = {
        name: 'Process Strict MCP Team',
        projectPath,
        leadSessionId: 'lead-session-1',
        members: [{ name: 'team-lead', agentType: 'team-lead' }, configuredMember],
      };

      (svc as any).buildProvisioningEnv = vi.fn(async () => ({
        env: { CODEX_API_KEY: 'test-openai-key' },
        authSource: 'openai_api_key',
        providerArgs: [],
      }));
      const launchIdentity = { providerId: 'codex' };
      (svc as any).resolveDirectMemberLaunchIdentity = vi.fn(async () => launchIdentity);
      (svc as any).buildTeamRuntimeLaunchArgsPlan = vi.fn(async () => ({
        fastModeArgs: [],
        runtimeTurnSettledHookArgs: [],
        providerArgs: [],
        settingsArgs: [],
        extraArgs: [],
        inheritedProviderArgs: [],
        appManagedSettingsPath: null,
      }));
      (svc as any).materializeDirectProcessNativeBootstrapContext = vi.fn(async () => ({}));
      (svc as any).updateDirectTmuxRestartMemberConfig = vi.fn(async () => {});
      (svc as any).enqueueDirectRestartPrompt = vi.fn();
      (svc as any).appendDirectProcessRuntimeEvent = vi.fn(async () => {});

      await (svc as any).launchDirectProcessMemberRestart({
        run,
        teamName,
        displayName: 'Process Strict MCP Team',
        leadName: 'team-lead',
        memberName: 'forge',
        config,
        configuredMember,
        persistedRuntimeMembers: [],
      });

      child.emit('close', 0, null);
      await new Promise((resolve) => setTimeout(resolve, 25));

      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        projectPath,
        expect.objectContaining({ mcpPolicy: configuredMember.mcpPolicy })
      );
      const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      expect(launchArgs).toEqual(
        expect.arrayContaining([
          '--setting-sources',
          'user,project,local',
          '--mcp-config',
          '/mock/strict-mcp-config.json',
          '--strict-mcp-config',
        ])
      );
    });

    it('rejects a second restart request while the first restart is still in flight', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map(),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.pendingMemberRestarts.set('bob', {
        requestedAt: new Date().toISOString(),
        desired: {
          name: 'bob',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'medium',
        },
      });

      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await expect(svc.restartMember('codex-team', 'bob')).rejects.toThrow(
        'Restart for teammate "bob" is already in progress'
      );
    });

    it('clears stale member spawn tool tracking before starting a manual restart', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              firstSpawnAcceptedAt: new Date().toISOString(),
            }),
          ],
        ]),
      });
      run.child = { pid: 111 };
      run.processKilled = false;
      run.cancelRequested = false;
      run.activeToolCalls.set('tool-agent-old', {
        memberName: 'bob',
        toolUseId: 'tool-agent-old',
        toolName: 'Agent',
        preview: 'Spawn teammate bob',
        startedAt: new Date().toISOString(),
        state: 'running',
        source: 'runtime',
      });
      run.memberSpawnToolUseIds.set('tool-agent-old', 'bob');

      const sendMessageToRun = vi.fn(async () => {});
      (svc as any).sendMessageToRun = sendMessageToRun;
      (svc as any).configReader = {
        getConfig: vi.fn(async () => ({
          name: 'Codex Team',
          members: [{ name: 'team-lead', agentType: 'team-lead' }],
        })),
      };
      (svc as any).membersMetaStore = {
        getMembers: vi.fn(async () => [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'codex',
            model: 'gpt-5.2',
            effort: 'medium',
            agentType: 'general-purpose',
          },
        ]),
      };
      (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
      (svc as any).aliveRunByTeam.set('codex-team', run.runId);
      (svc as any).runs.set(run.runId, run);

      await svc.restartMember('codex-team', 'bob');

      expect(run.activeToolCalls.has('tool-agent-old')).toBe(false);
      expect(run.memberSpawnToolUseIds.has('tool-agent-old')).toBe(false);
      expect(sendMessageToRun).toHaveBeenCalledTimes(1);

      (svc as any).finishRuntimeToolActivity(
        run,
        'tool-agent-old',
        [{ type: 'text', text: 'late stale result' }],
        true
      );

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'spawning',
        launchState: 'starting',
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(true);
    });

    it('marks a pending restart as failed when the teammate never rejoins within the restart grace window', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              firstSpawnAcceptedAt: new Date(Date.now() - 120_000).toISOString(),
            }),
          ],
        ]),
      });
      run.pendingMemberRestarts.set('bob', {
        requestedAt: new Date(Date.now() - 120_000).toISOString(),
        desired: {
          name: 'bob',
          providerId: 'codex',
          model: 'gpt-5.2',
          effort: 'medium',
        },
      });
      (svc as any).refreshMemberSpawnStatusesFromLeadInbox = vi.fn(async () => {});
      (svc as any).maybeAuditMemberSpawnStatuses = vi.fn(async () => {});

      await (svc as any).reevaluateMemberLaunchStatus(run, 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'Teammate "bob" did not rejoin within the restart grace window.',
        hardFailureReason: 'Teammate "bob" did not rejoin within the restart grace window.',
      });
      expect(run.pendingMemberRestarts.has('bob')).toBe(false);
    });

    it('does not let stale runtimeAlive bypass launch timeout when live metadata is weak', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'online',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: true,
              livenessSource: 'process',
              bootstrapConfirmed: false,
              firstSpawnAcceptedAt: new Date(Date.now() - 120_000).toISOString(),
            }),
          ],
        ]),
      });
      (svc as any).refreshMemberSpawnStatusesFromLeadInbox = vi.fn(async () => {});
      (svc as any).maybeAuditMemberSpawnStatuses = vi.fn(async () => {});
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'bob',
              {
                alive: false,
                livenessKind: 'shell_only',
                runtimeDiagnostic: 'tmux pane foreground command is zsh',
                runtimeDiagnosticSeverity: 'warning',
              },
            ],
          ])
      );

      await (svc as any).reevaluateMemberLaunchStatus(run, 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: false,
        livenessSource: undefined,
        bootstrapConfirmed: false,
        livenessKind: 'shell_only',
        runtimeDiagnostic: 'tmux pane foreground command is zsh',
        error: 'tmux pane foreground command is zsh',
      });
    });

    it('keeps verified runtime pending with a warning after the bootstrap stall window', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        mixedSecondaryLanes: [{ providerId: 'opencode', member: { name: 'bob' } }],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              firstSpawnAcceptedAt: new Date(Date.now() - 6 * 60_000).toISOString(),
            }),
          ],
        ]),
      });
      (svc as any).refreshMemberSpawnStatusesFromLeadInbox = vi.fn(async () => {});
      (svc as any).maybeAuditMemberSpawnStatuses = vi.fn(async () => {});
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'bob',
              {
                alive: true,
                livenessKind: 'runtime_process',
                runtimeDiagnostic: 'verified runtime process detected',
              },
            ],
          ])
      );

      await (svc as any).reevaluateMemberLaunchStatus(run, 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        runtimeAlive: true,
        bootstrapConfirmed: false,
        livenessSource: undefined,
        livenessKind: 'runtime_process',
        runtimeDiagnostic: 'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
        runtimeDiagnosticSeverity: 'warning',
        bootstrapStalled: true,
        hardFailure: false,
      });
    });

    it('keeps OpenCode runtime process pending before the bootstrap stall window', async () => {
      const svc = new TeamProvisioningService();
      const run = createMemberSpawnRun({
        teamName: 'codex-team',
        expectedMembers: ['bob'],
        mixedSecondaryLanes: [{ providerId: 'opencode', member: { name: 'bob' } }],
        memberSpawnStatuses: new Map([
          [
            'bob',
            createMemberSpawnStatusEntry({
              status: 'waiting',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              firstSpawnAcceptedAt: new Date(Date.now() - 60_000).toISOString(),
            }),
          ],
        ]),
      });
      (svc as any).refreshMemberSpawnStatusesFromLeadInbox = vi.fn(async () => {});
      (svc as any).maybeAuditMemberSpawnStatuses = vi.fn(async () => {});
      (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
        async () =>
          new Map([
            [
              'bob',
              {
                alive: true,
                livenessKind: 'runtime_process',
                runtimeDiagnostic: 'OpenCode runtime process detected',
              },
            ],
          ])
      );

      await (svc as any).reevaluateMemberLaunchStatus(run, 'bob');

      expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        runtimeAlive: true,
        bootstrapConfirmed: false,
        livenessSource: undefined,
        livenessKind: 'runtime_process',
        runtimeDiagnostic: 'OpenCode runtime process detected',
        runtimeDiagnosticSeverity: 'info',
        bootstrapStalled: undefined,
        hardFailure: false,
      });
    });
  });

  it('removes generated MCP config when createTeam spawn fails synchronously', async () => {
    allowConsoleLogs();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('spawn EINVAL');
    });

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);

    await expect(
      svc.createTeam(
        {
          teamName: 'cleanup-team',
          cwd: tempClaudeRoot,
          members: [{ name: 'alice' }],
        },
        () => {}
      )
    ).rejects.toThrow('spawn EINVAL');

    expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
      tempClaudeRoot,
      expect.objectContaining({ controlApiBaseUrl: undefined })
    );
    expect(mcpConfigBuilder.removeConfigFile).toHaveBeenCalledWith('/mock/mcp-config-create.json');
    expect(teamMetaStore.deleteMeta).toHaveBeenCalledWith('cleanup-team');
  });

  it('passes official Codex Fast config overrides when launch identity resolves Fast', async () => {
    allowConsoleLogs();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('spawn EINVAL');
    });

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { CODEX_API_KEY: 'test' },
      authSource: 'codex_runtime',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);
    (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
      providerId: 'codex',
      providerBackendId: 'codex-native',
      selectedModel: 'gpt-5.4',
      selectedModelKind: 'explicit',
      resolvedLaunchModel: 'gpt-5.4',
      catalogId: 'gpt-5.4',
      catalogSource: 'app-server',
      catalogFetchedAt: '2026-04-21T00:00:00.000Z',
      selectedEffort: 'xhigh',
      resolvedEffort: 'xhigh',
      selectedFastMode: 'on',
      resolvedFastMode: true,
      fastResolutionReason: null,
    }));

    await expect(
      svc.createTeam(
        {
          teamName: 'codex-fast-team',
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'xhigh',
          fastMode: 'on',
          members: [{ name: 'alice' }],
        },
        () => {}
      )
    ).rejects.toThrow('spawn EINVAL');

    const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
    expect(launchArgs).toEqual(
      expect.arrayContaining(['-c', 'service_tier="fast"', '-c', 'features.fast_mode=true'])
    );
  });

  describe('safe app launch matrix', () => {
    it('does not wait for OpenCode secondary inboxes before marking primary filesystem readiness', async () => {
      const teamName = 'mixed-secondary-fs-readiness';
      const teamDir = path.join(tempTeamsBase, teamName);
      fs.mkdirSync(path.join(teamDir, 'inboxes'), { recursive: true });
      fs.writeFileSync(
        path.join(teamDir, 'config.json'),
        `${JSON.stringify(
          {
            name: teamName,
            members: [
              { name: 'team-lead', agentType: 'team-lead', providerId: 'codex' },
              { name: 'alice', providerId: 'codex' },
            ],
          },
          null,
          2
        )}\n`,
        'utf8'
      );
      fs.writeFileSync(path.join(teamDir, 'inboxes', 'alice.json'), '[]\n', 'utf8');

      const svc = new TeamProvisioningService();
      const complete = vi
        .spyOn(svc as any, 'handleProvisioningTurnComplete')
        .mockResolvedValue(undefined);
      const run = {
        runId: 'run-mixed-secondary-fs-readiness',
        teamName,
        cancelRequested: false,
        processKilled: false,
        provisioningComplete: false,
        deterministicBootstrap: true,
        fsPhase: 'waiting_members',
        effectiveMembers: [{ name: 'alice', providerId: 'codex' }],
        progress: { state: 'assembling' },
        onProgress: vi.fn(),
        fsMonitorHandle: null,
      } as any;

      (svc as any).startFilesystemMonitor(run, {
        teamName,
        cwd: tempClaudeRoot,
        providerId: 'codex',
        model: 'gpt-5.4',
        members: [
          { name: 'alice', providerId: 'codex' },
          { name: 'tom', providerId: 'opencode' },
        ],
      });

      await vi.waitFor(() => expect(run.fsPhase).toBe('all_files_found'));
      expect(complete).not.toHaveBeenCalled();
      expect(run.onProgress).not.toHaveBeenCalledWith(
        expect.objectContaining({
          message: 'Prepared communication channels for 1/2 members',
        })
      );

      (svc as any).stopFilesystemMonitor(run);
    });

    function createSafeLaunchService(options?: {
      memberWorktreeManager?: { ensureMemberWorktree: ReturnType<typeof vi.fn> };
    }) {
      const mcpConfigBuilder = {
        writeConfigFile: vi.fn(async (_projectPath?: string, _policy?: unknown) =>
          path.join(tempClaudeRoot, 'mcp-config.json')
        ),
        removeConfigFile: vi.fn(async () => {}),
      };
      const membersMetaStore = {
        writeMembers: vi.fn(async () => {}),
        getMembers: vi.fn(async () => []),
        getMeta: vi.fn(async () => null),
      };
      const teamMetaStore = {
        writeMeta: vi.fn(async () => {}),
        deleteMeta: vi.fn(async () => {}),
        getMeta: vi.fn(async () => null),
      };
      const svc = new TeamProvisioningService(
        undefined,
        undefined,
        membersMetaStore as any,
        undefined,
        mcpConfigBuilder as any,
        teamMetaStore as any,
        undefined,
        undefined,
        options?.memberWorktreeManager as any
      );

      (svc as any).buildProvisioningEnv = vi.fn(async () => ({
        env: { CODEX_API_KEY: 'test' },
        authSource: 'codex_runtime',
      }));
      (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
      (svc as any).pathExists = vi.fn(async () => false);
      (svc as any).startFilesystemMonitor = vi.fn();
      (svc as any).stopFilesystemMonitor = vi.fn();
      (svc as any).startStallWatchdog = vi.fn();
      (svc as any).stopStallWatchdog = vi.fn();
      (svc as any).attachStdoutHandler = vi.fn();
      (svc as any).attachStderrHandler = vi.fn();
      (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
        providerId: 'codex',
        providerBackendId: 'codex-native',
        selectedModel: 'gpt-5.4',
        selectedModelKind: 'explicit',
        resolvedLaunchModel: 'gpt-5.4',
        catalogId: 'gpt-5.4',
        catalogSource: 'test',
        catalogFetchedAt: '2026-04-23T00:00:00.000Z',
        selectedEffort: 'medium',
        resolvedEffort: 'medium',
        selectedFastMode: null,
        resolvedFastMode: null,
        fastResolutionReason: null,
      }));
      stubOpenCodeAppManagedLaunchPrompt(svc);

      return { svc, mcpConfigBuilder, membersMetaStore, teamMetaStore };
    }

    function readBootstrapSpecFromSpawnArgs(spawnArgs: string[]) {
      const specIdx = spawnArgs.indexOf('--team-bootstrap-spec');
      expect(specIdx).toBeGreaterThanOrEqual(0);
      return JSON.parse(fs.readFileSync(spawnArgs[specIdx + 1], 'utf8')) as {
        mode: string;
        team: { name: string; cwd: string };
        members: Array<{
          name: string;
          provider?: string;
          model?: string;
          effort?: string;
          role?: string;
          mcpConfigPath?: string;
          mcpSettingSources?: string;
          strictMcpConfig?: boolean;
        }>;
      };
    }

    function getMockMcpPolicyMode(optionsOrPolicy: unknown): string | undefined {
      if (!optionsOrPolicy || typeof optionsOrPolicy !== 'object') {
        return undefined;
      }
      const record = optionsOrPolicy as { mode?: unknown; mcpPolicy?: unknown };
      if (typeof record.mode === 'string') {
        return record.mode;
      }
      if (record.mcpPolicy && typeof record.mcpPolicy === 'object') {
        const policy = record.mcpPolicy as { mode?: unknown };
        return typeof policy.mode === 'string' ? policy.mode : undefined;
      }
      return undefined;
    }

    it('materializes members.meta.json before config normalization for a repairable legacy launch', async () => {
      allowConsoleLogs();
      const teamName = 'legacy-pure-launch-repair';
      const leadSessionId = 'legacy-pure-launch-session';
      writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice', 'bob']);
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc, membersMetaStore } = createSafeLaunchService();
      const normalizeSpy = vi.spyOn(svc as any, 'normalizeTeamConfigForLaunch');

      const { runId } = await svc.launchTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
        },
        vi.fn()
      );

      expect(membersMetaStore.writeMembers).toHaveBeenCalledWith(
        teamName,
        expect.arrayContaining([
          expect.objectContaining({ name: 'alice' }),
          expect.objectContaining({ name: 'bob' }),
        ]),
        expect.objectContaining({ providerBackendId: 'codex-native' })
      );
      expect(membersMetaStore.writeMembers.mock.invocationCallOrder[0]).toBeLessThan(
        normalizeSpy.mock.invocationCallOrder[0]
      );

      await svc.cancelProvisioning(runId);
    });

    it('blocks unsafe old mixed OpenCode launches before config normalization or launch-state cleanup', async () => {
      allowConsoleLogs();
      const teamName = 'legacy-mixed-unsafe-launch';
      const teamDir = path.join(tempTeamsBase, teamName);
      fs.mkdirSync(teamDir, { recursive: true });
      fs.writeFileSync(
        path.join(teamDir, 'config.json'),
        `${JSON.stringify(
          {
            name: teamName,
            projectPath: tempClaudeRoot,
            leadSessionId: 'legacy-mixed-unsafe-session',
            members: [
              { name: 'team-lead', agentType: 'team-lead', providerId: 'codex' },
              { name: 'jack', role: 'Developer', providerId: 'opencode' },
            ],
          },
          null,
          2
        )}\n`,
        'utf8'
      );
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc, membersMetaStore } = createSafeLaunchService();
      const normalizeSpy = vi.spyOn(svc as any, 'normalizeTeamConfigForLaunch');
      const clearLaunchStateSpy = vi.spyOn(svc as any, 'clearPersistedLaunchState');

      await expect(
        svc.launchTeam(
          {
            teamName,
            cwd: tempClaudeRoot,
            providerId: 'codex',
            providerBackendId: 'codex-native',
            model: 'gpt-5.4',
          },
          () => {}
        )
      ).rejects.toThrow(getMixedLaunchFallbackRecoveryError());

      expect(membersMetaStore.writeMembers).not.toHaveBeenCalled();
      expect(normalizeSpy).not.toHaveBeenCalled();
      expect(clearLaunchStateSpy).not.toHaveBeenCalled();
      expect(spawnCli).not.toHaveBeenCalled();
    });

    it('invalidates config cache after writing OpenCode team config', async () => {
      const teamName = 'opencode-config-cache-prime';
      fs.mkdirSync(path.join(tempTeamsBase, teamName), { recursive: true });
      const invalidateSpy = vi.spyOn(TeamConfigReader, 'invalidateTeam');
      const { svc } = createSafeLaunchService();

      await (svc as any).writeOpenCodeTeamConfig(
        {
          teamName,
          displayName: 'OpenCode Config Cache Prime',
          cwd: tempClaudeRoot,
          providerId: 'opencode',
          model: 'openrouter/test/model',
          effort: 'medium',
        },
        [
          {
            name: 'bob',
            role: 'Developer',
            providerId: 'opencode',
            model: 'openrouter/test/model',
          },
        ]
      );

      expect(invalidateSpy).toHaveBeenCalledWith(teamName);
      expect((await new TeamConfigReader().getConfigSnapshot(teamName))?.name).toBe(
        'OpenCode Config Cache Prime'
      );
      invalidateSpy.mockRestore();
    });

    it('starts a pure Codex team through the app createTeam path without a real CLI process', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc, membersMetaStore } = createSafeLaunchService();
      const progress: string[] = [];
      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-codex-only-launch',
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
              effort: 'low',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
              effort: 'medium',
            },
          ],
        },
        (event) => progress.push(event.state)
      );

      const spawnCall = vi.mocked(spawnCli).mock.calls[0];
      expect(spawnCall?.[0]).toBe('/mock/claude');
      expect(spawnCall?.[2]).toMatchObject({
        cwd: tempClaudeRoot,
        stdio: ['pipe', 'pipe', 'pipe'],
      });
      const spawnArgs = spawnCall?.[1] as string[];
      expect(spawnArgs).toEqual(
        expect.arrayContaining(['--model', 'gpt-5.4', '--effort', 'medium'])
      );

      const bootstrapSpec = readBootstrapSpecFromSpawnArgs(spawnArgs);
      expect(bootstrapSpec).toMatchObject({
        mode: 'create',
        team: { name: 'safe-codex-only-launch', cwd: tempClaudeRoot },
      });
      expect(bootstrapSpec.members).toEqual([
        expect.objectContaining({
          name: 'alice',
          provider: 'codex',
          model: 'gpt-5.4-mini',
          effort: 'low',
          role: 'Reviewer',
        }),
        expect.objectContaining({
          name: 'bob',
          provider: 'codex',
          model: 'gpt-5.4-mini',
          effort: 'medium',
          role: 'Developer',
        }),
      ]);

      const run = (svc as any).runs.get(runId);
      expect(run.expectedMembers).toEqual(['alice', 'bob']);
      expect(run.allEffectiveMembers.map((member: { name: string }) => member.name)).toEqual([
        'alice',
        'bob',
      ]);
      expect(run.mixedSecondaryLanes).toEqual([]);
      expect(membersMetaStore.writeMembers).toHaveBeenCalledWith(
        'safe-codex-only-launch',
        expect.arrayContaining([
          expect.objectContaining({ name: 'alice', providerId: 'codex' }),
          expect.objectContaining({ name: 'bob', providerId: 'codex' }),
        ]),
        expect.objectContaining({ providerBackendId: 'codex-native' })
      );
      expect(progress).toEqual(expect.arrayContaining(['validating', 'spawning', 'configuring']));

      await svc.cancelProvisioning(runId);
    });

    it('passes per-member MCP launch settings into deterministic bootstrap specs', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc, mcpConfigBuilder } = createSafeLaunchService();
      mcpConfigBuilder.writeConfigFile.mockImplementation(async (_projectPath, policy) => {
        const mode = getMockMcpPolicyMode(policy);
        if (mode === 'appOnly') return '/mock/member-mcp-app-only.json';
        if (mode === 'inheritScopes') return '/mock/member-mcp-local-only.json';
        if (mode === 'strictAllowlist') return '/mock/member-mcp-strict.json';
        return '/mock/lead-mcp-config.json';
      });

      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-member-mcp-policy-bootstrap',
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              providerId: 'codex',
              mcpPolicy: { mode: 'appOnly' },
            },
            {
              name: 'bob',
              providerId: 'codex',
              mcpPolicy: {
                mode: 'inheritScopes',
                scopes: { user: false, project: false, local: true },
              },
            },
            {
              name: 'jack',
              providerId: 'codex',
              mcpPolicy: {
                mode: 'strictAllowlist',
                serverNames: ['github'],
              },
            },
          ],
        },
        () => {}
      );

      const spawnArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      const bootstrapSpec = readBootstrapSpecFromSpawnArgs(spawnArgs);
      expect(bootstrapSpec.members).toEqual([
        expect.objectContaining({
          name: 'alice',
          mcpConfigPath: '/mock/member-mcp-app-only.json',
          mcpSettingSources: 'user,project,local',
          strictMcpConfig: true,
        }),
        expect.objectContaining({
          name: 'bob',
          mcpConfigPath: '/mock/member-mcp-local-only.json',
          mcpSettingSources: 'local',
          strictMcpConfig: false,
        }),
        expect.objectContaining({
          name: 'jack',
          mcpConfigPath: '/mock/member-mcp-strict.json',
          mcpSettingSources: 'user,project,local',
          strictMcpConfig: true,
        }),
      ]);
      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        tempClaudeRoot,
        expect.objectContaining({ mcpPolicy: { mode: 'appOnly' } })
      );
      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        tempClaudeRoot,
        expect.objectContaining({
          mcpPolicy: {
            mode: 'inheritScopes',
            scopes: { user: false, project: false, local: true },
          },
        })
      );
      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        tempClaudeRoot,
        expect.objectContaining({
          mcpPolicy: {
            mode: 'strictAllowlist',
            serverNames: ['github'],
          },
        })
      );
      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        tempClaudeRoot,
        expect.objectContaining({ controlApiBaseUrl: undefined })
      );

      await svc.cancelProvisioning(runId);
    });

    it('preserves Agent Teams MCP only from members meta when relaunch config has no mcpPolicy', async () => {
      allowConsoleLogs();
      const teamName = 'safe-member-mcp-policy-meta-relaunch';
      const leadSessionId = 'safe-member-mcp-policy-meta-session';
      writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc, mcpConfigBuilder, membersMetaStore } = createSafeLaunchService();
      membersMetaStore.getMembers.mockResolvedValue([
        {
          name: 'alice',
          providerId: 'codex',
          model: 'gpt-5.4-mini',
          mcpPolicy: { mode: 'appOnly' },
        },
      ] as never);
      mcpConfigBuilder.writeConfigFile.mockImplementation(async (_projectPath, policy) => {
        const mode = getMockMcpPolicyMode(policy);
        return mode === 'appOnly' ? '/mock/member-mcp-app-only.json' : '/mock/lead-mcp-config.json';
      });

      const { runId } = await svc.launchTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
        },
        () => {}
      );

      const spawnArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      const bootstrapSpec = readBootstrapSpecFromSpawnArgs(spawnArgs);
      expect(bootstrapSpec.members).toEqual([
        expect.objectContaining({
          name: 'alice',
          provider: 'codex',
          model: 'gpt-5.4-mini',
          mcpConfigPath: '/mock/member-mcp-app-only.json',
          mcpSettingSources: 'user,project,local',
          strictMcpConfig: true,
        }),
      ]);
      expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
        tempClaudeRoot,
        expect.objectContaining({ mcpPolicy: { mode: 'appOnly' } })
      );

      await svc.cancelProvisioning(runId);
    });

    it('starts an Anthropic team without injecting lead effort into explicit teammate models', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const { svc } = createSafeLaunchService();
      (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
        providerId: 'anthropic',
        providerBackendId: undefined,
        selectedModel: 'sonnet',
        selectedModelKind: 'explicit',
        resolvedLaunchModel: 'sonnet',
        catalogId: 'sonnet',
        catalogSource: 'test',
        catalogFetchedAt: '2026-05-17T00:00:00.000Z',
        selectedEffort: 'low',
        resolvedEffort: 'low',
        selectedFastMode: null,
        resolvedFastMode: null,
        fastResolutionReason: null,
      }));

      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-anthropic-explicit-model-effort-launch',
          cwd: tempClaudeRoot,
          providerId: 'anthropic',
          model: 'sonnet',
          effort: 'low',
          members: [
            {
              name: 'jack',
              role: 'Reviewer',
              providerId: 'anthropic',
              model: 'haiku',
            },
            {
              name: 'alice',
              role: 'Developer',
            },
          ],
        },
        () => {}
      );

      const spawnArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      expect(spawnArgs).toEqual(expect.arrayContaining(['--model', 'sonnet', '--effort', 'low']));
      const bootstrapSpec = readBootstrapSpecFromSpawnArgs(spawnArgs);
      expect(bootstrapSpec).toMatchObject({
        mode: 'create',
        team: {
          name: 'safe-anthropic-explicit-model-effort-launch',
          cwd: tempClaudeRoot,
        },
      });
      expect(bootstrapSpec.members).toEqual([
        expect.objectContaining({
          name: 'jack',
          provider: 'anthropic',
          model: 'haiku',
          role: 'Reviewer',
        }),
        expect.objectContaining({
          name: 'alice',
          provider: 'anthropic',
          model: 'sonnet',
          effort: 'low',
          role: 'Developer',
        }),
      ]);
      expect(bootstrapSpec.members[0]).not.toHaveProperty('effort');

      await svc.cancelProvisioning(runId);
    });

    it('routes a pure OpenCode team directly through the runtime adapter without spawning the CLI lane', async () => {
      allowConsoleLogs();
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const expectedMembers = input.expectedMembers as Array<{ name: string }>;
        return {
          runId: String(input.runId),
          teamName: String(input.teamName),
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          leadSessionId: 'opencode-lead-session',
          members: Object.fromEntries(
            expectedMembers.map((member) => [
              member.name,
              {
                memberName: member.name,
                providerId: 'opencode',
                launchState: 'confirmed_alive',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: true,
                hardFailure: false,
                diagnostics: [],
              },
            ])
          ),
          warnings: [],
          diagnostics: [],
        };
      });

      const { svc, membersMetaStore } = createSafeLaunchService();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );
      const progress: string[] = [];

      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-opencode-only-launch',
          cwd: tempClaudeRoot,
          providerId: 'opencode',
          providerBackendId: 'adapter',
          model: 'big-pickle',
          effort: 'medium',
          members: [
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
            },
            {
              name: 'tom',
              role: 'Developer',
              providerId: 'opencode',
              model: 'nemotron-3-super-free',
            },
          ],
        },
        (event) => progress.push(event.state)
      );

      expect(runId).toEqual(expect.any(String));
      expect(spawnCli).not.toHaveBeenCalled();
      expect(ClaudeBinaryResolver.resolve).not.toHaveBeenCalled();
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'primary',
          providerId: 'opencode',
          model: 'big-pickle',
          effort: 'medium',
          cwd: tempClaudeRoot,
          expectedMembers: [
            expect.objectContaining({
              name: 'team-lead',
              providerId: 'opencode',
              model: 'big-pickle',
            }),
            expect.objectContaining({
              name: 'bob',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
            }),
            expect.objectContaining({
              name: 'tom',
              providerId: 'opencode',
              model: 'nemotron-3-super-free',
            }),
          ],
        })
      );
      expect(membersMetaStore.writeMembers).toHaveBeenCalledWith(
        'safe-opencode-only-launch',
        expect.arrayContaining([
          expect.objectContaining({ name: 'bob', providerId: 'opencode' }),
          expect.objectContaining({ name: 'tom', providerId: 'opencode' }),
        ]),
        expect.objectContaining({ providerBackendId: 'adapter' })
      );

      const config = JSON.parse(
        fs.readFileSync(
          path.join(tempTeamsBase, 'safe-opencode-only-launch', 'config.json'),
          'utf8'
        )
      ) as { members: Array<{ name: string; providerId?: string; model?: string }> };
      expect(config.members).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            name: 'team-lead',
            providerId: 'opencode',
            model: 'big-pickle',
          }),
          expect.objectContaining({
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
          }),
          expect.objectContaining({
            name: 'tom',
            providerId: 'opencode',
            model: 'nemotron-3-super-free',
          }),
        ])
      );

      const publicStatuses = await svc.getMemberSpawnStatuses('safe-opencode-only-launch');
      expect(publicStatuses.statuses.bob).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
      });
      expect(publicStatuses.statuses.tom).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
      });
      expect(publicStatuses.teamLaunchState).toBe('clean_success');
      expect(progress).toEqual(expect.arrayContaining(['validating', 'spawning', 'ready']));
    });

    it('keeps Codex in the primary CLI lane and starts OpenCode teammates as secondary runtime lanes', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const expectedMembers = input.expectedMembers as Array<{ name: string }>;
        const memberName = expectedMembers[0]?.name ?? 'unknown';
        const teamName = String(input.teamName);
        const laneId = String(input.laneId);
        const runId = String(input.runId);
        await writeCommittedOpenCodeSessionStore({
          teamName,
          laneId,
          runId,
          sessions: [
            {
              id: `oc-session-${memberName}`,
              teamName,
              memberName,
              laneId,
              runId,
              source: 'runtime_bootstrap_checkin',
            },
          ],
        });
        return {
          runId,
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            [memberName]: {
              memberName,
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              diagnostics: [],
            },
          },
          warnings: [],
          diagnostics: [],
        };
      });
      const adapterStop = vi.fn(async () => {});

      const { svc, membersMetaStore } = createSafeLaunchService();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: adapterStop,
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-mixed-codex-opencode-launch',
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
              effort: 'low',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
            },
            {
              name: 'tom',
              role: 'Developer',
              providerId: 'opencode',
              model: 'nemotron-3-super-free',
            },
          ],
        },
        () => {}
      );

      const spawnArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
      const bootstrapSpec = readBootstrapSpecFromSpawnArgs(spawnArgs);
      expect(bootstrapSpec.members).toEqual([
        expect.objectContaining({
          name: 'alice',
          provider: 'codex',
          model: 'gpt-5.4-mini',
        }),
      ]);

      const run = (svc as any).runs.get(runId);
      expect(run.expectedMembers).toEqual(['alice']);
      expect(run.effectiveMembers.map((member: { name: string }) => member.name)).toEqual([
        'alice',
      ]);
      expect(run.allEffectiveMembers.map((member: { name: string }) => member.name)).toEqual([
        'alice',
        'bob',
        'tom',
      ]);
      expect(run.mixedSecondaryLanes).toEqual([
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          state: 'queued',
          member: expect.objectContaining({
            name: 'bob',
            providerId: 'opencode',
            model: 'minimax-m2.5-free',
          }),
        }),
        expect.objectContaining({
          laneId: 'secondary:opencode:tom',
          state: 'queued',
          member: expect.objectContaining({
            name: 'tom',
            providerId: 'opencode',
            model: 'nemotron-3-super-free',
          }),
        }),
      ]);
      expect(membersMetaStore.writeMembers).toHaveBeenCalledWith(
        'safe-mixed-codex-opencode-launch',
        expect.arrayContaining([
          expect.objectContaining({ name: 'alice', providerId: 'codex' }),
          expect.objectContaining({ name: 'bob', providerId: 'opencode' }),
          expect.objectContaining({ name: 'tom', providerId: 'opencode' }),
        ]),
        expect.objectContaining({ providerBackendId: 'codex-native' })
      );

      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(() => expect(adapterLaunch).toHaveBeenCalledTimes(2), { timeout: 5_000 });
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          providerId: 'opencode',
          model: 'minimax-m2.5-free',
          cwd: tempClaudeRoot,
          expectedMembers: [
            expect.objectContaining({
              name: 'bob',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
            }),
          ],
        })
      );
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'secondary:opencode:tom',
          providerId: 'opencode',
          model: 'nemotron-3-super-free',
          cwd: tempClaudeRoot,
          expectedMembers: [
            expect.objectContaining({
              name: 'tom',
              providerId: 'opencode',
              model: 'nemotron-3-super-free',
            }),
          ],
        })
      );
      await vi.waitFor(
        () => {
          expect(run.mixedSecondaryLanes).toEqual([
            expect.objectContaining({
              laneId: 'secondary:opencode:bob',
              state: 'finished',
              result: expect.objectContaining({ teamLaunchState: 'clean_success' }),
            }),
            expect.objectContaining({
              laneId: 'secondary:opencode:tom',
              state: 'finished',
              result: expect.objectContaining({ teamLaunchState: 'clean_success' }),
            }),
          ]);
        },
        { timeout: 5_000 }
      );
      const publicStatuses = await svc.getMemberSpawnStatuses('safe-mixed-codex-opencode-launch');
      expect(publicStatuses.statuses.bob).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
      });
      expect(publicStatuses.statuses.tom).toMatchObject({
        status: 'online',
        launchState: 'confirmed_alive',
      });
      expect(publicStatuses.expectedMembers).toEqual(
        expect.arrayContaining(['alice', 'bob', 'tom'])
      );

      await svc.cancelProvisioning(runId);
    });

    it('resets stale OpenCode lane manifests before launch and retries exact stale watermark once', async () => {
      allowConsoleLogs();
      const teamName = 'safe-mixed-opencode-stale-manifest-recovery';
      const laneId = 'secondary:opencode:bob';
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'old-opencode-run',
        sessions: [
          {
            id: 'old-session-bob',
            teamName,
            memberName: 'bob',
            laneId,
            runId: 'old-opencode-run',
            source: 'runtime_bootstrap_checkin',
          },
        ],
      });
      await setOpenCodeRuntimeActiveRunManifest({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        runId: 'old-opencode-run',
      });

      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const runId = String(input.runId);
        const manifest = await createRuntimeStoreManifestStore({
          filePath: getOpenCodeRuntimeManifestPath(tempTeamsBase, teamName, laneId),
          teamName,
        }).read();
        expect(manifest).toMatchObject({
          activeRunId: runId,
          highWatermark: 0,
          entries: [],
        });

        if (adapterLaunch.mock.calls.length === 1) {
          throw new Error(
            'OpenCode bridge failed: Bridge server runtime manifest high watermark is stale'
          );
        }

        await writeCommittedOpenCodeSessionStore({
          teamName,
          laneId,
          runId,
          sessions: [
            {
              id: 'fresh-session-bob',
              teamName,
              memberName: 'bob',
              laneId,
              runId,
              source: 'runtime_bootstrap_checkin',
            },
          ],
        });
        return {
          runId,
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            bob: {
              memberName: 'bob',
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              diagnostics: [],
            },
          },
          warnings: [],
          diagnostics: [],
        };
      });

      const { svc } = createSafeLaunchService();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax/m2.5',
            },
          ],
        },
        () => {}
      );

      const run = (svc as any).runs.get(runId);
      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(() => expect(adapterLaunch).toHaveBeenCalledTimes(2), { timeout: 5_000 });

      await vi.waitFor(
        async () => {
          const publicStatuses = await svc.getMemberSpawnStatuses(teamName);
          expect(publicStatuses.statuses.bob).toMatchObject({
            status: 'online',
            launchState: 'confirmed_alive',
          });
        },
        { timeout: 5_000 }
      );

      await svc.cancelProvisioning(runId);
    });

    it('keeps stale OpenCode lane manifest recovery bounded when the bridge stays stale', async () => {
      allowConsoleLogs();
      const teamName = 'safe-mixed-opencode-stale-manifest-terminal';
      const laneId = 'secondary:opencode:bob';
      const staleWatermarkError =
        'OpenCode bridge failed: Bridge server runtime manifest high watermark is stale';
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);
      await writeCommittedOpenCodeSessionStore({
        teamName,
        laneId,
        runId: 'old-opencode-run',
        sessions: [
          {
            id: 'old-session-bob',
            teamName,
            memberName: 'bob',
            laneId,
            runId: 'old-opencode-run',
            source: 'runtime_bootstrap_checkin',
          },
        ],
      });
      await setOpenCodeRuntimeActiveRunManifest({
        teamsBasePath: tempTeamsBase,
        teamName,
        laneId,
        runId: 'old-opencode-run',
      });

      const adapterLaunch = vi.fn(async () => {
        throw new Error(staleWatermarkError);
      });
      const { svc } = createSafeLaunchService();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax/m2.5',
            },
          ],
        },
        () => {}
      );

      const run = (svc as any).runs.get(runId);
      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(() => expect(adapterLaunch).toHaveBeenCalledTimes(2), { timeout: 5_000 });
      await vi.waitFor(
        async () => {
          const publicStatuses = await svc.getMemberSpawnStatuses(teamName);
          expect(publicStatuses.statuses.bob).toMatchObject({
            status: 'error',
            launchState: 'failed_to_start',
          });
          expect(JSON.stringify(publicStatuses.statuses.bob)).toContain(staleWatermarkError);
        },
        { timeout: 5_000 }
      );

      await svc.cancelProvisioning(runId);
    });

    it('does not retry non-stale OpenCode provider launch failures as manifest recovery', async () => {
      allowConsoleLogs();
      const teamName = 'safe-mixed-opencode-provider-failure-no-stale-retry';
      const providerError =
        'OpenCode quota exhausted. This request requires more credits, or fewer max_tokens.';
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const adapterLaunch = vi.fn(async () => {
        throw new Error(providerError);
      });
      const { svc } = createSafeLaunchService();
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax/m2.5',
            },
          ],
        },
        () => {}
      );

      const run = (svc as any).runs.get(runId);
      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(() => expect(adapterLaunch).toHaveBeenCalledTimes(1), { timeout: 5_000 });
      await vi.waitFor(
        async () => {
          const publicStatuses = await svc.getMemberSpawnStatuses(teamName);
          expect(publicStatuses.statuses.bob).toMatchObject({
            status: 'error',
            launchState: 'failed_to_start',
          });
          expect(JSON.stringify(publicStatuses.statuses.bob)).toContain(providerError);
        },
        { timeout: 5_000 }
      );

      await svc.cancelProvisioning(runId);
    });

    it('restores missing OpenCode teammates into config before post-launch registration audit', async () => {
      allowConsoleLogs();
      const teamName = 'mixed-opencode-post-launch-config';
      const teamDir = path.join(tempTeamsBase, teamName);
      const jackWorktree = path.join(tempClaudeRoot, 'worktrees', 'jack');
      const invalidateSpy = vi.spyOn(TeamConfigReader, 'invalidateTeam');
      fs.mkdirSync(teamDir, { recursive: true });
      fs.writeFileSync(
        path.join(teamDir, 'config.json'),
        `${JSON.stringify(
          {
            name: teamName,
            projectPath: '/old/project',
            leadSessionId: 'old-lead-session',
            members: [{ name: 'team-lead', agentType: 'team-lead', providerId: 'anthropic' }],
          },
          null,
          2
        )}\n`,
        'utf8'
      );

      const { svc } = createSafeLaunchService();
      await (svc as any).updateConfigPostLaunch(
        teamName,
        tempClaudeRoot,
        'new-lead-session',
        undefined,
        {
          providerId: 'codex',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'openrouter/google/gemini-2.5-flash',
            },
            {
              name: 'jack',
              role: 'Developer',
              workflow: 'Work in the isolated checkout.',
              providerId: 'opencode',
              model: 'openrouter/qwen/qwen3-coder',
              isolation: 'worktree',
              cwd: jackWorktree,
            },
          ],
        }
      );

      const config = JSON.parse(fs.readFileSync(path.join(teamDir, 'config.json'), 'utf8')) as {
        leadSessionId?: string;
        projectPath?: string;
        members: Array<{
          name: string;
          agentId?: string;
          agentType?: string;
          providerId?: string;
          model?: string;
          role?: string;
          workflow?: string;
          isolation?: string;
          cwd?: string;
        }>;
      };

      expect(config.leadSessionId).toBe('new-lead-session');
      expect(config.projectPath).toBe(tempClaudeRoot);
      expect(invalidateSpy).toHaveBeenCalledWith(teamName);
      expect(config.members).toEqual([
        expect.objectContaining({
          name: 'team-lead',
          providerId: 'codex',
          model: 'gpt-5.4',
        }),
        expect.objectContaining({
          name: 'bob',
          agentId: `bob@${teamName}`,
          agentType: 'general-purpose',
          role: 'Developer',
          providerId: 'opencode',
          model: 'openrouter/google/gemini-2.5-flash',
        }),
        expect.objectContaining({
          name: 'jack',
          agentId: `jack@${teamName}`,
          agentType: 'general-purpose',
          role: 'Developer',
          workflow: 'Work in the isolated checkout.',
          providerId: 'opencode',
          model: 'openrouter/qwen/qwen3-coder',
          isolation: 'worktree',
          cwd: jackWorktree,
        }),
      ]);
      expect(config.members.some((member) => member.name === 'alice')).toBe(false);
      invalidateSpy.mockRestore();
    });

    it('launches isolated OpenCode side lanes from the resolved member worktree cwd', async () => {
      allowConsoleLogs();
      vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
      vi.mocked(spawnCli).mockReturnValue(createRunningChild() as any);

      const bobWorktree = path.join(tempClaudeRoot, 'worktrees', 'bob');
      const worktreeManager = {
        ensureMemberWorktree: vi.fn(async () => ({
          baseRepoPath: tempClaudeRoot,
          worktreePath: bobWorktree,
          branchName: 'agent-teams/test/bob',
        })),
      };
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const expectedMembers = input.expectedMembers as Array<{ name: string }>;
        const memberName = expectedMembers[0]?.name ?? 'unknown';
        const teamName = String(input.teamName);
        const laneId = String(input.laneId);
        const runId = String(input.runId);
        await writeCommittedOpenCodeSessionStore({
          teamName,
          laneId,
          runId,
          sessions: [
            {
              id: `oc-session-${memberName}`,
              teamName,
              memberName,
              laneId,
              runId,
              source: 'runtime_bootstrap_checkin',
            },
          ],
        });
        return {
          runId,
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: {
            [memberName]: {
              memberName,
              providerId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              diagnostics: [],
            },
          },
          warnings: [],
          diagnostics: [],
        };
      });

      const { svc, membersMetaStore } = createSafeLaunchService({
        memberWorktreeManager: worktreeManager,
      });
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName: 'safe-mixed-opencode-worktree-launch',
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
          effort: 'medium',
          members: [
            {
              name: 'alice',
              role: 'Reviewer',
              providerId: 'codex',
              model: 'gpt-5.4-mini',
            },
            {
              name: 'bob',
              role: 'Developer',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
              isolation: 'worktree',
            },
          ],
        },
        () => {}
      );

      expect(worktreeManager.ensureMemberWorktree).toHaveBeenCalledWith({
        teamName: 'safe-mixed-opencode-worktree-launch',
        memberName: 'bob',
        baseCwd: tempClaudeRoot,
      });
      expect(membersMetaStore.writeMembers).toHaveBeenCalledWith(
        'safe-mixed-opencode-worktree-launch',
        expect.arrayContaining([
          expect.objectContaining({
            name: 'bob',
            providerId: 'opencode',
            isolation: 'worktree',
            cwd: bobWorktree,
          }),
        ]),
        expect.objectContaining({ providerBackendId: 'codex-native' })
      );

      const run = (svc as any).runs.get(runId);
      await (svc as any).launchMixedSecondaryLaneIfNeeded(run);
      await vi.waitFor(() => expect(adapterLaunch).toHaveBeenCalledTimes(1));
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          cwd: bobWorktree,
          expectedMembers: [
            expect.objectContaining({
              name: 'bob',
              providerId: 'opencode',
              isolation: 'worktree',
              cwd: bobWorktree,
            }),
          ],
        })
      );

      await svc.cancelProvisioning(runId);
    });

    it('launches pure OpenCode worktree members through separate runtime lanes', async () => {
      allowConsoleLogs();
      const bobWorktree = path.join(tempClaudeRoot, 'worktrees', 'bob');
      const worktreeManager = {
        ensureMemberWorktree: vi.fn(async () => ({
          baseRepoPath: tempClaudeRoot,
          worktreePath: bobWorktree,
          branchName: 'agent-teams/test/bob',
        })),
      };
      const adapterLaunch = vi.fn(async (input: Record<string, unknown>) => {
        const expectedMembers = input.expectedMembers as Array<{ name: string }>;
        const teamName = String(input.teamName);
        const laneId = String(input.laneId);
        const runId = String(input.runId);
        await writeCommittedOpenCodeSessionStore({
          teamName,
          laneId,
          runId,
          sessions: expectedMembers.map((member) => ({
            id: `oc-session-${laneId}-${member.name}`,
            teamName,
            memberName: member.name,
            laneId,
            runId,
            source: 'runtime_bootstrap_checkin',
          })),
        });
        return {
          runId,
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'clean_success',
          members: Object.fromEntries(
            expectedMembers.map((member) => [
              member.name,
              {
                memberName: member.name,
                providerId: 'opencode',
                launchState: 'confirmed_alive',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: true,
                hardFailure: false,
                diagnostics: [],
              },
            ])
          ),
          warnings: [],
          diagnostics: [],
        };
      });
      const { svc } = createSafeLaunchService({
        memberWorktreeManager: worktreeManager,
      });
      svc.setRuntimeAdapterRegistry(
        new TeamRuntimeAdapterRegistry([
          {
            providerId: 'opencode',
            prepare: vi.fn(),
            launch: adapterLaunch,
            reconcile: vi.fn(),
            stop: vi.fn(),
          } as any,
        ])
      );

      const { runId } = await svc.createTeam(
        {
          teamName: 'opencode-multi-worktree-lanes',
          cwd: tempClaudeRoot,
          providerId: 'opencode',
          providerBackendId: 'adapter',
          model: 'big-pickle',
          members: [
            {
              name: 'bob',
              providerId: 'opencode',
              model: 'minimax-m2.5-free',
              isolation: 'worktree',
            },
            {
              name: 'tom',
              providerId: 'opencode',
              model: 'nemotron-3-super-free',
            },
          ],
        },
        () => {}
      );

      expect(worktreeManager.ensureMemberWorktree).toHaveBeenCalledWith({
        teamName: 'opencode-multi-worktree-lanes',
        memberName: 'bob',
        baseCwd: tempClaudeRoot,
      });
      expect(adapterLaunch).toHaveBeenCalledTimes(2);
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'primary',
          cwd: tempClaudeRoot,
          expectedMembers: [
            expect.objectContaining({
              name: 'tom',
              providerId: 'opencode',
              cwd: tempClaudeRoot,
            }),
          ],
        })
      );
      expect(adapterLaunch).toHaveBeenCalledWith(
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          cwd: bobWorktree,
          expectedMembers: [
            expect.objectContaining({
              name: 'bob',
              providerId: 'opencode',
              isolation: 'worktree',
              cwd: bobWorktree,
            }),
          ],
        })
      );
      const run = (svc as any).runs.get(runId);
      expect(run?.mixedSecondaryLanes).toEqual([
        expect.objectContaining({
          laneId: 'secondary:opencode:bob',
          state: 'finished',
          member: expect.objectContaining({ name: 'bob', cwd: bobWorktree }),
        }),
      ]);
    });
  });

  it('removes generated MCP config when launchTeam spawn fails synchronously', async () => {
    allowConsoleLogs();
    const teamName = 'launch-cleanup-team';
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'config.json'),
      JSON.stringify({
        name: teamName,
        projectPath: tempClaudeRoot,
        members: [{ name: 'team-lead', agentType: 'team-lead' }, { name: 'alice' }],
      }),
      'utf8'
    );

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const restorePrelaunchConfig = vi.fn(async () => {});

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      undefined,
      undefined,
      mcpConfigBuilder as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = restorePrelaunchConfig;
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);

    await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
      'launch spawn EINVAL'
    );

    expect(mcpConfigBuilder.writeConfigFile).toHaveBeenCalledWith(
      tempClaudeRoot,
      expect.objectContaining({ controlApiBaseUrl: undefined })
    );
    expect(mcpConfigBuilder.removeConfigFile).toHaveBeenCalledWith('/mock/mcp-config-launch.json');
    expect(restorePrelaunchConfig).toHaveBeenCalledWith(teamName);
  });

  it('regenerates a missing --mcp-config before auth-failure respawn', async () => {
    vi.useFakeTimers();
    allowConsoleLogs();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');

    const firstChild = createRunningChild();
    const secondChild = createRunningChild();
    vi.mocked(spawnCli)
      .mockImplementationOnce(() => firstChild as any)
      .mockImplementationOnce(() => secondChild as any);

    const mcpConfigBuilder = {
      writeConfigFile: vi
        .fn()
        .mockResolvedValueOnce('/missing/original-mcp-config.json')
        .mockResolvedValueOnce('/regenerated/mcp-config.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).stopFilesystemMonitor = vi.fn();
    (svc as any).startStallWatchdog = vi.fn();
    (svc as any).stopStallWatchdog = vi.fn();
    (svc as any).attachStdoutHandler = vi.fn();
    (svc as any).attachStderrHandler = vi.fn();

    const { runId } = await svc.createTeam(
      {
        teamName: 'retry-team',
        cwd: tempClaudeRoot,
        members: [{ name: 'alice' }],
      },
      () => {}
    );

    const run = (svc as any).runs.get(runId);
    expect(run).toBeTruthy();

    const mcpFlagIdx = run.spawnContext.args.indexOf('--mcp-config');
    expect(mcpFlagIdx).toBeGreaterThanOrEqual(0);
    run.spawnContext.args[mcpFlagIdx + 1] = path.join(tempClaudeRoot, 'deleted-mcp-config.json');
    run.mcpConfigPath = run.spawnContext.args[mcpFlagIdx + 1];
    run.authRetryInProgress = true;

    const respawnPromise = (svc as any).respawnAfterAuthFailure(run);
    await vi.advanceTimersByTimeAsync(2000);
    await respawnPromise;

    expect(mcpConfigBuilder.writeConfigFile).toHaveBeenNthCalledWith(
      2,
      tempClaudeRoot,
      expect.objectContaining({ controlApiBaseUrl: undefined })
    );
    expect(run.spawnContext.args[mcpFlagIdx + 1]).toBe('/regenerated/mcp-config.json');
    expect(run.mcpConfigPath).toBe('/regenerated/mcp-config.json');
    expect(vi.mocked(spawnCli)).toHaveBeenNthCalledWith(
      2,
      '/mock/claude',
      run.spawnContext.args,
      expect.objectContaining({
        cwd: tempClaudeRoot,
        stdio: ['pipe', 'pipe', 'pipe'],
      })
    );
    expect(run.child).toBe(secondChild);

    if (run.timeoutHandle) {
      clearTimeout(run.timeoutHandle);
      run.timeoutHandle = null;
    }
  });

  it('pre-seeds lead bootstrap MCP permissions before createTeam spawn', async () => {
    allowConsoleLogs();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('spawn EINVAL');
    });

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);

    await expect(
      svc.createTeam(
        {
          teamName: 'seeded-team',
          cwd: tempClaudeRoot,
          members: [{ name: 'alice' }],
          skipPermissions: false,
        },
        () => {}
      )
    ).rejects.toThrow('spawn EINVAL');

    const settingsPath = path.join(tempClaudeRoot, '.claude', 'settings.local.json');
    const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as {
      permissions?: { allow?: string[] };
    };
    expect(settings.permissions?.allow).toEqual(
      expect.arrayContaining([...AGENT_TEAMS_NAMESPACED_LEAD_BOOTSTRAP_TOOL_NAMES])
    );
    expect(settings.permissions?.allow).toContain('mcp__agent-teams__lead_briefing');
    expect(settings.permissions?.allow).not.toContain('mcp__agent-teams__team_stop');
    expect(settings.permissions?.allow).not.toContain('mcp__agent-teams__kanban_clear');
  });

  it('expands teammate permission suggestions to the operational tool set only', async () => {
    allowConsoleLogs();
    const getConfig = vi.fn(async () => ({
      projectPath: tempClaudeRoot,
      members: [{ cwd: tempClaudeRoot }],
    }));
    const getConfigSnapshot = vi.fn(async () => {
      throw new Error('snapshot config read should not be used for permission writes');
    });
    const svc = new TeamProvisioningService({
      getConfig,
      getConfigSnapshot,
    } as any);
    const persistInboxMessage = vi.fn();
    (svc as any).persistInboxMessage = persistInboxMessage;

    await (svc as any).respondToTeammatePermission(
      { teamName: 'ops-team' },
      'alice',
      'req-1',
      true,
      undefined,
      [
        {
          type: 'addRules',
          behavior: 'allow',
          destination: 'localSettings',
          rules: [{ toolName: 'mcp__agent-teams__task_get' }],
        },
      ]
    );

    const settingsPath = path.join(tempClaudeRoot, '.claude', 'settings.local.json');
    const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as {
      permissions?: { allow?: string[] };
    };
    expect(settings.permissions?.allow).toEqual(
      expect.arrayContaining([...AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES])
    );
    expect(settings.permissions?.allow).not.toContain('mcp__agent-teams__team_stop');
    expect(settings.permissions?.allow).not.toContain('mcp__agent-teams__kanban_clear');
    expect(getConfig).toHaveBeenCalledWith('ops-team');
    expect(getConfigSnapshot).not.toHaveBeenCalled();
    expect(persistInboxMessage).toHaveBeenCalledWith(
      'ops-team',
      'alice',
      expect.objectContaining({
        text: expect.stringContaining('"type":"permission_response"'),
      })
    );
  });

  it('does not broaden admin/runtime teammate permission suggestions', async () => {
    allowConsoleLogs();
    const svc = new TeamProvisioningService({
      getConfig: vi.fn(async () => ({
        projectPath: tempClaudeRoot,
        members: [{ cwd: tempClaudeRoot }],
      })),
    } as any);
    (svc as any).persistInboxMessage = vi.fn();

    await (svc as any).respondToTeammatePermission(
      { teamName: 'ops-team' },
      'alice',
      'req-2',
      true,
      undefined,
      [
        {
          type: 'addRules',
          behavior: 'allow',
          destination: 'localSettings',
          rules: [{ toolName: 'mcp__agent-teams__team_stop' }],
        },
      ]
    );

    const settingsPath = path.join(tempClaudeRoot, '.claude', 'settings.local.json');
    const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as {
      permissions?: { allow?: string[] };
    };
    expect(settings.permissions?.allow).toEqual(['mcp__agent-teams__team_stop']);
  });

  it('builds teammate AskUserQuestion permission responses with answers', () => {
    const svc = new TeamProvisioningService();
    const toolInput = {
      questions: [
        {
          question: 'What type of calculator app would you like?',
          header: 'App type',
          options: [
            { label: 'Web UI (Recommended)', description: 'Browser app' },
            { label: 'CLI', description: 'Terminal app' },
          ],
          multiSelect: false,
        },
      ],
    };

    expect(
      (svc as any).buildTeammatePermissionUpdatedInput(
        'AskUserQuestion',
        toolInput,
        JSON.stringify({
          'What type of calculator app would you like?': 'Web UI (Recommended)',
        })
      )
    ).toEqual({
      ...toolInput,
      answers: {
        'What type of calculator app would you like?': 'Web UI (Recommended)',
      },
    });
  });

  it('preserves blank teammate AskUserQuestion answers', () => {
    const svc = new TeamProvisioningService();
    const toolInput = {
      questions: [
        {
          question: 'Anything else?',
          options: [{ label: 'Skip', description: 'No extra details' }],
        },
      ],
    };

    expect(
      (svc as any).buildTeammatePermissionUpdatedInput('AskUserQuestion', toolInput, '')
    ).toEqual({
      ...toolInput,
      answers: {
        'Anything else?': '',
      },
    });
  });

  it('sends teammate AskUserQuestion permission responses to the teammate inbox', async () => {
    const svc = new TeamProvisioningService();
    const persistInboxMessage = vi.fn();
    (svc as any).persistInboxMessage = persistInboxMessage;
    const toolInput = {
      questions: [
        {
          question: 'What type of calculator app would you like?',
          options: [{ label: 'Web UI (Recommended)', description: 'Browser app' }],
        },
      ],
    };

    await (svc as any).respondToTeammatePermission(
      { teamName: 'ops-team', runId: 'run-1' },
      'bob',
      'perm-1',
      true,
      JSON.stringify({
        'What type of calculator app would you like?': 'Web UI (Recommended)',
      }),
      [],
      'AskUserQuestion',
      toolInput
    );

    expect(persistInboxMessage).toHaveBeenCalledTimes(1);
    const [, recipient, message] = persistInboxMessage.mock.calls[0];
    expect(recipient).toBe('bob');
    expect(JSON.parse(message.text)).toEqual({
      type: 'permission_response',
      request_id: 'perm-1',
      subtype: 'success',
      response: {
        updated_input: {
          ...toolInput,
          answers: {
            'What type of calculator app would you like?': 'Web UI (Recommended)',
          },
        },
        permission_updates: [],
      },
    });
  });

  it('sends teammate denial responses to the teammate inbox', async () => {
    const svc = new TeamProvisioningService();
    const persistInboxMessage = vi.fn();
    (svc as any).persistInboxMessage = persistInboxMessage;

    await (svc as any).respondToTeammatePermission(
      { teamName: 'ops-team', runId: 'run-1' },
      'bob',
      'perm-deny',
      false,
      'Denied by test',
      [],
      'Bash',
      { command: 'echo blocked' }
    );

    expect(persistInboxMessage).toHaveBeenCalledTimes(1);
    const [, recipient, message] = persistInboxMessage.mock.calls[0];
    expect(recipient).toBe('bob');
    expect(JSON.parse(message.text)).toEqual({
      type: 'permission_response',
      request_id: 'perm-deny',
      subtype: 'error',
      error: 'Denied by test',
    });
  });

  it('tags Codex app-server control_request approvals and replies through control_response', async () => {
    const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
      cb?.();
      return true;
    });
    const svc = new TeamProvisioningService();
    const events: unknown[] = [];
    svc.setToolApprovalEventEmitter((event) => events.push(event));
    svc.setMainWindow({
      isDestroyed: () => false,
      isFocused: () => true,
    } as never);

    const run = {
      teamName: 'codex-manual-team',
      runId: 'run-codex-manual',
      request: { color: '#2563eb', displayName: 'Codex Manual Team' },
      child: { stdin: { writable: true, write } },
      pendingApprovals: new Map(),
    };
    const internals = svc as unknown as {
      runs: Map<string, unknown>;
      aliveRunByTeam: Map<string, string>;
      handleControlRequest(run: unknown, msg: Record<string, unknown>): void;
    };
    internals.runs.set(run.runId, run);
    internals.aliveRunByTeam.set(run.teamName, run.runId);

    internals.handleControlRequest(run, {
      request_id: 'codex-approval-1',
      request: {
        subtype: 'can_use_tool',
        tool_name: 'Bash',
        input: {
          provider: 'codex',
          providerRequestId: 'codex:item/commandExecution/requestApproval:item-1',
          command: 'printf ok',
        },
      },
    });

    expect(events[0]).toMatchObject({
      requestId: 'codex-approval-1',
      runId: run.runId,
      teamName: run.teamName,
      providerId: 'codex',
      toolName: 'Bash',
      toolInput: {
        provider: 'codex',
        command: 'printf ok',
      },
    });

    await svc.respondToToolApproval(run.teamName, run.runId, 'codex-approval-1', true);

    expect(write).toHaveBeenCalledTimes(1);
    const firstWrite = write.mock.calls[0]?.[0];
    expect(typeof firstWrite).toBe('string');
    const payload = JSON.parse(firstWrite as string) as Record<string, unknown>;
    expect(payload).toMatchObject({
      type: 'control_response',
      response: {
        subtype: 'success',
        request_id: 'codex-approval-1',
        response: { behavior: 'allow', updatedInput: {} },
      },
    });
  });

  it('keeps control_request approvals pending when control_response write fails so retry works', async () => {
    let failNextWrite = true;
    const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
      if (failNextWrite) {
        failNextWrite = false;
        cb?.(new Error('broken pipe'));
        return false;
      }
      cb?.();
      return true;
    });
    const svc = new TeamProvisioningService();
    svc.setMainWindow({
      isDestroyed: () => false,
      isFocused: () => true,
    } as never);

    const run = {
      teamName: 'anthropic-manual-team',
      runId: 'run-anthropic-manual',
      request: { color: '#7c3aed', displayName: 'Anthropic Manual Team' },
      child: { stdin: { writable: true, write } },
      pendingApprovals: new Map(),
    };
    const internals = svc as unknown as {
      runs: Map<string, unknown>;
      aliveRunByTeam: Map<string, string>;
      handleControlRequest(run: unknown, msg: Record<string, unknown>): void;
    };
    internals.runs.set(run.runId, run);
    internals.aliveRunByTeam.set(run.teamName, run.runId);

    internals.handleControlRequest(run, {
      request_id: 'anthropic-approval-retry',
      request: {
        subtype: 'can_use_tool',
        tool_name: 'Bash',
        input: { command: 'printf ok' },
      },
    });

    await expect(
      svc.respondToToolApproval(run.teamName, run.runId, 'anthropic-approval-retry', true)
    ).rejects.toThrow('broken pipe');
    expect(
      vi.mocked(console.error).mock.calls.some((args) => args.join(' ').includes('broken pipe'))
    ).toBe(true);
    vi.mocked(console.error).mockClear();
    expect(run.pendingApprovals.has('anthropic-approval-retry')).toBe(true);

    await expect(
      svc.respondToToolApproval(run.teamName, run.runId, 'anthropic-approval-retry', true)
    ).resolves.toBeUndefined();
    expect(write).toHaveBeenCalledTimes(2);
    expect(run.pendingApprovals.has('anthropic-approval-retry')).toBe(false);
  });

  it('leaves control_request approvals tracked while a duplicate UI response is in flight', async () => {
    let releaseWrite: ((error?: Error | null) => void) | undefined;
    const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
      releaseWrite = cb;
      return true;
    });
    const svc = new TeamProvisioningService();
    svc.setMainWindow({
      isDestroyed: () => false,
      isFocused: () => true,
    } as never);

    const run = {
      teamName: 'codex-duplicate-response-team',
      runId: 'run-codex-duplicate-response',
      request: { color: '#2563eb', displayName: 'Codex Duplicate Response Team' },
      child: { stdin: { writable: true, write } },
      pendingApprovals: new Map(),
    };
    const internals = svc as unknown as {
      runs: Map<string, unknown>;
      aliveRunByTeam: Map<string, string>;
      handleControlRequest(run: unknown, msg: Record<string, unknown>): void;
    };
    internals.runs.set(run.runId, run);
    internals.aliveRunByTeam.set(run.teamName, run.runId);

    internals.handleControlRequest(run, {
      request_id: 'codex-approval-duplicate',
      request: {
        subtype: 'can_use_tool',
        tool_name: 'Bash',
        input: {
          provider: 'codex',
          providerRequestId: 'codex:item/commandExecution/requestApproval:item-2',
          command: 'printf ok',
        },
      },
    });

    const firstResponse = svc.respondToToolApproval(
      run.teamName,
      run.runId,
      'codex-approval-duplicate',
      true
    );
    await expect(
      svc.respondToToolApproval(run.teamName, run.runId, 'codex-approval-duplicate', false)
    ).resolves.toBeUndefined();
    expect(write).toHaveBeenCalledTimes(1);
    expect(run.pendingApprovals.has('codex-approval-duplicate')).toBe(true);

    releaseWrite?.();
    await expect(firstResponse).resolves.toBeUndefined();
    expect(run.pendingApprovals.has('codex-approval-duplicate')).toBe(false);
  });

  it('keeps AskUserQuestion answers in teammate fallback control responses', async () => {
    const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
      cb?.();
      return true;
    });
    const svc = new TeamProvisioningService({
      getConfig: vi.fn(async () => ({
        projectPath: tempClaudeRoot,
        members: [{ cwd: tempClaudeRoot }],
      })),
    } as any);
    (svc as any).persistInboxMessage = vi.fn();
    const toolInput = {
      questions: [
        {
          question: 'What features do you need?',
          options: [{ label: 'Basic', description: 'Arithmetic' }],
        },
      ],
    };

    await (svc as any).respondToTeammatePermission(
      {
        teamName: 'ops-team',
        runId: 'run-1',
        child: { stdin: { writable: true, write } },
      },
      'bob',
      'perm-2',
      true,
      JSON.stringify({ 'What features do you need?': 'Basic' }),
      [{ type: 'setMode', mode: 'acceptEdits', destination: 'session' }],
      'AskUserQuestion',
      toolInput
    );

    expect(write).toHaveBeenCalledTimes(1);
    const payload = JSON.parse(write.mock.calls[0][0]);
    expect(payload.response.response.updatedInput).toEqual({
      ...toolInput,
      answers: { 'What features do you need?': 'Basic' },
    });
  });

  it('sends teammate fallback control responses without permission suggestions', async () => {
    const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
      cb?.();
      return true;
    });
    const svc = new TeamProvisioningService();
    (svc as any).persistInboxMessage = vi.fn();
    const toolInput = {
      questions: [
        {
          question: 'Anything else?',
          options: [{ label: 'Skip', description: 'No extra details' }],
        },
      ],
    };

    await (svc as any).respondToTeammatePermission(
      {
        teamName: 'ops-team',
        runId: 'run-1',
        child: { stdin: { writable: true, write } },
      },
      'bob',
      'perm-3',
      true,
      '',
      [],
      'AskUserQuestion',
      toolInput
    );

    expect(write).toHaveBeenCalledTimes(1);
    const payload = JSON.parse(write.mock.calls[0][0]);
    expect(payload.response.response.updatedInput).toEqual({
      ...toolInput,
      answers: { 'Anything else?': '' },
    });
  });

  it('uses a non-alarming model delay message before 2 minutes of silence', () => {
    const svc = new TeamProvisioningService();

    expect((svc as any).buildStallProgressMessage(90, '1m 30s')).toBe(
      'Waiting for model response for 1m 30s - logs can be delayed, this is still OK'
    );

    expect(
      (svc as any).buildStallWarningText(90, {
        request: { model: 'sonnet' },
      })
    ).toContain('Logs can sometimes show up after 1-1.5 minutes, and that is still okay.');
  });

  it('marks a model wait as unusual after 2 minutes of silence', () => {
    const svc = new TeamProvisioningService();

    expect((svc as any).buildStallProgressMessage(120, '2m')).toBe(
      'Still waiting for model response for 2m - this is unusual'
    );

    expect(
      (svc as any).buildStallWarningText(120, {
        request: { model: 'sonnet' },
      })
    ).toContain('but no logs for 2m is already unusual.');
  });

  it('formats AskUserQuestion approvals with readable question text', () => {
    const svc = new TeamProvisioningService();

    expect(
      (svc as any).formatToolApprovalBody('AskUserQuestion', {
        questions: [
          {
            question:
              'Я испытываю технические трудности с отправкой сообщений с помощью инструмента `SendMessage`.',
          },
        ],
      })
    ).toBe(
      'Question: Я испытываю технические трудности с отправкой сообщений с помощью инструмента `SendMessage`.'
    );
  });

  it('formats AskUserQuestion approvals with a compact multi-question summary', () => {
    const svc = new TeamProvisioningService();

    expect(
      (svc as any).formatToolApprovalBody('AskUserQuestion', {
        questions: [
          { question: '  First question with   extra spacing.  ' },
          { question: 'Second question.' },
        ],
      })
    ).toBe('Questions (2): First question with extra spacing.');
  });

  it('skips --resume for deterministic bootstrap when previous launch state has no spawned teammates', async () => {
    allowConsoleLogs();
    const teamName = 'resume-skip-team';
    const leadSessionId = 'lead-session-skip';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice', 'bob']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'failed_to_start',
      },
      bob: {
        launchState: 'starting',
        hardFailure: false,
      },
    });

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }, { name: 'bob' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
      'launch spawn EINVAL'
    );

    const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
    expect(launchArgs).toBeTruthy();
    expect(launchArgs).not.toContain('--resume');
    expect(launchArgs).not.toContain(leadSessionId);
  });

  it('skips --resume for deterministic bootstrap when previous active launch is stale', async () => {
    allowConsoleLogs();
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2026-05-03T12:03:00.000Z'));

    const teamName = 'resume-skip-stale-active-team';
    const leadSessionId = 'lead-session-skip-stale-active';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice', 'bob']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        alice: {
          launchState: 'starting',
          hardFailure: false,
        },
        bob: {
          launchState: 'starting',
          hardFailure: false,
        },
      },
      {
        launchPhase: 'active',
        updatedAt: '2026-05-03T12:00:00.000Z',
      }
    );

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }, { name: 'bob' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
      'launch spawn EINVAL'
    );

    const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
    expect(launchArgs).toBeTruthy();
    expect(launchArgs).not.toContain('--resume');
    expect(launchArgs).not.toContain(leadSessionId);
  });

  it('skips --resume for deterministic bootstrap when stale active launch has no live teammates', async () => {
    allowConsoleLogs();
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2026-05-03T12:03:00.000Z'));

    const teamName = 'resume-skip-stale-active-dead-team';
    const leadSessionId = 'lead-session-skip-stale-active-dead';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice', 'bob']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        alice: {
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          firstSpawnAcceptedAt: '2026-05-03T12:00:15.000Z',
          hardFailure: false,
        },
        bob: {
          launchState: 'starting',
          hardFailure: false,
        },
      },
      {
        launchPhase: 'active',
        updatedAt: '2026-05-03T12:00:00.000Z',
      }
    );

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }, { name: 'bob' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
      'launch spawn EINVAL'
    );

    const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
    expect(launchArgs).toBeTruthy();
    expect(launchArgs).not.toContain('--resume');
    expect(launchArgs).not.toContain(leadSessionId);
  });

  it('skips --resume with deterministic bootstrap even after an accepted failed spawn', async () => {
    allowConsoleLogs();
    const teamName = 'resume-keep-team';
    const leadSessionId = 'lead-session-keep';
    const acceptedAt = '2026-04-14T12:00:00.000Z';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'failed_to_start',
        agentToolAccepted: true,
        firstSpawnAcceptedAt: acceptedAt,
        hardFailureReason: 'Teammate did not join within the launch grace window.',
      },
    });

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
      'launch spawn EINVAL'
    );

    const launchArgs = vi.mocked(spawnCli).mock.calls[0]?.[1] as string[];
    expect(launchArgs).not.toContain('--resume');
    expect(launchArgs).not.toContain(leadSessionId);
  });

  it('skips --resume with deterministic bootstrap after Codex backend normalization', async () => {
    allowConsoleLogs();
    const teamName = 'resume-backend-change-team';
    const leadSessionId = 'lead-session-backend-change';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { CODEX_API_KEY: 'test' },
      authSource: 'codex_runtime',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    (svc as any).teamMetaStore = {
      getMeta: vi.fn(async () => ({ providerBackendId: 'adapter' })),
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
    };

    await expect(
      svc.launchTeam(
        {
          teamName,
          cwd: tempClaudeRoot,
          providerId: 'codex',
          providerBackendId: 'codex-native',
          model: 'gpt-5.4',
        },
        () => {}
      )
    ).rejects.toThrow('launch spawn EINVAL');

    const launchArgs = vi.mocked(spawnCli).mock.calls.at(-1)?.[1] as string[];
    expect(launchArgs).toBeTruthy();
    expect(launchArgs).not.toContain('--resume');
    expect(launchArgs).not.toContain(leadSessionId);
  });

  it('does not seed the previous lead session id when deterministic bootstrap skips resume', async () => {
    allowConsoleLogs();
    const teamName = 'resume-seed-session-team';
    const leadSessionId = 'lead-session-seeded';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {});

    const launchArgs = vi.mocked(spawnCli).mock.calls.at(-1)?.[1] as string[];
    expect(launchArgs).not.toContain('--resume');
    expect((svc as any).pathExists).not.toHaveBeenCalled();
    expect(svc.getCurrentLeadSessionId(teamName)).toBeNull();

    await svc.cancelProvisioning(runId);
  });

  it('waits for child close before handling launch process exit so stream-json can drain', async () => {
    allowConsoleLogs();
    const teamName = 'launch-close-drains-stdout-team';
    const leadSessionId = 'lead-session-close-drain';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const handleProcessExit = vi
      .spyOn(svc as any, 'handleProcessExit')
      .mockResolvedValue(undefined);

    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {});

    child.emit('exit', 0);
    await Promise.resolve();
    expect(handleProcessExit).not.toHaveBeenCalled();

    child.emit('close', 0);
    await vi.waitFor(() => expect(handleProcessExit).toHaveBeenCalledTimes(1));
    expect(handleProcessExit.mock.calls[0]?.[1]).toBe(0);

    await svc.cancelProvisioning(runId);
  });

  it('flushes a final newline-less bootstrap completion event without promoting launch ready', async () => {
    allowConsoleLogs();
    const teamName = 'launch-close-flushes-final-json-team';
    const leadSessionId = 'lead-session-final-json-flush';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const complete = vi
      .spyOn(svc as any, 'handleProvisioningTurnComplete')
      .mockResolvedValue(undefined);

    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {});
    const run = (svc as any).runs.get(runId);
    expect(run).toBeTruthy();

    child.stdout.emit(
      'data',
      Buffer.from(
        JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'completed',
          run_id: runId,
          team_name: teamName,
          seq: 1,
          failed_members: [],
        }),
        'utf8'
      )
    );
    await Promise.resolve();
    expect(complete).not.toHaveBeenCalled();

    (svc as any).flushStdoutParserCarry(run);

    expect(complete).not.toHaveBeenCalled();
    expect(run.lastDeterministicBootstrapSeq).toBe(1);
    await svc.cancelProvisioning(runId);
  });

  it('flushes a final newline-less success result and completes deterministic launch', async () => {
    allowConsoleLogs();
    const teamName = 'launch-close-flushes-final-success-team';
    const leadSessionId = 'lead-session-final-success-flush';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const complete = vi
      .spyOn(svc as any, 'handleProvisioningTurnComplete')
      .mockImplementation(async (run: any) => {
        expect(run.processClosed).toBe(false);
        expect(run.firstRealTurnSucceeded).toBe(true);
        run.provisioningComplete = true;
      });

    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {});

    child.stdout.emit(
      'data',
      Buffer.from(
        JSON.stringify({
          type: 'result',
          subtype: 'success',
        }),
        'utf8'
      )
    );
    await Promise.resolve();
    expect(complete).not.toHaveBeenCalled();

    child.emit('close', 0);

    await vi.waitFor(() => expect(complete).toHaveBeenCalledTimes(1));
  });

  it('does not promote deterministic launch from bootstrap completed before first real turn succeeds', async () => {
    allowConsoleLogs();
    const teamName = 'bootstrap-completed-before-first-turn-team';
    const leadSessionId = 'lead-session-bootstrap-only';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const complete = vi
      .spyOn(svc as any, 'handleProvisioningTurnComplete')
      .mockResolvedValue(undefined);

    let runId = '';
    try {
      const launch = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {});
      runId = launch.runId;

      child.stdout.emit(
        'data',
        Buffer.from(
          `${JSON.stringify({
            type: 'system',
            subtype: 'team_bootstrap',
            event: 'completed',
            run_id: runId,
            team_name: teamName,
            seq: 1,
            failed_members: [],
          })}\n`,
          'utf8'
        )
      );

      await Promise.resolve();
      expect(complete).not.toHaveBeenCalled();
    } finally {
      if (runId) {
        await svc.cancelProvisioning(runId).catch(() => undefined);
      }
    }
  });

  it('promotes deterministic create bootstrap completion when no first turn was enqueued', async () => {
    allowConsoleLogs();
    const teamName = 'bootstrap-completed-no-first-turn-team';
    const child = createRunningChild();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
      getMembers: vi.fn(async () => []),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
      getMeta: vi.fn(async () => null),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { CODEX_API_KEY: 'test' },
      authSource: 'codex_runtime',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).startStallWatchdog = vi.fn();
    (svc as any).stopStallWatchdog = vi.fn();
    (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
      providerId: 'codex',
      providerBackendId: 'codex-native',
      selectedModel: 'gpt-5.5',
      selectedModelKind: 'explicit',
      resolvedLaunchModel: 'gpt-5.5',
      catalogId: 'gpt-5.5',
      catalogSource: 'test',
      catalogFetchedAt: '2026-05-07T00:00:00.000Z',
      selectedEffort: 'medium',
      resolvedEffort: 'medium',
      selectedFastMode: null,
      resolvedFastMode: null,
      fastResolutionReason: null,
    }));
    const complete = vi
      .spyOn(svc as any, 'handleProvisioningTurnComplete')
      .mockResolvedValue(undefined);

    const { runId } = await svc.createTeam(
      {
        teamName,
        cwd: tempClaudeRoot,
        providerId: 'codex',
        providerBackendId: 'codex-native',
        model: 'gpt-5.5',
        members: [{ name: 'alice' }],
      },
      () => {}
    );
    const run = (svc as any).runs.get(runId);
    expect(run).toBeTruthy();
    expect(run.requiresFirstRealTurnSuccess).toBe(false);

    child.stdout.emit(
      'data',
      Buffer.from(
        `${JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'completed',
          run_id: runId,
          team_name: teamName,
          seq: 1,
          failed_members: [],
        })}\n`,
        'utf8'
      )
    );

    await vi.waitFor(() => expect(complete).toHaveBeenCalledTimes(1));
    expect(complete).toHaveBeenCalledWith(run);
    await svc.cancelProvisioning(runId);
  });

  it('recovers ready progress when deterministic finalization stalls after first real turn success', async () => {
    allowConsoleLogs();
    const teamName = 'create-completed-bootstrap-finalization-stall';
    const child = createRunningChild();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
      getMembers: vi.fn(async () => []),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
      getMeta: vi.fn(async () => null),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { CODEX_API_KEY: 'test' },
      authSource: 'codex_runtime',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).startStallWatchdog = vi.fn();
    (svc as any).stopStallWatchdog = vi.fn();
    (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
      providerId: 'codex',
      providerBackendId: 'codex-native',
      selectedModel: 'gpt-5.5',
      selectedModelKind: 'explicit',
      resolvedLaunchModel: 'gpt-5.5',
      catalogId: 'gpt-5.5',
      catalogSource: 'test',
      catalogFetchedAt: '2026-05-07T00:00:00.000Z',
      selectedEffort: 'medium',
      resolvedEffort: 'medium',
      selectedFastMode: null,
      resolvedFastMode: null,
      fastResolutionReason: null,
    }));
    const progressStates: string[] = [];
    const { runId } = await svc.createTeam(
      {
        teamName,
        cwd: tempClaudeRoot,
        providerId: 'codex',
        providerBackendId: 'codex-native',
        model: 'gpt-5.5',
        members: [{ name: 'alice' }, { name: 'tom' }],
      },
      (progress) => {
        progressStates.push(progress.state);
      }
    );
    const run = (svc as any).runs.get(runId);
    expect(run).toBeTruthy();
    run.deterministicBootstrap = true;
    run.requiresFirstRealTurnSuccess = true;
    run.firstRealTurnSucceeded = true;
    run.provisioningComplete = true;

    writeBootstrapState(
      teamName,
      [
        { name: 'alice', status: 'bootstrap_confirmed' },
        { name: 'tom', status: 'bootstrap_confirmed' },
      ],
      new Date(Date.now() + 1_000).toISOString()
    );

    expect(progressStates.at(-1)).not.toBe('ready');

    await (svc as any).recoverDeterministicBootstrapCompletion(run);
    expect(progressStates.at(-1)).toBe('ready');
    expect((svc as any).provisioningRunByTeam.has(teamName)).toBe(false);
    expect((svc as any).aliveRunByTeam.get(teamName)).toBe(runId);
  });

  it('does not recover ready progress from completed bootstrap-state when the lead child is gone', async () => {
    allowConsoleLogs();
    const teamName = 'create-completed-bootstrap-dead-lead';
    const child = createRunningChild();
    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const mcpConfigBuilder = {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-create.json'),
      removeConfigFile: vi.fn(async () => {}),
    };
    const membersMetaStore = {
      writeMembers: vi.fn(async () => {}),
      getMembers: vi.fn(async () => []),
    };
    const teamMetaStore = {
      writeMeta: vi.fn(async () => {}),
      deleteMeta: vi.fn(async () => {}),
      getMeta: vi.fn(async () => null),
    };

    const svc = new TeamProvisioningService(
      undefined,
      undefined,
      membersMetaStore as any,
      undefined,
      mcpConfigBuilder as any,
      teamMetaStore as any
    );
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { CODEX_API_KEY: 'test' },
      authSource: 'codex_runtime',
    }));
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async () => false);
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).startStallWatchdog = vi.fn();
    (svc as any).stopStallWatchdog = vi.fn();
    (svc as any).resolveAndValidateLaunchIdentity = vi.fn(async () => ({
      providerId: 'codex',
      providerBackendId: 'codex-native',
      selectedModel: 'gpt-5.5',
      selectedModelKind: 'explicit',
      resolvedLaunchModel: 'gpt-5.5',
      catalogId: 'gpt-5.5',
      catalogSource: 'test',
      catalogFetchedAt: '2026-05-07T00:00:00.000Z',
      selectedEffort: 'medium',
      resolvedEffort: 'medium',
      selectedFastMode: null,
      resolvedFastMode: null,
      fastResolutionReason: null,
    }));

    const progressStates: string[] = [];
    const { runId } = await svc.createTeam(
      {
        teamName,
        cwd: tempClaudeRoot,
        providerId: 'codex',
        providerBackendId: 'codex-native',
        model: 'gpt-5.5',
        members: [{ name: 'alice' }],
      },
      (progress) => {
        progressStates.push(progress.state);
      }
    );
    const run = (svc as any).runs.get(runId);
    expect(run).toBeTruthy();
    run.deterministicBootstrap = true;
    run.provisioningComplete = true;
    run.child = null;

    writeBootstrapState(
      teamName,
      [{ name: 'alice', status: 'bootstrap_confirmed' }],
      new Date(Date.now() + 1_000).toISOString()
    );

    await (svc as any).recoverDeterministicBootstrapCompletion(run);

    expect(progressStates).not.toContain('ready');
    expect((svc as any).aliveRunByTeam.get(teamName)).toBeUndefined();
  });

  it('does not verify provisioning again after flushing a final newline-less error result', async () => {
    allowConsoleLogs();
    const teamName = 'launch-close-flushes-final-error-team';
    const leadSessionId = 'lead-session-final-error-flush';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const waitForValidConfig = vi
      .spyOn(svc as any, 'waitForValidConfig')
      .mockResolvedValue({ ok: false });
    const progressStates: string[] = [];

    await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, (progress) => {
      progressStates.push(progress.state);
    });

    child.stdout.emit(
      'data',
      Buffer.from(
        JSON.stringify({
          type: 'result',
          subtype: 'error',
          error: 'runtime failed before bootstrap completed',
        }),
        'utf8'
      )
    );
    child.emit('close', 1);

    await Promise.resolve();
    expect(waitForValidConfig).not.toHaveBeenCalled();
    expect(progressStates).toContain('failed');
    expect(progressStates).not.toContain('verifying');
  });

  it('clears lead-only bootstrap state before cleanup when deterministic launch process exits', async () => {
    allowConsoleLogs();
    const teamName = 'lead-only-launch-exit-clears-bootstrap-state';
    const leadSessionId = 'lead-session-lead-only-exit';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, []);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).writeLaunchFailureArtifactPackBestEffort = vi.fn();
    vi.spyOn(svc as any, 'waitForValidConfig').mockResolvedValue({
      ok: true,
      location: 'configured',
      configPath: path.join(tempTeamsBase, teamName, 'config.json'),
    });
    vi.spyOn(svc as any, 'waitForTeamInList').mockResolvedValue(true);
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    const progressStates: string[] = [];
    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, (progress) => {
      progressStates.push(progress.state);
    });

    fs.writeFileSync(
      getTeamBootstrapStatePath(teamName),
      `${JSON.stringify(
        {
          version: 1,
          runId,
          teamName,
          ownerPid: child.pid,
          startedAt: Date.now(),
          updatedAt: Date.now(),
          phase: 'auditing_truth',
          members: [],
        },
        null,
        2
      )}\n`,
      'utf8'
    );

    child.emit('close', 1);

    await vi.waitFor(() => expect(progressStates).toContain('disconnected'));
    expect(fs.existsSync(getTeamBootstrapStatePath(teamName))).toBe(false);
    expect(fs.existsSync(getTeamLaunchStatePath(teamName))).toBe(false);
    expect(fs.existsSync(getTeamLaunchSummaryPath(teamName))).toBe(false);
  });

  it('persists failed member launch state before cleanup when deterministic launch process exits', async () => {
    allowConsoleLogs();
    const teamName = 'member-launch-exit-finalizes-before-cleanup';
    const leadSessionId = 'lead-session-member-exit';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).writeLaunchFailureArtifactPackBestEffort = vi.fn();
    vi.spyOn(svc as any, 'waitForValidConfig').mockResolvedValue({
      ok: true,
      location: 'configured',
      configPath: path.join(tempTeamsBase, teamName, 'config.json'),
    });
    vi.spyOn(svc as any, 'waitForTeamInList').mockResolvedValue(true);
    (svc as any).pathExists = vi.fn(async (targetPath: string) => {
      const basename = path.basename(targetPath);
      return basename === `${leadSessionId}.jsonl` || basename === 'alice.json';
    });

    const progressStates: string[] = [];
    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, (progress) => {
      progressStates.push(progress.state);
    });

    fs.writeFileSync(
      getTeamBootstrapStatePath(teamName),
      `${JSON.stringify(
        {
          version: 1,
          runId,
          teamName,
          ownerPid: 987654321,
          startedAt: Date.now(),
          updatedAt: Date.now(),
          phase: 'auditing_truth',
          members: [{ name: 'alice', status: 'registered', lastAttemptAt: Date.now() }],
        },
        null,
        2
      )}\n`,
      'utf8'
    );

    child.emit('close', 1);

    await vi.waitFor(() => expect(progressStates).toContain('disconnected'));

    const persisted = JSON.parse(fs.readFileSync(getTeamLaunchStatePath(teamName), 'utf8')) as {
      teamLaunchState?: string;
      members?: Record<string, { launchState?: string; hardFailureReason?: string }>;
    };
    expect(persisted.teamLaunchState).toBe('partial_failure');
    expect(persisted.members?.alice?.launchState).toBe('failed_to_start');
    expect(persisted.members?.alice?.hardFailureReason).toContain('team provisioned but not alive');

    const reconciled = await (svc as any).reconcilePersistedLaunchState(teamName);
    expect(reconciled.snapshot?.teamLaunchState).toBe('partial_failure');
    expect(reconciled.statuses.alice?.launchState).toBe('failed_to_start');
  });

  it('does not verify provisioning while auth retry is scheduled from final newline-less output', async () => {
    allowConsoleLogs();
    const teamName = 'launch-close-flushes-final-auth-team';
    const leadSessionId = 'lead-session-final-auth-flush';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    const child = createRunningChild();
    vi.mocked(spawnCli).mockReturnValue(child as any);

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).persistLaunchStateSnapshot = vi.fn(async () => {});
    (svc as any).startFilesystemMonitor = vi.fn();
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );
    const waitForValidConfig = vi
      .spyOn(svc as any, 'waitForValidConfig')
      .mockResolvedValue({ ok: false });
    const respawnAfterAuthFailure = vi
      .spyOn(svc as any, 'respawnAfterAuthFailure')
      .mockResolvedValue(undefined);
    const progressStates: string[] = [];

    const { runId } = await svc.launchTeam({ teamName, cwd: tempClaudeRoot }, (progress) => {
      progressStates.push(progress.state);
    });
    const run = (svc as any).runs.get(runId);

    child.stdout.emit(
      'data',
      Buffer.from(
        JSON.stringify({
          type: 'assistant',
          content: [{ type: 'text', text: 'invalid api key' }],
        }),
        'utf8'
      )
    );
    child.emit('close', 1);

    await Promise.resolve();
    expect(run.authRetryInProgress).toBe(true);
    expect(respawnAfterAuthFailure).toHaveBeenCalledWith(run);
    expect(waitForValidConfig).not.toHaveBeenCalled();
    expect(progressStates).not.toContain('verifying');
  });

  it('warns but still starts deterministic launch with more than eight primary teammates', async () => {
    allowConsoleLogs();
    const members = Array.from({ length: 9 }, (_, index) => `member-${index + 1}`);
    const { progressUpdates } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-large-primary-team-warning',
      members,
    });

    expect(spawnCli).toHaveBeenCalled();
    expect(progressUpdates[0]?.warnings).toEqual(
      expect.arrayContaining([expect.stringContaining('9 primary teammates')])
    );
    expect(progressUpdates[0]?.warnings?.join('\n')).toContain('Launches above 8 teammates');
  });

  it('fails before spawning when deterministic launch exceeds the current primary teammate cap', async () => {
    allowConsoleLogs();
    const members = Array.from({ length: 21 }, (_, index) => `member-${index + 1}`);

    await expect(
      startDeterministicLaunchCloseHarness({
        teamName: 'launch-too-many-primary-members',
        members,
      })
    ).rejects.toThrow(/up to 20 primary teammates/);
    expect(spawnCli).not.toHaveBeenCalled();
  });

  it('keeps SessionStart hook payloads out of user-facing launch errors', async () => {
    allowConsoleLogs();
    const { child, progressUpdates } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-hook-payload-sanitized',
      members: ['alice'],
    });
    const hookPayload = [
      '<EXTREMELY_IMPORTANT>',
      'You have superpowers.',
      'digraph skill_flow {',
      'Might any skill apply?',
      'Invoke Skill tool',
      'TodoWrite',
      'superpowers:using-superpowers',
      '</EXTREMELY_IMPORTANT>',
    ].join('\n');

    child.stdout.emit(
      'data',
      Buffer.from(
        `${JSON.stringify({
          type: 'system',
          subtype: 'hook_started',
          hook_name: 'SessionStart:startup',
        })}\n${JSON.stringify({
          type: 'system',
          subtype: 'hook_response',
          hook_name: 'SessionStart:startup',
          output: JSON.stringify({
            hookSpecificOutput: {
              hookEventName: 'SessionStart',
              additionalContext: hookPayload,
            },
          }),
          exit_code: 0,
          outcome: 'success',
        })}\n`,
        'utf8'
      )
    );
    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.message).toBe('Launch bootstrap was not confirmed');
    expect(finalProgress.error).toContain('No team_bootstrap event was received');
    expect(finalProgress.error).not.toMatch(
      /skill_flow|EXTREMELY_IMPORTANT|additionalContext|TodoWrite|Skill tool/
    );
    expect(finalProgress.cliLogsTail).toMatch(/skill_flow|EXTREMELY_IMPORTANT|additionalContext/);
  });

  it('does not leak a mid-buffer hook payload when stdout starts inside hook JSON', async () => {
    allowConsoleLogs();
    const { child, progressUpdates, run } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-hook-mid-buffer-sanitized',
      members: ['alice'],
    });
    run.claudeLogLines = [];
    run.stdoutBuffer =
      'evant or requested skills BEFORE any response or action. digraph skill_flow { EXTREMELY_IMPORTANT TodoWrite Skill tool }';
    run.stderrBuffer = '';

    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.error).toContain('No team_bootstrap event was received');
    expect(finalProgress.error).not.toMatch(/skill_flow|EXTREMELY_IMPORTANT|TodoWrite|Skill tool/);
    expect(finalProgress.cliLogsTail).toContain('skill_flow');
  });

  it('reports the last bootstrap phase when Codex exits before spawning teammates', async () => {
    allowConsoleLogs();
    const { child, progressUpdates, runId, teamName } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-bootstrap-phase-before-spawn',
      members: ['alice'],
    });

    child.stdout.emit(
      'data',
      Buffer.from(
        `${JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'started',
          run_id: runId,
          team_name: teamName,
          seq: 1,
        })}\n${JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'phase_changed',
          phase: 'acquiring_bootstrap_lock',
          run_id: runId,
          team_name: teamName,
          seq: 2,
        })}\n`,
        'utf8'
      )
    );
    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.message).toBe('Launch bootstrap was not confirmed');
    expect(finalProgress.error).toContain('before teammate spawning started');
    expect(finalProgress.error).toContain('phase_changed/acquiring_bootstrap_lock');
  });

  it('reports pending teammates when Codex exits after member spawning starts', async () => {
    allowConsoleLogs();
    const { child, progressUpdates, runId, teamName } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-bootstrap-pending-members',
      members: ['alice', 'bob'],
    });

    child.stdout.emit(
      'data',
      Buffer.from(
        `${JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'started',
          run_id: runId,
          team_name: teamName,
          seq: 1,
        })}\n${JSON.stringify({
          type: 'system',
          subtype: 'team_bootstrap',
          event: 'member_spawn_started',
          member_name: 'alice',
          run_id: runId,
          team_name: teamName,
          seq: 2,
        })}\n`,
        'utf8'
      )
    );
    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.message).toBe('Launch bootstrap was not confirmed');
    expect(finalProgress.error).toContain('Pending teammates: alice, bob');
  });

  it('preserves real stderr as the user-facing launch error', async () => {
    allowConsoleLogs();
    const { child, progressUpdates } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-real-stderr-preserved',
      members: ['alice'],
    });

    child.stderr.emit('data', Buffer.from('Fatal runtime exploded before bootstrap\n', 'utf8'));
    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.error).toBe('Fatal runtime exploded before bootstrap');
    expect(finalProgress.message).not.toBe('Launch bootstrap was not confirmed');
  });

  it('preserves the CLI login hint on launch process exit', async () => {
    allowConsoleLogs();
    const { child, progressUpdates, svc } = await startDeterministicLaunchCloseHarness({
      teamName: 'launch-login-hint-preserved',
      members: ['alice'],
    });
    vi.spyOn(svc as any, 'handleAuthFailureInOutput').mockImplementation(() => {});

    child.stderr.emit('data', Buffer.from('Please run /login to continue\n', 'utf8'));
    child.emit('close', 1);

    await vi.waitFor(() => expect(progressUpdates.at(-1)?.state).toBe('failed'));
    const finalProgress = progressUpdates.at(-1);
    expect(finalProgress.error).toContain('reports it is not authenticated');
    expect(finalProgress.error).toContain('Please run /login');
  });

  it('clears stale team-scoped transient state before starting a new launch run', async () => {
    allowConsoleLogs();
    vi.useFakeTimers();

    const teamName = 'launch-clears-stale-runtime-state';
    const leadSessionId = 'lead-session-stale-state';
    writeLaunchConfig(teamName, tempClaudeRoot, leadSessionId, ['alice']);

    vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/claude');
    vi.mocked(spawnCli).mockImplementation(() => {
      throw new Error('launch spawn EINVAL');
    });

    const svc = new TeamProvisioningService(undefined, undefined, undefined, undefined, {
      writeConfigFile: vi.fn(async () => '/mock/mcp-config-launch.json'),
      removeConfigFile: vi.fn(async () => {}),
    } as any);
    (svc as any).buildProvisioningEnv = vi.fn(async () => ({
      env: { ANTHROPIC_API_KEY: 'test' },
      authSource: 'anthropic_api_key',
    }));
    (svc as any).resolveLaunchExpectedMembers = vi.fn(async () => ({
      members: [{ name: 'alice' }],
      source: 'members-meta',
      warning: undefined,
    }));
    (svc as any).normalizeTeamConfigForLaunch = vi.fn(async () => {});
    (svc as any).assertConfigLeadOnlyForLaunch = vi.fn(async () => {});
    (svc as any).updateConfigProjectPath = vi.fn(async () => {});
    (svc as any).restorePrelaunchConfig = vi.fn(async () => {});
    (svc as any).validateAgentTeamsMcpRuntime = vi.fn(async () => {});
    (svc as any).pathExists = vi.fn(async (targetPath: string) =>
      targetPath.endsWith(`${leadSessionId}.jsonl`)
    );

    const autoResumeProvisioning = {
      getCurrentRunId: vi.fn(() => 'run-1' as string | null),
      isTeamAlive: vi.fn(() => true),
      sendMessageToTeam: vi.fn(async () => undefined),
    };
    initializeAutoResumeService(autoResumeProvisioning);

    const configManagerModule = await import('@main/services/infrastructure/ConfigManager');
    const configManager = configManagerModule.ConfigManager.getInstance();
    const actualConfig = configManager.getConfig();
    const getConfigSpy = vi.spyOn(configManager, 'getConfig').mockImplementation(
      () =>
        ({
          ...actualConfig,
          notifications: {
            ...actualConfig.notifications,
            autoResumeOnRateLimit: true,
          },
        }) as never
    );

    try {
      getAutoResumeService().handleRateLimitMessage(
        teamName,
        "You've hit your limit. Resets in 5 minutes.",
        new Date('2026-04-17T12:00:00.000Z')
      );

      (svc as any).relayedLeadInboxMessageIds.set(teamName, new Set(['stale-msg']));
      (svc as any).liveLeadProcessMessages.set(teamName, [
        {
          from: 'team-lead',
          text: 'Old transient message',
          timestamp: '2026-04-17T12:00:00.000Z',
          read: true,
          source: 'lead_process',
          messageId: 'lead-turn-old-run-1',
        },
      ]);
      (svc as any).pendingTimeouts.set(
        `same-team-deferred:${teamName}`,
        setTimeout(() => undefined, 60_000)
      );

      await expect(svc.launchTeam({ teamName, cwd: tempClaudeRoot }, () => {})).rejects.toThrow(
        'launch spawn EINVAL'
      );

      expect((svc as any).relayedLeadInboxMessageIds.has(teamName)).toBe(false);
      expect((svc as any).liveLeadProcessMessages.has(teamName)).toBe(false);
      expect((svc as any).pendingTimeouts.has(`same-team-deferred:${teamName}`)).toBe(false);

      await vi.advanceTimersByTimeAsync(5 * 60 * 1000 + 30 * 1000 + 100);
      expect(autoResumeProvisioning.sendMessageToTeam).not.toHaveBeenCalled();
    } finally {
      getConfigSpy.mockRestore();
    }
  });

  it('marks persisted bootstrap as failed when member transcript shows an unsupported model error', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-unsupported-model';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const errorAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeLaunchState(teamName, leadSessionId, {
      jack: {
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: acceptedAt,
      },
    });

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${leadSessionId}.jsonl`),
      `${JSON.stringify({
        timestamp: new Date(Date.now() - 10_000).toISOString(),
        teamName,
        type: 'user',
        message: { role: 'user', content: 'Lead bootstrap context' },
      })}\n`,
      'utf8'
    );
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: errorAt,
          teamName,
          agentName: 'jack',
          type: 'assistant',
          isApiErrorMessage: true,
          message: {
            role: 'assistant',
            content: [
              {
                type: 'text',
                text: `API Error: 400 {"type":"error","error":{"type":"api_error","message":"Codex API error (400): {\\"detail\\":\\"The 'gpt-5.2-codex' model is not supported when using Codex with a ChatGPT account.\\"}"}}`,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack?.status).toBe('error');
    expect(result.statuses.jack?.launchState).toBe('failed_to_start');
    expect(result.statuses.jack?.error).toContain('gpt-5.2-codex');
    expect(result.statuses.jack?.hardFailureReason).toContain('not supported');
    expect(result.teamLaunchState).toBe('partial_failure');
  });

  it('marks persisted bootstrap as confirmed when member transcript shows successful member_briefing', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-transcript-success';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const successAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice', 'bob']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: true,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: acceptedAt,
      },
      bob: {
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: acceptedAt,
      },
    });

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${leadSessionId}.jsonl`),
      `${JSON.stringify({
        timestamp: new Date(Date.now() - 10_000).toISOString(),
        teamName,
        type: 'user',
        message: { role: 'user', content: 'Lead bootstrap context' },
      })}\n`,
      'utf8'
    );
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "alice".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for alice on team "${teamName}" (${teamName}).\nTask briefing for alice:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'alice',
            {
              alive: true,
              livenessKind: 'runtime_process',
              runtimeDiagnostic: 'verified runtime process detected',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.alice).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
    });
    expect(result.statuses.alice?.error).toBeUndefined();
  });

  it('heals terminal bootstrap-state failures when transcript shows successful member_briefing', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-transcript-heals';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const successAt = new Date(Date.now() - 60_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason:
            'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before submitted-confirmation timeout (3m). Last transport stage: bootstrap_submitted',
        },
      ],
      failureAt
    );

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for jack on team "${teamName}" (${teamName}).\nTask briefing for jack:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
      error: undefined,
    });
  });

  it('heals terminal bootstrap-state failures when runtime proof confirms member_briefing', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-runtime-proof-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const proofAt = new Date(Date.now() - 60_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();
    const proofToken = 'proof-token-jack';
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapProofToken: proofToken,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.',
        },
      ],
      failureAt
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_confirmed',
        timestamp: proofAt,
        pid: 1234,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        source: 'member_briefing_tool_success',
        bootstrapProofToken: proofToken,
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      hardFailure: false,
      error: undefined,
    });
  });

  it('heals terminal bootstrap-state failures when native app-managed proof matches token and hashes', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-native-runtime-proof-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const proofAt = new Date(Date.now() - 60_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();
    const proofToken = 'proof-token-jack-native';
    const bootstrapRunId = 'run-native-proof';
    const contextHash = 'a'.repeat(64);
    const briefingHash = 'b'.repeat(64);
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapProofToken: proofToken,
            bootstrapRunId,
            bootstrapProofMode: 'native_app_managed_context',
            bootstrapContextHash: contextHash,
            bootstrapBriefingHash: briefingHash,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.',
        },
      ],
      failureAt
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_confirmed',
        timestamp: proofAt,
        pid: 1234,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        runId: bootstrapRunId,
        source: 'native_app_managed_bootstrap_private_turn',
        bootstrapProofToken: proofToken,
        contextHash,
        briefingHash,
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      hardFailure: false,
      error: undefined,
    });
  });

  it('heals process-table unavailable launch-state failures from runtime bootstrap proof', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-table-unavailable-proof-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const proofAt = new Date(Date.now() - 60_000).toISOString();
    const proofToken = 'proof-token-alice';
    const bootstrapRunId = 'run-process-table-unavailable';
    const runtimePid = 35906;
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'alice.runtime.jsonl');
    const processTableReason =
      'runtime pid could not be verified because process table is unavailable';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'alice'
        ? {
            ...member,
            agentId: `alice@${teamName}`,
            backendType: 'process',
            tmuxPaneId: `process:${runtimePid}`,
            runtimePid,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapProofToken: proofToken,
            bootstrapRunId,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    const snapshot = createPersistedLaunchSnapshot({
      teamName,
      leadSessionId,
      launchPhase: 'finished',
      expectedMembers: ['alice'],
      members: {
        alice: {
          name: 'alice',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          runtimeRunId: bootstrapRunId,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: processTableReason,
          livenessKind: 'registered_only',
          runtimeDiagnostic: processTableReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: acceptedAt,
        },
      },
    });
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_confirmed',
        timestamp: proofAt,
        pid: runtimePid,
        teamName,
        agentName: 'alice',
        agentId: `alice@${teamName}`,
        bootstrapRunId,
        source: 'member_briefing_tool_success',
        bootstrapProofToken: proofToken,
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await privateHarness(svc).applyBootstrapTranscriptEvidenceOverlay(snapshot);

    expect(result?.members.alice).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
    });
  });

  it('heals cleanup-finalized launch failures when bootstrap-state confirms an Anthropic primary member', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-cleanup-finalized-bootstrap-state-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const bootstrapAt = new Date(Date.now() - 60_000).toISOString();
    const cleanupAt = new Date(Date.now() - 30_000).toISOString();
    const runtimePid = 45_724;

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          providerId: 'anthropic',
          model: 'claude-opus-4-7',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'anthropic',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: 'Launch ended before teammate bootstrap completed.',
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: 'persisted runtime pid is not alive',
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(bootstrapAt),
        },
      ],
      bootstrapAt
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'jack',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: 'persisted runtime pid is not alive',
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'claude-opus-4-7',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.jack?.hardFailureReason).toBeUndefined();
    expect(result.statuses.jack?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.jack?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('heals provisioned-but-not-alive launch failures when bootstrap-state confirms the member', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-provisioned-not-alive-bootstrap-state-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const bootstrapAt = new Date(Date.now() - 60_000).toISOString();
    const cleanupAt = new Date(Date.now() - 30_000).toISOString();
    const runtimePid = 27_036;
    const exitReason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
    const processTableReason =
      'runtime pid could not be verified because process table is unavailable';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        tom: {
          providerId: 'anthropic',
          model: 'sonnet',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'anthropic',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: exitReason,
          livenessKind: 'registered_only',
          runtimeDiagnostic: processTableReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(bootstrapAt),
        },
      ],
      bootstrapAt
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: processTableReason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'sonnet',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.hardFailureReason).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('does not heal provisioned-but-not-alive live status when refreshed runtime metadata is unsafe', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-provisioned-not-alive-live-runtime-error-stays-failed';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const bootstrapAt = new Date(Date.now() - 60_000).toISOString();
    const cleanupAt = new Date(Date.now() - 30_000).toISOString();
    const runtimePid = 27_036;
    const exitReason =
      'CLI process exited (code 1) - team provisioned but not alive; process table unavailable';
    const processTableReason =
      'runtime pid could not be verified because process table is unavailable';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        tom: {
          providerId: 'anthropic',
          model: 'sonnet',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'anthropic',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: exitReason,
          livenessKind: 'registered_only',
          runtimeDiagnostic: processTableReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(bootstrapAt),
        },
      ],
      bootstrapAt
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'not_found',
              pidSource: 'process_table',
              runtimeDiagnostic: 'Runtime process crashed',
              runtimeDiagnosticSeverity: 'error',
              metricsPid: runtimePid,
              model: 'sonnet',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'not_found',
      hardFailure: true,
      hardFailureReason: exitReason,
      runtimeDiagnostic: 'Runtime process crashed',
      runtimeDiagnosticSeverity: 'error',
    });
  });

  it('heals process-table unavailable failure when Anthropic bootstrap confirmation slightly predates delayed app acceptance', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-table-unavailable-bootstrap-skew-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const bootstrapAttemptAt = '2026-05-24T09:25:33.388Z';
    const bootstrapConfirmedAt = '2026-05-24T09:25:42.494Z';
    const appAcceptedAt = '2026-05-24T09:25:45.178Z';
    const cleanupAt = '2026-05-24T09:31:05.525Z';
    const runtimePid = 97_255;
    const bootstrapRunId = 'run-process-table-unavailable-skew';
    const reason = 'runtime pid could not be verified because process table is unavailable';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        tom: {
          providerId: 'anthropic',
          model: 'haiku',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: reason,
          livenessKind: 'registered_only',
          runtimeDiagnostic: reason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: appAcceptedAt,
          runtimeLastSeenAt: cleanupAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(bootstrapAttemptAt),
          lastObservedAt: Date.parse(bootstrapConfirmedAt),
        },
      ],
      cleanupAt,
      { runId: bootstrapRunId }
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: reason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'haiku',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.hardFailureReason).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('does not heal rapid relaunch failures from previous bootstrap-state run id', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-table-unavailable-stale-rapid-run-ignored';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const bootstrapAttemptAt = '2026-05-24T09:25:33.388Z';
    const bootstrapConfirmedAt = '2026-05-24T09:25:42.494Z';
    const appAcceptedAt = '2026-05-24T09:25:45.178Z';
    const cleanupAt = '2026-05-24T09:31:05.525Z';
    const runtimePid = 97_255;
    const currentRunId = 'run-new-process-table-unavailable';
    const staleRunId = 'run-old-process-table-unavailable';
    const reason = 'runtime pid could not be verified because process table is unavailable';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', currentRunId);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        tom: {
          providerId: 'anthropic',
          model: 'haiku',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: reason,
          livenessKind: 'registered_only',
          runtimeDiagnostic: reason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: appAcceptedAt,
          runtimeLastSeenAt: cleanupAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(bootstrapAttemptAt),
          lastObservedAt: Date.parse(bootstrapConfirmedAt),
        },
      ],
      cleanupAt,
      { runId: staleRunId }
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: reason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'haiku',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
    });
  });

  it('heals post-stop stale pid diagnostics when bootstrap-state already confirmed the Anthropic member', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-post-stop-stale-pid-bootstrap-skew-heals';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const bootstrapAttemptAt = '2026-05-24T09:25:33.388Z';
    const bootstrapConfirmedAt = '2026-05-24T09:25:42.904Z';
    const appAcceptedAt = '2026-05-24T09:25:45.178Z';
    const originalFailureAt = '2026-05-24T09:31:05.525Z';
    const postStopRefreshAt = '2026-05-24T11:36:56.881Z';
    const runtimePid = 97_255;
    const bootstrapRunId = 'run-post-stop-stale-pid-bootstrap-skew';
    const originalReason = 'runtime pid could not be verified because process table is unavailable';
    const postStopDiagnostic = 'persisted runtime pid is not alive';

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        tom: {
          providerId: 'anthropic',
          model: 'haiku',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: originalReason,
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: postStopDiagnostic,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: appAcceptedAt,
          runtimeLastSeenAt: originalFailureAt,
          lastEvaluatedAt: originalFailureAt,
        },
      },
      { launchPhase: 'finished', updatedAt: postStopRefreshAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(bootstrapAttemptAt),
          lastObservedAt: Date.parse(bootstrapConfirmedAt),
        },
      ],
      '2026-05-24T09:26:08.090Z',
      { runId: bootstrapRunId }
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: postStopDiagnostic,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'haiku',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.hardFailureReason).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('heals issue 209 Codex stale-pid hard failure when bootstrap-state confirms the member', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-issue-209-codex-stale-pid-bootstrap-heals';
    const leadSessionId = 'lead-session';
    const bootstrapRunId = '0ebe3b51-57e5-4281-b872-8184bdea34c7';
    const memberName = 'business-reviewer-alpha';
    const runtimePid = 21_580;
    const stalePidReason = 'persisted runtime pid is not alive';

    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [{ name: memberName, providerId: 'codex', model: 'gpt-5.5' }]);
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, [memberName]);
    writeMemberBootstrapRunId(teamName, memberName, bootstrapRunId);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        [memberName]: {
          providerId: 'codex',
          model: 'gpt-5.5',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          runtimeRunId: bootstrapRunId,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: stalePidReason,
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: stalePidReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: '2026-06-06T09:49:08.513Z',
          runtimeLastSeenAt: '2026-06-06T09:51:18.924Z',
          lastEvaluatedAt: '2026-06-06T09:51:18.924Z',
        },
      },
      { launchPhase: 'finished', updatedAt: '2026-06-06T09:59:23.165Z' }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: memberName,
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-06-06T09:49:06.287Z'),
          lastObservedAt: Date.parse('2026-06-06T09:51:53.070Z'),
        },
      ],
      '2026-06-06T09:59:43.154Z',
      { runId: bootstrapRunId }
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            memberName,
            {
              alive: false,
              backendType: 'process',
              providerId: 'codex',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: stalePidReason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'gpt-5.5',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses[memberName]).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses[memberName]?.hardFailureReason).toBeUndefined();
    expect(result.statuses[memberName]?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses[memberName]?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('heals issue 209 confirmed Codex member without clearing submitted-timeout failures', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-issue-209-codex-mixed-partial-reconcile';
    const leadSessionId = 'lead-session';
    const bootstrapRunId = '0ebe3b51-57e5-4281-b872-8184bdea34c7';
    const stalePidReason = 'persisted runtime pid is not alive';
    const submittedTimeoutReason =
      'Bootstrap prompt was submitted, but teammate did not bootstrap-confirm before submitted-confirmation timeout (3m). Last transport stage: bootstrap_submitted';

    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [
      { name: 'business-reviewer-alpha', providerId: 'codex', model: 'gpt-5.5' },
      { name: 'business-reviewer-beta', providerId: 'codex', model: 'gpt-5.4' },
      { name: 'ux-reviewer-beta', providerId: 'codex', model: 'gpt-5.4' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, [
      'business-reviewer-alpha',
      'business-reviewer-beta',
      'ux-reviewer-beta',
    ]);
    writeMemberBootstrapRunId(teamName, 'business-reviewer-alpha', bootstrapRunId);
    writeMemberBootstrapRunId(teamName, 'business-reviewer-beta', bootstrapRunId);
    writeMemberBootstrapRunId(teamName, 'ux-reviewer-beta', bootstrapRunId);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        'business-reviewer-alpha': {
          providerId: 'codex',
          model: 'gpt-5.5',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid: 21_580,
          runtimeRunId: bootstrapRunId,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: stalePidReason,
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: stalePidReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: '2026-06-06T09:49:08.513Z',
          runtimeLastSeenAt: '2026-06-06T09:51:18.924Z',
          lastEvaluatedAt: '2026-06-06T09:51:18.924Z',
        },
        'business-reviewer-beta': {
          providerId: 'codex',
          model: 'gpt-5.4',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid: 55_336,
          runtimeRunId: bootstrapRunId,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: submittedTimeoutReason,
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: stalePidReason,
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: '2026-06-06T09:49:11.249Z',
          runtimeLastSeenAt: '2026-06-06T09:51:18.923Z',
          lastEvaluatedAt: '2026-06-06T09:52:27.567Z',
        },
        'ux-reviewer-beta': {
          providerId: 'codex',
          model: 'gpt-5.4',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'codex',
          launchState: 'confirmed_alive',
          agentToolAccepted: true,
          runtimeAlive: true,
          runtimePid: 2_124,
          runtimeRunId: bootstrapRunId,
          bootstrapConfirmed: true,
          hardFailure: false,
          firstSpawnAcceptedAt: '2026-06-06T09:49:34.626Z',
          lastHeartbeatAt: '2026-06-06T09:50:52.188Z',
          lastRuntimeAliveAt: '2026-06-06T09:59:23.165Z',
          lastEvaluatedAt: '2026-06-06T09:59:23.165Z',
        },
      },
      { launchPhase: 'finished', updatedAt: '2026-06-06T09:59:23.165Z' }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'business-reviewer-alpha',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-06-06T09:49:06.287Z'),
          lastObservedAt: Date.parse('2026-06-06T09:51:53.070Z'),
        },
        {
          name: 'business-reviewer-beta',
          status: 'failed',
          lastAttemptAt: Date.parse('2026-06-06T09:49:08.512Z'),
          lastObservedAt: Date.parse('2026-06-06T09:52:11.246Z'),
          failureReason: submittedTimeoutReason,
        },
        {
          name: 'ux-reviewer-beta',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-06-06T09:49:30.803Z'),
          lastObservedAt: Date.parse('2026-06-06T09:50:52.188Z'),
        },
      ],
      '2026-06-06T09:59:43.154Z',
      { runId: bootstrapRunId }
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'business-reviewer-alpha',
            {
              alive: false,
              backendType: 'process',
              providerId: 'codex',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: stalePidReason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: 21_580,
              model: 'gpt-5.5',
            },
          ],
          [
            'business-reviewer-beta',
            {
              alive: false,
              backendType: 'process',
              providerId: 'codex',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: stalePidReason,
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: 55_336,
              model: 'gpt-5.4',
            },
          ],
          [
            'ux-reviewer-beta',
            {
              alive: true,
              backendType: 'process',
              providerId: 'codex',
              livenessKind: 'runtime_process',
              pidSource: 'process_table',
              metricsPid: 2_124,
              model: 'gpt-5.4',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses['business-reviewer-alpha']).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
    });
    expect(result.statuses['business-reviewer-alpha']?.hardFailureReason).toBeUndefined();
    expect(result.statuses['business-reviewer-alpha']?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses['business-reviewer-beta']).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      hardFailureReason: submittedTimeoutReason,
    });
    expect(result.statuses['ux-reviewer-beta']).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
  });

  it('refreshes cached bootstrap transcript outcome when the transcript file changes', async () => {
    const teamName = 'zz-unit-bootstrap-transcript-cache-refresh';
    const memberName = 'tom';
    const transcriptPath = path.join(tempProjectsBase, 'bootstrap-cache.jsonl');
    const writeTranscriptText = async (text: string, timestamp: string): Promise<void> => {
      await fsPromises.writeFile(
        transcriptPath,
        `${JSON.stringify({
          timestamp,
          agentName: memberName,
          text,
        })}\n`,
        'utf8'
      );
      const updatedAt = new Date(Date.now() + 5_000);
      await fsPromises.utimes(transcriptPath, updatedAt, updatedAt);
    };

    await writeTranscriptText(
      `member briefing for ${memberName} on team "${teamName}" (${teamName}). Ready.`,
      '2026-05-24T09:25:42.904Z'
    );

    const svc = new TeamProvisioningService();
    const firstOutcome = await privateHarness(svc).readRecentBootstrapTranscriptOutcome(
      transcriptPath,
      null,
      memberName,
      teamName
    );

    expect(firstOutcome).toMatchObject({ kind: 'success', source: 'member_briefing' });

    await writeTranscriptText(
      'bootstrap failed: model not found during teammate startup',
      '2026-05-24T09:26:42.904Z'
    );

    const secondOutcome = await privateHarness(svc).readRecentBootstrapTranscriptOutcome(
      transcriptPath,
      null,
      memberName,
      teamName
    );

    expect(secondOutcome).toMatchObject({
      kind: 'failure',
      reason: 'bootstrap failed: model not found during teammate startup',
    });
  });

  it('parses a bootstrap transcript tail once and shares it across members', async () => {
    const teamName = 'zz-unit-bootstrap-transcript-shared-parse';
    const transcriptPath = path.join(tempProjectsBase, 'bootstrap-shared-parse.jsonl');
    await fsPromises.writeFile(
      transcriptPath,
      `${JSON.stringify({
        timestamp: '2026-05-24T09:25:42.904Z',
        agentName: 'alice',
        text: `member briefing for alice on team "${teamName}" (${teamName}). Ready.`,
      })}\n`,
      'utf8'
    );
    const updatedAt = new Date(Date.now() + 5_000);
    await fsPromises.utimes(transcriptPath, updatedAt, updatedAt);

    const svc = new TeamProvisioningService();

    const aliceOutcome = await privateHarness(svc).readRecentBootstrapTranscriptOutcome(
      transcriptPath,
      null,
      'alice',
      teamName
    );
    const bobOutcome = await privateHarness(svc).readRecentBootstrapTranscriptOutcome(
      transcriptPath,
      null,
      'bob',
      teamName
    );

    // Per-member detection is unchanged: alice's briefing is a success, the same
    // line is not attributed to bob.
    expect(aliceOutcome).toMatchObject({ kind: 'success', source: 'member_briefing' });
    expect(bobOutcome).toBeNull();
    // The transcript tail is parsed once and shared: a single cache entry for the
    // file rather than one parse per member.
    expect(
      (svc as unknown as Record<string, Map<string, unknown>>).parsedBootstrapTranscriptTailCache
        .size
    ).toBe(1);
  });

  it('caches persisted bootstrap transcript outcome lookup between close polling reads', async () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
    const teamName = 'zz-unit-bootstrap-transcript-lookup-cache';
    const memberName = 'tom';
    const transcriptPath = path.join(tempProjectsBase, 'bootstrap-lookup-cache.jsonl');
    const svc = new TeamProvisioningService();
    const harness = svc as any;
    const findMemberLogs = vi.fn(async () => [{ filePath: transcriptPath }]);
    const readRecentBootstrapTranscriptOutcome = vi.fn(async () => ({
      kind: 'success',
      observedAt: '2026-05-24T09:25:42.904Z',
      source: 'member_briefing',
    }));
    const readBootstrapTranscriptOutcomesInProjectRoot = vi.fn(async () => []);
    harness.memberLogsFinder = { findMemberLogs };
    harness.readRecentBootstrapTranscriptOutcome = readRecentBootstrapTranscriptOutcome;
    harness.readBootstrapTranscriptOutcomesInProjectRoot =
      readBootstrapTranscriptOutcomesInProjectRoot;

    const firstOutcome = await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
    vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z'));
    const secondOutcome = await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);

    expect(secondOutcome).toEqual(firstOutcome);
    expect(findMemberLogs).toHaveBeenCalledTimes(1);
    expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(1);
    expect(readBootstrapTranscriptOutcomesInProjectRoot).toHaveBeenCalledTimes(1);

    vi.setSystemTime(new Date('2026-05-03T12:00:11.000Z'));
    await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);

    expect(findMemberLogs).toHaveBeenCalledTimes(2);
    expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(2);
  });

  it('does not use persisted bootstrap transcript outcome lookup cache for tracked runs', async () => {
    const teamName = 'zz-unit-bootstrap-transcript-active-lookup-cache';
    const memberName = 'tom';
    const transcriptPath = path.join(tempProjectsBase, 'bootstrap-active-lookup-cache.jsonl');
    const svc = new TeamProvisioningService();
    const harness = svc as any;
    const findMemberLogs = vi.fn(async () => [{ filePath: transcriptPath }]);
    const readRecentBootstrapTranscriptOutcome = vi.fn(async () => ({
      kind: 'success',
      observedAt: '2026-05-24T09:25:42.904Z',
      source: 'member_briefing',
    }));
    harness.memberLogsFinder = { findMemberLogs };
    harness.readRecentBootstrapTranscriptOutcome = readRecentBootstrapTranscriptOutcome;
    harness.readBootstrapTranscriptOutcomesInProjectRoot = vi.fn(async () => []);
    harness.aliveRunByTeam.set(teamName, 'run-1');

    await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
    await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);

    expect(findMemberLogs).toHaveBeenCalledTimes(2);
    expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(2);
  });

  it('caches persisted member spawn statuses between close polling reads', async () => {
    const teamName = 'zz-unit-persisted-status-cache';
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.reconcilePersistedLaunchState = vi.fn(async () => ({
      snapshot: null,
      statuses: {},
    }));
    harness.attachLiveRuntimeMetadataToStatuses = vi.fn(async (_teamName, statuses) => statuses);

    const first = await svc.getMemberSpawnStatuses(teamName);
    const second = await svc.getMemberSpawnStatuses(teamName);

    expect(first).toEqual(second);
    expect(harness.reconcilePersistedLaunchState).toHaveBeenCalledTimes(1);
    expect(harness.attachLiveRuntimeMetadataToStatuses).toHaveBeenCalledTimes(1);
  });

  it('does not heal cleanup-finalized launch failures from stale bootstrap-state confirmation', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-cleanup-finalized-stale-bootstrap-ignored';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const staleBootstrapAt = '2026-05-22T14:14:58.000Z';
    const acceptedAt = '2026-05-22T14:15:44.000Z';
    const cleanupAt = '2026-05-22T14:16:07.000Z';
    const runtimePid = 45_724;

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          providerId: 'anthropic',
          model: 'claude-opus-4-7',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'anthropic',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: 'Launch ended before teammate bootstrap completed.',
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: 'persisted runtime pid is not alive',
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(staleBootstrapAt),
          lastObservedAt: Date.parse(staleBootstrapAt),
        },
      ],
      staleBootstrapAt
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'jack',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: 'persisted runtime pid is not alive',
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'claude-opus-4-7',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      hardFailureReason: 'Launch ended before teammate bootstrap completed.',
    });
  });

  it('does not heal cleanup-finalized launch failures from stale member evidence in a rewritten bootstrap-state file', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-cleanup-finalized-stale-member-evidence-ignored';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const staleObservedAt = '2026-05-22T14:14:58.000Z';
    const acceptedAt = '2026-05-22T14:15:44.000Z';
    const cleanupAt = '2026-05-22T14:16:07.000Z';
    const bootstrapFileUpdatedAt = '2026-05-22T14:16:30.000Z';
    const runtimePid = 45_724;

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          providerId: 'anthropic',
          model: 'claude-opus-4-7',
          laneId: 'primary',
          laneKind: 'primary',
          laneOwnerProviderId: 'anthropic',
          launchState: 'failed_to_start',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: 'Launch ended before teammate bootstrap completed.',
          livenessKind: 'stale_metadata',
          runtimeDiagnostic: 'persisted runtime pid is not alive',
          runtimeDiagnosticSeverity: 'warning',
          firstSpawnAcceptedAt: acceptedAt,
          lastEvaluatedAt: cleanupAt,
        },
      },
      { launchPhase: 'finished', updatedAt: cleanupAt }
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse(staleObservedAt),
        },
      ],
      bootstrapFileUpdatedAt
    );

    const svc = new TeamProvisioningService();
    privateHarness(svc).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'jack',
            {
              alive: false,
              backendType: 'process',
              providerId: 'anthropic',
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: 'persisted runtime pid is not alive',
              runtimeDiagnosticSeverity: 'warning',
              metricsPid: runtimePid,
              model: 'claude-opus-4-7',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      hardFailureReason: 'Launch ended before teammate bootstrap completed.',
    });
  });

  it('does not heal terminal bootstrap-state failures from native app-managed proof with mismatched hashes', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-native-runtime-proof-hash-mismatch';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const proofAt = new Date(Date.now() - 60_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();
    const proofToken = 'proof-token-jack-native';
    const bootstrapRunId = 'run-native-proof';
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapProofToken: proofToken,
            bootstrapRunId,
            bootstrapProofMode: 'native_app_managed_context',
            bootstrapContextHash: 'a'.repeat(64),
            bootstrapBriefingHash: 'b'.repeat(64),
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.',
        },
      ],
      failureAt
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_confirmed',
        timestamp: proofAt,
        pid: 1234,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        runId: bootstrapRunId,
        source: 'native_app_managed_bootstrap_private_turn',
        bootstrapProofToken: proofToken,
        contextHash: 'c'.repeat(64),
        briefingHash: 'b'.repeat(64),
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      runtimeAlive: false,
      hardFailure: true,
    });
  });

  it('does not heal bootstrap-state failures from stale runtime proof before spawn acceptance', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-stale-runtime-proof-ignored';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const proofAt = new Date(Date.now() - 120_000).toISOString();
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();
    const proofToken = 'proof-token-jack';
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapProofToken: proofToken,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.',
        },
      ],
      failureAt
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_confirmed',
        timestamp: proofAt,
        pid: 1234,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        source: 'member_briefing_tool_success',
        bootstrapProofToken: proofToken,
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      runtimeAlive: false,
      hardFailure: true,
    });
  });

  it('does not heal bootstrap-state failures from stale pre-launch transcript success', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-state-stale-transcript-ignored';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const staleSuccessAt = new Date(Date.now() - 180_000).toISOString();
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const failureAt = new Date(Date.now() - 30_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'failed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(failureAt),
          failureReason: 'Teammate was registered but did not bootstrap-confirm before timeout.',
        },
      ],
      failureAt
    );

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      JSON.stringify({
        timestamp: staleSuccessAt,
        teamName,
        agentName: 'jack',
        type: 'user',
        message: {
          role: 'user',
          content: [
            {
              type: 'tool_result',
              tool_use_id: 'item_1',
              content: `Member briefing for jack on team "${teamName}" (${teamName}).\nTask briefing for jack:\nNo actionable tasks.`,
              is_error: false,
            },
          ],
        },
      }) + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
    });
  });

  it('keeps active process bootstrap transport progress pending without turning retryable rejection into failure', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-bootstrap-transport-pending';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 15_000).toISOString();
    const bootstrapRunId = 'run-process-transport-pending';
    const runtimePid = 1234;
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            backendType: 'process',
            tmuxPaneId: `process:${runtimePid}`,
            runtimePid,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapRunId,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          runtimePid,
          runtimeRunId: bootstrapRunId,
          tmuxPaneId: `process:${runtimePid}`,
          backendType: 'process',
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          firstSpawnAcceptedAt: acceptedAt,
        },
      },
      { launchPhase: 'active' }
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      [
        {
          version: 1,
          type: 'runtime_ready',
          timestamp: acceptedAt,
          pid: runtimePid,
          teamName,
          agentName: 'jack',
          agentId: `jack@${teamName}`,
          bootstrapRunId,
          detail: 'ready',
        },
        {
          version: 1,
          type: 'bootstrap_submit_rejected',
          timestamp: new Date(Date.now() - 10_000).toISOString(),
          pid: runtimePid,
          teamName,
          agentName: 'jack',
          agentId: `jack@${teamName}`,
          bootstrapRunId,
          retryable: true,
          detail: 'cooldown before retry',
        },
      ]
        .map((event) => JSON.stringify(event))
        .join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      bootstrapConfirmed: false,
      hardFailure: false,
      runtimeDiagnosticSeverity: 'warning',
    });
    expect(result.statuses.jack?.runtimeDiagnostic).toContain(
      'Bootstrap prompt has not been submitted yet. Last transport stage: bootstrap submit rejected'
    );
  });

  it('does not downgrade provisioned-but-not-alive failures from process transport progress alone', () => {
    const svc = new TeamProvisioningService();
    const reason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
    const result = privateHarness(svc).applyProcessBootstrapTransportOverlay({
      member: {
        name: 'jack',
        launchState: 'failed_to_start',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: true,
        hardFailureReason: reason,
        lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
      },
      summary: {
        hasProgress: true,
        submitted: true,
        lastStage: 'bootstrap submitted',
      },
      launchPhase: 'active',
    });

    expect(result).toMatchObject({
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      hardFailureReason: reason,
    });
  });

  it('uses the last process transport stage when active launch grace expires', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-bootstrap-transport-timeout';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 15 * 60_000).toISOString();
    const bootstrapRunId = 'run-process-transport-timeout';
    const runtimePid = 1235;
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            backendType: 'process',
            tmuxPaneId: `process:${runtimePid}`,
            runtimePid,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapRunId,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: false,
          runtimePid,
          runtimeRunId: bootstrapRunId,
          tmuxPaneId: `process:${runtimePid}`,
          backendType: 'process',
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          firstSpawnAcceptedAt: acceptedAt,
        },
      },
      { launchPhase: 'active' }
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_prompt_observed',
        timestamp: new Date(Date.now() - 14 * 60_000).toISOString(),
        pid: runtimePid,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        bootstrapRunId,
        detail: 'prompt seen',
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack).toMatchObject({
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      runtimeDiagnosticSeverity: 'error',
    });
    expect(result.statuses.jack?.hardFailureReason).toContain(
      'Last transport stage: bootstrap prompt observed: prompt seen'
    );
  });

  it('uses non-retryable process transport rejection as terminal launch failure', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-process-bootstrap-transport-terminal';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const acceptedAt = new Date(Date.now() - 15_000).toISOString();
    const bootstrapRunId = 'run-process-transport-terminal';
    const runtimePid = 1236;
    const runtimeEventsPath = path.join(tempTeamsBase, teamName, 'runtime', 'jack.runtime.jsonl');

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    const configPath = path.join(tempTeamsBase, teamName, 'config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as {
      members: Array<Record<string, unknown>>;
    };
    config.members = config.members.map((member) =>
      member.name === 'jack'
        ? {
            ...member,
            agentId: `jack@${teamName}`,
            backendType: 'process',
            tmuxPaneId: `process:${runtimePid}`,
            runtimePid,
            bootstrapExpectedAfter: acceptedAt,
            bootstrapRunId,
            bootstrapRuntimeEventsPath: runtimeEventsPath,
          }
        : member
    );
    fs.writeFileSync(configPath, JSON.stringify(config), 'utf8');
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        jack: {
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          runtimePid,
          runtimeRunId: bootstrapRunId,
          tmuxPaneId: `process:${runtimePid}`,
          backendType: 'process',
          bootstrapConfirmed: false,
          hardFailure: false,
          hardFailureReason: undefined,
          firstSpawnAcceptedAt: acceptedAt,
        },
      },
      { launchPhase: 'active' }
    );
    fs.mkdirSync(path.dirname(runtimeEventsPath), { recursive: true });
    fs.writeFileSync(
      runtimeEventsPath,
      `${JSON.stringify({
        version: 1,
        type: 'bootstrap_submit_rejected',
        timestamp: new Date(Date.now() - 10_000).toISOString(),
        pid: runtimePid,
        teamName,
        agentName: 'jack',
        agentId: `jack@${teamName}`,
        bootstrapRunId,
        retryable: false,
        detail: 'fatal submit rejection',
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack).toMatchObject({
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
      runtimeDiagnosticSeverity: 'error',
    });
    expect(result.statuses.jack?.hardFailureReason).toBe(
      'bootstrap submit rejected: fatal submit rejection'
    );
  });

  it('does not classify the bootstrap instruction prompt as a member launch failure', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-prompt-not-failure';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      `${JSON.stringify({
        timestamp: acceptedAt,
        teamName,
        agentName: 'alice',
        type: 'user',
        message: {
          role: 'user',
          content: `You are bootstrapping into team "${teamName}" as member "alice".\nYour first action is to call the MCP tool member_briefing on the agent-teams server with teamName="${teamName}" and memberName="alice".\nIf member_briefing is still unavailable after that one retry, send exactly one short SendMessage to "team-lead" with the exact error text, then stop this turn and wait.`,
        },
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const reason = await (svc as any).findBootstrapTranscriptFailureReason(
      teamName,
      'alice',
      Date.parse(acceptedAt) - 1
    );

    expect(reason).toBeNull();
  });

  it('extracts a human-readable bootstrap failure from message_send tool result JSON', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-message-send-json-failure';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const failureAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "alice".`,
          },
        }),
        JSON.stringify({
          timestamp: failureAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'toolu-message-send',
                content: JSON.stringify({
                  success: true,
                  message: "Message sent to team-lead's inbox",
                  routing: {
                    sender: 'alice',
                    target: '@team-lead',
                    summary: 'Bootstrap failed - no member_briefing tool',
                    content: 'Не могу выполнить member_briefing: tool not found.',
                  },
                }),
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const reason = await (svc as any).findBootstrapTranscriptFailureReason(
      teamName,
      'alice',
      Date.parse(acceptedAt) - 1
    );

    expect(reason).toBe(
      'Bootstrap failed - no member_briefing tool: Не могу выполнить member_briefing: tool not found.'
    );
    expect(reason).not.toContain('{"success":true');
  });

  it('clears a stale persisted bootstrap-prompt failure when member_briefing later succeeds', async () => {
    allowConsoleLogs();
    const teamName = 'zz-unit-bootstrap-stale-prompt-failure';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const successAt = new Date(Date.now() - 4_000).toISOString();
    const staleReason = `You are bootstrapping into team "${teamName}" as member "alice".\nYour first action is to call the MCP tool member_briefing on the agent-teams server with teamName="${teamName}" and memberName="alice".\nIf tool search shows only the prefixed MCP name, use mcp__agent-teams__member_briefing.`;

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice', 'bob']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'failed_to_start',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: true,
        hardFailureReason: staleReason,
        firstSpawnAcceptedAt: acceptedAt,
      },
      bob: {
        launchState: 'starting',
        agentToolAccepted: false,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
      },
    });

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: staleReason,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for alice on team "${teamName}" (${teamName}).\nTask briefing for alice:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.alice).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(result.statuses.alice?.hardFailureReason).toBeUndefined();
  });

  it('marks an online teammate bootstrap as failed when transcript shows model unavailability', async () => {
    allowConsoleLogs();
    const teamName = 'zz-live-bootstrap-model-unavailable';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const errorAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: errorAt,
          teamName,
          agentName: 'jack',
          type: 'assistant',
          isApiErrorMessage: true,
          message: {
            role: 'assistant',
            content: [
              {
                type: 'text',
                text: 'API Error: 400 {"detail":"The requested model is not available for your account."}',
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = {
      runId: 'run-live-1',
      teamName,
      startedAt: new Date(Date.now() - 60_000).toISOString(),
      request: {
        members: [],
      },
      expectedMembers: ['jack'],
      memberSpawnStatuses: new Map([
        [
          'jack',
          {
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            error: undefined,
            updatedAt: acceptedAt,
            runtimeAlive: true,
            livenessSource: 'process',
            bootstrapConfirmed: false,
            hardFailure: false,
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
            lastHeartbeatAt: undefined,
          },
        ],
      ]),
      provisioningOutputParts: [],
      activeToolCalls: new Map(),
      isLaunch: false,
    } as any;

    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(teamName, run.runId);

    await (svc as any).reconcileBootstrapTranscriptFailures(run);

    expect(run.memberSpawnStatuses.get('jack')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
    });
    expect(run.memberSpawnStatuses.get('jack')?.error).toContain(
      'requested model is not available'
    );
    expect(run.provisioningOutputParts.join('\n')).toContain('requested model is not available');
  });

  it('marks a live teammate bootstrap as confirmed when transcript shows successful member_briefing', async () => {
    allowConsoleLogs();
    const teamName = 'zz-live-bootstrap-transcript-success';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const successAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "alice".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'alice',
          type: 'assistant',
          message: {
            role: 'assistant',
            content: [
              {
                type: 'text',
                text: `Bootstrap выполнен для \`alice\` в команде \`${teamName}\`.`,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = {
      runId: 'run-live-success-1',
      teamName,
      startedAt: new Date(Date.now() - 60_000).toISOString(),
      request: {
        members: [],
      },
      expectedMembers: ['alice'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          {
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            error: undefined,
            updatedAt: acceptedAt,
            runtimeAlive: true,
            livenessSource: 'process',
            bootstrapConfirmed: false,
            hardFailure: false,
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
            lastHeartbeatAt: undefined,
          },
        ],
      ]),
      provisioningOutputParts: [],
      activeToolCalls: new Map(),
      isLaunch: false,
    } as any;

    await (svc as any).reconcileBootstrapTranscriptSuccesses(run);

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      bootstrapConfirmed: true,
    });
    expect(run.provisioningOutputParts.join('\n')).toContain('bootstrap confirmed via transcript');
  });

  it('clears a live grace-window failure when member transcript later shows successful member_briefing', async () => {
    allowConsoleLogs();
    const teamName = 'zz-live-bootstrap-late-transcript-success';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 170_000).toISOString();
    const successAt = new Date(Date.now() - 5_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for jack on team "${teamName}" (${teamName}).\nTask briefing for jack:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = {
      runId: 'run-live-late-success-1',
      teamName,
      startedAt: new Date(Date.now() - 220_000).toISOString(),
      request: {
        members: [],
      },
      expectedMembers: ['jack'],
      memberSpawnStatuses: new Map([
        [
          'jack',
          {
            status: 'error',
            launchState: 'failed_to_start',
            error: 'Teammate did not join within the launch grace window.',
            updatedAt: new Date(Date.now() - 10_000).toISOString(),
            runtimeAlive: false,
            livenessSource: undefined,
            bootstrapConfirmed: false,
            hardFailure: true,
            hardFailureReason: 'Teammate did not join within the launch grace window.',
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
            lastHeartbeatAt: undefined,
          },
        ],
      ]),
      lastMemberSpawnAuditAt: Date.now(),
      provisioningOutputParts: [],
      activeToolCalls: new Map(),
      isLaunch: false,
    } as any;

    await (svc as any).maybeAuditMemberSpawnStatuses(run);

    expect(run.memberSpawnStatuses.get('jack')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(run.memberSpawnStatuses.get('jack')?.hardFailureReason).toBeUndefined();
    expect(run.provisioningOutputParts.join('\n')).toContain('bootstrap confirmed via transcript');
  });

  it('does not treat OpenCode member_briefing transcript success as runtime bootstrap evidence', async () => {
    allowConsoleLogs();
    const teamName = 'zz-opencode-bootstrap-transcript-not-evidence';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-opencode-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 30_000).toISOString();
    const successAt = new Date(Date.now() - 5_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for jack on team "${teamName}" (${teamName}).\nTask briefing for jack:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = {
      runId: 'run-opencode-transcript-not-evidence',
      teamName,
      startedAt: new Date(Date.now() - 60_000).toISOString(),
      request: {
        members: [],
      },
      expectedMembers: ['jack'],
      mixedSecondaryLanes: [
        {
          laneId: 'secondary:opencode:jack',
          providerId: 'opencode',
          member: { name: 'jack', providerId: 'opencode', model: 'openrouter/qwen/qwen3-coder' },
          runId: 'opencode-run-jack',
          state: 'launching',
          result: null,
          warnings: [],
          diagnostics: [],
        },
      ],
      memberSpawnStatuses: new Map([
        [
          'jack',
          {
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            error: undefined,
            updatedAt: acceptedAt,
            runtimeAlive: true,
            livenessSource: 'process',
            bootstrapConfirmed: false,
            hardFailure: false,
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
            lastHeartbeatAt: undefined,
          },
        ],
      ]),
      provisioningOutputParts: [],
      activeToolCalls: new Map(),
      isLaunch: false,
    } as any;

    await (svc as any).reconcileBootstrapTranscriptSuccesses(run);

    expect(run.memberSpawnStatuses.get('jack')).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      bootstrapConfirmed: false,
    });
    expect(run.provisioningOutputParts.join('\n')).not.toContain(
      'bootstrap confirmed via transcript'
    );
  });

  it('marks OpenCode secondary partial member_briefing bootstrap as stalled instead of confirmed', async () => {
    allowConsoleLogs();
    const teamName = 'zz-opencode-partial-bootstrap-stalled';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'alice-opencode-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 6 * 60_000).toISOString();
    const successAt = new Date(Date.now() - 5 * 60_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['alice']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "alice".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'alice',
          type: 'user',
          message: {
            role: 'user',
            content: [
              {
                type: 'tool_result',
                tool_use_id: 'item_1',
                content: `Member briefing for alice on team "${teamName}" (${teamName}).\nTask briefing for alice:\nNo actionable tasks.`,
                is_error: false,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName,
      expectedMembers: ['alice'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: false,
            firstSpawnAcceptedAt: acceptedAt,
            livenessKind: 'registered_only',
          }),
        ],
      ]),
    });
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:alice',
        providerId: 'opencode',
        member: {
          name: 'alice',
          providerId: 'opencode',
          model: 'openrouter/qwen/qwen3-coder',
        },
        runId: 'opencode-run-alice',
        state: 'finished',
        result: null,
        warnings: [],
        diagnostics: [],
      },
    ];

    await (svc as any).maybeAuditMemberSpawnStatuses(run, { force: true });

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      bootstrapConfirmed: false,
      hardFailure: false,
      bootstrapStalled: true,
      runtimeDiagnostic: 'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
      runtimeDiagnosticSeverity: 'warning',
    });
    expect(run.provisioningOutputParts.join('\n')).not.toContain(
      'bootstrap confirmed via transcript'
    );
  });

  it('preserves OpenCode secondary bootstrapStalled through mixed launch snapshot rebuilds', () => {
    const teamName = 'zz-opencode-bootstrap-stalled-snapshot-rebuild';
    const svc = new TeamProvisioningService();
    const acceptedAt = new Date(Date.now() - 6 * 60_000).toISOString();
    const run = createMemberSpawnRun({
      teamName,
      expectedMembers: [],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: false,
            firstSpawnAcceptedAt: acceptedAt,
            livenessKind: 'registered_only',
            bootstrapStalled: true,
            runtimeDiagnostic:
              'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
            runtimeDiagnosticSeverity: 'warning',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.effectiveMembers = [];
    run.request = {
      ...run.request,
      teamName,
      cwd: '/tmp/opencode-bootstrap-stalled-snapshot-rebuild',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      skipPermissions: true,
    };
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:alice',
        providerId: 'opencode',
        member: {
          name: 'alice',
          providerId: 'opencode',
          model: 'openrouter/qwen/qwen3-coder',
        },
        runId: 'opencode-run-alice',
        state: 'finished',
        result: {
          runId: 'opencode-run-alice',
          teamName,
          launchPhase: 'active',
          teamLaunchState: 'partial_pending',
          members: {
            alice: {
              memberName: 'alice',
              providerId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              sessionId: 'ses_alice_partial_bootstrap',
              livenessKind: 'registered_only',
              diagnostics: ['OpenCode runtime session materialized.'],
            },
          },
          warnings: [],
          diagnostics: [],
        },
        warnings: [],
        diagnostics: [],
      },
    ];

    const snapshot = (svc as any).buildMixedPersistedLaunchSnapshotForRun(run, 'active');

    expect(snapshot?.members.alice).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      bootstrapConfirmed: false,
      hardFailure: false,
      runtimeSessionId: 'ses_alice_partial_bootstrap',
      livenessKind: 'registered_only',
      bootstrapStalled: true,
    });
  });

  it('does not copy bootstrap-state success into OpenCode secondary runtime evidence', async () => {
    const teamName = 'zz-opencode-bootstrap-state-not-evidence';
    const leadSessionId = 'lead-session';
    const acceptedAt = Date.now() - 30_000;
    const observedAt = Date.now() - 5_000;

    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'jack',
        providerId: 'opencode',
        model: 'openrouter/qwen/qwen3-coder',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['jack']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastAttemptAt: acceptedAt,
          lastObservedAt: observedAt,
        },
      ],
      new Date(observedAt - 10_000).toISOString()
    );
    writeLaunchState(teamName, leadSessionId, {
      jack: {
        providerId: 'opencode',
        laneId: 'secondary:opencode:jack',
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: true,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: new Date(acceptedAt).toISOString(),
        lastRuntimeAliveAt: new Date(observedAt).toISOString(),
        lastEvaluatedAt: new Date().toISOString(),
      },
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      bootstrapConfirmed: false,
    });
  });

  it('promotes OpenCode secondary pending launch state from committed bootstrap session evidence', async () => {
    const teamName = 'zz-opencode-committed-overlay-promotes';
    const leadSessionId = 'lead-session';
    const laneId = 'secondary:opencode:tom';
    const runId = 'opencode-run-tom';

    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'tom',
        providerId: 'opencode',
        model: 'openrouter/minimax/minimax-m2.5',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['tom']);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId,
      state: 'active',
      diagnostics: [
        'OpenCode bridge reported bootstrap confirmation, but no lane runtime evidence was committed.',
      ],
    });
    await writeCommittedOpenCodeSessionStore({
      teamName,
      laneId,
      runId,
      sessions: [
        {
          id: 'ses-tom',
          teamName,
          memberName: 'tom',
          laneId,
          runId,
          providerId: 'opencode',
          observedAt: '2026-04-22T12:00:00.000Z',
          source: 'runtime_bootstrap_checkin',
        },
      ],
    });
    writeLaunchState(teamName, leadSessionId, {
      tom: {
        providerId: 'opencode',
        laneId,
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        livenessKind: 'registered_only',
        diagnostics: [
          'OpenCode bridge reported bootstrap confirmation, but no lane runtime evidence was committed.',
        ],
        lastEvaluatedAt: '2026-04-22T12:00:01.000Z',
      },
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      launchState: 'confirmed_alive',
      agentToolAccepted: true,
      bootstrapConfirmed: true,
      runtimeAlive: true,
      livenessKind: 'confirmed_bootstrap',
    });
    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.tom).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      runtimeSessionId: 'ses-tom',
      livenessKind: 'confirmed_bootstrap',
    });
  });

  it('prevents stale OpenCode secondary pending or missing writes from downgrading committed bootstrap evidence', async () => {
    const teamName = 'zz-opencode-committed-overlay-write-boundary';
    const leadSessionId = 'lead-session';
    const laneId = 'secondary:opencode:tom';
    const runId = 'opencode-run-tom';

    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'opencode' }]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId,
      state: 'active',
    });
    await writeCommittedOpenCodeSessionStore({
      teamName,
      laneId,
      runId,
      sessions: [
        {
          id: 'ses-tom',
          teamName,
          memberName: 'tom',
          laneId,
          runId,
          observedAt: '2026-04-22T12:00:00.000Z',
          source: 'runtime_bootstrap_checkin',
        },
      ],
    });
    writeLaunchState(teamName, leadSessionId, {
      tom: {
        providerId: 'opencode',
        laneId,
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchState: 'confirmed_alive',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: true,
        hardFailure: false,
        runtimeSessionId: 'ses-tom',
      },
    });
    const staleSnapshot = createPersistedLaunchSnapshot({
      teamName,
      leadSessionId,
      launchPhase: 'active',
      expectedMembers: ['tom'],
      members: {
        tom: {
          name: 'tom',
          providerId: 'opencode',
          laneId,
          laneKind: 'secondary',
          laneOwnerProviderId: 'opencode',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: false,
          bootstrapConfirmed: false,
          hardFailure: false,
          lastEvaluatedAt: '2026-04-22T12:00:02.000Z',
          diagnostics: [
            'OpenCode bridge reported bootstrap confirmation, but no lane runtime evidence was committed.',
          ],
        },
      },
      updatedAt: '2026-04-22T12:00:02.000Z',
    });

    const svc = new TeamProvisioningService();
    await (svc as any).writeLaunchStateSnapshot(teamName, staleSnapshot);

    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.tom).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      runtimeSessionId: 'ses-tom',
    });
    expect(persisted.teamLaunchState).toBe('clean_success');

    const missingMemberSnapshot = createPersistedLaunchSnapshot({
      teamName,
      leadSessionId,
      launchPhase: 'active',
      expectedMembers: [],
      members: {},
      updatedAt: '2026-04-22T12:00:03.000Z',
    });
    await (svc as any).writeLaunchStateSnapshot(teamName, missingMemberSnapshot);

    const persistedAfterMissingWrite = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persistedAfterMissingWrite.members.tom).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      runtimeSessionId: 'ses-tom',
    });
    expect(persistedAfterMissingWrite.teamLaunchState).toBe('clean_success');
  });

  it('normalizes stale confirmed OpenCode secondary liveness from committed bootstrap evidence', async () => {
    const teamName = 'zz-opencode-committed-overlay-normalizes-liveness';
    const leadSessionId = 'lead-session';
    const laneId = 'secondary:opencode:tom';
    const runId = 'opencode-run-tom';

    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'opencode' }]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId,
      state: 'active',
    });
    await writeCommittedOpenCodeSessionStore({
      teamName,
      laneId,
      runId,
      sessions: [
        {
          id: 'ses-tom',
          teamName,
          memberName: 'tom',
          laneId,
          runId,
          observedAt: '2026-04-22T12:00:00.000Z',
          source: 'runtime_bootstrap_checkin',
        },
      ],
    });
    writeLaunchState(teamName, leadSessionId, {
      tom: {
        providerId: 'opencode',
        laneId,
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchState: 'confirmed_alive',
        agentToolAccepted: true,
        runtimeAlive: true,
        bootstrapConfirmed: true,
        hardFailure: false,
        runtimeSessionId: 'ses-tom',
        livenessKind: 'registered_only',
        runtimeDiagnostic: 'OpenCode bootstrap evidence committed.',
        diagnostics: ['opencode_bootstrap_evidence_committed'],
      },
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      livenessKind: 'confirmed_bootstrap',
    });
    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.tom).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      livenessKind: 'confirmed_bootstrap',
    });
  });

  it('marks a live teammate bootstrap as confirmed from transcript without claiming runtime is alive', async () => {
    allowConsoleLogs();
    const teamName = 'zz-live-bootstrap-transcript-success-without-runtime';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'atlas-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const successAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['atlas']);

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'atlas',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "atlas".`,
          },
        }),
        JSON.stringify({
          timestamp: successAt,
          teamName,
          agentName: 'atlas',
          type: 'assistant',
          message: {
            role: 'assistant',
            content: [
              {
                type: 'text',
                text: `Bootstrap выполнен для \`atlas\` в команде \`${teamName}\`.`,
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const run = {
      runId: 'run-live-success-2',
      teamName,
      startedAt: new Date(Date.now() - 60_000).toISOString(),
      request: {
        members: [],
      },
      expectedMembers: ['atlas'],
      memberSpawnStatuses: new Map([
        [
          'atlas',
          {
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            error: undefined,
            updatedAt: acceptedAt,
            runtimeAlive: false,
            livenessSource: undefined,
            bootstrapConfirmed: false,
            hardFailure: false,
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
            lastHeartbeatAt: undefined,
          },
        ],
      ]),
      provisioningOutputParts: [],
      activeToolCalls: new Map(),
      isLaunch: false,
    } as any;

    await (svc as any).reconcileBootstrapTranscriptSuccesses(run);

    expect(run.memberSpawnStatuses.get('atlas')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: false,
      bootstrapConfirmed: true,
    });
    expect(run.provisioningOutputParts.join('\n')).toContain('bootstrap confirmed via transcript');
  });

  it('marks a persisted online teammate bootstrap as failed when transcript shows model unavailability', async () => {
    allowConsoleLogs();
    const teamName = 'zz-persisted-live-bootstrap-model-unavailable';
    const leadSessionId = 'lead-session';
    const memberSessionId = 'jack-session';
    const projectPath = '/Users/test/proj';
    const projectId = '-Users-test-proj';
    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const errorAt = new Date(Date.now() - 4_000).toISOString();

    writeLaunchConfig(teamName, projectPath, leadSessionId, ['jack']);
    writeLaunchState(teamName, leadSessionId, {
      jack: {
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: acceptedAt,
      },
    });

    const projectRoot = path.join(tempProjectsBase, projectId);
    fs.mkdirSync(projectRoot, { recursive: true });
    fs.writeFileSync(
      path.join(projectRoot, `${memberSessionId}.jsonl`),
      [
        JSON.stringify({
          timestamp: acceptedAt,
          teamName,
          agentName: 'jack',
          type: 'user',
          message: {
            role: 'user',
            content: `You are bootstrapping into team "${teamName}" as member "jack".`,
          },
        }),
        JSON.stringify({
          timestamp: errorAt,
          teamName,
          agentName: 'jack',
          type: 'assistant',
          isApiErrorMessage: true,
          message: {
            role: 'assistant',
            content: [
              {
                type: 'text',
                text: 'API Error: 400 {"detail":"The requested model is not available for your account."}',
              },
            ],
          },
        }),
      ].join('\n') + '\n',
      'utf8'
    );

    const svc = new TeamProvisioningService();

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
    });
    expect(result.statuses.jack?.error).toContain('requested model is not available');
    expect(result.statuses.jack?.hardFailureReason).toContain('requested model is not available');
    expect(result.teamLaunchState).toBe('partial_failure');
  });

  it('does not reprocess already-seen teammate lead inbox messages', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
      memberSpawnLeadInboxCursorByMember: new Map([
        [
          'alice',
          {
            timestamp: '2026-04-16T10:00:00.000Z',
            messageId: 'msg-2',
          },
        ],
      ]),
    });

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: 'heartbeat',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-1',
        read: false,
      },
      {
        from: 'alice',
        text: 'heartbeat',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-2',
        read: false,
      },
    ]);

    const applySignalSpy = vi.spyOn(svc as any, 'applyLeadInboxSpawnSignal');

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(applySignalSpy).not.toHaveBeenCalled();
  });

  it('processes an unseen teammate heartbeat on the first refresh', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
    });

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: '{"type":"heartbeat","timestamp":"2026-04-16T10:00:00.000Z"}',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-1',
        read: false,
      },
    ]);

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
    expect(run.memberSpawnLeadInboxCursorByMember.get('alice')).toEqual({
      timestamp: '2026-04-16T10:00:00.000Z',
      messageId: 'msg-1',
    });
  });

  it('maps suffixed teammate heartbeats back onto the expected member during live refresh', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
      expectedMembers: ['alice'],
    });

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice-2',
        text: '{"type":"heartbeat","timestamp":"2026-04-16T10:00:00.000Z"}',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-suffixed',
        read: false,
      },
    ]);

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
    expect(run.memberSpawnLeadInboxCursorByMember.get('alice')).toEqual({
      timestamp: '2026-04-16T10:00:00.000Z',
      messageId: 'msg-suffixed',
    });
  });

  it('ignores teammate lead inbox signals that predate the current run', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T10:00:00.000Z',
    });

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: '{"type":"heartbeat","timestamp":"2026-04-16T09:59:59.000Z"}',
        timestamp: '2026-04-16T09:59:59.000Z',
        messageId: 'msg-early',
        read: false,
      },
    ]);

    const applySignalSpy = vi.spyOn(svc as any, 'applyLeadInboxSpawnSignal');

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(applySignalSpy).not.toHaveBeenCalled();
    expect(run.memberSpawnLeadInboxCursorByMember.size).toBe(0);
    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      bootstrapConfirmed: false,
    });
  });

  it('ignores an unseen older lead inbox signal without replaying older state', async () => {
    const latestHeartbeatAt = '2026-04-16T10:05:00.000Z';
    const existingEntry = createMemberSpawnStatusEntry({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      livenessSource: 'heartbeat',
      bootstrapConfirmed: true,
      lastHeartbeatAt: latestHeartbeatAt,
    });
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
      memberSpawnStatuses: new Map([['alice', existingEntry]]),
      memberSpawnLeadInboxCursorByMember: new Map([
        [
          'alice',
          {
            timestamp: latestHeartbeatAt,
            messageId: 'msg-3',
          },
        ],
      ]),
    });
    const svc = new TeamProvisioningService();

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: 'Bootstrap failed: unsupported model',
        timestamp: '2026-04-16T10:04:00.000Z',
        messageId: 'msg-2b',
        read: false,
      },
      {
        from: 'alice',
        text: 'heartbeat',
        timestamp: latestHeartbeatAt,
        messageId: 'msg-3',
        read: false,
      },
    ]);

    const applySignalSpy = vi.spyOn(svc as any, 'applyLeadInboxSpawnSignal');

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(applySignalSpy).not.toHaveBeenCalled();
    expect(run.memberSpawnStatuses.get('alice')).toBe(existingEntry);
    expect(run.memberSpawnLeadInboxCursorByMember.get('alice')).toEqual({
      timestamp: latestHeartbeatAt,
      messageId: 'msg-3',
    });
  });

  it('keeps lead work-sync inbox rows unread without accepted report or recovery', async () => {
    const svc = new TeamProvisioningService();
    const harness = leadWorkSyncReadCommitHarness(svc);
    const normalMessage = {
      from: 'alice',
      to: 'team-lead',
      text: 'Please check task-1.',
      timestamp: '2026-04-25T10:00:00.000Z',
      messageId: 'msg-normal',
      read: false,
    };
    const workSyncMessage = {
      from: 'system',
      to: 'team-lead',
      text: 'Work sync required.',
      timestamp: '2026-04-25T10:00:01.000Z',
      messageId: 'msg-work-sync',
      messageKind: 'member_work_sync_nudge',
      taskRefs: [{ taskId: 'task-1', displayId: 'task-1', teamName: 'team-a' }],
      read: false,
    };
    vi.spyOn(harness, 'hasAcceptedLeadWorkSyncReport').mockResolvedValue(false);

    const readCommitBatch = await harness.getLeadRelayReadCommitBatch({
      teamName: 'team-a',
      leadName: 'team-lead',
      batch: [normalMessage, workSyncMessage],
    });

    expect(readCommitBatch).toEqual([normalMessage]);
  });

  it('prioritizes lead work-sync nudges without mixing them into user-visible batches', async () => {
    const teamName = 'lead-work-sync-priority-team';
    const svc = new TeamProvisioningService();
    const harness = svc as unknown as LeadRelayPriorityServiceHarness;
    const run = createMemberSpawnRun({
      teamName,
      expectedMembers: ['alice'],
    }) as LeadRelayPriorityTestRun;
    run.child = { pid: 123 };
    run.processKilled = false;
    run.cancelRequested = false;
    run.provisioningComplete = true;

    const oldUserMessages = Array.from({ length: 10 }, (_, index) => {
      const suffix = String(index + 1).padStart(2, '0');
      return {
        from: 'user',
        to: 'team-lead',
        text: `Older user request ${suffix}.`,
        timestamp: `2026-04-25T09:${suffix}:00.000Z`,
        messageId: `msg-user-${suffix}`,
        source: 'user_sent',
        read: false,
      };
    });
    const workSyncMessage = {
      from: 'system',
      to: 'team-lead',
      text: 'Work sync required for task-1.',
      timestamp: '2026-04-25T10:00:00.000Z',
      messageId: 'msg-work-sync-priority',
      source: 'system_notification',
      messageKind: 'member_work_sync_nudge',
      workSyncIntent: 'agenda_sync',
      taskRefs: [{ taskId: 'task-1', displayId: 'task-1', teamName }],
      read: false,
    };
    const ordinarySystemMessage = {
      from: 'system',
      to: 'team-lead',
      text: 'Routine system notification.',
      timestamp: '2026-04-25T09:59:00.000Z',
      messageId: 'msg-system-routine',
      source: 'system_notification',
      read: false,
    };
    const inboxMessages: LeadRelayPriorityTestInboxMessage[] = [
      ...oldUserMessages,
      ordinarySystemMessage,
      workSyncMessage,
    ];
    let deliveredPrompt = '';
    const recoveryScheduler = vi.fn(async () => ({
      scheduled: true,
      reason: 'scheduled',
    }));
    const sendMessageToRun = vi.fn(async (targetRun: LeadRelayPriorityTestRun, message: string) => {
      deliveredPrompt = message;
      targetRun.leadRelayCapture?.resolveOnce('');
    });

    harness.runs.set(run.runId, run);
    harness.aliveRunByTeam.set(teamName, run.runId);
    harness.configReader = {
      getConfig: vi.fn(async () => ({
        projectPath: '/repo',
        members: [
          { name: 'team-lead', agentType: 'team-lead', role: 'Team Lead' },
          { name: 'alice', role: 'Developer' },
        ],
      })),
    };
    vi.spyOn(harness.inboxReader, 'getMessagesFor').mockResolvedValue(inboxMessages);
    harness.confirmSameTeamNativeMatches = vi.fn(async () => ({
      nativeMatchedMessageIds: new Set<string>(),
      persisted: true,
    }));
    harness.markInboxMessagesRead = vi.fn(async () => undefined);
    harness.resolveControlApiBaseUrl = vi.fn(async () => null);
    harness.scheduleLeadInboxFollowUpRelay = vi.fn();
    harness.sendMessageToRun = sendMessageToRun;
    svc.setMemberWorkSyncProofMissingRecoveryScheduler(recoveryScheduler);

    await expect(svc.relayLeadInboxMessages(teamName)).resolves.toBe(1);

    expect(sendMessageToRun).toHaveBeenCalledTimes(1);
    const messagesSection = deliveredPrompt.slice(deliveredPrompt.indexOf('Messages:'));
    expect(messagesSection).toContain('1) From: system');
    expect(messagesSection).toContain('Message kind: member_work_sync_nudge');
    expect(messagesSection).toContain('Work sync required for task-1.');
    expect(messagesSection).not.toContain('Older user request 01.');
    expect(messagesSection).not.toContain('Older user request 10.');
    expect(messagesSection).not.toContain('Routine system notification.');
    expect(deliveredPrompt).toContain(
      'Plain text reply visibility for this batch: internal lead activity only.'
    );
    expect(recoveryScheduler).toHaveBeenCalledWith({
      teamName,
      memberName: 'team-lead',
      originalMessageId: 'msg-work-sync-priority',
      taskRefs: [{ taskId: 'task-1', displayId: 'task-1', teamName }],
      reason: 'lead_member_work_sync_report_required',
    });
    expect(harness.scheduleLeadInboxFollowUpRelay).toHaveBeenCalledWith(teamName);
  });

  it('read-commits lead work-sync inbox rows after accepted report proof', async () => {
    const svc = new TeamProvisioningService();
    const harness = leadWorkSyncReadCommitHarness(svc);
    const recoveryScheduler = vi.fn();
    const workSyncMessage = {
      from: 'system',
      to: 'team-lead',
      text: 'Work sync required.',
      timestamp: '2026-04-25T10:00:01.000Z',
      messageId: 'msg-work-sync',
      messageKind: 'member_work_sync_nudge',
      taskRefs: [{ taskId: 'task-1', displayId: 'task-1', teamName: 'team-a' }],
      read: false,
    };
    vi.spyOn(harness, 'hasAcceptedLeadWorkSyncReport').mockResolvedValue(true);
    svc.setMemberWorkSyncProofMissingRecoveryScheduler(recoveryScheduler);

    const readCommitBatch = await harness.getLeadRelayReadCommitBatch({
      teamName: 'team-a',
      leadName: 'team-lead',
      batch: [workSyncMessage],
    });

    expect(readCommitBatch).toEqual([workSyncMessage]);
    expect(recoveryScheduler).not.toHaveBeenCalled();
  });

  it('read-commits lead work-sync inbox rows when proof-missing recovery is queued', async () => {
    const svc = new TeamProvisioningService();
    const harness = leadWorkSyncReadCommitHarness(svc);
    const recoveryScheduler = vi.fn(async () => ({
      scheduled: true,
      reason: 'scheduled',
      intentKey: 'proof-missing:msg-work-sync',
    }));
    const taskRefs = [{ taskId: 'task-1', displayId: 'task-1', teamName: 'team-a' }];
    const workSyncMessage = {
      from: 'system',
      to: 'team-lead',
      text: 'Work sync required.',
      timestamp: '2026-04-25T10:00:01.000Z',
      messageId: 'msg-work-sync',
      messageKind: 'member_work_sync_nudge',
      taskRefs,
      read: false,
    };
    vi.spyOn(harness, 'hasAcceptedLeadWorkSyncReport').mockResolvedValue(false);
    svc.setMemberWorkSyncProofMissingRecoveryScheduler(recoveryScheduler);

    const readCommitBatch = await harness.getLeadRelayReadCommitBatch({
      teamName: 'team-a',
      leadName: 'team-lead',
      batch: [workSyncMessage],
    });

    expect(readCommitBatch).toEqual([workSyncMessage]);
    expect(recoveryScheduler).toHaveBeenCalledWith({
      teamName: 'team-a',
      memberName: 'team-lead',
      originalMessageId: 'msg-work-sync',
      taskRefs,
      reason: 'lead_member_work_sync_report_required',
    });
  });

  it('applies an unseen newer failure signal and transitions the member to failed_to_start', async () => {
    const latestHeartbeatAt = '2026-04-16T10:00:00.000Z';
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            livenessSource: 'heartbeat',
            bootstrapConfirmed: true,
            lastHeartbeatAt: latestHeartbeatAt,
          }),
        ],
      ]),
      memberSpawnLeadInboxCursorByMember: new Map([
        [
          'alice',
          {
            timestamp: latestHeartbeatAt,
            messageId: 'msg-1',
          },
        ],
      ]),
    });
    const svc = new TeamProvisioningService();

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: 'Bootstrap failed: unsupported model',
        timestamp: '2026-04-16T10:01:00.000Z',
        messageId: 'msg-2',
        read: false,
      },
    ]);

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      hardFailureReason: 'Bootstrap failed: unsupported model',
    });
    expect(run.memberSpawnLeadInboxCursorByMember.get('alice')).toEqual({
      timestamp: '2026-04-16T10:01:00.000Z',
      messageId: 'msg-2',
    });
  });

  it('applies an unseen same-timestamp signal with a greater messageId and advances the cursor', async () => {
    const run = createMemberSpawnRun({
      startedAt: '2026-04-16T09:00:00.000Z',
      memberSpawnLeadInboxCursorByMember: new Map([
        [
          'alice',
          {
            timestamp: '2026-04-16T10:00:00.000Z',
            messageId: 'msg-2',
          },
        ],
      ]),
    });
    const svc = new TeamProvisioningService();

    vi.spyOn((svc as any).inboxReader, 'getMessagesFor').mockResolvedValue([
      {
        from: 'alice',
        text: 'heartbeat',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-2',
        read: false,
      },
      {
        from: 'alice',
        text: 'heartbeat',
        timestamp: '2026-04-16T10:00:00.000Z',
        messageId: 'msg-3',
        read: false,
      },
    ]);

    const applySignalSpy = vi.spyOn(svc as any, 'applyLeadInboxSpawnSignal');

    await (svc as any).refreshMemberSpawnStatusesFromLeadInbox(run);

    expect(applySignalSpy).toHaveBeenCalledTimes(1);
    expect(applySignalSpy).toHaveBeenCalledWith(
      run,
      'alice',
      expect.objectContaining({ messageId: 'msg-3' })
    );
    expect(run.memberSpawnLeadInboxCursorByMember.get('alice')).toEqual({
      timestamp: '2026-04-16T10:00:00.000Z',
      messageId: 'msg-3',
    });
  });

  it('does not bump lastHeartbeatAt for an equal heartbeat timestamp', () => {
    const existingEntry = createMemberSpawnStatusEntry({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      livenessSource: 'heartbeat',
      bootstrapConfirmed: true,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([['alice', existingEntry]]),
    });
    const svc = new TeamProvisioningService();

    (svc as any).setMemberSpawnStatus(
      run,
      'alice',
      'online',
      undefined,
      'heartbeat',
      '2026-04-16T10:00:00.000Z'
    );

    expect(run.memberSpawnStatuses.get('alice')).toBe(existingEntry);
  });

  it('does not bump lastHeartbeatAt for an older heartbeat timestamp', () => {
    const existingEntry = createMemberSpawnStatusEntry({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      livenessSource: 'heartbeat',
      bootstrapConfirmed: true,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([['alice', existingEntry]]),
    });
    const svc = new TeamProvisioningService();

    (svc as any).setMemberSpawnStatus(
      run,
      'alice',
      'online',
      undefined,
      'heartbeat',
      '2026-04-16T09:59:59.000Z'
    );

    expect(run.memberSpawnStatuses.get('alice')).toBe(existingEntry);
  });

  it('keeps duplicate_skipped already_running pending without strong evidence', () => {
    const run = createMemberSpawnRun();
    run.activeToolCalls.set('tool-agent-1', {
      memberName: 'alice',
      toolUseId: 'tool-agent-1',
      toolName: 'Agent',
      preview: 'Spawn teammate alice',
      startedAt: new Date().toISOString(),
      state: 'running',
      source: 'runtime',
    });
    run.memberSpawnToolUseIds.set('tool-agent-1', 'alice');

    const svc = new TeamProvisioningService();

    (svc as any).finishRuntimeToolActivity(
      run,
      'tool-agent-1',
      [
        {
          type: 'text',
          text: 'status: duplicate_skipped\nreason: already_running\nname: alice\nteam_name: nice-team',
        },
      ],
      false
    );

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      hardFailure: false,
    });
  });

  it('clears a pending restart when the teammate is confirmed online via process liveness', () => {
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            firstSpawnAcceptedAt: new Date().toISOString(),
          }),
        ],
      ]),
    });
    run.pendingMemberRestarts.set('alice', {
      requestedAt: new Date().toISOString(),
      desired: {
        name: 'alice',
        providerId: 'codex',
        model: 'gpt-5.4-mini',
        effort: 'medium',
      },
    });
    const svc = new TeamProvisioningService();

    (svc as any).setMemberSpawnStatus(run, 'alice', 'online', undefined, 'process');

    expect(run.pendingMemberRestarts.has('alice')).toBe(false);
    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      livenessSource: 'process',
    });
  });

  it('treats deterministic already_running as a failed restart when a restart is pending', () => {
    const run = createMemberSpawnRun({
      teamName: 'nice-team',
      expectedMembers: ['alice'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            firstSpawnAcceptedAt: new Date().toISOString(),
          }),
        ],
      ]),
    });
    run.pendingMemberRestarts.set('alice', {
      requestedAt: new Date().toISOString(),
      desired: {
        name: 'alice',
        providerId: 'codex',
        model: 'gpt-5.4-mini',
        effort: 'medium',
      },
    });
    const svc = new TeamProvisioningService();

    const handled = (svc as any).handleDeterministicBootstrapEvent(run, {
      type: 'system',
      subtype: 'team_bootstrap',
      event: 'member_spawn_result',
      member_name: 'alice',
      outcome: 'already_running',
      run_id: run.runId,
      team_name: run.teamName,
      seq: 1,
    });

    expect(handled).toBe(true);
    expect(run.pendingMemberRestarts.has('alice')).toBe(false);
    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      hardFailureReason:
        'Restart for teammate "alice" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
    });
  });

  it('clears a pending restart when deterministic spawn reports a hard failure', () => {
    const run = createMemberSpawnRun({
      teamName: 'nice-team',
      expectedMembers: ['alice'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            firstSpawnAcceptedAt: new Date().toISOString(),
          }),
        ],
      ]),
    });
    run.pendingMemberRestarts.set('alice', {
      requestedAt: new Date().toISOString(),
      desired: {
        name: 'alice',
        providerId: 'codex',
        model: 'gpt-5.4-mini',
        effort: 'medium',
      },
    });
    const svc = new TeamProvisioningService();

    const handled = (svc as any).handleDeterministicBootstrapEvent(run, {
      type: 'system',
      subtype: 'team_bootstrap',
      event: 'member_spawn_result',
      member_name: 'alice',
      outcome: 'failed',
      reason: 'spawn failed hard',
      run_id: run.runId,
      team_name: run.teamName,
      seq: 1,
    });

    expect(handled).toBe(true);
    expect(run.pendingMemberRestarts.has('alice')).toBe(false);
    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      hardFailureReason: 'spawn failed hard',
    });
  });

  it('reports workspace trust failures with a specific deterministic bootstrap title', () => {
    const reason =
      'Teammate "Gayani" cannot start in headless process runtime because workspace trust is not accepted for "C:\\Users\\vilok\\OneDrive\\Desktop\\Safar 0.1". Open that workspace once interactively and accept trust, then launch the team again.';
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-bootstrap',
      teamName: 'workspace-trust-bootstrap-team',
      expectedMembers: ['Gayani'],
    });
    Object.assign(run, {
      cancelRequested: false,
      isLaunch: false,
      lastDeterministicBootstrapSeq: 0,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'assembling',
        message: 'Spawning teammate runtimes',
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const svc = new TeamProvisioningService();
    vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(null);
    vi.spyOn(svc as any, 'cleanupRun').mockImplementation(() => {});

    const handled = (svc as any).handleDeterministicBootstrapEvent(run, {
      type: 'system',
      subtype: 'team_bootstrap',
      event: 'failed',
      reason,
      run_id: run.runId,
      team_name: run.teamName,
      seq: 1,
    });

    expect(handled).toBe(true);
    expect(progressUpdates.at(-1)).toMatchObject({
      state: 'failed',
      message: 'Workspace trust required',
      error: reason,
    });
    expect(run.memberSpawnStatuses.get('Gayani')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailureReason: reason,
    });
  });

  it('includes legacy member provider fields when planning workspace trust providers', () => {
    const svc = new TeamProvisioningService();

    expect(
      (svc as any).collectWorkspaceTrustProviders({
        leadProviderId: 'anthropic',
        members: [{ name: 'alice', provider: 'codex' }],
      })
    ).toEqual(['claude', 'codex']);
  });

  it('dedupes workspace trust providers across lead, member providerId, and legacy provider fields', () => {
    const svc = new TeamProvisioningService();

    expect(
      (svc as any).collectWorkspaceTrustProviders({
        leadProviderId: 'codex',
        members: [
          { name: 'alice', providerId: 'anthropic' },
          { name: 'bob', providerId: 'codex' },
          { name: 'cara', provider: 'gemini' },
          { name: 'drew', providerId: 'opencode' },
        ],
      })
    ).toEqual(['claude', 'codex', 'gemini', 'opencode']);
  });

  it('uses the canonical repository root for workspace trust git worktree candidates', async () => {
    const svc = new TeamProvisioningService();
    const harness = svc as unknown as {
      collectWorkspaceTrustWorkspaces(input: {
        cwd: string;
        members: Array<{ name: string; cwd: string; isolation: 'worktree' }>;
      }): Promise<WorkspaceTrustWorkspace[]>;
    };
    const tempRoot = fs.realpathSync(tempClaudeRoot);
    const repoDir = path.join(tempRoot, 'repo');
    const worktreeDir = path.join(tempRoot, 'worktrees', 'alice');
    const worktreeGitDir = path.join(repoDir, '.git', 'worktrees', 'alice');
    fs.mkdirSync(worktreeDir, { recursive: true });
    fs.mkdirSync(worktreeGitDir, { recursive: true });
    fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`, 'utf8');
    fs.writeFileSync(path.join(worktreeGitDir, 'commondir'), '../..\n', 'utf8');
    fs.writeFileSync(
      path.join(worktreeGitDir, 'gitdir'),
      `${path.join(worktreeDir, '.git')}\n`,
      'utf8'
    );

    const workspaces = await harness.collectWorkspaceTrustWorkspaces({
      cwd: repoDir,
      members: [{ name: 'alice', cwd: worktreeDir, isolation: 'worktree' }],
    });

    const memberWorktrees = workspaces.filter(
      (workspace) => workspace.source === 'member-worktree'
    );
    expect(memberWorktrees[0]).toMatchObject({
      cwd: worktreeDir,
      gitRootConfigKey: repoDir,
      memberId: 'alice',
    });
    expect(memberWorktrees.every((workspace) => workspace.gitRootConfigKey === repoDir)).toBe(true);
  });

  it('degrades workspace trust planning failures without blocking launch preparation', async () => {
    const svc = new TeamProvisioningService();
    const workspaces = buildWorkspaceTrustPathCandidates({
      cwd: '/tmp/workspace-trust-planning-fallback',
      platform: 'posix',
    });
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(async () => {
        throw new Error('args planning crashed');
      }),
      planFull: vi.fn(async () => {
        throw new Error('full planning crashed');
      }),
      execute: vi.fn(),
    } as any);

    await expect(
      (svc as any).planWorkspaceTrustArgsOnlySafely({
        providers: ['claude', 'codex'],
        workspaces,
        featureFlags: {
          enabled: true,
          claudePty: true,
          codexArgs: true,
          retry: false,
          fileLock: false,
        },
      })
    ).resolves.toEqual({ launchArgPatches: [] });

    await expect(
      (svc as any).planWorkspaceTrustFullSafely({
        providers: ['claude', 'codex'],
        workspaces,
        featureFlags: {
          enabled: true,
          claudePty: true,
          codexArgs: true,
          retry: false,
          fileLock: false,
        },
      })
    ).resolves.toEqual({ workspaces, launchArgPatches: [] });
    expect(vi.mocked(console.warn).mock.calls.map((call) => call.join(' '))).toEqual([
      expect.stringContaining(
        'Workspace trust args-only planning failed; continuing without trust arg patches'
      ),
      expect.stringContaining(
        'Workspace trust full planning failed; continuing without trust arg patches'
      ),
    ]);
    vi.mocked(console.warn).mockClear();
  });

  it('keeps launch moving with info diagnostics when workspace trust preflight succeeds', async () => {
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-preflight-ok',
      teamName: 'workspace-trust-preflight-ok-team',
      expectedMembers: ['alice'],
    });
    Object.assign(run, {
      cancelRequested: false,
      processKilled: false,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'validating',
        message: 'Validating launch',
        warnings: [],
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const execute = vi.fn(async () => ({
      id: 'claude-pty-workspace-trust',
      provider: 'claude',
      status: 'ok',
      workspaceIds: ['workspace-trust-1'],
      evidence: ['trusted project key'],
    }));
    const svc = new TeamProvisioningService();
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(),
      planFull: vi.fn(),
      execute,
    } as any);
    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    await (svc as any).prepareWorkspaceTrustForDeterministicRun({
      mode: 'create',
      run,
      claudePath: '/usr/local/bin/claude',
      shellEnv: {},
      stopAllGenerationAtStart: (svc as any).stopAllTeamsGeneration,
      workspaceTrustPlan: {
        launchArgPatches: [],
        workspaces: buildWorkspaceTrustPathCandidates({
          cwd: '/tmp/workspace-trust-preflight-ok-team',
          platform: 'posix',
        }),
      },
      featureFlags: {
        enabled: true,
        claudePty: true,
        codexArgs: true,
        retry: false,
        fileLock: false,
      },
      provisioningEnv: {
        anthropicApiKeyHelper: null,
      },
    });

    expect(execute).toHaveBeenCalledTimes(1);
    expect(run.workspaceTrustExecution).toMatchObject({ status: 'ok' });
    expect(run.progress.warnings).toEqual([]);
    expect(progressUpdates.at(-1).launchDiagnostics).toEqual([
      expect.objectContaining({
        severity: 'info',
        code: 'workspace_trust_preflight',
        label: 'Workspace trust preflight completed',
        detail: 'trusted project key',
      }),
    ]);
  });

  it('keeps launch alive with diagnostics when workspace trust preflight throws', async () => {
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-preflight-throw',
      teamName: 'workspace-trust-preflight-team',
      expectedMembers: ['alice'],
    });
    Object.assign(run, {
      cancelRequested: false,
      processKilled: false,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'validating',
        message: 'Validating launch',
        warnings: [],
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const svc = new TeamProvisioningService();
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(),
      planFull: vi.fn(),
      execute: vi.fn(async () => {
        throw new Error('preflight adapter crashed');
      }),
    } as any);
    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    await (svc as any).prepareWorkspaceTrustForDeterministicRun({
      mode: 'create',
      run,
      claudePath: '/usr/local/bin/claude',
      shellEnv: {
        CLAUDE_ENABLE_DETERMINISTIC_TEAM_BOOTSTRAP: '1',
        CLAUDE_TEAM_ANTHROPIC_API_KEY_HELPER_SETTINGS_PATH: '/tmp/helper.json',
      },
      stopAllGenerationAtStart: (svc as any).stopAllTeamsGeneration,
      workspaceTrustPlan: {
        launchArgPatches: [],
        workspaces: buildWorkspaceTrustPathCandidates({
          cwd: '/tmp/workspace-trust-preflight-team',
          platform: 'posix',
        }),
      },
      featureFlags: {
        enabled: true,
        claudePty: true,
        codexArgs: true,
        retry: false,
        fileLock: false,
      },
      provisioningEnv: {
        anthropicApiKeyHelper: null,
      },
    });

    expect(run.workspaceTrustExecution).toMatchObject({
      status: 'soft_failed',
      errorCode: 'workspace_trust_preflight_error',
      errorMessage: 'preflight adapter crashed',
    });
    expect(run.workspaceTrustDiagnostics).toMatchObject({
      attempt: 1,
      strategyResults: [
        expect.objectContaining({
          status: 'soft_failed',
          errorMessage: 'preflight adapter crashed',
        }),
      ],
    });
    expect(run.progress.warnings).toContain('preflight adapter crashed');
    expect(progressUpdates.at(0)).toMatchObject({
      state: 'spawning',
      message: 'Preparing workspace trust',
    });
    expect(progressUpdates.at(-1).warnings).toContain('preflight adapter crashed');
    expect(progressUpdates.at(-1).launchDiagnostics).toEqual([
      expect.objectContaining({
        severity: 'warning',
        code: 'workspace_trust_preflight',
        label: 'Workspace trust preflight could not verify trust',
        detail: 'preflight adapter crashed',
      }),
    ]);
  });

  it('blocks launch with structured workspace trust diagnostics when preflight is blocked', async () => {
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-preflight-blocked',
      teamName: 'workspace-trust-preflight-blocked-team',
      expectedMembers: ['alice'],
    });
    Object.assign(run, {
      cancelRequested: false,
      processKilled: false,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'validating',
        message: 'Validating launch',
        warnings: [],
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const svc = new TeamProvisioningService();
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(),
      planFull: vi.fn(),
      execute: vi.fn(async () => ({
        id: 'claude-pty-workspace-trust',
        provider: 'claude',
        status: 'blocked',
        workspaceIds: ['workspace-trust-1'],
        errorCode: 'workspace_trust_preflight_not_confirmed',
        errorMessage: 'Claude workspace trust was not confirmed for /tmp/project',
        evidence: ['claude workspace trust prompt'],
      })),
    } as any);
    vi.spyOn(svc as any, 'cleanupRun').mockImplementation(() => {});
    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    await expect(
      (svc as any).prepareWorkspaceTrustForDeterministicRun({
        mode: 'create',
        run,
        claudePath: '/usr/local/bin/claude',
        shellEnv: {},
        stopAllGenerationAtStart: (svc as any).stopAllTeamsGeneration,
        workspaceTrustPlan: {
          launchArgPatches: [],
          workspaces: buildWorkspaceTrustPathCandidates({
            cwd: '/tmp/project',
            platform: 'posix',
          }),
        },
        featureFlags: {
          enabled: true,
          claudePty: true,
          codexArgs: true,
          retry: false,
          fileLock: false,
        },
        provisioningEnv: {
          anthropicApiKeyHelper: null,
        },
      })
    ).rejects.toThrow('Claude workspace trust was not confirmed for /tmp/project');

    expect(progressUpdates.at(-1)).toMatchObject({
      state: 'failed',
      message: 'Workspace trust required',
      error: 'Claude workspace trust was not confirmed for /tmp/project',
    });
    expect(progressUpdates.at(-1).launchDiagnostics).toEqual([
      expect.objectContaining({
        severity: 'error',
        code: 'workspace_trust_preflight',
        label: 'Workspace trust preflight blocked launch',
        detail: 'Claude workspace trust was not confirmed for /tmp/project',
      }),
    ]);
  });

  it('cancels launch before spawn when workspace trust preflight is cancelled', async () => {
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-preflight-cancelled',
      teamName: 'workspace-trust-preflight-cancelled-team',
      expectedMembers: ['alice'],
    });
    Object.assign(run, {
      cancelRequested: false,
      processKilled: false,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'validating',
        message: 'Validating launch',
        warnings: [],
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const svc = new TeamProvisioningService();
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(),
      planFull: vi.fn(),
      execute: vi.fn(async () => ({
        id: 'claude-pty-workspace-trust',
        provider: 'claude',
        status: 'cancelled',
        workspaceIds: ['workspace-trust-1'],
        errorCode: 'workspace_trust_lock_cancelled',
      })),
    } as any);
    vi.spyOn(svc as any, 'cleanupRun').mockImplementation(() => {});
    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    await expect(
      (svc as any).prepareWorkspaceTrustForDeterministicRun({
        mode: 'create',
        run,
        claudePath: '/usr/local/bin/claude',
        shellEnv: {},
        stopAllGenerationAtStart: (svc as any).stopAllTeamsGeneration,
        workspaceTrustPlan: {
          launchArgPatches: [],
          workspaces: buildWorkspaceTrustPathCandidates({
            cwd: '/tmp/workspace-trust-preflight-cancelled-team',
            platform: 'posix',
          }),
        },
        featureFlags: {
          enabled: true,
          claudePty: true,
          codexArgs: true,
          retry: false,
          fileLock: false,
        },
        provisioningEnv: {
          anthropicApiKeyHelper: null,
        },
      })
    ).rejects.toThrow('Team launch cancelled');

    expect(run.cancelRequested).toBe(true);
    expect(progressUpdates.at(-1)).toMatchObject({
      state: 'cancelled',
      message: 'Team launch cancelled',
    });
    expect(progressUpdates.at(-1).launchDiagnostics).toBeUndefined();
  });

  it('does not execute workspace trust preflight when the feature is disabled', async () => {
    const progressUpdates: any[] = [];
    const run = createMemberSpawnRun({
      runId: 'run-workspace-trust-disabled',
      teamName: 'workspace-trust-disabled-team',
      expectedMembers: ['alice'],
    });
    Object.assign(run, {
      cancelRequested: false,
      processKilled: false,
      progress: {
        runId: run.runId,
        teamName: run.teamName,
        state: 'validating',
        message: 'Validating launch',
        warnings: [],
        startedAt: '2026-05-12T10:00:00.000Z',
        updatedAt: '2026-05-12T10:00:00.000Z',
      },
      onProgress: (progress: any) => {
        progressUpdates.push(progress);
      },
    });
    const execute = vi.fn();
    const svc = new TeamProvisioningService();
    svc.setWorkspaceTrustCoordinator({
      planArgsOnly: vi.fn(),
      planFull: vi.fn(),
      execute,
    } as any);

    await (svc as any).prepareWorkspaceTrustForDeterministicRun({
      mode: 'create',
      run,
      claudePath: '/usr/local/bin/claude',
      shellEnv: {},
      stopAllGenerationAtStart: (svc as any).stopAllTeamsGeneration,
      workspaceTrustPlan: {
        launchArgPatches: [],
        workspaces: buildWorkspaceTrustPathCandidates({
          cwd: '/tmp/workspace-trust-disabled-team',
          platform: 'posix',
        }),
      },
      featureFlags: {
        enabled: false,
        claudePty: false,
        codexArgs: false,
        retry: false,
        fileLock: false,
      },
      provisioningEnv: {
        anthropicApiKeyHelper: null,
      },
    });

    expect(execute).not.toHaveBeenCalled();
    expect(progressUpdates).toEqual([]);
    expect(run.workspaceTrustExecution).toBeUndefined();
    expect(run.workspaceTrustDiagnostics).toBeUndefined();
    expect(run.progress).toMatchObject({
      state: 'validating',
      message: 'Validating launch',
    });
  });

  it('clears stale failed_to_start state when live runtime metadata proves the teammate is alive', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: true,
              model: 'gpt-5.2',
              livenessKind: 'runtime_process',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'Teammate did not join within the launch grace window.',
        hardFailure: true,
        hardFailureReason: 'Teammate did not join within the launch grace window.',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'gpt-5.2',
      livenessSource: 'process',
    });
  });

  it('clears registered-only stale failure when a verified runtime process appears later', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: true,
              model: 'gpt-5.4',
              livenessKind: 'runtime_process',
              runtimeDiagnostic: 'verified runtime process detected',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('forge-labs-10', {
      tom: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'registered runtime metadata without live process',
        hardFailure: true,
        hardFailureReason: 'registered runtime metadata without live process',
        livenessKind: 'registered_only',
        runtimeDiagnostic: 'registered runtime metadata without live process',
      }),
    });

    expect(result.tom).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'gpt-5.4',
      livenessKind: 'runtime_process',
      runtimeDiagnostic: 'verified runtime process detected',
      livenessSource: 'process',
    });
  });

  it('clears registered-only stale failure with process-table diagnostic suffix', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: true,
              model: 'gpt-5.4',
              livenessKind: 'runtime_process',
              runtimeDiagnostic: 'verified runtime process detected',
            },
          ],
        ])
    );

    const reason = 'registered runtime metadata without live process; process table unavailable';
    const result = await harness.attachLiveRuntimeMetadataToStatuses('forge-labs-10', {
      tom: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: reason,
        hardFailure: true,
        hardFailureReason: reason,
        livenessKind: 'registered_only',
        runtimeDiagnostic: reason,
      }),
    });

    expect(result.tom).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'gpt-5.4',
      livenessKind: 'runtime_process',
      runtimeDiagnostic: 'verified runtime process detected',
      livenessSource: 'process',
    });
  });

  it('clears process-table unavailable failure when a verified runtime process appears later', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'alice',
            {
              alive: true,
              model: 'gpt-5.3-codex',
              livenessKind: 'runtime_process',
              runtimeDiagnostic: 'verified runtime process detected',
            },
          ],
        ])
    );

    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops-10', {
      alice: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'runtime pid could not be verified because process table is unavailable',
        hardFailure: true,
        hardFailureReason: 'runtime pid could not be verified because process table is unavailable',
        livenessKind: 'registered_only',
        runtimeDiagnostic: 'runtime pid could not be verified because process table is unavailable',
        runtimeDiagnosticSeverity: 'warning',
      }),
    });

    expect(result.alice).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'gpt-5.3-codex',
      livenessKind: 'runtime_process',
      runtimeDiagnostic: 'verified runtime process detected',
      livenessSource: 'process',
    });
  });

  it('keeps process-table unavailable failure failed when only weak metadata is available', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'alice',
            {
              alive: false,
              livenessKind: 'registered_only',
              runtimeDiagnostic:
                'runtime pid could not be verified because process table is unavailable',
              runtimeDiagnosticSeverity: 'warning',
            },
          ],
        ])
    );

    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops-10', {
      alice: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'runtime pid could not be verified because process table is unavailable',
        hardFailure: true,
        hardFailureReason: 'runtime pid could not be verified because process table is unavailable',
        livenessKind: 'registered_only',
        runtimeDiagnostic: 'runtime pid could not be verified because process table is unavailable',
        runtimeDiagnosticSeverity: 'warning',
      }),
    });

    expect(result.alice).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      runtimeAlive: false,
      hardFailure: true,
      hardFailureReason: 'runtime pid could not be verified because process table is unavailable',
      error: 'runtime pid could not be verified because process table is unavailable',
      livenessKind: 'registered_only',
      runtimeDiagnostic: 'runtime pid could not be verified because process table is unavailable',
      runtimeDiagnosticSeverity: 'warning',
      livenessSource: undefined,
    });
  });

  it('clears provisioned-but-not-alive failure from confirmed bootstrap even with weak metadata', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    const processTableReason =
      'runtime pid could not be verified because process table is unavailable';
    const exitReason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              model: 'sonnet',
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: processTableReason,
              runtimeDiagnosticSeverity: 'warning',
            },
          ],
        ])
    );

    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops', {
      tom: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: exitReason,
        hardFailure: true,
        hardFailureReason: exitReason,
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: true,
        livenessKind: 'confirmed_bootstrap',
        runtimeDiagnostic: processTableReason,
        runtimeDiagnosticSeverity: 'warning',
        firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
        lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
      }),
    });

    expect(result.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      bootstrapConfirmed: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'sonnet',
      livenessKind: 'confirmed_bootstrap',
      runtimeDiagnostic: undefined,
      runtimeDiagnosticSeverity: undefined,
      livenessSource: undefined,
    });
  });

  it('does not let weak metadata undo confirmed bootstrap failure healing', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    const processTableReason =
      'runtime pid could not be verified because process table is unavailable';
    const exitReason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              model: 'sonnet',
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: processTableReason,
              runtimeDiagnosticSeverity: 'warning',
            },
          ],
        ])
    );

    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops', {
      tom: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'confirmed_alive',
        error: exitReason,
        hardFailure: true,
        hardFailureReason: exitReason,
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        runtimeDiagnostic: processTableReason,
        runtimeDiagnosticSeverity: 'warning',
        firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
        lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
      }),
    });

    expect(result.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      bootstrapConfirmed: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'sonnet',
    });
  });

  it('does not keep healed confirmed-bootstrap status alive when refreshed runtime metadata is an error', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(() =>
      Promise.resolve(
        new Map([
          [
            'tom',
            {
              alive: false,
              model: 'sonnet',
              livenessKind: 'not_found',
              pidSource: 'process_table',
              runtimeDiagnostic: 'Runtime process crashed',
              runtimeDiagnosticSeverity: 'error',
            },
          ],
        ])
      )
    );

    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops', {
      tom: createMemberSpawnStatusEntry({
        status: 'online',
        launchState: 'confirmed_alive',
        agentToolAccepted: true,
        runtimeAlive: true,
        bootstrapConfirmed: true,
        hardFailure: false,
        livenessKind: 'confirmed_bootstrap',
        runtimeModel: 'sonnet',
      }),
    });

    expect(result.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: false,
      bootstrapConfirmed: true,
      hardFailure: false,
      livenessKind: 'not_found',
      runtimeDiagnostic: 'Runtime process crashed',
      runtimeDiagnosticSeverity: 'error',
      runtimeModel: 'sonnet',
      livenessSource: undefined,
    });
  });

  it('does not clear OpenCode bridge launch failure from process-only liveness', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: false,
              model: 'openrouter/google/gemini-2.5-flash',
              livenessKind: 'runtime_process_candidate',
              providerId: 'opencode',
              runtimeDiagnostic:
                'OpenCode runtime process detected, but teammate bootstrap is not confirmed',
              runtimeDiagnosticSeverity: 'warning',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('12vector-room-10', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'OpenCode bridge reported member launch failure',
        hardFailure: true,
        hardFailureReason: 'OpenCode bridge reported member launch failure',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      runtimeAlive: false,
      hardFailure: true,
      hardFailureReason: 'OpenCode bridge reported member launch failure',
      error: 'OpenCode bridge reported member launch failure',
      runtimeModel: 'openrouter/google/gemini-2.5-flash',
      livenessKind: 'runtime_process_candidate',
      runtimeDiagnostic:
        'OpenCode runtime process detected, but teammate bootstrap is not confirmed',
      runtimeDiagnosticSeverity: 'warning',
      livenessSource: undefined,
    });
  });

  it('does not clear concrete OpenCode bridge failures even when a process is visible', async () => {
    const svc = new TeamProvisioningService();
    const harness = privateHarness(svc);
    harness.getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: true,
              model: 'openrouter/minimax/minimax-m2.5',
              livenessKind: 'runtime_process',
              providerId: 'opencode',
              runtimeDiagnostic: 'OpenCode runtime process detected',
              runtimeDiagnosticSeverity: 'info',
            },
          ],
        ])
    );

    const reason =
      'OpenCode bridge failed: OpenCode app-managed bootstrap launch requires a fresh capability snapshot before state-changing launch; process table unavailable';
    const result = await harness.attachLiveRuntimeMetadataToStatuses('signal-ops-10', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: reason,
        hardFailure: true,
        hardFailureReason: reason,
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      runtimeAlive: false,
      hardFailure: true,
      hardFailureReason: reason,
      error: reason,
      runtimeModel: 'openrouter/minimax/minimax-m2.5',
      livenessKind: 'runtime_process',
      runtimeDiagnostic: 'OpenCode runtime process detected',
      runtimeDiagnosticSeverity: 'info',
      livenessSource: undefined,
    });
  });

  it('maps suffixed live runtime metadata keys back onto canonical spawn statuses', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob-2',
            {
              alive: true,
              model: 'gpt-5.2',
              livenessKind: 'runtime_process',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error: 'Teammate did not join within the launch grace window.',
        hardFailure: true,
        hardFailureReason: 'Teammate did not join within the launch grace window.',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'online',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      runtimeModel: 'gpt-5.2',
      livenessSource: 'process',
    });
  });

  it('downgrades stale process liveness to pending when live metadata is weak', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: false,
              livenessKind: 'runtime_process_candidate',
              runtimeDiagnostic:
                'OpenCode runtime pid is alive, but process identity is unverified',
              runtimeDiagnosticSeverity: 'warning',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'online',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: true,
        livenessSource: 'process',
        bootstrapConfirmed: false,
        hardFailure: false,
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      livenessSource: undefined,
      livenessKind: 'runtime_process_candidate',
      runtimeDiagnostic: 'OpenCode runtime pid is alive, but process identity is unverified',
      runtimeDiagnosticSeverity: 'warning',
    });
  });

  it('keeps OpenCode secondary pending-bootstrap status waiting when live runtime process is attached', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: true,
              model: 'openrouter/minimax/minimax-m2.5',
              livenessKind: 'runtime_process',
              providerId: 'opencode',
              runtimeDiagnostic: 'OpenCode runtime process detected',
              runtimeDiagnosticSeverity: 'info',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses(
      'beacon-desk-4',
      {
        tom: createMemberSpawnStatusEntry({
          status: 'waiting',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
        }),
      },
      { openCodeSecondaryBootstrapPendingMembers: new Set(['tom']) }
    );

    expect(result.tom).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      bootstrapConfirmed: false,
      livenessSource: undefined,
      livenessKind: 'runtime_process',
      runtimeModel: 'openrouter/minimax/minimax-m2.5',
      runtimeDiagnostic: 'OpenCode runtime process detected',
      runtimeDiagnosticSeverity: 'info',
    });
  });

  it('marks stale OpenCode secondary pending-bootstrap status stalled when live runtime is attached after restart', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: true,
              model: 'openrouter/minimax/minimax-m2.5',
              livenessKind: 'runtime_process',
              providerId: 'opencode',
              runtimeDiagnostic: 'OpenCode runtime process detected',
              runtimeDiagnosticSeverity: 'info',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses(
      'beacon-desk-4',
      {
        tom: createMemberSpawnStatusEntry({
          status: 'waiting',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
          firstSpawnAcceptedAt: new Date(Date.now() - 6 * 60_000).toISOString(),
        }),
      },
      { openCodeSecondaryBootstrapPendingMembers: new Set(['tom']) }
    );

    expect(result.tom).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: true,
      bootstrapConfirmed: false,
      livenessSource: undefined,
      livenessKind: 'runtime_process',
      bootstrapStalled: true,
      runtimeDiagnostic: 'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
      runtimeDiagnosticSeverity: 'warning',
    });
  });

  it('self-heals stale persisted OpenCode secondary bootstrap without live metadata', async () => {
    const teamName = 'zz-opencode-persisted-bootstrap-stall-no-live';
    const leadSessionId = 'lead-session';
    const acceptedAt = new Date(Date.now() - 6 * 60_000).toISOString();

    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'tom',
        providerId: 'opencode',
        model: 'openrouter/minimax/minimax-m2.5',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['tom']);
    writeLaunchState(teamName, leadSessionId, {
      tom: {
        providerId: 'opencode',
        model: 'openrouter/minimax/minimax-m2.5',
        laneId: 'secondary:opencode:tom',
        laneKind: 'secondary',
        laneOwnerProviderId: 'opencode',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        runtimeSessionId: 'ses_tom_partial',
        runtimePid: 55947,
        livenessKind: 'registered_only',
        firstSpawnAcceptedAt: acceptedAt,
        diagnostics: [
          'OpenCode bootstrap MCP tool failed before required attach completed: runtime_bootstrap_checkin',
          'member_briefing at 2026-05-04T18:25:43.091Z',
        ],
        lastEvaluatedAt: acceptedAt,
      },
    });

    const svc = new TeamProvisioningService();
    vi.spyOn(svc as any, 'getLiveTeamAgentRuntimeMetadata').mockResolvedValue(new Map());

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      bootstrapConfirmed: false,
      hardFailure: false,
      bootstrapStalled: true,
      runtimeDiagnostic: 'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
      runtimeDiagnosticSeverity: 'warning',
    });
    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.tom).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      bootstrapConfirmed: false,
      hardFailure: false,
      bootstrapStalled: true,
    });
  });

  it('sends one targeted OpenCode bootstrap check-in retry when a partial bootstrap stalls', async () => {
    const teamName = 'zz-opencode-bootstrap-checkin-retry';
    const acceptedAt = new Date(Date.now() - 6 * 60_000).toISOString();
    const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
      ok: true,
      providerId: 'opencode',
      memberName: String(input.memberName),
      diagnostics: [],
    }));
    const svc = new TeamProvisioningService();
    svc.setRuntimeAdapterRegistry(
      new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ])
    );
    vi.spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'getLiveTeamAgentRuntimeMetadata').mockResolvedValue(
      new Map([
        [
          'tom',
          {
            alive: true,
            providerId: 'opencode',
            livenessKind: 'runtime_process',
            runtimeSessionId: 'ses_tom_partial',
            runtimeDiagnostic: 'OpenCode runtime process detected',
            runtimeDiagnosticSeverity: 'info',
          },
        ],
      ])
    );

    const run = createMemberSpawnRun({
      teamName,
      runId: 'run-bootstrap-checkin-retry',
      expectedMembers: ['tom'],
      memberSpawnStatuses: new Map([
        [
          'tom',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            firstSpawnAcceptedAt: acceptedAt,
            livenessKind: 'runtime_process',
          }),
        ],
      ]),
    });
    run.onProgress = vi.fn();
    run.isLaunch = false;
    run.request = {
      teamName,
      cwd: '/Users/test/proj',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:tom',
        providerId: 'opencode',
        member: {
          name: 'tom',
          providerId: 'opencode',
          model: 'openrouter/minimax/minimax-m2.5',
          cwd: '/Users/test/proj',
        },
        runId: 'opencode-run-tom',
        state: 'finished',
        result: {
          runId: 'opencode-run-tom',
          teamName,
          launchPhase: 'active',
          teamLaunchState: 'partial_pending',
          members: {
            tom: {
              memberName: 'tom',
              providerId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              sessionId: 'ses_tom_partial',
              bootstrapEvidenceSource: 'runtime_bootstrap_checkin',
              bootstrapMode: 'model_tool_checkin',
              diagnostics: [
                'runtime_bootstrap_checkin failed: Not connected',
                'member_briefing at 2026-05-04T18:25:43.091Z',
              ],
            },
          },
          warnings: [],
          diagnostics: [],
        },
        warnings: [],
        diagnostics: [],
      },
    ];
    (svc as any).runs.set(run.runId, run);
    (svc as any).aliveRunByTeam.set(teamName, run.runId);

    await (svc as any).reevaluateMemberLaunchStatus(run, 'tom');
    await (svc as any).reevaluateMemberLaunchStatus(run, 'tom');

    expect(sendMessageToMember).toHaveBeenCalledTimes(1);
    expect(sendMessageToMember).toHaveBeenCalledWith(
      expect.objectContaining({
        runId: 'opencode-run-tom',
        teamName,
        laneId: 'secondary:opencode:tom',
        memberName: 'tom',
        cwd: '/Users/test/proj',
        bootstrapCheckinRetry: {
          runtimeSessionId: 'ses_tom_partial',
          reason: 'runtime_bootstrap_checkin failed: Not connected',
        },
      })
    );
  });

  it('does not send legacy OpenCode bootstrap check-in retry for app-managed bootstrap stalls', async () => {
    const teamName = 'zz-opencode-app-managed-no-checkin-retry';
    const acceptedAt = new Date(Date.now() - 6 * 60_000).toISOString();
    const laneId = 'secondary:opencode:tom';
    const runId = 'opencode-run-tom';
    const runtimeSessionId = 'ses_tom_app_managed';
    const appManagedBootstrapCandidate = {
      schemaVersion: 1,
      source: 'app_managed_bootstrap',
      teamName,
      memberName: 'tom',
      runId,
      laneId,
      runtimeSessionId,
      messageID: 'msg_tom_bootstrap',
      contextHash: 'ctx_tom',
      briefingHash: 'brief_tom',
      injectionVerifiedAt: acceptedAt,
      candidateAt: acceptedAt,
    } as const;
    const sendMessageToMember = vi.fn(async (input: Record<string, unknown>) => ({
      ok: true,
      providerId: 'opencode',
      memberName: String(input.memberName),
      diagnostics: [],
    }));
    const svc = new TeamProvisioningService();
    svc.setRuntimeAdapterRegistry(
      new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: vi.fn(),
          stop: vi.fn(),
          sendMessageToMember,
        } as any,
      ])
    );
    vi.spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'getLiveTeamAgentRuntimeMetadata').mockResolvedValue(
      new Map([
        [
          'tom',
          {
            alive: true,
            providerId: 'opencode',
            livenessKind: 'runtime_process',
            runtimeSessionId,
            runtimeDiagnostic: 'OpenCode runtime process detected',
            runtimeDiagnosticSeverity: 'info',
          },
        ],
      ])
    );

    const run = createMemberSpawnRun({
      teamName,
      runId: 'run-app-managed-no-checkin-retry',
      expectedMembers: ['tom'],
      memberSpawnStatuses: new Map([
        [
          'tom',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
            firstSpawnAcceptedAt: acceptedAt,
            livenessKind: 'runtime_process',
          }),
        ],
      ]),
    });
    run.onProgress = vi.fn();
    run.isLaunch = false;
    run.request = {
      teamName,
      cwd: '/Users/test/proj',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.mixedSecondaryLanes = [
      {
        laneId,
        providerId: 'opencode',
        member: {
          name: 'tom',
          providerId: 'opencode',
          model: 'openrouter/minimax/minimax-m2.5',
          cwd: '/Users/test/proj',
        },
        runId,
        state: 'finished',
        result: {
          runId,
          teamName,
          launchPhase: 'active',
          teamLaunchState: 'partial_pending',
          members: {
            tom: {
              memberName: 'tom',
              providerId: 'opencode',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              sessionId: runtimeSessionId,
              bootstrapEvidenceSource: 'app_managed_bootstrap',
              bootstrapMode: 'app_managed_context',
              appManagedBootstrapCandidate,
              diagnostics: [
                'OpenCode app-managed bootstrap context was injected and verified by the bridge.',
              ],
            },
          },
          warnings: [],
          diagnostics: [],
        },
        warnings: [],
        diagnostics: [],
      },
    ];
    (svc as any).runs.set(run.runId, run);
    (svc as any).aliveRunByTeam.set(teamName, run.runId);

    await (svc as any).reevaluateMemberLaunchStatus(run, 'tom');

    expect(sendMessageToMember).not.toHaveBeenCalled();
    expect(run.memberSpawnStatuses.get('tom')).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      runtimeDiagnostic: 'OpenCode app-managed bootstrap evidence did not commit within 5 min.',
      bootstrapStalled: true,
    });
  });

  it('keeps process table diagnostics visible when live metadata has no primary diagnostic', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: false,
              livenessKind: 'not_found',
              runtimeDiagnosticSeverity: 'warning',
              diagnostics: ['process table is unavailable'],
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'waiting',
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
      }),
    });

    expect(result.bob).toMatchObject({
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
      livenessKind: 'not_found',
      runtimeDiagnostic: 'process table unavailable',
      runtimeDiagnosticSeverity: 'warning',
    });
  });

  it('classifies process table unavailable launch diagnostics with natural wording', () => {
    const svc = new TeamProvisioningService();
    const onProgress = vi.fn();
    const run = createMemberSpawnRun({
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            livenessKind: 'shell_only',
            runtimeDiagnostic: 'tmux pane foreground command is zsh; process table is unavailable',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.progress = {
      runId: run.runId,
      teamName: run.teamName,
      status: 'running',
      updatedAt: '2026-04-22T12:00:00.000Z',
    };
    run.onProgress = onProgress;

    (svc as any).setMemberSpawnStatus(run, 'bob', 'online', undefined, 'process');

    expect(run.progress.launchDiagnostics).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          memberName: 'bob',
          code: 'process_table_unavailable',
          severity: 'warning',
          detail: 'tmux pane foreground command is zsh; process table is unavailable',
        }),
      ])
    );
    expect(onProgress).toHaveBeenCalledWith(
      expect.objectContaining({
        launchDiagnostics: expect.arrayContaining([
          expect.objectContaining({ code: 'process_table_unavailable' }),
        ]),
      })
    );
  });

  it('does not clear an explicit restart failure just because the old runtime is still alive', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: true,
              model: 'gpt-5.3-codex',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        error:
          'Restart for teammate "bob" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
        hardFailure: true,
        hardFailureReason:
          'Restart for teammate "bob" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      runtimeAlive: false,
      hardFailure: true,
      hardFailureReason:
        'Restart for teammate "bob" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
      error:
        'Restart for teammate "bob" was skipped because the previous runtime still appears to be active. The requested settings may not have been applied.',
      runtimeModel: 'gpt-5.3-codex',
    });
  });

  it('does not self-clear a failed launch from stale runtimeAlive state when no live pid exists', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      runId: 'run-self-clear-1',
      teamName: 'beacon-desk-4',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            runtimeAlive: true,
            livenessSource: 'process',
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Teammate did not join within the launch grace window.',
            hardFailureReason: 'Teammate did not join within the launch grace window.',
          }),
        ],
      ]),
    });

    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);
    (svc as any).configReader = {
      getConfig: vi.fn(async () => ({
        name: 'Beacon Desk',
        members: [
          { name: 'team-lead', agentType: 'team-lead' },
          {
            name: 'bob',
            agentType: 'general-purpose',
            providerId: 'codex',
            model: 'gpt-5.3-codex',
          },
        ],
      })),
    };
    (svc as any).membersMetaStore = {
      getMembers: vi.fn(async () => [
        {
          name: 'bob',
          role: 'Developer',
          providerId: 'codex',
          model: 'gpt-5.3-codex',
          effort: 'medium',
          agentType: 'general-purpose',
        },
      ]),
    };
    (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
    (svc as any).findLiveProcessPidByAgentId = vi.fn(() => new Map());

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('beacon-desk-4', {
      bob: createMemberSpawnStatusEntry({
        status: 'error',
        launchState: 'failed_to_start',
        runtimeAlive: true,
        livenessSource: 'process',
        bootstrapConfirmed: false,
        hardFailure: true,
        error: 'Teammate did not join within the launch grace window.',
        hardFailureReason: 'Teammate did not join within the launch grace window.',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      hardFailureReason: 'Teammate did not join within the launch grace window.',
      error: 'Teammate did not join within the launch grace window.',
      runtimeModel: 'gpt-5.3-codex',
    });
  });

  it('does not resurrect a skipped teammate when live runtime metadata is strong', async () => {
    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'bob',
            {
              alive: true,
              livenessKind: 'runtime_process',
              pid: 123,
              providerId: 'codex',
            },
          ],
        ])
    );

    const result = await (svc as any).attachLiveRuntimeMetadataToStatuses('codex-team', {
      bob: createMemberSpawnStatusEntry({
        status: 'skipped',
        launchState: 'skipped_for_launch',
        skippedForLaunch: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        agentToolAccepted: false,
        skipReason: 'Skipped by user after launch failure: spawn failed',
      }),
    });

    expect(result.bob).toMatchObject({
      status: 'skipped',
      launchState: 'skipped_for_launch',
      skippedForLaunch: true,
      runtimeAlive: false,
      bootstrapConfirmed: false,
      hardFailure: false,
      error: undefined,
      livenessSource: undefined,
    });
  });

  it('does not resurrect a skipped teammate during spawn status audit', async () => {
    const run = createMemberSpawnRun({
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'skipped',
            launchState: 'skipped_for_launch',
            skippedForLaunch: true,
            skipReason: 'Skipped by user after launch failure: spawn failed',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            firstSpawnAcceptedAt: undefined,
          }),
        ],
      ]),
    });
    const svc = new TeamProvisioningService();
    (svc as any).getRegisteredTeamMemberNames = vi.fn(async () => new Set(['bob']));
    (svc as any).getLiveTeamAgentNames = vi.fn(async () => new Set(['bob']));

    await (svc as any).auditMemberSpawnStatuses(run);

    expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
      status: 'skipped',
      launchState: 'skipped_for_launch',
      skippedForLaunch: true,
      runtimeAlive: false,
      bootstrapConfirmed: false,
      hardFailure: false,
    });
  });

  it('does not convert a skipped teammate to failed during final missing-member reconciliation', async () => {
    const run = createMemberSpawnRun({
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'skipped',
            launchState: 'skipped_for_launch',
            skippedForLaunch: true,
            skipReason: 'Skipped by user after launch failure: spawn failed',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            firstSpawnAcceptedAt: undefined,
          }),
        ],
      ]),
    });
    const svc = new TeamProvisioningService();
    (svc as any).getRegisteredTeamMemberNames = vi.fn(async () => new Set());

    await (svc as any).finalizeMissingRegisteredMembersAsFailed(run);

    expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
      status: 'skipped',
      launchState: 'skipped_for_launch',
      skippedForLaunch: true,
      runtimeAlive: false,
      bootstrapConfirmed: false,
      hardFailure: false,
    });
  });

  it('does not downgrade an already-online teammate when waiting is reported later', () => {
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            livenessSource: 'heartbeat',
            bootstrapConfirmed: true,
            lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
          }),
        ],
      ]),
    });
    const svc = new TeamProvisioningService();

    (svc as any).setMemberSpawnStatus(run, 'alice', 'waiting');

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      runtimeAlive: true,
      livenessSource: 'heartbeat',
      bootstrapConfirmed: true,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
  });

  it('clears stale hard failure state when a new spawn attempt starts', () => {
    const staleAcceptedAt = '2026-04-16T10:00:00.000Z';
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            error: 'Teammate was never spawned during launch.',
            hardFailure: true,
            hardFailureReason: 'Teammate was never spawned during launch.',
            runtimeAlive: true,
            bootstrapConfirmed: true,
            livenessSource: 'heartbeat',
            firstSpawnAcceptedAt: staleAcceptedAt,
            lastHeartbeatAt: staleAcceptedAt,
          }),
        ],
      ]),
    });
    const svc = new TeamProvisioningService();

    (svc as any).setMemberSpawnStatus(run, 'alice', 'spawning');

    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      status: 'spawning',
      launchState: 'starting',
      error: undefined,
      hardFailure: false,
      hardFailureReason: undefined,
      agentToolAccepted: false,
      runtimeAlive: false,
      bootstrapConfirmed: false,
      livenessSource: undefined,
      firstSpawnAcceptedAt: undefined,
      lastHeartbeatAt: undefined,
    });
  });

  it('clears an old member launch grace timer when a new spawn attempt resets acceptance state', () => {
    vi.useFakeTimers();

    const acceptedAt = new Date(Date.now() - 5_000).toISOString();
    const run = createMemberSpawnRun({
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            agentToolAccepted: true,
            firstSpawnAcceptedAt: acceptedAt,
          }),
        ],
      ]),
    });
    const svc = new TeamProvisioningService();
    const timerKey = (svc as any).getMemberLaunchGraceKey(run, 'alice');

    (svc as any).syncMemberLaunchGraceCheck(run, 'alice', run.memberSpawnStatuses.get('alice'));
    expect((svc as any).pendingTimeouts.has(timerKey)).toBe(true);

    (svc as any).setMemberSpawnStatus(run, 'alice', 'offline');
    expect((svc as any).pendingTimeouts.has(timerKey)).toBe(false);

    (svc as any).setMemberSpawnStatus(run, 'alice', 'spawning');
    expect((svc as any).pendingTimeouts.has(timerKey)).toBe(false);
    expect(run.memberSpawnStatuses.get('alice')).toMatchObject({
      firstSpawnAcceptedAt: undefined,
      lastHeartbeatAt: undefined,
      error: undefined,
      hardFailureReason: undefined,
      livenessSource: undefined,
    });
  });

  it('reconciles stale never-spawned failures when bootstrap state proves the teammate was registered', async () => {
    const teamName = 'registered-bootstrap-team';
    const leadSessionId = 'lead-session';
    const acceptedAt = new Date(Date.now() - 60_000).toISOString();
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['alice']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'failed_to_start',
        agentToolAccepted: false,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: true,
        hardFailureReason: 'Teammate was never spawned during launch.',
      },
    });
    writeBootstrapState(
      teamName,
      [
        {
          name: 'alice',
          status: 'registered',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(acceptedAt),
        },
      ],
      new Date(Date.now() - 30_000).toISOString()
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.alice).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      hardFailure: false,
      hardFailureReason: undefined,
      agentToolAccepted: true,
    });
  });

  it('clears stale launch-state and launch-summary when bootstrap truth supersedes a persisted failure', async () => {
    const teamName = 'bootstrap-supersedes-stale-launch-summary-team';
    const leadSessionId = 'lead-session';
    const staleUpdatedAt = '2026-04-16T09:55:00.000Z';
    const freshObservedAt = '2026-04-16T10:00:00.000Z';
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['bob']);
    writeLaunchState(
      teamName,
      leadSessionId,
      {
        bob: {
          launchState: 'failed_to_start',
          agentToolAccepted: false,
          runtimeAlive: false,
          bootstrapConfirmed: false,
          hardFailure: true,
          hardFailureReason: 'Teammate was never spawned during launch.',
        },
      },
      { updatedAt: staleUpdatedAt }
    );
    fs.writeFileSync(
      getTeamLaunchSummaryPath(teamName),
      `${JSON.stringify(
        {
          version: 1,
          teamName,
          updatedAt: staleUpdatedAt,
          launchPhase: 'finished',
          partialLaunchFailure: true,
          expectedMemberCount: 1,
          confirmedMemberCount: 0,
          missingMembers: ['bob'],
          teamLaunchState: 'partial_failure',
          launchUpdatedAt: staleUpdatedAt,
          confirmedCount: 0,
          pendingCount: 0,
          failedCount: 1,
          skippedCount: 0,
          runtimeAlivePendingCount: 0,
          shellOnlyPendingCount: 0,
          runtimeProcessPendingCount: 0,
          runtimeCandidatePendingCount: 0,
          noRuntimePendingCount: 0,
          permissionPendingCount: 0,
        },
        null,
        2
      )}\n`,
      'utf8'
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'bob',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(freshObservedAt),
          lastObservedAt: Date.parse(freshObservedAt),
        },
      ],
      freshObservedAt
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    await expect(
      fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    ).rejects.toMatchObject({
      code: 'ENOENT',
    });
    await expect(
      fsPromises.readFile(getTeamLaunchSummaryPath(teamName), 'utf8')
    ).rejects.toMatchObject({
      code: 'ENOENT',
    });
  });

  it('reconciles extra persisted launch members when bootstrap state proves they were registered', async () => {
    const teamName = 'registered-bootstrap-extra-member-team';
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'launch-state.json'),
      JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'active',
          expectedMembers: ['alice', 'bob'],
          members: {
            alice: {
              name: 'alice',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: new Date().toISOString(),
            },
            bob: {
              name: 'bob',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              lastEvaluatedAt: new Date().toISOString(),
            },
          },
          updatedAt: new Date().toISOString(),
        })
      ),
      'utf8'
    );
    writeBootstrapState(
      teamName,
      [
        {
          name: 'bob',
          status: 'registered',
          lastAttemptAt: Date.now() - 60_000,
          lastObservedAt: Date.now() - 60_000,
        },
      ],
      new Date(Date.now() - 30_000).toISOString()
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.expectedMembers).toEqual(['alice', 'bob']);
    expect(result.statuses.bob).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      hardFailure: false,
      hardFailureReason: undefined,
      agentToolAccepted: true,
    });
  });

  it('keeps suffixed weak runtime metadata pending during persisted launch reconcile', async () => {
    const teamName = 'suffixed-live-runtime-team';
    const leadSessionId = 'lead-session';
    writeLaunchConfig(teamName, '/Users/test/proj', leadSessionId, ['alice']);
    writeLaunchState(teamName, leadSessionId, {
      alice: {
        launchState: 'runtime_pending_bootstrap',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: false,
        hardFailureReason: undefined,
        firstSpawnAcceptedAt: new Date(Date.now() - 5_000).toISOString(),
      },
    });

    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'alice-2',
            {
              alive: false,
              livenessKind: 'registered_only',
              runtimeDiagnostic: 'registered runtime metadata without live process',
            },
          ],
        ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.alice).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      runtimeAlive: false,
    });
  });

  it('confirms a teammate from bootstrap transcript stored under its worktree cwd', async () => {
    const teamName = 'worktree-bootstrap-transcript-team';
    const leadSessionId = 'lead-session';
    const projectPath = '/Users/test/proj';
    const worktreePath = `${projectPath}/.claude/worktrees/team-${teamName}-tom-12345678`;
    const acceptedAt = new Date(Date.now() - 90_000).toISOString();
    const observedAt = new Date(Date.now() - 30_000).toISOString();
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'config.json'),
      JSON.stringify({
        name: teamName,
        projectPath,
        leadSessionId,
        members: [
          { name: 'team-lead', agentType: 'team-lead', cwd: projectPath },
          { name: 'tom', providerId: 'codex', model: 'gpt-5.4', cwd: worktreePath },
          { name: 'bob', providerId: 'codex', model: 'gpt-5.4-mini', cwd: projectPath },
        ],
      }),
      'utf8'
    );
    writeLaunchState(teamName, leadSessionId, {
      tom: {
        providerId: 'codex',
        laneId: 'primary',
        laneKind: 'primary',
        laneOwnerProviderId: 'codex',
        launchState: 'failed_to_start',
        agentToolAccepted: true,
        runtimeAlive: false,
        bootstrapConfirmed: false,
        hardFailure: true,
        hardFailureReason: 'registered runtime metadata without live process',
        firstSpawnAcceptedAt: acceptedAt,
        lastEvaluatedAt: acceptedAt,
      },
    });
    const worktreeProjectDir = path.join(tempProjectsBase, encodePath(worktreePath));
    fs.mkdirSync(worktreeProjectDir, { recursive: true });
    fs.writeFileSync(
      path.join(worktreeProjectDir, 'tom-session.jsonl'),
      `${JSON.stringify({
        type: 'user',
        teamName,
        agentName: 'tom',
        timestamp: observedAt,
        cwd: worktreePath,
        message: {
          role: 'user',
          content: [
            {
              type: 'tool_result',
              tool_use_id: 'item_0',
              content: `Member briefing for tom on team "${teamName}" (${teamName}).\nRole: developer.`,
            },
          ],
        },
      })}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      agentToolAccepted: true,
      runtimeAlive: false,
      bootstrapConfirmed: true,
      hardFailure: false,
      hardFailureReason: undefined,
      lastHeartbeatAt: observedAt,
    });
  });

  it('treats suffixed persisted heartbeat senders as the expected member during reconcile', async () => {
    const teamName = 'suffixed-heartbeat-reconcile-team';
    const svc = new TeamProvisioningService();
    (svc as any).launchStateStore = {
      read: vi.fn(async () =>
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'active',
          expectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              launchState: 'runtime_pending_bootstrap',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              hardFailureReason: undefined,
              firstSpawnAcceptedAt: '2026-04-16T09:55:00.000Z',
              lastEvaluatedAt: '2026-04-16T09:55:00.000Z',
            },
            bob: {
              name: 'bob',
              launchState: 'starting',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-16T09:55:00.000Z',
            },
          },
          updatedAt: '2026-04-16T09:55:00.000Z',
        })
      ),
      write: vi.fn(async () => {}),
      clear: vi.fn(async () => {}),
    };
    fs.mkdirSync(path.join(tempTeamsBase, teamName, 'inboxes'), { recursive: true });
    fs.writeFileSync(
      path.join(tempTeamsBase, teamName, 'inboxes', 'team-lead.json'),
      JSON.stringify(
        [
          {
            from: 'alice-2',
            text: 'heartbeat',
            timestamp: '2026-04-16T10:00:00.000Z',
            messageId: 'msg-suffixed-reconcile',
            read: false,
          },
        ],
        null,
        2
      )
    );
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());

    const result = await (svc as any).reconcilePersistedLaunchState(teamName);

    expect(result.snapshot.members.alice).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      lastHeartbeatAt: '2026-04-16T10:00:00.000Z',
    });
    expect(result.statuses.alice).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
  });

  it('returns persisted expectedMembers as the union of expected and materialized launch members', async () => {
    const teamName = 'persisted-union-member-spawn-team';
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'launch-state.json'),
      JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'active',
          expectedMembers: ['alice'],
          members: {
            alice: {
              name: 'alice',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
            bob: {
              name: 'bob',
              launchState: 'runtime_pending_permission',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: false,
              hardFailure: false,
              pendingPermissionRequestIds: ['perm-bob'],
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
          },
          updatedAt: '2026-04-23T10:00:00.000Z',
        })
      ),
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.expectedMembers).toEqual(['alice', 'bob']);
    expect(result.statuses.bob).toMatchObject({
      launchState: 'runtime_pending_permission',
    });
  });

  it('recovers stale mixed secondary lanes when lanes.json says active but lane state is missing', async () => {
    const teamName = 'signal-ops-6212';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'atlas',
        providerId: 'opencode',
        model: 'opencode/nemotron-3-super-free',
      },
      {
        name: 'bob',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
      {
        name: 'nova',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
      {
        name: 'tom',
        providerId: 'opencode',
        model: 'opencode/minimax-m2.5-free',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob', 'nova']);
    writeBootstrapState(teamName, [
      { name: 'bob', status: 'registered' },
      { name: 'nova', status: 'registered' },
    ]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:atlas',
      state: 'active',
    });
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:tom',
      state: 'active',
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.launchPhase).toBe('reconciled');
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['atlas', 'bob', 'nova', 'tom']));
    expect(result.statuses.atlas).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      error: expect.stringContaining('no lane state exists on disk'),
    });
    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      error: expect.stringContaining('no lane state exists on disk'),
    });
    await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
      lanes: {
        'secondary:opencode:atlas': {
          state: 'degraded',
        },
        'secondary:opencode:tom': {
          state: 'degraded',
        },
      },
    });
    await expect(fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')).resolves.toContain(
      '"secondary:opencode:atlas"'
    );
  });

  it('degrades mixed secondary lanes when lanes.json is active but the lane manifest has no runtime evidence', async () => {
    const teamName = 'atlas-hq-empty-lane';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'bob',
        providerId: 'opencode',
        model: 'openrouter/moonshotai/kimi-k2.6',
      },
      {
        name: 'jack',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['jack']);
    writeBootstrapState(teamName, [{ name: 'jack', status: 'registered' }]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:bob',
      state: 'active',
    });
    await setOpenCodeRuntimeActiveRunManifest({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:bob',
      runId: 'run-empty-bob',
      clock: () => new Date('2026-04-20T10:00:00.000Z'),
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      error: expect.stringContaining('no committed runtime evidence after launch grace'),
    });
    await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
      lanes: {
        'secondary:opencode:bob': {
          state: 'degraded',
        },
      },
    });
  });

  it('recovers stale mixed secondary lanes from live OpenCode runtime reconcile before degrading them', async () => {
    const teamName = 'relay-works-7';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'atlas',
        providerId: 'opencode',
        model: 'opencode/nemotron-3-super-free',
      },
      {
        name: 'bob',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
      {
        name: 'nova',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
      {
        name: 'tom',
        providerId: 'opencode',
        model: 'opencode/minimax-m2.5-free',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob', 'nova']);
    writeBootstrapState(teamName, [
      { name: 'bob', status: 'registered' },
      { name: 'nova', status: 'registered' },
    ]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:atlas',
      state: 'active',
    });
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:tom',
      state: 'active',
    });

    const adapterReconcile = vi.fn(async (input: Record<string, unknown>) => {
      const member = (input.expectedMembers as Array<{ name: string }>)[0]?.name;
      return {
        runId: String(input.runId),
        teamName,
        launchPhase: 'reconciled',
        teamLaunchState: 'clean_success',
        members: member
          ? {
              [member]: {
                memberName: member,
                providerId: 'opencode',
                launchState: 'confirmed_alive',
                agentToolAccepted: true,
                runtimeAlive: true,
                bootstrapConfirmed: true,
                hardFailure: false,
                diagnostics: ['bootstrap confirmed'],
              },
            }
          : {},
        snapshot: null,
        warnings: [],
        diagnostics: [],
      };
    });
    const svc = new TeamProvisioningService();
    svc.setRuntimeAdapterRegistry(
      new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: adapterReconcile,
          stop: vi.fn(),
        } as any,
      ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(adapterReconcile).toHaveBeenCalledTimes(2);
    expect(adapterReconcile).toHaveBeenCalledWith(
      expect.objectContaining({
        teamName,
        laneId: 'secondary:opencode:atlas',
        reason: 'startup_recovery',
        expectedMembers: [
          expect.objectContaining({
            name: 'atlas',
            providerId: 'opencode',
            cwd: '/Users/test/proj',
          }),
        ],
      })
    );
    expect(adapterReconcile).toHaveBeenCalledWith(
      expect.objectContaining({
        teamName,
        laneId: 'secondary:opencode:tom',
        reason: 'startup_recovery',
        expectedMembers: [
          expect.objectContaining({
            name: 'tom',
            providerId: 'opencode',
            cwd: '/Users/test/proj',
          }),
        ],
      })
    );
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['atlas', 'bob', 'nova', 'tom']));
    expect(result.statuses.atlas).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
    await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
      lanes: {
        'secondary:opencode:atlas': {
          state: 'active',
        },
        'secondary:opencode:tom': {
          state: 'active',
        },
      },
    });
  });

  it('does not keep an empty active OpenCode lane pending when runtime reconcile has no runtime handle', async () => {
    const teamName = 'atlas-hq-empty-lane-nonrecoverable';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'bob',
        providerId: 'opencode',
        model: 'openrouter/moonshotai/kimi-k2.6',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', []);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:bob',
      state: 'active',
    });
    await setOpenCodeRuntimeActiveRunManifest({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:bob',
      runId: 'run-empty-bob',
      clock: () => new Date('2026-04-20T10:00:00.000Z'),
    });

    const adapterReconcile = vi.fn(async () => ({
      runId: 'reconcile-run',
      teamName,
      launchPhase: 'reconciled' as const,
      teamLaunchState: 'partial_pending' as const,
      members: {
        bob: {
          memberName: 'bob',
          providerId: 'opencode' as const,
          launchState: 'runtime_pending_bootstrap' as const,
          agentToolAccepted: false,
          runtimeAlive: false,
          bootstrapConfirmed: false,
          hardFailure: false,
          livenessKind: 'registered_only' as const,
          diagnostics: ['bridge has no runtime session'],
        },
      },
      snapshot: null,
      warnings: [],
      diagnostics: [],
    }));
    const svc = new TeamProvisioningService();
    svc.setRuntimeAdapterRegistry(
      new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: adapterReconcile,
          stop: vi.fn(),
        } as any,
      ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(adapterReconcile).toHaveBeenCalledTimes(1);
    expect(result.statuses.bob).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      error: expect.stringContaining('no committed runtime evidence after launch grace'),
    });
  });

  it('recovers missing mixed secondary lane index from materialized OpenCode runtime evidence', async () => {
    const teamName = 'relay-works-missing-lane-recovery';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'atlas',
        providerId: 'opencode',
        model: 'opencode/nemotron-3-super-free',
      },
      {
        name: 'bob',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob']);
    writeBootstrapState(teamName, [{ name: 'bob', status: 'registered' }]);
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        {
          version: 2,
          teamName,
          updatedAt: '2026-04-23T10:00:00.000Z',
          expectedMembers: ['atlas', 'bob'],
          bootstrapExpectedMembers: ['bob'],
          leadSessionId: 'lead-session',
          launchPhase: 'reconciled',
          members: {
            atlas: {
              name: 'atlas',
              providerId: 'opencode',
              model: 'opencode/nemotron-3-super-free',
              laneId: 'secondary:opencode:atlas',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'OpenCode bridge reported member launch failure',
              runtimePid: 44123,
              runtimeSessionId: 'ses_atlas_materialized',
              livenessKind: 'runtime_process_candidate',
              pidSource: 'opencode_bridge',
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
            bob: {
              name: 'bob',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
          },
          summary: {
            confirmedCount: 1,
            pendingCount: 0,
            failedCount: 1,
            runtimeAlivePendingCount: 0,
          },
          teamLaunchState: 'partial_failure',
        },
        null,
        2
      )}\n`,
      'utf8'
    );

    const adapterReconcile = vi.fn(async (input: Record<string, unknown>) => {
      const member = (input.expectedMembers as Array<{ name: string }>)[0]?.name;
      return {
        runId: String(input.runId),
        teamName,
        launchPhase: 'reconciled',
        teamLaunchState: 'partial_pending',
        members: member
          ? {
              [member]: {
                memberName: member,
                providerId: 'opencode',
                launchState: 'runtime_pending_bootstrap',
                agentToolAccepted: true,
                runtimeAlive: false,
                bootstrapConfirmed: false,
                hardFailure: false,
                runtimePid: 44123,
                sessionId: 'ses_atlas_materialized',
                livenessKind: 'runtime_process_candidate',
                diagnostics: ['runtime process candidate recovered'],
              },
            }
          : {},
        snapshot: null,
        warnings: [],
        diagnostics: ['fake reconcile recovered materialized runtime'],
      };
    });
    const svc = new TeamProvisioningService();
    svc.setRuntimeAdapterRegistry(
      new TeamRuntimeAdapterRegistry([
        {
          providerId: 'opencode',
          prepare: vi.fn(),
          launch: vi.fn(),
          reconcile: adapterReconcile,
          stop: vi.fn(),
        } as any,
      ])
    );

    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(adapterReconcile).toHaveBeenCalledTimes(1);
    expect(adapterReconcile).toHaveBeenCalledWith(
      expect.objectContaining({
        teamName,
        laneId: 'secondary:opencode:atlas',
        reason: 'startup_recovery',
      })
    );
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['atlas', 'bob']));
    expect(result.statuses.atlas).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
    });
    await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
      lanes: {
        'secondary:opencode:atlas': {
          state: 'active',
        },
      },
    });
  });

  it('recovers degraded OpenCode file-lock failures when bootstrap evidence committed later', async () => {
    const teamName = 'atlas-hq-file-lock-late-evidence';
    const tomLaneId = 'secondary:opencode:tom';
    const tomRunId = 'tom-runtime-run';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [
      { name: 'bob', providerId: 'codex', model: 'gpt-5.5' },
      { name: 'jack', providerId: 'codex', model: 'gpt-5.4' },
      { name: 'tom', providerId: 'opencode', model: 'openrouter/minimax/minimax-m2.5' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob', 'jack']);
    writeBootstrapState(teamName, [
      { name: 'bob', status: 'bootstrap_confirmed', lastObservedAt: Date.now() - 60_000 },
      { name: 'jack', status: 'bootstrap_confirmed', lastObservedAt: Date.now() - 60_000 },
    ]);
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['bob', 'jack', 'tom'],
          bootstrapExpectedMembers: ['bob', 'jack'],
          members: {
            bob: {
              name: 'bob',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
            jack: {
              name: 'jack',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
            tom: {
              name: 'tom',
              providerId: 'opencode',
              model: 'openrouter/minimax/minimax-m2.5',
              laneId: tomLaneId,
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: `File lock timeout: ${path.join(
                tempTeamsBase,
                teamName,
                '.opencode-runtime',
                'lanes',
                encodeURIComponent(tomLaneId),
                'opencode-runtime-receipts.json'
              )}`,
              diagnostics: ['File lock timeout: opencode-runtime-receipts.json'],
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
          },
          updatedAt: '2026-04-23T10:00:00.000Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: tomLaneId,
      state: 'degraded',
      diagnostics: ['File lock timeout: opencode-runtime-receipts.json'],
    });
    await writeCommittedOpenCodeSessionStore({
      teamName,
      laneId: tomLaneId,
      runId: tomRunId,
      sessions: [
        {
          id: 'ses_tom_late',
          teamName,
          memberName: 'tom',
          runId: tomRunId,
          laneId: tomLaneId,
          providerId: 'opencode',
          observedAt: '2026-04-23T10:01:00.000Z',
          source: 'runtime_bootstrap_checkin',
        },
      ],
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      hardFailure: false,
      bootstrapConfirmed: true,
    });
    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      hardFailure: false,
    });
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      hardFailure: false,
    });
  });

  it('does not recover degraded OpenCode file-lock failures from stale run evidence', async () => {
    const teamName = 'atlas-hq-file-lock-stale-run-evidence';
    const tomLaneId = 'secondary:opencode:tom';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [
      { name: 'tom', providerId: 'opencode', model: 'openrouter/minimax/minimax-m2.5' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', []);
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['tom'],
          bootstrapExpectedMembers: [],
          members: {
            tom: {
              name: 'tom',
              providerId: 'opencode',
              model: 'openrouter/minimax/minimax-m2.5',
              laneId: tomLaneId,
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'File lock timeout: opencode-runtime-receipts.json',
              diagnostics: ['File lock timeout: opencode-runtime-receipts.json'],
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
          },
          updatedAt: '2026-04-23T10:00:00.000Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: tomLaneId,
      state: 'degraded',
      diagnostics: ['File lock timeout: opencode-runtime-receipts.json'],
    });
    await writeCommittedOpenCodeSessionStore({
      teamName,
      laneId: tomLaneId,
      runId: 'current-runtime-run',
      sessions: [
        {
          id: 'ses_tom_old',
          teamName,
          memberName: 'tom',
          runId: 'old-runtime-run',
          laneId: tomLaneId,
          providerId: 'opencode',
          observedAt: '2026-04-23T09:00:00.000Z',
          source: 'runtime_bootstrap_checkin',
        },
      ],
    });

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      bootstrapConfirmed: false,
    });
  });

  it('reconciles stale persisted mixed pending OpenCode lanes instead of keeping them pending forever', async () => {
    const teamName = 'signal-ops-7';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      {
        name: 'alice',
        providerId: 'codex',
        model: 'gpt-5.4-mini',
      },
      {
        name: 'jack',
        providerId: 'opencode',
        model: 'opencode/ling-2.6-flash-free',
      },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['alice']);
    writeBootstrapState(teamName, [{ name: 'alice', status: 'registered' }]);
    await upsertOpenCodeRuntimeLaneIndexEntry({
      teamsBasePath: tempTeamsBase,
      teamName,
      laneId: 'secondary:opencode:jack',
      state: 'active',
    });

    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        {
          version: 2,
          teamName,
          updatedAt: '2026-04-23T10:00:00.000Z',
          expectedMembers: ['alice', 'jack'],
          bootstrapExpectedMembers: ['alice'],
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
            },
            jack: {
              name: 'jack',
              providerId: 'opencode',
              model: 'opencode/ling-2.6-flash-free',
              laneId: 'secondary:opencode:jack',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'starting',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: false,
              lastEvaluatedAt: '2026-04-23T10:00:00.000Z',
              diagnostics: ['Launching through OpenCode secondary lane.'],
            },
          },
          summary: {
            confirmedCount: 1,
            pendingCount: 1,
            failedCount: 0,
            runtimeAlivePendingCount: 0,
          },
          teamLaunchState: 'partial_pending',
        },
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.jack).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      error: expect.stringContaining('no lane state exists on disk'),
    });
    await expect(readOpenCodeRuntimeLaneIndex(tempTeamsBase, teamName)).resolves.toMatchObject({
      lanes: {
        'secondary:opencode:jack': {
          state: 'degraded',
        },
      },
    });
  });

  it('includes queued OpenCode secondary lanes in live spawn statuses before the final mixed snapshot settles', async () => {
    const svc = new TeamProvisioningService();
    vi.spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);

    const run = createMemberSpawnRun({
      teamName: 'mixed-live-team',
      runId: 'run-mixed-live-1',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
            livenessSource: 'heartbeat',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.request = {
      teamName: 'mixed-live-team',
      cwd: '/tmp/mixed-live-team',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.effectiveMembers = [
      {
        name: 'bob',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
    ];
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:atlas',
        providerId: 'opencode',
        member: {
          name: 'atlas',
          providerId: 'opencode',
          model: 'opencode/nemotron-3-super-free',
        },
        runId: null,
        state: 'queued',
        result: null,
        warnings: [],
        diagnostics: [],
      },
    ];
    run.detectedSessionId = 'lead-session';

    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    const result = await svc.getMemberSpawnStatuses(run.teamName);

    expect(result.teamLaunchState).toBe('partial_pending');
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['bob', 'atlas']));
    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
    expect(result.statuses.atlas).toMatchObject({
      status: 'spawning',
      launchState: 'starting',
    });
  });

  it('keeps finished OpenCode secondary lanes pending when runtime evidence has not materialized yet', async () => {
    const svc = new TeamProvisioningService();
    vi.spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);

    const run = createMemberSpawnRun({
      teamName: 'mixed-live-finished-no-evidence',
      runId: 'run-mixed-live-2',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
            livenessSource: 'heartbeat',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.request = {
      teamName: 'mixed-live-finished-no-evidence',
      cwd: '/tmp/mixed-live-finished-no-evidence',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.effectiveMembers = [
      {
        name: 'bob',
        providerId: 'codex',
        model: 'gpt-5.4',
      },
    ];
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:atlas',
        providerId: 'opencode',
        member: {
          name: 'atlas',
          providerId: 'opencode',
          model: 'opencode/nemotron-3-super-free',
        },
        runId: 'lane-run-atlas',
        state: 'finished',
        result: null,
        warnings: [],
        diagnostics: [],
      },
    ];
    run.detectedSessionId = 'lead-session';

    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    const result = await svc.getMemberSpawnStatuses(run.teamName);

    expect(result.teamLaunchState).toBe('partial_pending');
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['bob', 'atlas']));
    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
    expect(result.statuses.atlas).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      hardFailure: false,
      hardFailureReason: undefined,
    });
  });

  it('keeps primary bootstrap-confirmed members alive when OpenCode secondary lanes fail', async () => {
    const teamName = 'atlas-hq-source-aware-live';
    const startedAt = '2026-04-23T10:00:00.000Z';
    const exactOpenCodeReason =
      'Latest assistant message msg_alice failed with APIError - Insufficient credits.';
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob', 'jack']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'bob',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse('2026-04-23T10:01:00.000Z'),
        },
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse('2026-04-23T10:01:00.000Z'),
        },
      ],
      '2026-04-23T10:01:00.000Z'
    );
    const run = createMemberSpawnRun({
      teamName,
      runId: 'run-atlas-hq-source-aware-live',
      startedAt,
      expectedMembers: ['bob', 'jack'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Teammate was never spawned during launch.',
            hardFailureReason: 'Teammate was never spawned during launch.',
            firstSpawnAcceptedAt: '2026-04-23T10:00:30.000Z',
          }),
        ],
        [
          'jack',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Teammate was never spawned during launch.',
            hardFailureReason: 'Teammate was never spawned during launch.',
            firstSpawnAcceptedAt: '2026-04-23T10:00:30.000Z',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.request = {
      teamName,
      cwd: '/Users/test/proj',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.effectiveMembers = [
      { name: 'bob', providerId: 'codex', model: 'gpt-5.3-codex' },
      { name: 'jack', providerId: 'codex', model: 'gpt-5.4' },
    ];
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:alice',
        providerId: 'opencode',
        member: {
          name: 'alice',
          providerId: 'opencode',
          model: 'openrouter/z-ai/glm-5.1',
        },
        runId: 'lane-run-alice',
        state: 'finished',
        result: {
          runId: 'lane-run-alice',
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'partial_failure',
          members: {
            alice: {
              memberName: 'alice',
              providerId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: exactOpenCodeReason,
              diagnostics: [exactOpenCodeReason],
            },
          },
          warnings: [],
          diagnostics: [exactOpenCodeReason],
        },
        warnings: [],
        diagnostics: [exactOpenCodeReason],
      },
      {
        laneId: 'secondary:opencode:tom',
        providerId: 'opencode',
        member: {
          name: 'tom',
          providerId: 'opencode',
          model: 'openrouter/minimax/minimax-m2.5',
        },
        runId: 'lane-run-tom',
        state: 'finished',
        result: {
          runId: 'lane-run-tom',
          teamName,
          launchPhase: 'finished',
          teamLaunchState: 'partial_failure',
          members: {
            tom: {
              memberName: 'tom',
              providerId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Tom provider launch failed.',
              diagnostics: ['Tom provider launch failed.'],
            },
          },
          warnings: [],
          diagnostics: ['Tom provider launch failed.'],
        },
        warnings: [],
        diagnostics: ['Tom provider launch failed.'],
      },
    ];
    run.detectedSessionId = 'lead-session';

    const svc = new TeamProvisioningService();
    const snapshot = await (svc as any).persistLaunchStateSnapshot(run, 'finished');

    expect(snapshot.members.bob).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(snapshot.members.jack).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(snapshot.members.alice).toMatchObject({
      launchState: 'failed_to_start',
      hardFailureReason: exactOpenCodeReason,
    });
    expect(snapshot.members.tom).toMatchObject({
      launchState: 'failed_to_start',
      hardFailureReason: 'Tom provider launch failed.',
    });
    expect(snapshot.summary.confirmedCount).toBe(2);
    expect(snapshot.summary.failedCount).toBe(2);
  });

  it('reconciles persisted mixed launch-state when primary bootstrap members were marked missing', async () => {
    const teamName = 'atlas-hq-source-aware-persisted';
    const exactOpenCodeReason =
      'Latest assistant message msg_alice failed with APIError - Insufficient credits.';
    const transientBobMcpFailure =
      'resources/read failed: resources/read failed for `agent-teams` (member_briefing?teamName=atlas-hq-source-aware-persisted&memberName=bob): Mcp error: -32601: Method not found';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
    });
    writeMembersMeta(teamName, [
      { name: 'bob', providerId: 'codex', model: 'gpt-5.3-codex' },
      { name: 'jack', providerId: 'codex', model: 'gpt-5.4' },
      { name: 'alice', providerId: 'opencode', model: 'openrouter/z-ai/glm-5.1' },
      { name: 'tom', providerId: 'opencode', model: 'openrouter/minimax/minimax-m2.5' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob', 'jack']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'bob',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse('2026-04-23T10:01:00.000Z'),
        },
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse('2026-04-23T10:01:00.000Z'),
        },
      ],
      '2026-04-23T10:01:00.000Z'
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['bob', 'jack', 'alice', 'tom'],
          bootstrapExpectedMembers: ['bob', 'jack'],
          members: {
            bob: {
              name: 'bob',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: transientBobMcpFailure,
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
            jack: {
              name: 'jack',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
            alice: {
              name: 'alice',
              providerId: 'opencode',
              model: 'openrouter/z-ai/glm-5.1',
              laneId: 'secondary:opencode:alice',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'OpenCode bridge reported member launch failure',
              diagnostics: [exactOpenCodeReason],
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
            tom: {
              name: 'tom',
              providerId: 'opencode',
              model: 'openrouter/minimax/minimax-m2.5',
              laneId: 'secondary:opencode:tom',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'OpenCode bridge reported member launch failure',
              diagnostics: ['Tom provider launch failed.'],
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
          },
          updatedAt: '2026-04-23T10:02:00.000Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(result.statuses.alice).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailureReason: exactOpenCodeReason,
    });
    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailureReason: 'Tom provider launch failed.',
    });
    const summary = JSON.parse(
      await fsPromises.readFile(getTeamLaunchSummaryPath(teamName), 'utf8')
    );
    expect(summary).toMatchObject({
      teamLaunchState: 'partial_failure',
      confirmedCount: 2,
      failedCount: 2,
      missingMembers: ['alice', 'tom'],
    });
  });

  it('reconciles mixed launch when Anthropic primary bootstrap confirmation slightly predates delayed app acceptance', async () => {
    const teamName = 'mixed-anthropic-primary-bootstrap-skew-heals';
    const reason = 'runtime pid could not be verified because process table is unavailable';
    const postStopDiagnostic = 'persisted runtime pid is not alive';
    const bootstrapRunId = 'run-mixed-anthropic-primary-bootstrap-skew';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [
      { name: 'alice', providerId: 'codex', model: 'gpt-5.5' },
      { name: 'tom', providerId: 'anthropic', model: 'haiku' },
      { name: 'bob', providerId: 'opencode', model: 'opencode/deepseek-v4-flash-free' },
      { name: 'jack', providerId: 'opencode', model: 'opencode/big-pickle' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['alice', 'tom']);
    writeMemberBootstrapRunId(teamName, 'alice', bootstrapRunId);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'alice',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-24T09:25:28.034Z'),
          lastObservedAt: Date.parse('2026-05-24T09:26:07.735Z'),
        },
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-24T09:25:33.388Z'),
          lastObservedAt: Date.parse('2026-05-24T09:25:42.494Z'),
        },
      ],
      '2026-05-24T09:26:08.090Z',
      { runId: bootstrapRunId }
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['alice', 'tom', 'bob', 'jack'],
          bootstrapExpectedMembers: ['alice', 'tom'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              firstSpawnAcceptedAt: '2026-05-24T09:25:45.176Z',
              lastHeartbeatAt: '2026-05-24T09:26:07.735Z',
              lastEvaluatedAt: '2026-05-24T09:26:09.249Z',
            },
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'haiku',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 97_255,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: reason,
              livenessKind: 'stale_metadata',
              runtimeDiagnostic: postStopDiagnostic,
              runtimeDiagnosticSeverity: 'warning',
              firstSpawnAcceptedAt: '2026-05-24T09:25:45.178Z',
              runtimeLastSeenAt: '2026-05-24T09:31:05.525Z',
              lastEvaluatedAt: '2026-05-24T09:31:05.525Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              model: 'opencode/deepseek-v4-flash-free',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              runtimePid: 2_756,
              runtimeSessionId: 'ses_bob',
              livenessKind: 'confirmed_bootstrap',
              lastHeartbeatAt: '2026-05-24T09:31:39.741Z',
              lastEvaluatedAt: '2026-05-24T09:31:39.741Z',
            },
            jack: {
              name: 'jack',
              providerId: 'opencode',
              model: 'opencode/big-pickle',
              laneId: 'secondary:opencode:jack',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              runtimePid: 2_756,
              runtimeSessionId: 'ses_jack',
              livenessKind: 'confirmed_bootstrap',
              lastHeartbeatAt: '2026-05-24T09:31:39.741Z',
              lastEvaluatedAt: '2026-05-24T09:31:39.741Z',
            },
          },
          updatedAt: '2026-05-24T11:36:56.881Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
    });
    expect(result.statuses.jack).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
    });
  });

  it('reconciles confirmed primary bootstrap after CLI provisioned-but-not-alive exit', async () => {
    const teamName = 'primary-bootstrap-cli-provisioned-not-alive-heals';
    const bootstrapRunId = 'run-primary-cli-exit-after-bootstrap';
    const reason = 'CLI process exited (code 1) \u2014 team provisioned but not alive';
    writeTeamMeta(teamName, {
      providerId: 'anthropic',
      model: 'sonnet',
    });
    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'anthropic', model: 'sonnet' }]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-25T20:13:46.326Z'),
          lastObservedAt: Date.parse('2026-05-25T20:13:56.110Z'),
        },
      ],
      '2026-05-25T20:14:03.317Z',
      { runId: bootstrapRunId }
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['tom'],
          members: {
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'sonnet',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'anthropic',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 27_036,
              bootstrapConfirmed: true,
              hardFailure: true,
              hardFailureReason: reason,
              livenessKind: 'confirmed_bootstrap',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic:
                'runtime pid could not be verified because process table is unavailable',
              runtimeDiagnosticSeverity: 'warning',
              firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
              lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
              runtimeLastSeenAt: '2026-05-25T20:13:46.326Z',
              lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
            },
          },
          updatedAt: '2026-05-25T20:14:05.411Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: true,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.hardFailureReason).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('keeps primary provisioned-but-not-alive reporting failed when runtime evidence is unsafe', async () => {
    const teamName = 'primary-bootstrap-cli-provisioned-not-alive-runtime-error';
    const bootstrapRunId = 'run-primary-cli-exit-runtime-error';
    const reason = 'CLI process exited (code 1) - team provisioned but not alive';
    writeTeamMeta(teamName, {
      providerId: 'anthropic',
      model: 'sonnet',
    });
    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'anthropic', model: 'sonnet' }]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-25T20:13:46.326Z'),
          lastObservedAt: Date.parse('2026-05-25T20:13:56.110Z'),
        },
      ],
      '2026-05-25T20:14:03.317Z',
      { runId: bootstrapRunId }
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['tom'],
          members: {
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'sonnet',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'anthropic',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 27_036,
              bootstrapConfirmed: true,
              hardFailure: true,
              hardFailureReason: reason,
              livenessKind: 'confirmed_bootstrap',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: 'Runtime process crashed',
              runtimeDiagnosticSeverity: 'error',
              firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
              lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
              runtimeLastSeenAt: '2026-05-25T20:13:46.326Z',
              lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
            },
          },
          updatedAt: '2026-05-25T20:14:05.411Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'stale_metadata',
      hardFailure: true,
      hardFailureReason: reason,
      runtimeDiagnostic: 'Runtime process crashed',
      runtimeDiagnosticSeverity: 'error',
    });
    expect(result.teamLaunchState).toBe('partial_failure');
  });

  it('keeps provisioned-but-not-alive failed when refreshed runtime evidence is unsafe', async () => {
    const teamName = 'primary-bootstrap-cli-provisioned-not-alive-refreshed-runtime-error';
    const bootstrapRunId = 'run-primary-cli-exit-refreshed-runtime-error';
    const reason =
      'CLI process exited (code 1) - team provisioned but not alive; process table unavailable';
    writeTeamMeta(teamName, {
      providerId: 'anthropic',
      model: 'sonnet',
    });
    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'anthropic', model: 'sonnet' }]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-25T20:13:46.326Z'),
          lastObservedAt: Date.parse('2026-05-25T20:13:56.110Z'),
        },
      ],
      '2026-05-25T20:14:03.317Z',
      { runId: bootstrapRunId }
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['tom'],
          members: {
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'sonnet',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'anthropic',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 27_036,
              bootstrapConfirmed: true,
              hardFailure: true,
              hardFailureReason: reason,
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic:
                'runtime pid could not be verified because process table is unavailable',
              runtimeDiagnosticSeverity: 'warning',
              firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
              lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
              runtimeLastSeenAt: '2026-05-25T20:13:46.326Z',
              lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
            },
          },
          updatedAt: '2026-05-25T20:14:05.411Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              livenessKind: 'not_found',
              runtimeDiagnostic: 'Runtime process crashed',
              runtimeDiagnosticSeverity: 'error',
              pidSource: 'process_table',
            },
          ],
        ])
    );
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'not_found',
      hardFailure: true,
      hardFailureReason: reason,
      runtimeDiagnostic: 'Runtime process crashed',
      runtimeDiagnosticSeverity: 'error',
    });
    expect(result.teamLaunchState).toBe('partial_failure');
  });

  it('keeps provisioned-but-not-alive failed when refreshed runtime evidence is only a candidate', async () => {
    const teamName = 'primary-bootstrap-cli-provisioned-not-alive-runtime-candidate';
    const bootstrapRunId = 'run-primary-cli-exit-runtime-candidate';
    const reason =
      'CLI process exited (code 1) - team provisioned but not alive; process table unavailable';
    writeTeamMeta(teamName, {
      providerId: 'anthropic',
      model: 'sonnet',
    });
    writeMembersMeta(teamName, [{ name: 'tom', providerId: 'anthropic', model: 'sonnet' }]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['tom']);
    writeMemberBootstrapRunId(teamName, 'tom', bootstrapRunId);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'tom',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse('2026-05-25T20:13:46.326Z'),
          lastObservedAt: Date.parse('2026-05-25T20:13:56.110Z'),
        },
      ],
      '2026-05-25T20:14:03.317Z',
      { runId: bootstrapRunId }
    );
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['tom'],
          members: {
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'sonnet',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'anthropic',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 27_036,
              bootstrapConfirmed: true,
              hardFailure: true,
              hardFailureReason: reason,
              livenessKind: 'registered_only',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic:
                'runtime pid could not be verified because process table is unavailable',
              runtimeDiagnosticSeverity: 'warning',
              firstSpawnAcceptedAt: '2026-05-25T20:13:46.326Z',
              lastHeartbeatAt: '2026-05-25T20:13:56.110Z',
              runtimeLastSeenAt: '2026-05-25T20:13:46.326Z',
              lastEvaluatedAt: '2026-05-25T20:14:05.411Z',
            },
          },
          updatedAt: '2026-05-25T20:14:05.411Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(
      async () =>
        new Map([
          [
            'tom',
            {
              alive: false,
              livenessKind: 'runtime_process_candidate',
              runtimeDiagnostic:
                'OpenCode runtime process detected, but teammate bootstrap is not confirmed',
              runtimeDiagnosticSeverity: 'warning',
              pidSource: 'opencode_bridge',
            },
          ],
        ])
    );
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.statuses.tom).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'runtime_process_candidate',
      hardFailure: true,
      hardFailureReason: reason,
      runtimeDiagnostic:
        'OpenCode runtime process detected, but teammate bootstrap is not confirmed',
      runtimeDiagnosticSeverity: 'warning',
    });
    expect(result.teamLaunchState).toBe('partial_failure');
  });

  it('cleans stale confirmed primary diagnostics from an already successful mixed launch', async () => {
    const teamName = 'mixed-confirmed-primary-stale-diagnostic-cleans';
    writeTeamMeta(teamName, {
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.5',
    });
    writeMembersMeta(teamName, [
      { name: 'alice', providerId: 'codex', model: 'gpt-5.5' },
      { name: 'tom', providerId: 'anthropic', model: 'haiku' },
      { name: 'bob', providerId: 'opencode', model: 'opencode/deepseek-v4-flash-free' },
      { name: 'jack', providerId: 'opencode', model: 'opencode/big-pickle' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['alice', 'tom']);
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['alice', 'tom', 'bob', 'jack'],
          members: {
            alice: {
              name: 'alice',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              lastEvaluatedAt: '2026-05-24T12:04:48.900Z',
            },
            tom: {
              name: 'tom',
              providerId: 'anthropic',
              model: 'haiku',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: false,
              runtimePid: 97_255,
              bootstrapConfirmed: true,
              hardFailure: false,
              livenessKind: 'stale_metadata',
              pidSource: 'persisted_metadata',
              runtimeDiagnostic: 'persisted runtime pid is not alive',
              runtimeDiagnosticSeverity: 'warning',
              firstSpawnAcceptedAt: '2026-05-24T09:25:45.178Z',
              lastHeartbeatAt: '2026-05-24T09:25:42.904Z',
              runtimeLastSeenAt: '2026-05-24T09:31:05.525Z',
              lastEvaluatedAt: '2026-05-24T12:04:48.900Z',
            },
            bob: {
              name: 'bob',
              providerId: 'opencode',
              model: 'opencode/deepseek-v4-flash-free',
              laneId: 'secondary:opencode:bob',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              runtimePid: 2_756,
              runtimeSessionId: 'ses_bob',
              livenessKind: 'confirmed_bootstrap',
              lastHeartbeatAt: '2026-05-24T09:31:39.741Z',
              lastEvaluatedAt: '2026-05-24T09:31:39.741Z',
            },
            jack: {
              name: 'jack',
              providerId: 'opencode',
              model: 'opencode/big-pickle',
              laneId: 'secondary:opencode:jack',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'confirmed_alive',
              agentToolAccepted: true,
              runtimeAlive: true,
              bootstrapConfirmed: true,
              hardFailure: false,
              runtimePid: 2_756,
              runtimeSessionId: 'ses_jack',
              livenessKind: 'confirmed_bootstrap',
              lastHeartbeatAt: '2026-05-24T09:31:39.741Z',
              lastEvaluatedAt: '2026-05-24T09:31:39.741Z',
            },
          },
          updatedAt: '2026-05-24T12:04:48.900Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('clean_success');
    expect(result.statuses.tom).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      runtimeAlive: false,
      livenessKind: 'confirmed_bootstrap',
      hardFailure: false,
      error: undefined,
    });
    expect(result.statuses.tom?.runtimeDiagnostic).toBeUndefined();
    expect(result.statuses.tom?.runtimeDiagnosticSeverity).toBeUndefined();
    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.tom).toMatchObject({
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      livenessKind: 'confirmed_bootstrap',
    });
    expect(persisted.members.tom.runtimeDiagnostic).toBeUndefined();
    expect(persisted.members.tom.runtimeDiagnosticSeverity).toBeUndefined();
  });

  it('does not collapse persisted mixed secondary failures when primary bootstrap snapshot is clean and richer', async () => {
    const teamName = 'mixed-clean-bootstrap-does-not-collapse-secondary-failure';
    writeMembersMeta(teamName, [
      { name: 'bob', providerId: 'codex', model: 'gpt-5.4' },
      { name: 'alice', providerId: 'opencode', model: 'openrouter/z-ai/glm-5.1' },
    ]);
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob']);
    writeBootstrapState(
      teamName,
      [
        { name: 'bob', status: 'bootstrap_confirmed' },
        { name: 'jack', status: 'bootstrap_confirmed' },
        { name: 'nova', status: 'bootstrap_confirmed' },
        { name: 'sam', status: 'bootstrap_confirmed' },
        { name: 'kim', status: 'bootstrap_confirmed' },
      ],
      '2026-04-23T10:03:00.000Z'
    );
    const exactOpenCodeReason =
      'Latest assistant message msg_alice failed with APIError - Insufficient credits.';
    fs.writeFileSync(
      getTeamLaunchStatePath(teamName),
      `${JSON.stringify(
        createPersistedLaunchSnapshot({
          teamName,
          leadSessionId: 'lead-session',
          launchPhase: 'finished',
          expectedMembers: ['bob', 'alice'],
          bootstrapExpectedMembers: ['bob'],
          members: {
            bob: {
              name: 'bob',
              providerId: 'codex',
              laneId: 'primary',
              laneKind: 'primary',
              laneOwnerProviderId: 'codex',
              launchState: 'failed_to_start',
              agentToolAccepted: false,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'Teammate was never spawned during launch.',
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
            alice: {
              name: 'alice',
              providerId: 'opencode',
              laneId: 'secondary:opencode:alice',
              laneKind: 'secondary',
              laneOwnerProviderId: 'opencode',
              launchState: 'failed_to_start',
              agentToolAccepted: true,
              runtimeAlive: false,
              bootstrapConfirmed: false,
              hardFailure: true,
              hardFailureReason: 'OpenCode bridge reported member launch failure',
              diagnostics: [exactOpenCodeReason],
              lastEvaluatedAt: '2026-04-23T10:02:00.000Z',
            },
          },
          updatedAt: '2026-04-23T10:02:00.000Z',
        }),
        null,
        2
      )}\n`,
      'utf8'
    );

    const svc = new TeamProvisioningService();
    const result = await svc.getMemberSpawnStatuses(teamName);

    expect(result.teamLaunchState).toBe('partial_failure');
    expect(result.statuses.bob).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      bootstrapConfirmed: true,
      hardFailure: false,
    });
    expect(result.statuses.alice).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailureReason: exactOpenCodeReason,
    });
    const persisted = JSON.parse(
      await fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    );
    expect(persisted.members.alice).toMatchObject({
      laneId: 'secondary:opencode:alice',
      launchState: 'failed_to_start',
      hardFailureReason: exactOpenCodeReason,
    });
  });

  it('does not revive primary members from stale bootstrap-state during mixed projection', async () => {
    const teamName = 'atlas-hq-stale-bootstrap-live';
    writeLaunchConfig(teamName, '/Users/test/proj', 'lead-session', ['bob']);
    writeBootstrapState(
      teamName,
      [
        {
          name: 'bob',
          status: 'bootstrap_confirmed',
          lastObservedAt: Date.parse('2026-04-23T09:59:00.000Z'),
        },
      ],
      '2026-04-23T10:01:00.000Z'
    );
    const run = createMemberSpawnRun({
      teamName,
      runId: 'run-atlas-hq-stale-bootstrap-live',
      startedAt: '2026-04-23T10:00:00.000Z',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            agentToolAccepted: false,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Teammate was never spawned during launch.',
            hardFailureReason: 'Teammate was never spawned during launch.',
            firstSpawnAcceptedAt: '2026-04-23T10:00:30.000Z',
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.request = {
      teamName,
      cwd: '/Users/test/proj',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.effectiveMembers = [{ name: 'bob', providerId: 'codex', model: 'gpt-5.3-codex' }];
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:alice',
        providerId: 'opencode',
        member: { name: 'alice', providerId: 'opencode', model: 'openrouter/model' },
        runId: 'lane-run-alice',
        state: 'finished',
        result: null,
        warnings: [],
        diagnostics: [],
      },
    ];

    const svc = new TeamProvisioningService();
    const snapshot = await (svc as any).persistLaunchStateSnapshot(run, 'finished');

    expect(snapshot.members.bob).toMatchObject({
      launchState: 'failed_to_start',
      bootstrapConfirmed: false,
      hardFailure: true,
    });
  });

  it('applies bootstrap-state truth before persisting pure launch cleanup snapshots', async () => {
    const teamName = 'pure-anthropic-cleanup-bootstrap-truth';
    const acceptedAt = '2026-05-22T14:15:44.000Z';
    const bootstrapAt = '2026-05-22T14:15:58.000Z';
    const run = createMemberSpawnRun({
      teamName,
      runId: 'run-pure-anthropic-cleanup-bootstrap-truth',
      startedAt: '2026-05-22T14:15:40.000Z',
      expectedMembers: ['jack'],
      memberSpawnStatuses: new Map([
        [
          'jack',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            agentToolAccepted: true,
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Launch ended before teammate bootstrap completed.',
            hardFailureReason: 'Launch ended before teammate bootstrap completed.',
            firstSpawnAcceptedAt: acceptedAt,
          }),
        ],
      ]),
    });
    run.isLaunch = true;
    run.request = {
      teamName,
      cwd: '/Users/test/proj',
      providerId: 'anthropic',
      model: 'claude-opus-4-7',
      members: [],
    };
    run.effectiveMembers = [{ name: 'jack', providerId: 'anthropic', model: 'claude-opus-4-7' }];
    fs.mkdirSync(path.join(tempTeamsBase, teamName), { recursive: true });
    writeBootstrapState(
      teamName,
      [
        {
          name: 'jack',
          status: 'bootstrap_confirmed',
          lastAttemptAt: Date.parse(acceptedAt),
          lastObservedAt: Date.parse(bootstrapAt),
        },
      ],
      bootstrapAt
    );

    const svc = new TeamProvisioningService();
    const snapshot = await (
      svc as unknown as {
        persistLaunchStateSnapshot: (
          run: ReturnType<typeof createMemberSpawnRun>,
          phase: 'finished'
        ) => Promise<unknown>;
      }
    ).persistLaunchStateSnapshot(run, 'finished');

    expect(snapshot).toBeNull();
    await expect(
      fsPromises.readFile(getTeamLaunchStatePath(teamName), 'utf8')
    ).rejects.toMatchObject({
      code: 'ENOENT',
    });
  });

  it('includes queued OpenCode secondary lanes in live spawn statuses during createTeam runs', async () => {
    const svc = new TeamProvisioningService();
    vi.spyOn(svc as any, 'refreshMemberSpawnStatusesFromLeadInbox').mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'maybeAuditMemberSpawnStatuses').mockResolvedValue(undefined);

    const run = createMemberSpawnRun({
      teamName: 'mixed-create-team',
      runId: 'run-mixed-create-1',
      expectedMembers: ['alice'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
            livenessSource: 'heartbeat',
          }),
        ],
      ]),
    });
    run.isLaunch = false;
    run.request = {
      teamName: 'mixed-create-team',
      cwd: '/tmp/mixed-create-team',
      providerId: 'codex',
      providerBackendId: 'codex-native',
      model: 'gpt-5.4',
      members: [],
    };
    run.effectiveMembers = [
      {
        name: 'alice',
        providerId: 'codex',
        model: 'gpt-5.4-mini',
      },
    ];
    run.mixedSecondaryLanes = [
      {
        laneId: 'secondary:opencode:bob',
        providerId: 'opencode',
        member: {
          name: 'bob',
          providerId: 'opencode',
          model: 'opencode/big-pickle',
        },
        runId: null,
        state: 'queued',
        result: null,
        warnings: [],
        diagnostics: [],
      },
      {
        laneId: 'secondary:opencode:tom',
        providerId: 'opencode',
        member: {
          name: 'tom',
          providerId: 'opencode',
          model: 'opencode/minimax-m2.5-free',
        },
        runId: null,
        state: 'queued',
        result: null,
        warnings: [],
        diagnostics: [],
      },
    ];
    run.detectedSessionId = 'lead-session';

    (svc as any).runs.set(run.runId, run);
    (svc as any).provisioningRunByTeam.set(run.teamName, run.runId);

    const result = await svc.getMemberSpawnStatuses(run.teamName);

    expect(result.teamLaunchState).toBe('partial_pending');
    expect(result.expectedMembers).toEqual(expect.arrayContaining(['alice', 'bob', 'tom']));
    expect(result.statuses.alice).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
    });
    expect(result.statuses.bob).toMatchObject({
      status: 'spawning',
      launchState: 'starting',
    });
    expect(result.statuses.tom).toMatchObject({
      status: 'spawning',
      launchState: 'starting',
    });
  });

  it('syncs stale live mixed-lane failures from a healthier persisted snapshot', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'forge-labs-4',
      runId: 'run-mixed-sync-1',
      expectedMembers: ['alice', 'jack'],
      memberSpawnStatuses: new Map([
        [
          'alice',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            runtimeAlive: true,
            bootstrapConfirmed: false,
            hardFailure: false,
          }),
        ],
        [
          'jack',
          createMemberSpawnStatusEntry({
            status: 'error',
            launchState: 'failed_to_start',
            runtimeAlive: false,
            bootstrapConfirmed: false,
            hardFailure: true,
            error: 'Teammate was never spawned during launch.',
            hardFailureReason: 'Teammate was never spawned during launch.',
          }),
        ],
      ]),
    });
    run.isLaunch = true;

    const snapshot = createPersistedLaunchSnapshot({
      teamName: 'forge-labs-4',
      leadSessionId: 'lead-session',
      launchPhase: 'finished',
      expectedMembers: ['alice', 'jack'],
      members: {
        alice: {
          name: 'alice',
          launchState: 'runtime_pending_bootstrap',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: false,
          hardFailure: false,
          lastEvaluatedAt: '2026-04-23T08:08:27.067Z',
        },
        jack: {
          name: 'jack',
          providerId: 'opencode',
          launchState: 'confirmed_alive',
          agentToolAccepted: true,
          runtimeAlive: true,
          bootstrapConfirmed: true,
          hardFailure: false,
          lastEvaluatedAt: '2026-04-23T08:08:27.067Z',
        },
      },
      updatedAt: '2026-04-23T08:08:27.067Z',
    });

    vi.spyOn(svc as any, 'persistLaunchStateSnapshot').mockResolvedValue(snapshot);
    vi.spyOn(svc as any, 'isCurrentTrackedRun').mockReturnValue(true);

    await (svc as any).publishMixedSecondaryLaneStatusChange(run, {
      laneId: 'secondary:opencode:jack',
      providerId: 'opencode',
      member: {
        name: 'jack',
        providerId: 'opencode',
        model: 'opencode/ling-2.6-flash-free',
      },
      runId: 'lane-run-jack',
      state: 'finished',
      result: null,
      warnings: [],
      diagnostics: [],
    });

    expect(run.memberSpawnStatuses.get('jack')).toMatchObject({
      status: 'online',
      launchState: 'confirmed_alive',
      hardFailure: false,
      hardFailureReason: undefined,
      error: undefined,
      bootstrapConfirmed: true,
      runtimeAlive: true,
    });
    expect(run.expectedMembers).toEqual(['alice', 'jack']);
  });

  it('bulk retries failed OpenCode secondary lanes sequentially and classifies outcomes', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'mixed-retry-team',
      runId: 'run-mixed-retry',
      expectedMembers: ['alice', 'tom', 'nova'],
    });
    run.isLaunch = true;
    run.provisioningComplete = true;

    (svc as any).runs.set(run.runId, run);
    (svc as any).aliveRunByTeam.set(run.teamName, run.runId);

    vi.spyOn(svc as any, 'collectFailedOpenCodeSecondaryRetryCandidates').mockResolvedValue([
      { memberName: 'alice', laneId: 'secondary:opencode:alice' },
      { memberName: 'tom', laneId: 'secondary:opencode:tom' },
      { memberName: 'nova', laneId: 'secondary:opencode:nova' },
    ]);
    const reattach = vi
      .spyOn(svc as any, 'reattachOpenCodeOwnedMemberLaneUnlocked')
      .mockResolvedValueOnce(undefined)
      .mockResolvedValueOnce(undefined)
      .mockRejectedValueOnce(new Error('OpenCode bridge crashed'));
    vi.spyOn(svc as any, 'readOpenCodeSecondaryRetryOutcome')
      .mockResolvedValueOnce({ launchState: 'confirmed_alive' })
      .mockResolvedValueOnce({
        launchState: 'failed_to_start',
        reason: 'Latest assistant message reported OpenRouter credits exhausted',
      });
    const notify = vi
      .spyOn(svc as any, 'notifyLeadAboutConfirmedOpenCodeRetries')
      .mockResolvedValue(undefined);

    const result = await svc.retryFailedOpenCodeSecondaryLanes(run.teamName);

    expect(reattach).toHaveBeenNthCalledWith(1, run.teamName, 'alice', {
      reason: 'manual_restart',
    });
    expect(reattach).toHaveBeenNthCalledWith(2, run.teamName, 'tom', {
      reason: 'manual_restart',
    });
    expect(reattach).toHaveBeenNthCalledWith(3, run.teamName, 'nova', {
      reason: 'manual_restart',
    });
    expect(result).toEqual({
      attempted: ['alice', 'tom'],
      confirmed: ['alice'],
      pending: [],
      failed: [
        {
          memberName: 'tom',
          error: 'Latest assistant message reported OpenRouter credits exhausted',
        },
        { memberName: 'nova', error: 'OpenCode bridge crashed' },
      ],
      skipped: [],
    });
    expect(notify).toHaveBeenCalledWith(run, result);
  });

  it('rejects a concurrent manual restart for the same teammate', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'codex-lifecycle-team',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'online',
            launchState: 'confirmed_alive',
            runtimeAlive: true,
            bootstrapConfirmed: true,
          }),
        ],
      ]),
    });
    run.child = { pid: 111 };
    run.processKilled = false;
    run.cancelRequested = false;

    const configReady = createDeferred<{
      name: string;
      members: Array<{ name: string; agentType?: string }>;
    }>();
    (svc as any).configReader = {
      getConfig: vi.fn(() => configReady.promise),
    };
    (svc as any).membersMetaStore = {
      getMembers: vi.fn(async () => [
        {
          name: 'bob',
          role: 'Developer',
          providerId: 'codex',
          model: 'gpt-5.4',
          agentType: 'general-purpose',
        },
      ]),
    };
    (svc as any).sendMessageToRun = vi.fn(async () => undefined);
    (svc as any).readPersistedRuntimeMembers = vi.fn(() => []);
    (svc as any).getLiveTeamAgentRuntimeMetadata = vi.fn(async () => new Map());
    (svc as any).aliveRunByTeam.set(run.teamName, run.runId);
    (svc as any).runs.set(run.runId, run);

    const firstRestart = svc.restartMember(run.teamName, 'bob');
    await Promise.resolve();

    await expect(svc.restartMember(run.teamName, 'bob')).rejects.toThrow(
      'Lifecycle operation for teammate "bob" is already in progress'
    );

    configReady.resolve({
      name: 'Codex Lifecycle Team',
      members: [{ name: 'team-lead', agentType: 'team-lead' }],
    });
    await firstRestart;
  });

  it('does not let one teammate lifecycle operation block another teammate or team', async () => {
    const svc = new TeamProvisioningService();
    const aliceDone = createDeferred<void>();
    const firstOperation = (svc as any).runMemberLifecycleOperation(
      'same-team',
      'alice',
      'manual_restart',
      () => aliceDone.promise
    );

    await expect(
      (svc as any).runMemberLifecycleOperation(
        'same-team',
        'bob',
        'manual_restart',
        async () => 'bob-ok'
      )
    ).resolves.toBe('bob-ok');

    await expect(
      (svc as any).runMemberLifecycleOperation(
        'other-team',
        'alice',
        'manual_restart',
        async () => 'other-ok'
      )
    ).resolves.toBe('other-ok');

    aliceDone.resolve();
    await firstOperation;
  });

  it('skips busy OpenCode retry candidates while continuing other candidates', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'mixed-retry-busy-team',
      runId: 'run-mixed-retry-busy',
      expectedMembers: ['alice', 'tom'],
    });
    run.isLaunch = true;
    run.provisioningComplete = true;
    (svc as any).runs.set(run.runId, run);
    (svc as any).aliveRunByTeam.set(run.teamName, run.runId);

    const busyDone = createDeferred<void>();
    const busyOperation = (svc as any).runMemberLifecycleOperation(
      run.teamName,
      'alice',
      'manual_restart',
      () => busyDone.promise
    );

    vi.spyOn(svc as any, 'collectFailedOpenCodeSecondaryRetryCandidates').mockResolvedValue([
      { memberName: 'alice', laneId: 'secondary:opencode:alice' },
      { memberName: 'tom', laneId: 'secondary:opencode:tom' },
    ]);
    const reattach = vi
      .spyOn(svc as any, 'reattachOpenCodeOwnedMemberLaneUnlocked')
      .mockResolvedValue(undefined);
    vi.spyOn(svc as any, 'readOpenCodeSecondaryRetryOutcome').mockResolvedValue({
      launchState: 'confirmed_alive',
    });
    vi.spyOn(svc as any, 'notifyLeadAboutConfirmedOpenCodeRetries').mockResolvedValue(undefined);

    const result = await svc.retryFailedOpenCodeSecondaryLanes(run.teamName);

    expect(reattach).toHaveBeenCalledTimes(1);
    expect(reattach).toHaveBeenCalledWith(run.teamName, 'tom', { reason: 'manual_restart' });
    expect(result).toMatchObject({
      attempted: ['tom'],
      confirmed: ['tom'],
      skipped: [
        {
          memberName: 'alice',
          reason: 'Lifecycle operation already in progress',
        },
      ],
    });

    busyDone.resolve();
    await busyOperation;
  });

  it('blocks manual restart while an OpenCode member update reattach is active', async () => {
    const svc = new TeamProvisioningService();
    const reattachDone = createDeferred<void>();
    const reattachOperation = (svc as any).runMemberLifecycleOperation(
      'mixed-update-team',
      'bob',
      'opencode_member_updated',
      () => reattachDone.promise
    );

    await expect(svc.restartMember('mixed-update-team', 'bob')).rejects.toThrow(
      'Lifecycle operation for teammate "bob" is already in progress'
    );

    reattachDone.resolve();
    await reattachOperation;
  });

  it('blocks manual restart while an OpenCode member removal detach is active', async () => {
    const svc = new TeamProvisioningService();
    const detachDone = createDeferred<void>();
    const detachOperation = (svc as any).runMemberLifecycleOperation(
      'mixed-remove-team',
      'bob',
      'opencode_member_removed',
      () => detachDone.promise
    );

    await expect(svc.restartMember('mixed-remove-team', 'bob')).rejects.toThrow(
      'Lifecycle operation for teammate "bob" is already in progress'
    );

    detachDone.resolve();
    await detachOperation;
  });

  it('does not let launch cleanup overwrite a teammate with an active lifecycle operation', async () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'cleanup-guard-team',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            runtimeAlive: false,
            bootstrapConfirmed: false,
          }),
        ],
      ]),
    });
    const lifecycleDone = createDeferred<void>();
    const lifecycleOperation = (svc as any).runMemberLifecycleOperation(
      run.teamName,
      'bob',
      'manual_restart',
      () => lifecycleDone.promise
    );

    (svc as any).markUnconfirmedBootstrapMembersFailed(run, 'launch cleanup failure', {
      cleanupRequested: true,
    });

    expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
      status: 'waiting',
      launchState: 'runtime_pending_bootstrap',
      hardFailure: false,
    });

    lifecycleDone.resolve();
    await lifecycleOperation;
  });

  it('still marks unconfirmed teammates failed during cleanup when no lifecycle operation is active', () => {
    const svc = new TeamProvisioningService();
    const run = createMemberSpawnRun({
      teamName: 'cleanup-no-guard-team',
      expectedMembers: ['bob'],
      memberSpawnStatuses: new Map([
        [
          'bob',
          createMemberSpawnStatusEntry({
            status: 'waiting',
            launchState: 'runtime_pending_bootstrap',
            runtimeAlive: true,
            bootstrapConfirmed: false,
            livenessSource: 'process',
          }),
        ],
      ]),
    });

    (svc as any).markUnconfirmedBootstrapMembersFailed(run, 'launch cleanup failure', {
      cleanupRequested: true,
    });

    expect(run.memberSpawnStatuses.get('bob')).toMatchObject({
      status: 'error',
      launchState: 'failed_to_start',
      hardFailure: true,
      runtimeAlive: false,
      livenessSource: undefined,
    });
  });
});
