@open-agent-toolkit/cli 0.2.72 → 0.2.74
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/agents/oat-reviewer.md +4 -3
- package/assets/agents/recon-worker.md +42 -8
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +3 -56
- package/assets/docs/cli-utilities/tool-packs.md +4 -4
- package/assets/docs/contributing/code.md +2 -2
- package/assets/docs/contributing/index.md +0 -1
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/reference/troubleshooting.md +17 -40
- package/assets/docs/workflows/projects/artifacts.md +16 -34
- package/assets/docs/workflows/projects/lifecycle.md +20 -19
- package/assets/docs/workflows/skills/explainer-kit.md +180 -440
- package/assets/docs/workflows/skills/index.md +1 -2
- package/assets/docs/workflows/skills/recon.md +114 -23
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +96 -171
- package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
- package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
- package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
- package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
- package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
- package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
- package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
- package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
- package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
- package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
- package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
- package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
- package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
- package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
- package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
- package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
- package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
- package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
- package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
- package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
- package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
- package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
- package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
- package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
- package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
- package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
- package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
- package/assets/skills/oat-project-complete/SKILL.md +168 -349
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
- package/assets/skills/oat-project-document/SKILL.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-lite/SKILL.md +1 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-plan/SKILL.md +5 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
- package/assets/skills/oat-project-summary/SKILL.md +35 -25
- package/assets/skills/oat-wave-execute/SKILL.md +11 -36
- package/assets/skills/oat-wave-program/SKILL.md +13 -37
- package/assets/skills/recon/SKILL.md +103 -40
- package/assets/skills/recon/references/packet-contract.md +150 -67
- package/assets/skills/recon/references/profiles.md +38 -16
- package/assets/skills/recon/references/worker-contract.md +39 -8
- package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
- package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
- package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
- package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
- package/assets/skills/recon/scripts/render-packet.mjs +69 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
- package/assets/skills/subagent-orchestration/SKILL.md +18 -9
- package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +9 -113
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +32 -184
- package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
- package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
- package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
- package/dist/config/oat-config.d.ts +0 -11
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +0 -34
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +0 -8
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +2 -10
- package/dist/validation/project-state.d.ts.map +1 -1
- package/dist/validation/project-state.js +42 -3
- package/package.json +2 -2
- package/assets/docs/contributing/explainer-kit-verification.md +0 -133
- package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
- package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
- package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
- package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
- package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
- package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
- package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
- package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
- package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
- package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
- package/assets/skills/explainer-kit/references/contracts.md +0 -324
- package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
- package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
- package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
- package/assets/skills/explainer-kit/references/visual-review.md +0 -71
- package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
- package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
- package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
- package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
- package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
- package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
- package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
- package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
- package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
- package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
- package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
- package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
- package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
- package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
- package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
- package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
- package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
- package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
- package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
- package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
- package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
- package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
- package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
- package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
- package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
- package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
- package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
- package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
- package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
- package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
- package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
- package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
- package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
- package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
- package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
- package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
- package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
- package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
- package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
- package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
- package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
- package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
- package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const SCHEMA_VERSION = 1;
|
|
2
|
+
export const MANIFEST_SCHEMA_VERSION = 2;
|
|
2
3
|
|
|
3
4
|
export const artifactKinds = new Set([
|
|
4
5
|
'recon.packet-manifest',
|
|
@@ -61,6 +62,110 @@ export const taskClasses = [
|
|
|
61
62
|
'consequential',
|
|
62
63
|
];
|
|
63
64
|
|
|
65
|
+
export const conditionPredicates = [
|
|
66
|
+
'insufficient-evidence',
|
|
67
|
+
'unresolved-material-challenge',
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
function profilePolicy(
|
|
71
|
+
orderedSingletonWaveModes,
|
|
72
|
+
allowedWaveModes,
|
|
73
|
+
countedLaneModes,
|
|
74
|
+
caps,
|
|
75
|
+
) {
|
|
76
|
+
return Object.freeze({
|
|
77
|
+
orderedSingletonWaveModes: Object.freeze(orderedSingletonWaveModes),
|
|
78
|
+
allowedWaveModes: Object.freeze(allowedWaveModes),
|
|
79
|
+
countedLaneModes: Object.freeze(countedLaneModes),
|
|
80
|
+
...caps,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const profileRoutingPolicy = Object.freeze({
|
|
85
|
+
quick: profilePolicy(
|
|
86
|
+
['map', 'gather', 'compile'],
|
|
87
|
+
['map', 'gather', 'compile'],
|
|
88
|
+
['gather'],
|
|
89
|
+
{
|
|
90
|
+
lanes: 4,
|
|
91
|
+
concurrency: 4,
|
|
92
|
+
conditions: 0,
|
|
93
|
+
},
|
|
94
|
+
),
|
|
95
|
+
standard: profilePolicy(
|
|
96
|
+
[
|
|
97
|
+
'map',
|
|
98
|
+
'gather',
|
|
99
|
+
'compile',
|
|
100
|
+
'semantic-verification',
|
|
101
|
+
'adversarial',
|
|
102
|
+
'coverage',
|
|
103
|
+
'reconciliation',
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
'map',
|
|
107
|
+
'gather',
|
|
108
|
+
'compile',
|
|
109
|
+
'semantic-verification',
|
|
110
|
+
'adversarial',
|
|
111
|
+
'coverage',
|
|
112
|
+
'reconciliation',
|
|
113
|
+
'contradiction-resolution',
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
'gather',
|
|
117
|
+
'semantic-verification',
|
|
118
|
+
'adversarial',
|
|
119
|
+
'coverage',
|
|
120
|
+
'contradiction-resolution',
|
|
121
|
+
],
|
|
122
|
+
{ lanes: 10, concurrency: 6, conditions: 1 },
|
|
123
|
+
),
|
|
124
|
+
thorough: profilePolicy(
|
|
125
|
+
[
|
|
126
|
+
'map',
|
|
127
|
+
'gather',
|
|
128
|
+
'redundant-gather',
|
|
129
|
+
'compile',
|
|
130
|
+
'semantic-verification',
|
|
131
|
+
'adversarial',
|
|
132
|
+
'coverage',
|
|
133
|
+
'redundant-verification',
|
|
134
|
+
'reconciliation',
|
|
135
|
+
],
|
|
136
|
+
[
|
|
137
|
+
'map',
|
|
138
|
+
'gather',
|
|
139
|
+
'compile',
|
|
140
|
+
'semantic-verification',
|
|
141
|
+
'adversarial',
|
|
142
|
+
'coverage',
|
|
143
|
+
'redundant-gather',
|
|
144
|
+
'redundant-verification',
|
|
145
|
+
'reconciliation',
|
|
146
|
+
'contradiction-resolution',
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
'gather',
|
|
150
|
+
'semantic-verification',
|
|
151
|
+
'adversarial',
|
|
152
|
+
'coverage',
|
|
153
|
+
'redundant-gather',
|
|
154
|
+
'redundant-verification',
|
|
155
|
+
'contradiction-resolution',
|
|
156
|
+
],
|
|
157
|
+
{ lanes: 20, concurrency: 8, conditions: 2 },
|
|
158
|
+
),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const supportedSchemaVersions = new Map([
|
|
162
|
+
['recon.packet-manifest', new Set([MANIFEST_SCHEMA_VERSION])],
|
|
163
|
+
['recon.claim-ledger', new Set([SCHEMA_VERSION])],
|
|
164
|
+
['recon.raw-dossier', new Set([SCHEMA_VERSION])],
|
|
165
|
+
['recon.review-brief', new Set([SCHEMA_VERSION])],
|
|
166
|
+
['recon.review-result', new Set([SCHEMA_VERSION])],
|
|
167
|
+
]);
|
|
168
|
+
|
|
64
169
|
const legalTransitions = new Set([
|
|
65
170
|
'provisional:supported',
|
|
66
171
|
'provisional:verified',
|
|
@@ -192,6 +297,22 @@ function requiredObject(value, key, errors, path = '$') {
|
|
|
192
297
|
}
|
|
193
298
|
}
|
|
194
299
|
|
|
300
|
+
function requiredNullableString(value, key, errors, path = '$') {
|
|
301
|
+
if (
|
|
302
|
+
!Object.hasOwn(value ?? {}, key) ||
|
|
303
|
+
(value[key] !== null &&
|
|
304
|
+
(typeof value[key] !== 'string' || value[key].length === 0))
|
|
305
|
+
) {
|
|
306
|
+
errors.push(
|
|
307
|
+
issue(
|
|
308
|
+
'MISSING_REQUIRED_FIELD',
|
|
309
|
+
`${key} must be a non-empty string or null`,
|
|
310
|
+
`${path}.${key}`,
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
195
316
|
function closedObject(value, allowed, errors, path = '$') {
|
|
196
317
|
if (!isObject(value)) return;
|
|
197
318
|
for (const key of Object.keys(value)) {
|
|
@@ -220,7 +341,7 @@ function duplicateIds(values, path, errors) {
|
|
|
220
341
|
}
|
|
221
342
|
}
|
|
222
343
|
|
|
223
|
-
const
|
|
344
|
+
const exactTargetKeys = new Set([
|
|
224
345
|
'provider',
|
|
225
346
|
'route',
|
|
226
347
|
'role',
|
|
@@ -228,27 +349,22 @@ const executionKeys = new Set([
|
|
|
228
349
|
'effort',
|
|
229
350
|
'reasoningMode',
|
|
230
351
|
'serviceTier',
|
|
352
|
+
]);
|
|
353
|
+
|
|
354
|
+
const executionV2Keys = new Set([
|
|
355
|
+
'target',
|
|
231
356
|
'authority',
|
|
232
357
|
'maxConcurrency',
|
|
233
358
|
'deadlineSeconds',
|
|
234
359
|
'retryLimit',
|
|
235
360
|
'waves',
|
|
361
|
+
'conditions',
|
|
236
362
|
'approval',
|
|
237
363
|
]);
|
|
238
364
|
|
|
239
|
-
export function approvalFingerprintInput(execution) {
|
|
240
|
-
const { approval: _approval, ...approved } = execution;
|
|
241
|
-
return approved;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
365
|
function validateApprovalEvidence(value, errors, path) {
|
|
245
366
|
if (!isObject(value)) return;
|
|
246
|
-
closedObject(
|
|
247
|
-
value,
|
|
248
|
-
new Set(['type', 'approvedAt', 'fingerprint']),
|
|
249
|
-
errors,
|
|
250
|
-
path,
|
|
251
|
-
);
|
|
367
|
+
closedObject(value, new Set(['type', 'approvedAt']), errors, path);
|
|
252
368
|
if (value.type !== 'explicit-user-approval') {
|
|
253
369
|
errors.push(
|
|
254
370
|
issue(
|
|
@@ -259,34 +375,24 @@ function validateApprovalEvidence(value, errors, path) {
|
|
|
259
375
|
);
|
|
260
376
|
}
|
|
261
377
|
requiredTimestamp(value, 'approvedAt', errors, path);
|
|
262
|
-
if (!isDigest(value.fingerprint)) {
|
|
263
|
-
errors.push(
|
|
264
|
-
issue(
|
|
265
|
-
'INVALID_APPROVAL_FINGERPRINT',
|
|
266
|
-
'Approval evidence fingerprint must be sha256',
|
|
267
|
-
`${path}.fingerprint`,
|
|
268
|
-
),
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
378
|
}
|
|
272
379
|
|
|
273
|
-
|
|
380
|
+
function validateExactTarget(value, errors, path) {
|
|
274
381
|
if (!isObject(value)) return;
|
|
275
|
-
closedObject(value,
|
|
276
|
-
for (const key of ['provider', 'route', 'role', 'model'
|
|
382
|
+
closedObject(value, exactTargetKeys, errors, path);
|
|
383
|
+
for (const key of ['provider', 'route', 'role', 'model']) {
|
|
277
384
|
requiredString(value, key, errors, path);
|
|
278
385
|
}
|
|
279
|
-
for (const key of ['reasoningMode', 'serviceTier']) {
|
|
280
|
-
|
|
281
|
-
errors.push(
|
|
282
|
-
issue(
|
|
283
|
-
'MISSING_REQUIRED_FIELD',
|
|
284
|
-
`${key} must be a string or null`,
|
|
285
|
-
`${path}.${key}`,
|
|
286
|
-
),
|
|
287
|
-
);
|
|
288
|
-
}
|
|
386
|
+
for (const key of ['effort', 'reasoningMode', 'serviceTier']) {
|
|
387
|
+
requiredNullableString(value, key, errors, path);
|
|
289
388
|
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function validateExecutionV2(value, errors, path) {
|
|
392
|
+
if (!isObject(value)) return;
|
|
393
|
+
closedObject(value, executionV2Keys, errors, path);
|
|
394
|
+
requiredObject(value, 'target', errors, path);
|
|
395
|
+
validateExactTarget(value.target, errors, `${path}.target`);
|
|
290
396
|
if (!authorityLevels.includes(value.authority)) {
|
|
291
397
|
errors.push(
|
|
292
398
|
issue(
|
|
@@ -300,20 +406,47 @@ export function validateExecution(value, errors, path = '$.execution') {
|
|
|
300
406
|
requiredInteger(value, 'deadlineSeconds', errors, path, 1);
|
|
301
407
|
requiredInteger(value, 'retryLimit', errors, path, 0);
|
|
302
408
|
requiredArray(value, 'waves', errors, path);
|
|
409
|
+
requiredArray(value, 'conditions', errors, path);
|
|
303
410
|
requiredObject(value, 'approval', errors, path);
|
|
304
411
|
validateApprovalEvidence(value.approval, errors, `${path}.approval`);
|
|
412
|
+
|
|
413
|
+
const waves = Array.isArray(value.waves) ? value.waves : [];
|
|
305
414
|
const waveIds = new Set();
|
|
306
415
|
const laneIds = new Set();
|
|
307
|
-
|
|
416
|
+
const writeRoots = new Set();
|
|
417
|
+
for (const [waveIndex, wave] of waves.entries()) {
|
|
308
418
|
const wavePath = `${path}.waves[${waveIndex}]`;
|
|
309
419
|
closedObject(
|
|
310
420
|
wave,
|
|
311
|
-
new Set([
|
|
421
|
+
new Set([
|
|
422
|
+
'waveId',
|
|
423
|
+
'mode',
|
|
424
|
+
'taskClass',
|
|
425
|
+
'classFloor',
|
|
426
|
+
'selectionReason',
|
|
427
|
+
'target',
|
|
428
|
+
'lanes',
|
|
429
|
+
'conditional',
|
|
430
|
+
]),
|
|
312
431
|
errors,
|
|
313
432
|
wavePath,
|
|
314
433
|
);
|
|
315
434
|
requiredString(wave, 'waveId', errors, wavePath);
|
|
435
|
+
requiredString(wave, 'selectionReason', errors, wavePath);
|
|
316
436
|
requiredArray(wave, 'lanes', errors, wavePath);
|
|
437
|
+
if (Object.hasOwn(wave ?? {}, 'target')) {
|
|
438
|
+
if (!isObject(wave.target)) {
|
|
439
|
+
errors.push(
|
|
440
|
+
issue(
|
|
441
|
+
'MISSING_REQUIRED_FIELD',
|
|
442
|
+
'target must be a complete exact target object',
|
|
443
|
+
`${wavePath}.target`,
|
|
444
|
+
),
|
|
445
|
+
);
|
|
446
|
+
} else {
|
|
447
|
+
validateExactTarget(wave.target, errors, `${wavePath}.target`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
317
450
|
if (Array.isArray(wave?.lanes) && wave.lanes.length === 0) {
|
|
318
451
|
errors.push(
|
|
319
452
|
issue(
|
|
@@ -328,7 +461,9 @@ export function validateExecution(value, errors, path = '$.execution') {
|
|
|
328
461
|
issue('INVALID_WAVE_MODE', 'Unknown wave mode', `${wavePath}.mode`),
|
|
329
462
|
);
|
|
330
463
|
}
|
|
331
|
-
|
|
464
|
+
const taskClassIndex = taskClasses.indexOf(wave?.taskClass);
|
|
465
|
+
const classFloorIndex = taskClasses.indexOf(wave?.classFloor);
|
|
466
|
+
if (taskClassIndex === -1) {
|
|
332
467
|
errors.push(
|
|
333
468
|
issue(
|
|
334
469
|
'INVALID_TASK_CLASS',
|
|
@@ -337,6 +472,23 @@ export function validateExecution(value, errors, path = '$.execution') {
|
|
|
337
472
|
),
|
|
338
473
|
);
|
|
339
474
|
}
|
|
475
|
+
if (classFloorIndex === -1) {
|
|
476
|
+
errors.push(
|
|
477
|
+
issue(
|
|
478
|
+
'INVALID_TASK_CLASS',
|
|
479
|
+
'Unknown wave class floor',
|
|
480
|
+
`${wavePath}.classFloor`,
|
|
481
|
+
),
|
|
482
|
+
);
|
|
483
|
+
} else if (taskClassIndex !== -1 && taskClassIndex < classFloorIndex) {
|
|
484
|
+
errors.push(
|
|
485
|
+
issue(
|
|
486
|
+
'TASK_CLASS_BELOW_FLOOR',
|
|
487
|
+
'Wave task class must meet or exceed its declared class floor',
|
|
488
|
+
`${wavePath}.taskClass`,
|
|
489
|
+
),
|
|
490
|
+
);
|
|
491
|
+
}
|
|
340
492
|
if (typeof wave?.conditional !== 'boolean') {
|
|
341
493
|
errors.push(
|
|
342
494
|
issue(
|
|
@@ -393,10 +545,425 @@ export function validateExecution(value, errors, path = '$.execution') {
|
|
|
393
545
|
);
|
|
394
546
|
}
|
|
395
547
|
laneIds.add(lane?.laneId);
|
|
548
|
+
if (
|
|
549
|
+
typeof lane?.writeRoot === 'string' &&
|
|
550
|
+
writeRoots.has(lane.writeRoot)
|
|
551
|
+
) {
|
|
552
|
+
errors.push(
|
|
553
|
+
issue(
|
|
554
|
+
'DUPLICATE_WAVE_OUTPUT',
|
|
555
|
+
`Wave output ${lane.writeRoot} is assigned more than once`,
|
|
556
|
+
`${lanePath}.writeRoot`,
|
|
557
|
+
),
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
if (typeof lane?.writeRoot === 'string') writeRoots.add(lane.writeRoot);
|
|
396
561
|
}
|
|
397
562
|
}
|
|
398
563
|
}
|
|
399
564
|
|
|
565
|
+
export function validateV2ProfileTopology(
|
|
566
|
+
manifest,
|
|
567
|
+
execution = manifest?.execution,
|
|
568
|
+
path = '$.execution',
|
|
569
|
+
) {
|
|
570
|
+
const errors = [];
|
|
571
|
+
if (manifest?.schemaVersion !== MANIFEST_SCHEMA_VERSION) return errors;
|
|
572
|
+
const requestedProfile = manifest.run?.requestedProfile;
|
|
573
|
+
const policy = profileRoutingPolicy[requestedProfile];
|
|
574
|
+
if (!policy) {
|
|
575
|
+
return [
|
|
576
|
+
issue(
|
|
577
|
+
'INVALID_ROUTING_PROFILE',
|
|
578
|
+
'A v2 routing proposal requires a supported requested profile',
|
|
579
|
+
'$.run.requestedProfile',
|
|
580
|
+
),
|
|
581
|
+
];
|
|
582
|
+
}
|
|
583
|
+
if (!isObject(execution)) return errors;
|
|
584
|
+
if (!Array.isArray(execution.waves)) {
|
|
585
|
+
return [
|
|
586
|
+
issue(
|
|
587
|
+
'MISSING_ROUTING_WAVES',
|
|
588
|
+
'execution.waves must be an array',
|
|
589
|
+
`${path}.waves`,
|
|
590
|
+
),
|
|
591
|
+
];
|
|
592
|
+
}
|
|
593
|
+
if (!Array.isArray(execution.conditions)) {
|
|
594
|
+
return [
|
|
595
|
+
issue(
|
|
596
|
+
'MISSING_ROUTING_CONDITIONS',
|
|
597
|
+
'execution.conditions must be an array',
|
|
598
|
+
`${path}.conditions`,
|
|
599
|
+
),
|
|
600
|
+
];
|
|
601
|
+
}
|
|
602
|
+
const waves = Array.isArray(execution.waves) ? execution.waves : [];
|
|
603
|
+
const conditions = Array.isArray(execution.conditions)
|
|
604
|
+
? execution.conditions
|
|
605
|
+
: [];
|
|
606
|
+
const forbiddenModes = waves
|
|
607
|
+
.filter(
|
|
608
|
+
(wave) =>
|
|
609
|
+
typeof wave?.mode === 'string' &&
|
|
610
|
+
!policy.allowedWaveModes.includes(wave.mode),
|
|
611
|
+
)
|
|
612
|
+
.map((wave) => wave.mode);
|
|
613
|
+
if (forbiddenModes.length > 0) {
|
|
614
|
+
errors.push(
|
|
615
|
+
issue(
|
|
616
|
+
'WAVE_MODE_NOT_ALLOWED_FOR_PROFILE',
|
|
617
|
+
`${requestedProfile} routing does not permit wave modes: ${[
|
|
618
|
+
...new Set(forbiddenModes),
|
|
619
|
+
].join(', ')}`,
|
|
620
|
+
`${path}.waves`,
|
|
621
|
+
),
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
for (const [waveIndex, wave] of waves.entries()) {
|
|
625
|
+
if (
|
|
626
|
+
policy.allowedWaveModes.includes(wave?.mode) &&
|
|
627
|
+
!policy.countedLaneModes.includes(wave.mode) &&
|
|
628
|
+
Array.isArray(wave?.lanes) &&
|
|
629
|
+
wave.lanes.length !== 1
|
|
630
|
+
) {
|
|
631
|
+
errors.push(
|
|
632
|
+
issue(
|
|
633
|
+
'INVALID_PROFILE_SINGLETON_LANE_COUNT',
|
|
634
|
+
`${requestedProfile} ${wave.mode} waves require exactly one lane`,
|
|
635
|
+
`${path}.waves[${waveIndex}].lanes`,
|
|
636
|
+
),
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const laneCount = waves.reduce(
|
|
641
|
+
(count, wave) =>
|
|
642
|
+
count +
|
|
643
|
+
(policy.countedLaneModes.includes(wave?.mode) &&
|
|
644
|
+
Array.isArray(wave?.lanes)
|
|
645
|
+
? wave.lanes.length
|
|
646
|
+
: 0),
|
|
647
|
+
0,
|
|
648
|
+
);
|
|
649
|
+
if (laneCount > policy.lanes) {
|
|
650
|
+
errors.push(
|
|
651
|
+
issue(
|
|
652
|
+
'PROFILE_LANE_CAP_EXCEEDED',
|
|
653
|
+
`${requestedProfile} routing permits at most ${policy.lanes} adaptive evidence lanes`,
|
|
654
|
+
`${path}.waves`,
|
|
655
|
+
),
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
if (execution.maxConcurrency > policy.concurrency) {
|
|
659
|
+
errors.push(
|
|
660
|
+
issue(
|
|
661
|
+
'PROFILE_CONCURRENCY_CAP_EXCEEDED',
|
|
662
|
+
`${requestedProfile} routing permits concurrency at most ${policy.concurrency}`,
|
|
663
|
+
`${path}.maxConcurrency`,
|
|
664
|
+
),
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
if (conditions.length > policy.conditions) {
|
|
668
|
+
errors.push(
|
|
669
|
+
issue(
|
|
670
|
+
'PROFILE_CONDITION_CAP_EXCEEDED',
|
|
671
|
+
`${requestedProfile} routing permits at most ${policy.conditions} conditional waves`,
|
|
672
|
+
`${path}.conditions`,
|
|
673
|
+
),
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
const reconciliationIndexes = waves
|
|
677
|
+
.map((wave, index) => (wave?.mode === 'reconciliation' ? index : -1))
|
|
678
|
+
.filter((index) => index !== -1);
|
|
679
|
+
const requiresReconciliation = ['standard', 'thorough'].includes(
|
|
680
|
+
requestedProfile,
|
|
681
|
+
);
|
|
682
|
+
if (
|
|
683
|
+
reconciliationIndexes.length > 1 ||
|
|
684
|
+
(requiresReconciliation && reconciliationIndexes.length !== 1) ||
|
|
685
|
+
reconciliationIndexes.some((index) => index !== waves.length - 1) ||
|
|
686
|
+
reconciliationIndexes.some((index) => waves[index]?.conditional === true)
|
|
687
|
+
) {
|
|
688
|
+
errors.push(
|
|
689
|
+
issue(
|
|
690
|
+
'INVALID_TERMINAL_TOPOLOGY',
|
|
691
|
+
'Standard and thorough routing require exactly one non-conditional terminal reconciliation wave',
|
|
692
|
+
`${path}.waves`,
|
|
693
|
+
),
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const waveIndexes = new Map(
|
|
698
|
+
waves
|
|
699
|
+
.map((wave, index) => (isObject(wave) ? [wave.waveId, index] : null))
|
|
700
|
+
.filter(Boolean),
|
|
701
|
+
);
|
|
702
|
+
const conditionIds = new Set();
|
|
703
|
+
const conditionDestinations = new Set();
|
|
704
|
+
const conditionFields = new Set([
|
|
705
|
+
'conditionId',
|
|
706
|
+
'destinationWaveId',
|
|
707
|
+
'afterWaveIds',
|
|
708
|
+
'predicate',
|
|
709
|
+
'maxActivations',
|
|
710
|
+
]);
|
|
711
|
+
const terminalIndex = reconciliationIndexes[0] ?? -1;
|
|
712
|
+
for (const [index, condition] of conditions.entries()) {
|
|
713
|
+
const conditionPath = `${path}.conditions[${index}]`;
|
|
714
|
+
if (
|
|
715
|
+
!isObject(condition) ||
|
|
716
|
+
Object.keys(condition).some((key) => !conditionFields.has(key))
|
|
717
|
+
) {
|
|
718
|
+
errors.push(
|
|
719
|
+
issue(
|
|
720
|
+
'INVALID_ROUTING_CONDITION',
|
|
721
|
+
'Routing condition must be a closed object',
|
|
722
|
+
conditionPath,
|
|
723
|
+
),
|
|
724
|
+
);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (
|
|
728
|
+
typeof condition.conditionId !== 'string' ||
|
|
729
|
+
condition.conditionId.length === 0 ||
|
|
730
|
+
typeof condition.destinationWaveId !== 'string' ||
|
|
731
|
+
condition.destinationWaveId.length === 0
|
|
732
|
+
) {
|
|
733
|
+
errors.push(
|
|
734
|
+
issue(
|
|
735
|
+
'INVALID_ROUTING_CONDITION',
|
|
736
|
+
'Condition and destination IDs must be non-empty strings',
|
|
737
|
+
conditionPath,
|
|
738
|
+
),
|
|
739
|
+
);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (
|
|
743
|
+
!Array.isArray(condition.afterWaveIds) ||
|
|
744
|
+
condition.afterWaveIds.length === 0
|
|
745
|
+
) {
|
|
746
|
+
errors.push(
|
|
747
|
+
issue(
|
|
748
|
+
'INVALID_CONDITION_DEPENDENCY',
|
|
749
|
+
'Condition dependencies must contain at least one predecessor wave',
|
|
750
|
+
`${conditionPath}.afterWaveIds`,
|
|
751
|
+
),
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
if (!conditionPredicates.includes(condition.predicate)) {
|
|
755
|
+
errors.push(
|
|
756
|
+
issue(
|
|
757
|
+
'INVALID_CONDITION_PREDICATE',
|
|
758
|
+
'Unknown routing condition predicate',
|
|
759
|
+
`${conditionPath}.predicate`,
|
|
760
|
+
),
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
if (condition.maxActivations !== 1) {
|
|
764
|
+
errors.push(
|
|
765
|
+
issue(
|
|
766
|
+
'INVALID_CONDITION_LIMIT',
|
|
767
|
+
'Routing conditions must allow exactly one activation',
|
|
768
|
+
`${conditionPath}.maxActivations`,
|
|
769
|
+
),
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
if (conditionIds.has(condition.conditionId)) {
|
|
773
|
+
errors.push(
|
|
774
|
+
issue(
|
|
775
|
+
'DUPLICATE_ROUTING_ID',
|
|
776
|
+
`Duplicate condition ${condition.conditionId}`,
|
|
777
|
+
`${conditionPath}.conditionId`,
|
|
778
|
+
),
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
conditionIds.add(condition.conditionId);
|
|
782
|
+
if (conditionDestinations.has(condition.destinationWaveId)) {
|
|
783
|
+
errors.push(
|
|
784
|
+
issue(
|
|
785
|
+
'DUPLICATE_CONDITION_DESTINATION',
|
|
786
|
+
`Conditional wave ${condition.destinationWaveId} has more than one activating condition`,
|
|
787
|
+
`${conditionPath}.destinationWaveId`,
|
|
788
|
+
),
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
conditionDestinations.add(condition.destinationWaveId);
|
|
792
|
+
|
|
793
|
+
const destinationIndex = waveIndexes.get(condition.destinationWaveId);
|
|
794
|
+
const destination =
|
|
795
|
+
destinationIndex === undefined ? null : waves[destinationIndex];
|
|
796
|
+
if (
|
|
797
|
+
!destination ||
|
|
798
|
+
destination.conditional !== true ||
|
|
799
|
+
destination.mode !== 'contradiction-resolution'
|
|
800
|
+
) {
|
|
801
|
+
errors.push(
|
|
802
|
+
issue(
|
|
803
|
+
'INVALID_CONDITION_DESTINATION',
|
|
804
|
+
'Condition destination must name a conditional contradiction-resolution wave',
|
|
805
|
+
`${conditionPath}.destinationWaveId`,
|
|
806
|
+
),
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
const afterIds = new Set();
|
|
810
|
+
for (const afterWaveId of Array.isArray(condition.afterWaveIds)
|
|
811
|
+
? condition.afterWaveIds
|
|
812
|
+
: []) {
|
|
813
|
+
if (typeof afterWaveId !== 'string' || afterWaveId.length === 0) {
|
|
814
|
+
errors.push(
|
|
815
|
+
issue(
|
|
816
|
+
'INVALID_CONDITION_DEPENDENCY',
|
|
817
|
+
'Condition dependencies must be non-empty wave ID strings',
|
|
818
|
+
`${conditionPath}.afterWaveIds`,
|
|
819
|
+
),
|
|
820
|
+
);
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
if (afterIds.has(afterWaveId)) {
|
|
824
|
+
errors.push(
|
|
825
|
+
issue(
|
|
826
|
+
'DUPLICATE_CONDITION_DEPENDENCY',
|
|
827
|
+
`Condition repeats predecessor ${afterWaveId}`,
|
|
828
|
+
`${conditionPath}.afterWaveIds`,
|
|
829
|
+
),
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
afterIds.add(afterWaveId);
|
|
833
|
+
const predecessorIndex = waveIndexes.get(afterWaveId);
|
|
834
|
+
if (predecessorIndex === undefined) {
|
|
835
|
+
errors.push(
|
|
836
|
+
issue(
|
|
837
|
+
'UNKNOWN_CONDITION_WAVE',
|
|
838
|
+
`Unknown predecessor wave ${afterWaveId}`,
|
|
839
|
+
`${conditionPath}.afterWaveIds`,
|
|
840
|
+
),
|
|
841
|
+
);
|
|
842
|
+
} else if (
|
|
843
|
+
destinationIndex !== undefined &&
|
|
844
|
+
predecessorIndex >= destinationIndex
|
|
845
|
+
) {
|
|
846
|
+
errors.push(
|
|
847
|
+
issue(
|
|
848
|
+
'NON_FORWARD_CONDITION',
|
|
849
|
+
`Condition predecessor ${afterWaveId} must appear before ${condition.destinationWaveId}`,
|
|
850
|
+
`${conditionPath}.afterWaveIds`,
|
|
851
|
+
),
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (
|
|
856
|
+
terminalIndex !== -1 &&
|
|
857
|
+
destinationIndex !== undefined &&
|
|
858
|
+
destinationIndex >= terminalIndex
|
|
859
|
+
) {
|
|
860
|
+
errors.push(
|
|
861
|
+
issue(
|
|
862
|
+
'INVALID_TERMINAL_TOPOLOGY',
|
|
863
|
+
'Conditional evidence waves must complete before the one terminal reconciliation',
|
|
864
|
+
`${conditionPath}.destinationWaveId`,
|
|
865
|
+
),
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
for (const wave of waves) {
|
|
870
|
+
if (
|
|
871
|
+
wave?.conditional === true &&
|
|
872
|
+
wave.mode !== 'reconciliation' &&
|
|
873
|
+
!policy.orderedSingletonWaveModes.includes(wave.mode) &&
|
|
874
|
+
!conditionDestinations.has(wave.waveId)
|
|
875
|
+
) {
|
|
876
|
+
errors.push(
|
|
877
|
+
issue(
|
|
878
|
+
'MISSING_WAVE_CONDITION',
|
|
879
|
+
`Conditional wave ${wave.waveId} must be bound to exactly one activating condition`,
|
|
880
|
+
`${path}.waves`,
|
|
881
|
+
),
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const unconditionalContradiction = waves.find(
|
|
886
|
+
(wave) =>
|
|
887
|
+
wave?.mode === 'contradiction-resolution' && wave.conditional !== true,
|
|
888
|
+
);
|
|
889
|
+
if (unconditionalContradiction) {
|
|
890
|
+
errors.push(
|
|
891
|
+
issue(
|
|
892
|
+
'UNCONDITIONAL_CONTRADICTION_RESOLUTION',
|
|
893
|
+
`Contradiction-resolution wave ${unconditionalContradiction.waveId} must be conditional and condition-bound`,
|
|
894
|
+
`${path}.waves`,
|
|
895
|
+
),
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const stageIndexes = policy.orderedSingletonWaveModes.map((mode) => ({
|
|
900
|
+
mode,
|
|
901
|
+
indexes: waves
|
|
902
|
+
.map((wave, index) => (wave?.mode === mode ? index : -1))
|
|
903
|
+
.filter((index) => index !== -1),
|
|
904
|
+
}));
|
|
905
|
+
const duplicateModes = stageIndexes
|
|
906
|
+
.filter(({ indexes }) => indexes.length > 1)
|
|
907
|
+
.map(({ mode }) => mode);
|
|
908
|
+
if (duplicateModes.length > 0) {
|
|
909
|
+
errors.push(
|
|
910
|
+
issue(
|
|
911
|
+
'DUPLICATE_PROFILE_WAVE_MODE',
|
|
912
|
+
`${requestedProfile} routing requires one wave for singleton modes: ${duplicateModes.join(', ')}`,
|
|
913
|
+
`${path}.waves`,
|
|
914
|
+
),
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
const missingModes = stageIndexes
|
|
918
|
+
.filter(({ indexes }) => indexes.length === 0)
|
|
919
|
+
.map(({ mode }) => mode);
|
|
920
|
+
if (missingModes.length > 0) {
|
|
921
|
+
errors.push(
|
|
922
|
+
issue(
|
|
923
|
+
'INCOMPLETE_PROFILE_TOPOLOGY',
|
|
924
|
+
`${requestedProfile} routing is missing required non-conditional wave modes: ${missingModes.join(', ')}`,
|
|
925
|
+
`${path}.waves`,
|
|
926
|
+
),
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
const conditionalRequired = stageIndexes.find(
|
|
930
|
+
({ mode, indexes }) =>
|
|
931
|
+
mode !== 'reconciliation' &&
|
|
932
|
+
indexes.length === 1 &&
|
|
933
|
+
waves[indexes[0]]?.conditional,
|
|
934
|
+
);
|
|
935
|
+
if (conditionalRequired) {
|
|
936
|
+
errors.push(
|
|
937
|
+
issue(
|
|
938
|
+
'INCOMPLETE_PROFILE_TOPOLOGY',
|
|
939
|
+
`${requestedProfile} required wave mode ${conditionalRequired.mode} must be non-conditional`,
|
|
940
|
+
`${path}.waves`,
|
|
941
|
+
),
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
const orderedIndexes = stageIndexes.map(({ indexes }) => indexes[0]);
|
|
945
|
+
if (
|
|
946
|
+
orderedIndexes.every((index) => index !== undefined) &&
|
|
947
|
+
orderedIndexes.some(
|
|
948
|
+
(index, position) =>
|
|
949
|
+
position > 0 && index <= orderedIndexes[position - 1],
|
|
950
|
+
)
|
|
951
|
+
) {
|
|
952
|
+
errors.push(
|
|
953
|
+
issue(
|
|
954
|
+
'OUT_OF_ORDER_PROFILE_TOPOLOGY',
|
|
955
|
+
`${requestedProfile} routing must order singleton modes as: ${policy.orderedSingletonWaveModes.join(', ')}`,
|
|
956
|
+
`${path}.waves`,
|
|
957
|
+
),
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
return errors;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
export function validateExecution(value, errors, path = '$.execution') {
|
|
964
|
+
validateExecutionV2(value, errors, path);
|
|
965
|
+
}
|
|
966
|
+
|
|
400
967
|
function validateManifest(value, errors) {
|
|
401
968
|
closedObject(
|
|
402
969
|
value,
|
|
@@ -409,6 +976,7 @@ function validateManifest(value, errors) {
|
|
|
409
976
|
'execution',
|
|
410
977
|
'artifacts',
|
|
411
978
|
'gaps',
|
|
979
|
+
'conditionOutcomes',
|
|
412
980
|
]),
|
|
413
981
|
errors,
|
|
414
982
|
);
|
|
@@ -500,13 +1068,76 @@ function validateManifest(value, errors) {
|
|
|
500
1068
|
}
|
|
501
1069
|
}
|
|
502
1070
|
validateExecution(value.execution, errors, '$.execution');
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
1071
|
+
errors.push(...validateV2ProfileTopology(value));
|
|
1072
|
+
requiredArray(value, 'conditionOutcomes', errors);
|
|
1073
|
+
const sources = Array.isArray(value.sources) ? value.sources : [];
|
|
1074
|
+
const artifacts = Array.isArray(value.artifacts) ? value.artifacts : [];
|
|
1075
|
+
const gaps = Array.isArray(value.gaps) ? value.gaps : [];
|
|
1076
|
+
const outcomeIds = new Set();
|
|
1077
|
+
for (const [index, outcome] of (Array.isArray(value.conditionOutcomes)
|
|
1078
|
+
? value.conditionOutcomes
|
|
1079
|
+
: []
|
|
1080
|
+
).entries()) {
|
|
1081
|
+
const outcomePath = `$.conditionOutcomes[${index}]`;
|
|
1082
|
+
if (!isObject(outcome)) {
|
|
1083
|
+
errors.push(
|
|
1084
|
+
issue(
|
|
1085
|
+
'INVALID_CONDITION_OUTCOME',
|
|
1086
|
+
'Condition outcome must be an object',
|
|
1087
|
+
outcomePath,
|
|
1088
|
+
),
|
|
1089
|
+
);
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
closedObject(
|
|
1093
|
+
outcome,
|
|
1094
|
+
new Set(['conditionId', 'disposition', 'reason', 'evidence']),
|
|
1095
|
+
errors,
|
|
1096
|
+
outcomePath,
|
|
1097
|
+
);
|
|
1098
|
+
requiredString(outcome, 'conditionId', errors, outcomePath);
|
|
1099
|
+
requiredString(outcome, 'reason', errors, outcomePath);
|
|
1100
|
+
requiredArray(outcome, 'evidence', errors, outcomePath);
|
|
1101
|
+
if (
|
|
1102
|
+
!['triggered', 'not-triggered', 'unresolved'].includes(
|
|
1103
|
+
outcome.disposition,
|
|
1104
|
+
)
|
|
1105
|
+
) {
|
|
1106
|
+
errors.push(
|
|
1107
|
+
issue(
|
|
1108
|
+
'INVALID_CONDITION_DISPOSITION',
|
|
1109
|
+
'Condition disposition must be triggered, not-triggered, or unresolved',
|
|
1110
|
+
`${outcomePath}.disposition`,
|
|
1111
|
+
),
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
for (const [evidenceIndex, reference] of (Array.isArray(outcome.evidence)
|
|
1115
|
+
? outcome.evidence
|
|
1116
|
+
: []
|
|
1117
|
+
).entries()) {
|
|
1118
|
+
validateExactReference(
|
|
1119
|
+
reference,
|
|
1120
|
+
`${outcomePath}.evidence[${evidenceIndex}]`,
|
|
1121
|
+
errors,
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
if (outcomeIds.has(outcome.conditionId)) {
|
|
1125
|
+
errors.push(
|
|
1126
|
+
issue(
|
|
1127
|
+
'DUPLICATE_CONDITION_OUTCOME',
|
|
1128
|
+
`Condition ${outcome.conditionId} has more than one disposition`,
|
|
1129
|
+
`${outcomePath}.conditionId`,
|
|
1130
|
+
),
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
outcomeIds.add(outcome.conditionId);
|
|
1134
|
+
}
|
|
1135
|
+
duplicateIds(sources, '$.sources', errors);
|
|
1136
|
+
duplicateIds(gaps, '$.gaps', errors);
|
|
1137
|
+
for (const [index, reference] of artifacts.entries()) {
|
|
507
1138
|
validateExactReference(reference, `$.artifacts[${index}]`, errors);
|
|
508
1139
|
}
|
|
509
|
-
for (const [index, source] of
|
|
1140
|
+
for (const [index, source] of sources.entries()) {
|
|
510
1141
|
for (const key of [
|
|
511
1142
|
'kind',
|
|
512
1143
|
'id',
|
|
@@ -615,7 +1246,7 @@ function validateManifest(value, errors) {
|
|
|
615
1246
|
}
|
|
616
1247
|
}
|
|
617
1248
|
}
|
|
618
|
-
for (const [index, gap] of
|
|
1249
|
+
for (const [index, gap] of gaps.entries()) {
|
|
619
1250
|
requiredString(gap, 'id', errors, `$.gaps[${index}]`);
|
|
620
1251
|
requiredString(gap, 'code', errors, `$.gaps[${index}]`);
|
|
621
1252
|
requiredString(gap, 'message', errors, `$.gaps[${index}]`);
|
|
@@ -635,6 +1266,8 @@ function validateManifest(value, errors) {
|
|
|
635
1266
|
'code',
|
|
636
1267
|
'message',
|
|
637
1268
|
'material',
|
|
1269
|
+
'waveId',
|
|
1270
|
+
'laneId',
|
|
638
1271
|
'sourceIds',
|
|
639
1272
|
'claimIds',
|
|
640
1273
|
'coverageFindingIds',
|
|
@@ -642,6 +1275,10 @@ function validateManifest(value, errors) {
|
|
|
642
1275
|
errors,
|
|
643
1276
|
`$.gaps[${index}]`,
|
|
644
1277
|
);
|
|
1278
|
+
if (Object.hasOwn(gap, 'waveId') || Object.hasOwn(gap, 'laneId')) {
|
|
1279
|
+
requiredString(gap, 'waveId', errors, `$.gaps[${index}]`);
|
|
1280
|
+
requiredString(gap, 'laneId', errors, `$.gaps[${index}]`);
|
|
1281
|
+
}
|
|
645
1282
|
}
|
|
646
1283
|
}
|
|
647
1284
|
|
|
@@ -757,10 +1394,16 @@ function validateLedger(value, errors) {
|
|
|
757
1394
|
? value.evidence.map((item) => item?.id).filter(Boolean)
|
|
758
1395
|
: [],
|
|
759
1396
|
);
|
|
760
|
-
|
|
1397
|
+
const inputArtifacts = Array.isArray(value.inputArtifacts)
|
|
1398
|
+
? value.inputArtifacts
|
|
1399
|
+
: [];
|
|
1400
|
+
const evidenceItems = Array.isArray(value.evidence) ? value.evidence : [];
|
|
1401
|
+
const claims = Array.isArray(value.claims) ? value.claims : [];
|
|
1402
|
+
const transitions = Array.isArray(value.transitions) ? value.transitions : [];
|
|
1403
|
+
for (const [index, reference] of inputArtifacts.entries()) {
|
|
761
1404
|
validateExactReference(reference, `$.inputArtifacts[${index}]`, errors);
|
|
762
1405
|
}
|
|
763
|
-
for (const [index, evidence] of
|
|
1406
|
+
for (const [index, evidence] of evidenceItems.entries()) {
|
|
764
1407
|
closedObject(
|
|
765
1408
|
evidence,
|
|
766
1409
|
new Set([
|
|
@@ -857,7 +1500,7 @@ function validateLedger(value, errors) {
|
|
|
857
1500
|
errors,
|
|
858
1501
|
);
|
|
859
1502
|
}
|
|
860
|
-
for (const [index, claim] of
|
|
1503
|
+
for (const [index, claim] of claims.entries()) {
|
|
861
1504
|
closedObject(
|
|
862
1505
|
claim,
|
|
863
1506
|
new Set([
|
|
@@ -894,7 +1537,10 @@ function validateLedger(value, errors) {
|
|
|
894
1537
|
requiredArray(claim, key, errors, `$.claims[${index}]`);
|
|
895
1538
|
}
|
|
896
1539
|
const linkedEvidenceIds = new Set();
|
|
897
|
-
for (const [linkIndex, link] of (claim.evidence
|
|
1540
|
+
for (const [linkIndex, link] of (Array.isArray(claim.evidence)
|
|
1541
|
+
? claim.evidence
|
|
1542
|
+
: []
|
|
1543
|
+
).entries()) {
|
|
898
1544
|
const linkPath = `$.claims[${index}].evidence[${linkIndex}]`;
|
|
899
1545
|
if (!isObject(link)) {
|
|
900
1546
|
errors.push(
|
|
@@ -940,8 +1586,9 @@ function validateLedger(value, errors) {
|
|
|
940
1586
|
}
|
|
941
1587
|
linkedEvidenceIds.add(link.evidenceId);
|
|
942
1588
|
}
|
|
943
|
-
for (const [referenceIndex, reference] of (
|
|
944
|
-
claim.derivedFrom
|
|
1589
|
+
for (const [referenceIndex, reference] of (Array.isArray(claim.derivedFrom)
|
|
1590
|
+
? claim.derivedFrom
|
|
1591
|
+
: []
|
|
945
1592
|
).entries()) {
|
|
946
1593
|
validateExactReference(
|
|
947
1594
|
reference,
|
|
@@ -950,7 +1597,7 @@ function validateLedger(value, errors) {
|
|
|
950
1597
|
);
|
|
951
1598
|
}
|
|
952
1599
|
}
|
|
953
|
-
for (const [index, transition] of
|
|
1600
|
+
for (const [index, transition] of transitions.entries()) {
|
|
954
1601
|
closedObject(
|
|
955
1602
|
transition,
|
|
956
1603
|
new Set(['claimId', 'from', 'to']),
|
|
@@ -967,9 +1614,9 @@ function validateLedger(value, errors) {
|
|
|
967
1614
|
);
|
|
968
1615
|
}
|
|
969
1616
|
}
|
|
970
|
-
const claimIds = new Set(
|
|
1617
|
+
const claimIds = new Set(claims.map((claim) => claim.id));
|
|
971
1618
|
const lastTransitionByClaim = new Map();
|
|
972
|
-
for (const transition of
|
|
1619
|
+
for (const transition of transitions) {
|
|
973
1620
|
if (!claimIds.has(transition.claimId)) {
|
|
974
1621
|
errors.push(
|
|
975
1622
|
issue(
|
|
@@ -981,7 +1628,7 @@ function validateLedger(value, errors) {
|
|
|
981
1628
|
}
|
|
982
1629
|
lastTransitionByClaim.set(transition.claimId, transition);
|
|
983
1630
|
}
|
|
984
|
-
for (const claim of
|
|
1631
|
+
for (const claim of claims) {
|
|
985
1632
|
const lastTransition = lastTransitionByClaim.get(claim.id);
|
|
986
1633
|
if (value.revision === 1 && claim.status === 'provisional') {
|
|
987
1634
|
if (lastTransition) {
|
|
@@ -1950,11 +2597,12 @@ export function validateArtifactShape(value) {
|
|
|
1950
2597
|
),
|
|
1951
2598
|
);
|
|
1952
2599
|
}
|
|
1953
|
-
|
|
2600
|
+
const versions = supportedSchemaVersions.get(value.kind);
|
|
2601
|
+
if (versions && !versions.has(value.schemaVersion)) {
|
|
1954
2602
|
errors.push(
|
|
1955
2603
|
issue(
|
|
1956
2604
|
'UNSUPPORTED_SCHEMA_VERSION',
|
|
1957
|
-
`
|
|
2605
|
+
`Unsupported schemaVersion ${value.schemaVersion} for ${value.kind}`,
|
|
1958
2606
|
'$.schemaVersion',
|
|
1959
2607
|
),
|
|
1960
2608
|
);
|