import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { executeRuntimeIntegratedGovernance, createFileDecisionStore } from '../src/index.mjs'; import { validateDecisionRecordArtifact } from '../src/storage/decision-artifact.mjs'; import capabilityDescriptor from '../capabilities/openclaw-watchdog-reference.json' with { type: 'json' }; const packageRoot = path.resolve(import.meta.dirname, '..'); const repoRoot = path.resolve(packageRoot, '..', '..'); 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', 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', 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' } } } ] } }; const strictProfileArtifact = { kind: 'DeploymentProfileArtifact', apiVersion: 'reporting-governance/v1alpha1', metadata: { id: 'strict-manager-mode', runtime: 'openclaw', compatibility_mode: 'strict_envelope', }, spec: { package: { pluginVersion: '0.1.0-mainline' }, policies: { overrides: { checkpoints: { overdueAction: 'force_checkpoint' } } }, notifications: { operatorVisibleRecoveryRequired: true }, bindings: { runtime: 'openclaw', entrypoint: 'scripts/watchdog_auto_notify_orchestrator.mjs', scripts: { watchdog: 'scripts/long_task_watchdog.mjs', dispatcher: 'scripts/operator_notify_dispatcher.mjs', bridgeSupervisor: 'scripts/operator_notify_bridge_supervisor.mjs', senderBinding: 'scripts/operator_notify_sender_binding.mjs', orchestrator: 'scripts/watchdog_auto_notify_orchestrator.mjs' }, artifact_roots: { queueItems: 'state/operator-notify-queue' } } }, capability_expectations: { required: [ 'emit_canonical_events', 'evaluate_watchdog_overdue', 'create_queue_items', 'create_spool_handoff', 'write_bridge_receipts' ], preferred: ['direct_sender_binding', 'final_delivery_ack'] } }; function createFixtureRoot() { return fs.mkdtempSync(path.join(os.tmpdir(), 'reporting-governance-decision-runtime-')); } function mkdirs(root, names) { for (const name of names) { fs.mkdirSync(path.join(root, name), { recursive: true }); } } function writeState(root) { const statePath = path.join(root, 'watchdog-state.json'); fs.writeFileSync(statePath, `${JSON.stringify({ version: 1, watchdogs: [ { id: 'reporting-governance-plugin-watchdog', task: 'reporting-governance plugin spec development', status: 'active', ownerSessionKey: 'agent:coder:main', reportChannel: 'telegram', reportTarget: '864811879', intervalMinutes: 10, lastMilestoneAt: '2026-05-07T08:00:00.000Z', lastAlertAt: null, }, ], }, null, 2)}\n`, 'utf8'); return statePath; } function readSingleJson(dirPath) { const files = fs.readdirSync(dirPath).filter((name) => name.endsWith('.json')).sort(); assert.equal(files.length, 1, `expected exactly one json file in ${dirPath}`); return JSON.parse(fs.readFileSync(path.join(dirPath, files[0]), 'utf8')); } function createBaseArgs() { return { event: { type: 'silence_timeout', payload: { checkpoint_overdue: true, } }, evidence: [ { id: 'ev-watchdog', quality: 'moderate', is_new: true } ], capabilityDescriptor, policyPacks: [noSilencePack], context: { signals: ['checkpoint_overdue'], }, profile: strictProfileArtifact, packageVersion: '0.1.0-mainline', repoRootOverride: repoRoot, }; } test('decision record integrates planning output with runtime receipts and queue artifacts', () => { const root = createFixtureRoot(); try { mkdirs(root, ['evidence', 'events', 'queue', 'spool', 'receipts', 'repo']); const statePath = writeState(root); const fakeRepoRoot = path.join(root, 'repo'); fs.mkdirSync(path.join(fakeRepoRoot, 'state', 'decisions'), { recursive: true }); const result = executeRuntimeIntegratedGovernance({ ...createBaseArgs(), runtime: { state: statePath, evidenceDir: path.join(root, 'evidence'), eventDir: path.join(root, 'events'), queueDir: path.join(root, 'queue'), spoolDir: path.join(root, 'spool'), receiptDir: path.join(root, 'receipts'), senderCommand: `node -e "process.stdout.write(JSON.stringify({state:'sent'}))"`, writeState: true, now: '2026-05-07T08:20:00.000Z', }, }); assert.equal(result.contract.decision, 'force_checkpoint'); assert.equal(result.planning.receipt.delivery_state, 'acked'); const queueItem = readSingleJson(path.join(root, 'queue')); const receipt = readSingleJson(path.join(root, 'receipts')); const runtimeEventRef = queueItem.evidence_refs.find((ref) => ref.label === 'watchdog_event'); const store = createFileDecisionStore({ decisionsDir: path.join(fakeRepoRoot, 'state', 'decisions'), repoRootOverride: fakeRepoRoot, }); const written = store.write({ decision: result.planning.decision, receipt: result.planning.receipt, recordedAt: '2026-05-08T04:00:00.000Z', source: { task_id: queueItem.governance.task_id, correlation_id: queueItem.governance.correlation_id, event_id: runtimeEventRef.path, }, }); const loaded = store.load(written.artifactPath); assert.equal(loaded.artifact.metadata.policy_id, 'no-silence.missed-checkpoint'); assert.equal(loaded.artifact.spec.receipt.delivery_state, 'acked'); assert.equal(loaded.artifact.metadata.task_id, 'reporting-governance-plugin-watchdog'); assert.equal(loaded.artifact.metadata.correlation_id, 'watchdog:reporting-governance-plugin-watchdog'); assert.equal(loaded.artifact.spec.source.event_id, runtimeEventRef.path); assert.equal(receipt.state, loaded.artifact.spec.receipt.delivery_state); assert.equal(queueItem.governance.decision, loaded.artifact.spec.decision.decision); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); test('persisted decision artifact is consumable by downstream validator without store context', () => { const root = createFixtureRoot(); try { mkdirs(root, ['evidence', 'events', 'queue', 'spool', 'receipts', 'repo']); const statePath = writeState(root); const fakeRepoRoot = path.join(root, 'repo'); const decisionsDir = path.join(fakeRepoRoot, 'state', 'decisions'); fs.mkdirSync(decisionsDir, { recursive: true }); const result = executeRuntimeIntegratedGovernance({ ...createBaseArgs(), runtime: { state: statePath, evidenceDir: path.join(root, 'evidence'), eventDir: path.join(root, 'events'), queueDir: path.join(root, 'queue'), spoolDir: path.join(root, 'spool'), receiptDir: path.join(root, 'receipts'), senderCommand: `node -e "process.stdout.write(JSON.stringify({state:'sent'}))"`, writeState: true, now: '2026-05-07T08:20:00.000Z', }, }); const queueItem = readSingleJson(path.join(root, 'queue')); const runtimeEventRef = queueItem.evidence_refs.find((ref) => ref.label === 'watchdog_event'); const store = createFileDecisionStore({ decisionsDir, repoRootOverride: fakeRepoRoot }); const written = store.write({ decision: result.planning.decision, receipt: result.planning.receipt, recordedAt: '2026-05-08T04:00:00.000Z', source: { task_id: queueItem.governance.task_id, correlation_id: queueItem.governance.correlation_id, event_id: runtimeEventRef.path, }, }); const persisted = JSON.parse(fs.readFileSync(written.artifactPath, 'utf8')); const consumed = validateDecisionRecordArtifact(persisted); assert.equal(consumed.metadata.record_id, written.artifact.metadata.record_id); assert.equal(consumed.spec.receipt.delivery_state, 'acked'); assert.equal(consumed.spec.source.event_id, runtimeEventRef.path); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); test('downstream validator rejects persisted decision artifact with invalid canonical decision datetime format', () => { const root = createFixtureRoot(); try { mkdirs(root, ['evidence', 'events', 'queue', 'spool', 'receipts', 'repo']); const statePath = writeState(root); const fakeRepoRoot = path.join(root, 'repo'); const decisionsDir = path.join(fakeRepoRoot, 'state', 'decisions'); fs.mkdirSync(decisionsDir, { recursive: true }); const result = executeRuntimeIntegratedGovernance({ ...createBaseArgs(), runtime: { state: statePath, evidenceDir: path.join(root, 'evidence'), eventDir: path.join(root, 'events'), queueDir: path.join(root, 'queue'), spoolDir: path.join(root, 'spool'), receiptDir: path.join(root, 'receipts'), senderCommand: `node -e "process.stdout.write(JSON.stringify({state:'sent'}))"`, writeState: true, now: '2026-05-07T08:20:00.000Z', }, }); const queueItem = readSingleJson(path.join(root, 'queue')); const runtimeEventRef = queueItem.evidence_refs.find((ref) => ref.label === 'watchdog_event'); const store = createFileDecisionStore({ decisionsDir, repoRootOverride: fakeRepoRoot }); const written = store.write({ decision: result.planning.decision, receipt: result.planning.receipt, recordedAt: '2026-05-08T04:00:00.000Z', source: { task_id: queueItem.governance.task_id, correlation_id: queueItem.governance.correlation_id, event_id: runtimeEventRef.path, }, }); const persisted = JSON.parse(fs.readFileSync(written.artifactPath, 'utf8')); persisted.spec.decision.operator_notice.deadline = 'tomorrow-ish'; fs.writeFileSync(written.artifactPath, `${JSON.stringify(persisted, null, 2)} `, 'utf8'); const malformedPersisted = JSON.parse(fs.readFileSync(written.artifactPath, 'utf8')); assert.throws( () => validateDecisionRecordArtifact(malformedPersisted), /spec\/decision\/operator_notice\/deadline must match format "date-time"/ ); } finally { fs.rmSync(root, { recursive: true, force: true }); } });