@vibe-engineer/artifacts 0.1.0 → 0.2.0
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/dist/{chunk-XQLK7EDB.js → chunk-FCLM65RB.js} +10 -10
- package/dist/index.js +383 -57
- package/dist/schema-registry.js +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +3 -3
- package/schemas/agent-registry-entry.schema.json +19 -102
- package/schemas/build-result.schema.json +31 -126
- package/schemas/context-file-header.schema.json +22 -126
- package/schemas/evidence-packet.schema.json +33 -97
- package/schemas/implementation-plan.schema.json +34 -179
- package/schemas/schematic-manifest.schema.json +24 -125
- package/schemas/ship-packet.schema.json +21 -116
- package/schemas/skill-manifest.schema.json +22 -117
- package/schemas/verification-delta.schema.json +14 -77
- package/schemas/work-brief.schema.json +16 -86
|
@@ -16,16 +16,16 @@ var ARTIFACT_KINDS = [
|
|
|
16
16
|
"skill_manifest"
|
|
17
17
|
];
|
|
18
18
|
var SCHEMA_FILES = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
work_brief: "schemas/work-brief.schema.json",
|
|
20
|
+
implementation_plan: "schemas/implementation-plan.schema.json",
|
|
21
|
+
verification_delta: "schemas/verification-delta.schema.json",
|
|
22
|
+
build_result: "schemas/build-result.schema.json",
|
|
23
|
+
ship_packet: "schemas/ship-packet.schema.json",
|
|
24
|
+
evidence_packet: "schemas/evidence-packet.schema.json",
|
|
25
|
+
agent_registry_entry: "schemas/agent-registry-entry.schema.json",
|
|
26
|
+
context_file_header: "schemas/context-file-header.schema.json",
|
|
27
|
+
schematic_manifest: "schemas/schematic-manifest.schema.json",
|
|
28
|
+
skill_manifest: "schemas/skill-manifest.schema.json"
|
|
29
29
|
};
|
|
30
30
|
var packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
31
|
function schemaPathForKind(kind) {
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadAllSchemas,
|
|
6
6
|
loadSchema,
|
|
7
7
|
schemaPathForKind
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FCLM65RB.js";
|
|
9
9
|
|
|
10
10
|
// src/errors.js
|
|
11
11
|
var ValidationErrorCode = Object.freeze({
|
|
@@ -35,7 +35,15 @@ var ValidationErrorCode = Object.freeze({
|
|
|
35
35
|
MALFORMED_EXTENSION: "MALFORMED_EXTENSION",
|
|
36
36
|
SCHEMA_INTERNAL_ERROR: "SCHEMA_INTERNAL_ERROR"
|
|
37
37
|
});
|
|
38
|
-
function makeValidationError({
|
|
38
|
+
function makeValidationError({
|
|
39
|
+
artifactPath,
|
|
40
|
+
artifactKind,
|
|
41
|
+
schemaId,
|
|
42
|
+
schemaVersion,
|
|
43
|
+
pointer,
|
|
44
|
+
code,
|
|
45
|
+
message
|
|
46
|
+
}) {
|
|
39
47
|
return {
|
|
40
48
|
artifactPath: artifactPath ?? null,
|
|
41
49
|
artifactKind: artifactKind ?? null,
|
|
@@ -51,7 +59,25 @@ function makeValidationError({ artifactPath, artifactKind, schemaId, schemaVersi
|
|
|
51
59
|
import fs from "fs";
|
|
52
60
|
import path from "path";
|
|
53
61
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
54
|
-
var CORE_FIELDS = /* @__PURE__ */ new Set([
|
|
62
|
+
var CORE_FIELDS = /* @__PURE__ */ new Set([
|
|
63
|
+
"schemaVersion",
|
|
64
|
+
"artifactKind",
|
|
65
|
+
"artifactId",
|
|
66
|
+
"title",
|
|
67
|
+
"createdAt",
|
|
68
|
+
"updatedAt",
|
|
69
|
+
"producer",
|
|
70
|
+
"status",
|
|
71
|
+
"ownership",
|
|
72
|
+
"links",
|
|
73
|
+
"extensions",
|
|
74
|
+
"description",
|
|
75
|
+
"tags",
|
|
76
|
+
"sourceRefs",
|
|
77
|
+
"approvedBy",
|
|
78
|
+
"supersessionReason",
|
|
79
|
+
"retention"
|
|
80
|
+
]);
|
|
55
81
|
var VERIFICATION_CATALOG_LAYERS = Object.freeze([
|
|
56
82
|
"safety_hooks",
|
|
57
83
|
"typecheck",
|
|
@@ -89,9 +115,12 @@ function createContext(data, artifactPath, schema) {
|
|
|
89
115
|
};
|
|
90
116
|
}
|
|
91
117
|
function ajvErrorPointer(error) {
|
|
92
|
-
if (error.keyword === "required" && error.params?.missingProperty)
|
|
93
|
-
|
|
94
|
-
if (error.keyword === "
|
|
118
|
+
if (error.keyword === "required" && error.params?.missingProperty)
|
|
119
|
+
return pointerJoin(error.instancePath || "", error.params.missingProperty);
|
|
120
|
+
if (error.keyword === "additionalProperties" && error.params?.additionalProperty)
|
|
121
|
+
return pointerJoin(error.instancePath || "", error.params.additionalProperty);
|
|
122
|
+
if (error.keyword === "propertyNames" && error.params?.propertyName)
|
|
123
|
+
return pointerJoin(error.instancePath || "", error.params.propertyName);
|
|
95
124
|
return error.instancePath || "";
|
|
96
125
|
}
|
|
97
126
|
function ajvErrorCode(error) {
|
|
@@ -121,12 +150,14 @@ function ajvErrorCode(error) {
|
|
|
121
150
|
}
|
|
122
151
|
}
|
|
123
152
|
function normalizeAjvErrors(ajvErrors, ctx) {
|
|
124
|
-
return (ajvErrors ?? []).map(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
return (ajvErrors ?? []).map(
|
|
154
|
+
(error) => makeValidationError({
|
|
155
|
+
...ctx,
|
|
156
|
+
pointer: ajvErrorPointer(error),
|
|
157
|
+
code: ajvErrorCode(error),
|
|
158
|
+
message: error.message ?? `Schema validation failed for keyword ${error.keyword}.`
|
|
159
|
+
})
|
|
160
|
+
);
|
|
130
161
|
}
|
|
131
162
|
function createAjv() {
|
|
132
163
|
return new Ajv2020({
|
|
@@ -162,15 +193,39 @@ function validateVersionAndKind(data, expectedKind, errors, ctx) {
|
|
|
162
193
|
return;
|
|
163
194
|
}
|
|
164
195
|
if (typeof data.schemaVersion !== "string" || !/^\d+\.\d+\.\d+$/.test(data.schemaVersion)) {
|
|
165
|
-
addError(
|
|
196
|
+
addError(
|
|
197
|
+
errors,
|
|
198
|
+
ctx,
|
|
199
|
+
"/schemaVersion",
|
|
200
|
+
ValidationErrorCode.UNSUPPORTED_VERSION,
|
|
201
|
+
"schemaVersion must be semantic version 1.0.0."
|
|
202
|
+
);
|
|
166
203
|
} else if (data.schemaVersion !== SUPPORTED_SCHEMA_VERSION) {
|
|
167
|
-
addError(
|
|
204
|
+
addError(
|
|
205
|
+
errors,
|
|
206
|
+
ctx,
|
|
207
|
+
"/schemaVersion",
|
|
208
|
+
ValidationErrorCode.UNSUPPORTED_VERSION,
|
|
209
|
+
`Unsupported artifact schemaVersion ${data.schemaVersion}.`
|
|
210
|
+
);
|
|
168
211
|
}
|
|
169
212
|
if (!ARTIFACT_KINDS.includes(data.artifactKind)) {
|
|
170
|
-
addError(
|
|
213
|
+
addError(
|
|
214
|
+
errors,
|
|
215
|
+
ctx,
|
|
216
|
+
"/artifactKind",
|
|
217
|
+
ValidationErrorCode.ARTIFACT_KIND_MISMATCH,
|
|
218
|
+
"Unknown artifactKind."
|
|
219
|
+
);
|
|
171
220
|
}
|
|
172
221
|
if (expectedKind && data.artifactKind !== expectedKind) {
|
|
173
|
-
addError(
|
|
222
|
+
addError(
|
|
223
|
+
errors,
|
|
224
|
+
ctx,
|
|
225
|
+
"/artifactKind",
|
|
226
|
+
ValidationErrorCode.ARTIFACT_KIND_MISMATCH,
|
|
227
|
+
`Expected artifactKind ${expectedKind}.`
|
|
228
|
+
);
|
|
174
229
|
}
|
|
175
230
|
}
|
|
176
231
|
function linkMatches(link, rel, artifactKind, requiredStatus) {
|
|
@@ -182,12 +237,24 @@ function evidencePacketLinkMatches(link) {
|
|
|
182
237
|
function requireLink(errors, ctx, pointer, link, rel, artifactKind, requiredStatus) {
|
|
183
238
|
if (!linkMatches(link, rel, artifactKind, requiredStatus)) {
|
|
184
239
|
const statusText = requiredStatus ? ` with statusAtLinkTime ${requiredStatus}` : "";
|
|
185
|
-
addError(
|
|
240
|
+
addError(
|
|
241
|
+
errors,
|
|
242
|
+
ctx,
|
|
243
|
+
pointer,
|
|
244
|
+
ValidationErrorCode.BAD_LINK,
|
|
245
|
+
`Required ${rel} link to ${artifactKind}${statusText} is missing or malformed.`
|
|
246
|
+
);
|
|
186
247
|
}
|
|
187
248
|
}
|
|
188
249
|
function requireEvidencePacketRef(errors, ctx, pointer, link) {
|
|
189
250
|
if (!evidencePacketLinkMatches(link)) {
|
|
190
|
-
addError(
|
|
251
|
+
addError(
|
|
252
|
+
errors,
|
|
253
|
+
ctx,
|
|
254
|
+
pointer,
|
|
255
|
+
ValidationErrorCode.EVIDENCE_LINK_REQUIRED,
|
|
256
|
+
"Verification evidence references must be required evidence_for links to evidence_packet artifacts."
|
|
257
|
+
);
|
|
191
258
|
}
|
|
192
259
|
}
|
|
193
260
|
function validateVerificationCatalog(data, errors, ctx, basePointer = "") {
|
|
@@ -197,31 +264,69 @@ function validateVerificationCatalog(data, errors, ctx, basePointer = "") {
|
|
|
197
264
|
if (!isObject(item)) return;
|
|
198
265
|
if (typeof item.layer === "string") seen.add(item.layer);
|
|
199
266
|
if (item.action === "not_applicable" && (typeof item.rationale !== "string" || item.rationale.trim() === "")) {
|
|
200
|
-
addError(
|
|
267
|
+
addError(
|
|
268
|
+
errors,
|
|
269
|
+
ctx,
|
|
270
|
+
`${basePointer}/requiredItems/${index}/rationale`,
|
|
271
|
+
ValidationErrorCode.NOT_APPLICABLE_RATIONALE_REQUIRED,
|
|
272
|
+
"not_applicable verification catalog items require a rationale."
|
|
273
|
+
);
|
|
201
274
|
}
|
|
202
275
|
if (item.action === "blocked") {
|
|
203
276
|
if (typeof item.blockedBy !== "string" || item.blockedBy.trim() === "") {
|
|
204
|
-
addError(
|
|
277
|
+
addError(
|
|
278
|
+
errors,
|
|
279
|
+
ctx,
|
|
280
|
+
`${basePointer}/requiredItems/${index}/blockedBy`,
|
|
281
|
+
ValidationErrorCode.BLOCKED_ITEM_METADATA_REQUIRED,
|
|
282
|
+
"blocked verification catalog items require blockedBy."
|
|
283
|
+
);
|
|
205
284
|
}
|
|
206
285
|
if (typeof item.unblockCondition !== "string" || item.unblockCondition.trim() === "") {
|
|
207
|
-
addError(
|
|
286
|
+
addError(
|
|
287
|
+
errors,
|
|
288
|
+
ctx,
|
|
289
|
+
`${basePointer}/requiredItems/${index}/unblockCondition`,
|
|
290
|
+
ValidationErrorCode.BLOCKED_ITEM_METADATA_REQUIRED,
|
|
291
|
+
"blocked verification catalog items require unblockCondition."
|
|
292
|
+
);
|
|
208
293
|
}
|
|
209
294
|
}
|
|
210
295
|
});
|
|
211
296
|
for (const layer of VERIFICATION_CATALOG_LAYERS) {
|
|
212
297
|
if (!seen.has(layer)) {
|
|
213
|
-
addError(
|
|
298
|
+
addError(
|
|
299
|
+
errors,
|
|
300
|
+
ctx,
|
|
301
|
+
`${basePointer}/requiredItems`,
|
|
302
|
+
ValidationErrorCode.VERIFICATION_CATALOG_INCOMPLETE,
|
|
303
|
+
`Verification Delta must include catalog layer ${layer}.`
|
|
304
|
+
);
|
|
214
305
|
}
|
|
215
306
|
}
|
|
216
307
|
}
|
|
217
308
|
function requireEvidenceForLinks(data, errors, ctx) {
|
|
218
|
-
if (!Array.isArray(data.links) || !data.links.some(
|
|
219
|
-
|
|
309
|
+
if (!Array.isArray(data.links) || !data.links.some(
|
|
310
|
+
(link) => isObject(link) && link.rel === "evidence_for" && link.required === true
|
|
311
|
+
)) {
|
|
312
|
+
addError(
|
|
313
|
+
errors,
|
|
314
|
+
ctx,
|
|
315
|
+
"/links",
|
|
316
|
+
ValidationErrorCode.EVIDENCE_FOR_REQUIRED,
|
|
317
|
+
"Evidence Packet must include a required top-level evidence_for link."
|
|
318
|
+
);
|
|
220
319
|
}
|
|
221
320
|
if (!Array.isArray(data.subjectRefs) || data.subjectRefs.length === 0) return;
|
|
222
321
|
data.subjectRefs.forEach((ref, index) => {
|
|
223
322
|
if (!isObject(ref) || ref.rel !== "evidence_for" || ref.required !== true) {
|
|
224
|
-
addError(
|
|
323
|
+
addError(
|
|
324
|
+
errors,
|
|
325
|
+
ctx,
|
|
326
|
+
`/subjectRefs/${index}`,
|
|
327
|
+
ValidationErrorCode.EVIDENCE_FOR_REQUIRED,
|
|
328
|
+
"Evidence Packet subjectRefs must use required evidence_for links."
|
|
329
|
+
);
|
|
225
330
|
}
|
|
226
331
|
});
|
|
227
332
|
}
|
|
@@ -230,12 +335,24 @@ function semanticChecks(kind, data, errors, ctx) {
|
|
|
230
335
|
if (isObject(data.extensions)) {
|
|
231
336
|
for (const [namespace, value] of Object.entries(data.extensions)) {
|
|
232
337
|
if (!isObject(value) || typeof value.schemaVersion !== "string") {
|
|
233
|
-
addError(
|
|
338
|
+
addError(
|
|
339
|
+
errors,
|
|
340
|
+
ctx,
|
|
341
|
+
`/extensions/${namespace}`,
|
|
342
|
+
ValidationErrorCode.MALFORMED_EXTENSION,
|
|
343
|
+
"Extension value must be an object with schemaVersion."
|
|
344
|
+
);
|
|
234
345
|
continue;
|
|
235
346
|
}
|
|
236
347
|
for (const key of Object.keys(value)) {
|
|
237
348
|
if (key !== "schemaVersion" && CORE_FIELDS.has(key)) {
|
|
238
|
-
addError(
|
|
349
|
+
addError(
|
|
350
|
+
errors,
|
|
351
|
+
ctx,
|
|
352
|
+
`/extensions/${namespace}/${key}`,
|
|
353
|
+
ValidationErrorCode.MALFORMED_EXTENSION,
|
|
354
|
+
`Extension ${namespace} must not redefine core field ${key}.`
|
|
355
|
+
);
|
|
239
356
|
}
|
|
240
357
|
}
|
|
241
358
|
}
|
|
@@ -243,92 +360,238 @@ function semanticChecks(kind, data, errors, ctx) {
|
|
|
243
360
|
switch (kind) {
|
|
244
361
|
case "work_brief": {
|
|
245
362
|
if (!Array.isArray(data.links) || !data.links.some((link) => linkMatches(link, "raw_intent", "work_brief"))) {
|
|
246
|
-
addError(
|
|
363
|
+
addError(
|
|
364
|
+
errors,
|
|
365
|
+
ctx,
|
|
366
|
+
"/links",
|
|
367
|
+
ValidationErrorCode.BAD_LINK,
|
|
368
|
+
"Work Brief must include a required raw_intent link."
|
|
369
|
+
);
|
|
247
370
|
}
|
|
248
371
|
break;
|
|
249
372
|
}
|
|
250
373
|
case "implementation_plan": {
|
|
251
|
-
requireLink(
|
|
374
|
+
requireLink(
|
|
375
|
+
errors,
|
|
376
|
+
ctx,
|
|
377
|
+
"/workBriefRef",
|
|
378
|
+
data.workBriefRef,
|
|
379
|
+
"derived_from",
|
|
380
|
+
"work_brief",
|
|
381
|
+
"ready"
|
|
382
|
+
);
|
|
252
383
|
if (data.status === "approved" && Array.isArray(data.openBlockers) && data.openBlockers.some((b) => b?.blocking === true)) {
|
|
253
|
-
addError(
|
|
384
|
+
addError(
|
|
385
|
+
errors,
|
|
386
|
+
ctx,
|
|
387
|
+
"/openBlockers",
|
|
388
|
+
ValidationErrorCode.BAD_STATUS_HANDOFF,
|
|
389
|
+
"Approved Implementation Plan cannot contain unresolved blocking openBlockers."
|
|
390
|
+
);
|
|
254
391
|
}
|
|
255
392
|
if (data.verificationDelta?.artifactKind !== "verification_delta") {
|
|
256
|
-
addError(
|
|
393
|
+
addError(
|
|
394
|
+
errors,
|
|
395
|
+
ctx,
|
|
396
|
+
"/verificationDelta/artifactKind",
|
|
397
|
+
ValidationErrorCode.BAD_LINK,
|
|
398
|
+
"Implementation Plan must embed a Verification Delta artifact."
|
|
399
|
+
);
|
|
257
400
|
} else {
|
|
258
401
|
validateVerificationCatalog(data.verificationDelta, errors, ctx, "/verificationDelta");
|
|
259
402
|
}
|
|
260
403
|
break;
|
|
261
404
|
}
|
|
262
405
|
case "verification_delta": {
|
|
263
|
-
requireLink(
|
|
406
|
+
requireLink(
|
|
407
|
+
errors,
|
|
408
|
+
ctx,
|
|
409
|
+
"/implementationPlanRef",
|
|
410
|
+
data.implementationPlanRef,
|
|
411
|
+
"verification_delta_of",
|
|
412
|
+
"implementation_plan"
|
|
413
|
+
);
|
|
264
414
|
if (!Array.isArray(data.requiredItems) || data.requiredItems.length === 0) {
|
|
265
|
-
addError(
|
|
415
|
+
addError(
|
|
416
|
+
errors,
|
|
417
|
+
ctx,
|
|
418
|
+
"/requiredItems",
|
|
419
|
+
ValidationErrorCode.REQUIRED,
|
|
420
|
+
"Verification Delta must include required catalog items."
|
|
421
|
+
);
|
|
266
422
|
}
|
|
267
423
|
validateVerificationCatalog(data, errors, ctx);
|
|
268
424
|
break;
|
|
269
425
|
}
|
|
270
426
|
case "build_result": {
|
|
271
|
-
requireLink(
|
|
427
|
+
requireLink(
|
|
428
|
+
errors,
|
|
429
|
+
ctx,
|
|
430
|
+
"/implementationPlanRef",
|
|
431
|
+
data.implementationPlanRef,
|
|
432
|
+
"implements",
|
|
433
|
+
"implementation_plan",
|
|
434
|
+
"approved"
|
|
435
|
+
);
|
|
272
436
|
if (!Array.isArray(data.verificationRuns) || data.verificationRuns.length === 0) {
|
|
273
|
-
addError(
|
|
437
|
+
addError(
|
|
438
|
+
errors,
|
|
439
|
+
ctx,
|
|
440
|
+
"/verificationRuns",
|
|
441
|
+
ValidationErrorCode.REQUIRED,
|
|
442
|
+
"Build Result must reference Evidence Packets for verification claims."
|
|
443
|
+
);
|
|
274
444
|
} else {
|
|
275
|
-
data.verificationRuns.forEach(
|
|
445
|
+
data.verificationRuns.forEach(
|
|
446
|
+
(run, index) => requireEvidencePacketRef(
|
|
447
|
+
errors,
|
|
448
|
+
ctx,
|
|
449
|
+
`/verificationRuns/${index}/evidencePacketRef`,
|
|
450
|
+
run?.evidencePacketRef
|
|
451
|
+
)
|
|
452
|
+
);
|
|
276
453
|
}
|
|
277
454
|
if (Array.isArray(data.testsAndVerificationChanged)) {
|
|
278
|
-
data.testsAndVerificationChanged.forEach(
|
|
455
|
+
data.testsAndVerificationChanged.forEach(
|
|
456
|
+
(item, index) => requireEvidencePacketRef(
|
|
457
|
+
errors,
|
|
458
|
+
ctx,
|
|
459
|
+
`/testsAndVerificationChanged/${index}/evidenceRef`,
|
|
460
|
+
item?.evidenceRef
|
|
461
|
+
)
|
|
462
|
+
);
|
|
279
463
|
}
|
|
280
464
|
if (Array.isArray(data.warningsAndBlockers)) {
|
|
281
|
-
data.warningsAndBlockers.forEach(
|
|
465
|
+
data.warningsAndBlockers.forEach(
|
|
466
|
+
(item, index) => requireEvidencePacketRef(
|
|
467
|
+
errors,
|
|
468
|
+
ctx,
|
|
469
|
+
`/warningsAndBlockers/${index}/evidenceRef`,
|
|
470
|
+
item?.evidenceRef
|
|
471
|
+
)
|
|
472
|
+
);
|
|
282
473
|
}
|
|
283
|
-
if (data.status === "passed" && Array.isArray(data.warningsAndBlockers) && data.warningsAndBlockers.some(
|
|
284
|
-
|
|
474
|
+
if (data.status === "passed" && Array.isArray(data.warningsAndBlockers) && data.warningsAndBlockers.some(
|
|
475
|
+
(item) => item?.blocking === true && ["critical", "major-local"].includes(item?.severity)
|
|
476
|
+
)) {
|
|
477
|
+
addError(
|
|
478
|
+
errors,
|
|
479
|
+
ctx,
|
|
480
|
+
"/warningsAndBlockers",
|
|
481
|
+
ValidationErrorCode.BAD_STATUS_HANDOFF,
|
|
482
|
+
"Passed Build Result cannot contain blocking critical or major-local evidence."
|
|
483
|
+
);
|
|
285
484
|
}
|
|
286
485
|
break;
|
|
287
486
|
}
|
|
288
487
|
case "ship_packet": {
|
|
289
|
-
requireLink(
|
|
488
|
+
requireLink(
|
|
489
|
+
errors,
|
|
490
|
+
ctx,
|
|
491
|
+
"/buildResultRef",
|
|
492
|
+
data.buildResultRef,
|
|
493
|
+
"ship_packet_of",
|
|
494
|
+
"build_result",
|
|
495
|
+
"passed"
|
|
496
|
+
);
|
|
290
497
|
if (Array.isArray(data.finalVerification)) {
|
|
291
|
-
data.finalVerification.forEach(
|
|
498
|
+
data.finalVerification.forEach(
|
|
499
|
+
(item, index) => requireEvidencePacketRef(
|
|
500
|
+
errors,
|
|
501
|
+
ctx,
|
|
502
|
+
`/finalVerification/${index}/evidencePacketRef`,
|
|
503
|
+
item?.evidencePacketRef
|
|
504
|
+
)
|
|
505
|
+
);
|
|
292
506
|
}
|
|
293
507
|
if (isObject(data.contextPreservation) && Array.isArray(data.contextPreservation.driftCheckEvidenceRefs)) {
|
|
294
|
-
data.contextPreservation.driftCheckEvidenceRefs.forEach(
|
|
508
|
+
data.contextPreservation.driftCheckEvidenceRefs.forEach(
|
|
509
|
+
(ref, index) => requireEvidencePacketRef(
|
|
510
|
+
errors,
|
|
511
|
+
ctx,
|
|
512
|
+
`/contextPreservation/driftCheckEvidenceRefs/${index}`,
|
|
513
|
+
ref
|
|
514
|
+
)
|
|
515
|
+
);
|
|
295
516
|
}
|
|
296
517
|
if (data.noPushWithoutApproval !== true || data.commitPreparation?.commitPerformedByAgent !== false || data.prPreparation?.prOpenedByAgent !== false) {
|
|
297
|
-
addError(
|
|
518
|
+
addError(
|
|
519
|
+
errors,
|
|
520
|
+
ctx,
|
|
521
|
+
"/noPushWithoutApproval",
|
|
522
|
+
ValidationErrorCode.BAD_STATUS_HANDOFF,
|
|
523
|
+
"Ship Packet must preserve no-push/no-PR-without-approval policy."
|
|
524
|
+
);
|
|
298
525
|
}
|
|
299
526
|
break;
|
|
300
527
|
}
|
|
301
528
|
case "evidence_packet": {
|
|
302
529
|
requireEvidenceForLinks(data, errors, ctx);
|
|
303
530
|
if (["fail", "blocked"].includes(data.result) && !isObject(data.failureDetails)) {
|
|
304
|
-
addError(
|
|
531
|
+
addError(
|
|
532
|
+
errors,
|
|
533
|
+
ctx,
|
|
534
|
+
"/failureDetails",
|
|
535
|
+
ValidationErrorCode.REQUIRED,
|
|
536
|
+
"Failure or blocked evidence requires failureDetails."
|
|
537
|
+
);
|
|
305
538
|
}
|
|
306
539
|
if (data.evidenceClass === "advisory" && data.blocking === true && data.result !== "advisory") {
|
|
307
|
-
addError(
|
|
540
|
+
addError(
|
|
541
|
+
errors,
|
|
542
|
+
ctx,
|
|
543
|
+
"/blocking",
|
|
544
|
+
ValidationErrorCode.BAD_STATUS_HANDOFF,
|
|
545
|
+
"Advisory evidence cannot masquerade as deterministic blocker."
|
|
546
|
+
);
|
|
308
547
|
}
|
|
309
548
|
break;
|
|
310
549
|
}
|
|
311
550
|
case "agent_registry_entry": {
|
|
312
551
|
if (!Array.isArray(data.inputSchemas) || data.inputSchemas.length === 0 || !Array.isArray(data.outputSchemas) || data.outputSchemas.length === 0) {
|
|
313
|
-
addError(
|
|
552
|
+
addError(
|
|
553
|
+
errors,
|
|
554
|
+
ctx,
|
|
555
|
+
"/inputSchemas",
|
|
556
|
+
ValidationErrorCode.REQUIRED,
|
|
557
|
+
"Agent registry entries require input and output schema refs."
|
|
558
|
+
);
|
|
314
559
|
}
|
|
315
560
|
break;
|
|
316
561
|
}
|
|
317
562
|
case "context_file_header": {
|
|
318
563
|
if (!isObject(data.driftMetadata) || !isObject(data.updateMetadata)) {
|
|
319
|
-
addError(
|
|
564
|
+
addError(
|
|
565
|
+
errors,
|
|
566
|
+
ctx,
|
|
567
|
+
"/driftMetadata",
|
|
568
|
+
ValidationErrorCode.REQUIRED,
|
|
569
|
+
"Context headers require update and drift metadata."
|
|
570
|
+
);
|
|
320
571
|
}
|
|
321
572
|
break;
|
|
322
573
|
}
|
|
323
574
|
case "schematic_manifest": {
|
|
324
575
|
if (!Array.isArray(data.inputs) || data.inputs.length === 0 || !Array.isArray(data.generatedPaths) || data.generatedPaths.length === 0) {
|
|
325
|
-
addError(
|
|
576
|
+
addError(
|
|
577
|
+
errors,
|
|
578
|
+
ctx,
|
|
579
|
+
"/inputs",
|
|
580
|
+
ValidationErrorCode.REQUIRED,
|
|
581
|
+
"Schematic manifests require input schema refs and generated path policy."
|
|
582
|
+
);
|
|
326
583
|
}
|
|
327
584
|
break;
|
|
328
585
|
}
|
|
329
586
|
case "skill_manifest": {
|
|
330
587
|
if (!Array.isArray(data.outputArtifactSchemas) || data.outputArtifactSchemas.length === 0 || !Array.isArray(data.forbiddenActions) || data.forbiddenActions.length === 0) {
|
|
331
|
-
addError(
|
|
588
|
+
addError(
|
|
589
|
+
errors,
|
|
590
|
+
ctx,
|
|
591
|
+
"/outputArtifactSchemas",
|
|
592
|
+
ValidationErrorCode.REQUIRED,
|
|
593
|
+
"Skill manifests require output schema refs and forbidden action policy."
|
|
594
|
+
);
|
|
332
595
|
}
|
|
333
596
|
break;
|
|
334
597
|
}
|
|
@@ -340,37 +603,100 @@ function validateArtifactKind(kind, data, options = {}) {
|
|
|
340
603
|
const errors = [];
|
|
341
604
|
const validator = compiledRegistry.compiled.get(kind);
|
|
342
605
|
if (!schema || !validator) {
|
|
343
|
-
addError(
|
|
606
|
+
addError(
|
|
607
|
+
errors,
|
|
608
|
+
ctx,
|
|
609
|
+
"",
|
|
610
|
+
ValidationErrorCode.SCHEMA_INTERNAL_ERROR,
|
|
611
|
+
`No schema registered for kind ${kind}.`
|
|
612
|
+
);
|
|
344
613
|
return { ok: false, errors };
|
|
345
614
|
}
|
|
346
615
|
validateVersionAndKind(data, kind, errors, ctx);
|
|
347
616
|
const schemaOk = validator(data);
|
|
348
617
|
if (!schemaOk) errors.push(...normalizeAjvErrors(validator.errors, ctx));
|
|
349
618
|
semanticChecks(kind, data, errors, ctx);
|
|
350
|
-
return errors.length === 0 ? {
|
|
619
|
+
return errors.length === 0 ? {
|
|
620
|
+
ok: true,
|
|
621
|
+
artifact: data,
|
|
622
|
+
kind,
|
|
623
|
+
schemaId: schema.$id,
|
|
624
|
+
schemaVersion: SUPPORTED_SCHEMA_VERSION
|
|
625
|
+
} : { ok: false, errors };
|
|
351
626
|
}
|
|
352
627
|
function validateArtifact(data, options = {}) {
|
|
353
628
|
if (!isObject(data)) {
|
|
354
|
-
const ctx = {
|
|
355
|
-
|
|
629
|
+
const ctx = {
|
|
630
|
+
artifactPath: options.artifactPath ?? null,
|
|
631
|
+
artifactKind: null,
|
|
632
|
+
schemaVersion: null,
|
|
633
|
+
schemaId: null
|
|
634
|
+
};
|
|
635
|
+
return {
|
|
636
|
+
ok: false,
|
|
637
|
+
errors: [
|
|
638
|
+
makeValidationError({
|
|
639
|
+
...ctx,
|
|
640
|
+
pointer: "",
|
|
641
|
+
code: ValidationErrorCode.NOT_OBJECT,
|
|
642
|
+
message: "Artifact must be a JSON object."
|
|
643
|
+
})
|
|
644
|
+
]
|
|
645
|
+
};
|
|
356
646
|
}
|
|
357
647
|
const kind = data.artifactKind;
|
|
358
648
|
if (!ARTIFACT_KINDS.includes(kind)) {
|
|
359
649
|
const ctx = createContext(data, options.artifactPath, null);
|
|
360
|
-
return {
|
|
650
|
+
return {
|
|
651
|
+
ok: false,
|
|
652
|
+
errors: [
|
|
653
|
+
makeValidationError({
|
|
654
|
+
...ctx,
|
|
655
|
+
pointer: "/artifactKind",
|
|
656
|
+
code: ValidationErrorCode.ARTIFACT_KIND_MISMATCH,
|
|
657
|
+
message: "Unknown artifactKind."
|
|
658
|
+
})
|
|
659
|
+
]
|
|
660
|
+
};
|
|
361
661
|
}
|
|
362
662
|
return validateArtifactKind(kind, data, options);
|
|
363
663
|
}
|
|
364
664
|
function validateArtifactFile(filePath, options = {}) {
|
|
365
665
|
const artifactPath = options.artifactPath ?? filePath;
|
|
366
666
|
if (path.extname(filePath) !== ".json") {
|
|
367
|
-
return {
|
|
667
|
+
return {
|
|
668
|
+
ok: false,
|
|
669
|
+
errors: [
|
|
670
|
+
makeValidationError({
|
|
671
|
+
artifactPath,
|
|
672
|
+
artifactKind: options.kind ?? null,
|
|
673
|
+
schemaId: null,
|
|
674
|
+
schemaVersion: null,
|
|
675
|
+
pointer: "",
|
|
676
|
+
code: ValidationErrorCode.CARRIER_NOT_JSON,
|
|
677
|
+
message: "Canonical artifact carriers must be UTF-8 JSON files; Markdown, YAML, frontmatter, and chat text are not accepted."
|
|
678
|
+
})
|
|
679
|
+
]
|
|
680
|
+
};
|
|
368
681
|
}
|
|
369
682
|
let parsed;
|
|
370
683
|
try {
|
|
371
684
|
parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
372
685
|
} catch (error) {
|
|
373
|
-
return {
|
|
686
|
+
return {
|
|
687
|
+
ok: false,
|
|
688
|
+
errors: [
|
|
689
|
+
makeValidationError({
|
|
690
|
+
artifactPath,
|
|
691
|
+
artifactKind: options.kind ?? null,
|
|
692
|
+
schemaId: null,
|
|
693
|
+
schemaVersion: null,
|
|
694
|
+
pointer: "",
|
|
695
|
+
code: ValidationErrorCode.JSON_PARSE_ERROR,
|
|
696
|
+
message: `Invalid JSON artifact: ${error.message}`
|
|
697
|
+
})
|
|
698
|
+
]
|
|
699
|
+
};
|
|
374
700
|
}
|
|
375
701
|
return options.kind ? validateArtifactKind(options.kind, parsed, { artifactPath }) : validateArtifact(parsed, { artifactPath });
|
|
376
702
|
}
|