feat(orchestrator): observe Page Data validation

This commit is contained in:
john
2026-07-25 14:16:57 +08:00
parent e02495a6c0
commit 417aa9c78b
17 changed files with 784 additions and 16 deletions
+62
View File
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildRunEvent,
normalizePageDataValidationObservation,
normalizeRunSpec,
stableRolloutBucket,
} from './contracts.mjs';
@@ -33,6 +34,59 @@ test('orchestrator contracts normalize run specs without exposing framework type
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',
@@ -71,6 +125,10 @@ test('remote workflow engine speaks only the versioned orchestrator HTTP contrac
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');
@@ -86,6 +144,10 @@ test('remote workflow engine speaks only the versioned orchestrator HTTP contrac
['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'],
[