import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
  chmodSync,
  mkdtempSync,
  mkdirSync,
  writeFileSync,
  rmSync,
  readFileSync,
  symlinkSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { parseFleetRosterV1, type FleetRoster, type FleetAgent } from './fleet-roster-v1.js';
import {
  buildFleetCommsBlock,
  renderPeerReach,
  readFleetCommsBlock,
  resolveCommsBlock,
  resolvePeerCommand,
  renderToolsContractStatus,
} from './comms-onboarding.js';

const ROSTER = [
  'version: 1',
  'transport: tmux',
  'tmux:',
  '  socket_name: mosaic-fleet',
  'agents:',
  '  - name: orchestrator',
  '    runtime: claude',
  '    class: orchestrator',
  '    host: w-jarvis',
  '  - name: enhancer',
  '    runtime: claude',
  '    class: enhancer',
  '    host: w-jarvis',
  '  - name: coder0',
  '    runtime: pi',
  '    class: implementer',
  '    host: w-jarvis',
  '  - name: coder0-0',
  '    runtime: claude',
  '    class: implementer',
  '    host: 10.1.10.37',
  '    ssh: jwoltje@10.1.10.37',
  '',
].join('\n');

function roster(source = ROSTER): FleetRoster {
  return parseFleetRosterV1(source, 'yaml');
}

describe('shared fleet roster v1 resolver', () => {
  it('resolves comms fields and the global socket through the canonical roster contract', () => {
    const resolved = roster();
    expect(resolved.tmux.socketName).toBe('mosaic-fleet');
    expect(resolved.agents.find((agent) => agent.name === 'coder0-0')).toMatchObject({
      className: 'code',
      host: '10.1.10.37',
      ssh: 'jwoltje@10.1.10.37',
    });
  });

  it('rejects unknown fields instead of leniently constructing a second roster view', () => {
    expect(() => parseFleetRosterV1(`${ROSTER}\nunknown: value\n`, 'yaml')).toThrow(
      /unknown field/i,
    );
  });

  it('rejects an unsupported independent per-agent socket instead of targeting a nonexistent session', () => {
    expect(() =>
      roster(
        ROSTER.replace(
          '    host: w-jarvis\n  - name: coder0',
          '    host: w-jarvis\n    socket: other-socket\n  - name: coder0',
        ),
      ),
    ).toThrow(/independent per-agent sockets are not supported/i);
  });

  it('rejects unsafe operational targeting values', () => {
    expect(() =>
      roster(ROSTER.replace('    ssh: jwoltje@10.1.10.37', '    ssh: host;touch-owned')),
    ).toThrow(/unsupported targeting characters/i);
  });

  it('normalizes matching connector settings for YAML and JSON rosters', () => {
    const yamlSource = `${ROSTER}connector:\n  kind: discord\n  discord:\n    channel_id: "123"\n`;
    const jsonSource = JSON.stringify({
      version: 1,
      transport: 'tmux',
      agents: [{ name: 'a', runtime: 'claude', class: 'worker' }],
      connector: {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '@a:example',
          room_id: '!room:example',
        },
      },
    });

    expect(parseFleetRosterV1(yamlSource, 'yaml').connector).toEqual({
      kind: 'discord',
      discord: { channelId: '123' },
    });
    expect(parseFleetRosterV1(jsonSource, 'json').connector).toEqual({
      kind: 'matrix',
      matrix: {
        homeserverUrl: 'https://matrix.example',
        userId: '@a:example',
        roomId: '!room:example',
      },
    });
  });

  it.each([
    ['discord channel_id', { kind: 'discord', discord: { channel_id: '' } }],
    ['discord channel_id whitespace', { kind: 'discord', discord: { channel_id: '   ' } }],
    [
      'matrix homeserver_url',
      {
        kind: 'matrix',
        matrix: { homeserver_url: '', user_id: '@a:example', room_id: '!room:example' },
      },
    ],
    [
      'matrix homeserver_url whitespace',
      {
        kind: 'matrix',
        matrix: { homeserver_url: '\t', user_id: '@a:example', room_id: '!room:example' },
      },
    ],
    [
      'matrix user_id',
      {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '',
          room_id: '!room:example',
        },
      },
    ],
    [
      'matrix user_id whitespace',
      {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '  ',
          room_id: '!room:example',
        },
      },
    ],
    [
      'matrix room_id',
      {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '@a:example',
          room_id: '',
        },
      },
    ],
    [
      'matrix room_id whitespace',
      {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '@a:example',
          room_id: '\n',
        },
      },
    ],
  ])(
    'rejects empty or whitespace-only parser-required connector string: %s',
    (_label, connector) => {
      expect(() =>
        parseFleetRosterV1(
          JSON.stringify({
            version: 1,
            transport: 'tmux',
            agents: [{ name: 'a', runtime: 'claude', class: 'worker' }],
            connector,
          }),
          'json',
        ),
      ).toThrow(/required/i);
    },
  );

  it.each([
    ['tmux with discord settings', { kind: 'tmux', discord: { channel_id: '123' } }],
    ['discord without discord settings', { kind: 'discord' }],
    [
      'discord with matrix settings',
      { kind: 'discord', discord: { channel_id: '123' }, matrix: {} },
    ],
    ['matrix without matrix settings', { kind: 'matrix' }],
    [
      'matrix with discord settings',
      {
        kind: 'matrix',
        matrix: {
          homeserver_url: 'https://matrix.example',
          user_id: '@a:example',
          room_id: '!room:example',
        },
        discord: { channel_id: '123' },
      },
    ],
  ])('rejects connector kind/settings mismatch: %s', (_label, connector) => {
    expect(() =>
      parseFleetRosterV1(
        JSON.stringify({
          version: 1,
          transport: 'tmux',
          agents: [{ name: 'a', runtime: 'claude', class: 'worker' }],
          connector,
        }),
        'json',
      ),
    ).toThrow();
  });

  it.each([
    ['tmux socket', ['tmux', 'socket_name'], ['tmux', 'socketName'], 'same', 'different'],
    ['tmux holder', ['tmux', 'holder_session'], ['tmux', 'holderSession'], 'same', 'different'],
    [
      'defaults working directory',
      ['defaults', 'working_directory'],
      ['defaults', 'workingDirectory'],
      'same',
      'different',
    ],
    [
      'runtime reset command',
      ['runtimes', 'claude', 'reset_command'],
      ['runtimes', 'claude', 'resetCommand'],
      'same',
      'different',
    ],
    [
      'agent working directory',
      ['agents', 0, 'working_directory'],
      ['agents', 0, 'workingDirectory'],
      'same',
      'different',
    ],
    [
      'agent model hint',
      ['agents', 0, 'model_hint'],
      ['agents', 0, 'modelHint'],
      'same',
      'different',
    ],
    [
      'agent reasoning level',
      ['agents', 0, 'reasoning_level'],
      ['agents', 0, 'reasoningLevel'],
      'same',
      'different',
    ],
    [
      'agent tool policy',
      ['agents', 0, 'tool_policy'],
      ['agents', 0, 'toolPolicy'],
      'same',
      'different',
    ],
    [
      'agent persistent persona',
      ['agents', 0, 'persistent_persona'],
      ['agents', 0, 'persistentPersona'],
      true,
      false,
    ],
    [
      'agent reset between tasks',
      ['agents', 0, 'reset_between_tasks'],
      ['agents', 0, 'resetBetweenTasks'],
      true,
      false,
    ],
    [
      'agent kickstart template',
      ['agents', 0, 'kickstart_template'],
      ['agents', 0, 'kickstartTemplate'],
      'same',
      'different',
    ],
  ] as const)(
    'rejects conflicting %s aliases and accepts identical aliases',
    (_label, snake, camel, same, different) => {
      const base: Record<string, unknown> = {
        version: 1,
        transport: 'tmux',
        tmux: {},
        defaults: {},
        runtimes: { claude: {} },
        agents: [{ name: 'a', runtime: 'claude', class: 'worker' }],
      };
      const assign = (
        root: Record<string, unknown>,
        path: readonly (string | number)[],
        value: unknown,
      ) => {
        let cursor: unknown = root;
        for (const segment of path.slice(0, -1)) {
          cursor = (cursor as Record<string | number, unknown>)[segment];
        }
        (cursor as Record<string | number, unknown>)[path.at(-1)!] = value;
      };
      assign(base, snake, same);
      assign(base, camel, different);
      expect(() => parseFleetRosterV1(JSON.stringify(base), 'json')).toThrow(
        /aliases .* conflict/i,
      );
      assign(base, camel, same);
      expect(() => parseFleetRosterV1(JSON.stringify(base), 'json')).not.toThrow();
    },
  );
});

describe('renderPeerReach — exact same-host/cross-host/socket targeting', () => {
  const send = '/home/u/.config/mosaic/tools/tmux/agent-send.sh';
  const base: FleetAgent = {
    name: 'peer',
    runtime: 'claude',
    className: 'worker',
  };

  it('renders the global named socket and omits -H for a same-host peer', () => {
    expect(renderPeerReach(base, 'w-jarvis', 'w-jarvis', 'mosaic-fleet', send)).toBe(
      `${send} -L mosaic-fleet -s peer -m "…"`,
    );
  });

  it('uses only the explicit roster ssh target for a cross-host peer', () => {
    const peer: FleetAgent = {
      ...base,
      name: 'coder0-0',
      host: '10.1.10.37',
      ssh: 'jwoltje@10.1.10.37',
    };
    expect(renderPeerReach(peer, 'w-jarvis', 'w-jarvis', 'mosaic-fleet', send)).toBe(
      `${send} -L mosaic-fleet -H jwoltje@10.1.10.37 -s coder0-0 -m "…"`,
    );
  });

  it('fails closed when a cross-host peer has no explicit roster ssh target', () => {
    const peer: FleetAgent = { ...base, name: 'x', host: '10.0.0.9' };
    expect(() => renderPeerReach(peer, 'w-jarvis', 'w-jarvis', 'mosaic-fleet', send)).toThrow(
      /explicit roster ssh target/i,
    );
  });

  it('renders only the fleet-wide supported socket', () => {
    const peer: FleetAgent = { ...base, socket: 'mosaic-fleet' };
    expect(renderPeerReach(peer, 'w-jarvis', 'w-jarvis', 'mosaic-fleet', send)).toBe(
      `${send} -L mosaic-fleet -s peer -m "…"`,
    );
  });

  it('resolves hostless peers against the stable fleet-host baseline, not the viewer host', () => {
    const peer: FleetAgent = { ...base, ssh: 'fleet-user@w-jarvis' };
    expect(renderPeerReach(peer, 'remote-host', 'w-jarvis', 'mosaic-fleet', send)).toBe(
      `${send} -L mosaic-fleet -H fleet-user@w-jarvis -s peer -m "…"`,
    );
  });

  it('shell-quotes an exact helper path that contains spaces', () => {
    expect(
      renderPeerReach(base, 'w-jarvis', 'w-jarvis', 'mosaic-fleet', '/home/test user/send.sh'),
    ).toBe(`'/home/test user/send.sh' -L mosaic-fleet -s peer -m "…"`);
  });

  it('omits -L only for the literal default socket', () => {
    expect(renderPeerReach(base, 'w-jarvis', 'w-jarvis', '', send)).toBe(`${send} -s peer -m "…"`);
  });
});

describe('buildFleetCommsBlock', () => {
  const send = '/h/.config/mosaic/tools/tmux/agent-send.sh';

  it('renders authoritative identity, exact rows, generation, and no operational metavariables', () => {
    const block = buildFleetCommsBlock({
      selfName: 'enhancer',
      roster: roster(),
      localHost: 'ignored-process-host',
      agentSendPath: send,
    });

    expect(block).toContain('# Fleet Comms');
    expect(block).toContain('Host: `w-jarvis`');
    expect(block).toContain('Agent/session: `enhancer`');
    expect(block).toContain('tmux socket: `mosaic-fleet`');
    expect(block).toContain(`Helper: \`${send}\``);
    expect(block).toMatch(/Comms generation: `[a-f0-9]{64}`/);
    expect(block).not.toMatch(/\|\s*enhancer\s*\|/);
    expect(block).toContain(`${send} -L mosaic-fleet -s orchestrator -m "…"`);
    expect(block).toContain(`${send} -L mosaic-fleet -H jwoltje@10.1.10.37 -s coder0-0 -m "…"`);
    expect(block).toContain(`mosaic agent comms-block enhancer`);
    expect(block).toMatch(/Never invent, substitute, or fuzzy-match/i);
    expect(block).not.toMatch(
      /<(?:user@host|src_host|src_session|dst_host|dst_session|target-session)>/,
    );
    expect(block).not.toContain('FLIP the preamble');
  });

  it('changes the generation when a rendered peer role changes', () => {
    const generation = (block: string) => block.match(/Comms generation: `([a-f0-9]{64})`/)?.[1];
    const before = buildFleetCommsBlock({
      selfName: 'enhancer',
      roster: roster(),
      localHost: 'w-jarvis',
      agentSendPath: send,
    });
    const changedRoster = roster(ROSTER.replace('class: implementer', 'class: reviewer'));
    const after = buildFleetCommsBlock({
      selfName: 'enhancer',
      roster: changedRoster,
      localHost: 'w-jarvis',
      agentSendPath: send,
    });
    expect(generation(before)).toMatch(/^[a-f0-9]{64}$/);
    expect(generation(after)).not.toBe(generation(before));
  });

  it('fails closed when any rendered cross-host row lacks ssh', () => {
    const bad = roster(ROSTER.replace('    ssh: jwoltje@10.1.10.37\n', ''));
    expect(() =>
      buildFleetCommsBlock({
        selfName: 'enhancer',
        roster: bad,
        localHost: 'w-jarvis',
        agentSendPath: send,
      }),
    ).toThrow(/explicit roster ssh target/i);
  });

  it('still renders authoritative local identity when the agent has no peers', () => {
    const solo = roster(
      [
        'version: 1',
        'transport: tmux',
        'agents:',
        '  - name: solo',
        '    runtime: claude',
        '    class: orchestrator',
      ].join('\n'),
    );
    const block = buildFleetCommsBlock({
      selfName: 'solo',
      roster: solo,
      localHost: 'h',
      agentSendPath: send,
    });
    expect(block).toContain('Host: `h`');
    expect(block).toContain('Agent/session: `solo`');
    expect(block).toContain('Role/class: `orchestrator`');
    expect(block).toMatch(/Comms generation: `[a-f0-9]{64}`/);
    expect(block).toContain('This roster has no peers');
    expect(block).toContain('## Solo authority boundaries');
    expect(block).toContain('no peer, orchestrator, or remote communication authority');
    expect(block).toContain('Do not send, infer a target, or claim fleet coordination');
  });
});

describe('resolvePeerCommand', () => {
  const send = '/h/.config/mosaic/tools/tmux/agent-send.sh';

  it('returns one exact known-peer row', () => {
    const result = resolvePeerCommand(roster(), 'enhancer', 'coder0-0', 'w-jarvis', send);
    expect(result.ok).toBe(true);
    expect(result.command).toContain('-H jwoltje@10.1.10.37 -s coder0-0');
  });

  it('fails closed for an unknown peer with exact-name discovery guidance', () => {
    const result = resolvePeerCommand(roster(), 'enhancer', 'invented-host', 'w-jarvis', send);
    expect(result.ok).toBe(false);
    expect(result.command).toBe('');
    expect(result.error).toContain('invented-host');
    expect(result.error).toContain('orchestrator, coder0, coder0-0');
    expect(result.error).toContain('mosaic agent comms-block enhancer');
    expect(result.error).not.toContain('tmux ls');
  });
});

describe('readFleetCommsBlock — spawned-agent context', () => {
  let home: string;
  beforeEach(() => {
    home = mkdtempSync(join(tmpdir(), 'mosaic-comms-'));
    mkdirSync(join(home, 'fleet'), { recursive: true });
    mkdirSync(join(home, 'tools', 'tmux'), { recursive: true });
    writeFileSync(join(home, 'fleet', 'roster.yaml'), ROSTER);
    const helper = join(home, 'tools', 'tmux', 'agent-send.sh');
    writeFileSync(helper, '#!/bin/sh\n');
    chmodSync(helper, 0o755);
  });
  afterEach(() => rmSync(home, { recursive: true, force: true }));

  it('uses the authoritative self host and global socket from the shared roster resolver', () => {
    const result = readFleetCommsBlock(home, 'enhancer', 'process-host-must-not-win');
    expect(result.ok).toBe(true);
    expect(result.output).toContain('Host: `w-jarvis`');
    expect(result.output).toContain('tmux socket: `mosaic-fleet`');
    expect(result.output).toContain('-L mosaic-fleet -s orchestrator');
  });

  it('fails closed for a requested fleet identity that is absent', () => {
    const result = readFleetCommsBlock(home, 'stranger', 'w-jarvis');
    expect(result.ok).toBe(false);
    expect(result.output).toBe('');
    expect(result.error).toContain('Known exact names');
  });

  it('resolves a supported JSON-only installed roster', () => {
    rmSync(join(home, 'fleet', 'roster.yaml'));
    writeFileSync(
      join(home, 'fleet', 'roster.json'),
      JSON.stringify({
        version: 1,
        transport: 'tmux',
        tmux: { socket_name: 'mosaic-fleet' },
        agents: [
          { name: 'enhancer', runtime: 'claude', class: 'enhancer', host: 'w-jarvis' },
          { name: 'orchestrator', runtime: 'claude', class: 'orchestrator', host: 'w-jarvis' },
        ],
      }),
    );
    const result = readFleetCommsBlock(home, 'enhancer', 'process-host-must-not-win');
    expect(result.ok).toBe(true);
    expect(result.output).toContain('-L mosaic-fleet -s orchestrator');
  });

  it('fails closed on a YAML I/O error instead of falling back to JSON', () => {
    rmSync(join(home, 'fleet', 'roster.yaml'));
    mkdirSync(join(home, 'fleet', 'roster.yaml'));
    writeFileSync(
      join(home, 'fleet', 'roster.json'),
      JSON.stringify({
        version: 1,
        transport: 'tmux',
        agents: [{ name: 'enhancer', runtime: 'claude', class: 'enhancer' }],
      }),
    );
    const result = readFleetCommsBlock(home, 'enhancer', 'w-jarvis');
    expect(result.ok).toBe(false);
    expect(result.error).toContain('invalid fleet roster at');
    expect(result.error).toContain('roster.yaml');
  });

  it.each([
    ['missing', () => rmSync(join(home, 'tools', 'tmux', 'agent-send.sh'))],
    [
      'directory',
      () => {
        rmSync(join(home, 'tools', 'tmux', 'agent-send.sh'));
        mkdirSync(join(home, 'tools', 'tmux', 'agent-send.sh'));
      },
    ],
    [
      'symlink',
      () => {
        const helper = join(home, 'tools', 'tmux', 'agent-send.sh');
        rmSync(helper);
        writeFileSync(join(home, 'real-send.sh'), '#!/bin/sh\n');
        symlinkSync(join(home, 'real-send.sh'), helper);
      },
    ],
    ['non-executable', () => chmodSync(join(home, 'tools', 'tmux', 'agent-send.sh'), 0o644)],
  ])('fails closed for a %s helper with deterministic repair guidance', (_case, mutate) => {
    mutate();
    const result = readFleetCommsBlock(home, 'enhancer', 'w-jarvis');
    expect(result.ok).toBe(false);
    expect(result.output).toBe('');
    expect(result.error).toContain('mosaic update --repair-tools');
    expect(result.error).toContain('no active context or session was rewritten');
  });

  it('does not rewrite the roster while resolving context', () => {
    const path = join(home, 'fleet', 'roster.yaml');
    const before = readFileSync(path, 'utf8');
    readFleetCommsBlock(home, 'enhancer', 'w-jarvis');
    expect(readFileSync(path, 'utf8')).toBe(before);
  });
});

describe('renderToolsContractStatus — non-mutating install drift', () => {
  let home: string;

  beforeEach(() => {
    home = mkdtempSync(join(tmpdir(), 'mosaic-tools-status-'));
    mkdirSync(join(home, 'defaults'), { recursive: true });
    writeFileSync(
      join(home, 'defaults', 'TOOLS.md'),
      '# authoritative tools\n<!-- fleet-comms-contract: 1 -->\n',
    );
  });
  afterEach(() => rmSync(home, { recursive: true, force: true }));

  it('uses the supported repair command when installed TOOLS.md is missing', () => {
    const status = renderToolsContractStatus(home);
    expect(status).toContain('mosaic update --repair-tools');
    expect(status).not.toContain('--reseed');
    expect(status).toContain('authorized operator');
  });

  it('reports stale preserved content without rewriting it', () => {
    const path = join(home, 'TOOLS.md');
    const stale = '# customized tools\n';
    writeFileSync(path, stale);
    const status = renderToolsContractStatus(home);
    expect(status).toContain('fleet-comms-contract: 1');
    expect(status).toContain('digest-qualified backup');
    expect(status).toContain('mosaic update --repair-tools');
    expect(status).toContain('active context was not rewritten');
    expect(readFileSync(path, 'utf8')).toBe(stale);
  });

  it('does not accept marker-only customized content as current', () => {
    const path = join(home, 'TOOLS.md');
    writeFileSync(path, '<!-- fleet-comms-contract: 1 -->\ncorrupt\n');
    expect(renderToolsContractStatus(home)).toContain('does not byte-match');
  });

  it('rejects markerless byte-equal source and installed content', () => {
    const content = '# markerless but equal\n';
    writeFileSync(join(home, 'defaults', 'TOOLS.md'), content);
    writeFileSync(join(home, 'TOOLS.md'), content);
    const status = renderToolsContractStatus(home);
    expect(status).toContain('source contract');
    expect(status).toContain('does not declare the expected');
  });

  it.each([
    ['source', join('defaults', 'TOOLS.md')],
    ['installed', 'TOOLS.md'],
  ])('rejects a wrong contract version in %s content', (_case, relativePath) => {
    const current = '# authoritative tools\n<!-- fleet-comms-contract: 1 -->\n';
    writeFileSync(join(home, 'TOOLS.md'), current);
    writeFileSync(join(home, 'defaults', 'TOOLS.md'), current);
    writeFileSync(join(home, relativePath), current.replace('contract: 1', 'contract: 2'));
    expect(renderToolsContractStatus(home)).not.toBe('');
  });

  it('treats installed TOOLS.md symlinks as stale without following or rewriting them', () => {
    const external = join(home, 'external-tools.md');
    const externalContent = '# external\n<!-- fleet-comms-contract: 1 -->\n';
    writeFileSync(external, externalContent);
    symlinkSync(external, join(home, 'TOOLS.md'));

    const status = renderToolsContractStatus(home);

    expect(status).toContain('unavailable');
    expect(status).toContain('mosaic update --repair-tools');
    expect(readFileSync(external, 'utf8')).toBe(externalContent);
  });

  it('treats source TOOLS.md symlinks as unavailable without following them', () => {
    const external = join(home, 'external-source.md');
    const content = '# authoritative tools\n<!-- fleet-comms-contract: 1 -->\n';
    writeFileSync(external, content);
    rmSync(join(home, 'defaults', 'TOOLS.md'));
    symlinkSync(external, join(home, 'defaults', 'TOOLS.md'));
    writeFileSync(join(home, 'TOOLS.md'), content);

    const status = renderToolsContractStatus(home);

    expect(status).toContain('source contract');
    expect(status).toContain('unavailable');
    expect(readFileSync(external, 'utf8')).toBe(content);
  });

  it('accepts byte-equal bounded source and installed contracts', () => {
    const source = readFileSync(join(home, 'defaults', 'TOOLS.md'), 'utf8');
    writeFileSync(join(home, 'TOOLS.md'), source);
    expect(renderToolsContractStatus(home)).toBe('');
  });
});

describe('resolveCommsBlock — mosaic agent comms-block', () => {
  let home: string;
  beforeEach(() => {
    home = mkdtempSync(join(tmpdir(), 'mosaic-commsblk-'));
    mkdirSync(join(home, 'fleet'), { recursive: true });
    mkdirSync(join(home, 'tools', 'tmux'), { recursive: true });
    writeFileSync(join(home, 'fleet', 'roster.yaml'), ROSTER);
    const helper = join(home, 'tools', 'tmux', 'agent-send.sh');
    writeFileSync(helper, '#!/bin/sh\n');
    chmodSync(helper, 0o755);
  });
  afterEach(() => rmSync(home, { recursive: true, force: true }));

  it('returns the exact contract for a roster member', () => {
    const result = resolveCommsBlock(home, 'enhancer');
    expect(result.ok).toBe(true);
    expect(result.output).toContain('Host: `w-jarvis`');
    expect(result.error).toBeUndefined();
  });

  it('fails loud and lists known exact names for a non-member', () => {
    const result = resolveCommsBlock(home, 'stranger');
    expect(result.ok).toBe(false);
    expect(result.output).toBe('');
    expect(result.error).toContain('stranger');
    expect(result.error).toContain('orchestrator');
    expect(result.error).toContain('enhancer');
  });

  it('fails loud when no roster exists', () => {
    const noRoster = mkdtempSync(join(tmpdir(), 'mosaic-noroster-'));
    mkdirSync(join(noRoster, 'tools', 'tmux'), { recursive: true });
    const helper = join(noRoster, 'tools', 'tmux', 'agent-send.sh');
    writeFileSync(helper, '#!/bin/sh\n');
    chmodSync(helper, 0o755);
    const result = resolveCommsBlock(noRoster, 'orchestrator');
    expect(result.ok).toBe(false);
    expect(result.error).toContain('no fleet roster');
    rmSync(noRoster, { recursive: true, force: true });
  });

  it('fails loud for a missing role argument', () => {
    const result = resolveCommsBlock(home, undefined);
    expect(result.ok).toBe(false);
    expect(result.error).toContain('requires');
  });
});
