@kungfu-tech/buildchain 4.1.3-alpha.0 → 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 (278) 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 +272 -12
  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 +371 -9
  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 +331 -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 +9 -9
  23. package/architecture/universal-workflow-fault-campaign.json +3 -1
  24. package/architecture/workflow-taxonomy.json +77 -328
  25. package/contracts/release-discussion-v1.schema.json +3 -0
  26. package/dist/readers/business-attempt.cjs +809 -0
  27. package/dist/readers/release-discussion.cjs +4 -0
  28. package/dist/site/buildchain-contract.json +16 -68
  29. package/dist/site/buildchain-site.json +47 -27
  30. package/dist/site/capability-registry.json +4 -4
  31. package/dist/site/controller-registry.json +1 -47
  32. package/dist/site/kfd-claims.json +499 -494
  33. package/dist/site/kfd-upstream-aggregate.json +1 -1
  34. package/dist/site/manual-registry.json +3 -3
  35. package/dist/site/node-api-registry.json +56 -56
  36. package/dist/site/page-registry.json +40 -20
  37. package/dist/site/public-surface-audit.json +286 -349
  38. package/dist/site/publication-authority-registry.json +203 -523
  39. package/dist/site/publication-registry.json +4 -4
  40. package/dist/site/site-manifest.json +7 -7
  41. package/dist/site/workflow-registry.json +590 -940
  42. package/docs/code-organization.md +9 -6
  43. package/docs/node-api-reference.md +103 -103
  44. package/docs/release-discussions.md +44 -5
  45. package/docs/release-governance.md +10 -12
  46. package/docs/runtime-entry.md +73 -5
  47. package/docs/runtime-train-validation.md +39 -11
  48. package/docs/stable-candidate-patrol.md +6 -5
  49. package/docs/workflow-catalog.md +9 -32
  50. package/package.json +4 -3
  51. package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
  52. package/packages/core/build/standalone/build.js +7 -1
  53. package/packages/core/consumer/buildchain-config.js +2 -21
  54. package/packages/core/consumer/contract/entries.js +85 -0
  55. package/packages/core/consumer/contract/examples.js +96 -0
  56. package/packages/core/consumer/contract/identity.js +65 -0
  57. package/packages/core/consumer/contract/inspection.js +45 -0
  58. package/packages/core/consumer/contract/plan.js +149 -0
  59. package/packages/core/consumer/contract/products.js +119 -0
  60. package/packages/core/consumer/contract/reader.js +47 -0
  61. package/packages/core/consumer/contract/shape.js +46 -0
  62. package/packages/core/dev-delivery/candidate/admission.js +9 -5
  63. package/packages/core/dev-delivery/candidate/reservation.js +11 -0
  64. package/packages/core/dev-delivery/candidate/source-paths.js +12 -4
  65. package/packages/core/dev-delivery/commands/project-cut-merge-queue-admission.mjs +47 -20
  66. package/packages/core/dev-delivery/dev-delivery-provider-heartbeat.js +18 -15
  67. package/packages/core/dev-delivery/native/actions.js +9 -1
  68. package/packages/core/dev-delivery/native/heartbeat-action.js +16 -0
  69. package/packages/core/dev-delivery/native/heartbeat.js +6 -4
  70. package/packages/core/dev-delivery/native/process-entry.js +19 -0
  71. package/packages/core/dev-delivery/queue/landing-action.js +53 -0
  72. package/packages/core/dev-delivery/warrant/failure-settlement.js +8 -6
  73. package/packages/core/dev-delivery/warrant/service.js +7 -0
  74. package/packages/core/governance/buildchain-publication-authority.js +8 -23
  75. package/packages/core/governance/github-governance-authority.js +1 -0
  76. package/packages/core/observability/controller-evidence.js +0 -8
  77. package/packages/core/paper/operations/bootstrap.js +2 -0
  78. package/packages/core/paper/paper-npm-bootstrap.js +2 -2
  79. package/packages/core/providers/github/attempt-index.js +60 -0
  80. package/packages/core/providers/github/attempt-journal.js +154 -0
  81. package/packages/core/providers/github/discussions/materials.js +35 -11
  82. package/packages/core/providers/github/discussions/transport.js +16 -5
  83. package/packages/core/providers/github/pipeline-checkout.js +49 -0
  84. package/packages/core/providers/github/pipeline-entry-runs.js +210 -0
  85. package/packages/core/providers/github/pipeline-events.js +131 -0
  86. package/packages/core/providers/github/pipeline-integration.js +95 -0
  87. package/packages/core/providers/github/pipeline-policy.js +240 -0
  88. package/packages/core/providers/github/pipeline-product-payload.js +50 -0
  89. package/packages/core/providers/github/pipeline-product-release.js +198 -0
  90. package/packages/core/providers/github/pipeline-publication-artifacts.js +153 -0
  91. package/packages/core/providers/github/pipeline-release-evidence.js +30 -0
  92. package/packages/core/providers/github/pipeline-run-entry.js +86 -0
  93. package/packages/core/providers/github/pipeline-runs.js +132 -0
  94. package/packages/core/providers/github/pipeline-source.js +195 -0
  95. package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
  96. package/packages/core/providers/github/pipeline-stable-source.js +372 -0
  97. package/packages/core/providers/github/pipeline-version-artifacts.js +91 -0
  98. package/packages/core/providers/github/pipeline-version-readback.js +117 -0
  99. package/packages/core/providers/github/pipeline-version.js +199 -0
  100. package/packages/core/providers/github/pipeline-worker.js +108 -0
  101. package/packages/core/providers/github/release-evidence-assets.js +97 -0
  102. package/packages/core/publication/candidate/registry-hydration.js +2 -2
  103. package/packages/core/publication/npm/pack-preview.js +2 -2
  104. package/packages/core/publication/npm/pack-result.js +31 -0
  105. package/packages/core/publication/npm/package.js +3 -6
  106. package/packages/core/publication/npm/pipeline-channel.js +36 -0
  107. package/packages/core/publication/npm/pipeline-provider.js +137 -0
  108. package/packages/core/publication/npm/registry.js +12 -9
  109. package/packages/core/publication/pipeline/actions.js +116 -0
  110. package/packages/core/publication/pipeline/apply.js +189 -0
  111. package/packages/core/publication/pipeline/build-segments.js +107 -0
  112. package/packages/core/publication/pipeline/capsules.js +92 -0
  113. package/packages/core/publication/pipeline/context.js +39 -0
  114. package/packages/core/publication/pipeline/development-anchor.js +91 -0
  115. package/packages/core/publication/pipeline/development-current.js +63 -0
  116. package/packages/core/publication/pipeline/development-pr.js +112 -0
  117. package/packages/core/publication/pipeline/development-proof.js +76 -0
  118. package/packages/core/publication/pipeline/development-transition.js +112 -0
  119. package/packages/core/publication/pipeline/distribution.js +130 -0
  120. package/packages/core/publication/pipeline/documents.js +192 -0
  121. package/packages/core/publication/pipeline/effects.js +192 -0
  122. package/packages/core/publication/pipeline/files.js +66 -0
  123. package/packages/core/publication/pipeline/imported-materials.js +32 -0
  124. package/packages/core/publication/pipeline/journal.js +122 -0
  125. package/packages/core/publication/pipeline/next-development.js +117 -0
  126. package/packages/core/publication/pipeline/pack.js +180 -0
  127. package/packages/core/publication/pipeline/package-policy.js +22 -0
  128. package/packages/core/publication/pipeline/plan.js +136 -0
  129. package/packages/core/publication/pipeline/prepare.js +139 -0
  130. package/packages/core/publication/pipeline/qualification.js +175 -0
  131. package/packages/core/publication/pipeline/qualify.js +104 -0
  132. package/packages/core/publication/pipeline/recovery-admission.js +122 -0
  133. package/packages/core/publication/pipeline/recovery-build-download.js +80 -0
  134. package/packages/core/publication/pipeline/recovery-build-plan.js +122 -0
  135. package/packages/core/publication/pipeline/recovery-build-readback.js +68 -0
  136. package/packages/core/publication/pipeline/recovery-capsules.js +54 -0
  137. package/packages/core/publication/pipeline/recovery-import.js +80 -0
  138. package/packages/core/publication/pipeline/recovery-inspection.js +218 -0
  139. package/packages/core/publication/pipeline/recovery-materials.js +104 -0
  140. package/packages/core/publication/pipeline/recovery-plan.js +46 -0
  141. package/packages/core/publication/pipeline/recovery-prepare.js +73 -0
  142. package/packages/core/publication/pipeline/recovery-qualification.js +128 -0
  143. package/packages/core/publication/pipeline/recovery-readback.js +56 -0
  144. package/packages/core/publication/pipeline/recovery-signing.js +58 -0
  145. package/packages/core/publication/pipeline/sealed-products.js +79 -0
  146. package/packages/core/publication/pipeline/settle.js +42 -0
  147. package/packages/core/publication/pipeline/signing.js +140 -0
  148. package/packages/core/publication/pipeline/source-plan.js +83 -0
  149. package/packages/core/publication/pipeline/stable-entry.js +219 -0
  150. package/packages/core/publication/pipeline/stable-products.js +317 -0
  151. package/packages/core/publication/pipeline/stable-wait.js +170 -0
  152. package/packages/core/publication/pipeline/stable.js +82 -0
  153. package/packages/core/publication/pipeline/version-actions.js +93 -0
  154. package/packages/core/publication/pipeline/version-build.js +130 -0
  155. package/packages/core/publication/pipeline/version-context.js +143 -0
  156. package/packages/core/publication/pipeline/version-material.js +36 -0
  157. package/packages/core/publication/pipeline/version-preparation.js +69 -0
  158. package/packages/core/publication/pipeline/version-regeneration.js +162 -0
  159. package/packages/core/publication/pipeline/version-source-guard.js +69 -0
  160. package/packages/core/publication/pipeline/version.js +81 -0
  161. package/packages/core/publication/pipeline/worker.js +72 -0
  162. package/packages/core/publication/publication-reproducibility.js +2 -2
  163. package/packages/core/release/discussion/binary.js +11 -0
  164. package/packages/core/release/discussion/checkpoints.js +45 -4
  165. package/packages/core/release/discussion/envelope.js +1 -0
  166. package/packages/core/release/discussion/evidence.js +51 -0
  167. package/packages/core/release/discussion/presentation.js +92 -0
  168. package/packages/core/release/discussion/publication.js +3 -1
  169. package/packages/core/release/discussion/qualification.js +1 -0
  170. package/packages/core/release/discussion/reader.js +9 -0
  171. package/packages/core/release/discussion/session.js +2 -0
  172. package/packages/core/release/discussion/store.js +28 -31
  173. package/packages/core/release/discussion/threads.js +83 -0
  174. package/packages/core/release/next-development-transition.js +15 -15
  175. package/packages/core/release/promote-candidate/product-provider-adapters.js +4 -3
  176. package/packages/core/release/promote-ref/internal/npm-existing-evidence.js +2 -1
  177. package/packages/core/release/promote-ref/internal/publish-command.js +16 -5
  178. package/packages/core/release/stable-release-gate.js +11 -0
  179. package/packages/core/runtime/buildchain-domain.wasm +0 -0
  180. package/packages/core/runtime/domain-wasm-artifact.js +2 -2
  181. package/packages/core/runtime/entry/actions.js +27 -25
  182. package/packages/core/runtime/entry/attempt.js +25 -0
  183. package/packages/core/runtime/entry/selection.js +14 -0
  184. package/packages/core/runtime/entry/source.js +62 -0
  185. package/packages/core/workflow/attempt/identity.js +170 -0
  186. package/packages/core/workflow/attempt/journal.js +89 -0
  187. package/packages/core/workflow/attempt/materials.js +72 -0
  188. package/packages/core/workflow/attempt/reader-entry.js +12 -0
  189. package/packages/core/workflow/attempt/reader.js +149 -0
  190. package/packages/core/workflow/attempt/records.js +153 -0
  191. package/packages/core/workflow/attempt/store.js +75 -0
  192. package/packages/core/workflow/commands/pipeline-build.mjs +23 -0
  193. package/packages/core/workflow/pipeline/action-output.js +11 -0
  194. package/packages/core/workflow/pipeline/actions.js +52 -0
  195. package/packages/core/workflow/pipeline/build-control.js +140 -0
  196. package/packages/core/workflow/pipeline/build-evidence.js +210 -0
  197. package/packages/core/workflow/pipeline/build-qualification.js +145 -0
  198. package/packages/core/workflow/pipeline/build-result.js +78 -0
  199. package/packages/core/workflow/pipeline/build.js +142 -0
  200. package/packages/core/workflow/pipeline/cancellation.js +169 -0
  201. package/packages/core/workflow/pipeline/channel-control.js +157 -0
  202. package/packages/core/workflow/pipeline/channel-source.js +53 -0
  203. package/packages/core/workflow/pipeline/controller.js +141 -0
  204. package/packages/core/workflow/pipeline/delivery-control.js +249 -0
  205. package/packages/core/workflow/pipeline/delivery-observation.js +124 -0
  206. package/packages/core/workflow/pipeline/delivery-request.js +198 -0
  207. package/packages/core/workflow/pipeline/events.js +84 -0
  208. package/packages/core/workflow/pipeline/fence.js +42 -0
  209. package/packages/core/workflow/pipeline/group-control.js +59 -0
  210. package/packages/core/workflow/pipeline/guard-build.js +74 -0
  211. package/packages/core/workflow/pipeline/guard.js +158 -0
  212. package/packages/core/workflow/pipeline/host.js +144 -0
  213. package/packages/core/workflow/pipeline/integration-status.js +113 -0
  214. package/packages/core/workflow/pipeline/materials.js +58 -0
  215. package/packages/core/workflow/pipeline/notifications.js +36 -0
  216. package/packages/core/workflow/pipeline/parent-notification.js +27 -0
  217. package/packages/core/workflow/pipeline/platforms.js +21 -0
  218. package/packages/core/workflow/pipeline/progress.js +108 -0
  219. package/packages/core/workflow/pipeline/projection.js +20 -0
  220. package/packages/core/workflow/pipeline/reconcile.js +194 -0
  221. package/packages/core/workflow/pipeline/record-action.js +27 -0
  222. package/packages/core/workflow/pipeline/recorder-status.js +69 -0
  223. package/packages/core/workflow/pipeline/recovery-action.js +30 -0
  224. package/packages/core/workflow/pipeline/recovery-admission.js +122 -0
  225. package/packages/core/workflow/pipeline/recovery-build-control.js +95 -0
  226. package/packages/core/workflow/pipeline/recovery-build-evidence.js +71 -0
  227. package/packages/core/workflow/pipeline/recovery-build.js +121 -0
  228. package/packages/core/workflow/pipeline/recovery-controller.js +91 -0
  229. package/packages/core/workflow/pipeline/recovery-integration.js +79 -0
  230. package/packages/core/workflow/pipeline/recovery-merge-proof.js +90 -0
  231. package/packages/core/workflow/pipeline/recovery-ownership-settlement.js +150 -0
  232. package/packages/core/workflow/pipeline/recovery-ownership.js +91 -0
  233. package/packages/core/workflow/pipeline/recovery-plan.js +126 -0
  234. package/packages/core/workflow/pipeline/recovery-runtime.js +68 -0
  235. package/packages/core/workflow/pipeline/recovery-session.js +129 -0
  236. package/packages/core/workflow/pipeline/recovery-transition.js +135 -0
  237. package/packages/core/workflow/pipeline/recovery-unrecorded-build.js +74 -0
  238. package/packages/core/workflow/pipeline/runtime-source.js +111 -0
  239. package/packages/core/workflow/pipeline/selection.js +111 -0
  240. package/packages/core/workflow/pipeline/session.js +72 -0
  241. package/packages/core/workflow/pipeline/settlement.js +137 -0
  242. package/packages/core/workflow/pipeline/wake.js +38 -0
  243. package/packages/core/workflow/pipeline/web-status.js +108 -0
  244. package/packages/core/workflow/workflow-taxonomy.mjs +67 -47
  245. package/scripts/build-release-discussion-reader.mjs +11 -7
  246. package/scripts/check-inventory.mjs +7 -70
  247. package/scripts/check-pipeline-publication-topology.mjs +50 -0
  248. package/scripts/check-public-dogfood-contract.mjs +38 -51
  249. package/scripts/check-release-topology.mjs +15 -8
  250. package/scripts/check-self-consumer-contract.mjs +133 -0
  251. package/scripts/check-universal-workflow-bootstrap.mjs +4 -6
  252. package/scripts/generate-minimal-consumer-contract.mjs +155 -0
  253. package/scripts/generate-site-bundle.mjs +1 -1
  254. package/scripts/inventory/binary.mjs +9 -19
  255. package/scripts/inventory/promotion.mjs +2 -13
  256. package/scripts/prepare-product-toolchain.mjs +10 -0
  257. package/scripts/verify-golden-path.mjs +5 -1
  258. package/scripts/verify-product-platform.mjs +65 -0
  259. package/scripts/verify-standalone-product.mjs +42 -0
  260. package/templates/minimal-consumer/binary/.buildchain/buildchain.toml +45 -0
  261. package/templates/minimal-consumer/binary/.github/workflows/buildchain-recover.yml +30 -0
  262. package/templates/minimal-consumer/binary/.github/workflows/buildchain.yml +28 -0
  263. package/templates/minimal-consumer/binary/package.json +6 -0
  264. package/templates/minimal-consumer/binary/src/build.mjs +5 -0
  265. package/templates/minimal-consumer/binary/src/hello.c +2 -0
  266. package/templates/minimal-consumer/binary/src/verify.mjs +3 -0
  267. package/templates/minimal-consumer/npm/.buildchain/buildchain.toml +46 -0
  268. package/templates/minimal-consumer/npm/.github/workflows/buildchain-recover.yml +30 -0
  269. package/templates/minimal-consumer/npm/.github/workflows/buildchain.yml +28 -0
  270. package/templates/minimal-consumer/npm/package.json +6 -0
  271. package/templates/minimal-consumer/npm/src/build.mjs +5 -0
  272. package/templates/minimal-consumer/npm/src/verify.mjs +3 -0
  273. package/templates/minimal-consumer/paper/.buildchain/buildchain.toml +45 -0
  274. package/templates/minimal-consumer/paper/.github/workflows/buildchain-recover.yml +30 -0
  275. package/templates/minimal-consumer/paper/.github/workflows/buildchain.yml +28 -0
  276. package/templates/minimal-consumer/paper/package.json +6 -0
  277. package/templates/minimal-consumer/paper/src/build.mjs +12 -0
  278. package/templates/minimal-consumer/paper/src/verify.mjs +3 -0
@@ -1,10 +1,7 @@
1
1
  import path from "node:path";
2
2
  import fs from "node:fs";
3
3
  import crypto from "node:crypto";
4
- import {
5
- sourceCoordinates,
6
- validateNativeContract,
7
- } from "./coordinates.js";
4
+ import { sourceCoordinates, validateNativeContract } from "./coordinates.js";
8
5
  import {
9
6
  verifyProjectCutReplayProof,
10
7
  createSourceQualificationProof,
@@ -16,6 +13,7 @@ import { assertBranchUnlocked } from "../../providers/dev-delivery/protection.js
16
13
  import { deriveSourcePaths } from "./source-paths.js";
17
14
  import { admissionPolicyRequest } from "../admission/request.js";
18
15
  import { runAdmissionTransaction } from "../admission/transaction.js";
16
+ import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
19
17
  export function admitDeliveryRequest(input, defaultBranch) {
20
18
  const target = sourceCoordinates({
21
19
  branch: input["target-branch"],
@@ -46,6 +44,7 @@ export async function qualifyDeliverySource(
46
44
  },
47
45
  dependencies = {},
48
46
  ) {
47
+ const pipeline = await guardPipelineAdmission(request, { repository, token });
49
48
  const outputs = {
50
49
  "runtime-sha": runtimeSha,
51
50
  "qualify-outcome": "skipped",
@@ -146,7 +145,12 @@ export async function qualifyDeliverySource(
146
145
  if (!affectedPaths.length)
147
146
  affectedPaths = await (dependencies.derivePaths || deriveSourcePaths)(
148
147
  source,
149
- { token, apiUrl, environment },
148
+ {
149
+ token,
150
+ apiUrl,
151
+ environment,
152
+ qualifiedBase: pipeline?.history.at(-1).generation.baseCommit,
153
+ },
150
154
  );
151
155
  const receiptRoot = qualification.result.receiptRoot;
152
156
  if (!/^sha256:[0-9a-f]{64}$/u.test(receiptRoot || ""))
@@ -10,6 +10,8 @@ import { dispatchDeliveryHandoff } from "../warrant/handoff.js";
10
10
  import { resolveCandidateAffectedPaths } from "./evidence.js";
11
11
  import { deliverySubmissionRequest } from "./submission.js";
12
12
  import { jsonList } from "../warrant/values.js";
13
+ import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
14
+ import { wakePipelineCandidate } from "../../workflow/pipeline/wake.js";
13
15
  export async function reserveDeliveryCandidate(
14
16
  {
15
17
  workspace,
@@ -31,6 +33,7 @@ export async function reserveDeliveryCandidate(
31
33
  );
32
34
  if (!predecessorsOk || proofOutcome !== "success")
33
35
  throw new Error("Delivery candidate submission failed");
36
+ await guardPipelineAdmission(input, connection);
34
37
  const service =
35
38
  dependencies.service ||
36
39
  createDeliveryWarrantService({ ...connection, branch });
@@ -77,6 +80,14 @@ export async function reserveDeliveryCandidate(
77
80
  warrant.pullRequestNumber !== Number(input["expected-pr-number"]) ||
78
81
  warrant.sourceHead !== input["expected-head-sha"]
79
82
  ) {
83
+ if (input["pipeline-attempt"]) {
84
+ await wakePipelineCandidate(warrant, { ...connection, branch });
85
+ return {
86
+ "warrant-outcome": "success",
87
+ "warrant-handoff-required": "true",
88
+ "warrant-handoff-dispatched": "true",
89
+ };
90
+ }
80
91
  outputs = await dispatchDeliveryHandoff(
81
92
  {
82
93
  warrant,
@@ -52,7 +52,7 @@ export function pathsAtQualifiedSource(directory, qualifiedBase, source) {
52
52
 
53
53
  export async function deriveSourcePaths(
54
54
  source,
55
- { token, apiUrl, environment = process.env },
55
+ { token, apiUrl, environment = process.env, qualifiedBase },
56
56
  provider = new GitHubTwoPhaseClient({
57
57
  repository: source.repository,
58
58
  token,
@@ -65,10 +65,18 @@ export async function deriveSourcePaths(
65
65
  /^[1-9]\d*$/u.test(String(source.sourceWorkflowRunId || "")),
66
66
  "Exact source run coordinates are required",
67
67
  );
68
- const run = await provider.request(
69
- `/repos/${source.repository}/actions/runs/${source.sourceWorkflowRunId}`,
68
+ if (qualifiedBase === undefined) {
69
+ const run = await provider.request(
70
+ `/repos/${source.repository}/actions/runs/${source.sourceWorkflowRunId}`,
71
+ );
72
+ qualifiedBase = qualifiedRunBase(run, source);
73
+ }
74
+ // The internal pipeline guard already requalifies the source run and immutable
75
+ // generation. Zero-delta recovery still verifies the full source identity below.
76
+ requireValue(
77
+ /^[0-9a-f]{40}$/u.test(qualifiedBase || ""),
78
+ "Exact qualified source base is required",
70
79
  );
71
- const qualifiedBase = qualifiedRunBase(run, source);
72
80
  const directory = fs.mkdtempSync(
73
81
  path.join(os.tmpdir(), "buildchain-source-paths-"),
74
82
  );
@@ -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
  },