@kungfu-tech/buildchain 4.0.0 → 4.0.1-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 (69) hide show
  1. package/AGENTS.md +13 -5
  2. package/actions/promote-buildchain-ref/README.md +11 -6
  3. package/architecture/internal-capabilities.json +11 -2
  4. package/architecture/maintainability-policy.json +149 -17
  5. package/architecture/v4-floating-consumer-policy.json +75 -0
  6. package/architecture/v4-floating-consumer-policy.md +32 -0
  7. package/architecture/v4-runtime-ref-resume-authority.json +64 -0
  8. package/architecture/v4-stage-capsule-qualification.json +2 -2
  9. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  10. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +53 -0
  11. package/contracts/fixtures/v4-runtime-ref-resume-authority-v1/scenario.json +15 -0
  12. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +105 -0
  13. package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -0
  14. package/dist/site/buildchain-contract.json +101 -31
  15. package/dist/site/buildchain-site.json +73 -27
  16. package/dist/site/capability-registry.json +3 -3
  17. package/dist/site/controller-registry.json +62 -6
  18. package/dist/site/kfd-claims.json +68 -13
  19. package/dist/site/kfd-upstream-aggregate.json +1 -1
  20. package/dist/site/manual-registry.json +6 -6
  21. package/dist/site/node-api-registry.json +799 -127
  22. package/dist/site/page-registry.json +63 -17
  23. package/dist/site/public-surface-audit.json +49 -17
  24. package/dist/site/publication-registry.json +4 -4
  25. package/dist/site/release-provenance.json +2 -0
  26. package/dist/site/site-manifest.json +10 -10
  27. package/dist/site/workflow-registry.json +46 -18
  28. package/docs/MAP.md +2 -0
  29. package/docs/dev-delivery-warrant.md +19 -1
  30. package/docs/lifecycle-protocol.md +41 -0
  31. package/docs/node-api-reference.md +207 -134
  32. package/docs/reusable-build-surface.md +41 -0
  33. package/docs/runtime-train-validation.md +10 -0
  34. package/docs/v4-runtime-ref-resume-authority.md +69 -0
  35. package/docs/versioning.md +1 -0
  36. package/package.json +6 -4
  37. package/packages/core/artifact-signing.js +61 -0
  38. package/packages/core/buildchain-config.js +66 -1
  39. package/packages/core/dev-delivery-proof.js +37 -3
  40. package/packages/core/dev-delivery-warrant.js +3 -3
  41. package/packages/core/index.js +2 -0
  42. package/packages/core/publication-authority.js +5 -5
  43. package/packages/core/release-candidate.js +79 -19
  44. package/packages/core/release-passport.js +239 -33
  45. package/packages/core/v4-floating-consumer-evidence.js +324 -0
  46. package/packages/core/v4-floating-consumer-policy.js +446 -0
  47. package/packages/core/v4-floating-consumer-release-passport.js +126 -0
  48. package/packages/core/v4-runtime-ref-resume-authority.js +625 -0
  49. package/packages/core/v4-runtime-selector-persistence.js +228 -0
  50. package/packages/core/workflow-yaml-contract.js +48 -0
  51. package/scripts/audit-publication-control-plane.mjs +31 -31
  52. package/scripts/check-inventory.mjs +1 -1
  53. package/scripts/check-v4-floating-consumer-policy-contract.mjs +198 -0
  54. package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
  55. package/scripts/dev-delivery-source-proof-reuse.mjs +631 -0
  56. package/scripts/dev-pr-auto-merge.mjs +37 -48
  57. package/scripts/dev-pr-delivery-warrant.mjs +205 -2
  58. package/scripts/dev-pr-prequeue-guard.mjs +399 -0
  59. package/scripts/ensure-github-release.mjs +3 -3
  60. package/scripts/generate-channel-build-workflow.mjs +3 -0
  61. package/scripts/generate-channel-promotion-workflow.mjs +112 -12
  62. package/scripts/generate-release-candidate-passport.mjs +41 -33
  63. package/scripts/init-repo.mjs +5 -1
  64. package/scripts/inspect-artifact-signing-requests.mjs +6 -0
  65. package/scripts/npm-publish-transaction.mjs +101 -89
  66. package/scripts/resume-from-candidate-run.mjs +11 -14
  67. package/scripts/seal-artifact-signing-requests.mjs +6 -0
  68. package/scripts/site-capability-metadata.mjs +2 -0
  69. package/scripts/v4-consumer-policy.mjs +231 -0
@@ -102,6 +102,36 @@ The router is generated from `.build.yml`'s input/output surface. Run
102
102
  `node scripts/generate-channel-build-workflow.mjs` after changing the advanced
103
103
  build workflow; inventory and unit tests reject a stale generated router.
104
104
 
105
+ ### v4 floating consumer admission
106
+
107
+ The v4 public surfaces add a source policy gate before dependency setup, matrix
108
+ planning, build, publication, or promotion work. Tracked caller YAML may use
109
+ only `@v4` or `@v4-alpha`, and the repository must commit both
110
+ `.buildchain/contract-lock.json` and `.buildchain/alpha-contract-lock.json`.
111
+ Source-persisted exact SHAs, exact defaults, repository/input/environment
112
+ indirection, nested composite indirection, a missing lock, or a stale selected
113
+ lock fail closed.
114
+
115
+ Admission emits a rooted receipt binding the caller source, public workflow,
116
+ visible selector and workflow-shell SHA, actual runtime SHA, both lock roots,
117
+ and policy/scanner roots. A trusted `workflow_dispatch` train or SHA remains a
118
+ non-persistent runtime override: the tracked floating selector and its selected
119
+ lock still bind the visible workflow shell. Release-candidate evidence carries
120
+ the receipt; promotion certifies it independently; final v4 Release Passport
121
+ construction requires the matching certification.
122
+
123
+ Transient train, authority, and exact-SHA selections additionally produce a
124
+ provider-readback-bound runtime authorization receipt. A resumed failed tail
125
+ uses a fresh attempt and records the original build runtime, the resume runtime,
126
+ the exact sealed Stage Capsules reused per platform, and the platforms rebuilt
127
+ in the final Release Passport. See
128
+ [`v4-runtime-ref-resume-authority.md`](v4-runtime-ref-resume-authority.md).
129
+
130
+ See the normative
131
+ [`v4 floating consumer policy`](../architecture/v4-floating-consumer-policy.md)
132
+ and its machine declaration for the exact acceptance boundary. This policy does
133
+ not change v3 behavior.
134
+
105
135
  ## Advanced Workflow
106
136
 
107
137
  Consumers that need direct workflow-shell or runtime control call the advanced
@@ -699,6 +729,17 @@ For a declared macOS `archive`, the authority safely extracts the sealed
699
729
  container, signs and verifies every Mach-O payload, signs Mach-O payloads inside
700
730
  embedded Python wheels, rebuilds each affected wheel's PEP 427 `RECORD`, and
701
731
  recreates the original zip or tar.gz before returning the exact final bytes.
732
+ Archives whose executable hosts a JIT runtime can additionally request the
733
+ Buildchain-owned `entitlements_profile = "jit-executable-v1"` and exact paths,
734
+ for example `entitlements_paths = ["product/runtime/python/bin/python3"]`.
735
+ Paths are relative to the extracted archive root. The authority
736
+ then attaches only `com.apple.security.cs.allow-jit` to the exact executable
737
+ Mach-O files sealed in `entitlements_paths`, leaves every other executable and
738
+ library without exception entitlements, and records the profile, paths, and
739
+ entitled executable count in provider evidence. Consumer-provided entitlement
740
+ files and wildcard target paths are unsupported. The profile is valid only for
741
+ Apple `archive` requests and fails closed on an unsafe, missing,
742
+ non-executable, duplicate, or unsealed target path.
702
743
  Windows `pe` and `binary` artifacts
703
744
  resolve to timestamped native `windows-authenticode`; Windows PE never falls
704
745
  back to a detached signature. Linux and other non-native binary files,
@@ -176,3 +176,13 @@ actions, configuration parsing, and lifecycle behavior. It cannot validate
176
176
  changes that require the outer reusable workflow YAML itself to change, such as
177
177
  new jobs, permissions, workflow outputs, or matrix topology. Those changes need
178
178
  a canary workflow path or a temporary explicit workflow ref.
179
+
180
+ ## v4 persisted-selector boundary
181
+
182
+ For v4 consumers, never commit a train or exact SHA in a `uses` node. Keep the
183
+ caller on `@v4` or `@v4-alpha`, retain both stable and alpha contract locks, and
184
+ pass a temporary train/SHA only through the trusted `workflow_dispatch`
185
+ `buildchain-ref` input. Consumer admission binds the selected lock to the exact
186
+ workflow-shell commit while recording the separately resolved runtime SHA.
187
+ Therefore a train can exercise candidate runtime code without weakening the
188
+ durable floating-selector policy or masquerading as a channel promotion.
@@ -0,0 +1,69 @@
1
+ ---
2
+ status: draft
3
+ period: ongoing
4
+ theme: buildchain-v4-runtime-ref-resume-authority
5
+ doc_type: contract-guide
6
+ source_level: local-files
7
+ confidence: high
8
+ sensitivity: public
9
+ evidence_grade: A
10
+ review_state: unreviewed
11
+ last_reviewed: 2026-08-15
12
+ ai_provenance:
13
+ model_family: GPT-5
14
+ product: Codex
15
+ generated_at: 2026-08-14
16
+ invisible_context: not asserted
17
+ ---
18
+
19
+ # Buildchain v4 transient runtime and resume authority
20
+
21
+ Buildchain v4 consumers keep `v4` or `v4-alpha` in tracked workflow source.
22
+ A train, authority ref, or exact SHA is transient run data, never a new
23
+ committed default. The public promotion router accepts that escape only from a
24
+ trusted `workflow_dispatch` actor with write, maintain, or admin permission.
25
+ Before heavy promotion work, it binds an exact source tree, operator reason,
26
+ dual consumer contract-lock roots, the floating-consumer policy receipt, a
27
+ clean persistence scan, and GitHub provider readbacks proving that the selected
28
+ runtime is reachable from an approved v4 ref.
29
+
30
+ The persistence scan covers tracked workflow, composite-action, JSON, TOML, and
31
+ YAML surfaces. Exact SHA, train/authority defaults, and runtime selectors hidden
32
+ through repository variables, secrets, or environment indirection reject the
33
+ run. OIDC, IAM, and other external authority references are inventoried as
34
+ metadata only; credential values are never read or written to evidence.
35
+
36
+ ## Resume after a floating ref moves
37
+
38
+ A late platform failure is resumed as a new governed attempt. It is not a
39
+ GitHub failed-job rerun. The lineage keeps runtime A (the runtime that built and
40
+ sealed each reusable Stage Capsule) separate from runtime B (the authorized
41
+ runtime executing the new attempt). A Capsule is reusable only when its source
42
+ SHA, source tree, platform, policy root, build runtime, identity root, Capsule
43
+ root, and artifact digest still match. Valid unaffected platforms are restored;
44
+ only missing platforms rebuild. Stale, ambiguous, unsealed, cross-source, or
45
+ cross-policy evidence fails closed.
46
+
47
+ This deliberately does not weaken the existing Stage Capsule resume planner's
48
+ `runtime-changed` invalidation. That planner decides whether a stage may be
49
+ reused under one expected build identity. This contract instead proves that an
50
+ already sealed output built under runtime A remains the exact product input
51
+ while a distinct orchestration attempt runs under runtime B. Runtime B never
52
+ rewrites the Capsule's build identity.
53
+
54
+ Final Release Passport evidence embeds the complete authorization receipt and
55
+ the A+B resume lineage under `v4RuntimeResume`. The section binds the new
56
+ attempt, floating-ref movement, reused and rebuilt platform partition,
57
+ consumer-policy receipt, resume plan, and final public readback roots. Tampering
58
+ with either document or crossing authorization/lineage roots rejects Passport
59
+ construction and verification.
60
+
61
+ Normative files:
62
+
63
+ - `architecture/v4-runtime-ref-resume-authority.json`
64
+ - `contracts/v4-runtime-ref-resume-authority-v1.schema.json`
65
+ - `packages/core/v4-runtime-ref-resume-authority.js`
66
+ - `scripts/authorize-promotion-runtime-override.cjs`
67
+
68
+ This contract changes no v3 behavior, grants no provider mutation authority,
69
+ and stores no credential material.
@@ -79,6 +79,7 @@ repository changes are patch".
79
79
 
80
80
  | Date | Action | Line | Faces | Class | Rationale | PR |
81
81
  | ---------- | ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
82
+ | 2026-08-14 | extend-minor | `v4.0` | source-qualification-proof-v2, source-proof-reuse-decision, reusable-check-workflow | additive | Forward-port opt-in exact accepted source-proof reuse for merge groups, binding source, base, patch, runtime, plan, policy, closure, dependency, required-context, producer receipt, and caller run. Any missing or changed predicate fails closed to the existing full source lifecycle, which remains the default. | |
82
83
  | 2026-08-11 | extend-minor | `v4.0` | release-blocker-repair-contract, release-blocker-priority-claim, release-train-node-export, dev-delivery-warrant-node-export | additive | Forward-port rooted successor candidate generations, exact semantic patch identity across cut and Dev landings, publication blocking through conflict or mismatch, and the narrow non-preemptive Warrant blocker lane already protected and self-dogfood-proven on v3. | |
83
84
  | 2026-08-10 | extend-minor | `v4.0` | release-cut-contract, release-train-state-machine, release-train-node-export | additive | Forward-port the provider-neutral rooted Release Cut and idempotent Release Train state contract already proven on v3; candidate, tree, Alpha base, runtime, generation, and authority stay frozen while later Dev movement remains an observation rather than implicit supersession. | |
84
85
  | 2026-08-08 | extend-minor | `v4.0` | dev-delivery-warrant-read-candidate, rust-state-projection, dev-delivery-cli | additive | Add an opt-in, qualification-bound read projection behind `observe --read-mode v4`; v3 remains the sole writer and default reader, mutation commands ignore the switch, failures stop, and rollback is the explicit caller change back to v3. | |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "4.0.0",
3
+ "version": "4.0.1-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",
@@ -40,6 +40,8 @@
40
40
  "./release-line-bootstrap": "./packages/core/release-line-bootstrap.js",
41
41
  "./readme-badges": "./packages/core/readme-badges.js",
42
42
  "./public-surface-audit": "./packages/core/public-surface-audit.js",
43
+ "./v4-floating-consumer-policy": "./packages/core/v4-floating-consumer-policy.js",
44
+ "./v4-runtime-ref-resume-authority": "./packages/core/v4-runtime-ref-resume-authority.js",
43
45
  "./logging": "./packages/core/logging.js",
44
46
  "./portable-dev-cache": "./packages/core/portable-dev-cache.js",
45
47
  "./publication-artifact": "./packages/core/publication-artifact.js",
@@ -123,10 +125,10 @@
123
125
  "packageManager": "pnpm@11.7.0",
124
126
  "scripts": {
125
127
  "operator:windows-jit": "bash scripts/aws-windows-jit-operator.sh",
126
- "check": "pnpm run check:static && node scripts/check-v4-public-dogfood-contract.mjs && pnpm run check:v4-bridge && pnpm run check:v4-contracts && node scripts/check-maintainability.mjs && node scripts/check-internal-architecture.mjs && node scripts/check-inventory.mjs && node scripts/check-core-mechanism-inventory.mjs && node scripts/check-release-tail-contract.mjs && node scripts/v4-architecture.mjs validate && node scripts/v4-warrant-shadow-plan.mjs validate && pnpm run check:site && pnpm run check:workflows && pnpm run test:unit && pnpm run check:golden-path && node scripts/check-action-bundles.mjs",
127
- "check:static": "node scripts/check-javascript-syntax.mjs && prettier --check architecture/*.json contracts/release-tail-provider-bindings-v1.schema.json contracts/v4-canonical-contracts-v1.schema.json contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json contracts/v4-delivery-warrant-trace-v1.schema.json contracts/v4-host-contract-v1.schema.json contracts/v4-provider-operation-journal-v1.schema.json contracts/v4-release-activation-shadow-v1.schema.json contracts/v4-stable-publication-fence-v1.schema.json contracts/v4-partial-mutation-recovery-v1.schema.json contracts/v4-stage-capsule-v1.schema.json contracts/v4-stage-capsule-store-v1.schema.json contracts/v4-stage-capsule-resume-v1.schema.json contracts/release-tail-capabilities-v1.schema.json contracts/fixtures/release-tail-capabilities-v1/*.json contracts/fixtures/v4-delivery-warrant-trace-v1/*.json contracts/fixtures/v4-provider-operation-journal-v1/*.json contracts/fixtures/v4-provider-readback-idempotency-v1/*.json contracts/fixtures/v4-release-activation-shadow-v1/*.json contracts/fixtures/v4-stable-publication-fence-v1/*.json contracts/fixtures/v4-partial-mutation-recovery-v1/*.json contracts/fixtures/v4-stage-capsule-v1/*.json contracts/fixtures/v4-stage-capsule-store-v1/*.json contracts/fixtures/v4-stage-capsule-resume-v1/*.json bin/internal/command-registry.mjs packages/core/v4-canonical-contracts.js packages/core/v4-provider-operation-journal.js packages/core/v4-provider-readback-idempotency.js packages/core/v4-release-activation-shadow.js packages/core/v4-stable-publication-fence.js packages/core/v4-partial-mutation-recovery-qualification.js packages/core/v4-stage-capsule.js packages/core/v4-stage-capsule-store.js packages/core/v4-stage-capsule-local-store.js packages/core/v4-platform-stage-checkpoints.js packages/core/v4-stage-capsule-resume-planner.js packages/core/v4-stage-capsule-qualification.js packages/core/v4-stage-capsule-qualification-campaign.js packages/core/v4-delivery-warrant-fixture-runner.js packages/core/v4-delivery-warrant-read-candidate.js packages/core/v4-delivery-warrant-semantic-diff-gate.js packages/core/v4-delivery-warrant-shadow-adapter.js scripts/authorize-promotion-runtime-override.cjs scripts/check-core-mechanism-inventory.mjs scripts/check-release-tail-contract.mjs scripts/check-javascript-syntax.mjs scripts/check-maintainability.mjs scripts/check-v4-public-dogfood-contract.mjs scripts/maintainability-metrics.mjs scripts/public-reference.mjs scripts/site-reference-registry.mjs scripts/generate-public-reference.mjs scripts/v4-architecture.mjs scripts/v4-bridge-bootstrap.mjs scripts/v4-bridge-evidence.mjs scripts/v4-host-adapter.mjs scripts/v4-platform-stage-checkpoint-rehearsal.mjs scripts/v4-stage-capsule-resume-rehearsal.mjs scripts/v4-stage-capsule-qualification.mjs scripts/v4-warrant-shadow-plan.mjs scripts/verify-golden-path.mjs tests/authorize-promotion-runtime-override.test.mjs tests/command-registry.test.mjs tests/core-mechanism-inventory.test.mjs tests/release-tail-contract.test.mjs tests/maintainability.test.mjs tests/promotion-responsibilities.test.mjs tests/public-reference.test.mjs tests/v4-architecture.test.mjs tests/v4-bridge.test.mjs tests/v4-canonical-contracts.test.mjs tests/v4-delivery-warrant-fixture-runner.test.mjs tests/v4-delivery-warrant-read-candidate.test.mjs tests/v4-platform-stage-checkpoints.test.mjs tests/v4-provider-operation-journal.test.mjs tests/v4-provider-readback-idempotency.test.mjs tests/v4-public-dogfood-contract.test.mjs tests/v4-release-activation-shadow.test.mjs tests/v4-stable-publication-fence.test.mjs tests/v4-partial-mutation-recovery-qualification.test.mjs tests/v4-stage-capsule.test.mjs tests/v4-stage-capsule-store.test.mjs tests/v4-stage-capsule-resume-planner.test.mjs tests/v4-stage-capsule-qualification.test.mjs tests/v4-warrant-shadow-plan.test.mjs",
128
+ "check": "pnpm run check:static && node scripts/check-v4-public-dogfood-contract.mjs && node scripts/check-v4-floating-consumer-policy-contract.mjs && pnpm run check:v4-bridge && pnpm run check:v4-contracts && node scripts/check-maintainability.mjs && node scripts/check-internal-architecture.mjs && node scripts/check-inventory.mjs && node scripts/check-core-mechanism-inventory.mjs && node scripts/check-release-tail-contract.mjs && node scripts/v4-architecture.mjs validate && node scripts/v4-warrant-shadow-plan.mjs validate && pnpm run check:site && pnpm run check:workflows && pnpm run test:unit && pnpm run check:golden-path && node scripts/check-action-bundles.mjs",
129
+ "check:static": "node scripts/check-javascript-syntax.mjs && prettier --check architecture/*.json contracts/release-tail-provider-bindings-v1.schema.json contracts/v4-canonical-contracts-v1.schema.json contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json contracts/v4-delivery-warrant-trace-v1.schema.json contracts/v4-floating-consumer-policy-receipt-v1.schema.json contracts/v4-runtime-ref-resume-authority-v1.schema.json contracts/v4-host-contract-v1.schema.json contracts/v4-provider-operation-journal-v1.schema.json contracts/v4-release-activation-shadow-v1.schema.json contracts/v4-stable-publication-fence-v1.schema.json contracts/v4-partial-mutation-recovery-v1.schema.json contracts/v4-stage-capsule-v1.schema.json contracts/v4-stage-capsule-store-v1.schema.json contracts/v4-stage-capsule-resume-v1.schema.json contracts/release-tail-capabilities-v1.schema.json contracts/fixtures/release-tail-capabilities-v1/*.json contracts/fixtures/v4-delivery-warrant-trace-v1/*.json contracts/fixtures/v4-floating-consumer-policy-v1/*.json contracts/fixtures/v4-runtime-ref-resume-authority-v1/*.json contracts/fixtures/v4-provider-operation-journal-v1/*.json contracts/fixtures/v4-provider-readback-idempotency-v1/*.json contracts/fixtures/v4-release-activation-shadow-v1/*.json contracts/fixtures/v4-stable-publication-fence-v1/*.json contracts/fixtures/v4-partial-mutation-recovery-v1/*.json contracts/fixtures/v4-stage-capsule-v1/*.json contracts/fixtures/v4-stage-capsule-store-v1/*.json contracts/fixtures/v4-stage-capsule-resume-v1/*.json bin/internal/command-registry.mjs packages/core/v4-canonical-contracts.js packages/core/v4-floating-consumer-evidence.js packages/core/v4-floating-consumer-policy.js packages/core/v4-floating-consumer-release-passport.js packages/core/v4-runtime-ref-resume-authority.js packages/core/v4-runtime-selector-persistence.js packages/core/v4-provider-operation-journal.js packages/core/v4-provider-readback-idempotency.js packages/core/v4-release-activation-shadow.js packages/core/v4-stable-publication-fence.js packages/core/v4-partial-mutation-recovery-qualification.js packages/core/v4-stage-capsule.js packages/core/v4-stage-capsule-store.js packages/core/v4-stage-capsule-local-store.js packages/core/v4-platform-stage-checkpoints.js packages/core/v4-stage-capsule-resume-planner.js packages/core/v4-stage-capsule-qualification.js packages/core/v4-stage-capsule-qualification-campaign.js packages/core/v4-delivery-warrant-fixture-runner.js packages/core/v4-delivery-warrant-read-candidate.js packages/core/v4-delivery-warrant-semantic-diff-gate.js packages/core/v4-delivery-warrant-shadow-adapter.js scripts/authorize-promotion-runtime-override.cjs scripts/check-core-mechanism-inventory.mjs scripts/check-release-tail-contract.mjs scripts/check-javascript-syntax.mjs scripts/check-maintainability.mjs scripts/dev-pr-prequeue-guard.mjs scripts/check-v4-public-dogfood-contract.mjs scripts/maintainability-metrics.mjs scripts/public-reference.mjs scripts/site-reference-registry.mjs scripts/generate-public-reference.mjs scripts/v4-architecture.mjs scripts/v4-bridge-bootstrap.mjs scripts/v4-bridge-evidence.mjs scripts/v4-consumer-policy.mjs scripts/v4-host-adapter.mjs scripts/v4-platform-stage-checkpoint-rehearsal.mjs scripts/v4-stage-capsule-resume-rehearsal.mjs scripts/v4-stage-capsule-qualification.mjs scripts/v4-warrant-shadow-plan.mjs scripts/verify-golden-path.mjs tests/authorize-promotion-runtime-override.test.mjs tests/command-registry.test.mjs tests/core-mechanism-inventory.test.mjs tests/release-tail-contract.test.mjs tests/maintainability.test.mjs tests/promotion-responsibilities.test.mjs tests/public-reference.test.mjs tests/v4-architecture.test.mjs tests/v4-bridge.test.mjs tests/v4-canonical-contracts.test.mjs tests/v4-delivery-warrant-fixture-runner.test.mjs tests/v4-delivery-warrant-read-candidate.test.mjs tests/v4-floating-consumer-policy.test.mjs tests/v4-runtime-ref-resume-authority.test.mjs tests/v4-platform-stage-checkpoints.test.mjs tests/v4-provider-operation-journal.test.mjs tests/v4-provider-readback-idempotency.test.mjs tests/v4-public-dogfood-contract.test.mjs tests/v4-release-activation-shadow.test.mjs tests/v4-stable-publication-fence.test.mjs tests/v4-partial-mutation-recovery-qualification.test.mjs tests/v4-stage-capsule.test.mjs tests/v4-stage-capsule-store.test.mjs tests/v4-stage-capsule-resume-planner.test.mjs tests/v4-stage-capsule-qualification.test.mjs tests/v4-warrant-shadow-plan.test.mjs",
128
130
  "check:v4-bridge": "cargo fmt --manifest-path crates/buildchain-v4-bridge/Cargo.toml -- --check && cargo clippy --locked --manifest-path crates/buildchain-v4-bridge/Cargo.toml -- -D warnings && cargo test --locked --manifest-path crates/buildchain-v4-bridge/Cargo.toml",
129
- "check:v4-contracts": "cargo fmt --manifest-path crates/buildchain-v4-contracts/Cargo.toml -- --check && cargo clippy --locked --manifest-path crates/buildchain-v4-contracts/Cargo.toml -- -D warnings && cargo test --locked --manifest-path crates/buildchain-v4-contracts/Cargo.toml && node --test tests/v4-canonical-contracts.test.mjs tests/v4-delivery-warrant-fixture-runner.test.mjs tests/v4-delivery-warrant-read-candidate.test.mjs tests/v4-platform-stage-checkpoints.test.mjs tests/v4-provider-operation-journal.test.mjs tests/v4-provider-readback-idempotency.test.mjs tests/v4-public-dogfood-contract.test.mjs tests/v4-release-activation-shadow.test.mjs tests/v4-stable-publication-fence.test.mjs tests/v4-partial-mutation-recovery-qualification.test.mjs tests/v4-stage-capsule.test.mjs tests/v4-stage-capsule-store.test.mjs tests/v4-stage-capsule-resume-planner.test.mjs tests/v4-stage-capsule-qualification.test.mjs",
131
+ "check:v4-contracts": "cargo fmt --manifest-path crates/buildchain-v4-contracts/Cargo.toml -- --check && cargo clippy --locked --manifest-path crates/buildchain-v4-contracts/Cargo.toml -- -D warnings && cargo test --locked --manifest-path crates/buildchain-v4-contracts/Cargo.toml && node --test tests/v4-canonical-contracts.test.mjs tests/v4-delivery-warrant-fixture-runner.test.mjs tests/v4-delivery-warrant-read-candidate.test.mjs tests/v4-floating-consumer-policy-contract.test.mjs tests/v4-floating-consumer-policy.test.mjs tests/v4-runtime-ref-resume-authority.test.mjs tests/v4-platform-stage-checkpoints.test.mjs tests/v4-provider-operation-journal.test.mjs tests/v4-provider-readback-idempotency.test.mjs tests/v4-public-dogfood-contract.test.mjs tests/v4-release-activation-shadow.test.mjs tests/v4-stable-publication-fence.test.mjs tests/v4-partial-mutation-recovery-qualification.test.mjs tests/v4-stage-capsule.test.mjs tests/v4-stage-capsule-store.test.mjs tests/v4-stage-capsule-resume-planner.test.mjs tests/v4-stage-capsule-qualification.test.mjs",
130
132
  "check:workflows": "bash scripts/check-workflows.sh",
131
133
  "generate:reference": "node scripts/generate-public-reference.mjs",
132
134
  "check:reference": "node scripts/generate-public-reference.mjs --check",
@@ -9,6 +9,7 @@ export const ARTIFACT_SIGNING_AUTHORITY_CONTRACT =
9
9
 
10
10
  const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u;
11
11
  const SOURCE_SHA_PATTERN = /^[0-9a-f]{40}$/u;
12
+ const ENTITLEMENTS_PROFILES = new Set(["none", "jit-executable-v1"]);
12
13
  const FORBIDDEN_CREDENTIAL_KEYS =
13
14
  /(?:certificate|password|private.?key|secret|token|notary|issuer|team.?id|environment)/iu;
14
15
 
@@ -88,6 +89,64 @@ function exactSourceSha(value, label) {
88
89
  return normalized;
89
90
  }
90
91
 
92
+ function entitlementsPaths(value) {
93
+ if (value === undefined) return [];
94
+ if (!Array.isArray(value)) {
95
+ throw new Error("signing entitlements paths must be an array");
96
+ }
97
+ const paths = value.map((entry, index) => {
98
+ const normalized = nonEmptyString(
99
+ entry,
100
+ `signing entitlements paths[${index}]`,
101
+ );
102
+ const parts = normalized.split("/");
103
+ if (
104
+ normalized.startsWith("/") ||
105
+ normalized.includes("\\") ||
106
+ normalized.includes(",") ||
107
+ parts.some((part) => part === "" || part === "." || part === "..")
108
+ ) {
109
+ throw new Error(
110
+ `signing entitlements paths[${index}] must be a safe archive-relative path`,
111
+ );
112
+ }
113
+ return normalized;
114
+ });
115
+ if (new Set(paths).size !== paths.length) {
116
+ throw new Error("signing entitlements paths must not contain duplicates");
117
+ }
118
+ return paths;
119
+ }
120
+
121
+ function signingEntitlements(profileId, kind, signature) {
122
+ const profile = String(signature.entitlementsProfile || "none").trim();
123
+ const paths = entitlementsPaths(signature.entitlementsPaths);
124
+ if (!ENTITLEMENTS_PROFILES.has(profile)) {
125
+ throw new Error(
126
+ `unsupported signing entitlements profile: ${profile || "<empty>"}`,
127
+ );
128
+ }
129
+ if (
130
+ profile !== "none" &&
131
+ (profileId !== "apple-developer-id" || kind !== "archive")
132
+ ) {
133
+ throw new Error(
134
+ `signing entitlements profile ${profile} requires an Apple archive`,
135
+ );
136
+ }
137
+ if (
138
+ (profile === "none" && paths.length !== 0) ||
139
+ (profile !== "none" && paths.length === 0)
140
+ ) {
141
+ throw new Error(
142
+ "signing entitlements paths must be non-empty exactly when an entitlements profile is enabled",
143
+ );
144
+ }
145
+ return profile === "none"
146
+ ? {}
147
+ : { entitlementsProfile: profile, entitlementsPaths: paths };
148
+ }
149
+
91
150
  function assertNoCredentialMaterial(value, path = "request") {
92
151
  if (!value || typeof value !== "object") return;
93
152
  for (const [key, child] of Object.entries(value)) {
@@ -191,6 +250,7 @@ export function createArtifactSigningRequest({
191
250
  platform,
192
251
  artifactKind: kind,
193
252
  });
253
+ const entitlementIntent = signingEntitlements(profile.id, kind, signature);
194
254
  const request = {
195
255
  schemaVersion: 1,
196
256
  contract: ARTIFACT_SIGNING_REQUEST_CONTRACT,
@@ -253,6 +313,7 @@ export function createArtifactSigningRequest({
253
313
  profile: profile.id,
254
314
  provider: profile.provider,
255
315
  semantics: profile.semantics,
316
+ ...entitlementIntent,
256
317
  },
257
318
  delivery: {
258
319
  mode: nonEmptyString(
@@ -61,6 +61,10 @@ const SUPPORTED_SIGNING_PROFILES = new Set([
61
61
  "windows-authenticode",
62
62
  "detached-signature-v1",
63
63
  ]);
64
+ const SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES = new Set([
65
+ "none",
66
+ "jit-executable-v1",
67
+ ]);
64
68
  const SUPPORTED_SIGNING_ARTIFACT_KINDS = new Set([
65
69
  "auto", "binary", "archive", "blob", "directory", "mach-o", "app-bundle",
66
70
  "framework-bundle", "plugin-bundle", "xpc-bundle", "dylib", "pkg", "dmg",
@@ -99,6 +103,27 @@ function normalizeStringArray(value, label) {
99
103
  return value.map((entry, index) => assertString(entry, `${label}[${index}]`));
100
104
  }
101
105
 
106
+ function normalizeSigningEntitlementsPaths(value, label) {
107
+ const paths = normalizeStringArray(value, label);
108
+ for (const [index, entry] of paths.entries()) {
109
+ const parts = entry.split("/");
110
+ if (
111
+ entry.startsWith("/") ||
112
+ entry.includes("\\") ||
113
+ entry.includes(",") ||
114
+ parts.some((part) => part === "" || part === "." || part === "..")
115
+ ) {
116
+ throw new Error(
117
+ `${label}[${index}] must be a safe archive-relative path`,
118
+ );
119
+ }
120
+ }
121
+ if (new Set(paths).size !== paths.length) {
122
+ throw new Error(`${label} must not contain duplicate paths`);
123
+ }
124
+ return paths;
125
+ }
126
+
102
127
  function getByDottedKey(target, key) {
103
128
  return String(key)
104
129
  .split(".")
@@ -362,7 +387,16 @@ function normalizeSigningSection(signing) {
362
387
  const artifacts = signing.artifacts.map((artifact, index) => {
363
388
  const label = `signing.artifacts[${index}]`;
364
389
  assertPlainObject(artifact, label);
365
- const allowedKeys = new Set(["id", "path", "profile", "kind", "platforms", "required"]);
390
+ const allowedKeys = new Set([
391
+ "id",
392
+ "path",
393
+ "profile",
394
+ "entitlements_profile",
395
+ "entitlements_paths",
396
+ "kind",
397
+ "platforms",
398
+ "required",
399
+ ]);
366
400
  for (const key of Object.keys(artifact)) {
367
401
  if (!allowedKeys.has(key)) {
368
402
  throw new Error(`${label}.${key} is not supported; declare desired signature state only`);
@@ -377,10 +411,41 @@ function normalizeSigningSection(signing) {
377
411
  if (!SUPPORTED_SIGNING_ARTIFACT_KINDS.has(kind)) {
378
412
  throw new Error(`${label}.kind is not a supported signing artifact kind`);
379
413
  }
414
+ const entitlementsProfile =
415
+ artifact.entitlements_profile === undefined
416
+ ? "none"
417
+ : assertString(
418
+ artifact.entitlements_profile,
419
+ `${label}.entitlements_profile`,
420
+ );
421
+ if (!SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES.has(entitlementsProfile)) {
422
+ throw new Error(
423
+ `${label}.entitlements_profile must be one of none or jit-executable-v1`,
424
+ );
425
+ }
426
+ if (entitlementsProfile !== "none" && kind !== "archive") {
427
+ throw new Error(
428
+ `${label}.entitlements_profile ${entitlementsProfile} requires kind = "archive"`,
429
+ );
430
+ }
431
+ const entitlementsPaths = normalizeSigningEntitlementsPaths(
432
+ artifact.entitlements_paths,
433
+ `${label}.entitlements_paths`,
434
+ );
435
+ if (
436
+ (entitlementsProfile === "none" && entitlementsPaths.length !== 0) ||
437
+ (entitlementsProfile !== "none" && entitlementsPaths.length === 0)
438
+ ) {
439
+ throw new Error(
440
+ `${label}.entitlements_paths must be non-empty exactly when entitlements_profile is enabled`,
441
+ );
442
+ }
380
443
  return {
381
444
  id: artifact.id === undefined ? artifactPath : assertString(artifact.id, `${label}.id`),
382
445
  path: artifactPath,
383
446
  profile,
447
+ entitlementsProfile,
448
+ entitlementsPaths,
384
449
  kind,
385
450
  platforms: normalizeStringArray(artifact.platforms, `${label}.platforms`),
386
451
  required: optionalBoolean(artifact.required, true),
@@ -1,6 +1,7 @@
1
1
  import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot as exactRoot, devDeliveryExactSha as exactSha, devDeliveryPositiveInteger as positiveInteger, devDeliveryProtectedBase as protectedBase, devDeliveryRepository as repository, devDeliveryText as text, devDeliveryTimestamp as timestamp } from "./dev-delivery-common.js";
2
2
 
3
3
  export const SOURCE_QUALIFICATION_PROOF_SCHEMA = "kungfu.buildchain.source-qualification-proof/v1";
4
+ export const SOURCE_QUALIFICATION_PROOF_V2_SCHEMA = "kungfu.buildchain.source-qualification-proof/v2";
4
5
  export const PROJECT_CUT_REPLAY_PROOF_SCHEMA = "kungfu.buildchain.project-cut-replay-proof/v1";
5
6
  export const INTEGRATION_DELIVERY_PROOF_SCHEMA = "kungfu.buildchain.integration-delivery-proof/v1";
6
7
 
@@ -49,10 +50,38 @@ export function createSourceQualificationProof(input = {}) {
49
50
  return { ...body, proofRoot: devDeliveryContentRoot(sourceQualificationIdentity(body)) };
50
51
  }
51
52
 
53
+ export function createSourceQualificationProofV2(input = {}) {
54
+ const body = {
55
+ schema: SOURCE_QUALIFICATION_PROOF_V2_SCHEMA,
56
+ rootSemantics: SOURCE_QUALIFICATION_ROOT_SEMANTICS,
57
+ authority: "exact-source-qualification",
58
+ repository: repository(input.repository),
59
+ protectedBase: protectedBase(input.protectedBase),
60
+ qualifiedBase: exactSha(input.qualifiedBase, "qualifiedBase"),
61
+ sourceIdentityRoot: exactRoot(input.sourceIdentityRoot, "sourceIdentityRoot"),
62
+ sourceHead: exactSha(input.sourceHead, "sourceHead"),
63
+ sourceTree: exactSha(input.sourceTree, "sourceTree"),
64
+ sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
65
+ planRoot: exactRoot(input.planRoot, "planRoot"),
66
+ closureRoot: exactRoot(input.closureRoot, "closureRoot"),
67
+ dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
68
+ toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
69
+ policyRoot: exactRoot(input.policyRoot, "policyRoot"),
70
+ requiredContextRoot: exactRoot(input.requiredContextRoot, "requiredContextRoot"),
71
+ controllerReceiptRoot: exactRoot(input.controllerReceiptRoot, "controllerReceiptRoot"),
72
+ sourceWorkflowRunId: positiveInteger(input.sourceWorkflowRunId, "sourceWorkflowRunId"),
73
+ affectedPaths: [...new Set((input.affectedPaths || []).map(text).filter(Boolean))].sort(),
74
+ shardEvidenceRoots: exactRoots(input.shardEvidenceRoots, "shardEvidenceRoots"),
75
+ qualifiedAt: timestamp(input.qualifiedAt, "qualifiedAt"),
76
+ };
77
+ body.observationRoot = devDeliveryContentRoot(sourceQualificationObservation(body));
78
+ return { ...body, proofRoot: devDeliveryContentRoot(sourceQualificationIdentity(body)) };
79
+ }
80
+
52
81
  export function verifySourceQualificationProof(proofInput, expected = {}) {
53
82
  try {
54
83
  const proof = clone(proofInput || {});
55
- if (proof.schema !== SOURCE_QUALIFICATION_PROOF_SCHEMA) return { ok: false, reason: "unsupported-schema" };
84
+ if (![SOURCE_QUALIFICATION_PROOF_SCHEMA, SOURCE_QUALIFICATION_PROOF_V2_SCHEMA].includes(proof.schema)) return { ok: false, reason: "unsupported-schema" };
56
85
  const proofRoot = proof.proofRoot;
57
86
  delete proof.proofRoot;
58
87
  if (proof.rootSemantics === undefined) {
@@ -65,7 +94,12 @@ export function verifySourceQualificationProof(proofInput, expected = {}) {
65
94
  for (const [field, value] of Object.entries(expected)) {
66
95
  if (value !== undefined && proof[field] !== value) return { ok: false, reason: `${field}-mismatch` };
67
96
  }
68
- createSourceQualificationProof(proof);
97
+ if (proof.schema === SOURCE_QUALIFICATION_PROOF_V2_SCHEMA) {
98
+ if (proof.authority !== "exact-source-qualification") return { ok: false, reason: "non-exact-authority" };
99
+ createSourceQualificationProofV2(proof);
100
+ } else {
101
+ createSourceQualificationProof(proof);
102
+ }
69
103
  return { ok: true, reason: "exact-source-proof", proofRoot };
70
104
  } catch (error) {
71
105
  return { ok: false, reason: "invalid-proof", error: error.message };
@@ -80,7 +114,7 @@ export function classifyDevDeliveryDelta({ proof, current = {} } = {}) {
80
114
  reason: verification.reason,
81
115
  reusable: false,
82
116
  };
83
- const exactPredicates = ["sourceIdentityRoot", "sourcePatchRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
117
+ const exactPredicates = ["sourceIdentityRoot", "sourcePatchRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot", ...(proof.schema === SOURCE_QUALIFICATION_PROOF_V2_SCHEMA ? ["qualifiedBase", "sourceHead", "sourceTree", "policyRoot", "requiredContextRoot"] : [])];
84
118
  for (const field of exactPredicates) {
85
119
  if (!current[field] || current[field] !== proof[field]) {
86
120
  return {
@@ -5,7 +5,7 @@ import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettle
5
5
  import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
6
6
  export { devDeliveryContentRoot } from "./dev-delivery-common.js";
7
7
  export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
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";
8
+ export { SOURCE_QUALIFICATION_PROOF_SCHEMA, SOURCE_QUALIFICATION_PROOF_V2_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, createSourceQualificationProofV2, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
9
9
  export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
10
10
  export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
11
11
  export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
@@ -70,7 +70,7 @@ function normalizeCandidate(input, expected) {
70
70
  planRoot: exactRoot(input.planRoot, "planRoot"),
71
71
  closureRoot: exactRoot(input.closureRoot, "closureRoot"),
72
72
  dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
73
- toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
73
+ toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"), ...(input.environmentRoot ? { environmentRoot: exactRoot(input.environmentRoot, "environmentRoot") } : {}), ...(Object.hasOwn(input, "sourceWorkflowRunId") ? { sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0) } : {}),
74
74
  priority: priority(input.priority),
75
75
  enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
76
76
  updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
@@ -79,7 +79,7 @@ function normalizeCandidate(input, expected) {
79
79
  status,
80
80
  terminal: input.terminal || null,
81
81
  };
82
- if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
82
+ if (Object.hasOwn(input, "affectedPaths")) { if (!Array.isArray(input.affectedPaths)) throw new Error("candidate affectedPaths must be an array"); candidate.affectedPaths = [...new Set(input.affectedPaths.map(text).filter(Boolean))].sort(); } if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
83
83
  return candidate;
84
84
  }
85
85
 
@@ -639,6 +639,8 @@ export {
639
639
  } from "./kfd3-surface-register.js";
640
640
 
641
641
  export * from "./release-propagation.js";
642
+ export * from "./v4-floating-consumer-policy.js";
643
+ export * from "./v4-runtime-ref-resume-authority.js";
642
644
 
643
645
  export {
644
646
  RELEASE_ACTIVATION_CONTRACT,
@@ -639,18 +639,18 @@ function validatePublicationEvidence(publicationEvidence, admission) {
639
639
  repository: admission.repository,
640
640
  targetChannel: releaseCandidateEvidenceChannel(admission.channel),
641
641
  });
642
- if (!passportValidation.ok) {
643
- throw new Error(`release-candidate passport did not qualify: ${passportValidation.errors.join("; ")}`);
644
- }
642
+ if (!passportValidation.ok) throw new Error(`release-candidate passport did not qualify: ${passportValidation.errors.join("; ")}`);
643
+ const evidence = (key) => passport[key] ? { [key]: passport[key] } : {};
645
644
  const candidateHash = sha256Json({
646
645
  repository: passport.repository,
647
646
  target: passport.target,
648
647
  source: passport.source,
649
648
  platformMatrix: passport.platformMatrix,
650
649
  buildchain: passport.buildchain,
651
- ...(passport.gateProfileEvidence ? { gateProfileEvidence: passport.gateProfileEvidence } : {}),
650
+ ...evidence("gateProfileEvidence"),
652
651
  ...(passport.familyEvidence ? { familyEvidence: passport.familyEvidence } : {}),
653
- ...(passport.controllerReceipts ? { controllerReceipts: passport.controllerReceipts } : {}),
652
+ ...evidence("consumerPolicy"),
653
+ ...evidence("controllerReceipts"),
654
654
  });
655
655
  if (normalizeDigest(passport.candidateHash, "releaseCandidatePassport.candidateHash") !== candidateHash) {
656
656
  throw new Error("release-candidate passport candidate hash mismatch");