@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
@@ -25,42 +25,61 @@ function parseAlphaRef(ref) {
25
25
  return Number(match[1]);
26
26
  }
27
27
 
28
- export function resolveSelfDogfoodMajor({
28
+ function isExactSha(value) {
29
+ return /^[0-9a-f]{40}$/u.test(String(value || ""));
30
+ }
31
+
32
+ export function hasQualifiedSelfDogfoodBootstrapAuthority({
29
33
  packageVersion,
30
34
  alphaRef,
31
- majorBootstrap = false,
35
+ authority,
32
36
  } = {}) {
33
37
  const version = parsePackageVersion(packageVersion);
34
38
  const acceptedMajor = parseAlphaRef(alphaRef);
35
- if (acceptedMajor === version.major) {
36
- return {
37
- packageMajor: version.major,
38
- workflowMajor: acceptedMajor,
39
- bootstrap: false,
40
- };
41
- }
42
-
43
- const stableBootstrap =
44
- version.minor === 0 && version.patch === 0 && version.alpha === undefined;
45
- const nextAlphaBootstrap =
46
- version.minor === 0 && version.patch === 1 && version.alpha !== undefined;
47
- if (
48
- majorBootstrap === true &&
39
+ const releaseLine = authority?.releaseLine;
40
+ const qualification = authority?.qualification;
41
+ return (
49
42
  acceptedMajor + 1 === version.major &&
50
- (stableBootstrap || nextAlphaBootstrap)
51
- ) {
52
- return {
53
- packageMajor: version.major,
54
- workflowMajor: acceptedMajor,
55
- bootstrap: true,
56
- };
57
- }
58
-
59
- throw new Error(
60
- "Buildchain self-dogfood alpha lock must target the current major alpha ref",
43
+ authority?.contract ===
44
+ `kungfu-buildchain-v${version.major}-bootstrap-authority` &&
45
+ releaseLine?.candidateBranch ===
46
+ `dev/v${version.major}/v${version.major}.0` &&
47
+ String(releaseLine?.sourceBranch || "").startsWith(
48
+ `dev/v${acceptedMajor}/v${acceptedMajor}.`,
49
+ ) &&
50
+ releaseLine?.status === `qualified-protected-v${version.major}-bootstrap` &&
51
+ isExactSha(releaseLine?.sourceCommit) &&
52
+ isExactSha(releaseLine?.bootstrapCommit) &&
53
+ qualification?.contract ===
54
+ `kungfu-buildchain-v${version.major}-n-minus-one-qualification` &&
55
+ qualification?.authorityRevision === releaseLine.sourceCommit &&
56
+ qualification?.candidateRevision === releaseLine.bootstrapCommit &&
57
+ qualification?.candidateSelfQualified === false &&
58
+ qualification?.activeExceptions === 0 &&
59
+ /^sha256:[0-9a-f]{64}$/u.test(
60
+ String(qualification?.qualificationRoot || ""),
61
+ )
61
62
  );
62
63
  }
63
64
 
65
+ export function resolveSelfDogfoodMajor({
66
+ packageVersion,
67
+ alphaRef,
68
+ } = {}) {
69
+ const version = parsePackageVersion(packageVersion);
70
+ const acceptedMajor = parseAlphaRef(alphaRef);
71
+ if (acceptedMajor !== version.major) {
72
+ throw new Error(
73
+ "Buildchain self-dogfood alpha lock must target the current major alpha ref",
74
+ );
75
+ }
76
+ return {
77
+ packageMajor: version.major,
78
+ workflowMajor: acceptedMajor,
79
+ bootstrap: false,
80
+ };
81
+ }
82
+
64
83
  export function contractForSelfDogfoodEvaluation({
65
84
  currentContract,
66
85
  majorResolution,
@@ -68,18 +87,12 @@ export function contractForSelfDogfoodEvaluation({
68
87
  if (!currentContract || typeof currentContract !== "object") {
69
88
  throw new Error("Buildchain self-dogfood requires a current contract");
70
89
  }
71
- if (!majorResolution?.bootstrap) {
72
- return currentContract;
73
- }
74
- return {
75
- ...currentContract,
76
- majorLine: `v${majorResolution.workflowMajor}`,
77
- };
90
+ return currentContract;
78
91
  }
79
92
 
80
93
  export function canAdmitSelfDogfoodLockEvaluation({
81
94
  evaluation,
82
95
  majorResolution,
83
96
  } = {}) {
84
- return evaluation?.compatible === true || majorResolution?.bootstrap === true;
97
+ return evaluation?.compatible === true;
85
98
  }
@@ -1,16 +1,66 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { execFileSync, execSync, spawn, spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
2
5
 
3
6
  const WINDOWS_CMD_SHIMS = new Set(["corepack", "npm", "npx", "pnpm", "yarn"]);
7
+ const CMD_META_CHARACTERS = /([()\][%!^`"<>&|;, *?])/g;
8
+ const INTERNAL_PROCESS_TREE_COMMAND = "--buildchain-internal-process-tree-command";
9
+ const TIMEOUT_EXIT_CODE = 124;
10
+ const modulePath = fileURLToPath(import.meta.url);
4
11
 
5
- export function resolveSpawnCommand(command, platform = process.platform) {
6
- if (platform !== "win32" || !WINDOWS_CMD_SHIMS.has(command)) {
12
+ function runningStandaloneBundle() {
13
+ return process.argv[1] && process.argv[2] !== INTERNAL_PROCESS_TREE_COMMAND
14
+ && import.meta.url === pathToFileURL(process.argv[1]).href;
15
+ }
16
+
17
+ function pathCommandShim(command, env) {
18
+ for (const directory of String(env.PATH || "").split(";")) {
19
+ const candidate = path.join(directory, `${command}.cmd`);
20
+ if (directory && fs.existsSync(candidate)) return true;
21
+ }
22
+ return false;
23
+ }
24
+
25
+ export function resolveSpawnCommand(command, platform = process.platform, env = process.env) {
26
+ if (platform !== "win32") {
7
27
  return command;
8
28
  }
9
- return `${command}.cmd`;
29
+ return pathCommandShim(command, env) || WINDOWS_CMD_SHIMS.has(command)
30
+ ? `${command}.cmd`
31
+ : command;
10
32
  }
11
33
 
12
- export function usesShellForSpawnCommand(command, platform = process.platform) {
13
- return platform === "win32" && WINDOWS_CMD_SHIMS.has(command);
34
+ export function usesShellForSpawnCommand(command, platform = process.platform, env = process.env) {
35
+ return platform === "win32" && /\.cmd$/i.test(resolveSpawnCommand(command, platform, env));
36
+ }
37
+
38
+ function cmdEscapeCommand(value) {
39
+ return String(value).replace(CMD_META_CHARACTERS, "^$1");
40
+ }
41
+
42
+ function cmdEscapeArgument(value, doubleEscapeMetaCharacters = false) {
43
+ let text = String(value);
44
+ text = text.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
45
+ text = text.replace(/(?=(\\+?)?)\1$/g, "$1$1");
46
+ let escaped = `"${text}"`.replace(CMD_META_CHARACTERS, "^$1");
47
+ if (doubleEscapeMetaCharacters) {
48
+ escaped = escaped.replace(CMD_META_CHARACTERS, "^$1");
49
+ }
50
+ return escaped;
51
+ }
52
+
53
+ export function windowsBatchInvocation(command, args, env = process.env) {
54
+ const shellCommand = [
55
+ cmdEscapeCommand(command),
56
+ ...args.map((argument) =>
57
+ cmdEscapeArgument(argument, /\.cmd$/i.test(command)),
58
+ ),
59
+ ].join(" ");
60
+ return {
61
+ command: env.ComSpec || env.comspec || process.env.ComSpec || process.env.comspec || "cmd.exe",
62
+ args: ["/d", "/s", "/c", `"${shellCommand}"`],
63
+ };
14
64
  }
15
65
 
16
66
  export function spawnSyncCommand(
@@ -19,8 +69,182 @@ export function spawnSyncCommand(
19
69
  options = {},
20
70
  { platform = process.platform, spawn = spawnSync } = {},
21
71
  ) {
22
- return spawn(resolveSpawnCommand(command, platform), args, {
72
+ const env = options.env || process.env;
73
+ const resolvedCommand = resolveSpawnCommand(command, platform, env);
74
+ if (
75
+ platform === "win32" &&
76
+ /\.cmd$/i.test(resolvedCommand) &&
77
+ options.shell !== false
78
+ ) {
79
+ const invocation = windowsBatchInvocation(resolvedCommand, args, env);
80
+ return spawn(invocation.command, invocation.args, {
81
+ ...options,
82
+ shell: false,
83
+ windowsVerbatimArguments: true,
84
+ });
85
+ }
86
+ return spawn(resolvedCommand, args, {
23
87
  ...options,
24
- shell: options.shell ?? usesShellForSpawnCommand(command, platform),
88
+ shell: options.shell ?? false,
89
+ });
90
+ }
91
+
92
+ async function terminateProcessTree(child, graceMs) {
93
+ if (!child?.pid) return;
94
+ if (process.platform === "win32") {
95
+ await new Promise((resolve) => {
96
+ const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
97
+ stdio: "ignore",
98
+ windowsHide: true,
99
+ });
100
+ const fallback = setTimeout(() => {
101
+ try {
102
+ child.kill("SIGKILL");
103
+ } catch {
104
+ // The process tree may already be gone.
105
+ }
106
+ resolve();
107
+ }, graceMs);
108
+ killer.once("error", () => {
109
+ clearTimeout(fallback);
110
+ try {
111
+ child.kill("SIGKILL");
112
+ } catch {
113
+ // The process tree may already be gone.
114
+ }
115
+ resolve();
116
+ });
117
+ killer.once("close", () => {
118
+ clearTimeout(fallback);
119
+ resolve();
120
+ });
121
+ });
122
+ return;
123
+ }
124
+ try {
125
+ process.kill(-child.pid, "SIGTERM");
126
+ } catch {
127
+ return;
128
+ }
129
+ await new Promise((resolve) => setTimeout(resolve, graceMs));
130
+ try {
131
+ process.kill(-child.pid, "SIGKILL");
132
+ } catch {
133
+ // The process group exited during the grace period.
134
+ }
135
+ }
136
+
137
+ async function runInternalProcessTreeCommand() {
138
+ const payload = JSON.parse(fs.readFileSync(0, "utf8"));
139
+ const child = spawn(payload.command, payload.args, {
140
+ cwd: payload.cwd,
141
+ env: process.env,
142
+ detached: process.platform !== "win32",
143
+ stdio: ["ignore", "inherit", "inherit"],
144
+ windowsHide: true,
145
+ windowsVerbatimArguments: payload.windowsVerbatimArguments === true,
146
+ });
147
+ let timedOut = false;
148
+ let spawnError;
149
+ const closed = new Promise((resolve) => {
150
+ child.once("error", (error) => {
151
+ spawnError = error;
152
+ resolve({ code: 1, signal: "" });
153
+ });
154
+ child.once("close", (code, signal) => resolve({ code, signal }));
155
+ });
156
+ const timer = Number.isFinite(payload.timeoutMs) && payload.timeoutMs > 0
157
+ ? setTimeout(() => {
158
+ timedOut = true;
159
+ void terminateProcessTree(child, payload.graceMs);
160
+ }, payload.timeoutMs)
161
+ : undefined;
162
+ const result = await closed;
163
+ if (timer) clearTimeout(timer);
164
+ process.exitCode = timedOut
165
+ ? TIMEOUT_EXIT_CODE
166
+ : spawnError || result.signal
167
+ ? 1
168
+ : (result.code ?? 1);
169
+ }
170
+
171
+ function shellCommandInvocation(command, shell) {
172
+ if (typeof shell === "string" && shell.trim()) {
173
+ return { command: shell, args: ["-c", command] };
174
+ }
175
+ if (process.platform === "win32") {
176
+ return {
177
+ command: process.env.ComSpec || process.env.comspec || "cmd.exe",
178
+ args: ["/d", "/s", "/c", `"${command}"`],
179
+ windowsVerbatimArguments: true,
180
+ };
181
+ }
182
+ return { command: process.env.SHELL || "/bin/sh", args: ["-c", command] };
183
+ }
184
+
185
+ function standaloneProcessCommand(command, args, options) {
186
+ const result = spawnSync(command, args, { ...options, windowsHide: true });
187
+ if (result.error) throw result.error;
188
+ if (result.status !== 0 || result.signal) {
189
+ const error = new Error(`process tree command failed with status ${result.status ?? ""}`);
190
+ error.status = result.status ?? 1;
191
+ error.signal = result.signal || "";
192
+ throw error;
193
+ }
194
+ return result.stdout;
195
+ }
196
+
197
+ export function runProcessTreeCommandSync(command, args, {
198
+ cwd = process.cwd(),
199
+ env = process.env,
200
+ stdio = "inherit",
201
+ timeout,
202
+ graceMs = 2000,
203
+ windowsVerbatimArguments = false,
204
+ } = {}) {
205
+ if (runningStandaloneBundle()) {
206
+ return standaloneProcessCommand(command, args, { cwd, env, stdio, timeout });
207
+ }
208
+ const commandStdio = Array.isArray(stdio)
209
+ ? ["pipe", stdio[1] || "inherit", stdio[2] || "inherit"]
210
+ : ["pipe", stdio, stdio];
211
+ try {
212
+ return execFileSync(process.execPath, [modulePath, INTERNAL_PROCESS_TREE_COMMAND], {
213
+ cwd,
214
+ env,
215
+ stdio: commandStdio,
216
+ input: JSON.stringify({
217
+ command,
218
+ args,
219
+ cwd,
220
+ timeoutMs: timeout,
221
+ graceMs,
222
+ windowsVerbatimArguments,
223
+ }),
224
+ windowsHide: true,
225
+ });
226
+ } catch (error) {
227
+ if (error?.status === TIMEOUT_EXIT_CODE) error.code = "ETIMEDOUT";
228
+ throw error;
229
+ }
230
+ }
231
+
232
+ export function runShellCommandSync(command, options = {}) {
233
+ if (runningStandaloneBundle()) return execSync(command, options);
234
+ const invocation = shellCommandInvocation(command, options.shell);
235
+ return runProcessTreeCommandSync(invocation.command, invocation.args, {
236
+ ...options,
237
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments,
238
+ });
239
+ }
240
+
241
+ if (
242
+ process.argv[1]
243
+ && process.argv[2] === INTERNAL_PROCESS_TREE_COMMAND
244
+ && import.meta.url === pathToFileURL(process.argv[1]).href
245
+ ) {
246
+ void runInternalProcessTreeCommand().catch((error) => {
247
+ console.error(`buildchain: process tree command failed: ${error.message}`);
248
+ process.exitCode = 1;
25
249
  });
26
250
  }
@@ -0,0 +1,219 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import {
7
+ adopterDeliveryGateDigest,
8
+ createAdopterDeliveryGate,
9
+ } from "./adopter-delivery-gate.js";
10
+
11
+ export const V4_ADOPTER_DELIVERY_PARITY_INPUT_CONTRACT =
12
+ "kungfu-buildchain-v4-adopter-delivery-parity-input";
13
+ export const V4_ADOPTER_DELIVERY_PARITY_PROJECTION_CONTRACT =
14
+ "buildchain-v4-adopter-delivery-parity-projection/v1";
15
+
16
+ const HOST_REQUEST_CONTRACT = "kungfu-buildchain-v4-host-request";
17
+ const HOST_RESPONSE_CONTRACT = "kungfu-buildchain-v4-host-response";
18
+ const DRIVER_INTERFACE = "kungfu-buildchain-adopter-protocol-driver/v1";
19
+ const PROFILE_INTERFACE = "kungfu-buildchain-adopter-artifact-profile/v1";
20
+ const REQUIRED_CAPABILITIES = Object.freeze([
21
+ "canonical-input-v1",
22
+ "adopter-delivery-parity-projection-v1",
23
+ "diagnostics-v1",
24
+ "effects-disabled-v1",
25
+ "structured-result-v1",
26
+ ]);
27
+ const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
28
+ const root = path.resolve(
29
+ path.dirname(fileURLToPath(import.meta.url)),
30
+ "../..",
31
+ );
32
+
33
+ export const V4_ADOPTER_DELIVERY_PARITY_SOURCE = Object.freeze({
34
+ v3Commit: "c3f58d76391c1c6ceddfc900a68e91c7ab82a575",
35
+ vectorSuiteRoot:
36
+ "sha256:cf329805d928a9883cbafbdfdf21ef66c6a0889ed8dfe14356b4e0d25d6738f9",
37
+ kfdPackage: "@kungfu-tech/kfd@1.0.0-alpha.62",
38
+ });
39
+
40
+ function clone(value) {
41
+ return structuredClone(value);
42
+ }
43
+
44
+ function exactKey(definition) {
45
+ return `${definition.id}@${definition.version}`;
46
+ }
47
+
48
+ function capture(operation) {
49
+ try {
50
+ return { state: "returned", result: clone(operation()) };
51
+ } catch {
52
+ return { state: "threw", result: null };
53
+ }
54
+ }
55
+
56
+ function definition(definition, execution, { profile = false } = {}) {
57
+ if (!definition) return null;
58
+ return {
59
+ interface: definition.interface,
60
+ id: definition.id,
61
+ version: definition.version,
62
+ ...(profile ? { kinds: [...definition.kinds] } : {}),
63
+ execution,
64
+ };
65
+ }
66
+
67
+ export function createV4AdopterDeliveryParityInput({
68
+ vectorId,
69
+ request,
70
+ context = {},
71
+ drivers = [],
72
+ artifactProfiles = [],
73
+ sourceAuthority = V4_ADOPTER_DELIVERY_PARITY_SOURCE,
74
+ } = {}) {
75
+ if (typeof vectorId !== "string" || vectorId.length === 0) {
76
+ throw new TypeError("vectorId must be a non-empty string");
77
+ }
78
+ const gate = createAdopterDeliveryGate({ drivers, artifactProfiles });
79
+ const expectedResult = gate.evaluate(clone(request), clone(context));
80
+ const driver = drivers.find(
81
+ (entry) =>
82
+ exactKey(entry) === `${request.protocol.id}@${request.protocol.version}`,
83
+ );
84
+ const artifactProfile = artifactProfiles.find(
85
+ (entry) =>
86
+ exactKey(entry) ===
87
+ `${request.artifactProfile.id}@${request.artifactProfile.version}`,
88
+ );
89
+ let profileExecution = null;
90
+ if (artifactProfile?.kinds.includes(request.artifact.kind)) {
91
+ profileExecution = capture(() =>
92
+ artifactProfile.verify(clone(request.artifact)),
93
+ );
94
+ }
95
+ let driverExecution = null;
96
+ if (
97
+ driver &&
98
+ profileExecution?.state === "returned" &&
99
+ profileExecution.result?.valid === true
100
+ ) {
101
+ driverExecution = capture(() =>
102
+ driver.verify({ request: clone(request), context: clone(context) }),
103
+ );
104
+ }
105
+ return {
106
+ schemaVersion: 1,
107
+ contract: V4_ADOPTER_DELIVERY_PARITY_INPUT_CONTRACT,
108
+ vectorId,
109
+ sourceAuthority: clone(sourceAuthority),
110
+ request: clone(request),
111
+ driver: definition(driver, driverExecution),
112
+ artifactProfile: definition(artifactProfile, profileExecution, {
113
+ profile: true,
114
+ }),
115
+ expectedResult,
116
+ };
117
+ }
118
+
119
+ function hostRequest(input, timeoutMs) {
120
+ return {
121
+ schemaVersion: 1,
122
+ contract: HOST_REQUEST_CONTRACT,
123
+ protocolVersion: "1.0",
124
+ requestId: crypto.randomUUID(),
125
+ command: { id: "adopter-delivery.parity-project", arguments: [] },
126
+ input: {
127
+ encoding: "base64",
128
+ bytes: Buffer.from(JSON.stringify(input), "utf8").toString("base64"),
129
+ },
130
+ requiredCapabilities: [...REQUIRED_CAPABILITIES],
131
+ timeoutMs,
132
+ };
133
+ }
134
+
135
+ export function runV4AdopterDeliveryParity(
136
+ input,
137
+ {
138
+ command = process.platform === "win32" ? "cargo.exe" : "cargo",
139
+ arguments: commandArguments = [
140
+ "run",
141
+ "--locked",
142
+ "--quiet",
143
+ "--manifest-path",
144
+ "crates/buildchain-v4-contracts/Cargo.toml",
145
+ "--",
146
+ "host",
147
+ ],
148
+ cwd = root,
149
+ timeoutMs = 30_000,
150
+ } = {},
151
+ ) {
152
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {
153
+ throw new RangeError("timeoutMs must be between 1 and 30000");
154
+ }
155
+ const request = hostRequest(input, timeoutMs);
156
+ const completed = spawnSync(command, commandArguments, {
157
+ cwd,
158
+ input: JSON.stringify(request),
159
+ encoding: "utf8",
160
+ timeout: timeoutMs,
161
+ maxBuffer: MAX_RESPONSE_BYTES,
162
+ env: process.env,
163
+ });
164
+ if (completed.error) {
165
+ throw new Error(
166
+ `v4 adopter delivery parity host failed: ${completed.error.message}`,
167
+ );
168
+ }
169
+ if (completed.status !== 0) {
170
+ throw new Error(
171
+ `v4 adopter delivery parity host exited ${completed.status}: ${completed.stderr.trim()}`,
172
+ );
173
+ }
174
+ let response;
175
+ try {
176
+ response = JSON.parse(completed.stdout);
177
+ } catch {
178
+ throw new Error("v4 adopter delivery parity host returned invalid JSON");
179
+ }
180
+ if (
181
+ response.schemaVersion !== 1 ||
182
+ response.contract !== HOST_RESPONSE_CONTRACT ||
183
+ response.protocolVersion !== "1.0" ||
184
+ response.requestId !== request.requestId ||
185
+ response.command?.id !== request.command.id ||
186
+ response.status !== "ok"
187
+ ) {
188
+ const code = response.diagnostics?.[0]?.code || "invalid-response";
189
+ throw new Error(`v4 adopter delivery parity failed closed: ${code}`);
190
+ }
191
+ const projection = response.structuredResult;
192
+ if (
193
+ projection?.schema !== V4_ADOPTER_DELIVERY_PARITY_PROJECTION_CONTRACT ||
194
+ projection.effectMode !== "disabled" ||
195
+ projection.vectorId !== input.vectorId ||
196
+ !/^sha256:[0-9a-f]{64}$/u.test(projection.projectionRoot || "")
197
+ ) {
198
+ throw new Error("v4 adopter delivery parity projection is invalid");
199
+ }
200
+ return projection;
201
+ }
202
+
203
+ export function assertV4AdopterDeliveryParity(input, options = {}) {
204
+ const projection = runV4AdopterDeliveryParity(input, options);
205
+ if (
206
+ adopterDeliveryGateDigest(projection.result) !==
207
+ adopterDeliveryGateDigest(input.expectedResult)
208
+ ) {
209
+ throw new Error(
210
+ "v4 adopter delivery parity result differs from v3 authority",
211
+ );
212
+ }
213
+ return projection;
214
+ }
215
+
216
+ export const V4_ADOPTER_DELIVERY_PARITY_INTERFACES = Object.freeze({
217
+ driver: DRIVER_INTERFACE,
218
+ artifactProfile: PROFILE_INTERFACE,
219
+ });