@kungfu-tech/buildchain 4.0.1-alpha.9 → 4.0.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.
Files changed (98) hide show
  1. package/README.md +9 -6
  2. package/actions/v4-release-candidate-promote/README.md +3 -0
  3. package/architecture/agent-change-map.md +1015 -0
  4. package/architecture/ci-lane-change-budget.json +70 -1
  5. package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
  6. package/architecture/internal-capabilities.json +156 -11
  7. package/architecture/maintainability-debt.json +3229 -0
  8. package/architecture/maintainability-policy.json +480 -129
  9. package/architecture/v3-core-mechanism-inventory.json +4 -3
  10. package/architecture/v3-v4-capability-closure.md +59 -0
  11. package/architecture/v3-v4-live-capability-inventory.json +102404 -0
  12. package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
  13. package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
  14. package/architecture/v4-runtime-semantic-closure.json +710 -0
  15. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  16. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  17. package/contracts/v4-publication-qualification-v1.schema.json +51 -0
  18. package/dist/site/agent-index.json +1 -0
  19. package/dist/site/artifact-schemas.json +2 -0
  20. package/dist/site/buildchain-contract.json +123 -32
  21. package/dist/site/buildchain-site.json +96 -28
  22. package/dist/site/capability-registry.json +6 -6
  23. package/dist/site/cli-registry.json +5 -2
  24. package/dist/site/controller-registry.json +41 -5
  25. package/dist/site/kfd-claims.json +170 -21
  26. package/dist/site/kfd-upstream-aggregate.json +1 -1
  27. package/dist/site/manual-registry.json +6 -6
  28. package/dist/site/node-api-registry.json +1474 -215
  29. package/dist/site/page-registry.json +85 -17
  30. package/dist/site/public-surface-audit.json +85 -20
  31. package/dist/site/publication-authority-registry.json +28 -1
  32. package/dist/site/publication-registry.json +4 -4
  33. package/dist/site/release-provenance.json +5 -0
  34. package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
  35. package/dist/site/site-manifest.json +10 -10
  36. package/dist/site/workflow-registry.json +94 -18
  37. package/docs/MAP.md +1 -1
  38. package/docs/cli-reference.md +4 -4
  39. package/docs/dev-delivery-warrant.md +12 -9
  40. package/docs/node-api-reference.md +291 -206
  41. package/docs/publish-transaction.md +11 -4
  42. package/docs/release-candidate.md +7 -0
  43. package/docs/v4-adopter-delivery.md +23 -4
  44. package/docs/v4-declarative-release-tail-migration.md +43 -0
  45. package/package.json +8 -3
  46. package/packages/core/adopter-delivery-passport.js +36 -0
  47. package/packages/core/buildchain-contract.js +31 -1
  48. package/packages/core/buildchain-publication-authority.js +1 -0
  49. package/packages/core/dev-delivery-warrant-settlement.js +239 -3
  50. package/packages/core/dev-delivery-warrant-state.js +15 -2
  51. package/packages/core/dev-delivery-warrant.js +21 -4
  52. package/packages/core/github-governance-authority.js +4 -4
  53. package/packages/core/index.js +1 -0
  54. package/packages/core/paper-scaffold-content.js +401 -0
  55. package/packages/core/paper.js +12 -378
  56. package/packages/core/publication-control-plane-audit.js +135 -111
  57. package/packages/core/publication-rehearsal-projection.js +104 -0
  58. package/packages/core/publication-rehearsal-runtime.js +190 -0
  59. package/packages/core/release-candidate-recovery.js +6 -5
  60. package/packages/core/release-passport.js +82 -184
  61. package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
  62. package/packages/core/v4-floating-consumer-policy.js +3 -2
  63. package/packages/core/v4-floating-consumer-release-passport.js +134 -15
  64. package/packages/core/v4-publication-qualification.js +222 -0
  65. package/scripts/artifact-signing-controller-core.mjs +1 -1
  66. package/scripts/audit-publication-control-plane.mjs +17 -17
  67. package/scripts/buildchain-cli-help.mjs +3 -0
  68. package/scripts/check-internal-architecture.mjs +23 -3
  69. package/scripts/check-inventory.mjs +23 -5
  70. package/scripts/check-maintainability.mjs +145 -4
  71. package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
  72. package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
  73. package/scripts/check-v4-floating-consumer-policy-contract.mjs +10 -3
  74. package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
  75. package/scripts/dev-delivery-warrant-options.mjs +27 -11
  76. package/scripts/dev-delivery-warrant.mjs +45 -5
  77. package/scripts/dev-pr-auto-merge.mjs +25 -11
  78. package/scripts/dev-pr-delivery-warrant.mjs +44 -0
  79. package/scripts/dev-pr-prequeue-guard.mjs +0 -18
  80. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  81. package/scripts/generate-agent-change-map.mjs +118 -0
  82. package/scripts/generate-release-candidate-passport.mjs +26 -1
  83. package/scripts/generate-site-bundle.mjs +6 -0
  84. package/scripts/maintainability-domain-metrics.mjs +237 -0
  85. package/scripts/maintainability-governance.mjs +473 -0
  86. package/scripts/maintainability-metrics.mjs +28 -6
  87. package/scripts/project-cut-merge-queue-admission.mjs +394 -0
  88. package/scripts/promotion-routing-evidence.mjs +35 -6
  89. package/scripts/release-candidate-resolver.mjs +44 -0
  90. package/scripts/release-line-policy.mjs +25 -15
  91. package/scripts/release-tail.mjs +19 -2
  92. package/scripts/resume-from-candidate-run.mjs +106 -103
  93. package/scripts/run-lifecycle-core.mjs +341 -376
  94. package/scripts/site-capability-metadata.mjs +4 -0
  95. package/scripts/stable-candidate-qualification.mjs +9 -10
  96. package/scripts/v3-v4-capability-catalog.mjs +439 -0
  97. package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
  98. package/scripts/v4-declarative-promotion-admission.mjs +42 -0
@@ -35,6 +35,9 @@ import {
35
35
  } from "./github-artifact-attestation.js";
36
36
  import { verifyV4FloatingConsumerPolicyCertification } from "./v4-floating-consumer-evidence.js";
37
37
  import {
38
+ normalizePromotionEvidence,
39
+ normalizePromotionRouting,
40
+ normalizeV4RuntimeResumeEvidence,
38
41
  isV4PromotionRouting,
39
42
  releasePassportCertificationVerificationOptions,
40
43
  requireV4ConsumerPolicyCertification,
@@ -401,13 +404,6 @@ function parseJsonInputWithMeta(value, fallback = undefined, { cwd = process.cwd
401
404
  };
402
405
  }
403
406
 
404
- function parseV4ConsumerPolicyCertification(input, cwd) {
405
- return parseJsonInputWithMeta(input, undefined, {
406
- cwd,
407
- label: "v4ConsumerPolicyCertificationJson",
408
- });
409
- }
410
-
411
407
  function mergeAuthoritativePassportBase(passport, basePassport = undefined, { requireKfd = false } = {}) {
412
408
  if (!basePassport) {
413
409
  if (requireKfd) {
@@ -1072,123 +1068,6 @@ function normalizeTransaction(value = undefined) {
1072
1068
  };
1073
1069
  }
1074
1070
 
1075
- function normalizePromotionRouting(value = undefined) {
1076
- if (value === undefined) return undefined;
1077
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1078
- throw new Error("release.promotionRouting must be a JSON object");
1079
- }
1080
- if (value.contract !== "buildchain.promotion-routing/v1") {
1081
- throw new Error("release.promotionRouting contract must be buildchain.promotion-routing/v1");
1082
- }
1083
- for (const [label, field] of [
1084
- ["router.ref", value.router?.ref],
1085
- ["router.sha", value.router?.sha],
1086
- ["shell.ref", value.shell?.ref],
1087
- ["shell.sha", value.shell?.sha],
1088
- ["runtime.requestedRef", value.runtime?.requestedRef],
1089
- ["runtime.resolvedSha", value.runtime?.resolvedSha],
1090
- ["contractLock.path", value.contractLock?.path],
1091
- ["contractLock.digest", value.contractLock?.digest],
1092
- ["publication.channel", value.publication?.channel],
1093
- ["publication.targetRef", value.publication?.targetRef],
1094
- ]) {
1095
- if (!String(field || "").trim()) throw new Error(`release.promotionRouting.${label} is required`);
1096
- }
1097
- for (const [label, sha] of [
1098
- ["router.sha", value.router.sha],
1099
- ["shell.sha", value.shell.sha],
1100
- ["runtime.resolvedSha", value.runtime.resolvedSha],
1101
- ]) {
1102
- if (!/^[0-9a-f]{40}$/i.test(String(sha))) {
1103
- throw new Error(`release.promotionRouting.${label} must be a 40-character Git SHA`);
1104
- }
1105
- }
1106
- if (!/^sha256:[0-9a-f]{64}$/i.test(String(value.contractLock.digest))) {
1107
- throw new Error("release.promotionRouting.contractLock.digest must be a sha256 digest");
1108
- }
1109
- return JSON.parse(JSON.stringify(value));
1110
- }
1111
-
1112
- function normalizePromotionEvidence({
1113
- release = {},
1114
- v4ConsumerPolicyCertification: certification,
1115
- v4ConsumerPolicyCertificationRoot: certificationRoot,
1116
- v4RuntimeResumeEvidence,
1117
- repository,
1118
- sourceSha,
1119
- cwd,
1120
- }) {
1121
- const routing = normalizePromotionRouting(release.promotionRouting);
1122
- const identity = resolveV4ConsumerPolicyCertificationIdentity({
1123
- release,
1124
- routing,
1125
- runtimeResume: v4RuntimeResumeEvidence,
1126
- sourceSha,
1127
- });
1128
- return {
1129
- routing,
1130
- consumerPolicy: requireV4ConsumerPolicyCertification({
1131
- value: certification,
1132
- repository,
1133
- sourceSha: identity.sourceSha,
1134
- runtimeSha: identity.runtimeSha,
1135
- routing,
1136
- cwd,
1137
- certificationRoot,
1138
- }),
1139
- };
1140
- }
1141
-
1142
- function normalizeV4RuntimeResumeEvidence(value, expected = {}) {
1143
- if (value === undefined || value === null || value === "") return undefined;
1144
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1145
- throw new Error("v4RuntimeResumeEvidence must be a JSON object");
1146
- }
1147
- const authorizationVerification = verifyV4RuntimeAuthorizationReceipt({
1148
- receipt: value.authorization,
1149
- receiptRoot: value.authorizationRoot,
1150
- repository: expected.repository,
1151
- sourceSha: expected.sourceSha,
1152
- runtimeSha: expected.resumeRuntimeSha,
1153
- consumerPolicyReceiptRoot: expected.consumerPolicyReceiptRoot,
1154
- });
1155
- if (!authorizationVerification.ok) {
1156
- throw new Error(`v4 runtime authorization invalid: ${authorizationVerification.failures.join(", ")}`);
1157
- }
1158
- const lineageVerification = verifyV4RuntimeResumeLineage({
1159
- lineage: value.lineage,
1160
- lineageRoot: value.lineageRoot,
1161
- repository: expected.repository,
1162
- sourceSha: expected.sourceSha,
1163
- resumeRuntimeSha: expected.resumeRuntimeSha,
1164
- consumerPolicyReceiptRoot: expected.consumerPolicyReceiptRoot,
1165
- });
1166
- if (!lineageVerification.ok) {
1167
- throw new Error(`v4 runtime resume lineage invalid: ${lineageVerification.failures.join(", ")}`);
1168
- }
1169
- if (value.lineage?.authorizationRoot !== value.authorizationRoot) {
1170
- throw new Error("v4 runtime resume lineage authorization root mismatch");
1171
- }
1172
- return {
1173
- authorizationRoot: value.authorizationRoot,
1174
- authorization: structuredClone(value.authorization),
1175
- lineageRoot: value.lineageRoot,
1176
- lineage: structuredClone(value.lineage),
1177
- };
1178
- }
1179
-
1180
- function v4RuntimeResumeSourceSha(release, fallback = "") {
1181
- const builtSourceSha = releaseField(
1182
- release || {},
1183
- "builtSourceSha",
1184
- "built_source_sha",
1185
- );
1186
- return release?.treeEquivalent === true && builtSourceSha
1187
- ? builtSourceSha
1188
- : fallback;
1189
- }
1190
-
1191
-
1192
1071
  function prepareBuildEvidence({
1193
1072
  buildSummary,
1194
1073
  buildFacts,
@@ -1262,6 +1141,8 @@ function releaseField(release, camelKey, snakeKey, ...fallbacks) {
1262
1141
  return optionalString(firstTruthy(release[camelKey], release[snakeKey], ...fallbacks));
1263
1142
  }
1264
1143
 
1144
+ export { releasePassportKfdAdopterSourceSha } from "./adopter-delivery-passport.js";
1145
+
1265
1146
  function optionalSections(entries) {
1266
1147
  return Object.fromEntries(entries.filter(([, value]) => value && (!Array.isArray(value) || value.length > 0)));
1267
1148
  }
@@ -1889,7 +1770,6 @@ export function collectGitHubReleasePassport({
1889
1770
  function issue(level, code, message, details = {}) {
1890
1771
  return { level, code, message, details };
1891
1772
  }
1892
-
1893
1773
  function validateContract(value, expectedContract, label, issues) {
1894
1774
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1895
1775
  issues.push(issue("error", `${label}.object`, `${label} must be a JSON object`));
@@ -2297,65 +2177,6 @@ function validateReleaseEvidenceAttachments({
2297
2177
  return { evidenceArtifacts, releaseEvidence };
2298
2178
  }
2299
2179
 
2300
- function validateV4ConsumerPolicyPassportSection({ passport, issues }) {
2301
- const routing = passport?.promotionRouting;
2302
- const evidence = passport?.v4ConsumerPolicy;
2303
- if (isV4PromotionRouting(routing) && !evidence) {
2304
- issues.push(issue(
2305
- "error",
2306
- "v4ConsumerPolicy.missing",
2307
- "Buildchain v4 Release Passport requires an external floating consumer policy certification",
2308
- ));
2309
- return;
2310
- }
2311
- if (!evidence) return;
2312
- const verification = verifyV4FloatingConsumerPolicyCertification(
2313
- releasePassportCertificationVerificationOptions({
2314
- evidence,
2315
- passport,
2316
- routing,
2317
- }),
2318
- );
2319
- for (const failure of verification.failures) {
2320
- issues.push(issue("error", `v4ConsumerPolicy.${failure.code}`, failure.message));
2321
- }
2322
- }
2323
-
2324
- function validateV4RuntimeResumePassportSection({ passport, issues }) {
2325
- const evidence = passport?.v4RuntimeResume;
2326
- if (!evidence) return;
2327
- const consumerPolicyReceiptRoot = passport?.v4ConsumerPolicy?.certification?.receiptRoot || "";
2328
- const authorization = verifyV4RuntimeAuthorizationReceipt({
2329
- receipt: evidence.authorization,
2330
- receiptRoot: evidence.authorizationRoot,
2331
- repository: passport?.product?.repository || "",
2332
- sourceSha: v4RuntimeResumeSourceSha(passport?.release, passport?.release?.sourceSha || ""),
2333
- runtimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
2334
- consumerPolicyReceiptRoot,
2335
- });
2336
- for (const failure of authorization.failures) {
2337
- issues.push(issue("error", `v4RuntimeResume.authorization.${failure}`, failure));
2338
- }
2339
- const lineage = verifyV4RuntimeResumeLineage({
2340
- lineage: evidence.lineage,
2341
- lineageRoot: evidence.lineageRoot,
2342
- repository: passport?.product?.repository || "",
2343
- sourceSha: v4RuntimeResumeSourceSha(passport?.release, passport?.release?.sourceSha || ""),
2344
- resumeRuntimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
2345
- consumerPolicyReceiptRoot,
2346
- });
2347
- for (const failure of lineage.failures) {
2348
- issues.push(issue("error", `v4RuntimeResume.lineage.${failure}`, failure));
2349
- }
2350
- if (evidence.lineage?.authorizationRoot !== evidence.authorizationRoot) {
2351
- issues.push(issue(
2352
- "error",
2353
- "v4RuntimeResume.authorization-root-mismatch",
2354
- "runtime resume lineage must bind the embedded authorization receipt root",
2355
- ));
2356
- }
2357
- }
2358
-
2359
2180
  function validatePublishEvidenceSection({ passport, publishEvidence, normalizedPublishEvidence, issues }) {
2360
2181
  const publishEvidenceSupplied =
2361
2182
  Boolean(passport?.evidence?.publishEvidence) ||
@@ -2983,3 +2804,80 @@ export function makeReleasePassportFixtureAssets(dir) {
2983
2804
  export function validateKnownReleasePassportContracts() {
2984
2805
  return [...CONTRACTS];
2985
2806
  }
2807
+ function parseV4ConsumerPolicyCertification(input, cwd) {
2808
+ return parseJsonInputWithMeta(input, undefined, {
2809
+ cwd,
2810
+ label: "v4ConsumerPolicyCertificationJson",
2811
+ });
2812
+ }
2813
+
2814
+ function v4RuntimeResumeSourceSha(release, fallback = "") {
2815
+ const builtSourceSha = releaseField(
2816
+ release || {},
2817
+ "builtSourceSha",
2818
+ "built_source_sha",
2819
+ );
2820
+ return release?.treeEquivalent === true && builtSourceSha
2821
+ ? builtSourceSha
2822
+ : fallback;
2823
+ }
2824
+
2825
+
2826
+ function validateV4ConsumerPolicyPassportSection({ passport, issues }) {
2827
+ const routing = passport?.promotionRouting;
2828
+ const evidence = passport?.v4ConsumerPolicy;
2829
+ if (isV4PromotionRouting(routing) && !evidence) {
2830
+ issues.push(issue(
2831
+ "error",
2832
+ "v4ConsumerPolicy.missing",
2833
+ "Buildchain v4 Release Passport requires an external floating consumer policy certification",
2834
+ ));
2835
+ return;
2836
+ }
2837
+ if (!evidence) return;
2838
+ const verification = verifyV4FloatingConsumerPolicyCertification(
2839
+ releasePassportCertificationVerificationOptions({
2840
+ evidence,
2841
+ passport,
2842
+ routing,
2843
+ }),
2844
+ );
2845
+ for (const failure of verification.failures) {
2846
+ issues.push(issue("error", `v4ConsumerPolicy.${failure.code}`, failure.message));
2847
+ }
2848
+ }
2849
+
2850
+ function validateV4RuntimeResumePassportSection({ passport, issues }) {
2851
+ const evidence = passport?.v4RuntimeResume;
2852
+ if (!evidence) return;
2853
+ const consumerPolicyReceiptRoot = passport?.v4ConsumerPolicy?.certification?.receiptRoot || "";
2854
+ const authorization = verifyV4RuntimeAuthorizationReceipt({
2855
+ receipt: evidence.authorization,
2856
+ receiptRoot: evidence.authorizationRoot,
2857
+ repository: passport?.product?.repository || "",
2858
+ sourceSha: v4RuntimeResumeSourceSha(passport?.release, passport?.release?.sourceSha || ""),
2859
+ runtimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
2860
+ consumerPolicyReceiptRoot,
2861
+ });
2862
+ for (const failure of authorization.failures) {
2863
+ issues.push(issue("error", `v4RuntimeResume.authorization.${failure}`, failure));
2864
+ }
2865
+ const lineage = verifyV4RuntimeResumeLineage({
2866
+ lineage: evidence.lineage,
2867
+ lineageRoot: evidence.lineageRoot,
2868
+ repository: passport?.product?.repository || "",
2869
+ sourceSha: v4RuntimeResumeSourceSha(passport?.release, passport?.release?.sourceSha || ""),
2870
+ resumeRuntimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
2871
+ consumerPolicyReceiptRoot,
2872
+ });
2873
+ for (const failure of lineage.failures) {
2874
+ issues.push(issue("error", `v4RuntimeResume.lineage.${failure}`, failure));
2875
+ }
2876
+ if (evidence.lineage?.authorizationRoot !== evidence.authorizationRoot) {
2877
+ issues.push(issue(
2878
+ "error",
2879
+ "v4RuntimeResume.authorization-root-mismatch",
2880
+ "runtime resume lineage must bind the embedded authorization receipt root",
2881
+ ));
2882
+ }
2883
+ }
@@ -0,0 +1,343 @@
1
+ import { adopterDeliveryGateDigest } from "./adopter-delivery-json.js";
2
+
3
+ export const V4_CROSS_PLATFORM_ADOPTER_REPORT_CONTRACT =
4
+ "kungfu-buildchain-v4-cross-platform-adopter-report/v1";
5
+ export const V4_CROSS_PLATFORM_ADOPTER_QUALIFICATION_CONTRACT =
6
+ "kungfu-buildchain-v4-cross-platform-adopter-qualification/v1";
7
+ export const V4_CROSS_PLATFORM_ADOPTER_PLATFORMS = Object.freeze([
8
+ "linux-x64",
9
+ "macos-arm64",
10
+ "windows-x64",
11
+ ]);
12
+ export const V4_CROSS_PLATFORM_NEUTRAL_DRIVER = "ledger-specification-driver";
13
+
14
+ const SHA = /^[0-9a-f]{40}$/u;
15
+ const ROOT = /^sha256:[0-9a-f]{64}$/u;
16
+ const CONSUMER = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
17
+
18
+ function fail(message) {
19
+ throw new Error(`v4 cross-platform adopter qualification: ${message}`);
20
+ }
21
+
22
+ function exactKeys(value, keys, label) {
23
+ if (!value || typeof value !== "object" || Array.isArray(value))
24
+ fail(`${label} must be an object`);
25
+ const actual = Object.keys(value).sort();
26
+ const expected = [...keys].sort();
27
+ if (
28
+ actual.length !== expected.length ||
29
+ actual.some((key, index) => key !== expected[index])
30
+ )
31
+ fail(`${label} has an unsupported field set`);
32
+ return value;
33
+ }
34
+
35
+ function same(left, right) {
36
+ return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
37
+ }
38
+
39
+ export function summarizeV3V4CapabilityInventory(inventory) {
40
+ exactKeys(
41
+ inventory,
42
+ [
43
+ "capabilities",
44
+ "contract",
45
+ "extraction",
46
+ "reverseHistory",
47
+ "schemaVersion",
48
+ "sourceCuts",
49
+ "summary",
50
+ ],
51
+ "capability inventory",
52
+ );
53
+ if (
54
+ inventory.schemaVersion !== 1 ||
55
+ inventory.contract !==
56
+ "kungfu-buildchain-v3-v4-live-capability-inventory" ||
57
+ !Array.isArray(inventory.capabilities) ||
58
+ inventory.capabilities.length === 0
59
+ )
60
+ fail("capability inventory contract is unsupported");
61
+
62
+ const categoryCounts = {};
63
+ const dispositionCounts = {};
64
+ const ids = new Set();
65
+ for (const [index, capability] of inventory.capabilities.entries()) {
66
+ if (
67
+ !capability ||
68
+ typeof capability !== "object" ||
69
+ typeof capability.id !== "string" ||
70
+ typeof capability.category !== "string" ||
71
+ typeof capability.disposition !== "string" ||
72
+ !capability.sourceEvidence ||
73
+ !capability.v4Route ||
74
+ typeof capability.v4Route.capabilityId !== "string" ||
75
+ capability.v4Route.capabilityId.length === 0 ||
76
+ !capability.v4Route.evidence ||
77
+ capability.residual !== null
78
+ )
79
+ fail(
80
+ `capability ${index} does not bind an exact v3 source to a v4 route`,
81
+ );
82
+ if (ids.has(capability.id))
83
+ fail(`duplicate capability id ${capability.id}`);
84
+ ids.add(capability.id);
85
+ categoryCounts[capability.category] =
86
+ (categoryCounts[capability.category] || 0) + 1;
87
+ dispositionCounts[capability.disposition] =
88
+ (dispositionCounts[capability.disposition] || 0) + 1;
89
+ }
90
+ const summary = {
91
+ capabilityCount: inventory.capabilities.length,
92
+ categoryCounts: Object.fromEntries(
93
+ Object.entries(categoryCounts).sort(([left], [right]) =>
94
+ left.localeCompare(right, "en"),
95
+ ),
96
+ ),
97
+ dispositionCounts: Object.fromEntries(
98
+ Object.entries(dispositionCounts).sort(([left], [right]) =>
99
+ left.localeCompare(right, "en"),
100
+ ),
101
+ ),
102
+ residualCount: 0,
103
+ unknownCount: 0,
104
+ unownedCount: 0,
105
+ };
106
+ if (!same(summary, inventory.summary))
107
+ fail("capability inventory summary does not match its raw rows");
108
+ return {
109
+ inventoryRoot: adopterDeliveryGateDigest(inventory),
110
+ sourceCuts: structuredClone(inventory.sourceCuts),
111
+ summary,
112
+ categories: Object.entries(summary.categoryCounts).map(
113
+ ([category, count]) => ({
114
+ category,
115
+ count,
116
+ applicability: "exact-source-route",
117
+ status: "passed",
118
+ }),
119
+ ),
120
+ };
121
+ }
122
+
123
+ function validateSourceBinding(binding) {
124
+ exactKeys(
125
+ binding,
126
+ ["runtimeSha", "consumerSha", "inventoryRoot", "sourceCuts"],
127
+ "source binding",
128
+ );
129
+ if (
130
+ !SHA.test(binding.runtimeSha || "") ||
131
+ !SHA.test(binding.consumerSha || "") ||
132
+ !ROOT.test(binding.inventoryRoot || "")
133
+ )
134
+ fail("report requires exact runtime, consumer and inventory roots");
135
+ }
136
+
137
+ function completeCategory(entry, counts) {
138
+ return (
139
+ entry &&
140
+ typeof entry.category === "string" &&
141
+ Number.isSafeInteger(entry.count) &&
142
+ entry.count > 0 &&
143
+ entry.applicability === "exact-source-route" &&
144
+ entry.status === "passed" &&
145
+ counts[entry.category] === entry.count
146
+ );
147
+ }
148
+
149
+ function sumCounts(counts) {
150
+ return Object.values(counts).reduce((total, count) => total + count, 0);
151
+ }
152
+
153
+ function validateCapabilityMatrix(matrix) {
154
+ exactKeys(
155
+ matrix,
156
+ ["capabilityCount", "categoryCounts", "dispositionCounts", "categories"],
157
+ "capability matrix",
158
+ );
159
+ const complete =
160
+ Number.isSafeInteger(matrix.capabilityCount) &&
161
+ matrix.capabilityCount > 0 &&
162
+ Array.isArray(matrix.categories) &&
163
+ matrix.categories.length > 0 &&
164
+ matrix.categories.every((entry) =>
165
+ completeCategory(entry, matrix.categoryCounts),
166
+ ) &&
167
+ Object.keys(matrix.categoryCounts).length === matrix.categories.length &&
168
+ sumCounts(matrix.categoryCounts) === matrix.capabilityCount &&
169
+ sumCounts(matrix.dispositionCounts) === matrix.capabilityCount;
170
+ if (!complete)
171
+ fail("platform report does not carry the complete raw capability matrix");
172
+ }
173
+
174
+ function validateExecutionEvidence(execution) {
175
+ exactKeys(
176
+ execution,
177
+ [
178
+ "initialRun",
179
+ "tamperFailure",
180
+ "retryRun",
181
+ "terminalVerify",
182
+ "bootstrap",
183
+ "neutralDriver",
184
+ ],
185
+ "execution evidence",
186
+ );
187
+ const complete =
188
+ execution.initialRun?.status === "passed" &&
189
+ ROOT.test(execution.initialRun?.readbackRoot || "") &&
190
+ execution.tamperFailure?.status === "failed-as-required" &&
191
+ Number.isSafeInteger(execution.tamperFailure?.exitCode) &&
192
+ execution.tamperFailure.exitCode > 0 &&
193
+ execution.retryRun?.status === "passed" &&
194
+ execution.retryRun?.readbackRoot === execution.initialRun.readbackRoot &&
195
+ execution.terminalVerify?.status === "passed" &&
196
+ execution.terminalVerify?.readbackRoot ===
197
+ execution.retryRun.readbackRoot &&
198
+ execution.bootstrap?.status === "passed" &&
199
+ ROOT.test(execution.bootstrap?.resultRoot || "") &&
200
+ execution.neutralDriver?.id === V4_CROSS_PLATFORM_NEUTRAL_DRIVER &&
201
+ execution.neutralDriver?.status === "passed" &&
202
+ execution.neutralDriver?.kfdDependencyPresent === false;
203
+ if (!complete)
204
+ fail("failure, retry, terminal or neutral-driver evidence is incomplete");
205
+ }
206
+
207
+ function validateAuthorityCeiling(authority) {
208
+ exactKeys(
209
+ authority,
210
+ [
211
+ "productionWrites",
212
+ "providerEffects",
213
+ "releaseEffects",
214
+ "stablePublication",
215
+ ],
216
+ "authority ceiling",
217
+ );
218
+ if (Object.values(authority).some((value) => value !== false))
219
+ fail("qualification report must not grant production or release authority");
220
+ }
221
+
222
+ export function validateV4CrossPlatformAdopterReport(report) {
223
+ exactKeys(
224
+ report,
225
+ [
226
+ "schemaVersion",
227
+ "contract",
228
+ "platform",
229
+ "consumer",
230
+ "sourceBinding",
231
+ "capabilityMatrix",
232
+ "execution",
233
+ "authority",
234
+ "reportRoot",
235
+ ],
236
+ "platform report",
237
+ );
238
+ if (
239
+ report.schemaVersion !== 1 ||
240
+ report.contract !== V4_CROSS_PLATFORM_ADOPTER_REPORT_CONTRACT
241
+ )
242
+ fail("platform report contract is unsupported");
243
+ if (!V4_CROSS_PLATFORM_ADOPTER_PLATFORMS.includes(report.platform))
244
+ fail(`undeclared platform ${report.platform}`);
245
+ if (!CONSUMER.test(report.consumer || ""))
246
+ fail("consumer must be a stable lowercase identity");
247
+ validateSourceBinding(report.sourceBinding);
248
+ validateCapabilityMatrix(report.capabilityMatrix);
249
+ validateExecutionEvidence(report.execution);
250
+ validateAuthorityCeiling(report.authority);
251
+ if (!ROOT.test(report.reportRoot || "")) fail("report root is invalid");
252
+ const body = structuredClone(report);
253
+ delete body.reportRoot;
254
+ if (adopterDeliveryGateDigest(body) !== report.reportRoot)
255
+ fail("report root does not match exact report bytes");
256
+ return structuredClone(report);
257
+ }
258
+
259
+ export function createV4CrossPlatformAdopterReport(input) {
260
+ const body = structuredClone(input);
261
+ const report = {
262
+ schemaVersion: 1,
263
+ contract: V4_CROSS_PLATFORM_ADOPTER_REPORT_CONTRACT,
264
+ ...body,
265
+ };
266
+ report.reportRoot = adopterDeliveryGateDigest(report);
267
+ return validateV4CrossPlatformAdopterReport(report);
268
+ }
269
+
270
+ export function qualifyV4CrossPlatformAdopters({ reports, consumers }) {
271
+ if (!Array.isArray(reports) || reports.length === 0)
272
+ fail("at least one raw platform report is required");
273
+ if (
274
+ !Array.isArray(consumers) ||
275
+ consumers.length === 0 ||
276
+ consumers.some((consumer) => !CONSUMER.test(consumer)) ||
277
+ new Set(consumers).size !== consumers.length
278
+ )
279
+ fail("required consumers must be unique stable lowercase identities");
280
+ const normalized = reports.map(validateV4CrossPlatformAdopterReport);
281
+ const expectedKeys = consumers.flatMap((consumer) =>
282
+ V4_CROSS_PLATFORM_ADOPTER_PLATFORMS.map(
283
+ (platform) => `${consumer}:${platform}`,
284
+ ),
285
+ );
286
+ const reportByKey = new Map();
287
+ for (const report of normalized) {
288
+ const key = `${report.consumer}:${report.platform}`;
289
+ if (reportByKey.has(key)) fail(`duplicate report ${key}`);
290
+ reportByKey.set(key, report);
291
+ }
292
+ const missing = expectedKeys.filter((key) => !reportByKey.has(key));
293
+ const unexpected = [...reportByKey.keys()].filter(
294
+ (key) => !expectedKeys.includes(key),
295
+ );
296
+ if (missing.length || unexpected.length)
297
+ fail(
298
+ `platform matrix mismatch; missing=${missing.join(",") || "none"}; unexpected=${unexpected.join(",") || "none"}`,
299
+ );
300
+ const reference = normalized[0].sourceBinding;
301
+ for (const report of normalized.slice(1)) {
302
+ if (
303
+ report.sourceBinding.runtimeSha !== reference.runtimeSha ||
304
+ report.sourceBinding.inventoryRoot !== reference.inventoryRoot ||
305
+ !same(report.sourceBinding.sourceCuts, reference.sourceCuts) ||
306
+ !same(report.capabilityMatrix, normalized[0].capabilityMatrix)
307
+ )
308
+ fail("all platform reports must bind the same exact source and matrix");
309
+ }
310
+ const orderedReports = expectedKeys.map((key) => reportByKey.get(key));
311
+ const body = {
312
+ schemaVersion: 1,
313
+ contract: V4_CROSS_PLATFORM_ADOPTER_QUALIFICATION_CONTRACT,
314
+ sourceBinding: {
315
+ runtimeSha: reference.runtimeSha,
316
+ inventoryRoot: reference.inventoryRoot,
317
+ sourceCuts: structuredClone(reference.sourceCuts),
318
+ },
319
+ consumers: [...consumers],
320
+ platforms: [...V4_CROSS_PLATFORM_ADOPTER_PLATFORMS],
321
+ capabilityMatrix: structuredClone(normalized[0].capabilityMatrix),
322
+ reports: orderedReports.map(({ consumer, platform, reportRoot }) => ({
323
+ consumer,
324
+ platform,
325
+ reportRoot,
326
+ })),
327
+ neutralDriver: {
328
+ id: V4_CROSS_PLATFORM_NEUTRAL_DRIVER,
329
+ platformCoverage: [...V4_CROSS_PLATFORM_ADOPTER_PLATFORMS],
330
+ status: "passed",
331
+ },
332
+ authority: {
333
+ productionWrites: false,
334
+ providerEffects: false,
335
+ releaseEffects: false,
336
+ stablePublication: false,
337
+ },
338
+ };
339
+ return {
340
+ ...body,
341
+ qualificationRoot: adopterDeliveryGateDigest(body),
342
+ };
343
+ }
@@ -295,12 +295,13 @@ function classifyBuildchainUses(records, failures, { repository } = {}) {
295
295
  EXACT_SHA.test(parsed.selector.toLowerCase()) ||
296
296
  parsed.selector.includes("${{");
297
297
  if (!v4Candidate) continue;
298
- const protectedBootstrap =
299
- repository === BUILDCHAIN_REPOSITORY &&
298
+ const bootstrapShape =
300
299
  normalizeWorkflowPath(parsed.path) ===
301
300
  ALPHA_RECOVERY_BOOTSTRAP.workflow &&
302
301
  record.sourcePath === ALPHA_RECOVERY_BOOTSTRAP.sourcePath &&
303
302
  parsed.selector === ALPHA_RECOVERY_BOOTSTRAP.selector;
303
+ const protectedBootstrap =
304
+ repository === BUILDCHAIN_REPOSITORY && bootstrapShape;
304
305
  const channel =
305
306
  CHANNELS[parsed.selector] || (protectedBootstrap ? "alpha" : "");
306
307
  uses.push({