Files
approved-plan-continuity-ha…/plugins/continuity/test/continuity.evaluator.test.mjs

98 lines
2.5 KiB
JavaScript

import assert from 'node:assert/strict';
import {
buildContinuityGateBlock,
evaluateContinuity,
hasValidDispatchReceipt,
receiptMatchesPayload,
} from '../src/continuity/evaluator.mjs';
function test(name, fn) {
try {
fn();
console.log(`ok - ${name}`);
} catch (error) {
console.error(`not ok - ${name}`);
throw error;
}
}
const validReceipt = {
planId: 'plan-1',
currentTask: 'task-7',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
dispatchedAt: '2026-04-24T09:00:00.000Z',
};
test('recognizes minimum valid dispatch receipt', () => {
assert.equal(hasValidDispatchReceipt(validReceipt), true);
});
test('matches payload against valid receipt', () => {
const payload = {
planId: 'plan-1',
currentTask: 'task-7',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
};
assert.equal(receiptMatchesPayload(payload, validReceipt), true);
});
test('fails when completion has next action and no receipt', () => {
const result = evaluateContinuity({
planId: 'plan-1',
currentTask: 'task-7',
taskState: 'complete',
nextDerivedAction: { type: 'message_subagent' },
replyClosureState: 'completed',
dispatchReceipt: null,
});
assert.equal(result.ok, false);
assert.equal(result.reason, 'missing_dispatch_receipt');
});
test('fails auto-next boundary without dispatch', () => {
const result = evaluateContinuity({
planId: 'plan-1',
currentTask: 'task-8',
taskState: 'complete',
nextTaskKnown: true,
sameApprovedPlan: true,
taskBoundaryStop: true,
nextTaskId: 'task-9',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
replyClosureState: 'completed',
dispatchReceipt: null,
});
assert.equal(result.ok, false);
assert.equal(result.reason, 'missing_auto_next_dispatch');
});
test('passes allowed terminal state', () => {
const result = evaluateContinuity({
planId: 'plan-1',
currentTask: 'task-7',
taskState: 'complete',
nextDerivedAction: { type: 'message_subagent' },
replyClosureState: 'waiting_user',
dispatchReceipt: null,
});
assert.equal(result.ok, true);
});
test('renders hard-gate block', () => {
const text = buildContinuityGateBlock({
ok: false,
status: 'continuity_failure',
verdict: 'continuity_failure',
reason: 'missing_dispatch_receipt',
});
assert.match(text, /APPROVED_PLAN_CONTINUITY_GATE/);
assert.match(text, /HARD_GATE/);
});
console.log('continuity.evaluator.test.mjs PASS');