@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
@@ -1,19 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
4
5
  import { pathToFileURL } from "node:url";
5
- import {
6
- createReleaseCandidatePassport,
7
- validateReleaseCandidatePassport,
8
- } from "../packages/core/release-candidate.js";
6
+ import { createReleaseCandidatePassport, validateReleaseCandidatePassport } from "../packages/core/release-candidate.js";
7
+ import { scanV4FloatingConsumerPolicy, v4ConsumerPolicyScannerRoot } from "../packages/core/v4-floating-consumer-policy.js";
9
8
  import { writeGitHubOutputs } from "./build-contract-core.mjs";
10
9
 
11
- function env(name, fallback = "") {
12
- return process.env[name] || fallback;
13
- }
10
+ const env = (name, fallback = "") => process.env[name] || fallback;
11
+ const readJsonFile = (filePath) => JSON.parse(fs.readFileSync(filePath, "utf8"));
14
12
 
15
- function readJsonFile(filePath) {
16
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
13
+ export function resolveLegacyConsumerPolicyReceipt(options = {}) {
14
+ if (options.json) return { receipt: JSON.parse(options.json), receiptRoot: options.receiptRoot };
15
+ if (options.repository !== "kungfu-systems/buildchain" || options.targetChannel !== "alpha" || options.workflowShellRef !== "v4-alpha" || options.runtimeOverride !== true) return undefined;
16
+ if (!/^[0-9a-f]{40}$/u.test(options.runtimeRef || "") || options.runtimeRef !== options.runtimeSha || options.sourceTreeHash !== options.runtimeTreeHash()) return undefined;
17
+ const root = path.resolve(options.root || ".buildchain/runtime");
18
+ const alphaLock = readJsonFile(path.join(root, ".buildchain/alpha-contract-lock.json"));
19
+ const result = scanV4FloatingConsumerPolicy({
20
+ root, repository: options.repository, sourceSha: options.sourceSha,
21
+ invokedWorkflow: options.invokedWorkflow || ".github/workflows/build.yml", invocationSourcePath: options.invocationSourcePath,
22
+ expectedInvocationChannel: "alpha", resolvedWorkflowSha: alphaLock.buildchain?.resolvedSha,
23
+ resolvedRuntimeSha: options.runtimeSha, scannerRoot: v4ConsumerPolicyScannerRoot(),
24
+ policy: readJsonFile(path.resolve(import.meta.dirname, "../architecture/v4-floating-consumer-policy.json")),
25
+ });
26
+ if (!result.ok) throw new Error(`legacy floating-shell policy receipt invalid: ${result.failures.map(({ code }) => code).join(", ")}`);
27
+ return { receipt: result.receipt, receiptRoot: result.receiptRoot };
17
28
  }
18
29
 
19
30
  export function generateReleaseCandidatePassportCli() {
@@ -21,23 +32,28 @@ export function generateReleaseCandidatePassportCli() {
21
32
  const outputPath = path.resolve(env("BUILDCHAIN_RC_PASSPORT_PATH", ".buildchain/artifacts/release-candidate-passport.json"));
22
33
  const buildSummary = readJsonFile(buildSummaryPath);
23
34
  const sourceSha = env("BUILDCHAIN_RC_SOURCE_HEAD_SHA", buildSummary.publishSource?.sha || buildSummary.git?.sha || "");
24
- const version = env(
25
- "BUILDCHAIN_RC_VERSION",
26
- buildSummary.publishSource?.consumerVersion || "",
27
- );
35
+ const version = env("BUILDCHAIN_RC_VERSION", buildSummary.publishSource?.consumerVersion || "");
28
36
  const gateAggregateJson = env("BUILDCHAIN_GATE_PROFILE_AGGREGATE_JSON");
29
37
  const gateAggregate = gateAggregateJson ? JSON.parse(gateAggregateJson) : undefined;
30
38
  const controllerReceiptJson = env("BUILDCHAIN_CONTROLLER_RECEIPT_JSON");
31
39
  const controllerReceipts = controllerReceiptJson ? [JSON.parse(controllerReceiptJson)] : [];
32
40
  const familyEvidenceJson = env("BUILDCHAIN_RC_FAMILY_EVIDENCE_JSON");
33
41
  const familyEvidence = familyEvidenceJson ? JSON.parse(familyEvidenceJson) : undefined;
42
+ const consumerPolicyJson = env("BUILDCHAIN_V4_POLICY_RECEIPT_JSON");
43
+ const consumerPolicyReceipt = resolveLegacyConsumerPolicyReceipt({
44
+ json: consumerPolicyJson, receiptRoot: env("BUILDCHAIN_V4_POLICY_RECEIPT_ROOT"),
45
+ repository: env("GITHUB_REPOSITORY", buildSummary.git?.repository || ""), sourceSha,
46
+ targetChannel: env("BUILDCHAIN_RC_TARGET_CHANNEL", buildSummary.publishSource?.channel || buildSummary.publishGate?.channel || ""),
47
+ runtimeRef: env("BUILDCHAIN_RUNTIME_REF", buildSummary.runtime?.ref || ""), runtimeSha: env("BUILDCHAIN_RUNTIME_SHA", buildSummary.runtime?.sha || ""),
48
+ workflowShellRef: env("BUILDCHAIN_WORKFLOW_SHELL_REF", buildSummary.runtime?.workflowShellRef || ""), runtimeOverride: buildSummary.runtime?.override === true,
49
+ root: ".buildchain/runtime", invocationSourcePath: env("GITHUB_WORKFLOW_REF"), sourceTreeHash: env("BUILDCHAIN_RC_SOURCE_TREE_HASH"),
50
+ runtimeTreeHash: () => execFileSync("git", ["-C", ".buildchain/runtime", "rev-parse", "HEAD^{tree}"], { encoding: "utf8" }).trim(),
51
+ });
34
52
  const passport = createReleaseCandidatePassport({
35
53
  repository: env("GITHUB_REPOSITORY", buildSummary.git?.repository || ""),
36
54
  pullRequest: {
37
- number: env("BUILDCHAIN_PULL_REQUEST_NUMBER"),
38
- url: env("BUILDCHAIN_PULL_REQUEST_URL"),
39
- headRef: env("BUILDCHAIN_PULL_REQUEST_HEAD_REF"),
40
- baseRef: env("BUILDCHAIN_PULL_REQUEST_BASE_REF"),
55
+ number: env("BUILDCHAIN_PULL_REQUEST_NUMBER"), url: env("BUILDCHAIN_PULL_REQUEST_URL"),
56
+ headRef: env("BUILDCHAIN_PULL_REQUEST_HEAD_REF"), baseRef: env("BUILDCHAIN_PULL_REQUEST_BASE_REF"),
41
57
  },
42
58
  targetChannel: env("BUILDCHAIN_RC_TARGET_CHANNEL", buildSummary.publishSource?.channel || buildSummary.publishGate?.channel || ""),
43
59
  version,
@@ -47,30 +63,21 @@ export function generateReleaseCandidatePassportCli() {
47
63
  sourceTreeHash: env("BUILDCHAIN_RC_SOURCE_TREE_HASH"),
48
64
  buildSummary,
49
65
  buildchain: {
50
- ref: env("BUILDCHAIN_RUNTIME_REF", buildSummary.runtime?.ref || ""),
51
- sha: env("BUILDCHAIN_RUNTIME_SHA", buildSummary.runtime?.sha || ""),
52
- version: env("BUILDCHAIN_RUNTIME_VERSION"),
53
- workflowShellRef: env("BUILDCHAIN_WORKFLOW_SHELL_REF", buildSummary.runtime?.workflowShellRef || ""),
66
+ ref: env("BUILDCHAIN_RUNTIME_REF", buildSummary.runtime?.ref || ""), sha: env("BUILDCHAIN_RUNTIME_SHA", buildSummary.runtime?.sha || ""),
67
+ version: env("BUILDCHAIN_RUNTIME_VERSION"), workflowShellRef: env("BUILDCHAIN_WORKFLOW_SHELL_REF", buildSummary.runtime?.workflowShellRef || ""),
54
68
  },
55
69
  gateAggregate,
56
70
  familyEvidence,
71
+ consumerPolicyReceipt,
57
72
  controllerReceipts,
58
73
  workflow: {
59
- name: env("GITHUB_WORKFLOW"),
60
- runId: env("GITHUB_RUN_ID", buildSummary.git?.runId || ""),
61
- runAttempt: env("GITHUB_RUN_ATTEMPT", buildSummary.git?.runAttempt || ""),
62
- url: env("BUILDCHAIN_WORKFLOW_RUN_URL"),
74
+ name: env("GITHUB_WORKFLOW"), runId: env("GITHUB_RUN_ID", buildSummary.git?.runId || ""),
75
+ runAttempt: env("GITHUB_RUN_ATTEMPT", buildSummary.git?.runAttempt || ""), url: env("BUILDCHAIN_WORKFLOW_RUN_URL"),
63
76
  },
64
77
  });
65
78
  const validation = validateReleaseCandidatePassport({
66
- passport,
67
- repository: env("GITHUB_REPOSITORY", buildSummary.git?.repository || ""),
68
- sourceHeadSha: sourceSha,
69
- buildSummary,
70
- });
71
- if (!validation.ok) {
72
- throw new Error(`release candidate passport invalid: ${validation.errors.join("; ")}`);
73
- }
79
+ passport, repository: env("GITHUB_REPOSITORY", buildSummary.git?.repository || ""), sourceHeadSha: sourceSha, buildSummary });
80
+ if (!validation.ok) throw new Error(`release candidate passport invalid: ${validation.errors.join("; ")}`);
74
81
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
75
82
  fs.writeFileSync(outputPath, `${JSON.stringify(passport, null, 2)}\n`);
76
83
  writeGitHubOutputs({
@@ -84,6 +91,7 @@ export function generateReleaseCandidatePassportCli() {
84
91
  platformCount: passport.platformMatrix.length,
85
92
  gateProfileEvidence: passport.gateProfileEvidence,
86
93
  familyEvidence: passport.familyEvidence,
94
+ consumerPolicy: passport.consumerPolicy,
87
95
  controllerReceipts: passport.controllerReceipts || [],
88
96
  }),
89
97
  });
@@ -327,7 +327,9 @@ function workflowArtifactPaths(type) {
327
327
  }
328
328
 
329
329
  function publicationWorkflowYaml() {
330
- return `name: Build
330
+ return `# Commit both .buildchain/contract-lock.json and .buildchain/alpha-contract-lock.json.
331
+ # Persist only @v4 or @v4-alpha; pass temporary train/SHA runtimes through workflow_dispatch.
332
+ name: Build
331
333
 
332
334
  on:
333
335
  workflow_dispatch:
@@ -361,6 +363,8 @@ jobs:
361
363
  function workflowYaml({ type, runnerPreset, artifactName }) {
362
364
  return `# Buildchain v4 lifecycle checkpoints are governed by architecture/v4-platform-stage-checkpoints.json.
363
365
  # Do not add undeclared runner-only inputs, outputs, environment, or provider effects.
366
+ # Commit both .buildchain/contract-lock.json and .buildchain/alpha-contract-lock.json.
367
+ # Persist only @v4 or @v4-alpha; pass temporary train/SHA runtimes through workflow_dispatch.
364
368
  name: Build
365
369
 
366
370
  on:
@@ -82,6 +82,10 @@ export function inspectArtifactSigningRequests({
82
82
  if (seen.has(key))
83
83
  throw new Error(`duplicate artifact signing request: ${key}`);
84
84
  seen.add(key);
85
+ const {
86
+ entitlementsProfile = "none",
87
+ entitlementsPaths = [],
88
+ } = request.signature;
85
89
  const item = {
86
90
  id: request.artifact.id,
87
91
  slug: safeId(request.artifact.id),
@@ -104,6 +108,8 @@ export function inspectArtifactSigningRequests({
104
108
  sourceSha: request.source.sha,
105
109
  sourceTreeSha: request.source.treeSha,
106
110
  transportFormat: request.artifact.transport?.format || "",
111
+ entitlementsProfile,
112
+ entitlementsPaths: entitlementsPaths.join(","),
107
113
  };
108
114
  if (request.signature.profile === "detached-signature-v1")
109
115
  matrices.detached.push(item);
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import crypto from "node:crypto";
3
3
  import fs from "node:fs";
4
+ import os from "node:os";
4
5
  import path from "node:path";
5
6
  import { pathToFileURL } from "node:url";
6
7
  import { spawnSyncCommand } from "../packages/core/spawn-command.js";
@@ -105,6 +106,20 @@ function sealedPackResult() {
105
106
  };
106
107
  }
107
108
 
109
+ function materializedPackResult({ cwd, registry }) {
110
+ const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "buildchain-npm-pack-"));
111
+ try {
112
+ const pack = parsePackResult(runNpm({
113
+ cwd,
114
+ args: ["pack", "--json", "--pack-destination", temporaryRoot, `--registry=${registry}`],
115
+ }).stdout);
116
+ return { ...pack, tarballPath: path.join(temporaryRoot, pack.filename), temporaryRoot };
117
+ } catch (error) {
118
+ fs.rmSync(temporaryRoot, { recursive: true, force: true });
119
+ throw error;
120
+ }
121
+ }
122
+
108
123
  function parseNpmView(stdout) {
109
124
  const raw = String(stdout || "").trim();
110
125
  if (!raw) {
@@ -188,46 +203,11 @@ export function npmPublishTransaction({
188
203
  const exactTag = assertPackageVersion({ pkg, expectedVersion });
189
204
  const distTag = readEnv("BUILDCHAIN_NPM_DIST_TAG", pkg.version.includes("-") ? "alpha" : "latest");
190
205
 
191
- const pack =
192
- sealedPackResult() ||
193
- parsePackResult(
194
- runNpm({
195
- cwd: resolvedCwd,
196
- args: ["pack", "--dry-run", "--json", `--registry=${registry}`],
197
- }).stdout,
198
- );
206
+ const pack = sealedPackResult() || materializedPackResult({ cwd: resolvedCwd, registry });
199
207
  const digest = artifactDigest(pack);
200
208
 
201
- const existingDigest = skipRegistryLookup
202
- ? undefined
203
- : publishedDigest({
204
- cwd: resolvedCwd,
205
- name: pkg.name,
206
- version: pkg.version,
207
- registry,
208
- });
209
- let publishAction = "already-published";
210
- if (existingDigest) {
211
- if (existingDigest !== digest) {
212
- throw new Error(`artifact digest mismatch: npm:${pkg.name}@${pkg.version}`);
213
- }
214
- } else if (dryRunPublish) {
215
- publishAction = "dry-run";
216
- } else {
217
- runNpm({
218
- cwd: resolvedCwd,
219
- args: [
220
- "publish",
221
- ...(pack.tarballPath ? [pack.tarballPath] : []),
222
- "--access",
223
- access,
224
- "--tag",
225
- distTag,
226
- `--registry=${registry}`,
227
- ],
228
- });
229
- publishAction = "published";
230
- const registryDigest = skipRegistryLookup
209
+ try {
210
+ const existingDigest = skipRegistryLookup
231
211
  ? undefined
232
212
  : publishedDigest({
233
213
  cwd: resolvedCwd,
@@ -235,60 +215,92 @@ export function npmPublishTransaction({
235
215
  version: pkg.version,
236
216
  registry,
237
217
  });
238
- if (registryDigest && registryDigest !== digest) {
239
- throw new Error(`artifact digest mismatch: npm:${pkg.name}@${pkg.version}`);
218
+ let publishAction = "already-published";
219
+ if (existingDigest) {
220
+ if (existingDigest !== digest) {
221
+ throw new Error(`artifact digest mismatch: npm:${pkg.name}@${pkg.version}`);
222
+ }
223
+ } else if (dryRunPublish) {
224
+ publishAction = "dry-run";
225
+ } else {
226
+ runNpm({
227
+ cwd: resolvedCwd,
228
+ args: [
229
+ "publish",
230
+ ...(pack.tarballPath ? [pack.tarballPath] : []),
231
+ "--access",
232
+ access,
233
+ "--tag",
234
+ distTag,
235
+ `--registry=${registry}`,
236
+ ],
237
+ });
238
+ publishAction = "published";
239
+ const registryDigest = skipRegistryLookup
240
+ ? undefined
241
+ : publishedDigest({
242
+ cwd: resolvedCwd,
243
+ name: pkg.name,
244
+ version: pkg.version,
245
+ registry,
246
+ });
247
+ if (registryDigest && registryDigest !== digest) {
248
+ throw new Error(`artifact digest mismatch: npm:${pkg.name}@${pkg.version}`);
249
+ }
240
250
  }
241
- }
242
251
 
243
- const evidencePath = readEnv("BUILDCHAIN_PUBLISH_EVIDENCE");
244
- if (!evidencePath) {
245
- throw new Error("BUILDCHAIN_PUBLISH_EVIDENCE is required");
246
- }
247
- const evidence = {
248
- schema: 1,
249
- version: expectedVersion || pkg.version,
250
- channel: readEnv("BUILDCHAIN_CHANNEL"),
251
- source_sha: readEnv("BUILDCHAIN_SOURCE_SHA"),
252
- release_sha: readEnv("BUILDCHAIN_RELEASE_SHA"),
253
- target_ref: readEnv("BUILDCHAIN_TARGET_REF"),
254
- release_material_sha: readEnv("BUILDCHAIN_RELEASE_MATERIAL_SHA", readEnv("BUILDCHAIN_RELEASE_SHA")),
255
- publish_tooling_sha: readEnv("BUILDCHAIN_PUBLISH_TOOLING_SHA", readEnv("BUILDCHAIN_RELEASE_SHA")),
256
- artifacts: [{
257
- group: "node",
258
- kind: "npm",
259
- name: pkg.name,
260
- ref: pkg.version,
261
- digest,
262
- }],
263
- };
264
- const resolvedEvidencePath = writeEvidence({
265
- cwd: resolvedCwd,
266
- evidencePath,
267
- evidence,
268
- });
269
- writeGitHubOutputs({
270
- version: pkg.version,
271
- "exact-tag": exactTag,
272
- "dist-tag": distTag,
273
- "artifact-digest": digest,
274
- "publish-action": publishAction,
275
- "publish-evidence": resolvedEvidencePath,
276
- });
277
- return {
278
- schemaVersion: 1,
279
- package: {
280
- name: pkg.name,
252
+ const evidencePath = readEnv("BUILDCHAIN_PUBLISH_EVIDENCE");
253
+ if (!evidencePath) {
254
+ throw new Error("BUILDCHAIN_PUBLISH_EVIDENCE is required");
255
+ }
256
+ const evidence = {
257
+ schema: 1,
258
+ version: expectedVersion || pkg.version,
259
+ channel: readEnv("BUILDCHAIN_CHANNEL"),
260
+ source_sha: readEnv("BUILDCHAIN_SOURCE_SHA"),
261
+ release_sha: readEnv("BUILDCHAIN_RELEASE_SHA"),
262
+ target_ref: readEnv("BUILDCHAIN_TARGET_REF"),
263
+ release_material_sha: readEnv("BUILDCHAIN_RELEASE_MATERIAL_SHA", readEnv("BUILDCHAIN_RELEASE_SHA")),
264
+ publish_tooling_sha: readEnv("BUILDCHAIN_PUBLISH_TOOLING_SHA", readEnv("BUILDCHAIN_RELEASE_SHA")),
265
+ artifacts: [{
266
+ group: "node",
267
+ kind: "npm",
268
+ name: pkg.name,
269
+ ref: pkg.version,
270
+ digest,
271
+ }],
272
+ };
273
+ const resolvedEvidencePath = writeEvidence({
274
+ cwd: resolvedCwd,
275
+ evidencePath,
276
+ evidence,
277
+ });
278
+ writeGitHubOutputs({
281
279
  version: pkg.version,
282
- },
283
- exactTag,
284
- distTag,
285
- registry,
286
- publishAction,
287
- pack,
288
- sealedBundleRoot: readEnv("BUILDCHAIN_SEALED_BUNDLE_ROOT"),
289
- evidencePath: resolvedEvidencePath,
290
- evidence,
291
- };
280
+ "exact-tag": exactTag,
281
+ "dist-tag": distTag,
282
+ "artifact-digest": digest,
283
+ "publish-action": publishAction,
284
+ "publish-evidence": resolvedEvidencePath,
285
+ });
286
+ return {
287
+ schemaVersion: 1,
288
+ package: {
289
+ name: pkg.name,
290
+ version: pkg.version,
291
+ },
292
+ exactTag,
293
+ distTag,
294
+ registry,
295
+ publishAction,
296
+ pack: { ...pack, temporaryRoot: undefined },
297
+ sealedBundleRoot: readEnv("BUILDCHAIN_SEALED_BUNDLE_ROOT"),
298
+ evidencePath: resolvedEvidencePath,
299
+ evidence,
300
+ };
301
+ } finally {
302
+ if (pack.temporaryRoot) fs.rmSync(pack.temporaryRoot, { recursive: true, force: true });
303
+ }
292
304
  }
293
305
 
294
306
  function usage() {
@@ -8,12 +8,11 @@ import { pathToFileURL } from "node:url";
8
8
  import { writeGitHubOutputs } from "./build-contract-core.mjs";
9
9
  import {
10
10
  generatePublishRequiredArtifacts,
11
- readNpmPackageArtifact,
12
- selectReleaseCandidateArtifacts,
13
- } from "./release-candidate-resolver.mjs";
14
- import {
15
11
  githubDownload,
16
12
  githubJson,
13
+ readNpmPackageArtifact,
14
+ selectPayloadArtifacts,
15
+ selectReleaseCandidateArtifacts,
17
16
  unzip,
18
17
  verifyArtifactArchive,
19
18
  } from "./release-candidate-resolver.mjs";
@@ -156,7 +155,7 @@ async function downloadArtifact({ artifact, repoInfo, apiUrl, token, archiveDir,
156
155
  };
157
156
  }
158
157
 
159
- function candidateArtifactNames({ passport, selected, artifacts, artifactPatterns }) {
158
+ export function candidateArtifactNames({ passport, selected, artifacts, artifactPatterns }) {
160
159
  const names = new Set([selected.passport.name, selected.summary.name]);
161
160
  for (const reference of passport.controllerReceipts || []) {
162
161
  if (!reference.artifact) throw new Error(`Passport controller receipt ${reference.controllerId} has no artifact identity`);
@@ -171,11 +170,9 @@ function candidateArtifactNames({ passport, selected, artifacts, artifactPattern
171
170
  for (const artifact of artifacts) {
172
171
  if (manifestPattern.test(String(artifact.name || ""))) names.add(artifact.name);
173
172
  }
174
- const matchers = splitPatterns(artifactPatterns).map(patternMatcher);
175
- for (const artifact of artifacts) {
176
- if (matchers.some((matcher) => matcher.test(String(artifact.name || "")))) names.add(artifact.name);
177
- }
178
- return names;
173
+ const publicationNames = new Set(selectPayloadArtifacts({ artifacts, artifactName: selected.prefix, sourceSha: selected.sourceSha, patterns: artifactPatterns }).map((artifact) => artifact.name));
174
+ for (const name of publicationNames) names.add(name);
175
+ return { names, publicationNames };
179
176
  }
180
177
 
181
178
  export function normalizePlatformManifests(downloads, passport) {
@@ -352,7 +349,7 @@ async function recoverCandidateEvidence({
352
349
  for (const artifact of [selected.passport, selected.summary]) initialDownloads.push(await downloadArtifact({ artifact, repoInfo, apiUrl, token, archiveDir, bundleRoot, fetchImpl }));
353
350
  const passport = readOnlyJson(initialDownloads[0].files.filter((file) => path.basename(file.path) === "release-candidate-passport.json"), "release-candidate-passport.json");
354
351
  const buildSummary = readOnlyJson(initialDownloads[1].files.filter((file) => path.basename(file.path) === "build-summary.json"), "build-summary.json");
355
- const requiredNames = candidateArtifactNames({ passport, selected, artifacts, artifactPatterns });
352
+ const { names: requiredNames, publicationNames } = candidateArtifactNames({ passport, selected, artifacts, artifactPatterns });
356
353
  const chosen = artifacts.filter((artifact) => requiredNames.has(artifact.name));
357
354
  if (chosen.length !== requiredNames.size) {
358
355
  const found = new Set(chosen.map((artifact) => artifact.name));
@@ -363,7 +360,7 @@ async function recoverCandidateEvidence({
363
360
  for (const artifact of chosen.filter((entry) => ![selected.passport.id, selected.summary.id].includes(entry.id))) downloads.push(await downloadArtifact({ artifact, repoInfo, apiUrl, token, archiveDir, bundleRoot, fetchImpl }));
364
361
  return {
365
362
  run, workflow, selected, resolvedOutput, bundleRoot, initialDownloads,
366
- passport, buildSummary, chosen, downloads,
363
+ passport, buildSummary, chosen, downloads, publicationNames,
367
364
  };
368
365
  }
369
366
 
@@ -396,7 +393,7 @@ export async function resumeFromCandidateRun({
396
393
  try {
397
394
  const {
398
395
  run, workflow, selected, resolvedOutput, bundleRoot, initialDownloads,
399
- passport, buildSummary, chosen, downloads,
396
+ passport, buildSummary, chosen, downloads, publicationNames,
400
397
  } = await recoverCandidateEvidence({
401
398
  repoInfo, runId, artifactName, artifactPatterns, requiredArtifactCount,
402
399
  outputDir, apiUrl, token, fetchImpl, archiveDir,
@@ -412,7 +409,7 @@ export async function resumeFromCandidateRun({
412
409
  const controllerReceipts = normalizeControllerReceipts(downloads, passport);
413
410
  const productPayloadManifests = normalizeProductPayloadManifests(downloads);
414
411
  const publication = createRecoveredPublication({
415
- downloads,
412
+ downloads: downloads.filter(({ artifact }) => publicationNames.has(artifact.name)),
416
413
  bundleRoot,
417
414
  repository: repoInfo.fullName,
418
415
  passport,
@@ -381,6 +381,12 @@ export function sealArtifactSigningRequests({
381
381
  signature: {
382
382
  profile: declaration.profile,
383
383
  required: declaration.required,
384
+ ...(declaration.entitlementsProfile !== "none"
385
+ ? {
386
+ entitlementsProfile: declaration.entitlementsProfile,
387
+ entitlementsPaths: declaration.entitlementsPaths,
388
+ }
389
+ : {}),
384
390
  },
385
391
  });
386
392
  const check = validateArtifactSigningRequest(request);
@@ -201,6 +201,8 @@ export function nodeApiMeta(exportName) {
201
201
  "./kfd-agent-hub": { group: "kfd-trust", summary: "Declarative Agent Hub adapter inspection, fixed-suite execution, exact KFD cut locking, and agent explanation APIs." },
202
202
  "./release-passport-contract": { group: "release-passport-trust", summary: "Standalone release passport JSON Schema, ownership/check manifest, and structural validation APIs." },
203
203
  "./release-candidate": { group: "reusable-build", summary: "PR-stage release-candidate artifact, passport, and promote-only resolver APIs." },
204
+ "./v4-floating-consumer-policy": { group: "governance-versioning", summary: "Semantic floating-selector admission, dual-lock receipt, and independent v4 consumer certification APIs." },
205
+ "./v4-runtime-ref-resume-authority": { group: "release-passport-trust", summary: "Transient exact-runtime authorization, persisted-selector rejection, fresh-attempt Stage Capsule reuse, and A+B Passport lineage APIs." },
204
206
  "./release-candidate-recovery": { group: "release-passport-trust", summary: "Fail-closed verification and immutable receipts for reusing a successful sealed candidate run without rebuilding product payloads." },
205
207
  "./release-train": { group: "governance-versioning", summary: "Frozen Release Cut identity, rooted Release Train transitions, Dev observations, supersession, replay, and legacy read compatibility APIs." },
206
208
  "./stable-candidate-ledger": { group: "governance-versioning", summary: "Immutable alpha candidate ledger, qualification, revocation, selection, and exact stable source-lock APIs." },