@kungfu-tech/buildchain 3.0.8 → 3.0.9-alpha.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 +8 -7
- package/bin/buildchain.mjs +34 -33
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
- package/contracts/release-cut-v1.schema.json +1 -0
- package/contracts/release-train-transition-v1.schema.json +1 -0
- package/dist/site/agent-index.json +0 -1
- package/dist/site/artifact-schemas.json +0 -2
- package/dist/site/badge-endpoint-registry.json +9 -9
- package/dist/site/badges/v1/kfd-8/aligned.json +1 -1
- package/dist/site/badges/v1/kfd-8/declared.json +1 -1
- package/dist/site/badges/v1/kfd-8/downgraded.json +1 -1
- package/dist/site/badges/v1/kfd-8/draft.json +1 -1
- package/dist/site/badges/v1/kfd-8/failed.json +1 -1
- package/dist/site/badges/v1/kfd-8/missing.json +1 -1
- package/dist/site/badges/v1/kfd-8/passed.json +1 -1
- package/dist/site/badges/v1/kfd-8/planned.json +1 -1
- package/dist/site/buildchain-contract.json +38 -34
- package/dist/site/buildchain-site.json +59 -39
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/cli-registry.json +8 -7
- package/dist/site/controller-registry.json +13 -5
- package/dist/site/kfd-claims.json +35 -8
- package/dist/site/kfd-upstream-aggregate.json +10 -10
- package/dist/site/manual-registry.json +9 -9
- package/dist/site/node-api-registry.json +1143 -320
- package/dist/site/page-registry.json +46 -26
- package/dist/site/public-surface-audit.json +19 -9
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +33 -5
- package/dist/site/release-provenance.json +2 -1
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +13 -13
- package/dist/site/workflow-registry.json +19 -9
- package/docs/aws-us-elastic-runner-burst-plane.md +15 -3
- package/docs/cli-reference.md +6 -6
- package/docs/cli.md +2 -3
- package/docs/dev-alpha-candidate-patrol.md +45 -20
- package/docs/dev-delivery-warrant.md +12 -2
- package/docs/kfd-support.md +7 -7
- package/docs/node-api-reference.md +295 -246
- package/docs/release-passport.md +25 -12
- package/docs/release-train.md +54 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +3 -2
- package/package.json +4 -3
- package/packages/core/artifact-verification-envelope.js +114 -0
- package/packages/core/buildchain-channel-identity.js +119 -0
- package/packages/core/buildchain-compatibility-proof.js +32 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-alpha-active-release-train.js +460 -0
- package/packages/core/dev-alpha-candidate-selection.js +181 -0
- package/packages/core/dev-delivery-warrant.js +15 -4
- package/packages/core/index.js +4 -5
- package/packages/core/kfd-adopter-manifest.js +390 -0
- package/packages/core/kfd-product-gates.js +0 -300
- package/packages/core/release-blocker-priority.js +192 -0
- package/packages/core/release-passport-contract.js +219 -6
- package/packages/core/release-passport.js +139 -242
- package/packages/core/release-train.js +220 -0
- package/scripts/aws-macos-jit-controller-core.mjs +76 -0
- package/scripts/aws-macos-jit-controller.mjs +29 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +503 -0
- package/scripts/buildchain-channel-router.mjs +18 -2
- package/scripts/buildchain-cli-help.mjs +4 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-inventory.mjs +4 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +196 -201
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +78 -31
- package/scripts/generate-site-bundle.mjs +1 -8
- package/scripts/promotion-channel-router.mjs +5 -3
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
- package/scripts/site-capability-metadata.mjs +3 -1
- package/dist/site/schemas/kfd-support-projection-v1.schema.json +0 -106
|
@@ -2,7 +2,9 @@ import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot
|
|
|
2
2
|
import { chainedDevDeliveryAttemptInput, createDevDeliveryCandidateIdentity, validateDevDeliveryCandidateChain } from "./dev-delivery-candidate-identity.js";
|
|
3
3
|
import { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA, createCancelQueuedDevDeliveryCandidate } from "./dev-delivery-warrant-cancellation.js";
|
|
4
4
|
import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettler } from "./dev-delivery-warrant-settlement.js";
|
|
5
|
+
import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
|
|
5
6
|
export { devDeliveryContentRoot } from "./dev-delivery-common.js";
|
|
7
|
+
export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
|
|
6
8
|
export { SOURCE_QUALIFICATION_PROOF_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
|
|
7
9
|
export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
|
|
8
10
|
export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
|
|
@@ -60,7 +62,7 @@ function normalizeCandidate(input, expected) {
|
|
|
60
62
|
if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
|
|
61
63
|
throw new Error(`unsupported candidate status ${status || "<empty>"}`);
|
|
62
64
|
}
|
|
63
|
-
|
|
65
|
+
const candidate = {
|
|
64
66
|
...identity,
|
|
65
67
|
sourceHead: exactSha(input.sourceHead, "sourceHead"),
|
|
66
68
|
sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
|
|
@@ -78,6 +80,8 @@ function normalizeCandidate(input, expected) {
|
|
|
78
80
|
status,
|
|
79
81
|
terminal: input.terminal || null,
|
|
80
82
|
};
|
|
83
|
+
if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
|
|
84
|
+
return candidate;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
function queueBody(queue) {
|
|
@@ -140,6 +144,7 @@ export function normalizeDevDeliveryQueue(input, expected = {}) {
|
|
|
140
144
|
if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
|
|
141
145
|
throw new Error("exactly one active candidate must match the active Warrant");
|
|
142
146
|
}
|
|
147
|
+
if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
|
|
143
148
|
} else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
|
|
144
149
|
throw new Error("active candidate exists without an active Warrant");
|
|
145
150
|
}
|
|
@@ -180,6 +185,7 @@ function submissionReceipt({ before, after, candidate, action, now }) {
|
|
|
180
185
|
dependencyRoot: candidate.dependencyRoot,
|
|
181
186
|
toolchainRoot: candidate.toolchainRoot,
|
|
182
187
|
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
188
|
+
...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
|
|
183
189
|
deliveryClass: candidate.deliveryClass,
|
|
184
190
|
priority: candidate.priority,
|
|
185
191
|
retainedEnqueuedAt: candidate.enqueuedAt,
|
|
@@ -219,7 +225,7 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
219
225
|
throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
|
|
220
226
|
}
|
|
221
227
|
const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
|
|
222
|
-
const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]);
|
|
228
|
+
const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
|
|
223
229
|
if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
|
|
224
230
|
action = "duplicate-noop";
|
|
225
231
|
selected = existing;
|
|
@@ -235,6 +241,8 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
235
241
|
const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
|
|
236
242
|
existing.sourceHead = attemptedCandidate.sourceHead;
|
|
237
243
|
for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
|
|
244
|
+
if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
|
|
245
|
+
else delete existing.releaseBlockerPriority;
|
|
238
246
|
existing.updatedAt = currentTime;
|
|
239
247
|
existing.attempts += 1;
|
|
240
248
|
existing.status = "queued";
|
|
@@ -270,6 +278,7 @@ function effectivePriority(candidate, policy, now) {
|
|
|
270
278
|
basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
|
|
271
279
|
agingBoost,
|
|
272
280
|
score: Math.min(policy.maxPriority, DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost),
|
|
281
|
+
releaseBlocker: Boolean(candidate.releaseBlockerPriority),
|
|
273
282
|
};
|
|
274
283
|
}
|
|
275
284
|
|
|
@@ -282,7 +291,7 @@ export function rankDevDeliveryCandidates(queueInput, { now = new Date().toISOSt
|
|
|
282
291
|
candidate,
|
|
283
292
|
priority: effectivePriority(candidate, queue.policy, currentTime),
|
|
284
293
|
}))
|
|
285
|
-
.sort((left, right) => right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
|
|
294
|
+
.sort((left, right) => compareReleaseBlockerPriority(left, right) || right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
|
|
286
295
|
}
|
|
287
296
|
|
|
288
297
|
function warrantToken(input) {
|
|
@@ -371,6 +380,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
371
380
|
dependencyRoot: candidate.dependencyRoot,
|
|
372
381
|
toolchainRoot: candidate.toolchainRoot,
|
|
373
382
|
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
383
|
+
...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
|
|
374
384
|
deliveryClass: candidate.deliveryClass,
|
|
375
385
|
generation: next.fencingCounter,
|
|
376
386
|
expectedOldStateRoot: before.stateRoot,
|
|
@@ -390,7 +400,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
390
400
|
const receipt = {
|
|
391
401
|
schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
|
|
392
402
|
selected: true,
|
|
393
|
-
reason: selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
|
|
403
|
+
reason: selected.priority.releaseBlocker ? "release-blocker-bounded-priority" : selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
|
|
394
404
|
repository: transaction.after.repository,
|
|
395
405
|
protectedBase: transaction.after.protectedBase,
|
|
396
406
|
candidateId: transaction.result.candidate.candidateId,
|
|
@@ -406,6 +416,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
406
416
|
dependencyRoot: transaction.result.candidate.dependencyRoot,
|
|
407
417
|
toolchainRoot: transaction.result.candidate.toolchainRoot,
|
|
408
418
|
sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
|
|
419
|
+
...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority } : {}),
|
|
409
420
|
deliveryClass: transaction.result.candidate.deliveryClass,
|
|
410
421
|
queueAgeSeconds: selected.priority.ageSeconds,
|
|
411
422
|
basePriority: selected.priority.basePriority,
|
package/packages/core/index.js
CHANGED
|
@@ -305,10 +305,14 @@ export {
|
|
|
305
305
|
export {
|
|
306
306
|
ARTIFACT_VERIFICATION_ENVELOPE_CHECK_CONTRACT,
|
|
307
307
|
ARTIFACT_VERIFICATION_ENVELOPE_CONTRACT,
|
|
308
|
+
KFD_ADOPTER_RELEASE_BINDING_CONTRACT,
|
|
308
309
|
KFX_ADMISSION_INPUTS_CONTRACT,
|
|
309
310
|
artifactVerificationEnvelopeDigest,
|
|
311
|
+
createKfdAdopterReleaseBinding,
|
|
312
|
+
installedKfdPackageArtifactRoot,
|
|
310
313
|
projectArtifactVerificationEnvelopeToKfx,
|
|
311
314
|
sealArtifactVerificationReport,
|
|
315
|
+
validateKfdAdopterReleaseBinding,
|
|
312
316
|
verifyArtifactVerificationEnvelope,
|
|
313
317
|
} from "./artifact-verification-envelope.js";
|
|
314
318
|
|
|
@@ -488,15 +492,10 @@ export {
|
|
|
488
492
|
KFD_PRODUCT_GATE_INPUT_CONTRACT,
|
|
489
493
|
KFD_PRODUCT_GATE_INPUT_SCHEMA,
|
|
490
494
|
KFD_PRODUCT_GATE_INPUT_SCHEMA_ID,
|
|
491
|
-
KFD_SUPPORT_PROJECTION_CONTRACT,
|
|
492
|
-
KFD_SUPPORT_PROJECTION_SCHEMA,
|
|
493
|
-
KFD_SUPPORT_PROJECTION_SCHEMA_ID,
|
|
494
|
-
createKfdSupportProjection,
|
|
495
495
|
evaluateKfdProductGate,
|
|
496
496
|
kfdProductGateDigest,
|
|
497
497
|
kfdProductGates,
|
|
498
498
|
validateKfdProductGateResult,
|
|
499
|
-
validateKfdSupportProjection,
|
|
500
499
|
verifyKfdRecord,
|
|
501
500
|
} from "./kfd-product-gates.js";
|
|
502
501
|
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
|
|
5
|
+
import kfdPackageJson from "@kungfu-tech/kfd/package.json" with { type: "json" };
|
|
6
|
+
import kfdStandards from "@kungfu-tech/kfd/standards.json" with { type: "json" };
|
|
7
|
+
import {
|
|
8
|
+
bundleAdopterManifest,
|
|
9
|
+
verifyAdopterManifestFromPackage,
|
|
10
|
+
} from "@kungfu-tech/kfd/adopter-conformance/toolchain";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
KFD_PRODUCT_GATE_CONTRACT,
|
|
14
|
+
kfdProductGateDigest,
|
|
15
|
+
validateKfdProductGateResult,
|
|
16
|
+
} from "./kfd-product-gates.js";
|
|
17
|
+
|
|
18
|
+
export const KFD_ADOPTER_MANIFEST_GATE_CONTRACT =
|
|
19
|
+
"kungfu-buildchain-kfd-adopter-manifest-gate";
|
|
20
|
+
export const KFD_LEGACY_SUPPORT_MATRIX_CONTRACT = "kungfu-kfd-support-matrix";
|
|
21
|
+
|
|
22
|
+
const BUILDCHAIN_ADOPTER_ID = "kungfu-systems/buildchain";
|
|
23
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
24
|
+
const SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
25
|
+
const REQUIRED_DECISIONS = Object.freeze(["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]);
|
|
26
|
+
const PRODUCT_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
|
|
27
|
+
const PRODUCT_GATE_SET = new Set(PRODUCT_GATE_STANDARDS);
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
|
|
30
|
+
function issue(code, path, message, detail = {}) {
|
|
31
|
+
return { level: "error", code, path, message, ...detail };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function text(value) {
|
|
35
|
+
return value === undefined || value === null ? "" : String(value).trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function standardsFileDigest() {
|
|
39
|
+
const bytes = fs.readFileSync(require.resolve("@kungfu-tech/kfd/standards.json"));
|
|
40
|
+
return `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function standardMetadata(standard) {
|
|
44
|
+
return kfdStandards?.standards?.[standard] || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function decisionWitnessRow(row) {
|
|
48
|
+
const roots = (entries) => (entries || []).map((entry) => entry?.root || "");
|
|
49
|
+
return {
|
|
50
|
+
id: row.id,
|
|
51
|
+
state: row.state,
|
|
52
|
+
usage: row.usage,
|
|
53
|
+
implementationRoots: roots(row.implementationEvidence),
|
|
54
|
+
verificationRoots: roots(row.verificationEvidence),
|
|
55
|
+
negativeRoots: roots(row.negativeEvidence),
|
|
56
|
+
reviewRoots: roots(row.reviews),
|
|
57
|
+
witnessRoots: (row.witnessBindings || []).map((entry) => entry?.witnessRoot || ""),
|
|
58
|
+
releaseBindingIds: [...(row.releaseBindingIds || [])],
|
|
59
|
+
claims: [...(row.claims || [])],
|
|
60
|
+
gaps: [...(row.gaps || [])],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function verifyPublishedManifest(manifest, packageOptions, issues) {
|
|
65
|
+
try {
|
|
66
|
+
const report = verifyAdopterManifestFromPackage(manifest, packageOptions);
|
|
67
|
+
if (!report.valid) {
|
|
68
|
+
issues.push(issue("adopter-manifest-invalid", "manifest", "published KFD adopter verifier rejected the manifest", {
|
|
69
|
+
manifestIssues: report.issues,
|
|
70
|
+
}));
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return bundleAdopterManifest(manifest, packageOptions);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
issues.push(issue("adopter-manifest-invalid", "manifest", error.message));
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function manifestSourceSha(manifest) {
|
|
81
|
+
const match = /^kungfu-systems\/buildchain@([0-9a-f]{40})$/.exec(text(manifest?.adopter?.artifact?.coordinate));
|
|
82
|
+
return match?.[1] || "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues) {
|
|
86
|
+
if (manifest?.adopter?.id !== BUILDCHAIN_ADOPTER_ID) {
|
|
87
|
+
issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${BUILDCHAIN_ADOPTER_ID}`));
|
|
88
|
+
}
|
|
89
|
+
const pinned = manifest?.kfdCut?.package;
|
|
90
|
+
if (pinned?.name !== kfdPackageJson.name
|
|
91
|
+
|| pinned?.version !== kfdPackageJson.version
|
|
92
|
+
|| pinned?.artifactRoot !== packageArtifactRoot) {
|
|
93
|
+
issues.push(issue("adopter-package-cut", "manifest.kfdCut.package", "manifest must bind the exact installed KFD package cut"));
|
|
94
|
+
}
|
|
95
|
+
const sourceSha = manifestSourceSha(manifest);
|
|
96
|
+
if (manifest?.adopter?.artifact?.kind !== "git-commit" || !sourceSha
|
|
97
|
+
|| !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
|
|
98
|
+
issues.push(issue("adopter-source", "manifest.adopter.artifact", "Buildchain adopter authority must bind one exact git commit and artifact root"));
|
|
99
|
+
} else if (expectedSourceSha && sourceSha !== expectedSourceSha) {
|
|
100
|
+
issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", "manifest source does not match the requested release source"));
|
|
101
|
+
}
|
|
102
|
+
return sourceSha;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function validateRequiredDecisions(decisions, issues) {
|
|
106
|
+
for (const id of REQUIRED_DECISIONS) {
|
|
107
|
+
const row = decisions.get(id);
|
|
108
|
+
if (!row || !["candidate", "adopted"].includes(row.state) || row.usage === "unused") {
|
|
109
|
+
issues.push(issue("adopter-required-decision", `manifest.decisions.${id}`, `${id} must remain an explicit used candidate or adopted declaration`));
|
|
110
|
+
} else if ((row.implementationEvidence || []).length === 0 || (row.verificationEvidence || []).length === 0) {
|
|
111
|
+
issues.push(issue("adopter-required-evidence", `manifest.decisions.${id}`, `${id} must bind implementation and verification evidence`));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const kfd6 = decisions.get("KFD-6");
|
|
115
|
+
if (kfd6?.state !== "unsupported" || kfd6?.usage !== "unused") {
|
|
116
|
+
issues.push(issue("adopter-kfd6-barrier", "manifest.decisions.KFD-6", "KFD-6 must remain explicitly unsupported and unused"));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function validateWarrantWitness(decisions, issues) {
|
|
121
|
+
const row = decisions.get("KFD-10");
|
|
122
|
+
const witnesses = (row?.witnessBindings || []).filter((entry) =>
|
|
123
|
+
entry?.decisionId === "KFD-10" && entry?.profileId === "kfd-warrant-evidence");
|
|
124
|
+
if (row?.state !== "draft-evidence" || row?.usage !== "evaluating" || witnesses.length !== 1) {
|
|
125
|
+
issues.push(issue("adopter-kfd10-witness", "manifest.decisions.KFD-10", "KFD-10 must retain exactly one draft Warrant-evidence witness binding"));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function collectProductGates(gateResults, decisions, verificationCut, issues) {
|
|
130
|
+
const gates = new Map();
|
|
131
|
+
for (const [index, gate] of gateResults.entries()) {
|
|
132
|
+
const validation = validateKfdProductGateResult(gate, verificationCut);
|
|
133
|
+
if (!validation.valid) {
|
|
134
|
+
issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "Buildchain product gate is invalid", {
|
|
135
|
+
gateIssues: validation.issues,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
if (!PRODUCT_GATE_SET.has(gate?.standard)) {
|
|
139
|
+
issues.push(issue("adopter-gate-standard", `gateResults[${index}].standard`, "only the exact KFD-4/5/7 product-gate set is allowed"));
|
|
140
|
+
}
|
|
141
|
+
if (gates.has(gate?.standard)) {
|
|
142
|
+
issues.push(issue("adopter-gate-duplicate", `gateResults[${index}].standard`, `${gate.standard} gate is duplicated`));
|
|
143
|
+
}
|
|
144
|
+
gates.set(gate?.standard, gate);
|
|
145
|
+
}
|
|
146
|
+
for (const standard of PRODUCT_GATE_STANDARDS) {
|
|
147
|
+
const gate = gates.get(standard);
|
|
148
|
+
const id = standard.toUpperCase();
|
|
149
|
+
if (!gate) {
|
|
150
|
+
issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its existing Buildchain product gate`));
|
|
151
|
+
} else if (!(decisions.get(id)?.verificationEvidence || []).some((entry) => entry?.root === gate.gateRoot)) {
|
|
152
|
+
issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact Buildchain gate root`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return gates;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function projectedProductGates(gates) {
|
|
159
|
+
return PRODUCT_GATE_STANDARDS.map((standard) => {
|
|
160
|
+
const gate = gates.get(standard);
|
|
161
|
+
return gate
|
|
162
|
+
? { standard, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
|
|
163
|
+
: { standard, sourceSha: "", gateRoot: "", status: "missing" };
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues }) {
|
|
168
|
+
const gate = {
|
|
169
|
+
schemaVersion: 1,
|
|
170
|
+
contract: KFD_ADOPTER_MANIFEST_GATE_CONTRACT,
|
|
171
|
+
checkedAt,
|
|
172
|
+
authority: {
|
|
173
|
+
path: authorityPath,
|
|
174
|
+
contract: text(manifest?.contract),
|
|
175
|
+
manifestRoot: bundle?.roots?.manifestRoot || "",
|
|
176
|
+
},
|
|
177
|
+
source: {
|
|
178
|
+
sha: sourceSha,
|
|
179
|
+
artifactRoot: text(manifest?.adopter?.artifact?.root),
|
|
180
|
+
},
|
|
181
|
+
verificationCut: { checkedAt, maxAgeSeconds },
|
|
182
|
+
standardPackage: {
|
|
183
|
+
name: kfdPackageJson.name,
|
|
184
|
+
version: kfdPackageJson.version,
|
|
185
|
+
artifactRoot: text(manifest?.kfdCut?.package?.artifactRoot || packageArtifactRoot),
|
|
186
|
+
registryRoot: text(manifest?.kfdCut?.registry?.root),
|
|
187
|
+
verifierSetRoot: text(manifest?.kfdCut?.verifierSetRoot),
|
|
188
|
+
},
|
|
189
|
+
decisionWitness: {
|
|
190
|
+
rootAlgorithm: "sha256-buildchain-stable-json-v1",
|
|
191
|
+
root: kfdProductGateDigest((manifest?.decisions || []).map(decisionWitnessRow)),
|
|
192
|
+
},
|
|
193
|
+
gateResults: projectedProductGates(gates),
|
|
194
|
+
manifestVerificationReportRoot: bundle?.roots?.verificationReportRoot || "",
|
|
195
|
+
manifestBundleRoot: bundle?.bundleRoot || "",
|
|
196
|
+
status: bundle && issues.length === 0 ? "passed" : "failed",
|
|
197
|
+
qualifying: false,
|
|
198
|
+
selfCertified: false,
|
|
199
|
+
nonClaims: [
|
|
200
|
+
"The standard adopter manifest is the sole declaration authority; legacy support matrices are projections only.",
|
|
201
|
+
"A passing manifest gate does not authorize release, runtime action, activation, or independent certification.",
|
|
202
|
+
],
|
|
203
|
+
issues,
|
|
204
|
+
};
|
|
205
|
+
gate.gateRoot = kfdProductGateDigest(gate);
|
|
206
|
+
return gate;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function createKfdAdopterManifestGate({
|
|
210
|
+
manifest,
|
|
211
|
+
packageArtifactRoot = "",
|
|
212
|
+
gateResults = [],
|
|
213
|
+
authorityPath = ".buildchain/kfd/adopter-manifest.json",
|
|
214
|
+
expectedSourceSha = "",
|
|
215
|
+
checkedAt = new Date().toISOString(),
|
|
216
|
+
maxAgeSeconds = 86400,
|
|
217
|
+
} = {}) {
|
|
218
|
+
const issues = [];
|
|
219
|
+
const bundle = verifyPublishedManifest(manifest, {
|
|
220
|
+
packageArtifactRoot,
|
|
221
|
+
verifiedAt: checkedAt,
|
|
222
|
+
maxAgeSeconds,
|
|
223
|
+
}, issues);
|
|
224
|
+
const decisions = new Map((manifest?.decisions || []).map((row) => [row.id, row]));
|
|
225
|
+
let sourceSha = manifestSourceSha(manifest);
|
|
226
|
+
if (bundle) {
|
|
227
|
+
sourceSha = validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues);
|
|
228
|
+
validateRequiredDecisions(decisions, issues);
|
|
229
|
+
validateWarrantWitness(decisions, issues);
|
|
230
|
+
}
|
|
231
|
+
const gates = collectProductGates(gateResults, decisions, { expectedSourceSha: sourceSha || expectedSourceSha, checkedAt }, issues);
|
|
232
|
+
return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function validateGateDocument(gate, issues) {
|
|
236
|
+
const copy = structuredClone(gate);
|
|
237
|
+
const root = copy.gateRoot;
|
|
238
|
+
delete copy.gateRoot;
|
|
239
|
+
if (root !== kfdProductGateDigest(copy)) issues.push(issue("adopter-gate-root", "gateRoot", "adopter manifest gate root does not match its content"));
|
|
240
|
+
if (gate.status !== "passed" || gate.qualifying !== false || gate.selfCertified !== false || (gate.issues || []).length !== 0) {
|
|
241
|
+
issues.push(issue("adopter-gate-status", "status", "release consumption requires a passing non-qualifying, non-self-certifying manifest gate"));
|
|
242
|
+
}
|
|
243
|
+
if (gate?.authority?.contract !== "kfd.adopter-conformance-manifest/v1"
|
|
244
|
+
|| !ROOT_PATTERN.test(text(gate?.authority?.manifestRoot))) {
|
|
245
|
+
issues.push(issue("adopter-gate-authority", "authority", "adopter gate must bind the standard manifest contract and root"));
|
|
246
|
+
}
|
|
247
|
+
if (gate?.standardPackage?.name !== kfdPackageJson.name || gate?.standardPackage?.version !== kfdPackageJson.version
|
|
248
|
+
|| ![gate?.standardPackage?.artifactRoot, gate?.standardPackage?.registryRoot, gate?.standardPackage?.verifierSetRoot].every((value) => ROOT_PATTERN.test(text(value)))) {
|
|
249
|
+
issues.push(issue("adopter-gate-package", "standardPackage", "adopter gate uses stale KFD package metadata"));
|
|
250
|
+
}
|
|
251
|
+
if (!SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
|
|
252
|
+
issues.push(issue("adopter-gate-source", "source", "adopter gate must bind one exact Buildchain source commit and artifact root"));
|
|
253
|
+
}
|
|
254
|
+
if (gate?.decisionWitness?.rootAlgorithm !== "sha256-buildchain-stable-json-v1"
|
|
255
|
+
|| !ROOT_PATTERN.test(text(gate?.decisionWitness?.root))
|
|
256
|
+
|| !ROOT_PATTERN.test(text(gate?.manifestVerificationReportRoot))
|
|
257
|
+
|| !ROOT_PATTERN.test(text(gate?.manifestBundleRoot))) {
|
|
258
|
+
issues.push(issue("adopter-gate-evidence-root", "", "decision, report, and bundle roots are required"));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function validateProjectedGates(gate, expectedSourceSha, issues) {
|
|
263
|
+
const seenStandards = new Set();
|
|
264
|
+
for (const [index, productGate] of (gate.gateResults || []).entries()) {
|
|
265
|
+
if (!PRODUCT_GATE_SET.has(productGate?.standard) || productGate?.status !== "passed" || !ROOT_PATTERN.test(text(productGate?.gateRoot))) {
|
|
266
|
+
issues.push(issue("adopter-gate-result", `gateResults[${index}]`, "KFD-4/5/7 gate projections must be passing and rooted"));
|
|
267
|
+
}
|
|
268
|
+
if (seenStandards.has(productGate?.standard)) issues.push(issue("adopter-gate-result-set", `gateResults[${index}]`, "product gate standards must be unique"));
|
|
269
|
+
seenStandards.add(productGate?.standard);
|
|
270
|
+
if (productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
|
|
271
|
+
issues.push(issue("adopter-gate-source", `gateResults[${index}].sourceSha`, "product gate source must match the release source"));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (seenStandards.size !== PRODUCT_GATE_STANDARDS.length || PRODUCT_GATE_STANDARDS.some((standard) => !seenStandards.has(standard))) {
|
|
275
|
+
issues.push(issue("adopter-gate-result-set", "gateResults", "adopter gate must contain exactly one KFD-4, KFD-5, and KFD-7 product gate"));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function validateKfdAdopterManifestGate(gate, {
|
|
280
|
+
expectedSourceSha = "",
|
|
281
|
+
checkedAt = gate?.checkedAt || new Date().toISOString(),
|
|
282
|
+
} = {}) {
|
|
283
|
+
const issues = [];
|
|
284
|
+
if (!gate || gate.schemaVersion !== 1 || gate.contract !== KFD_ADOPTER_MANIFEST_GATE_CONTRACT) {
|
|
285
|
+
return { valid: false, issues: [issue("adopter-gate-contract", "", `gate must use ${KFD_ADOPTER_MANIFEST_GATE_CONTRACT} v1`)] };
|
|
286
|
+
}
|
|
287
|
+
validateGateDocument(gate, issues);
|
|
288
|
+
validateProjectedGates(gate, expectedSourceSha, issues);
|
|
289
|
+
if (gate.checkedAt !== checkedAt || gate?.verificationCut?.checkedAt !== checkedAt || !Number.isFinite(Date.parse(checkedAt))
|
|
290
|
+
|| !Number.isSafeInteger(gate?.verificationCut?.maxAgeSeconds) || gate.verificationCut.maxAgeSeconds < 0) {
|
|
291
|
+
issues.push(issue("adopter-gate-time", "verificationCut", "adopter gate verification cut does not match the requested cut"));
|
|
292
|
+
}
|
|
293
|
+
return { valid: issues.length === 0, issues };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function legacyStatus(row) {
|
|
297
|
+
if (row.state === "adopted") return "source-supported-release-bound";
|
|
298
|
+
if (row.state === "candidate") return "candidate";
|
|
299
|
+
if (row.state === "draft-evidence") return "draft-adopter-evidence";
|
|
300
|
+
return row.state;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function legacyRow(row, gates) {
|
|
304
|
+
const key = row.id.toLowerCase();
|
|
305
|
+
const productGate = gates.get(key);
|
|
306
|
+
return {
|
|
307
|
+
id: row.id,
|
|
308
|
+
key,
|
|
309
|
+
title: standardMetadata(key).title,
|
|
310
|
+
supportStatus: legacyStatus(row),
|
|
311
|
+
normative: { status: row.registryStatus, revision: standardMetadata(key).revision },
|
|
312
|
+
implementation: { status: row.implementationEvidence.length > 0 ? "implemented" : "not-declared" },
|
|
313
|
+
verification: { status: row.verificationEvidence.length > 0 || row.witnessBindings.length > 0 ? "passed" : "not-declared" },
|
|
314
|
+
buildchain: {
|
|
315
|
+
protocol: productGate ? `${KFD_PRODUCT_GATE_CONTRACT}/v1` : `${KFD_ADOPTER_MANIFEST_GATE_CONTRACT}/v1`,
|
|
316
|
+
gateStatus: productGate?.status || "manifest-verified",
|
|
317
|
+
},
|
|
318
|
+
releaseQualification: { shippedSupport: false },
|
|
319
|
+
claimClass: "standard-adopter-manifest-projection",
|
|
320
|
+
knownLimitations: [...row.gaps],
|
|
321
|
+
owner: BUILDCHAIN_ADOPTER_ID,
|
|
322
|
+
nextGate: "Release Passport artifact binding and independent release decision",
|
|
323
|
+
declaration: { state: row.state, usage: row.usage, root: kfdProductGateDigest(decisionWitnessRow(row)) },
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate } = {}) {
|
|
328
|
+
const validation = validateKfdAdopterManifestGate(manifestGate, {
|
|
329
|
+
expectedSourceSha: manifestGate?.source?.sha,
|
|
330
|
+
checkedAt: manifestGate?.checkedAt,
|
|
331
|
+
});
|
|
332
|
+
if (!validation.valid || manifest?.contract !== "kfd.adopter-conformance-manifest/v1") {
|
|
333
|
+
throw new Error("legacy support projection requires the exact passing standard adopter manifest authority");
|
|
334
|
+
}
|
|
335
|
+
const authorityIssues = [];
|
|
336
|
+
const bundle = verifyPublishedManifest(manifest, {
|
|
337
|
+
packageArtifactRoot: manifestGate.standardPackage.artifactRoot,
|
|
338
|
+
verifiedAt: manifestGate.verificationCut.checkedAt,
|
|
339
|
+
maxAgeSeconds: manifestGate.verificationCut.maxAgeSeconds,
|
|
340
|
+
}, authorityIssues);
|
|
341
|
+
const sourceSha = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, manifestGate.source.sha, authorityIssues);
|
|
342
|
+
const decisionRoot = kfdProductGateDigest(manifest.decisions.map(decisionWitnessRow));
|
|
343
|
+
if (!bundle || sourceSha !== manifestGate.source.sha || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
|
|
344
|
+
|| bundle.roots.manifestRoot !== manifestGate.authority.manifestRoot
|
|
345
|
+
|| bundle.roots.verificationReportRoot !== manifestGate.manifestVerificationReportRoot
|
|
346
|
+
|| bundle.bundleRoot !== manifestGate.manifestBundleRoot
|
|
347
|
+
|| decisionRoot !== manifestGate.decisionWitness.root
|
|
348
|
+
|| manifest.kfdCut.registry.root !== manifestGate.standardPackage.registryRoot
|
|
349
|
+
|| manifest.kfdCut.verifierSetRoot !== manifestGate.standardPackage.verifierSetRoot) {
|
|
350
|
+
authorityIssues.push(issue("legacy-projection-authority", "manifest", "manifest does not match the exact gate authority closure"));
|
|
351
|
+
}
|
|
352
|
+
const gates = new Map(manifestGate.gateResults.map((entry) => [entry.standard, entry]));
|
|
353
|
+
for (const [standard, gate] of gates) {
|
|
354
|
+
const row = manifest.decisions.find((entry) => entry.id === standard.toUpperCase());
|
|
355
|
+
if (!(row?.verificationEvidence || []).some((entry) => entry?.root === gate.gateRoot)) {
|
|
356
|
+
authorityIssues.push(issue("legacy-projection-gate-binding", `manifest.decisions.${standard.toUpperCase()}`, "manifest does not bind the projected product gate root"));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (authorityIssues.length > 0) throw new Error(`legacy support projection authority failed: ${JSON.stringify(authorityIssues)}`);
|
|
360
|
+
return {
|
|
361
|
+
schemaVersion: 1,
|
|
362
|
+
contract: KFD_LEGACY_SUPPORT_MATRIX_CONTRACT,
|
|
363
|
+
authority: {
|
|
364
|
+
path: manifestGate.authority.path,
|
|
365
|
+
contract: manifest.contract,
|
|
366
|
+
root: manifestGate.authority.manifestRoot,
|
|
367
|
+
gateRoot: manifestGate.gateRoot,
|
|
368
|
+
},
|
|
369
|
+
upstream: {
|
|
370
|
+
package: manifestGate.standardPackage.name,
|
|
371
|
+
version: manifestGate.standardPackage.version,
|
|
372
|
+
artifactRoot: manifestGate.standardPackage.artifactRoot,
|
|
373
|
+
registryRoot: manifestGate.standardPackage.registryRoot,
|
|
374
|
+
verifierSetRoot: manifestGate.standardPackage.verifierSetRoot,
|
|
375
|
+
standardsSha256: standardsFileDigest(),
|
|
376
|
+
},
|
|
377
|
+
rows: manifest.decisions.map((row) => legacyRow(row, gates)),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function validateKfdLegacySupportMatrixProjection(matrix, { manifest, manifestGate } = {}) {
|
|
382
|
+
try {
|
|
383
|
+
const expected = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
|
|
384
|
+
return kfdProductGateDigest(matrix) === kfdProductGateDigest(expected)
|
|
385
|
+
? { valid: true, issues: [] }
|
|
386
|
+
: { valid: false, issues: [issue("legacy-projection-drift", "", "legacy support matrix differs from the sole standard adopter manifest projection")] };
|
|
387
|
+
} catch (error) {
|
|
388
|
+
return { valid: false, issues: [issue("legacy-projection-authority", "", error.message)] };
|
|
389
|
+
}
|
|
390
|
+
}
|