@kungfu-tech/buildchain 3.0.4-alpha.2 → 3.0.4-alpha.4

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 (50) hide show
  1. package/bin/buildchain.mjs +126 -72
  2. package/bin/internal/trust-release-cli.mjs +15 -537
  3. package/bin/internal/trust-release-command-handlers.mjs +14 -0
  4. package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
  5. package/bin/internal/trust-release-release-handlers.mjs +317 -0
  6. package/bin/internal/trust-release-verification-handlers.mjs +306 -0
  7. package/dist/site/buildchain-contract.json +35 -24
  8. package/dist/site/buildchain-site.json +22 -10
  9. package/dist/site/controller-registry.json +16 -3
  10. package/dist/site/kfd-claims.json +9 -7
  11. package/dist/site/kfd-upstream-aggregate.json +1 -1
  12. package/dist/site/manual-registry.json +2 -2
  13. package/dist/site/node-api-registry.json +7 -7
  14. package/dist/site/page-registry.json +9 -4
  15. package/dist/site/public-surface-audit.json +56 -8
  16. package/dist/site/publication-registry.json +4 -4
  17. package/dist/site/release-model.json +7 -0
  18. package/dist/site/site-manifest.json +6 -6
  19. package/dist/site/workflow-registry.json +13 -5
  20. package/docs/MAP.md +1 -1
  21. package/docs/release-propagation.md +166 -8
  22. package/package.json +1 -1
  23. package/packages/core/buildchain-kfd-claims.js +1 -1
  24. package/packages/core/controller-evidence.js +8 -1
  25. package/packages/core/index.js +1 -13
  26. package/packages/core/paper-agent-entry.js +10 -11
  27. package/packages/core/paper-npm-bootstrap.js +492 -0
  28. package/packages/core/paper.js +271 -669
  29. package/packages/core/public-surface-cli.js +12 -1
  30. package/packages/core/release-passport.js +67 -71
  31. package/packages/core/release-propagation-common.js +64 -0
  32. package/packages/core/release-propagation-execution-profile.js +59 -0
  33. package/packages/core/release-propagation-release.js +196 -0
  34. package/packages/core/release-propagation-stage-evidence.js +364 -0
  35. package/packages/core/release-propagation-work-capture.js +64 -0
  36. package/packages/core/release-propagation-work-constants.js +34 -0
  37. package/packages/core/release-propagation-work-control.js +203 -0
  38. package/packages/core/release-propagation-work-transitions.js +145 -0
  39. package/packages/core/release-propagation-work.js +517 -0
  40. package/packages/core/release-propagation.js +34 -158
  41. package/scripts/aws-windows-jit-controller-core.mjs +269 -0
  42. package/scripts/aws-windows-jit-controller.mjs +502 -0
  43. package/scripts/check-internal-architecture.mjs +178 -52
  44. package/scripts/check-maintainability.mjs +76 -17
  45. package/scripts/generate-site-bundle.mjs +16 -7
  46. package/scripts/maintainability-metrics.mjs +24 -4
  47. package/scripts/release-propagation.mjs +126 -0
  48. package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
  49. package/scripts/web-surface-core.mjs +46 -207
  50. package/scripts/web-surface-routing.mjs +286 -0
@@ -0,0 +1,145 @@
1
+ import {
2
+ normalizeFamilyStateReference,
3
+ normalizeWorkAuthority,
4
+ normalizeTypedReference,
5
+ } from "./release-propagation-work-control.js";
6
+ import { FAILURE_DISPOSITIONS } from "./release-propagation-work-constants.js";
7
+ import {
8
+ RELEASE_PROPAGATION_WORK_STAGES,
9
+ assertExpectedWorkRoot,
10
+ nextWorkAction,
11
+ normalizeStageReceipt,
12
+ successorWork,
13
+ verifyReleasePropagationWork,
14
+ withWorkRoot,
15
+ } from "./release-propagation-work.js";
16
+
17
+ export function claimReleasePropagationWork({
18
+ work,
19
+ expectedWorkRoot,
20
+ authority,
21
+ familyState,
22
+ } = {}) {
23
+ const current = verifyReleasePropagationWork(work).work;
24
+ assertExpectedWorkRoot(current, expectedWorkRoot);
25
+ if (current.state.lifecycle !== "paused" || current.state.nextAction.action !== "claim") {
26
+ throw new Error("only paused capture-only propagation work can be claimed");
27
+ }
28
+ const normalizedFamilyState = current.workControl.bindingState === "bound"
29
+ ? current.workControl.familyState
30
+ : normalizeFamilyStateReference(familyState);
31
+ const normalizedAuthority = normalizeWorkAuthority(authority, normalizedFamilyState);
32
+ if (normalizedAuthority.mode !== "execute") {
33
+ throw new Error("claim requires execute authority");
34
+ }
35
+ const successor = successorWork(current);
36
+ successor.workControl.bindingState = "bound";
37
+ successor.workControl.familyState = normalizedFamilyState;
38
+ successor.authority = normalizedAuthority;
39
+ successor.intent.publishToProduction = normalizedAuthority.publishToProduction;
40
+ successor.state.lifecycle = "ready";
41
+ successor.state.nextAction = nextWorkAction(successor, "record", successor.state.currentStage);
42
+ return verifyReleasePropagationWork(withWorkRoot(successor)).work;
43
+ }
44
+
45
+ export function recordReleasePropagationStage({ work, expectedWorkRoot, receipt } = {}) {
46
+ const current = verifyReleasePropagationWork(work).work;
47
+ assertExpectedWorkRoot(current, expectedWorkRoot);
48
+ if (current.authority.mode !== "execute") {
49
+ throw new Error("propagation work must be claimed before recording execution");
50
+ }
51
+ if (!new Set(["ready", "retryable-failure"]).has(current.state.lifecycle)) {
52
+ throw new Error(`propagation work cannot record from lifecycle ${current.state.lifecycle}`);
53
+ }
54
+ if (current.state.currentStage === "complete") {
55
+ throw new Error("the complete stage requires a Work Control completion decision");
56
+ }
57
+ const normalizedReceipt = normalizeStageReceipt(receipt, {
58
+ work: current,
59
+ workId: current.workId,
60
+ expectedWorkRoot: current.contentRoot,
61
+ expectedStage: current.state.currentStage,
62
+ allowedOutcomes: new Set(["success", "failure"]),
63
+ });
64
+ const successor = successorWork(current);
65
+ successor.stageReceipts.push(normalizedReceipt);
66
+ successor.state.recoveryCursor.lastReceiptRoot = normalizedReceipt.receiptRoot;
67
+ if (normalizedReceipt.outcome === "failure") {
68
+ const disposition = FAILURE_DISPOSITIONS.get(normalizedReceipt.failure.class);
69
+ successor.state.lifecycle = disposition === "retry" ? "retryable-failure" : disposition;
70
+ successor.state.nextAction = disposition === "retry"
71
+ ? nextWorkAction(successor, "repair", successor.state.currentStage, normalizedReceipt.failure.code)
72
+ : nextWorkAction(successor, disposition, successor.state.currentStage, normalizedReceipt.failure.code);
73
+ return verifyReleasePropagationWork(withWorkRoot(successor)).work;
74
+ }
75
+ const nextIndex = successor.state.recoveryCursor.index + 1;
76
+ successor.state.recoveryCursor = {
77
+ stage: RELEASE_PROPAGATION_WORK_STAGES[nextIndex],
78
+ index: nextIndex,
79
+ attempt: 1,
80
+ lastReceiptRoot: normalizedReceipt.receiptRoot,
81
+ };
82
+ successor.state.currentStage = RELEASE_PROPAGATION_WORK_STAGES[nextIndex];
83
+ successor.state.lifecycle = "ready";
84
+ successor.state.nextAction = nextWorkAction(successor, "record", successor.state.currentStage);
85
+ return verifyReleasePropagationWork(withWorkRoot(successor)).work;
86
+ }
87
+
88
+ export function repairReleasePropagationWork({ work, expectedWorkRoot, receipt } = {}) {
89
+ const current = verifyReleasePropagationWork(work).work;
90
+ assertExpectedWorkRoot(current, expectedWorkRoot);
91
+ if (current.state.lifecycle !== "retryable-failure") {
92
+ throw new Error("only retryable propagation failures can be repaired automatically");
93
+ }
94
+ const normalizedReceipt = normalizeStageReceipt(receipt, {
95
+ work: current,
96
+ workId: current.workId,
97
+ expectedWorkRoot: current.contentRoot,
98
+ expectedStage: current.state.currentStage,
99
+ allowedOutcomes: new Set(["repair"]),
100
+ });
101
+ const successor = successorWork(current);
102
+ successor.stageReceipts.push(normalizedReceipt);
103
+ successor.state.lifecycle = "ready";
104
+ successor.state.recoveryCursor.attempt += 1;
105
+ successor.state.recoveryCursor.lastReceiptRoot = normalizedReceipt.receiptRoot;
106
+ successor.state.nextAction = nextWorkAction(successor, "record", successor.state.currentStage);
107
+ return verifyReleasePropagationWork(withWorkRoot(successor)).work;
108
+ }
109
+
110
+ export function completeReleasePropagationWork({
111
+ work,
112
+ expectedWorkRoot,
113
+ receipt,
114
+ completionDecision,
115
+ } = {}) {
116
+ const current = verifyReleasePropagationWork(work).work;
117
+ assertExpectedWorkRoot(current, expectedWorkRoot);
118
+ if (current.state.currentStage !== "complete" || current.state.lifecycle !== "ready") {
119
+ throw new Error("propagation work is not ready for completion");
120
+ }
121
+ const decision = normalizeTypedReference(completionDecision, {
122
+ kind: "decision",
123
+ status: "accepted",
124
+ factWorld: current.workControl.familyState.factWorld,
125
+ cutRoot: current.workControl.familyState.cutRoot,
126
+ label: "completionDecision",
127
+ });
128
+ const normalizedReceipt = normalizeStageReceipt(receipt, {
129
+ work: current,
130
+ workId: current.workId,
131
+ expectedWorkRoot: current.contentRoot,
132
+ expectedStage: "complete",
133
+ allowedOutcomes: new Set(["success"]),
134
+ });
135
+ if (!normalizedReceipt.evidence.some((entry) => entry.root === decision.root)) {
136
+ throw new Error("complete receipt must bind the accepted Work Control Decision root");
137
+ }
138
+ const successor = successorWork(current);
139
+ successor.stageReceipts.push(normalizedReceipt);
140
+ successor.completionDecision = decision;
141
+ successor.state.lifecycle = "complete";
142
+ successor.state.recoveryCursor.lastReceiptRoot = normalizedReceipt.receiptRoot;
143
+ successor.state.nextAction = nextWorkAction(successor, "none", "", "production-visible-and-work-control-accepted");
144
+ return verifyReleasePropagationWork(withWorkRoot(successor)).work;
145
+ }
@@ -0,0 +1,517 @@
1
+ import {
2
+ RELEASE_PROPAGATION_PLAN_CONTRACT,
3
+ assertPlainObject,
4
+ assertString,
5
+ normalizeChannel,
6
+ sha256Json,
7
+ } from "./release-propagation-common.js";
8
+ import { normalizeExecutionProfile } from "./release-propagation-execution-profile.js";
9
+ import {
10
+ normalizeUpstreamRelease,
11
+ verifyReleaseLockBinding,
12
+ } from "./release-propagation-release.js";
13
+ import { resolveWorkContext } from "./release-propagation-work-capture.js";
14
+ import { normalizeStageReceipt } from "./release-propagation-stage-evidence.js";
15
+ export { normalizeStageReceipt } from "./release-propagation-stage-evidence.js";
16
+ import {
17
+ assertCommitSha,
18
+ assertContentRoot,
19
+ assertExactFields,
20
+ contentRoot,
21
+ normalizeFamilyStateReference,
22
+ normalizeTypedReference,
23
+ normalizeWorkAuthority,
24
+ normalizeWorkRef,
25
+ } from "./release-propagation-work-control.js";
26
+ export {
27
+ RELEASE_PROPAGATION_STAGE_RECEIPT_CONTRACT,
28
+ RELEASE_PROPAGATION_WORK_CONTRACT,
29
+ RELEASE_PROPAGATION_WORK_STAGES,
30
+ } from "./release-propagation-work-constants.js";
31
+ import {
32
+ RELEASE_PROPAGATION_STAGE_RECEIPT_CONTRACT,
33
+ RELEASE_PROPAGATION_WORK_CONTRACT,
34
+ RELEASE_PROPAGATION_WORK_STAGES,
35
+ } from "./release-propagation-work-constants.js";
36
+
37
+ function selectPropagationTarget(plan, target) {
38
+ const selectedPlan = assertPlainObject(plan, "plan");
39
+ if (selectedPlan.contract !== RELEASE_PROPAGATION_PLAN_CONTRACT || selectedPlan.schemaVersion !== 1) {
40
+ throw new Error("plan must be a Buildchain release propagation plan v1");
41
+ }
42
+ if (!Array.isArray(selectedPlan.targets)) {
43
+ throw new Error("plan.targets must be an array");
44
+ }
45
+ const matches = selectedPlan.targets.filter((entry) =>
46
+ !target || entry.target === target || entry.repository === target);
47
+ if (matches.length !== 1) {
48
+ throw new Error(`expected exactly one propagation target, found ${matches.length}`);
49
+ }
50
+ const selected = matches[0];
51
+ const expectedLockSha = sha256Json({ ...selected.lock, lockSha256: undefined });
52
+ if (selected.lock?.lockSha256 !== expectedLockSha) {
53
+ throw new Error("propagation target release lock root does not verify");
54
+ }
55
+ const propagationKey = selected.propagationKey || selected.lock.propagation?.propagationKey || "";
56
+ if (!/^[0-9a-f]{64}$/.test(propagationKey)) {
57
+ throw new Error("propagation target key is not an exact SHA-256 digest");
58
+ }
59
+ if (selected.lock.propagation.propagationKey !== propagationKey) {
60
+ throw new Error("propagation target key disagrees with the exact release lock");
61
+ }
62
+ return { selectedPlan, selected, propagationKey };
63
+ }
64
+
65
+ function workCommands(executionProfile = null, repository = "", branch = "", baseRef = "") {
66
+ const profile = executionProfile || {};
67
+ const workflow = profile.workflow || "buildchain-web-surface.yml";
68
+ const releaseLabel = profile.productionReleaseLabel || "buildchain-release";
69
+ return {
70
+ plan: "buildchain release-propagation work status --work <work.json> --json",
71
+ status: "buildchain release-propagation work status --work <work.json> --json",
72
+ nextAction: "buildchain release-propagation work resume --work <work.json> --json",
73
+ resume: "buildchain release-propagation work resume --work <work.json> --json",
74
+ repair: "buildchain release-propagation work repair --work <work.json> --expected-work-root <sha256:...> --receipt <receipt.json> --output <successor.json> --json",
75
+ record: "buildchain release-propagation work record --work <work.json> --expected-work-root <sha256:...> --receipt <receipt.json> --output <successor.json> --json",
76
+ complete: "buildchain release-propagation work complete --work <work.json> --expected-work-root <sha256:...> --receipt <receipt.json> --completion-decision <reference.json> --output <successor.json> --json",
77
+ stages: {
78
+ materialize: profile.updateCommand || "<consumer update command from the exact execution profile>",
79
+ "verify-release": profile.verifyCommand || "<consumer verification command from the exact execution profile>",
80
+ "push-branch": `git push --force-with-lease=<exact-expected-old> origin HEAD:refs/heads/${branch || "<managed-branch>"}`,
81
+ "pull-request": `gh pr create --repo ${repository || "<downstream-repository>"} --base ${baseRef || "<base-ref>"} --head ${branch || "<managed-branch>"}`,
82
+ preview: `gh run list --repo ${repository || "<downstream-repository>"} --workflow ${workflow} --branch ${branch || "<managed-branch>"} --event pull_request --json databaseId,status,conclusion,url,headSha`,
83
+ "independent-review": `gh pr review <pr-number> --repo ${repository || "<downstream-repository>"} --approve`,
84
+ "protected-merge": `gh pr merge <pr-number> --repo ${repository || "<downstream-repository>"} --squash --delete-branch=false`,
85
+ staging: `gh run list --repo ${repository || "<downstream-repository>"} --workflow ${workflow} --branch ${baseRef || "<base-ref>"} --event push --json databaseId,status,conclusion,url,headSha`,
86
+ "production-release": `gh pr list --repo ${repository || "<downstream-repository>"} --state open --label ${releaseLabel} --json number,url,headRefName,baseRefName,headRefOid`,
87
+ "production-deploy": `gh run list --repo ${repository || "<downstream-repository>"} --workflow ${workflow} --event pull_request --json databaseId,status,conclusion,url,headSha`,
88
+ "online-readback": [profile.productionStatusUrl, ...(profile.readbackUrls || [])].filter(Boolean).map((url) => `curl --fail --location --silent --show-error ${url}`).join(" && "),
89
+ complete: "buildchain release-propagation work complete <exact receipt and accepted Work Control Decision>",
90
+ },
91
+ };
92
+ }
93
+
94
+ export function nextWorkAction(work, action, stage, reason = "") {
95
+ const command = action === "claim"
96
+ ? "buildchain release-propagation work claim --work <work.json> --family-state <family-state.json> --authority <authority.json> --expected-work-root <sha256:...> --output <successor.json> --json"
97
+ : action === "none"
98
+ ? ""
99
+ : action === "repair"
100
+ ? work.plan.commands.repair
101
+ : work.plan.commands.record;
102
+ return { action, stage, command, reason };
103
+ }
104
+
105
+ export function withWorkRoot(body) {
106
+ const value = structuredClone(body);
107
+ delete value.contentRoot;
108
+ return { ...value, contentRoot: contentRoot(value) };
109
+ }
110
+
111
+ export function createReleasePropagationWork({
112
+ plan,
113
+ target = "",
114
+ workContext,
115
+ expectedDownstreamBaseSha,
116
+ } = {}) {
117
+ const { selectedPlan, selected, propagationKey } = selectPropagationTarget(plan, target);
118
+ const context = resolveWorkContext(workContext, selectedPlan, selected, propagationKey);
119
+ const parentWorkRef = normalizeWorkRef(context.parentWorkRef, "workContext.parentWorkRef");
120
+ const childWorkRef = normalizeWorkRef(context.childWorkRef, "workContext.childWorkRef");
121
+ if (childWorkRef.object_kind !== "assignment") {
122
+ throw new Error("workContext.childWorkRef must identify an Assignment");
123
+ }
124
+ if (parentWorkRef.subject === childWorkRef.subject
125
+ && parentWorkRef.workspace_identity_root === childWorkRef.workspace_identity_root) {
126
+ throw new Error("parent and child WorkRefs must identify different work");
127
+ }
128
+ const familyState = context.bindingState === "bound"
129
+ ? normalizeFamilyStateReference(context.familyState)
130
+ : null;
131
+ const authority = normalizeWorkAuthority(context.authority, familyState);
132
+ const supersedesWorkRoot = assertContentRoot(
133
+ context.supersedesWorkRoot,
134
+ "workContext.supersedesWorkRoot",
135
+ { optional: true },
136
+ );
137
+ const identity = {
138
+ propagationKey,
139
+ parentWorkRef,
140
+ childWorkRef,
141
+ };
142
+ const identityRoot = contentRoot(identity);
143
+ const workId = `propagation-${propagationKey.slice(0, 16)}-${identityRoot.slice(-12)}`;
144
+ if (!selected.executionProfile) {
145
+ throw new Error("agent-native propagation work requires a downstream execution profile");
146
+ }
147
+ const commands = workCommands(
148
+ selected.executionProfile,
149
+ selected.repository,
150
+ selected.branch,
151
+ selected.baseRef,
152
+ );
153
+ const initial = {
154
+ schemaVersion: 1,
155
+ contract: RELEASE_PROPAGATION_WORK_CONTRACT,
156
+ workId,
157
+ revision: 0,
158
+ previousWorkRoot: "",
159
+ propagationKey,
160
+ upstream: {
161
+ releaseRoot: contentRoot(selectedPlan.upstreamRelease),
162
+ release: structuredClone(selectedPlan.upstreamRelease),
163
+ },
164
+ downstream: {
165
+ target: selected.target,
166
+ repository: selected.repository,
167
+ channel: selected.channel,
168
+ baseRef: selected.baseRef,
169
+ expectedBaseSha: assertCommitSha(expectedDownstreamBaseSha, "expectedDownstreamBaseSha"),
170
+ branch: selected.branch,
171
+ lockPath: selected.lockPath,
172
+ lockSha256: selected.lock.lockSha256,
173
+ releaseLock: structuredClone(selected.lock),
174
+ executionProfile: structuredClone(selected.executionProfile),
175
+ },
176
+ workControl: {
177
+ bindingState: context.bindingState,
178
+ parentWorkRef,
179
+ childWorkRef,
180
+ familyState,
181
+ },
182
+ intent: { publishToProduction: authority.publishToProduction },
183
+ authority,
184
+ deduplication: {
185
+ identityRoot,
186
+ propagationKey,
187
+ supersedesWorkRoot,
188
+ policy: "exact-identity-reuse-newer-release-explicit-supersession",
189
+ },
190
+ plan: {
191
+ stages: [...RELEASE_PROPAGATION_WORK_STAGES],
192
+ commands,
193
+ },
194
+ state: {
195
+ lifecycle: authority.mode === "execute" ? "ready" : "paused",
196
+ currentStage: RELEASE_PROPAGATION_WORK_STAGES[0],
197
+ nextAction: null,
198
+ recoveryCursor: {
199
+ stage: RELEASE_PROPAGATION_WORK_STAGES[0],
200
+ index: 0,
201
+ attempt: 1,
202
+ lastReceiptRoot: "",
203
+ },
204
+ },
205
+ stageReceipts: [],
206
+ completionDecision: null,
207
+ };
208
+ initial.state.nextAction = authority.mode === "execute"
209
+ ? nextWorkAction(initial, "record", initial.state.currentStage)
210
+ : nextWorkAction(initial, "claim", initial.state.currentStage, "execution-authority-required");
211
+ return verifyReleasePropagationWork(withWorkRoot(initial)).work;
212
+ }
213
+
214
+ export function createReleasePropagationStageReceipt({
215
+ work,
216
+ stage,
217
+ outcome = "success",
218
+ observedAt,
219
+ actor,
220
+ summary,
221
+ evidence,
222
+ failure = null,
223
+ } = {}) {
224
+ const verification = verifyReleasePropagationWork(work);
225
+ const body = {
226
+ schemaVersion: 1,
227
+ contract: RELEASE_PROPAGATION_STAGE_RECEIPT_CONTRACT,
228
+ workId: verification.work.workId,
229
+ expectedWorkRoot: verification.work.contentRoot,
230
+ stage: stage || verification.currentStage,
231
+ outcome,
232
+ attempt: verification.work.state.recoveryCursor.attempt,
233
+ observedAt,
234
+ actor,
235
+ summary,
236
+ evidence,
237
+ failure,
238
+ };
239
+ const receipt = { ...body, receiptRoot: contentRoot(body) };
240
+ return normalizeStageReceipt(receipt, {
241
+ work: verification.work,
242
+ workId: verification.work.workId,
243
+ expectedWorkRoot: verification.work.contentRoot,
244
+ expectedStage: verification.currentStage,
245
+ allowedOutcomes: new Set(["success", "failure", "repair"]),
246
+ });
247
+ }
248
+
249
+ export function assertExpectedWorkRoot(work, expectedWorkRoot) {
250
+ const expected = assertContentRoot(expectedWorkRoot, "expectedWorkRoot");
251
+ if (work.contentRoot !== expected) {
252
+ throw new Error("propagation work changed before transition");
253
+ }
254
+ }
255
+
256
+ export function successorWork(work) {
257
+ const successor = structuredClone(work);
258
+ successor.revision += 1;
259
+ successor.previousWorkRoot = work.contentRoot;
260
+ delete successor.contentRoot;
261
+ return successor;
262
+ }
263
+
264
+ function verifyWorkHeader(work) {
265
+ if (work.schemaVersion !== 1 || work.contract !== RELEASE_PROPAGATION_WORK_CONTRACT) {
266
+ throw new Error("release propagation work must use the v1 contract");
267
+ }
268
+ if (!Number.isInteger(work.revision) || work.revision < 0) {
269
+ throw new Error("release propagation work revision must be a non-negative integer");
270
+ }
271
+ assertContentRoot(work.previousWorkRoot, "release propagation work previousWorkRoot", {
272
+ optional: work.revision === 0,
273
+ });
274
+ if (work.revision === 0 && work.previousWorkRoot !== "") {
275
+ throw new Error("initial propagation work cannot name a previous root");
276
+ }
277
+ if (!/^[0-9a-f]{64}$/.test(work.propagationKey)) {
278
+ throw new Error("release propagation work key must be an exact SHA-256 digest");
279
+ }
280
+ }
281
+
282
+ function verifyWorkRelease(work) {
283
+ const upstream = assertExactFields(
284
+ work.upstream,
285
+ ["releaseRoot", "release"],
286
+ "release propagation work upstream",
287
+ );
288
+ const normalizedRelease = normalizeUpstreamRelease(upstream.release);
289
+ if (upstream.releaseRoot !== contentRoot(normalizedRelease)) {
290
+ throw new Error("release propagation work upstream root does not verify");
291
+ }
292
+ const downstream = assertExactFields(work.downstream, [
293
+ "target", "repository", "channel", "baseRef", "expectedBaseSha", "branch",
294
+ "lockPath", "lockSha256", "releaseLock", "executionProfile",
295
+ ], "release propagation work downstream");
296
+ assertString(downstream.target, "release propagation work downstream.target");
297
+ assertString(downstream.repository, "release propagation work downstream.repository");
298
+ normalizeChannel(downstream.channel, "release propagation work downstream.channel");
299
+ assertString(downstream.baseRef, "release propagation work downstream.baseRef");
300
+ assertCommitSha(downstream.expectedBaseSha, "release propagation work downstream.expectedBaseSha");
301
+ assertString(downstream.branch, "release propagation work downstream.branch");
302
+ assertString(downstream.lockPath, "release propagation work downstream.lockPath");
303
+ if (downstream.lockSha256 !== sha256Json({ ...downstream.releaseLock, lockSha256: undefined })) {
304
+ throw new Error("release propagation work downstream lock root does not verify");
305
+ }
306
+ if (downstream.releaseLock.lockSha256 !== downstream.lockSha256
307
+ || downstream.releaseLock.propagation.propagationKey !== work.propagationKey) {
308
+ throw new Error("release propagation work downstream lock identity disagrees with the work");
309
+ }
310
+ verifyReleaseLockBinding({
311
+ release: normalizedRelease,
312
+ lock: downstream.releaseLock,
313
+ downstream,
314
+ propagationKey: work.propagationKey,
315
+ });
316
+ const profile = normalizeExecutionProfile(
317
+ downstream.executionProfile,
318
+ "release propagation work downstream.executionProfile",
319
+ );
320
+ if (!profile
321
+ || JSON.stringify(profile) !== JSON.stringify(downstream.releaseLock.downstream.executionProfile)) {
322
+ throw new Error("release propagation work execution profile disagrees with the exact release lock");
323
+ }
324
+ return { downstream, profile };
325
+ }
326
+
327
+ function verifyWorkControl(work) {
328
+ const workControl = assertExactFields(
329
+ work.workControl,
330
+ ["bindingState", "parentWorkRef", "childWorkRef", "familyState"],
331
+ "release propagation work Work Control",
332
+ );
333
+ const parentWorkRef = normalizeWorkRef(
334
+ workControl.parentWorkRef,
335
+ "release propagation work parentWorkRef",
336
+ );
337
+ const childWorkRef = normalizeWorkRef(
338
+ workControl.childWorkRef,
339
+ "release propagation work childWorkRef",
340
+ );
341
+ if (!new Set(["pending", "bound"]).has(workControl.bindingState)) {
342
+ throw new Error("release propagation work binding state is unsupported");
343
+ }
344
+ const familyState = workControl.bindingState === "bound"
345
+ ? normalizeFamilyStateReference(workControl.familyState)
346
+ : null;
347
+ if (workControl.bindingState === "pending" && workControl.familyState !== null) {
348
+ throw new Error("pending propagation work cannot claim a Family State");
349
+ }
350
+ const authority = normalizeWorkAuthority(work.authority, familyState);
351
+ if (workControl.bindingState === "pending" && authority.mode !== "capture-only") {
352
+ throw new Error("pending propagation work must remain capture-only");
353
+ }
354
+ const intent = assertExactFields(
355
+ work.intent,
356
+ ["publishToProduction"],
357
+ "release propagation work intent",
358
+ );
359
+ if (intent.publishToProduction !== authority.publishToProduction) {
360
+ throw new Error("release propagation work intent disagrees with its authority");
361
+ }
362
+ const identityRoot = contentRoot({
363
+ propagationKey: work.propagationKey,
364
+ parentWorkRef,
365
+ childWorkRef,
366
+ });
367
+ const deduplication = assertExactFields(work.deduplication, [
368
+ "identityRoot", "propagationKey", "supersedesWorkRoot", "policy",
369
+ ], "release propagation work deduplication");
370
+ if (deduplication.identityRoot !== identityRoot
371
+ || deduplication.propagationKey !== work.propagationKey) {
372
+ throw new Error("release propagation work deterministic identity does not verify");
373
+ }
374
+ assertContentRoot(
375
+ deduplication.supersedesWorkRoot,
376
+ "release propagation work supersedesWorkRoot",
377
+ { optional: true },
378
+ );
379
+ if (deduplication.policy !== "exact-identity-reuse-newer-release-explicit-supersession") {
380
+ throw new Error("release propagation work supersession policy is unsupported");
381
+ }
382
+ if (work.workId !== `propagation-${work.propagationKey.slice(0, 16)}-${identityRoot.slice(-12)}`) {
383
+ throw new Error("release propagation work id is not deterministic");
384
+ }
385
+ return familyState;
386
+ }
387
+
388
+ function verifyWorkPlanAndState(work, downstream, profile) {
389
+ const plan = assertExactFields(
390
+ work.plan,
391
+ ["stages", "commands"],
392
+ "release propagation work plan",
393
+ );
394
+ if (JSON.stringify(plan.stages) !== JSON.stringify(RELEASE_PROPAGATION_WORK_STAGES)) {
395
+ throw new Error("release propagation work stage order is not canonical");
396
+ }
397
+ const expectedCommands = workCommands(
398
+ profile,
399
+ downstream.repository,
400
+ downstream.branch,
401
+ downstream.baseRef,
402
+ );
403
+ if (JSON.stringify(plan.commands) !== JSON.stringify(expectedCommands)) {
404
+ throw new Error("release propagation work commands are not canonical");
405
+ }
406
+ const state = assertExactFields(
407
+ work.state,
408
+ ["lifecycle", "currentStage", "nextAction", "recoveryCursor"],
409
+ "release propagation work state",
410
+ );
411
+ const cursor = assertExactFields(
412
+ state.recoveryCursor,
413
+ ["stage", "index", "attempt", "lastReceiptRoot"],
414
+ "release propagation work recovery cursor",
415
+ );
416
+ if (cursor.stage !== state.currentStage
417
+ || RELEASE_PROPAGATION_WORK_STAGES[cursor.index] !== state.currentStage) {
418
+ throw new Error("release propagation work recovery cursor is inconsistent");
419
+ }
420
+ if (!Number.isInteger(cursor.attempt) || cursor.attempt < 1) {
421
+ throw new Error("release propagation work recovery attempt must be positive");
422
+ }
423
+ assertContentRoot(
424
+ cursor.lastReceiptRoot,
425
+ "release propagation work lastReceiptRoot",
426
+ { optional: true },
427
+ );
428
+ return { state, cursor };
429
+ }
430
+
431
+ function verifyWorkReceipts(work, state, cursor, familyState) {
432
+ if (!Array.isArray(work.stageReceipts)) {
433
+ throw new Error("release propagation work stageReceipts must be an array");
434
+ }
435
+ for (const receipt of work.stageReceipts) {
436
+ normalizeStageReceipt(receipt, {
437
+ work,
438
+ workId: work.workId,
439
+ expectedWorkRoot: receipt.expectedWorkRoot,
440
+ expectedStage: receipt.stage,
441
+ allowedOutcomes: new Set(["success", "failure", "repair"]),
442
+ });
443
+ }
444
+ const lastReceipt = work.stageReceipts.at(-1);
445
+ if (lastReceipt) {
446
+ if (lastReceipt.expectedWorkRoot !== work.previousWorkRoot) {
447
+ throw new Error("latest stage receipt does not bind the predecessor work root");
448
+ }
449
+ if (cursor.lastReceiptRoot !== lastReceipt.receiptRoot) {
450
+ throw new Error("recovery cursor does not bind the latest stage receipt");
451
+ }
452
+ } else if (cursor.lastReceiptRoot !== "") {
453
+ throw new Error("recovery cursor cannot name a receipt before any transition");
454
+ }
455
+ const successfulStages = work.stageReceipts
456
+ .filter((receipt) => receipt.outcome === "success")
457
+ .map((receipt) => receipt.stage);
458
+ const expectedSuccessful = RELEASE_PROPAGATION_WORK_STAGES.slice(
459
+ 0,
460
+ state.lifecycle === "complete" ? -1 : cursor.index,
461
+ );
462
+ if (state.lifecycle === "complete") expectedSuccessful.push("complete");
463
+ if (JSON.stringify(successfulStages) !== JSON.stringify(expectedSuccessful)) {
464
+ throw new Error("release propagation work successful receipts do not match canonical stage order");
465
+ }
466
+ if (state.lifecycle === "complete") {
467
+ if (!familyState) {
468
+ throw new Error("complete propagation work requires a bound Family State");
469
+ }
470
+ normalizeTypedReference(work.completionDecision, {
471
+ kind: "decision",
472
+ status: "accepted",
473
+ factWorld: familyState.factWorld,
474
+ cutRoot: familyState.cutRoot,
475
+ label: "completionDecision",
476
+ });
477
+ } else if (work.completionDecision !== null) {
478
+ throw new Error("unfinished propagation work cannot carry a completion decision");
479
+ }
480
+ }
481
+
482
+ export function verifyReleasePropagationWork(input) {
483
+ const work = assertExactFields(input, [
484
+ "schemaVersion", "contract", "workId", "revision", "previousWorkRoot",
485
+ "propagationKey", "upstream", "downstream", "workControl", "intent",
486
+ "authority", "deduplication", "plan", "state", "stageReceipts",
487
+ "completionDecision", "contentRoot",
488
+ ], "release propagation work");
489
+ verifyWorkHeader(work);
490
+ const { downstream, profile } = verifyWorkRelease(work);
491
+ const familyState = verifyWorkControl(work);
492
+ const { state, cursor } = verifyWorkPlanAndState(work, downstream, profile);
493
+ verifyWorkReceipts(work, state, cursor, familyState);
494
+ const rootBody = structuredClone(work);
495
+ delete rootBody.contentRoot;
496
+ if (work.contentRoot !== contentRoot(rootBody)) {
497
+ throw new Error("release propagation work content root does not verify");
498
+ }
499
+ return {
500
+ ok: true,
501
+ contract: RELEASE_PROPAGATION_WORK_CONTRACT,
502
+ work,
503
+ workId: work.workId,
504
+ contentRoot: work.contentRoot,
505
+ lifecycle: state.lifecycle,
506
+ currentStage: state.currentStage,
507
+ nextAction: state.nextAction,
508
+ recoveryCursor: state.recoveryCursor,
509
+ productionVisible: state.lifecycle === "complete",
510
+ completionQualified: state.lifecycle === "complete" && work.completionDecision !== null,
511
+ };
512
+ }
513
+
514
+ export function resumeReleasePropagationWork(work) {
515
+ const status = verifyReleasePropagationWork(work);
516
+ return { ...status, resumed: true };
517
+ }