@kungfu-tech/buildchain 2.8.0 → 2.8.1
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/actions/promote-buildchain-ref/README.md +13 -0
- package/bin/buildchain.mjs +8 -0
- package/dist/site/agent-index.json +2 -1
- package/dist/site/artifact-schemas.json +1 -0
- package/dist/site/buildchain-contract.json +298 -0
- package/dist/site/release-provenance.json +2 -0
- package/docs/MAP.md +9 -0
- package/docs/cli.md +39 -0
- package/docs/migration-inventory.md +11 -0
- package/docs/release-candidate.md +38 -1
- package/docs/release-governance.md +18 -0
- package/docs/release-passport.md +126 -0
- package/docs/reusable-build-surface.md +79 -3
- package/docs/site-bundle-contract.md +6 -0
- package/docs/versioning.md +1 -1
- package/package.json +4 -2
- package/packages/core/buildchain-contract.js +524 -0
- package/packages/core/index.js +23 -0
- package/packages/core/kfd-gate.js +1064 -4
- package/packages/core/release-passport.js +298 -0
- package/scripts/buildchain-contract-lock.mjs +170 -0
- package/scripts/check-inventory.mjs +105 -3
- package/scripts/ensure-github-release.mjs +5 -5
- package/scripts/generate-site-bundle.mjs +4 -0
- package/scripts/release-candidate-resolver.mjs +1 -1
|
@@ -7,6 +7,9 @@ const require = createRequire(import.meta.url);
|
|
|
7
7
|
|
|
8
8
|
export const KFD1_RELEASE_GATE_CONTRACT = "kungfu-buildchain-kfd-1-release-gate";
|
|
9
9
|
export const KFD1_WITNESS_SET_CONTRACT = "kungfu-buildchain-kfd-1-witness-set";
|
|
10
|
+
export const KFD3_RELEASE_GATE_CONTRACT = "kungfu-buildchain-kfd-3-collaboration-interface-release-gate";
|
|
11
|
+
export const KFD3_PREBUILD_WITNESS_CONTRACT = "kungfu-buildchain-kfd-3-collaboration-interface-prebuild-witness";
|
|
12
|
+
export const KFD3_ARTIFACT_WITNESS_CONTRACT = "kungfu-buildchain-kfd-3-collaboration-interface-artifact-witness";
|
|
10
13
|
export const BUILDCHAIN_JSON_FORMATTING_POLICY = Object.freeze({
|
|
11
14
|
name: "buildchain-release-evidence-json-v1",
|
|
12
15
|
indentation: 2,
|
|
@@ -100,6 +103,51 @@ export function resolveKfd1Metadata() {
|
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
export function resolveKfd3Metadata({ requireSchemas = false } = {}) {
|
|
107
|
+
const { packageJson, standards } = loadKfdPackageMetadata();
|
|
108
|
+
const entries = Object.entries(standards.standards || {});
|
|
109
|
+
const entry = entries.find(([key, value]) => {
|
|
110
|
+
const names = [key, value?.key, value?.id, value?.label]
|
|
111
|
+
.filter(Boolean)
|
|
112
|
+
.map((name) => String(name).toLowerCase());
|
|
113
|
+
return names.includes("kfd-3");
|
|
114
|
+
});
|
|
115
|
+
if (!entry) {
|
|
116
|
+
throw new Error("KFD metadata package does not expose KFD-3 metadata");
|
|
117
|
+
}
|
|
118
|
+
const [key, standard] = entry;
|
|
119
|
+
const schemaIds = { ...(standard.schemaIds || {}) };
|
|
120
|
+
const schemaPaths = { ...(standard.schemaPaths || {}) };
|
|
121
|
+
const hasCollaborationSchemas = Boolean(
|
|
122
|
+
schemaIds.collaborationInterface &&
|
|
123
|
+
schemaIds.witness &&
|
|
124
|
+
schemaPaths.collaborationInterface &&
|
|
125
|
+
schemaPaths.witness,
|
|
126
|
+
);
|
|
127
|
+
if (requireSchemas && !hasCollaborationSchemas) {
|
|
128
|
+
throw new Error("KFD metadata package does not expose the KFD-3 collaboration-interface witness standard");
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
key: standard.key || key,
|
|
132
|
+
id: standard.id || standard.label || key,
|
|
133
|
+
label: standard.label || standard.id || key,
|
|
134
|
+
title: standard.title || "",
|
|
135
|
+
status: standard.status || "",
|
|
136
|
+
revision: standard.revision || 0,
|
|
137
|
+
package: {
|
|
138
|
+
name: packageJson.name,
|
|
139
|
+
version: packageJson.version,
|
|
140
|
+
repository: packageJson.repository?.url || "",
|
|
141
|
+
},
|
|
142
|
+
schemaIds,
|
|
143
|
+
schemaPaths,
|
|
144
|
+
hasCollaborationSchemas,
|
|
145
|
+
concepts: { ...(standard.concepts || {}) },
|
|
146
|
+
metadataSchema: standards.metadataSchema || {},
|
|
147
|
+
source: standards.source || {},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
103
151
|
function normalizeHash(value, label, { required = false } = {}) {
|
|
104
152
|
const text = optionalString(value).replace(/^sha256:/, "").trim();
|
|
105
153
|
if (!text && !required) {
|
|
@@ -158,13 +206,504 @@ function normalizeSurface(surface, index) {
|
|
|
158
206
|
};
|
|
159
207
|
}
|
|
160
208
|
|
|
209
|
+
function normalizeStringArray(value) {
|
|
210
|
+
return Array.isArray(value) ? value.map((entry) => optionalString(entry)).filter(Boolean) : [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const KFD3_SURFACE_GROUPS = [
|
|
214
|
+
{
|
|
215
|
+
kind: "documentation",
|
|
216
|
+
keys: ["docs", "documents", "documentation"],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
kind: "schema",
|
|
220
|
+
keys: ["schemas"],
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
kind: "standards-metadata",
|
|
224
|
+
keys: ["standardsMetadata", "standards_metadata", "standards", "metadata"],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
kind: "package-export",
|
|
228
|
+
keys: ["packageExports", "package_exports", "exports"],
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
kind: "site-consumption-contract",
|
|
232
|
+
keys: ["siteConsumptionContracts", "site_consumption_contracts", "siteContracts", "site_contracts"],
|
|
233
|
+
},
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
function groupedKfd3SurfacesFromSource(source = {}) {
|
|
237
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) {
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
const surfaces = [];
|
|
241
|
+
for (const group of KFD3_SURFACE_GROUPS) {
|
|
242
|
+
const values = group.keys
|
|
243
|
+
.map((key) => source[key])
|
|
244
|
+
.find((entry) => Array.isArray(entry));
|
|
245
|
+
if (!values) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
values.forEach((entry, index) => {
|
|
249
|
+
if (typeof entry === "string") {
|
|
250
|
+
surfaces.push({
|
|
251
|
+
id: entry,
|
|
252
|
+
name: entry,
|
|
253
|
+
kind: group.kind,
|
|
254
|
+
availability: "shipped",
|
|
255
|
+
visibility: "public",
|
|
256
|
+
participantFacing: true,
|
|
257
|
+
public: true,
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
262
|
+
throw new Error(`${group.keys[0]}[${index}] must be a string or object`);
|
|
263
|
+
}
|
|
264
|
+
surfaces.push({
|
|
265
|
+
kind: group.kind,
|
|
266
|
+
availability: "shipped",
|
|
267
|
+
visibility: "public",
|
|
268
|
+
participantFacing: true,
|
|
269
|
+
public: true,
|
|
270
|
+
...entry,
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return surfaces;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function groupedKfd1SurfacesFromSource(source = {}) {
|
|
278
|
+
return groupedKfd3SurfacesFromSource(source).map((surface) => ({
|
|
279
|
+
name: surface.name || surface.id,
|
|
280
|
+
sourcePath: surface.sourcePath || surface.source_path || "",
|
|
281
|
+
sourceSha256: surface.sourceSha256 || surface.source_sha256 || surface.sha256 || "",
|
|
282
|
+
artifactPath: surface.artifactPath || surface.artifact_path || surface.path || surface.sourcePath || surface.source_path || "",
|
|
283
|
+
expectedSha256: surface.expectedSha256 || surface.expected_sha256 || surface.artifactSha256 || surface.artifact_sha256 || surface.sha256 || "",
|
|
284
|
+
byteForByte: surface.byteForByte ?? surface.byte_for_byte ?? true,
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function normalizeKfd3Surface(surface, index, label) {
|
|
289
|
+
if (!surface || typeof surface !== "object" || Array.isArray(surface)) {
|
|
290
|
+
throw new Error(`${label}[${index}] must be an object`);
|
|
291
|
+
}
|
|
292
|
+
const visibility = optionalString(surface.visibility || (surface.public === false ? "internal" : "public")).toLowerCase();
|
|
293
|
+
const availability = optionalString(surface.availability || surface.state || surface.maturity || (surface.shipped === false ? "not-shipped" : "shipped")).toLowerCase();
|
|
294
|
+
const participantFacing = surface.participantFacing ?? surface.participant_facing ?? true;
|
|
295
|
+
const publicSurface = surface.public ?? (visibility === "public");
|
|
296
|
+
return {
|
|
297
|
+
id: nonEmptyString(surface.id || surface.name || surface.command, `${label}[${index}].id`),
|
|
298
|
+
name: optionalString(surface.name || surface.label || surface.id),
|
|
299
|
+
kind: optionalString(surface.kind || surface.type || "control-surface"),
|
|
300
|
+
participantProfile: optionalString(surface.participantProfile || surface.participant_profile || surface.profile),
|
|
301
|
+
availability,
|
|
302
|
+
visibility,
|
|
303
|
+
participantFacing: Boolean(participantFacing),
|
|
304
|
+
public: Boolean(publicSurface),
|
|
305
|
+
sourcePath: surface.sourcePath || surface.source_path
|
|
306
|
+
? normalizePath(surface.sourcePath || surface.source_path, `${label}[${index}].sourcePath`)
|
|
307
|
+
: "",
|
|
308
|
+
evidencePath: surface.evidencePath || surface.evidence_path
|
|
309
|
+
? normalizePath(surface.evidencePath || surface.evidence_path, `${label}[${index}].evidencePath`)
|
|
310
|
+
: "",
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function declaredSurfacesFromWitness(witness) {
|
|
315
|
+
return [
|
|
316
|
+
...(Array.isArray(witness.declaredSurfaces) ? witness.declaredSurfaces : []),
|
|
317
|
+
...(Array.isArray(witness.declared_surfaces) ? witness.declared_surfaces : []),
|
|
318
|
+
...(Array.isArray(witness.surfaces) ? witness.surfaces : []),
|
|
319
|
+
...(Array.isArray(witness.collaborationInterface?.surfaces) ? witness.collaborationInterface.surfaces : []),
|
|
320
|
+
...(Array.isArray(witness.collaboration_interface?.surfaces) ? witness.collaboration_interface.surfaces : []),
|
|
321
|
+
...(Array.isArray(witness.collaborationInterfaceDocument?.surfaces) ? witness.collaborationInterfaceDocument.surfaces : []),
|
|
322
|
+
...(Array.isArray(witness.collaboration_interface_document?.surfaces) ? witness.collaboration_interface_document.surfaces : []),
|
|
323
|
+
...groupedKfd3SurfacesFromSource(witness),
|
|
324
|
+
...groupedKfd3SurfacesFromSource(witness.collaborationInterface),
|
|
325
|
+
...groupedKfd3SurfacesFromSource(witness.collaboration_interface),
|
|
326
|
+
...groupedKfd3SurfacesFromSource(witness.collaborationInterfaceDocument),
|
|
327
|
+
...groupedKfd3SurfacesFromSource(witness.collaboration_interface_document),
|
|
328
|
+
];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function artifactSurfacesFromWitness(witness) {
|
|
332
|
+
const groupedSurfaces = [
|
|
333
|
+
...groupedKfd3SurfacesFromSource(witness),
|
|
334
|
+
...groupedKfd3SurfacesFromSource(witness.artifact),
|
|
335
|
+
...groupedKfd3SurfacesFromSource(witness.closure),
|
|
336
|
+
];
|
|
337
|
+
const explicitSurfaces = [
|
|
338
|
+
...(Array.isArray(witness.exposedSurfaces) ? witness.exposedSurfaces : []),
|
|
339
|
+
...(Array.isArray(witness.exposed_surfaces) ? witness.exposed_surfaces : []),
|
|
340
|
+
...(Array.isArray(witness.artifactPublicSurfaces) ? witness.artifactPublicSurfaces : []),
|
|
341
|
+
...(Array.isArray(witness.artifact_public_surfaces) ? witness.artifact_public_surfaces : []),
|
|
342
|
+
...(Array.isArray(witness.surfaces) ? witness.surfaces : []),
|
|
343
|
+
];
|
|
344
|
+
if (Array.isArray(witness.closure?.reachableEntrypoints)) {
|
|
345
|
+
explicitSurfaces.push(...witness.closure.reachableEntrypoints.map((id) => ({
|
|
346
|
+
id,
|
|
347
|
+
kind: "entrypoint",
|
|
348
|
+
availability: "shipped",
|
|
349
|
+
visibility: "public",
|
|
350
|
+
participantFacing: true,
|
|
351
|
+
public: true,
|
|
352
|
+
})));
|
|
353
|
+
}
|
|
354
|
+
return [...explicitSurfaces, ...groupedSurfaces];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function normalizeRegistry(value = {}) {
|
|
358
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
359
|
+
return {};
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
id: optionalString(value.id || value.name),
|
|
363
|
+
version: optionalString(value.version),
|
|
364
|
+
path: value.path ? normalizePath(value.path, "registry.path") : "",
|
|
365
|
+
sha256: value.sha256 ? normalizeHash(value.sha256, "registry.sha256") : "",
|
|
366
|
+
digest: optionalString(value.digest || (value.sha256 ? `sha256:${normalizeHash(value.sha256, "registry.sha256")}` : "")),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function normalizeArtifactIdentity(value = {}) {
|
|
371
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
372
|
+
return {};
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
name: optionalString(value.name || value.fileName || value.filename),
|
|
376
|
+
path: value.path ? normalizePath(value.path, "artifact.path") : "",
|
|
377
|
+
digest: optionalString(value.digest || (value.sha256 ? `sha256:${normalizeHash(value.sha256, "artifact.sha256")}` : "")),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function normalizeStringObjectArray(value, label) {
|
|
382
|
+
if (!Array.isArray(value)) {
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
return value
|
|
386
|
+
.map((entry, index) => {
|
|
387
|
+
if (typeof entry === "string") {
|
|
388
|
+
return { id: entry, note: "" };
|
|
389
|
+
}
|
|
390
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
391
|
+
throw new Error(`${label}[${index}] must be a string or object`);
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
id: optionalString(entry.id || entry.name || entry.surface || entry.path || entry.kind || `entry-${index}`),
|
|
395
|
+
kind: optionalString(entry.kind || entry.type),
|
|
396
|
+
reason: optionalString(entry.reason || entry.rationale || entry.note || entry.description),
|
|
397
|
+
owner: optionalString(entry.owner),
|
|
398
|
+
};
|
|
399
|
+
})
|
|
400
|
+
.filter((entry) => entry.id || entry.reason);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function normalizeKfd3AuditBoundary(value = {}, { closure = {}, label = "auditBoundary" } = {}) {
|
|
404
|
+
const boundary = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
405
|
+
const sourceClosure = closure && typeof closure === "object" && !Array.isArray(closure) ? closure : {};
|
|
406
|
+
const nonExhaustiveSource =
|
|
407
|
+
boundary.nonExhaustivelyEnumerableSurfaces ||
|
|
408
|
+
boundary.non_exhaustively_enumerable_surfaces ||
|
|
409
|
+
boundary.nonExhaustiveSurfaces ||
|
|
410
|
+
boundary.non_exhaustive_surfaces ||
|
|
411
|
+
sourceClosure.nonExhaustivelyEnumerableSurfaces ||
|
|
412
|
+
sourceClosure.non_exhaustively_enumerable_surfaces ||
|
|
413
|
+
sourceClosure.nonExhaustiveSurfaces ||
|
|
414
|
+
sourceClosure.non_exhaustive_surfaces ||
|
|
415
|
+
[];
|
|
416
|
+
return {
|
|
417
|
+
mode: optionalString(boundary.mode || boundary.classificationMode || sourceClosure.classificationMode || "closed-world"),
|
|
418
|
+
scope: optionalString(boundary.scope || "participant-facing public collaboration/control surfaces"),
|
|
419
|
+
reachableSurfaceMode: optionalString(
|
|
420
|
+
boundary.reachableSurfaceMode ||
|
|
421
|
+
boundary.reachable_surface_mode ||
|
|
422
|
+
sourceClosure.reachableSurfaceMode ||
|
|
423
|
+
sourceClosure.reachable_surface_mode ||
|
|
424
|
+
sourceClosure.classificationMode ||
|
|
425
|
+
"declared-boundary",
|
|
426
|
+
),
|
|
427
|
+
unclassifiedPolicy: optionalString(
|
|
428
|
+
boundary.unclassifiedPolicy ||
|
|
429
|
+
boundary.unclassified_policy ||
|
|
430
|
+
sourceClosure.unclassifiedEntrypointsPolicy ||
|
|
431
|
+
sourceClosure.unclassified_entrypoints_policy ||
|
|
432
|
+
"fail",
|
|
433
|
+
),
|
|
434
|
+
nonExhaustivelyEnumerableSurfaces: normalizeStringObjectArray(nonExhaustiveSource, `${label}.nonExhaustivelyEnumerableSurfaces`),
|
|
435
|
+
explicitlyExemptedSurfaces: normalizeStringObjectArray(
|
|
436
|
+
boundary.explicitlyExemptedSurfaces ||
|
|
437
|
+
boundary.explicitly_exempted_surfaces ||
|
|
438
|
+
boundary.exemptions ||
|
|
439
|
+
sourceClosure.explicitlyExemptedSurfaces ||
|
|
440
|
+
sourceClosure.exemptions ||
|
|
441
|
+
[],
|
|
442
|
+
`${label}.explicitlyExemptedSurfaces`,
|
|
443
|
+
),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function normalizeKfd3ResidualRisk(value = [], { auditBoundary = {} } = {}) {
|
|
448
|
+
const explicitRisks = normalizeStringObjectArray(value, "residualRisk");
|
|
449
|
+
const boundaryRisks = (auditBoundary.nonExhaustivelyEnumerableSurfaces || []).map((entry) => ({
|
|
450
|
+
id: entry.id,
|
|
451
|
+
kind: entry.kind || "non-exhaustive-surface",
|
|
452
|
+
reason: entry.reason || "Surface cannot be exhaustively enumerated by the release passport.",
|
|
453
|
+
owner: entry.owner || "",
|
|
454
|
+
}));
|
|
455
|
+
const byId = new Map();
|
|
456
|
+
for (const entry of [...explicitRisks, ...boundaryRisks]) {
|
|
457
|
+
byId.set(entry.id || entry.reason, entry);
|
|
458
|
+
}
|
|
459
|
+
return [...byId.values()];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function normalizeKfd3Responsibility(value = {}, { registry = {}, artifactVerifyCommand = "" } = {}) {
|
|
463
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
464
|
+
return {
|
|
465
|
+
registryFactsOwner: optionalString(
|
|
466
|
+
source.registryFactsOwner ||
|
|
467
|
+
source.registry_facts_owner ||
|
|
468
|
+
source.productRegistryOwner ||
|
|
469
|
+
source.product_registry_owner ||
|
|
470
|
+
source.product ||
|
|
471
|
+
registry.id ||
|
|
472
|
+
"product",
|
|
473
|
+
),
|
|
474
|
+
artifactVerificationOwner: optionalString(
|
|
475
|
+
source.artifactVerificationOwner ||
|
|
476
|
+
source.artifact_verification_owner ||
|
|
477
|
+
source.verifierOwner ||
|
|
478
|
+
source.verifier_owner ||
|
|
479
|
+
(artifactVerifyCommand ? "product-owned verify command" : "product"),
|
|
480
|
+
),
|
|
481
|
+
releasePassportProofOwner: optionalString(
|
|
482
|
+
source.releasePassportProofOwner ||
|
|
483
|
+
source.release_passport_proof_owner ||
|
|
484
|
+
source.passportOwner ||
|
|
485
|
+
source.passport_owner ||
|
|
486
|
+
"buildchain",
|
|
487
|
+
),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export function normalizeKfd3CollaborationInterfacePrebuildWitness(witness, { metadata = resolveKfd3Metadata() } = {}) {
|
|
492
|
+
if (!witness || typeof witness !== "object" || Array.isArray(witness)) {
|
|
493
|
+
throw new Error("KFD-3 pre-build witness must be a JSON object");
|
|
494
|
+
}
|
|
495
|
+
const standard = optionalString(witness.standard || metadata.key);
|
|
496
|
+
if (standard && standard !== metadata.key && standard !== metadata.id && standard !== metadata.label) {
|
|
497
|
+
throw new Error(`KFD-3 witness standard must match ${metadata.key}`);
|
|
498
|
+
}
|
|
499
|
+
const declaredSurfaces = declaredSurfacesFromWitness(witness).map((surface, index) => normalizeKfd3Surface(surface, index, "declaredSurfaces"));
|
|
500
|
+
if (declaredSurfaces.length === 0) {
|
|
501
|
+
throw new Error("KFD-3 pre-build witness must declare at least one collaboration/control surface");
|
|
502
|
+
}
|
|
503
|
+
const registry = normalizeRegistry(witness.registry || witness.sourceRegistry || witness.source_registry || witness.collaborationInterface?.sourceRegistry || witness.collaboration_interface?.sourceRegistry);
|
|
504
|
+
const closure = witness.collaborationInterface?.closure || witness.collaboration_interface?.closure || {};
|
|
505
|
+
const auditBoundary = normalizeKfd3AuditBoundary(witness.auditBoundary || witness.audit_boundary || closure, { closure });
|
|
506
|
+
const expectedArtifactVerification = witness.expectedArtifactVerification && typeof witness.expectedArtifactVerification === "object" && !Array.isArray(witness.expectedArtifactVerification)
|
|
507
|
+
? { ...witness.expectedArtifactVerification, command: optionalString(witness.expectedArtifactVerification.command) }
|
|
508
|
+
: { command: optionalString(witness.artifactVerifyCommand || witness.artifact_verify_command) };
|
|
509
|
+
return {
|
|
510
|
+
schemaVersion: 1,
|
|
511
|
+
contract: KFD3_PREBUILD_WITNESS_CONTRACT,
|
|
512
|
+
id: nonEmptyString(witness.id, "KFD-3 pre-build witness id"),
|
|
513
|
+
standard: metadata.key,
|
|
514
|
+
standardLabel: metadata.label,
|
|
515
|
+
supportLevel: optionalString(witness.supportLevel || witness.support_level || witness.support?.level || "release"),
|
|
516
|
+
source: witness.source && typeof witness.source === "object" && !Array.isArray(witness.source)
|
|
517
|
+
? { ...witness.source }
|
|
518
|
+
: {},
|
|
519
|
+
registry,
|
|
520
|
+
participantProfiles: normalizeStringArray(
|
|
521
|
+
witness.participantProfiles ||
|
|
522
|
+
witness.participant_profiles ||
|
|
523
|
+
witness.profiles ||
|
|
524
|
+
witness.collaborationInterface?.participants?.map((entry) => entry.id || entry.name) ||
|
|
525
|
+
witness.collaboration_interface?.participants?.map((entry) => entry.id || entry.name),
|
|
526
|
+
),
|
|
527
|
+
expectedArtifactVerification,
|
|
528
|
+
collaborationInterfaceDigest: optionalString(
|
|
529
|
+
witness.collaborationInterfaceDigest ||
|
|
530
|
+
witness.collaboration_interface_digest ||
|
|
531
|
+
witness.collaborationInterface?.digest ||
|
|
532
|
+
witness.collaboration_interface?.digest,
|
|
533
|
+
),
|
|
534
|
+
auditBoundary,
|
|
535
|
+
residualRisk: normalizeKfd3ResidualRisk(
|
|
536
|
+
witness.residualRisk || witness.residual_risk || witness.auditBoundary?.residualRisk || witness.audit_boundary?.residual_risk || [],
|
|
537
|
+
{ auditBoundary },
|
|
538
|
+
),
|
|
539
|
+
responsibility: normalizeKfd3Responsibility(witness.responsibility || witness.owners, {
|
|
540
|
+
registry,
|
|
541
|
+
artifactVerifyCommand: expectedArtifactVerification.command,
|
|
542
|
+
}),
|
|
543
|
+
declaredSurfaces,
|
|
544
|
+
metadata: {
|
|
545
|
+
kfdPackage: metadata.package,
|
|
546
|
+
schemaIds: metadata.schemaIds,
|
|
547
|
+
schemaPaths: metadata.schemaPaths,
|
|
548
|
+
hasCollaborationSchemas: Boolean(metadata.hasCollaborationSchemas),
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export function normalizeKfd3CollaborationInterfaceArtifactWitness(witness, { metadata = resolveKfd3Metadata() } = {}) {
|
|
554
|
+
if (!witness || typeof witness !== "object" || Array.isArray(witness)) {
|
|
555
|
+
throw new Error("KFD-3 artifact witness must be a JSON object");
|
|
556
|
+
}
|
|
557
|
+
const standard = optionalString(witness.standard || metadata.key);
|
|
558
|
+
if (standard && standard !== metadata.key && standard !== metadata.id && standard !== metadata.label) {
|
|
559
|
+
throw new Error(`KFD-3 artifact witness standard must match ${metadata.key}`);
|
|
560
|
+
}
|
|
561
|
+
const exposedSurfaces = artifactSurfacesFromWitness(witness).map((surface, index) => normalizeKfd3Surface(surface, index, "exposedSurfaces"));
|
|
562
|
+
return {
|
|
563
|
+
schemaVersion: 1,
|
|
564
|
+
contract: KFD3_ARTIFACT_WITNESS_CONTRACT,
|
|
565
|
+
id: nonEmptyString(witness.id, "KFD-3 artifact witness id"),
|
|
566
|
+
standard: metadata.key,
|
|
567
|
+
standardLabel: metadata.label,
|
|
568
|
+
artifact: normalizeArtifactIdentity(witness.artifact),
|
|
569
|
+
registry: normalizeRegistry(witness.registry || witness.sourceRegistry || witness.source_registry),
|
|
570
|
+
collaborationInterfaceDigest: optionalString(
|
|
571
|
+
witness.collaborationInterfaceDigest ||
|
|
572
|
+
witness.collaboration_interface_digest ||
|
|
573
|
+
witness.collaborationInterface?.digest ||
|
|
574
|
+
witness.collaboration_interface?.digest,
|
|
575
|
+
),
|
|
576
|
+
exposedSurfaces,
|
|
577
|
+
verifier: witness.verifier && typeof witness.verifier === "object" && !Array.isArray(witness.verifier)
|
|
578
|
+
? { ...witness.verifier }
|
|
579
|
+
: {},
|
|
580
|
+
metadata: {
|
|
581
|
+
kfdPackage: metadata.package,
|
|
582
|
+
schemaIds: metadata.schemaIds,
|
|
583
|
+
schemaPaths: metadata.schemaPaths,
|
|
584
|
+
hasCollaborationSchemas: Boolean(metadata.hasCollaborationSchemas),
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function isParticipantPublic(surface) {
|
|
590
|
+
return Boolean(surface.public && surface.participantFacing);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function isDeclaredShipped(surface) {
|
|
594
|
+
return ["shipped", "stable", "available", "ga", "production"].includes(surface.availability);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function isDeclaredPublic(surface) {
|
|
598
|
+
return isParticipantPublic(surface) && !["internal", "unsupported", "not-shipped", "not_shipped"].includes(surface.availability);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function idSet(surfaces) {
|
|
602
|
+
return new Set(surfaces.map((surface) => surface.id));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function sortedSetDiff(left, right) {
|
|
606
|
+
return [...left].filter((entry) => !right.has(entry)).sort();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function kfd3ReleaseStatus({ comparisonStatus, supportLevel, residualRisk = [] } = {}) {
|
|
610
|
+
if (comparisonStatus === "passed") {
|
|
611
|
+
return residualRisk.length > 0 ? "audited" : "enforced";
|
|
612
|
+
}
|
|
613
|
+
if (["draft", "partial", "missing", "unsupported"].includes(supportLevel)) {
|
|
614
|
+
return "draft";
|
|
615
|
+
}
|
|
616
|
+
return "declared";
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function kfd3TrustResult(comparisonStatus) {
|
|
620
|
+
if (comparisonStatus === "passed") return "pass";
|
|
621
|
+
if (comparisonStatus === "downgraded") return "downgraded";
|
|
622
|
+
return "fail";
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function kfd3TrustStatement({
|
|
626
|
+
comparisonStatus,
|
|
627
|
+
missingDeclaredShipped = [],
|
|
628
|
+
unclassifiedArtifactPublic = [],
|
|
629
|
+
residualRisk = [],
|
|
630
|
+
} = {}) {
|
|
631
|
+
if (comparisonStatus === "passed") {
|
|
632
|
+
return unclassifiedArtifactPublic.length === 0
|
|
633
|
+
? "No unclassified reachable surface within the declared audit boundary."
|
|
634
|
+
: "Unclassified reachable surfaces remain within the declared audit boundary.";
|
|
635
|
+
}
|
|
636
|
+
if (missingDeclaredShipped.length > 0) {
|
|
637
|
+
return "One or more declared shipped public surfaces are missing from the artifact evidence.";
|
|
638
|
+
}
|
|
639
|
+
if (unclassifiedArtifactPublic.length > 0) {
|
|
640
|
+
return "The artifact exposes participant-facing public surfaces not declared by the pre-build witness.";
|
|
641
|
+
}
|
|
642
|
+
if (residualRisk.length > 0) {
|
|
643
|
+
return "KFD-3 proof is downgraded and residual risks remain explicit.";
|
|
644
|
+
}
|
|
645
|
+
return "KFD-3 collaboration-interface proof did not pass.";
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function evidenceDescriptor({ path = "", sha256 = "", canonicalSha256 = "" } = {}) {
|
|
649
|
+
return {
|
|
650
|
+
path: optionalString(path),
|
|
651
|
+
sha256: optionalString(sha256),
|
|
652
|
+
canonicalSha256: optionalString(canonicalSha256),
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function kfd1SurfacesFromWitness(witness = {}) {
|
|
657
|
+
return [
|
|
658
|
+
...(Array.isArray(witness.surfaces) ? witness.surfaces : []),
|
|
659
|
+
...groupedKfd1SurfacesFromSource(witness),
|
|
660
|
+
...groupedKfd1SurfacesFromSource(witness.standardContract),
|
|
661
|
+
...groupedKfd1SurfacesFromSource(witness.standard_contract),
|
|
662
|
+
...groupedKfd1SurfacesFromSource(witness.selfContract),
|
|
663
|
+
...groupedKfd1SurfacesFromSource(witness.self_contract),
|
|
664
|
+
...groupedKfd1SurfacesFromSource(witness.contractWorld),
|
|
665
|
+
];
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function normalizeKfd1SelfHostingBoundary(value = {}, { contractWorld = {} } = {}) {
|
|
669
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
670
|
+
return {
|
|
671
|
+
mode: optionalString(source.mode || (source.enabled || contractWorld.selfHosted ? "self-hosted-standard-contract" : "external-contract-world")),
|
|
672
|
+
sourceScope: optionalString(source.sourceScope || source.source_scope || "KFD source standard metadata, schemas, package exports, and site-consumption entrypoints"),
|
|
673
|
+
artifactScope: optionalString(source.artifactScope || source.artifact_scope || "packaged public artifact surfaces"),
|
|
674
|
+
boundary: optionalString(source.boundary || "source-to-artifact byte equality for declared standard-contract surfaces"),
|
|
675
|
+
residualRisk: normalizeStringObjectArray(
|
|
676
|
+
source.residualRisk || source.residual_risk || source.nonEnumerableResidualRisk || source.non_enumerable_residual_risk || [],
|
|
677
|
+
"selfHostingBoundary.residualRisk",
|
|
678
|
+
),
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function normalizeKfd1Responsibility(value = {}, { contractWorld = {} } = {}) {
|
|
683
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
684
|
+
return {
|
|
685
|
+
sourceContractOwner: optionalString(source.sourceContractOwner || source.source_contract_owner || source.standardOwner || source.standard_owner || contractWorld.owner || "product"),
|
|
686
|
+
artifactVerificationOwner: optionalString(source.artifactVerificationOwner || source.artifact_verification_owner || "buildchain-release-passport"),
|
|
687
|
+
releasePassportProofOwner: optionalString(source.releasePassportProofOwner || source.release_passport_proof_owner || "buildchain"),
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function normalizeKfd1StandardContract(value = {}, { metadata = resolveKfd1Metadata() } = {}) {
|
|
692
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
693
|
+
return {
|
|
694
|
+
id: optionalString(source.id || source.name),
|
|
695
|
+
schemaId: optionalString(source.schemaId || source.schema_id || metadata.schemaIds.contractWorld),
|
|
696
|
+
path: source.path ? normalizePath(source.path, "standardContract.path") : "",
|
|
697
|
+
sha256: source.sha256 ? normalizeHash(source.sha256, "standardContract.sha256") : "",
|
|
698
|
+
digest: optionalString(source.digest || (source.sha256 ? `sha256:${normalizeHash(source.sha256, "standardContract.sha256")}` : "")),
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
161
702
|
export function normalizeKfd1ContractWorldWitness(witness, { metadata = resolveKfd1Metadata() } = {}) {
|
|
162
703
|
if (!witness || typeof witness !== "object" || Array.isArray(witness)) {
|
|
163
704
|
throw new Error("KFD-1 witness must be a JSON object");
|
|
164
705
|
}
|
|
165
|
-
const surfaces =
|
|
166
|
-
? witness.surfaces.map((surface, index) => normalizeSurface(surface, index))
|
|
167
|
-
: [];
|
|
706
|
+
const surfaces = kfd1SurfacesFromWitness(witness).map((surface, index) => normalizeSurface(surface, index));
|
|
168
707
|
if (surfaces.length === 0) {
|
|
169
708
|
throw new Error("KFD-1 witness surfaces[] must include at least one artifact surface");
|
|
170
709
|
}
|
|
@@ -191,6 +730,14 @@ export function normalizeKfd1ContractWorldWitness(witness, { metadata = resolveK
|
|
|
191
730
|
schemaId: metadata.schemaIds.contractWorld,
|
|
192
731
|
digest: "",
|
|
193
732
|
},
|
|
733
|
+
standardContract: normalizeKfd1StandardContract(witness.standardContract || witness.standard_contract || witness.selfContract || witness.self_contract || witness.contractWorld, { metadata }),
|
|
734
|
+
selfHostingBoundary: normalizeKfd1SelfHostingBoundary(witness.selfHostingBoundary || witness.self_hosting_boundary, {
|
|
735
|
+
contractWorld: witness.contractWorld,
|
|
736
|
+
}),
|
|
737
|
+
responsibility: normalizeKfd1Responsibility(witness.responsibility || witness.owners, {
|
|
738
|
+
contractWorld: witness.contractWorld,
|
|
739
|
+
}),
|
|
740
|
+
sourceVerificationRequired: Boolean(witness.standardContract || witness.standard_contract || witness.selfContract || witness.self_contract || witness.selfHostingBoundary || witness.self_hosting_boundary),
|
|
194
741
|
canonicalPolicy: normalizeEvidenceFile(witness.canonicalPolicy || witness.canonical_policy, "canonicalPolicy"),
|
|
195
742
|
registry: normalizeEvidenceFile(witness.registry, "registry"),
|
|
196
743
|
surfaces,
|
|
@@ -224,6 +771,14 @@ function resolveArtifactFile({ cwd, artifactRoot, artifacts = [], artifactPath }
|
|
|
224
771
|
return candidates.find((candidate) => candidate && fs.existsSync(candidate) && fs.statSync(candidate).isFile()) || "";
|
|
225
772
|
}
|
|
226
773
|
|
|
774
|
+
function resolveSourceFile({ cwd, sourcePath }) {
|
|
775
|
+
if (!sourcePath) {
|
|
776
|
+
return "";
|
|
777
|
+
}
|
|
778
|
+
const candidate = path.resolve(cwd, sourcePath);
|
|
779
|
+
return fs.existsSync(candidate) && fs.statSync(candidate).isFile() ? candidate : "";
|
|
780
|
+
}
|
|
781
|
+
|
|
227
782
|
export function createKfd1ReleaseGateEvidence({
|
|
228
783
|
cwd = process.cwd(),
|
|
229
784
|
artifactRoot = "",
|
|
@@ -238,6 +793,39 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
238
793
|
}
|
|
239
794
|
const worlds = normalizedWitnesses.map((witness) => {
|
|
240
795
|
const preBuildWitnessSha256 = sha256Json(witness);
|
|
796
|
+
const sourceResults = witness.surfaces.map((surface) => {
|
|
797
|
+
if (!surface.sourcePath || !surface.sourceSha256) {
|
|
798
|
+
return {
|
|
799
|
+
name: surface.name,
|
|
800
|
+
sourcePath: surface.sourcePath,
|
|
801
|
+
expectedSha256: surface.sourceSha256,
|
|
802
|
+
actualSha256: "",
|
|
803
|
+
status: witness.sourceVerificationRequired ? "failed" : "not-declared",
|
|
804
|
+
reason: witness.sourceVerificationRequired ? "source-digest-not-declared" : "",
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const filePath = resolveSourceFile({ cwd, sourcePath: surface.sourcePath });
|
|
808
|
+
if (!filePath) {
|
|
809
|
+
return {
|
|
810
|
+
name: surface.name,
|
|
811
|
+
sourcePath: surface.sourcePath,
|
|
812
|
+
expectedSha256: surface.sourceSha256,
|
|
813
|
+
actualSha256: "",
|
|
814
|
+
status: witness.sourceVerificationRequired ? "failed" : "unavailable",
|
|
815
|
+
reason: "source-missing",
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
const actualSha256 = sha256File(filePath);
|
|
819
|
+
const passed = actualSha256 === surface.sourceSha256;
|
|
820
|
+
return {
|
|
821
|
+
name: surface.name,
|
|
822
|
+
sourcePath: surface.sourcePath,
|
|
823
|
+
expectedSha256: surface.sourceSha256,
|
|
824
|
+
actualSha256,
|
|
825
|
+
status: passed ? "passed" : "failed",
|
|
826
|
+
reason: passed ? "" : "source-digest-mismatch",
|
|
827
|
+
};
|
|
828
|
+
});
|
|
241
829
|
const surfaceResults = witness.surfaces.map((surface) => {
|
|
242
830
|
const filePath = resolveArtifactFile({
|
|
243
831
|
cwd,
|
|
@@ -248,6 +836,8 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
248
836
|
if (!filePath) {
|
|
249
837
|
return {
|
|
250
838
|
name: surface.name,
|
|
839
|
+
sourcePath: surface.sourcePath,
|
|
840
|
+
sourceSha256: surface.sourceSha256,
|
|
251
841
|
artifactPath: surface.artifactPath,
|
|
252
842
|
expectedSha256: surface.expectedSha256,
|
|
253
843
|
actualSha256: "",
|
|
@@ -260,6 +850,8 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
260
850
|
const passed = actualSha256 === surface.expectedSha256;
|
|
261
851
|
return {
|
|
262
852
|
name: surface.name,
|
|
853
|
+
sourcePath: surface.sourcePath,
|
|
854
|
+
sourceSha256: surface.sourceSha256,
|
|
263
855
|
artifactPath: surface.artifactPath,
|
|
264
856
|
expectedSha256: surface.expectedSha256,
|
|
265
857
|
actualSha256,
|
|
@@ -269,12 +861,35 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
269
861
|
};
|
|
270
862
|
});
|
|
271
863
|
const status = surfaceResults.every((surface) => surface.status === "passed") ? "passed" : "failed";
|
|
864
|
+
const sourceStatus = sourceResults.every((surface) => ["passed", "not-declared", "unavailable"].includes(surface.status)) &&
|
|
865
|
+
(!witness.sourceVerificationRequired || sourceResults.every((surface) => surface.status === "passed"))
|
|
866
|
+
? "passed"
|
|
867
|
+
: "failed";
|
|
868
|
+
const worldStatus = status === "passed" && sourceStatus === "passed" ? "passed" : "failed";
|
|
272
869
|
return {
|
|
273
870
|
id: witness.id,
|
|
274
871
|
standard: metadata.label,
|
|
275
872
|
standardKey: metadata.key,
|
|
873
|
+
result: worldStatus,
|
|
276
874
|
preBuildWitnessSha256,
|
|
875
|
+
sourceHashes: {
|
|
876
|
+
sha256: sha256Json(sourceResults),
|
|
877
|
+
surfaceCount: sourceResults.length,
|
|
878
|
+
},
|
|
879
|
+
artifactHashes: {
|
|
880
|
+
sha256: sha256Json(surfaceResults),
|
|
881
|
+
surfaceCount: surfaceResults.length,
|
|
882
|
+
},
|
|
277
883
|
witness,
|
|
884
|
+
standardContract: witness.standardContract,
|
|
885
|
+
selfHostingBoundary: witness.selfHostingBoundary,
|
|
886
|
+
responsibility: witness.responsibility,
|
|
887
|
+
sourceVerification: {
|
|
888
|
+
status: sourceStatus,
|
|
889
|
+
verifiedAt,
|
|
890
|
+
required: Boolean(witness.sourceVerificationRequired),
|
|
891
|
+
surfaces: sourceResults,
|
|
892
|
+
},
|
|
278
893
|
artifactVerification: {
|
|
279
894
|
status,
|
|
280
895
|
verifiedAt,
|
|
@@ -282,13 +897,21 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
282
897
|
},
|
|
283
898
|
};
|
|
284
899
|
});
|
|
285
|
-
const status = worlds.every((world) => world.
|
|
900
|
+
const status = worlds.every((world) => world.result === "passed") ? "passed" : "failed";
|
|
286
901
|
return {
|
|
287
902
|
key: metadata.key,
|
|
288
903
|
passportSection: {
|
|
289
904
|
schemaVersion: 1,
|
|
290
905
|
contract: KFD1_RELEASE_GATE_CONTRACT,
|
|
291
906
|
status,
|
|
907
|
+
selfContractVerification: {
|
|
908
|
+
result: status === "passed" ? "pass" : "fail",
|
|
909
|
+
contractWorldCount: worlds.length,
|
|
910
|
+
selfHosted: worlds.some((world) => world.sourceVerification.required),
|
|
911
|
+
responsibility: {
|
|
912
|
+
proofOwner: "buildchain-release-passport",
|
|
913
|
+
},
|
|
914
|
+
},
|
|
292
915
|
metadata: {
|
|
293
916
|
standard: {
|
|
294
917
|
key: metadata.key,
|
|
@@ -311,6 +934,207 @@ export function createKfd1ReleaseGateEvidence({
|
|
|
311
934
|
};
|
|
312
935
|
}
|
|
313
936
|
|
|
937
|
+
export function createKfd3CollaborationInterfaceReleaseGateEvidence({
|
|
938
|
+
prebuildWitnesses = [],
|
|
939
|
+
artifactWitnesses = [],
|
|
940
|
+
prebuildWitnessMetas = [],
|
|
941
|
+
artifactWitnessMetas = [],
|
|
942
|
+
artifactCommandMeta = undefined,
|
|
943
|
+
verifiedAt = new Date().toISOString(),
|
|
944
|
+
metadata = resolveKfd3Metadata(),
|
|
945
|
+
} = {}) {
|
|
946
|
+
const normalizedPrebuildWitnesses = (prebuildWitnesses || [])
|
|
947
|
+
.filter(Boolean)
|
|
948
|
+
.map((witness) => normalizeKfd3CollaborationInterfacePrebuildWitness(witness, { metadata }));
|
|
949
|
+
if (normalizedPrebuildWitnesses.length === 0) {
|
|
950
|
+
return undefined;
|
|
951
|
+
}
|
|
952
|
+
const normalizedArtifactWitnesses = (artifactWitnesses || [])
|
|
953
|
+
.filter(Boolean)
|
|
954
|
+
.map((witness) => normalizeKfd3CollaborationInterfaceArtifactWitness(witness, { metadata }));
|
|
955
|
+
const artifactById = new Map(normalizedArtifactWitnesses.map((witness) => [witness.id, witness]));
|
|
956
|
+
const singleArtifactWitness = normalizedArtifactWitnesses.length === 1 ? normalizedArtifactWitnesses[0] : undefined;
|
|
957
|
+
const prebuildMetaById = new Map((prebuildWitnessMetas || [])
|
|
958
|
+
.filter((meta) => meta?.value?.id)
|
|
959
|
+
.map((meta) => [meta.value.id, meta]));
|
|
960
|
+
const artifactMetaById = new Map((artifactWitnessMetas || [])
|
|
961
|
+
.filter((meta) => meta?.value?.id)
|
|
962
|
+
.map((meta) => [meta.value.id, meta]));
|
|
963
|
+
const singleArtifactMeta = (artifactWitnessMetas || []).filter((meta) => meta?.value).length === 1
|
|
964
|
+
? (artifactWitnessMetas || []).find((meta) => meta?.value)
|
|
965
|
+
: undefined;
|
|
966
|
+
const worlds = normalizedPrebuildWitnesses.map((prebuildWitness) => {
|
|
967
|
+
const artifactWitness = artifactById.get(prebuildWitness.id) || singleArtifactWitness;
|
|
968
|
+
const prebuildMeta = prebuildMetaById.get(prebuildWitness.id) || {};
|
|
969
|
+
const artifactMeta = artifactWitness ? (artifactMetaById.get(artifactWitness.id) || singleArtifactMeta || artifactCommandMeta || {}) : {};
|
|
970
|
+
const preBuildWitnessSha256 = sha256Json(prebuildWitness);
|
|
971
|
+
const artifactWitnessSha256 = artifactWitness ? sha256Json(artifactWitness) : "";
|
|
972
|
+
const declaredPublic = prebuildWitness.declaredSurfaces.filter(isDeclaredPublic);
|
|
973
|
+
const declaredShippedPublic = declaredPublic.filter(isDeclaredShipped);
|
|
974
|
+
const artifactPublic = artifactWitness
|
|
975
|
+
? artifactWitness.exposedSurfaces.filter(isParticipantPublic)
|
|
976
|
+
: [];
|
|
977
|
+
const declaredPublicIds = idSet(declaredPublic);
|
|
978
|
+
const declaredShippedIds = idSet(declaredShippedPublic);
|
|
979
|
+
const artifactPublicIds = idSet(artifactPublic);
|
|
980
|
+
const missingDeclaredShipped = sortedSetDiff(declaredShippedIds, artifactPublicIds);
|
|
981
|
+
const unclassifiedArtifactPublic = sortedSetDiff(artifactPublicIds, declaredPublicIds);
|
|
982
|
+
const reasons = [];
|
|
983
|
+
if (!metadata.hasCollaborationSchemas) {
|
|
984
|
+
reasons.push("kfd-metadata-schema-missing");
|
|
985
|
+
}
|
|
986
|
+
if (!artifactWitness) {
|
|
987
|
+
reasons.push("artifact-witness-missing");
|
|
988
|
+
}
|
|
989
|
+
if (
|
|
990
|
+
prebuildWitness.collaborationInterfaceDigest &&
|
|
991
|
+
artifactWitness?.collaborationInterfaceDigest &&
|
|
992
|
+
prebuildWitness.collaborationInterfaceDigest !== artifactWitness.collaborationInterfaceDigest
|
|
993
|
+
) {
|
|
994
|
+
reasons.push("collaboration-interface-digest-mismatch");
|
|
995
|
+
}
|
|
996
|
+
if (missingDeclaredShipped.length > 0) {
|
|
997
|
+
reasons.push("declared-shipped-surface-missing");
|
|
998
|
+
}
|
|
999
|
+
if (unclassifiedArtifactPublic.length > 0) {
|
|
1000
|
+
reasons.push("artifact-public-surface-not-declared");
|
|
1001
|
+
}
|
|
1002
|
+
const supportLevel = prebuildWitness.supportLevel || "release";
|
|
1003
|
+
if (["draft", "partial", "missing", "unsupported"].includes(supportLevel)) {
|
|
1004
|
+
reasons.push(`support-level-${supportLevel}`);
|
|
1005
|
+
}
|
|
1006
|
+
const status = reasons.length === 0 ? "passed" : supportLevel === "release" ? "failed" : "downgraded";
|
|
1007
|
+
const residualRisk = prebuildWitness.residualRisk || [];
|
|
1008
|
+
const releaseStatus = kfd3ReleaseStatus({ comparisonStatus: status, supportLevel, residualRisk });
|
|
1009
|
+
const trustResult = kfd3TrustResult(status);
|
|
1010
|
+
const declaredCapabilityVerification = {
|
|
1011
|
+
status,
|
|
1012
|
+
result: missingDeclaredShipped.length === 0 ? "passed" : "failed",
|
|
1013
|
+
declaredCapabilityCount: declaredPublic.length,
|
|
1014
|
+
declaredShippedPublicSurfaceCount: declaredShippedPublic.length,
|
|
1015
|
+
implementedPublicSurfaceCount: artifactPublic.length,
|
|
1016
|
+
missingDeclaredShipped,
|
|
1017
|
+
};
|
|
1018
|
+
const reverseAudit = {
|
|
1019
|
+
status: unclassifiedArtifactPublic.length === 0 ? "passed" : "failed",
|
|
1020
|
+
auditBoundary: prebuildWitness.auditBoundary,
|
|
1021
|
+
reachablePublicSurfaceCount: artifactPublic.length,
|
|
1022
|
+
unclassifiedReachableSurfaces: unclassifiedArtifactPublic,
|
|
1023
|
+
explicitlyExemptedSurfaces: prebuildWitness.auditBoundary.explicitlyExemptedSurfaces,
|
|
1024
|
+
nonExhaustivelyEnumerableSurfaces: prebuildWitness.auditBoundary.nonExhaustivelyEnumerableSurfaces,
|
|
1025
|
+
statement: unclassifiedArtifactPublic.length === 0
|
|
1026
|
+
? "No unclassified reachable surface within the declared audit boundary."
|
|
1027
|
+
: "Unclassified reachable surfaces were found within the declared audit boundary.",
|
|
1028
|
+
};
|
|
1029
|
+
const witnessEvidence = {
|
|
1030
|
+
prebuild: evidenceDescriptor({
|
|
1031
|
+
path: prebuildMeta.path,
|
|
1032
|
+
sha256: prebuildMeta.sha256,
|
|
1033
|
+
canonicalSha256: preBuildWitnessSha256,
|
|
1034
|
+
}),
|
|
1035
|
+
artifact: evidenceDescriptor({
|
|
1036
|
+
path: artifactMeta.path,
|
|
1037
|
+
sha256: artifactMeta.sha256,
|
|
1038
|
+
canonicalSha256: artifactWitnessSha256,
|
|
1039
|
+
}),
|
|
1040
|
+
artifactVerifyCommandSha256: optionalString(artifactCommandMeta?.sha256),
|
|
1041
|
+
};
|
|
1042
|
+
return {
|
|
1043
|
+
id: prebuildWitness.id,
|
|
1044
|
+
standard: metadata.label,
|
|
1045
|
+
standardKey: metadata.key,
|
|
1046
|
+
preBuildWitnessSha256,
|
|
1047
|
+
artifactWitnessSha256,
|
|
1048
|
+
prebuildWitness,
|
|
1049
|
+
artifactWitness: artifactWitness || undefined,
|
|
1050
|
+
declaredSurfaces: prebuildWitness.declaredSurfaces,
|
|
1051
|
+
exposedSurfaces: artifactPublic,
|
|
1052
|
+
factSource: {
|
|
1053
|
+
role: "product-owned",
|
|
1054
|
+
registry: prebuildWitness.registry,
|
|
1055
|
+
collaborationInterfaceDigest: prebuildWitness.collaborationInterfaceDigest,
|
|
1056
|
+
},
|
|
1057
|
+
witnessEvidence,
|
|
1058
|
+
auditBoundary: prebuildWitness.auditBoundary,
|
|
1059
|
+
residualRisk,
|
|
1060
|
+
responsibility: prebuildWitness.responsibility,
|
|
1061
|
+
releaseStatus,
|
|
1062
|
+
trustProof: {
|
|
1063
|
+
contract: "kungfu-buildchain-kfd-3-passport-trust-proof",
|
|
1064
|
+
result: trustResult,
|
|
1065
|
+
releaseStatus,
|
|
1066
|
+
statement: kfd3TrustStatement({
|
|
1067
|
+
comparisonStatus: status,
|
|
1068
|
+
missingDeclaredShipped,
|
|
1069
|
+
unclassifiedArtifactPublic,
|
|
1070
|
+
residualRisk,
|
|
1071
|
+
}),
|
|
1072
|
+
factSourceRole: "product-owned-registry-and-witnesses",
|
|
1073
|
+
proofOwner: "buildchain-release-passport",
|
|
1074
|
+
declaredCapabilityVerification,
|
|
1075
|
+
reverseAudit,
|
|
1076
|
+
residualRisk,
|
|
1077
|
+
responsibility: prebuildWitness.responsibility,
|
|
1078
|
+
},
|
|
1079
|
+
declaredCapabilityVerification,
|
|
1080
|
+
reverseAudit,
|
|
1081
|
+
comparison: {
|
|
1082
|
+
status,
|
|
1083
|
+
verifiedAt,
|
|
1084
|
+
supportLevel,
|
|
1085
|
+
declaredPublicSurfaceCount: declaredPublic.length,
|
|
1086
|
+
declaredShippedPublicSurfaceCount: declaredShippedPublic.length,
|
|
1087
|
+
artifactPublicSurfaceCount: artifactPublic.length,
|
|
1088
|
+
missingDeclaredShipped,
|
|
1089
|
+
unclassifiedArtifactPublic,
|
|
1090
|
+
reasons,
|
|
1091
|
+
},
|
|
1092
|
+
};
|
|
1093
|
+
});
|
|
1094
|
+
const status = worlds.every((world) => world.comparison.status === "passed") ? "passed" : "failed";
|
|
1095
|
+
const releaseStatuses = [...new Set(worlds.map((world) => world.releaseStatus))];
|
|
1096
|
+
const trustResult = status === "passed" ? "pass" : worlds.some((world) => world.trustProof.result === "downgraded") ? "downgraded" : "fail";
|
|
1097
|
+
return {
|
|
1098
|
+
key: metadata.key,
|
|
1099
|
+
passportSection: {
|
|
1100
|
+
schemaVersion: 1,
|
|
1101
|
+
contract: KFD3_RELEASE_GATE_CONTRACT,
|
|
1102
|
+
status,
|
|
1103
|
+
releaseStatus: releaseStatuses.length === 1 ? releaseStatuses[0] : status === "passed" ? "audited" : "declared",
|
|
1104
|
+
trustProof: {
|
|
1105
|
+
contract: "kungfu-buildchain-kfd-3-passport-trust-proof",
|
|
1106
|
+
result: trustResult,
|
|
1107
|
+
statement: status === "passed"
|
|
1108
|
+
? "No unclassified reachable surface within the declared audit boundary."
|
|
1109
|
+
: "One or more KFD-3 collaboration-interface witness pairs failed or downgraded.",
|
|
1110
|
+
factSourceRole: "product-owned-registry-and-witnesses",
|
|
1111
|
+
proofOwner: "buildchain-release-passport",
|
|
1112
|
+
collaborationInterfaceCount: worlds.length,
|
|
1113
|
+
audited: worlds.every((world) => world.trustProof.result === "pass"),
|
|
1114
|
+
},
|
|
1115
|
+
metadata: {
|
|
1116
|
+
standard: {
|
|
1117
|
+
key: metadata.key,
|
|
1118
|
+
id: metadata.id,
|
|
1119
|
+
label: metadata.label,
|
|
1120
|
+
title: metadata.title,
|
|
1121
|
+
revision: metadata.revision,
|
|
1122
|
+
status: metadata.status,
|
|
1123
|
+
},
|
|
1124
|
+
schemas: {
|
|
1125
|
+
ids: metadata.schemaIds,
|
|
1126
|
+
paths: metadata.schemaPaths,
|
|
1127
|
+
metadata: metadata.metadataSchema,
|
|
1128
|
+
hasCollaborationSchemas: Boolean(metadata.hasCollaborationSchemas),
|
|
1129
|
+
},
|
|
1130
|
+
package: metadata.package,
|
|
1131
|
+
},
|
|
1132
|
+
formatting: BUILDCHAIN_JSON_FORMATTING_POLICY,
|
|
1133
|
+
collaborationInterfaces: worlds,
|
|
1134
|
+
},
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
|
|
314
1138
|
export function validateKfd1ReleaseGateEvidence(section, { metadata = resolveKfd1Metadata() } = {}) {
|
|
315
1139
|
const issues = [];
|
|
316
1140
|
if (!section) {
|
|
@@ -367,6 +1191,38 @@ export function validateKfd1ReleaseGateEvidence(section, { metadata = resolveKfd
|
|
|
367
1191
|
details: { expected: metadata.key, actual: world.standardKey || "" },
|
|
368
1192
|
});
|
|
369
1193
|
}
|
|
1194
|
+
if (!world.sourceHashes?.sha256 || !world.artifactHashes?.sha256) {
|
|
1195
|
+
issues.push({
|
|
1196
|
+
level: "error",
|
|
1197
|
+
code: `${metadata.key}.contractWorlds[${worldIndex}].hashes`,
|
|
1198
|
+
message: "KFD-1 self contract evidence must record source and artifact hash summaries",
|
|
1199
|
+
details: { id: world.id || "" },
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
if (!world.selfHostingBoundary || typeof world.selfHostingBoundary !== "object" || Array.isArray(world.selfHostingBoundary)) {
|
|
1203
|
+
issues.push({
|
|
1204
|
+
level: "error",
|
|
1205
|
+
code: `${metadata.key}.contractWorlds[${worldIndex}].selfHostingBoundary`,
|
|
1206
|
+
message: "KFD-1 contract world must record the self-hosting boundary",
|
|
1207
|
+
details: { id: world.id || "" },
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
if (!world.responsibility?.sourceContractOwner || !world.responsibility?.artifactVerificationOwner || !world.responsibility?.releasePassportProofOwner) {
|
|
1211
|
+
issues.push({
|
|
1212
|
+
level: "error",
|
|
1213
|
+
code: `${metadata.key}.contractWorlds[${worldIndex}].responsibility`,
|
|
1214
|
+
message: "KFD-1 contract world must record source, artifact verification, and passport proof owners",
|
|
1215
|
+
details: { id: world.id || "" },
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
if (world.sourceVerification?.required && world.sourceVerification?.status !== "passed") {
|
|
1219
|
+
issues.push({
|
|
1220
|
+
level: "error",
|
|
1221
|
+
code: `${metadata.key}.contractWorlds[${worldIndex}].sourceVerification`,
|
|
1222
|
+
message: "KFD-1 self contract source verification must pass before release passport finalization",
|
|
1223
|
+
details: { id: world.id || "", status: world.sourceVerification?.status || "" },
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
370
1226
|
if (world.artifactVerification?.status !== "passed") {
|
|
371
1227
|
issues.push({
|
|
372
1228
|
level: "error",
|
|
@@ -411,3 +1267,207 @@ export function validateKfd1ReleaseGateEvidence(section, { metadata = resolveKfd
|
|
|
411
1267
|
}
|
|
412
1268
|
return issues;
|
|
413
1269
|
}
|
|
1270
|
+
|
|
1271
|
+
export function validateKfd3CollaborationInterfaceReleaseGateEvidence(section, { metadata = resolveKfd3Metadata() } = {}) {
|
|
1272
|
+
const issues = [];
|
|
1273
|
+
if (!section) {
|
|
1274
|
+
return issues;
|
|
1275
|
+
}
|
|
1276
|
+
if (typeof section !== "object" || Array.isArray(section)) {
|
|
1277
|
+
issues.push({
|
|
1278
|
+
level: "error",
|
|
1279
|
+
code: `${metadata.key}.object`,
|
|
1280
|
+
message: `${metadata.key} collaboration-interface evidence must be an object`,
|
|
1281
|
+
details: {},
|
|
1282
|
+
});
|
|
1283
|
+
return issues;
|
|
1284
|
+
}
|
|
1285
|
+
if (section.contract !== KFD3_RELEASE_GATE_CONTRACT) {
|
|
1286
|
+
issues.push({
|
|
1287
|
+
level: "error",
|
|
1288
|
+
code: `${metadata.key}.contract`,
|
|
1289
|
+
message: `${metadata.key}.contract must be ${KFD3_RELEASE_GATE_CONTRACT}`,
|
|
1290
|
+
details: {},
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
if (section.metadata?.standard?.key !== metadata.key) {
|
|
1294
|
+
issues.push({
|
|
1295
|
+
level: "error",
|
|
1296
|
+
code: `${metadata.key}.metadata.standard.key`,
|
|
1297
|
+
message: `${metadata.key} metadata key must come from the KFD metadata package`,
|
|
1298
|
+
details: { expected: metadata.key, actual: section.metadata?.standard?.key || "" },
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
if (!section.metadata?.schemas?.hasCollaborationSchemas) {
|
|
1302
|
+
issues.push({
|
|
1303
|
+
level: "error",
|
|
1304
|
+
code: `${metadata.key}.metadata.schemas.collaborationInterface`,
|
|
1305
|
+
message: "KFD-3 release evidence requires KFD package metadata for collaboration-interface and witness schemas",
|
|
1306
|
+
details: { package: metadata.package, schemaIds: section.metadata?.schemas?.ids || {} },
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
const interfaces = Array.isArray(section.collaborationInterfaces) ? section.collaborationInterfaces : [];
|
|
1310
|
+
if (interfaces.length === 0) {
|
|
1311
|
+
issues.push({
|
|
1312
|
+
level: "error",
|
|
1313
|
+
code: `${metadata.key}.collaborationInterfaces.empty`,
|
|
1314
|
+
message: `${metadata.key}.collaborationInterfaces must include at least one witness pair`,
|
|
1315
|
+
details: {},
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
if (!section.trustProof || typeof section.trustProof !== "object" || Array.isArray(section.trustProof)) {
|
|
1319
|
+
issues.push({
|
|
1320
|
+
level: "error",
|
|
1321
|
+
code: `${metadata.key}.trustProof`,
|
|
1322
|
+
message: "KFD-3 release evidence must include a release-passport trust proof summary",
|
|
1323
|
+
details: {},
|
|
1324
|
+
});
|
|
1325
|
+
} else {
|
|
1326
|
+
const allowedStatuses = new Set(["not-applicable", "declared", "draft", "audited", "enforced"]);
|
|
1327
|
+
if (!allowedStatuses.has(section.releaseStatus)) {
|
|
1328
|
+
issues.push({
|
|
1329
|
+
level: "error",
|
|
1330
|
+
code: `${metadata.key}.releaseStatus`,
|
|
1331
|
+
message: "KFD-3 release status must be not-applicable, declared, draft, audited, or enforced",
|
|
1332
|
+
details: { status: section.releaseStatus || "" },
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
if (!["pass", "fail", "downgraded"].includes(section.trustProof.result)) {
|
|
1336
|
+
issues.push({
|
|
1337
|
+
level: "error",
|
|
1338
|
+
code: `${metadata.key}.trustProof.result`,
|
|
1339
|
+
message: "KFD-3 trust proof result must be pass, fail, or downgraded",
|
|
1340
|
+
details: { result: section.trustProof.result || "" },
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
for (const [index, entry] of interfaces.entries()) {
|
|
1345
|
+
if (!entry.preBuildWitnessSha256) {
|
|
1346
|
+
issues.push({
|
|
1347
|
+
level: "error",
|
|
1348
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].preBuildWitnessSha256`,
|
|
1349
|
+
message: "KFD-3 collaboration-interface evidence must record the frozen pre-build witness digest",
|
|
1350
|
+
details: {},
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
if (!entry.artifactWitnessSha256) {
|
|
1354
|
+
issues.push({
|
|
1355
|
+
level: "error",
|
|
1356
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].artifactWitnessSha256`,
|
|
1357
|
+
message: "KFD-3 collaboration-interface evidence must record the artifact-side witness digest",
|
|
1358
|
+
details: { id: entry.id || "" },
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
if (entry.standardKey !== metadata.key) {
|
|
1362
|
+
issues.push({
|
|
1363
|
+
level: "error",
|
|
1364
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].standardKey`,
|
|
1365
|
+
message: "KFD-3 collaboration-interface standardKey must match KFD metadata",
|
|
1366
|
+
details: { expected: metadata.key, actual: entry.standardKey || "" },
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
const comparison = entry.comparison || {};
|
|
1370
|
+
if (comparison.status !== "passed") {
|
|
1371
|
+
issues.push({
|
|
1372
|
+
level: "error",
|
|
1373
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].comparison`,
|
|
1374
|
+
message: "KFD-3 collaboration-interface artifact closure verification must pass before release passport finalization",
|
|
1375
|
+
details: {
|
|
1376
|
+
id: entry.id || "",
|
|
1377
|
+
status: comparison.status || "",
|
|
1378
|
+
reasons: comparison.reasons || [],
|
|
1379
|
+
},
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
if (Array.isArray(comparison.missingDeclaredShipped) && comparison.missingDeclaredShipped.length > 0) {
|
|
1383
|
+
issues.push({
|
|
1384
|
+
level: "error",
|
|
1385
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].missingDeclaredShipped`,
|
|
1386
|
+
message: "KFD-3 artifact is missing declared shipped public surfaces",
|
|
1387
|
+
details: { id: entry.id || "", surfaces: comparison.missingDeclaredShipped },
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
if (Array.isArray(comparison.unclassifiedArtifactPublic) && comparison.unclassifiedArtifactPublic.length > 0) {
|
|
1391
|
+
issues.push({
|
|
1392
|
+
level: "error",
|
|
1393
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].unclassifiedArtifactPublic`,
|
|
1394
|
+
message: "KFD-3 artifact exposes public participant-facing surfaces not declared by the pre-build witness",
|
|
1395
|
+
details: { id: entry.id || "", surfaces: comparison.unclassifiedArtifactPublic },
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
if (!entry.trustProof || typeof entry.trustProof !== "object" || Array.isArray(entry.trustProof)) {
|
|
1399
|
+
issues.push({
|
|
1400
|
+
level: "error",
|
|
1401
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].trustProof`,
|
|
1402
|
+
message: "KFD-3 collaboration-interface evidence must include a trust proof",
|
|
1403
|
+
details: { id: entry.id || "" },
|
|
1404
|
+
});
|
|
1405
|
+
} else {
|
|
1406
|
+
if (entry.trustProof.result === "pass" && comparison.status !== "passed") {
|
|
1407
|
+
issues.push({
|
|
1408
|
+
level: "error",
|
|
1409
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].trustProof.result`,
|
|
1410
|
+
message: "KFD-3 trust proof cannot pass when closure comparison did not pass",
|
|
1411
|
+
details: { id: entry.id || "", result: entry.trustProof.result, comparisonStatus: comparison.status || "" },
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
if (!entry.trustProof.declaredCapabilityVerification) {
|
|
1415
|
+
issues.push({
|
|
1416
|
+
level: "error",
|
|
1417
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].trustProof.declaredCapabilityVerification`,
|
|
1418
|
+
message: "KFD-3 trust proof must record declared capability verification",
|
|
1419
|
+
details: { id: entry.id || "" },
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
if (!entry.trustProof.reverseAudit) {
|
|
1423
|
+
issues.push({
|
|
1424
|
+
level: "error",
|
|
1425
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].trustProof.reverseAudit`,
|
|
1426
|
+
message: "KFD-3 trust proof must record reverse audit results",
|
|
1427
|
+
details: { id: entry.id || "" },
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
if (!entry.witnessEvidence?.prebuild?.canonicalSha256 || !entry.witnessEvidence?.artifact?.canonicalSha256) {
|
|
1432
|
+
issues.push({
|
|
1433
|
+
level: "error",
|
|
1434
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].witnessEvidence`,
|
|
1435
|
+
message: "KFD-3 trust proof must record pre-build and artifact witness hashes",
|
|
1436
|
+
details: { id: entry.id || "" },
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
if (!entry.auditBoundary || typeof entry.auditBoundary !== "object" || Array.isArray(entry.auditBoundary)) {
|
|
1440
|
+
issues.push({
|
|
1441
|
+
level: "error",
|
|
1442
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].auditBoundary`,
|
|
1443
|
+
message: "KFD-3 trust proof must record the reverse audit boundary",
|
|
1444
|
+
details: { id: entry.id || "" },
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
if (!Array.isArray(entry.residualRisk)) {
|
|
1448
|
+
issues.push({
|
|
1449
|
+
level: "error",
|
|
1450
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].residualRisk`,
|
|
1451
|
+
message: "KFD-3 trust proof must record residual risk as an array, even when empty",
|
|
1452
|
+
details: { id: entry.id || "" },
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
if (!entry.responsibility?.registryFactsOwner || !entry.responsibility?.artifactVerificationOwner || !entry.responsibility?.releasePassportProofOwner) {
|
|
1456
|
+
issues.push({
|
|
1457
|
+
level: "error",
|
|
1458
|
+
code: `${metadata.key}.collaborationInterfaces[${index}].responsibility`,
|
|
1459
|
+
message: "KFD-3 trust proof must record registry, artifact verification, and passport proof owners",
|
|
1460
|
+
details: { id: entry.id || "" },
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (section.status !== "passed") {
|
|
1465
|
+
issues.push({
|
|
1466
|
+
level: "error",
|
|
1467
|
+
code: `${metadata.key}.status`,
|
|
1468
|
+
message: "KFD-3 collaboration-interface release gate status must be passed",
|
|
1469
|
+
details: { status: section.status || "" },
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
return issues;
|
|
1473
|
+
}
|