import assert from 'node:assert/strict'; import test from 'node:test'; import { buildRunEvent, normalizePageDataValidationObservation, normalizeRunSpec, stableRolloutBucket, } from './contracts.mjs'; import { createRemoteWorkflowEngine, createWorkflowEngineRegistry, } from './engine-registry.mjs'; test('orchestrator contracts normalize run specs without exposing framework types', () => { const spec = normalizeRunSpec({ runId: 'run-1', requestId: 'request-1', workflow: { name: 'code-run-v1', version: 2 }, subject: { userId: 'user-1' }, input: { instruction: 'fix tests' }, }); assert.equal(spec.version, 'orchestrator-run-v1'); assert.equal(spec.workflow.name, 'code-run-v1'); assert.equal(spec.workflow.version, 2); assert.equal(spec.subject.userId, 'user-1'); const event = buildRunEvent({ runId: spec.runId, sequence: 1, type: 'workflow.started', }); assert.equal(event.version, 'orchestrator-event-v1'); assert.equal(event.type, 'workflow.started'); assert.equal(stableRolloutBucket('run-1'), stableRolloutBucket('run-1')); }); test('Page Data validation observations derive a bounded control-plane verdict', () => { const observation = normalizePageDataValidationObservation({ idempotencyKey: 'run-1:page-data-delivery:v1', taskType: 'page_data_dev_complex', required: true, checks: [ { id: 'agent_run_completion', status: 'passed' }, { id: 'page_data_binding', status: 'passed' }, { id: 'page_data_storage_policy', status: 'passed' }, { id: 'independent_review', status: 'skipped' }, ], metrics: { pageCount: 2, publicationCount: 1 }, source: 'portal-agent-run', observedAt: 123, ignoredSensitivePayload: { workspacePath: '/private/workspace', instruction: 'must not be projected', }, }); assert.equal(observation.version, 'page-data-validation-observation-v1'); assert.equal(observation.verdict, 'passed'); assert.equal(observation.dataPolicy, 'control-plane-only-v1'); assert.deepEqual(observation.metrics, { pageCount: 2, publicationCount: 1 }); assert.equal('ignoredSensitivePayload' in observation, false); assert.equal(JSON.stringify(observation).includes('/private/workspace'), false); const failed = normalizePageDataValidationObservation({ idempotencyKey: 'run-2:page-data-delivery:v1', required: true, checks: [ { id: 'page_data_storage_policy', status: 'failed', codes: ['browser_storage_forbidden'], }, ], }); assert.equal(failed.verdict, 'failed'); assert.deepEqual(failed.checks[0].codes, ['BROWSER_STORAGE_FORBIDDEN']); const inconclusive = normalizePageDataValidationObservation({ idempotencyKey: 'run-3:page-data-delivery:v1', required: true, checks: [{ id: 'agent_run_completion', status: 'passed' }], }); assert.equal(inconclusive.verdict, 'inconclusive'); assert.throws( () => normalizePageDataValidationObservation({ idempotencyKey: 'missing-checks' }), (error) => error.code === 'WORKFLOW_VALIDATION_INVALID' && error.status === 422, ); }); test('workflow engine registry enforces the framework-neutral contract', () => { const engine = { id: 'native', start() {}, resume() {}, cancel() {}, getState() {}, async *streamEvents() {}, }; const registry = createWorkflowEngineRegistry([engine]); assert.equal(registry.has('native'), true); assert.equal(registry.get('native'), engine); assert.throws( () => registry.register({ id: 'broken' }), /missing methods/, ); }); test('remote workflow engine speaks only the versioned orchestrator HTTP contract', async () => { const calls = []; const engine = createRemoteWorkflowEngine({ baseUrl: 'http://orchestrator.internal', fetchImpl: async (url, init) => { calls.push({ url, init }); return { ok: true, async json() { return url.includes('/events') ? { events: [{ type: 'run.succeeded' }] } : { ok: true }; }, }; }, }); await engine.start({ runId: 'run-1' }); await engine.resume('run-1', { approvalId: 'approval-1', decision: 'approve' }); await engine.cancel('run-1'); await engine.getState('run-1'); await engine.recordValidationObservation('run-1', { idempotencyKey: 'run-1:page-data-delivery:v1', checks: [{ id: 'agent_run_completion', status: 'passed' }], }); const events = []; for await (const event of engine.streamEvents('run-1')) events.push(event); await engine.getExecutorJob('run-1:executor-preview'); const executorEvents = []; for await ( const event of engine.streamExecutorJobEvents('run-1:executor-preview', 2) ) { executorEvents.push(event); } assert.deepEqual(calls.map((call) => [call.init?.method ?? 'GET', call.url]), [ ['POST', 'http://orchestrator.internal/v1/runs'], ['POST', 'http://orchestrator.internal/v1/runs/run-1/resume'], ['POST', 'http://orchestrator.internal/v1/runs/run-1/cancel'], ['GET', 'http://orchestrator.internal/v1/runs/run-1'], [ 'POST', 'http://orchestrator.internal/v1/runs/run-1/validation-observations', ], ['GET', 'http://orchestrator.internal/v1/runs/run-1/events'], ['GET', 'http://orchestrator.internal/v1/executor-jobs/run-1%3Aexecutor-preview'], [ 'GET', 'http://orchestrator.internal/v1/executor-jobs/run-1%3Aexecutor-preview/events?after=2', ], ]); assert.deepEqual(events, [{ type: 'run.succeeded' }]); assert.deepEqual(executorEvents, [{ type: 'run.succeeded' }]); });