@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.10
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/bin/buildchain.mjs +9 -1
- package/contracts/dev-delivery-authority-v2.schema.json +253 -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/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -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 +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +91 -32
- package/dist/site/buildchain-site.json +353 -45
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +41 -2
- package/dist/site/controller-registry.json +8 -4
- package/dist/site/kfd-claims.json +193 -9
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +26 -10
- package/dist/site/node-api-registry.json +8012 -4574
- package/dist/site/page-registry.json +331 -31
- package/dist/site/public-surface-audit.json +353 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +12 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +22 -14
- package/dist/site/workflow-registry.json +15 -7
- package/docs/MAP.md +5 -2
- package/docs/adopter-delivery-gate.md +106 -0
- package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
- package/docs/cli-reference.md +200 -13
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +137 -0
- package/docs/dev-delivery-warrant.md +164 -27
- package/docs/kfd-support.md +7 -2
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +464 -277
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +15 -2
- package/docs/release-train.md +46 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +6 -2
- package/package.json +21 -6
- package/packages/core/README.md +25 -0
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- package/packages/core/adopter-delivery-passport.js +259 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-channel-identity.js +118 -0
- package/packages/core/buildchain-compatibility-proof.js +54 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +52 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority.js +996 -0
- package/packages/core/dev-delivery-native-proof.js +418 -0
- package/packages/core/dev-delivery-warrant-settlement.js +67 -20
- package/packages/core/dev-delivery-warrant.js +141 -48
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +49 -50
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/next-development-candidate-reservation.js +186 -0
- package/packages/core/next-development-controller.js +726 -0
- package/packages/core/next-development-projection.js +288 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +7 -4
- package/packages/core/paper.js +14 -7
- package/packages/core/release-blocker-priority.js +192 -0
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +29 -12
- package/packages/core/release-train.js +220 -0
- package/scripts/aws-macos-jit-controller-core.mjs +239 -4
- package/scripts/aws-macos-jit-controller.mjs +198 -2
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
- package/scripts/aws-macos-jit.mjs +12 -0
- package/scripts/buildchain-channel-router.mjs +18 -2
- package/scripts/buildchain-cli-help.mjs +9 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-internal-architecture.mjs +1 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority.mjs +368 -0
- package/scripts/dev-delivery-native-run.mjs +235 -0
- package/scripts/dev-delivery-proof.mjs +64 -2
- package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
- package/scripts/dev-delivery-two-phase.mjs +577 -0
- package/scripts/dev-delivery-warrant.mjs +47 -5
- package/scripts/dev-pr-delivery-warrant.mjs +19 -14
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +82 -31
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-site-bundle.mjs +8 -3
- package/scripts/init-repo.mjs +135 -62
- package/scripts/next-development-self-dogfood-harness.mjs +409 -0
- package/scripts/next-development-self-dogfood.mjs +532 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/promotion-channel-router.mjs +5 -3
- package/scripts/release-candidate-tail-reseal.mjs +426 -0
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
- package/scripts/seal-artifact-signing-requests.mjs +7 -4
- package/scripts/site-capability-metadata.mjs +23 -1
- package/scripts/stable-candidate-qualification.mjs +7 -7
- package/scripts/workflow-call-contract.mjs +1 -1
- package/templates/native-dev-delivery.yml +86 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ADOPTER_DELIVERY_GATE_RESULT_CONTRACT,
|
|
3
|
+
adopterDeliveryGateDigest,
|
|
4
|
+
} from "./adopter-delivery-gate.js";
|
|
5
|
+
|
|
6
|
+
export const ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT =
|
|
7
|
+
"kungfu-buildchain-adopter-delivery-passport-binding";
|
|
8
|
+
|
|
9
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
10
|
+
|
|
11
|
+
function issue(code, message, details = {}) {
|
|
12
|
+
return { code, message, details };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function validateRootedJson(value, rootField, code, label) {
|
|
16
|
+
const root = value?.[rootField];
|
|
17
|
+
if (!ROOT_PATTERN.test(root ?? "")) {
|
|
18
|
+
return [issue(code, `${label} must be a canonical sha256 root`)];
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const preimage = structuredClone(value);
|
|
22
|
+
delete preimage[rootField];
|
|
23
|
+
return adopterDeliveryGateDigest(preimage) === root
|
|
24
|
+
? []
|
|
25
|
+
: [issue(code, `${label} does not match its exact JSON closure`)];
|
|
26
|
+
} catch {
|
|
27
|
+
return [issue(code, `${label} requires finite acyclic JSON`)];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateGateIdentity(result) {
|
|
32
|
+
const issues = [];
|
|
33
|
+
if (
|
|
34
|
+
result.schemaVersion !== 1 ||
|
|
35
|
+
result.contract !== ADOPTER_DELIVERY_GATE_RESULT_CONTRACT
|
|
36
|
+
) {
|
|
37
|
+
issues.push(
|
|
38
|
+
issue(
|
|
39
|
+
"adopterDelivery.gateResult.contract",
|
|
40
|
+
"adopter delivery gate result contract is unsupported",
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
!result.project ||
|
|
46
|
+
!result.artifact ||
|
|
47
|
+
!result.protocol ||
|
|
48
|
+
!result.artifactProfile
|
|
49
|
+
) {
|
|
50
|
+
issues.push(
|
|
51
|
+
issue(
|
|
52
|
+
"adopterDelivery.gateResult.closure",
|
|
53
|
+
"gate result must retain project, artifact, protocol, and artifact-profile bindings",
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (!ROOT_PATTERN.test(result.artifact?.root ?? "")) {
|
|
58
|
+
issues.push(
|
|
59
|
+
issue(
|
|
60
|
+
"adopterDelivery.gateResult.artifact.root",
|
|
61
|
+
"delivery artifact root must be a canonical sha256 root",
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (result.qualifying !== false || result.selfCertified !== false) {
|
|
66
|
+
issues.push(
|
|
67
|
+
issue(
|
|
68
|
+
"adopterDelivery.gateResult.nonClaims",
|
|
69
|
+
"delivery evidence must remain non-qualifying and non-self-certifying",
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return issues;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validatePassedGate(result) {
|
|
77
|
+
if (result.status !== "passed") {
|
|
78
|
+
return result.status === "failed"
|
|
79
|
+
? []
|
|
80
|
+
: [
|
|
81
|
+
issue(
|
|
82
|
+
"adopterDelivery.gateResult.status",
|
|
83
|
+
"gate status must be passed or failed",
|
|
84
|
+
),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
const passed =
|
|
88
|
+
result.protocol?.loaded === true &&
|
|
89
|
+
ROOT_PATTERN.test(result.protocol?.reportRoot ?? "") &&
|
|
90
|
+
result.artifactProfile?.loaded === true &&
|
|
91
|
+
result.artifactProfile?.valid === true &&
|
|
92
|
+
result.semanticReport !== null &&
|
|
93
|
+
(result.issues?.length ?? -1) === 0;
|
|
94
|
+
return passed
|
|
95
|
+
? []
|
|
96
|
+
: [
|
|
97
|
+
issue(
|
|
98
|
+
"adopterDelivery.gateResult.status",
|
|
99
|
+
"a passed gate must retain successful exact driver and artifact-profile evidence",
|
|
100
|
+
),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function validateAdopterDeliveryGateResult(result) {
|
|
105
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
106
|
+
return {
|
|
107
|
+
valid: false,
|
|
108
|
+
passed: false,
|
|
109
|
+
issues: [
|
|
110
|
+
issue(
|
|
111
|
+
"adopterDelivery.gateResult",
|
|
112
|
+
"adopter delivery gate result must be an object",
|
|
113
|
+
),
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const issues = [
|
|
118
|
+
...validateGateIdentity(result),
|
|
119
|
+
...validateRootedJson(
|
|
120
|
+
result,
|
|
121
|
+
"gateRoot",
|
|
122
|
+
"adopterDelivery.gateResult.gateRoot",
|
|
123
|
+
"adopter delivery gate root",
|
|
124
|
+
),
|
|
125
|
+
...validatePassedGate(result),
|
|
126
|
+
];
|
|
127
|
+
const valid = issues.length === 0;
|
|
128
|
+
return { valid, passed: valid && result.status === "passed", issues };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createAdopterDeliveryPassportBinding(gateResult) {
|
|
132
|
+
const normalized = structuredClone(gateResult);
|
|
133
|
+
const validation = validateAdopterDeliveryGateResult(normalized);
|
|
134
|
+
const binding = {
|
|
135
|
+
schemaVersion: 1,
|
|
136
|
+
contract: ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT,
|
|
137
|
+
valid: validation.passed,
|
|
138
|
+
qualifying: false,
|
|
139
|
+
selfCertified: false,
|
|
140
|
+
gateResult: normalized,
|
|
141
|
+
validationIssues: validation.issues,
|
|
142
|
+
nonClaims: [
|
|
143
|
+
"A passing adopter delivery gate does not grant runtime permission or release authority.",
|
|
144
|
+
"The selected protocol driver retains semantic authority; Buildchain only binds its exact result into delivery evidence.",
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
binding.bindingRoot = adopterDeliveryGateDigest(binding);
|
|
148
|
+
return binding;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function normalizeAdopterDeliveryPassportBinding(value) {
|
|
152
|
+
if (!value) return undefined;
|
|
153
|
+
return value.contract === ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
|
|
154
|
+
? structuredClone(value)
|
|
155
|
+
: createAdopterDeliveryPassportBinding(value);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function validateAdopterDeliveryPassportBinding(
|
|
159
|
+
binding,
|
|
160
|
+
{ expectedProjectRepository = "" } = {},
|
|
161
|
+
) {
|
|
162
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
163
|
+
return [
|
|
164
|
+
issue(
|
|
165
|
+
"adopterDelivery.binding",
|
|
166
|
+
"adopter delivery passport binding must be an object",
|
|
167
|
+
),
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
const gateValidation = validateAdopterDeliveryGateResult(binding.gateResult);
|
|
171
|
+
const issues = [
|
|
172
|
+
...validateRootedJson(
|
|
173
|
+
binding,
|
|
174
|
+
"bindingRoot",
|
|
175
|
+
"adopterDelivery.binding.bindingRoot",
|
|
176
|
+
"adopter delivery binding root",
|
|
177
|
+
),
|
|
178
|
+
...gateValidation.issues,
|
|
179
|
+
];
|
|
180
|
+
if (
|
|
181
|
+
binding.schemaVersion !== 1 ||
|
|
182
|
+
binding.contract !== ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
|
|
183
|
+
) {
|
|
184
|
+
issues.push(
|
|
185
|
+
issue(
|
|
186
|
+
"adopterDelivery.binding.contract",
|
|
187
|
+
"adopter delivery passport binding contract is unsupported",
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
binding.valid !== gateValidation.passed ||
|
|
193
|
+
binding.qualifying !== false ||
|
|
194
|
+
binding.selfCertified !== false
|
|
195
|
+
) {
|
|
196
|
+
issues.push(
|
|
197
|
+
issue(
|
|
198
|
+
"adopterDelivery.binding.status",
|
|
199
|
+
"binding status must preserve a passing non-authoritative gate result",
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (!gateValidation.passed) {
|
|
204
|
+
issues.push(
|
|
205
|
+
issue(
|
|
206
|
+
"adopterDelivery.status",
|
|
207
|
+
"release passport requires a passed adopter delivery gate",
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (
|
|
212
|
+
expectedProjectRepository &&
|
|
213
|
+
binding.gateResult?.project?.adopterId !== expectedProjectRepository
|
|
214
|
+
) {
|
|
215
|
+
issues.push(
|
|
216
|
+
issue(
|
|
217
|
+
"adopterDelivery.project",
|
|
218
|
+
"delivery project must match the release passport product repository",
|
|
219
|
+
),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
return issues;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function sameJson(left, right) {
|
|
226
|
+
try {
|
|
227
|
+
return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function validateAdopterDeliveryReleaseEvidence({
|
|
234
|
+
binding,
|
|
235
|
+
artifactBinding,
|
|
236
|
+
expectedProjectRepository = "",
|
|
237
|
+
} = {}) {
|
|
238
|
+
if (!binding && !artifactBinding) return [];
|
|
239
|
+
if (!binding) {
|
|
240
|
+
return [
|
|
241
|
+
issue(
|
|
242
|
+
"adopterDelivery.passport",
|
|
243
|
+
"artifact evidence cannot introduce delivery authority absent from the passport",
|
|
244
|
+
),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
const issues = validateAdopterDeliveryPassportBinding(binding, {
|
|
248
|
+
expectedProjectRepository,
|
|
249
|
+
});
|
|
250
|
+
if (!artifactBinding || !sameJson(artifactBinding, binding)) {
|
|
251
|
+
issues.push(
|
|
252
|
+
issue(
|
|
253
|
+
"adopterDelivery.artifactEvidence",
|
|
254
|
+
"passport and artifact evidence must bind the same exact delivery result",
|
|
255
|
+
),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return issues;
|
|
259
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import vectorSuite from "./adopter-delivery-vectors.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
import { adopterDeliveryGateDigest } from "./adopter-delivery-gate.js";
|
|
4
|
+
|
|
5
|
+
export const ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT =
|
|
6
|
+
"kungfu-buildchain-adopter-delivery-vectors/v1";
|
|
7
|
+
|
|
8
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
9
|
+
const REQUIRED_CASES = new Map([
|
|
10
|
+
["golden-two-driver-offline-replay", "golden"],
|
|
11
|
+
["negative-driver-mismatch", "negative"],
|
|
12
|
+
["negative-category-conflict", "negative"],
|
|
13
|
+
["negative-evidence-substitution", "negative"],
|
|
14
|
+
["negative-stale-package-cut", "negative"],
|
|
15
|
+
["fault-driver-throw", "fault"],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function freezeJson(value) {
|
|
19
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
20
|
+
Object.freeze(value);
|
|
21
|
+
for (const child of Object.values(value)) freezeJson(child);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function issue(code, path, message) {
|
|
27
|
+
return { code, path, message };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function suiteDigest(suite) {
|
|
31
|
+
const preimage = structuredClone(suite);
|
|
32
|
+
delete preimage.suiteRoot;
|
|
33
|
+
return adopterDeliveryGateDigest(preimage);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validateAdopterDeliveryVectorSuite(suite) {
|
|
37
|
+
const issues = [];
|
|
38
|
+
if (!suite || typeof suite !== "object" || Array.isArray(suite)) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
issues: [
|
|
42
|
+
issue(
|
|
43
|
+
"adopter-delivery-vectors.invalid",
|
|
44
|
+
"/",
|
|
45
|
+
"Vector suite must be a JSON object.",
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
suite.schemaVersion !== 1 ||
|
|
52
|
+
suite.contract !== ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT
|
|
53
|
+
) {
|
|
54
|
+
issues.push(
|
|
55
|
+
issue(
|
|
56
|
+
"adopter-delivery-vectors.contract",
|
|
57
|
+
"/contract",
|
|
58
|
+
"Vector suite contract is unsupported.",
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(suite.cases)) {
|
|
63
|
+
issues.push(
|
|
64
|
+
issue(
|
|
65
|
+
"adopter-delivery-vectors.cases",
|
|
66
|
+
"/cases",
|
|
67
|
+
"Vector suite cases must be an array.",
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
for (const [index, entry] of suite.cases.entries()) {
|
|
73
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
74
|
+
issues.push(
|
|
75
|
+
issue(
|
|
76
|
+
"adopter-delivery-vectors.case",
|
|
77
|
+
`/cases/${index}`,
|
|
78
|
+
"Vector case must be an object.",
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (typeof entry.id !== "string" || seen.has(entry.id)) {
|
|
84
|
+
issues.push(
|
|
85
|
+
issue(
|
|
86
|
+
"adopter-delivery-vectors.case-id",
|
|
87
|
+
`/cases/${index}/id`,
|
|
88
|
+
"Vector case id must be non-empty and unique.",
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
seen.add(entry.id);
|
|
93
|
+
if (!entry.expected || typeof entry.expected !== "object") {
|
|
94
|
+
issues.push(
|
|
95
|
+
issue(
|
|
96
|
+
"adopter-delivery-vectors.expected",
|
|
97
|
+
`/cases/${index}/expected`,
|
|
98
|
+
"Vector case must declare its expected result.",
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const [id, className] of REQUIRED_CASES) {
|
|
104
|
+
const entry = suite.cases.find((candidate) => candidate?.id === id);
|
|
105
|
+
if (!entry || entry.class !== className) {
|
|
106
|
+
issues.push(
|
|
107
|
+
issue(
|
|
108
|
+
"adopter-delivery-vectors.required-case",
|
|
109
|
+
"/cases",
|
|
110
|
+
`Required ${className} vector ${id} is missing.`,
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!ROOT_PATTERN.test(suite.suiteRoot ?? "")) {
|
|
117
|
+
issues.push(
|
|
118
|
+
issue(
|
|
119
|
+
"adopter-delivery-vectors.root",
|
|
120
|
+
"/suiteRoot",
|
|
121
|
+
"Vector suite root must be a canonical sha256 root.",
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
} else {
|
|
125
|
+
try {
|
|
126
|
+
if (suiteDigest(suite) !== suite.suiteRoot) {
|
|
127
|
+
issues.push(
|
|
128
|
+
issue(
|
|
129
|
+
"adopter-delivery-vectors.root",
|
|
130
|
+
"/suiteRoot",
|
|
131
|
+
"Vector suite root does not match the exact JSON closure.",
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
issues.push(
|
|
137
|
+
issue(
|
|
138
|
+
"adopter-delivery-vectors.root",
|
|
139
|
+
"/suiteRoot",
|
|
140
|
+
"Vector suite must contain finite acyclic JSON.",
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { valid: issues.length === 0, issues };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const ADOPTER_DELIVERY_VECTOR_SUITE = freezeJson(
|
|
149
|
+
structuredClone(vectorSuite),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
export function getAdopterDeliveryVector(id) {
|
|
153
|
+
const entry = ADOPTER_DELIVERY_VECTOR_SUITE.cases.find(
|
|
154
|
+
(candidate) => candidate.id === id,
|
|
155
|
+
);
|
|
156
|
+
if (!entry) throw new RangeError(`Unknown adopter delivery vector: ${id}`);
|
|
157
|
+
return structuredClone(entry);
|
|
158
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"contract": "kungfu-buildchain-adopter-delivery-vectors/v1",
|
|
4
|
+
"rootAlgorithm": "sha256-buildchain-canonical-json-v1",
|
|
5
|
+
"authorities": {
|
|
6
|
+
"kfdPackage": "@kungfu-tech/kfd@1.0.0-alpha.62",
|
|
7
|
+
"categoryProfileVectors": "@kungfu-tech/kfd/adopter-conformance/category-profile-vectors.json",
|
|
8
|
+
"categoryInstanceVectors": "@kungfu-tech/kfd/adopter-conformance/category-instance-vectors.json"
|
|
9
|
+
},
|
|
10
|
+
"cases": [
|
|
11
|
+
{
|
|
12
|
+
"id": "golden-two-driver-offline-replay",
|
|
13
|
+
"class": "golden",
|
|
14
|
+
"surface": "protocol-neutral-gate",
|
|
15
|
+
"operation": "evaluate identical exact requests with two isolated protocol drivers and replay each request offline",
|
|
16
|
+
"expected": {
|
|
17
|
+
"statuses": ["passed", "passed"],
|
|
18
|
+
"issueCodes": [],
|
|
19
|
+
"deterministicRoots": true,
|
|
20
|
+
"qualifying": false,
|
|
21
|
+
"selfCertified": false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "negative-driver-mismatch",
|
|
26
|
+
"class": "negative",
|
|
27
|
+
"surface": "protocol-neutral-gate",
|
|
28
|
+
"operation": "select an exact protocol identity and version that are not registered",
|
|
29
|
+
"expected": {
|
|
30
|
+
"status": "failed",
|
|
31
|
+
"issueCodes": ["delivery-driver-unknown"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "negative-category-conflict",
|
|
36
|
+
"class": "negative",
|
|
37
|
+
"surface": "published-kfd-category-driver",
|
|
38
|
+
"operation": "append a duplicate published category profile to the exact KFD instance selection",
|
|
39
|
+
"expected": {
|
|
40
|
+
"status": "failed",
|
|
41
|
+
"issueCodes": ["acp-composition-invalid"]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "negative-evidence-substitution",
|
|
46
|
+
"class": "negative",
|
|
47
|
+
"surface": "release-passport-binding",
|
|
48
|
+
"operation": "substitute the artifact root in artifact evidence after binding the passport",
|
|
49
|
+
"expected": {
|
|
50
|
+
"status": "failed",
|
|
51
|
+
"issueCodes": ["adopterDelivery.artifactEvidence"]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "negative-stale-package-cut",
|
|
56
|
+
"class": "negative",
|
|
57
|
+
"surface": "published-kfd-category-driver",
|
|
58
|
+
"operation": "replace the KFD instance package version with an uninstalled stale cut",
|
|
59
|
+
"expected": {
|
|
60
|
+
"status": "failed",
|
|
61
|
+
"issueCodes": ["delivery-kfd-package-cut-mismatch"]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "fault-driver-throw",
|
|
66
|
+
"class": "fault",
|
|
67
|
+
"surface": "protocol-neutral-gate",
|
|
68
|
+
"operation": "throw a private exception from the selected protocol driver",
|
|
69
|
+
"expected": {
|
|
70
|
+
"status": "failed",
|
|
71
|
+
"issueCodes": ["delivery-driver-error"],
|
|
72
|
+
"privateDetailRedacted": true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"suiteRoot": "sha256:cf329805d928a9883cbafbdfdf21ef66c6a0889ed8dfe14356b4e0d25d6738f9"
|
|
77
|
+
}
|
|
@@ -417,7 +417,7 @@ function kfdBindingBody({ manifest, manifestGate, legacyProjection, paths }) {
|
|
|
417
417
|
gatePath: paths.gate,
|
|
418
418
|
gateRoot: manifestGate.gateRoot,
|
|
419
419
|
},
|
|
420
|
-
source: jsonClone(manifestGate.source),
|
|
420
|
+
adopter: jsonClone(manifestGate.adopter), source: jsonClone(manifestGate.source),
|
|
421
421
|
standardPackage: jsonClone(manifestGate.standardPackage),
|
|
422
422
|
witness: {
|
|
423
423
|
decisionRoot: manifestGate.decisionWitness.root,
|
|
@@ -445,10 +445,10 @@ export function createKfdAdopterReleaseBinding({
|
|
|
445
445
|
manifestPath = "kfd-adopter-manifest.json",
|
|
446
446
|
gatePath = "kfd-adopter-manifest-gate.json",
|
|
447
447
|
legacyProjectionPath = "kfd-support.json",
|
|
448
|
-
expectedSourceSha = "",
|
|
448
|
+
expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "",
|
|
449
449
|
} = {}) {
|
|
450
450
|
const gate = validateKfdAdopterManifestGate(manifestGate, {
|
|
451
|
-
expectedSourceSha,
|
|
451
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
452
452
|
checkedAt: manifestGate?.checkedAt,
|
|
453
453
|
});
|
|
454
454
|
const legacy = validateKfdLegacySupportMatrixProjection(legacyProjection, { manifest, manifestGate });
|
|
@@ -468,7 +468,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
468
468
|
manifest,
|
|
469
469
|
manifestGate,
|
|
470
470
|
legacyProjection,
|
|
471
|
-
expectedSourceSha = "",
|
|
471
|
+
expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "",
|
|
472
472
|
} = {}) {
|
|
473
473
|
const issues = [];
|
|
474
474
|
if (!binding || binding.schemaVersion !== 1 || binding.contract !== KFD_ADOPTER_RELEASE_BINDING_CONTRACT) {
|
|
@@ -488,7 +488,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
488
488
|
manifestPath: binding.authority?.manifestPath,
|
|
489
489
|
gatePath: binding.authority?.gatePath,
|
|
490
490
|
legacyProjectionPath: binding.legacyProjection?.path,
|
|
491
|
-
expectedSourceSha,
|
|
491
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
492
492
|
});
|
|
493
493
|
if (stableJson(binding) !== stableJson(expected)) {
|
|
494
494
|
issues.push({ code: "adopter-release-binding-drift", path: "", message: "release binding differs from the exact manifest, gate, or legacy projection closure" });
|
|
@@ -10,6 +10,7 @@ export const BUILDCHAIN_AGENT_MANUALS = Object.freeze([
|
|
|
10
10
|
{ id: "github-governance-authority", title: "GitHub governance authority", path: "docs/github-governance-authority.md", plane: "verify" },
|
|
11
11
|
{ id: "release-candidate", title: "Release Candidate Passport", path: "docs/release-candidate.md", plane: "verify" },
|
|
12
12
|
{ id: "release-train", title: "Release Train and Release Cut", path: "docs/release-train.md", plane: "verify" },
|
|
13
|
+
{ id: "next-development-transition", title: "Next-development transition", path: "docs/next-development-transition.md", plane: "verify" },
|
|
13
14
|
{ id: "release-propagation", title: "Release propagation", path: "docs/release-propagation.md", plane: "use" },
|
|
14
15
|
{ id: "readme-badges", title: "README badge blocks", path: "docs/readme-badges.md", plane: "use" },
|
|
15
16
|
{ id: "homebrew", title: "Homebrew distribution indexes", path: "docs/homebrew.md", plane: "use" },
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const EXACT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
2
|
+
const OFFICIAL_REF_RE = /^v(\d+)(?:\.\d+)?(-alpha)?$/;
|
|
3
|
+
const STABLE_RELEASE_RE = /^v(\d+)\.\d+\.\d+$/;
|
|
4
|
+
const ALPHA_RELEASE_RE = /^v(\d+)\.\d+\.\d+-alpha\.\d+$/;
|
|
5
|
+
const TRAIN_REF_RE = /^train\/v(\d+)\/v(\d+)\.\d+\/[A-Za-z0-9._/-]+$/;
|
|
6
|
+
const AUTHORITY_REF_RE = /^authority\/v(\d+)\/v(\d+)\.\d+\/[A-Za-z0-9._/-]+$/;
|
|
7
|
+
|
|
8
|
+
export const BUILDCHAIN_CHANNELS = new Set(["stable", "alpha"]);
|
|
9
|
+
|
|
10
|
+
export function normalizeBuildchainRef(ref = "") {
|
|
11
|
+
const value = String(ref || "").trim();
|
|
12
|
+
const workflowRef = value.includes("/.github/workflows/") ? value.split("@").pop() || "" : value;
|
|
13
|
+
return workflowRef.replace(/^refs\/(?:heads|tags)\//, "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function parseBuildchainRefIdentity(ref = "") {
|
|
17
|
+
const value = normalizeBuildchainRef(ref);
|
|
18
|
+
if (!value) return { ref: value, kind: "missing", channel: null, major: null };
|
|
19
|
+
if (EXACT_SHA_RE.test(value)) return { ref: value, kind: "exact-sha", channel: null, major: null };
|
|
20
|
+
|
|
21
|
+
for (const [pattern, channel, kind] of [
|
|
22
|
+
[OFFICIAL_REF_RE, null, "official-channel"],
|
|
23
|
+
[STABLE_RELEASE_RE, "stable", "release"],
|
|
24
|
+
[ALPHA_RELEASE_RE, "alpha", "release"],
|
|
25
|
+
]) {
|
|
26
|
+
const match = value.match(pattern);
|
|
27
|
+
if (match) {
|
|
28
|
+
return {
|
|
29
|
+
ref: value,
|
|
30
|
+
kind,
|
|
31
|
+
channel: channel || (match[2] ? "alpha" : "stable"),
|
|
32
|
+
major: Number(match[1]),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const [pattern, kind] of [
|
|
38
|
+
[TRAIN_REF_RE, "train"],
|
|
39
|
+
[AUTHORITY_REF_RE, "authority"],
|
|
40
|
+
]) {
|
|
41
|
+
const match = value.match(pattern);
|
|
42
|
+
if (match) {
|
|
43
|
+
return {
|
|
44
|
+
ref: value,
|
|
45
|
+
kind,
|
|
46
|
+
channel: null,
|
|
47
|
+
major: Number(match[1]),
|
|
48
|
+
coherentMajor: match[1] === match[2],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { ref: value, kind: "development", channel: null, major: null };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function expectedIdentity({ workflowShellRef, expectedChannel, expectedMajor }) {
|
|
56
|
+
const shell = parseBuildchainRefIdentity(workflowShellRef);
|
|
57
|
+
const channel =
|
|
58
|
+
String(expectedChannel || "")
|
|
59
|
+
.trim()
|
|
60
|
+
.toLowerCase() || shell.channel;
|
|
61
|
+
const majorText = String(expectedMajor ?? "").trim();
|
|
62
|
+
const major = majorText ? Number(majorText.replace(/^v/, "")) : shell.major;
|
|
63
|
+
return { shell, channel, major };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function expectedReasons(expected, { expectedChannel, expectedMajor }) {
|
|
67
|
+
const reasons = [];
|
|
68
|
+
if (!BUILDCHAIN_CHANNELS.has(expected.channel)) reasons.push("Buildchain channel is missing or ambiguous");
|
|
69
|
+
if (!Number.isInteger(expected.major) || expected.major < 1) reasons.push("Buildchain major is missing or ambiguous");
|
|
70
|
+
if (expected.shell.channel && expected.shell.channel !== expected.channel) {
|
|
71
|
+
reasons.push(`workflow shell ${expected.shell.ref} is ${expected.shell.channel}, expected ${expected.channel}`);
|
|
72
|
+
}
|
|
73
|
+
if (Number.isInteger(expected.shell.major) && expected.shell.major !== expected.major) {
|
|
74
|
+
reasons.push(`workflow shell ${expected.shell.ref} is v${expected.shell.major}, expected v${expected.major}`);
|
|
75
|
+
}
|
|
76
|
+
if (!expected.shell.channel && !expectedChannel) reasons.push(`workflow shell ${expected.shell.ref || "(missing)"} does not declare a stable or alpha channel`);
|
|
77
|
+
if (!Number.isInteger(expected.shell.major) && !expectedMajor) reasons.push(`workflow shell ${expected.shell.ref || "(missing)"} does not declare a Buildchain major`);
|
|
78
|
+
return reasons;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function runtimeReasons(expected, runtime, allowOpaqueRuntime) {
|
|
82
|
+
if (runtime.channel) {
|
|
83
|
+
return [runtime.channel !== expected.channel ? `runtime ${runtime.ref} is ${runtime.channel}, expected ${expected.channel}` : "", runtime.major !== expected.major ? `runtime ${runtime.ref} is v${runtime.major}, expected v${expected.major}` : ""].filter(Boolean);
|
|
84
|
+
}
|
|
85
|
+
if (new Set(["train", "authority", "exact-sha"]).has(runtime.kind) && allowOpaqueRuntime) {
|
|
86
|
+
return [runtime.coherentMajor === false ? `runtime ${runtime.ref} has inconsistent major segments` : "", Number.isInteger(runtime.major) && runtime.major !== expected.major ? `runtime ${runtime.ref} is v${runtime.major}, expected v${expected.major}` : ""].filter(Boolean);
|
|
87
|
+
}
|
|
88
|
+
return [`runtime ${runtime.ref || "(missing)"} does not prove the ${expected.channel || "selected"} channel`];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function lockReasons(expected, lock, lockMajor, lockMajorLine) {
|
|
92
|
+
if (!lock.ref) return ["Buildchain contract lock is required for a channel-bound run"];
|
|
93
|
+
return [lock.channel !== expected.channel ? `contract lock ref ${lock.ref} does not prove the ${expected.channel} channel` : "", lock.major !== expected.major ? `contract lock ref ${lock.ref} does not prove major v${expected.major}` : "", lockMajorLine && lockMajor.major !== expected.major ? `contract lock major line ${lockMajor.ref} does not match v${expected.major}` : ""].filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function evaluateBuildchainChannelBinding({ workflowShellRef = "", runtimeRef = "", lockRef = "", lockMajorLine = "", expectedChannel = "", expectedMajor = "", allowOpaqueRuntime = false } = {}) {
|
|
97
|
+
const expected = expectedIdentity({
|
|
98
|
+
workflowShellRef,
|
|
99
|
+
expectedChannel,
|
|
100
|
+
expectedMajor,
|
|
101
|
+
});
|
|
102
|
+
const runtime = parseBuildchainRefIdentity(runtimeRef);
|
|
103
|
+
const lock = parseBuildchainRefIdentity(lockRef);
|
|
104
|
+
const lockMajor = parseBuildchainRefIdentity(lockMajorLine);
|
|
105
|
+
const reasons = [...expectedReasons(expected, { expectedChannel, expectedMajor }), ...runtimeReasons(expected, runtime, allowOpaqueRuntime), ...lockReasons(expected, lock, lockMajor, lockMajorLine)];
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
ok: reasons.length === 0,
|
|
109
|
+
status: reasons.length === 0 ? "channel-bound" : "channel-binding-mismatch",
|
|
110
|
+
channel: expected.channel || null,
|
|
111
|
+
major: Number.isInteger(expected.major) ? expected.major : null,
|
|
112
|
+
shell: expected.shell,
|
|
113
|
+
runtime,
|
|
114
|
+
lock,
|
|
115
|
+
lockMajor,
|
|
116
|
+
reasons,
|
|
117
|
+
};
|
|
118
|
+
}
|