@kungfu-tech/buildchain 4.0.2-alpha.0 → 4.0.2-alpha.4

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 (59) hide show
  1. package/architecture/agent-change-map.md +7 -4
  2. package/architecture/ci-lane-change-budget.json +76 -8
  3. package/architecture/internal-capabilities.json +2 -0
  4. package/architecture/maintainability-debt.json +52 -124
  5. package/architecture/maintainability-policy.json +15 -10
  6. package/architecture/release-tail-contract-inventory.json +4 -4
  7. package/architecture/v3-core-mechanism-inventory.json +1 -0
  8. package/architecture/v3-v4-live-capability-inventory.json +17 -17
  9. package/architecture/v4-delivery-warrant-shadow-fixtures.json +5 -5
  10. package/architecture/v4-release-invocation-fixtures.json +139 -0
  11. package/architecture/v4-release-topology.json +243 -0
  12. package/architecture/v4-runtime-semantic-closure.json +4 -1
  13. package/contracts/buildchain-v2-residuals-v1.json +0 -9
  14. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  15. package/contracts/v4-release-invocation-v1.schema.json +134 -0
  16. package/dist/site/buildchain-contract.json +9 -9
  17. package/dist/site/buildchain-site.json +7 -7
  18. package/dist/site/kfd-claims.json +24 -5
  19. package/dist/site/kfd-upstream-aggregate.json +1 -1
  20. package/dist/site/manual-registry.json +1 -1
  21. package/dist/site/node-api-registry.json +90 -15
  22. package/dist/site/page-registry.json +2 -2
  23. package/dist/site/public-surface-audit.json +34 -9
  24. package/dist/site/publication-authority-registry.json +2 -3
  25. package/dist/site/publication-registry.json +4 -4
  26. package/dist/site/site-manifest.json +5 -5
  27. package/dist/site/workflow-registry.json +36 -11
  28. package/docs/node-api-reference.md +16 -13
  29. package/package.json +2 -2
  30. package/packages/core/dev-delivery-candidate-identity.js +45 -17
  31. package/packages/core/dev-delivery-execution-transfer.js +6 -7
  32. package/packages/core/dev-delivery-provider-heartbeat.js +22 -6
  33. package/packages/core/dev-delivery-warrant-legacy-recovery.js +4 -2
  34. package/packages/core/dev-delivery-warrant-native-compatibility.js +33 -0
  35. package/packages/core/dev-delivery-warrant-state.js +54 -54
  36. package/packages/core/dev-delivery-warrant.js +8 -0
  37. package/packages/core/dev-delivery-writer-protocol-transition.js +72 -0
  38. package/packages/core/v4-canonical-contracts.js +10 -0
  39. package/packages/core/v4-floating-consumer-policy.js +4 -1
  40. package/packages/core/v4-protected-publication-source.js +93 -0
  41. package/packages/core/v4-publication-qualification.js +1 -0
  42. package/packages/core/v4-release-invocation.js +425 -0
  43. package/scripts/audit-publication-control-plane.mjs +0 -1
  44. package/scripts/check-inventory.mjs +27 -59
  45. package/scripts/check-maintainability.mjs +13 -5
  46. package/scripts/check-v3-v4-capability-inventory.mjs +3 -61
  47. package/scripts/check-v4-floating-consumer-policy-contract.mjs +10 -20
  48. package/scripts/check-v4-release-topology.mjs +383 -0
  49. package/scripts/dev-delivery-warrant.mjs +26 -5
  50. package/scripts/generate-channel-promotion-workflow.mjs +14 -55
  51. package/scripts/release-candidate-resolver.mjs +17 -17
  52. package/scripts/resume-from-candidate-run.mjs +15 -16
  53. package/scripts/v3-v4-capability-catalog.mjs +107 -0
  54. package/scripts/v4-declarative-promotion-admission.mjs +4 -1
  55. package/scripts/v4-release-candidate-adapter.mjs +41 -0
  56. package/scripts/capture-package-release-propagation.mjs +0 -263
  57. package/scripts/publication-commit-evidence.mjs +0 -444
  58. package/scripts/publish-github-artifact-attestation-evidence.mjs +0 -201
  59. package/scripts/stage-github-artifact-attestation-inputs.mjs +0 -65
@@ -0,0 +1,425 @@
1
+ import {
2
+ V4ContractFault,
3
+ v4CanonicalBytes,
4
+ v4ContentRoot,
5
+ validateV4Root,
6
+ } from "./v4-canonical-contracts.js";
7
+
8
+ export const V4_RELEASE_INVOCATION_CONTRACT =
9
+ "kungfu-buildchain-v4-release-invocation/v1";
10
+ export const V4_RELEASE_INVOCATION_ADAPTER_CONTRACT =
11
+ "kungfu-buildchain-v4-release-invocation-adapter/v1";
12
+ export const V4_RELEASE_TRANSACTION_CONTRACT =
13
+ "kungfu-buildchain-v4-release-transaction/v1";
14
+ export const V4_RELEASE_RECEIPT_CONTRACT =
15
+ "kungfu-buildchain-v4-release-receipt/v1";
16
+ export const V4_RELEASE_PROVIDER_CONTRACT =
17
+ "kungfu-buildchain-release-tail-provider/v1";
18
+
19
+ const SHA_PATTERN = /^[0-9a-f]{40}$/u;
20
+ const TAG_PATTERN = /^v[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/u;
21
+ const ROUTE_SURFACES = new Set([
22
+ "alpha",
23
+ "stable",
24
+ "public",
25
+ "private",
26
+ "declarative",
27
+ "legacy-compatible",
28
+ ]);
29
+ const EXECUTION_MODES = new Set(["fresh", "resume"]);
30
+ const COMPARISON_STATES = new Set(["identical", "ahead", "behind", "diverged"]);
31
+
32
+ function fault(code, path, message) {
33
+ throw new V4ContractFault(code, path, message);
34
+ }
35
+
36
+ function exactKeys(value, keys, path) {
37
+ if (!value || typeof value !== "object" || Array.isArray(value))
38
+ fault("invalid-release-invocation", path, `${path} must be an object`);
39
+ const actual = Object.keys(value).sort();
40
+ const expected = [...keys].sort();
41
+ if (
42
+ actual.length !== expected.length ||
43
+ actual.some((key, index) => key !== expected[index])
44
+ )
45
+ fault(
46
+ "invalid-release-invocation-shape",
47
+ path,
48
+ `${path} keys are not canonical`,
49
+ );
50
+ }
51
+
52
+ function sha(value, path, nullable = false) {
53
+ if (nullable && value === null) return value;
54
+ if (typeof value !== "string" || !SHA_PATTERN.test(value))
55
+ fault("invalid-release-sha", path, `${path} must be an exact Git SHA`);
56
+ return value;
57
+ }
58
+
59
+ function text(value, path) {
60
+ if (
61
+ typeof value !== "string" ||
62
+ value.length === 0 ||
63
+ /[^\x20-\x7e]/u.test(value)
64
+ )
65
+ fault("invalid-release-text", path, `${path} must be printable ASCII`);
66
+ return value;
67
+ }
68
+
69
+ function validatePublisher(value) {
70
+ exactKeys(
71
+ value,
72
+ ["repository", "workflow", "workflowSha", "job"],
73
+ "$/publisher",
74
+ );
75
+ if (
76
+ value.repository !== "kungfu-systems/buildchain" ||
77
+ value.workflow !== ".github/workflows/.release-candidate-promote.yml" ||
78
+ value.job !== "apply"
79
+ )
80
+ fault(
81
+ "invalid-publisher-identity",
82
+ "$/publisher",
83
+ "publisher identity is not the canonical v4 APPLY job",
84
+ );
85
+ sha(value.workflowSha, "$/publisher/workflowSha");
86
+ }
87
+
88
+ function validateRuntime(value) {
89
+ exactKeys(value, ["repository", "commit", "tree"], "$/runtime");
90
+ if (value.repository !== "kungfu-systems/buildchain")
91
+ fault(
92
+ "invalid-runtime-identity",
93
+ "$/runtime/repository",
94
+ "runtime repository is not canonical",
95
+ );
96
+ sha(value.commit, "$/runtime/commit");
97
+ sha(value.tree, "$/runtime/tree");
98
+ }
99
+
100
+ function validateCandidate(value) {
101
+ exactKeys(value, ["repository", "commit", "tree", "version"], "$/candidate");
102
+ text(value.repository, "$/candidate/repository");
103
+ sha(value.commit, "$/candidate/commit");
104
+ sha(value.tree, "$/candidate/tree");
105
+ text(value.version, "$/candidate/version");
106
+ }
107
+
108
+ function validateTarget(value) {
109
+ exactKeys(value, ["channel", "tag", "expectedOldSha"], "$/target");
110
+ if (
111
+ !["alpha", "stable"].includes(value.channel) ||
112
+ !TAG_PATTERN.test(value.tag)
113
+ )
114
+ fault(
115
+ "invalid-release-target",
116
+ "$/target",
117
+ "release channel and exact tag are not canonical",
118
+ );
119
+ sha(value.expectedOldSha, "$/target/expectedOldSha", true);
120
+ }
121
+
122
+ function validateAuthority(value) {
123
+ exactKeys(
124
+ value,
125
+ ["policyRoot", "qualificationRoot", "warrantRoot"],
126
+ "$/authority",
127
+ );
128
+ for (const name of ["policyRoot", "qualificationRoot", "warrantRoot"])
129
+ validateV4Root(value[name], `$/authority/${name}`);
130
+ }
131
+
132
+ function validateProvider(value, candidate) {
133
+ exactKeys(value, ["adapter", "contract", "repository"], "$/provider");
134
+ if (
135
+ value.adapter !== "built-in-provider-plane" ||
136
+ value.contract !== V4_RELEASE_PROVIDER_CONTRACT ||
137
+ value.repository !== candidate.repository
138
+ )
139
+ fault(
140
+ "invalid-release-provider",
141
+ "$/provider",
142
+ "provider identity must bind the built-in Provider Plane and candidate repository",
143
+ );
144
+ }
145
+
146
+ function validateParent(value) {
147
+ exactKeys(
148
+ value,
149
+ ["invocationRoot", "transactionRoot", "receiptRoot"],
150
+ "$/parent",
151
+ );
152
+ const roots = [
153
+ value.invocationRoot,
154
+ value.transactionRoot,
155
+ value.receiptRoot,
156
+ ];
157
+ if (roots.every((root) => root === null)) return;
158
+ if (roots.some((root) => root === null))
159
+ fault(
160
+ "invalid-release-parent-lineage",
161
+ "$/parent",
162
+ "parent lineage roots must be either all null or all present",
163
+ );
164
+ roots.forEach((root, index) =>
165
+ validateV4Root(
166
+ root,
167
+ `$/parent/${["invocationRoot", "transactionRoot", "receiptRoot"][index]}`,
168
+ ),
169
+ );
170
+ }
171
+
172
+ export function createV4ReleaseInvocation(value) {
173
+ exactKeys(
174
+ value,
175
+ [
176
+ "schema",
177
+ "publisher",
178
+ "runtime",
179
+ "candidate",
180
+ "target",
181
+ "authority",
182
+ "provider",
183
+ "parent",
184
+ ],
185
+ "$",
186
+ );
187
+ if (value.schema !== V4_RELEASE_INVOCATION_CONTRACT)
188
+ fault(
189
+ "invalid-release-invocation",
190
+ "$/schema",
191
+ "unsupported invocation schema",
192
+ );
193
+ validatePublisher(value.publisher);
194
+ validateRuntime(value.runtime);
195
+ validateCandidate(value.candidate);
196
+ validateTarget(value.target);
197
+ validateAuthority(value.authority);
198
+ validateProvider(value.provider, value.candidate);
199
+ validateParent(value.parent);
200
+ v4CanonicalBytes(value);
201
+ const roots = {
202
+ publisherRoot: v4ContentRoot(
203
+ "release-invocation-publisher",
204
+ value.publisher,
205
+ ),
206
+ runtimeRoot: v4ContentRoot("release-invocation-runtime", value.runtime),
207
+ candidateRoot: v4ContentRoot(
208
+ "release-invocation-candidate",
209
+ value.candidate,
210
+ ),
211
+ targetRoot: v4ContentRoot("release-invocation-target", value.target),
212
+ authorityRoot: v4ContentRoot(
213
+ "release-invocation-authority",
214
+ value.authority,
215
+ ),
216
+ providerRoot: v4ContentRoot("release-invocation-provider", value.provider),
217
+ parentRoot: v4ContentRoot("release-invocation-parent", value.parent),
218
+ };
219
+ const invocationRoot = v4ContentRoot("release-invocation", {
220
+ schema: V4_RELEASE_INVOCATION_CONTRACT,
221
+ ...roots,
222
+ });
223
+ return { invocation: value, roots: { ...roots, invocationRoot } };
224
+ }
225
+
226
+ export function adaptV4ReleaseInvocation(value) {
227
+ exactKeys(value, ["schema", "route", "invocation"], "$adapter");
228
+ if (value.schema !== V4_RELEASE_INVOCATION_ADAPTER_CONTRACT)
229
+ fault(
230
+ "invalid-release-adapter",
231
+ "$adapter/schema",
232
+ "unsupported adapter schema",
233
+ );
234
+ exactKeys(value.route, ["surface", "execution"], "$adapter/route");
235
+ if (!ROUTE_SURFACES.has(value.route.surface))
236
+ fault(
237
+ "invalid-release-adapter",
238
+ "$adapter/route/surface",
239
+ "unsupported route surface",
240
+ );
241
+ if (!EXECUTION_MODES.has(value.route.execution))
242
+ fault(
243
+ "invalid-release-adapter",
244
+ "$adapter/route/execution",
245
+ "unsupported execution mode",
246
+ );
247
+ return createV4ReleaseInvocation(value.invocation);
248
+ }
249
+
250
+ export function planV4ReleaseRoute({
251
+ requestedSha,
252
+ observedSha,
253
+ comparisonStatus,
254
+ requestedChannel = "",
255
+ targetRef,
256
+ dryRun = false,
257
+ resume = false,
258
+ }) {
259
+ sha(requestedSha, "$route/requestedSha");
260
+ sha(observedSha, "$route/observedSha");
261
+ if (!COMPARISON_STATES.has(comparisonStatus))
262
+ fault(
263
+ "invalid-release-route",
264
+ "$route/comparisonStatus",
265
+ "unsupported source comparison state",
266
+ );
267
+ text(targetRef, "$route/targetRef");
268
+ const alphaLane = /^alpha\/v[0-9]+\/v[0-9]+\.[0-9]+$/u.test(targetRef);
269
+ const stableLane =
270
+ /^release\/v[0-9]+\/v[0-9]+\.[0-9]+$/u.test(targetRef) ||
271
+ ["publish-gate/major", "major-gate"].includes(targetRef);
272
+ if (!alphaLane && !stableLane)
273
+ fault(
274
+ "invalid-release-route",
275
+ "$route/targetRef",
276
+ "source lane is not a supported v4 release lane",
277
+ );
278
+ const derivedChannel = alphaLane ? "alpha" : "stable";
279
+ const normalizedRequested =
280
+ requestedChannel === "alpha"
281
+ ? "alpha"
282
+ : ["release", "stable", "major"].includes(requestedChannel)
283
+ ? "stable"
284
+ : requestedChannel === ""
285
+ ? derivedChannel
286
+ : "";
287
+ if (!normalizedRequested || normalizedRequested !== derivedChannel)
288
+ fault(
289
+ "invalid-release-route",
290
+ "$route/channel",
291
+ "requested channel does not match the source lane",
292
+ );
293
+ let decision = resume ? "Resume" : "Fresh";
294
+ let reason = resume ? "resume" : "fresh";
295
+ if (comparisonStatus === "ahead" && !resume && !dryRun) {
296
+ decision = "NoOp";
297
+ reason = "source-advanced";
298
+ } else if (
299
+ requestedSha !== observedSha &&
300
+ !(resume && comparisonStatus === "ahead") &&
301
+ !(dryRun && comparisonStatus === "ahead")
302
+ ) {
303
+ decision = "Blocked";
304
+ reason = `source-${comparisonStatus}`;
305
+ }
306
+ return Object.freeze({
307
+ decision,
308
+ reason,
309
+ channel: normalizedRequested,
310
+ targetRef,
311
+ requestedSha,
312
+ observedSha,
313
+ });
314
+ }
315
+
316
+ export function createV4ReleaseTransaction(value) {
317
+ exactKeys(
318
+ value,
319
+ [
320
+ "invocationRoot",
321
+ "publisherRoot",
322
+ "runtimeRoot",
323
+ "providerRoot",
324
+ "parentRoot",
325
+ ],
326
+ "$transaction",
327
+ );
328
+ for (const name of [
329
+ "invocationRoot",
330
+ "publisherRoot",
331
+ "runtimeRoot",
332
+ "providerRoot",
333
+ "parentRoot",
334
+ ])
335
+ validateV4Root(value[name], `$transaction/${name}`);
336
+ const transaction = {
337
+ schema: V4_RELEASE_TRANSACTION_CONTRACT,
338
+ invocationRoot: value.invocationRoot,
339
+ publisherRoot: value.publisherRoot,
340
+ runtimeRoot: value.runtimeRoot,
341
+ providerRoot: value.providerRoot,
342
+ parentRoot: value.parentRoot,
343
+ phases: ["QUALIFY", "APPLY", "SETTLE"],
344
+ writer: "canonical-v4-apply",
345
+ };
346
+ return {
347
+ transaction,
348
+ transactionRoot: v4ContentRoot("release-transaction", transaction),
349
+ };
350
+ }
351
+
352
+ export function createV4ReleaseReceipt(value) {
353
+ exactKeys(
354
+ value,
355
+ [
356
+ "schema",
357
+ "transactionRoot",
358
+ "outcome",
359
+ "releasePassportRoot",
360
+ "providerTransactionRoot",
361
+ "providerStateRoot",
362
+ "providerReceiptRoots",
363
+ ],
364
+ "$receipt",
365
+ );
366
+ if (value.schema !== V4_RELEASE_RECEIPT_CONTRACT)
367
+ fault(
368
+ "invalid-release-receipt",
369
+ "$receipt/schema",
370
+ "unsupported receipt schema",
371
+ );
372
+ validateV4Root(value.transactionRoot, "$receipt/transactionRoot");
373
+ if (!["complete", "blocked"].includes(value.outcome))
374
+ fault(
375
+ "invalid-release-receipt",
376
+ "$receipt/outcome",
377
+ "unsupported receipt outcome",
378
+ );
379
+ for (const name of [
380
+ "releasePassportRoot",
381
+ "providerTransactionRoot",
382
+ "providerStateRoot",
383
+ ]) {
384
+ if (value[name] !== null) validateV4Root(value[name], `$receipt/${name}`);
385
+ }
386
+ if (!Array.isArray(value.providerReceiptRoots))
387
+ fault(
388
+ "invalid-release-receipt",
389
+ "$receipt/providerReceiptRoots",
390
+ "receipt roots must be an array",
391
+ );
392
+ value.providerReceiptRoots.forEach((root, index) =>
393
+ validateV4Root(root, `$receipt/providerReceiptRoots/${index}`),
394
+ );
395
+ const canonicalRoots = [...new Set(value.providerReceiptRoots)].sort();
396
+ if (
397
+ canonicalRoots.length !== value.providerReceiptRoots.length ||
398
+ canonicalRoots.some(
399
+ (root, index) => root !== value.providerReceiptRoots[index],
400
+ )
401
+ )
402
+ fault(
403
+ "invalid-release-receipt",
404
+ "$receipt/providerReceiptRoots",
405
+ "provider receipt roots must be sorted and unique",
406
+ );
407
+ if (
408
+ value.outcome === "complete" &&
409
+ [
410
+ value.releasePassportRoot,
411
+ value.providerTransactionRoot,
412
+ value.providerStateRoot,
413
+ ].some((root) => root === null)
414
+ )
415
+ fault(
416
+ "invalid-release-receipt",
417
+ "$receipt/outcome",
418
+ "complete receipt requires every terminal root",
419
+ );
420
+ v4CanonicalBytes(value);
421
+ return {
422
+ receipt: value,
423
+ receiptRoot: v4ContentRoot("release-receipt", value),
424
+ };
425
+ }
@@ -196,7 +196,6 @@ function normalizeNpmPublisher(value, { packageName, repository, workflowFilenam
196
196
  configurationRead: true,
197
197
  };
198
198
  }
199
-
200
199
  function main() {
201
200
  const repository = flag("repository");
202
201
  const workflowRepository = flag("workflow-repository", repository);
@@ -127,7 +127,6 @@ const requiredPaths = [
127
127
  "scripts/npm-publish-transaction.mjs",
128
128
  "scripts/paper.mjs",
129
129
  "scripts/publication-package.mjs",
130
- "scripts/publication-commit-evidence.mjs",
131
130
  "scripts/publication-reproducibility.mjs",
132
131
  "scripts/release-candidate-resolver.mjs",
133
132
  "scripts/resume-from-candidate-run.mjs",
@@ -300,8 +299,8 @@ if (channelPromotionWorkflow !== generateChannelPromotionWorkflow(advancedPromot
300
299
  throw new Error("generated channel promotion workflow is stale");
301
300
  }
302
301
  for (const requiredSnippet of [
303
- "uses: kungfu-systems/buildchain/.github/workflows/.release-candidate-promote.yml@alpha/v4/v4.0",
304
- "promotion-shell-ref: ${{ needs.consumer-admission.outputs.shell-call-ref }}",
302
+ "name: Adapt recovery inputs into the canonical v4 publisher",
303
+ "uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4-alpha",
305
304
  "declarative-release-tail: true",
306
305
  ]) {
307
306
  if (!boundedAlphaRecoveryWorkflow.includes(requiredSnippet)) {
@@ -316,8 +315,8 @@ for (const workflowSource of [boundedAlphaRecoveryWorkflow, channelPromotionWork
316
315
  for (const requiredSnippet of [
317
316
  "buildchain-channel:",
318
317
  `/${promotionShellRouting.alpha.workflowPath}@${promotionShellRouting.alpha.callRef}`,
319
- `/${promotionShellRouting.stable.workflowPath}@${promotionShellRouting.stable.callRef}`,
320
- `STABLE_SHELL_REF: v${selfDogfoodMajor}`,
318
+ `SELECTED_SHELL_REF: v${selfDogfoodMajor}-alpha`,
319
+ "name: Invoke the single v4 publisher adapter",
321
320
  "promotion-contract-lock-digest:",
322
321
  "authorize-promotion-runtime-override.cjs",
323
322
  "BUILDCHAIN_ROUTER_REPOSITORY: ${{ inputs.buildchain-repository }}",
@@ -329,6 +328,9 @@ for (const requiredSnippet of [
329
328
  throw new Error(`channel promotion workflow missing routing contract: ${requiredSnippet}`);
330
329
  }
331
330
  }
331
+ if (channelPromotionWorkflow.includes("Promote with stable workflow shell")) {
332
+ throw new Error("channel promotion workflow retains a parallel stable publisher");
333
+ }
332
334
  for (const forbiddenSnippet of ["job.workflow_repository", "job.workflow_sha"]) {
333
335
  if (channelPromotionWorkflow.includes(forbiddenSnippet)) {
334
336
  throw new Error(`channel promotion workflow uses unsupported GitHub context: ${forbiddenSnippet}`);
@@ -1046,7 +1048,6 @@ for (const requiredSnippet of [
1046
1048
  "resume-candidate-run-id:",
1047
1049
  "resume-expected-source-tree:",
1048
1050
  "resume-buildchain-runtime-ref:",
1049
- "uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4",
1050
1051
  "release-passport-buildchain-self-kfd: true",
1051
1052
  "declarative-release-tail: true",
1052
1053
  "release-passport-impact-json: .buildchain/release-impact.json",
@@ -1057,61 +1058,28 @@ for (const requiredSnippet of [
1057
1058
  }
1058
1059
  const releaseCandidatePromoteWorkflow = fs.readFileSync(path.join(root, ".github/workflows/.release-candidate-promote.yml"), "utf8");
1059
1060
  for (const requiredSnippet of [
1060
- "release-passport-kfd-1-witness-jsons:",
1061
- "release-passport-kfd-1-witness-jsons: ${{ inputs.release-passport-kfd-1-witness-jsons }}",
1062
- "release-passport-kfd-2-claim-jsons:",
1063
- "release-passport-kfd-2-claim-jsons: ${{ inputs.release-passport-kfd-2-claim-jsons }}",
1064
- "release-passport-kfd-3-prebuild-witness-jsons:",
1065
- "release-passport-kfd-3-prebuild-witness-jsons: ${{ inputs.release-passport-kfd-3-prebuild-witness-jsons }}",
1066
- "release-passport-kfd-3-artifact-witness-jsons:",
1067
- "release-passport-kfd-3-artifact-witness-jsons: ${{ inputs.release-passport-kfd-3-artifact-witness-jsons }}",
1068
- "release-passport-kfd-3-artifact-verify-command:",
1069
- "release-passport-kfd-adopter-manifest-json:", "release-passport-kfd-adopter-manifest-json: ${{ inputs.release-passport-kfd-adopter-manifest-json }}",
1070
- "release-passport-kfd-support-matrix-json:", "release-passport-kfd-support-matrix-json: ${{ inputs.release-passport-kfd-support-matrix-json }}",
1071
- "release-passport-kfd-product-gate-jsons:",
1072
- "release-passport-kfd-product-gate-jsons: ${{ inputs.release-passport-kfd-product-gate-jsons }}",
1073
- "release-passport-invariant-passport-jsons:",
1074
- "release-passport-invariant-passport-jsons: ${{ inputs.release-passport-invariant-passport-jsons }}",
1075
- "release-passport-invariant-passport-command:",
1076
- "release-passport-invariant-passport-command: ${{ inputs.release-passport-invariant-passport-command }}",
1077
- "release-passport-buildchain-self-kfd:",
1078
- "release-passport-buildchain-self-kfd: ${{ inputs.release-passport-buildchain-self-kfd }}",
1079
- "github-release:",
1080
- "default: true",
1081
- "github-release: ${{ inputs.github-release }}",
1082
- "github-release-payload-patterns:",
1083
- "github-release-artifact-paths: ${{ steps.rc.outputs.release-candidate-github-release-artifact-paths }}",
1084
- "github-release-title: ${{ inputs.github-release-title }}",
1085
- "github-release-notes: ${{ inputs.github-release-notes }}",
1086
- "publication-commit-command:",
1087
- "BUILDCHAIN_PUBLICATION_COMMIT_SIGNING_KEY:",
1088
- "KUNGFU_GOVERNANCE_AUDITOR_APP_PRIVATE_KEY:",
1089
- "Commit consumer publication authority last",
1090
- "node .buildchain/runtime/scripts/publication-commit-evidence.mjs",
1091
- "require-publish-source-lock: \"true\"",
1092
- "publish-source-ref: ${{ steps.publish-gate.outputs.ref }}",
1093
- "publish-source-sha: ${{ steps.publish-gate.outputs.sha }}",
1094
- "publish-source-locked: ${{ steps.publish-gate.outputs.locked }}",
1095
- "expected-publication-version: ${{ needs.publication-plan.outputs.version }}",
1096
- "publication-version: ${{ needs.publication-plan.outputs.version }}",
1097
- "name: Plan exact publication version",
1098
- "name: Install exact publication planning dependencies",
1099
- "DRY_RUN: ${{ inputs.dry-run }}",
1100
- "if (dryRun) {",
1101
- "Enforce Buildchain stable release canary gate",
1102
- "BUILDCHAIN_STABLE_RELEASE_POLICY: .buildchain/stable-release-policy.json",
1103
- "Consumer has no binary-distribution.yml; standalone binary dispatch is not applicable.",
1104
- "gh workflow view binary-distribution.yml",
1105
- "resume-candidate-run-id:",
1106
- "node .buildchain/runtime/scripts/resume-from-candidate-run.mjs",
1107
- "publish-sealed-bundle-root: ${{ steps.rc.outputs.publish-sealed-bundle-root }}",
1108
- "BUILDCHAIN_EXPECTED_TRANSACTION_ID: ${{ inputs.resume-transaction-id }}",
1109
- "if: ${{ inputs.resume-candidate-run-id == '' }}",
1110
- "Bridge Buildchain self-runtime dependencies",
1111
- "ln -s .buildchain/runtime/node_modules node_modules",
1061
+ "name: QUALIFY canonical v4 release invocation inputs",
1062
+ "name: APPLY one rooted provider transaction",
1063
+ "name: SETTLE terminal ReleaseReceipt projection",
1064
+ "uses: ./.buildchain/runtime/actions/v4-release-candidate-promote",
1065
+ "publisher-workflow-sha:",
1066
+ "runtime-commit:",
1067
+ "runtime-tree:",
1068
+ "release-invocation-root:",
1069
+ "release-transaction-root:",
1070
+ "release-receipt-root:",
1112
1071
  ]) {
1113
1072
  if (!releaseCandidatePromoteWorkflow.includes(requiredSnippet)) {
1114
- throw new Error(`release candidate promote workflow missing KFD gate pass-through: ${requiredSnippet}`);
1073
+ throw new Error(`release candidate promote workflow missing canonical v4 boundary: ${requiredSnippet}`);
1074
+ }
1075
+ }
1076
+ for (const retiredSnippet of [
1077
+ "Commit consumer publication authority last",
1078
+ "publish-github-artifact-attestation-evidence.mjs",
1079
+ "capture-package-release-propagation.mjs",
1080
+ ]) {
1081
+ if (releaseCandidatePromoteWorkflow.includes(retiredSnippet)) {
1082
+ throw new Error(`release candidate promote workflow retains parallel publication authority: ${retiredSnippet}`);
1115
1083
  }
1116
1084
  }
1117
1085
  const workflowDir = path.join(root, ".github/workflows");
@@ -20,17 +20,25 @@ import {
20
20
  evaluateWorkflowBudgets,
21
21
  } from "./maintainability-governance.mjs";
22
22
 
23
- function collectHotspots(root, current, limit = 20, shallowFallback = []) {
23
+ function collectHotspots(
24
+ root,
25
+ current,
26
+ limit = 20,
27
+ shallowFallback = [],
28
+ {
29
+ baseRef = process.env.GITHUB_BASE_REF || process.env.GITHUB_REF_NAME || "",
30
+ } = {},
31
+ ) {
24
32
  const maintained = new Set([
25
33
  ...Object.keys(current.files || {}),
26
34
  ...Object.keys(current.tests || {}),
27
35
  ...Object.keys(current.workflows || {}),
28
36
  ]);
29
- // Shallow consumers cannot reproduce repository-wide churn ranking.
30
- // Reuse the audited routes; full clones still re-rank them below.
31
- // This keeps consumer verification deterministic without inventing history.
37
+ // Release-line reconciliation reuses audited routes across equivalent-tree DAG shapes.
32
38
  if (
33
- gitOutput(root, ["rev-parse", "--is-shallow-repository"]).trim() === "true"
39
+ gitOutput(root, ["rev-parse", "--is-shallow-repository"]).trim() ===
40
+ "true" ||
41
+ /^(?:alpha|release|publish-gate)\//u.test(baseRef)
34
42
  ) {
35
43
  return shallowFallback.slice(0, limit);
36
44
  }
@@ -8,6 +8,7 @@ import {
8
8
  collectHistoryRows,
9
9
  collectRevisionCatalog,
10
10
  countsBy,
11
+ ensureCapabilityCutLineage,
11
12
  git,
12
13
  sha256,
13
14
  } from "./v3-v4-capability-catalog.mjs";
@@ -17,9 +18,8 @@ export const V3_V4_CAPABILITY_INVENTORY_CONTRACT =
17
18
  export const V3_V4_CAPABILITY_CUTS = Object.freeze({
18
19
  priorFamilyV3: "6b96bdad8d9f8ccf9275f27d9370a226a9c78465",
19
20
  liveV3: "8493bf140a7f567e76aff3119f3d39ff026afc84",
20
- liveV4: "11b4ab21405cdffcc9d6dc10cd947618d9d1bf9c",
21
+ liveV4: "97edd5b93eb67e5bf36ad55726e08231b47b70c0",
21
22
  });
22
-
23
23
  const INVENTORY_PATH = "architecture/v3-v4-live-capability-inventory.json";
24
24
  const ALLOWED_DISPOSITIONS = new Set([
25
25
  "v4-native",
@@ -92,64 +92,6 @@ function capabilityCutAvailable(root, revision) {
92
92
  }
93
93
  }
94
94
 
95
- export function assertCapabilityCutAncestor({
96
- root = process.cwd(),
97
- revision,
98
- descendant = "HEAD",
99
- label = "capability cut",
100
- } = {}) {
101
- try {
102
- execFileSync("git", ["merge-base", "--is-ancestor", revision, descendant], {
103
- cwd: root,
104
- stdio: "ignore",
105
- });
106
- } catch {
107
- throw new Error(
108
- `${label} ${revision} must be an ancestor of ${descendant}; regenerate the cut after rebasing instead of relying on a retained local object`,
109
- );
110
- }
111
- }
112
-
113
- function repositoryIsShallow(root) {
114
- return (
115
- execFileSync("git", ["rev-parse", "--is-shallow-repository"], {
116
- cwd: root,
117
- encoding: "utf8",
118
- }).trim() === "true"
119
- );
120
- }
121
-
122
- export function ensureCapabilityCutAncestor({
123
- root = process.cwd(),
124
- revision,
125
- descendant = "HEAD",
126
- label = "capability cut",
127
- } = {}) {
128
- try {
129
- assertCapabilityCutAncestor({ root, revision, descendant, label });
130
- return;
131
- } catch (error) {
132
- if (!repositoryIsShallow(root)) throw error;
133
- }
134
- const descendantCommit = execFileSync(
135
- "git",
136
- ["rev-parse", `${descendant}^{commit}`],
137
- { cwd: root, encoding: "utf8" },
138
- ).trim();
139
- try {
140
- execFileSync(
141
- "git",
142
- ["fetch", "--no-tags", "--depth=128", "origin", descendantCommit],
143
- { cwd: root, stdio: "ignore" },
144
- );
145
- } catch {
146
- throw new Error(
147
- `${label} ${revision} ancestry could not be hydrated from ${descendantCommit} through a bounded origin fetch`,
148
- );
149
- }
150
- assertCapabilityCutAncestor({ root, revision, descendant, label });
151
- }
152
-
153
95
  function ensureCapabilityCutsAvailable(root) {
154
96
  const revisions = Object.values(V3_V4_CAPABILITY_CUTS);
155
97
  const missing = revisions.filter(
@@ -170,7 +112,7 @@ function ensureCapabilityCutsAvailable(root) {
170
112
  `v3/v4 capability cuts are unavailable after a bounded origin fetch: ${unavailable.join(", ")}`,
171
113
  );
172
114
  }
173
- ensureCapabilityCutAncestor({
115
+ ensureCapabilityCutLineage({
174
116
  root,
175
117
  revision: V3_V4_CAPABILITY_CUTS.liveV4,
176
118
  label: "live v4 capability cut",