@open-agent-toolkit/cli 0.2.54 → 0.2.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/skills/recon.md +28 -12
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -105
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +0 -226
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/recon/SKILL.md +76 -38
- package/assets/skills/recon/references/packet-contract.md +138 -105
- package/assets/skills/recon/scripts/lib/contracts.mjs +181 -674
- package/assets/skills/recon/scripts/lib/validated-run.mjs +10 -16
- package/assets/skills/recon/scripts/render-packet.mjs +7 -20
- package/assets/skills/recon/scripts/validate-packet.mjs +177 -344
- package/package.json +2 -2
|
@@ -6,8 +6,6 @@ export const artifactKinds = new Set([
|
|
|
6
6
|
'recon.raw-dossier',
|
|
7
7
|
'recon.review-brief',
|
|
8
8
|
'recon.review-result',
|
|
9
|
-
'recon.stage-result',
|
|
10
|
-
'recon.dispatch-receipt',
|
|
11
9
|
]);
|
|
12
10
|
|
|
13
11
|
export const profiles = ['quick', 'standard', 'thorough'];
|
|
@@ -36,11 +34,17 @@ export const workerModes = [
|
|
|
36
34
|
'coverage',
|
|
37
35
|
'reconcile',
|
|
38
36
|
];
|
|
39
|
-
export const
|
|
37
|
+
export const evidenceRelations = [
|
|
38
|
+
'supports',
|
|
39
|
+
'contradicts',
|
|
40
|
+
'qualifies',
|
|
41
|
+
'context',
|
|
42
|
+
];
|
|
43
|
+
export const authorityLevels = ['provider-enforced', 'contract-enforced'];
|
|
44
|
+
export const waveModes = [
|
|
40
45
|
'map',
|
|
41
46
|
'gather',
|
|
42
47
|
'compile',
|
|
43
|
-
'locator-validation',
|
|
44
48
|
'semantic-verification',
|
|
45
49
|
'adversarial',
|
|
46
50
|
'coverage',
|
|
@@ -176,44 +180,6 @@ function requiredArray(value, key, errors, path = '$') {
|
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
182
|
|
|
179
|
-
function requiredCanonicalStringSet(value, key, errors, path = '$') {
|
|
180
|
-
requiredArray(value, key, errors, path);
|
|
181
|
-
if (!Array.isArray(value?.[key])) return;
|
|
182
|
-
const entries = value[key];
|
|
183
|
-
for (const [index, entry] of entries.entries()) {
|
|
184
|
-
if (typeof entry !== 'string' || entry.trim().length === 0) {
|
|
185
|
-
errors.push(
|
|
186
|
-
issue(
|
|
187
|
-
'INVALID_ARRAY_ENTRY',
|
|
188
|
-
`${key} entries must be non-empty strings`,
|
|
189
|
-
`${path}.${key}[${index}]`,
|
|
190
|
-
),
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (new Set(entries).size !== entries.length) {
|
|
195
|
-
errors.push(
|
|
196
|
-
issue(
|
|
197
|
-
'DUPLICATE_ARRAY_ENTRY',
|
|
198
|
-
`${key} entries must be unique`,
|
|
199
|
-
`${path}.${key}`,
|
|
200
|
-
),
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
const sorted = entries.every((entry) => typeof entry === 'string')
|
|
204
|
-
? [...entries].sort()
|
|
205
|
-
: null;
|
|
206
|
-
if (sorted && entries.some((entry, index) => entry !== sorted[index])) {
|
|
207
|
-
errors.push(
|
|
208
|
-
issue(
|
|
209
|
-
'NON_CANONICAL_ARRAY_ORDER',
|
|
210
|
-
`${key} entries must use stable sorted order`,
|
|
211
|
-
`${path}.${key}`,
|
|
212
|
-
),
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
183
|
function requiredObject(value, key, errors, path = '$') {
|
|
218
184
|
if (!isObject(value?.[key])) {
|
|
219
185
|
errors.push(
|
|
@@ -254,401 +220,181 @@ function duplicateIds(values, path, errors) {
|
|
|
254
220
|
}
|
|
255
221
|
}
|
|
256
222
|
|
|
257
|
-
const
|
|
258
|
-
'schema',
|
|
259
|
-
'prepared_record_version',
|
|
260
|
-
'run_id',
|
|
261
|
-
'prepared_at',
|
|
262
|
-
'request',
|
|
263
|
-
'selection',
|
|
264
|
-
'execution',
|
|
265
|
-
'catalog_observation',
|
|
266
|
-
]);
|
|
267
|
-
const requestKeys = new Set([
|
|
268
|
-
'request_id',
|
|
269
|
-
'caller',
|
|
270
|
-
'objective',
|
|
271
|
-
'action',
|
|
272
|
-
'expected_output',
|
|
273
|
-
'verification_evidence',
|
|
274
|
-
'escalate_when',
|
|
275
|
-
]);
|
|
276
|
-
const selectionKeys = new Set([
|
|
277
|
-
'provider',
|
|
278
|
-
'dispatch_context',
|
|
279
|
-
'dispatch_policy',
|
|
280
|
-
'dispatch_ceiling',
|
|
281
|
-
'selected_route',
|
|
282
|
-
'selection_source',
|
|
283
|
-
'candidates_considered',
|
|
284
|
-
'selection_reason',
|
|
285
|
-
'role_name',
|
|
286
|
-
'role_class',
|
|
287
|
-
'role_selector',
|
|
288
|
-
'model_selector',
|
|
289
|
-
'model_selector_granularity',
|
|
290
|
-
'effort_selector',
|
|
291
|
-
'reasoning_mode_selector',
|
|
292
|
-
'service_tier_selector',
|
|
293
|
-
'guidance_reference',
|
|
294
|
-
'guidance_version',
|
|
295
|
-
'guidance_verified_at',
|
|
296
|
-
'guidance_status',
|
|
297
|
-
]);
|
|
298
|
-
const pinnedTargetKeys = new Set([
|
|
223
|
+
const executionKeys = new Set([
|
|
299
224
|
'provider',
|
|
300
|
-
'
|
|
301
|
-
'
|
|
302
|
-
'
|
|
303
|
-
'
|
|
304
|
-
'
|
|
305
|
-
'
|
|
306
|
-
'reasoning_mode_selector',
|
|
307
|
-
'service_tier_selector',
|
|
308
|
-
]);
|
|
309
|
-
const waveKeys = new Set([
|
|
310
|
-
'wave_id',
|
|
311
|
-
'conditional',
|
|
312
|
-
'task_class',
|
|
313
|
-
'model_class_floor',
|
|
314
|
-
'scope',
|
|
315
|
-
'lanes',
|
|
225
|
+
'route',
|
|
226
|
+
'role',
|
|
227
|
+
'model',
|
|
228
|
+
'effort',
|
|
229
|
+
'reasoningMode',
|
|
230
|
+
'serviceTier',
|
|
316
231
|
'authority',
|
|
317
|
-
'
|
|
318
|
-
'
|
|
319
|
-
'
|
|
320
|
-
'
|
|
321
|
-
'
|
|
322
|
-
'dispatch_mode',
|
|
323
|
-
'context_fork_controls',
|
|
324
|
-
'concurrency',
|
|
325
|
-
'lane_cap',
|
|
326
|
-
'payload_digest',
|
|
327
|
-
]);
|
|
328
|
-
const catalogKeys = new Set([
|
|
329
|
-
'id',
|
|
330
|
-
'source',
|
|
331
|
-
'dispatch_context',
|
|
332
|
-
'observed_at',
|
|
333
|
-
'relevant_catalog_fingerprint',
|
|
232
|
+
'maxConcurrency',
|
|
233
|
+
'deadlineSeconds',
|
|
234
|
+
'retryLimit',
|
|
235
|
+
'waves',
|
|
236
|
+
'approval',
|
|
334
237
|
]);
|
|
335
238
|
|
|
336
|
-
export function
|
|
239
|
+
export function approvalFingerprintInput(execution) {
|
|
240
|
+
const { approval: _approval, ...approved } = execution;
|
|
241
|
+
return approved;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function validateApprovalEvidence(value, errors, path) {
|
|
337
245
|
if (!isObject(value)) return;
|
|
338
|
-
closedObject(
|
|
339
|
-
|
|
246
|
+
closedObject(
|
|
247
|
+
value,
|
|
248
|
+
new Set(['type', 'approvedAt', 'fingerprint']),
|
|
249
|
+
errors,
|
|
250
|
+
path,
|
|
251
|
+
);
|
|
252
|
+
if (value.type !== 'explicit-user-approval') {
|
|
340
253
|
errors.push(
|
|
341
254
|
issue(
|
|
342
|
-
'
|
|
343
|
-
'Approval
|
|
344
|
-
`${path}.
|
|
255
|
+
'INVALID_APPROVAL_EVIDENCE',
|
|
256
|
+
'Approval evidence must record explicit user approval',
|
|
257
|
+
`${path}.type`,
|
|
345
258
|
),
|
|
346
259
|
);
|
|
347
260
|
}
|
|
348
|
-
|
|
261
|
+
requiredTimestamp(value, 'approvedAt', errors, path);
|
|
262
|
+
if (!isDigest(value.fingerprint)) {
|
|
349
263
|
errors.push(
|
|
350
264
|
issue(
|
|
351
|
-
'
|
|
352
|
-
'
|
|
353
|
-
`${path}.
|
|
265
|
+
'INVALID_APPROVAL_FINGERPRINT',
|
|
266
|
+
'Approval evidence fingerprint must be sha256',
|
|
267
|
+
`${path}.fingerprint`,
|
|
354
268
|
),
|
|
355
269
|
);
|
|
356
270
|
}
|
|
357
|
-
|
|
358
|
-
requiredString(value, 'prepared_at', errors, path);
|
|
359
|
-
requiredObject(value, 'request', errors, path);
|
|
360
|
-
requiredObject(value, 'selection', errors, path);
|
|
361
|
-
requiredObject(value, 'execution', errors, path);
|
|
362
|
-
requiredObject(value, 'catalog_observation', errors, path);
|
|
271
|
+
}
|
|
363
272
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
'action',
|
|
370
|
-
'expected_output',
|
|
371
|
-
'verification_evidence',
|
|
372
|
-
]) {
|
|
373
|
-
requiredString(value.request, key, errors, `${path}.request`);
|
|
273
|
+
export function validateExecution(value, errors, path = '$.execution') {
|
|
274
|
+
if (!isObject(value)) return;
|
|
275
|
+
closedObject(value, executionKeys, errors, path);
|
|
276
|
+
for (const key of ['provider', 'route', 'role', 'model', 'effort']) {
|
|
277
|
+
requiredString(value, key, errors, path);
|
|
374
278
|
}
|
|
375
|
-
|
|
376
|
-
value
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
(item) => item !== 'candidates_considered',
|
|
385
|
-
)) {
|
|
386
|
-
if (
|
|
387
|
-
key === 'reasoning_mode_selector' &&
|
|
388
|
-
value.selection?.reasoning_mode_selector === null
|
|
389
|
-
) {
|
|
390
|
-
continue;
|
|
279
|
+
for (const key of ['reasoningMode', 'serviceTier']) {
|
|
280
|
+
if (value[key] !== null && typeof value[key] !== 'string') {
|
|
281
|
+
errors.push(
|
|
282
|
+
issue(
|
|
283
|
+
'MISSING_REQUIRED_FIELD',
|
|
284
|
+
`${key} must be a string or null`,
|
|
285
|
+
`${path}.${key}`,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
391
288
|
}
|
|
392
|
-
requiredString(value.selection, key, errors, `${path}.selection`);
|
|
393
289
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
value.execution,
|
|
410
|
-
'run_maximum_floor',
|
|
411
|
-
errors,
|
|
412
|
-
`${path}.execution`,
|
|
413
|
-
);
|
|
414
|
-
requiredObject(value.execution, 'pinned_target', errors, `${path}.execution`);
|
|
290
|
+
if (!authorityLevels.includes(value.authority)) {
|
|
291
|
+
errors.push(
|
|
292
|
+
issue(
|
|
293
|
+
'INVALID_AUTHORITY_LEVEL',
|
|
294
|
+
'Approved authority must be provider-enforced or contract-enforced',
|
|
295
|
+
`${path}.authority`,
|
|
296
|
+
),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
requiredInteger(value, 'maxConcurrency', errors, path, 1);
|
|
300
|
+
requiredInteger(value, 'deadlineSeconds', errors, path, 1);
|
|
301
|
+
requiredInteger(value, 'retryLimit', errors, path, 0);
|
|
302
|
+
requiredArray(value, 'waves', errors, path);
|
|
303
|
+
requiredObject(value, 'approval', errors, path);
|
|
304
|
+
validateApprovalEvidence(value.approval, errors, `${path}.approval`);
|
|
415
305
|
const waveIds = new Set();
|
|
416
306
|
const laneIds = new Set();
|
|
417
|
-
for (const [waveIndex, wave] of (value.
|
|
418
|
-
const wavePath = `${path}.
|
|
419
|
-
closedObject(
|
|
420
|
-
|
|
421
|
-
'
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
'authorization_scope',
|
|
427
|
-
'dispatch_mode',
|
|
428
|
-
]) {
|
|
429
|
-
requiredString(wave, key, errors, wavePath);
|
|
430
|
-
}
|
|
307
|
+
for (const [waveIndex, wave] of (value.waves ?? []).entries()) {
|
|
308
|
+
const wavePath = `${path}.waves[${waveIndex}]`;
|
|
309
|
+
closedObject(
|
|
310
|
+
wave,
|
|
311
|
+
new Set(['waveId', 'mode', 'taskClass', 'lanes', 'conditional']),
|
|
312
|
+
errors,
|
|
313
|
+
wavePath,
|
|
314
|
+
);
|
|
315
|
+
requiredString(wave, 'waveId', errors, wavePath);
|
|
431
316
|
requiredArray(wave, 'lanes', errors, wavePath);
|
|
432
|
-
|
|
433
|
-
requiredObject(wave, 'fallback', errors, wavePath);
|
|
434
|
-
requiredObject(wave, 'context_fork_controls', errors, wavePath);
|
|
435
|
-
requiredInteger(wave, 'deadline_seconds', errors, wavePath, 1);
|
|
436
|
-
requiredInteger(wave, 'retry_limit', errors, wavePath);
|
|
437
|
-
requiredInteger(wave, 'concurrency', errors, wavePath, 1);
|
|
438
|
-
requiredInteger(wave, 'lane_cap', errors, wavePath, 1);
|
|
439
|
-
if (typeof wave.conditional !== 'boolean') {
|
|
317
|
+
if (Array.isArray(wave?.lanes) && wave.lanes.length === 0) {
|
|
440
318
|
errors.push(
|
|
441
319
|
issue(
|
|
442
320
|
'MISSING_REQUIRED_FIELD',
|
|
443
|
-
'
|
|
444
|
-
`${wavePath}.
|
|
321
|
+
'Every approved wave needs at least one lane',
|
|
322
|
+
`${wavePath}.lanes`,
|
|
445
323
|
),
|
|
446
324
|
);
|
|
447
325
|
}
|
|
448
|
-
if (!
|
|
326
|
+
if (!waveModes.includes(wave?.mode)) {
|
|
449
327
|
errors.push(
|
|
450
|
-
issue(
|
|
451
|
-
'INVALID_TASK_CLASS',
|
|
452
|
-
'Unknown wave task class',
|
|
453
|
-
`${wavePath}.task_class`,
|
|
454
|
-
),
|
|
328
|
+
issue('INVALID_WAVE_MODE', 'Unknown wave mode', `${wavePath}.mode`),
|
|
455
329
|
);
|
|
456
330
|
}
|
|
457
|
-
if (
|
|
331
|
+
if (!taskClasses.includes(wave?.taskClass)) {
|
|
458
332
|
errors.push(
|
|
459
333
|
issue(
|
|
460
|
-
'
|
|
461
|
-
'
|
|
462
|
-
`${wavePath}.
|
|
334
|
+
'INVALID_TASK_CLASS',
|
|
335
|
+
'Unknown wave task class',
|
|
336
|
+
`${wavePath}.taskClass`,
|
|
463
337
|
),
|
|
464
338
|
);
|
|
465
339
|
}
|
|
466
|
-
if (
|
|
340
|
+
if (typeof wave?.conditional !== 'boolean') {
|
|
467
341
|
errors.push(
|
|
468
342
|
issue(
|
|
469
|
-
'
|
|
470
|
-
'
|
|
471
|
-
`${wavePath}.
|
|
343
|
+
'MISSING_REQUIRED_FIELD',
|
|
344
|
+
'conditional must be a boolean',
|
|
345
|
+
`${wavePath}.conditional`,
|
|
472
346
|
),
|
|
473
347
|
);
|
|
474
348
|
}
|
|
475
|
-
if (waveIds.has(wave
|
|
349
|
+
if (waveIds.has(wave?.waveId)) {
|
|
476
350
|
errors.push(
|
|
477
351
|
issue(
|
|
478
352
|
'DUPLICATE_ID',
|
|
479
|
-
`Duplicate wave ${wave.
|
|
480
|
-
`${wavePath}.
|
|
353
|
+
`Duplicate wave ${wave.waveId}`,
|
|
354
|
+
`${wavePath}.waveId`,
|
|
481
355
|
),
|
|
482
356
|
);
|
|
483
357
|
}
|
|
484
|
-
waveIds.add(wave
|
|
485
|
-
|
|
486
|
-
wave.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
`${wavePath}.fallback`,
|
|
490
|
-
);
|
|
491
|
-
requiredString(wave.fallback, 'mode', errors, `${wavePath}.fallback`);
|
|
492
|
-
closedObject(
|
|
493
|
-
wave.context_fork_controls,
|
|
494
|
-
new Set(['fork_turns']),
|
|
495
|
-
errors,
|
|
496
|
-
`${wavePath}.context_fork_controls`,
|
|
497
|
-
);
|
|
498
|
-
requiredString(
|
|
499
|
-
wave.context_fork_controls,
|
|
500
|
-
'fork_turns',
|
|
501
|
-
errors,
|
|
502
|
-
`${wavePath}.context_fork_controls`,
|
|
503
|
-
);
|
|
504
|
-
for (const [laneIndex, lane] of (wave.lanes ?? []).entries()) {
|
|
358
|
+
waveIds.add(wave?.waveId);
|
|
359
|
+
for (const [laneIndex, lane] of (Array.isArray(wave?.lanes)
|
|
360
|
+
? wave.lanes
|
|
361
|
+
: []
|
|
362
|
+
).entries()) {
|
|
505
363
|
const lanePath = `${wavePath}.lanes[${laneIndex}]`;
|
|
506
|
-
closedObject(
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
364
|
+
closedObject(
|
|
365
|
+
lane,
|
|
366
|
+
new Set(['laneId', 'scope', 'writeRoot']),
|
|
367
|
+
errors,
|
|
368
|
+
lanePath,
|
|
369
|
+
);
|
|
370
|
+
for (const key of ['laneId', 'scope', 'writeRoot']) {
|
|
371
|
+
requiredString(lane, key, errors, lanePath);
|
|
372
|
+
}
|
|
373
|
+
if (
|
|
374
|
+
typeof lane?.writeRoot === 'string' &&
|
|
375
|
+
(lane.writeRoot.startsWith('/') ||
|
|
376
|
+
lane.writeRoot.split('/').some((segment) => segment === '..'))
|
|
377
|
+
) {
|
|
378
|
+
errors.push(
|
|
379
|
+
issue(
|
|
380
|
+
'INVALID_WRITE_ROOT',
|
|
381
|
+
'Lane write root must be a packet-relative path',
|
|
382
|
+
`${lanePath}.writeRoot`,
|
|
383
|
+
),
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
if (laneIds.has(lane?.laneId)) {
|
|
510
387
|
errors.push(
|
|
511
388
|
issue(
|
|
512
389
|
'DUPLICATE_ID',
|
|
513
|
-
`Duplicate lane ${lane.
|
|
514
|
-
`${lanePath}.
|
|
390
|
+
`Duplicate lane ${lane.laneId}`,
|
|
391
|
+
`${lanePath}.laneId`,
|
|
515
392
|
),
|
|
516
393
|
);
|
|
517
394
|
}
|
|
518
|
-
laneIds.add(lane
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const floors = (value.execution?.waves ?? [])
|
|
523
|
-
.map((wave) => taskClasses.indexOf(wave.model_class_floor))
|
|
524
|
-
.filter((index) => index >= 0);
|
|
525
|
-
const maximumFloor = taskClasses[Math.max(...floors)];
|
|
526
|
-
if (maximumFloor && value.execution?.run_maximum_floor !== maximumFloor) {
|
|
527
|
-
errors.push(
|
|
528
|
-
issue(
|
|
529
|
-
'RUN_MAXIMUM_FLOOR_MISMATCH',
|
|
530
|
-
'Run maximum floor does not equal the strongest wave floor',
|
|
531
|
-
`${path}.execution.run_maximum_floor`,
|
|
532
|
-
),
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
if (!taskClasses.includes(value.execution?.run_maximum_floor)) {
|
|
536
|
-
errors.push(
|
|
537
|
-
issue(
|
|
538
|
-
'INVALID_TASK_CLASS',
|
|
539
|
-
'Unknown run maximum floor',
|
|
540
|
-
`${path}.execution.run_maximum_floor`,
|
|
541
|
-
),
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
closedObject(
|
|
545
|
-
value.execution?.pinned_target,
|
|
546
|
-
pinnedTargetKeys,
|
|
547
|
-
errors,
|
|
548
|
-
`${path}.execution.pinned_target`,
|
|
549
|
-
);
|
|
550
|
-
for (const key of pinnedTargetKeys) {
|
|
551
|
-
if (
|
|
552
|
-
key === 'reasoning_mode_selector' &&
|
|
553
|
-
value.execution?.pinned_target?.reasoning_mode_selector === null
|
|
554
|
-
) {
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
requiredString(
|
|
558
|
-
value.execution?.pinned_target,
|
|
559
|
-
key,
|
|
560
|
-
errors,
|
|
561
|
-
`${path}.execution.pinned_target`,
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
for (const key of pinnedTargetKeys) {
|
|
565
|
-
if (value.execution?.pinned_target?.[key] !== value.selection?.[key]) {
|
|
566
|
-
errors.push(
|
|
567
|
-
issue(
|
|
568
|
-
'PINNED_TARGET_MISMATCH',
|
|
569
|
-
`${key} differs from the approved selection`,
|
|
570
|
-
`${path}.execution.pinned_target.${key}`,
|
|
571
|
-
),
|
|
572
|
-
);
|
|
395
|
+
laneIds.add(lane?.laneId);
|
|
573
396
|
}
|
|
574
397
|
}
|
|
575
|
-
|
|
576
|
-
closedObject(
|
|
577
|
-
value.catalog_observation,
|
|
578
|
-
catalogKeys,
|
|
579
|
-
errors,
|
|
580
|
-
`${path}.catalog_observation`,
|
|
581
|
-
);
|
|
582
|
-
for (const key of ['id', 'source', 'dispatch_context', 'observed_at']) {
|
|
583
|
-
requiredString(
|
|
584
|
-
value.catalog_observation,
|
|
585
|
-
key,
|
|
586
|
-
errors,
|
|
587
|
-
`${path}.catalog_observation`,
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
if (!isDigest(value.catalog_observation?.relevant_catalog_fingerprint)) {
|
|
591
|
-
errors.push(
|
|
592
|
-
issue(
|
|
593
|
-
'INVALID_CATALOG_FINGERPRINT',
|
|
594
|
-
'Catalog fingerprint must be sha256',
|
|
595
|
-
`${path}.catalog_observation.relevant_catalog_fingerprint`,
|
|
596
|
-
),
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
if (
|
|
600
|
-
value.catalog_observation?.dispatch_context !==
|
|
601
|
-
value.selection?.dispatch_context
|
|
602
|
-
) {
|
|
603
|
-
errors.push(
|
|
604
|
-
issue(
|
|
605
|
-
'CATALOG_CONTEXT_MISMATCH',
|
|
606
|
-
'Catalog context differs from the approved selection',
|
|
607
|
-
`${path}.catalog_observation.dispatch_context`,
|
|
608
|
-
),
|
|
609
|
-
);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
function validateApprovalEvidence(value, errors, path) {
|
|
614
|
-
if (!isObject(value)) return;
|
|
615
|
-
closedObject(value, new Set(['type', 'fingerprint']), errors, path);
|
|
616
|
-
if (value.type !== 'explicit-user-approval') {
|
|
617
|
-
errors.push(
|
|
618
|
-
issue(
|
|
619
|
-
'INVALID_APPROVAL_EVIDENCE',
|
|
620
|
-
'Approval evidence must record explicit user approval',
|
|
621
|
-
`${path}.type`,
|
|
622
|
-
),
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
if (!isDigest(value.fingerprint)) {
|
|
626
|
-
errors.push(
|
|
627
|
-
issue(
|
|
628
|
-
'INVALID_APPROVAL_FINGERPRINT',
|
|
629
|
-
'Approval evidence fingerprint must be sha256',
|
|
630
|
-
`${path}.fingerprint`,
|
|
631
|
-
),
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function validateCatalogRecheck(value, errors, path) {
|
|
637
|
-
if (!isObject(value)) return;
|
|
638
|
-
closedObject(value, catalogKeys, errors, path);
|
|
639
|
-
for (const key of ['id', 'source', 'dispatch_context']) {
|
|
640
|
-
requiredString(value, key, errors, path);
|
|
641
|
-
}
|
|
642
|
-
requiredTimestamp(value, 'observed_at', errors, path);
|
|
643
|
-
if (!isDigest(value.relevant_catalog_fingerprint)) {
|
|
644
|
-
errors.push(
|
|
645
|
-
issue(
|
|
646
|
-
'INVALID_CATALOG_FINGERPRINT',
|
|
647
|
-
'Catalog recheck fingerprint must be sha256',
|
|
648
|
-
`${path}.relevant_catalog_fingerprint`,
|
|
649
|
-
),
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
398
|
}
|
|
653
399
|
|
|
654
400
|
function validateManifest(value, errors) {
|
|
@@ -661,7 +407,6 @@ function validateManifest(value, errors) {
|
|
|
661
407
|
'request',
|
|
662
408
|
'sources',
|
|
663
409
|
'execution',
|
|
664
|
-
'stages',
|
|
665
410
|
'artifacts',
|
|
666
411
|
'gaps',
|
|
667
412
|
]),
|
|
@@ -725,7 +470,6 @@ function validateManifest(value, errors) {
|
|
|
725
470
|
}
|
|
726
471
|
}
|
|
727
472
|
requiredArray(value, 'sources', errors);
|
|
728
|
-
requiredArray(value, 'stages', errors);
|
|
729
473
|
requiredArray(value, 'artifacts', errors);
|
|
730
474
|
requiredArray(value, 'gaps', errors);
|
|
731
475
|
requiredObject(value, 'request', errors);
|
|
@@ -755,61 +499,9 @@ function validateManifest(value, errors) {
|
|
|
755
499
|
requiredArray(value.request, key, errors, '$.request');
|
|
756
500
|
}
|
|
757
501
|
}
|
|
758
|
-
|
|
759
|
-
closedObject(
|
|
760
|
-
value.execution,
|
|
761
|
-
new Set([
|
|
762
|
-
'approvalProjection',
|
|
763
|
-
'approvalCanonicalJson',
|
|
764
|
-
'approvalFingerprint',
|
|
765
|
-
'approvedAt',
|
|
766
|
-
'approvalEvidence',
|
|
767
|
-
'catalogRecheck',
|
|
768
|
-
]),
|
|
769
|
-
errors,
|
|
770
|
-
'$.execution',
|
|
771
|
-
);
|
|
772
|
-
requiredObject(
|
|
773
|
-
value.execution,
|
|
774
|
-
'approvalProjection',
|
|
775
|
-
errors,
|
|
776
|
-
'$.execution',
|
|
777
|
-
);
|
|
778
|
-
requiredString(
|
|
779
|
-
value.execution,
|
|
780
|
-
'approvalCanonicalJson',
|
|
781
|
-
errors,
|
|
782
|
-
'$.execution',
|
|
783
|
-
);
|
|
784
|
-
requiredString(
|
|
785
|
-
value.execution,
|
|
786
|
-
'approvalFingerprint',
|
|
787
|
-
errors,
|
|
788
|
-
'$.execution',
|
|
789
|
-
);
|
|
790
|
-
requiredTimestamp(value.execution, 'approvedAt', errors, '$.execution');
|
|
791
|
-
requiredObject(value.execution, 'approvalEvidence', errors, '$.execution');
|
|
792
|
-
requiredObject(value.execution, 'catalogRecheck', errors, '$.execution');
|
|
793
|
-
validateApprovalProjection(
|
|
794
|
-
value.execution.approvalProjection,
|
|
795
|
-
errors,
|
|
796
|
-
'$.execution.approvalProjection',
|
|
797
|
-
);
|
|
798
|
-
validateApprovalEvidence(
|
|
799
|
-
value.execution.approvalEvidence,
|
|
800
|
-
errors,
|
|
801
|
-
'$.execution.approvalEvidence',
|
|
802
|
-
);
|
|
803
|
-
validateCatalogRecheck(
|
|
804
|
-
value.execution.catalogRecheck,
|
|
805
|
-
errors,
|
|
806
|
-
'$.execution.catalogRecheck',
|
|
807
|
-
);
|
|
808
|
-
}
|
|
502
|
+
validateExecution(value.execution, errors, '$.execution');
|
|
809
503
|
if (Array.isArray(value.sources))
|
|
810
504
|
duplicateIds(value.sources, '$.sources', errors);
|
|
811
|
-
if (Array.isArray(value.stages))
|
|
812
|
-
duplicateIds(value.stages, '$.stages', errors);
|
|
813
505
|
if (Array.isArray(value.gaps)) duplicateIds(value.gaps, '$.gaps', errors);
|
|
814
506
|
for (const [index, reference] of (value.artifacts ?? []).entries()) {
|
|
815
507
|
validateExactReference(reference, `$.artifacts[${index}]`, errors);
|
|
@@ -951,62 +643,6 @@ function validateManifest(value, errors) {
|
|
|
951
643
|
`$.gaps[${index}]`,
|
|
952
644
|
);
|
|
953
645
|
}
|
|
954
|
-
for (const [index, stage] of (value.stages ?? []).entries()) {
|
|
955
|
-
requiredString(stage, 'id', errors, `$.stages[${index}]`);
|
|
956
|
-
requiredString(stage, 'waveId', errors, `$.stages[${index}]`);
|
|
957
|
-
requiredString(stage, 'mode', errors, `$.stages[${index}]`);
|
|
958
|
-
requiredString(stage, 'status', errors, `$.stages[${index}]`);
|
|
959
|
-
requiredArray(stage, 'artifactIds', errors, `$.stages[${index}]`);
|
|
960
|
-
requiredArray(stage, 'dispatchReceiptIds', errors, `$.stages[${index}]`);
|
|
961
|
-
requiredString(stage, 'laneId', errors, `$.stages[${index}]`);
|
|
962
|
-
if (!stageModes.includes(stage.mode)) {
|
|
963
|
-
errors.push(
|
|
964
|
-
issue(
|
|
965
|
-
'INVALID_STAGE_MODE',
|
|
966
|
-
'Unknown stage mode',
|
|
967
|
-
`$.stages[${index}].mode`,
|
|
968
|
-
),
|
|
969
|
-
);
|
|
970
|
-
}
|
|
971
|
-
if (!['complete', 'failed', 'omitted'].includes(stage.status)) {
|
|
972
|
-
errors.push(
|
|
973
|
-
issue(
|
|
974
|
-
'INVALID_STAGE_STATUS',
|
|
975
|
-
'Unknown stage status',
|
|
976
|
-
`$.stages[${index}].status`,
|
|
977
|
-
),
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
if (
|
|
981
|
-
stage.kind !== 'recon.stage-result' ||
|
|
982
|
-
stage.schemaVersion !== SCHEMA_VERSION
|
|
983
|
-
) {
|
|
984
|
-
errors.push(
|
|
985
|
-
issue(
|
|
986
|
-
'INVALID_STAGE_RESULT',
|
|
987
|
-
'Expected recon.stage-result version 1',
|
|
988
|
-
`$.stages[${index}]`,
|
|
989
|
-
),
|
|
990
|
-
);
|
|
991
|
-
}
|
|
992
|
-
closedObject(
|
|
993
|
-
stage,
|
|
994
|
-
new Set([
|
|
995
|
-
'kind',
|
|
996
|
-
'schemaVersion',
|
|
997
|
-
'id',
|
|
998
|
-
'waveId',
|
|
999
|
-
'mode',
|
|
1000
|
-
'status',
|
|
1001
|
-
'artifactIds',
|
|
1002
|
-
'dispatchReceiptIds',
|
|
1003
|
-
'laneId',
|
|
1004
|
-
'message',
|
|
1005
|
-
]),
|
|
1006
|
-
errors,
|
|
1007
|
-
`$.stages[${index}]`,
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
646
|
}
|
|
1011
647
|
|
|
1012
648
|
function validateLedger(value, errors) {
|
|
@@ -1116,6 +752,11 @@ function validateLedger(value, errors) {
|
|
|
1116
752
|
for (const key of ['evidence', 'claims', 'unresolvedQuestions']) {
|
|
1117
753
|
if (Array.isArray(value[key])) duplicateIds(value[key], `$.${key}`, errors);
|
|
1118
754
|
}
|
|
755
|
+
const evidenceIds = new Set(
|
|
756
|
+
Array.isArray(value.evidence)
|
|
757
|
+
? value.evidence.map((item) => item?.id).filter(Boolean)
|
|
758
|
+
: [],
|
|
759
|
+
);
|
|
1119
760
|
for (const [index, reference] of (value.inputArtifacts ?? []).entries()) {
|
|
1120
761
|
validateExactReference(reference, `$.inputArtifacts[${index}]`, errors);
|
|
1121
762
|
}
|
|
@@ -1252,6 +893,53 @@ function validateLedger(value, errors) {
|
|
|
1252
893
|
]) {
|
|
1253
894
|
requiredArray(claim, key, errors, `$.claims[${index}]`);
|
|
1254
895
|
}
|
|
896
|
+
const linkedEvidenceIds = new Set();
|
|
897
|
+
for (const [linkIndex, link] of (claim.evidence ?? []).entries()) {
|
|
898
|
+
const linkPath = `$.claims[${index}].evidence[${linkIndex}]`;
|
|
899
|
+
if (!isObject(link)) {
|
|
900
|
+
errors.push(
|
|
901
|
+
issue(
|
|
902
|
+
'INVALID_EVIDENCE_LINK',
|
|
903
|
+
'Claim evidence links must be { evidenceId, relation } objects',
|
|
904
|
+
linkPath,
|
|
905
|
+
),
|
|
906
|
+
);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
closedObject(link, new Set(['evidenceId', 'relation']), errors, linkPath);
|
|
910
|
+
requiredString(link, 'evidenceId', errors, linkPath);
|
|
911
|
+
if (!evidenceRelations.includes(link.relation)) {
|
|
912
|
+
errors.push(
|
|
913
|
+
issue(
|
|
914
|
+
'INVALID_EVIDENCE_RELATION',
|
|
915
|
+
'Claim evidence relation is not supported',
|
|
916
|
+
`${linkPath}.relation`,
|
|
917
|
+
),
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
if (
|
|
921
|
+
typeof link.evidenceId === 'string' &&
|
|
922
|
+
!evidenceIds.has(link.evidenceId)
|
|
923
|
+
) {
|
|
924
|
+
errors.push(
|
|
925
|
+
issue(
|
|
926
|
+
'UNRESOLVED_EVIDENCE_LINK',
|
|
927
|
+
`Claim evidence link ${link.evidenceId} does not resolve in this ledger`,
|
|
928
|
+
`${linkPath}.evidenceId`,
|
|
929
|
+
),
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
if (linkedEvidenceIds.has(link.evidenceId)) {
|
|
933
|
+
errors.push(
|
|
934
|
+
issue(
|
|
935
|
+
'DUPLICATE_EVIDENCE_LINK',
|
|
936
|
+
`Claim links evidence ${link.evidenceId} more than once`,
|
|
937
|
+
linkPath,
|
|
938
|
+
),
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
linkedEvidenceIds.add(link.evidenceId);
|
|
942
|
+
}
|
|
1255
943
|
for (const [referenceIndex, reference] of (
|
|
1256
944
|
claim.derivedFrom ?? []
|
|
1257
945
|
).entries()) {
|
|
@@ -2190,11 +1878,7 @@ function validateReviewResult(value, errors) {
|
|
|
2190
1878
|
`$.evidenceAssociations[${index}]`,
|
|
2191
1879
|
);
|
|
2192
1880
|
}
|
|
2193
|
-
if (
|
|
2194
|
-
!['supports', 'contradicts', 'qualifies', 'context'].includes(
|
|
2195
|
-
association.relation,
|
|
2196
|
-
)
|
|
2197
|
-
) {
|
|
1881
|
+
if (!evidenceRelations.includes(association.relation)) {
|
|
2198
1882
|
errors.push(
|
|
2199
1883
|
issue(
|
|
2200
1884
|
'INVALID_EVIDENCE_RELATION',
|
|
@@ -2249,180 +1933,6 @@ function validateReviewResult(value, errors) {
|
|
|
2249
1933
|
);
|
|
2250
1934
|
}
|
|
2251
1935
|
|
|
2252
|
-
function validateDispatchReceipt(value, errors) {
|
|
2253
|
-
for (const key of ['id', 'runId', 'stageId', 'laneId', 'state']) {
|
|
2254
|
-
requiredString(value, key, errors);
|
|
2255
|
-
}
|
|
2256
|
-
requiredObject(value, 'approvalProjection', errors);
|
|
2257
|
-
requiredString(value, 'approvalCanonicalJson', errors);
|
|
2258
|
-
requiredString(value, 'approvalFingerprint', errors);
|
|
2259
|
-
validateApprovalProjection(
|
|
2260
|
-
value.approvalProjection,
|
|
2261
|
-
errors,
|
|
2262
|
-
'$.approvalProjection',
|
|
2263
|
-
);
|
|
2264
|
-
if (
|
|
2265
|
-
!['prepared', 'approved', 'accepted', 'completed', 'failed'].includes(
|
|
2266
|
-
value.state,
|
|
2267
|
-
)
|
|
2268
|
-
) {
|
|
2269
|
-
errors.push(
|
|
2270
|
-
issue('INVALID_DISPATCH_STATE', 'Unknown dispatch state', '$.state'),
|
|
2271
|
-
);
|
|
2272
|
-
}
|
|
2273
|
-
const approved = ['approved', 'accepted', 'completed', 'failed'].includes(
|
|
2274
|
-
value.state,
|
|
2275
|
-
);
|
|
2276
|
-
const launched = ['accepted', 'completed', 'failed'].includes(value.state);
|
|
2277
|
-
if (approved) {
|
|
2278
|
-
requiredTimestamp(value, 'approvedAt', errors);
|
|
2279
|
-
requiredObject(value, 'approvalEvidence', errors);
|
|
2280
|
-
validateApprovalEvidence(
|
|
2281
|
-
value.approvalEvidence,
|
|
2282
|
-
errors,
|
|
2283
|
-
'$.approvalEvidence',
|
|
2284
|
-
);
|
|
2285
|
-
} else if (value.approvedAt !== null || value.approvalEvidence !== null) {
|
|
2286
|
-
errors.push(
|
|
2287
|
-
issue(
|
|
2288
|
-
'INVALID_DISPATCH_STATE_EVIDENCE',
|
|
2289
|
-
'Prepared receipt cannot contain approval evidence',
|
|
2290
|
-
'$.approvalEvidence',
|
|
2291
|
-
),
|
|
2292
|
-
);
|
|
2293
|
-
}
|
|
2294
|
-
if (launched) {
|
|
2295
|
-
requiredObject(value, 'catalogRecheck', errors);
|
|
2296
|
-
requiredObject(value, 'launchAcceptance', errors);
|
|
2297
|
-
validateCatalogRecheck(value.catalogRecheck, errors, '$.catalogRecheck');
|
|
2298
|
-
if (isObject(value.launchAcceptance)) {
|
|
2299
|
-
closedObject(
|
|
2300
|
-
value.launchAcceptance,
|
|
2301
|
-
new Set(['status', 'acceptedAt', 'handle']),
|
|
2302
|
-
errors,
|
|
2303
|
-
'$.launchAcceptance',
|
|
2304
|
-
);
|
|
2305
|
-
if (value.launchAcceptance.status !== 'accepted') {
|
|
2306
|
-
errors.push(
|
|
2307
|
-
issue(
|
|
2308
|
-
'INVALID_LAUNCH_ACCEPTANCE',
|
|
2309
|
-
'Launch was not accepted',
|
|
2310
|
-
'$.launchAcceptance.status',
|
|
2311
|
-
),
|
|
2312
|
-
);
|
|
2313
|
-
}
|
|
2314
|
-
requiredTimestamp(
|
|
2315
|
-
value.launchAcceptance,
|
|
2316
|
-
'acceptedAt',
|
|
2317
|
-
errors,
|
|
2318
|
-
'$.launchAcceptance',
|
|
2319
|
-
);
|
|
2320
|
-
requiredString(
|
|
2321
|
-
value.launchAcceptance,
|
|
2322
|
-
'handle',
|
|
2323
|
-
errors,
|
|
2324
|
-
'$.launchAcceptance',
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2327
|
-
} else if (value.catalogRecheck !== null || value.launchAcceptance !== null) {
|
|
2328
|
-
errors.push(
|
|
2329
|
-
issue(
|
|
2330
|
-
'INVALID_DISPATCH_STATE_EVIDENCE',
|
|
2331
|
-
'Pre-launch receipt cannot contain catalog or acceptance evidence',
|
|
2332
|
-
'$.catalogRecheck',
|
|
2333
|
-
),
|
|
2334
|
-
);
|
|
2335
|
-
}
|
|
2336
|
-
if (value.state === 'completed') {
|
|
2337
|
-
requiredArray(value, 'artifactIds', errors);
|
|
2338
|
-
requiredObject(value, 'terminalOutcome', errors);
|
|
2339
|
-
if (isObject(value.terminalOutcome)) {
|
|
2340
|
-
closedObject(
|
|
2341
|
-
value.terminalOutcome,
|
|
2342
|
-
new Set(['status', 'completedAt']),
|
|
2343
|
-
errors,
|
|
2344
|
-
'$.terminalOutcome',
|
|
2345
|
-
);
|
|
2346
|
-
if (value.terminalOutcome.status !== 'completed') {
|
|
2347
|
-
errors.push(
|
|
2348
|
-
issue(
|
|
2349
|
-
'INVALID_TERMINAL_OUTCOME',
|
|
2350
|
-
'Terminal outcome is not completed',
|
|
2351
|
-
'$.terminalOutcome.status',
|
|
2352
|
-
),
|
|
2353
|
-
);
|
|
2354
|
-
}
|
|
2355
|
-
requiredTimestamp(
|
|
2356
|
-
value.terminalOutcome,
|
|
2357
|
-
'completedAt',
|
|
2358
|
-
errors,
|
|
2359
|
-
'$.terminalOutcome',
|
|
2360
|
-
);
|
|
2361
|
-
}
|
|
2362
|
-
} else if (value.terminalOutcome !== null) {
|
|
2363
|
-
errors.push(
|
|
2364
|
-
issue(
|
|
2365
|
-
'INVALID_DISPATCH_STATE_EVIDENCE',
|
|
2366
|
-
'Non-completed receipt cannot contain a terminal outcome',
|
|
2367
|
-
'$.terminalOutcome',
|
|
2368
|
-
),
|
|
2369
|
-
);
|
|
2370
|
-
}
|
|
2371
|
-
closedObject(
|
|
2372
|
-
value,
|
|
2373
|
-
new Set([
|
|
2374
|
-
'kind',
|
|
2375
|
-
'schemaVersion',
|
|
2376
|
-
'id',
|
|
2377
|
-
'runId',
|
|
2378
|
-
'stageId',
|
|
2379
|
-
'laneId',
|
|
2380
|
-
'state',
|
|
2381
|
-
'approvalProjection',
|
|
2382
|
-
'approvalCanonicalJson',
|
|
2383
|
-
'approvalFingerprint',
|
|
2384
|
-
'approvedAt',
|
|
2385
|
-
'approvalEvidence',
|
|
2386
|
-
'catalogRecheck',
|
|
2387
|
-
'launchAcceptance',
|
|
2388
|
-
'terminalOutcome',
|
|
2389
|
-
'artifactIds',
|
|
2390
|
-
]),
|
|
2391
|
-
errors,
|
|
2392
|
-
);
|
|
2393
|
-
}
|
|
2394
|
-
|
|
2395
|
-
function validateStageResult(value, errors) {
|
|
2396
|
-
for (const key of ['id', 'waveId', 'mode', 'laneId', 'status'])
|
|
2397
|
-
requiredString(value, key, errors);
|
|
2398
|
-
requiredArray(value, 'artifactIds', errors);
|
|
2399
|
-
requiredArray(value, 'dispatchReceiptIds', errors);
|
|
2400
|
-
if (!stageModes.includes(value.mode)) {
|
|
2401
|
-
errors.push(issue('INVALID_STAGE_MODE', 'Unknown stage mode', '$.mode'));
|
|
2402
|
-
}
|
|
2403
|
-
if (!['complete', 'failed', 'omitted'].includes(value.status)) {
|
|
2404
|
-
errors.push(
|
|
2405
|
-
issue('INVALID_STAGE_STATUS', 'Unknown stage status', '$.status'),
|
|
2406
|
-
);
|
|
2407
|
-
}
|
|
2408
|
-
closedObject(
|
|
2409
|
-
value,
|
|
2410
|
-
new Set([
|
|
2411
|
-
'kind',
|
|
2412
|
-
'schemaVersion',
|
|
2413
|
-
'id',
|
|
2414
|
-
'waveId',
|
|
2415
|
-
'mode',
|
|
2416
|
-
'laneId',
|
|
2417
|
-
'status',
|
|
2418
|
-
'artifactIds',
|
|
2419
|
-
'dispatchReceiptIds',
|
|
2420
|
-
'message',
|
|
2421
|
-
]),
|
|
2422
|
-
errors,
|
|
2423
|
-
);
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
1936
|
export function validateArtifactShape(value) {
|
|
2427
1937
|
const errors = [];
|
|
2428
1938
|
if (!isObject(value)) {
|
|
@@ -2455,8 +1965,5 @@ export function validateArtifactShape(value) {
|
|
|
2455
1965
|
if (value.kind === 'recon.review-brief')
|
|
2456
1966
|
validateReviewBriefArtifact(value, errors);
|
|
2457
1967
|
if (value.kind === 'recon.review-result') validateReviewResult(value, errors);
|
|
2458
|
-
if (value.kind === 'recon.dispatch-receipt')
|
|
2459
|
-
validateDispatchReceipt(value, errors);
|
|
2460
|
-
if (value.kind === 'recon.stage-result') validateStageResult(value, errors);
|
|
2461
1968
|
return { valid: errors.length === 0, errors };
|
|
2462
1969
|
}
|