@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
@@ -0,0 +1,92 @@
1
+ import { publicationArtifactProducer } from "./build-segments.js";
2
+ import { recordDigest } from "../../release/discussion/envelope.js";
3
+ import {
4
+ STAGE_CAPSULE_CONTRACT,
5
+ STAGE_CAPSULE_IDENTITY_CONTRACT,
6
+ stageCapsuleIdentityRoot,
7
+ stageCapsuleRoot,
8
+ validateStageCapsule,
9
+ } from "../../build/stage-capsule.js";
10
+ import { verifyPipelineQualification } from "./documents.js";
11
+
12
+ export function pipelineProductCapsules({
13
+ plan,
14
+ materialization,
15
+ qualified,
16
+ bundles,
17
+ evaluatedAt,
18
+ }) {
19
+ verifyPipelineQualification({
20
+ plan,
21
+ materialization,
22
+ qualified,
23
+ evaluatedAt,
24
+ });
25
+ const capsules = qualified.artifacts.map((artifact) => {
26
+ const bundle = bundles.find(
27
+ ({ providerArtifact }) =>
28
+ providerArtifact.id === artifact.providerArtifactId,
29
+ );
30
+ if (
31
+ !bundle ||
32
+ !bundle.providerArtifact.expires_at ||
33
+ Date.parse(bundle.providerArtifact.expires_at) <=
34
+ Date.parse(qualified.qualification.issuedAt)
35
+ )
36
+ throw new Error(
37
+ "Product Capsule requires a live provider retention promise",
38
+ );
39
+ const producer = publicationArtifactProducer(
40
+ qualified.build,
41
+ artifact.providerArtifactId,
42
+ );
43
+ const identity = {
44
+ schema: STAGE_CAPSULE_IDENTITY_CONTRACT,
45
+ sourceRoot: recordDigest(qualified.source),
46
+ platform: artifact.platform,
47
+ platformRoot: recordDigest({ platform: artifact.platform }),
48
+ stage: "verify",
49
+ toolchainRoots: [],
50
+ runtimeRoot: recordDigest(producer.plan?.runtime || plan.runtime),
51
+ policyRoot: plan.contractRoot,
52
+ declaredInputs: [
53
+ { name: "publication-plan", root: producer.plan?.root || plan.root },
54
+ ],
55
+ transformationRoot: recordDigest({
56
+ source: qualified.source,
57
+ product: artifact.product,
58
+ artifact: artifact.id,
59
+ kind: artifact.kind,
60
+ }),
61
+ outputManifestRoot: artifact.manifestRoot,
62
+ qualificationRoot: qualified.qualification.receiptRoot,
63
+ observationRoots: [
64
+ { name: "provider-build", root: qualified.build.root },
65
+ ],
66
+ };
67
+ const capsule = {
68
+ schema: STAGE_CAPSULE_CONTRACT,
69
+ writerAuthority: "typescript-v3",
70
+ rustAuthority: "validation-only",
71
+ identity,
72
+ identityRoot: stageCapsuleIdentityRoot(identity),
73
+ retentionPromise: {
74
+ class: "github-artifact",
75
+ retainUntil: bundle.providerArtifact.expires_at.replace(
76
+ /T(\d{2}:\d{2}:\d{2})Z$/u,
77
+ "T$1.000Z",
78
+ ),
79
+ },
80
+ capsuleRoot: `sha256:${"0".repeat(64)}`,
81
+ };
82
+ capsule.capsuleRoot = stageCapsuleRoot(capsule);
83
+ validateStageCapsule(capsule);
84
+ return { id: artifact.id, artifact, capsule };
85
+ });
86
+ const body = {
87
+ schema: "buildchain.pipeline-product-capsules/v1",
88
+ qualificationRoot: qualified.root,
89
+ capsules,
90
+ };
91
+ return { ...body, root: recordDigest(body) };
92
+ }
@@ -0,0 +1,39 @@
1
+ import { recordDigest } from "../../release/discussion/envelope.js";
2
+ import { resumePipelineSession } from "../../workflow/pipeline/session.js";
3
+ import { pipelinePublicationJournal } from "./journal.js";
4
+
5
+ export async function publicationContext(context, host) {
6
+ if (
7
+ context.schema !== "buildchain.pipeline-publication-context/v1" ||
8
+ context.runId !== host.runId ||
9
+ context.runAttempt !== host.runAttempt
10
+ )
11
+ throw new Error(
12
+ "Publication context is not from this exact provider execution",
13
+ );
14
+ const session = await resumePipelineSession(
15
+ { ...host, attempt: context.attempt },
16
+ host,
17
+ );
18
+ if (session.observed.generation !== context.generation)
19
+ throw new Error(
20
+ "Publication context belongs to a superseded source generation",
21
+ );
22
+ const journal = pipelinePublicationJournal(session, host);
23
+ await journal.fence();
24
+ const contexts = await journal.materials("publication/context/");
25
+ if (!contexts.some((value) => recordDigest(value) === recordDigest(context)))
26
+ throw new Error(
27
+ "Publication context is not retained in the authoritative attempt",
28
+ );
29
+ return { session, journal, archive: host.productArchive(session) };
30
+ }
31
+
32
+ export async function uniquePublicationMaterial(journal, prefix) {
33
+ const values = await journal.materials(prefix);
34
+ if (values.length !== 1)
35
+ throw new Error(
36
+ `Publication requires one exact retained material: ${prefix}`,
37
+ );
38
+ return values[0];
39
+ }
@@ -0,0 +1,91 @@
1
+ import { createHash } from "node:crypto";
2
+ import { recordDigest } from "../../release/discussion/envelope.js";
3
+ import { bindPipelineDevelopmentAnchor } from "./development-transition.js";
4
+ import { githubPipelineVersion } from "../../providers/github/pipeline-version.js";
5
+ import { developmentTransitionReadback } from "./development-proof.js";
6
+
7
+ export async function observePipelineVersionAnchor(context, host, transition) {
8
+ const { plan } = context;
9
+ const commit = await host.source.branchHead(plan.developmentBranch);
10
+ const source = await host.source.source(commit, plan.source.configPath);
11
+ const ancestry =
12
+ plan.channel === "stable"
13
+ ? null
14
+ : await host.request(
15
+ `/repos/${host.repository}/compare/${plan.intentSource.commit}...${commit}`,
16
+ );
17
+ if (
18
+ ancestry &&
19
+ (!["identical", "ahead"].includes(ancestry.status) ||
20
+ ancestry.merge_base_commit?.sha !== plan.intentSource.commit)
21
+ )
22
+ throw new Error(
23
+ "Version anchor is not descended from the released development source",
24
+ );
25
+ if (recordDigest(source.plan) !== plan.contractRoot)
26
+ throw new Error("Protected anchor changed the product contract");
27
+ const version = await githubPipelineVersion(
28
+ host.request,
29
+ host.repository,
30
+ ).inspect(source.identity, plan.versionPolicy);
31
+ if (version.version === plan.version)
32
+ return {
33
+ state: "waiting",
34
+ reason: "waiting-for-protected-version-anchor",
35
+ transitionRoot: transition.idempotencyKey,
36
+ };
37
+ const pulls = await host.request(
38
+ `/repos/${host.repository}/commits/${commit}/pulls?per_page=100`,
39
+ );
40
+ const candidates = pulls.filter(
41
+ (pr) =>
42
+ pr.base?.ref === plan.developmentBranch && pr.merge_commit_sha === commit,
43
+ );
44
+ if (candidates.length !== 1)
45
+ throw new Error("Version anchor requires one exact protected merged PR");
46
+ const pr = await host.request(
47
+ `/repos/${host.repository}/pulls/${candidates[0].number}`,
48
+ );
49
+ const integration = await host.integration.observe({
50
+ intent: {
51
+ source: { pullRequest: pr.number, targetBranch: plan.developmentBranch },
52
+ },
53
+ generation: {
54
+ source: { commit: pr.head.sha, configPath: plan.source.configPath },
55
+ },
56
+ });
57
+ if (
58
+ plan.channel === "stable" &&
59
+ Date.parse(pr.merged_at) <=
60
+ Date.parse(transition.completedStable.completedAt)
61
+ )
62
+ return {
63
+ state: "waiting",
64
+ reason: "waiting-for-protected-version-anchor",
65
+ transitionRoot: transition.idempotencyKey,
66
+ };
67
+ const file = plan.versionPolicy.files[0].path;
68
+ const anchor = {
69
+ manifestPath: file,
70
+ manifestRoot: `sha256:${createHash("sha256").update(version.files[file]).digest("hex")}`,
71
+ };
72
+ const bound = bindPipelineDevelopmentAnchor(transition, {
73
+ targetVersion: version.version,
74
+ anchor,
75
+ });
76
+ const verified = developmentTransitionReadback(bound, {
77
+ before: version,
78
+ after: version,
79
+ evidence: integration,
80
+ createdAt: pr.created_at,
81
+ mergedAt: pr.merged_at,
82
+ });
83
+ return {
84
+ state: "success",
85
+ reason: "protected-version-anchor-verified",
86
+ transition: verified,
87
+ source: source.identity,
88
+ integration,
89
+ anchor,
90
+ };
91
+ }
@@ -0,0 +1,63 @@
1
+ import { compareDevelopmentVersions } from "../publication-development.js";
2
+ import { recordDigest } from "../../release/discussion/envelope.js";
3
+
4
+ export async function observeAdvancedPipelineDevelopment(
5
+ context,
6
+ host,
7
+ transition,
8
+ adapter,
9
+ ) {
10
+ const { plan } = context;
11
+ const commit = await host.source.branchHead(plan.developmentBranch);
12
+ const source = await host.source.source(commit, plan.source.configPath);
13
+ if (recordDigest(source.plan) !== plan.contractRoot)
14
+ throw new Error(
15
+ "Protected development contract changed before next-version readback",
16
+ );
17
+ const observed = await adapter.inspect(source.identity, plan.versionPolicy);
18
+ const order = compareDevelopmentVersions(
19
+ observed.version,
20
+ transition.target.version,
21
+ );
22
+ if (order < 0) return null;
23
+ const pulls = await host.request(
24
+ `/repos/${host.repository}/commits/${commit}/pulls?per_page=100`,
25
+ );
26
+ const matches = pulls.filter(
27
+ (pr) =>
28
+ pr.base?.ref === plan.developmentBranch && pr.merge_commit_sha === commit,
29
+ );
30
+ if (matches.length !== 1)
31
+ throw new Error(
32
+ "Advanced development requires one exact protected merged PR",
33
+ );
34
+ const pr = await host.request(
35
+ `/repos/${host.repository}/pulls/${matches[0].number}`,
36
+ );
37
+ const integration = await host.integration.observe({
38
+ intent: {
39
+ source: { pullRequest: pr.number, targetBranch: plan.developmentBranch },
40
+ },
41
+ generation: {
42
+ source: { commit: pr.head.sha, configPath: plan.source.configPath },
43
+ },
44
+ });
45
+ if (integration.mergeCommit !== commit)
46
+ throw new Error(
47
+ "Advanced development proof differs from the observed protected commit",
48
+ );
49
+ return {
50
+ state: "success",
51
+ reason:
52
+ order === 0
53
+ ? "protected-development-already-current"
54
+ : "protected-development-already-advanced",
55
+ transitionRoot: transition.idempotencyKey,
56
+ targetVersion: transition.target.version,
57
+ observedVersion: observed.version,
58
+ source: source.identity,
59
+ integration,
60
+ filesRoot: recordDigest(observed.files),
61
+ publicationOutcome: "preserved-success",
62
+ };
63
+ }
@@ -0,0 +1,112 @@
1
+ import { recordDigest } from "../../release/discussion/envelope.js";
2
+ import {
3
+ preparePipelineVersionContext,
4
+ retainedPipelineVersionRegeneration,
5
+ } from "./version-context.js";
6
+
7
+ export async function prepareNextDevelopmentPullRequest(
8
+ context,
9
+ host,
10
+ journal,
11
+ transition,
12
+ adapter,
13
+ ) {
14
+ const { plan } = context;
15
+ const retained = await journal.materials(
16
+ "publication/next-development-source/",
17
+ );
18
+ if (retained.length > 1)
19
+ throw new Error("Next-development source reservation is ambiguous");
20
+ let request = retained[0];
21
+ if (!request) {
22
+ const baseCommit = await host.source.branchHead(plan.developmentBranch);
23
+ const source = await host.source.source(baseCommit, plan.source.configPath);
24
+ if (recordDigest(source.plan) !== plan.contractRoot)
25
+ throw new Error(
26
+ "Protected development contract changed before version preparation",
27
+ );
28
+ const input = await adapter.inspect(source.identity, plan.versionPolicy);
29
+ if (
30
+ plan.channel === "stable"
31
+ ? input.version.split("-alpha.")[0] !== plan.version
32
+ : input.version !== plan.version
33
+ )
34
+ throw new Error(
35
+ "Development version already changed; reconcile its protected provenance before preparing another PR",
36
+ );
37
+ request = {
38
+ source: source.identity,
39
+ versionPolicy: plan.versionPolicy,
40
+ version: transition.target.version,
41
+ root: recordDigest({
42
+ transitionRoot: transition.idempotencyKey,
43
+ source: source.identity,
44
+ }),
45
+ sourceTimestamp: input.sourceTimestamp,
46
+ };
47
+ await journal.record("publication/next-development-source", request, {
48
+ phase: "next-development",
49
+ });
50
+ }
51
+ const regeneration = await retainedPipelineVersionRegeneration(
52
+ journal,
53
+ request,
54
+ "development",
55
+ host.runtime,
56
+ );
57
+ if (request.versionPolicy.derived_files?.length && !regeneration)
58
+ return {
59
+ state: "waiting",
60
+ reason: "next-development-version-regeneration-required",
61
+ versionContext: await preparePipelineVersionContext(
62
+ request,
63
+ "development",
64
+ context,
65
+ host,
66
+ journal,
67
+ context.recovery?.execution.publisher.workflowSha ||
68
+ plan.publisher.workflowSha,
69
+ ),
70
+ };
71
+ await journal.fence();
72
+ const materialization = await adapter.materializeDevelopment(
73
+ request,
74
+ regeneration,
75
+ );
76
+ const branch = materialization.branch;
77
+ const pulls = await host.request(
78
+ `/repos/${host.repository}/pulls?state=all&head=${encodeURIComponent(`${host.repository.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(plan.developmentBranch)}&per_page=100`,
79
+ );
80
+ if (!Array.isArray(pulls) || pulls.length > 1)
81
+ throw new Error("Next-development PR identity is ambiguous");
82
+ let pr = pulls[0];
83
+ if (!pr) {
84
+ await journal.fence();
85
+ if (!host.pullRequests)
86
+ throw new Error(
87
+ "Next-development requires the repository-authorized automation App or token to trigger ordinary PR checks",
88
+ );
89
+ pr = await host.pullRequests(`/repos/${host.repository}/pulls`, {
90
+ method: "POST",
91
+ body: {
92
+ title: `chore(version): prepare ${transition.target.version}`,
93
+ head: branch,
94
+ base: plan.developmentBranch,
95
+ body: `Prepare the declared next development version after ${plan.tag}.\n\nBuildchain-Parent-Attempt: ${context.attempt}\nBuildchain-Transition: ${transition.idempotencyKey}\n\nThe normal protected pipeline verifies the declared products before merge.`,
96
+ },
97
+ });
98
+ }
99
+ const selected = {
100
+ schema: "buildchain.pipeline-next-development-pr/v1",
101
+ number: pr.number,
102
+ branch,
103
+ base: plan.developmentBranch,
104
+ source: materialization.source,
105
+ materialization,
106
+ transitionRoot: transition.idempotencyKey,
107
+ };
108
+ await journal.record("publication/next-development-pr", selected, {
109
+ phase: "next-development",
110
+ });
111
+ return selected;
112
+ }
@@ -0,0 +1,76 @@
1
+ import { createHash } from "node:crypto";
2
+ import { stablePipelineDevelopmentReadback } from "./development-transition.js";
3
+ import { recordDigest } from "../../release/discussion/envelope.js";
4
+ import {
5
+ nextDevelopmentRoot,
6
+ recordNextDevelopmentMaterialization,
7
+ advanceNextDevelopmentTransition,
8
+ } from "../../release/next-development-transition.js";
9
+
10
+ export function developmentTransitionReadback(
11
+ transition,
12
+ { before, after, evidence, createdAt, mergedAt },
13
+ ) {
14
+ const hash = (bytes) =>
15
+ `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
16
+ const paths = [
17
+ ...transition.adapter.sourcePaths,
18
+ ...transition.adapter.derivedPaths,
19
+ ]
20
+ .sort()
21
+ .map((file) => {
22
+ if (
23
+ typeof before.files[file] !== "string" ||
24
+ typeof after.files[file] !== "string"
25
+ )
26
+ throw new Error("Next-development lacks exact declared file bytes");
27
+ return {
28
+ path: file,
29
+ beforeRoot: hash(before.files[file]),
30
+ afterRoot: hash(after.files[file]),
31
+ changed: before.files[file] !== after.files[file],
32
+ };
33
+ });
34
+ if (after.version !== transition.target.version)
35
+ throw new Error(
36
+ "Next-development materialization does not match its authorized target",
37
+ );
38
+ const body = {
39
+ targetVersion: transition.target.version,
40
+ anchorRoot: transition.target.anchor?.manifestRoot || null,
41
+ paths,
42
+ };
43
+ if (!transition.completedAlpha)
44
+ return stablePipelineDevelopmentReadback(
45
+ transition,
46
+ body,
47
+ evidence,
48
+ createdAt,
49
+ mergedAt,
50
+ );
51
+ let state = recordNextDevelopmentMaterialization(transition, {
52
+ materialization: {
53
+ ...body,
54
+ materializationRoot: nextDevelopmentRoot(body),
55
+ },
56
+ evidenceRoot: recordDigest(evidence),
57
+ recordedAt: new Date(createdAt).toISOString(),
58
+ });
59
+ state = advanceNextDevelopmentTransition(state, {
60
+ to: "pr-pending",
61
+ event: "protected-version-pr-observed",
62
+ evidenceRoot: recordDigest(evidence),
63
+ expectedStateRoot: state.state.stateRoot,
64
+ recordedAt: new Date(createdAt).toISOString(),
65
+ });
66
+ if (mergedAt)
67
+ for (const phase of ["merged", "verified"])
68
+ state = advanceNextDevelopmentTransition(state, {
69
+ to: phase,
70
+ event: `protected-version-${phase}`,
71
+ evidenceRoot: recordDigest(evidence),
72
+ expectedStateRoot: state.state.stateRoot,
73
+ recordedAt: new Date(mergedAt).toISOString(),
74
+ });
75
+ return state;
76
+ }
@@ -0,0 +1,112 @@
1
+ import {
2
+ createNextDevelopmentTransition,
3
+ bindNextDevelopmentAnchor,
4
+ nextDevelopmentRoot,
5
+ } from "../../release/next-development-transition.js";
6
+ import { createStableDevelopmentTransition } from "../publication-development.js";
7
+ import { recordDigest } from "../../release/discussion/envelope.js";
8
+
9
+ export function createPipelineDevelopmentTransition(context, publication) {
10
+ const { plan, materialization } = context;
11
+ const input = {
12
+ repository: plan.source.repository,
13
+ model: {
14
+ strategy: plan.versionPolicy.strategy,
15
+ next: plan.versionPolicy.strategy === "semver" ? "auto" : "manual",
16
+ },
17
+ sourcePaths: [
18
+ ...new Set(plan.versionPolicy.files.map(({ path }) => path)),
19
+ ].sort(),
20
+ derivedPaths: [...(plan.versionPolicy.derived_files || [])].sort(),
21
+ };
22
+ const completed = {
23
+ outcome: "succeeded",
24
+ version: plan.version,
25
+ exactTag: plan.tag,
26
+ releaseSha: materialization.source.commit,
27
+ treeSha: materialization.source.tree,
28
+ publicationRoot: publication.release.receiptRoot,
29
+ completedAt: publication.completedAt,
30
+ };
31
+ if (plan.channel === "alpha")
32
+ return createNextDevelopmentTransition({
33
+ ...input,
34
+ completedAlpha: completed,
35
+ });
36
+ const adapter = {
37
+ sourcePaths: input.sourcePaths,
38
+ derivedPaths: input.derivedPaths,
39
+ };
40
+ if (input.model.strategy === "semver")
41
+ return {
42
+ ...createStableDevelopmentTransition({
43
+ ...input,
44
+ completedStable: completed,
45
+ }),
46
+ adapter,
47
+ };
48
+ const identity = {
49
+ contract: "buildchain.pipeline-stable-anchor-transition/v1",
50
+ ...input,
51
+ completedStable: completed,
52
+ };
53
+ return {
54
+ ...identity,
55
+ idempotencyKey: nextDevelopmentRoot(identity),
56
+ adapter,
57
+ target: { version: null, anchor: null },
58
+ state: { status: "waiting-anchor" },
59
+ publicationOutcome: "preserved-success",
60
+ };
61
+ }
62
+
63
+ export function bindPipelineDevelopmentAnchor(transition, input) {
64
+ if (transition.completedAlpha)
65
+ return bindNextDevelopmentAnchor(transition, input);
66
+ if (
67
+ transition.contract !== "buildchain.pipeline-stable-anchor-transition/v1" ||
68
+ transition.state.status !== "waiting-anchor" ||
69
+ !input.targetVersion ||
70
+ !input.anchor?.manifestRoot
71
+ )
72
+ throw new Error(
73
+ "Stable manual development requires its explicit protected anchor",
74
+ );
75
+ return {
76
+ ...transition,
77
+ target: { version: input.targetVersion, anchor: input.anchor },
78
+ };
79
+ }
80
+
81
+ export function stablePipelineDevelopmentReadback(
82
+ transition,
83
+ materialization,
84
+ evidence,
85
+ createdAt,
86
+ mergedAt,
87
+ ) {
88
+ if (
89
+ ![
90
+ "buildchain.stable-development-transition/v1",
91
+ "buildchain.pipeline-stable-anchor-transition/v1",
92
+ ].includes(transition.contract)
93
+ )
94
+ throw new Error("Unknown stable development transition");
95
+ const state = {
96
+ status: mergedAt ? "verified" : "pr-pending",
97
+ evidenceRoot: recordDigest(evidence),
98
+ recordedAt: new Date(mergedAt || createdAt).toISOString(),
99
+ materializationRoot: nextDevelopmentRoot(materialization),
100
+ };
101
+ return {
102
+ ...transition,
103
+ materialization,
104
+ state: {
105
+ ...state,
106
+ stateRoot: nextDevelopmentRoot({
107
+ transitionRoot: transition.idempotencyKey,
108
+ ...state,
109
+ }),
110
+ },
111
+ };
112
+ }