@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.21

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 (203) hide show
  1. package/actions/release-tail/README.md +8 -0
  2. package/architecture/ci-lane-change-budget.json +187 -0
  3. package/architecture/decisions/0002-next-development-transition.md +101 -0
  4. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  5. package/architecture/internal-capabilities.json +211 -8
  6. package/architecture/maintainability-policy.json +113 -28
  7. package/architecture/release-tail-contract-inventory.json +5 -2
  8. package/architecture/v3-core-mechanism-inventory.json +91 -0
  9. package/architecture/v4-adopter-delivery-parity.json +32 -5
  10. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  11. package/architecture/v4-compatibility-facts-parity.json +77 -0
  12. package/architecture/v4-delivery-authority-parity.json +250 -0
  13. package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
  14. package/architecture/v4-floating-consumer-policy.json +6 -0
  15. package/architecture/v4-next-development-parity.json +192 -0
  16. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  17. package/architecture/v4-release-train-parity.json +1 -0
  18. package/architecture/v4-tail-reseal-parity.json +150 -0
  19. package/bin/buildchain.mjs +32 -3
  20. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  21. package/bin/internal/command-registry.mjs +3 -0
  22. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  23. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  24. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  25. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  26. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  28. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  29. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  30. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  31. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  32. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  33. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  38. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  39. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  40. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  41. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  42. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  43. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  44. package/contracts/next-development-request-v1.schema.json +66 -0
  45. package/contracts/next-development-transition-v1.schema.json +292 -0
  46. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  47. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  48. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  49. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  50. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  51. package/dist/site/agent-index.json +5 -0
  52. package/dist/site/artifact-schemas.json +10 -0
  53. package/dist/site/buildchain-contract.json +1475 -27
  54. package/dist/site/buildchain-site.json +627 -31
  55. package/dist/site/capability-registry.json +17 -14
  56. package/dist/site/cli-registry.json +205 -3
  57. package/dist/site/kfd-claims.json +490 -16
  58. package/dist/site/kfd-upstream-aggregate.json +1 -1
  59. package/dist/site/manual-registry.json +52 -6
  60. package/dist/site/node-api-registry.json +14736 -8851
  61. package/dist/site/page-registry.json +593 -21
  62. package/dist/site/public-surface-audit.json +837 -48
  63. package/dist/site/publication-authority-registry.json +44 -8
  64. package/dist/site/publication-registry.json +4 -4
  65. package/dist/site/release-provenance.json +24 -0
  66. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  67. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  68. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  69. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  70. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  71. package/dist/site/site-manifest.json +34 -10
  72. package/dist/site/workflow-registry.json +160 -10
  73. package/docs/MAP.md +8 -2
  74. package/docs/cli-reference.md +404 -13
  75. package/docs/cli.md +8 -0
  76. package/docs/dev-alpha-candidate-patrol.md +8 -0
  77. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  78. package/docs/dev-delivery-warrant.md +351 -38
  79. package/docs/next-development-transition.md +118 -0
  80. package/docs/node-api-reference.md +646 -293
  81. package/docs/release-tail-provider-plane.md +30 -0
  82. package/docs/v4-adopter-delivery.md +133 -0
  83. package/docs/v4-compatibility-facts.md +91 -0
  84. package/docs/v4-publication-rehearsal.md +130 -0
  85. package/docs/v4-tail-reseal.md +73 -0
  86. package/docs/versioning.md +4 -3
  87. package/package.json +34 -6
  88. package/packages/core/adopter-delivery-passport.js +259 -0
  89. package/packages/core/adopter-delivery-vectors.js +158 -0
  90. package/packages/core/adopter-delivery-vectors.json +77 -0
  91. package/packages/core/buildchain-agent-manuals.js +3 -0
  92. package/packages/core/buildchain-compatibility-authority.js +336 -0
  93. package/packages/core/buildchain-compatibility-fact.js +394 -0
  94. package/packages/core/buildchain-compatibility-facts.json +217 -0
  95. package/packages/core/buildchain-compatibility-proof.js +566 -0
  96. package/packages/core/buildchain-config.js +84 -0
  97. package/packages/core/buildchain-contract.js +96 -143
  98. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  99. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  100. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  101. package/packages/core/buildchain-publication-authority.js +1 -0
  102. package/packages/core/channel-candidate.js +8 -0
  103. package/packages/core/channel-promotion-baseline.js +55 -0
  104. package/packages/core/ci-lane-change-budget.js +247 -0
  105. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  106. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  107. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  108. package/packages/core/dev-delivery-authority-landing.js +461 -0
  109. package/packages/core/dev-delivery-authority-observation.js +48 -0
  110. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  111. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  112. package/packages/core/dev-delivery-authority-state.js +583 -0
  113. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  114. package/packages/core/dev-delivery-contract-surface.js +76 -0
  115. package/packages/core/dev-delivery-execution-failure.js +133 -0
  116. package/packages/core/dev-delivery-execution-transfer.js +592 -0
  117. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  118. package/packages/core/dev-delivery-landing-readback.js +598 -0
  119. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  120. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  121. package/packages/core/dev-delivery-native-execution.js +110 -0
  122. package/packages/core/dev-delivery-native-proof.js +562 -0
  123. package/packages/core/dev-delivery-process-boundary.js +563 -0
  124. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  125. package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
  126. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  127. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  128. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  129. package/packages/core/dev-delivery-warrant-state.js +587 -0
  130. package/packages/core/dev-delivery-warrant.js +328 -370
  131. package/packages/core/github-governance-authority.js +1 -0
  132. package/packages/core/index.js +5 -0
  133. package/packages/core/kungfu-temporal-fact.js +557 -0
  134. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  135. package/packages/core/next-development-candidate-reservation.js +186 -0
  136. package/packages/core/next-development-controller.js +728 -0
  137. package/packages/core/next-development-projection.js +287 -0
  138. package/packages/core/next-development-transition.js +738 -0
  139. package/packages/core/paper-agent-entry.js +11 -5
  140. package/packages/core/paper.js +16 -5
  141. package/packages/core/published-delivery-authority.js +266 -0
  142. package/packages/core/release-candidate-recovery.js +70 -15
  143. package/packages/core/release-candidate.js +2 -2
  144. package/packages/core/release-passport.js +36 -9
  145. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  146. package/packages/core/v4-adopter-delivery.js +359 -0
  147. package/packages/core/v4-canonical-contracts.js +5 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  149. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  150. package/packages/core/v4-floating-consumer-policy.js +143 -20
  151. package/packages/core/v4-floating-consumer-release-passport.js +32 -5
  152. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  153. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  154. package/packages/core/v4-publication-rehearsal.js +481 -0
  155. package/packages/core/v4-tail-reseal-contract.js +26 -0
  156. package/packages/core/v4-tail-reseal-github.js +178 -0
  157. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  158. package/packages/core/v4-tail-reseal.js +594 -0
  159. package/packages/core/workflow-yaml-contract.js +24 -2
  160. package/scripts/buildchain-cli-help.mjs +41 -2
  161. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  162. package/scripts/check-inventory.mjs +14 -3
  163. package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
  164. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  165. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  166. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  167. package/scripts/dev-delivery-authority.mjs +490 -0
  168. package/scripts/dev-delivery-native-run.mjs +177 -0
  169. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  170. package/scripts/dev-delivery-proof.mjs +67 -2
  171. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  172. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  173. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  174. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  175. package/scripts/dev-delivery-two-phase.mjs +573 -0
  176. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  177. package/scripts/dev-delivery-warrant-store.mjs +231 -0
  178. package/scripts/dev-delivery-warrant.mjs +232 -194
  179. package/scripts/dev-pr-auto-merge.mjs +1 -1
  180. package/scripts/dev-pr-delivery-warrant.mjs +11 -0
  181. package/scripts/dev-qualification-patrol.mjs +9 -9
  182. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  183. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  184. package/scripts/generate-next-development-guidance.mjs +49 -0
  185. package/scripts/generate-release-candidate-passport.mjs +152 -0
  186. package/scripts/generate-site-bundle.mjs +69 -4
  187. package/scripts/init-repo.mjs +22 -4
  188. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  189. package/scripts/next-development-self-dogfood.mjs +597 -0
  190. package/scripts/next-development-transition.mjs +47 -0
  191. package/scripts/npm-publish-transaction.mjs +2 -2
  192. package/scripts/promotion-channel-router.mjs +4 -4
  193. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  194. package/scripts/release-tail.mjs +51 -3
  195. package/scripts/release-train-self-dogfood.mjs +94 -92
  196. package/scripts/resume-from-candidate-run.mjs +557 -30
  197. package/scripts/site-capability-metadata.mjs +41 -0
  198. package/scripts/stable-candidate-qualification.mjs +9 -10
  199. package/scripts/v4-consumer-policy.mjs +5 -5
  200. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  201. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  202. package/scripts/v4-tail-reseal.mjs +416 -0
  203. package/templates/native-dev-delivery.yml +143 -0
@@ -0,0 +1,597 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+
8
+ import {
9
+ runNextDevelopmentController,
10
+ validateNextDevelopmentController,
11
+ } from "../packages/core/next-development-controller.js";
12
+ import {
13
+ NEXT_DEVELOPMENT_TRANSITION_CONTRACT,
14
+ nextDevelopmentRoot,
15
+ } from "../packages/core/next-development-transition.js";
16
+ import {
17
+ DurableStore,
18
+ HOSTED_READBACK_PATHS,
19
+ PROTECTED_DEV_BRANCH,
20
+ RECORDED_AT,
21
+ VersionStateAdapterHarness,
22
+ adapterTransition,
23
+ anchorInput,
24
+ exactRoot,
25
+ exactSha,
26
+ fixtureSha,
27
+ repository,
28
+ required,
29
+ validateAdapterEvidence,
30
+ validateAdapterOperation,
31
+ validateRecoveryEvidence,
32
+ validateSelfDogfoodRoute,
33
+ } from "./next-development-self-dogfood-harness.mjs";
34
+
35
+ export const NEXT_DEVELOPMENT_SELF_DOGFOOD_CHECKPOINT =
36
+ "kungfu-buildchain-next-development-self-dogfood-checkpoint/v1";
37
+ export const NEXT_DEVELOPMENT_SELF_DOGFOOD_EVIDENCE =
38
+ "kungfu-buildchain-next-development-self-dogfood/v1";
39
+ export const HOSTED_SELF_DOGFOOD_EVIDENCE =
40
+ "kungfu-buildchain-alpha-self-dogfood";
41
+
42
+ function checkpointBody(checkpoint) {
43
+ const body = structuredClone(checkpoint);
44
+ delete body.checkpointRoot;
45
+ return body;
46
+ }
47
+
48
+ function evidenceBody(evidence) {
49
+ const body = structuredClone(evidence);
50
+ delete body.evidenceRoot;
51
+ return body;
52
+ }
53
+
54
+ export function validateSelfDogfoodCheckpoint(checkpoint) {
55
+ if (checkpoint?.contract !== NEXT_DEVELOPMENT_SELF_DOGFOOD_CHECKPOINT) {
56
+ throw new Error(
57
+ "next-development self-dogfood checkpoint contract mismatch",
58
+ );
59
+ }
60
+ const runtimeSha = exactSha(checkpoint.route?.runtimeSha, "route.runtimeSha");
61
+ exactRoot(checkpoint.route?.contractRoot, "route.contractRoot");
62
+ const targetRepository = repository(
63
+ checkpoint.route?.repository,
64
+ "route.repository",
65
+ );
66
+ if (
67
+ checkpoint.route.runtimeRef !== "v4-alpha" ||
68
+ checkpoint.route.publicWorkflow !==
69
+ `${targetRepository}/.github/workflows/build.yml@v4-alpha` ||
70
+ targetRepository !== "kungfu-systems/buildchain"
71
+ ) {
72
+ throw new Error("checkpoint requires the public owner/repo v4-alpha route");
73
+ }
74
+ if (runtimeSha !== checkpoint.route.runtimeSha) {
75
+ throw new Error("checkpoint runtime SHA is not canonical");
76
+ }
77
+ required(checkpoint.faultRunnerId, "faultRunnerId");
78
+ const semver = validateNextDevelopmentController(
79
+ checkpoint.semver.controller,
80
+ );
81
+ const anchored = validateNextDevelopmentController(
82
+ checkpoint.anchored.controller,
83
+ );
84
+ if (
85
+ semver.transition.state.status !== "planned" ||
86
+ semver.activeAttempt !== null ||
87
+ checkpoint.semver.materializations !== 1 ||
88
+ checkpoint.semver.adapterOperations?.length !== 1 ||
89
+ checkpoint.semver.durableStateFailures !== 1
90
+ ) {
91
+ throw new Error(
92
+ "semver checkpoint must prove one transient durable-state failure",
93
+ );
94
+ }
95
+ checkpoint.semver.adapterOperations.forEach(validateAdapterOperation);
96
+ if (
97
+ anchored.transition.state.status !== "waiting-anchor" ||
98
+ anchored.transition.target.version !== null ||
99
+ checkpoint.anchored.materializations !== 0
100
+ ) {
101
+ throw new Error("anchored checkpoint must persist manual waiting");
102
+ }
103
+ if (checkpoint.alphaCandidateRebuilds !== 0) {
104
+ throw new Error("post-Alpha proof must not rebuild the Alpha candidate");
105
+ }
106
+ if (
107
+ checkpoint.checkpointRoot !==
108
+ nextDevelopmentRoot(checkpointBody(checkpoint))
109
+ ) {
110
+ throw new Error("next-development self-dogfood checkpoint root drifted");
111
+ }
112
+ return structuredClone(checkpoint);
113
+ }
114
+
115
+ export async function createSelfDogfoodCheckpoint({
116
+ repository: targetRepository,
117
+ runtimeRef,
118
+ runtimeSha,
119
+ contractRoot,
120
+ runnerId,
121
+ } = {}) {
122
+ const routeRepository = repository(targetRepository);
123
+ const route = {
124
+ repository: routeRepository,
125
+ publicWorkflow: `${routeRepository}/.github/workflows/build.yml@v4-alpha`,
126
+ runtimeRef: required(runtimeRef, "runtimeRef"),
127
+ runtimeSha: exactSha(runtimeSha, "runtimeSha"),
128
+ contractRoot: exactRoot(contractRoot, "contractRoot"),
129
+ protectedDevBranch: PROTECTED_DEV_BRANCH,
130
+ hostedReadbackPaths: HOSTED_READBACK_PATHS,
131
+ };
132
+ if (
133
+ route.repository !== "kungfu-systems/buildchain" ||
134
+ route.runtimeRef !== "v4-alpha"
135
+ ) {
136
+ throw new Error(
137
+ "self-dogfood checkpoint requires the public kungfu-systems/buildchain v4-alpha route",
138
+ );
139
+ }
140
+
141
+ const semverStore = new DurableStore([], { transientFailures: 1 });
142
+ const semverExecutor = new VersionStateAdapterHarness({ route });
143
+ await runNextDevelopmentController(
144
+ {
145
+ transition: adapterTransition(route, "semver"),
146
+ protectedDevBranch: PROTECTED_DEV_BRANCH,
147
+ recordedAt: RECORDED_AT,
148
+ },
149
+ { store: semverStore, executor: semverExecutor },
150
+ ).then(
151
+ () => {
152
+ throw new Error("transient durable-state fault injection did not run");
153
+ },
154
+ (error) => {
155
+ if (
156
+ !/injected transient durable-state write failure/u.test(error.message)
157
+ ) {
158
+ throw error;
159
+ }
160
+ },
161
+ );
162
+
163
+ const anchoredStore = new DurableStore();
164
+ const anchoredExecutor = new VersionStateAdapterHarness({ route });
165
+ await runNextDevelopmentController(
166
+ {
167
+ transition: adapterTransition(route, "anchored"),
168
+ protectedDevBranch: PROTECTED_DEV_BRANCH,
169
+ recordedAt: RECORDED_AT,
170
+ },
171
+ { store: anchoredStore, executor: anchoredExecutor },
172
+ );
173
+
174
+ const checkpoint = {
175
+ schemaVersion: 1,
176
+ contract: NEXT_DEVELOPMENT_SELF_DOGFOOD_CHECKPOINT,
177
+ route,
178
+ faultRunnerId: required(runnerId, "runnerId"),
179
+ alphaCandidateRebuilds: 0,
180
+ semver: {
181
+ controller: semverStore.only(),
182
+ materializations: semverExecutor.materializations,
183
+ adapterOperations: semverExecutor.operationRecords(),
184
+ durableStateFailures: semverStore.transientFailures,
185
+ },
186
+ anchored: {
187
+ controller: anchoredStore.only(),
188
+ materializations: anchoredExecutor.materializations,
189
+ },
190
+ };
191
+ checkpoint.checkpointRoot = nextDevelopmentRoot(checkpoint);
192
+ return validateSelfDogfoodCheckpoint(checkpoint);
193
+ }
194
+
195
+ function assertVerifiedModel(model, label) {
196
+ const controller = validateNextDevelopmentController(model?.controller);
197
+ if (
198
+ model.status !== "verified" ||
199
+ controller.transition.state.status !== "verified" ||
200
+ model.controllerRoot !== controller.controllerRoot ||
201
+ model.readbackRoot !== nextDevelopmentRoot(controller.readback)
202
+ ) {
203
+ throw new Error(
204
+ `${label} does not bind its verified controller and readback`,
205
+ );
206
+ }
207
+ return controller;
208
+ }
209
+
210
+ export function validateSelfDogfoodEvidence(evidence) {
211
+ if (evidence?.contract !== NEXT_DEVELOPMENT_SELF_DOGFOOD_EVIDENCE) {
212
+ throw new Error("next-development self-dogfood evidence contract mismatch");
213
+ }
214
+ exactRoot(evidence.checkpointRoot, "checkpointRoot");
215
+ exactRoot(evidence.transactionRoot, "transactionRoot");
216
+ exactRoot(evidence.protectedDevReadbackRoot, "protectedDevReadbackRoot");
217
+ validateSelfDogfoodRoute(evidence.route);
218
+ const semver = assertVerifiedModel(
219
+ evidence.models?.semverAuto,
220
+ "semver/auto",
221
+ );
222
+ const anchored = assertVerifiedModel(
223
+ evidence.models?.anchoredManual,
224
+ "anchored/manual",
225
+ );
226
+ if (evidence.status !== "passed")
227
+ throw new Error("self-dogfood evidence did not pass");
228
+ validateRecoveryEvidence(evidence.recovery);
229
+ if (
230
+ semver.controllerRoot !== evidence.transactionRoot ||
231
+ nextDevelopmentRoot(semver.readback) !==
232
+ evidence.protectedDevReadbackRoot ||
233
+ evidence.models.anchoredManual.waitingStatus !== "waiting-anchor" ||
234
+ evidence.models.anchoredManual.waitingControllerRoot ===
235
+ anchored.controllerRoot
236
+ ) {
237
+ throw new Error("both legal next-development models must be proved");
238
+ }
239
+ validateAdapterEvidence(evidence.adapter, semver, anchored);
240
+ if (
241
+ evidence.adoption?.coordinate !==
242
+ "kungfu-systems/buildchain/.github/workflows/build.yml@v4" ||
243
+ evidence.adoption?.exactShaProductionPin !== false
244
+ ) {
245
+ throw new Error("Kungfu adoption must use the floating v4 coordinate");
246
+ }
247
+ if (evidence.evidenceRoot !== nextDevelopmentRoot(evidenceBody(evidence))) {
248
+ throw new Error("next-development self-dogfood evidence root drifted");
249
+ }
250
+ return structuredClone(evidence);
251
+ }
252
+
253
+ export async function resumeSelfDogfoodCheckpoint(
254
+ checkpointInput,
255
+ { runnerId } = {},
256
+ ) {
257
+ const checkpoint = validateSelfDogfoodCheckpoint(checkpointInput);
258
+ const resumeRunnerId = required(runnerId, "runnerId");
259
+ if (resumeRunnerId === checkpoint.faultRunnerId) {
260
+ throw new Error("post-Alpha recovery must run on a fresh runner identity");
261
+ }
262
+
263
+ const initialDevSha = fixtureSha("protected-dev:initial");
264
+ const movedDevSha = fixtureSha("protected-dev:moved-during-recovery");
265
+ const semverStore = new DurableStore([checkpoint.semver.controller]);
266
+ const semverExecutor = new VersionStateAdapterHarness({
267
+ route: checkpoint.route,
268
+ operations: checkpoint.semver.adapterOperations,
269
+ devSha: initialDevSha,
270
+ movedDevSha,
271
+ moveAfterRecoveredOperation: true,
272
+ pullRequestReadsBeforeMerge: 2,
273
+ });
274
+ const input = {
275
+ transition: adapterTransition(checkpoint.route, "semver"),
276
+ protectedDevBranch: PROTECTED_DEV_BRANCH,
277
+ recordedAt: RECORDED_AT,
278
+ };
279
+ const superseded = await runNextDevelopmentController(input, {
280
+ store: semverStore,
281
+ executor: semverExecutor,
282
+ });
283
+ const pending = await runNextDevelopmentController(input, {
284
+ store: semverStore,
285
+ executor: semverExecutor,
286
+ });
287
+ const delayed = await runNextDevelopmentController(input, {
288
+ store: semverStore,
289
+ executor: semverExecutor,
290
+ });
291
+ const semver = await runNextDevelopmentController(input, {
292
+ store: semverStore,
293
+ executor: semverExecutor,
294
+ });
295
+
296
+ const anchoredStore = new DurableStore([checkpoint.anchored.controller]);
297
+ const anchoredExecutor = new VersionStateAdapterHarness({
298
+ route: checkpoint.route,
299
+ });
300
+ const anchored = await runNextDevelopmentController(
301
+ {
302
+ transition: adapterTransition(checkpoint.route, "anchored"),
303
+ protectedDevBranch: PROTECTED_DEV_BRANCH,
304
+ reviewedInput: anchorInput(),
305
+ recordedAt: RECORDED_AT,
306
+ },
307
+ { store: anchoredStore, executor: anchoredExecutor },
308
+ );
309
+
310
+ const semverReadbackRoot = nextDevelopmentRoot(semver.readback);
311
+ const anchoredReadbackRoot = nextDevelopmentRoot(anchored.readback);
312
+ const evidence = {
313
+ schemaVersion: 1,
314
+ contract: NEXT_DEVELOPMENT_SELF_DOGFOOD_EVIDENCE,
315
+ status: "passed",
316
+ route: checkpoint.route,
317
+ checkpointRoot: checkpoint.checkpointRoot,
318
+ transactionRoot: semver.controllerRoot,
319
+ protectedDevReadbackRoot: semverReadbackRoot,
320
+ recovery: {
321
+ faultRunnerId: checkpoint.faultRunnerId,
322
+ resumeRunnerId,
323
+ freshRunner: true,
324
+ transientDurableStateFailures: checkpoint.semver.durableStateFailures,
325
+ recoveredAdapterOperations: semverExecutor.recoveredOperations,
326
+ alphaCandidateRebuilds: 0,
327
+ devMaterializationsBefore: checkpoint.semver.materializations,
328
+ devMaterializationsAfter: semverExecutor.materializations,
329
+ protectedDevMovement: {
330
+ initialSha: initialDevSha,
331
+ movedSha: movedDevSha,
332
+ supersededAttempts: superseded.attempts.filter(
333
+ (attempt) => attempt.status === "superseded",
334
+ ).length,
335
+ },
336
+ protectedPrDelay: {
337
+ status: pending.transition.state.status,
338
+ delayedStatus: delayed.transition.state.status,
339
+ unchangedControllerRoot:
340
+ pending.controllerRoot === delayed.controllerRoot,
341
+ },
342
+ },
343
+ adapter: {
344
+ boundary: "materializeNextDevelopmentTransition",
345
+ contract: NEXT_DEVELOPMENT_TRANSITION_CONTRACT,
346
+ operations: [
347
+ ...semverExecutor.operationRecords(),
348
+ ...anchoredExecutor.operationRecords(),
349
+ ],
350
+ },
351
+ models: {
352
+ semverAuto: {
353
+ status: semver.transition.state.status,
354
+ targetVersion: semver.transition.target.version,
355
+ controllerRoot: semver.controllerRoot,
356
+ readbackRoot: semverReadbackRoot,
357
+ controller: semver,
358
+ },
359
+ anchoredManual: {
360
+ waitingStatus: checkpoint.anchored.controller.transition.state.status,
361
+ waitingControllerRoot: checkpoint.anchored.controller.controllerRoot,
362
+ status: anchored.transition.state.status,
363
+ targetVersion: anchored.transition.target.version,
364
+ controllerRoot: anchored.controllerRoot,
365
+ readbackRoot: anchoredReadbackRoot,
366
+ controller: anchored,
367
+ },
368
+ },
369
+ adoption: {
370
+ coordinate: "kungfu-systems/buildchain/.github/workflows/build.yml@v4",
371
+ runtimeRef: "v4",
372
+ exactShaProductionPin: false,
373
+ },
374
+ };
375
+ evidence.evidenceRoot = nextDevelopmentRoot(evidence);
376
+ return validateSelfDogfoodEvidence(evidence);
377
+ }
378
+
379
+ function normalizeHostedReadback(value, route) {
380
+ const paths = (value?.versionRoots || [])
381
+ .map((entry, index) => ({
382
+ path: required(entry?.path, `hosted versionRoots[${index}].path`),
383
+ gitBlobSha: exactSha(
384
+ entry?.gitBlobSha,
385
+ `hosted versionRoots[${index}].gitBlobSha`,
386
+ ),
387
+ }))
388
+ .sort((left, right) => left.path.localeCompare(right.path));
389
+ if (
390
+ JSON.stringify(paths.map((entry) => entry.path)) !==
391
+ JSON.stringify(route.hostedReadbackPaths)
392
+ ) {
393
+ throw new Error(
394
+ "hosted protected Dev readback must exactly cover declared paths",
395
+ );
396
+ }
397
+ const body = {
398
+ repository: repository(value?.repository, "hosted readback repository"),
399
+ branch: required(value?.branch, "hosted readback branch"),
400
+ commitSha: exactSha(value?.commitSha, "hosted readback commitSha"),
401
+ treeSha: exactSha(value?.treeSha, "hosted readback treeSha"),
402
+ versionRoots: paths,
403
+ };
404
+ if (
405
+ body.repository !== route.repository ||
406
+ body.branch !== route.protectedDevBranch
407
+ ) {
408
+ throw new Error("hosted protected Dev readback route drifted");
409
+ }
410
+ return { ...body, evidenceRoot: nextDevelopmentRoot(body) };
411
+ }
412
+
413
+ export function createHostedSelfDogfoodEvidence({
414
+ nextDevelopment,
415
+ observation,
416
+ } = {}) {
417
+ const proof = validateSelfDogfoodEvidence(nextDevelopment);
418
+ const hostedProtectedDevReadback = normalizeHostedReadback(
419
+ observation?.protectedDevReadback,
420
+ proof.route,
421
+ );
422
+ const observed = structuredClone(observation?.observed || {});
423
+ const failures = [];
424
+ if (observed.alpha?.ref !== "v4-alpha")
425
+ failures.push("alpha runtime ref mismatch");
426
+ if (observed.alpha?.class !== "alpha")
427
+ failures.push("alpha runtime class mismatch");
428
+ if (observed.alpha?.sha !== observed.alpha?.expectedSha)
429
+ failures.push("alpha runtime SHA mismatch");
430
+ if (observed.alpha?.sha !== proof.route.runtimeSha)
431
+ failures.push("next-development runtime SHA mismatch");
432
+ if (observed.stable?.ref !== "v4")
433
+ failures.push("stable runtime ref mismatch");
434
+ if (observed.stable?.class !== "stable")
435
+ failures.push("stable runtime class mismatch");
436
+ if (observed.stable?.sha !== observed.stable?.expectedSha)
437
+ failures.push("stable runtime SHA mismatch");
438
+ for (const [lane, item] of Object.entries(observed)) {
439
+ exactSha(item?.sha, `observed ${lane} SHA`);
440
+ exactSha(item?.expectedSha, `expected ${lane} SHA`);
441
+ }
442
+ const evidence = {
443
+ schemaVersion: 1,
444
+ contract: HOSTED_SELF_DOGFOOD_EVIDENCE,
445
+ repository: repository(observation?.repository),
446
+ callerSha: exactSha(observation?.callerSha, "callerSha"),
447
+ checkedAt: required(observation?.checkedAt, "checkedAt"),
448
+ status: failures.length === 0 ? "passed" : "failed",
449
+ observed,
450
+ nextDevelopment: proof,
451
+ hostedProtectedDevReadback,
452
+ failures,
453
+ };
454
+ if (evidence.repository !== proof.route.repository) {
455
+ throw new Error("hosted evidence repository differs from the public route");
456
+ }
457
+ evidence.evidenceRoot = nextDevelopmentRoot(evidence);
458
+ return evidence;
459
+ }
460
+
461
+ export function validateHostedSelfDogfoodEvidence(evidence) {
462
+ if (evidence?.contract !== HOSTED_SELF_DOGFOOD_EVIDENCE) {
463
+ throw new Error("hosted self-dogfood evidence contract mismatch");
464
+ }
465
+ const proof = validateSelfDogfoodEvidence(evidence.nextDevelopment);
466
+ repository(evidence.repository);
467
+ exactSha(evidence.callerSha, "callerSha");
468
+ required(evidence.checkedAt, "checkedAt");
469
+ for (const [lane, item] of Object.entries(evidence.observed || {})) {
470
+ exactSha(item?.sha, `observed ${lane} SHA`);
471
+ exactSha(item?.expectedSha, `expected ${lane} SHA`);
472
+ }
473
+ const readback = normalizeHostedReadback(
474
+ evidence.hostedProtectedDevReadback,
475
+ proof.route,
476
+ );
477
+ if (
478
+ readback.evidenceRoot !==
479
+ evidence.hostedProtectedDevReadback?.evidenceRoot ||
480
+ evidence.evidenceRoot !== nextDevelopmentRoot(evidenceBody(evidence))
481
+ ) {
482
+ throw new Error("hosted self-dogfood evidence root drifted");
483
+ }
484
+ if (
485
+ evidence.status !== "passed" ||
486
+ evidence.failures?.length !== 0 ||
487
+ evidence.repository !== proof.route.repository ||
488
+ evidence.observed?.alpha?.ref !== "v4-alpha" ||
489
+ evidence.observed?.alpha?.class !== "alpha" ||
490
+ evidence.observed?.alpha?.sha !== evidence.observed?.alpha?.expectedSha ||
491
+ evidence.observed?.alpha?.sha !== proof.route.runtimeSha ||
492
+ evidence.observed?.stable?.ref !== "v4" ||
493
+ evidence.observed?.stable?.class !== "stable" ||
494
+ evidence.observed?.stable?.sha !== evidence.observed?.stable?.expectedSha
495
+ ) {
496
+ throw new Error(
497
+ "hosted self-dogfood evidence is not a passing exact readback",
498
+ );
499
+ }
500
+ return structuredClone(evidence);
501
+ }
502
+
503
+ function parseArgs(argv) {
504
+ const options = {};
505
+ for (let index = 0; index < argv.length; index += 1) {
506
+ const name = argv[index];
507
+ if (!name.startsWith("--") || index + 1 >= argv.length) {
508
+ throw new Error(`invalid argument: ${name}`);
509
+ }
510
+ options[
511
+ name.slice(2).replace(/-([a-z])/gu, (_, letter) => letter.toUpperCase())
512
+ ] = argv[index + 1];
513
+ index += 1;
514
+ }
515
+ return options;
516
+ }
517
+
518
+ function readJson(filePath) {
519
+ return JSON.parse(fs.readFileSync(path.resolve(filePath), "utf8"));
520
+ }
521
+
522
+ function writeJson(filePath, value) {
523
+ const output = path.resolve(filePath);
524
+ fs.mkdirSync(path.dirname(output), { recursive: true });
525
+ fs.writeFileSync(output, `${JSON.stringify(value, null, 2)}\n`);
526
+ }
527
+
528
+ function writeActionsOutputs(value) {
529
+ const output = process.env.GITHUB_OUTPUT;
530
+ if (!output) return;
531
+ fs.appendFileSync(
532
+ output,
533
+ [
534
+ `transaction-root=${value.transactionRoot || value.nextDevelopment?.transactionRoot || ""}`,
535
+ `protected-dev-readback-root=${value.protectedDevReadbackRoot || value.hostedProtectedDevReadback?.evidenceRoot || ""}`,
536
+ `evidence-root=${value.evidenceRoot || value.checkpointRoot || ""}`,
537
+ ].join("\n") + "\n",
538
+ );
539
+ }
540
+
541
+ async function main(argv = process.argv.slice(2)) {
542
+ const command = argv.shift();
543
+ const options = parseArgs(argv);
544
+ if (command === "checkpoint") {
545
+ const checkpoint = await createSelfDogfoodCheckpoint({
546
+ repository: options.repository,
547
+ runtimeRef: options.runtimeRef,
548
+ runtimeSha: options.runtimeSha,
549
+ contractRoot: options.contractRoot,
550
+ runnerId: options.runnerId,
551
+ });
552
+ writeJson(required(options.output, "output"), checkpoint);
553
+ writeActionsOutputs(checkpoint);
554
+ return checkpoint;
555
+ }
556
+ if (command === "resume") {
557
+ const evidence = await resumeSelfDogfoodCheckpoint(
558
+ readJson(required(options.input, "input")),
559
+ { runnerId: options.runnerId },
560
+ );
561
+ writeJson(required(options.output, "output"), evidence);
562
+ writeActionsOutputs(evidence);
563
+ return evidence;
564
+ }
565
+ if (command === "attest-hosted") {
566
+ const evidence = createHostedSelfDogfoodEvidence({
567
+ nextDevelopment: readJson(required(options.input, "input")),
568
+ observation: readJson(required(options.observation, "observation")),
569
+ });
570
+ writeJson(required(options.output, "output"), evidence);
571
+ writeActionsOutputs(evidence);
572
+ return evidence;
573
+ }
574
+ if (command === "verify") {
575
+ return validateSelfDogfoodEvidence(
576
+ readJson(required(options.input, "input")),
577
+ );
578
+ }
579
+ if (command === "verify-hosted") {
580
+ return validateHostedSelfDogfoodEvidence(
581
+ readJson(required(options.input, "input")),
582
+ );
583
+ }
584
+ throw new Error(
585
+ "usage: next-development-self-dogfood.mjs checkpoint|resume|attest-hosted|verify|verify-hosted ...",
586
+ );
587
+ }
588
+
589
+ if (
590
+ process.argv[1] &&
591
+ import.meta.url === pathToFileURL(process.argv[1]).href
592
+ ) {
593
+ main().catch((error) => {
594
+ process.stderr.write(`next-development self-dogfood: ${error.message}\n`);
595
+ process.exitCode = 1;
596
+ });
597
+ }
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ import { materializeNextDevelopmentTransition } from "../packages/core/next-development-transition.js";
8
+
9
+ function flag(args, name, fallback = "") {
10
+ const index = args.indexOf(`--${name}`);
11
+ return index === -1 ? fallback : String(args[index + 1] || "");
12
+ }
13
+
14
+ function usage() {
15
+ return `usage: node scripts/next-development-transition.mjs materialize --input <request.json> [--cwd <checkout>] [--write]\n`;
16
+ }
17
+
18
+ export function runNextDevelopmentAdapter(args = process.argv.slice(2)) {
19
+ const [command] = args;
20
+ if (command !== "materialize") throw new Error(usage().trim());
21
+ const inputPath = flag(args, "input");
22
+ if (!inputPath)
23
+ throw new Error(
24
+ "next-development materialize requires --input <request.json>",
25
+ );
26
+ const cwd = path.resolve(flag(args, "cwd", process.cwd()));
27
+ const request = JSON.parse(fs.readFileSync(path.resolve(inputPath), "utf8"));
28
+ return materializeNextDevelopmentTransition({
29
+ cwd,
30
+ request,
31
+ write: args.includes("--write"),
32
+ });
33
+ }
34
+
35
+ if (
36
+ process.argv[1] &&
37
+ import.meta.url === pathToFileURL(process.argv[1]).href
38
+ ) {
39
+ try {
40
+ process.stdout.write(
41
+ `${JSON.stringify(runNextDevelopmentAdapter(), null, 2)}\n`,
42
+ );
43
+ } catch (error) {
44
+ process.stderr.write(`next-development transition: ${error.message}\n`);
45
+ process.exitCode = 1;
46
+ }
47
+ }
@@ -128,8 +128,8 @@ function parseNpmView(stdout) {
128
128
  const parsed = JSON.parse(raw);
129
129
  const dist = parsed?.dist || parsed;
130
130
  return {
131
- integrity: dist?.integrity || "",
132
- shasum: dist?.shasum || "",
131
+ integrity: dist?.integrity || parsed?.["dist.integrity"] || "",
132
+ shasum: dist?.shasum || parsed?.["dist.shasum"] || "",
133
133
  };
134
134
  }
135
135
 
@@ -66,8 +66,8 @@ export function resolvePromotionChannel({
66
66
  }
67
67
  const shellRef = intent.shellChannel === "alpha" ? `v${selected.major}-alpha` : `v${selected.major}`;
68
68
  return {
69
- targetRef: intent.targetRef,
70
- publicationChannel: intent.publicationChannel,
69
+ targetRef: intent.targetRef, publicationChannel: intent.publicationChannel,
70
+ routerRef: normalized(routerRef), routerSha: normalized(routerSha).toLowerCase(),
71
71
  channel: intent.shellChannel,
72
72
  major: selected.major,
73
73
  shellRef,
@@ -99,8 +99,8 @@ function readPackageVersion(cwd) {
99
99
 
100
100
  function writeOutputs(file, result) {
101
101
  const outputs = {
102
- "target-ref": result.targetRef,
103
- "publication-channel": result.publicationChannel,
102
+ "target-ref": result.targetRef, "publication-channel": result.publicationChannel,
103
+ "router-ref": result.routerRef, "router-sha": result.routerSha,
104
104
  channel: result.channel,
105
105
  major: String(result.major),
106
106
  "shell-ref": result.shellRef,