@kungfu-tech/buildchain 4.1.3-alpha.1 → 4.1.3-alpha.10

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 (263) hide show
  1. package/AGENTS.md +41 -75
  2. package/CONTRIBUTING.md +19 -26
  3. package/architecture/action-taxonomy.json +24 -0
  4. package/architecture/agent-change-map.md +266 -10
  5. package/architecture/capability-state-machine-manifest.json +1 -4
  6. package/architecture/ci-lane-change-budget.json +190 -807
  7. package/architecture/core-mechanisms.json +3 -6
  8. package/architecture/decisions/0005-minimal-consumer-contract.md +122 -0
  9. package/architecture/decisions/0006-business-attempt-journal.md +89 -0
  10. package/architecture/decisions/0007-hosted-pipeline-controller.md +106 -0
  11. package/architecture/decisions/0008-pipeline-product-publication.md +120 -0
  12. package/architecture/decisions/0009-pipeline-version-preparation.md +186 -0
  13. package/architecture/internal-capabilities.json +362 -7
  14. package/architecture/maintainability-debt.json +50 -34
  15. package/architecture/maintainability-policy.json +8 -8
  16. package/architecture/minimal-consumer-migration.json +5670 -0
  17. package/architecture/platform-stage-checkpoints.json +31 -7
  18. package/architecture/product-upstreams.json +20 -0
  19. package/architecture/release-topology.json +328 -74
  20. package/architecture/stage-capsule-qualification.json +3 -1
  21. package/architecture/universal-workflow-bootstrap.json +15 -1
  22. package/architecture/universal-workflow-capability-policy.json +4 -4
  23. package/architecture/universal-workflow-fault-campaign.json +3 -1
  24. package/architecture/workflow-taxonomy.json +77 -328
  25. package/dist/readers/business-attempt.cjs +809 -0
  26. package/dist/site/buildchain-contract.json +16 -68
  27. package/dist/site/buildchain-site.json +40 -25
  28. package/dist/site/capability-registry.json +4 -4
  29. package/dist/site/controller-registry.json +1 -47
  30. package/dist/site/kfd-claims.json +499 -494
  31. package/dist/site/kfd-upstream-aggregate.json +1 -1
  32. package/dist/site/manual-registry.json +3 -3
  33. package/dist/site/node-api-registry.json +54 -54
  34. package/dist/site/page-registry.json +33 -18
  35. package/dist/site/public-surface-audit.json +286 -349
  36. package/dist/site/publication-authority-registry.json +203 -523
  37. package/dist/site/publication-registry.json +4 -4
  38. package/dist/site/site-manifest.json +7 -7
  39. package/dist/site/workflow-registry.json +590 -940
  40. package/docs/code-organization.md +9 -6
  41. package/docs/node-api-reference.md +102 -102
  42. package/docs/release-governance.md +10 -12
  43. package/docs/runtime-entry.md +73 -5
  44. package/docs/runtime-train-validation.md +39 -11
  45. package/docs/stable-candidate-patrol.md +6 -5
  46. package/docs/workflow-catalog.md +9 -32
  47. package/package.json +4 -3
  48. package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
  49. package/packages/core/build/standalone/build.js +7 -1
  50. package/packages/core/consumer/buildchain-config.js +2 -21
  51. package/packages/core/consumer/contract/entries.js +85 -0
  52. package/packages/core/consumer/contract/examples.js +96 -0
  53. package/packages/core/consumer/contract/identity.js +65 -0
  54. package/packages/core/consumer/contract/inspection.js +45 -0
  55. package/packages/core/consumer/contract/plan.js +149 -0
  56. package/packages/core/consumer/contract/products.js +119 -0
  57. package/packages/core/consumer/contract/reader.js +47 -0
  58. package/packages/core/consumer/contract/shape.js +46 -0
  59. package/packages/core/dev-delivery/candidate/admission.js +9 -5
  60. package/packages/core/dev-delivery/candidate/reservation.js +11 -0
  61. package/packages/core/dev-delivery/candidate/source-paths.js +12 -4
  62. package/packages/core/dev-delivery/commands/project-cut-merge-queue-admission.mjs +47 -20
  63. package/packages/core/dev-delivery/dev-delivery-provider-heartbeat.js +18 -15
  64. package/packages/core/dev-delivery/native/actions.js +9 -1
  65. package/packages/core/dev-delivery/native/heartbeat-action.js +16 -0
  66. package/packages/core/dev-delivery/native/heartbeat.js +6 -4
  67. package/packages/core/dev-delivery/native/process-entry.js +19 -0
  68. package/packages/core/dev-delivery/queue/landing-action.js +53 -0
  69. package/packages/core/dev-delivery/warrant/failure-settlement.js +8 -6
  70. package/packages/core/dev-delivery/warrant/service.js +7 -0
  71. package/packages/core/governance/buildchain-publication-authority.js +8 -23
  72. package/packages/core/governance/github-governance-authority.js +1 -0
  73. package/packages/core/observability/controller-evidence.js +0 -8
  74. package/packages/core/paper/operations/bootstrap.js +2 -0
  75. package/packages/core/paper/paper-npm-bootstrap.js +2 -2
  76. package/packages/core/providers/github/attempt-index.js +60 -0
  77. package/packages/core/providers/github/attempt-journal.js +154 -0
  78. package/packages/core/providers/github/discussions/materials.js +19 -6
  79. package/packages/core/providers/github/pipeline-checkout.js +49 -0
  80. package/packages/core/providers/github/pipeline-entry-runs.js +210 -0
  81. package/packages/core/providers/github/pipeline-events.js +131 -0
  82. package/packages/core/providers/github/pipeline-integration.js +95 -0
  83. package/packages/core/providers/github/pipeline-policy.js +240 -0
  84. package/packages/core/providers/github/pipeline-product-payload.js +50 -0
  85. package/packages/core/providers/github/pipeline-product-release.js +198 -0
  86. package/packages/core/providers/github/pipeline-publication-artifacts.js +153 -0
  87. package/packages/core/providers/github/pipeline-release-evidence.js +30 -0
  88. package/packages/core/providers/github/pipeline-run-entry.js +86 -0
  89. package/packages/core/providers/github/pipeline-runs.js +132 -0
  90. package/packages/core/providers/github/pipeline-source.js +195 -0
  91. package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
  92. package/packages/core/providers/github/pipeline-stable-source.js +372 -0
  93. package/packages/core/providers/github/pipeline-version-artifacts.js +91 -0
  94. package/packages/core/providers/github/pipeline-version-readback.js +117 -0
  95. package/packages/core/providers/github/pipeline-version.js +199 -0
  96. package/packages/core/providers/github/pipeline-worker.js +108 -0
  97. package/packages/core/providers/github/release-evidence-assets.js +97 -0
  98. package/packages/core/publication/candidate/registry-hydration.js +2 -2
  99. package/packages/core/publication/npm/pack-preview.js +2 -2
  100. package/packages/core/publication/npm/pack-result.js +31 -0
  101. package/packages/core/publication/npm/package.js +3 -6
  102. package/packages/core/publication/npm/pipeline-channel.js +36 -0
  103. package/packages/core/publication/npm/pipeline-provider.js +137 -0
  104. package/packages/core/publication/npm/registry.js +12 -9
  105. package/packages/core/publication/pipeline/actions.js +116 -0
  106. package/packages/core/publication/pipeline/apply.js +189 -0
  107. package/packages/core/publication/pipeline/build-segments.js +107 -0
  108. package/packages/core/publication/pipeline/capsules.js +92 -0
  109. package/packages/core/publication/pipeline/context.js +39 -0
  110. package/packages/core/publication/pipeline/development-anchor.js +91 -0
  111. package/packages/core/publication/pipeline/development-current.js +63 -0
  112. package/packages/core/publication/pipeline/development-pr.js +112 -0
  113. package/packages/core/publication/pipeline/development-proof.js +76 -0
  114. package/packages/core/publication/pipeline/development-transition.js +112 -0
  115. package/packages/core/publication/pipeline/distribution.js +130 -0
  116. package/packages/core/publication/pipeline/documents.js +192 -0
  117. package/packages/core/publication/pipeline/effects.js +192 -0
  118. package/packages/core/publication/pipeline/files.js +66 -0
  119. package/packages/core/publication/pipeline/imported-materials.js +32 -0
  120. package/packages/core/publication/pipeline/journal.js +122 -0
  121. package/packages/core/publication/pipeline/next-development.js +117 -0
  122. package/packages/core/publication/pipeline/pack.js +180 -0
  123. package/packages/core/publication/pipeline/package-policy.js +22 -0
  124. package/packages/core/publication/pipeline/plan.js +136 -0
  125. package/packages/core/publication/pipeline/prepare.js +139 -0
  126. package/packages/core/publication/pipeline/qualification.js +175 -0
  127. package/packages/core/publication/pipeline/qualify.js +104 -0
  128. package/packages/core/publication/pipeline/recovery-admission.js +122 -0
  129. package/packages/core/publication/pipeline/recovery-build-download.js +80 -0
  130. package/packages/core/publication/pipeline/recovery-build-plan.js +122 -0
  131. package/packages/core/publication/pipeline/recovery-build-readback.js +68 -0
  132. package/packages/core/publication/pipeline/recovery-capsules.js +54 -0
  133. package/packages/core/publication/pipeline/recovery-import.js +80 -0
  134. package/packages/core/publication/pipeline/recovery-inspection.js +218 -0
  135. package/packages/core/publication/pipeline/recovery-materials.js +104 -0
  136. package/packages/core/publication/pipeline/recovery-plan.js +46 -0
  137. package/packages/core/publication/pipeline/recovery-prepare.js +73 -0
  138. package/packages/core/publication/pipeline/recovery-qualification.js +128 -0
  139. package/packages/core/publication/pipeline/recovery-readback.js +56 -0
  140. package/packages/core/publication/pipeline/recovery-signing.js +58 -0
  141. package/packages/core/publication/pipeline/sealed-products.js +79 -0
  142. package/packages/core/publication/pipeline/settle.js +42 -0
  143. package/packages/core/publication/pipeline/signing.js +140 -0
  144. package/packages/core/publication/pipeline/source-plan.js +83 -0
  145. package/packages/core/publication/pipeline/stable-entry.js +219 -0
  146. package/packages/core/publication/pipeline/stable-products.js +317 -0
  147. package/packages/core/publication/pipeline/stable-wait.js +170 -0
  148. package/packages/core/publication/pipeline/stable.js +82 -0
  149. package/packages/core/publication/pipeline/version-actions.js +93 -0
  150. package/packages/core/publication/pipeline/version-build.js +130 -0
  151. package/packages/core/publication/pipeline/version-context.js +143 -0
  152. package/packages/core/publication/pipeline/version-material.js +36 -0
  153. package/packages/core/publication/pipeline/version-preparation.js +69 -0
  154. package/packages/core/publication/pipeline/version-regeneration.js +162 -0
  155. package/packages/core/publication/pipeline/version-source-guard.js +69 -0
  156. package/packages/core/publication/pipeline/version.js +81 -0
  157. package/packages/core/publication/pipeline/worker.js +72 -0
  158. package/packages/core/publication/publication-reproducibility.js +2 -2
  159. package/packages/core/release/next-development-transition.js +15 -15
  160. package/packages/core/release/promote-candidate/product-provider-adapters.js +4 -3
  161. package/packages/core/release/promote-ref/internal/npm-existing-evidence.js +2 -1
  162. package/packages/core/release/promote-ref/internal/publish-command.js +16 -5
  163. package/packages/core/release/stable-release-gate.js +11 -0
  164. package/packages/core/runtime/buildchain-domain.wasm +0 -0
  165. package/packages/core/runtime/domain-wasm-artifact.js +2 -2
  166. package/packages/core/runtime/entry/actions.js +27 -25
  167. package/packages/core/runtime/entry/attempt.js +25 -0
  168. package/packages/core/runtime/entry/selection.js +14 -0
  169. package/packages/core/runtime/entry/source.js +62 -0
  170. package/packages/core/workflow/attempt/identity.js +170 -0
  171. package/packages/core/workflow/attempt/journal.js +89 -0
  172. package/packages/core/workflow/attempt/materials.js +72 -0
  173. package/packages/core/workflow/attempt/reader-entry.js +12 -0
  174. package/packages/core/workflow/attempt/reader.js +149 -0
  175. package/packages/core/workflow/attempt/records.js +153 -0
  176. package/packages/core/workflow/attempt/store.js +75 -0
  177. package/packages/core/workflow/commands/pipeline-build.mjs +23 -0
  178. package/packages/core/workflow/pipeline/action-output.js +11 -0
  179. package/packages/core/workflow/pipeline/actions.js +52 -0
  180. package/packages/core/workflow/pipeline/build-control.js +140 -0
  181. package/packages/core/workflow/pipeline/build-evidence.js +210 -0
  182. package/packages/core/workflow/pipeline/build-qualification.js +145 -0
  183. package/packages/core/workflow/pipeline/build-result.js +78 -0
  184. package/packages/core/workflow/pipeline/build.js +142 -0
  185. package/packages/core/workflow/pipeline/cancellation.js +169 -0
  186. package/packages/core/workflow/pipeline/channel-control.js +157 -0
  187. package/packages/core/workflow/pipeline/channel-source.js +53 -0
  188. package/packages/core/workflow/pipeline/controller.js +141 -0
  189. package/packages/core/workflow/pipeline/delivery-control.js +249 -0
  190. package/packages/core/workflow/pipeline/delivery-observation.js +124 -0
  191. package/packages/core/workflow/pipeline/delivery-request.js +198 -0
  192. package/packages/core/workflow/pipeline/events.js +84 -0
  193. package/packages/core/workflow/pipeline/fence.js +42 -0
  194. package/packages/core/workflow/pipeline/group-control.js +59 -0
  195. package/packages/core/workflow/pipeline/guard-build.js +74 -0
  196. package/packages/core/workflow/pipeline/guard.js +158 -0
  197. package/packages/core/workflow/pipeline/host.js +144 -0
  198. package/packages/core/workflow/pipeline/integration-status.js +113 -0
  199. package/packages/core/workflow/pipeline/materials.js +58 -0
  200. package/packages/core/workflow/pipeline/notifications.js +36 -0
  201. package/packages/core/workflow/pipeline/parent-notification.js +27 -0
  202. package/packages/core/workflow/pipeline/platforms.js +21 -0
  203. package/packages/core/workflow/pipeline/progress.js +108 -0
  204. package/packages/core/workflow/pipeline/projection.js +20 -0
  205. package/packages/core/workflow/pipeline/reconcile.js +194 -0
  206. package/packages/core/workflow/pipeline/record-action.js +27 -0
  207. package/packages/core/workflow/pipeline/recorder-status.js +69 -0
  208. package/packages/core/workflow/pipeline/recovery-action.js +30 -0
  209. package/packages/core/workflow/pipeline/recovery-admission.js +122 -0
  210. package/packages/core/workflow/pipeline/recovery-build-control.js +95 -0
  211. package/packages/core/workflow/pipeline/recovery-build-evidence.js +71 -0
  212. package/packages/core/workflow/pipeline/recovery-build.js +121 -0
  213. package/packages/core/workflow/pipeline/recovery-controller.js +91 -0
  214. package/packages/core/workflow/pipeline/recovery-integration.js +79 -0
  215. package/packages/core/workflow/pipeline/recovery-merge-proof.js +90 -0
  216. package/packages/core/workflow/pipeline/recovery-ownership-settlement.js +150 -0
  217. package/packages/core/workflow/pipeline/recovery-ownership.js +91 -0
  218. package/packages/core/workflow/pipeline/recovery-plan.js +126 -0
  219. package/packages/core/workflow/pipeline/recovery-runtime.js +68 -0
  220. package/packages/core/workflow/pipeline/recovery-session.js +129 -0
  221. package/packages/core/workflow/pipeline/recovery-transition.js +135 -0
  222. package/packages/core/workflow/pipeline/recovery-unrecorded-build.js +74 -0
  223. package/packages/core/workflow/pipeline/runtime-source.js +111 -0
  224. package/packages/core/workflow/pipeline/selection.js +111 -0
  225. package/packages/core/workflow/pipeline/session.js +72 -0
  226. package/packages/core/workflow/pipeline/settlement.js +137 -0
  227. package/packages/core/workflow/pipeline/wake.js +38 -0
  228. package/packages/core/workflow/pipeline/web-status.js +108 -0
  229. package/packages/core/workflow/workflow-taxonomy.mjs +67 -47
  230. package/scripts/build-release-discussion-reader.mjs +11 -7
  231. package/scripts/check-inventory.mjs +7 -70
  232. package/scripts/check-pipeline-publication-topology.mjs +50 -0
  233. package/scripts/check-public-dogfood-contract.mjs +38 -51
  234. package/scripts/check-release-topology.mjs +15 -8
  235. package/scripts/check-self-consumer-contract.mjs +133 -0
  236. package/scripts/check-universal-workflow-bootstrap.mjs +4 -6
  237. package/scripts/generate-minimal-consumer-contract.mjs +155 -0
  238. package/scripts/generate-site-bundle.mjs +1 -1
  239. package/scripts/inventory/binary.mjs +9 -19
  240. package/scripts/inventory/promotion.mjs +2 -13
  241. package/scripts/prepare-product-toolchain.mjs +10 -0
  242. package/scripts/verify-golden-path.mjs +5 -1
  243. package/scripts/verify-product-platform.mjs +65 -0
  244. package/scripts/verify-standalone-product.mjs +42 -0
  245. package/templates/minimal-consumer/binary/.buildchain/buildchain.toml +45 -0
  246. package/templates/minimal-consumer/binary/.github/workflows/buildchain-recover.yml +30 -0
  247. package/templates/minimal-consumer/binary/.github/workflows/buildchain.yml +28 -0
  248. package/templates/minimal-consumer/binary/package.json +6 -0
  249. package/templates/minimal-consumer/binary/src/build.mjs +5 -0
  250. package/templates/minimal-consumer/binary/src/hello.c +2 -0
  251. package/templates/minimal-consumer/binary/src/verify.mjs +3 -0
  252. package/templates/minimal-consumer/npm/.buildchain/buildchain.toml +46 -0
  253. package/templates/minimal-consumer/npm/.github/workflows/buildchain-recover.yml +30 -0
  254. package/templates/minimal-consumer/npm/.github/workflows/buildchain.yml +28 -0
  255. package/templates/minimal-consumer/npm/package.json +6 -0
  256. package/templates/minimal-consumer/npm/src/build.mjs +5 -0
  257. package/templates/minimal-consumer/npm/src/verify.mjs +3 -0
  258. package/templates/minimal-consumer/paper/.buildchain/buildchain.toml +45 -0
  259. package/templates/minimal-consumer/paper/.github/workflows/buildchain-recover.yml +30 -0
  260. package/templates/minimal-consumer/paper/.github/workflows/buildchain.yml +28 -0
  261. package/templates/minimal-consumer/paper/package.json +6 -0
  262. package/templates/minimal-consumer/paper/src/build.mjs +12 -0
  263. package/templates/minimal-consumer/paper/src/verify.mjs +3 -0
@@ -61,19 +61,32 @@ function git(
61
61
  {
62
62
  env = {},
63
63
  input = undefined,
64
+ inputFile = undefined,
65
+ outputFile = undefined,
64
66
  encoding = "utf8",
65
67
  reason = "git-failed",
66
68
  } = {},
67
69
  ) {
68
- const result = spawnSync("git", args, {
69
- cwd,
70
- env: { ...process.env, ...env },
71
- input,
72
- encoding,
73
- maxBuffer: 64 * 1024 * 1024,
74
- });
70
+ let result, inputDescriptor, outputDescriptor;
71
+ try {
72
+ if (inputFile) inputDescriptor = fs.openSync(inputFile, "r");
73
+ if (outputFile) outputDescriptor = fs.openSync(outputFile, "w");
74
+ result = spawnSync("git", args, {
75
+ cwd,
76
+ env: { ...process.env, ...env },
77
+ input,
78
+ encoding,
79
+ stdio: [inputDescriptor ?? "pipe", outputDescriptor ?? "pipe", "pipe"],
80
+ maxBuffer: 64 * 1024 * 1024,
81
+ });
82
+ } finally {
83
+ for (const descriptor of [inputDescriptor, outputDescriptor])
84
+ if (descriptor !== undefined) fs.closeSync(descriptor);
85
+ }
75
86
  if (result.status !== 0) {
76
- const detail = String(result.stderr || result.stdout || "").trim();
87
+ const detail = String(
88
+ result.stderr || result.error?.message || result.stdout || "",
89
+ ).trim();
77
90
  throw new ProjectCutAdmissionError(
78
91
  reason,
79
92
  `git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`,
@@ -135,7 +148,7 @@ function linearSourceCommits(cwd, fork, head) {
135
148
 
136
149
  function patchId(cwd, patch) {
137
150
  const output = git(cwd, ["patch-id", "--stable"], {
138
- input: patch,
151
+ inputFile: patch,
139
152
  encoding: "utf8",
140
153
  reason: "patch-id-failed",
141
154
  });
@@ -184,30 +197,41 @@ function replayProjectCut(cwd, base, head) {
184
197
  env: indexEnvironment,
185
198
  reason: "base-tree-unreadable",
186
199
  });
187
- const sourcePatch = git(
200
+ // Generated bundles can exceed the metadata-output bound. Git streams the
201
+ // exact patches through private files; patch identity and replay stay intact.
202
+ const sourcePatch = path.join(temporary, "source.patch");
203
+ git(
188
204
  cwd,
189
205
  ["diff", "--binary", "--full-index", "--no-ext-diff", fork, head],
190
206
  {
191
- encoding: null,
207
+ outputFile: sourcePatch,
192
208
  reason: "source-patch-unreadable",
193
209
  },
194
210
  );
195
- const emptySourcePatch = sourcePatch.length === 0;
211
+ const emptySourcePatch = fs.statSync(sourcePatch).size === 0;
196
212
  if (emptySourcePatch && commits.length !== 1)
197
- throw new ProjectCutAdmissionError("empty-source-composition", "an empty source composition must be exactly one linear commit");
198
- if (!emptySourcePatch) git(cwd, ["apply", "--cached", "--3way", "--whitespace=nowarn", "-"], {
199
- env: indexEnvironment, input: sourcePatch, encoding: "utf8", reason: "project-cut-conflict",
213
+ throw new ProjectCutAdmissionError(
214
+ "empty-source-composition",
215
+ "an empty source composition must be exactly one linear commit",
216
+ );
217
+ if (!emptySourcePatch)
218
+ git(cwd, ["apply", "--cached", "--3way", "--whitespace=nowarn", "-"], {
219
+ env: indexEnvironment,
220
+ inputFile: sourcePatch,
221
+ encoding: "utf8",
222
+ reason: "project-cut-conflict",
200
223
  });
201
224
  const candidateTreeOid = git(cwd, ["write-tree"], {
202
225
  env: indexEnvironment,
203
226
  reason: "candidate-tree-unreadable",
204
227
  });
205
- const replayPatch = git(
228
+ const replayPatch = path.join(temporary, "replay.patch");
229
+ git(
206
230
  cwd,
207
231
  ["diff", "--cached", "--binary", "--full-index", "--no-ext-diff", base],
208
232
  {
209
233
  env: indexEnvironment,
210
- encoding: null,
234
+ outputFile: replayPatch,
211
235
  reason: "replay-patch-unreadable",
212
236
  },
213
237
  );
@@ -226,9 +250,12 @@ function replayProjectCut(cwd, base, head) {
226
250
  reason: "replay-paths-unreadable",
227
251
  },
228
252
  );
229
- const compositionDrifted = emptySourcePatch ? replayPatch.length !== 0 ||
230
- sourceNames !== "" || replayNames !== "" : patchId(cwd, sourcePatch) !==
231
- patchId(cwd, replayPatch) || sourceNames !== replayNames;
253
+ const compositionDrifted = emptySourcePatch
254
+ ? fs.statSync(replayPatch).size !== 0 ||
255
+ sourceNames !== "" ||
256
+ replayNames !== ""
257
+ : patchId(cwd, sourcePatch) !== patchId(cwd, replayPatch) ||
258
+ sourceNames !== replayNames;
232
259
  if (compositionDrifted) {
233
260
  throw new ProjectCutAdmissionError(
234
261
  "composition-drift",
@@ -207,29 +207,32 @@ export async function runDevDeliveryProviderHeartbeat(
207
207
  const startedAt = timestamp(now(), "provider heartbeat startedAt");
208
208
  let previousStateRoot = initial.stateRoot;
209
209
  const heartbeats = [];
210
+ let untilHeartbeat = 0;
210
211
  let jobs;
211
212
  try {
212
213
  while (true) {
213
- const result = await heartbeat({
214
- expectedOldStateRoot: previousStateRoot,
215
- fencingToken: initial.fencingToken,
216
- leaseGeneration: initial.generation,
217
- leaseSeconds: duration,
218
- });
219
- const entry = heartbeatEntry(result, previousStateRoot);
220
- heartbeats.push(entry);
221
- previousStateRoot = entry.nextStateRoot;
214
+ if (untilHeartbeat === 0) {
215
+ const result = await heartbeat({
216
+ expectedOldStateRoot: previousStateRoot,
217
+ fencingToken: initial.fencingToken,
218
+ leaseGeneration: initial.generation,
219
+ leaseSeconds: duration,
220
+ });
221
+ const entry = heartbeatEntry(result, previousStateRoot);
222
+ heartbeats.push(entry);
223
+ previousStateRoot = entry.nextStateRoot;
224
+ untilHeartbeat = cadence * 1000;
225
+ }
222
226
  jobs = providerJobs(
223
227
  await readJobs(),
224
228
  { nativeJobName, sealJobName },
225
229
  { allowMissingSeal: true },
226
230
  );
227
- if (!jobs) {
228
- await wait(cadence * 1000);
229
- continue;
230
- }
231
- if (terminal(jobs.native) && terminal(jobs.seal)) break;
232
- await wait(cadence * 1000);
231
+ if (jobs && terminal(jobs.native) && terminal(jobs.seal)) break;
232
+ // Terminal detection must not wait for the next durable lease renewal.
233
+ const interval = Math.min(untilHeartbeat, 5000);
234
+ await wait(interval);
235
+ untilHeartbeat -= interval;
233
236
  }
234
237
  } catch (error) {
235
238
  await onHeartbeatLoss({
@@ -5,6 +5,8 @@ import {
5
5
  qualifyTransferredNative,
6
6
  } from "./transactions.js";
7
7
  import { admitProviderFinalizer } from "./finalizer-admission.js";
8
+ import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
9
+ import { replaceNativeActionProcess } from "./process-entry.js";
8
10
  function request(core, env) {
9
11
  const candidate = JSON.parse(
10
12
  core.getInput("request-json", { required: true }),
@@ -22,6 +24,7 @@ function outputs(core, result) {
22
24
  core.setOutput(name, value);
23
25
  }
24
26
  export async function executeDeliveryNativeAction(core, env) {
27
+ replaceNativeActionProcess();
25
28
  const context = deliveryActionContext(core, env);
26
29
  const input = request(core, env);
27
30
  outputs(
@@ -41,11 +44,16 @@ export async function executeDeliveryNativeAction(core, env) {
41
44
  }
42
45
  export async function qualifyTransferredNativeAction(core, env) {
43
46
  const context = deliveryActionContext(core, env);
47
+ const input = request(core, env);
48
+ await guardPipelineAdmission(input.request, {
49
+ repository: env.GITHUB_REPOSITORY,
50
+ token: core.getInput("token", { required: true }),
51
+ });
44
52
  outputs(
45
53
  core,
46
54
  await qualifyTransferredNative({
47
55
  ...context,
48
- candidate: request(core, env).candidate,
56
+ candidate: input.candidate,
49
57
  sourceProofRoot: core.getInput("source-proof-root", { required: true }),
50
58
  token: core.getInput("token", { required: true }),
51
59
  apiUrl: env.GITHUB_API_URL || "https://api.github.com",
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { deliveryActionContext } from "./action-context.js";
4
4
  import { heartbeatDeliveryAttempt } from "./heartbeat.js";
5
+ import { guardPipelineDelivery } from "../../workflow/pipeline/guard.js";
5
6
  export async function heartbeatDeliveryAttemptAction(core, env) {
6
7
  const { workspace, run } = deliveryActionContext(core, env);
7
8
  const admission = JSON.parse(
@@ -20,6 +21,21 @@ export async function heartbeatDeliveryAttemptAction(core, env) {
20
21
  apiUrl: env.GITHUB_API_URL || "https://api.github.com",
21
22
  leaseSeconds: Number(core.getInput("lease-seconds")),
22
23
  heartbeatSeconds: Number(core.getInput("heartbeat-seconds")),
24
+ beforeHeartbeat: () => {
25
+ const warrant = admission.observation?.activeWarrant || admission.warrant;
26
+ return guardPipelineDelivery(
27
+ {
28
+ "pipeline-attempt": core.getInput("pipeline-attempt"),
29
+ "target-branch": core.getInput("branch", { required: true }),
30
+ "expected-pr-number": warrant?.pullRequestNumber,
31
+ "expected-head-sha": warrant?.sourceHead,
32
+ },
33
+ {
34
+ repository: env.GITHUB_REPOSITORY,
35
+ token: core.getInput("token", { required: true }),
36
+ },
37
+ );
38
+ },
23
39
  });
24
40
  fs.writeFileSync(
25
41
  path.join(workspace, ".buildchain/provider-heartbeat-receipt.json"),
@@ -68,13 +68,14 @@ export async function heartbeatDeliveryAttempt(options) {
68
68
  heartbeatSeconds: options.heartbeatSeconds,
69
69
  },
70
70
  {
71
- heartbeat: ({
71
+ heartbeat: async ({
72
72
  expectedOldStateRoot,
73
73
  fencingToken,
74
74
  leaseGeneration,
75
75
  leaseSeconds,
76
- }) =>
77
- runDevDeliveryCommand({
76
+ }) => {
77
+ await options.beforeHeartbeat?.();
78
+ return runDevDeliveryCommand({
78
79
  command: "heartbeat",
79
80
  repository: options.repository,
80
81
  branch: options.branch,
@@ -85,7 +86,8 @@ export async function heartbeatDeliveryAttempt(options) {
85
86
  execute: true,
86
87
  token: options.token,
87
88
  apiUrl: options.apiUrl,
88
- }),
89
+ });
90
+ },
89
91
  readJobs: () =>
90
92
  githubJson(
91
93
  options.apiUrl,
@@ -0,0 +1,19 @@
1
+ // Node Actions receive artifact/OIDC authority even without token inputs.
2
+ // Replace the process before candidate work so the original environment is
3
+ // absent from /proc, memory and ancestry. A scrubbed child would retain it.
4
+ export function replaceNativeActionProcess(host = process) {
5
+ const injected = ["ACTIONS_RUNTIME_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"];
6
+ if (!injected.some((name) => host.env[name])) return;
7
+ if (host.platform !== "linux" || typeof host.execve !== "function") {
8
+ throw new Error("credentialless native action requires Linux execve");
9
+ }
10
+ const environment = { ...host.env };
11
+ for (const name of injected) delete environment[name];
12
+ // Other credential-like variables remain subject to the ancestry guard.
13
+ host.execve(
14
+ host.execPath,
15
+ [host.execPath, ...host.execArgv, ...host.argv.slice(1)],
16
+ environment,
17
+ );
18
+ throw new Error("credentialless native process replacement returned");
19
+ }
@@ -3,9 +3,31 @@ import { deliveryActionContext } from "../native/action-context.js";
3
3
  import { admissionPolicyRequest } from "../admission/request.js";
4
4
  import { runAdmissionTransaction } from "../admission/transaction.js";
5
5
  import { enforceLanding } from "./completion.js";
6
+ import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
7
+ import { githubJsonClient } from "../../providers/github/json-client.js";
8
+ import { publishPipelineBuildStatus } from "../../workflow/pipeline/guard-build.js";
9
+ import { publishIntegratedPipelineStatus } from "../../workflow/pipeline/integration-status.js";
10
+ import { githubPipelineIntegration } from "../../providers/github/pipeline-integration.js";
11
+ import { githubPipelineSource } from "../../providers/github/pipeline-source.js";
12
+ import { githubPipelineRuns } from "../../providers/github/pipeline-runs.js";
13
+ import { GitHubClient } from "../admission/github-client.js";
6
14
  export async function admitLandingAction(core, env) {
7
15
  const context = deliveryActionContext(core, env);
8
16
  const request = JSON.parse(core.getInput("request-json", { required: true }));
17
+ const admitted = await guardPipelineAdmission(request, {
18
+ repository: env.GITHUB_REPOSITORY,
19
+ token: core.getInput("token", { required: true }),
20
+ publishBuildStatus: (build) =>
21
+ publishPipelineBuildStatus(
22
+ request,
23
+ build,
24
+ githubJsonClient({
25
+ token: core.getInput("status-token", { required: true }),
26
+ userAgent: "buildchain-qualified-status",
27
+ }),
28
+ env.GITHUB_REPOSITORY,
29
+ ),
30
+ });
9
31
  const result = await runAdmissionTransaction(
10
32
  {
11
33
  ...admissionPolicyRequest(request, {
@@ -40,6 +62,37 @@ export async function admitLandingAction(core, env) {
40
62
  await core.summary.addRaw(result.summary).write();
41
63
  if (!result.ok)
42
64
  throw new Error("Targeted PR was not admitted at its expected head");
65
+ if (admitted) {
66
+ const repository = env.GITHUB_REPOSITORY;
67
+ const token = core.getInput("token", { required: true });
68
+ const read = githubJsonClient({
69
+ token,
70
+ userAgent: "buildchain-integration-status",
71
+ });
72
+ const [owner, repo] = repository.split("/");
73
+ const queue = new GitHubClient({ repository: { owner, repo }, token });
74
+ await publishIntegratedPipelineStatus(
75
+ {
76
+ ...admitted.history.at(-1),
77
+ intent: admitted.intent,
78
+ },
79
+ {
80
+ repository,
81
+ request: read,
82
+ queue: (branch) => queue.getMergeQueueState(branch),
83
+ integration: githubPipelineIntegration(
84
+ read,
85
+ repository,
86
+ githubPipelineSource(read, repository),
87
+ githubPipelineRuns(read, repository),
88
+ ),
89
+ status: githubJsonClient({
90
+ token: core.getInput("status-token", { required: true }),
91
+ userAgent: "buildchain-qualified-integration-status",
92
+ }),
93
+ },
94
+ );
95
+ }
43
96
  }
44
97
  export function completeDeliveryAction(core) {
45
98
  enforceLanding({
@@ -11,13 +11,15 @@ export function verifyFailureSettlement(result, settlement, stateRoot) {
11
11
  result.receipt.evidenceRoot === settlement.evidenceRoot,
12
12
  "Failure settlement receipt does not bind its exact evidence and prior state",
13
13
  );
14
- const candidate = result.observation?.candidates?.find(
15
- (item) =>
16
- item.pullRequestNumber === settlement.pullRequestNumber &&
17
- item.sourceHead === settlement.sourceHead &&
18
- item.terminal?.evidenceRoot === settlement.evidenceRoot,
14
+ const candidate = result.terminalCandidate;
15
+ requireValue(
16
+ candidate?.candidateId === result.receipt.candidateId &&
17
+ candidate?.pullRequestNumber === settlement.pullRequestNumber &&
18
+ candidate?.sourceHead === settlement.sourceHead &&
19
+ candidate?.status === "terminal-failure" &&
20
+ candidate?.terminal?.evidenceRoot === settlement.evidenceRoot,
21
+ "Failure settlement terminal candidate is missing",
19
22
  );
20
- requireValue(candidate, "Failure settlement terminal candidate is missing");
21
23
  for (const key of [
22
24
  "transferRoot",
23
25
  "finalizerBoundaryRoot",
@@ -104,6 +104,13 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
104
104
  concurrencyRecovery,
105
105
  receipt: persisted.changed.receipt,
106
106
  receiptRoot: persisted.changed.receiptRoot,
107
+ terminalCandidate:
108
+ options.command === "settle"
109
+ ? persisted.changed.queue.candidates.find(
110
+ (candidate) =>
111
+ candidate.candidateId === persisted.changed.receipt.candidateId,
112
+ ) || null
113
+ : null,
107
114
  warrant:
108
115
  persisted.changed.warrant ||
109
116
  persisted.changed.queue.activeWarrant ||
@@ -4,6 +4,8 @@ import path from "node:path";
4
4
  import { createPublicationAuthorityRegistry } from "../publication/publication-authority.js";
5
5
 
6
6
  const DESCRIPTORS = Object.freeze([
7
+ [".github/workflows/buildchain.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
8
+ [".github/workflows/buildchain-recover.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
7
9
  [".github/workflows/public-release-oci-compose-preview.yml", "product-publication", true, ["oci-compose-preview"], "caller-secrets", "none", "fixed", "caller-bound"],
8
10
  [".github/workflows/.build-demo-adapter.yml", "non-publication-oidc"],
9
11
  [".github/workflows/public-build-demo.yml", "governance-write"],
@@ -16,36 +18,23 @@ const DESCRIPTORS = Object.freeze([
16
18
  [".github/workflows/public-release-web.yml", "product-publication", true, ["web-production"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
17
19
  [".github/workflows/.build-wheel-verify-compat.yml", "retired-deny"],
18
20
  [".github/workflows/.release-binary-assets.yml", "product-publication", true, ["github-release"], "github-token", "buildchain-release-assets"],
19
- [".github/workflows/self-build-binary-distribution.yml", "evidence-publication"],
20
- [".github/workflows/self-release-binary-assets.yml", "governance-write"],
21
21
  [".github/workflows/public-release-signing-authority.yml", "evidence-publication"],
22
- [".github/workflows/self-build-demo-dogfood.yml", "governance-write"],
23
- [".github/workflows/self-build-fixture.yml", "non-publication-oidc"],
24
22
  [".github/workflows/build.yml", "non-publication-oidc"],
25
23
  [".github/workflows/public-ops-bootstrap.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
26
- [".github/workflows/self-build-alpha-dogfood.yml", "non-publication-oidc"],
27
- [".github/workflows/self-build-stable-dogfood.yml", "non-publication-oidc"],
28
- [".github/workflows/self-ops-dev-delivery.yml", "governance-write"],
29
- [".github/workflows/self-ops-patrol-daily.yml", "governance-write"],
30
- [".github/workflows/self-ops-patrol-monthly.yml", "governance-write"],
31
- [".github/workflows/self-ops-patrol-weekly.yml", "governance-write"],
32
24
  [".github/workflows/public-ops-patrol.yml", "governance-write"],
33
- [".github/workflows/self-ops-promotion-recovery.yml", "governance-write"],
34
- [".github/workflows/self-release-promote.yml", "governance-write"],
35
- [".github/workflows/self-ops-stable-candidate-patrol.yml", "governance-write"],
36
25
  [".github/workflows/public-ops-dev-qualification-patrol.yml", "governance-write"],
37
26
  [".github/workflows/public-ops-alpha-candidate-patrol.yml", "governance-write"],
38
27
  [".github/workflows/public-ops-warrant-close.yml", "governance-write"],
39
28
  [".github/workflows/public-ops-warrant-cancel.yml", "governance-write"],
40
- [".github/workflows/self-ops-merge-queue.yml", "governance-write"],
41
29
  [".github/workflows/public-ops-dev-auto-merge.yml", "governance-write"],
42
- [".github/workflows/self-ops-housekeeping-daily.yml", "governance-write"],
43
- [".github/workflows/self-ops-housekeeping-monthly.yml", "governance-write"],
44
- [".github/workflows/self-ops-housekeeping-weekly.yml", "governance-write"],
30
+ [".github/workflows/public-ops-pipeline.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
31
+ [".github/workflows/public-ops-recover.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
32
+ [".github/workflows/.ops-pipeline-execute.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
33
+ [".github/workflows/.release-pipeline-products.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed"],
34
+ [".github/workflows/.release-pipeline-version.yml", "governance-write"],
35
+ [".github/workflows/.ops-pipeline-delivery.yml", "governance-write"],
45
36
  [".github/workflows/public-ops-housekeeping.yml", "governance-write"],
46
- [".github/workflows/self-ops-governance-audit.yml", "governance-write"],
47
37
  [".github/workflows/public-release-artifact-attestation.yml", "evidence-publication", true, ["github-artifact-attestation"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
48
- [".github/workflows/self-release-npm-dry-run.yml", "dry-run-only"],
49
38
  [".github/workflows/public-release-paper.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
50
39
  [".github/workflows/public-ops-patrol-daily.yml", "governance-write"],
51
40
  [".github/workflows/public-ops-patrol-monthly.yml", "governance-write"],
@@ -53,13 +42,9 @@ const DESCRIPTORS = Object.freeze([
53
42
  [".github/workflows/public-ops-patrol-weekly.yml", "governance-write"],
54
43
  [".github/workflows/public-build-publication.yml", "evidence-publication"],
55
44
  [".github/workflows/public-release-promote.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "caller-bound"],
56
- [".github/workflows/self-release-line-open.yml", "governance-write"],
57
45
  [".github/workflows/public-release-propagation.yml", "governance-write"],
58
46
  [".github/workflows/public-release-tail.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "caller-bound", "caller-bound", "fixed"],
59
- [".github/workflows/self-build-release-verify-compat.yml", "governance-write"],
60
47
  [".github/workflows/public-ops-stable-candidate-patrol.yml", "governance-write"],
61
- [".github/workflows/self-ops-bootstrap-dogfood.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
62
- [".github/workflows/self-release-tail-dogfood.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "none", "fixed", "fixed"],
63
48
  ]);
64
49
 
65
50
  export function buildchainPublicationAuthorityDescriptors({ root = process.cwd() } = {}) {
@@ -1257,6 +1257,7 @@ export function createGithubRulesetGovernanceRolloutPlan({
1257
1257
  dismiss_stale_reviews_on_push: true,
1258
1258
  require_code_owner_review: true,
1259
1259
  require_last_push_approval: true,
1260
+ require_extra_approval_for_unattributed_changes: true,
1260
1261
  required_approving_review_count: requiredApprovals,
1261
1262
  required_review_thread_resolution: true,
1262
1263
  },
@@ -124,14 +124,6 @@ const CONTROLLER_SPECS = [
124
124
  ],
125
125
  evidence: ["propagation-plan", "propagation-lock", "controller-receipt"],
126
126
  },
127
- {
128
- id: "binary-distribution",
129
- workflowId: "binary-distribution",
130
- version: 1,
131
- capabilities: ["standalone-binary-build", "release-evidence-bundle", "artifact-admission"],
132
- stages: ["resolve-runtime", "build", "verify", "bundle", "aggregate"],
133
- evidence: ["release-evidence-bundle", "release-passport", "controller-receipt"],
134
- },
135
127
  ];
136
128
 
137
129
  function stableJson(value) {
@@ -1,3 +1,4 @@
1
+ import { readNpmPackResult } from "../../publication/npm/pack-result.js";
1
2
  import path from "node:path";
2
3
  import fs from "node:fs";
3
4
  import { jsonText, extractUrls, safeParseJson, toPosix } from "./files.js";
@@ -66,6 +67,7 @@ export function executePaperNpmBootstrap(options = {}) {
66
67
  path,
67
68
  resolvePaperRepository,
68
69
  safeParseJson,
70
+ readNpmPackResult,
69
71
  toPosix,
70
72
  validatePaperProvisioningAuthority,
71
73
  writePaperReceipt,
@@ -20,6 +20,7 @@ function executePaperNpmBootstrap(options = {}, runtime) {
20
20
  path,
21
21
  resolvePaperRepository,
22
22
  safeParseJson,
23
+ readNpmPackResult,
23
24
  toPosix,
24
25
  validatePaperProvisioningAuthority,
25
26
  writePaperReceipt,
@@ -208,8 +209,7 @@ function executePaperNpmBootstrap(options = {}, runtime) {
208
209
  pack: {
209
210
  status: pack.ok ? "pass" : "fail",
210
211
  entryCount: (() => {
211
- const parsed = safeParseJson(pack.stdout);
212
- const value = Array.isArray(parsed) ? parsed[0] : parsed;
212
+ const value = pack.ok ? readNpmPackResult(pack.stdout) : null;
213
213
  return Array.isArray(value?.files) ? value.files.length : 0;
214
214
  })(),
215
215
  },
@@ -0,0 +1,60 @@
1
+ // Immutable lookup pointers are derived from admitted journal commits. The
2
+ // canonical intent journal must contain the selected root before a pointer can
3
+ // resolve; an orphan commit or pointer cannot create attempt authority.
4
+ export function githubAttemptIndex(request, journal, repository) {
5
+ if (!/^[\w.-]+\/[\w.-]+$/u.test(repository || ""))
6
+ throw new Error("Invalid attempt lookup repository");
7
+ const base = `/repos/${repository}`;
8
+ function ref(attempt) {
9
+ if (!/^attempt-[0-9a-f]{64}$/u.test(attempt || ""))
10
+ throw new Error("Invalid business attempt selector");
11
+ return `buildchain/attempt-index/${attempt}`;
12
+ }
13
+ function root(snapshot, attempt) {
14
+ const roots = snapshot.records.filter(
15
+ (record) => record.attempt === attempt && record.node === "attempt",
16
+ );
17
+ if (roots.length !== 1)
18
+ throw new Error("Lookup does not identify one admitted attempt root");
19
+ return roots[0];
20
+ }
21
+ async function resolve(attempt) {
22
+ const pointer = await request(`${base}/git/ref/heads/${ref(attempt)}`, {
23
+ allow404: true,
24
+ });
25
+ if (!pointer) throw new Error("Business attempt lookup is not retained");
26
+ const indexed = await journal.readCommit(repository, pointer.object?.sha);
27
+ const selected = root(indexed.snapshot, attempt);
28
+ const current = await journal.read(indexed.snapshot.intent);
29
+ if (!current || root(current.snapshot, attempt).id !== selected.id)
30
+ throw new Error(
31
+ "Lookup attempt is absent from the authoritative journal",
32
+ );
33
+ return { ...current, selectedAttempt: attempt };
34
+ }
35
+ async function retain(intent, attempt) {
36
+ if (intent.repository !== repository)
37
+ throw new Error("Attempt lookup repository drift");
38
+ const current = await journal.read(intent);
39
+ if (!current) throw new Error("Cannot index an unadmitted attempt");
40
+ const expected = root(current.snapshot, attempt);
41
+ let failure;
42
+ try {
43
+ await request(`${base}/git/refs`, {
44
+ method: "POST",
45
+ body: { ref: `refs/heads/${ref(attempt)}`, sha: current.commit },
46
+ });
47
+ } catch (error) {
48
+ failure = error;
49
+ }
50
+ try {
51
+ const result = await resolve(attempt);
52
+ if (root(result.snapshot, attempt).id !== expected.id)
53
+ throw new Error("Conflicting business attempt lookup");
54
+ return result;
55
+ } catch (error) {
56
+ throw failure || error;
57
+ }
58
+ }
59
+ return { retain, resolve };
60
+ }