@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.
- package/README.md +9 -6
- package/actions/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +70 -1
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +156 -11
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +480 -129
- package/architecture/v3-core-mechanism-inventory.json +4 -3
- package/architecture/v3-v4-capability-closure.md +59 -0
- package/architecture/v3-v4-live-capability-inventory.json +102404 -0
- package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +123 -32
- package/dist/site/buildchain-site.json +96 -28
- package/dist/site/capability-registry.json +6 -6
- package/dist/site/cli-registry.json +5 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +170 -21
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +6 -6
- package/dist/site/node-api-registry.json +1474 -215
- package/dist/site/page-registry.json +85 -17
- package/dist/site/public-surface-audit.json +85 -20
- package/dist/site/publication-authority-registry.json +28 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +5 -0
- package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/site-manifest.json +10 -10
- package/dist/site/workflow-registry.json +94 -18
- package/docs/MAP.md +1 -1
- package/docs/cli-reference.md +4 -4
- package/docs/dev-delivery-warrant.md +12 -9
- package/docs/node-api-reference.md +291 -206
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/v4-adopter-delivery.md +23 -4
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/package.json +8 -3
- package/packages/core/adopter-delivery-passport.js +36 -0
- package/packages/core/buildchain-contract.js +31 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +15 -2
- package/packages/core/dev-delivery-warrant.js +21 -4
- package/packages/core/github-governance-authority.js +4 -4
- package/packages/core/index.js +1 -0
- package/packages/core/paper-scaffold-content.js +401 -0
- package/packages/core/paper.js +12 -378
- package/packages/core/publication-control-plane-audit.js +135 -111
- package/packages/core/publication-rehearsal-projection.js +104 -0
- package/packages/core/publication-rehearsal-runtime.js +190 -0
- package/packages/core/release-candidate-recovery.js +6 -5
- package/packages/core/release-passport.js +82 -184
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
- package/packages/core/v4-floating-consumer-policy.js +3 -2
- package/packages/core/v4-floating-consumer-release-passport.js +134 -15
- package/packages/core/v4-publication-qualification.js +222 -0
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +3 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +23 -5
- package/scripts/check-maintainability.mjs +145 -4
- package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
- package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +10 -3
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
- package/scripts/dev-delivery-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant.mjs +45 -5
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +44 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-release-candidate-passport.mjs +26 -1
- package/scripts/generate-site-bundle.mjs +6 -0
- package/scripts/maintainability-domain-metrics.mjs +237 -0
- package/scripts/maintainability-governance.mjs +473 -0
- package/scripts/maintainability-metrics.mjs +28 -6
- package/scripts/project-cut-merge-queue-admission.mjs +394 -0
- package/scripts/promotion-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +19 -2
- package/scripts/resume-from-candidate-run.mjs +106 -103
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +4 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- 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
|
|
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({
|