@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,594 @@
1
+ import {
2
+ v4ContentRoot,
3
+ validateV4Clock,
4
+ validateV4Root,
5
+ } from "./v4-canonical-contracts.js";
6
+ import {
7
+ V4_TAIL_RESEAL_PLAN_CONTRACT,
8
+ V4_TAIL_RESEAL_PLATFORMS as PLATFORM_IDS,
9
+ V4_TAIL_RESEAL_PLATFORM_RUNNERS as PLATFORM_RUNNERS,
10
+ V4_TAIL_RESEAL_REQUEST_CONTRACT,
11
+ V4_TAIL_RESEAL_REUSED_STAGE_KEYS as REUSED_STAGE_KEYS,
12
+ } from "./v4-tail-reseal-contract.js";
13
+
14
+ export {
15
+ V4_TAIL_RESEAL_PLAN_CONTRACT,
16
+ V4_TAIL_RESEAL_REQUEST_CONTRACT,
17
+ } from "./v4-tail-reseal-contract.js";
18
+
19
+ const SHA = /^[0-9a-f]{40}$/u;
20
+ const TOKEN = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/u;
21
+ const V4_RUNTIME_REF = /^(?:v4-alpha|train\/v4\/v4\.0\/[A-Za-z0-9._/-]+)$/u;
22
+
23
+ export class V4TailResealFault extends Error {
24
+ constructor(code, path, message) {
25
+ super(`${path}: ${message}`);
26
+ this.name = "V4TailResealFault";
27
+ this.code = code;
28
+ this.path = path;
29
+ }
30
+ }
31
+
32
+ function fault(code, path, message) {
33
+ throw new V4TailResealFault(code, path, message);
34
+ }
35
+
36
+ function exactKeys(value, expected, path) {
37
+ if (!value || typeof value !== "object" || Array.isArray(value))
38
+ fault("invalid-tail-reseal-shape", path, "object required");
39
+ const actual = Object.keys(value).sort();
40
+ const canonical = [...expected].sort();
41
+ if (
42
+ actual.length !== canonical.length ||
43
+ actual.some((key, index) => key !== canonical[index])
44
+ )
45
+ fault(
46
+ "invalid-tail-reseal-shape",
47
+ path,
48
+ "keys do not match the closed tail-reseal contract",
49
+ );
50
+ }
51
+
52
+ function token(value, path) {
53
+ if (typeof value !== "string" || !TOKEN.test(value))
54
+ fault("invalid-tail-reseal-token", path, "ASCII identity token required");
55
+ return value;
56
+ }
57
+
58
+ function text(value, path) {
59
+ if (typeof value !== "string" || value.trim() === "")
60
+ fault("invalid-tail-reseal-text", path, "non-empty string required");
61
+ return value.trim();
62
+ }
63
+
64
+ function artifactPath(value, path) {
65
+ const normalized = text(value, path).replaceAll("\\", "/");
66
+ if (
67
+ normalized.startsWith("/") ||
68
+ normalized.split("/").includes("..") ||
69
+ normalized.startsWith(".buildchain/tail-reseal")
70
+ )
71
+ fault(
72
+ "invalid-tail-reseal-artifact-path",
73
+ path,
74
+ "artifact paths must be safe relative payload paths",
75
+ );
76
+ return normalized;
77
+ }
78
+
79
+ function sha(value, path) {
80
+ if (typeof value !== "string" || !SHA.test(value))
81
+ fault("invalid-tail-reseal-sha", path, "exact lowercase Git SHA required");
82
+ return value;
83
+ }
84
+
85
+ function root(value, path) {
86
+ try {
87
+ validateV4Root(value, path);
88
+ } catch (error) {
89
+ fault("invalid-tail-reseal-root", path, error.message);
90
+ }
91
+ return value;
92
+ }
93
+
94
+ function clock(value, path) {
95
+ try {
96
+ validateV4Clock(value, path);
97
+ } catch (error) {
98
+ fault("invalid-tail-reseal-clock", path, error.message);
99
+ }
100
+ return value;
101
+ }
102
+
103
+ function positiveInteger(value, path) {
104
+ if (!Number.isSafeInteger(value) || value < 1)
105
+ fault(
106
+ "invalid-tail-reseal-integer",
107
+ path,
108
+ "positive safe integer required",
109
+ );
110
+ return value;
111
+ }
112
+
113
+ function sortedUnique(values, path, validate = token) {
114
+ if (!Array.isArray(values) || values.length === 0)
115
+ fault("invalid-tail-reseal-shape", path, "non-empty array required");
116
+ const normalized = values.map((value, index) =>
117
+ validate(value, `${path}/${index}`),
118
+ );
119
+ const canonical = [...new Set(normalized)].sort((left, right) =>
120
+ Buffer.from(left).compare(Buffer.from(right)),
121
+ );
122
+ if (
123
+ normalized.length !== canonical.length ||
124
+ normalized.some((value, index) => value !== canonical[index])
125
+ )
126
+ fault(
127
+ "unordered-tail-reseal-evidence",
128
+ path,
129
+ "values must be byte-sorted and duplicate-free",
130
+ );
131
+ return normalized;
132
+ }
133
+
134
+ function validateSource(value) {
135
+ exactKeys(
136
+ value,
137
+ [
138
+ "runId",
139
+ "runAttempt",
140
+ "sha",
141
+ "treeSha",
142
+ "sourceRoot",
143
+ "workflowFile",
144
+ "workflowName",
145
+ ],
146
+ "$/source",
147
+ );
148
+ positiveInteger(value.runId, "$/source/runId");
149
+ positiveInteger(value.runAttempt, "$/source/runAttempt");
150
+ sha(value.sha, "$/source/sha");
151
+ sha(value.treeSha, "$/source/treeSha");
152
+ root(value.sourceRoot, "$/source/sourceRoot");
153
+ text(value.workflowFile, "$/source/workflowFile");
154
+ text(value.workflowName, "$/source/workflowName");
155
+ }
156
+
157
+ function validateTarget(value) {
158
+ exactKeys(value, ["channel", "ref", "baseSha", "version"], "$/target");
159
+ if (value.channel !== "alpha" || value.ref !== "alpha/v4/v4.0")
160
+ fault(
161
+ "tail-reseal-target-mismatch",
162
+ "$/target",
163
+ "tail reseal is limited to alpha/v4/v4.0",
164
+ );
165
+ sha(value.baseSha, "$/target/baseSha");
166
+ if (!/^4\.0\.0-alpha\.\d+$/u.test(value.version))
167
+ fault(
168
+ "tail-reseal-target-mismatch",
169
+ "$/target/version",
170
+ "v4 Alpha version required",
171
+ );
172
+ }
173
+
174
+ function validateRuntime(value) {
175
+ exactKeys(
176
+ value,
177
+ ["repository", "ref", "sha", "contractRoot", "consumerPolicyReceiptRoot"],
178
+ "$/runtime",
179
+ );
180
+ if (value.repository !== "kungfu-systems/buildchain")
181
+ fault(
182
+ "tail-reseal-runtime-mismatch",
183
+ "$/runtime/repository",
184
+ "official Buildchain runtime required",
185
+ );
186
+ if (!V4_RUNTIME_REF.test(value.ref))
187
+ fault(
188
+ "tail-reseal-runtime-mismatch",
189
+ "$/runtime/ref",
190
+ "v4-alpha or a transient v4 train is required",
191
+ );
192
+ sha(value.sha, "$/runtime/sha");
193
+ root(value.contractRoot, "$/runtime/contractRoot");
194
+ root(value.consumerPolicyReceiptRoot, "$/runtime/consumerPolicyReceiptRoot");
195
+ }
196
+
197
+ function validateWarrant(value) {
198
+ exactKeys(
199
+ value,
200
+ [
201
+ "candidateId",
202
+ "generation",
203
+ "fencingToken",
204
+ "status",
205
+ "lineageRoot",
206
+ "qualificationRoot",
207
+ "stateReadbackRoot",
208
+ ],
209
+ "$/warrant",
210
+ );
211
+ token(value.candidateId, "$/warrant/candidateId");
212
+ positiveInteger(value.generation, "$/warrant/generation");
213
+ token(value.fencingToken, "$/warrant/fencingToken");
214
+ if (value.status !== "qualified")
215
+ fault(
216
+ "tail-reseal-warrant-ineligible",
217
+ "$/warrant/status",
218
+ "exact qualified Warrant required",
219
+ );
220
+ root(value.lineageRoot, "$/warrant/lineageRoot");
221
+ root(value.qualificationRoot, "$/warrant/qualificationRoot");
222
+ root(value.stateReadbackRoot, "$/warrant/stateReadbackRoot");
223
+ }
224
+
225
+ function validateRetention(value, evaluatedAt) {
226
+ exactKeys(
227
+ value,
228
+ ["status", "retainUntil", "policyRoot", "readbackRoot"],
229
+ "$/retention",
230
+ );
231
+ if (value.status !== "retained")
232
+ fault(
233
+ "tail-reseal-retention-invalid",
234
+ "$/retention/status",
235
+ "retained artifact status required",
236
+ );
237
+ clock(value.retainUntil, "$/retention/retainUntil");
238
+ if (Date.parse(value.retainUntil) <= Date.parse(evaluatedAt))
239
+ fault(
240
+ "tail-reseal-retention-expired",
241
+ "$/retention/retainUntil",
242
+ "retention must extend beyond evaluation",
243
+ );
244
+ root(value.policyRoot, "$/retention/policyRoot");
245
+ root(value.readbackRoot, "$/retention/readbackRoot");
246
+ }
247
+
248
+ function validateFailure(value) {
249
+ exactKeys(
250
+ value,
251
+ [
252
+ "platformId",
253
+ "stage",
254
+ "jobId",
255
+ "jobName",
256
+ "stepName",
257
+ "code",
258
+ "evidenceRoot",
259
+ ],
260
+ "$/failure",
261
+ );
262
+ const exactFailure =
263
+ value.platformId === "macos-arm64" &&
264
+ value.stage === "signing-finalization" &&
265
+ value.jobName === "build / Finalize signed artifact macOS ARM64" &&
266
+ value.stepName === "Recompute manifest over final signed bytes";
267
+ if (!exactFailure)
268
+ fault(
269
+ "tail-reseal-failure-mismatch",
270
+ "$/failure",
271
+ "single known macOS signing-finalization failure required",
272
+ );
273
+ positiveInteger(value.jobId, "$/failure/jobId");
274
+ token(value.code, "$/failure/code");
275
+ root(value.evidenceRoot, "$/failure/evidenceRoot");
276
+ }
277
+
278
+ function validateStageResume(value) {
279
+ exactKeys(
280
+ value,
281
+ ["planRoot", "qualificationRoot", "zeroEffectReuse", "decisions"],
282
+ "$/stageResume",
283
+ );
284
+ root(value.planRoot, "$/stageResume/planRoot");
285
+ root(value.qualificationRoot, "$/stageResume/qualificationRoot");
286
+ if (value.zeroEffectReuse !== true)
287
+ fault(
288
+ "tail-reseal-capsule-effect",
289
+ "$/stageResume/zeroEffectReuse",
290
+ "Stage Capsule reuse must remain effect-pure",
291
+ );
292
+ if (!Array.isArray(value.decisions))
293
+ fault(
294
+ "invalid-tail-reseal-shape",
295
+ "$/stageResume/decisions",
296
+ "array required",
297
+ );
298
+ const stageKeys = [];
299
+ for (const [index, decision] of value.decisions.entries()) {
300
+ const path = `$/stageResume/decisions/${index}`;
301
+ exactKeys(
302
+ decision,
303
+ ["stageKey", "decision", "capsuleRoot", "evidenceRoot"],
304
+ path,
305
+ );
306
+ stageKeys.push(token(decision.stageKey, `${path}/stageKey`));
307
+ if (decision.decision !== "reuse")
308
+ fault(
309
+ "tail-reseal-rebuild-required",
310
+ `${path}/decision`,
311
+ "all pre-tail stages must be exact reusable capsules",
312
+ );
313
+ root(decision.capsuleRoot, `${path}/capsuleRoot`);
314
+ root(decision.evidenceRoot, `${path}/evidenceRoot`);
315
+ }
316
+ if (JSON.stringify(stageKeys) !== JSON.stringify(REUSED_STAGE_KEYS))
317
+ fault(
318
+ "tail-reseal-stage-coverage-mismatch",
319
+ "$/stageResume/decisions",
320
+ "exact install, build, verify, and package reuse proof is required for every platform",
321
+ );
322
+ }
323
+
324
+ function validateSigning(value, evaluatedAt) {
325
+ exactKeys(
326
+ value,
327
+ [
328
+ "authorityRepository",
329
+ "authorityRunId",
330
+ "runtimeSha",
331
+ "requestRoot",
332
+ "delegationRoot",
333
+ "resultArtifact",
334
+ "resultArtifactRoot",
335
+ "credentialAuthorityId",
336
+ "credentialScopeRoot",
337
+ "credentialLeaseRoot",
338
+ "credentialExpiresAt",
339
+ "providerReadbackRoot",
340
+ ],
341
+ "$/signing",
342
+ );
343
+ text(value.authorityRepository, "$/signing/authorityRepository");
344
+ positiveInteger(value.authorityRunId, "$/signing/authorityRunId");
345
+ sha(value.runtimeSha, "$/signing/runtimeSha");
346
+ for (const field of [
347
+ "requestRoot",
348
+ "delegationRoot",
349
+ "resultArtifactRoot",
350
+ "credentialScopeRoot",
351
+ "credentialLeaseRoot",
352
+ "providerReadbackRoot",
353
+ ])
354
+ root(value[field], `$/signing/${field}`);
355
+ text(value.resultArtifact, "$/signing/resultArtifact");
356
+ token(value.credentialAuthorityId, "$/signing/credentialAuthorityId");
357
+ clock(value.credentialExpiresAt, "$/signing/credentialExpiresAt");
358
+ if (Date.parse(value.credentialExpiresAt) <= Date.parse(evaluatedAt))
359
+ fault(
360
+ "tail-reseal-credential-expired",
361
+ "$/signing/credentialExpiresAt",
362
+ "live credential authority is required",
363
+ );
364
+ }
365
+
366
+ function validateReleaseTail(value) {
367
+ exactKeys(
368
+ value,
369
+ [
370
+ "declarationRoot",
371
+ "transactionRoot",
372
+ "operationRoot",
373
+ "idempotencyKey",
374
+ "providerReadbackRoot",
375
+ ],
376
+ "$/releaseTail",
377
+ );
378
+ for (const field of [
379
+ "declarationRoot",
380
+ "transactionRoot",
381
+ "operationRoot",
382
+ "providerReadbackRoot",
383
+ ])
384
+ root(value[field], `$/releaseTail/${field}`);
385
+ token(value.idempotencyKey, "$/releaseTail/idempotencyKey");
386
+ }
387
+
388
+ function validatePlatforms(values) {
389
+ if (!Array.isArray(values) || values.length !== PLATFORM_IDS.length)
390
+ fault(
391
+ "tail-reseal-platform-mismatch",
392
+ "$/platforms",
393
+ "exact four-platform candidate required",
394
+ );
395
+ const ids = [];
396
+ const names = new Set();
397
+ for (const [index, value] of values.entries()) {
398
+ const path = `$/platforms/${index}`;
399
+ exactKeys(
400
+ value,
401
+ [
402
+ "id",
403
+ "name",
404
+ "runner",
405
+ "artifactName",
406
+ "artifactRoot",
407
+ "artifactArchiveRoot",
408
+ "manifestArtifactName",
409
+ "manifestRoot",
410
+ "manifestArchiveRoot",
411
+ "artifactPaths",
412
+ "capsuleRoot",
413
+ ],
414
+ path,
415
+ );
416
+ ids.push(token(value.id, `${path}/id`));
417
+ text(value.name, `${path}/name`);
418
+ text(value.runner, `${path}/runner`);
419
+ if (value.runner !== PLATFORM_RUNNERS[value.id])
420
+ fault(
421
+ "tail-reseal-runner-mismatch",
422
+ `${path}/runner`,
423
+ "platform runner differs from the declared v4 platform authority",
424
+ );
425
+ for (const field of ["artifactName", "manifestArtifactName"]) {
426
+ const name = text(value[field], `${path}/${field}`);
427
+ if (names.has(name))
428
+ fault(
429
+ "tail-reseal-artifact-ambiguous",
430
+ `${path}/${field}`,
431
+ "artifact names must be unique",
432
+ );
433
+ names.add(name);
434
+ }
435
+ root(value.artifactRoot, `${path}/artifactRoot`);
436
+ root(value.artifactArchiveRoot, `${path}/artifactArchiveRoot`);
437
+ root(value.manifestRoot, `${path}/manifestRoot`);
438
+ root(value.manifestArchiveRoot, `${path}/manifestArchiveRoot`);
439
+ const artifactPaths = sortedUnique(
440
+ value.artifactPaths,
441
+ `${path}/artifactPaths`,
442
+ artifactPath,
443
+ );
444
+ if (artifactPaths.length !== 2)
445
+ fault(
446
+ "tail-reseal-artifact-path-mismatch",
447
+ `${path}/artifactPaths`,
448
+ "exactly two declared payload roots are required",
449
+ );
450
+ root(value.capsuleRoot, `${path}/capsuleRoot`);
451
+ }
452
+ if (JSON.stringify(ids) !== JSON.stringify(PLATFORM_IDS))
453
+ fault(
454
+ "tail-reseal-platform-mismatch",
455
+ "$/platforms",
456
+ `platforms must be byte-ordered as ${PLATFORM_IDS.join(", ")}`,
457
+ );
458
+ }
459
+
460
+ export function normalizeV4TailResealRequest(request) {
461
+ exactKeys(
462
+ request,
463
+ [
464
+ "schema",
465
+ "evaluatedAt",
466
+ "repository",
467
+ "source",
468
+ "target",
469
+ "runtime",
470
+ "warrant",
471
+ "retention",
472
+ "failure",
473
+ "stageResume",
474
+ "signing",
475
+ "releaseTail",
476
+ "platforms",
477
+ ],
478
+ "$",
479
+ );
480
+ if (request.schema !== V4_TAIL_RESEAL_REQUEST_CONTRACT)
481
+ fault(
482
+ "unsupported-tail-reseal-version",
483
+ "$/schema",
484
+ "unsupported v4 tail-reseal request",
485
+ );
486
+ clock(request.evaluatedAt, "$/evaluatedAt");
487
+ text(request.repository, "$/repository");
488
+ validateSource(request.source);
489
+ validateTarget(request.target);
490
+ validateRuntime(request.runtime);
491
+ validateWarrant(request.warrant);
492
+ validateRetention(request.retention, request.evaluatedAt);
493
+ validateFailure(request.failure);
494
+ validateStageResume(request.stageResume);
495
+ validateSigning(request.signing, request.evaluatedAt);
496
+ validateReleaseTail(request.releaseTail);
497
+ validatePlatforms(request.platforms);
498
+ if (request.signing.runtimeSha !== request.runtime.sha)
499
+ fault(
500
+ "tail-reseal-signing-runtime-mismatch",
501
+ "$/signing/runtimeSha",
502
+ "signing authority runtime differs from candidate runtime",
503
+ );
504
+ const capsuleRoots = new Map(
505
+ request.platforms.map((platform) => [platform.id, platform.capsuleRoot]),
506
+ );
507
+ for (const decision of request.stageResume.decisions) {
508
+ const platformId = decision.stageKey.split(":", 1)[0];
509
+ if (decision.capsuleRoot !== capsuleRoots.get(platformId))
510
+ fault(
511
+ "tail-reseal-capsule-mismatch",
512
+ "$/stageResume/decisions",
513
+ `Stage Capsule root drifted for ${platformId}`,
514
+ );
515
+ }
516
+ return structuredClone(request);
517
+ }
518
+
519
+ export function planV4TailReseal(request) {
520
+ const normalized = normalizeV4TailResealRequest(request);
521
+ const payload = {
522
+ schema: V4_TAIL_RESEAL_PLAN_CONTRACT,
523
+ mode: "production",
524
+ productionAuthority: "v4",
525
+ evaluatedAt: normalized.evaluatedAt,
526
+ repository: normalized.repository,
527
+ sourceRoot: normalized.source.sourceRoot,
528
+ runtimeSha: normalized.runtime.sha,
529
+ warrantLineageRoot: normalized.warrant.lineageRoot,
530
+ stageResumePlanRoot: normalized.stageResume.planRoot,
531
+ retainedPlatformRoots: normalized.platforms.map((platform) => ({
532
+ platformId: platform.id,
533
+ artifactRoot: platform.artifactRoot,
534
+ artifactArchiveRoot: platform.artifactArchiveRoot,
535
+ manifestRoot: platform.manifestRoot,
536
+ manifestArchiveRoot: platform.manifestArchiveRoot,
537
+ capsuleRoot: platform.capsuleRoot,
538
+ })),
539
+ skippedStages: ["build", "install", "package", "platform-matrix", "verify"],
540
+ rerunStages: [
541
+ "macos-signing-finalization",
542
+ "aggregate",
543
+ "candidate-passport",
544
+ "protected-readback",
545
+ ],
546
+ capsuleReuse: {
547
+ authority: "content-addressed-stage-capsule",
548
+ effectAuthority: "none",
549
+ zeroExternalMutations: true,
550
+ qualificationRoot: normalized.stageResume.qualificationRoot,
551
+ },
552
+ signingEffect: {
553
+ authority: "explicit-live-release-tail",
554
+ platformId: "macos-arm64",
555
+ operationRoot: normalized.releaseTail.operationRoot,
556
+ idempotencyKey: normalized.releaseTail.idempotencyKey,
557
+ fencedBy: [
558
+ normalized.warrant.stateReadbackRoot,
559
+ normalized.signing.credentialLeaseRoot,
560
+ normalized.releaseTail.transactionRoot,
561
+ ].sort(),
562
+ providerReadbackRoots: [
563
+ normalized.signing.providerReadbackRoot,
564
+ normalized.releaseTail.providerReadbackRoot,
565
+ ].sort(),
566
+ },
567
+ passport: {
568
+ contract: "kungfu-buildchain-release-candidate-passport",
569
+ consumerPolicyReceiptRoot: normalized.runtime.consumerPolicyReceiptRoot,
570
+ sourceSha: normalized.source.sha,
571
+ version: normalized.target.version,
572
+ },
573
+ invalidationConditions: [
574
+ "artifact-byte-drift",
575
+ "credential-authority-drift",
576
+ "manifest-drift",
577
+ "platform-drift",
578
+ "prior-failure-drift",
579
+ "retention-expired",
580
+ "signer-authority-drift",
581
+ "source-drift",
582
+ "warrant-lineage-drift",
583
+ ],
584
+ };
585
+ return {
586
+ ...payload,
587
+ planRoot: v4ContentRoot("tail-reseal-plan", payload),
588
+ };
589
+ }
590
+
591
+ export {
592
+ V4_TAIL_RESEAL_PLATFORMS,
593
+ V4_TAIL_RESEAL_REUSED_STAGE_KEYS,
594
+ } from "./v4-tail-reseal-contract.js";
@@ -67,8 +67,7 @@ function directEntries(lines, parentLine) {
67
67
  const trimmed = line.trim();
68
68
  const indent = indentation(line);
69
69
  if (trimmed && indent <= parentIndent) break;
70
- if (!trimmed || trimmed.startsWith("#") || indent !== childIndent)
71
- continue;
70
+ if (!trimmed || trimmed.startsWith("#") || indent !== childIndent) continue;
72
71
  const match = line.match(/^\s*([A-Za-z0-9_.-]+):(?:\s*(.*))?$/);
73
72
  if (match) {
74
73
  entries.push({
@@ -263,6 +262,28 @@ export function parseWorkflowCallJobs(text) {
263
262
  .filter((job) => job.uses);
264
263
  }
265
264
 
265
+ export function parseWorkflowJobs(text) {
266
+ const lines = String(text || "").split(/\r?\n/);
267
+ const jobs = topLevelEntry(lines, "jobs");
268
+ if (!jobs) return [];
269
+ return directEntries(lines, jobs.line)
270
+ .map((job) => {
271
+ const properties = directEntries(lines, job.line);
272
+ const property = (name) =>
273
+ properties.find((entry) => entry.key === name) || null;
274
+ return {
275
+ id: job.key,
276
+ name: unquote(property("name")?.value || job.key),
277
+ uses: unquote(property("uses")?.value || ""),
278
+ runsOn: unquote(property("runs-on")?.value || ""),
279
+ timeoutMinutes: property("timeout-minutes")
280
+ ? scalar(property("timeout-minutes").value)
281
+ : null,
282
+ };
283
+ })
284
+ .sort((left, right) => compareCodeUnits(left.id, right.id));
285
+ }
286
+
266
287
  function blockScalarIndent(lines, lineIndex, parentIndent) {
267
288
  for (let index = lineIndex + 1; index < lines.length; index += 1) {
268
289
  if (!lines[index].trim()) continue;
@@ -315,6 +336,7 @@ export function parseWorkflowDocument(text) {
315
336
  return {
316
337
  triggers: parseTriggers(String(text || "").split(/\r?\n/)),
317
338
  interface: parseReusableWorkflowInterface(text),
339
+ jobs: parseWorkflowJobs(text),
318
340
  callJobs: parseWorkflowCallJobs(text),
319
341
  };
320
342
  }