@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.21

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 (203) hide show
  1. package/actions/release-tail/README.md +8 -0
  2. package/architecture/ci-lane-change-budget.json +187 -0
  3. package/architecture/decisions/0002-next-development-transition.md +101 -0
  4. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  5. package/architecture/internal-capabilities.json +211 -8
  6. package/architecture/maintainability-policy.json +113 -28
  7. package/architecture/release-tail-contract-inventory.json +5 -2
  8. package/architecture/v3-core-mechanism-inventory.json +91 -0
  9. package/architecture/v4-adopter-delivery-parity.json +32 -5
  10. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  11. package/architecture/v4-compatibility-facts-parity.json +77 -0
  12. package/architecture/v4-delivery-authority-parity.json +250 -0
  13. package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
  14. package/architecture/v4-floating-consumer-policy.json +6 -0
  15. package/architecture/v4-next-development-parity.json +192 -0
  16. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  17. package/architecture/v4-release-train-parity.json +1 -0
  18. package/architecture/v4-tail-reseal-parity.json +150 -0
  19. package/bin/buildchain.mjs +32 -3
  20. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  21. package/bin/internal/command-registry.mjs +3 -0
  22. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  23. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  24. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  25. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  26. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  28. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  29. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  30. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  31. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  32. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  33. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  38. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  39. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  40. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  41. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  42. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  43. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  44. package/contracts/next-development-request-v1.schema.json +66 -0
  45. package/contracts/next-development-transition-v1.schema.json +292 -0
  46. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  47. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  48. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  49. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  50. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  51. package/dist/site/agent-index.json +5 -0
  52. package/dist/site/artifact-schemas.json +10 -0
  53. package/dist/site/buildchain-contract.json +1475 -27
  54. package/dist/site/buildchain-site.json +627 -31
  55. package/dist/site/capability-registry.json +17 -14
  56. package/dist/site/cli-registry.json +205 -3
  57. package/dist/site/kfd-claims.json +490 -16
  58. package/dist/site/kfd-upstream-aggregate.json +1 -1
  59. package/dist/site/manual-registry.json +52 -6
  60. package/dist/site/node-api-registry.json +14736 -8851
  61. package/dist/site/page-registry.json +593 -21
  62. package/dist/site/public-surface-audit.json +837 -48
  63. package/dist/site/publication-authority-registry.json +44 -8
  64. package/dist/site/publication-registry.json +4 -4
  65. package/dist/site/release-provenance.json +24 -0
  66. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  67. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  68. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  69. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  70. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  71. package/dist/site/site-manifest.json +34 -10
  72. package/dist/site/workflow-registry.json +160 -10
  73. package/docs/MAP.md +8 -2
  74. package/docs/cli-reference.md +404 -13
  75. package/docs/cli.md +8 -0
  76. package/docs/dev-alpha-candidate-patrol.md +8 -0
  77. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  78. package/docs/dev-delivery-warrant.md +351 -38
  79. package/docs/next-development-transition.md +118 -0
  80. package/docs/node-api-reference.md +646 -293
  81. package/docs/release-tail-provider-plane.md +30 -0
  82. package/docs/v4-adopter-delivery.md +133 -0
  83. package/docs/v4-compatibility-facts.md +91 -0
  84. package/docs/v4-publication-rehearsal.md +130 -0
  85. package/docs/v4-tail-reseal.md +73 -0
  86. package/docs/versioning.md +4 -3
  87. package/package.json +34 -6
  88. package/packages/core/adopter-delivery-passport.js +259 -0
  89. package/packages/core/adopter-delivery-vectors.js +158 -0
  90. package/packages/core/adopter-delivery-vectors.json +77 -0
  91. package/packages/core/buildchain-agent-manuals.js +3 -0
  92. package/packages/core/buildchain-compatibility-authority.js +336 -0
  93. package/packages/core/buildchain-compatibility-fact.js +394 -0
  94. package/packages/core/buildchain-compatibility-facts.json +217 -0
  95. package/packages/core/buildchain-compatibility-proof.js +566 -0
  96. package/packages/core/buildchain-config.js +84 -0
  97. package/packages/core/buildchain-contract.js +96 -143
  98. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  99. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  100. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  101. package/packages/core/buildchain-publication-authority.js +1 -0
  102. package/packages/core/channel-candidate.js +8 -0
  103. package/packages/core/channel-promotion-baseline.js +55 -0
  104. package/packages/core/ci-lane-change-budget.js +247 -0
  105. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  106. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  107. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  108. package/packages/core/dev-delivery-authority-landing.js +461 -0
  109. package/packages/core/dev-delivery-authority-observation.js +48 -0
  110. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  111. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  112. package/packages/core/dev-delivery-authority-state.js +583 -0
  113. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  114. package/packages/core/dev-delivery-contract-surface.js +76 -0
  115. package/packages/core/dev-delivery-execution-failure.js +133 -0
  116. package/packages/core/dev-delivery-execution-transfer.js +592 -0
  117. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  118. package/packages/core/dev-delivery-landing-readback.js +598 -0
  119. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  120. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  121. package/packages/core/dev-delivery-native-execution.js +110 -0
  122. package/packages/core/dev-delivery-native-proof.js +562 -0
  123. package/packages/core/dev-delivery-process-boundary.js +563 -0
  124. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  125. package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
  126. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  127. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  128. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  129. package/packages/core/dev-delivery-warrant-state.js +587 -0
  130. package/packages/core/dev-delivery-warrant.js +328 -370
  131. package/packages/core/github-governance-authority.js +1 -0
  132. package/packages/core/index.js +5 -0
  133. package/packages/core/kungfu-temporal-fact.js +557 -0
  134. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  135. package/packages/core/next-development-candidate-reservation.js +186 -0
  136. package/packages/core/next-development-controller.js +728 -0
  137. package/packages/core/next-development-projection.js +287 -0
  138. package/packages/core/next-development-transition.js +738 -0
  139. package/packages/core/paper-agent-entry.js +11 -5
  140. package/packages/core/paper.js +16 -5
  141. package/packages/core/published-delivery-authority.js +266 -0
  142. package/packages/core/release-candidate-recovery.js +70 -15
  143. package/packages/core/release-candidate.js +2 -2
  144. package/packages/core/release-passport.js +36 -9
  145. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  146. package/packages/core/v4-adopter-delivery.js +359 -0
  147. package/packages/core/v4-canonical-contracts.js +5 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  149. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  150. package/packages/core/v4-floating-consumer-policy.js +143 -20
  151. package/packages/core/v4-floating-consumer-release-passport.js +32 -5
  152. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  153. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  154. package/packages/core/v4-publication-rehearsal.js +481 -0
  155. package/packages/core/v4-tail-reseal-contract.js +26 -0
  156. package/packages/core/v4-tail-reseal-github.js +178 -0
  157. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  158. package/packages/core/v4-tail-reseal.js +594 -0
  159. package/packages/core/workflow-yaml-contract.js +24 -2
  160. package/scripts/buildchain-cli-help.mjs +41 -2
  161. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  162. package/scripts/check-inventory.mjs +14 -3
  163. package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
  164. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  165. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  166. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  167. package/scripts/dev-delivery-authority.mjs +490 -0
  168. package/scripts/dev-delivery-native-run.mjs +177 -0
  169. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  170. package/scripts/dev-delivery-proof.mjs +67 -2
  171. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  172. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  173. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  174. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  175. package/scripts/dev-delivery-two-phase.mjs +573 -0
  176. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  177. package/scripts/dev-delivery-warrant-store.mjs +231 -0
  178. package/scripts/dev-delivery-warrant.mjs +232 -194
  179. package/scripts/dev-pr-auto-merge.mjs +1 -1
  180. package/scripts/dev-pr-delivery-warrant.mjs +11 -0
  181. package/scripts/dev-qualification-patrol.mjs +9 -9
  182. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  183. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  184. package/scripts/generate-next-development-guidance.mjs +49 -0
  185. package/scripts/generate-release-candidate-passport.mjs +152 -0
  186. package/scripts/generate-site-bundle.mjs +69 -4
  187. package/scripts/init-repo.mjs +22 -4
  188. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  189. package/scripts/next-development-self-dogfood.mjs +597 -0
  190. package/scripts/next-development-transition.mjs +47 -0
  191. package/scripts/npm-publish-transaction.mjs +2 -2
  192. package/scripts/promotion-channel-router.mjs +4 -4
  193. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  194. package/scripts/release-tail.mjs +51 -3
  195. package/scripts/release-train-self-dogfood.mjs +94 -92
  196. package/scripts/resume-from-candidate-run.mjs +557 -30
  197. package/scripts/site-capability-metadata.mjs +41 -0
  198. package/scripts/stable-candidate-qualification.mjs +9 -10
  199. package/scripts/v4-consumer-policy.mjs +5 -5
  200. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  201. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  202. package/scripts/v4-tail-reseal.mjs +416 -0
  203. package/templates/native-dev-delivery.yml +143 -0
@@ -0,0 +1,167 @@
1
+ import {
2
+ ADOPTER_PROTOCOL_DRIVER_INTERFACE,
3
+ adopterDeliveryGateDigest,
4
+ defineAdopterProtocolDriver,
5
+ } from "./adopter-delivery-gate.js";
6
+ import {
7
+ KFD_LEGACY_SUPPORT_MATRIX_CONTRACT,
8
+ validateKfdAdopterManifestGate,
9
+ validateKfdLegacySupportMatrixProjection,
10
+ } from "./kfd-adopter-manifest.js";
11
+
12
+ export const LEGACY_KFD_ADOPTER_PROTOCOL_ID =
13
+ "buildchain.kfd-adopter/legacy-support-matrix";
14
+ export const LEGACY_KFD_ADOPTER_PROTOCOL_VERSION = "1.0.0";
15
+ export const LEGACY_KFD_ADOPTER_DRIVER_REPORT_CONTRACT =
16
+ "kungfu-buildchain-legacy-kfd-adopter-driver-report";
17
+
18
+ function issue(code, path, message) {
19
+ return { code, path, message };
20
+ }
21
+
22
+ function normalizedIssues(entries) {
23
+ return (entries ?? []).map(({ code, path, message }) => ({
24
+ code,
25
+ path: path ? `/${path.replaceAll(".", "/")}` : "/declaration",
26
+ message,
27
+ }));
28
+ }
29
+
30
+ function sameJson(left, right) {
31
+ try {
32
+ return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ function bindingIssues(request, manifest) {
39
+ const issues = [];
40
+ if (
41
+ request.project.instanceId !== manifest?.manifestId ||
42
+ request.project.adopterId !== manifest?.adopter?.id
43
+ ) {
44
+ issues.push(
45
+ issue(
46
+ "delivery-legacy-project-binding-mismatch",
47
+ "/project",
48
+ "Delivery project identity must match the standard adopter manifest.",
49
+ ),
50
+ );
51
+ }
52
+ if (!sameJson(request.artifact, manifest?.adopter?.artifact)) {
53
+ issues.push(
54
+ issue(
55
+ "delivery-legacy-artifact-binding-mismatch",
56
+ "/artifact",
57
+ "Delivery artifact must match the standard adopter manifest artifact.",
58
+ ),
59
+ );
60
+ }
61
+ return issues;
62
+ }
63
+
64
+ function invalidContextResult(issues) {
65
+ const report = {
66
+ schemaVersion: 1,
67
+ contract: LEGACY_KFD_ADOPTER_DRIVER_REPORT_CONTRACT,
68
+ valid: false,
69
+ qualifying: false,
70
+ selfCertified: false,
71
+ independentlyCertified: false,
72
+ manifestRoot: null,
73
+ manifestGateRoot: null,
74
+ legacyProjectionRoot: null,
75
+ legacyProjection: null,
76
+ adapterIssues: issues,
77
+ nonClaims: [
78
+ "The legacy support matrix is a one-way projection, not declaration authority.",
79
+ "Buildchain carries the existing standard manifest decision and does not certify KFD adoption.",
80
+ ],
81
+ };
82
+ return {
83
+ valid: false,
84
+ report,
85
+ reportRoot: adopterDeliveryGateDigest(report),
86
+ issues,
87
+ };
88
+ }
89
+
90
+ export function createLegacyKfdAdopterProtocolDriver() {
91
+ return defineAdopterProtocolDriver({
92
+ interface: ADOPTER_PROTOCOL_DRIVER_INTERFACE,
93
+ id: LEGACY_KFD_ADOPTER_PROTOCOL_ID,
94
+ version: LEGACY_KFD_ADOPTER_PROTOCOL_VERSION,
95
+ verify({ request, context }) {
96
+ const manifest = context?.adopterManifest;
97
+ const manifestGate = context?.adopterManifestGate;
98
+ if (!manifest || !manifestGate) {
99
+ return invalidContextResult([
100
+ issue(
101
+ "delivery-legacy-verification-context-invalid",
102
+ "/protocol",
103
+ "The exact standard adopter manifest and manifest gate are required.",
104
+ ),
105
+ ]);
106
+ }
107
+
108
+ const source = manifest?.adopter?.artifact?.coordinate?.match(
109
+ /^([^@\s]+)@([0-9a-f]{40})$/,
110
+ );
111
+ const gateValidation = validateKfdAdopterManifestGate(manifestGate, {
112
+ expectedAdopterId: manifest?.adopter?.id,
113
+ expectedSourceRepository: source?.[1] ?? "",
114
+ expectedSourceSha: source?.[2] ?? "",
115
+ checkedAt: manifestGate?.checkedAt,
116
+ });
117
+ const projectionValidation = validateKfdLegacySupportMatrixProjection(
118
+ request.declaration,
119
+ { manifest, manifestGate },
120
+ );
121
+ const adapterIssues = [
122
+ ...bindingIssues(request, manifest),
123
+ ...normalizedIssues(gateValidation.issues),
124
+ ...normalizedIssues(projectionValidation.issues),
125
+ ];
126
+ if (
127
+ request.declaration?.contract !== KFD_LEGACY_SUPPORT_MATRIX_CONTRACT
128
+ ) {
129
+ adapterIssues.push(
130
+ issue(
131
+ "delivery-legacy-contract-invalid",
132
+ "/declaration/contract",
133
+ `Legacy declaration must use ${KFD_LEGACY_SUPPORT_MATRIX_CONTRACT}.`,
134
+ ),
135
+ );
136
+ }
137
+
138
+ const valid =
139
+ gateValidation.valid &&
140
+ projectionValidation.valid &&
141
+ adapterIssues.length === 0;
142
+ const report = {
143
+ schemaVersion: 1,
144
+ contract: LEGACY_KFD_ADOPTER_DRIVER_REPORT_CONTRACT,
145
+ valid,
146
+ qualifying: false,
147
+ selfCertified: false,
148
+ independentlyCertified: false,
149
+ manifestRoot: manifestGate?.authority?.manifestRoot ?? null,
150
+ manifestGateRoot: manifestGate?.gateRoot ?? null,
151
+ legacyProjectionRoot: adopterDeliveryGateDigest(request.declaration),
152
+ legacyProjection: structuredClone(request.declaration),
153
+ adapterIssues,
154
+ nonClaims: [
155
+ "The legacy support matrix is a one-way projection, not declaration authority.",
156
+ "Buildchain carries the existing standard manifest decision and does not certify KFD adoption.",
157
+ ],
158
+ };
159
+ return {
160
+ valid,
161
+ report,
162
+ reportRoot: adopterDeliveryGateDigest(report),
163
+ issues: adapterIssues,
164
+ };
165
+ },
166
+ });
167
+ }
@@ -0,0 +1,186 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import crypto from "node:crypto";
4
+
5
+ const SHA = /^[0-9a-f]{40}$/u;
6
+ const ROOT = /^sha256:[0-9a-f]{64}$/u;
7
+
8
+ function text(value = "") {
9
+ return String(value ?? "").trim();
10
+ }
11
+
12
+ function exactSha(value, label) {
13
+ const normalized = text(value).toLowerCase();
14
+ if (!SHA.test(normalized)) {
15
+ throw new Error(`${label} must be an exact 40-character SHA`);
16
+ }
17
+ return normalized;
18
+ }
19
+
20
+ function exactRoot(value, label) {
21
+ const normalized = text(value).toLowerCase();
22
+ if (!ROOT.test(normalized)) {
23
+ throw new Error(`${label} must be an exact sha256 content root`);
24
+ }
25
+ return normalized;
26
+ }
27
+
28
+ function canonical(value) {
29
+ if (Array.isArray(value)) return value.map(canonical);
30
+ if (value && typeof value === "object") {
31
+ return Object.fromEntries(
32
+ Object.entries(value)
33
+ .sort(([left], [right]) => left.localeCompare(right))
34
+ .map(([key, item]) => [key, canonical(item)]),
35
+ );
36
+ }
37
+ return value;
38
+ }
39
+
40
+ function evidenceRoot(value) {
41
+ return `sha256:${crypto
42
+ .createHash("sha256")
43
+ .update(JSON.stringify(canonical(value)))
44
+ .digest("hex")}`;
45
+ }
46
+
47
+ export function nextDevelopmentPreparedVersion(message) {
48
+ const title = text(message).split("\n", 1)[0];
49
+ return (
50
+ title.match(
51
+ /^(?:chore\(release\): prepare|Prepare)\s+v?([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)$/u,
52
+ )?.[1] || ""
53
+ );
54
+ }
55
+
56
+ function preparation(entry) {
57
+ if (!entry || typeof entry === "string") return undefined;
58
+ const targetVersion = nextDevelopmentPreparedVersion(entry.message);
59
+ if (!targetVersion) return undefined;
60
+ return {
61
+ sha: exactSha(entry.sha, "sourceHistory.sha"),
62
+ targetVersion,
63
+ };
64
+ }
65
+
66
+ export function nextDevelopmentPatrolContext(sourceHistory) {
67
+ const preparations = sourceHistory
68
+ .map((entry, index) => ({ index, preparation: preparation(entry) }))
69
+ .filter((entry) => entry.preparation);
70
+ const preparationShas = new Set(
71
+ preparations.map(({ preparation: item }) => item.sha),
72
+ );
73
+ const integrationShas = sourceHistory
74
+ .filter(
75
+ (entry) =>
76
+ entry &&
77
+ typeof entry !== "string" &&
78
+ Array.isArray(entry.parents) &&
79
+ entry.parents.length === 2 &&
80
+ entry.parents.some((parent) => preparationShas.has(parent)),
81
+ )
82
+ .map((entry) => exactSha(entry.sha, "sourceHistory.sha"));
83
+ return {
84
+ preparations,
85
+ ignoredSourceShas: [...preparationShas, ...integrationShas],
86
+ };
87
+ }
88
+
89
+ export function normalizeNextDevelopmentVersionReservation(input, expected) {
90
+ const status = text(input?.status);
91
+ if (!["current", "absent", "stale"].includes(status)) {
92
+ throw new Error("next-development version reservation status is invalid");
93
+ }
94
+ const normalized = {
95
+ status,
96
+ reservationSha: exactSha(
97
+ input?.reservationSha,
98
+ "versionReservation.reservationSha",
99
+ ),
100
+ candidateSha: exactSha(
101
+ input?.candidateSha,
102
+ "versionReservation.candidateSha",
103
+ ),
104
+ targetVersion: text(input?.targetVersion),
105
+ paths: [...new Set((input?.paths || []).map(text))].sort(),
106
+ evidenceRoot: exactRoot(
107
+ input?.evidenceRoot,
108
+ "versionReservation.evidenceRoot",
109
+ ),
110
+ };
111
+ if (
112
+ normalized.reservationSha !== expected.reservationSha ||
113
+ normalized.candidateSha !== expected.candidateSha ||
114
+ normalized.targetVersion !== expected.targetVersion
115
+ ) {
116
+ throw new Error("next-development version reservation identity drifted");
117
+ }
118
+ if (normalized.paths.length === 0 || normalized.paths.some((path) => !path)) {
119
+ throw new Error("next-development version reservation paths are required");
120
+ }
121
+ return normalized;
122
+ }
123
+
124
+ function encodeRepositoryPath(value) {
125
+ return value.split("/").map(encodeURIComponent).join("/");
126
+ }
127
+
128
+ export async function readGitHubNextDevelopmentVersionReservation({
129
+ api,
130
+ owner,
131
+ repo,
132
+ reservationSha,
133
+ candidateSha,
134
+ targetVersion,
135
+ }) {
136
+ const commit = await api(
137
+ `/repos/${owner}/${repo}/commits/${encodeURIComponent(reservationSha)}`,
138
+ );
139
+ if (
140
+ text(commit.sha) !== reservationSha ||
141
+ nextDevelopmentPreparedVersion(commit.commit?.message) !== targetVersion
142
+ ) {
143
+ throw new Error(
144
+ "next-development preparation commit identity or version drifted",
145
+ );
146
+ }
147
+ const paths = [
148
+ ...new Set((commit.files || []).map((file) => text(file.filename))),
149
+ ]
150
+ .filter(Boolean)
151
+ .sort();
152
+ if (paths.length === 0) {
153
+ throw new Error(
154
+ "next-development preparation commit has no version-state paths",
155
+ );
156
+ }
157
+ const observations = [];
158
+ let status = "current";
159
+ for (const filePath of paths) {
160
+ const endpoint = `/repos/${owner}/${repo}/contents/${encodeRepositoryPath(filePath)}`;
161
+ const prepared = await api(
162
+ `${endpoint}?ref=${encodeURIComponent(reservationSha)}`,
163
+ { allow404: true },
164
+ );
165
+ const candidate = await api(
166
+ `${endpoint}?ref=${encodeURIComponent(candidateSha)}`,
167
+ { allow404: true },
168
+ );
169
+ const preparedBlob =
170
+ text(prepared?.type) === "file" ? text(prepared?.sha) : "";
171
+ const candidateBlob =
172
+ text(candidate?.type) === "file" ? text(candidate?.sha) : "";
173
+ if (!candidateBlob) status = "absent";
174
+ else if (!preparedBlob || candidateBlob !== preparedBlob) status = "stale";
175
+ observations.push({ path: filePath, preparedBlob, candidateBlob });
176
+ }
177
+ const body = {
178
+ status,
179
+ reservationSha,
180
+ candidateSha,
181
+ targetVersion,
182
+ paths,
183
+ observations,
184
+ };
185
+ return { ...body, evidenceRoot: evidenceRoot(body) };
186
+ }