import { describe, expect, it } from 'vitest'; import { decideOpenCodeRuntimeDeliveryAdvisory, OPENCODE_RUNTIME_DELIVERY_GENERIC_PROOF_GRACE_MS, } from '../../../../src/main/services/team/opencode/delivery/OpenCodeRuntimeDeliveryAdvisoryPolicy'; import type { OpenCodePromptDeliveryLedgerRecord } from '../../../../src/main/services/team/opencode/delivery/OpenCodePromptDeliveryLedger'; function makeRecord( overrides: Partial ): OpenCodePromptDeliveryLedgerRecord { const now = '2026-05-09T12:00:00.000Z'; return { id: 'opencode-prompt:test', teamName: 'team', memberName: 'jack', laneId: 'secondary:opencode:jack', runId: 'run-1', runtimeSessionId: 'session-1', inboxMessageId: 'msg-1', inboxTimestamp: now, source: 'ui-send', messageKind: null, replyRecipient: 'user', actionMode: null, taskRefs: [], payloadHash: 'sha256:test', status: 'failed_terminal', responseState: 'empty_assistant_turn', attempts: 3, maxAttempts: 3, acceptanceUnknown: false, nextAttemptAt: null, lastAttemptAt: now, lastObservedAt: now, acceptedAt: now, respondedAt: now, failedAt: now, 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: 'empty_assistant_turn', diagnostics: ['empty_assistant_turn'], createdAt: now, updatedAt: now, ...overrides, }; } describe('OpenCodeRuntimeDeliveryAdvisoryPolicy', () => { it('defers fresh generic terminal failures for proof observation', () => { const record = makeRecord({}); const decision = decideOpenCodeRuntimeDeliveryAdvisory({ record, now: Date.parse(record.failedAt!) + 1_000, }); expect(decision).toMatchObject({ action: 'defer', reasonCode: 'backend_error', nextReviewAt: new Date( Date.parse(record.failedAt!) + OPENCODE_RUNTIME_DELIVERY_GENERIC_PROOF_GRACE_MS ).toISOString(), }); }); it('surfaces action-required failures immediately', () => { const record = makeRecord({ responseState: 'permission_blocked', lastReason: 'authentication_failed', diagnostics: ['authentication_failed'], }); expect( decideOpenCodeRuntimeDeliveryAdvisory({ record, now: Date.parse(record.failedAt!) + 1_000, }) ).toMatchObject({ action: 'surface', severity: 'error', reasonCode: 'auth_error', }); }); it('surfaces disk-full delivery failures immediately', () => { const record = makeRecord({ responseState: 'empty_assistant_turn', lastReason: 'empty_assistant_turn', diagnostics: [ "OpenCode message bridge failed: ENOSPC: no space left on device, open '/tmp/.auth.json.tmp'", 'Latest assistant message msg_1 failed with MessageAbortedError - Aborted', 'empty_assistant_turn', ], }); expect( decideOpenCodeRuntimeDeliveryAdvisory({ record, now: Date.parse(record.failedAt!) + 1_000, }) ).toMatchObject({ action: 'surface', severity: 'error', reasonCode: 'filesystem_error', reason: 'Local disk is full (ENOSPC). Free disk space and retry OpenCode delivery.', }); }); it('suppresses generic retryable tool errors before terminal failure', () => { const record = makeRecord({ status: 'failed_retryable', responseState: 'tool_error', failedAt: null, nextAttemptAt: '2026-05-09T12:00:30.000Z', lastReason: 'opencode bridge command timed out', diagnostics: ['opencode bridge command timed out'], }); expect( decideOpenCodeRuntimeDeliveryAdvisory({ record, now: Date.parse(record.updatedAt) + 1_000, }) ).toMatchObject({ action: 'suppress' }); }); it('surfaces permission-blocked retryable failures before terminal failure', () => { const record = makeRecord({ status: 'failed_retryable', responseState: 'permission_blocked', failedAt: null, nextAttemptAt: '2026-05-09T12:00:30.000Z', lastReason: 'authentication_failed', diagnostics: ['authentication_failed'], }); expect( decideOpenCodeRuntimeDeliveryAdvisory({ record, now: Date.parse(record.updatedAt) + 1_000, }) ).toMatchObject({ action: 'surface', severity: 'error', reasonCode: 'auth_error', }); }); it('suppresses terminal failures when visible proof already exists', () => { const record = makeRecord({}); expect( decideOpenCodeRuntimeDeliveryAdvisory({ record, proof: { visibleReplyAt: Date.parse(record.failedAt!) + 1_000, visibleReplyMessageId: 'reply-1', visibleReplyInbox: 'user', }, now: Date.parse(record.failedAt!) + OPENCODE_RUNTIME_DELIVERY_GENERIC_PROOF_GRACE_MS + 1, }) ).toMatchObject({ action: 'suppress' }); }); });