@kungfu-tech/buildchain 3.0.9-alpha.8 → 4.0.0-alpha.0

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 (255) hide show
  1. package/AGENTS.md +39 -0
  2. package/README.md +17 -13
  3. package/actions/promote-buildchain-ref/README.md +0 -10
  4. package/actions/release-tail/README.md +6 -10
  5. package/architecture/internal-capabilities.json +502 -0
  6. package/architecture/maintainability-baseline.json +59 -0
  7. package/architecture/maintainability-policy.json +1384 -0
  8. package/architecture/release-tail-contract-inventory.json +587 -0
  9. package/architecture/v3-core-mechanism-inventory.json +1043 -0
  10. package/architecture/v3-core-mechanism-inventory.md +67 -0
  11. package/architecture/v4-adopter-delivery-parity.json +47 -0
  12. package/architecture/v4-architecture-constitution.md +210 -0
  13. package/architecture/v4-bootstrap-authority.json +56 -0
  14. package/architecture/v4-canonical-contract-fixtures.json +105 -0
  15. package/architecture/v4-capability-state-machine-manifest.json +480 -0
  16. package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
  17. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
  18. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
  19. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
  20. package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
  21. package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
  22. package/architecture/v4-exception-ledger.json +5 -0
  23. package/architecture/v4-exception-ledger.schema.json +56 -0
  24. package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
  25. package/architecture/v4-platform-stage-checkpoints.json +97 -0
  26. package/architecture/v4-provider-operation-journal-contract.json +113 -0
  27. package/architecture/v4-release-activation-shadow-domain.json +61 -0
  28. package/architecture/v4-release-train-parity.json +68 -0
  29. package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
  30. package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
  31. package/architecture/v4-stable-publication-fence.json +66 -0
  32. package/architecture/v4-stage-capsule-contract.json +90 -0
  33. package/architecture/v4-stage-capsule-qualification.json +99 -0
  34. package/architecture/v4-stage-capsule-resume-planner.json +52 -0
  35. package/architecture/v4-stage-capsule-store-contract.json +72 -0
  36. package/bin/buildchain.mjs +15 -6
  37. package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
  38. package/contracts/buildchain-v2-residuals-v1.json +2 -2
  39. package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
  40. package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
  41. package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
  42. package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
  43. package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
  44. package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
  45. package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
  46. package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
  47. package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
  48. package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
  49. package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
  50. package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
  51. package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
  52. package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
  53. package/contracts/v4-host-contract-v1.schema.json +135 -0
  54. package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
  55. package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
  56. package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
  57. package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
  58. package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
  59. package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
  60. package/contracts/v4-stage-capsule-v1.schema.json +178 -0
  61. package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
  62. package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
  63. package/crates/buildchain-v4-bridge/src/main.rs +495 -0
  64. package/dist/site/agent-index.json +0 -3
  65. package/dist/site/artifact-schemas.json +0 -6
  66. package/dist/site/buildchain-contract.json +103 -397
  67. package/dist/site/buildchain-site.json +302 -270
  68. package/dist/site/capability-registry.json +9 -11
  69. package/dist/site/cli-registry.json +8 -30
  70. package/dist/site/controller-registry.json +48 -8
  71. package/dist/site/kfd-claims.json +129 -142
  72. package/dist/site/kfd-upstream-aggregate.json +9 -9
  73. package/dist/site/manual-registry.json +18 -49
  74. package/dist/site/node-api-registry.json +1882 -4118
  75. package/dist/site/page-registry.json +278 -230
  76. package/dist/site/public-surface-audit.json +87 -185
  77. package/dist/site/publication-authority-registry.json +11 -1
  78. package/dist/site/publication-registry.json +4 -4
  79. package/dist/site/release-provenance.json +3 -7
  80. package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
  81. package/dist/site/site-manifest.json +22 -38
  82. package/dist/site/workflow-registry.json +80 -35
  83. package/docs/MAP.md +8 -4
  84. package/docs/auditable-demo.md +2 -2
  85. package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
  86. package/docs/cli-reference.md +15 -92
  87. package/docs/cli.md +14 -14
  88. package/docs/dev-alpha-candidate-patrol.md +3 -11
  89. package/docs/dev-delivery-warrant.md +28 -100
  90. package/docs/engineering-housekeeper.md +12 -4
  91. package/docs/getting-started.md +1 -1
  92. package/docs/install.md +5 -5
  93. package/docs/kfd-support.md +2 -7
  94. package/docs/lifecycle-protocol.md +2 -4
  95. package/docs/node-api-reference.md +414 -523
  96. package/docs/release-flow.md +31 -31
  97. package/docs/release-governance.md +16 -28
  98. package/docs/release-passport.md +2 -15
  99. package/docs/release-tail-contract.md +13 -6
  100. package/docs/release-tail-provider-plane.md +13 -19
  101. package/docs/release-train.md +4 -4
  102. package/docs/reusable-build-surface.md +14 -27
  103. package/docs/site-bundle-contract.md +1 -5
  104. package/docs/stable-candidate-patrol.md +7 -7
  105. package/docs/v4-canonical-contracts.md +92 -0
  106. package/docs/v4-delivery-warrant-read-candidate.md +73 -0
  107. package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
  108. package/docs/v4-production-release.md +63 -0
  109. package/docs/v4-stage-capsule.md +193 -0
  110. package/docs/versioning.md +3 -3
  111. package/package.json +19 -17
  112. package/packages/core/adopter-delivery-gate.js +576 -0
  113. package/packages/core/adopter-delivery-json.js +66 -0
  114. package/packages/core/artifact-signing.js +0 -61
  115. package/packages/core/artifact-verification-envelope.js +5 -5
  116. package/packages/core/build-facts.js +12 -4
  117. package/packages/core/buildchain-agent-manuals.js +0 -2
  118. package/packages/core/buildchain-config.js +4 -69
  119. package/packages/core/buildchain-contract.js +232 -46
  120. package/packages/core/buildchain-kfd-claims.js +1 -1
  121. package/packages/core/channel-candidate.js +0 -8
  122. package/packages/core/channel-promotion-baseline.js +0 -52
  123. package/packages/core/controller-evidence.js +3 -4
  124. package/packages/core/dev-alpha-candidate-selection.js +2 -10
  125. package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
  126. package/packages/core/dev-delivery-warrant-settlement.js +1 -5
  127. package/packages/core/dev-delivery-warrant.js +12 -52
  128. package/packages/core/diagnostics.js +3 -8
  129. package/packages/core/github-governance-authority.js +3 -1
  130. package/packages/core/kfd-adopter-category-driver.js +167 -0
  131. package/packages/core/kfd-adopter-manifest.js +47 -46
  132. package/packages/core/kfd-gate.js +15 -45
  133. package/packages/core/paper-agent-entry.js +7 -16
  134. package/packages/core/paper-fleet.js +2 -2
  135. package/packages/core/paper-repository.js +2 -3
  136. package/packages/core/paper-scaffold-content.js +2 -23
  137. package/packages/core/paper.js +11 -43
  138. package/packages/core/publication-reproducibility.js +4 -4
  139. package/packages/core/release-line-bootstrap.js +21 -8
  140. package/packages/core/release-passport-contract.js +5 -5
  141. package/packages/core/release-passport.js +20 -131
  142. package/packages/core/self-dogfood-version.js +48 -35
  143. package/packages/core/spawn-command.js +232 -8
  144. package/packages/core/v4-adopter-delivery-parity.js +219 -0
  145. package/packages/core/v4-canonical-contracts.js +284 -0
  146. package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
  147. package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
  148. package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
  149. package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
  150. package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
  151. package/packages/core/v4-platform-stage-checkpoints.js +471 -0
  152. package/packages/core/v4-provider-operation-journal.js +531 -0
  153. package/packages/core/v4-provider-readback-idempotency.js +394 -0
  154. package/packages/core/v4-release-activation-shadow.js +519 -0
  155. package/packages/core/v4-stable-publication-fence.js +357 -0
  156. package/packages/core/v4-stage-capsule-local-store.js +426 -0
  157. package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
  158. package/packages/core/v4-stage-capsule-qualification.js +456 -0
  159. package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
  160. package/packages/core/v4-stage-capsule-store.js +401 -0
  161. package/packages/core/v4-stage-capsule.js +319 -0
  162. package/scripts/assemble-self-publication-admission.mjs +1 -1
  163. package/scripts/audit-publication-control-plane.mjs +5 -5
  164. package/scripts/auditable-demo-bundle-verification.mjs +3 -2
  165. package/scripts/auditable-demo-platform.mjs +2 -2
  166. package/scripts/auditable-demo-renditions.mjs +1 -1
  167. package/scripts/auditable-demo.mjs +2 -2
  168. package/scripts/aws-macos-jit-controller-core.mjs +13 -346
  169. package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
  170. package/scripts/aws-macos-jit-controller.mjs +37 -275
  171. package/scripts/aws-macos-jit-core.mjs +3 -41
  172. package/scripts/aws-macos-jit.mjs +0 -12
  173. package/scripts/aws-windows-jit-campaign.mjs +2 -2
  174. package/scripts/aws-windows-jit-controller-core.mjs +1 -1
  175. package/scripts/aws-windows-jit-controller.mjs +3 -3
  176. package/scripts/build-contract-core.mjs +3 -8
  177. package/scripts/build-standalone-binary.mjs +3 -14
  178. package/scripts/buildchain-channel-router.mjs +4 -0
  179. package/scripts/buildchain-cli-help.mjs +7 -13
  180. package/scripts/buildchain-contract-lock.mjs +0 -6
  181. package/scripts/check-action-bundles.mjs +3 -4
  182. package/scripts/check-internal-architecture.mjs +7 -6
  183. package/scripts/check-inventory.mjs +30 -25
  184. package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
  185. package/scripts/create-release-bundle.mjs +8 -4
  186. package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
  187. package/scripts/dev-delivery-proof.mjs +2 -54
  188. package/scripts/dev-delivery-warrant.mjs +59 -77
  189. package/scripts/dev-pr-auto-merge.mjs +4 -30
  190. package/scripts/dev-pr-delivery-warrant.mjs +0 -55
  191. package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
  192. package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
  193. package/scripts/generate-channel-build-workflow.mjs +54 -24
  194. package/scripts/generate-channel-promotion-workflow.mjs +15 -6
  195. package/scripts/generate-site-bundle.mjs +5 -21
  196. package/scripts/git-fetch-process-tree.mjs +34 -1
  197. package/scripts/init-repo.mjs +64 -159
  198. package/scripts/inspect-artifact-signing-requests.mjs +0 -6
  199. package/scripts/locked-source-checkout.mjs +3 -4
  200. package/scripts/maintainability-metrics.mjs +8 -2
  201. package/scripts/npm-publish-dry-run.mjs +2 -2
  202. package/scripts/npm-publish-transaction.mjs +2 -2
  203. package/scripts/publication-commit-evidence.mjs +23 -69
  204. package/scripts/release-candidate-resolver.mjs +11 -17
  205. package/scripts/release-tail.mjs +3 -49
  206. package/scripts/resume-from-candidate-run.mjs +9 -123
  207. package/scripts/run-lifecycle-core.mjs +12 -13
  208. package/scripts/seal-artifact-signing-requests.mjs +17 -21
  209. package/scripts/site-capability-metadata.mjs +2 -7
  210. package/scripts/stable-candidate-qualification.mjs +10 -10
  211. package/scripts/v4-architecture.mjs +12 -2
  212. package/scripts/v4-bridge-bootstrap.mjs +141 -0
  213. package/scripts/v4-bridge-evidence.mjs +141 -0
  214. package/scripts/v4-host-adapter.mjs +182 -0
  215. package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
  216. package/scripts/v4-stage-capsule-qualification.mjs +528 -0
  217. package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
  218. package/scripts/v4-warrant-shadow-plan.mjs +601 -0
  219. package/scripts/verify-golden-path.mjs +5 -5
  220. package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
  221. package/scripts/web-surface-core.mjs +2 -8
  222. package/scripts/workflow-call-contract.mjs +5 -184
  223. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
  224. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
  225. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
  226. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
  227. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
  228. package/contracts/next-development-request-v1.schema.json +0 -66
  229. package/contracts/next-development-transition-v1.schema.json +0 -292
  230. package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
  231. package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
  232. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
  233. package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
  234. package/docs/next-development-transition.md +0 -119
  235. package/docs/publication-rehearsal.md +0 -94
  236. package/packages/core/buildchain-compatibility-proof.js +0 -631
  237. package/packages/core/dev-delivery-native-proof.js +0 -333
  238. package/packages/core/dev-delivery-warrant-shadow.js +0 -502
  239. package/packages/core/next-development-candidate-reservation.js +0 -186
  240. package/packages/core/next-development-controller.js +0 -726
  241. package/packages/core/next-development-projection.js +0 -288
  242. package/packages/core/next-development-transition.js +0 -738
  243. package/packages/core/publication-rehearsal-projection.js +0 -173
  244. package/packages/core/publication-rehearsal-runtime.js +0 -921
  245. package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
  246. package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
  247. package/scripts/dev-delivery-native-run.mjs +0 -187
  248. package/scripts/dev-delivery-two-phase.mjs +0 -598
  249. package/scripts/generate-next-development-guidance.mjs +0 -49
  250. package/scripts/materialize-self-release-candidate-version.mjs +0 -137
  251. package/scripts/next-development-self-dogfood-harness.mjs +0 -409
  252. package/scripts/next-development-self-dogfood.mjs +0 -532
  253. package/scripts/next-development-transition.mjs +0 -47
  254. package/scripts/release-candidate-tail-reseal.mjs +0 -426
  255. package/templates/native-dev-delivery.yml +0 -82
@@ -1,726 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- import {
4
- advanceNextDevelopmentTransition,
5
- bindNextDevelopmentAnchor,
6
- nextDevelopmentRoot,
7
- recordNextDevelopmentMaterialization,
8
- validateNextDevelopmentTransition,
9
- } from "./next-development-transition.js";
10
-
11
- export const NEXT_DEVELOPMENT_CONTROLLER_CONTRACT =
12
- "kungfu-buildchain-next-development-controller/v1";
13
-
14
- const ROOT = /^sha256:[0-9a-f]{64}$/u;
15
- const SHA = /^[0-9a-f]{40}$/u;
16
-
17
- function exactRoot(value, label) {
18
- const normalized = String(value || "")
19
- .trim()
20
- .toLowerCase();
21
- if (!ROOT.test(normalized)) {
22
- throw new Error(`${label} must be a sha256 content root`);
23
- }
24
- return normalized;
25
- }
26
-
27
- function exactSha(value, label) {
28
- const normalized = String(value || "")
29
- .trim()
30
- .toLowerCase();
31
- if (!SHA.test(normalized)) {
32
- throw new Error(`${label} must be an exact 40-character Git SHA`);
33
- }
34
- return normalized;
35
- }
36
-
37
- function requiredString(value, label) {
38
- const normalized = String(value || "").trim();
39
- if (!normalized) throw new Error(`${label} must be a non-empty string`);
40
- return normalized;
41
- }
42
-
43
- function protectedBranch(value) {
44
- const normalized = requiredString(value, "protectedDev.branch");
45
- if (
46
- normalized.startsWith("refs/") ||
47
- normalized.startsWith("/") ||
48
- normalized.endsWith("/") ||
49
- normalized.includes("..") ||
50
- /\s/u.test(normalized)
51
- ) {
52
- throw new Error("protectedDev.branch must be an unambiguous branch name");
53
- }
54
- return normalized;
55
- }
56
-
57
- function sortedUnique(values, label) {
58
- if (!Array.isArray(values)) throw new Error(`${label} must be an array`);
59
- const normalized = values.map((value, index) =>
60
- requiredString(value, `${label}[${index}]`),
61
- );
62
- const expected = [...new Set(normalized)].sort();
63
- if (
64
- expected.length !== normalized.length ||
65
- expected.some((value, index) => value !== normalized[index])
66
- ) {
67
- throw new Error(`${label} must be sorted and duplicate-free`);
68
- }
69
- return normalized;
70
- }
71
-
72
- function rootedPaths(values, expectedPaths, label, { version = false } = {}) {
73
- if (!Array.isArray(values)) throw new Error(`${label} must be an array`);
74
- const normalized = values.map((entry, index) => ({
75
- path: requiredString(entry?.path, `${label}[${index}].path`),
76
- root: exactRoot(entry?.root, `${label}[${index}].root`),
77
- ...(version
78
- ? {
79
- version: requiredString(entry?.version, `${label}[${index}].version`),
80
- }
81
- : {}),
82
- }));
83
- normalized.sort((left, right) => left.path.localeCompare(right.path));
84
- if (
85
- JSON.stringify(normalized.map((entry) => entry.path)) !==
86
- JSON.stringify(expectedPaths)
87
- ) {
88
- throw new Error(`${label} must exactly cover its declared paths`);
89
- }
90
- return normalized;
91
- }
92
-
93
- function controllerBody(record) {
94
- const body = structuredClone(record);
95
- delete body.controllerRoot;
96
- return body;
97
- }
98
-
99
- export function nextDevelopmentControllerRoot(record) {
100
- return nextDevelopmentRoot(controllerBody(record));
101
- }
102
-
103
- export function nextDevelopmentChildKey(transition) {
104
- const current = validateNextDevelopmentTransition(transition);
105
- return nextDevelopmentRoot({
106
- contract: NEXT_DEVELOPMENT_CONTROLLER_CONTRACT,
107
- repository: current.repository,
108
- completedAlphaRoot: current.completedAlphaRoot,
109
- });
110
- }
111
-
112
- function refreshController(record) {
113
- const next = structuredClone(record);
114
- next.controllerRoot = nextDevelopmentControllerRoot(next);
115
- return next;
116
- }
117
-
118
- export function createNextDevelopmentController({
119
- transition,
120
- protectedDevBranch,
121
- } = {}) {
122
- const child = validateNextDevelopmentTransition(transition);
123
- if (!["planned", "waiting-anchor"].includes(child.state.status)) {
124
- throw new Error(
125
- "new next-development controller requires an initial child transition",
126
- );
127
- }
128
- return refreshController({
129
- schemaVersion: 1,
130
- contract: NEXT_DEVELOPMENT_CONTROLLER_CONTRACT,
131
- childKey: nextDevelopmentChildKey(child),
132
- idempotencyKey: child.idempotencyKey,
133
- repository: child.repository,
134
- protectedDev: { branch: protectedBranch(protectedDevBranch) },
135
- completedAlphaRoot: child.completedAlphaRoot,
136
- alphaOutcome: "preserved-success",
137
- revision: 1,
138
- transition: child,
139
- attempts: [],
140
- activeAttempt: null,
141
- pullRequest: null,
142
- readback: null,
143
- });
144
- }
145
-
146
- function normalizeSourceRoots(values, expectedPaths, targetVersion) {
147
- if (!Array.isArray(values)) {
148
- throw new Error("materialization.sourceRoots must be an array");
149
- }
150
- const normalized = values
151
- .map((entry, index) => ({
152
- path: requiredString(
153
- entry?.path,
154
- `materialization.sourceRoots[${index}].path`,
155
- ),
156
- beforeRoot: exactRoot(
157
- entry?.beforeRoot,
158
- `materialization.sourceRoots[${index}].beforeRoot`,
159
- ),
160
- afterRoot: exactRoot(
161
- entry?.afterRoot,
162
- `materialization.sourceRoots[${index}].afterRoot`,
163
- ),
164
- version: requiredString(
165
- entry?.version,
166
- `materialization.sourceRoots[${index}].version`,
167
- ),
168
- }))
169
- .sort((left, right) => left.path.localeCompare(right.path));
170
- if (
171
- JSON.stringify(normalized.map((entry) => entry.path)) !==
172
- JSON.stringify(expectedPaths)
173
- ) {
174
- throw new Error(
175
- "materialization.sourceRoots must exactly cover declared source paths",
176
- );
177
- }
178
- if (normalized.some((entry) => entry.version !== targetVersion)) {
179
- throw new Error(
180
- "materialization source versions must equal the declared successor",
181
- );
182
- }
183
- return normalized;
184
- }
185
-
186
- function normalizeMaterialization(record, value, baseDevSha, operationKey) {
187
- const transition = record.transition;
188
- const sourceRoots = normalizeSourceRoots(
189
- value?.sourceRoots,
190
- transition.adapter.sourcePaths,
191
- transition.target.version,
192
- );
193
- const derivedRoots = rootedPaths(
194
- value?.derivedRoots || [],
195
- transition.adapter.derivedPaths,
196
- "materialization.derivedRoots",
197
- );
198
- const changedPaths = sortedUnique(
199
- value?.changedPaths || [],
200
- "materialization.changedPaths",
201
- );
202
- const unexpected = changedPaths.filter(
203
- (changedPath) =>
204
- !transition.adapter.allowedChangePaths.includes(changedPath),
205
- );
206
- if (unexpected.length > 0) {
207
- throw new Error(
208
- `materialization changed paths outside declared version state: ${unexpected.join(", ")}`,
209
- );
210
- }
211
- const normalized = {
212
- operationKey,
213
- generation: record.attempts.length + 1,
214
- status: "prepared",
215
- baseDevSha: exactSha(value?.baseDevSha, "materialization.baseDevSha"),
216
- preparedCommitSha: exactSha(value?.commitSha, "materialization.commitSha"),
217
- preparedTreeSha: exactSha(value?.treeSha, "materialization.treeSha"),
218
- targetVersion: transition.target.version,
219
- sourceRoots,
220
- derivedRoots,
221
- changedPaths,
222
- lifecycleEvidenceRoot: exactRoot(
223
- value?.lifecycleEvidenceRoot,
224
- "materialization.lifecycleEvidenceRoot",
225
- ),
226
- };
227
- if (normalized.baseDevSha !== baseDevSha) {
228
- throw new Error(
229
- "materialization must be regenerated from the exact observed protected Dev SHA",
230
- );
231
- }
232
- normalized.materializationRoot = nextDevelopmentRoot({
233
- baseDevSha: normalized.baseDevSha,
234
- preparedCommitSha: normalized.preparedCommitSha,
235
- preparedTreeSha: normalized.preparedTreeSha,
236
- targetVersion: normalized.targetVersion,
237
- sourceRoots: normalized.sourceRoots,
238
- derivedRoots: normalized.derivedRoots,
239
- changedPaths: normalized.changedPaths,
240
- lifecycleEvidenceRoot: normalized.lifecycleEvidenceRoot,
241
- });
242
- return normalized;
243
- }
244
-
245
- function normalizePullRequest(record, value, attempt) {
246
- const number = Number(value?.number);
247
- if (!Number.isSafeInteger(number) || number < 1) {
248
- throw new Error("pullRequest.number must be a positive integer");
249
- }
250
- const status = requiredString(value?.status, "pullRequest.status");
251
- if (!["open", "merged"].includes(status)) {
252
- throw new Error("pullRequest.status must be open or merged");
253
- }
254
- const normalized = {
255
- number,
256
- url: requiredString(value?.url, "pullRequest.url"),
257
- baseBranch: protectedBranch(value?.baseBranch),
258
- headSha: exactSha(value?.headSha, "pullRequest.headSha"),
259
- status,
260
- evidenceRoot: exactRoot(value?.evidenceRoot, "pullRequest.evidenceRoot"),
261
- };
262
- if (
263
- normalized.baseBranch !== record.protectedDev.branch ||
264
- normalized.headSha !== attempt.preparedCommitSha
265
- ) {
266
- throw new Error(
267
- "pull request must bind the protected Dev branch and prepared commit",
268
- );
269
- }
270
- return normalized;
271
- }
272
-
273
- function normalizeReadback(record, value, attempt) {
274
- const normalized = {
275
- devSha: exactSha(value?.devSha, "readback.devSha"),
276
- treeSha: exactSha(value?.treeSha, "readback.treeSha"),
277
- version: requiredString(value?.version, "readback.version"),
278
- containsPreparedCommit: value?.containsPreparedCommit === true,
279
- versionRoots: rootedPaths(
280
- value?.versionRoots,
281
- record.transition.adapter.sourcePaths,
282
- "readback.versionRoots",
283
- ),
284
- derivedRoots: rootedPaths(
285
- value?.derivedRoots || [],
286
- record.transition.adapter.derivedPaths,
287
- "readback.derivedRoots",
288
- ),
289
- evidenceRoot: exactRoot(value?.evidenceRoot, "readback.evidenceRoot"),
290
- };
291
- const expectedVersionRoots = attempt.sourceRoots.map(
292
- ({ path, afterRoot }) => ({
293
- path,
294
- root: afterRoot,
295
- }),
296
- );
297
- normalized.agrees =
298
- normalized.containsPreparedCommit &&
299
- normalized.version === record.transition.target.version &&
300
- JSON.stringify(normalized.versionRoots) ===
301
- JSON.stringify(expectedVersionRoots) &&
302
- JSON.stringify(normalized.derivedRoots) ===
303
- JSON.stringify(attempt.derivedRoots);
304
- return normalized;
305
- }
306
-
307
- function normalizedAttempt(record, attempt, index) {
308
- const normalized = normalizeMaterialization(
309
- { ...record, attempts: Array(index).fill(null) },
310
- {
311
- baseDevSha: attempt?.baseDevSha,
312
- commitSha: attempt?.preparedCommitSha,
313
- treeSha: attempt?.preparedTreeSha,
314
- sourceRoots: attempt?.sourceRoots,
315
- derivedRoots: attempt?.derivedRoots,
316
- changedPaths: attempt?.changedPaths,
317
- lifecycleEvidenceRoot: attempt?.lifecycleEvidenceRoot,
318
- },
319
- attempt?.baseDevSha,
320
- exactRoot(attempt?.operationKey, `attempts[${index}].operationKey`),
321
- );
322
- if (
323
- !["prepared", "pr-pending", "merged", "superseded"].includes(
324
- attempt?.status,
325
- )
326
- ) {
327
- throw new Error(`attempts[${index}].status is invalid`);
328
- }
329
- normalized.status = attempt.status;
330
- if (attempt.materializationRoot !== normalized.materializationRoot) {
331
- throw new Error(`attempts[${index}].materializationRoot drifted`);
332
- }
333
- return normalized;
334
- }
335
-
336
- function validateControllerObservations(record, attempts) {
337
- let activeAttempt = null;
338
- if (record.activeAttempt !== null) {
339
- activeAttempt = attempts.find(
340
- (attempt) => attempt.operationKey === record.activeAttempt.operationKey,
341
- );
342
- if (
343
- !activeAttempt ||
344
- JSON.stringify(activeAttempt) !== JSON.stringify(record.activeAttempt)
345
- ) {
346
- throw new Error("next-development controller active attempt drifted");
347
- }
348
- }
349
- if (record.pullRequest !== null) {
350
- if (!activeAttempt) {
351
- throw new Error(
352
- "next-development controller pull request requires an active attempt",
353
- );
354
- }
355
- const pullRequest = normalizePullRequest(
356
- record,
357
- record.pullRequest,
358
- activeAttempt,
359
- );
360
- if (JSON.stringify(pullRequest) !== JSON.stringify(record.pullRequest)) {
361
- throw new Error("next-development controller pull request drifted");
362
- }
363
- }
364
- if (record.readback !== null) {
365
- if (!activeAttempt) {
366
- throw new Error(
367
- "next-development controller readback requires an active attempt",
368
- );
369
- }
370
- const readback = normalizeReadback(record, record.readback, activeAttempt);
371
- if (JSON.stringify(readback) !== JSON.stringify(record.readback)) {
372
- throw new Error("next-development controller readback drifted");
373
- }
374
- }
375
- if (
376
- record.transition.state.status === "verified" &&
377
- (!record.readback?.agrees || !activeAttempt)
378
- ) {
379
- throw new Error(
380
- "verified next-development controller requires agreeing protected Dev readback",
381
- );
382
- }
383
- }
384
-
385
- export function validateNextDevelopmentController(record) {
386
- if (!record || typeof record !== "object" || Array.isArray(record)) {
387
- throw new Error("next-development controller must be an object");
388
- }
389
- if (record.contract !== NEXT_DEVELOPMENT_CONTROLLER_CONTRACT) {
390
- throw new Error("next-development controller contract mismatch");
391
- }
392
- if (record.schemaVersion !== 1) {
393
- throw new Error("next-development controller schemaVersion must be 1");
394
- }
395
- const transition = validateNextDevelopmentTransition(record.transition);
396
- const expectedChildKey = nextDevelopmentChildKey(transition);
397
- if (
398
- record.childKey !== expectedChildKey ||
399
- record.idempotencyKey !== transition.idempotencyKey ||
400
- record.repository !== transition.repository ||
401
- record.completedAlphaRoot !== transition.completedAlphaRoot
402
- ) {
403
- throw new Error("next-development controller child identity drifted");
404
- }
405
- if (protectedBranch(record.protectedDev?.branch) !== record.protectedDev.branch) {
406
- throw new Error("next-development controller protected Dev branch drifted");
407
- }
408
- if (
409
- record.alphaOutcome !== "preserved-success" ||
410
- transition.alphaOutcome !== "preserved-success" ||
411
- transition.completedAlpha.outcome !== "succeeded"
412
- ) {
413
- throw new Error("next-development controller invalidated completed Alpha");
414
- }
415
- if (!Number.isSafeInteger(record.revision) || record.revision < 1) {
416
- throw new Error("next-development controller revision must be positive");
417
- }
418
- if (!Array.isArray(record.attempts)) {
419
- throw new Error("next-development controller attempts must be an array");
420
- }
421
- const attempts = record.attempts.map((attempt, index) =>
422
- normalizedAttempt(record, attempt, index),
423
- );
424
- if (
425
- new Set(attempts.map((attempt) => attempt.operationKey)).size !==
426
- attempts.length
427
- ) {
428
- throw new Error(
429
- "next-development controller attempt operation keys must be unique",
430
- );
431
- }
432
- validateControllerObservations(record, attempts);
433
- if (record.controllerRoot !== nextDevelopmentControllerRoot(record)) {
434
- throw new Error("next-development controller root drifted");
435
- }
436
- return structuredClone(record);
437
- }
438
-
439
- function assertStore(store) {
440
- for (const method of ["readChild", "createChild", "compareAndSwapChild"]) {
441
- if (typeof store?.[method] !== "function") {
442
- throw new Error(`next-development durable store requires ${method}`);
443
- }
444
- }
445
- }
446
-
447
- function assertExecutor(executor) {
448
- for (const method of [
449
- "readProtectedDev",
450
- "materialize",
451
- "ensurePullRequest",
452
- "readPullRequest",
453
- "readProtectedVersionState",
454
- ]) {
455
- if (typeof executor?.[method] !== "function") {
456
- throw new Error(`next-development executor requires ${method}`);
457
- }
458
- }
459
- }
460
-
461
- function assertSameScheduledChild(existing, requested) {
462
- const current = validateNextDevelopmentController(existing);
463
- if (
464
- current.childKey !== requested.childKey ||
465
- current.idempotencyKey !== requested.idempotencyKey ||
466
- current.protectedDev.branch !== requested.protectedDev.branch
467
- ) {
468
- throw new Error(
469
- "durable next-development child conflicts with the completed Alpha schedule",
470
- );
471
- }
472
- return current;
473
- }
474
-
475
- export async function scheduleNextDevelopmentController(
476
- { transition, protectedDevBranch } = {},
477
- store,
478
- ) {
479
- assertStore(store);
480
- const requested = createNextDevelopmentController({
481
- transition,
482
- protectedDevBranch,
483
- });
484
- const existing = await store.readChild(requested.childKey);
485
- if (existing) return assertSameScheduledChild(existing, requested);
486
- const created = await store.createChild(requested.childKey, requested);
487
- if (!created?.record) {
488
- throw new Error("durable store createChild must return the stored record");
489
- }
490
- return assertSameScheduledChild(created.record, requested);
491
- }
492
-
493
- async function persist(record, store) {
494
- const expectedRoot = exactRoot(record?.controllerRoot, "controllerRoot");
495
- const current = structuredClone(record);
496
- current.controllerRoot = nextDevelopmentControllerRoot(current);
497
- validateNextDevelopmentController(current);
498
- const next = structuredClone(current);
499
- next.revision += 1;
500
- next.controllerRoot = nextDevelopmentControllerRoot(next);
501
- const stored = await store.compareAndSwapChild(
502
- next.childKey,
503
- expectedRoot,
504
- next,
505
- );
506
- return validateNextDevelopmentController(stored?.record || stored);
507
- }
508
-
509
- function replaceActiveAttempt(record, attempt) {
510
- const next = structuredClone(record);
511
- const index = next.attempts.findIndex(
512
- (candidate) => candidate.operationKey === attempt.operationKey,
513
- );
514
- if (index < 0) next.attempts.push(attempt);
515
- else next.attempts[index] = attempt;
516
- next.activeAttempt = attempt.status === "superseded" ? null : attempt;
517
- return next;
518
- }
519
-
520
- async function checkpoint(fault, name, record) {
521
- if (typeof fault === "function") await fault(name, structuredClone(record));
522
- }
523
-
524
- function transitionMaterialization(attempt, transition) {
525
- const body = {
526
- targetVersion: transition.target.version,
527
- anchorRoot: transition.target.anchor?.manifestRoot || null,
528
- paths: attempt.sourceRoots.map((entry) => ({
529
- path: entry.path,
530
- beforeRoot: entry.beforeRoot,
531
- afterRoot: entry.afterRoot,
532
- changed: entry.beforeRoot !== entry.afterRoot,
533
- })),
534
- };
535
- return {
536
- ...body,
537
- materializationRoot: nextDevelopmentRoot(body),
538
- };
539
- }
540
-
541
- function transitionEvidence(attempt) {
542
- return nextDevelopmentRoot({
543
- materializationRoot: attempt.materializationRoot,
544
- lifecycleEvidenceRoot: attempt.lifecycleEvidenceRoot,
545
- });
546
- }
547
-
548
- export async function reconcileNextDevelopmentController(
549
- record,
550
- { store, executor, reviewedInput, recordedAt, fault } = {},
551
- ) {
552
- assertStore(store);
553
- assertExecutor(executor);
554
- let current = validateNextDevelopmentController(record);
555
- if (current.transition.state.status === "verified") return current;
556
-
557
- if (
558
- current.transition.state.status === "waiting-anchor" &&
559
- !current.transition.target.version
560
- ) {
561
- if (!reviewedInput) return current;
562
- current.transition = bindNextDevelopmentAnchor(
563
- current.transition,
564
- reviewedInput,
565
- );
566
- current = await persist(current, store);
567
- await checkpoint(fault, "anchor-bound", current);
568
- }
569
-
570
- if (!current.activeAttempt) {
571
- const observed = await executor.readProtectedDev({
572
- repository: current.repository,
573
- branch: current.protectedDev.branch,
574
- });
575
- const baseDevSha = exactSha(observed?.sha, "protectedDev.sha");
576
- const operationKey = nextDevelopmentRoot({
577
- childKey: current.childKey,
578
- baseDevSha,
579
- target: current.transition.target,
580
- });
581
- const materialized = await executor.materialize({
582
- operationKey,
583
- repository: current.repository,
584
- branch: current.protectedDev.branch,
585
- baseDevSha,
586
- targetVersion: current.transition.target.version,
587
- anchor: current.transition.target.anchor,
588
- adapter: structuredClone(current.transition.adapter),
589
- });
590
- const attempt = normalizeMaterialization(
591
- current,
592
- materialized,
593
- baseDevSha,
594
- operationKey,
595
- );
596
- current = replaceActiveAttempt(current, attempt);
597
- current = await persist(current, store);
598
- await checkpoint(fault, "materialized", current);
599
- }
600
-
601
- let attempt = current.activeAttempt;
602
- const latest = await executor.readProtectedDev({
603
- repository: current.repository,
604
- branch: current.protectedDev.branch,
605
- });
606
- const latestDevSha = exactSha(latest?.sha, "protectedDev.sha");
607
- if (latestDevSha !== attempt.baseDevSha && !current.pullRequest) {
608
- attempt = { ...attempt, status: "superseded" };
609
- current = replaceActiveAttempt(current, attempt);
610
- current = await persist(current, store);
611
- await checkpoint(fault, "dev-advanced", current);
612
- return current;
613
- }
614
-
615
- if (["planned", "waiting-anchor"].includes(current.transition.state.status)) {
616
- current.transition = recordNextDevelopmentMaterialization(
617
- current.transition,
618
- {
619
- materialization: transitionMaterialization(attempt, current.transition),
620
- evidenceRoot: transitionEvidence(attempt),
621
- recordedAt,
622
- },
623
- );
624
- current = await persist(current, store);
625
- await checkpoint(fault, "materialization-recorded", current);
626
- }
627
-
628
- if (!current.pullRequest) {
629
- const ensured = await executor.ensurePullRequest({
630
- operationKey: current.childKey,
631
- repository: current.repository,
632
- baseBranch: current.protectedDev.branch,
633
- headSha: attempt.preparedCommitSha,
634
- targetVersion: current.transition.target.version,
635
- completedAlphaRoot: current.completedAlphaRoot,
636
- });
637
- current.pullRequest = normalizePullRequest(current, ensured, attempt);
638
- attempt = { ...attempt, status: "pr-pending" };
639
- current = replaceActiveAttempt(current, attempt);
640
- if (current.transition.state.status === "materialized") {
641
- current.transition = advanceNextDevelopmentTransition(
642
- current.transition,
643
- {
644
- to: "pr-pending",
645
- event: "protected-version-pr-opened",
646
- expectedStateRoot: current.transition.state.stateRoot,
647
- evidenceRoot: current.pullRequest.evidenceRoot,
648
- recordedAt,
649
- },
650
- );
651
- }
652
- current = await persist(current, store);
653
- await checkpoint(fault, "pull-request-recorded", current);
654
- }
655
-
656
- const pullRequest = normalizePullRequest(
657
- current,
658
- await executor.readPullRequest({
659
- repository: current.repository,
660
- number: current.pullRequest.number,
661
- }),
662
- current.activeAttempt,
663
- );
664
- current.pullRequest = pullRequest;
665
- if (pullRequest.status !== "merged") return current;
666
- if (current.transition.state.status === "pr-pending") {
667
- current.transition = advanceNextDevelopmentTransition(current.transition, {
668
- to: "merged",
669
- event: "protected-version-pr-merged",
670
- expectedStateRoot: current.transition.state.stateRoot,
671
- evidenceRoot: pullRequest.evidenceRoot,
672
- recordedAt,
673
- });
674
- attempt = { ...current.activeAttempt, status: "merged" };
675
- current = replaceActiveAttempt(current, attempt);
676
- current = await persist(current, store);
677
- await checkpoint(fault, "merge-recorded", current);
678
- }
679
-
680
- const readback = normalizeReadback(
681
- current,
682
- await executor.readProtectedVersionState({
683
- repository: current.repository,
684
- branch: current.protectedDev.branch,
685
- sourcePaths: current.transition.adapter.sourcePaths,
686
- derivedPaths: current.transition.adapter.derivedPaths,
687
- targetVersion: current.transition.target.version,
688
- preparedCommitSha: current.activeAttempt.preparedCommitSha,
689
- }),
690
- current.activeAttempt,
691
- );
692
- current.readback = readback;
693
- if (!readback.agrees) {
694
- current = await persist(current, store);
695
- await checkpoint(fault, "readback-mismatch", current);
696
- return current;
697
- }
698
- current.transition = advanceNextDevelopmentTransition(current.transition, {
699
- to: "verified",
700
- event: "protected-dev-version-state-readback-verified",
701
- expectedStateRoot: current.transition.state.stateRoot,
702
- evidenceRoot: readback.evidenceRoot,
703
- recordedAt,
704
- });
705
- current = await persist(current, store);
706
- await checkpoint(fault, "verified", current);
707
- return current;
708
- }
709
-
710
- export async function runNextDevelopmentController(
711
- { transition, protectedDevBranch, reviewedInput, recordedAt, fault } = {},
712
- { store, executor } = {},
713
- ) {
714
- const scheduled = await scheduleNextDevelopmentController(
715
- { transition, protectedDevBranch },
716
- store,
717
- );
718
- await checkpoint(fault, "scheduled", scheduled);
719
- return reconcileNextDevelopmentController(scheduled, {
720
- store,
721
- executor,
722
- reviewedInput,
723
- recordedAt,
724
- fault,
725
- });
726
- }