98 lines
2.5 KiB
JavaScript
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');
|