@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.
- package/.buildchain/kfd-1/contract-world.witness.json +579 -91
- package/.buildchain/kfd-2/kfd-foundation.trust-assessment.json +238 -0
- package/.buildchain/kfd-2/kfd-foundation.trust-claims.json +225 -0
- package/.buildchain/kfd-2/public-release-trust.claim.json +30 -18
- package/.buildchain/kfd-3/collaboration-interface.artifact.json +242 -57
- package/.buildchain/kfd-3/collaboration-interface.json +99 -1
- package/.buildchain/kfd-3/collaboration-interface.prebuild.json +261 -32
- package/README.md +18 -13
- package/buildchain.contract-lock.json +5 -5
- package/decisions/{kfd-1.md → KFD-1.md} +40 -12
- package/decisions/{kfd-2.md → KFD-2.md} +37 -4
- package/decisions/{kfd-3.md → KFD-3.md} +17 -1
- package/docs/KFD-1-usage.md +37 -0
- package/docs/KFD-2-usage.md +123 -0
- package/docs/{kfd-3-collaboration-interface.md → KFD-3-usage.md} +21 -0
- package/docs/KFD-4-usage.md +31 -0
- package/docs/MAP.md +13 -7
- package/kfd.release.json +1 -1
- package/package.json +3 -1
- package/registry.json +4 -4
- package/release-impact.json +41 -5
- package/schemas/kfd-1/contract-world.schema.json +67 -4
- package/schemas/kfd-1/witness.schema.json +113 -0
- package/schemas/kfd-2/trust-assessment.schema.json +313 -0
- package/schemas/kfd-2/trust-claims.schema.json +334 -0
- package/schemas/kfd-3/collaboration-interface.schema.json +60 -0
- package/schemas/kfd-3/witness.schema.json +8 -0
- package/schemas/kfd-standards.schema.json +124 -0
- package/scripts/check.mjs +256 -12
- package/scripts/update-kfd-1-witness.mjs +17 -6
- package/scripts/update-kfd-2-claim.mjs +226 -5
- package/scripts/update-kfd-3-witness.mjs +35 -3
- package/scripts/update-site-bundle.mjs +32 -2
- package/site/kfd-site.json +67 -4
- package/standards.json +634 -11
- package/docs/kfd-2-release-trust.md +0 -78
- /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/
|
|
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/
|
|
148
|
-
fail(`${e.id} path must be decisions/
|
|
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-
|
|
440
|
-
"kfd-
|
|
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/
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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}`);
|