@kungfu-tech/buildchain 4.0.1-alpha.4 → 4.0.1-alpha.41

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 (172) 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/internal-capabilities.json +117 -8
  5. package/architecture/maintainability-policy.json +43 -25
  6. package/architecture/release-tail-contract-inventory.json +5 -2
  7. package/architecture/v3-core-mechanism-inventory.json +89 -0
  8. package/architecture/v4-adopter-delivery-parity.json +32 -5
  9. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  10. package/architecture/v4-compatibility-facts-parity.json +77 -0
  11. package/architecture/v4-floating-consumer-policy.json +6 -0
  12. package/architecture/v4-next-development-parity.json +192 -0
  13. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  14. package/architecture/v4-release-train-parity.json +1 -0
  15. package/architecture/v4-tail-reseal-parity.json +150 -0
  16. package/bin/buildchain.mjs +23 -2
  17. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  18. package/bin/internal/command-registry.mjs +3 -0
  19. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  20. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  21. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  22. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  23. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  24. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  25. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  26. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  28. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  29. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
  30. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  31. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  32. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  33. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  38. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  39. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  40. package/contracts/next-development-request-v1.schema.json +66 -0
  41. package/contracts/next-development-transition-v1.schema.json +292 -0
  42. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  43. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  44. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  45. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  46. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  47. package/dist/site/agent-index.json +4 -0
  48. package/dist/site/artifact-schemas.json +8 -0
  49. package/dist/site/buildchain-contract.json +1393 -28
  50. package/dist/site/buildchain-site.json +477 -32
  51. package/dist/site/capability-registry.json +17 -14
  52. package/dist/site/cli-registry.json +159 -1
  53. package/dist/site/kfd-claims.json +455 -15
  54. package/dist/site/kfd-upstream-aggregate.json +1 -1
  55. package/dist/site/manual-registry.json +53 -7
  56. package/dist/site/node-api-registry.json +14008 -9602
  57. package/dist/site/page-registry.json +442 -21
  58. package/dist/site/public-surface-audit.json +440 -11
  59. package/dist/site/publication-authority-registry.json +44 -8
  60. package/dist/site/publication-registry.json +4 -4
  61. package/dist/site/release-provenance.json +22 -0
  62. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  63. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  64. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  65. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  66. package/dist/site/site-manifest.json +35 -11
  67. package/dist/site/workflow-registry.json +142 -8
  68. package/docs/MAP.md +7 -2
  69. package/docs/cli-reference.md +193 -0
  70. package/docs/cli.md +8 -0
  71. package/docs/dev-alpha-candidate-patrol.md +8 -0
  72. package/docs/dev-delivery-warrant.md +22 -1
  73. package/docs/next-development-transition.md +118 -0
  74. package/docs/node-api-reference.md +583 -288
  75. package/docs/release-candidate.md +7 -0
  76. package/docs/release-tail-provider-plane.md +30 -0
  77. package/docs/v4-adopter-delivery.md +133 -0
  78. package/docs/v4-compatibility-facts.md +91 -0
  79. package/docs/v4-publication-rehearsal.md +130 -0
  80. package/docs/v4-tail-reseal.md +73 -0
  81. package/docs/versioning.md +4 -3
  82. package/package.json +30 -6
  83. package/packages/core/adopter-delivery-passport.js +259 -0
  84. package/packages/core/adopter-delivery-vectors.js +158 -0
  85. package/packages/core/adopter-delivery-vectors.json +77 -0
  86. package/packages/core/buildchain-agent-manuals.js +3 -0
  87. package/packages/core/buildchain-compatibility-authority.js +336 -0
  88. package/packages/core/buildchain-compatibility-fact.js +394 -0
  89. package/packages/core/buildchain-compatibility-facts.json +217 -0
  90. package/packages/core/buildchain-compatibility-proof.js +566 -0
  91. package/packages/core/buildchain-config.js +84 -0
  92. package/packages/core/buildchain-contract.js +93 -141
  93. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  94. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  95. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  96. package/packages/core/buildchain-publication-authority.js +1 -0
  97. package/packages/core/channel-candidate.js +8 -0
  98. package/packages/core/channel-promotion-baseline.js +55 -0
  99. package/packages/core/ci-lane-change-budget.js +247 -0
  100. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  101. package/packages/core/dev-delivery-execution-transfer.js +21 -1
  102. package/packages/core/dev-delivery-native-proof.js +43 -27
  103. package/packages/core/dev-delivery-process-boundary.js +19 -6
  104. package/packages/core/dev-delivery-provider-heartbeat.js +18 -6
  105. package/packages/core/dev-delivery-warrant-settlement.js +234 -2
  106. package/packages/core/dev-delivery-warrant-state.js +46 -10
  107. package/packages/core/dev-delivery-warrant.js +16 -3
  108. package/packages/core/github-governance-authority.js +1 -0
  109. package/packages/core/index.js +5 -0
  110. package/packages/core/kungfu-temporal-fact.js +557 -0
  111. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  112. package/packages/core/next-development-candidate-reservation.js +186 -0
  113. package/packages/core/next-development-controller.js +728 -0
  114. package/packages/core/next-development-projection.js +287 -0
  115. package/packages/core/next-development-transition.js +738 -0
  116. package/packages/core/paper-agent-entry.js +11 -5
  117. package/packages/core/paper.js +16 -5
  118. package/packages/core/published-delivery-authority.js +266 -0
  119. package/packages/core/release-candidate-recovery.js +70 -15
  120. package/packages/core/release-candidate.js +2 -2
  121. package/packages/core/release-passport.js +36 -9
  122. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  123. package/packages/core/v4-adopter-delivery.js +359 -0
  124. package/packages/core/v4-canonical-contracts.js +5 -0
  125. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  126. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  127. package/packages/core/v4-floating-consumer-policy.js +143 -20
  128. package/packages/core/v4-floating-consumer-release-passport.js +35 -8
  129. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  130. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  131. package/packages/core/v4-publication-rehearsal.js +481 -0
  132. package/packages/core/v4-tail-reseal-contract.js +26 -0
  133. package/packages/core/v4-tail-reseal-github.js +178 -0
  134. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  135. package/packages/core/v4-tail-reseal.js +594 -0
  136. package/packages/core/workflow-yaml-contract.js +24 -2
  137. package/scripts/artifact-signing-controller-core.mjs +1 -1
  138. package/scripts/buildchain-cli-help.mjs +30 -0
  139. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  140. package/scripts/check-inventory.mjs +14 -3
  141. package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
  142. package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
  143. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  144. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  145. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  146. package/scripts/dev-delivery-warrant-options.mjs +27 -11
  147. package/scripts/dev-delivery-warrant-store.mjs +10 -6
  148. package/scripts/dev-delivery-warrant.mjs +50 -6
  149. package/scripts/dev-pr-auto-merge.mjs +25 -11
  150. package/scripts/dev-pr-delivery-warrant.mjs +45 -0
  151. package/scripts/dev-pr-prequeue-guard.mjs +0 -18
  152. package/scripts/dev-qualification-patrol.mjs +9 -9
  153. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  154. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  155. package/scripts/generate-next-development-guidance.mjs +49 -0
  156. package/scripts/generate-release-candidate-passport.mjs +152 -0
  157. package/scripts/generate-site-bundle.mjs +46 -3
  158. package/scripts/init-repo.mjs +22 -4
  159. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  160. package/scripts/next-development-self-dogfood.mjs +597 -0
  161. package/scripts/next-development-transition.mjs +47 -0
  162. package/scripts/project-cut-merge-queue-admission.mjs +394 -0
  163. package/scripts/promotion-channel-router.mjs +4 -4
  164. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  165. package/scripts/release-tail.mjs +51 -3
  166. package/scripts/resume-from-candidate-run.mjs +595 -65
  167. package/scripts/site-capability-metadata.mjs +29 -0
  168. package/scripts/stable-candidate-qualification.mjs +9 -10
  169. package/scripts/v4-consumer-policy.mjs +5 -5
  170. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  171. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  172. package/scripts/v4-tail-reseal.mjs +416 -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
  }
@@ -374,6 +374,7 @@ export function assertArtifactSigningControlRequestContext(
374
374
  export function artifactSigningControlRequestOutputs(request) {
375
375
  const value = validateArtifactSigningControlRequest(request);
376
376
  return {
377
+ "source-run-attempt": String(value.source.runAttempt),
377
378
  "request-count": String(value.request.count),
378
379
  "request-artifact": value.request.artifact,
379
380
  "request-root": value.request.root,
@@ -384,7 +385,6 @@ export function artifactSigningControlRequestOutputs(request) {
384
385
  "runtime-sha": value.runtime.sha,
385
386
  };
386
387
  }
387
-
388
388
  export function normalizeControllerStatus(value) {
389
389
  const normalized = required(value, "authority status");
390
390
  if (