@kungfu-tech/buildchain 3.0.7 → 3.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/contracts/engineering-housekeeper-v1.schema.json +11 -1
  2. package/contracts/fixtures/engineering-housekeeper-v1/cases.json +10 -0
  3. package/contracts/release-cut-v1.schema.json +70 -0
  4. package/contracts/release-train-transition-v1.schema.json +85 -0
  5. package/contracts/release-train-v1.schema.json +82 -0
  6. package/dist/site/buildchain-contract.json +302 -16
  7. package/dist/site/buildchain-site.json +83 -27
  8. package/dist/site/capability-registry.json +4 -3
  9. package/dist/site/kfd-claims.json +45 -8
  10. package/dist/site/kfd-upstream-aggregate.json +1 -1
  11. package/dist/site/manual-registry.json +22 -6
  12. package/dist/site/node-api-registry.json +1218 -489
  13. package/dist/site/page-registry.json +65 -17
  14. package/dist/site/public-surface-audit.json +13 -8
  15. package/dist/site/publication-authority-registry.json +2 -4
  16. package/dist/site/publication-registry.json +4 -4
  17. package/dist/site/release-provenance.json +1 -0
  18. package/dist/site/site-manifest.json +18 -10
  19. package/dist/site/workflow-registry.json +10 -9
  20. package/docs/MAP.md +1 -1
  21. package/docs/aws-us-elastic-runner-burst-plane.md +20 -7
  22. package/docs/engineering-housekeeper.md +61 -28
  23. package/docs/node-api-reference.md +109 -77
  24. package/docs/release-governance.md +13 -10
  25. package/docs/release-train.md +102 -0
  26. package/docs/reusable-build-surface.md +16 -6
  27. package/docs/site-bundle-contract.md +5 -1
  28. package/docs/versioning.md +1 -0
  29. package/package.json +2 -1
  30. package/packages/core/buildchain-agent-manuals.js +1 -0
  31. package/packages/core/buildchain-compatibility-proof.js +577 -0
  32. package/packages/core/buildchain-contract.js +46 -181
  33. package/packages/core/engineering-housekeeper-github-client.js +18 -1
  34. package/packages/core/engineering-housekeeper-github.js +247 -41
  35. package/packages/core/engineering-housekeeper.js +15 -0
  36. package/packages/core/release-train.js +520 -0
  37. package/scripts/aws-macos-jit-controller-core.mjs +33 -11
  38. package/scripts/aws-macos-jit-controller-runtime.mjs +208 -0
  39. package/scripts/aws-macos-jit-controller.mjs +50 -37
  40. package/scripts/aws-macos-jit-core.mjs +41 -3
  41. package/scripts/buildchain-contract-lock.mjs +6 -0
  42. package/scripts/dispatch-artifact-signing-authority.mjs +4 -7
  43. package/scripts/engineering-housekeeper-workflow.mjs +13 -6
  44. package/scripts/generate-site-bundle.mjs +2 -0
  45. package/scripts/site-capability-metadata.mjs +1 -0
@@ -0,0 +1,577 @@
1
+ import {
2
+ devDeliveryClone as clone,
3
+ devDeliveryContentRoot as contentRoot,
4
+ devDeliveryExactRoot as exactRoot,
5
+ devDeliveryExactSha as exactSha,
6
+ devDeliveryProtectedBase as protectedBase,
7
+ devDeliveryRepository as repository,
8
+ devDeliveryText as text,
9
+ } from "./dev-delivery-common.js";
10
+
11
+ export const BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA = "kungfu.buildchain.compatibility-proof/v1";
12
+ export const BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA = "kungfu.buildchain.compatibility-proof-registry/v1";
13
+ export const BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA = "kungfu.buildchain.compatibility-verification-receipt/v1";
14
+
15
+ const BUILDCHAIN_CONTRACT = "kungfu-buildchain-runtime-contract-world";
16
+ const COMPATIBILITY_PREDICATE = "compatible-breaking-digest";
17
+ const COMPATIBILITY_OPERATION = "accept-contract-lock";
18
+ const COMPATIBILITY_DIRECTION = "source-to-target";
19
+ const CURRENT_PROOF_CUT = {
20
+ kind: "protected-git-cut",
21
+ repository: "kungfu-systems/buildchain",
22
+ protectedBase: "dev/v3/v3.0",
23
+ commit: "ff0f9006be1c8f63be40e67789e0cfd28fd69d46",
24
+ };
25
+ const CURRENT_AUTHORITY = {
26
+ kind: "protected-base-contract-authority",
27
+ repository: "kungfu-systems/buildchain",
28
+ protectedBase: "dev/v3/v3.0",
29
+ };
30
+
31
+ const HISTORICAL_ACCEPTANCES = [
32
+ {
33
+ proofId: "release-candidate-promote-acd401-to-e60642",
34
+ surfaceId: "release-candidate-promote",
35
+ surfaceKind: "workflow",
36
+ sourceBreakingDigest: "sha256:acd401cfc46450115a3763fd4b679d85f185e8757ebd52510d6262e1533df4cf",
37
+ targetBreakingDigest: "sha256:e60642aa298933d2c103284794921a6c340a50aa91ee528b06e58892a2a006bf",
38
+ evidence: {
39
+ kind: "protected-pull-request-contract-acceptance",
40
+ pullRequest: "https://github.com/kungfu-systems/buildchain/pull/2342",
41
+ protectedBase: "dev/v3/v3.0",
42
+ sourceCommit: "8870db287a9ee5fb95eebbbe907b1649d84d5b7f",
43
+ mergeCommit: "30b605adea1ed33dcf0bbb4cc7b666b64a6ccd18",
44
+ contractDigest: "sha256:9c07dbdb9aa8390569cd07ace044af45e6751b91ffe8fcc0cb69a77388b0f72b",
45
+ compatibilityDigest: "sha256:80cfdf1786019217c439810f1a80d2866bf8f7ef35a6fa569e1a687fb2bf4cbe",
46
+ },
47
+ },
48
+ {
49
+ proofId: "advanced-release-candidate-promote-aa30f2-to-9db8dd",
50
+ surfaceId: "advanced-release-candidate-promote",
51
+ surfaceKind: "workflow",
52
+ sourceBreakingDigest: "sha256:aa30f22e3af0a89841310bdbdc900844dd95a66974db173fa140a71bbd7e82c0",
53
+ targetBreakingDigest: "sha256:9db8dd07152855d03ff30f9c571438593c689d3e8139fa45431d000f548242f5",
54
+ evidence: {
55
+ kind: "protected-pull-request-contract-acceptance",
56
+ pullRequest: "https://github.com/kungfu-systems/buildchain/pull/2342",
57
+ protectedBase: "dev/v3/v3.0",
58
+ sourceCommit: "8870db287a9ee5fb95eebbbe907b1649d84d5b7f",
59
+ mergeCommit: "30b605adea1ed33dcf0bbb4cc7b666b64a6ccd18",
60
+ contractDigest: "sha256:9c07dbdb9aa8390569cd07ace044af45e6751b91ffe8fcc0cb69a77388b0f72b",
61
+ compatibilityDigest: "sha256:80cfdf1786019217c439810f1a80d2866bf8f7ef35a6fa569e1a687fb2bf4cbe",
62
+ },
63
+ },
64
+ {
65
+ proofId: "promote-buildchain-ref-action-a59f09-to-6147c0",
66
+ surfaceId: "promote-buildchain-ref-action",
67
+ surfaceKind: "action",
68
+ sourceBreakingDigest: "sha256:a59f0910e6df842e7699139472e5dd69ac2fdd7f7213bf2cb346d1d622556874",
69
+ targetBreakingDigest: "sha256:6147c0a8d5c36bfaa6021871574e1fc28192a3f1b8cfdae4d24ef3059ac1ebda",
70
+ evidence: {
71
+ kind: "protected-pull-request-contract-acceptance",
72
+ pullRequest: "https://github.com/kungfu-systems/buildchain/pull/1556",
73
+ protectedBase: "dev/v2/v2.14",
74
+ sourceCommit: "f8e2c5d28ef34d711892589331b7e018095a96d7",
75
+ mergeCommit: "6e0c29be1489f686a9a3792b46bfaa4466828393",
76
+ contractDigest: "sha256:f6dbf5147d9ad3b8c49ba6630cd52b8c7853ead7c921d7e1210a393aebf97d1f",
77
+ compatibilityDigest: "sha256:cdf588422ec6b460bd6f631400efa6e361a045e052c8967d6c50fadd7aa4dd90",
78
+ },
79
+ },
80
+ {
81
+ proofId: "build-lifecycle-e264a7-to-6b8d38",
82
+ surfaceId: "controller:build-lifecycle",
83
+ surfaceKind: "controller",
84
+ sourceBreakingDigest: "sha256:e264a79f9f399038c2fcfd21e4168c68c2e1485ee5c651c02242a02b622ac2be",
85
+ targetBreakingDigest: "sha256:6b8d38e92b2c67b5bc83fae0a3fd95bfa47582fbc176e6cdeebe7630a57534cb",
86
+ evidence: {
87
+ kind: "protected-pull-request-contract-acceptance",
88
+ pullRequest: "https://github.com/kungfu-systems/buildchain/pull/2058",
89
+ protectedBase: "dev/v3/v3.0",
90
+ sourceCommit: "c9a86bd1ac17b4fae54b0410498830edad80d831",
91
+ mergeCommit: "3991801a7a7d6fe5e31b9a670dfde7ea59e61253",
92
+ contractDigest: "sha256:da72850bbd3088f0e9eb20c17382557e0dfb7e68bf4469d352ed870ee8a334e8",
93
+ compatibilityDigest: "sha256:32a651504c57829085dc8863f0e41dee98eb60708ff6dec958c9c7e4b1010102",
94
+ },
95
+ },
96
+ {
97
+ proofId: "build-lifecycle-307459-to-6b8d38",
98
+ surfaceId: "controller:build-lifecycle",
99
+ surfaceKind: "controller",
100
+ sourceBreakingDigest: "sha256:30745921541e9b0f70475bb2178c2559f6aef248f6680670ccd44d8c5a69a6b1",
101
+ targetBreakingDigest: "sha256:6b8d38e92b2c67b5bc83fae0a3fd95bfa47582fbc176e6cdeebe7630a57534cb",
102
+ evidence: {
103
+ kind: "protected-pull-request-contract-acceptance",
104
+ pullRequest: "https://github.com/kungfu-systems/buildchain/pull/2058",
105
+ protectedBase: "dev/v3/v3.0",
106
+ sourceCommit: "c9a86bd1ac17b4fae54b0410498830edad80d831",
107
+ mergeCommit: "3991801a7a7d6fe5e31b9a670dfde7ea59e61253",
108
+ contractDigest: "sha256:da72850bbd3088f0e9eb20c17382557e0dfb7e68bf4469d352ed870ee8a334e8",
109
+ compatibilityDigest: "sha256:32a651504c57829085dc8863f0e41dee98eb60708ff6dec958c9c7e4b1010102",
110
+ },
111
+ },
112
+ ];
113
+
114
+ function exactText(value, label) {
115
+ const normalized = text(value);
116
+ if (!normalized) throw new Error(`${label} must not be empty`);
117
+ return normalized;
118
+ }
119
+
120
+ function majorLines(values) {
121
+ const normalized = [...new Set((values || []).map((value) => exactText(value, "majorLine")))].sort();
122
+ if (normalized.length === 0 || normalized.some((value) => !/^v\d+$/.test(value))) {
123
+ throw new Error("scope.majorLines must contain version-major identifiers");
124
+ }
125
+ return normalized;
126
+ }
127
+
128
+ function normalizeEvidence(value = {}) {
129
+ return {
130
+ kind: exactText(value.kind, "evidence.kind"),
131
+ pullRequest: exactText(value.pullRequest, "evidence.pullRequest"),
132
+ protectedBase: protectedBase(value.protectedBase),
133
+ sourceCommit: exactSha(value.sourceCommit, "evidence.sourceCommit"),
134
+ mergeCommit: exactSha(value.mergeCommit, "evidence.mergeCommit"),
135
+ contractDigest: exactRoot(value.contractDigest, "evidence.contractDigest"),
136
+ compatibilityDigest: exactRoot(value.compatibilityDigest, "evidence.compatibilityDigest"),
137
+ };
138
+ }
139
+
140
+ function normalizeAuthority(value = {}) {
141
+ return {
142
+ kind: exactText(value.kind, "authority.kind"),
143
+ repository: repository(value.repository),
144
+ protectedBase: protectedBase(value.protectedBase),
145
+ };
146
+ }
147
+
148
+ function normalizeCut(value = {}) {
149
+ return {
150
+ kind: exactText(value.kind, "cut.kind"),
151
+ repository: repository(value.repository),
152
+ protectedBase: protectedBase(value.protectedBase),
153
+ commit: exactSha(value.commit, "cut.commit"),
154
+ };
155
+ }
156
+
157
+ export function createBuildchainCompatibilityProof(input = {}) {
158
+ const surfaceId = exactText(input.surfaceId, "surfaceId");
159
+ const surfaceKind = exactText(input.surfaceKind, "surfaceKind");
160
+ const sourceBreakingDigest = exactRoot(input.sourceBreakingDigest, "sourceBreakingDigest");
161
+ const targetBreakingDigest = exactRoot(input.targetBreakingDigest, "targetBreakingDigest");
162
+ if (sourceBreakingDigest === targetBreakingDigest) {
163
+ throw new Error("compatibility proof source and target must differ");
164
+ }
165
+ const scope = {
166
+ contract: BUILDCHAIN_CONTRACT,
167
+ surfaceId,
168
+ surfaceKind,
169
+ majorLines: majorLines(input.scope?.majorLines || ["v2", "v3"]),
170
+ operation: COMPATIBILITY_OPERATION,
171
+ };
172
+ const evidence = normalizeEvidence(input.evidence);
173
+ const authority = normalizeAuthority(input.authority || CURRENT_AUTHORITY);
174
+ const cut = normalizeCut(input.cut || CURRENT_PROOF_CUT);
175
+ const body = {
176
+ schema: BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA,
177
+ proofId: exactText(input.proofId, "proofId"),
178
+ predicate: COMPATIBILITY_PREDICATE,
179
+ direction: COMPATIBILITY_DIRECTION,
180
+ operation: COMPATIBILITY_OPERATION,
181
+ source: {
182
+ contract: BUILDCHAIN_CONTRACT,
183
+ surfaceId,
184
+ surfaceKind,
185
+ breakingDigest: sourceBreakingDigest,
186
+ },
187
+ target: {
188
+ contract: BUILDCHAIN_CONTRACT,
189
+ surfaceId,
190
+ surfaceKind,
191
+ breakingDigest: targetBreakingDigest,
192
+ },
193
+ scope,
194
+ scopeRoot: contentRoot(scope),
195
+ evidence,
196
+ evidenceRoot: contentRoot(evidence),
197
+ authority,
198
+ authorityRoot: contentRoot(authority),
199
+ cut,
200
+ cutRoot: contentRoot(cut),
201
+ };
202
+ return { ...body, proofRoot: contentRoot(body) };
203
+ }
204
+
205
+ export function verifyBuildchainCompatibilityProof(proofInput, expected = {}) {
206
+ try {
207
+ const proof = clone(proofInput || {});
208
+ if (proof.schema !== BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA) return { ok: false, reason: "unsupported-schema" };
209
+ const normalized = createBuildchainCompatibilityProof({
210
+ ...proof,
211
+ proofId: proof.proofId,
212
+ surfaceId: proof.source?.surfaceId,
213
+ surfaceKind: proof.source?.surfaceKind,
214
+ sourceBreakingDigest: proof.source?.breakingDigest,
215
+ targetBreakingDigest: proof.target?.breakingDigest,
216
+ scope: proof.scope,
217
+ });
218
+ if (normalized.proofRoot !== proof.proofRoot) return { ok: false, reason: "proof-root-drift" };
219
+ if (JSON.stringify(normalized) !== JSON.stringify(proof)) return { ok: false, reason: "non-canonical-proof" };
220
+ for (const [field, value] of Object.entries(expected)) {
221
+ if (value !== undefined && normalized[field] !== value) return { ok: false, reason: `${field}-mismatch` };
222
+ }
223
+ return { ok: true, reason: "exact-compatibility-proof", proofRoot: normalized.proofRoot, proof: normalized };
224
+ } catch (error) {
225
+ return { ok: false, reason: "invalid-proof", error: error.message };
226
+ }
227
+ }
228
+
229
+ export function createHistoricalBuildchainCompatibilityProofs() {
230
+ return HISTORICAL_ACCEPTANCES.map((entry) => createBuildchainCompatibilityProof(entry)).sort((left, right) => left.proofRoot.localeCompare(right.proofRoot));
231
+ }
232
+
233
+ function projectionForSurface({ proofs, surface, majorLine }) {
234
+ const matching = proofs.filter((proof) => (
235
+ proof.target.surfaceId === surface.id
236
+ && proof.target.surfaceKind === surface.kind
237
+ && proof.target.breakingDigest === surface.breakingDigest
238
+ && proof.scope.majorLines.includes(majorLine)
239
+ && proof.operation === COMPATIBILITY_OPERATION
240
+ ));
241
+ const bySource = new Map();
242
+ for (const proof of matching) {
243
+ const sourceDigest = proof.source.breakingDigest;
244
+ if (bySource.has(sourceDigest)) throw new Error(`ambiguous compatibility proof for ${surface.id} ${sourceDigest}`);
245
+ bySource.set(sourceDigest, proof);
246
+ }
247
+ return [...bySource.values()]
248
+ .sort((left, right) => left.source.breakingDigest.localeCompare(right.source.breakingDigest))
249
+ .map((proof) => ({ breakingDigest: proof.source.breakingDigest, proofRoot: proof.proofRoot }));
250
+ }
251
+
252
+ export function createBuildchainCompatibilityProofRegistry({ proofs = createHistoricalBuildchainCompatibilityProofs(), surfaces = [], majorLine = "" } = {}) {
253
+ const verifiedProofs = proofs.map((proof) => {
254
+ const verification = verifyBuildchainCompatibilityProof(proof);
255
+ if (!verification.ok) throw new Error(`invalid compatibility proof: ${verification.reason}${verification.error ? `: ${verification.error}` : ""}`);
256
+ return verification.proof;
257
+ }).sort((left, right) => left.proofRoot.localeCompare(right.proofRoot));
258
+ const projections = Object.fromEntries(
259
+ [...surfaces]
260
+ .sort((left, right) => left.id.localeCompare(right.id))
261
+ .map((surface) => [surface.id, projectionForSurface({ proofs: verifiedProofs, surface, majorLine })])
262
+ .filter(([, entries]) => entries.length > 0),
263
+ );
264
+ const identity = {
265
+ schema: BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA,
266
+ proofRoots: verifiedProofs.map((proof) => proof.proofRoot),
267
+ };
268
+ return {
269
+ ...identity,
270
+ proofs: verifiedProofs,
271
+ projections,
272
+ registryRoot: contentRoot(identity),
273
+ };
274
+ }
275
+
276
+ export function assertBuildchainCompatibilityProjection(contractWorld) {
277
+ if (!Array.isArray(contractWorld?.compatibilityProofs)) return { ok: true, reason: "legacy-world-without-proofs" };
278
+ const registry = createBuildchainCompatibilityProofRegistry({
279
+ proofs: contractWorld.compatibilityProofs,
280
+ surfaces: contractWorld.surfaces || [],
281
+ majorLine: contractWorld.majorLine,
282
+ });
283
+ if (contractWorld.compatibilityProofRegistryRoot !== registry.registryRoot) {
284
+ throw new Error("compatibility proof registry root mismatch");
285
+ }
286
+ for (const surface of contractWorld.surfaces || []) {
287
+ const expected = registry.projections[surface.id] || [];
288
+ const expectedDigests = expected.map((entry) => entry.breakingDigest);
289
+ const expectedRoots = expected.map((entry) => entry.proofRoot);
290
+ const actualDigests = surface.compatibleBreakingDigests || [];
291
+ const actualRoots = surface.compatibilityProofRoots || [];
292
+ if (JSON.stringify(actualDigests) !== JSON.stringify(expectedDigests)) {
293
+ throw new Error(`compatibleBreakingDigests projection mismatch for ${surface.id}`);
294
+ }
295
+ if (JSON.stringify(actualRoots) !== JSON.stringify(expectedRoots)) {
296
+ throw new Error(`compatibilityProofRoots projection mismatch for ${surface.id}`);
297
+ }
298
+ }
299
+ return { ok: true, reason: "exact-proof-backed-projection", registryRoot: registry.registryRoot };
300
+ }
301
+
302
+ export function resolveBuildchainCompatibilityProof({ registry, surface, sourceBreakingDigest, majorLine } = {}) {
303
+ const candidates = (registry?.proofs || []).filter((proof) => (
304
+ proof.source.surfaceId === surface.id
305
+ && proof.source.surfaceKind === surface.kind
306
+ && proof.source.breakingDigest === sourceBreakingDigest
307
+ && proof.target.breakingDigest === surface.breakingDigest
308
+ && proof.scope.majorLines.includes(majorLine)
309
+ && proof.operation === COMPATIBILITY_OPERATION
310
+ ));
311
+ if (candidates.length === 0) return { ok: false, reason: "compatibility-proof-missing" };
312
+ if (candidates.length > 1) return { ok: false, reason: "ambiguous-compatibility-proof" };
313
+ const verification = verifyBuildchainCompatibilityProof(candidates[0]);
314
+ if (!verification.ok) return verification;
315
+ const projection = registry.projections?.[surface.id] || [];
316
+ if (!projection.some((entry) => entry.breakingDigest === sourceBreakingDigest && entry.proofRoot === verification.proofRoot)) {
317
+ return { ok: false, reason: "compatibility-proof-projection-missing" };
318
+ }
319
+ return { ok: true, reason: "proof-backed-compatible-relation", proof: verification.proof, proofRoot: verification.proofRoot };
320
+ }
321
+
322
+ export function createBuildchainCompatibilityVerificationReceipt(input = {}) {
323
+ const decisions = clone(input.decisions || []);
324
+ const usedProofRoots = [...new Set(decisions.map((decision) => decision.proofRoot).filter(Boolean).map((root) => exactRoot(root, "decision.proofRoot")))].sort();
325
+ const body = {
326
+ schema: BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA,
327
+ operation: COMPATIBILITY_OPERATION,
328
+ direction: COMPATIBILITY_DIRECTION,
329
+ status: exactText(input.status, "status"),
330
+ compatible: input.compatible === true,
331
+ policy: exactText(input.policy, "policy"),
332
+ scope: clone(input.scope || {}),
333
+ acceptedContractDigest: text(input.acceptedContractDigest),
334
+ acceptedCompatibilityDigest: text(input.acceptedCompatibilityDigest),
335
+ acceptedProofRegistryRoot: text(input.acceptedProofRegistryRoot),
336
+ currentContractDigest: text(input.currentContractDigest),
337
+ currentCompatibilityDigest: text(input.currentCompatibilityDigest),
338
+ currentProofRegistryRoot: text(input.currentProofRegistryRoot),
339
+ decisions,
340
+ usedProofRoots,
341
+ reasonCodes: [...new Set((input.reasonCodes || []).map((reason) => exactText(reason, "reasonCode")))].sort(),
342
+ };
343
+ return { ...body, receiptRoot: contentRoot(body) };
344
+ }
345
+
346
+ export function verifyBuildchainCompatibilityVerificationReceipt(receiptInput) {
347
+ try {
348
+ const receipt = clone(receiptInput || {});
349
+ if (receipt.schema !== BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA) return { ok: false, reason: "unsupported-schema" };
350
+ const normalized = createBuildchainCompatibilityVerificationReceipt(receipt);
351
+ if (normalized.receiptRoot !== receipt.receiptRoot) return { ok: false, reason: "receipt-root-drift" };
352
+ if (JSON.stringify(normalized) !== JSON.stringify(receipt)) return { ok: false, reason: "non-canonical-receipt" };
353
+ return { ok: true, reason: "exact-compatibility-verification-receipt", receiptRoot: receipt.receiptRoot };
354
+ } catch (error) {
355
+ return { ok: false, reason: "invalid-receipt", error: error.message };
356
+ }
357
+ }
358
+
359
+ export function contractSummary(contractWorld, runtimeRef = "", runtimeSha = "") {
360
+ return {
361
+ ref: runtimeRef,
362
+ resolvedSha: runtimeSha,
363
+ contract: contractWorld.contract,
364
+ contractDigest: contractWorld.contractDigest,
365
+ compatibilityDigest: contractWorld.compatibilityDigest,
366
+ compatibilityProofRegistryRoot: contractWorld.compatibilityProofRegistryRoot,
367
+ majorLine: contractWorld.majorLine,
368
+ surfaceCount: Array.isArray(contractWorld.surfaces) ? contractWorld.surfaces.length : 0,
369
+ };
370
+ }
371
+
372
+ function evaluationReceipt({ status, compatible, policy, accepted, current, decisions = [], reasons = [] }) {
373
+ return createBuildchainCompatibilityVerificationReceipt({
374
+ status,
375
+ compatible,
376
+ policy,
377
+ scope: { contract: current.contract, majorLine: current.majorLine },
378
+ acceptedContractDigest: accepted.contractDigest,
379
+ acceptedCompatibilityDigest: accepted.compatibilityDigest,
380
+ acceptedProofRegistryRoot: accepted.compatibilityProofRegistryRoot,
381
+ currentContractDigest: current.contractDigest,
382
+ currentCompatibilityDigest: current.compatibilityDigest,
383
+ currentProofRegistryRoot: current.compatibilityProofRegistryRoot,
384
+ decisions,
385
+ reasonCodes: reasons,
386
+ });
387
+ }
388
+
389
+ function contractPolicyReasons(accepted, current, policy) {
390
+ const reasons = [];
391
+ if (accepted.contract !== current.contract) reasons.push(`contract changed from ${accepted.contract || "(unknown)"} to ${current.contract}`);
392
+ if (accepted.majorLine && accepted.majorLine !== current.majorLine) reasons.push(`major line changed from ${accepted.majorLine} to ${current.majorLine}`);
393
+ if (policy === "exact" && accepted.contractDigest !== current.contractDigest) reasons.push("exact policy requires the contract digest to remain unchanged");
394
+ if (!["major-compatible", "allow-additive", "exact"].includes(policy)) reasons.push(`unsupported compatibility policy: ${policy}`);
395
+ return reasons;
396
+ }
397
+
398
+ function evaluateSurfaceRelation({ oldSurface, nextSurface, proofRegistry, majorLine }) {
399
+ if (!nextSurface) {
400
+ return {
401
+ reason: `surface removed: ${oldSurface.id}`,
402
+ decision: { surfaceId: oldSurface.id, status: "surface-removed", sourceBreakingDigest: oldSurface.breakingDigest, targetBreakingDigest: "", reason: "surface-removed" },
403
+ };
404
+ }
405
+ if (nextSurface.breakingDigest === oldSurface.breakingDigest) return {};
406
+ const resolution = resolveBuildchainCompatibilityProof({
407
+ registry: proofRegistry,
408
+ surface: nextSurface,
409
+ sourceBreakingDigest: oldSurface.breakingDigest,
410
+ majorLine,
411
+ });
412
+ if (!resolution.ok) {
413
+ return {
414
+ reason: `surface breaking digest changed without a valid compatibility proof: ${oldSurface.id} (${resolution.reason})`,
415
+ decision: { surfaceId: oldSurface.id, status: "rejected", sourceBreakingDigest: oldSurface.breakingDigest, targetBreakingDigest: nextSurface.breakingDigest, reason: resolution.reason },
416
+ };
417
+ }
418
+ const proof = resolution.proof;
419
+ return {
420
+ decision: {
421
+ surfaceId: oldSurface.id,
422
+ status: "accepted",
423
+ sourceBreakingDigest: oldSurface.breakingDigest,
424
+ targetBreakingDigest: nextSurface.breakingDigest,
425
+ proofRoot: proof.proofRoot,
426
+ direction: proof.direction,
427
+ operation: proof.operation,
428
+ scope: proof.scope,
429
+ scopeRoot: proof.scopeRoot,
430
+ evidence: proof.evidence,
431
+ evidenceRoot: proof.evidenceRoot,
432
+ authority: proof.authority,
433
+ authorityRoot: proof.authorityRoot,
434
+ cut: proof.cut,
435
+ cutRoot: proof.cutRoot,
436
+ },
437
+ };
438
+ }
439
+
440
+ export function evaluateBuildchainContractLock({
441
+ lock,
442
+ current,
443
+ runtimeRef = "",
444
+ runtimeSha = "",
445
+ runtimeClass = "",
446
+ compatibilityPolicy = "",
447
+ } = {}) {
448
+ if (!current || current.contract !== BUILDCHAIN_CONTRACT) {
449
+ throw new Error("current must be a Buildchain runtime contract world");
450
+ }
451
+ assertBuildchainCompatibilityProjection(current);
452
+ if (!new Set(["stable", "alpha"]).has(runtimeClass)) {
453
+ return {
454
+ ok: true,
455
+ status: "non-floating-runtime",
456
+ drift: false,
457
+ compatible: true,
458
+ issueRecommended: false,
459
+ reason: `runtime class ${runtimeClass || "unknown"} is not a stable floating ref`,
460
+ };
461
+ }
462
+ if (!lock) {
463
+ return {
464
+ ok: true,
465
+ status: "missing-lock",
466
+ drift: false,
467
+ compatible: true,
468
+ issueRecommended: false,
469
+ reason: "consumer repository has no Buildchain contract lock",
470
+ };
471
+ }
472
+ const accepted = lock.buildchain || {};
473
+ const policy = compatibilityPolicy || accepted.compatibilityPolicy || "major-compatible";
474
+ const shaDrift = !!accepted.resolvedSha && !!runtimeSha && accepted.resolvedSha !== runtimeSha;
475
+ const contractDrift = !!accepted.contractDigest && accepted.contractDigest !== current.contractDigest;
476
+ if (!shaDrift && !contractDrift) {
477
+ const verificationReceipt = evaluationReceipt({ status: "unchanged", compatible: true, policy, accepted, current });
478
+ return {
479
+ ok: true,
480
+ status: "unchanged",
481
+ drift: false,
482
+ compatible: true,
483
+ issueRecommended: false,
484
+ policy,
485
+ accepted,
486
+ current: contractSummary(current, runtimeRef, runtimeSha),
487
+ compatibilityDecisions: [],
488
+ usedProofRoots: [],
489
+ verificationReceipt,
490
+ receiptRoot: verificationReceipt.receiptRoot,
491
+ };
492
+ }
493
+ const reasons = contractPolicyReasons(accepted, current, policy);
494
+ const compatibilityDecisions = [];
495
+ const currentSurfaces = new Map(current.surfaces.map((entry) => [entry.id, entry]));
496
+ const proofRegistry = createBuildchainCompatibilityProofRegistry({
497
+ proofs: current.compatibilityProofs,
498
+ surfaces: current.surfaces,
499
+ majorLine: current.majorLine,
500
+ });
501
+ for (const oldSurface of accepted.surfaces || []) {
502
+ const result = evaluateSurfaceRelation({
503
+ oldSurface,
504
+ nextSurface: currentSurfaces.get(oldSurface.id),
505
+ proofRegistry,
506
+ majorLine: current.majorLine,
507
+ });
508
+ if (result.reason) reasons.push(result.reason);
509
+ if (result.decision) compatibilityDecisions.push(result.decision);
510
+ }
511
+ const compatible = reasons.length === 0;
512
+ const status = compatible ? "compatible-drift" : "breaking-drift";
513
+ const verificationReceipt = evaluationReceipt({ status, compatible, policy, accepted, current, decisions: compatibilityDecisions, reasons });
514
+ return {
515
+ ok: compatible,
516
+ status,
517
+ drift: shaDrift || contractDrift,
518
+ shaDrift,
519
+ contractDrift,
520
+ compatible,
521
+ issueRecommended: true,
522
+ policy,
523
+ reasons,
524
+ accepted,
525
+ current: contractSummary(current, runtimeRef, runtimeSha),
526
+ compatibilityDecisions,
527
+ usedProofRoots: verificationReceipt.usedProofRoots,
528
+ verificationReceipt,
529
+ receiptRoot: verificationReceipt.receiptRoot,
530
+ };
531
+ }
532
+
533
+ function shown(value, fallback = "(unknown)") {
534
+ return value ? value : fallback;
535
+ }
536
+
537
+ function renderCompatibilityDecision(decision) {
538
+ const lines = [
539
+ `- Surface \`${decision.surfaceId}\`: ${decision.status}`,
540
+ ` - Relation: \`${shown(decision.sourceBreakingDigest, "(missing)")}\` -> \`${shown(decision.targetBreakingDigest, "(missing)")}\``,
541
+ ` - Reason: \`${shown(decision.reason, "proof-backed-compatible-relation")}\``,
542
+ ];
543
+ if (!decision.proofRoot) return lines.join("\n");
544
+ lines.push(
545
+ ` - Proof root: \`${decision.proofRoot}\``,
546
+ ` - Direction / operation: \`${decision.direction}\` / \`${decision.operation}\``,
547
+ ` - Scope root: \`${decision.scopeRoot}\``,
548
+ ` - Evidence: ${shown(decision.evidence && decision.evidence.pullRequest)} (\`${decision.evidenceRoot}\`)`,
549
+ ` - Authority: \`${shown(decision.authority && decision.authority.repository)}@${shown(decision.authority && decision.authority.protectedBase)}\` (\`${decision.authorityRoot}\`)`,
550
+ ` - Cut: \`${shown(decision.cut && decision.cut.commit)}\` (\`${decision.cutRoot}\`)`,
551
+ );
552
+ return lines.join("\n");
553
+ }
554
+
555
+ function renderCompatibilityDecisions(decisions) {
556
+ if (decisions.length === 0) return "- No breaking-digest relation required proof resolution.";
557
+ return decisions.map(renderCompatibilityDecision).join("\n");
558
+ }
559
+
560
+ export function renderBuildchainContractDriftIssueBody({ repository = "", workflow = "", runUrl = "", lockPath = "", evaluation } = {}) {
561
+ const accepted = evaluation.accepted || {};
562
+ const current = evaluation.current || {};
563
+ const decisions = evaluation.compatibilityDecisions || [];
564
+ const renderedDecisions = renderCompatibilityDecisions(decisions);
565
+ return [
566
+ "# Buildchain contract drift", "", "## Summary", "",
567
+ `Buildchain detected ${evaluation.compatible ? "compatible" : "breaking"} contract drift for a floating runtime ref before expensive Buildchain work continued.`,
568
+ "", "## Consumer", "", `- Repository: ${shown(repository)}`, `- Workflow: ${shown(workflow)}`, `- Run: ${shown(runUrl)}`, `- Lock path: ${shown(lockPath)}`,
569
+ "", "## Accepted Buildchain contract", "", `- Ref: ${shown(accepted.ref)}`, `- SHA: ${shown(accepted.resolvedSha)}`, `- Contract digest: ${shown(accepted.contractDigest)}`, `- Compatibility digest: ${shown(accepted.compatibilityDigest)}`, `- Compatibility proof registry: ${shown(accepted.compatibilityProofRegistryRoot, "(legacy lock without proof registry)")}`, `- Policy: ${shown(evaluation.policy || accepted.compatibilityPolicy)}`,
570
+ "", "## Current Buildchain contract", "", `- Ref: ${shown(current.ref)}`, `- SHA: ${shown(current.resolvedSha)}`, `- Contract digest: ${shown(current.contractDigest)}`, `- Compatibility digest: ${shown(current.compatibilityDigest)}`, `- Compatibility proof registry: ${shown(current.compatibilityProofRegistryRoot)}`, `- Major line: ${shown(current.majorLine)}`,
571
+ "", "## Compatibility", "", `- Status: ${shown(evaluation.status)}`, `- Compatible: ${evaluation.compatible ? "yes" : "no"}`, evaluation.reasons && evaluation.reasons.length ? evaluation.reasons.map((reason) => `- ${reason}`).join("\n") : "- No breaking drift detected.",
572
+ "", "## Proof-backed decisions", "", renderedDecisions, `- Verification receipt: \`${shown(evaluation.receiptRoot, "(not produced)")}\``,
573
+ "", "## Suggested next action", "", evaluation.compatible
574
+ ? "Review the Buildchain release notes, then update the consumer contract lock to the current SHA and contract digest."
575
+ : "Failing before heavy build is intentional. Review the Buildchain contract change, update the consumer workflow/configuration, or pin the previous Buildchain SHA.",
576
+ ].join("\n");
577
+ }