@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
@@ -11,10 +11,6 @@ import {
11
11
  writeReleaseTailTransaction,
12
12
  } from "../packages/core/release-tail-provider-plane.js";
13
13
  import { diagnoseLegacyReleaseTailHooks } from "../packages/core/release-tail-compatibility.js";
14
- import {
15
- executePublicationRehearsal,
16
- publicationRehearsalDiagnostic,
17
- } from "../packages/core/publication-rehearsal-runtime.js";
18
14
 
19
15
  function flag(args, name, fallback = "") {
20
16
  const index = args.indexOf(`--${name}`);
@@ -38,50 +34,8 @@ function output(value, filePath = "") {
38
34
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
39
35
  }
40
36
 
41
- export async function runReleaseTailCli(args = process.argv.slice(2)) {
37
+ export function runReleaseTailCli(args = process.argv.slice(2)) {
42
38
  const [mode = "status", ...options] = args;
43
- if (mode === "rehearse") {
44
- const capsulePath = flag(options, "capsule");
45
- const capsuleRoot = flag(options, "capsule-root");
46
- const statePath = flag(options, "state");
47
- const evidencePath = flag(options, "evidence");
48
- for (const [name, value] of Object.entries({
49
- "--capsule": capsulePath,
50
- "--capsule-root": capsuleRoot,
51
- "--state": statePath,
52
- "--evidence": evidencePath,
53
- })) {
54
- if (!value || !path.isAbsolute(value)) {
55
- throw new Error(`${name} must be an explicit absolute path`);
56
- }
57
- }
58
- const capsule = readJson(capsulePath, "--capsule");
59
- const rehearsalMode = flag(options, "mode", "simulate");
60
- if (!new Set(["simulate", "replay"]).has(rehearsalMode)) {
61
- throw new Error("local rehearsal --mode must be simulate or replay");
62
- }
63
- const environment = flag(options, "environment-json")
64
- ? readJson(flag(options, "environment-json"), "--environment-json")
65
- : {};
66
- let result;
67
- try {
68
- result = await executePublicationRehearsal({
69
- capsule,
70
- capsuleRoot,
71
- mode: rehearsalMode,
72
- environment,
73
- checkpoint: (transaction) =>
74
- writeReleaseTailTransaction(statePath, transaction),
75
- });
76
- } catch (error) {
77
- const diagnostic = publicationRehearsalDiagnostic(error, { capsule });
78
- output(diagnostic, evidencePath);
79
- throw error;
80
- }
81
- writeReleaseTailTransaction(statePath, result.transaction);
82
- output(result.evidence, evidencePath);
83
- return result;
84
- }
85
39
  if (mode === "plan") {
86
40
  const plan = compileReleaseTailDeclaration(
87
41
  readJson(flag(options, "declaration"), "--declaration"),
@@ -141,7 +95,7 @@ export async function runReleaseTailCli(args = process.argv.slice(2)) {
141
95
  return report;
142
96
  }
143
97
  throw new Error(
144
- "usage: buildchain release-tail <plan|init|status|verify|compat|rehearse> [--declaration <json-or-path>] [--capsule <path>] [--capsule-root <absolute-path>] [--mode <simulate|replay>] [--state <path>] [--evidence <path>] [--hooks-json <json-or-path>] [--output <path>]",
98
+ "usage: buildchain release-tail <plan|init|status|verify|compat> [--declaration <json-or-path>] [--state <path>] [--hooks-json <json-or-path>] [--output <path>]",
145
99
  );
146
100
  }
147
101
 
@@ -151,7 +105,7 @@ if (
151
105
  import.meta.url === pathToFileURL(process.argv[1]).href
152
106
  ) {
153
107
  try {
154
- await runReleaseTailCli();
108
+ runReleaseTailCli();
155
109
  } catch (error) {
156
110
  console.error(`release-tail: ${error.message}`);
157
111
  process.exitCode = 1;
@@ -95,18 +95,12 @@ function findFiles(root, predicate) {
95
95
  return collectFiles(root).filter((file) => predicate(file.path, file.absolutePath));
96
96
  }
97
97
 
98
- export function recoveredArtifactPathsByBasename(downloads, filename) {
99
- return downloads.flatMap((download) => download.files
100
- .filter((file) => path.basename(file.path) === filename)
101
- .map((file) => outputPath(file.absolutePath)));
102
- }
103
-
104
98
  function readOnlyJson(files, label) {
105
99
  if (files.length !== 1) throw new Error(`expected exactly one ${label}, found ${files.length}`);
106
100
  return JSON.parse(fs.readFileSync(files[0].absolutePath, "utf8"));
107
101
  }
108
102
 
109
- export async function readExistingTransaction({ repoInfo, apiUrl, token, fetchImpl, version }) {
103
+ async function readExistingTransaction({ repoInfo, apiUrl, token, fetchImpl, version }) {
110
104
  const stateRef = releaseTransactionStateRef(version);
111
105
  const response = await githubJson({
112
106
  apiUrl,
@@ -116,31 +110,10 @@ export async function readExistingTransaction({ repoInfo, apiUrl, token, fetchIm
116
110
  path: `/repos/${repoInfo.owner}/${repoInfo.repo}/contents/state.json?ref=${encodeURIComponent(stateRef)}`,
117
111
  });
118
112
  if (!response) return undefined;
119
- if (response.type !== "file") {
120
- throw new Error(`durable transaction ${stateRef} did not expose a state.json file`);
121
- }
122
- let encoded = response.encoding === "base64" && response.content
123
- ? response
124
- : undefined;
125
- if (!encoded) {
126
- const blobSha = String(response.sha || "").trim();
127
- if (!/^[0-9a-f]{40}$/i.test(blobSha)) {
128
- throw new Error(`durable transaction ${stateRef} did not expose inline content or an exact blob identity`);
129
- }
130
- encoded = await githubJson({
131
- apiUrl,
132
- token,
133
- fetchImpl,
134
- path: `/repos/${repoInfo.owner}/${repoInfo.repo}/git/blobs/${blobSha}`,
135
- });
136
- if (String(encoded?.sha || "").trim() !== blobSha) {
137
- throw new Error(`durable transaction ${stateRef} blob identity drifted from ${blobSha}`);
138
- }
139
- }
140
- if (encoded?.encoding !== "base64" || !encoded.content) {
141
- throw new Error(`durable transaction ${stateRef} did not expose base64 state.json content`);
113
+ if (response.type !== "file" || response.encoding !== "base64" || !response.content) {
114
+ throw new Error(`durable transaction ${stateRef} did not expose a base64 state.json file`);
142
115
  }
143
- return JSON.parse(Buffer.from(String(encoded.content).replace(/\s/g, ""), "base64").toString("utf8"));
116
+ return JSON.parse(Buffer.from(String(response.content).replace(/\s/g, ""), "base64").toString("utf8"));
144
117
  }
145
118
 
146
119
  function outputPath(filePath) {
@@ -207,7 +180,6 @@ function candidateArtifactNames({ passport, selected, artifacts, artifactPattern
207
180
 
208
181
  export function normalizePlatformManifests(downloads, passport) {
209
182
  const manifests = [];
210
- const paths = [];
211
183
  const evidenceByArtifact = new Map();
212
184
  const platformById = new Map((passport.platformMatrix || []).map((entry) => [String(entry.platformId || ""), entry]));
213
185
  const seenPlatformIds = new Set();
@@ -236,7 +208,6 @@ export function normalizePlatformManifests(downloads, passport) {
236
208
  manifest.artifactName = expectedPlatform.artifactName;
237
209
  seenPlatformIds.add(platformId);
238
210
  manifests.push(manifest);
239
- paths.push(file.absolutePath);
240
211
  addEvidence(manifest.artifactName, download.record.files);
241
212
  }
242
213
  if (String(download.artifact.name).includes("-diagnostics-")) {
@@ -251,7 +222,7 @@ export function normalizePlatformManifests(downloads, passport) {
251
222
  artifactName,
252
223
  files: [...files.values()].sort((left, right) => left.path.localeCompare(right.path)),
253
224
  }));
254
- return { manifests, paths, evidence };
225
+ return { manifests, evidence };
255
226
  }
256
227
 
257
228
  function normalizeControllerReceipts(downloads, passport) {
@@ -297,53 +268,16 @@ export function createRecoveredPublicationCandidate({
297
268
  return { ...payload, candidateDigest: publicationArtifactCandidateDigest(payload) };
298
269
  }
299
270
 
300
- export function deduplicateReleaseAssets(files = []) {
301
- const assets = new Map();
302
- for (const file of files) {
303
- const name = path.basename(file.path);
304
- const existing = assets.get(name);
305
- if (!existing) {
306
- assets.set(name, file);
307
- continue;
308
- }
309
- const digest = String(file.sha256 || "").replace(/^sha256:/, "");
310
- const existingDigest = String(existing.sha256 || "").replace(/^sha256:/, "");
311
- if (digest !== existingDigest || Number(file.size) !== Number(existing.size)) {
312
- throw new Error(
313
- `recovered GitHub Release asset basename collision: ${name} maps to different sealed bytes`,
314
- );
315
- }
316
- if (
317
- String(file.path).length < String(existing.path).length ||
318
- (String(file.path).length === String(existing.path).length &&
319
- String(file.path).localeCompare(String(existing.path)) < 0)
320
- ) {
321
- assets.set(name, file);
322
- }
323
- }
324
- return [...assets.values()].sort((left, right) =>
325
- path.basename(left.path).localeCompare(path.basename(right.path)),
326
- );
327
- }
328
-
329
271
  export function createRecoveredPublication({ downloads, bundleRoot, repository, passport, candidateRuntimeSha, publishArtifactKind, publishPackageMain, releasePatterns, platformManifests }) {
330
272
  const allFiles = downloads.flatMap((download) => download.files.map((file) => ({
331
273
  path: path.relative(bundleRoot, file.absolutePath).split(path.sep).join("/"),
332
274
  size: file.size,
333
275
  sha256: file.sha256.replace(/^sha256:/, ""),
334
276
  absolutePath: file.absolutePath,
335
- artifactName: String(download.artifact?.name || ""),
336
277
  }))).sort((left, right) => left.path.localeCompare(right.path));
337
278
  const kind = String(publishArtifactKind || "npm");
338
279
  const releaseMatchers = splitPatterns(releasePatterns).map(patternMatcher);
339
- const platformArtifactNames = new Set(
340
- platformManifests.map((manifest) => String(manifest.artifactName || "")).filter(Boolean),
341
- );
342
- const releaseAssetFiles = kind !== "npm" && platformArtifactNames.size > 0
343
- ? allFiles.filter((file) => platformArtifactNames.has(file.artifactName))
344
- : allFiles;
345
- const releaseAssets = deduplicateReleaseAssets(releaseAssetFiles.filter((file) =>
346
- releaseMatchers.some((matcher) => matcher.test(path.basename(file.path)))));
280
+ const releaseAssets = allFiles.filter((file) => releaseMatchers.some((matcher) => matcher.test(path.basename(file.path))));
347
281
  if (kind !== "npm") {
348
282
  createRecoveredPublicationCandidate({ allFiles, repository, passport, candidateRuntimeSha });
349
283
  const version = String(passport.target?.version || "").trim();
@@ -433,42 +367,6 @@ async function recoverCandidateEvidence({
433
367
  };
434
368
  }
435
369
 
436
- export function exposeRecoveredPayloadRoot({ resolvedOutput, bundleRoot }) {
437
- const recoveredPayloadRoot = path.join(bundleRoot, "artifacts");
438
- if (!fs.existsSync(recoveredPayloadRoot) || !fs.statSync(recoveredPayloadRoot).isDirectory()) {
439
- throw new Error(`recovered candidate payload root is missing: ${recoveredPayloadRoot}`);
440
- }
441
- const compatibilityRoot = path.join(resolvedOutput, "payloads");
442
- try {
443
- fs.lstatSync(compatibilityRoot);
444
- if (fs.realpathSync(compatibilityRoot) !== fs.realpathSync(recoveredPayloadRoot)) {
445
- throw new Error(`recovered candidate payload compatibility path is already occupied: ${compatibilityRoot}`);
446
- }
447
- } catch (error) {
448
- if (error?.code !== "ENOENT") throw error;
449
- fs.symlinkSync(path.relative(resolvedOutput, recoveredPayloadRoot), compatibilityRoot, "dir");
450
- }
451
- return compatibilityRoot;
452
- }
453
-
454
- export function exposeRecoveredPassportPath({ resolvedOutput, recoveredPassportPath }) {
455
- if (!fs.existsSync(recoveredPassportPath) || !fs.statSync(recoveredPassportPath).isFile()) {
456
- throw new Error(`recovered candidate passport is missing: ${recoveredPassportPath}`);
457
- }
458
- const recoveredPassportRoot = path.dirname(recoveredPassportPath);
459
- const compatibilityRoot = path.join(resolvedOutput, "passport");
460
- try {
461
- fs.lstatSync(compatibilityRoot);
462
- if (fs.realpathSync(compatibilityRoot) !== fs.realpathSync(recoveredPassportRoot)) {
463
- throw new Error(`recovered candidate passport compatibility path is already occupied: ${compatibilityRoot}`);
464
- }
465
- } catch (error) {
466
- if (error?.code !== "ENOENT") throw error;
467
- fs.symlinkSync(path.relative(resolvedOutput, recoveredPassportRoot), compatibilityRoot, "dir");
468
- }
469
- return path.join(compatibilityRoot, path.basename(recoveredPassportPath));
470
- }
471
-
472
370
  async function resolveTargetAdvance({ observedTargetSha, targetSha, transactionId, existingTransaction, repoInfo, apiUrl, token, fetchImpl }) {
473
371
  if (observedTargetSha === targetSha || !transactionId || existingTransaction?.id !== transactionId) return undefined;
474
372
  const comparison = await githubJson({ apiUrl, token, fetchImpl, path: `/repos/${repoInfo.owner}/${repoInfo.repo}/compare/${targetSha}...${observedTargetSha}` });
@@ -596,16 +494,7 @@ export async function resumeFromCandidateRun({
596
494
  if (publication.manifest) fs.writeFileSync(sealedManifestPath, `${JSON.stringify(publication.manifest, null, 2)}\n`);
597
495
  const publishRequiredArtifacts = publication.publishRequiredArtifacts;
598
496
  fs.writeFileSync(requiredArtifactsPath, `${JSON.stringify(publishRequiredArtifacts, null, 2)}\n`);
599
- const payloadRoot = exposeRecoveredPayloadRoot({ resolvedOutput, bundleRoot });
600
- const recoveredPassportPath = initialDownloads[0].files.find(
601
- (file) => path.basename(file.path) === "release-candidate-passport.json",
602
- ).absolutePath;
603
- const passportPath = exposeRecoveredPassportPath({ resolvedOutput, recoveredPassportPath });
604
497
  const tarballs = publication.npmArtifacts.map((entry) => outputPath(entry.file.absolutePath));
605
- const githubArtifactAttestationPolicies = recoveredArtifactPathsByBasename(
606
- downloads,
607
- "github-artifact-attestation-policy.json",
608
- );
609
498
  return {
610
499
  enabled: true,
611
500
  action: "reused",
@@ -618,11 +507,10 @@ export async function resumeFromCandidateRun({
618
507
  receipt: recovery.receipt,
619
508
  publishRequiredArtifacts,
620
509
  paths: {
621
- passport: outputPath(passportPath),
510
+ passport: outputPath(initialDownloads[0].files.find((file) => path.basename(file.path) === "release-candidate-passport.json").absolutePath),
622
511
  buildSummary: outputPath(initialDownloads[1].files.find((file) => path.basename(file.path) === "build-summary.json").absolutePath),
623
- payloads: outputPath(payloadRoot),
624
- platformManifests: platformManifestEvidence.paths.map(outputPath),
625
- githubArtifactAttestationPolicies,
512
+ payloads: outputPath(path.join(bundleRoot, "artifacts")),
513
+ platformManifests: downloads.flatMap((download) => download.files.filter((file) => path.basename(file.path) === "manifest.json").map((file) => outputPath(file.absolutePath))),
626
514
  npmTarballs: tarballs,
627
515
  releaseAssets: publication.releaseAssets.map((asset) => outputPath(asset.absolutePath)),
628
516
  publishRequiredArtifacts: outputPath(requiredArtifactsPath),
@@ -672,8 +560,6 @@ export async function resumeFromCandidateRunCli() {
672
560
  "release-candidate-payload-artifacts": result.artifacts.payloads.join(","),
673
561
  "release-candidate-payload-dir": result.paths.payloads,
674
562
  "release-candidate-platform-manifest-paths": result.paths.platformManifests.join(","),
675
- "release-candidate-github-artifact-attestation-policy-paths": result.paths.githubArtifactAttestationPolicies.join(","),
676
- "release-candidate-github-artifact-attestation-policy-count": String(result.paths.githubArtifactAttestationPolicies.length),
677
563
  "release-candidate-npm-tarball-paths": result.paths.npmTarballs.join(","),
678
564
  "release-candidate-github-release-artifact-paths": result.paths.releaseAssets.join("\n"),
679
565
  "publish-required-artifacts-json": JSON.stringify(result.publishRequiredArtifacts),
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { execFileSync, execSync, spawnSync } from "node:child_process";
2
+ import { execFileSync } from "node:child_process";
3
3
  import fs from "node:fs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
@@ -9,6 +9,7 @@ import {
9
9
  normalizeLifecycleStage,
10
10
  runLifecycleStage,
11
11
  } from "../packages/core/buildchain-config.js";
12
+ import { runProcessTreeCommandSync, runShellCommandSync } from "../packages/core/spawn-command.js";
12
13
  import {
13
14
  createBuildchainLogger,
14
15
  readBuildchainLogEvents,
@@ -201,7 +202,7 @@ function shellCommandArgs(command, shell) {
201
202
  return [shell, "-c", command];
202
203
  }
203
204
  if (process.platform === "win32") {
204
- return [process.env.ComSpec || "cmd.exe", "/d", "/s", "/c", command];
205
+ return [process.env.ComSpec || "cmd.exe", "/d", "/s", "/c", `"${command}"`];
205
206
  }
206
207
  return [process.env.SHELL || "/bin/sh", "-c", command];
207
208
  }
@@ -244,16 +245,14 @@ function executeSampledShellCommand({
244
245
  args.push("--requested-parallelism", String(Number(requestedParallelism)));
245
246
  }
246
247
  args.push("--", ...shellCommandArgs(command, shell));
247
- const result = spawnSync(process.execPath, args, {
248
- cwd,
249
- env,
250
- stdio: "inherit",
251
- timeout,
252
- });
253
- if (result.error || result.status !== 0 || result.signal) {
254
- const error = result.error || new Error(`sampled lifecycle command failed with status ${result.status ?? ""}`);
255
- error.status = result.status ?? error.status ?? 1;
256
- error.signal = result.signal || error.signal || "";
248
+ try {
249
+ runProcessTreeCommandSync(process.execPath, args, {
250
+ cwd,
251
+ env,
252
+ stdio: "inherit",
253
+ timeout,
254
+ });
255
+ } catch (error) {
257
256
  attachProcessSampleFailureEvidence(error, processSummaryPath);
258
257
  throw error;
259
258
  }
@@ -565,7 +564,7 @@ export function runLifecycle({
565
564
  timeout: effectiveCommandTimeoutMinutes ? effectiveCommandTimeoutMinutes * 60_000 : undefined,
566
565
  });
567
566
  } else {
568
- execSync(command, {
567
+ runShellCommandSync(command, {
569
568
  cwd: resolvedCwd,
570
569
  env: commandEnv,
571
570
  shell: commandShell,
@@ -162,10 +162,11 @@ function inferKind(subjectPath, declaredKind) {
162
162
  return "binary";
163
163
  }
164
164
 
165
- function run(command, args) {
165
+ function run(command, args, options = {}) {
166
166
  const result = spawnSync(command, args, {
167
167
  encoding: "utf8",
168
168
  stdio: ["ignore", "pipe", "pipe"],
169
+ ...options,
169
170
  });
170
171
  if (result.error || result.status !== 0) {
171
172
  throw (
@@ -205,13 +206,17 @@ function archiveSubject({ subjectPath, outputRoot, id, kind, platform }) {
205
206
  format = "ditto-zip";
206
207
  } else {
207
208
  archivePath = path.join(artifactRoot, "subject.tar");
208
- run("tar", [
209
- "-cf",
210
- archivePath,
211
- "-C",
212
- path.dirname(subjectPath),
213
- path.basename(subjectPath),
214
- ]);
209
+ run(
210
+ "tar",
211
+ [
212
+ "-cf",
213
+ path.basename(archivePath),
214
+ "-C",
215
+ path.dirname(subjectPath),
216
+ path.basename(subjectPath),
217
+ ],
218
+ { cwd: artifactRoot },
219
+ );
215
220
  format = "tar";
216
221
  }
217
222
  return {
@@ -275,19 +280,16 @@ export function sealArtifactSigningRequests({
275
280
  sourceTreeSha = process.env.BUILDCHAIN_SOURCE_TREE_SHA,
276
281
  runtimeSha = process.env.BUILDCHAIN_RUNTIME_SHA,
277
282
  platformId = process.env.BUILDCHAIN_PLATFORM_ID,
278
- requestsEnabled = process.env.BUILDCHAIN_SIGNING_REQUESTS_ENABLED !== "false",
279
283
  } = {}) {
280
284
  const resolvedWorkspace = path.resolve(workspace);
281
285
  const resolvedCwd = path.resolve(resolvedWorkspace, cwd);
282
286
  assertInside(resolvedWorkspace, resolvedCwd, "signing working directory");
283
287
  const loaded = loadBuildchainConfig(resolvedCwd);
284
288
  const declarations = loaded?.config?.signing?.artifacts || [];
285
- const selected = requestsEnabled
286
- ? declarations.filter(
287
- (entry) =>
288
- entry.platforms.length === 0 || entry.platforms.includes(platformId),
289
- )
290
- : [];
289
+ const selected = declarations.filter(
290
+ (entry) =>
291
+ entry.platforms.length === 0 || entry.platforms.includes(platformId),
292
+ );
291
293
  const resolvedOutputRoot = path.resolve(resolvedWorkspace, outputRoot);
292
294
  assertInside(resolvedWorkspace, resolvedOutputRoot, "signing request output");
293
295
  if (selected.length === 0) {
@@ -379,12 +381,6 @@ export function sealArtifactSigningRequests({
379
381
  signature: {
380
382
  profile: declaration.profile,
381
383
  required: declaration.required,
382
- ...(declaration.entitlementsProfile !== "none"
383
- ? {
384
- entitlementsProfile: declaration.entitlementsProfile,
385
- entitlementsPaths: declaration.entitlementsPaths,
386
- }
387
- : {}),
388
384
  },
389
385
  });
390
386
  const check = validateArtifactSigningRequest(request);
@@ -84,7 +84,7 @@ export function cliCommandMeta(id) {
84
84
  "kfd-7-schema": { group: "kfd-trust", purpose: "Print the default KFD-7 schema exposed by the KFD package standards metadata." },
85
85
  "kfd-7-gate": { group: "kfd-trust", purpose: "Evaluate a source-bound KFD-7 Domain Profile, evidence obligations, and independent review." },
86
86
  "kfd-7-verify": { group: "kfd-trust", purpose: "Verify a retained KFD-7 product-gate result without widening product support." },
87
- "kfd-support": { group: "kfd-trust", purpose: "Project and verify a legacy KFD support matrix from one exact standard adopter manifest authority closure." },
87
+ "kfd-support": { group: "kfd-trust", purpose: "Project and verify a product-owned KFD-1 through KFD-13 support matrix against KFD-4/5/7 gates." },
88
88
  "kfd-aggregate": { group: "kfd-trust", purpose: "Return a product KFD view that combines own KFD status with upstream KFD aggregate facts." },
89
89
  "kfd-upstream": { group: "kfd-trust", purpose: "Inspect KFD upstream aggregate command families." },
90
90
  "kfd-upstream-check": { group: "kfd-trust", purpose: "Validate a KFD upstream aggregate document and fail closed on missing evidence." },
@@ -139,7 +139,7 @@ export function cliCommandMeta(id) {
139
139
  "release-line-open": { group: "governance-versioning", purpose: "Plan or write the initial version-state commit for a new minor release line." },
140
140
  "release-governance": { group: "governance-versioning", purpose: "Reconcile a managed branch's Buildchain aggregate check to an already-tested pull request SHA." },
141
141
  "release-propagation": { group: "site-and-propagation", purpose: "Route exact Paper, KFD, Buildchain, or Core Site updates through one resumable propagation Work." },
142
- "release-tail": { group: "release-passport-trust", purpose: "Plan, initialize, rehearse, resume, and verify one declarative provider-owned release-tail transaction." },
142
+ "release-tail": { group: "release-passport-trust", purpose: "Plan, initialize, resume, and verify one declarative provider-owned release-tail transaction." },
143
143
  "release-transaction": { group: "release-passport-trust", purpose: "Inspect, recover, finalize, or abort durable release transactions." },
144
144
  sample: { group: "observability-diagnostics", purpose: "Inspect sampler command families." },
145
145
  "sample-process-tree": { group: "observability-diagnostics", purpose: "Sample process-tree diagnostics for a wrapped command." },
@@ -203,17 +203,12 @@ export function nodeApiMeta(exportName) {
203
203
  "./release-candidate": { group: "reusable-build", summary: "PR-stage release-candidate artifact, passport, and promote-only resolver APIs." },
204
204
  "./release-candidate-recovery": { group: "release-passport-trust", summary: "Fail-closed verification and immutable receipts for reusing a successful sealed candidate run without rebuilding product payloads." },
205
205
  "./release-train": { group: "governance-versioning", summary: "Frozen Release Cut identity, rooted Release Train transitions, Dev observations, supersession, replay, and legacy read compatibility APIs." },
206
- "./next-development-transition": { group: "governance-versioning", summary: "Provider-neutral completed-Alpha preservation and rooted next-development state, version-model, idempotency, and local materialization APIs." },
207
- "./next-development-controller": { group: "governance-versioning", summary: "Durable completed-Alpha child scheduling, fresh-runner checkpoint resume, latest protected-Dev reconciliation, and exact version-root readback APIs." },
208
- "./next-development-projection": { group: "governance-versioning", summary: "Canonical generated config, Agent, and manual projections for the next-development transition contract." },
209
206
  "./stable-candidate-ledger": { group: "governance-versioning", summary: "Immutable alpha candidate ledger, qualification, revocation, selection, and exact stable source-lock APIs." },
210
207
  "./release-propagation": { group: "site-and-propagation", summary: "Release propagation graph, plan, and exact upstream lock APIs." },
211
208
  "./release-activation-transaction": { group: "release-passport-trust", summary: "Ordered cross-repository activation, exact receipt-set binding, retry, abort, rollback, and shadow-rehearsal APIs." },
212
209
  "./release-tail-provider-plane": { group: "release-passport-trust", summary: "Versioned declarative release-tail parsing, deterministic effect planning, durable transaction execution, readback, retry, and rooted receipt APIs." },
213
210
  "./release-tail-provider-adapters": { group: "release-passport-trust", summary: "Buildchain-owned GitHub Release, signed-channel, activation, and released-evidence provider adapters." },
214
211
  "./release-tail-compatibility": { group: "release-passport-trust", summary: "Bounded v3 release-hook compatibility diagnostics that reject new executable release-tail inputs." },
215
- "./publication-rehearsal-runtime": { group: "release-passport-trust", summary: "Content-addressed local, replay, and provider-recording publication rehearsal over the shared release-tail core." },
216
- "./publication-rehearsal-projection": { group: "release-passport-trust", summary: "Canonical generated config, workflow, and Agent projections for release local constructibility." },
217
212
  "./release-line-bootstrap": { group: "governance-versioning", summary: "Semver release-line bootstrap planning and version-state APIs." },
218
213
  "./buildchain-contract": { group: "governance-versioning", summary: "Runtime contract world and compatibility digest APIs for floating-ref drift checks." },
219
214
  "./controller-evidence": { group: "reusable-build", summary: "Project-independent controller descriptors, source/runtime-bound plans, receipts, aggregates, and validation APIs." },
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { validateHostedSelfDogfoodEvidence } from "./next-development-self-dogfood.mjs";
4
-
5
3
  const DEFAULTS = {
6
4
  buildWorkflowFile: "build-surface-fixture.yml",
7
5
  buildWorkflowName: "Build Surface Fixture",
@@ -50,16 +48,18 @@ export function resolveStableCandidateQualificationCandidate({ eventName, inputC
50
48
  if (text(eventName) !== "workflow_run") {
51
49
  throw new Error(`qualification candidate resolver does not admit event ${eventName || "<empty>"}`);
52
50
  }
53
- let evidence;
54
- try {
55
- evidence = validateHostedSelfDogfoodEvidence(selfDogfoodEvidence);
56
- } catch (error) {
57
- throw new Error(`self-dogfood evidence is not a passing rooted v3-alpha observation: ${error.message}`);
51
+ const evidence = selfDogfoodEvidence;
52
+ if (
53
+ evidence?.contract !== "kungfu-buildchain-alpha-self-dogfood"
54
+ || evidence?.status !== "passed"
55
+ || evidence?.observed?.alpha?.ref !== "v4-alpha"
56
+ ) {
57
+ throw new Error("self-dogfood evidence is not a passing v4-alpha observation");
58
58
  }
59
- const observedSha = optionalSha(evidence.observed.alpha.sha, "observed v3-alpha SHA");
60
- const expectedSha = optionalSha(evidence.observed.alpha.expectedSha, "expected v3-alpha SHA");
59
+ const observedSha = optionalSha(evidence.observed.alpha.sha, "observed v4-alpha SHA");
60
+ const expectedSha = optionalSha(evidence.observed.alpha.expectedSha, "expected v4-alpha SHA");
61
61
  if (!observedSha || observedSha !== expectedSha) {
62
- throw new Error("self-dogfood evidence does not bind observed and expected v3-alpha SHAs");
62
+ throw new Error("self-dogfood evidence does not bind observed and expected v4-alpha SHAs");
63
63
  }
64
64
  return observedSha;
65
65
  }
@@ -238,6 +238,8 @@ function validateCapabilities(capabilities, inventory, issues) {
238
238
  }
239
239
  if (!MIGRATION_PHASES.has(capability?.migrationPhase))
240
240
  issues.push(`${capability?.id} has invalid migrationPhase`);
241
+ if (capability?.migrationPhase !== "legacy-retired")
242
+ issues.push(`${capability?.id} must retire its legacy v3 authority`);
241
243
  }
242
244
  for (const id of inventoryById.keys()) {
243
245
  if (!(capabilities || []).some((entry) => entry.sourceInventoryId === id))
@@ -279,8 +281,12 @@ function validateStateMachines(machines, capabilityIds, issues) {
279
281
  `${coordinate}.writer.runtime`,
280
282
  issues,
281
283
  );
284
+ if (machine?.writer?.runtime !== "typescript-v4")
285
+ issues.push(`${machine?.id} writer.runtime must be typescript-v4`);
282
286
  if (!MIGRATION_PHASES.has(machine?.migrationPhase))
283
287
  issues.push(`${machine?.id} has invalid migrationPhase`);
288
+ if (machine?.migrationPhase !== "legacy-retired")
289
+ issues.push(`${machine?.id} must retire its legacy v3 writer`);
284
290
  for (const field of [
285
291
  "undeclaredStates",
286
292
  "undeclaredEvents",
@@ -302,8 +308,12 @@ function validateManifest({ manifest, inventory, bootstrap, ledger }) {
302
308
  issues.push("manifest contract is invalid");
303
309
  if (manifest?.releaseLine?.branch !== "dev/v4/v4.0")
304
310
  issues.push("releaseLine.branch must be dev/v4/v4.0");
305
- if (manifest?.releaseLine?.productionFallback !== "dev/v3/v3.0")
306
- issues.push("releaseLine.productionFallback must remain dev/v3/v3.0");
311
+ if (manifest?.releaseLine?.productionAuthority !== "v4-native")
312
+ issues.push("releaseLine.productionAuthority must be v4-native");
313
+ if (manifest?.releaseLine?.productionFallback !== null)
314
+ issues.push("releaseLine.productionFallback must be null after cutover");
315
+ if (manifest?.releaseLine?.rollbackRef !== "release/v3/v3.0")
316
+ issues.push("releaseLine.rollbackRef must retain release/v3/v3.0");
307
317
  if (
308
318
  manifest?.releaseLine?.qualificationMode !==
309
319
  "exact-n-minus-one-git-revision"