@kungfu-tech/buildchain 3.0.9-alpha.4 → 3.0.9-alpha.5

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 (42) hide show
  1. package/bin/buildchain.mjs +5 -1
  2. package/dist/site/buildchain-contract.json +9 -9
  3. package/dist/site/buildchain-site.json +56 -26
  4. package/dist/site/cli-registry.json +18 -2
  5. package/dist/site/kfd-claims.json +8 -5
  6. package/dist/site/kfd-upstream-aggregate.json +1 -1
  7. package/dist/site/manual-registry.json +6 -6
  8. package/dist/site/node-api-registry.json +290 -49
  9. package/dist/site/page-registry.json +46 -16
  10. package/dist/site/public-surface-audit.json +122 -6
  11. package/dist/site/publication-registry.json +4 -4
  12. package/dist/site/site-manifest.json +10 -10
  13. package/dist/site/workflow-registry.json +12 -6
  14. package/docs/cli-reference.md +79 -13
  15. package/docs/dev-delivery-warrant.md +53 -19
  16. package/docs/kfd-support.md +7 -2
  17. package/docs/node-api-reference.md +59 -51
  18. package/docs/release-governance.md +5 -4
  19. package/docs/release-passport.md +8 -2
  20. package/docs/versioning.md +1 -0
  21. package/package.json +3 -2
  22. package/packages/core/artifact-verification-envelope.js +5 -5
  23. package/packages/core/dev-delivery-native-proof.js +333 -0
  24. package/packages/core/dev-delivery-warrant-settlement.js +5 -1
  25. package/packages/core/dev-delivery-warrant.js +40 -6
  26. package/packages/core/kfd-adopter-manifest.js +46 -47
  27. package/packages/core/release-passport-contract.js +5 -5
  28. package/packages/core/release-passport.js +5 -4
  29. package/scripts/aws-macos-jit-controller-core.mjs +82 -2
  30. package/scripts/aws-macos-jit-controller.mjs +81 -1
  31. package/scripts/aws-macos-jit-source-rebind.mjs +147 -24
  32. package/scripts/buildchain-cli-help.mjs +6 -2
  33. package/scripts/check-internal-architecture.mjs +1 -0
  34. package/scripts/dev-delivery-native-run.mjs +187 -0
  35. package/scripts/dev-delivery-proof.mjs +54 -2
  36. package/scripts/dev-delivery-two-phase.mjs +598 -0
  37. package/scripts/dev-delivery-warrant.mjs +38 -3
  38. package/scripts/dev-pr-delivery-warrant.mjs +6 -1
  39. package/scripts/generate-channel-build-workflow.mjs +4 -0
  40. package/scripts/init-repo.mjs +16 -0
  41. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  42. package/templates/native-dev-delivery.yml +82 -0
@@ -28,8 +28,8 @@ function priorOwnershipPlan(plan) {
28
28
  };
29
29
  }
30
30
 
31
- function assertRunMatchesPolicy(plan, run, jobs, jobCount, artifactCount) {
32
- if (plan.github.priorRunPolicy === "unused") {
31
+ function assertRunMatchesPolicy(policy, run, jobs, jobCount, artifactCount) {
32
+ if (policy === "unused") {
33
33
  if (jobCount !== 0 || artifactCount !== 0) {
34
34
  throw new Error(
35
35
  `macOS campaign run ${run.id} already has jobs or artifacts`,
@@ -37,6 +37,32 @@ function assertRunMatchesPolicy(plan, run, jobs, jobCount, artifactCount) {
37
37
  }
38
38
  return;
39
39
  }
40
+ if (policy === "preflight-failure") {
41
+ const jobList = jobs.jobs || [];
42
+ const failedJobs = jobList.filter((job) => job.conclusion === "failure");
43
+ const nonFailedJobs = jobList.filter((job) => job.conclusion !== "failure");
44
+ const hasJitLabel = jobList.some((job) =>
45
+ (job.labels || []).some((label) =>
46
+ String(label.name || label).startsWith(MACOS_JIT_RUNNER_LABEL_PREFIX),
47
+ ),
48
+ );
49
+ if (
50
+ run.status !== "completed" ||
51
+ run.conclusion !== "failure" ||
52
+ jobCount === 0 ||
53
+ artifactCount === 0 ||
54
+ jobList.some((job) => job.status !== "completed") ||
55
+ failedJobs.length !== 1 ||
56
+ !String(failedJobs[0].name || "").endsWith(" / Trust gate") ||
57
+ nonFailedJobs.some((job) => job.conclusion !== "skipped") ||
58
+ hasJitLabel
59
+ ) {
60
+ throw new Error(
61
+ `macOS campaign run ${run.id} is not an exact preflight Trust gate failure`,
62
+ );
63
+ }
64
+ return;
65
+ }
40
66
  if (
41
67
  run.status !== "completed" ||
42
68
  run.conclusion !== "failure" ||
@@ -50,6 +76,88 @@ function assertRunMatchesPolicy(plan, run, jobs, jobCount, artifactCount) {
50
76
  }
51
77
  }
52
78
 
79
+ function assertExactStartupFailure(run, jobCount, artifactCount) {
80
+ if (
81
+ run.status !== "completed" ||
82
+ run.conclusion !== "startup_failure" ||
83
+ jobCount !== 0 ||
84
+ artifactCount !== 0
85
+ ) {
86
+ throw new Error(
87
+ `macOS campaign run ${run.id} is not an exact startup failure`,
88
+ );
89
+ }
90
+ }
91
+
92
+ function classifyCampaignRun(plan, run) {
93
+ const startupFailure = plan.github.startupFailureRuns.find(
94
+ ({ runId }) => runId === String(run.id),
95
+ );
96
+ const historicalTerminalFailure =
97
+ plan.github.historicalTerminalFailureRuns.find(
98
+ ({ runId }) => runId === String(run.id),
99
+ );
100
+ const preflightFailure = plan.github.preflightFailureRunIds.includes(
101
+ String(run.id),
102
+ );
103
+ return {
104
+ startupFailure,
105
+ historicalTerminalFailure,
106
+ expectedSourceSha:
107
+ startupFailure?.sourceSha ||
108
+ historicalTerminalFailure?.sourceSha ||
109
+ plan.previousSource.sha,
110
+ classification: startupFailure
111
+ ? "startup-failure"
112
+ : historicalTerminalFailure
113
+ ? "historical-terminal-failure"
114
+ : preflightFailure
115
+ ? "preflight-failure"
116
+ : plan.github.priorRunPolicy,
117
+ };
118
+ }
119
+
120
+ function exactReceiptInventory(receipts, classification, withSource = false) {
121
+ return receipts
122
+ .filter((receipt) => receipt.classification === classification)
123
+ .map((receipt) =>
124
+ withSource
125
+ ? { runId: receipt.runId, sourceSha: receipt.sourceSha }
126
+ : receipt.runId,
127
+ )
128
+ .sort(
129
+ (left, right) =>
130
+ Number(withSource ? left.runId : left) -
131
+ Number(withSource ? right.runId : right),
132
+ );
133
+ }
134
+
135
+ function assertExactReceiptInventories(plan, receipts) {
136
+ if (plan.github.priorRunPolicy === "unused") return;
137
+ const inventories = [
138
+ ["terminal-failure", plan.github.terminalFailureRunIds, false],
139
+ ["preflight-failure", plan.github.preflightFailureRunIds, false],
140
+ ["startup-failure", plan.github.startupFailureRuns, true],
141
+ [
142
+ "historical-terminal-failure",
143
+ plan.github.historicalTerminalFailureRuns,
144
+ true,
145
+ ],
146
+ ];
147
+ for (const [classification, expected, withSource] of inventories) {
148
+ const observed = exactReceiptInventory(
149
+ receipts,
150
+ classification,
151
+ withSource,
152
+ );
153
+ if (JSON.stringify(observed) !== JSON.stringify(expected)) {
154
+ throw new Error(
155
+ `macOS campaign ${classification} inventory does not match the confirmed runs`,
156
+ );
157
+ }
158
+ }
159
+ }
160
+
53
161
  function campaignRunReceipts(plan) {
54
162
  const branch = sourceRefName(plan.source.ref);
55
163
  const response = ghJson(
@@ -68,7 +176,8 @@ function campaignRunReceipts(plan) {
68
176
  }
69
177
  const receipts = [];
70
178
  for (const run of matching) {
71
- if (run.head_sha !== plan.previousSource.sha) {
179
+ const classification = classifyCampaignRun(plan, run);
180
+ if (run.head_sha !== classification.expectedSourceSha) {
72
181
  throw new Error(
73
182
  `macOS campaign run ${run.id} is bound to an unexpected source`,
74
183
  );
@@ -90,9 +199,23 @@ function campaignRunReceipts(plan) {
90
199
  const artifactCount = Number(
91
200
  artifacts.total_count || (artifacts.artifacts || []).length || 0,
92
201
  );
93
- assertRunMatchesPolicy(plan, run, jobs, jobCount, artifactCount);
202
+ if (classification.startupFailure) {
203
+ assertExactStartupFailure(run, jobCount, artifactCount);
204
+ } else {
205
+ assertRunMatchesPolicy(
206
+ classification.historicalTerminalFailure
207
+ ? "terminal-failure"
208
+ : plan.github.priorRunPolicy,
209
+ run,
210
+ jobs,
211
+ jobCount,
212
+ artifactCount,
213
+ );
214
+ }
94
215
  receipts.push({
95
216
  runId: String(run.id),
217
+ sourceSha: String(run.head_sha || ""),
218
+ classification: classification.classification,
96
219
  status: String(run.status || "unknown"),
97
220
  conclusion: String(run.conclusion || ""),
98
221
  jobCount,
@@ -106,19 +229,7 @@ function campaignRunReceipts(plan) {
106
229
  })),
107
230
  });
108
231
  }
109
- if (plan.github.priorRunPolicy === "terminal-failure") {
110
- const observed = receipts
111
- .map((receipt) => receipt.runId)
112
- .sort((left, right) => Number(left) - Number(right));
113
- if (
114
- JSON.stringify(observed) !==
115
- JSON.stringify(plan.github.terminalFailureRunIds)
116
- ) {
117
- throw new Error(
118
- "macOS campaign terminal failed run inventory does not match the confirmed run ids",
119
- );
120
- }
121
- }
232
+ assertExactReceiptInventories(plan, receipts);
122
233
  return receipts;
123
234
  }
124
235
 
@@ -325,7 +436,16 @@ function assertZeroCampaignResidue(plan, profile) {
325
436
  throw new Error("macOS campaign already has a registered JIT runner");
326
437
  }
327
438
  const evidenceObjects = {};
328
- for (const sourceSha of [plan.previousSource.sha, plan.source.sha]) {
439
+ const historicalTerminalSources =
440
+ plan.github.historicalTerminalFailureRuns.map(({ sourceSha }) => sourceSha);
441
+ const evidenceSources = [
442
+ ...new Set([
443
+ plan.previousSource.sha,
444
+ ...historicalTerminalSources,
445
+ plan.source.sha,
446
+ ]),
447
+ ];
448
+ for (const sourceSha of evidenceSources) {
329
449
  const evidence = awsJson(
330
450
  plan,
331
451
  profile,
@@ -356,12 +476,13 @@ function assertZeroCampaignResidue(plan, profile) {
356
476
  const count = Number(
357
477
  evidence.KeyCount || (evidence.Contents || []).length || 0,
358
478
  );
359
- const previousTerminalEvidence =
360
- sourceSha === plan.previousSource.sha &&
361
- plan.github.priorRunPolicy === "terminal-failure";
479
+ const terminalEvidenceSource =
480
+ (sourceSha === plan.previousSource.sha &&
481
+ plan.github.priorRunPolicy === "terminal-failure") ||
482
+ historicalTerminalSources.includes(sourceSha);
362
483
  if (
363
- (!previousTerminalEvidence && count !== 0) ||
364
- (previousTerminalEvidence && count === 0)
484
+ (!terminalEvidenceSource && count !== 0) ||
485
+ (terminalEvidenceSource && count === 0)
365
486
  ) {
366
487
  throw new Error(
367
488
  `macOS campaign source ${sourceSha} bootstrap evidence does not match the rebind policy`,
@@ -537,7 +658,9 @@ export function executeMacosJitSourceRebind(plan, { profile = "" } = {}) {
537
658
  status:
538
659
  plan.github.priorRunPolicy === "terminal-failure"
539
660
  ? "rebound-after-terminal-failure-zero-allocation"
540
- : "rebound-zero-allocation",
661
+ : plan.github.priorRunPolicy === "preflight-failure"
662
+ ? "rebound-after-preflight-failure-zero-allocation"
663
+ : "rebound-zero-allocation",
541
664
  repository: plan.repository,
542
665
  campaign: plan.campaign,
543
666
  previousSource: plan.previousSource,
@@ -129,11 +129,15 @@ export const BUILDCHAIN_USAGE = `Usage:
129
129
  [--from-config | --workflow <required-workflow.yml>...] [--cwd <dir>]
130
130
  [--check-response-timeout-minutes <n>]
131
131
  [--max-entries-to-build <n>] [--apply]
132
- buildchain dev warrant <submit|select|heartbeat|recover|close|cancel-queued|observe>
132
+ buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|cancel-queued|observe>
133
133
  --repository <owner/repo> --branch <dev/vN/vN.M>
134
134
  [--execute] [--output <file>] [--json]
135
- buildchain dev proof <source|verify-source|classify|replay|integration|verify-integration>
135
+ buildchain dev proof <source|verify-source|classify|native|verify-native|classify-native|verify-native-reuse|replay|integration|verify-integration>
136
136
  [--output <file>] [--json]
137
+ buildchain dev two-phase --repository <owner/repo> --branch <dev/vN/vN.M>
138
+ --pull-request <n> --expected-head <sha>
139
+ --warrant-result <file> [--native-proof <file>]
140
+ [--native-command <command>]
137
141
  buildchain log <info|warn|error> --event <name> [--phase <phase>]
138
142
  [--component <name>] [--source <name>] [--attribute key=value]...
139
143
  [--path <jsonl>] [--json]
@@ -131,6 +131,7 @@ function repositoryJavaScriptFiles(root) {
131
131
  .filter((file) => [".js", ".mjs", ".cjs"].includes(path.extname(file)))
132
132
  .filter((file) => !file.startsWith("tests/"))
133
133
  .filter((file) => !/(?:^|\/)tests?\//u.test(file))
134
+ .filter((file) => !file.startsWith(".buildchain/"))
134
135
  .filter((file) => !/^actions\/[^/]+\/dist\//u.test(file))
135
136
  .sort();
136
137
  }
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import { devDeliveryContentRoot } from "../packages/core/dev-delivery-warrant.js";
7
+ import { runDevDeliveryCommand } from "./dev-delivery-warrant.mjs";
8
+
9
+ function flag(args, name, fallback = "") {
10
+ const index = args.indexOf(`--${name}`);
11
+ return index === -1 ? fallback : args[index + 1] || "";
12
+ }
13
+
14
+ function positiveInteger(value, label, fallback) {
15
+ const parsed = Number(value || fallback);
16
+ if (!Number.isInteger(parsed) || parsed < 1) {
17
+ throw new Error(`${label} must be a positive integer`);
18
+ }
19
+ return parsed;
20
+ }
21
+
22
+ function exactRoot(value, label) {
23
+ const normalized = String(value || "")
24
+ .trim()
25
+ .toLowerCase();
26
+ if (!/^sha256:[0-9a-f]{64}$/u.test(normalized)) {
27
+ throw new Error(`${label} must be a sha256 content root`);
28
+ }
29
+ return normalized;
30
+ }
31
+
32
+ export async function runNativeWithHeartbeat({
33
+ command,
34
+ cwd = process.cwd(),
35
+ heartbeat,
36
+ executionBinding = {},
37
+ intervalMs = 30_000,
38
+ spawnImpl = spawn,
39
+ now = () => new Date().toISOString(),
40
+ } = {}) {
41
+ if (!String(command || "").trim()) {
42
+ throw new Error("native command is required");
43
+ }
44
+ if (typeof heartbeat !== "function") {
45
+ throw new Error("heartbeat callback is required");
46
+ }
47
+ const startedAt = now();
48
+ try {
49
+ await heartbeat();
50
+ } catch (error) {
51
+ throw new Error(`native heartbeat failed: ${error.message}`);
52
+ }
53
+ let heartbeatCount = 1;
54
+ let heartbeatError = null;
55
+ let heartbeatRunning = false;
56
+ const child = spawnImpl("bash", ["-lc", command], {
57
+ cwd,
58
+ env: process.env,
59
+ stdio: "inherit",
60
+ });
61
+
62
+ const beat = async () => {
63
+ if (heartbeatRunning || heartbeatError) return;
64
+ heartbeatRunning = true;
65
+ try {
66
+ await heartbeat();
67
+ heartbeatCount += 1;
68
+ } catch (error) {
69
+ heartbeatError = error;
70
+ child.kill("SIGTERM");
71
+ } finally {
72
+ heartbeatRunning = false;
73
+ }
74
+ };
75
+ const timer = setInterval(beat, intervalMs);
76
+ const exit = await new Promise((resolve, reject) => {
77
+ child.once("error", reject);
78
+ child.once("exit", (code, signal) => resolve({ code, signal }));
79
+ }).finally(() => clearInterval(timer));
80
+ while (heartbeatRunning) {
81
+ await new Promise((resolve) => setTimeout(resolve, 10));
82
+ }
83
+ if (heartbeatError) {
84
+ throw new Error(`native heartbeat failed: ${heartbeatError.message}`);
85
+ }
86
+ if (exit.code !== 0) {
87
+ throw new Error(
88
+ `native command failed with ${exit.signal || `exit ${exit.code}`}`,
89
+ );
90
+ }
91
+ await beat();
92
+ const completedAt = now();
93
+ const receipt = {
94
+ schema: "kungfu.buildchain.native-heartbeat-run-receipt/v1",
95
+ outcome: "succeeded",
96
+ commandRoot: devDeliveryContentRoot({ command }),
97
+ executionBindingRoot: devDeliveryContentRoot(executionBinding),
98
+ startedAt,
99
+ completedAt,
100
+ heartbeatCount,
101
+ };
102
+ return { ...receipt, receiptRoot: devDeliveryContentRoot(receipt) };
103
+ }
104
+
105
+ async function main() {
106
+ const args = process.argv.slice(2);
107
+ const repository = flag(args, "repository", process.env.GITHUB_REPOSITORY);
108
+ const branch = flag(
109
+ args,
110
+ "branch",
111
+ process.env.BUILDCHAIN_DEV_DELIVERY_BRANCH || process.env.GITHUB_BASE_REF,
112
+ );
113
+ const command = flag(
114
+ args,
115
+ "command",
116
+ process.env.BUILDCHAIN_DEV_DELIVERY_NATIVE_COMMAND,
117
+ );
118
+ const cwd = path.resolve(
119
+ flag(args, "working-directory", process.env.GITHUB_WORKSPACE),
120
+ );
121
+ const fencingToken = exactRoot(
122
+ flag(
123
+ args,
124
+ "fencing-token",
125
+ process.env.BUILDCHAIN_DEV_DELIVERY_FENCING_TOKEN,
126
+ ),
127
+ "fencingToken",
128
+ );
129
+ const leaseGeneration = positiveInteger(
130
+ flag(
131
+ args,
132
+ "lease-generation",
133
+ process.env.BUILDCHAIN_DEV_DELIVERY_LEASE_GENERATION,
134
+ ),
135
+ "leaseGeneration",
136
+ );
137
+ const leaseSeconds = positiveInteger(
138
+ flag(
139
+ args,
140
+ "lease-seconds",
141
+ process.env.BUILDCHAIN_DEV_DELIVERY_LEASE_SECONDS,
142
+ ),
143
+ "leaseSeconds",
144
+ 3600,
145
+ );
146
+ const heartbeatSeconds = positiveInteger(
147
+ flag(
148
+ args,
149
+ "heartbeat-seconds",
150
+ process.env.BUILDCHAIN_DEV_DELIVERY_HEARTBEAT_SECONDS,
151
+ ),
152
+ "heartbeatSeconds",
153
+ Math.max(15, Math.floor(leaseSeconds / 3)),
154
+ );
155
+ if (heartbeatSeconds >= leaseSeconds) {
156
+ throw new Error("heartbeatSeconds must be less than leaseSeconds");
157
+ }
158
+ const output = path.resolve(
159
+ flag(args, "output", ".buildchain/dev-delivery/native-heartbeat-run.json"),
160
+ );
161
+ const result = await runNativeWithHeartbeat({
162
+ command,
163
+ cwd,
164
+ intervalMs: heartbeatSeconds * 1000,
165
+ heartbeat: async () => {
166
+ await runDevDeliveryCommand({
167
+ command: "heartbeat",
168
+ repository,
169
+ branch,
170
+ fencingToken,
171
+ leaseGeneration,
172
+ leaseSeconds,
173
+ execute: true,
174
+ });
175
+ },
176
+ });
177
+ fs.mkdirSync(path.dirname(output), { recursive: true });
178
+ fs.writeFileSync(output, `${JSON.stringify(result, null, 2)}\n`);
179
+ process.stdout.write(`Native heartbeat run: ${result.receiptRoot}\n`);
180
+ }
181
+
182
+ if (import.meta.url === `file://${process.argv[1]}`) {
183
+ main().catch((error) => {
184
+ console.error(`buildchain dev native: ${error.message}`);
185
+ process.exit(1);
186
+ });
187
+ }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "../packages/core/dev-delivery-warrant.js";
4
+ import { classifyDevDeliveryDelta, createIntegrationDeliveryProof, createNativeProofReuseDecision, createNativeQualificationProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyNativeProofReuseDecision, verifyNativeQualificationProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "../packages/core/dev-delivery-warrant.js";
5
5
 
6
6
  function flag(args, name, fallback = "") {
7
7
  const index = args.indexOf(`--${name}`);
@@ -52,6 +52,7 @@ export function devDeliveryProofCliOptions(args = [], environment = process.env)
52
52
  affectedPaths: flag(rest, "affected-paths-json", environment.BUILDCHAIN_DEV_DELIVERY_AFFECTED_PATHS || "[]"),
53
53
  shardEvidenceRoots: flag(rest, "shard-evidence-roots-json", environment.BUILDCHAIN_DEV_DELIVERY_SHARD_EVIDENCE_ROOTS || "[]"),
54
54
  qualifiedAt: flag(rest, "qualified-at", environment.BUILDCHAIN_DEV_DELIVERY_QUALIFIED_AT),
55
+ qualifiedBase: flag(rest, "qualified-base", environment.BUILDCHAIN_DEV_DELIVERY_QUALIFIED_BASE),
55
56
  sourceProofPath: flag(rest, "source-proof", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF),
56
57
  sourceProofRoot: flag(rest, "source-proof-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF_ROOT),
57
58
  warrantResultPath: flag(rest, "warrant-result", environment.BUILDCHAIN_DEV_DELIVERY_WARRANT_RESULT),
@@ -117,6 +118,57 @@ export function runDevDeliveryProofCommand(options) {
117
118
  },
118
119
  });
119
120
  }
121
+ if (options.command === "native") {
122
+ return createNativeQualificationProof({
123
+ repository: options.repository,
124
+ protectedBase: options.protectedBase,
125
+ sourceIdentityRoot: options.sourceIdentityRoot,
126
+ sourcePatchRoot: options.sourcePatchRoot,
127
+ planRoot: options.planRoot,
128
+ closureRoot: options.closureRoot,
129
+ dependencyRoot: options.dependencyRoot,
130
+ toolchainRoot: options.toolchainRoot,
131
+ qualifiedBase: options.qualifiedBase,
132
+ affectedPaths: jsonList(options.affectedPaths, "affected paths"),
133
+ shardEvidenceRoots: jsonList(options.shardEvidenceRoots, "shard evidence roots", { required: true }),
134
+ qualifiedAt: options.qualifiedAt,
135
+ });
136
+ }
137
+ if (options.command === "verify-native") {
138
+ return verifyNativeQualificationProof(jsonFile(options.sourceProofPath, "native proof"));
139
+ }
140
+ if (options.command === "classify-native") {
141
+ return createNativeProofReuseDecision({
142
+ proof: jsonFile(options.sourceProofPath, "native proof"),
143
+ current: {
144
+ sourceIdentityRoot: options.sourceIdentityRoot,
145
+ sourcePatchRoot: options.sourcePatchRoot,
146
+ planRoot: options.planRoot,
147
+ closureRoot: options.closureRoot,
148
+ dependencyRoot: options.dependencyRoot,
149
+ toolchainRoot: options.toolchainRoot,
150
+ currentBase: options.currentBase,
151
+ graphKnown: options.graphKnown,
152
+ changedPaths: jsonList(options.changedPaths, "changed paths"),
153
+ },
154
+ });
155
+ }
156
+ if (options.command === "verify-native-reuse") {
157
+ return verifyNativeProofReuseDecision(jsonFile(options.qualificationReceiptPath, "native reuse decision"), {
158
+ proof: jsonFile(options.sourceProofPath, "native proof"),
159
+ current: {
160
+ sourceIdentityRoot: options.sourceIdentityRoot,
161
+ sourcePatchRoot: options.sourcePatchRoot,
162
+ planRoot: options.planRoot,
163
+ closureRoot: options.closureRoot,
164
+ dependencyRoot: options.dependencyRoot,
165
+ toolchainRoot: options.toolchainRoot,
166
+ currentBase: options.currentBase,
167
+ graphKnown: options.graphKnown,
168
+ changedPaths: jsonList(options.changedPaths, "changed paths"),
169
+ },
170
+ });
171
+ }
120
172
  if (options.command === "replay") {
121
173
  return createProjectCutReplayPlan({
122
174
  repository: options.repository,
@@ -168,7 +220,7 @@ export function runDevDeliveryProofCommand(options) {
168
220
  }
169
221
 
170
222
  function usage() {
171
- return "Usage:\n buildchain dev proof <source|verify-source|classify|replay|replay-proof|verify-replay|integration|verify-integration> [options] [--output FILE] [--json]\n";
223
+ return "Usage:\n buildchain dev proof <source|verify-source|classify|native|verify-native|classify-native|verify-native-reuse|replay|replay-proof|verify-replay|integration|verify-integration> [options] [--output FILE] [--json]\n";
172
224
  }
173
225
 
174
226
  async function main() {