feat: add governance evaluator and decision runner skeleton
This commit is contained in:
81
plugins/reporting-governance/test/decision-runner.test.mjs
Normal file
81
plugins/reporting-governance/test/decision-runner.test.mjs
Normal file
@@ -0,0 +1,81 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { planDecisionExecution } from '../src/core/decision-runner.mjs';
|
||||
|
||||
const baseCapabilityDescriptor = {
|
||||
capabilities: {
|
||||
enforcement: {
|
||||
block_transition: { supported: true, level: 'partial' },
|
||||
force_checkpoint: { supported: true, level: 'partial' },
|
||||
rewrite_message: { supported: false, level: 'none' },
|
||||
annotate_placeholder: { supported: false, level: 'none' },
|
||||
request_review: { supported: false, level: 'none' },
|
||||
downgrade_status: { supported: false, level: 'none' },
|
||||
escalate: { supported: true, level: 'full' }
|
||||
},
|
||||
notification_path: {
|
||||
truth_model: {
|
||||
delivery_states: ['prepared', 'queued', 'dispatched', 'pending_external_send', 'acked', 'blocked'],
|
||||
ack_requires_proven_send: true,
|
||||
pending_external_send_supported: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test('planDecisionExecution produces runtime-adapter dispatch intent for force_checkpoint', () => {
|
||||
const result = planDecisionExecution({
|
||||
decision: {
|
||||
decision: 'force_checkpoint',
|
||||
policy_id: 'no-silence.missed-checkpoint',
|
||||
severity: 'high',
|
||||
reason: 'checkpoint overdue',
|
||||
rewritten_message: 'Required update.',
|
||||
suggested_status: 'in_progress',
|
||||
required_actions: [
|
||||
{ action: 'notify_operator', target: 'operator_channel', mandatory: true },
|
||||
{ action: 'emit_event', target: 'event_stream', mandatory: true }
|
||||
],
|
||||
operator_notice: {
|
||||
required: true,
|
||||
channel: 'telegram',
|
||||
urgency: 'high',
|
||||
message: 'Required update.',
|
||||
deadline: '2026-01-01T00:00:00.000Z'
|
||||
}
|
||||
},
|
||||
capabilityDescriptor: baseCapabilityDescriptor
|
||||
});
|
||||
|
||||
assert.equal(result.receipt.delivery_state, 'pending_external_send');
|
||||
assert.deepEqual(result.enforcement_intent.runtime_adapter_required, ['notify_operator']);
|
||||
assert.ok(result.receipt.notes.some((note) => note.includes('runtime-adapter responsibility')));
|
||||
});
|
||||
|
||||
test('planDecisionExecution truthfully blocks unsupported package action paths', () => {
|
||||
const result = planDecisionExecution({
|
||||
decision: {
|
||||
decision: 'downgrade_status',
|
||||
policy_id: 'verified-completion-only.insufficient-evidence',
|
||||
severity: 'high',
|
||||
reason: 'completion evidence too weak',
|
||||
rewritten_message: null,
|
||||
suggested_status: 'pending_verification',
|
||||
required_actions: [
|
||||
{
|
||||
action: 'set_status',
|
||||
target: 'task_record',
|
||||
mandatory: true,
|
||||
details: { to: 'pending_verification' }
|
||||
}
|
||||
],
|
||||
operator_notice: null
|
||||
},
|
||||
capabilityDescriptor: baseCapabilityDescriptor
|
||||
});
|
||||
|
||||
assert.equal(result.receipt.status, 'degraded');
|
||||
assert.equal(result.enforcement_intent.planned_actions.length, 0);
|
||||
assert.equal(result.enforcement_intent.blocked_actions[0].action, 'set_status');
|
||||
});
|
||||
143
plugins/reporting-governance/test/policy-evaluator.test.mjs
Normal file
143
plugins/reporting-governance/test/policy-evaluator.test.mjs
Normal file
@@ -0,0 +1,143 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { evaluatePolicyPack, evaluatePolicies } from '../src/core/policy-evaluator.mjs';
|
||||
|
||||
const capabilityDescriptor = {
|
||||
capabilities: {
|
||||
enforcement: {
|
||||
block_transition: { supported: true, level: 'partial' },
|
||||
force_checkpoint: { supported: true, level: 'partial' },
|
||||
rewrite_message: { supported: false, level: 'none' },
|
||||
annotate_placeholder: { supported: false, level: 'none' },
|
||||
request_review: { supported: false, level: 'none' },
|
||||
downgrade_status: { supported: false, level: 'none' },
|
||||
escalate: { supported: true, level: 'full' }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const noSilencePack = {
|
||||
metadata: { id: 'no-silence', severity_default: 'high' },
|
||||
spec: {
|
||||
evaluation_mode: 'any_rule_match',
|
||||
rules: [
|
||||
{
|
||||
id: 'no-silence.missed-checkpoint',
|
||||
title: 'Missed checkpoint requires visible recovery',
|
||||
intent: 'Prevent overdue checkpoints from becoming invisible.',
|
||||
triggers: { event_types: ['silence_timeout'] },
|
||||
conditions: {
|
||||
all: [
|
||||
{ fact: 'checkpoint.is_overdue', equals: true }
|
||||
]
|
||||
},
|
||||
decision_output: {
|
||||
decision: 'force_checkpoint',
|
||||
severity: 'high',
|
||||
reason: 'checkpoint overdue triggered forced operator-visible recovery',
|
||||
suggested_status: 'in_progress',
|
||||
required_actions: [
|
||||
{ action: 'notify_operator', target: 'operator_channel', mandatory: true },
|
||||
{ action: 'emit_event', target: 'event_stream', mandatory: true }
|
||||
],
|
||||
operator_notice: {
|
||||
required: true,
|
||||
channel: 'telegram',
|
||||
urgency: 'high',
|
||||
message: 'Required update: checkpoint overdue.',
|
||||
deadline: '2026-01-01T00:00:00.000Z'
|
||||
}
|
||||
},
|
||||
operator_message_templates: {
|
||||
checkpoint_forced: 'Required update: task exceeded allowed silence window.'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const verifiedCompletionPack = {
|
||||
metadata: { id: 'verified-completion-only', severity_default: 'medium' },
|
||||
spec: {
|
||||
evaluation_mode: 'any_rule_match',
|
||||
rules: [
|
||||
{
|
||||
id: 'verified-completion-only.insufficient-evidence',
|
||||
title: 'Unsupported completion is downgraded',
|
||||
intent: 'Completion claims require moderate evidence.',
|
||||
triggers: {
|
||||
event_types: ['task_claimed_complete'],
|
||||
claim_types: ['completion']
|
||||
},
|
||||
conditions: {
|
||||
all: [
|
||||
{ fact: 'evidence.completion_min_quality', equals: false }
|
||||
]
|
||||
},
|
||||
decision_output: {
|
||||
decision: 'downgrade_status',
|
||||
severity: 'high',
|
||||
reason: 'completion evidence does not meet moderate threshold',
|
||||
suggested_status: 'pending_verification',
|
||||
required_actions: [
|
||||
{
|
||||
action: 'set_status',
|
||||
target: 'task_record',
|
||||
mandatory: true,
|
||||
details: { to: 'pending_verification' }
|
||||
},
|
||||
{
|
||||
action: 'append_audit_note',
|
||||
target: 'task_record',
|
||||
mandatory: true,
|
||||
details: { note: 'downgraded unsupported completion' }
|
||||
}
|
||||
],
|
||||
operator_notice: null
|
||||
},
|
||||
operator_message_templates: {
|
||||
status_downgraded: 'Completion claim downgraded to pending verification.'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
test('evaluatePolicyPack returns force_checkpoint for overdue silence event', () => {
|
||||
const result = evaluatePolicyPack({
|
||||
event: {
|
||||
type: 'silence_timeout',
|
||||
payload: { checkpoint_overdue: true }
|
||||
},
|
||||
evidence: [],
|
||||
capabilityDescriptor,
|
||||
policyPack: noSilencePack,
|
||||
context: {
|
||||
signals: ['checkpoint_overdue']
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.decision.decision, 'force_checkpoint');
|
||||
assert.equal(result.decision.policy_id, 'no-silence.missed-checkpoint');
|
||||
assert.equal(result.decision.operator_notice.required, true);
|
||||
assert.equal(result.facts.checkpoint.is_overdue, true);
|
||||
});
|
||||
|
||||
test('evaluatePolicies picks downgrade_status over allow for weak completion claim', () => {
|
||||
const result = evaluatePolicies({
|
||||
event: {
|
||||
type: 'task_claimed_complete',
|
||||
payload: {}
|
||||
},
|
||||
evidence: [
|
||||
{ id: 'ev-1', quality: 'weak', is_new: true }
|
||||
],
|
||||
capabilityDescriptor,
|
||||
policyPacks: [verifiedCompletionPack]
|
||||
});
|
||||
|
||||
assert.equal(result.decision.decision, 'downgrade_status');
|
||||
assert.equal(result.decision.suggested_status, 'pending_verification');
|
||||
assert.equal(result.evaluations[0].decision.policy_id, 'verified-completion-only.insufficient-evidence');
|
||||
});
|
||||
Reference in New Issue
Block a user