@kungfu-tech/buildchain 4.0.2-alpha.11 → 4.0.2-alpha.13

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.
@@ -84,6 +84,7 @@ Safe change route: Reduce one owned responsibility behind the stable facade, run
84
84
  - sources: `scripts/run-lifecycle-core.mjs`
85
85
  - sources: `scripts/stable-candidate-patrol.mjs`
86
86
  - sources: `scripts/stable-release-gate.mjs`
87
+ - sources: `scripts/v4-universal-workflow-engine.mjs`
87
88
  - sources: `scripts/v4-architecture.mjs`
88
89
  - sources: `scripts/v4-warrant-shadow-plan.mjs`
89
90
  - sources: `scripts/web-surface-core.mjs`
@@ -152,12 +153,15 @@ Safe change route: Reduce one owned responsibility behind the stable facade, run
152
153
 
153
154
  - `.github/workflows/bootstrap.yml`
154
155
  - `scripts/check-v4-universal-workflow-bootstrap.mjs`
156
+ - `scripts/v4-universal-workflow-engine.mjs`
157
+ - `tests/v4-universal-workflow-bootstrap.test.mjs`
155
158
  - `tests/v3-v4-capability-inventory.test.mjs`
156
159
  - `tests/v4-floating-consumer-policy-contract.test.mjs`
157
160
  - `tests/v4-publication-qualification.test.mjs`
158
161
  - `.github/workflows/v4-adopter-delivery.yml`
159
162
  - `.github/workflows/buildchain-ref-promotion.yml`
160
163
  - `tests/release-candidate-recovery.test.mjs`
164
+ - `tests/buildchain-ref-promotion-recovery-entry.test.mjs`
161
165
  - `.github/workflows/.release-candidate-promote.yml`
162
166
  - `tests/build-surface-promotion.test.mjs`
163
167
  - `.github/workflows/buildchain-ref-promotion-recovery.yml`
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "totalExcessLedger": {
9
9
  "metric": "calibrated-full-dimensional-excess-v1",
10
- "measuredExcess": 64347,
11
- "current": 56286
10
+ "measuredExcess": 64361,
11
+ "current": 56300
12
12
  },
13
13
  "defaults": {
14
14
  "owner": "Buildchain architecture maintainers",
@@ -1425,7 +1425,7 @@
1425
1425
  "evaluatePullRequest@204": {
1426
1426
  "complexity": 31
1427
1427
  },
1428
- "runDevPrAdmission@764": {
1428
+ "runDevPrAdmission@767": {
1429
1429
  "complexity": 32
1430
1430
  },
1431
1431
  "runDevPrAutoMerge@909": {
@@ -1440,7 +1440,7 @@
1440
1440
  "evaluatePullRequest@204": {
1441
1441
  "complexity": 31
1442
1442
  },
1443
- "runDevPrAdmission@764": {
1443
+ "runDevPrAdmission@767": {
1444
1444
  "complexity": 32
1445
1445
  },
1446
1446
  "runDevPrAutoMerge@909": {
@@ -1455,7 +1455,7 @@
1455
1455
  "evaluatePullRequest@204": {
1456
1456
  "complexity": 25
1457
1457
  },
1458
- "runDevPrAdmission@764": {
1458
+ "runDevPrAdmission@767": {
1459
1459
  "complexity": 25
1460
1460
  },
1461
1461
  "runDevPrAutoMerge@909": {
@@ -1804,6 +1804,17 @@
1804
1804
  }
1805
1805
  }
1806
1806
  },
1807
+ "scripts/v4-universal-workflow-engine.mjs": {
1808
+ "baseline": {
1809
+ "lines": 620
1810
+ },
1811
+ "current": {
1812
+ "lines": 616
1813
+ },
1814
+ "target": {
1815
+ "lines": 600
1816
+ }
1817
+ },
1807
1818
  "scripts/v4-architecture.mjs": {
1808
1819
  "baseline": {
1809
1820
  "lines": 610
@@ -2373,7 +2384,7 @@
2373
2384
  "lines": 626
2374
2385
  },
2375
2386
  "current": {
2376
- "lines": 626
2387
+ "lines": 624
2377
2388
  },
2378
2389
  "target": {
2379
2390
  "lines": 600
@@ -3085,12 +3096,15 @@
3085
3096
  "hotspots": [
3086
3097
  ".github/workflows/bootstrap.yml",
3087
3098
  "scripts/check-v4-universal-workflow-bootstrap.mjs",
3099
+ "scripts/v4-universal-workflow-engine.mjs",
3100
+ "tests/v4-universal-workflow-bootstrap.test.mjs",
3088
3101
  "tests/v3-v4-capability-inventory.test.mjs",
3089
3102
  "tests/v4-floating-consumer-policy-contract.test.mjs",
3090
3103
  "tests/v4-publication-qualification.test.mjs",
3091
3104
  ".github/workflows/v4-adopter-delivery.yml",
3092
3105
  ".github/workflows/buildchain-ref-promotion.yml",
3093
3106
  "tests/release-candidate-recovery.test.mjs",
3107
+ "tests/buildchain-ref-promotion-recovery-entry.test.mjs",
3094
3108
  ".github/workflows/.release-candidate-promote.yml",
3095
3109
  "tests/build-surface-promotion.test.mjs",
3096
3110
  ".github/workflows/buildchain-ref-promotion-recovery.yml",
@@ -13,10 +13,10 @@
13
13
  "rationale": "Preserve the accepted 66,712 governance ledger at the exact protected Wave 4 cut while charging every subsequent source, Rust, test, and workflow line, function, job, step, and decision excess one-for-one. The fixed calibration offset cannot hide new debt because both the measured ledger and calibrated total are recomputed from the complete exact debt map."
14
14
  },
15
15
  "exceptionBudget": {
16
- "maxTotal": 94,
16
+ "maxTotal": 95,
17
17
  "maxByCollection": {
18
18
  "approvedExistingDebtTransitions": 19,
19
- "approvedNewFileTransitions": 21,
19
+ "approvedNewFileTransitions": 22,
20
20
  "approvedExtractedDebt": 20,
21
21
  "approvedPublicSurfaceTransitions": 19,
22
22
  "approvedTestDebtTransitions": 9,
@@ -27,11 +27,11 @@
27
27
  "repositoryBudgets": {
28
28
  "baselineRevision": "858dbe00b6206a8f356b4a12d2659bee7a72e71b",
29
29
  "maxHandMaintainedSourceFiles": 462,
30
- "maxHandMaintainedSourceLines": 170241,
30
+ "maxHandMaintainedSourceLines": 170347,
31
31
  "maxWorkflowFiles": 80,
32
32
  "maxWorkflowLines": 26791,
33
- "latestTransition": "The canonical Buildchain publisher accepts one exact-candidate request and materializes the rooted product intent before APPLY.",
34
- "latestTransitionRationale": "The canonical publisher adds one mutually exclusive Bootstrap delegation instead of another typed release adapter, so the existing compatibility path remains unchanged while Train discovery is locked to an independently reviewed exact SHA before execution. The exact candidate engine now materializes the existing v4 product-publication intent from provider readback and passes that root into the sole APPLY action; it does not add a second provider, publication engine, or mutation boundary. The exact inventory contains 462 hand-maintained source files and 170241 lines; the ceiling binds the measured request guard, intent wiring, lane declaration, durable receipt retention, and recurrence tests rather than hiding their cost in debt or exceptions.",
33
+ "latestTransition": "The proven r31 publication runtime is absorbed into protected dev without replacing later dev work.",
34
+ "latestTransitionRationale": "The linear Project Cut absorbs the complete reviewed Train r31 runtime delta while preserving the current protected dev tree, including later Warrant readback and protected product-finalization repairs; the immutable r31 SHA remains the evidence and publication-engine coordinate. The combined runtime keeps the single admitted universal engine, clean-consumer dependency bridge, source-bound recovery version, rooted result channel, and protected finalization ancestry without adding a second publisher, mutation authority, or release engine. The exact inventory contains 462 hand-maintained source files and 170347 lines; the ceiling binds the complete combined tree rather than hiding merge-time debt.",
35
35
  "rationale": "Bounded modules separate candidate, evidence, native proof and execution, canonical failure verification, closed-set execution transfer, process-boundary verification, Warrant state and qualification, authority state, qualification, Landing acquisition, verified-attempt admission, Landing settlement and fence validation, provider attempt and heartbeat readback, sealed terminal evidence, hosted heartbeat, resume, store, options, command-specific adapters, and CLI orchestration responsibilities. The protected delivery workflow gives candidate and seal jobs no provider write credential, recursively rejects transfer drift, scopes every transfer artifact to the exact provider run attempt, chains every durable heartbeat state and receipt root on a distinct hosted runner domain, and permits qualification or exact failure settlement only after the finalizer verifies live jobs and the latest durable authority state. Public Landing admission and heartbeat always perform non-injectable fresh exact-attempt provider readback, tolerate only a not-yet-materialized seal job while retaining exact native and duplicate-job rejection, and heartbeat-loss coordination never invokes GitHub's run-scoped cancellation API where a successor rerun could race. The hosted process boundary anchors trust in the kernel-resolved versioned Runner.Worker path while accepting an equivalent argv-zero spelling with the same basename; its public boundary marker is allowed only at the one exact non-secret value, so a forged value under the same credential-like name still fails closed. The credentialless transfer seal validates the rooted native reuse decision against the Warrant-bound source, closure, dependency, toolchain, environment and command inputs; qualifiedBase stays in the proof and decision where it is rooted and later independently rechecked, rather than being assumed on the Warrant schema. V2 mutation cannot initialize a second empty authority beside a legacy ref: migration reads and roots the canonical live v1 ref, rechecks it, applies the explicit configured v2 policy, and atomically replaces its bytes through one expected-old non-force update, so old v1 controllers fail closed while the prior commit remains immutable history. Candidate ancestry classifies the GitHub artifact runtime token as credential authority. Every Landing settlement, including an unexpired fence, requires product-owned terminal readback. Terminal cleanup reads the immutable historical attempt, derives workflow and hosted Landing job identity from live run data, reads the exact protected ref head and compares containment against that immutable SHA, and seals merged only when the admitted merge-group head is contained by that protected base; a later different-head merge settles the old attempt as dequeued. Terminal normalization preserves transfer, finalizer boundary, native and seal job, admitted provider attempt, protected-base head, containment result, and terminal readback coordinates. The public reusable workflow truthfully retains its single-flight v1 authority without advertising v2 migration, while bounded v2 remains a separate opt-in CLI, Node API, and schema contract; the tracked self-caller stays on public v4-alpha with matching stable and alpha locks, and transient train selection is dispatch-only. Next-development parity adds one versioned transition authority, one durable controller, one bounded reservation adapter, and pure public-contract recovery evidence while retaining the existing thin floating-channel callers byte-for-byte. The floating-consumer gate reuses the existing contract-world evaluator so SHA drift passes only under the selected lock's compatibility policy, breaking surface drift still fails closed, and external certification reconstructs the receipt from the observed workflow SHA rather than the lock's historical evidence SHA. Tail reseal adds no Stage Capsule effect or general rebuild authority: it verifies the exact retained four-platform bytes, admits one known macOS tail failure through live provider readback, fences signing and finalization to explicit credentials and Warrant identity, and requires standard candidate Passport plus provider and protected readback. Compatibility Facts parity isolates canonical encoding, temporal verification, lifecycle validation, fixture projection, public authority projection, and CLI adaptation into bounded modules; legacy digest and proof arrays are derived only from exact current Fact lineage. Publication Rehearsal adds no production release authority: the content-addressed capsule contract binds source, candidate, manifest, config, canonical data-only provider bindings, provider policy, observations and core version separately from filesystem, runtime and rehearsal-only provider authority; simulate, replay and explicitly authorized provider rehearsal all reuse the release-tail planner, while same-commit local dogfood resolves the new reusable contract and exact current runtime before external callers remain on floating v4-alpha. Cross-runtime candidate recovery preserves original runtime and provider roots and creates resume lineage only after exact public ref, tag, ancestry, npm version and integrity readback. Adopter Delivery restores the reviewed public v3 gate, drivers, Passport binding, N-1 bootstrap and exact archive loader through one public v4 surface while keeping provider and release authority explicit non-claims. Floating Alpha bootstrap uses only the existing exact resolver and protected Alpha private shell to move an otherwise stale floating shell, retains public v4 stable promotion, and removes itself after protected readback. CI lane change-budget enforcement adds a bounded parser, evaluator, guard, and tests without creating another merge authority.",
36
36
  "governance": "legacy-debt-sunset-2026q4"
37
37
  },
@@ -144,6 +144,11 @@
144
144
  "rationale": "The isolated source scanner rejects persisted exact, train, authority, variable, secret, and environment selector authority while inventorying OIDC, IAM, and external authority metadata without reading credentials; this exact ceiling cannot grow implicitly.",
145
145
  "governance": "legacy-debt-sunset-2026q4"
146
146
  },
147
+ "scripts/v4-universal-workflow-engine.mjs": {
148
+ "maxLines": 620,
149
+ "rationale": "The exact candidate engine adds only a fail-closed OIDC preflight and a fixed npm 11.13.0 trusted-publishing shim under the already admitted ephemeral runtime path. This lets a reviewed Train candidate execute through an older floating Alpha Bootstrap shell without broadening capability, permission, provider, or mutation authority; this exact live-repair ceiling cannot grow implicitly.",
150
+ "governance": "legacy-debt-sunset-2026q4"
151
+ },
147
152
  "scripts/resume-from-candidate-run.mjs": {
148
153
  "maxLines": 1125,
149
154
  "rationale": "The candidate recovery adapter retains the existing artifact normalization and immutable recovery receipt boundary while adding fresh authorized cross-runtime Stage Capsule resume material, post-publication ref, tag, ancestry, and npm readback, and rooted Release Passport lineage. Its main orchestration remains below the default function length and complexity limits, and this exact file ceiling cannot grow implicitly.",
@@ -1851,8 +1856,8 @@
1851
1856
  "governance": "stage1-test-split-net-reduction"
1852
1857
  },
1853
1858
  "tests/dev-delivery-warrant-command.test.mjs": {
1854
- "maxLines": 626,
1855
- "rationale": "Twelve exact workflow assertions bind the newly public successor-wake output and fail closed unless the terminal controller dispatches only a successful non-null rooted wake. The existing command-store and provider fixtures remain unchanged, and this exact 626-line ceiling cannot grow implicitly.",
1859
+ "maxLines": 624,
1860
+ "rationale": "Exact command-store assertions cover raw GitHub media reads and JSON-envelope fallback for persisted Dev Delivery state. The test surface has reduced to an exact 624-line ceiling and cannot grow implicitly.",
1856
1861
  "governance": "legacy-debt-sunset-2026q4"
1857
1862
  },
1858
1863
  "tests/build-surface-reusable-build.test.mjs": {
@@ -23,11 +23,11 @@
23
23
  "statuses": "write"
24
24
  },
25
25
  "contractRoots": [
26
- "sha256:3e5ec3bb803169de0b64747cbc1e5bbff536a8409d7ba1a6528f2930bc294a5c",
26
+ "sha256:51792fe2928fe9851f717728f4820b70029a52d04804246d87242e9e482fbb18",
27
27
  "sha256:60fc0802b7e46cb4460264b8b7359d06b2406d66a9d805d0d076bb692f80b35e",
28
28
  "sha256:65a0c2fc3faecaddf5076fff086b5d6e12eb9ba27796efc10aed19f2d93b9457",
29
- "sha256:93cd47a50798199fcb778e90b338542ab58842392da11cc4f8914525e98790be",
30
- "sha256:a31570dd0f1a85dae851a9acd3731a60967c474cd09615a11d6d8d10c6f9a78b",
29
+ "sha256:7ca5dffff9eaff8b9265d35c7d8573b8cedd56a69dc26c12e4a5c26e723412b0",
30
+ "sha256:83d6b141d3c60ac5b861029a7c6241ee0ca8e6674dd346d27d476151dd16ea6c",
31
31
  "sha256:b6737ed958e8f53f7770b1c214c092591573ad63af41e05797117d9b7b212ed8",
32
32
  "sha256:d6ad9cf661c0ba3825cabdc7185fd122a60fc1929dfc799cda3352650588663f"
33
33
  ],
@@ -4,7 +4,7 @@
4
4
  "product": {
5
5
  "name": "Buildchain",
6
6
  "package": "@kungfu-tech/buildchain",
7
- "version": "4.0.2-alpha.11",
7
+ "version": "4.0.2-alpha.13",
8
8
  "repository": "https://github.com/kungfu-systems/buildchain"
9
9
  },
10
10
  "majorLine": "v4",
@@ -5444,5 +5444,5 @@
5444
5444
  "compatibilityFactCutRoot": "sha256:f61b975d4a96c921cc4bd37fb3ed9a0596b47004fe34dc7219979d697e3707f9",
5445
5445
  "compatibilityProofRegistryRoot": "sha256:b7194b811e4d3af2df4390bc4d1731e16a86215bd64ee18cb630dd5492f4fb63",
5446
5446
  "compatibilityDigest": "sha256:20411029d83211dff426fb6535a608902129844a5ccb4788db77d3f81becb839",
5447
- "contractDigest": "sha256:da026f0e9a8b265b9876cc9dd74f11fe197c49970953c1d2abb68d110b650acf"
5447
+ "contractDigest": "sha256:9d7303e2f34f325fa0e924eddc44594d94bf2a8b345c6c4c3a095f8e77d63c4e"
5448
5448
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-bundle",
4
- "generatedAt": "2026-08-31T21:12:16.000Z",
5
- "publishedAt": "2026-08-31T21:12:16.000Z",
4
+ "generatedAt": "2026-09-01T14:47:21.000Z",
5
+ "publishedAt": "2026-09-01T14:47:21.000Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "f6ad53d9db073a6940b0cc08a80f0fcd1b3f14fb",
22
+ "sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "4.0.2-alpha.11",
40
+ "version": "4.0.2-alpha.13",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "source": {
@@ -21,7 +21,7 @@
21
21
  "contract": "kungfu-buildchain-public-surface-reverse-audit",
22
22
  "path": "dist/site/public-surface-audit.json",
23
23
  "status": "passed",
24
- "sha256": "1cad80fe47700d775ca40e02480e6bf1549329230ad32650825063520ace76c1",
24
+ "sha256": "87647050443c07907370fc052a89fdd0370afbda0178de586e60c298991f6f54",
25
25
  "summary": {
26
26
  "cliCommandCount": 117,
27
27
  "workflowCount": 80,
@@ -246,7 +246,7 @@
246
246
  "contract": "kungfu-buildchain-public-surface-reverse-audit",
247
247
  "path": "dist/site/public-surface-audit.json",
248
248
  "status": "passed",
249
- "sha256": "1cad80fe47700d775ca40e02480e6bf1549329230ad32650825063520ace76c1",
249
+ "sha256": "87647050443c07907370fc052a89fdd0370afbda0178de586e60c298991f6f54",
250
250
  "summary": {
251
251
  "cliCommandCount": 117,
252
252
  "workflowCount": 80,
@@ -4,7 +4,7 @@
4
4
  "product": {
5
5
  "id": "kungfu-tech-buildchain",
6
6
  "name": "@kungfu-tech/buildchain",
7
- "version": "4.0.2-alpha.11",
7
+ "version": "4.0.2-alpha.13",
8
8
  "repository": "https://github.com/kungfu-systems/buildchain"
9
9
  },
10
10
  "source": {
@@ -9035,7 +9035,7 @@
9035
9035
  "workflowRegistryPath": "dist/site/workflow-registry.json",
9036
9036
  "pageRegistryPath": "dist/site/page-registry.json",
9037
9037
  "cliRegistryDigest": "fbc23073bbae240489a2f826b2c26daadc58e704050c5f8df366509f5c5a1c3e",
9038
- "workflowRegistryDigest": "44ab451d80617dab1bebcf9e1c9866d98f8d81673aa16e6fd4bed5f58c4e8bf0",
9038
+ "workflowRegistryDigest": "ee92a630b978f429097bbd082d5c45ec67aa6dc33b01cfa3bfc128ed0b661b51",
9039
9039
  "pageRegistryDigest": "8e2a30416d969f390d1fa57468c8d29f5e89d179b6d1ea1600159e72adff8130"
9040
9040
  },
9041
9041
  "comparison": {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-publication-release-registry",
4
- "generatedAt": "2026-08-31T21:12:16.000Z",
5
- "publishedAt": "2026-08-31T21:12:16.000Z",
4
+ "generatedAt": "2026-09-01T14:47:21.000Z",
5
+ "publishedAt": "2026-09-01T14:47:21.000Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "f6ad53d9db073a6940b0cc08a80f0fcd1b3f14fb",
22
+ "sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "package": {
34
34
  "name": "@kungfu-tech/buildchain",
35
- "version": "4.0.2-alpha.11",
35
+ "version": "4.0.2-alpha.13",
36
36
  "versionSource": "package.json#version"
37
37
  },
38
38
  "sourceKind": "package-site-bundle",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-manifest",
4
- "generatedAt": "2026-08-31T21:12:16.000Z",
5
- "publishedAt": "2026-08-31T21:12:16.000Z",
4
+ "generatedAt": "2026-09-01T14:47:21.000Z",
5
+ "publishedAt": "2026-09-01T14:47:21.000Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "f6ad53d9db073a6940b0cc08a80f0fcd1b3f14fb",
22
+ "sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "4.0.2-alpha.11",
40
+ "version": "4.0.2-alpha.13",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "entrypoint": "buildchain-site.json",
@@ -2096,7 +2096,7 @@
2096
2096
  "status": "preview",
2097
2097
  "owner": "buildchain-workflows",
2098
2098
  "maturity": "preview",
2099
- "introducedVersion": "4.0.2-alpha.11",
2099
+ "introducedVersion": "4.0.2-alpha.13",
2100
2100
  "compatibilityPromise": "preserved-through-the-v3-major-line",
2101
2101
  "deprecationReplacement": "",
2102
2102
  "sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
@@ -2117,7 +2117,7 @@
2117
2117
  "status": "preview",
2118
2118
  "owner": "buildchain-workflows",
2119
2119
  "maturity": "preview",
2120
- "introducedVersion": "4.0.2-alpha.11",
2120
+ "introducedVersion": "4.0.2-alpha.13",
2121
2121
  "compatibilityPromise": "preserved-through-the-v3-major-line",
2122
2122
  "deprecationReplacement": "",
2123
2123
  "sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
@@ -2138,7 +2138,7 @@
2138
2138
  "status": "preview",
2139
2139
  "owner": "buildchain-workflows",
2140
2140
  "maturity": "preview",
2141
- "introducedVersion": "4.0.2-alpha.11",
2141
+ "introducedVersion": "4.0.2-alpha.13",
2142
2142
  "compatibilityPromise": "preserved-through-the-v3-major-line",
2143
2143
  "deprecationReplacement": "",
2144
2144
  "sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
@@ -2189,7 +2189,7 @@
2189
2189
  "status": "preview",
2190
2190
  "owner": "buildchain-workflows",
2191
2191
  "maturity": "preview",
2192
- "introducedVersion": "4.0.2-alpha.11",
2192
+ "introducedVersion": "4.0.2-alpha.13",
2193
2193
  "compatibilityPromise": "preserved-through-the-v3-major-line",
2194
2194
  "deprecationReplacement": "",
2195
2195
  "sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "4.0.2-alpha.11",
3
+ "version": "4.0.2-alpha.13",
4
4
  "private": false,
5
5
  "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -38,6 +38,24 @@ export function matchesExactDevDeliveryCandidate(existing, attempted) {
38
38
  );
39
39
  }
40
40
 
41
+ export function reuseExactActiveDevDeliverySourceProof(active, input) {
42
+ const exact =
43
+ ["provisional", "qualified"].includes(active?.phase) &&
44
+ [
45
+ "pullRequestNumber",
46
+ "assignmentRoot",
47
+ "initiativeRoot",
48
+ "sourceIdentityRoot",
49
+ "deliveryClass",
50
+ "priority",
51
+ ].every((field) => active[field] === input[field]) &&
52
+ matchesExactDevDeliveryCandidate(active, {
53
+ ...input,
54
+ sourceProofRoot: active.sourceProofRoot,
55
+ });
56
+ return exact ? { ...input, sourceProofRoot: active.sourceProofRoot } : input;
57
+ }
58
+
41
59
  export function createDevDeliveryCandidateIdentity(
42
60
  input,
43
61
  expected,
@@ -8,7 +8,6 @@ export const V4_UNIVERSAL_WORKFLOW_ADMISSION =
8
8
  "kungfu-buildchain-v4-universal-workflow-admission/v1";
9
9
  export const V4_UNIVERSAL_WORKFLOW_TERMINAL_RECEIPT =
10
10
  "kungfu-buildchain-v4-universal-workflow-terminal-receipt/v1";
11
-
12
11
  const EXACT_SHA = /^[0-9a-f]{40}$/u;
13
12
  const ROOT = /^sha256:[0-9a-f]{64}$/u;
14
13
  const REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
@@ -36,7 +35,6 @@ const LEVELS = new Map([
36
35
  ["read", 1],
37
36
  ["write", 2],
38
37
  ]);
39
-
40
38
  function fail(code, message) {
41
39
  const error = new Error(message);
42
40
  error.code = code;
@@ -275,7 +273,13 @@ export function v4UniversalWorkflowRequestRoot(value) {
275
273
  validateV4UniversalWorkflowRequest(value),
276
274
  );
277
275
  }
278
-
276
+ export function v4ProductStateVersion(value, requestedSha) { const sourceSha = exactSha(requestedSha, "requestedSha"), ref = nonEmpty(value?.ref, "productStateRef.ref"), prefix = `refs/heads/buildchain/v4-product-state/${sourceSha}-`; if (!ref.startsWith(prefix)) fail("recovery-state-mismatch", "release recovery product state does not bind the requested source"); const match = ref.slice(prefix.length).match(/^(\d+)-(\d+)-(\d+)(?:-alpha-(\d+))?$/u); if (!match) fail("invalid-recovery-version", "release recovery product state version is invalid"); return `${match[1]}.${match[2]}.${match[3]}${match[4] === undefined ? "" : `-alpha.${match[4]}`}`; }
277
+ export function selectV4RecoveredProductPublicationVersion(value) {
278
+ if (value.routeDecision !== "Resume") return ""; const version = nonEmpty(value.candidateVersion, "candidateVersion"), sourceSha = exactSha(value.requestedSha, "requestedSha"), candidate = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-alpha\.(\d+))?$/u); if (!candidate) fail("invalid-recovery-version", "release recovery candidate version is invalid"); if (value.explicitResume) return version;
279
+ const states = Array.isArray(value.recoveryStates) ? value.recoveryStates : []; if (states.length) { const recovered = states.map(({ stateRef, stateCommit, exactTagRef }) => { const selected = v4ProductStateVersion(stateRef, sourceSha), parsed = selected.match(/^(\d+)\.(\d+)\.(\d+)(?:-alpha\.(\d+))?$/u), parents = stateCommit?.parents || []; if (stateRef?.object?.type !== "commit" || exactSha(stateRef?.object?.sha, "productStateRef.object.sha") !== exactSha(stateCommit?.sha, "productStateCommit.sha") || parents.length !== 1 || exactSha(parents[0]?.sha, "productStateCommit.parents[0].sha") !== sourceSha) fail("recovery-state-mismatch", "release recovery product state commit does not bind the requested source"); if (exactTagRef && (exactTagRef.ref !== `refs/tags/v${selected}` || exactTagRef.object?.type !== "commit" || exactSha(exactTagRef.object?.sha, "exactTagRef.object.sha") !== sourceSha)) fail("recovery-tag-mismatch", "release recovery exact tag does not bind the requested source"); if (parsed[1] !== candidate[1] || parsed[2] !== candidate[2] || parsed[3] !== candidate[3] || (parsed[4] === undefined) !== (candidate[4] === undefined)) fail("recovery-state-mismatch", "release recovery product state is outside the candidate release line"); return selected; }); recovered.sort((left, right) => Number(left.match(/(?:-alpha\.)?(\d+)$/u)[1]) - Number(right.match(/(?:-alpha\.)?(\d+)$/u)[1])); return recovered.at(-1); }
280
+ if (!value.exactTagRef) fail("recovery-tag-missing", "release recovery requires an exact partial-publication tag"); if (value.exactTagRef.ref !== `refs/tags/v${version}` || value.exactTagRef.object?.type !== "commit" || exactSha(value.exactTagRef.object?.sha, "exactTagRef.object.sha") !== sourceSha) fail("recovery-tag-mismatch", "release recovery exact tag does not bind the requested source");
281
+ return version;
282
+ }
279
283
  function validatePolicy(value) {
280
284
  exactKeys(
281
285
  value,
@@ -35,11 +35,10 @@ function encodeRef(ref) {
35
35
  return ref.split("/").map(encodeURIComponent).join("/");
36
36
  }
37
37
 
38
- function decodeBlob(blob) {
39
- if (blob.encoding !== "base64")
40
- throw new Error(
41
- `unsupported Git blob encoding ${blob.encoding || "<empty>"}`,
42
- );
38
+ function decodeGitHubBlobEnvelope(raw) {
39
+ if (!/"encoding"\s*:\s*"base64"/.test(raw)) return raw;
40
+ const blob = JSON.parse(raw);
41
+ if (blob?.encoding !== "base64") return raw;
43
42
  return Buffer.from(
44
43
  String(blob.content || "").replace(/\s+/g, ""),
45
44
  "base64",
@@ -108,6 +107,30 @@ export class GitHubDevDeliveryStore {
108
107
  return data;
109
108
  }
110
109
 
110
+ async requestRaw(method, requestPath) {
111
+ const response = await this.fetch(`${this.apiUrl}${requestPath}`, {
112
+ method,
113
+ headers: {
114
+ accept: "application/vnd.github.raw+json",
115
+ authorization: `Bearer ${this.token}`,
116
+ "x-github-api-version": "2022-11-28",
117
+ },
118
+ });
119
+ const raw = await response.text();
120
+ if (!response.ok) {
121
+ let message = raw || `${method} ${requestPath} failed`;
122
+ try {
123
+ message = JSON.parse(raw)?.message || message;
124
+ } catch {
125
+ // Raw blob responses are not required to be JSON.
126
+ }
127
+ const error = new Error(message);
128
+ error.status = response.status;
129
+ throw error;
130
+ }
131
+ return raw;
132
+ }
133
+
111
134
  async read({ stateRef, protectedBase, now, allowLegacyV3Readback = false }) {
112
135
  let ref;
113
136
  try {
@@ -149,11 +172,11 @@ export class GitHubDevDeliveryStore {
149
172
  );
150
173
  if (!entry?.sha)
151
174
  throw new Error(`${commitSha} does not contain ${STATE_PATH}`);
152
- const blob = await this.request(
175
+ const raw = await this.requestRaw(
153
176
  "GET",
154
177
  `/repos/${this.repository.fullName}/git/blobs/${entry.sha}`,
155
178
  );
156
- const bytes = decodeBlob(blob);
179
+ const bytes = decodeGitHubBlobEnvelope(raw);
157
180
  const queue = validateStoredState(JSON.parse(bytes), {
158
181
  allowLegacyV3Readback,
159
182
  });
@@ -17,6 +17,7 @@ import {
17
17
  submitDevDeliveryCandidate,
18
18
  } from "../packages/core/dev-delivery-warrant.js";
19
19
  import { recoverLegacyTerminalDevDeliveryQueue } from "../packages/core/dev-delivery-warrant-legacy-recovery.js";
20
+ import { reuseExactActiveDevDeliverySourceProof } from "../packages/core/dev-delivery-candidate-identity.js";
20
21
  import { runV4DeliveryWarrantReadCandidate } from "../packages/core/v4-delivery-warrant-read-candidate.js";
21
22
 
22
23
  import { GitHubDevDeliveryStore } from "./dev-delivery-warrant-store.mjs";
@@ -199,54 +200,55 @@ function transitionFor(command, queue, options) {
199
200
  "native command contract root does not match native-command",
200
201
  );
201
202
  }
203
+ const input = {
204
+ pullRequestNumber: positiveInteger(
205
+ options.pullRequestNumber,
206
+ "pullRequestNumber",
207
+ ),
208
+ sourceHead: exactSha(options.sourceHead, "sourceHead"),
209
+ assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
210
+ initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
211
+ sourceIdentityRoot: exactRoot(
212
+ options.sourceIdentityRoot,
213
+ "sourceIdentityRoot",
214
+ ),
215
+ sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
216
+ sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
217
+ planRoot: exactRoot(options.planRoot, "planRoot"),
218
+ closureRoot: exactRoot(options.closureRoot, "closureRoot"),
219
+ dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
220
+ toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
221
+ ...(options.environmentRoot
222
+ ? {
223
+ environmentRoot: exactRoot(
224
+ options.environmentRoot,
225
+ "environmentRoot",
226
+ ),
227
+ }
228
+ : {}),
229
+ ...(nativeCommandContract ? { nativeCommandContract } : {}),
230
+ affectedPaths: jsonList(options.affectedPaths, "affected paths"),
231
+ shardEvidenceRoots: jsonList(
232
+ options.shardEvidenceRoots,
233
+ "shard evidence roots",
234
+ ),
235
+ sourceWorkflowRunId: options.sourceWorkflowRunId
236
+ ? positiveInteger(options.sourceWorkflowRunId, "sourceWorkflowRunId")
237
+ : 0,
238
+ deliveryClass: options.deliveryClass,
239
+ priority: options.priority || "ordinary",
240
+ ...(options.releaseBlockerPriority
241
+ ? {
242
+ releaseBlockerPriority: jsonObject(
243
+ options.releaseBlockerPriority,
244
+ "release blocker priority",
245
+ ),
246
+ }
247
+ : {}),
248
+ };
202
249
  return submitDevDeliveryCandidate(
203
250
  queue,
204
- {
205
- pullRequestNumber: positiveInteger(
206
- options.pullRequestNumber,
207
- "pullRequestNumber",
208
- ),
209
- sourceHead: exactSha(options.sourceHead, "sourceHead"),
210
- assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
211
- initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
212
- sourceIdentityRoot: exactRoot(
213
- options.sourceIdentityRoot,
214
- "sourceIdentityRoot",
215
- ),
216
- sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
217
- sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
218
- planRoot: exactRoot(options.planRoot, "planRoot"),
219
- closureRoot: exactRoot(options.closureRoot, "closureRoot"),
220
- dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
221
- toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
222
- ...(options.environmentRoot
223
- ? {
224
- environmentRoot: exactRoot(
225
- options.environmentRoot,
226
- "environmentRoot",
227
- ),
228
- }
229
- : {}),
230
- ...(nativeCommandContract ? { nativeCommandContract } : {}),
231
- affectedPaths: jsonList(options.affectedPaths, "affected paths"),
232
- shardEvidenceRoots: jsonList(
233
- options.shardEvidenceRoots,
234
- "shard evidence roots",
235
- ),
236
- sourceWorkflowRunId: options.sourceWorkflowRunId
237
- ? positiveInteger(options.sourceWorkflowRunId, "sourceWorkflowRunId")
238
- : 0,
239
- deliveryClass: options.deliveryClass,
240
- priority: options.priority || "ordinary",
241
- ...(options.releaseBlockerPriority
242
- ? {
243
- releaseBlockerPriority: jsonObject(
244
- options.releaseBlockerPriority,
245
- "release blocker priority",
246
- ),
247
- }
248
- : {}),
249
- },
251
+ reuseExactActiveDevDeliverySourceProof(queue.activeWarrant, input),
250
252
  { now: options.now },
251
253
  );
252
254
  }
@@ -723,7 +723,6 @@ function renderAdmissionComment(receipt, receiptRoot) {
723
723
  "GitHub auto-merge state is observed evidence only; it is not Buildchain admission authority.",
724
724
  ].join("\n");
725
725
  }
726
-
727
726
  async function publishAdmissionDiagnostic(client, options, receipt, receiptRoot) {
728
727
  const marker = `<!-- ${AGENT_ADMISSION_MARKER} pr=${receipt.pullRequestNumber} head=${receipt.expectedHeadSha} -->`;
729
728
  const body = renderAdmissionComment(receipt, receiptRoot);
@@ -743,7 +742,6 @@ async function publishAdmissionDiagnostic(client, options, receipt, receiptRoot)
743
742
  }
744
743
  return { commentId: comment?.id || null, commentUrl: comment?.html_url || "", statusPublished: Boolean(status) };
745
744
  }
746
-
747
745
  function createAdmissionReceipt({ options, pr = {}, state, reason, readiness, decision = {}, queue = null, warrant = null }) {
748
746
  return createDevPrAdmissionReceipt({ options, pr, state, reason, readiness, decision, queue, warrant, labels: labelsOf(pr), nextAction: nextAdmissionAction });
749
747
  }
@@ -751,16 +749,21 @@ function createAdmissionReceipt({ options, pr = {}, state, reason, readiness, de
751
749
  function targetedFailure({ options, pr, state, reason, readiness, decision, queue }) {
752
750
  const receipt = createAdmissionReceipt({ options, pr, state, reason, readiness, decision, queue });
753
751
  return {
754
- schema: AGENT_ADMISSION_RESULT_SCHEMA,
755
- ok: false,
756
- mode: options.dryRun ? "plan" : "execute",
757
- outcome: "targeted-admission-failed",
758
- receipt,
759
- receiptRoot: contentRoot(receipt),
760
- diagnostic: null,
752
+ schema: AGENT_ADMISSION_RESULT_SCHEMA, ok: false, mode: options.dryRun ? "plan" : "execute",
753
+ outcome: "targeted-admission-failed", receipt, receiptRoot: contentRoot(receipt), diagnostic: null,
761
754
  };
762
755
  }
763
-
756
+ async function readTargetPullRequest(client, number, options) {
757
+ let { pollMergeableAttempts: attempts, pollMergeableDelayMs: delayMs } = options;
758
+ for (; attempts > 1; attempts -= 1) {
759
+ try {
760
+ return await client.getPullRequest(number, { attempts, delayMs });
761
+ } catch {
762
+ await delay(delayMs);
763
+ }
764
+ }
765
+ return client.getPullRequest(number, { attempts, delayMs });
766
+ }
764
767
  export async function runDevPrAdmission(optionsInput = {}, clientInput) {
765
768
  const options = normalizeOptions(optionsInput);
766
769
  if (!options.targetBranch) throw new Error("target branch is required");
@@ -776,12 +779,9 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
776
779
 
777
780
  let pr;
778
781
  try {
779
- pr = await client.getPullRequest(options.targetPullRequestNumber, {
780
- attempts: options.pollMergeableAttempts,
781
- delayMs: options.pollMergeableDelayMs,
782
- });
782
+ pr = await readTargetPullRequest(client, options.targetPullRequestNumber, options);
783
783
  } catch (error) {
784
- return targetedFailure({ options, pr: {}, state: "missing", reason: "pull-request-not-found" });
784
+ return targetedFailure({ options, pr: {}, state: "blocked", reason: "pull-request-read-failed" });
785
785
  }
786
786
 
787
787
  const readinessFence = createPreReadinessQueueFence({ client, options, sleep: delay });
@@ -6,6 +6,7 @@ import { execFileSync } from "node:child_process";
6
6
  import {
7
7
  admitV4UniversalWorkflow,
8
8
  completeV4UniversalWorkflow,
9
+ selectV4RecoveredProductPublicationVersion, v4ProductStateVersion,
9
10
  validateV4UniversalWorkflowRequest,
10
11
  v4UniversalWorkflowRequestRoot,
11
12
  } from "../packages/core/v4-universal-workflow-bootstrap.js";
@@ -24,7 +25,6 @@ import {
24
25
  function fail(message) {
25
26
  throw new Error(message);
26
27
  }
27
-
28
28
  function readJsonEnvironment(name) {
29
29
  const source = process.env[name];
30
30
  if (!source) fail(`${name} is required`);
@@ -34,7 +34,6 @@ function readJsonEnvironment(name) {
34
34
  throw new Error(`${name} is not valid JSON`, { cause: error });
35
35
  }
36
36
  }
37
-
38
37
  function exactRuntime(admission) {
39
38
  const runtime = admission?.runtime;
40
39
  if (
@@ -45,7 +44,6 @@ function exactRuntime(admission) {
45
44
  fail("an exact admitted Buildchain runtime is required");
46
45
  return runtime;
47
46
  }
48
-
49
47
  function contentRoot(domain, value) {
50
48
  const hash = crypto.createHash("sha256");
51
49
  hash.update(domain, "utf8");
@@ -53,11 +51,9 @@ function contentRoot(domain, value) {
53
51
  hash.update(`${JSON.stringify(value)}\n`, "utf8");
54
52
  return `sha256:${hash.digest("hex")}`;
55
53
  }
56
-
57
54
  function emit(value) {
58
55
  process.stdout.write(`${JSON.stringify(value)}\n`);
59
56
  }
60
-
61
57
  function exactObject(value, keys, label) {
62
58
  if (!value || typeof value !== "object" || Array.isArray(value))
63
59
  fail(`${label} must be an object`);
@@ -85,7 +81,38 @@ function validateWorkflowInputs(workflowId, inputs) {
85
81
  `candidate payload has unregistered workflow inputs: ${unknown.sort().join(", ")}`,
86
82
  );
87
83
  }
88
-
84
+ const TRUSTED_PUBLISHING_NPM_VERSION = "11.13.0";
85
+ function prepareTrustedPublishingNpm() {
86
+ if (
87
+ !process.env.ACTIONS_ID_TOKEN_REQUEST_URL ||
88
+ !process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
89
+ )
90
+ fail("trusted publishing requires GitHub Actions OIDC authority");
91
+ const binDirectory = path.resolve(".buildchain/runtime/bin");
92
+ const npmShim = path.join(binDirectory, "npm");
93
+ fs.mkdirSync(binDirectory, { recursive: true });
94
+ fs.writeFileSync(
95
+ npmShim,
96
+ `#!/bin/sh\nexec corepack pnpm@11.7.0 dlx npm@${TRUSTED_PUBLISHING_NPM_VERSION} "$@"\n`,
97
+ { mode: 0o755 },
98
+ );
99
+ fs.chmodSync(npmShim, 0o755);
100
+ process.env.PATH = `${binDirectory}${path.delimiter}${process.env.PATH || ""}`;
101
+ }
102
+ function prepareReleasePromotionConsumerDependencies(repository) {
103
+ if (repository !== "kungfu-systems/buildchain") return;
104
+ const manifest = JSON.parse(fs.readFileSync("package.json", "utf8"));
105
+ if (manifest.packageManager !== "pnpm@11.7.0" || !fs.existsSync("pnpm-lock.yaml")) fail("Buildchain release consumer dependency lock is unavailable");
106
+ const consumerModules = path.resolve("node_modules"), runtimeModules = path.resolve(".buildchain/runtime/node_modules");
107
+ if (!fs.existsSync(consumerModules) && fs.existsSync(runtimeModules)) fs.symlinkSync(path.relative(path.dirname(consumerModules), runtimeModules), consumerModules, "dir");
108
+ if (!fs.existsSync("node_modules/@kungfu-tech/kfd/package.json")) execFileSync("corepack", "pnpm@11.7.0 install --frozen-lockfile --ignore-scripts".split(" "), { stdio: ["ignore", 2, 2] });
109
+ if (fs.existsSync(runtimeModules)) {
110
+ if (fs.realpathSync(consumerModules) !== fs.realpathSync(runtimeModules)) fail("Buildchain release consumer runtime bridge conflicts");
111
+ return;
112
+ }
113
+ fs.mkdirSync(path.dirname(runtimeModules), { recursive: true }); fs.renameSync(consumerModules, runtimeModules);
114
+ fs.symlinkSync(path.relative(path.dirname(consumerModules), runtimeModules), consumerModules, "dir");
115
+ }
89
116
  function actionEnvironment(inputs, outputPath) {
90
117
  const environment = { ...process.env, GITHUB_OUTPUT: outputPath };
91
118
  for (const [name, value] of Object.entries(inputs))
@@ -237,7 +264,12 @@ async function observedSourceTimestamp(repository, sourceSha) {
237
264
  fail("protected publication source timestamp is unavailable");
238
265
  return new Date(observed).toISOString();
239
266
  }
240
-
267
+ async function observeProductPublicationRecovery(repository, sourceSha, candidateVersion) {
268
+ const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com", token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "", prefix = `heads/buildchain/v4-product-state/${sourceSha}-`;
269
+ const stateRefs = (await githubJson({ apiUrl, token, path: `/repos/${repository}/git/matching-refs/${prefix}`, allowNotFound: true })) || [];
270
+ const recoveryStates = await Promise.all(stateRefs.map(async (stateRef) => { const version = v4ProductStateVersion(stateRef, sourceSha); const [stateCommit, exactTagRef] = await Promise.all([githubJson({ apiUrl, token, path: `/repos/${repository}/git/commits/${stateRef.object.sha}` }), githubJson({ apiUrl, token, path: `/repos/${repository}/git/ref/tags/v${version}`, allowNotFound: true })]); return { stateRef, stateCommit, exactTagRef }; }));
271
+ const exactTagRef = recoveryStates.length ? undefined : await githubJson({ apiUrl, token, path: `/repos/${repository}/git/ref/tags/v${encodeURIComponent(candidateVersion)}`, allowNotFound: true }); return { recoveryStates, exactTagRef };
272
+ }
241
273
  async function materializeProductPublicationIntent({
242
274
  candidate,
243
275
  inputs,
@@ -247,10 +279,10 @@ async function materializeProductPublicationIntent({
247
279
  const intentPath = path.resolve(
248
280
  ".buildchain/release-candidate/v4-product-publication-intent.json",
249
281
  );
250
- const sourceTimestamp = await observedSourceTimestamp(
251
- repository,
252
- route.requestedSha,
253
- );
282
+ const sourceTimestamp = await observedSourceTimestamp(repository, route.requestedSha);
283
+ const version = String(candidate.publicationVersion || candidate.version || "").trim(), explicitResume = String(inputs["resume-transaction-id"] || "") !== "";
284
+ const recovery = route.decision !== "Resume" || explicitResume ? {} : await observeProductPublicationRecovery(repository, route.requestedSha, version);
285
+ const recoveredVersion = selectV4RecoveredProductPublicationVersion({ routeDecision: route.decision, candidateVersion: version, requestedSha: route.requestedSha, explicitResume, ...recovery });
254
286
  execFileSync(
255
287
  process.execPath,
256
288
  [
@@ -269,10 +301,7 @@ async function materializeProductPublicationIntent({
269
301
  BUILDCHAIN_SOURCE_TIMESTAMP: sourceTimestamp,
270
302
  BUILDCHAIN_CANDIDATE_VERSION:
271
303
  candidate.publicationVersion || candidate.version,
272
- BUILDCHAIN_RECOVERED_PUBLICATION_VERSION:
273
- String(inputs["resume-transaction-id"] || "") !== ""
274
- ? candidate.publicationVersion || candidate.version
275
- : "",
304
+ BUILDCHAIN_RECOVERED_PUBLICATION_VERSION: recoveredVersion,
276
305
  BUILDCHAIN_SEALED_BUNDLE_MANIFEST: candidate.paths.sealedBundleManifest,
277
306
  BUILDCHAIN_REQUIRED_ARTIFACTS_PATH:
278
307
  candidate.paths.publishRequiredArtifacts,
@@ -285,7 +314,6 @@ async function materializeProductPublicationIntent({
285
314
  );
286
315
  return intentPath;
287
316
  }
288
-
289
317
  function verifiedReleaseDocuments() {
290
318
  const base = path.resolve(".buildchain/release-tail");
291
319
  const invocation = JSON.parse(
@@ -370,6 +398,9 @@ async function executeReleasePromotion(request, admission) {
370
398
  fail(`release route blocked: ${route.reason}`);
371
399
  if (route.decision === "NoOp" || payload.inputs["dry-run"] === true)
372
400
  return { route, dryRun: payload.inputs["dry-run"] === true };
401
+ prepareReleasePromotionConsumerDependencies(repository);
402
+ if (payload.inputs["trusted-publishing"] === true)
403
+ prepareTrustedPublishingNpm();
373
404
  const candidate = await resolveReleaseCandidateArtifacts({
374
405
  repository,
375
406
  targetRef: route.targetRef,
@@ -390,13 +421,13 @@ async function executeReleasePromotion(request, admission) {
390
421
  });
391
422
  if (!candidate.enabled)
392
423
  fail(candidate.reason || "release candidate is unavailable");
393
- const productPublicationIntentPath =
394
- await materializeProductPublicationIntent({
395
- candidate,
396
- inputs: payload.inputs,
397
- repository,
398
- route,
399
- });
424
+ const productPublicationIntentPath = await materializeProductPublicationIntent({
425
+ candidate,
426
+ inputs: payload.inputs,
427
+ repository,
428
+ route,
429
+ });
430
+ const productPublicationIntent = JSON.parse(fs.readFileSync(productPublicationIntentPath));
400
431
  const runtimeTree = execFileSync(
401
432
  "git",
402
433
  ["-C", ".buildchain/candidate", "rev-parse", "HEAD^{tree}"],
@@ -408,8 +439,8 @@ async function executeReleasePromotion(request, admission) {
408
439
  process.env.BUILDCHAIN_PROMOTION_TOKEN || process.env.GH_TOKEN,
409
440
  repository,
410
441
  "source-sha": route.requestedSha,
411
- version: candidate.version,
412
- tag: `v${candidate.version}`,
442
+ version: productPublicationIntent.version,
443
+ tag: productPublicationIntent.exactTag,
413
444
  channel: route.channel,
414
445
  "target-ref": route.targetRef,
415
446
  "target-sha": route.requestedSha,
@@ -481,7 +512,6 @@ async function capabilityResult(request, admission) {
481
512
  return executeReleasePromotion(request, admission);
482
513
  fail(`candidate capability is not implemented: ${request.capability.id}`);
483
514
  }
484
-
485
515
  async function executeCandidate(request, admission) {
486
516
  const runtime = exactRuntime(admission);
487
517
  if (v4UniversalWorkflowRequestRoot(request) !== admission.requestRoot)