@kungfu-tech/buildchain 3.0.8 → 3.0.9-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/promote-buildchain-ref/README.md +8 -7
- package/bin/buildchain.mjs +34 -33
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
- package/contracts/release-cut-v1.schema.json +1 -0
- package/contracts/release-train-transition-v1.schema.json +1 -0
- package/dist/site/agent-index.json +0 -1
- package/dist/site/artifact-schemas.json +0 -2
- package/dist/site/badge-endpoint-registry.json +9 -9
- package/dist/site/badges/v1/kfd-8/aligned.json +1 -1
- package/dist/site/badges/v1/kfd-8/declared.json +1 -1
- package/dist/site/badges/v1/kfd-8/downgraded.json +1 -1
- package/dist/site/badges/v1/kfd-8/draft.json +1 -1
- package/dist/site/badges/v1/kfd-8/failed.json +1 -1
- package/dist/site/badges/v1/kfd-8/missing.json +1 -1
- package/dist/site/badges/v1/kfd-8/passed.json +1 -1
- package/dist/site/badges/v1/kfd-8/planned.json +1 -1
- package/dist/site/buildchain-contract.json +38 -34
- package/dist/site/buildchain-site.json +59 -39
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/cli-registry.json +8 -7
- package/dist/site/controller-registry.json +13 -5
- package/dist/site/kfd-claims.json +35 -8
- package/dist/site/kfd-upstream-aggregate.json +10 -10
- package/dist/site/manual-registry.json +9 -9
- package/dist/site/node-api-registry.json +1143 -320
- package/dist/site/page-registry.json +46 -26
- package/dist/site/public-surface-audit.json +19 -9
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +33 -5
- package/dist/site/release-provenance.json +2 -1
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +13 -13
- package/dist/site/workflow-registry.json +19 -9
- package/docs/aws-us-elastic-runner-burst-plane.md +15 -3
- package/docs/cli-reference.md +6 -6
- package/docs/cli.md +2 -3
- package/docs/dev-alpha-candidate-patrol.md +45 -20
- package/docs/dev-delivery-warrant.md +12 -2
- package/docs/kfd-support.md +7 -7
- package/docs/node-api-reference.md +295 -246
- package/docs/release-passport.md +25 -12
- package/docs/release-train.md +54 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +3 -2
- package/package.json +4 -3
- package/packages/core/artifact-verification-envelope.js +114 -0
- package/packages/core/buildchain-channel-identity.js +119 -0
- package/packages/core/buildchain-compatibility-proof.js +32 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-alpha-active-release-train.js +460 -0
- package/packages/core/dev-alpha-candidate-selection.js +181 -0
- package/packages/core/dev-delivery-warrant.js +15 -4
- package/packages/core/index.js +4 -5
- package/packages/core/kfd-adopter-manifest.js +390 -0
- package/packages/core/kfd-product-gates.js +0 -300
- package/packages/core/release-blocker-priority.js +192 -0
- package/packages/core/release-passport-contract.js +219 -6
- package/packages/core/release-passport.js +139 -242
- package/packages/core/release-train.js +220 -0
- package/scripts/aws-macos-jit-controller-core.mjs +76 -0
- package/scripts/aws-macos-jit-controller.mjs +29 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +503 -0
- package/scripts/buildchain-channel-router.mjs +18 -2
- package/scripts/buildchain-cli-help.mjs +4 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-inventory.mjs +4 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +196 -201
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +78 -31
- package/scripts/generate-site-bundle.mjs +1 -8
- package/scripts/promotion-channel-router.mjs +5 -3
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
- package/scripts/site-capability-metadata.mjs +3 -1
- package/dist/site/schemas/kfd-support-projection-v1.schema.json +0 -106
|
@@ -10,12 +10,8 @@ export const KFD_PRODUCT_GATE_INPUT_CONTRACT =
|
|
|
10
10
|
"kungfu-buildchain-kfd-product-gate-input";
|
|
11
11
|
export const KFD_PRODUCT_GATE_CONTRACT =
|
|
12
12
|
"kungfu-buildchain-kfd-product-gate";
|
|
13
|
-
export const KFD_SUPPORT_PROJECTION_CONTRACT =
|
|
14
|
-
"kungfu-buildchain-kfd-support-projection";
|
|
15
13
|
export const KFD_PRODUCT_GATE_INPUT_SCHEMA_ID =
|
|
16
14
|
"https://buildchain.libkungfu.dev/schemas/kfd-product-gate-input-v1.schema.json";
|
|
17
|
-
export const KFD_SUPPORT_PROJECTION_SCHEMA_ID =
|
|
18
|
-
"https://buildchain.libkungfu.dev/schemas/kfd-support-projection-v1.schema.json";
|
|
19
15
|
|
|
20
16
|
const SUPPORTED_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
|
|
21
17
|
const SUPPORTED_GATE_STANDARD_SET = new Set(SUPPORTED_GATE_STANDARDS);
|
|
@@ -127,48 +123,6 @@ export const KFD_PRODUCT_GATE_INPUT_SCHEMA = {
|
|
|
127
123
|
},
|
|
128
124
|
};
|
|
129
125
|
|
|
130
|
-
export const KFD_SUPPORT_PROJECTION_SCHEMA = {
|
|
131
|
-
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
132
|
-
$id: KFD_SUPPORT_PROJECTION_SCHEMA_ID,
|
|
133
|
-
title: "Buildchain KFD support projection v1",
|
|
134
|
-
description:
|
|
135
|
-
"A release-bound, non-authoritative projection of one product-owned KFD support matrix.",
|
|
136
|
-
type: "object",
|
|
137
|
-
additionalProperties: false,
|
|
138
|
-
required: [
|
|
139
|
-
"schemaVersion",
|
|
140
|
-
"contract",
|
|
141
|
-
"matrix",
|
|
142
|
-
"standardPackage",
|
|
143
|
-
"gateResults",
|
|
144
|
-
"rows",
|
|
145
|
-
"status",
|
|
146
|
-
"projectionRoot",
|
|
147
|
-
"nonClaims",
|
|
148
|
-
],
|
|
149
|
-
properties: {
|
|
150
|
-
schemaVersion: { const: 1 },
|
|
151
|
-
contract: { const: KFD_SUPPORT_PROJECTION_CONTRACT },
|
|
152
|
-
matrix: {
|
|
153
|
-
type: "object",
|
|
154
|
-
additionalProperties: false,
|
|
155
|
-
required: ["contract", "root", "authorityPath"],
|
|
156
|
-
properties: { contract: STRING, root: SHA256, authorityPath: STRING },
|
|
157
|
-
},
|
|
158
|
-
standardPackage: {
|
|
159
|
-
type: "object",
|
|
160
|
-
additionalProperties: false,
|
|
161
|
-
required: ["name", "version", "standardsSha256"],
|
|
162
|
-
properties: { name: STRING, version: STRING, standardsSha256: SHA256 },
|
|
163
|
-
},
|
|
164
|
-
gateResults: { type: "array", minItems: 3, items: { type: "object" } },
|
|
165
|
-
rows: { type: "array", minItems: 13, maxItems: 13, items: { type: "object" } },
|
|
166
|
-
status: { enum: ["passed", "failed"] },
|
|
167
|
-
projectionRoot: SHA256,
|
|
168
|
-
nonClaims: { type: "array", minItems: 1, items: STRING },
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
|
|
172
126
|
const require = createRequire(import.meta.url);
|
|
173
127
|
let verifierPromise;
|
|
174
128
|
|
|
@@ -604,262 +558,8 @@ export function validateKfdProductGateResult(result, {
|
|
|
604
558
|
return { valid: issues.length === 0, issues };
|
|
605
559
|
}
|
|
606
560
|
|
|
607
|
-
function expectedKfdIds() {
|
|
608
|
-
return Array.from({ length: 13 }, (_, index) => `KFD-${index + 1}`);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function normalizeMatrixRoot(matrix, matrixRoot) {
|
|
612
|
-
const normalized = normalizeSha256(matrixRoot);
|
|
613
|
-
return normalized || kfdProductGateDigest(matrix);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function projectMatrixRow(row) {
|
|
617
|
-
return {
|
|
618
|
-
id: row.id,
|
|
619
|
-
key: row.key,
|
|
620
|
-
title: row.title,
|
|
621
|
-
supportStatus: row.supportStatus,
|
|
622
|
-
normative: row.normative,
|
|
623
|
-
implementation: row.implementation,
|
|
624
|
-
verification: row.verification,
|
|
625
|
-
buildchain: row.buildchain,
|
|
626
|
-
releaseQualification: row.releaseQualification,
|
|
627
|
-
claimClass: row.claimClass,
|
|
628
|
-
knownLimitations: row.knownLimitations,
|
|
629
|
-
owner: row.owner,
|
|
630
|
-
nextGate: row.nextGate,
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function validateMatrixRows(matrix, gatesByStandard, issues) {
|
|
635
|
-
const rows = Array.isArray(matrix?.rows) ? matrix.rows : [];
|
|
636
|
-
const expectedIds = expectedKfdIds();
|
|
637
|
-
const ids = rows.map((row) => row?.id);
|
|
638
|
-
if (rows.length !== expectedIds.length || new Set(ids).size !== expectedIds.length) {
|
|
639
|
-
issues.push(issue("matrix-row-set", "rows", "support matrix must contain exactly one row for KFD-1 through KFD-13"));
|
|
640
|
-
}
|
|
641
|
-
for (const id of expectedIds) {
|
|
642
|
-
const row = rows.find((entry) => entry?.id === id);
|
|
643
|
-
if (!row) {
|
|
644
|
-
issues.push(issue("matrix-row-missing", "rows", `${id} is missing`));
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
647
|
-
const key = id.toLowerCase();
|
|
648
|
-
const metadata = standardMetadata(key);
|
|
649
|
-
if (row.key !== key || row?.normative?.status !== metadata.status || row?.normative?.revision !== metadata.revision) {
|
|
650
|
-
issues.push(issue("matrix-normative-drift", `rows.${id}.normative`, `${id} must match installed KFD status and revision`, {
|
|
651
|
-
expected: { key, status: metadata.status, revision: metadata.revision },
|
|
652
|
-
actual: { key: row.key, status: row?.normative?.status, revision: row?.normative?.revision },
|
|
653
|
-
}));
|
|
654
|
-
}
|
|
655
|
-
const shipped = row?.releaseQualification?.shippedSupport === true;
|
|
656
|
-
if (
|
|
657
|
-
shipped &&
|
|
658
|
-
(
|
|
659
|
-
row?.implementation?.status !== "implemented" ||
|
|
660
|
-
row?.verification?.status !== "passed" ||
|
|
661
|
-
row?.buildchain?.gateStatus !== "passed" ||
|
|
662
|
-
!String(row.supportStatus || "").includes("supported")
|
|
663
|
-
)
|
|
664
|
-
) {
|
|
665
|
-
issues.push(issue("matrix-shipped-widening", `rows.${id}`, `${id} cannot be shipped without implemented, verified, and passed Buildchain evidence`));
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
for (const id of ["KFD-4", "KFD-5"]) {
|
|
669
|
-
const row = rows.find((entry) => entry?.id === id);
|
|
670
|
-
if (row?.supportStatus !== "candidate" || row?.releaseQualification?.shippedSupport !== false) {
|
|
671
|
-
issues.push(issue("matrix-candidate-widening", `rows.${id}`, `${id} must remain a non-shipped candidate`));
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
const kfd6 = rows.find((entry) => entry?.id === "KFD-6");
|
|
675
|
-
if (
|
|
676
|
-
kfd6?.supportStatus !== "unsupported" ||
|
|
677
|
-
kfd6?.releaseQualification?.shippedSupport !== false ||
|
|
678
|
-
kfd6?.buildchain?.gateStatus === "passed"
|
|
679
|
-
) {
|
|
680
|
-
issues.push(issue("matrix-kfd6-barrier", "rows.KFD-6", "KFD-6 must remain explicitly unsupported and non-shipped"));
|
|
681
|
-
}
|
|
682
|
-
for (let number = 8; number <= 13; number += 1) {
|
|
683
|
-
const id = `KFD-${number}`;
|
|
684
|
-
const row = rows.find((entry) => entry?.id === id);
|
|
685
|
-
if (
|
|
686
|
-
row?.supportStatus !== "draft-adopter-evidence" ||
|
|
687
|
-
row?.releaseQualification?.shippedSupport !== false ||
|
|
688
|
-
row?.buildchain?.gateStatus === "passed"
|
|
689
|
-
) {
|
|
690
|
-
issues.push(issue("matrix-draft-barrier", `rows.${id}`, `${id} must remain draft adopter evidence and non-shipped`));
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
for (const standard of SUPPORTED_GATE_STANDARDS) {
|
|
694
|
-
const id = standard.toUpperCase();
|
|
695
|
-
const row = rows.find((entry) => entry?.id === id);
|
|
696
|
-
const gate = gatesByStandard.get(standard);
|
|
697
|
-
if (!gate) {
|
|
698
|
-
issues.push(issue("matrix-gate-missing", `rows.${id}.buildchain`, `${id} requires a Buildchain product-gate result`));
|
|
699
|
-
continue;
|
|
700
|
-
}
|
|
701
|
-
const rowPassed = row?.buildchain?.gateStatus === "passed";
|
|
702
|
-
const gatePassed = gate.status === "passed";
|
|
703
|
-
if (rowPassed !== gatePassed) {
|
|
704
|
-
issues.push(issue("matrix-gate-disagreement", `rows.${id}.buildchain.gateStatus`, `${id} matrix and Buildchain gate disagree`, {
|
|
705
|
-
matrix: row?.buildchain?.gateStatus,
|
|
706
|
-
gate: gate.status,
|
|
707
|
-
}));
|
|
708
|
-
}
|
|
709
|
-
if (row?.buildchain?.protocol !== `${KFD_PRODUCT_GATE_CONTRACT}/v1`) {
|
|
710
|
-
issues.push(issue("matrix-gate-protocol", `rows.${id}.buildchain.protocol`, `${id} must name the versioned Buildchain product-gate protocol`));
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
return rows;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
export function createKfdSupportProjection({
|
|
717
|
-
matrix,
|
|
718
|
-
matrixRoot = "",
|
|
719
|
-
gateResults = [],
|
|
720
|
-
authorityPath = ".buildchain/kfd/support-matrix.json",
|
|
721
|
-
expectedSourceSha = "",
|
|
722
|
-
checkedAt = new Date().toISOString(),
|
|
723
|
-
} = {}) {
|
|
724
|
-
const issues = [];
|
|
725
|
-
if (!isObject(matrix) || matrix.contract !== "kungfu-kfd-support-matrix" || matrix.schemaVersion !== 1) {
|
|
726
|
-
issues.push(issue("matrix-contract", "", "support matrix must use kungfu-kfd-support-matrix v1"));
|
|
727
|
-
}
|
|
728
|
-
if (
|
|
729
|
-
matrix?.upstream?.package !== kfdPackageJson.name ||
|
|
730
|
-
matrix?.upstream?.version !== kfdPackageJson.version ||
|
|
731
|
-
matrix?.upstream?.standardsSha256 !== standardsFileDigest()
|
|
732
|
-
) {
|
|
733
|
-
issues.push(issue("matrix-standard-package", "upstream", "support matrix must bind the installed KFD package and standards bytes", {
|
|
734
|
-
expected: {
|
|
735
|
-
package: kfdPackageJson.name,
|
|
736
|
-
version: kfdPackageJson.version,
|
|
737
|
-
standardsSha256: standardsFileDigest(),
|
|
738
|
-
},
|
|
739
|
-
actual: matrix?.upstream,
|
|
740
|
-
}));
|
|
741
|
-
}
|
|
742
|
-
const gatesByStandard = new Map();
|
|
743
|
-
for (const [index, gate] of gateResults.entries()) {
|
|
744
|
-
const validation = validateKfdProductGateResult(gate, { expectedSourceSha, checkedAt });
|
|
745
|
-
if (!validation.valid) {
|
|
746
|
-
issues.push(issue("gate-result-invalid", `gateResults[${index}]`, "gate result is invalid", {
|
|
747
|
-
gateIssues: validation.issues,
|
|
748
|
-
}));
|
|
749
|
-
}
|
|
750
|
-
if (gatesByStandard.has(gate?.standard)) {
|
|
751
|
-
issues.push(issue("gate-result-duplicate", `gateResults[${index}].standard`, `${gate.standard} gate result is duplicated`));
|
|
752
|
-
}
|
|
753
|
-
gatesByStandard.set(gate?.standard, gate);
|
|
754
|
-
}
|
|
755
|
-
const rows = validateMatrixRows(matrix, gatesByStandard, issues);
|
|
756
|
-
const projection = {
|
|
757
|
-
schemaVersion: 1,
|
|
758
|
-
contract: KFD_SUPPORT_PROJECTION_CONTRACT,
|
|
759
|
-
matrix: {
|
|
760
|
-
contract: matrix?.contract || "",
|
|
761
|
-
root: normalizeMatrixRoot(matrix, matrixRoot),
|
|
762
|
-
authorityPath: optionalString(matrix?.authority?.path || authorityPath),
|
|
763
|
-
},
|
|
764
|
-
standardPackage: {
|
|
765
|
-
name: kfdPackageJson.name,
|
|
766
|
-
version: kfdPackageJson.version,
|
|
767
|
-
standardsSha256: standardsFileDigest(),
|
|
768
|
-
},
|
|
769
|
-
gateResults: SUPPORTED_GATE_STANDARDS.map((standard) => {
|
|
770
|
-
const gate = gatesByStandard.get(standard);
|
|
771
|
-
return gate
|
|
772
|
-
? {
|
|
773
|
-
standard,
|
|
774
|
-
standardRevision: gate.standardRevision,
|
|
775
|
-
sourceSha: gate?.source?.sha || "",
|
|
776
|
-
evidenceCut: gate.evidenceCut,
|
|
777
|
-
status: gate.status,
|
|
778
|
-
gateRoot: gate.gateRoot,
|
|
779
|
-
issueCount: Array.isArray(gate.issues) ? gate.issues.length : 0,
|
|
780
|
-
}
|
|
781
|
-
: { standard, status: "missing", gateRoot: "", issueCount: 1 };
|
|
782
|
-
}),
|
|
783
|
-
rows: rows.map(projectMatrixRow),
|
|
784
|
-
status: issues.length === 0 ? "passed" : "failed",
|
|
785
|
-
nonClaims: [
|
|
786
|
-
"This projection does not replace the product-owned support matrix.",
|
|
787
|
-
"A passed Buildchain gate does not by itself activate, ship, or certify a KFD adoption.",
|
|
788
|
-
"Candidate, unsupported, draft, and non-qualifying matrix states cannot be widened by this projection.",
|
|
789
|
-
],
|
|
790
|
-
issues,
|
|
791
|
-
};
|
|
792
|
-
projection.projectionRoot = kfdProductGateDigest(projection);
|
|
793
|
-
return projection;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
export function validateKfdSupportProjection(projection, {
|
|
797
|
-
expectedSourceSha = "",
|
|
798
|
-
checkedAt = new Date().toISOString(),
|
|
799
|
-
} = {}) {
|
|
800
|
-
const issues = [];
|
|
801
|
-
if (!isObject(projection) || projection.schemaVersion !== 1 || projection.contract !== KFD_SUPPORT_PROJECTION_CONTRACT) {
|
|
802
|
-
issues.push(issue("projection-contract", "", `projection must use ${KFD_SUPPORT_PROJECTION_CONTRACT} v1`));
|
|
803
|
-
return { valid: false, issues };
|
|
804
|
-
}
|
|
805
|
-
const copy = structuredClone(projection);
|
|
806
|
-
const root = copy.projectionRoot;
|
|
807
|
-
delete copy.projectionRoot;
|
|
808
|
-
if (root !== kfdProductGateDigest(copy)) {
|
|
809
|
-
issues.push(issue("projection-root", "projectionRoot", "support projection root does not match its content"));
|
|
810
|
-
}
|
|
811
|
-
if (projection.status !== "passed" || (projection.issues || []).length !== 0) {
|
|
812
|
-
issues.push(issue("projection-status", "status", "release passport requires a passed support projection"));
|
|
813
|
-
}
|
|
814
|
-
if (
|
|
815
|
-
projection?.standardPackage?.name !== kfdPackageJson.name ||
|
|
816
|
-
projection?.standardPackage?.version !== kfdPackageJson.version ||
|
|
817
|
-
projection?.standardPackage?.standardsSha256 !== standardsFileDigest()
|
|
818
|
-
) {
|
|
819
|
-
issues.push(issue("projection-standard-package", "standardPackage", "support projection uses stale KFD package metadata"));
|
|
820
|
-
}
|
|
821
|
-
if (!new RegExp(SHA256_PATTERN).test(optionalString(projection?.matrix?.root))) {
|
|
822
|
-
issues.push(issue("projection-matrix-root", "matrix.root", "support projection must bind the exact support matrix bytes"));
|
|
823
|
-
}
|
|
824
|
-
if (!optionalString(projection?.matrix?.authorityPath)) {
|
|
825
|
-
issues.push(issue("projection-matrix-authority", "matrix.authorityPath", "support projection must retain the product-owned matrix authority path"));
|
|
826
|
-
}
|
|
827
|
-
const gatesByStandard = new Map();
|
|
828
|
-
for (const [index, gate] of (projection.gateResults || []).entries()) {
|
|
829
|
-
if (!SUPPORTED_GATE_STANDARD_SET.has(gate?.standard)) {
|
|
830
|
-
issues.push(issue("projection-gate-standard", `gateResults[${index}].standard`, "projected gate must be kfd-4, kfd-5, or kfd-7"));
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
if (gatesByStandard.has(gate.standard)) {
|
|
834
|
-
issues.push(issue("projection-gate-duplicate", `gateResults[${index}].standard`, `${gate.standard} is duplicated`));
|
|
835
|
-
}
|
|
836
|
-
gatesByStandard.set(gate.standard, gate);
|
|
837
|
-
if (gate.standardRevision !== standardMetadata(gate.standard).revision) {
|
|
838
|
-
issues.push(issue("projection-gate-revision", `gateResults[${index}].standardRevision`, "projected gate revision is stale"));
|
|
839
|
-
}
|
|
840
|
-
if (!new RegExp(SHA256_PATTERN).test(optionalString(gate.gateRoot))) {
|
|
841
|
-
issues.push(issue("projection-gate-root", `gateResults[${index}].gateRoot`, "projected gate must retain its gate root"));
|
|
842
|
-
}
|
|
843
|
-
if (expectedSourceSha && gate.sourceSha !== expectedSourceSha) {
|
|
844
|
-
issues.push(issue("projection-source", `gateResults[${index}].sourceSha`, "gate source must match the release source"));
|
|
845
|
-
}
|
|
846
|
-
const checked = parseDate(checkedAt);
|
|
847
|
-
const expires = parseDate(gate?.evidenceCut?.expiresAt);
|
|
848
|
-
if (!Number.isFinite(expires) || (Number.isFinite(checked) && expires <= checked)) {
|
|
849
|
-
issues.push(issue("projection-stale-gate", `gateResults[${index}].evidenceCut.expiresAt`, "projected gate evidence is stale"));
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
validateMatrixRows({ rows: projection.rows }, gatesByStandard, issues);
|
|
853
|
-
if (!Array.isArray(projection.nonClaims) || projection.nonClaims.length === 0) {
|
|
854
|
-
issues.push(issue("projection-non-claims", "nonClaims", "support projection must preserve non-claims"));
|
|
855
|
-
}
|
|
856
|
-
return { valid: issues.length === 0, issues };
|
|
857
|
-
}
|
|
858
|
-
|
|
859
561
|
export const kfdProductGates = Object.freeze({
|
|
860
562
|
evaluate: evaluateKfdProductGate,
|
|
861
|
-
projectSupport: createKfdSupportProjection,
|
|
862
563
|
validateGateResult: validateKfdProductGateResult,
|
|
863
|
-
validateSupportProjection: validateKfdSupportProjection,
|
|
864
564
|
verifyRecord: verifyKfdRecord,
|
|
865
565
|
});
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
devDeliveryContentRoot,
|
|
5
|
+
devDeliveryExactRoot as exactRoot,
|
|
6
|
+
devDeliveryExactSha as exactSha,
|
|
7
|
+
devDeliveryPositiveInteger as positiveInteger,
|
|
8
|
+
devDeliveryProtectedBase as protectedBase,
|
|
9
|
+
devDeliveryRepository as repository,
|
|
10
|
+
devDeliveryText as text,
|
|
11
|
+
devDeliveryTimestamp as timestamp,
|
|
12
|
+
} from "./dev-delivery-common.js";
|
|
13
|
+
import { validateReleaseBlockerRepair } from "./release-train.js";
|
|
14
|
+
|
|
15
|
+
export const RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA =
|
|
16
|
+
"kungfu.buildchain.release-blocker-priority-claim/v1";
|
|
17
|
+
|
|
18
|
+
function exactFields(value, fields, label) {
|
|
19
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
20
|
+
throw new Error(`${label} must be an object`);
|
|
21
|
+
}
|
|
22
|
+
const actual = Object.keys(value).sort();
|
|
23
|
+
const expected = [...fields].sort();
|
|
24
|
+
if (
|
|
25
|
+
actual.length !== expected.length ||
|
|
26
|
+
actual.some((field, index) => field !== expected[index])
|
|
27
|
+
) {
|
|
28
|
+
throw new Error(`${label} has an invalid field set`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createReleaseBlockerPriorityClaim(repairInput, input = {}) {
|
|
33
|
+
const repair = validateReleaseBlockerRepair(repairInput);
|
|
34
|
+
if (!repair.publication.eligible || repair.devLanding.status !== "landed") {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"release-blocker priority requires an exact settled dev landing",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const body = {
|
|
40
|
+
schema: RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA,
|
|
41
|
+
repository: repository(repair.successorTrain.releaseCut.repository),
|
|
42
|
+
protectedBase: protectedBase(repair.successorTrain.releaseCut.sourceBranch),
|
|
43
|
+
assignmentRoot: exactRoot(input.assignmentRoot, "assignmentRoot"),
|
|
44
|
+
initiativeRoot: exactRoot(input.initiativeRoot, "initiativeRoot"),
|
|
45
|
+
repairRoot: exactRoot(repair.repairRoot, "repairRoot"),
|
|
46
|
+
priorCutRoot: exactRoot(
|
|
47
|
+
repair.priorTrain.releaseCut.cutRoot,
|
|
48
|
+
"priorCutRoot",
|
|
49
|
+
),
|
|
50
|
+
successorCutRoot: exactRoot(
|
|
51
|
+
repair.successorTrain.releaseCut.cutRoot,
|
|
52
|
+
"successorCutRoot",
|
|
53
|
+
),
|
|
54
|
+
candidateGeneration: positiveInteger(
|
|
55
|
+
repair.successorTrain.releaseCut.generation,
|
|
56
|
+
"candidateGeneration",
|
|
57
|
+
),
|
|
58
|
+
cutCandidateSha: exactSha(repair.cutLanding.landedSha, "cutCandidateSha"),
|
|
59
|
+
sourceHead: exactSha(repair.devLanding.landedSha, "sourceHead"),
|
|
60
|
+
sourcePatchRoot: exactRoot(repair.patchRoot, "sourcePatchRoot"),
|
|
61
|
+
cutLandingEvidenceRoot: exactRoot(
|
|
62
|
+
repair.cutLanding.evidenceRoot,
|
|
63
|
+
"cutLandingEvidenceRoot",
|
|
64
|
+
),
|
|
65
|
+
devLandingEvidenceRoot: exactRoot(
|
|
66
|
+
repair.devLanding.evidenceRoot,
|
|
67
|
+
"devLandingEvidenceRoot",
|
|
68
|
+
),
|
|
69
|
+
publicationGateRoot: exactRoot(
|
|
70
|
+
repair.publication.gateRoot,
|
|
71
|
+
"publicationGateRoot",
|
|
72
|
+
),
|
|
73
|
+
issuedAt: timestamp(input.issuedAt, "issuedAt"),
|
|
74
|
+
};
|
|
75
|
+
return { ...body, claimRoot: devDeliveryContentRoot(body) };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function normalizeReleaseBlockerPriorityClaim(
|
|
79
|
+
input,
|
|
80
|
+
candidate,
|
|
81
|
+
expected,
|
|
82
|
+
) {
|
|
83
|
+
exactFields(
|
|
84
|
+
input,
|
|
85
|
+
[
|
|
86
|
+
"schema",
|
|
87
|
+
"repository",
|
|
88
|
+
"protectedBase",
|
|
89
|
+
"assignmentRoot",
|
|
90
|
+
"initiativeRoot",
|
|
91
|
+
"repairRoot",
|
|
92
|
+
"priorCutRoot",
|
|
93
|
+
"successorCutRoot",
|
|
94
|
+
"candidateGeneration",
|
|
95
|
+
"cutCandidateSha",
|
|
96
|
+
"sourceHead",
|
|
97
|
+
"sourcePatchRoot",
|
|
98
|
+
"cutLandingEvidenceRoot",
|
|
99
|
+
"devLandingEvidenceRoot",
|
|
100
|
+
"publicationGateRoot",
|
|
101
|
+
"issuedAt",
|
|
102
|
+
"claimRoot",
|
|
103
|
+
],
|
|
104
|
+
"releaseBlockerPriority",
|
|
105
|
+
);
|
|
106
|
+
const body = {
|
|
107
|
+
schema: text(input.schema),
|
|
108
|
+
repository: repository(input.repository),
|
|
109
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
110
|
+
assignmentRoot: exactRoot(
|
|
111
|
+
input.assignmentRoot,
|
|
112
|
+
"releaseBlockerPriority.assignmentRoot",
|
|
113
|
+
),
|
|
114
|
+
initiativeRoot: exactRoot(
|
|
115
|
+
input.initiativeRoot,
|
|
116
|
+
"releaseBlockerPriority.initiativeRoot",
|
|
117
|
+
),
|
|
118
|
+
repairRoot: exactRoot(
|
|
119
|
+
input.repairRoot,
|
|
120
|
+
"releaseBlockerPriority.repairRoot",
|
|
121
|
+
),
|
|
122
|
+
priorCutRoot: exactRoot(
|
|
123
|
+
input.priorCutRoot,
|
|
124
|
+
"releaseBlockerPriority.priorCutRoot",
|
|
125
|
+
),
|
|
126
|
+
successorCutRoot: exactRoot(
|
|
127
|
+
input.successorCutRoot,
|
|
128
|
+
"releaseBlockerPriority.successorCutRoot",
|
|
129
|
+
),
|
|
130
|
+
candidateGeneration: positiveInteger(
|
|
131
|
+
input.candidateGeneration,
|
|
132
|
+
"releaseBlockerPriority.candidateGeneration",
|
|
133
|
+
),
|
|
134
|
+
cutCandidateSha: exactSha(
|
|
135
|
+
input.cutCandidateSha,
|
|
136
|
+
"releaseBlockerPriority.cutCandidateSha",
|
|
137
|
+
),
|
|
138
|
+
sourceHead: exactSha(input.sourceHead, "releaseBlockerPriority.sourceHead"),
|
|
139
|
+
sourcePatchRoot: exactRoot(
|
|
140
|
+
input.sourcePatchRoot,
|
|
141
|
+
"releaseBlockerPriority.sourcePatchRoot",
|
|
142
|
+
),
|
|
143
|
+
cutLandingEvidenceRoot: exactRoot(
|
|
144
|
+
input.cutLandingEvidenceRoot,
|
|
145
|
+
"releaseBlockerPriority.cutLandingEvidenceRoot",
|
|
146
|
+
),
|
|
147
|
+
devLandingEvidenceRoot: exactRoot(
|
|
148
|
+
input.devLandingEvidenceRoot,
|
|
149
|
+
"releaseBlockerPriority.devLandingEvidenceRoot",
|
|
150
|
+
),
|
|
151
|
+
publicationGateRoot: exactRoot(
|
|
152
|
+
input.publicationGateRoot,
|
|
153
|
+
"releaseBlockerPriority.publicationGateRoot",
|
|
154
|
+
),
|
|
155
|
+
issuedAt: timestamp(input.issuedAt, "releaseBlockerPriority.issuedAt"),
|
|
156
|
+
};
|
|
157
|
+
if (body.schema !== RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA) {
|
|
158
|
+
throw new Error("releaseBlockerPriority schema is unsupported");
|
|
159
|
+
}
|
|
160
|
+
if (input.claimRoot !== devDeliveryContentRoot(body)) {
|
|
161
|
+
throw new Error("releaseBlockerPriority claimRoot drift");
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
body.repository !== expected.repository ||
|
|
165
|
+
body.protectedBase !== expected.protectedBase
|
|
166
|
+
) {
|
|
167
|
+
throw new Error("releaseBlockerPriority queue route mismatch");
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
body.assignmentRoot !== candidate.assignmentRoot ||
|
|
171
|
+
body.initiativeRoot !== candidate.initiativeRoot
|
|
172
|
+
) {
|
|
173
|
+
throw new Error("releaseBlockerPriority Work identity mismatch");
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
body.sourceHead !== candidate.sourceHead ||
|
|
177
|
+
body.sourcePatchRoot !== candidate.sourcePatchRoot
|
|
178
|
+
) {
|
|
179
|
+
throw new Error("releaseBlockerPriority source identity mismatch");
|
|
180
|
+
}
|
|
181
|
+
return { ...body, claimRoot: input.claimRoot };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function compareReleaseBlockerPriority(left, right) {
|
|
185
|
+
const leftBlocker =
|
|
186
|
+
left.priority.releaseBlocker && left.candidate.priority === "ordinary";
|
|
187
|
+
const rightBlocker =
|
|
188
|
+
right.priority.releaseBlocker && right.candidate.priority === "ordinary";
|
|
189
|
+
if (leftBlocker === rightBlocker) return 0;
|
|
190
|
+
const peer = leftBlocker ? right : left;
|
|
191
|
+
return peer.candidate.priority === "ordinary" ? (leftBlocker ? -1 : 1) : 0;
|
|
192
|
+
}
|