@kungfu-tech/buildchain 3.0.8 → 3.0.9-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/actions/promote-buildchain-ref/README.md +8 -7
  2. package/bin/buildchain.mjs +34 -33
  3. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  4. package/dist/site/agent-index.json +0 -1
  5. package/dist/site/artifact-schemas.json +0 -2
  6. package/dist/site/badge-endpoint-registry.json +9 -9
  7. package/dist/site/badges/v1/kfd-8/aligned.json +1 -1
  8. package/dist/site/badges/v1/kfd-8/declared.json +1 -1
  9. package/dist/site/badges/v1/kfd-8/downgraded.json +1 -1
  10. package/dist/site/badges/v1/kfd-8/draft.json +1 -1
  11. package/dist/site/badges/v1/kfd-8/failed.json +1 -1
  12. package/dist/site/badges/v1/kfd-8/missing.json +1 -1
  13. package/dist/site/badges/v1/kfd-8/passed.json +1 -1
  14. package/dist/site/badges/v1/kfd-8/planned.json +1 -1
  15. package/dist/site/buildchain-contract.json +27 -22
  16. package/dist/site/buildchain-site.json +38 -28
  17. package/dist/site/capability-registry.json +1 -1
  18. package/dist/site/cli-registry.json +8 -7
  19. package/dist/site/controller-registry.json +6 -2
  20. package/dist/site/kfd-claims.json +34 -7
  21. package/dist/site/kfd-upstream-aggregate.json +10 -10
  22. package/dist/site/manual-registry.json +6 -6
  23. package/dist/site/node-api-registry.json +754 -228
  24. package/dist/site/page-registry.json +28 -18
  25. package/dist/site/public-surface-audit.json +19 -9
  26. package/dist/site/publication-registry.json +4 -4
  27. package/dist/site/release-passport-check-manifest.json +33 -5
  28. package/dist/site/release-provenance.json +2 -1
  29. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  30. package/dist/site/site-manifest.json +10 -10
  31. package/dist/site/workflow-registry.json +19 -9
  32. package/docs/aws-us-elastic-runner-burst-plane.md +15 -3
  33. package/docs/cli-reference.md +6 -6
  34. package/docs/cli.md +2 -3
  35. package/docs/dev-alpha-candidate-patrol.md +45 -20
  36. package/docs/kfd-support.md +7 -7
  37. package/docs/node-api-reference.md +179 -140
  38. package/docs/release-passport.md +18 -12
  39. package/docs/release-train.md +8 -0
  40. package/package.json +4 -3
  41. package/packages/core/artifact-verification-envelope.js +114 -0
  42. package/packages/core/dev-alpha-active-release-train.js +460 -0
  43. package/packages/core/dev-alpha-candidate-selection.js +181 -0
  44. package/packages/core/index.js +4 -5
  45. package/packages/core/kfd-adopter-manifest.js +390 -0
  46. package/packages/core/kfd-product-gates.js +0 -300
  47. package/packages/core/release-passport-contract.js +219 -6
  48. package/packages/core/release-passport.js +139 -242
  49. package/scripts/aws-macos-jit-controller-core.mjs +76 -0
  50. package/scripts/aws-macos-jit-controller.mjs +29 -0
  51. package/scripts/aws-macos-jit-source-rebind.mjs +503 -0
  52. package/scripts/buildchain-cli-help.mjs +4 -2
  53. package/scripts/check-inventory.mjs +2 -2
  54. package/scripts/dev-alpha-candidate-patrol.mjs +196 -201
  55. package/scripts/generate-site-bundle.mjs +1 -8
  56. package/scripts/site-capability-metadata.mjs +3 -1
  57. package/dist/site/schemas/kfd-support-projection-v1.schema.json +0 -106
@@ -0,0 +1,181 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import crypto from "node:crypto";
4
+
5
+ function text(value = "") {
6
+ return String(value ?? "").trim();
7
+ }
8
+
9
+ function canonical(value) {
10
+ if (Array.isArray(value)) return value.map(canonical);
11
+ if (value && typeof value === "object") {
12
+ return Object.fromEntries(
13
+ Object.entries(value)
14
+ .sort(([left], [right]) => left.localeCompare(right))
15
+ .map(([key, item]) => [key, canonical(item)]),
16
+ );
17
+ }
18
+ return value;
19
+ }
20
+
21
+ function evidenceRoot(value) {
22
+ return `sha256:${crypto
23
+ .createHash("sha256")
24
+ .update(JSON.stringify(canonical(value)))
25
+ .digest("hex")}`;
26
+ }
27
+
28
+ function latestWorkflowEvidence(runs, workflowPath, sourceSha) {
29
+ const matching = runs
30
+ .filter(
31
+ (run) =>
32
+ run.conclusion !== "cancelled" &&
33
+ run.path === workflowPath &&
34
+ run.head_sha === sourceSha,
35
+ )
36
+ .sort((left, right) => Number(right.id) - Number(left.id));
37
+ if (matching.length === 0) {
38
+ throw new Error(`missing completed same-SHA workflow run: ${workflowPath}`);
39
+ }
40
+ const run = matching[0];
41
+ return {
42
+ workflowPath,
43
+ workflowName: run.name,
44
+ runId: run.id,
45
+ runAttempt: run.run_attempt,
46
+ headSha: run.head_sha,
47
+ status: run.status,
48
+ conclusion: run.conclusion,
49
+ completedAt: run.updated_at,
50
+ url: run.html_url,
51
+ };
52
+ }
53
+
54
+ function workflowEvidenceIsFreshAndSuccessful(run, { now, maxAgeSeconds }) {
55
+ const completedAt = Date.parse(run.updated_at);
56
+ const ageSeconds = (Date.parse(now) - completedAt) / 1000;
57
+ return (
58
+ run.status === "completed" &&
59
+ run.conclusion === "success" &&
60
+ Number.isFinite(ageSeconds) &&
61
+ ageSeconds >= 0 &&
62
+ ageSeconds <= maxAgeSeconds
63
+ );
64
+ }
65
+
66
+ function latestRunsBySha(runs, workflowPath) {
67
+ const latest = new Map();
68
+ for (const run of runs.filter(
69
+ (row) => row.path === workflowPath && row.conclusion !== "cancelled",
70
+ )) {
71
+ const current = latest.get(run.head_sha);
72
+ if (!current || Number(run.id) > Number(current.id)) {
73
+ latest.set(run.head_sha, run);
74
+ }
75
+ }
76
+ return latest;
77
+ }
78
+
79
+ export function selectLatestQualifiedSource({
80
+ sourceHistory,
81
+ workflowRunsByPath,
82
+ requiredWorkflowPaths,
83
+ now,
84
+ maxAgeSeconds,
85
+ }) {
86
+ const latestByPath = new Map(
87
+ requiredWorkflowPaths.map((workflow) => [
88
+ workflow,
89
+ latestRunsBySha(workflowRunsByPath.get(workflow) || [], workflow),
90
+ ]),
91
+ );
92
+ for (let index = 0; index < sourceHistory.length; index += 1) {
93
+ const sourceSha = sourceHistory[index];
94
+ const rows = requiredWorkflowPaths.map((workflow) =>
95
+ latestByPath.get(workflow).get(sourceSha),
96
+ );
97
+ if (
98
+ rows.every((run) =>
99
+ workflowEvidenceIsFreshAndSuccessful(run || {}, {
100
+ now,
101
+ maxAgeSeconds,
102
+ }),
103
+ )
104
+ ) {
105
+ return {
106
+ sourceSha,
107
+ skippedNewerCommitCount: index,
108
+ workflowEvidence: requiredWorkflowPaths.map((workflow) =>
109
+ latestWorkflowEvidence(
110
+ workflowRunsByPath.get(workflow) || [],
111
+ workflow,
112
+ sourceSha,
113
+ ),
114
+ ),
115
+ };
116
+ }
117
+ }
118
+ const staleSuccessfulPair = sourceHistory.some((sourceSha) => {
119
+ const rows = requiredWorkflowPaths.map((workflow) =>
120
+ latestByPath.get(workflow).get(sourceSha),
121
+ );
122
+ return (
123
+ rows.every(
124
+ (run) =>
125
+ run?.status === "completed" &&
126
+ run?.conclusion === "success" &&
127
+ run?.head_sha === sourceSha,
128
+ ) &&
129
+ rows.some(
130
+ (run) =>
131
+ !workflowEvidenceIsFreshAndSuccessful(run, { now, maxAgeSeconds }),
132
+ )
133
+ );
134
+ });
135
+ if (staleSuccessfulPair) {
136
+ throw new Error(
137
+ "same-SHA workflow evidence is stale for every qualified source commit",
138
+ );
139
+ }
140
+ throw new Error(
141
+ "no source commit ahead of target has fresh completed successful same-SHA workflow evidence",
142
+ );
143
+ }
144
+
145
+ export function blockedCandidateDecision({
146
+ options,
147
+ sourceSha,
148
+ targetSha,
149
+ comparison,
150
+ reason,
151
+ }) {
152
+ const body = {
153
+ schema: "kungfu-buildchain-channel-candidate-decision/v1",
154
+ eligible: false,
155
+ reason: "qualification-evidence-blocked",
156
+ repository: options.repository,
157
+ source: { branch: options.sourceBranch, sha: sourceSha },
158
+ target: { branch: options.targetBranch, sha: targetSha },
159
+ comparison: {
160
+ status: text(comparison.status || "unknown"),
161
+ aheadBy: Number(comparison.ahead_by || 0),
162
+ },
163
+ blockReason: text(reason),
164
+ decidedAt: options.now,
165
+ };
166
+ return { ...body, decisionRoot: evidenceRoot(body) };
167
+ }
168
+
169
+ export function candidateFromDecision(decision) {
170
+ if (!decision.eligible) return null;
171
+ return {
172
+ sourceSha: decision.source.sha,
173
+ sourceLockRef: decision.sourceLockRef,
174
+ decisionRoot: decision.decisionRoot,
175
+ workflowEvidence: decision.workflowEvidence,
176
+ qualificationRoot: evidenceRoot({
177
+ sourceSha: decision.source.sha,
178
+ workflowEvidence: decision.workflowEvidence,
179
+ }),
180
+ };
181
+ }
@@ -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
+ }