const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const vm = require('node:vm') const { historicalPageOnlyReceipts, isHistoricalPageOnlyReceipt, hasSourceReadingProof, recoverySchedulingGate, remainingRecoveryCandidates } = require('./wiki_recovery_scheduler_policy.cjs') const pageResult = { recovery_status: 'wiki_recovered', pending: false, pending_finalize: 0, new_failures: 0, failed_page_spans: 0, span_status: 'done', full_read_status: 'not_verified' } const legacy = historicalPageOnlyReceipts.map(receipt => ({ ...receipt, ...pageResult })) const current = { ...pageResult, tenant_id: 10059, dead_letter_id: 10, attempt_number: 4, pending_op_id: 9999, read_coverage_version: 'wiki-full-read-v1', source_read_complete: true, full_read_status: 'source_processed' } const report = (recoveries, remaining = 21) => ({ recoveries, remaining_not_attempted: remaining }) test('the eleven historical page receipts stay unverified and do not block proven new recovery', () => { const before = structuredClone(legacy) const result = recoverySchedulingGate(report([...legacy, current])) assert.equal(result.state, 'ready_for_next_batch') assert.deepEqual(result.missing_full_read_ids, []) assert.deepEqual(result.historical_full_read_unverified_ids, [2, 5, 6, 15, 16, 17, 28, 30, 31, 32, 33]) assert.equal(result.source_reading_complete, false) assert.deepEqual(legacy, before) }) test('new page recovery without full reading stops before another document can be scheduled', () => { for (const change of [ { full_read_status: 'not_verified' }, { full_read_status: 'in_progress' }, { full_read_status: 'paused_budget' }, { source_read_complete: false }, { source_read_complete: undefined }, { read_coverage_version: 'old-prefix' }, { read_coverage_version: undefined }, ]) { const result = recoverySchedulingGate(report([...legacy, { ...current, ...change }])) assert.equal(result.state, 'stopped_for_review') assert.deepEqual(result.missing_full_read_ids, [10]) assert.deepEqual(result.unresolved_ids, [10]) } }) test('complete candidate and summary windows alone do not establish source reading proof', () => { const row = { ...pageResult, dead_letter_id: 1, read_progress: { candidates: { stage_complete: true, source_windows_complete: true, status: 'complete' }, summary: { stage_complete: true, source_windows_complete: true, status: 'complete' }, } } assert.equal(hasSourceReadingProof(row), false) assert.equal(recoverySchedulingGate(report([row])).state, 'stopped_for_review') }) test('the legacy exemption cannot extend to a new attempt, pending op, source, model or tenant', () => { for (const change of [{ attempt_number: 3 }, { pending_op_id: 9000 }, { tenant_id: 10060 }, { knowledge_id: 'other-doc' }, { knowledge_base_id: 'other-kb' }, { model_id: 'other-model' }, { queued_at: '2026-09-20T00:00:00Z' }]) { const row = { ...legacy[0], ...change } assert.equal(isHistoricalPageOnlyReceipt(row), false) assert.equal(recoverySchedulingGate(report([row])).state, 'stopped_for_review') } }) test('pending reading and finalization wait without prematurely failing coverage', () => { for (const change of [{ pending: true, recovery_status: 'running' }, { pending_finalize: 2 }]) { const row = { ...current, source_read_complete: undefined, full_read_status: 'in_progress', ...change } assert.equal(recoverySchedulingGate(report([...legacy, row])).state, 'waiting_for_current_batch') } }) test('terminal page recovery explicitly retains historical full-reading debt', () => { const result = recoverySchedulingGate(report([...legacy, current], 0)) assert.equal(result.state, 'all_original_pages_recovered') assert.equal(result.source_reading_complete, false) assert.equal(result.historical_full_read_unverified_ids.length, 11) assert.notEqual(result.state, 'all_originals_verified') }) test('terminal state requires source reading proof for every new receipt, including after restart', () => { const rows = [...legacy, { ...current, full_read_status: 'not_verified', source_read_complete: undefined }] for (const remaining of [0, 21]) { const serialized = JSON.parse(JSON.stringify(report(rows, remaining))) assert.equal(recoverySchedulingGate(serialized).state, 'stopped_for_review') } const complete = recoverySchedulingGate(report([current], 0)) assert.equal(complete.state, 'all_original_pages_and_source_reading_complete') assert.equal(complete.source_reading_complete, true) }) test('historical and newly recovered records are never candidates for automatic requeue', () => { const state = report([...legacy, current]) state.candidates = [...legacy.map(row => ({ id: row.dead_letter_id, eligible: true, chunks: 1, pending: 0 })), { id: 10, eligible: true, chunks: 624, pending: 0 }, { id: 1, eligible: true, chunks: 100, pending: 0 }, { id: 3, eligible: false, chunks: 100, pending: 0 }, { id: 4, eligible: true, chunks: 100, pending: 1 }] assert.deepEqual(remainingRecoveryCandidates(state).map(row => row.id), [1]) }) test('full-reading proof never bypasses page failures or an unknown settled outcome', () => { for (const change of [{ recovery_status: 'partial' }, { recovery_status: 'unexpected' }, { span_status: 'failed' }, { new_failures: 1 }, { failed_page_spans: 1 }]) { assert.equal(recoverySchedulingGate(report([{ ...current, ...change }], 0)).state, 'stopped_for_review') } }) // Evaluate the actual runner with all I/O replaced. No recovery command, // filesystem write, timer or model call can escape this fixture. function runIsolatedScheduler(reports, hasReview = true) { const directory = path.resolve(__dirname, '../../acceptance/wiki-deadletter-recovery-20260919') const reportFile = path.join(directory, 'isolated-status.json') const states = [] const commands = [] let inspected let unlocked = false const context = { __dirname, __filename: path.join(__dirname, 'run_wiki_deadletter_recovery.cjs'), process: { argv: ['node', 'run_wiki_deadletter_recovery.cjs', '--apply', '--review-directory=/reviewed'], execPath: 'isolated-node', pid: 123, on() {} }, console: { log(value) { states.push(JSON.parse(value)) }, error() {} }, setTimeout() { throw new Error('Unexpected scheduler timer') }, require(name) { if (name === 'node:fs') return { mkdirSync() {}, openSync() { return 1 }, closeSync() {}, writeFileSync() {}, renameSync() {}, appendFileSync() {}, existsSync(file) { assert.match(file, /^\/reviewed\/dead-letter-\d+-attempt-1\.json$/); return hasReview }, readFileSync(file) { assert.equal(file, reportFile); return JSON.stringify(inspected) }, unlinkSync(file) { assert.equal(path.basename(file), 'runner.lock'); unlocked = true }, } if (name === 'node:child_process') return { execFileSync(command, args) { assert.equal(command, 'isolated-node') assert.equal(path.basename(args[0]), 'recover_wiki_deadletters.cjs') commands.push(args.slice(1)) inspected = reports[commands.length - 1] assert.ok(inspected, 'Unexpected recovery command') return JSON.stringify({ report_file: reportFile }) }, spawn() { throw new Error('Unexpected subprocess') }, } if (name === './wiki_recovery_scheduler_policy.cjs') return require(name) if (name === 'node:path' || name === 'node:assert/strict') return require(name) throw new Error(`Unexpected runner dependency: ${name}`) }, } vm.runInNewContext(fs.readFileSync(context.__filename, 'utf8'), context) return { states, commands, unlocked } } test('actual runner stops before apply when a new result lacks source proof', () => { const fixture = runIsolatedScheduler([report([...legacy, { ...current, source_read_complete: false }])]) assert.equal(fixture.commands.length, 1) assert.equal(fixture.commands[0].length, 0) assert.equal(fixture.states.at(-1).state, 'stopped_for_review') assert.deepEqual(fixture.states.at(-1).missing_full_read_ids, [10]) assert.equal(fixture.unlocked, true) }) test('actual runner queues only an untouched original and preserves historical debt on completion', () => { const next = { ...current, dead_letter_id: 1, attempt_number: 1, pending_op_id: 10000 } const initial = { ...report([...legacy, current], 1), candidates: [ { id: 2, eligible: true, chunks: 100, pending: 0 }, { id: 10, eligible: true, chunks: 624, pending: 0 }, { id: 1, eligible: true, chunks: 100, pending: 0 }, ] } const complete = report([...legacy, current, next], 0) const fixture = runIsolatedScheduler([initial, complete, complete]) assert.deepEqual(fixture.commands.map(args => Array.from(args)), [[], ['--apply', '--ids=1', '--review-file=/reviewed/dead-letter-1-attempt-1.json'], []]) assert.equal(fixture.states.at(-1).state, 'all_original_pages_recovered') assert.equal(fixture.states.at(-1).source_reading_complete, false) assert.equal(fixture.states.at(-1).historical_full_read_unverified_ids.length, 11) assert.equal(fixture.states.at(-1).newly_enqueued, 1) assert.equal(fixture.unlocked, true) }) test('runner stops before enqueueing an untouched record without a durable review', () => { const initial = { ...report([...legacy, current], 1), candidates: [{ id: 1, eligible: true, chunks: 100, pending: 0 }] } const fixture = runIsolatedScheduler([initial], false) assert.equal(fixture.commands.length, 1) assert.equal(fixture.states.at(-1).state, 'stopped_for_review') assert.match(fixture.states.at(-1).reason, /request limit/) assert.equal(fixture.unlocked, true) })