@kungfu-tech/kfd 1.0.0-alpha.19 → 1.0.0-alpha.20

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.
Files changed (37) hide show
  1. package/.buildchain/kfd-1/contract-world.witness.json +579 -91
  2. package/.buildchain/kfd-2/kfd-foundation.trust-assessment.json +238 -0
  3. package/.buildchain/kfd-2/kfd-foundation.trust-claims.json +225 -0
  4. package/.buildchain/kfd-2/public-release-trust.claim.json +30 -18
  5. package/.buildchain/kfd-3/collaboration-interface.artifact.json +242 -57
  6. package/.buildchain/kfd-3/collaboration-interface.json +99 -1
  7. package/.buildchain/kfd-3/collaboration-interface.prebuild.json +261 -32
  8. package/README.md +18 -13
  9. package/buildchain.contract-lock.json +5 -5
  10. package/decisions/{kfd-1.md → KFD-1.md} +40 -12
  11. package/decisions/{kfd-2.md → KFD-2.md} +37 -4
  12. package/decisions/{kfd-3.md → KFD-3.md} +17 -1
  13. package/docs/KFD-1-usage.md +37 -0
  14. package/docs/KFD-2-usage.md +123 -0
  15. package/docs/{kfd-3-collaboration-interface.md → KFD-3-usage.md} +21 -0
  16. package/docs/KFD-4-usage.md +31 -0
  17. package/docs/MAP.md +13 -7
  18. package/kfd.release.json +1 -1
  19. package/package.json +3 -1
  20. package/registry.json +4 -4
  21. package/release-impact.json +41 -5
  22. package/schemas/kfd-1/contract-world.schema.json +67 -4
  23. package/schemas/kfd-1/witness.schema.json +113 -0
  24. package/schemas/kfd-2/trust-assessment.schema.json +313 -0
  25. package/schemas/kfd-2/trust-claims.schema.json +334 -0
  26. package/schemas/kfd-3/collaboration-interface.schema.json +60 -0
  27. package/schemas/kfd-3/witness.schema.json +8 -0
  28. package/schemas/kfd-standards.schema.json +124 -0
  29. package/scripts/check.mjs +256 -12
  30. package/scripts/update-kfd-1-witness.mjs +17 -6
  31. package/scripts/update-kfd-2-claim.mjs +226 -5
  32. package/scripts/update-kfd-3-witness.mjs +35 -3
  33. package/scripts/update-site-bundle.mjs +32 -2
  34. package/site/kfd-site.json +67 -4
  35. package/standards.json +634 -11
  36. package/docs/kfd-2-release-trust.md +0 -78
  37. /package/decisions/{kfd-4.md → KFD-4.md} +0 -0
package/scripts/check.mjs CHANGED
@@ -34,6 +34,10 @@ const kfd1WitnessPath = ".buildchain/kfd-1/contract-world.witness.json";
34
34
  const kfd1Witness = existsSync(kfd1WitnessPath) ? JSON.parse(readFileSync(kfd1WitnessPath, "utf8")) : undefined;
35
35
  const kfd2ClaimPath = ".buildchain/kfd-2/public-release-trust.claim.json";
36
36
  const kfd2Claim = existsSync(kfd2ClaimPath) ? JSON.parse(readFileSync(kfd2ClaimPath, "utf8")) : undefined;
37
+ const kfd2TrustClaimsPath = ".buildchain/kfd-2/kfd-foundation.trust-claims.json";
38
+ const kfd2TrustClaims = existsSync(kfd2TrustClaimsPath) ? JSON.parse(readFileSync(kfd2TrustClaimsPath, "utf8")) : undefined;
39
+ const kfd2TrustAssessmentPath = ".buildchain/kfd-2/kfd-foundation.trust-assessment.json";
40
+ const kfd2TrustAssessment = existsSync(kfd2TrustAssessmentPath) ? JSON.parse(readFileSync(kfd2TrustAssessmentPath, "utf8")) : undefined;
37
41
  const kfd3InterfacePath = ".buildchain/kfd-3/collaboration-interface.json";
38
42
  const kfd3PrebuildWitnessPath = ".buildchain/kfd-3/collaboration-interface.prebuild.json";
39
43
  const kfd3ArtifactWitnessPath = ".buildchain/kfd-3/collaboration-interface.artifact.json";
@@ -98,6 +102,37 @@ if (
98
102
  if (siteBundle.homepage?.currentDecisions?.source !== "registry.json") fail("site bundle currentDecisions source must be registry.json");
99
103
  if (siteBundle.decisionPages?.source !== "registry.json") fail("site bundle decisionPages source must be registry.json");
100
104
  if (siteBundle.decisionPages?.bodySource !== "registry.entries[].path") fail("site bundle decision page body source must be registry.entries[].path");
105
+ if (siteBundle.routes?.decisionUsagePattern !== "/{number}/usage") fail("site bundle routes.decisionUsagePattern must be /{number}/usage");
106
+ if (siteBundle.decisionPages?.usagePages?.relationship !== "usage-child-of-decision") {
107
+ fail("site bundle decisionPages.usagePages.relationship must be usage-child-of-decision");
108
+ }
109
+ if (siteBundle.decisionPages?.usagePages?.bodySource !== "docs/KFD-{number}-usage.md") {
110
+ fail("site bundle decisionPages.usagePages.bodySource must be docs/KFD-{number}-usage.md");
111
+ }
112
+ if (siteBundle.decisionPages?.usagePages?.stableUrlPattern !== "/{number}/usage") {
113
+ fail("site bundle decisionPages.usagePages.stableUrlPattern must be /{number}/usage");
114
+ }
115
+ const usagePagesByDecision = new Map((siteBundle.decisionPages?.usagePages?.pages ?? []).map((entry) => [entry.decisionId, entry]));
116
+ for (const e of registry.entries) {
117
+ const usagePage = usagePagesByDecision.get(e.id);
118
+ const expectedPath = `docs/KFD-${e.number}-usage.md`;
119
+ const expectedUrl = `${e.url}/usage`;
120
+ if (!usagePage) fail(`site bundle decisionPages.usagePages missing ${e.id}`);
121
+ else {
122
+ if (usagePage.parentPath !== e.path) fail(`site bundle ${e.id} usage parentPath must be ${e.path}`);
123
+ if (usagePage.parentUrl !== e.url) fail(`site bundle ${e.id} usage parentUrl must be ${e.url}`);
124
+ if (usagePage.path !== expectedPath) fail(`site bundle ${e.id} usage path must be ${expectedPath}`);
125
+ if (usagePage.sourcePath !== expectedPath) fail(`site bundle ${e.id} usage sourcePath must be ${expectedPath}`);
126
+ if (usagePage.url !== expectedUrl) fail(`site bundle ${e.id} usage url must be ${expectedUrl}`);
127
+ if (usagePage.sourceExists !== true) fail(`site bundle ${e.id} usage sourceExists must be true`);
128
+ }
129
+ if (!existsSync(expectedPath)) fail(`missing usage document ${expectedPath} for ${e.id}`);
130
+ }
131
+ for (const decisionId of usagePagesByDecision.keys()) {
132
+ if (!registry.entries.some((entry) => entry.id === decisionId)) {
133
+ fail(`site bundle decisionPages.usagePages has unknown decision ${decisionId}`);
134
+ }
135
+ }
101
136
  if (siteBundle.decisionPages?.metadata?.licenseBoundary?.license !== "Apache-2.0") {
102
137
  fail("site bundle decision metadata licenseBoundary.license must be Apache-2.0");
103
138
  }
@@ -118,7 +153,7 @@ if (sitePublicFactSource?.loadBearingCoordinate !== "commit-addressed repository
118
153
  if (sitePublicFactSource?.stableRenderedIndex !== "https://kfd.libkungfu.dev") {
119
154
  fail("site bundle decision metadata publicFactSource.stableRenderedIndex must be https://kfd.libkungfu.dev");
120
155
  }
121
- for (const requiredPath of ["decisions/kfd-N.md", "registry.json", "standards.json"]) {
156
+ for (const requiredPath of ["decisions/KFD-N.md", "registry.json", "standards.json"]) {
122
157
  if (!sitePublicFactSource?.canonicalPaths?.includes(requiredPath)) {
123
158
  fail(`site bundle decision metadata publicFactSource.canonicalPaths must include ${requiredPath}`);
124
159
  }
@@ -128,7 +163,7 @@ for (const requiredFile of ["README.md", "TRADEMARKS.md", "decisions", "registry
128
163
  fail(`package.json files[] must include ${requiredFile}`);
129
164
  }
130
165
  }
131
- for (const requiredExport of ["./TRADEMARKS.md", "./registry.json", "./standards.json", "./kfd.release.json", "./site/kfd-site.json", "./buildchain.contract-lock.json", "./buildchain.release-propagation.json", "./release-impact.json", "./buildchain/kfd-1/contract-world.witness.json", "./buildchain/kfd-2/public-release-trust.claim.json", "./buildchain/kfd-3/collaboration-interface.json", "./buildchain/kfd-3/collaboration-interface.prebuild.json", "./buildchain/kfd-3/collaboration-interface.artifact.json", "./schemas/*.json", "./schemas/*/*.json"]) {
166
+ for (const requiredExport of ["./TRADEMARKS.md", "./registry.json", "./standards.json", "./kfd.release.json", "./site/kfd-site.json", "./buildchain.contract-lock.json", "./buildchain.release-propagation.json", "./release-impact.json", "./buildchain/kfd-1/contract-world.witness.json", "./buildchain/kfd-2/public-release-trust.claim.json", "./buildchain/kfd-2/kfd-foundation.trust-claims.json", "./buildchain/kfd-2/kfd-foundation.trust-assessment.json", "./buildchain/kfd-3/collaboration-interface.json", "./buildchain/kfd-3/collaboration-interface.prebuild.json", "./buildchain/kfd-3/collaboration-interface.artifact.json", "./schemas/*.json", "./schemas/*/*.json"]) {
132
167
  if (!packageJson.exports || !packageJson.exports[requiredExport]) {
133
168
  fail(`package.json exports must include ${requiredExport}`);
134
169
  }
@@ -144,8 +179,8 @@ for (const e of registry.entries) {
144
179
  seen.add(e.number);
145
180
  if (e.id !== `KFD-${e.number}`) fail(`id ${e.id} does not match number ${e.number}`);
146
181
  if (e.slug !== `kfd-${e.number}`) fail(`${e.id} slug must be kfd-${e.number}, not ${e.slug}`);
147
- if (e.path !== `decisions/kfd-${e.number}.md`) {
148
- fail(`${e.id} path must be decisions/kfd-${e.number}.md, not ${e.path}`);
182
+ if (e.path !== `decisions/KFD-${e.number}.md`) {
183
+ fail(`${e.id} path must be decisions/KFD-${e.number}.md, not ${e.path}`);
149
184
  }
150
185
  if (!kinds.has(e.kind)) fail(`bad kind ${e.kind} on ${e.id}`);
151
186
  if (!statuses.has(e.status)) fail(`bad status ${e.status} on ${e.id}`);
@@ -238,16 +273,60 @@ if (kfd1?.schemaIds?.contractWorld !== "https://kfd.libkungfu.dev/schemas/kfd-1/
238
273
  if (kfd1?.schemaIds?.witness !== "https://kfd.libkungfu.dev/schemas/kfd-1/witness.schema.json") {
239
274
  fail("KFD-1 standards metadata must expose the canonical witness schema URI");
240
275
  }
241
- for (const concept of ["factSource", "contractWorld", "weldedSurfaceRegister", "witness"]) {
276
+ for (const concept of ["factSource", "contractWorld", "weldedSurfaceRegister", "witness", "surfaceClass", "compatibilityImpact", "impactProjection"]) {
242
277
  if (!kfd1?.concepts?.[concept]) fail(`KFD-1 standards metadata missing concept ${concept}`);
243
278
  }
244
279
  for (const iface of ["contractWorld", "witness"]) {
245
280
  if (!kfd1?.interfaces?.[iface]) fail(`KFD-1 standards metadata missing interface ${iface}`);
246
281
  }
282
+ const expectedKfd1SurfaceClasses = ["integration-time", "cross-time"];
283
+ const expectedKfd1ImpactClasses = ["breaking", "additive", "none", "unclassifiable"];
284
+ const kfd1ContractWorldSchema = JSON.parse(readFileSync("schemas/kfd-1/contract-world.schema.json", "utf8"));
285
+ const kfd1WitnessSchema = JSON.parse(readFileSync("schemas/kfd-1/witness.schema.json", "utf8"));
286
+ for (const [schemaName, schemaDoc] of [["contractWorld", kfd1ContractWorldSchema], ["witness", kfd1WitnessSchema]]) {
287
+ requireSameEnum(schemaDoc.$defs?.surfaceClass?.enum, expectedKfd1SurfaceClasses, `KFD-1 ${schemaName} surfaceClass`);
288
+ requireSameEnum(schemaDoc.$defs?.compatibilityImpact?.enum, expectedKfd1ImpactClasses, `KFD-1 ${schemaName} compatibilityImpact`);
289
+ for (const impact of expectedKfd1ImpactClasses) {
290
+ if (!schemaDoc.$defs?.impactProjection?.required?.includes(impact)) {
291
+ fail(`KFD-1 ${schemaName} impactProjection must require ${impact}`);
292
+ }
293
+ }
294
+ }
295
+ const kfd1SurfaceRegister = kfd1?.surfaceRegister;
296
+ if (kfd1SurfaceRegister?.factSource !== "standards.json#/standards/kfd-1/surfaceRegister") {
297
+ fail("KFD-1 surfaceRegister must declare standards.json as its fact source");
298
+ }
299
+ requireSameEnum(kfd1SurfaceRegister?.surfaceClasses, expectedKfd1SurfaceClasses, "KFD-1 surfaceRegister surfaceClasses");
300
+ requireSameEnum(kfd1SurfaceRegister?.compatibilityImpactClasses, expectedKfd1ImpactClasses, "KFD-1 surfaceRegister compatibilityImpactClasses");
301
+ if (!Array.isArray(kfd1SurfaceRegister?.surfaces) || kfd1SurfaceRegister.surfaces.length === 0) {
302
+ fail("KFD-1 surfaceRegister.surfaces[] is required");
303
+ } else {
304
+ const surfaceIds = new Set();
305
+ for (const [index, surface] of kfd1SurfaceRegister.surfaces.entries()) {
306
+ if (!surface.id) fail(`KFD-1 surfaceRegister.surfaces[${index}].id is required`);
307
+ else if (surfaceIds.has(surface.id)) fail(`KFD-1 surfaceRegister duplicate surface ${surface.id}`);
308
+ else surfaceIds.add(surface.id);
309
+ if (!expectedKfd1SurfaceClasses.includes(surface.class)) fail(`KFD-1 surfaceRegister ${surface.id} has invalid class`);
310
+ if (!Array.isArray(surface.classes) || !surface.classes.includes(surface.class)) {
311
+ fail(`KFD-1 surfaceRegister ${surface.id} classes[] must include class`);
312
+ }
313
+ if (!surface.description) fail(`KFD-1 surfaceRegister ${surface.id} description is required`);
314
+ if (!surface.sourcePath || !existsSync(surface.sourcePath)) fail(`KFD-1 surfaceRegister ${surface.id} sourcePath is missing`);
315
+ for (const impact of expectedKfd1ImpactClasses) {
316
+ if (!surface.impactProjection?.[impact]) fail(`KFD-1 surfaceRegister ${surface.id} missing impactProjection.${impact}`);
317
+ }
318
+ }
319
+ }
247
320
  const kfd2 = standardsMetadata.standards?.["kfd-2"];
248
321
  if (kfd2?.schemaIds?.trustTaxonomy !== "https://kfd.libkungfu.dev/schemas/kfd-2/trust-taxonomy.schema.json") {
249
322
  fail("KFD-2 standards metadata must expose the canonical trustTaxonomy schema URI");
250
323
  }
324
+ if (kfd2?.schemaIds?.trustClaims !== "https://kfd.libkungfu.dev/schemas/kfd-2/trust-claims.schema.json") {
325
+ fail("KFD-2 standards metadata must expose the canonical trustClaims schema URI");
326
+ }
327
+ if (kfd2?.schemaIds?.trustAssessment !== "https://kfd.libkungfu.dev/schemas/kfd-2/trust-assessment.schema.json") {
328
+ fail("KFD-2 standards metadata must expose the canonical trustAssessment schema URI");
329
+ }
251
330
  if (kfd2?.schemaIds?.releaseClaims !== "https://kfd.libkungfu.dev/schemas/kfd-2/release-claims.schema.json") {
252
331
  fail("KFD-2 standards metadata must expose the canonical releaseClaims schema URI");
253
332
  }
@@ -260,15 +339,29 @@ if (kfd2?.schemaPaths?.releaseClaims !== "schemas/kfd-2/release-claims.schema.js
260
339
  if (kfd2?.schemaPaths?.trustTaxonomy !== "schemas/kfd-2/trust-taxonomy.schema.json") {
261
340
  fail("KFD-2 standards metadata must expose the trustTaxonomy schema path");
262
341
  }
342
+ if (kfd2?.schemaPaths?.trustClaims !== "schemas/kfd-2/trust-claims.schema.json") {
343
+ fail("KFD-2 standards metadata must expose the trustClaims schema path");
344
+ }
345
+ if (kfd2?.schemaPaths?.trustAssessment !== "schemas/kfd-2/trust-assessment.schema.json") {
346
+ fail("KFD-2 standards metadata must expose the trustAssessment schema path");
347
+ }
263
348
  if (kfd2?.schemaPaths?.releaseTrustPassport !== "schemas/kfd-2/release-trust-passport.schema.json") {
264
349
  fail("KFD-2 standards metadata must expose the releaseTrustPassport schema path");
265
350
  }
266
351
  const kfd2TrustTaxonomySchema = JSON.parse(readFileSync("schemas/kfd-2/trust-taxonomy.schema.json", "utf8"));
352
+ const kfd2TrustClaimsSchema = JSON.parse(readFileSync("schemas/kfd-2/trust-claims.schema.json", "utf8"));
353
+ const kfd2TrustAssessmentSchema = JSON.parse(readFileSync("schemas/kfd-2/trust-assessment.schema.json", "utf8"));
267
354
  const kfd2ClaimsSchema = JSON.parse(readFileSync("schemas/kfd-2/release-claims.schema.json", "utf8"));
268
355
  const kfd2TrustPassportSchema = JSON.parse(readFileSync("schemas/kfd-2/release-trust-passport.schema.json", "utf8"));
269
356
  if (kfd2TrustTaxonomySchema.properties?.contract?.const !== "kfd-2-trust-taxonomy") {
270
357
  fail("KFD-2 trustTaxonomy schema must describe the kfd-2-trust-taxonomy contract");
271
358
  }
359
+ if (kfd2TrustClaimsSchema.properties?.contract?.const !== "kfd-2-trust-claims") {
360
+ fail("KFD-2 trustClaims schema must describe the kfd-2-trust-claims contract");
361
+ }
362
+ if (kfd2TrustAssessmentSchema.properties?.contract?.const !== "kfd-2-trust-assessment") {
363
+ fail("KFD-2 trustAssessment schema must describe the kfd-2-trust-assessment contract");
364
+ }
272
365
  if (kfd2ClaimsSchema.properties?.contract?.const !== "kfd-2-release-claims") {
273
366
  fail("KFD-2 releaseClaims schema must describe the kfd-2-release-claims contract");
274
367
  }
@@ -315,16 +408,25 @@ const checkResidualRisk = (risk, label) => {
315
408
  if (kfd2ClaimsSchema.$defs?.claim?.properties?.residualRisk?.items?.$ref !== residualRiskRef) {
316
409
  fail("KFD-2 releaseClaims residualRisk must reference the KFD-2 trust taxonomy residualRisk definition");
317
410
  }
411
+ if (kfd2TrustClaimsSchema.$defs?.claim?.properties?.residualRisk?.items?.$ref !== residualRiskRef) {
412
+ fail("KFD-2 trustClaims residualRisk must reference the KFD-2 trust taxonomy residualRisk definition");
413
+ }
414
+ if (kfd2TrustAssessmentSchema.$defs?.claimAssessment?.properties?.residualRisk?.items?.$ref !== residualRiskRef) {
415
+ fail("KFD-2 trustAssessment residualRisk must reference the KFD-2 trust taxonomy residualRisk definition");
416
+ }
417
+ if (kfd2TrustAssessmentSchema.properties?.downgradeReasons?.items?.$ref !== downgradeReasonRef) {
418
+ fail("KFD-2 trustAssessment downgradeReasons must reference the KFD-2 trust taxonomy downgradeReason definition");
419
+ }
318
420
  if (kfd2TrustPassportSchema.$defs?.claimResult?.properties?.residualRisk?.items?.$ref !== residualRiskRef) {
319
421
  fail("KFD-2 releaseTrustPassport claim residualRisk must reference the KFD-2 trust taxonomy residualRisk definition");
320
422
  }
321
423
  if (kfd2TrustPassportSchema.properties?.downgradeReasons?.items?.$ref !== downgradeReasonRef) {
322
424
  fail("KFD-2 releaseTrustPassport downgradeReasons must reference the KFD-2 trust taxonomy downgradeReason definition");
323
425
  }
324
- for (const concept of ["facts", "releaseClaim", "releaseClaims", "evidenceBinding", "auditBoundary", "residualRisk", "riskType", "trustImpact", "machineProvability", "agentAction", "extensionRequest", "releaseTrustPassport", "responsibilityState", "trust"]) {
426
+ for (const concept of ["facts", "trustClaim", "trustClaims", "trustAssessment", "claimSubject", "claimSubjectKind", "projection", "releaseClaim", "releaseClaims", "evidenceBinding", "auditBoundary", "residualRisk", "riskType", "trustImpact", "machineProvability", "agentAction", "extensionRequest", "releaseTrustPassport", "responsibilityState", "trust"]) {
325
427
  if (!kfd2?.concepts?.[concept]) fail(`KFD-2 standards metadata missing concept ${concept}`);
326
428
  }
327
- for (const iface of ["trustTaxonomy", "releaseClaims", "releaseTrustPassport"]) {
429
+ for (const iface of ["trustTaxonomy", "trustClaims", "trustAssessment", "releaseClaims", "releaseTrustPassport"]) {
328
430
  if (!kfd2?.interfaces?.[iface]) fail(`KFD-2 standards metadata missing interface ${iface}`);
329
431
  }
330
432
  const kfd3 = standardsMetadata.standards?.["kfd-3"];
@@ -357,10 +459,25 @@ if (!kfd3CollaborationSchema.properties?.extensionRequests) {
357
459
  if (!kfd3CollaborationSchema.properties?.factSources) {
358
460
  fail("KFD-3 collaborationInterface schema must expose factSources");
359
461
  }
462
+ if (!kfd3CollaborationSchema.properties?.valueEvidence) {
463
+ fail("KFD-3 collaborationInterface schema must expose valueEvidence");
464
+ }
465
+ if (!kfd3CollaborationSchema.required?.includes("valueEvidence")) {
466
+ fail("KFD-3 collaborationInterface schema must require valueEvidence");
467
+ }
468
+ if (!kfd3CollaborationSchema.$defs?.valueEvidence?.properties?.trustAssessment) {
469
+ fail("KFD-3 collaborationInterface valueEvidence must support trustAssessment");
470
+ }
471
+ if (!kfd3WitnessSchema.properties?.evidence?.required?.includes("valueEvidence")) {
472
+ fail("KFD-3 witness evidence must require valueEvidence");
473
+ }
474
+ if (!kfd3WitnessSchema.properties?.evidence?.properties?.valueEvidence) {
475
+ fail("KFD-3 witness evidence must expose valueEvidence");
476
+ }
360
477
  if (!kfd3CollaborationSchema.$defs?.extensionRequest?.properties?.requestPath?.properties?.kind?.enum?.includes("github-issue")) {
361
478
  fail("KFD-3 extensionRequest.requestPath.kind must support github-issue");
362
479
  }
363
- for (const concept of ["participant", "collaborationInterface", "minimalEntrypoint", "closure", "choicePath", "extensionRequest", "extensionPath"]) {
480
+ for (const concept of ["trustedValueClaim", "valueEvidence", "trustAssessmentLink", "participant", "collaborationInterface", "minimalEntrypoint", "closure", "choicePath", "extensionRequest", "extensionPath"]) {
364
481
  if (!kfd3?.concepts?.[concept]) fail(`KFD-3 standards metadata missing concept ${concept}`);
365
482
  }
366
483
  for (const iface of ["collaborationInterface", "witness"]) {
@@ -430,14 +547,22 @@ if (!kfd1Witness) {
430
547
  if (!Array.isArray(kfd1Witness.surfaces) || kfd1Witness.surfaces.length === 0) {
431
548
  fail("KFD-1 release witness surfaces[] is required");
432
549
  } else {
550
+ requireSameEnum(kfd1Witness.compatibilityImpactClasses, expectedKfd1ImpactClasses, "KFD-1 witness compatibilityImpactClasses");
551
+ const registeredSurfaceById = new Map((kfd1SurfaceRegister?.surfaces ?? []).map((surface) => [surface.id, surface]));
433
552
  const witnessedSurfaceNames = new Set(kfd1Witness.surfaces.map((surface) => surface.name));
434
553
  for (const requiredSurface of [
435
554
  "readme",
436
555
  "kfd-2-trust-taxonomy-schema",
556
+ "kfd-2-trust-claims-schema",
557
+ "kfd-2-trust-assessment-schema",
437
558
  "kfd-site-bundle",
438
559
  "kfd-doc-map",
439
- "kfd-2-release-trust-doc",
440
- "kfd-3-collaboration-interface-doc",
560
+ "kfd-1-usage-doc",
561
+ "kfd-2-usage-doc",
562
+ "kfd-3-usage-doc",
563
+ "kfd-4-usage-doc",
564
+ "kfd-2-foundation-trust-claims",
565
+ "kfd-2-foundation-trust-assessment",
441
566
  "release-impact-ledger",
442
567
  "kfd-check-gate"
443
568
  ]) {
@@ -447,6 +572,19 @@ if (!kfd1Witness) {
447
572
  }
448
573
  for (const [index, surface] of kfd1Witness.surfaces.entries()) {
449
574
  if (!surface.name) fail(`KFD-1 release witness surfaces[${index}].name is required`);
575
+ const registered = registeredSurfaceById.get(surface.name);
576
+ if (!registered) fail(`KFD-1 release witness surface ${surface.name || index} is not in standards surfaceRegister`);
577
+ else {
578
+ if (surface.class !== registered.class) fail(`KFD-1 release witness surface ${surface.name} class must match surfaceRegister`);
579
+ requireSameEnum(surface.classes, registered.classes, `KFD-1 witness ${surface.name} classes`);
580
+ if (surface.description !== registered.description) fail(`KFD-1 release witness surface ${surface.name} description must match surfaceRegister`);
581
+ if (surface.weldRationale !== registered.weldRationale) fail(`KFD-1 release witness surface ${surface.name} weldRationale must match surfaceRegister`);
582
+ for (const impact of expectedKfd1ImpactClasses) {
583
+ if (surface.impactProjection?.[impact] !== registered.impactProjection?.[impact]) {
584
+ fail(`KFD-1 release witness surface ${surface.name} impactProjection.${impact} must match surfaceRegister`);
585
+ }
586
+ }
587
+ }
450
588
  if (!surface.artifactPath) fail(`KFD-1 release witness surfaces[${index}].artifactPath is required`);
451
589
  else if (!existsSync(surface.artifactPath)) fail(`KFD-1 release witness surface ${surface.name || index} points to missing ${surface.artifactPath}`);
452
590
  else {
@@ -517,6 +655,83 @@ if (!kfd2Claim) {
517
655
  if (!Array.isArray(kfd2Claim.residualRisk)) fail("KFD-2 release trust claim residualRisk must be an array");
518
656
  }
519
657
 
658
+ const checkKfd2AssessmentPointer = (entry, label) => {
659
+ if (!entry?.path) fail(`${label}.path is required`);
660
+ else {
661
+ const filePath = hashablePath(entry.path);
662
+ if (!existsSync(filePath)) fail(`${label} points to missing ${filePath}`);
663
+ else if (entry.sha256 && entry.sha256 !== sha256File(filePath)) fail(`${label}.sha256 does not match ${filePath}`);
664
+ }
665
+ };
666
+
667
+ if (!kfd2TrustClaims) {
668
+ fail(`missing KFD-2 generic trust claims ${kfd2TrustClaimsPath}`);
669
+ } else {
670
+ if (kfd2TrustClaims.contract !== "kfd-2-trust-claims") fail("KFD-2 generic trust claims contract must be kfd-2-trust-claims");
671
+ if (kfd2TrustClaims.standard !== "kfd-2") fail("KFD-2 generic trust claims standard must be kfd-2");
672
+ if (kfd2TrustClaims.projection?.kind !== "generic") fail("KFD-2 generic trust claims projection.kind must be generic");
673
+ const claimsById = new Map((kfd2TrustClaims.claims ?? []).map((claim) => [claim.id, claim]));
674
+ for (const requiredClaim of ["kfd-1-contract-world-trust", "kfd-3-collaboration-interface-trust", "kfd-4-observer-perspective-trust"]) {
675
+ if (!claimsById.has(requiredClaim)) fail(`KFD-2 generic trust claims missing ${requiredClaim}`);
676
+ }
677
+ const expectedSubjectKinds = new Map([
678
+ ["kfd-1-contract-world-trust", "contract-world"],
679
+ ["kfd-3-collaboration-interface-trust", "collaboration-interface"],
680
+ ["kfd-4-observer-perspective-trust", "observer-perspective"],
681
+ ]);
682
+ for (const [claimId, expectedKind] of expectedSubjectKinds.entries()) {
683
+ const claim = claimsById.get(claimId);
684
+ if (claim?.subject?.kind !== expectedKind) fail(`KFD-2 generic trust claim ${claimId} subject.kind must be ${expectedKind}`);
685
+ if (!Array.isArray(claim?.facts) || claim.facts.length === 0) fail(`KFD-2 generic trust claim ${claimId} facts[] is required`);
686
+ else claim.facts.forEach((entry, index) => checkKfd2AssessmentPointer(entry, `KFD-2 generic trust claim ${claimId}.facts[${index}]`));
687
+ if (!Array.isArray(claim?.evidence) || claim.evidence.length === 0) fail(`KFD-2 generic trust claim ${claimId} evidence[] is required`);
688
+ for (const [index, entry] of (claim?.evidence ?? []).entries()) {
689
+ if (!kfd2TrustTaxonomySchema.$defs?.machineProvability?.enum?.includes(entry.machineProvability)) {
690
+ fail(`KFD-2 generic trust claim ${claimId}.evidence[${index}].machineProvability must be a KFD-2 value`);
691
+ }
692
+ if (entry.pointer) checkKfd2AssessmentPointer(entry.pointer, `KFD-2 generic trust claim ${claimId}.evidence[${index}].pointer`);
693
+ }
694
+ for (const [index, risk] of (claim?.residualRisk ?? []).entries()) {
695
+ checkResidualRisk(risk, `KFD-2 generic trust claim ${claimId}.residualRisk[${index}]`);
696
+ }
697
+ }
698
+ }
699
+
700
+ if (!kfd2TrustAssessment) {
701
+ fail(`missing KFD-2 generic trust assessment ${kfd2TrustAssessmentPath}`);
702
+ } else {
703
+ if (kfd2TrustAssessment.contract !== "kfd-2-trust-assessment") fail("KFD-2 generic trust assessment contract must be kfd-2-trust-assessment");
704
+ if (kfd2TrustAssessment.standard !== "kfd-2") fail("KFD-2 generic trust assessment standard must be kfd-2");
705
+ if (kfd2TrustAssessment.assessedClaims?.schemaId !== "https://kfd.libkungfu.dev/schemas/kfd-2/trust-claims.schema.json") {
706
+ fail("KFD-2 generic trust assessment assessedClaims.schemaId must be canonical");
707
+ }
708
+ if (kfd2TrustAssessment.assessedClaims?.path !== kfd2TrustClaimsPath) {
709
+ fail("KFD-2 generic trust assessment assessedClaims.path must point to generic trust claims");
710
+ }
711
+ if (kfd2TrustAssessment.assessedClaims?.digest !== `sha256:${sha256File(kfd2TrustClaimsPath)}`) {
712
+ fail("KFD-2 generic trust assessment assessedClaims.digest must match generic trust claims");
713
+ }
714
+ if (kfd2TrustAssessment.result !== "warning") fail("KFD-2 generic trust assessment result must be warning because KFD-3 declares semantic residual risk");
715
+ const assessmentsByClaim = new Map((kfd2TrustAssessment.assessments ?? []).map((entry) => [entry.claimId, entry]));
716
+ for (const requiredClaim of ["kfd-1-contract-world-trust", "kfd-3-collaboration-interface-trust", "kfd-4-observer-perspective-trust"]) {
717
+ if (!assessmentsByClaim.has(requiredClaim)) fail(`KFD-2 generic trust assessment missing claim ${requiredClaim}`);
718
+ }
719
+ if (assessmentsByClaim.get("kfd-3-collaboration-interface-trust")?.result !== "warning") {
720
+ fail("KFD-2 generic trust assessment must downgrade KFD-3 to warning");
721
+ }
722
+ for (const [index, reason] of (kfd2TrustAssessment.downgradeReasons ?? []).entries()) {
723
+ if (!kfd2TrustTaxonomySchema.$defs?.riskType?.enum?.includes(reason.riskType)) fail(`KFD-2 generic trust assessment downgradeReasons[${index}].riskType must be a KFD-2 value`);
724
+ if (!kfd2TrustTaxonomySchema.$defs?.trustImpact?.enum?.includes(reason.trustImpact)) fail(`KFD-2 generic trust assessment downgradeReasons[${index}].trustImpact must be a KFD-2 value`);
725
+ if (reason.agentAction && !kfd2TrustTaxonomySchema.$defs?.agentAction?.enum?.includes(reason.agentAction)) fail(`KFD-2 generic trust assessment downgradeReasons[${index}].agentAction must be a KFD-2 value`);
726
+ }
727
+ for (const [index, entry] of (kfd2TrustAssessment.assessments ?? []).entries()) {
728
+ if (!["pass", "warning", "fail", "unverifiable"].includes(entry.result)) fail(`KFD-2 generic trust assessment assessments[${index}].result is invalid`);
729
+ for (const [riskIndex, risk] of (entry.residualRisk ?? []).entries()) {
730
+ checkResidualRisk(risk, `KFD-2 generic trust assessment assessments[${index}].residualRisk[${riskIndex}]`);
731
+ }
732
+ }
733
+ }
734
+
520
735
  const kfd3SurfaceGroups = ["docs", "schemas", "standardsMetadata", "packageExports", "siteConsumptionContracts"];
521
736
  const kfd3SurfaceIds = (witness) => {
522
737
  const ids = new Set();
@@ -572,7 +787,7 @@ if (!kfd3Interface) {
572
787
  entry.url === "https://github.com/kungfu-systems/kfd" &&
573
788
  entry.loadBearingCoordinate === "commit-addressed repository contents" &&
574
789
  entry.stableRenderedIndex === "https://kfd.libkungfu.dev" &&
575
- entry.canonicalPaths?.includes("decisions/kfd-N.md") &&
790
+ entry.canonicalPaths?.includes("decisions/KFD-N.md") &&
576
791
  entry.canonicalPaths?.includes("registry.json") &&
577
792
  entry.canonicalPaths?.includes("standards.json")
578
793
  )) {
@@ -587,6 +802,32 @@ if (!kfd3Interface) {
587
802
  if (!kfd3Interface.surfaces.some((entry) => entry.id === "official-status-and-trademarks" && entry.discoverability?.path === "TRADEMARKS.md")) {
588
803
  fail("KFD-3 collaboration interface must expose TRADEMARKS.md as a participant-facing surface");
589
804
  }
805
+ if (!Array.isArray(kfd3Interface.valueEvidence) || kfd3Interface.valueEvidence.length === 0) {
806
+ fail("KFD-3 collaboration interface valueEvidence[] is required");
807
+ } else {
808
+ for (const [index, entry] of kfd3Interface.valueEvidence.entries()) {
809
+ if (!entry.id) fail(`KFD-3 collaboration interface valueEvidence[${index}].id is required`);
810
+ if (!entry.claim) fail(`KFD-3 collaboration interface valueEvidence[${index}].claim is required`);
811
+ if (!Array.isArray(entry.participants) || entry.participants.length === 0) fail(`KFD-3 collaboration interface valueEvidence[${index}].participants[] is required`);
812
+ if (!Array.isArray(entry.facts) || entry.facts.length === 0) fail(`KFD-3 collaboration interface valueEvidence[${index}].facts[] is required`);
813
+ if (!Array.isArray(entry.evidence) || entry.evidence.length === 0) fail(`KFD-3 collaboration interface valueEvidence[${index}].evidence[] is required`);
814
+ for (const [factIndex, fact] of (entry.facts ?? []).entries()) {
815
+ checkPointer(fact, `KFD-3 collaboration interface valueEvidence[${index}].facts[${factIndex}]`);
816
+ }
817
+ for (const [evidenceIndex, evidenceEntry] of (entry.evidence ?? []).entries()) {
818
+ checkPointer(evidenceEntry, `KFD-3 collaboration interface valueEvidence[${index}].evidence[${evidenceIndex}]`);
819
+ }
820
+ if (entry.trustAssessment) {
821
+ checkPointer(entry.trustAssessment, `KFD-3 collaboration interface valueEvidence[${index}].trustAssessment`);
822
+ }
823
+ for (const [riskIndex, risk] of (entry.residualRisk ?? []).entries()) {
824
+ checkResidualRisk(risk, `KFD-3 collaboration interface valueEvidence[${index}].residualRisk[${riskIndex}]`);
825
+ }
826
+ }
827
+ if (!kfd3Interface.valueEvidence.some((entry) => entry.trustAssessment?.path === kfd2TrustAssessmentPath)) {
828
+ fail("KFD-3 collaboration interface valueEvidence must link to the KFD-2 generic trust assessment");
829
+ }
830
+ }
590
831
  if (!Array.isArray(kfd3Interface.extensionRequests) || kfd3Interface.extensionRequests.length === 0) fail("KFD-3 collaboration interface extensionRequests[] is required");
591
832
  if (!kfd3Interface.extensionRequests.some((entry) => entry.id === "kfd-2-trust-taxonomy-extension" && entry.requestPath?.kind === "github-issue" && String(entry.requestPath?.target || "").startsWith("https://github.com/kungfu-systems/kfd/issues/new"))) {
592
833
  fail("KFD-3 collaboration interface must declare the KFD-2 trust taxonomy GitHub issue extension path");
@@ -630,6 +871,9 @@ if (!kfd3ArtifactWitness) {
630
871
  for (const [index, risk] of (kfd3ArtifactWitness.residualRisk ?? []).entries()) {
631
872
  checkResidualRisk(risk, `KFD-3 artifact witness residualRisk[${index}]`);
632
873
  }
874
+ if (!Array.isArray(kfd3ArtifactWitness.evidence?.valueEvidence) || kfd3ArtifactWitness.evidence.valueEvidence.length === 0) {
875
+ fail("KFD-3 artifact witness evidence.valueEvidence[] is required");
876
+ }
633
877
  if (kfd3ArtifactWitness.closure?.classificationMode !== "closed-world") fail("KFD-3 artifact witness closure must be closed-world");
634
878
  if (!Array.isArray(kfd3ArtifactWitness.closure?.unclassifiedEntrypoints) || kfd3ArtifactWitness.closure.unclassifiedEntrypoints.length !== 0) {
635
879
  fail("KFD-3 artifact witness must have zero unclassifiedEntrypoints");
@@ -655,7 +899,7 @@ if (kfd3PrebuildWitness && kfd3ArtifactWitness) {
655
899
  }
656
900
 
657
901
  const impactLevels = new Set(["patch", "minor", "major"]);
658
- const requiredSurfaces = new Set(["kfd-content", "kfd-registry-schema", "kfd-standards-metadata", "kfd-package-structure", "kfd-2-public-release-trust-claim"]);
902
+ const requiredSurfaces = new Set(["kfd-content", "kfd-registry-schema", "kfd-standards-metadata", "kfd-package-structure", "kfd-2-public-release-trust-claim", "kfd-3-trusted-value-evidence", "kfd-site-decision-usage-pages"]);
659
903
 
660
904
  if (releaseImpact.schemaVersion !== 1) fail(`unsupported release-impact schemaVersion ${releaseImpact.schemaVersion}`);
661
905
  if (releaseImpact.contract !== "kungfu-buildchain-impact") fail(`unexpected release-impact contract ${releaseImpact.contract}`);
@@ -7,18 +7,29 @@ const readJson = (filePath) => JSON.parse(readFileSync(filePath, "utf8"));
7
7
  const sha256File = (filePath) => crypto.createHash("sha256").update(readFileSync(filePath)).digest("hex");
8
8
 
9
9
  const witness = readJson(outputPath);
10
+ const standards = readJson("standards.json");
10
11
  const standardsSha = sha256File("standards.json");
11
12
  const registrySha = sha256File("registry.json");
13
+ const surfaceRegister = standards.standards?.["kfd-1"]?.surfaceRegister;
14
+ const registeredSurfaces = new Map((surfaceRegister?.surfaces ?? []).map((surface) => [surface.id, surface]));
12
15
 
13
16
  witness.contractWorld.digest = `sha256:${standardsSha}`;
14
17
  witness.canonicalPolicy.sha256 = standardsSha;
15
18
  witness.registry.sha256 = registrySha;
16
-
17
- for (const surface of witness.surfaces ?? []) {
18
- if (!surface.sourcePath || !surface.artifactPath) continue;
19
- surface.sourceSha256 = sha256File(surface.sourcePath);
20
- surface.expectedSha256 = sha256File(surface.artifactPath);
21
- }
19
+ witness.compatibilityImpactClasses = surfaceRegister?.compatibilityImpactClasses ?? [];
20
+ witness.surfaces = [...registeredSurfaces.values()].map((registered) => ({
21
+ name: registered.id,
22
+ sourcePath: registered.sourcePath,
23
+ sourceSha256: sha256File(registered.sourcePath),
24
+ artifactPath: registered.sourcePath,
25
+ expectedSha256: sha256File(registered.sourcePath),
26
+ byteForByte: true,
27
+ class: registered.class,
28
+ classes: registered.classes ?? [registered.class],
29
+ description: registered.description,
30
+ weldRationale: registered.weldRationale,
31
+ impactProjection: registered.impactProjection,
32
+ }));
22
33
 
23
34
  writeFileSync(outputPath, `${JSON.stringify(witness, null, 2)}\n`);
24
35
  console.log(`updated ${outputPath}`);