@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
@@ -0,0 +1,528 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { spawnSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ import {
10
+ V4ContractFault,
11
+ v4CanonicalBytes,
12
+ v4ContentRoot,
13
+ } from "../packages/core/v4-canonical-contracts.js";
14
+ import { V4StageCapsuleLocalStore } from "../packages/core/v4-stage-capsule-local-store.js";
15
+ import {
16
+ restoreV4PlatformStageCheckpoint,
17
+ validateV4PlatformStageCheckpointDeclaration,
18
+ } from "../packages/core/v4-platform-stage-checkpoints.js";
19
+ import {
20
+ V4_STAGE_CAPSULE_CAMPAIGN_RECORDED_AT,
21
+ createV4StageCapsuleCampaignProfile,
22
+ emitV4StageCapsuleCampaignCheckpoint,
23
+ runV4StageCapsuleFaultCampaign,
24
+ v4StageCapsuleCampaignAggregateRoots,
25
+ v4StageCapsuleCampaignDependencies,
26
+ v4StageCapsuleCampaignStages,
27
+ } from "../packages/core/v4-stage-capsule-qualification-campaign.js";
28
+ import {
29
+ V4_STAGE_CAPSULE_PLATFORM_QUALIFICATION_CONTRACT,
30
+ qualifyV4StageCapsuleCampaign,
31
+ reconcileV4StageCapsuleWave,
32
+ validateV4StageCapsulePlatformQualification,
33
+ } from "../packages/core/v4-stage-capsule-qualification.js";
34
+ import { planV4StageCapsuleResume } from "../packages/core/v4-stage-capsule-resume-planner.js";
35
+
36
+ const repoRoot = path.resolve(
37
+ path.dirname(fileURLToPath(import.meta.url)),
38
+ "..",
39
+ );
40
+ const scriptPath = fileURLToPath(import.meta.url);
41
+
42
+ function option(name, fallback = "") {
43
+ const index = process.argv.indexOf(`--${name}`);
44
+ return index >= 0 ? process.argv[index + 1] : fallback;
45
+ }
46
+
47
+ function required(name) {
48
+ const value = option(name);
49
+ if (!value) throw new Error(`--${name} is required`);
50
+ return value;
51
+ }
52
+
53
+ function readJson(file) {
54
+ return JSON.parse(fs.readFileSync(file, "utf8"));
55
+ }
56
+
57
+ function writeJson(file, value) {
58
+ fs.mkdirSync(path.dirname(file), { recursive: true });
59
+ fs.writeFileSync(file, v4CanonicalBytes(value), { flag: "wx" });
60
+ }
61
+
62
+ function declaration() {
63
+ return validateV4PlatformStageCheckpointDeclaration(
64
+ readJson(
65
+ path.join(repoRoot, "architecture/v4-platform-stage-checkpoints.json"),
66
+ ),
67
+ );
68
+ }
69
+
70
+ function contextFromArgs() {
71
+ const context = {
72
+ workRoot: path.resolve(required("work-root")),
73
+ platform: required("platform"),
74
+ consumer: required("consumer"),
75
+ runtimeRef: required("runtime-ref"),
76
+ consumerSourceRevision: required("consumer-source-revision"),
77
+ lifecycleEvidenceRoot: option("lifecycle-evidence-root")
78
+ ? path.resolve(option("lifecycle-evidence-root"))
79
+ : "",
80
+ consumerRoot: option("consumer-root")
81
+ ? path.resolve(option("consumer-root"))
82
+ : repoRoot,
83
+ repoRoot,
84
+ declaration: declaration(),
85
+ };
86
+ if (!context.declaration.platforms.some(({ id }) => id === context.platform))
87
+ throw new Error(`undeclared platform: ${context.platform}`);
88
+ if (!/^[0-9a-f]{40}$/u.test(context.consumerSourceRevision))
89
+ throw new Error("--consumer-source-revision must be an exact commit");
90
+ return context;
91
+ }
92
+
93
+ function prepareContext(context) {
94
+ if (!buildchainConfigModule)
95
+ throw new Error("real lifecycle config loader is unavailable");
96
+ context.lifecycleConfig = buildchainConfigModule.loadBuildchainConfig(
97
+ context.consumerRoot,
98
+ );
99
+ context.campaignProfile = createV4StageCapsuleCampaignProfile(context);
100
+ return context;
101
+ }
102
+
103
+ function directoryBytes(directory) {
104
+ let total = 0;
105
+ const visit = (current) => {
106
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
107
+ const target = path.join(current, entry.name);
108
+ if (entry.isDirectory()) visit(target);
109
+ else if (entry.isFile()) total += fs.statSync(target).size;
110
+ }
111
+ };
112
+ visit(directory);
113
+ return total;
114
+ }
115
+
116
+ function seed(context) {
117
+ prepareContext(context);
118
+ const campaignStages = v4StageCapsuleCampaignStages(context);
119
+ const retainedStages = campaignStages.slice(
120
+ 0,
121
+ Math.max(1, campaignStages.indexOf("verify")),
122
+ );
123
+ const failedStage = "verify";
124
+ const targetStage = campaignStages.at(-1);
125
+ const referenceStore = new V4StageCapsuleLocalStore(
126
+ path.join(context.workRoot, "reference-store"),
127
+ );
128
+ const retainedStore = new V4StageCapsuleLocalStore(
129
+ path.join(context.workRoot, "retained-store"),
130
+ );
131
+ const reference = campaignStages.map((stage) => ({
132
+ stage,
133
+ ...emitV4StageCapsuleCampaignCheckpoint(context, stage, referenceStore),
134
+ }));
135
+ const retained = retainedStages.map((stage) => ({
136
+ stage,
137
+ ...emitV4StageCapsuleCampaignCheckpoint(context, stage, retainedStore),
138
+ }));
139
+ const lateFailure = emitV4StageCapsuleCampaignCheckpoint(
140
+ context,
141
+ failedStage,
142
+ retainedStore,
143
+ "failure",
144
+ );
145
+ if (lateFailure.emitted !== false)
146
+ throw new Error("late failure emitted a capsule");
147
+ const body = {
148
+ schema: "buildchain-v4-stage-capsule-seed-evidence/v1",
149
+ consumer: context.consumer,
150
+ platform: context.platform,
151
+ runtimeRef: context.runtimeRef,
152
+ campaignProfileRoot: context.campaignProfile.profileRoot,
153
+ referenceRoots: v4StageCapsuleCampaignAggregateRoots(reference),
154
+ retainedCapsuleRoots: retained.map(({ stage, capsule }) => ({
155
+ stage,
156
+ capsuleRoot: capsule.capsuleRoot,
157
+ })),
158
+ failedStage,
159
+ failedStageCapsuleEmitted: false,
160
+ retainedBytes: directoryBytes(
161
+ path.join(context.workRoot, "retained-store"),
162
+ ),
163
+ productionAuthority: "v3",
164
+ productionWrites: false,
165
+ };
166
+ const evidence = {
167
+ ...body,
168
+ evidenceRoot: v4ContentRoot("stage-capsule-seed-evidence", body),
169
+ };
170
+ writeJson(path.join(context.workRoot, "seed-evidence.json"), evidence);
171
+ writeJson(path.join(context.workRoot, "campaign-state.json"), {
172
+ schema: "buildchain-v4-stage-capsule-campaign-state/v1",
173
+ consumer: context.consumer,
174
+ platform: context.platform,
175
+ runtimeRef: context.runtimeRef,
176
+ consumerSourceRevision: context.consumerSourceRevision,
177
+ campaignProfileRoot: context.campaignProfile.profileRoot,
178
+ campaignStages,
179
+ dependencies: Object.fromEntries(
180
+ campaignStages.map((stage) => [
181
+ stage,
182
+ v4StageCapsuleCampaignDependencies(context, stage),
183
+ ]),
184
+ ),
185
+ retainedStages,
186
+ failedStage,
187
+ targetStage,
188
+ reference: reference.map(({ stage, capsule, manifest }) => ({
189
+ stage,
190
+ capsule,
191
+ manifest,
192
+ })),
193
+ seedEvidenceRoot: evidence.evidenceRoot,
194
+ retainedBytes: evidence.retainedBytes,
195
+ });
196
+ }
197
+
198
+ function stageNode(state, store, reference) {
199
+ let candidate = null;
200
+ if (state.retainedStages.includes(reference.stage)) {
201
+ const restored = store.restore({
202
+ capsuleRoot: reference.capsule.capsuleRoot,
203
+ recordedAt: V4_STAGE_CAPSULE_CAMPAIGN_RECORDED_AT,
204
+ });
205
+ candidate = {
206
+ capsule: restored.capsule,
207
+ availability: restored.availability,
208
+ };
209
+ }
210
+ return {
211
+ key: reference.stage,
212
+ dependencies: state.dependencies[reference.stage],
213
+ expectedIdentity: reference.capsule.identity,
214
+ expectedRetentionPromise: reference.capsule.retentionPromise,
215
+ candidate,
216
+ };
217
+ }
218
+
219
+ function resumeRequest(state, store) {
220
+ return {
221
+ schema: "buildchain-v4-stage-capsule-resume-request/v1",
222
+ evaluatedAt: V4_STAGE_CAPSULE_CAMPAIGN_RECORDED_AT,
223
+ nodes: state.reference.map((reference) =>
224
+ stageNode(state, store, reference),
225
+ ),
226
+ targets: [state.targetStage],
227
+ effects: [],
228
+ };
229
+ }
230
+
231
+ function emitMissing(context, state, store, stageId) {
232
+ const emitted = emitV4StageCapsuleCampaignCheckpoint(context, stageId, store);
233
+ const expected = state.reference.find(({ stage }) => stage === stageId);
234
+ if (
235
+ emitted.capsule.capsuleRoot !== expected.capsule.capsuleRoot ||
236
+ emitted.manifest.manifestRoot !== expected.manifest.manifestRoot
237
+ )
238
+ throw new Error(`resumed ${stageId} differs from the fresh build`);
239
+ return {
240
+ stage: stageId,
241
+ capsule: emitted.capsule,
242
+ manifest: emitted.manifest,
243
+ };
244
+ }
245
+
246
+ function resume(context) {
247
+ prepareContext(context);
248
+ const state = readJson(path.join(context.workRoot, "campaign-state.json"));
249
+ for (const [name, code] of [
250
+ ["consumer", "stage-capsule-campaign-consumer-drift"],
251
+ ["platform", "stage-capsule-campaign-platform-drift"],
252
+ ["runtimeRef", "stage-capsule-campaign-runtime-ref-drift"],
253
+ ["consumerSourceRevision", "stage-capsule-campaign-source-revision-drift"],
254
+ ])
255
+ if (state[name] !== context[name])
256
+ throw new V4ContractFault(code, `$/campaign/${name}`, `${name} differs`);
257
+ if (state.campaignProfileRoot !== context.campaignProfile.profileRoot)
258
+ throw new V4ContractFault(
259
+ "stage-capsule-campaign-lifecycle-profile-drift",
260
+ "$/campaign/campaignProfileRoot",
261
+ "campaign lifecycle profile differs",
262
+ );
263
+ const store = new V4StageCapsuleLocalStore(
264
+ path.join(context.workRoot, "retained-store"),
265
+ );
266
+ const request = resumeRequest(state, store);
267
+ const plan = planV4StageCapsuleResume(request);
268
+ const expectedRestores = [state.retainedStages.at(-1)];
269
+ const expectedStages = state.campaignStages.filter(
270
+ (stage) => !state.retainedStages.includes(stage),
271
+ );
272
+ if (
273
+ JSON.stringify(plan.requiredRestores) !==
274
+ JSON.stringify(expectedRestores) ||
275
+ JSON.stringify(plan.requiredStages) !== JSON.stringify(expectedStages)
276
+ )
277
+ throw new Error(
278
+ `resume planner did not select the minimal recovery set: ${JSON.stringify(
279
+ {
280
+ requiredRestores: plan.requiredRestores,
281
+ requiredStages: plan.requiredStages,
282
+ decisions: plan.decisions.map(
283
+ ({ stageKey, decision, reasonCode }) => ({
284
+ stageKey,
285
+ decision,
286
+ reasonCode,
287
+ }),
288
+ ),
289
+ },
290
+ )}`,
291
+ );
292
+ const started = process.hrtime.bigint();
293
+ for (const stageId of plan.requiredRestores) {
294
+ const reference = state.reference.find(({ stage }) => stage === stageId);
295
+ restoreV4PlatformStageCheckpoint({
296
+ declaration: context.declaration,
297
+ expectedPlatform: context.platform,
298
+ expectedStage: stageId,
299
+ capsuleRoot: reference.capsule.capsuleRoot,
300
+ recordedAt: V4_STAGE_CAPSULE_CAMPAIGN_RECORDED_AT,
301
+ targetDirectory: path.join(context.workRoot, "restored", stageId),
302
+ store,
303
+ });
304
+ }
305
+ const restoreOverheadMs = Number(
306
+ (process.hrtime.bigint() - started) / 1000000n,
307
+ );
308
+ const rebuilt = plan.requiredStages.map((stageId) =>
309
+ emitMissing(context, state, store, stageId),
310
+ );
311
+ const retained = state.reference
312
+ .filter(({ stage }) => state.retainedStages.includes(stage))
313
+ .map(({ stage, capsule, manifest }) => ({ stage, capsule, manifest }));
314
+ const resumedEntries = [...retained, ...rebuilt].sort(
315
+ (left, right) =>
316
+ state.campaignStages.indexOf(left.stage) -
317
+ state.campaignStages.indexOf(right.stage),
318
+ );
319
+ const freshBuild = v4StageCapsuleCampaignAggregateRoots(state.reference);
320
+ const resumedBuild = v4StageCapsuleCampaignAggregateRoots(resumedEntries);
321
+ const campaignFaults = runV4StageCapsuleFaultCampaign(request);
322
+ const metrics = {
323
+ fullStageCount: state.campaignStages.length,
324
+ rebuiltStageCount: plan.requiredStages.length,
325
+ retainedStageCount: retained.length,
326
+ restoredStageCount: plan.requiredRestores.length,
327
+ retainedBytes: state.retainedBytes,
328
+ restoreOverheadMs,
329
+ falseReuseCount: campaignFaults.filter(
330
+ ({ expected, actual }) => expected !== "reuse" && actual === "reuse",
331
+ ).length,
332
+ falseRebuildCount: campaignFaults.filter(
333
+ ({ expected, actual }) => expected === "reuse" && actual !== "reuse",
334
+ ).length,
335
+ plannerAccurate: campaignFaults.every(({ passed }) => passed),
336
+ };
337
+ const resumeBody = {
338
+ schema: "buildchain-v4-stage-capsule-resume-evidence/v1",
339
+ platform: context.platform,
340
+ consumer: context.consumer,
341
+ planRoot: plan.planRoot,
342
+ freshBuild,
343
+ resumedBuild,
344
+ faultCampaignRoot: v4ContentRoot(
345
+ "stage-capsule-fault-campaign",
346
+ campaignFaults,
347
+ ),
348
+ metrics,
349
+ };
350
+ const resumeEvidence = {
351
+ ...resumeBody,
352
+ evidenceRoot: v4ContentRoot("stage-capsule-resume-evidence", resumeBody),
353
+ };
354
+ const reportBody = {
355
+ schema: V4_STAGE_CAPSULE_PLATFORM_QUALIFICATION_CONTRACT,
356
+ mode: "shadow-only",
357
+ productionAuthority: "v3",
358
+ consumer: context.consumer,
359
+ platform: context.platform,
360
+ runtimeRef: context.runtimeRef,
361
+ campaignProfileRoot: context.campaignProfile.profileRoot,
362
+ processRuns: [
363
+ {
364
+ id: "seed",
365
+ outcome: "late-failure",
366
+ evidenceRoot: state.seedEvidenceRoot,
367
+ },
368
+ {
369
+ id: "resume",
370
+ outcome: "qualified",
371
+ evidenceRoot: resumeEvidence.evidenceRoot,
372
+ },
373
+ ],
374
+ freshBuild,
375
+ resumedBuild,
376
+ resumePlan: {
377
+ planRoot: plan.planRoot,
378
+ requiredRestores: plan.requiredRestores,
379
+ requiredStages: plan.requiredStages,
380
+ },
381
+ faultCampaign: campaignFaults,
382
+ metrics,
383
+ rollback: {
384
+ switch: "stage-capsule-mode=v3",
385
+ productionAuthority: "v3",
386
+ migrationRequired: false,
387
+ retainedStateDestroyed: false,
388
+ },
389
+ providerEffects: false,
390
+ productionWrites: false,
391
+ };
392
+ const report = validateV4StageCapsulePlatformQualification({
393
+ ...reportBody,
394
+ reportRoot: v4ContentRoot(
395
+ "stage-capsule-platform-qualification",
396
+ reportBody,
397
+ ),
398
+ });
399
+ writeJson(
400
+ path.join(context.workRoot, "resume-evidence.json"),
401
+ resumeEvidence,
402
+ );
403
+ writeJson(path.join(context.workRoot, "report.json"), report);
404
+ process.stdout.write(v4CanonicalBytes(report));
405
+ }
406
+
407
+ function child(action, context) {
408
+ const result = spawnSync(
409
+ process.execPath,
410
+ [
411
+ scriptPath,
412
+ action,
413
+ "--work-root",
414
+ context.workRoot,
415
+ "--platform",
416
+ context.platform,
417
+ "--consumer",
418
+ context.consumer,
419
+ "--runtime-ref",
420
+ context.runtimeRef,
421
+ "--consumer-source-revision",
422
+ context.consumerSourceRevision,
423
+ ...(context.lifecycleEvidenceRoot
424
+ ? ["--lifecycle-evidence-root", context.lifecycleEvidenceRoot]
425
+ : []),
426
+ ...(context.consumerRoot
427
+ ? ["--consumer-root", context.consumerRoot]
428
+ : []),
429
+ ],
430
+ { cwd: repoRoot, encoding: "utf8" },
431
+ );
432
+ if (result.status !== 0)
433
+ throw new Error(
434
+ `${action} child failed: ${(result.stderr || result.stdout).trim()}`,
435
+ );
436
+ return result.stdout;
437
+ }
438
+
439
+ function campaign(context) {
440
+ if (option("stage-capsule-mode", "shadow") === "v3") {
441
+ process.stdout.write(
442
+ v4CanonicalBytes({
443
+ schema: "buildchain-v4-stage-capsule-rollback/v1",
444
+ switch: "stage-capsule-mode=v3",
445
+ productionAuthority: "v3",
446
+ migrationRequired: false,
447
+ retainedStateDestroyed: false,
448
+ productionWrites: false,
449
+ }),
450
+ );
451
+ return;
452
+ }
453
+ fs.mkdirSync(context.workRoot, { recursive: true });
454
+ child("seed", context);
455
+ process.stdout.write(child("resume", context));
456
+ }
457
+
458
+ function reportFiles(directory) {
459
+ const files = [];
460
+ const visit = (current) => {
461
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
462
+ const target = path.join(current, entry.name);
463
+ if (entry.isDirectory()) visit(target);
464
+ else if (entry.isFile() && entry.name === "report.json")
465
+ files.push(target);
466
+ }
467
+ };
468
+ visit(directory);
469
+ return files.sort();
470
+ }
471
+
472
+ function aggregate() {
473
+ const directory = path.resolve(required("input-dir"));
474
+ const reports = reportFiles(directory)
475
+ .map(readJson)
476
+ .sort((left, right) =>
477
+ `${left.consumer}/${left.platform}`.localeCompare(
478
+ `${right.consumer}/${right.platform}`,
479
+ "en",
480
+ ),
481
+ );
482
+ const expectedConsumers = option("expected-consumers")
483
+ ? option("expected-consumers")
484
+ .split(",")
485
+ .map((value) => value.trim())
486
+ .filter(Boolean)
487
+ : undefined;
488
+ const qualification = qualifyV4StageCapsuleCampaign(
489
+ reports,
490
+ expectedConsumers,
491
+ );
492
+ const output = option("output");
493
+ if (output) writeJson(path.resolve(output), qualification);
494
+ process.stdout.write(v4CanonicalBytes(qualification));
495
+ }
496
+
497
+ function reconcile() {
498
+ const qualification = readJson(path.resolve(required("qualification")));
499
+ const request = readJson(path.resolve(required("wave-evidence")));
500
+ if (request.qualificationRoot !== qualification.qualificationRoot)
501
+ throw new Error("wave evidence does not bind the qualification root");
502
+ const reconciliation = reconcileV4StageCapsuleWave(request);
503
+ const output = option("output");
504
+ if (output) writeJson(path.resolve(output), reconciliation);
505
+ process.stdout.write(v4CanonicalBytes(reconciliation));
506
+ }
507
+
508
+ const action = process.argv[2] || "";
509
+ const buildchainConfigModule = option("consumer")
510
+ ? await import("../packages/core/buildchain-config.js")
511
+ : null;
512
+ if (action === "seed") seed(contextFromArgs());
513
+ else if (action === "resume") resume(contextFromArgs());
514
+ else if (action === "campaign") campaign(contextFromArgs());
515
+ else if (action === "aggregate") aggregate();
516
+ else if (action === "reconcile") reconcile();
517
+ else if (action === "smoke") {
518
+ const temporary = fs.mkdtempSync(
519
+ path.join(os.tmpdir(), "buildchain-v4-capsule-"),
520
+ );
521
+ const context = contextFromArgs();
522
+ context.workRoot = temporary;
523
+ campaign(context);
524
+ } else {
525
+ throw new Error(
526
+ "action must be seed, resume, campaign, aggregate, reconcile, or smoke",
527
+ );
528
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import { v4CanonicalBytes } from "../packages/core/v4-canonical-contracts.js";
8
+ import { planV4StageCapsuleResume } from "../packages/core/v4-stage-capsule-resume-planner.js";
9
+
10
+ const repoRoot = path.resolve(
11
+ path.dirname(fileURLToPath(import.meta.url)),
12
+ "..",
13
+ );
14
+
15
+ function option(name) {
16
+ const index = process.argv.indexOf(`--${name}`);
17
+ return index >= 0 ? process.argv[index + 1] : "";
18
+ }
19
+
20
+ const platform = option("platform");
21
+ if (!platform) throw new Error("--platform is required");
22
+
23
+ const declaration = JSON.parse(
24
+ fs.readFileSync(
25
+ path.join(repoRoot, "architecture/v4-platform-stage-checkpoints.json"),
26
+ "utf8",
27
+ ),
28
+ );
29
+ if (!declaration.platforms.some(({ id }) => id === platform))
30
+ throw new Error(`undeclared platform: ${platform}`);
31
+
32
+ const fixture = JSON.parse(
33
+ fs.readFileSync(
34
+ path.join(
35
+ repoRoot,
36
+ "contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json",
37
+ ),
38
+ "utf8",
39
+ ),
40
+ );
41
+ const first = planV4StageCapsuleResume(fixture);
42
+ const second = planV4StageCapsuleResume(structuredClone(fixture));
43
+ if (first.planRoot !== second.planRoot)
44
+ throw new Error("resume planner is not deterministic");
45
+
46
+ process.stdout.write(
47
+ v4CanonicalBytes({
48
+ schema: "buildchain-v4-stage-capsule-resume-rehearsal/v1",
49
+ mode: "shadow-only",
50
+ platform,
51
+ productionAuthority: first.productionAuthority,
52
+ planRoot: first.planRoot,
53
+ requiredRestores: first.requiredRestores,
54
+ requiredStages: first.requiredStages,
55
+ requiredEffects: first.requiredEffects,
56
+ }),
57
+ );