@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.2

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 (41) hide show
  1. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
  2. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
  3. package/contracts/release-cut-v1.schema.json +1 -0
  4. package/contracts/release-train-transition-v1.schema.json +1 -0
  5. package/dist/site/buildchain-contract.json +27 -28
  6. package/dist/site/buildchain-site.json +37 -27
  7. package/dist/site/controller-registry.json +8 -4
  8. package/dist/site/kfd-claims.json +3 -3
  9. package/dist/site/kfd-upstream-aggregate.json +1 -1
  10. package/dist/site/manual-registry.json +7 -7
  11. package/dist/site/node-api-registry.json +370 -73
  12. package/dist/site/page-registry.json +26 -16
  13. package/dist/site/public-surface-audit.json +2 -2
  14. package/dist/site/publication-registry.json +4 -4
  15. package/dist/site/site-manifest.json +11 -11
  16. package/dist/site/workflow-registry.json +4 -4
  17. package/docs/dev-delivery-warrant.md +12 -2
  18. package/docs/node-api-reference.md +116 -106
  19. package/docs/release-governance.md +5 -3
  20. package/docs/release-passport.md +7 -0
  21. package/docs/release-train.md +46 -4
  22. package/docs/reusable-build-surface.md +26 -9
  23. package/docs/runtime-train-validation.md +6 -0
  24. package/docs/versioning.md +3 -2
  25. package/package.json +1 -1
  26. package/packages/core/buildchain-channel-identity.js +119 -0
  27. package/packages/core/buildchain-compatibility-proof.js +32 -0
  28. package/packages/core/buildchain-contract.js +7 -0
  29. package/packages/core/controller-evidence.js +2 -1
  30. package/packages/core/dev-delivery-warrant.js +15 -4
  31. package/packages/core/kfd-adopter-manifest.js +3 -3
  32. package/packages/core/release-blocker-priority.js +192 -0
  33. package/packages/core/release-train.js +220 -0
  34. package/scripts/buildchain-channel-router.mjs +18 -2
  35. package/scripts/buildchain-contract-lock.mjs +11 -1
  36. package/scripts/check-inventory.mjs +2 -2
  37. package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
  38. package/scripts/generate-channel-build-workflow.mjs +78 -31
  39. package/scripts/promotion-channel-router.mjs +5 -3
  40. package/scripts/release-train-self-dogfood.mjs +579 -0
  41. package/scripts/runtime-ref-core.mjs +4 -17
@@ -263,9 +263,11 @@ semantics and fail-closed verification requirements.
263
263
  The authority verifies the complete result set on GitHub-hosted infrastructure
264
264
  before delivery. The consumer controller also performs final result verification,
265
265
  exact-byte import, manifest recomputation, and deterministic-artifact replacement
266
- on a GitHub-hosted lane. Self-hosted build runners do not download authority
267
- result payloads, and aggregate/release evidence fails closed until this
268
- finalization succeeds.
266
+ on a GitHub-hosted lane. macOS results finalize on a GitHub-hosted macOS runner so
267
+ the consumer lifecycle can perform native post-sign checks before publication;
268
+ other platforms retain the GitHub-hosted Ubuntu control lane. Self-hosted build
269
+ runners do not download authority result payloads, and aggregate/release evidence
270
+ fails closed until this finalization succeeds.
269
271
 
270
272
  The central `buildchain-artifact-signing` environment reuses the established
271
273
  macOS Credential Island names (`BUILDCHAIN_MACOS_CERTIFICATE_*`,
@@ -313,6 +313,13 @@ fails closed if any claim is missing source bindings, machine evidence, hashes,
313
313
  artifact coordinates, verification result, audit boundary, responsibility, or
314
314
  residual risk.
315
315
 
316
+ The same self-promotion boundary extends the Buildchain-owned KFD witness
317
+ generator against the transaction's exact source SHA. It produces the standard
318
+ full-cut adopter manifest, the non-qualifying KFD-4/5/7 product gates, and the
319
+ one-way legacy projection before the final passport is collected. An explicitly
320
+ supplied adopter manifest still takes precedence; an omitted self-release
321
+ manifest no longer silently produces a KFD-1/2/3-only passport.
322
+
316
323
  ### KFD-3 collaboration-interface release gate
317
324
 
318
325
  KFD-3 asks a different release question than KFD-1. KFD-1 proves that named
@@ -8,12 +8,12 @@ confidence: high
8
8
  sensitivity: public
9
9
  evidence_grade: A
10
10
  review_state: self-reviewed
11
- last_reviewed: 2026-08-10
11
+ last_reviewed: 2026-08-11
12
12
  ai_provenance:
13
13
  model_family: GPT-5
14
14
  product: Codex
15
- generated_at: 2026-08-10
16
- visible_context: Buildchain v3 release candidate, recovery, Dev to Alpha Candidate Patrol, Warrant, publication transaction, and release governance sources.
15
+ generated_at: 2026-08-11
16
+ visible_context: Buildchain v3 release candidate, recovery, Dev to Alpha Candidate Patrol, rooted blocker repair, Warrant, publication transaction, and release governance sources.
17
17
  invisible_context_boundary: Live provider state and credentials were not read.
18
18
  ---
19
19
 
@@ -38,8 +38,9 @@ own those effects and persist the rooted contract in their declared store.
38
38
  - a canonical creation timestamp.
39
39
 
40
40
  The resulting `cutRoot` covers all of those fields. A generation greater than
41
- one must name the prior cut root and one of four supersession causes:
41
+ one must name the prior cut root and one of five supersession causes:
42
42
 
43
+ - `release-blocker-repair` (reserved for the rooted repair contract below);
43
44
  - `incompatible-semantics`;
44
45
  - `alpha-base-incompatibility`;
45
46
  - `invalid-authority`; or
@@ -74,6 +75,23 @@ an invalid transition fails closed.
74
75
  changing `trainRoot`, `cutRoot`, candidate identity, generation, or state. The
75
76
  same exact observation is idempotent.
76
77
 
78
+ ## Rooted release-blocker repair
79
+
80
+ `createReleaseBlockerRepair()` starts only from the active train's exact
81
+ `repair-required` state root. It creates generation N+1 from the frozen cut,
82
+ changing only the candidate commit and tree while preserving the original Dev
83
+ cut, Alpha base, runtime, route, and prior cut root. The prior train receives an
84
+ explicit `release-blocker-repair` supersession transition; later Dev movement
85
+ alone still cannot advance a generation.
86
+
87
+ The repair binds one semantic patch root to a cut landing and a Dev
88
+ forward-port. A successful cut landing makes the successor candidate buildable
89
+ even while the Dev forward-port is in conflict. Publication remains blocked
90
+ until `settleReleaseBlockerDevLanding()` compare-and-swap checks the repair root
91
+ and records a Dev landing with the same patch root. A landed but different Dev
92
+ patch produces a rooted `cut-dev-patch-root-mismatch` gate rather than
93
+ publication authority.
94
+
77
95
  Buildchain v3 Candidate Patrol resolves the open managed candidate's persisted
78
96
  train before it considers a new qualified development head. It reads back the
79
97
  candidate ref and tree, Alpha base, and exact Buildchain runtime. Matching
@@ -82,6 +100,30 @@ single rooted observation. Candidate, tree, base, runtime, or route drift emits
82
100
  a rooted hold and stops settlement. Only a validated train already carrying an
83
101
  enumerated `superseded` transition is reported as superseded.
84
102
 
103
+ ## Buildchain self-dogfood campaign
104
+
105
+ Buildchain qualifies the complete v3 mechanism with
106
+ `scripts/release-train-self-dogfood.mjs`. The campaign composes one frozen
107
+ Release Cut, moving-dev observation, deterministic failed build, successor
108
+ repair, cut/dev patch-root settlement, publication gate, and bounded Delivery
109
+ Warrant priority. It also proves that an active Warrant is not preempted and
110
+ that duplicate events, invalid authority, and unrelated priority claims fail
111
+ closed.
112
+
113
+ The command accepts an exact evidence input and emits a replayable rooted
114
+ report:
115
+
116
+ ```sh
117
+ node scripts/release-train-self-dogfood.mjs \
118
+ --input /path/to/exact-protected-delivery.json \
119
+ --output /tmp/buildchain-release-train-self-dogfood.json
120
+ ```
121
+
122
+ A passing report requires a merged protected PR, exact-head approval,
123
+ merge-group CI success, tree-equivalent merge readback, artifact evidence, and
124
+ installed-product evidence. Synthetic or unit-only evidence is useful for
125
+ negative tests but is not sufficient for release qualification.
126
+
85
127
  ## Readback and legacy state
86
128
 
87
129
  `validateReleaseCut()` checks the standalone immutable cut and its canonical
@@ -66,10 +66,25 @@ release events, and non-semver release-like tags fail before the build matrix;
66
66
  they never guess alpha for a stable release.
67
67
 
68
68
  The router automatically selects `.buildchain/alpha-contract-lock.json` for
69
- alpha and `.buildchain/contract-lock.json` for stable. A repository can override
70
- the common path with `buildchain-contract-lock-path`, or override one channel
71
- with `buildchain-alpha-contract-lock-path` /
72
- `buildchain-stable-contract-lock-path`.
69
+ alpha and `.buildchain/contract-lock.json` for stable. The generated workflow
70
+ uses two static advanced-workflow calls: `@vN-alpha` for alpha and `@vN` for
71
+ stable. A repository can override the common path with
72
+ `buildchain-contract-lock-path`, or override one channel with
73
+ `buildchain-alpha-contract-lock-path` / `buildchain-stable-contract-lock-path`,
74
+ but the path is never channel authority. The selected file's
75
+ `buildchain.ref` must prove the same channel and major as the workflow shell and
76
+ runtime.
77
+
78
+ Channel binding is independent of the current Buildchain major. Every
79
+ channel-bound run validates the complete triad before compatibility drift:
80
+
81
+ - stable = shell `vN` + runtime `vN` + a lock whose ref is stable `vN`;
82
+ - alpha = shell `vN-alpha` + runtime `vN-alpha` + a lock whose ref is alpha
83
+ `vN-alpha`.
84
+
85
+ Exact release refs are classified the same way. A missing lock, an ambiguous
86
+ identity, a major mismatch, or any stable/alpha mixture fails even when the
87
+ contract digest is unchanged or the drift would otherwise be additive.
73
88
 
74
89
  Only repositories changing the default policy need extra routing input:
75
90
 
@@ -279,7 +294,8 @@ shell. The full train validation protocol is documented in
279
294
  [`runtime-train-validation.md`](runtime-train-validation.md).
280
295
 
281
296
  For one-off manual validation, a trusted maintainer can run the caller workflow
282
- with a temporary runtime override:
297
+ with a temporary runtime override. The override inherits the caller's declared
298
+ alpha or stable lane; trust authorizes the opaque ref, not cross-channel use:
283
299
 
284
300
  ```yaml
285
301
  on:
@@ -1092,10 +1108,11 @@ jobs:
1092
1108
  ```
1093
1109
 
1094
1110
  Existing callers may keep `buildchain-contract-lock-path`; a non-empty explicit
1095
- path overrides channel-specific selection for compatibility. Migration only
1096
- requires adding the two channel lock inputs and may retain the remaining common
1097
- promotion declaration unchanged. Consumers must not call the dot-prefixed
1098
- advanced workflow directly.
1111
+ path overrides channel-specific path selection only. Its lock content must
1112
+ still prove the selected channel and major. Migration only requires adding the
1113
+ two channel lock inputs and may retain the remaining common promotion
1114
+ declaration unchanged. Consumers must not call the dot-prefixed advanced
1115
+ workflow directly.
1099
1116
 
1100
1117
  `buildchain-issue-app-id` and `buildchain-issue-app-private-key` are optional
1101
1118
  but recommended for cross-repository consumers. They should identify a GitHub
@@ -165,6 +165,12 @@ maintain, or admin permission on the caller repository. Pull requests,
165
165
  including fork-originated pull requests, cannot use train or exact-SHA
166
166
  overrides.
167
167
 
168
+ That permission does not create a third channel. A train, authority ref, or
169
+ exact SHA must be bound to an alpha or stable shell lane, and the consumer lock
170
+ must prove that same lane and major. Trusted overrides can replace the runtime
171
+ coordinate for validation; they cannot combine a stable shell or lock with an
172
+ alpha runtime, or the reverse.
173
+
168
174
  Runtime train validation covers Buildchain runtime scripts, CLI code, local
169
175
  actions, configuration parsing, and lifecycle behavior. It cannot validate
170
176
  changes that require the outer reusable workflow YAML itself to change, such as
@@ -79,8 +79,9 @@ repository changes are patch".
79
79
 
80
80
  | Date | Action | Line | Faces | Class | Rationale | PR |
81
81
  | ---------- | ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
82
- | 2026-08-10 | extend-minor | `v3.0` | release-cut-contract, release-train-state-machine, release-train-node-export | additive | Add a provider-neutral rooted Release Cut and idempotent Release Train state contract that freezes candidate, tree, Alpha base, runtime, generation, and authority while treating later Dev movement as observation rather than implicit supersession. | |
83
- | 2026-08-05 | extend-minor | `v3.0` | auditable-demo-scenario, auditable-demo-capture, auditable-demo-adapter | additive | Add an opt-in deterministic readable-playback declaration that preserves captured terminal payloads and order while normalizing only presentation timing; omitted playback continues to use observed PTY timestamps, and existing composition modes remain unchanged. | |
82
+ | 2026-08-11 | extend-minor | `v3.0` | release-blocker-repair-contract, release-blocker-priority-claim, release-train-node-export, dev-delivery-warrant-node-export | additive | Add rooted successor candidate generations on a frozen Release Cut, exact semantic patch identity across cut and Dev landings, publication blocking through conflict or mismatch, and a narrow non-preemptive Warrant blocker lane. | |
83
+ | 2026-08-10 | extend-minor | `v3.0` | release-cut-contract, release-train-state-machine, release-train-node-export | additive | Add a provider-neutral rooted Release Cut and idempotent Release Train state contract that freezes candidate, tree, Alpha base, runtime, generation, and authority while treating later Dev movement as observation rather than implicit supersession. | |
84
+ | 2026-08-05 | extend-minor | `v3.0` | auditable-demo-scenario, auditable-demo-capture, auditable-demo-adapter | additive | Add an opt-in deterministic readable-playback declaration that preserves captured terminal payloads and order while normalizing only presentation timing; omitted playback continues to use observed PTY timestamps, and existing composition modes remain unchanged. | |
84
85
  | 2026-08-04 | extend-minor | `v3.0` | dev-delivery-warrant-queue, source-qualification-proof, integration-delivery-proof, dev-delivery-cli, reusable-dev-delivery-workflows | additive | Add durable fair and fenced protected-dev scheduling plus split source/integration proof contracts. Existing PR admission remains available through explicit `off` and `shadow` rollout modes; no existing command, export, or release flow is removed. | |
85
86
  | 2026-08-01 | extend-minor | `v3.0` | cli-reference-registry, node-api-symbol-registry, golden-path-manual | additive | Add generated, drift-checked CLI and Node API reference registries to the public site bundle, plus a package-tested first-user Golden Path; existing command execution and import semantics remain unchanged. | |
86
87
  | 2026-07-31 | extend-minor | `v3.0` | auditable-demo-workflow, auditable-demo-media-profile, auditable-demo-media-receipt | additive | Add an opt-in responsive profile that binds source-resolution and exact 1280x720 MP4/WebM/GIF renditions to one Gate and receipt while rejecting upscales, aspect-ratio drift, and profile changes between Gate-only and full-render paths. | |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "3.0.9-alpha.0",
3
+ "version": "3.0.9-alpha.2",
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",
@@ -0,0 +1,119 @@
1
+ const EXACT_SHA_RE = /^[0-9a-f]{40}$/i;
2
+ const OFFICIAL_REF_RE = /^v(\d+)(?:\.\d+)?(-alpha)?$/;
3
+ const STABLE_RELEASE_RE = /^v(\d+)\.\d+\.\d+$/;
4
+ const ALPHA_RELEASE_RE = /^v(\d+)\.\d+\.\d+-alpha\.\d+$/;
5
+ const TRAIN_REF_RE = /^train\/v(\d+)\/v(\d+)\.\d+\/[A-Za-z0-9._/-]+$/;
6
+ const AUTHORITY_REF_RE = /^authority\/v(\d+)\/v(\d+)\.\d+\/[A-Za-z0-9._/-]+$/;
7
+
8
+ export const BUILDCHAIN_CHANNELS = new Set(["stable", "alpha"]);
9
+
10
+ export function normalizeBuildchainRef(ref = "") {
11
+ const value = String(ref || "").trim();
12
+ const workflowRef = value.includes("/.github/workflows/") ? value.split("@").pop() || "" : value;
13
+ return workflowRef.replace(/^refs\/(?:heads|tags)\//, "");
14
+ }
15
+
16
+ export function parseBuildchainRefIdentity(ref = "") {
17
+ const value = normalizeBuildchainRef(ref);
18
+ if (!value) return { ref: value, kind: "missing", channel: null, major: null };
19
+ if (EXACT_SHA_RE.test(value)) return { ref: value, kind: "exact-sha", channel: null, major: null };
20
+
21
+ for (const [pattern, channel, kind] of [
22
+ [OFFICIAL_REF_RE, null, "official-channel"],
23
+ [STABLE_RELEASE_RE, "stable", "release"],
24
+ [ALPHA_RELEASE_RE, "alpha", "release"],
25
+ ]) {
26
+ const match = value.match(pattern);
27
+ if (match) {
28
+ return {
29
+ ref: value,
30
+ kind,
31
+ channel: channel || (match[2] ? "alpha" : "stable"),
32
+ major: Number(match[1]),
33
+ };
34
+ }
35
+ }
36
+
37
+ for (const [pattern, kind] of [
38
+ [TRAIN_REF_RE, "train"],
39
+ [AUTHORITY_REF_RE, "authority"],
40
+ ]) {
41
+ const match = value.match(pattern);
42
+ if (match) {
43
+ return {
44
+ ref: value,
45
+ kind,
46
+ channel: null,
47
+ major: Number(match[1]),
48
+ coherentMajor: match[1] === match[2],
49
+ };
50
+ }
51
+ }
52
+ return { ref: value, kind: "development", channel: null, major: null };
53
+ }
54
+
55
+ function expectedIdentity({ workflowShellRef, expectedChannel, expectedMajor }) {
56
+ const shell = parseBuildchainRefIdentity(workflowShellRef);
57
+ const channel =
58
+ String(expectedChannel || "")
59
+ .trim()
60
+ .toLowerCase() || shell.channel;
61
+ const majorText = String(expectedMajor ?? "").trim();
62
+ const major = majorText ? Number(majorText.replace(/^v/, "")) : shell.major;
63
+ return { shell, channel, major };
64
+ }
65
+
66
+ function expectedReasons(expected, { expectedChannel, expectedMajor }) {
67
+ const reasons = [];
68
+ if (!BUILDCHAIN_CHANNELS.has(expected.channel)) reasons.push("Buildchain channel is missing or ambiguous");
69
+ if (!Number.isInteger(expected.major) || expected.major < 1) reasons.push("Buildchain major is missing or ambiguous");
70
+ if (expected.shell.channel && expected.shell.channel !== expected.channel) {
71
+ reasons.push(`workflow shell ${expected.shell.ref} is ${expected.shell.channel}, expected ${expected.channel}`);
72
+ }
73
+ if (Number.isInteger(expected.shell.major) && expected.shell.major !== expected.major) {
74
+ reasons.push(`workflow shell ${expected.shell.ref} is v${expected.shell.major}, expected v${expected.major}`);
75
+ }
76
+ if (!expected.shell.channel && (!expectedChannel || !expectedMajor)) {
77
+ reasons.push(`workflow shell ${expected.shell.ref || "(missing)"} does not declare a stable or alpha channel`);
78
+ }
79
+ return reasons;
80
+ }
81
+
82
+ function runtimeReasons(expected, runtime, allowOpaqueRuntime) {
83
+ if (runtime.channel) {
84
+ return [runtime.channel !== expected.channel ? `runtime ${runtime.ref} is ${runtime.channel}, expected ${expected.channel}` : "", runtime.major !== expected.major ? `runtime ${runtime.ref} is v${runtime.major}, expected v${expected.major}` : ""].filter(Boolean);
85
+ }
86
+ if (new Set(["train", "authority", "exact-sha"]).has(runtime.kind) && allowOpaqueRuntime) {
87
+ return [runtime.coherentMajor === false ? `runtime ${runtime.ref} has inconsistent major segments` : "", Number.isInteger(runtime.major) && runtime.major !== expected.major ? `runtime ${runtime.ref} is v${runtime.major}, expected v${expected.major}` : ""].filter(Boolean);
88
+ }
89
+ return [`runtime ${runtime.ref || "(missing)"} does not prove the ${expected.channel || "selected"} channel`];
90
+ }
91
+
92
+ function lockReasons(expected, lock, lockMajor, lockMajorLine) {
93
+ if (!lock.ref) return ["Buildchain contract lock is required for a channel-bound run"];
94
+ return [lock.channel !== expected.channel ? `contract lock ref ${lock.ref} does not prove the ${expected.channel} channel` : "", lock.major !== expected.major ? `contract lock ref ${lock.ref} does not prove major v${expected.major}` : "", lockMajorLine && lockMajor.major !== expected.major ? `contract lock major line ${lockMajor.ref} does not match v${expected.major}` : ""].filter(Boolean);
95
+ }
96
+
97
+ export function evaluateBuildchainChannelBinding({ workflowShellRef = "", runtimeRef = "", lockRef = "", lockMajorLine = "", expectedChannel = "", expectedMajor = "", allowOpaqueRuntime = false } = {}) {
98
+ const expected = expectedIdentity({
99
+ workflowShellRef,
100
+ expectedChannel,
101
+ expectedMajor,
102
+ });
103
+ const runtime = parseBuildchainRefIdentity(runtimeRef);
104
+ const lock = parseBuildchainRefIdentity(lockRef);
105
+ const lockMajor = parseBuildchainRefIdentity(lockMajorLine);
106
+ const reasons = [...expectedReasons(expected, { expectedChannel, expectedMajor }), ...runtimeReasons(expected, runtime, allowOpaqueRuntime), ...lockReasons(expected, lock, lockMajor, lockMajorLine)];
107
+
108
+ return {
109
+ ok: reasons.length === 0,
110
+ status: reasons.length === 0 ? "channel-bound" : "channel-binding-mismatch",
111
+ channel: expected.channel || null,
112
+ major: Number.isInteger(expected.major) ? expected.major : null,
113
+ shell: expected.shell,
114
+ runtime,
115
+ lock,
116
+ lockMajor,
117
+ reasons,
118
+ };
119
+ }
@@ -7,6 +7,7 @@ import {
7
7
  devDeliveryRepository as repository,
8
8
  devDeliveryText as text,
9
9
  } from "./dev-delivery-common.js";
10
+ import { evaluateBuildchainChannelBinding } from "./buildchain-channel-identity.js";
10
11
 
11
12
  export const BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA = "kungfu.buildchain.compatibility-proof/v1";
12
13
  export const BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA = "kungfu.buildchain.compatibility-proof-registry/v1";
@@ -437,6 +438,19 @@ function evaluateSurfaceRelation({ oldSurface, nextSurface, proofRegistry, major
437
438
  };
438
439
  }
439
440
 
441
+ function evaluateChannelBinding({ lock, workflowShellRef, runtimeRef, expectedChannel, expectedMajor, allowOpaqueRuntime }) {
442
+ if (!workflowShellRef && !expectedChannel && !expectedMajor) return null;
443
+ return evaluateBuildchainChannelBinding({
444
+ workflowShellRef,
445
+ runtimeRef,
446
+ lockRef: lock?.buildchain?.ref || "",
447
+ lockMajorLine: lock?.buildchain?.majorLine || "",
448
+ expectedChannel,
449
+ expectedMajor,
450
+ allowOpaqueRuntime,
451
+ });
452
+ }
453
+
440
454
  export function evaluateBuildchainContractLock({
441
455
  lock,
442
456
  current,
@@ -444,11 +458,29 @@ export function evaluateBuildchainContractLock({
444
458
  runtimeSha = "",
445
459
  runtimeClass = "",
446
460
  compatibilityPolicy = "",
461
+ workflowShellRef = "",
462
+ expectedChannel = "",
463
+ expectedMajor = "",
464
+ allowOpaqueRuntime = false,
447
465
  } = {}) {
448
466
  if (!current || current.contract !== BUILDCHAIN_CONTRACT) {
449
467
  throw new Error("current must be a Buildchain runtime contract world");
450
468
  }
451
469
  assertBuildchainCompatibilityProjection(current);
470
+ const binding = evaluateChannelBinding({
471
+ lock, workflowShellRef, runtimeRef, expectedChannel, expectedMajor, allowOpaqueRuntime,
472
+ });
473
+ if (binding?.ok === false) {
474
+ return {
475
+ ok: false,
476
+ status: binding.status,
477
+ drift: false,
478
+ compatible: false,
479
+ issueRecommended: false,
480
+ reasons: binding.reasons,
481
+ channelBinding: binding,
482
+ };
483
+ }
452
484
  if (!new Set(["stable", "alpha"]).has(runtimeClass)) {
453
485
  return {
454
486
  ok: true,
@@ -9,6 +9,13 @@ import {
9
9
  import { createControllerRegistry } from "./controller-evidence.js";
10
10
  import { enumerateWorkflowInputs } from "./public-surface-audit.js";
11
11
 
12
+ export {
13
+ BUILDCHAIN_CHANNELS,
14
+ evaluateBuildchainChannelBinding,
15
+ normalizeBuildchainRef,
16
+ parseBuildchainRefIdentity,
17
+ } from "./buildchain-channel-identity.js";
18
+
12
19
  export {
13
20
  BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA,
14
21
  BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA,
@@ -53,7 +53,8 @@ const CONTROLLER_SPECS = [
53
53
  workflowId: "build",
54
54
  version: 1,
55
55
  capabilities: ["channel-selection", "runtime-selection", "build-delegation"],
56
- stages: ["resolve-channel", "build", "aggregate"],
56
+ stages: ["resolve-channel", "alpha", "stable", "aggregate"],
57
+ optionalStages: ["alpha", "stable"],
57
58
  evidence: ["nested-controller-receipt", "controller-receipt"],
58
59
  },
59
60
  {
@@ -2,7 +2,9 @@ import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot
2
2
  import { chainedDevDeliveryAttemptInput, createDevDeliveryCandidateIdentity, validateDevDeliveryCandidateChain } from "./dev-delivery-candidate-identity.js";
3
3
  import { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA, createCancelQueuedDevDeliveryCandidate } from "./dev-delivery-warrant-cancellation.js";
4
4
  import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettler } from "./dev-delivery-warrant-settlement.js";
5
+ import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
5
6
  export { devDeliveryContentRoot } from "./dev-delivery-common.js";
7
+ export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
6
8
  export { SOURCE_QUALIFICATION_PROOF_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
7
9
  export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
8
10
  export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
@@ -60,7 +62,7 @@ function normalizeCandidate(input, expected) {
60
62
  if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
61
63
  throw new Error(`unsupported candidate status ${status || "<empty>"}`);
62
64
  }
63
- return {
65
+ const candidate = {
64
66
  ...identity,
65
67
  sourceHead: exactSha(input.sourceHead, "sourceHead"),
66
68
  sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
@@ -78,6 +80,8 @@ function normalizeCandidate(input, expected) {
78
80
  status,
79
81
  terminal: input.terminal || null,
80
82
  };
83
+ if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
84
+ return candidate;
81
85
  }
82
86
 
83
87
  function queueBody(queue) {
@@ -140,6 +144,7 @@ export function normalizeDevDeliveryQueue(input, expected = {}) {
140
144
  if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
141
145
  throw new Error("exactly one active candidate must match the active Warrant");
142
146
  }
147
+ if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
143
148
  } else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
144
149
  throw new Error("active candidate exists without an active Warrant");
145
150
  }
@@ -180,6 +185,7 @@ function submissionReceipt({ before, after, candidate, action, now }) {
180
185
  dependencyRoot: candidate.dependencyRoot,
181
186
  toolchainRoot: candidate.toolchainRoot,
182
187
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
188
+ ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
183
189
  deliveryClass: candidate.deliveryClass,
184
190
  priority: candidate.priority,
185
191
  retainedEnqueuedAt: candidate.enqueuedAt,
@@ -219,7 +225,7 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
219
225
  throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
220
226
  }
221
227
  const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
222
- const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]);
228
+ const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
223
229
  if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
224
230
  action = "duplicate-noop";
225
231
  selected = existing;
@@ -235,6 +241,8 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
235
241
  const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
236
242
  existing.sourceHead = attemptedCandidate.sourceHead;
237
243
  for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
244
+ if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
245
+ else delete existing.releaseBlockerPriority;
238
246
  existing.updatedAt = currentTime;
239
247
  existing.attempts += 1;
240
248
  existing.status = "queued";
@@ -270,6 +278,7 @@ function effectivePriority(candidate, policy, now) {
270
278
  basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
271
279
  agingBoost,
272
280
  score: Math.min(policy.maxPriority, DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost),
281
+ releaseBlocker: Boolean(candidate.releaseBlockerPriority),
273
282
  };
274
283
  }
275
284
 
@@ -282,7 +291,7 @@ export function rankDevDeliveryCandidates(queueInput, { now = new Date().toISOSt
282
291
  candidate,
283
292
  priority: effectivePriority(candidate, queue.policy, currentTime),
284
293
  }))
285
- .sort((left, right) => right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
294
+ .sort((left, right) => compareReleaseBlockerPriority(left, right) || right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
286
295
  }
287
296
 
288
297
  function warrantToken(input) {
@@ -371,6 +380,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
371
380
  dependencyRoot: candidate.dependencyRoot,
372
381
  toolchainRoot: candidate.toolchainRoot,
373
382
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
383
+ ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
374
384
  deliveryClass: candidate.deliveryClass,
375
385
  generation: next.fencingCounter,
376
386
  expectedOldStateRoot: before.stateRoot,
@@ -390,7 +400,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
390
400
  const receipt = {
391
401
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
392
402
  selected: true,
393
- reason: selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
403
+ reason: selected.priority.releaseBlocker ? "release-blocker-bounded-priority" : selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
394
404
  repository: transaction.after.repository,
395
405
  protectedBase: transaction.after.protectedBase,
396
406
  candidateId: transaction.result.candidate.candidateId,
@@ -406,6 +416,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
406
416
  dependencyRoot: transaction.result.candidate.dependencyRoot,
407
417
  toolchainRoot: transaction.result.candidate.toolchainRoot,
408
418
  sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
419
+ ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority } : {}),
409
420
  deliveryClass: transaction.result.candidate.deliveryClass,
410
421
  queueAgeSeconds: selected.priority.ageSeconds,
411
422
  basePriority: selected.priority.basePriority,
@@ -25,7 +25,7 @@ const SHA_PATTERN = /^[0-9a-f]{40}$/;
25
25
  const REQUIRED_DECISIONS = Object.freeze(["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]);
26
26
  const PRODUCT_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
27
27
  const PRODUCT_GATE_SET = new Set(PRODUCT_GATE_STANDARDS);
28
- const require = createRequire(import.meta.url);
28
+ const require = createRequire(import.meta.url), installedKfdPackageOptions = (options) => { try { return { ...options, packageRoot: require.resolve("@kungfu-tech/kfd/package.json").replace(/[\\/]package\.json$/, "") }; } catch { return options; } };
29
29
 
30
30
  function issue(code, path, message, detail = {}) {
31
31
  return { level: "error", code, path, message, ...detail };
@@ -63,14 +63,14 @@ function decisionWitnessRow(row) {
63
63
 
64
64
  function verifyPublishedManifest(manifest, packageOptions, issues) {
65
65
  try {
66
- const report = verifyAdopterManifestFromPackage(manifest, packageOptions);
66
+ const report = verifyAdopterManifestFromPackage(manifest, installedKfdPackageOptions(packageOptions));
67
67
  if (!report.valid) {
68
68
  issues.push(issue("adopter-manifest-invalid", "manifest", "published KFD adopter verifier rejected the manifest", {
69
69
  manifestIssues: report.issues,
70
70
  }));
71
71
  return null;
72
72
  }
73
- return bundleAdopterManifest(manifest, packageOptions);
73
+ return bundleAdopterManifest(manifest, installedKfdPackageOptions(packageOptions));
74
74
  } catch (error) {
75
75
  issues.push(issue("adopter-manifest-invalid", "manifest", error.message));
76
76
  return null;