@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55

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 (159) hide show
  1. package/README.md +9 -6
  2. package/actions/release-tail/README.md +8 -0
  3. package/actions/v4-release-candidate-promote/README.md +3 -0
  4. package/architecture/agent-change-map.md +1015 -0
  5. package/architecture/ci-lane-change-budget.json +256 -0
  6. package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
  7. package/architecture/internal-capabilities.json +220 -15
  8. package/architecture/maintainability-debt.json +3229 -0
  9. package/architecture/maintainability-policy.json +489 -132
  10. package/architecture/release-tail-contract-inventory.json +5 -2
  11. package/architecture/v3-core-mechanism-inventory.json +4 -3
  12. package/architecture/v3-v4-capability-closure.md +59 -0
  13. package/architecture/v3-v4-live-capability-inventory.json +102404 -0
  14. package/architecture/v4-adopter-delivery-parity.json +32 -5
  15. package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
  16. package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
  17. package/architecture/v4-floating-consumer-policy.json +6 -0
  18. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  19. package/architecture/v4-runtime-semantic-closure.json +710 -0
  20. package/bin/buildchain.mjs +7 -1
  21. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  22. package/bin/internal/command-registry.mjs +1 -0
  23. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  24. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  25. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  26. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  28. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  29. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  30. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  31. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  32. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  33. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  38. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  39. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  40. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  41. package/contracts/v4-publication-qualification-v1.schema.json +51 -0
  42. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  43. package/dist/site/agent-index.json +4 -0
  44. package/dist/site/artifact-schemas.json +8 -0
  45. package/dist/site/buildchain-contract.json +125 -34
  46. package/dist/site/buildchain-site.json +308 -33
  47. package/dist/site/capability-registry.json +13 -12
  48. package/dist/site/cli-registry.json +57 -2
  49. package/dist/site/controller-registry.json +41 -5
  50. package/dist/site/kfd-claims.json +438 -24
  51. package/dist/site/kfd-upstream-aggregate.json +1 -1
  52. package/dist/site/manual-registry.json +22 -7
  53. package/dist/site/node-api-registry.json +12843 -9314
  54. package/dist/site/page-registry.json +288 -21
  55. package/dist/site/public-surface-audit.json +281 -21
  56. package/dist/site/publication-authority-registry.json +66 -8
  57. package/dist/site/publication-registry.json +4 -4
  58. package/dist/site/release-provenance.json +20 -0
  59. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  60. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  61. package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
  62. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  63. package/dist/site/site-manifest.json +19 -11
  64. package/dist/site/workflow-registry.json +195 -17
  65. package/docs/MAP.md +4 -2
  66. package/docs/cli-reference.md +70 -2
  67. package/docs/cli.md +8 -0
  68. package/docs/dev-delivery-warrant.md +34 -10
  69. package/docs/node-api-reference.md +524 -279
  70. package/docs/publish-transaction.md +11 -4
  71. package/docs/release-candidate.md +7 -0
  72. package/docs/release-tail-provider-plane.md +30 -0
  73. package/docs/v4-adopter-delivery.md +152 -0
  74. package/docs/v4-declarative-release-tail-migration.md +43 -0
  75. package/docs/v4-publication-rehearsal.md +130 -0
  76. package/package.json +24 -4
  77. package/packages/core/adopter-delivery-passport.js +295 -0
  78. package/packages/core/adopter-delivery-vectors.js +158 -0
  79. package/packages/core/adopter-delivery-vectors.json +77 -0
  80. package/packages/core/buildchain-agent-manuals.js +1 -0
  81. package/packages/core/buildchain-config.js +84 -0
  82. package/packages/core/buildchain-contract.js +31 -1
  83. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  84. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  85. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  86. package/packages/core/buildchain-publication-authority.js +2 -0
  87. package/packages/core/ci-lane-change-budget.js +247 -0
  88. package/packages/core/dev-delivery-native-proof.js +43 -27
  89. package/packages/core/dev-delivery-warrant-settlement.js +239 -3
  90. package/packages/core/dev-delivery-warrant-state.js +46 -10
  91. package/packages/core/dev-delivery-warrant.js +29 -7
  92. package/packages/core/github-governance-authority.js +5 -4
  93. package/packages/core/index.js +6 -0
  94. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  95. package/packages/core/paper-scaffold-content.js +401 -0
  96. package/packages/core/paper.js +12 -378
  97. package/packages/core/publication-control-plane-audit.js +135 -111
  98. package/packages/core/publication-rehearsal-projection.js +104 -0
  99. package/packages/core/publication-rehearsal-runtime.js +190 -0
  100. package/packages/core/published-delivery-authority.js +266 -0
  101. package/packages/core/release-candidate-recovery.js +71 -15
  102. package/packages/core/release-candidate.js +2 -2
  103. package/packages/core/release-passport.js +95 -170
  104. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  105. package/packages/core/v4-adopter-delivery.js +359 -0
  106. package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
  107. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  108. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  109. package/packages/core/v4-floating-consumer-policy.js +139 -35
  110. package/packages/core/v4-floating-consumer-release-passport.js +154 -8
  111. package/packages/core/v4-publication-qualification.js +222 -0
  112. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  113. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  114. package/packages/core/v4-publication-rehearsal.js +481 -0
  115. package/packages/core/workflow-yaml-contract.js +24 -2
  116. package/scripts/artifact-signing-controller-core.mjs +1 -1
  117. package/scripts/audit-publication-control-plane.mjs +17 -17
  118. package/scripts/buildchain-cli-help.mjs +13 -0
  119. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  120. package/scripts/check-internal-architecture.mjs +23 -3
  121. package/scripts/check-inventory.mjs +24 -6
  122. package/scripts/check-maintainability.mjs +145 -4
  123. package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
  124. package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
  125. package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
  126. package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
  127. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  128. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  129. package/scripts/dev-delivery-warrant-options.mjs +27 -11
  130. package/scripts/dev-delivery-warrant-store.mjs +10 -6
  131. package/scripts/dev-delivery-warrant.mjs +50 -6
  132. package/scripts/dev-pr-auto-merge.mjs +25 -11
  133. package/scripts/dev-pr-delivery-warrant.mjs +45 -0
  134. package/scripts/dev-pr-prequeue-guard.mjs +0 -18
  135. package/scripts/dev-qualification-patrol.mjs +9 -9
  136. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  137. package/scripts/generate-agent-change-map.mjs +118 -0
  138. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  139. package/scripts/generate-release-candidate-passport.mjs +177 -0
  140. package/scripts/generate-site-bundle.mjs +36 -0
  141. package/scripts/maintainability-domain-metrics.mjs +237 -0
  142. package/scripts/maintainability-governance.mjs +473 -0
  143. package/scripts/maintainability-metrics.mjs +28 -6
  144. package/scripts/project-cut-merge-queue-admission.mjs +394 -0
  145. package/scripts/promotion-channel-router.mjs +4 -4
  146. package/scripts/promotion-routing-evidence.mjs +35 -6
  147. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  148. package/scripts/release-candidate-resolver.mjs +44 -0
  149. package/scripts/release-line-policy.mjs +25 -15
  150. package/scripts/release-tail.mjs +68 -3
  151. package/scripts/resume-from-candidate-run.mjs +595 -65
  152. package/scripts/run-lifecycle-core.mjs +341 -376
  153. package/scripts/site-capability-metadata.mjs +21 -0
  154. package/scripts/stable-candidate-qualification.mjs +9 -10
  155. package/scripts/v3-v4-capability-catalog.mjs +439 -0
  156. package/scripts/v4-consumer-policy.mjs +4 -0
  157. package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
  158. package/scripts/v4-declarative-promotion-admission.mjs +42 -0
  159. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
@@ -0,0 +1,395 @@
1
+ import { initAdopterManifest } from "@kungfu-tech/kfd/adopter-conformance/toolchain";
2
+
3
+ import { adopterDeliveryGateDigest } from "./adopter-delivery-gate.js";
4
+ import { withPublishedBuildchainDeliveryAuthority } from "./published-delivery-authority.js";
5
+
6
+ export const BUILDCHAIN_DELIVERY_SELF_DOGFOOD_CONTRACT =
7
+ "kungfu-buildchain-delivery-infrastructure-self-dogfood/v1";
8
+
9
+ const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
10
+ const SHA_PATTERN = /^[0-9a-f]{40}$/;
11
+ const BUILDCHAIN_PACKAGE = "@kungfu-tech/buildchain";
12
+ const KFD_PACKAGE = "@kungfu-tech/kfd";
13
+ const PROFILE_REQUIREMENTS = Object.freeze([
14
+ ["adopter-identity", ["declaration"]],
15
+ ["artifact-readback", ["verification"]],
16
+ ["claim-boundary", ["declaration"]],
17
+ ["delivery-policy-cut", ["declaration", "verification"]],
18
+ ["kfd-cut", ["verification"]],
19
+ ["protected-delivery", ["implementation", "review", "verification"]],
20
+ ["source-artifact", ["implementation"]],
21
+ ]);
22
+
23
+ function exactPackage(value, { name, version = "", root = "", label }) {
24
+ if (
25
+ value?.name !== name ||
26
+ typeof value.version !== "string" ||
27
+ value.version.length === 0 ||
28
+ !ROOT_PATTERN.test(value.artifactRoot ?? "")
29
+ ) {
30
+ throw new TypeError(`${label} package identity is incomplete`);
31
+ }
32
+ if (version && value.version !== version) {
33
+ throw new TypeError(`${label} package version does not match its binding`);
34
+ }
35
+ if (root && value.artifactRoot !== root) {
36
+ throw new TypeError(`${label} package root does not match its binding`);
37
+ }
38
+ return structuredClone(value);
39
+ }
40
+
41
+ function authorityFunctions(authorityRuntime) {
42
+ const names = [
43
+ "createBuildchainDeliveryInfrastructureInstanceManifest",
44
+ "verifyBuildchainDeliveryInfrastructureInstance",
45
+ "createAdopterDeliveryGate",
46
+ "createPackageArtifactProfile",
47
+ "createKfdAdopterCategoryProtocolDriver",
48
+ "qualifyBuildchainDeliveryInfrastructureBootstrap",
49
+ ];
50
+ for (const name of names) {
51
+ if (typeof authorityRuntime?.[name] !== "function") {
52
+ throw new TypeError(`authority runtime is missing ${name}`);
53
+ }
54
+ }
55
+ return authorityRuntime;
56
+ }
57
+
58
+ function passed(value, label) {
59
+ if (value?.status !== "passed") {
60
+ throw new Error(
61
+ `${label} failed closed: ${JSON.stringify(value?.issues ?? [])}`,
62
+ );
63
+ }
64
+ return value;
65
+ }
66
+
67
+ function candidateWarrant(candidate) {
68
+ const terminal = candidate?.terminal;
69
+ if (
70
+ candidate?.status !== "merged" ||
71
+ !SHA_PATTERN.test(candidate?.sourceHead ?? "") ||
72
+ !ROOT_PATTERN.test(candidate?.sourceProofRoot ?? "") ||
73
+ terminal?.outcome !== "merged" ||
74
+ !ROOT_PATTERN.test(terminal?.nativeProofRoot ?? "") ||
75
+ !ROOT_PATTERN.test(terminal?.evidenceRoot ?? "")
76
+ ) {
77
+ throw new TypeError("candidate Warrant settlement is incomplete");
78
+ }
79
+ return {
80
+ outcome: "merged",
81
+ sourceHead: candidate.sourceHead,
82
+ sourceProofRoot: candidate.sourceProofRoot,
83
+ nativeProofRoot: terminal.nativeProofRoot,
84
+ integrationProofRoot: terminal.evidenceRoot,
85
+ };
86
+ }
87
+
88
+ function deliveryCut() {
89
+ const protocol = {
90
+ id: "kfd.adopter-category/instance-manifest",
91
+ version: "1.0.0",
92
+ };
93
+ protocol.root = adopterDeliveryGateDigest(protocol);
94
+ const profile = {
95
+ id: "kfd.adopter-category/delivery-infrastructure",
96
+ version: "1.0.0",
97
+ };
98
+ profile.root = adopterDeliveryGateDigest({
99
+ ...profile,
100
+ requirements: PROFILE_REQUIREMENTS,
101
+ });
102
+ return {
103
+ gateRoot: adopterDeliveryGateDigest({
104
+ protocol,
105
+ profile,
106
+ artifactProfile: {
107
+ id: "buildchain.artifact/package",
108
+ version: "1.0.0",
109
+ },
110
+ }),
111
+ protocol,
112
+ profile,
113
+ };
114
+ }
115
+
116
+ function candidateEvidence({
117
+ source,
118
+ artifact,
119
+ authority,
120
+ warrant,
121
+ kfdRoot,
122
+ observedAt,
123
+ }) {
124
+ const roots = {
125
+ "adopter-identity:declaration": source.root,
126
+ "artifact-readback:verification": artifact.root,
127
+ "claim-boundary:declaration": adopterDeliveryGateDigest({
128
+ qualifying: false,
129
+ selfCertified: false,
130
+ releaseAuthorized: false,
131
+ }),
132
+ "delivery-policy-cut:declaration": authority.gateRoot,
133
+ "delivery-policy-cut:verification": warrant.integrationProofRoot,
134
+ "kfd-cut:verification": kfdRoot,
135
+ "protected-delivery:implementation": artifact.root,
136
+ "protected-delivery:review": warrant.sourceProofRoot,
137
+ "protected-delivery:verification": warrant.nativeProofRoot,
138
+ "source-artifact:implementation": source.root,
139
+ };
140
+ return PROFILE_REQUIREMENTS.flatMap(([requirementId, kinds]) =>
141
+ kinds.map((kind) => ({
142
+ requirementId,
143
+ kind,
144
+ coordinate: `evidence://kungfu-systems/buildchain/${requirementId}/${kind}`,
145
+ root: roots[`${requirementId}:${kind}`],
146
+ observedAt,
147
+ })),
148
+ );
149
+ }
150
+
151
+ export function createBuildchainDeliveryInfrastructureSelfDogfood(
152
+ {
153
+ authorityPackage,
154
+ kfdPackage,
155
+ authority,
156
+ candidate,
157
+ instanceId,
158
+ adopterManifest,
159
+ source,
160
+ artifact,
161
+ release,
162
+ evidence = [],
163
+ warrant,
164
+ transitionEvidence,
165
+ verifiedAt,
166
+ maxAgeSeconds,
167
+ } = {},
168
+ authorityRuntime,
169
+ ) {
170
+ const runtime = authorityFunctions(authorityRuntime);
171
+ const boundAuthorityPackage = exactPackage(authorityPackage, {
172
+ name: BUILDCHAIN_PACKAGE,
173
+ version: authority?.version,
174
+ root: authority?.packageRoot,
175
+ label: "Buildchain authority",
176
+ });
177
+ const boundKfdPackage = exactPackage(kfdPackage, {
178
+ name: KFD_PACKAGE,
179
+ version: adopterManifest?.kfdCut?.package?.version,
180
+ root: adopterManifest?.kfdCut?.package?.artifactRoot,
181
+ label: "KFD authority",
182
+ });
183
+ if (candidate?.authorityVersion !== boundAuthorityPackage.version) {
184
+ throw new TypeError(
185
+ "candidate does not name the exact Buildchain N-1 authority",
186
+ );
187
+ }
188
+
189
+ const instanceManifest =
190
+ runtime.createBuildchainDeliveryInfrastructureInstanceManifest({
191
+ instanceId,
192
+ adopterManifest,
193
+ source,
194
+ artifact,
195
+ release,
196
+ evidence,
197
+ });
198
+ const instanceReport = runtime.verifyBuildchainDeliveryInfrastructureInstance(
199
+ instanceManifest,
200
+ {
201
+ adopterManifest,
202
+ verifiedAt,
203
+ maxAgeSeconds,
204
+ },
205
+ );
206
+ if (instanceReport?.valid !== true) {
207
+ throw new Error(
208
+ `delivery-infrastructure instance failed closed: ${JSON.stringify(instanceReport?.issues ?? [])}`,
209
+ );
210
+ }
211
+
212
+ const gateResult = passed(
213
+ runtime
214
+ .createAdopterDeliveryGate({
215
+ drivers: [runtime.createKfdAdopterCategoryProtocolDriver()],
216
+ artifactProfiles: [runtime.createPackageArtifactProfile()],
217
+ })
218
+ .evaluate(
219
+ {
220
+ schemaVersion: 1,
221
+ contract: "kungfu-buildchain-adopter-delivery-request",
222
+ protocol: {
223
+ id: "kfd.adopter-category/instance-manifest",
224
+ version: "1.0.0",
225
+ },
226
+ artifactProfile: {
227
+ id: "buildchain.artifact/package",
228
+ version: "1.0.0",
229
+ },
230
+ project: {
231
+ instanceId,
232
+ adopterId: "kungfu-systems/buildchain",
233
+ },
234
+ artifact: structuredClone(artifact),
235
+ declaration: instanceManifest,
236
+ },
237
+ { adopterManifest, verifiedAt, maxAgeSeconds },
238
+ ),
239
+ "adopter delivery gate",
240
+ );
241
+ const bootstrap = passed(
242
+ runtime.qualifyBuildchainDeliveryInfrastructureBootstrap({
243
+ authority,
244
+ candidate,
245
+ gateResult,
246
+ warrant,
247
+ transitionEvidence,
248
+ }),
249
+ "delivery-infrastructure bootstrap",
250
+ );
251
+
252
+ const result = {
253
+ schemaVersion: 1,
254
+ contract: BUILDCHAIN_DELIVERY_SELF_DOGFOOD_CONTRACT,
255
+ status: "passed",
256
+ authorityRuntime: {
257
+ buildchainPackage: boundAuthorityPackage,
258
+ kfdPackage: boundKfdPackage,
259
+ },
260
+ candidate: structuredClone(candidate),
261
+ roots: {
262
+ adopterManifest: adopterDeliveryGateDigest(adopterManifest),
263
+ instanceManifest: adopterDeliveryGateDigest(instanceManifest),
264
+ instanceReport: adopterDeliveryGateDigest(instanceReport),
265
+ gateResult: gateResult.gateRoot,
266
+ bootstrap: bootstrap.bootstrapRoot,
267
+ warrant: adopterDeliveryGateDigest(warrant),
268
+ },
269
+ instanceManifest,
270
+ instanceReport,
271
+ gateResult,
272
+ bootstrap,
273
+ qualifying: false,
274
+ selfCertified: false,
275
+ releaseAuthorized: false,
276
+ finalAuthority:
277
+ "protected-n-minus-one-runtime-plus-exact-delivery-evidence",
278
+ };
279
+ result.selfDogfoodRoot = adopterDeliveryGateDigest(result);
280
+ return result;
281
+ }
282
+
283
+ export async function createPublishedBuildchainDeliveryInfrastructureSelfDogfood({
284
+ authorityPackages,
285
+ selfDogfood,
286
+ } = {}) {
287
+ return withPublishedBuildchainDeliveryAuthority(
288
+ authorityPackages,
289
+ async ({ packages, authorityRuntime, authorityRoot }) => {
290
+ const result = createBuildchainDeliveryInfrastructureSelfDogfood(
291
+ {
292
+ ...structuredClone(selfDogfood ?? {}),
293
+ authorityPackage: packages.buildchain,
294
+ kfdPackage: packages.kfd,
295
+ },
296
+ authorityRuntime,
297
+ );
298
+ return {
299
+ ...result,
300
+ publishedAuthorityRoot: authorityRoot,
301
+ };
302
+ },
303
+ );
304
+ }
305
+
306
+ export async function createPublishedBuildchainDeliveryInfrastructureCandidateSelfDogfood({
307
+ authorityPackages,
308
+ authoritySourceCommit,
309
+ candidatePackage,
310
+ warrantCandidate,
311
+ source,
312
+ release,
313
+ verifiedAt,
314
+ maxAgeSeconds = 86400,
315
+ } = {}) {
316
+ if (!SHA_PATTERN.test(authoritySourceCommit ?? "")) {
317
+ throw new TypeError("authority source commit is invalid");
318
+ }
319
+ const artifact = exactPackage(candidatePackage, {
320
+ name: BUILDCHAIN_PACKAGE,
321
+ label: "Buildchain candidate",
322
+ });
323
+ if (
324
+ source?.coordinate !==
325
+ `kungfu-systems/buildchain@${warrantCandidate?.sourceHead ?? ""}` ||
326
+ !ROOT_PATTERN.test(source?.root ?? "") ||
327
+ !ROOT_PATTERN.test(release?.root ?? "")
328
+ ) {
329
+ throw new TypeError("candidate source or release binding is incomplete");
330
+ }
331
+ const warrant = candidateWarrant(warrantCandidate);
332
+ const cut = deliveryCut();
333
+ const authority = {
334
+ version: authorityPackages?.buildchain?.version,
335
+ sourceCommit: authoritySourceCommit,
336
+ packageRoot: authorityPackages?.buildchain?.artifactRoot,
337
+ ...cut,
338
+ protected: true,
339
+ published: true,
340
+ };
341
+ const candidate = {
342
+ version: artifact.version,
343
+ sourceCommit: warrant.sourceHead,
344
+ packageRoot: artifact.artifactRoot,
345
+ ...cut,
346
+ authorityVersion: authority.version,
347
+ };
348
+ const adopterManifest = initAdopterManifest({
349
+ manifestId: `buildchain-delivery-infrastructure-${artifact.version}`,
350
+ adopterId: "kungfu-systems/buildchain",
351
+ artifactKind: "package",
352
+ artifactCoordinate: `${artifact.name}@${artifact.version}`,
353
+ artifactRoot: artifact.artifactRoot,
354
+ scope: "Buildchain delivery infrastructure release candidate self-dogfood",
355
+ packageArtifactRoot: authorityPackages?.kfd?.artifactRoot,
356
+ verifiedAt,
357
+ maxAgeSeconds,
358
+ });
359
+ adopterManifest.releaseBindings.push({
360
+ id: `buildchain-${artifact.version}`,
361
+ artifact: {
362
+ kind: "package",
363
+ coordinate: `${artifact.name}@${artifact.version}`,
364
+ root: artifact.artifactRoot,
365
+ },
366
+ releasePassport: structuredClone(release),
367
+ kfdPackageRoot: authorityPackages.kfd.artifactRoot,
368
+ });
369
+ const evidence = candidateEvidence({
370
+ source,
371
+ artifact: adopterManifest.releaseBindings[0].artifact,
372
+ authority,
373
+ warrant,
374
+ kfdRoot: authorityPackages.kfd.artifactRoot,
375
+ observedAt: verifiedAt,
376
+ });
377
+ const result =
378
+ await createPublishedBuildchainDeliveryInfrastructureSelfDogfood({
379
+ authorityPackages,
380
+ selfDogfood: {
381
+ authority,
382
+ candidate,
383
+ instanceId: `kungfu-systems/buildchain@${artifact.version}`,
384
+ adopterManifest,
385
+ source,
386
+ artifact: adopterManifest.releaseBindings[0].artifact,
387
+ release,
388
+ evidence,
389
+ warrant,
390
+ verifiedAt,
391
+ maxAgeSeconds,
392
+ },
393
+ });
394
+ return { ...result, adopterManifest, deliveryEvidence: evidence };
395
+ }
@@ -29,6 +29,7 @@ const DESCRIPTORS = Object.freeze([
29
29
  [".github/workflows/buildchain-patrol-monthly.yml", "governance-write"],
30
30
  [".github/workflows/buildchain-patrol-weekly.yml", "governance-write"],
31
31
  [".github/workflows/buildchain-patrol.yml", "governance-write"],
32
+ [".github/workflows/buildchain-ref-promotion-recovery.yml", "governance-write"],
32
33
  [".github/workflows/buildchain-ref-promotion.yml", "governance-write"],
33
34
  [".github/workflows/buildchain-stable-candidate-patrol.yml", "governance-write"],
34
35
  [".github/workflows/dev-qualification-patrol.yml", "governance-write"],
@@ -57,6 +58,7 @@ const DESCRIPTORS = Object.freeze([
57
58
  [".github/workflows/release-tail.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "caller-bound", "caller-bound", "fixed"],
58
59
  [".github/workflows/release-verify.yml", "governance-write"],
59
60
  [".github/workflows/stable-candidate-patrol.yml", "governance-write"],
61
+ [".github/workflows/v4-declarative-release-tail-dogfood.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "none", "fixed", "fixed"],
60
62
  ]);
61
63
 
62
64
  export function buildchainPublicationAuthorityDescriptors() {
@@ -0,0 +1,247 @@
1
+ import crypto from "node:crypto";
2
+ import { parseWorkflowDocument } from "./workflow-yaml-contract.js";
3
+
4
+ export const CI_LANE_CHANGE_BUDGET_CONTRACT =
5
+ "buildchain.ci-lane-change-budget/v1";
6
+
7
+ const AUTHORITY_CLASSES = new Set([
8
+ "merge-critical-required",
9
+ "merge-critical-admission",
10
+ "post-merge-advisory",
11
+ "diagnostic",
12
+ "evidence",
13
+ "release",
14
+ ]);
15
+ const TRIGGER_CLASSES = new Set([
16
+ "merge-group",
17
+ "pull-request",
18
+ "push",
19
+ "scheduled",
20
+ "manual",
21
+ "reusable",
22
+ "release",
23
+ "mixed",
24
+ ]);
25
+ const CONCURRENCY_MODES = new Set([
26
+ "none",
27
+ "ref-scoped",
28
+ "workflow-scoped",
29
+ "serialized",
30
+ ]);
31
+ const CANCELLATION_BEHAVIORS = new Set([
32
+ "cancel-stale",
33
+ "finish-started",
34
+ "provider-default",
35
+ "not-applicable",
36
+ ]);
37
+
38
+ function canonical(value) {
39
+ if (Array.isArray(value)) return value.map(canonical);
40
+ if (value && typeof value === "object") {
41
+ return Object.fromEntries(
42
+ Object.keys(value)
43
+ .sort()
44
+ .map((key) => [key, canonical(value[key])]),
45
+ );
46
+ }
47
+ return value;
48
+ }
49
+
50
+ function root(value) {
51
+ return `sha256:${crypto
52
+ .createHash("sha256")
53
+ .update(JSON.stringify(canonical(value)))
54
+ .digest("hex")}`;
55
+ }
56
+
57
+ function triggerClass(triggers) {
58
+ const classes = new Set();
59
+ for (const trigger of triggers) {
60
+ if (trigger === "merge_group") classes.add("merge-group");
61
+ else if (trigger.startsWith("pull_request")) classes.add("pull-request");
62
+ else if (trigger === "push") classes.add("push");
63
+ else if (trigger === "schedule") classes.add("scheduled");
64
+ else if (trigger === "workflow_dispatch") classes.add("manual");
65
+ else if (trigger === "workflow_call") classes.add("reusable");
66
+ else if (trigger === "release") classes.add("release");
67
+ else classes.add("mixed");
68
+ }
69
+ return classes.size === 1 ? [...classes][0] : "mixed";
70
+ }
71
+
72
+ function diagnostic(code, laneId, message) {
73
+ return { code, laneId, message };
74
+ }
75
+
76
+ function validateDeclaration(declaration, observed) {
77
+ const laneId = String(declaration?.laneId || "");
78
+ const diagnostics = [];
79
+ if (!AUTHORITY_CLASSES.has(declaration?.authorityClass)) {
80
+ diagnostics.push(
81
+ diagnostic(
82
+ "invalid-authority-class",
83
+ laneId,
84
+ "authorityClass must identify the lane's merge or non-merge authority",
85
+ ),
86
+ );
87
+ }
88
+ if (!TRIGGER_CLASSES.has(declaration?.triggerClass)) {
89
+ diagnostics.push(
90
+ diagnostic(
91
+ "invalid-trigger-class",
92
+ laneId,
93
+ "triggerClass must identify the workflow trigger class",
94
+ ),
95
+ );
96
+ } else if (observed && declaration.triggerClass !== observed.triggerClass) {
97
+ diagnostics.push(
98
+ diagnostic(
99
+ "trigger-class-mismatch",
100
+ laneId,
101
+ `declared ${declaration.triggerClass}, observed ${observed.triggerClass}`,
102
+ ),
103
+ );
104
+ }
105
+ const concurrency = declaration?.concurrencyPolicy;
106
+ if (
107
+ !concurrency ||
108
+ !CONCURRENCY_MODES.has(concurrency.mode) ||
109
+ typeof concurrency.cancelInProgress !== "boolean"
110
+ ) {
111
+ diagnostics.push(
112
+ diagnostic(
113
+ "invalid-concurrency-policy",
114
+ laneId,
115
+ "concurrencyPolicy requires a known mode and boolean cancelInProgress",
116
+ ),
117
+ );
118
+ }
119
+ if (
120
+ !Number.isFinite(declaration?.expectedRunnerMinutes) ||
121
+ declaration.expectedRunnerMinutes <= 0
122
+ ) {
123
+ diagnostics.push(
124
+ diagnostic(
125
+ "invalid-runner-budget",
126
+ laneId,
127
+ "expectedRunnerMinutes must be greater than zero",
128
+ ),
129
+ );
130
+ }
131
+ if (!CANCELLATION_BEHAVIORS.has(declaration?.cancellationBehavior)) {
132
+ diagnostics.push(
133
+ diagnostic(
134
+ "invalid-cancellation-behavior",
135
+ laneId,
136
+ "cancellationBehavior must describe how stale or started work settles",
137
+ ),
138
+ );
139
+ }
140
+ const impact = declaration?.sloImpact;
141
+ if (
142
+ !impact ||
143
+ typeof impact.mergeCritical !== "boolean" ||
144
+ !String(impact.metric || "").trim() ||
145
+ !Number.isFinite(impact.expectedContributionSeconds) ||
146
+ impact.expectedContributionSeconds < 0 ||
147
+ !String(impact.rationale || "").trim()
148
+ ) {
149
+ diagnostics.push(
150
+ diagnostic(
151
+ "invalid-slo-impact",
152
+ laneId,
153
+ "sloImpact requires mergeCritical, metric, expectedContributionSeconds, and rationale",
154
+ ),
155
+ );
156
+ }
157
+ return diagnostics;
158
+ }
159
+
160
+ function collectLanes(workflows) {
161
+ const lanes = new Map();
162
+ for (const workflow of workflows) {
163
+ const workflowPath = String(workflow?.path || "").trim();
164
+ if (!workflowPath) throw new Error("workflow path is required");
165
+ const document = parseWorkflowDocument(workflow.text);
166
+ for (const job of document.jobs) {
167
+ const laneId = `${workflowPath}#${job.id}`;
168
+ if (lanes.has(laneId)) throw new Error(`duplicate CI lane: ${laneId}`);
169
+ lanes.set(laneId, {
170
+ laneId,
171
+ workflowPath,
172
+ jobId: job.id,
173
+ triggerClass: triggerClass(document.triggers),
174
+ });
175
+ }
176
+ }
177
+ return lanes;
178
+ }
179
+
180
+ export function evaluateCiLaneChangeBudget({
181
+ policy,
182
+ workflows,
183
+ baselineWorkflows = null,
184
+ } = {}) {
185
+ if (policy?.schema !== CI_LANE_CHANGE_BUDGET_CONTRACT) {
186
+ throw new Error(`expected ${CI_LANE_CHANGE_BUDGET_CONTRACT}`);
187
+ }
188
+ if (!Array.isArray(workflows)) throw new Error("workflows must be an array");
189
+ const observed = collectLanes(workflows);
190
+ const baselineLaneIds = baselineWorkflows
191
+ ? [...collectLanes(baselineWorkflows).keys()]
192
+ : policy.baseline?.lanes || [];
193
+ const baseline = new Set(baselineLaneIds);
194
+ if (baseline.size !== baselineLaneIds.length) {
195
+ throw new Error("baseline CI lanes must be unique");
196
+ }
197
+ const declarations = new Map();
198
+ const diagnostics = [];
199
+ for (const declaration of policy.declarations || []) {
200
+ const laneId = String(declaration?.laneId || "").trim();
201
+ if (!laneId) throw new Error("CI lane declaration laneId is required");
202
+ if (declarations.has(laneId))
203
+ throw new Error(`duplicate CI lane declaration: ${laneId}`);
204
+ declarations.set(laneId, declaration);
205
+ if (!observed.has(laneId)) {
206
+ diagnostics.push(
207
+ diagnostic(
208
+ "stale-declaration",
209
+ laneId,
210
+ "declared CI lane is absent from the current workflow cut",
211
+ ),
212
+ );
213
+ }
214
+ diagnostics.push(
215
+ ...validateDeclaration(declaration, observed.get(laneId) || null),
216
+ );
217
+ }
218
+ const newLanes = [...observed.keys()]
219
+ .filter((laneId) => !baseline.has(laneId))
220
+ .sort();
221
+ for (const laneId of newLanes) {
222
+ if (!declarations.has(laneId)) {
223
+ diagnostics.push(
224
+ diagnostic(
225
+ "undeclared-new-lane",
226
+ laneId,
227
+ "new CI lane must declare authority, trigger, concurrency, runner, cancellation, and SLO budgets",
228
+ ),
229
+ );
230
+ }
231
+ }
232
+ const result = {
233
+ schema: "buildchain.ci-lane-change-budget.evaluation/v1",
234
+ ok: diagnostics.length === 0,
235
+ observedLaneCount: observed.size,
236
+ baselineLaneCount: baseline.size,
237
+ newLaneCount: newLanes.length,
238
+ declaredLaneCount: declarations.size,
239
+ newLanes,
240
+ diagnostics: diagnostics.sort((left, right) =>
241
+ `${left.laneId}:${left.code}`.localeCompare(
242
+ `${right.laneId}:${right.code}`,
243
+ ),
244
+ ),
245
+ };
246
+ return { ...result, evaluationRoot: root(result) };
247
+ }