import { promises as fs } from 'fs';
import * as os from 'os';
import * as path from 'path';

import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import {
  createOpenCodePromptDeliveryLedgerStore,
  hashOpenCodePromptDeliveryPayload,
  isOpenCodePromptDeliveryAttemptDue,
} from '@main/services/team/opencode/delivery/OpenCodePromptDeliveryLedger';

describe('OpenCodePromptDeliveryLedger', () => {
  let tempDir = '';
  const corruptionCases: Array<[string, (record: Record<string, unknown>) => void]> = [
    [
      'unknown delivery status',
      (record) => {
        record.status = 'quietly_broken';
      },
    ],
    [
      'unknown response state',
      (record) => {
        record.responseState = 'assistant_maybe_replied';
      },
    ],
    [
      'invalid task reference shape',
      (record) => {
        record.taskRefs = [{ taskId: 'task-1', displayId: '#1' }];
      },
    ],
    [
      'invalid diagnostic array',
      (record) => {
        record.diagnostics = ['ok', 42];
      },
    ],
    [
      'invalid visible reply correlation',
      (record) => {
        record.visibleReplyCorrelation = 'guessed_from_text';
      },
    ],
  ];

  beforeEach(async () => {
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'opencode-prompt-ledger-'));
  });

  afterEach(async () => {
    if (tempDir) {
      await fs.rm(tempDir, { recursive: true, force: true });
    }
  });

  function createStore() {
    return createOpenCodePromptDeliveryLedgerStore({
      filePath: path.join(tempDir, 'opencode-prompt-delivery-ledger.json'),
      clock: () => new Date('2026-04-25T10:00:00.000Z'),
    });
  }

  function ledgerPath() {
    return path.join(tempDir, 'opencode-prompt-delivery-ledger.json');
  }

  async function writeCorruptedLedgerRecord(
    mutate: (record: Record<string, unknown>) => void
  ): Promise<ReturnType<typeof createStore>> {
    const store = createStore();
    await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-corrupt',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      actionMode: 'ask',
      taskRefs: [],
      payloadHash: 'sha256:corrupt',
      now: '2026-04-25T10:00:00.000Z',
    });

    const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
      data: Record<string, unknown>[];
    };
    mutate(envelope.data[0]);
    await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
    return store;
  }

  it('is idempotent for the same inbox message and payload hash', async () => {
    const store = createStore();
    const payloadHash = hashOpenCodePromptDeliveryPayload({
      text: 'Please answer',
      replyRecipient: 'user',
      actionMode: 'ask',
      source: 'watcher',
    });

    const first = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      actionMode: 'ask',
      taskRefs: [],
      payloadHash,
      now: '2026-04-25T10:00:00.000Z',
    });
    const second = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      actionMode: 'ask',
      taskRefs: [],
      payloadHash,
      now: '2026-04-25T10:00:30.000Z',
    });

    expect(second.id).toBe(first.id);
    expect(second.attempts).toBe(0);
    await expect(store.list()).resolves.toHaveLength(1);
  });

  it('upgrades legacy pending records with message kind without changing payload identity', async () => {
    const store = createStore();
    const payloadHash = hashOpenCodePromptDeliveryPayload({
      text: 'Work sync check',
      replyRecipient: 'team-lead',
      actionMode: 'do',
      source: 'watcher',
    });

    const legacy = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-work-sync',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'team-lead',
      actionMode: 'do',
      taskRefs: [],
      payloadHash,
      now: '2026-04-25T10:00:00.000Z',
    });
    const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
      data: Record<string, unknown>[];
    };
    delete envelope.data[0].messageKind;
    await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');

    const upgraded = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-work-sync',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      messageKind: 'member_work_sync_nudge',
      replyRecipient: 'team-lead',
      actionMode: 'do',
      taskRefs: [],
      payloadHash,
      now: '2026-04-25T10:00:30.000Z',
    });

    expect(upgraded.id).toBe(legacy.id);
    expect(upgraded.messageKind).toBe('member_work_sync_nudge');
    expect(upgraded.payloadHash).toBe(payloadHash);
    expect(upgraded.attempts).toBe(0);
    await expect(store.list()).resolves.toHaveLength(1);
  });

  it.each(corruptionCases)('rejects corrupted persisted records with %s', async (_name, mutate) => {
    const store = await writeCorruptedLedgerRecord(mutate);

    await expect(store.list()).rejects.toMatchObject({
      reason: 'invalid_data',
    });
    await expect(fs.readdir(tempDir)).resolves.toContain('opencode-prompt-delivery-ledger.json');
    expect((await fs.readdir(tempDir)).some((name) => name.includes('.invalid_data.'))).toBe(true);
  });

  it('marks same logical delivery with a different payload hash terminal', async () => {
    const store = createStore();
    const original = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:first',
      now: '2026-04-25T10:00:00.000Z',
    });

    const mismatch = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:second',
      now: '2026-04-25T10:00:30.000Z',
    });

    expect(mismatch.id).toBe(original.id);
    expect(mismatch.status).toBe('failed_terminal');
    expect(mismatch.lastReason).toBe('opencode_prompt_delivery_payload_mismatch');
    expect(mismatch.diagnostics.join('\n')).toContain('payload hash does not match');
    await expect(store.list()).resolves.toHaveLength(1);
  });

  it('keeps ack-only destination proof nonterminal and due retry checks deterministic', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:first',
      now: '2026-04-25T10:00:00.000Z',
    });

    const ackOnly = await store.applyDestinationProof({
      id: record.id,
      visibleReplyInbox: 'user',
      visibleReplyMessageId: 'reply-1',
      visibleReplyCorrelation: 'relayOfMessageId',
      semanticallySufficient: false,
      observedAt: '2026-04-25T10:00:01.000Z',
    });
    expect(ackOnly.status).toBe('pending');
    expect(ackOnly.responseState).toBe('responded_visible_message');
    expect(ackOnly.lastReason).toBe('visible_reply_ack_only_still_requires_answer');

    const scheduled = await store.markNextAttemptScheduled({
      id: record.id,
      status: 'retry_scheduled',
      nextAttemptAt: '2026-04-25T10:00:30.000Z',
      reason: 'visible_reply_ack_only_still_requires_answer',
      scheduledAt: '2026-04-25T10:00:02.000Z',
    });
    expect(
      isOpenCodePromptDeliveryAttemptDue(scheduled, Date.parse('2026-04-25T10:00:29.000Z'))
    ).toBe(false);
    expect(
      isOpenCodePromptDeliveryAttemptDue(scheduled, Date.parse('2026-04-25T10:00:30.000Z'))
    ).toBe(true);
  });

  it('preserves missing taskRefs as the pending reason for insufficient destination proof', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-taskrefs',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:taskrefs',
      now: '2026-04-25T10:00:00.000Z',
    });

    const missingTaskRefs = await store.applyDestinationProof({
      id: record.id,
      visibleReplyInbox: 'user',
      visibleReplyMessageId: 'reply-taskrefs',
      visibleReplyCorrelation: 'relayOfMessageId',
      semanticallySufficient: false,
      diagnostics: ['visible_reply_missing_task_refs_after_merge'],
      observedAt: '2026-04-25T10:00:01.000Z',
    });

    expect(missingTaskRefs.status).toBe('pending');
    expect(missingTaskRefs.responseState).toBe('responded_visible_message');
    expect(missingTaskRefs.lastReason).toBe('visible_reply_missing_task_refs');
    expect(missingTaskRefs.diagnostics).toContain('visible_reply_missing_task_refs_after_merge');
  });

  it('records empty assistant delivery results as unanswered and stores plain text previews', async () => {
    const store = createStore();
    const unanswered = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-empty',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:empty',
      now: '2026-04-25T10:00:00.000Z',
    });

    const emptyResult = await store.applyDeliveryResult({
      id: unanswered.id,
      accepted: true,
      attempted: true,
      sessionId: 'oc-session-1',
      runtimePromptMessageId: 'msg_prompt_1',
      responseObservation: {
        state: 'empty_assistant_turn',
        deliveredUserMessageId: 'oc-user-1',
        assistantMessageId: 'oc-assistant-1',
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: null,
        reason: 'empty_assistant_turn',
      },
      now: '2026-04-25T10:00:05.000Z',
    });

    expect(emptyResult.status).toBe('unanswered');
    expect(emptyResult.responseState).toBe('empty_assistant_turn');
    expect(emptyResult.attempts).toBe(1);
    expect(emptyResult.runtimeSessionId).toBe('oc-session-1');
    expect(emptyResult.runtimePromptMessageId).toBe('msg_prompt_1');

    const noAssistant = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-no-assistant',
      inboxTimestamp: '2026-04-25T09:59:05.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:no-assistant',
      now: '2026-04-25T10:00:06.000Z',
    });
    const noAssistantResult = await store.applyDeliveryResult({
      id: noAssistant.id,
      accepted: true,
      attempted: true,
      responseObservation: {
        state: 'prompt_delivered_no_assistant_message',
        deliveredUserMessageId: 'oc-user-no-assistant',
        assistantMessageId: null,
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: null,
        reason: 'prompt_delivered_no_assistant_message',
      },
      now: '2026-04-25T10:00:07.000Z',
    });

    expect(noAssistantResult.status).toBe('unanswered');
    expect(noAssistantResult.responseState).toBe('prompt_delivered_no_assistant_message');

    const plain = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-plain',
      inboxTimestamp: '2026-04-25T09:59:10.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:plain',
      now: '2026-04-25T10:00:10.000Z',
    });
    const observed = await store.applyObservation({
      id: plain.id,
      responseObservation: {
        state: 'responded_plain_text',
        deliveredUserMessageId: 'oc-user-2',
        assistantMessageId: 'oc-assistant-2',
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: 'Понял',
        reason: null,
      },
      observedAt: '2026-04-25T10:00:15.000Z',
    });

    expect(observed.status).toBe('responded');
    expect(observed.observedAssistantPreview).toBe('Понял');
  });

  it('tracks accepted runtime prompt ids without double-counting recovered command status', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-accepted',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:accepted',
      now: '2026-04-25T10:00:00.000Z',
    });

    const firstAccepted = await store.applyDeliveryResult({
      id: record.id,
      accepted: true,
      attempted: true,
      sessionId: 'oc-session-1',
      runtimePromptMessageId: 'msg_prompt_1',
      deliveryAttemptId: 'attempt-1',
      now: '2026-04-25T10:00:05.000Z',
    });
    expect(firstAccepted).toMatchObject({
      status: 'accepted',
      attempts: 1,
      runtimePromptMessageId: 'msg_prompt_1',
      lastRuntimePromptMessageId: 'msg_prompt_1',
      lastDeliveryAttemptIdWithAcceptedPrompt: 'attempt-1',
    });
    expect(firstAccepted.runtimePromptMessageIds).toEqual(['msg_prompt_1']);

    const recoveredSamePrompt = await store.applyDeliveryResult({
      id: record.id,
      accepted: true,
      attempted: true,
      sessionId: 'oc-session-1',
      runtimePromptMessageId: 'msg_prompt_1',
      deliveryAttemptId: 'attempt-1',
      now: '2026-04-25T10:00:06.000Z',
    });
    expect(recoveredSamePrompt.attempts).toBe(1);
    expect(recoveredSamePrompt.runtimePromptMessageIds).toEqual(['msg_prompt_1']);

    const retryAccepted = await store.applyDeliveryResult({
      id: record.id,
      accepted: true,
      attempted: true,
      sessionId: 'oc-session-2',
      runtimePromptMessageId: 'msg_prompt_2',
      deliveryAttemptId: 'attempt-2',
      now: '2026-04-25T10:01:00.000Z',
    });
    expect(retryAccepted.attempts).toBe(2);
    expect(retryAccepted.runtimePromptMessageIds).toEqual(['msg_prompt_1', 'msg_prompt_2']);
    expect(retryAccepted.lastRuntimePromptMessageId).toBe('msg_prompt_2');
  });

  it('keeps schema-1 legacy prompt-id fields compatible and normalizes when touched', async () => {
    const store = createStore();
    const legacy = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-legacy-runtime-prompt',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:legacy-runtime-prompt',
      now: '2026-04-25T10:00:00.000Z',
    });

    const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
      data: Record<string, unknown>[];
    };
    delete envelope.data[0].runtimePromptMessageIds;
    delete envelope.data[0].lastRuntimePromptMessageId;
    delete envelope.data[0].lastDeliveryAttemptIdWithAcceptedPrompt;
    await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');

    await expect(store.list()).resolves.toHaveLength(1);

    const touched = await store.applyDeliveryResult({
      id: legacy.id,
      accepted: true,
      attempted: true,
      runtimePromptMessageId: 'msg_prompt_legacy_touch',
      deliveryAttemptId: 'attempt-legacy-touch',
      now: '2026-04-25T10:00:05.000Z',
    });
    expect(touched.runtimePromptMessageIds).toEqual(['msg_prompt_legacy_touch']);
    expect(touched.lastRuntimePromptMessageId).toBe('msg_prompt_legacy_touch');
    expect(touched.lastDeliveryAttemptIdWithAcceptedPrompt).toBe('attempt-legacy-touch');
  });

  it('accepts task stall remediation message kind across ledger validation', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'task-stall:team-a:jack:task-a',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watchdog',
      messageKind: 'task_stall_remediation',
      replyRecipient: 'team-lead',
      actionMode: 'do',
      payloadHash: 'sha256:task-stall',
      now: '2026-04-25T10:00:00.000Z',
    });

    expect(record.messageKind).toBe('task_stall_remediation');
    await expect(store.list()).resolves.toMatchObject([
      { messageKind: 'task_stall_remediation' },
    ]);
  });

  it('upgrades acceptance-unknown records when exact observation finds the prompt', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-observed-later',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:observed-later',
      now: '2026-04-25T10:00:00.000Z',
    });
    const unknown = await store.markAcceptanceUnknown({
      id: record.id,
      reason: 'opencode_prompt_acceptance_unknown_after_bridge_timeout',
      nextAttemptAt: '2026-04-25T10:01:00.000Z',
      markedAt: '2026-04-25T10:00:45.000Z',
    });
    expect(unknown.acceptanceUnknown).toBe(true);

    const observed = await store.applyObservation({
      id: record.id,
      sessionId: 'oc-session-recovered',
      runtimePromptMessageId: 'msg_prompt_recovered',
      responseObservation: {
        state: 'pending',
        deliveredUserMessageId: 'msg_prompt_recovered',
        assistantMessageId: null,
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: null,
        reason: 'assistant_response_pending',
      },
      observedAt: '2026-04-25T10:00:50.000Z',
    });

    expect(observed.status).toBe('accepted');
    expect(observed.acceptanceUnknown).toBe(false);
    expect(observed.acceptedAt).toBe('2026-04-25T10:00:50.000Z');
    expect(observed.runtimeSessionId).toBe('oc-session-recovered');
    expect(observed.runtimePromptMessageIds).toEqual(['msg_prompt_recovered']);
  });

  it('keeps plain-text responses active until their visible inbox reply is materialized', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-plain-visible',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      actionMode: 'ask',
      taskRefs: [],
      payloadHash: 'sha256:plain-visible',
      now: '2026-04-25T10:00:00.000Z',
    });

    const responded = await store.applyDeliveryResult({
      id: record.id,
      accepted: true,
      attempted: true,
      responseObservation: {
        state: 'responded_plain_text',
        deliveredUserMessageId: 'oc-user-plain',
        assistantMessageId: 'oc-assistant-plain',
        toolCallNames: [],
        visibleMessageToolCallId: null,
        visibleReplyMessageId: null,
        visibleReplyCorrelation: null,
        latestAssistantPreview: 'Concrete visible answer.',
        reason: null,
      },
      now: '2026-04-25T10:00:05.000Z',
    });
    expect(responded.status).toBe('responded');

    await expect(
      store.getActiveForMember({
        teamName: 'team-a',
        memberName: 'jack',
        laneId: 'secondary:opencode:jack',
      })
    ).resolves.toMatchObject({
      id: record.id,
      responseState: 'responded_plain_text',
    });

    const materialized = await store.applyDestinationProof({
      id: record.id,
      visibleReplyInbox: 'user',
      visibleReplyMessageId: 'opencode-plain-reply-1',
      visibleReplyCorrelation: 'plain_assistant_text',
      semanticallySufficient: true,
      observedAt: '2026-04-25T10:00:06.000Z',
    });
    expect(materialized).toMatchObject({
      status: 'responded',
      responseState: 'responded_plain_text',
      visibleReplyCorrelation: 'plain_assistant_text',
    });

    await expect(
      store.getActiveForMember({
        teamName: 'team-a',
        memberName: 'jack',
        laneId: 'secondary:opencode:jack',
      })
    ).resolves.toBeNull();
  });

  it('does not keep responded live deliveries active when no inbox commit is needed', async () => {
    const store = createStore();
    const direct = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'bob',
      laneId: 'secondary:opencode:bob',
      inboxMessageId: 'direct-ui-send',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'ui-send',
      replyRecipient: 'user',
      actionMode: 'ask',
      taskRefs: [],
      payloadHash: 'sha256:direct',
      now: '2026-04-25T10:00:00.000Z',
    });

    const responded = await store.applyDeliveryResult({
      id: direct.id,
      accepted: true,
      attempted: true,
      responseObservation: {
        state: 'responded_visible_message',
        deliveredUserMessageId: 'oc-user-direct',
        assistantMessageId: 'oc-assistant-direct',
        toolCallNames: ['agent-teams_message_send'],
        visibleMessageToolCallId: 'tool-call-direct',
        visibleReplyMessageId: 'reply-direct',
        visibleReplyCorrelation: 'direct_child_message_send',
        latestAssistantPreview: 'I will send the requested update.',
        reason: null,
      },
      now: '2026-04-25T10:00:05.000Z',
    });
    expect(responded.status).toBe('responded');
    expect(responded.inboxReadCommittedAt).toBeNull();

    await expect(
      store.getActiveForMember({
        teamName: 'team-a',
        memberName: 'bob',
        laneId: 'secondary:opencode:bob',
      })
    ).resolves.toBeNull();

    const peer = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'bob',
      laneId: 'secondary:opencode:bob',
      inboxMessageId: 'peer-relay',
      inboxTimestamp: '2026-04-25T10:01:00.000Z',
      source: 'manual',
      replyRecipient: 'jack',
      actionMode: 'delegate',
      taskRefs: [],
      payloadHash: 'sha256:peer',
      now: '2026-04-25T10:01:00.000Z',
    });

    await expect(
      store.getActiveForMember({
        teamName: 'team-a',
        memberName: 'bob',
        laneId: 'secondary:opencode:bob',
      })
    ).resolves.toMatchObject({
      id: peer.id,
      inboxMessageId: 'peer-relay',
    });
  });

  it('lists due nonterminal records in deterministic due order', async () => {
    const store = createStore();
    const first = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:first',
      now: '2026-04-25T10:00:00.000Z',
    });
    const second = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-2',
      inboxTimestamp: '2026-04-25T09:59:10.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:second',
      now: '2026-04-25T10:00:01.000Z',
    });
    await store.markNextAttemptScheduled({
      id: first.id,
      status: 'retry_scheduled',
      nextAttemptAt: '2026-04-25T10:00:20.000Z',
      reason: 'empty_assistant_turn',
      scheduledAt: '2026-04-25T10:00:02.000Z',
    });
    await store.markNextAttemptScheduled({
      id: second.id,
      status: 'retry_scheduled',
      nextAttemptAt: '2026-04-25T10:00:10.000Z',
      reason: 'empty_assistant_turn',
      scheduledAt: '2026-04-25T10:00:02.000Z',
    });

    const dueBefore = await store.listDue({
      teamName: 'team-a',
      now: new Date('2026-04-25T10:00:15.000Z'),
      limit: 10,
    });
    expect(dueBefore.map((record) => record.inboxMessageId)).toEqual(['msg-2']);

    const dueAfter = await store.listDue({
      teamName: 'team-a',
      now: new Date('2026-04-25T10:00:21.000Z'),
      limit: 10,
    });
    expect(dueAfter.map((record) => record.inboxMessageId)).toEqual(['msg-2', 'msg-1']);
  });

  it('rebuilds missing ledger rows as acceptance-unknown retryable records', async () => {
    const store = createStore();
    const record = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'msg-1',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watchdog',
      replyRecipient: 'user',
      payloadHash: 'sha256:first',
      now: '2026-04-25T10:00:00.000Z',
    });

    const rebuilt = await store.markAcceptanceUnknown({
      id: record.id,
      reason: 'opencode_prompt_delivery_ledger_rebuilt_from_unread_inbox',
      nextAttemptAt: '2026-04-25T10:00:00.000Z',
      markedAt: '2026-04-25T10:00:00.000Z',
    });

    expect(rebuilt.status).toBe('failed_retryable');
    expect(rebuilt.acceptanceUnknown).toBe(true);
    expect(rebuilt.responseState).toBe('not_observed');
    expect(rebuilt.lastReason).toBe('opencode_prompt_delivery_ledger_rebuilt_from_unread_inbox');
  });

  it('prunes only terminal records after their retention windows', async () => {
    const store = createStore();
    const responded = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'responded',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:responded',
      now: '2026-04-25T10:00:00.000Z',
    });
    await store.applyDestinationProof({
      id: responded.id,
      visibleReplyInbox: 'user',
      visibleReplyMessageId: 'reply-1',
      visibleReplyCorrelation: 'relayOfMessageId',
      semanticallySufficient: true,
      observedAt: '2026-04-25T10:00:01.000Z',
    });
    await store.markInboxReadCommitted({
      id: responded.id,
      committedAt: '2026-04-25T10:00:02.000Z',
    });

    const failed = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'failed',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:failed',
      now: '2026-04-25T10:00:00.000Z',
    });
    await store.markFailedTerminal({
      id: failed.id,
      reason: 'opencode_runtime_not_active',
      failedAt: '2026-04-25T10:00:03.000Z',
    });

    const active = await store.ensurePending({
      teamName: 'team-a',
      memberName: 'jack',
      laneId: 'secondary:opencode:jack',
      inboxMessageId: 'active',
      inboxTimestamp: '2026-04-25T09:59:00.000Z',
      source: 'watcher',
      replyRecipient: 'user',
      payloadHash: 'sha256:active',
      now: '2026-04-25T10:00:00.000Z',
    });

    await expect(
      store.pruneTerminalRecords({
        now: new Date('2026-04-25T10:00:20.000Z'),
        respondedRetentionMs: 10_000,
        failedRetentionMs: 30_000,
      })
    ).resolves.toEqual({ pruned: 1, remaining: 2 });
    expect((await store.list()).map((record) => record.inboxMessageId).sort()).toEqual([
      active.inboxMessageId,
      failed.inboxMessageId,
    ]);

    await expect(
      store.pruneTerminalRecords({
        now: new Date('2026-04-25T10:00:40.000Z'),
        respondedRetentionMs: 10_000,
        failedRetentionMs: 30_000,
      })
    ).resolves.toEqual({ pruned: 1, remaining: 1 });
    expect((await store.list()).map((record) => record.inboxMessageId)).toEqual([
      active.inboxMessageId,
    ]);
  });
});
