@kungfu-tech/buildchain 3.0.9-alpha.3 → 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 (43) 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 +58 -28
  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 +48 -18
  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/aws-us-elastic-runner-burst-plane.md +14 -0
  15. package/docs/cli-reference.md +79 -13
  16. package/docs/dev-delivery-warrant.md +53 -19
  17. package/docs/kfd-support.md +7 -2
  18. package/docs/node-api-reference.md +59 -51
  19. package/docs/release-governance.md +5 -4
  20. package/docs/release-passport.md +8 -2
  21. package/docs/versioning.md +1 -0
  22. package/package.json +3 -2
  23. package/packages/core/artifact-verification-envelope.js +5 -5
  24. package/packages/core/dev-delivery-native-proof.js +333 -0
  25. package/packages/core/dev-delivery-warrant-settlement.js +5 -1
  26. package/packages/core/dev-delivery-warrant.js +40 -6
  27. package/packages/core/kfd-adopter-manifest.js +46 -47
  28. package/packages/core/release-passport-contract.js +5 -5
  29. package/packages/core/release-passport.js +5 -4
  30. package/scripts/aws-macos-jit-controller-core.mjs +106 -2
  31. package/scripts/aws-macos-jit-controller.mjs +119 -1
  32. package/scripts/aws-macos-jit-source-rebind.mjs +194 -14
  33. package/scripts/buildchain-cli-help.mjs +6 -2
  34. package/scripts/check-internal-architecture.mjs +1 -0
  35. package/scripts/dev-delivery-native-run.mjs +187 -0
  36. package/scripts/dev-delivery-proof.mjs +54 -2
  37. package/scripts/dev-delivery-two-phase.mjs +598 -0
  38. package/scripts/dev-delivery-warrant.mjs +38 -3
  39. package/scripts/dev-pr-delivery-warrant.mjs +6 -1
  40. package/scripts/generate-channel-build-workflow.mjs +4 -0
  41. package/scripts/init-repo.mjs +16 -0
  42. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  43. package/templates/native-dev-delivery.yml +82 -0
@@ -28,7 +28,137 @@ function priorOwnershipPlan(plan) {
28
28
  };
29
29
  }
30
30
 
31
- function campaignRunsWithNoWork(plan) {
31
+ function assertRunMatchesPolicy(policy, run, jobs, jobCount, artifactCount) {
32
+ if (policy === "unused") {
33
+ if (jobCount !== 0 || artifactCount !== 0) {
34
+ throw new Error(
35
+ `macOS campaign run ${run.id} already has jobs or artifacts`,
36
+ );
37
+ }
38
+ return;
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
+ }
66
+ if (
67
+ run.status !== "completed" ||
68
+ run.conclusion !== "failure" ||
69
+ jobCount === 0 ||
70
+ artifactCount === 0 ||
71
+ (jobs.jobs || []).some((job) => job.status !== "completed")
72
+ ) {
73
+ throw new Error(
74
+ `macOS campaign run ${run.id} is not a terminal failed run with retained evidence`,
75
+ );
76
+ }
77
+ }
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
+
161
+ function campaignRunReceipts(plan) {
32
162
  const branch = sourceRefName(plan.source.ref);
33
163
  const response = ghJson(
34
164
  [
@@ -46,7 +176,8 @@ function campaignRunsWithNoWork(plan) {
46
176
  }
47
177
  const receipts = [];
48
178
  for (const run of matching) {
49
- if (run.head_sha !== plan.previousSource.sha) {
179
+ const classification = classifyCampaignRun(plan, run);
180
+ if (run.head_sha !== classification.expectedSourceSha) {
50
181
  throw new Error(
51
182
  `macOS campaign run ${run.id} is bound to an unexpected source`,
52
183
  );
@@ -68,19 +199,37 @@ function campaignRunsWithNoWork(plan) {
68
199
  const artifactCount = Number(
69
200
  artifacts.total_count || (artifacts.artifacts || []).length || 0,
70
201
  );
71
- if (jobCount !== 0 || artifactCount !== 0) {
72
- throw new Error(
73
- `macOS campaign run ${run.id} already has jobs or artifacts`,
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,
74
213
  );
75
214
  }
76
215
  receipts.push({
77
216
  runId: String(run.id),
217
+ sourceSha: String(run.head_sha || ""),
218
+ classification: classification.classification,
78
219
  status: String(run.status || "unknown"),
79
220
  conclusion: String(run.conclusion || ""),
80
221
  jobCount,
81
222
  artifactCount,
223
+ artifacts: (artifacts.artifacts || []).map((artifact) => ({
224
+ id: String(artifact.id),
225
+ name: String(artifact.name || ""),
226
+ sizeInBytes: Number(artifact.size_in_bytes || 0),
227
+ digest: String(artifact.digest || ""),
228
+ expired: artifact.expired === true,
229
+ })),
82
230
  });
83
231
  }
232
+ assertExactReceiptInventories(plan, receipts);
84
233
  return receipts;
85
234
  }
86
235
 
@@ -286,8 +435,17 @@ function assertZeroCampaignResidue(plan, profile) {
286
435
  if (matchingRunners.length !== 0) {
287
436
  throw new Error("macOS campaign already has a registered JIT runner");
288
437
  }
289
- const evidenceObjectCounts = {};
290
- for (const sourceSha of [plan.previousSource.sha, plan.source.sha]) {
438
+ const evidenceObjects = {};
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) {
291
449
  const evidence = awsJson(
292
450
  plan,
293
451
  profile,
@@ -299,27 +457,44 @@ function assertZeroCampaignResidue(plan, profile) {
299
457
  "--prefix",
300
458
  `macos/${sourceSha}/`,
301
459
  "--max-keys",
302
- "1",
460
+ "1000",
303
461
  "--output",
304
462
  "json",
305
463
  ],
306
464
  `macOS campaign evidence inventory for ${sourceSha}`,
307
465
  );
466
+ if (evidence.IsTruncated === true) {
467
+ throw new Error(
468
+ `macOS campaign source ${sourceSha} evidence inventory is incomplete`,
469
+ );
470
+ }
471
+ const objects = (evidence.Contents || []).map((object) => ({
472
+ key: String(object.Key || ""),
473
+ size: Number(object.Size || 0),
474
+ etag: String(object.ETag || ""),
475
+ }));
308
476
  const count = Number(
309
477
  evidence.KeyCount || (evidence.Contents || []).length || 0,
310
478
  );
311
- if (count !== 0) {
479
+ const terminalEvidenceSource =
480
+ (sourceSha === plan.previousSource.sha &&
481
+ plan.github.priorRunPolicy === "terminal-failure") ||
482
+ historicalTerminalSources.includes(sourceSha);
483
+ if (
484
+ (!terminalEvidenceSource && count !== 0) ||
485
+ (terminalEvidenceSource && count === 0)
486
+ ) {
312
487
  throw new Error(
313
- `macOS campaign source ${sourceSha} already has bootstrap evidence`,
488
+ `macOS campaign source ${sourceSha} bootstrap evidence does not match the rebind policy`,
314
489
  );
315
490
  }
316
- evidenceObjectCounts[sourceSha] = count;
491
+ evidenceObjects[sourceSha] = objects;
317
492
  }
318
493
  return {
319
- priorRuns: campaignRunsWithNoWork(plan),
494
+ priorRuns: campaignRunReceipts(plan),
320
495
  jitParameterCount: parameters.length,
321
496
  runnerCount: matchingRunners.length,
322
- evidenceObjectCounts,
497
+ evidenceObjects,
323
498
  };
324
499
  }
325
500
 
@@ -480,7 +655,12 @@ export function executeMacosJitSourceRebind(plan, { profile = "" } = {}) {
480
655
  schemaVersion: 1,
481
656
  contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
482
657
  kind: "campaign-source-rebind-result",
483
- status: "rebound-zero-allocation",
658
+ status:
659
+ plan.github.priorRunPolicy === "terminal-failure"
660
+ ? "rebound-after-terminal-failure-zero-allocation"
661
+ : plan.github.priorRunPolicy === "preflight-failure"
662
+ ? "rebound-after-preflight-failure-zero-allocation"
663
+ : "rebound-zero-allocation",
484
664
  repository: plan.repository,
485
665
  campaign: plan.campaign,
486
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() {