@vela-science/canopus 0.6.5 → 0.8.0

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 (120) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +67 -139
  3. package/SECURITY.md +3 -2
  4. package/capsules/erdos1056-k15/bin/linux-arm64/{10428801-10429000 → 10429201-10429400}/verifier +0 -0
  5. package/capsules/erdos1056-k15/bin/linux-x86_64/{10428801-10429000 → 10429201-10429400}/verifier +0 -0
  6. package/compatibility.json +25 -0
  7. package/dist/src/activity/events.d.ts +4 -1
  8. package/dist/src/activity/events.js +8 -2
  9. package/dist/src/activity/store.d.ts +2 -2
  10. package/dist/src/activity/store.js +4 -3
  11. package/dist/src/{receipt/map.d.ts → candidate/finalize.d.ts} +2 -9
  12. package/dist/src/candidate/finalize.js +71 -0
  13. package/dist/src/cli.js +88 -229
  14. package/dist/src/contracts/mission.d.ts +1 -13
  15. package/dist/src/contracts/mission.js +4 -42
  16. package/dist/src/contracts/validation.d.ts +1 -25
  17. package/dist/src/contracts/validation.js +1 -95
  18. package/dist/src/engines/codex-tools-native.js +2 -2
  19. package/dist/src/index.d.ts +5 -34
  20. package/dist/src/index.js +4 -34
  21. package/dist/src/mission/prepare.d.ts +10 -1
  22. package/dist/src/mission/prepare.js +29 -30
  23. package/dist/src/product/coverage.d.ts +17 -0
  24. package/dist/src/product/coverage.js +137 -0
  25. package/dist/src/product/doctor.d.ts +1 -2
  26. package/dist/src/product/doctor.js +29 -8
  27. package/dist/src/product/profile.js +8 -2
  28. package/dist/src/product/replay.js +11 -12
  29. package/dist/src/product/run.d.ts +5 -9
  30. package/dist/src/product/run.js +17 -88
  31. package/dist/src/product/submission.d.ts +40 -0
  32. package/dist/src/product/submission.js +217 -0
  33. package/dist/src/product/submit.d.ts +22 -0
  34. package/dist/src/product/submit.js +264 -0
  35. package/dist/src/product/version.d.ts +2 -2
  36. package/dist/src/product/version.js +27 -4
  37. package/dist/src/projection/current-run.d.ts +12 -0
  38. package/dist/src/projection/current-run.js +94 -0
  39. package/dist/src/projection/failure.d.ts +17 -7
  40. package/dist/src/projection/failure.js +43 -12
  41. package/dist/src/projection/retained-mission.d.ts +6 -0
  42. package/dist/src/projection/retained-mission.js +41 -0
  43. package/dist/src/projection/retained-run.d.ts +7 -0
  44. package/dist/src/projection/retained-run.js +50 -0
  45. package/dist/src/run.d.ts +43 -41
  46. package/dist/src/run.js +34 -412
  47. package/dist/src/util/canonical.d.ts +1 -5
  48. package/dist/src/util/canonical.js +1 -53
  49. package/dist/src/vela/cli.d.ts +7 -51
  50. package/dist/src/vela/cli.js +102 -523
  51. package/dist/src/vela/types.d.ts +3 -19
  52. package/dist/src/verifier/run.js +1 -1
  53. package/dist/src/workspace/prepare.d.ts +1 -1
  54. package/dist/src/workspace/prepare.js +15 -15
  55. package/docs/EVALUATION.md +142 -0
  56. package/docs/MISSIONS.md +42 -80
  57. package/docs/RUN_RECORD.md +77 -99
  58. package/docs/adr/0010-nonmutating-runs-and-explicit-submission.md +191 -0
  59. package/missions/erdos1056-k15-next/mission.draft.json +10 -8
  60. package/package.json +18 -22
  61. package/profiles/{erdos1056-k15-10428801-10429000.json → erdos1056-k15-10429201-10429400.json} +10 -10
  62. package/schemas/failure.v1.json +17 -0
  63. package/schemas/mission.v0.json +3 -4
  64. package/schemas/mission.v1.json +7 -28
  65. package/schemas/{run.v1.json → run.v2.json} +58 -116
  66. package/schemas/submission-bundle.v1.json +63 -0
  67. package/toolchain.lock.json +31 -0
  68. package/BUILD_WEEK.md +0 -198
  69. package/advisories/erdos1056-claim-fidelity/output.schema.json +0 -90
  70. package/advisories/erdos1056-claim-fidelity/registration.json +0 -42
  71. package/advisories/erdos1056-claim-fidelity/results/assessment.json +0 -1
  72. package/advisories/erdos1056-claim-fidelity/results/verification.json +0 -1
  73. package/capsules/formal-erdos-505-test-dim-one/verifier +0 -105
  74. package/capsules/quantum-10-1-4/verifier.py +0 -152
  75. package/capsules/sidon-a24/bin/linux-arm64/verifier +0 -0
  76. package/capsules/sidon-a24/bin/linux-x86_64/verifier +0 -0
  77. package/dist/src/capability/withdrawal.d.ts +0 -47
  78. package/dist/src/capability/withdrawal.js +0 -487
  79. package/dist/src/engines/codex-exec.d.ts +0 -17
  80. package/dist/src/engines/codex-exec.js +0 -136
  81. package/dist/src/engines/codex-tool-free.d.ts +0 -26
  82. package/dist/src/engines/codex-tool-free.js +0 -213
  83. package/dist/src/product/withdraw.d.ts +0 -8
  84. package/dist/src/product/withdraw.js +0 -255
  85. package/dist/src/projection/diagnostic.d.ts +0 -12
  86. package/dist/src/projection/diagnostic.js +0 -61
  87. package/dist/src/projection/public-run.d.ts +0 -59
  88. package/dist/src/projection/public-run.js +0 -106
  89. package/dist/src/projection/publication.d.ts +0 -36
  90. package/dist/src/projection/publication.js +0 -47
  91. package/dist/src/projection/run.d.ts +0 -95
  92. package/dist/src/projection/run.js +0 -173
  93. package/dist/src/receipt/map.js +0 -168
  94. package/dist/src/repair/plan.d.ts +0 -2
  95. package/dist/src/repair/plan.js +0 -22
  96. package/dist/src/roles.d.ts +0 -2
  97. package/dist/src/roles.js +0 -9
  98. package/docs/RELEASES.md +0 -1389
  99. package/evidence/build-week/run_eb6bcd46-cffd-4ae8-b630-2681bd84da71.public.json +0 -1
  100. package/evidence/build-week/run_f68e4cfc-e5c7-4c73-86cb-d79807c47ec4.public.json +0 -1
  101. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/pending-commands.json +0 -1
  102. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/public-run.json +0 -1
  103. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/root-manifest.json +0 -1
  104. package/evidence/erdos/run_192b3bef-9d6e-49e5-b72d-7ae903b29d5e/web-import.json +0 -1
  105. package/evidence/erdos/run_b3b0cf07-b149-4d53-8258-76ba0e1fc0a5/pending-commands.json +0 -1
  106. package/evidence/erdos/run_b3b0cf07-b149-4d53-8258-76ba0e1fc0a5/public-run.json +0 -1
  107. package/evidence/erdos/run_b3b0cf07-b149-4d53-8258-76ba0e1fc0a5/root-manifest.json +0 -1
  108. package/evidence/erdos/run_b3b0cf07-b149-4d53-8258-76ba0e1fc0a5/web-import.json +0 -1
  109. package/missions/formal-erdos-505-test-dim-one-gpt56/mission.draft.json +0 -40
  110. package/missions/quantum-10-1-4-retry/mission.draft.json +0 -40
  111. package/missions/sidon-a24-at-least-7194-gpt56/mission.draft.json +0 -42
  112. package/missions/sidon-a24-at-least-7194-gpt56-v2/mission.draft.json +0 -42
  113. package/missions/sidon-a24-at-least-7194-gpt56-v3/mission.draft.json +0 -42
  114. package/profiles/formal-erdos-505-test-dim-one-gpt56.json +0 -35
  115. package/profiles/quantum-10-1-4-stabilizer-retry.json +0 -35
  116. package/profiles/sidon-a24-at-least-7194-gpt56-v2.json +0 -35
  117. package/profiles/sidon-a24-at-least-7194-gpt56-v3.json +0 -35
  118. package/profiles/sidon-a24-at-least-7194-gpt56.json +0 -35
  119. package/schemas/canopus.public-run.v1.json +0 -116
  120. package/scripts/run-claim-fidelity-advisory.mjs +0 -235
package/dist/src/cli.js CHANGED
@@ -3,48 +3,37 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { fileURLToPath } from "node:url";
6
- import { mkdir, mkdtemp, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
6
+ import { readdir, stat } from "node:fs/promises";
7
7
  import { parseMission } from "./contracts/mission.js";
8
- import { CodexExecEngine } from "./engines/codex-exec.js";
9
- import { CodexToolsNativeEngine } from "./engines/codex-tools-native.js";
10
8
  import { prepareMission, validateMissionBundle } from "./mission/prepare.js";
11
- import { parseDiagnosticRunRecord, projectDiagnosticRun } from "./projection/diagnostic.js";
9
+ import { projectCurrentRun } from "./projection/current-run.js";
10
+ import { parseRetainedRunRecord } from "./projection/retained-run.js";
12
11
  import { parseFailureRecord, projectFailure } from "./projection/failure.js";
13
- import { parseRunRecord, projectRun } from "./projection/run.js";
14
- import { projectPublicRun } from "./projection/public-run.js";
15
- import { buildPublicationBundle } from "./projection/publication.js";
16
12
  import { doctorProduct } from "./product/doctor.js";
17
13
  import { replayProduct } from "./product/replay.js";
18
14
  import { runProduct } from "./product/run.js";
19
- import { withdrawProduct } from "./product/withdraw.js";
15
+ import { exportSubmission } from "./product/submission.js";
16
+ import { submitBundle } from "./product/submit.js";
20
17
  import { CANOPUS_VERSION } from "./product/version.js";
21
18
  import { listProductProfiles, packProductProfile, validateProductProfile, } from "./product/profile-bundle.js";
22
19
  import { loadProductProfile } from "./product/profile.js";
23
- import { runCanopus } from "./run.js";
24
20
  import { readBoundedRegularFile } from "./util/files.js";
25
- import { canonicalJson, contentDigest } from "./util/canonical.js";
26
- import { VelaClient } from "./vela/cli.js";
27
- import { withdrawalCapabilityStatus } from "./capability/withdrawal.js";
28
21
  function usage() {
29
22
  return `Canopus — bounded Vela research harness
30
23
 
31
24
  Primary workflow:
32
- canopus --version
33
25
  canopus doctor [frontier] [--profile <name>]
34
- canopus run [frontier] [--first | --target <id>] [--profile <name>] \\
35
- [--output <dir>] [--no-land]
36
- canopus inspect [run.json | latest]
37
- canopus public-run <run.json> --mission <mission.json> \
38
- --repository <public-url> --output <file>
39
- canopus publish-run <run.json> --mission <mission.json> \
40
- --repository <public-url> --output <new-directory>
26
+ canopus run [frontier] [--first | --target <id>] [--profile <name>] [--output <dir>]
27
+ canopus show [run.json | failure.json | latest]
41
28
  canopus replay <run.json>
42
- canopus withdraw [frontier] [--run <run.json|latest>] --reason <text>
29
+ canopus export <run.json | latest> [--output <new-directory>] [--as <agent:id>] \\
30
+ [--claim <corrected-bounded-claim> --scope-limit <limit>]
31
+ canopus submit <submission-bundle> [frontier] [--vela <binary>]
43
32
 
44
33
  Mission v1 prepare/validate remains available under advanced help.
45
34
 
46
- Canopus may land a Receipt v1 as an agent after verifier success. It cannot
47
- sign, accept, or make a human scientific decision.`;
35
+ Run and export never mutate Vela. submit explicitly registers an authenticated
36
+ Submission as pending review. Canopus cannot verify, decide, or change Standing.`;
48
37
  }
49
38
  function missionUsage() {
50
39
  return `Usage:
@@ -69,19 +58,19 @@ remain replay-only; new tool-using missions require a validated version 2 profil
69
58
  function runUsage() {
70
59
  return `Usage:
71
60
  canopus run [frontier] [--first | --target <id>] [--profile <name>] \\
72
- [--output <dir>] [--no-land]
61
+ [--output <dir>]
73
62
 
74
63
  Discovers and binds Vela, Codex, Git, Docker, the exact frontier roots, and the
75
- registered verifier profile. --no-land runs the worker and verifier in disposable
76
- clones and leaves the source frontier unchanged.`;
64
+ registered verifier profile. Runs the worker and verifier in disposable clones
65
+ and always leaves the source frontier unchanged.`;
77
66
  }
78
- function inspectUsage() {
67
+ function showUsage() {
79
68
  return `Usage:
80
- canopus inspect [run.json | failure.json | latest]
69
+ canopus show [run.json | failure.json | latest]
81
70
 
82
- Projects the newest completed or safely stopped non-authoritative run record
83
- without mutating Vela. A failed run never implies that landing was unchanged;
84
- inspect reports whether retained landing-recovery evidence is required.`;
71
+ Shows the newest completed or safely stopped non-authoritative Run without
72
+ mutating Vela. Historical formats remain readable through their exact Canopus
73
+ release; the current product accepts only Run v2 and failure records.`;
85
74
  }
86
75
  function doctorUsage() {
87
76
  return `Usage:
@@ -98,31 +87,23 @@ function replayUsage() {
98
87
  Re-runs the frozen verifier over the content-addressed candidate without a
99
88
  model call, Vela mutation, network, or authority action.`;
100
89
  }
101
- function publicRunUsage() {
102
- return `Usage:
103
- canopus public-run <run.json> --mission <mission.json> \\
104
- --repository <public-url> --output <file>
105
-
106
- Generates one canonical, sanitized canopus.public-run.v1 projection from a
107
- submission-ready completed run. Raw worker logs, homes, credentials, private
108
- paths, and unrestricted transcripts are never copied.`;
109
- }
110
- function publishRunUsage() {
90
+ function exportUsage() {
111
91
  return `Usage:
112
- canopus publish-run <run.json> --mission <mission.json> \\
113
- --repository <public-url> --output <new-directory>
92
+ canopus export <run.json | latest> [--output <new-directory>] [--as <agent:id>] \\
93
+ [--claim <corrected-bounded-claim> --scope-limit <limit>]
114
94
 
115
- Creates a sanitized public projection, root manifest, proposal-scoped pending
116
- commands, and a read-only Vela Observatory import descriptor. It never lands,
117
- signs, accepts, pushes, or deploys.`;
95
+ Creates a signed portable vela.submission.v1 bundle from a successful Run.
96
+ Export does not touch a frontier and does not create Verification or Standing.
97
+ The correction pair is accepted only when the retained Run Claim has stale
98
+ verifier wording or control bytes; the immutable Run remains unchanged.`;
118
99
  }
119
- function withdrawUsage() {
100
+ function submitUsage() {
120
101
  return `Usage:
121
- canopus withdraw [frontier] [--run <run.json|latest>] --reason <text>
102
+ canopus submit <submission-bundle> [frontier] [--vela <binary>]
122
103
 
123
- Uses only the retained proposal-scoped producer capability. The operation runs
124
- in a disposable exact-head clone, verifies strict and clean-clone replay, then
125
- fast-forwards the clean source. It never mounts a worker or human key.`;
104
+ Explicitly registers one authenticated Submission through Vela in a disposable
105
+ exact-head clone, then fast-forwards the clean source checkout. The result is
106
+ pending review with zero accepted-event delta.`;
126
107
  }
127
108
  function isHelp(value) {
128
109
  return value === "--help" || value === "-h" || value === "help";
@@ -179,9 +160,6 @@ function required(values, key) {
179
160
  throw new Error(`${key} is required`);
180
161
  return value;
181
162
  }
182
- function authHome(values) {
183
- return path.resolve(values.get("--codex-home") ?? process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex"));
184
- }
185
163
  function packagedOutputSchema() {
186
164
  return fileURLToPath(new URL("../../schemas/engine-output.v0.json", import.meta.url));
187
165
  }
@@ -285,67 +263,6 @@ async function profileCommand(args) {
285
263
  }
286
264
  throw new Error(`unknown profile command ${subcommand}`);
287
265
  }
288
- async function runMission(file, rest) {
289
- const values = options(rest, [
290
- "--source",
291
- "--run-root",
292
- "--vela",
293
- "--docker",
294
- "--codex",
295
- "--codex-version",
296
- "--codex-sha256",
297
- "--model",
298
- "--codex-home",
299
- ]);
300
- const mission = parseMission(await jsonFile(file));
301
- const sourceRepo = path.resolve(required(values, "--source"));
302
- const runRoot = path.resolve(required(values, "--run-root"));
303
- const velaBinary = path.resolve(required(values, "--vela"));
304
- const outputSchema = mission.schema === "canopus.mission.v1"
305
- ? path.join(path.dirname(path.resolve(file)), "contract", "engine-output.v0.json")
306
- : packagedOutputSchema();
307
- const vela = new VelaClient({
308
- binary: velaBinary,
309
- expectedVersion: mission.vela_version,
310
- expectedSha256: mission.vela_sha256,
311
- home: path.join(runRoot, "vela-home"),
312
- });
313
- const engine = mission.schema === "canopus.mission.v1"
314
- ? new CodexToolsNativeEngine({
315
- binary: path.resolve(required(values, "--codex")),
316
- authHome: authHome(values),
317
- outputSchema,
318
- permissionProfile: path.join(path.dirname(path.resolve(file)), mission.worker.permission_profile_path),
319
- })
320
- : new CodexExecEngine({
321
- binary: path.resolve(required(values, "--codex")),
322
- expectedSha256: required(values, "--codex-sha256"),
323
- expectedVersion: required(values, "--codex-version"),
324
- model: required(values, "--model"),
325
- authHome: authHome(values),
326
- outputSchema,
327
- });
328
- const result = await runCanopus({
329
- mission,
330
- sourceRepo,
331
- runRoot,
332
- vela,
333
- engine,
334
- bundleRoot: path.dirname(path.resolve(file)),
335
- dockerBinary: values.get("--docker") ?? "docker",
336
- });
337
- process.stdout.write(`${JSON.stringify({
338
- ok: true,
339
- command: "run",
340
- run_id: result.record.run_id,
341
- candidate_digest: result.record.candidate.digest,
342
- receipt_root: result.record.landing.receipt_root,
343
- route: result.record.landing.route,
344
- accepted_event_delta: result.record.landing.accepted_event_delta,
345
- clean_clone_reproduced: result.record.reproduction.matched,
346
- run_file: path.join(result.paths.root, "run.json"),
347
- })}\n`);
348
- }
349
266
  async function doctorCommand(args) {
350
267
  const parsed = productOptions(args, ["--profile"], []);
351
268
  if (parsed.positional.length > 1)
@@ -358,7 +275,7 @@ async function doctorCommand(args) {
358
275
  process.stdout.write(`${JSON.stringify(result.public)}\n`);
359
276
  }
360
277
  async function productRunCommand(args) {
361
- const parsed = productOptions(args, ["--target", "--profile", "--output", "--codex-home"], ["--first", "--no-land"]);
278
+ const parsed = productOptions(args, ["--target", "--profile", "--output", "--codex-home"], ["--first"]);
362
279
  if (parsed.positional.length > 1)
363
280
  throw new Error("run accepts at most one frontier");
364
281
  if (parsed.flags.has("--first") && parsed.values.has("--target")) {
@@ -375,25 +292,20 @@ async function productRunCommand(args) {
375
292
  ...(requested === undefined ? {} : { requestedTarget: requested }),
376
293
  ...(outputRoot === undefined ? {} : { outputRoot: path.resolve(outputRoot) }),
377
294
  ...(codexHome === undefined ? {} : { codexHome: path.resolve(codexHome) }),
378
- noLand: parsed.flags.has("--no-land"),
379
295
  });
380
- const landing = result.run.record.landing;
381
296
  process.stdout.write(`${JSON.stringify({
382
297
  ok: true,
383
298
  command: "run",
384
- mode: landing === null ? "no_land" : "land",
299
+ effect: "none",
385
300
  run_id: result.run.record.run_id,
386
301
  target: result.run.record.mission.target,
387
302
  candidate_digest: result.run.record.candidate.digest,
388
303
  verifier_status: result.run.record.verifier.status,
389
304
  observed_tokens: result.run.record.budget.observed_tokens,
390
- receipt_root: landing?.receipt_root ?? null,
391
- proposal_id: landing?.proposal_id ?? null,
392
- route: landing?.route ?? null,
393
- accepted_event_delta: landing?.accepted_event_delta ?? null,
305
+ submission: null,
394
306
  clean_clone_reproduced: result.run.record.reproduction.matched,
395
307
  evidence_root: result.evidence_root,
396
- source_publication: result.source_publication,
308
+ source_state: result.source_state,
397
309
  run_file: path.join(result.run.paths.root, "run.json"),
398
310
  })}\n`);
399
311
  }
@@ -430,9 +342,9 @@ async function latestInspectionFile() {
430
342
  ranked.sort((left, right) => right.modified - left.modified || left.file.localeCompare(right.file));
431
343
  return ranked[0]?.file ?? (() => { throw new Error("no inspectable Canopus product run was found"); })();
432
344
  }
433
- async function inspectCommand(value, rest) {
345
+ async function showCommand(value, rest) {
434
346
  if (rest.length !== 0)
435
- throw new Error("inspect accepts at most one run file");
347
+ throw new Error("show accepts at most one run file");
436
348
  const file = value === undefined || value === "latest"
437
349
  ? await latestInspectionFile()
438
350
  : path.resolve(value);
@@ -440,95 +352,52 @@ async function inspectCommand(value, rest) {
440
352
  const schema = typeof raw === "object" && raw !== null && !Array.isArray(raw)
441
353
  ? raw.schema
442
354
  : undefined;
443
- const projection = schema === "canopus.diagnostic-run.v1"
444
- ? projectDiagnosticRun(parseDiagnosticRunRecord(raw))
445
- : schema === "canopus.failure.v0"
355
+ const projection = schema === "canopus.run.v2"
356
+ ? projectCurrentRun(parseRetainedRunRecord(raw).record)
357
+ : schema === "canopus.failure.v0" || schema === "canopus.failure.v1"
446
358
  ? projectFailure(parseFailureRecord(raw))
447
- : projectRun(parseRunRecord(raw));
448
- const withdrawal = "proposal_id" in projection
449
- ? await withdrawalCapabilityStatus(projection.proposal_id)
450
- : { state: "not_applicable", available: false };
451
- process.stdout.write(`${JSON.stringify({ ok: true, command: "inspect", run_file: file, projection, withdrawal })}\n`);
359
+ : (() => {
360
+ throw new Error("current Canopus accepts only canopus.run.v2 and retained failure records; use the exact historical release for older Run schemas");
361
+ })();
362
+ process.stdout.write(`${JSON.stringify({ ok: true, command: "show", run_file: file, projection })}\n`);
452
363
  }
453
364
  async function replayCommand(file, rest) {
454
365
  if (file === undefined || rest.length !== 0)
455
366
  throw new Error("replay requires exactly one run file");
456
367
  process.stdout.write(`${JSON.stringify(await replayProduct(path.resolve(file)))}\n`);
457
368
  }
458
- async function publicRunCommand(file, rest) {
459
- if (file === undefined)
460
- throw new Error("public-run requires one completed run file");
461
- const values = options(rest, ["--mission", "--repository", "--output"]);
462
- const record = parseRunRecord(await jsonFile(file));
463
- const mission = parseMission(await jsonFile(required(values, "--mission")));
464
- const projection = projectPublicRun({
465
- record,
466
- mission,
467
- repository: required(values, "--repository"),
468
- });
469
- const output = path.resolve(required(values, "--output"));
470
- await writeFile(output, canonicalJson(projection), { flag: "wx", mode: 0o644 });
471
- process.stdout.write(`${JSON.stringify({
472
- ok: true,
473
- command: "public-run",
474
- run_id: projection.run_id,
475
- output,
476
- projection_root: contentDigest(projection),
477
- })}\n`);
478
- }
479
- async function publishRunCommand(file, rest) {
369
+ async function exportCommand(file, rest) {
480
370
  if (file === undefined)
481
- throw new Error("publish-run requires one completed run file");
482
- const values = options(rest, ["--mission", "--repository", "--output"]);
483
- const record = parseRunRecord(await jsonFile(file));
484
- const mission = parseMission(await jsonFile(required(values, "--mission")));
485
- const bundle = buildPublicationBundle({
486
- record,
487
- mission,
488
- repository: required(values, "--repository"),
489
- });
490
- const output = path.resolve(required(values, "--output"));
491
- const parent = path.dirname(output);
492
- await mkdir(parent, { recursive: true, mode: 0o755 });
493
- const temporary = await mkdtemp(path.join(parent, `.${path.basename(output)}.tmp-`));
494
- try {
495
- await Promise.all([
496
- writeFile(path.join(temporary, "public-run.json"), canonicalJson(bundle.projection), { flag: "wx", mode: 0o644 }),
497
- writeFile(path.join(temporary, "pending-commands.json"), canonicalJson(bundle.pendingCommands), { flag: "wx", mode: 0o644 }),
498
- writeFile(path.join(temporary, "web-import.json"), canonicalJson(bundle.webImport), { flag: "wx", mode: 0o644 }),
499
- writeFile(path.join(temporary, "root-manifest.json"), canonicalJson(bundle.manifest), { flag: "wx", mode: 0o644 }),
500
- ]);
501
- await rename(temporary, output);
502
- }
503
- catch (error) {
504
- await rm(temporary, { recursive: true, force: true });
505
- throw error;
506
- }
507
- process.stdout.write(`${JSON.stringify({
508
- ok: true,
509
- command: "publish-run",
510
- run_id: bundle.projection.run_id,
511
- output,
512
- projection_root: contentDigest(bundle.projection),
513
- bundle_root: contentDigest(bundle.manifest),
514
- authority_effect: "none",
515
- })}\n`);
371
+ throw new Error("export requires a Run file or latest");
372
+ const parsed = productOptions(rest, ["--output", "--as", "--claim", "--scope-limit"], []);
373
+ if (parsed.positional.length !== 0)
374
+ throw new Error("export accepts one Run file");
375
+ const runFile = file === "latest" ? await latestRunFile() : path.resolve(file);
376
+ const output = parsed.values.get("--output") ??
377
+ path.join(path.dirname(path.dirname(runFile)), `submission-${Date.now()}`);
378
+ const actor = parsed.values.get("--as");
379
+ const correctedClaim = parsed.values.get("--claim");
380
+ const scopeLimit = parsed.values.get("--scope-limit");
381
+ process.stdout.write(`${JSON.stringify(await exportSubmission({
382
+ runFile,
383
+ outputRoot: path.resolve(output),
384
+ ...(actor === undefined ? {} : { actor }),
385
+ ...(correctedClaim === undefined ? {} : { correctedClaim }),
386
+ ...(scopeLimit === undefined ? {} : { scopeLimit }),
387
+ }))}\n`);
516
388
  }
517
- async function withdrawCommand(args) {
518
- const parsed = productOptions(args, ["--run", "--reason"], []);
389
+ async function submitCommand(bundle, rest) {
390
+ if (bundle === undefined)
391
+ throw new Error("submit requires a Submission bundle");
392
+ const parsed = productOptions(rest, ["--vela"], []);
519
393
  if (parsed.positional.length > 1)
520
- throw new Error("withdraw accepts at most one frontier");
521
- const reason = parsed.values.get("--reason");
522
- if (reason === undefined || reason.trim().length === 0)
523
- throw new Error("withdraw requires --reason");
524
- const runValue = parsed.values.get("--run") ?? "latest";
525
- const runFile = runValue === "latest" ? await latestRunFile() : path.resolve(runValue);
526
- const result = await withdrawProduct({
394
+ throw new Error("submit accepts at most one frontier");
395
+ const velaBinary = parsed.values.get("--vela");
396
+ process.stdout.write(`${JSON.stringify(await submitBundle({
397
+ bundle: path.resolve(bundle),
527
398
  frontier: path.resolve(parsed.positional[0] ?? "."),
528
- runFile,
529
- reason,
530
- });
531
- process.stdout.write(`${JSON.stringify(result)}\n`);
399
+ ...(velaBinary === undefined ? {} : { velaBinary }),
400
+ }))}\n`);
532
401
  }
533
402
  async function main(argv) {
534
403
  const [command, file, ...rest] = argv;
@@ -552,13 +421,12 @@ async function main(argv) {
552
421
  }
553
422
  if (isHelp(file)) {
554
423
  process.stdout.write(`${command === "run" ? runUsage()
555
- : command === "inspect" ? inspectUsage()
424
+ : command === "show" ? showUsage()
556
425
  : command === "doctor" ? doctorUsage()
557
426
  : command === "replay" ? replayUsage()
558
- : command === "public-run" ? publicRunUsage()
559
- : command === "publish-run" ? publishRunUsage()
560
- : command === "withdraw" ? withdrawUsage()
561
- : usage()}\n`);
427
+ : command === "export" ? exportUsage()
428
+ : command === "submit" ? submitUsage()
429
+ : usage()}\n`);
562
430
  return;
563
431
  }
564
432
  if (command === "doctor") {
@@ -569,35 +437,26 @@ async function main(argv) {
569
437
  await replayCommand(file, rest);
570
438
  return;
571
439
  }
572
- if (command === "public-run") {
573
- await publicRunCommand(file, rest);
574
- return;
575
- }
576
- if (command === "publish-run") {
577
- await publishRunCommand(file, rest);
578
- return;
579
- }
580
- if (command === "withdraw") {
581
- await withdrawCommand(argv.slice(1));
582
- return;
583
- }
584
440
  if (command === "validate") {
585
441
  if (file === undefined)
586
442
  throw new Error("validate requires a mission file");
587
443
  await missionCommand(["validate", file, ...rest]);
588
444
  return;
589
445
  }
590
- if (command === "inspect") {
591
- await inspectCommand(file, rest);
446
+ if (command === "show") {
447
+ await showCommand(file, rest);
592
448
  return;
593
449
  }
594
450
  if (command === "run") {
595
- if (file !== undefined && (file.endsWith(".json") || rest.includes("--source"))) {
596
- await runMission(file, rest);
597
- }
598
- else {
599
- await productRunCommand(argv.slice(1));
600
- }
451
+ await productRunCommand(argv.slice(1));
452
+ return;
453
+ }
454
+ if (command === "export") {
455
+ await exportCommand(file, rest);
456
+ return;
457
+ }
458
+ if (command === "submit") {
459
+ await submitCommand(file, rest);
601
460
  return;
602
461
  }
603
462
  throw new Error(`unknown command ${command}`);
@@ -13,8 +13,7 @@ export type Replayability = (typeof REPLAYABILITY)[number];
13
13
  export interface MissionRoots {
14
14
  git_commit: string;
15
15
  git_tree: string;
16
- vela_event_log: string;
17
- vela_snapshot: string;
16
+ vela_repository: string;
18
17
  }
19
18
  export interface MissionBudgets {
20
19
  max_research_wall_time_ms: number;
@@ -61,16 +60,6 @@ export interface ProducerProfileSpec {
61
60
  name: string;
62
61
  root: string;
63
62
  }
64
- export interface StrictRuleCount {
65
- rule: string;
66
- count: number;
67
- }
68
- export interface StrictBaseline {
69
- status: "pass" | "fail";
70
- blocker_count: number;
71
- blockers_root: string;
72
- rule_counts: StrictRuleCount[];
73
- }
74
63
  export interface LandingSpec {
75
64
  expected_routes: LandRoute[];
76
65
  max_accepted_delta: number;
@@ -127,7 +116,6 @@ export interface MissionV1 extends MissionBase {
127
116
  schema: typeof MISSION_V1_SCHEMA;
128
117
  target_packet: TargetPacketSpec;
129
118
  profile?: ProducerProfileSpec;
130
- strict_baseline: StrictBaseline;
131
119
  worker: WorkerSpec;
132
120
  verifier: ContainerVerifierSpec;
133
121
  execution_binding?: ExecutionBindingV1;
@@ -24,12 +24,11 @@ export const REPLAYABILITY = [
24
24
  ];
25
25
  function parseRoots(value) {
26
26
  const object = objectAt(value, "mission.roots");
27
- exactKeys(object, ["git_commit", "git_tree", "vela_event_log", "vela_snapshot"], [], "mission.roots");
27
+ exactKeys(object, ["git_commit", "git_tree", "vela_repository"], [], "mission.roots");
28
28
  return {
29
29
  git_commit: gitObjectAt(object.git_commit, "mission.roots.git_commit"),
30
30
  git_tree: gitObjectAt(object.git_tree, "mission.roots.git_tree"),
31
- vela_event_log: sha256At(object.vela_event_log, "mission.roots.vela_event_log"),
32
- vela_snapshot: sha256At(object.vela_snapshot, "mission.roots.vela_snapshot"),
31
+ vela_repository: sha256At(object.vela_repository, "mission.roots.vela_repository"),
33
32
  };
34
33
  }
35
34
  function frontierPathAt(value) {
@@ -118,41 +117,6 @@ function parseTargetPacket(value) {
118
117
  sha256: sha256At(object.sha256, "mission.target_packet.sha256"),
119
118
  };
120
119
  }
121
- function parseStrictBaseline(value) {
122
- const object = objectAt(value, "mission.strict_baseline");
123
- exactKeys(object, ["status", "blocker_count", "blockers_root", "rule_counts"], [], "mission.strict_baseline");
124
- const rule_counts = arrayAt(object.rule_counts, "mission.strict_baseline.rule_counts", { min: 0, max: 64 }, (item, at) => {
125
- const entry = objectAt(item, at);
126
- exactKeys(entry, ["rule", "count"], [], at);
127
- return {
128
- rule: stringAt(entry.rule, `${at}.rule`, {
129
- min: 1,
130
- max: 128,
131
- pattern: /^[a-z][a-z0-9_]*$/u,
132
- }),
133
- count: integerAt(entry.count, `${at}.count`, 1, 1_000_000),
134
- };
135
- });
136
- for (let index = 1; index < rule_counts.length; index += 1) {
137
- if ((rule_counts[index - 1]?.rule ?? "") >= (rule_counts[index]?.rule ?? "")) {
138
- throw new ContractError("mission.strict_baseline.rule_counts must be sorted and unique");
139
- }
140
- }
141
- const blocker_count = integerAt(object.blocker_count, "mission.strict_baseline.blocker_count", 0, 1_000_000);
142
- if (rule_counts.reduce((sum, entry) => sum + entry.count, 0) !== blocker_count) {
143
- throw new ContractError("mission.strict_baseline.rule_counts must sum to blocker_count");
144
- }
145
- const status = enumAt(object.status, "mission.strict_baseline.status", ["pass", "fail"]);
146
- if ((status === "pass") !== (blocker_count === 0)) {
147
- throw new ContractError("mission.strict_baseline status must agree with blocker_count");
148
- }
149
- return {
150
- status,
151
- blocker_count,
152
- blockers_root: sha256At(object.blockers_root, "mission.strict_baseline.blockers_root"),
153
- rule_counts,
154
- };
155
- }
156
120
  function parseLanding(value) {
157
121
  const object = objectAt(value, "mission.landing");
158
122
  exactKeys(object, ["expected_routes", "max_accepted_delta"], [], "mission.landing");
@@ -275,14 +239,13 @@ export function parseMission(value) {
275
239
  "repair_reason",
276
240
  "target_packet",
277
241
  "profile",
278
- "strict_baseline",
279
242
  "worker",
280
243
  "execution_binding",
281
244
  "result_contract",
282
245
  ]
283
246
  : ["parent_candidate", "repair_reason"], "mission");
284
247
  if (isV1) {
285
- for (const required of ["target_packet", "strict_baseline", "worker"]) {
248
+ for (const required of ["target_packet", "worker"]) {
286
249
  if (object[required] === undefined) {
287
250
  throw new ContractError(`mission.${required} is required for mission v1`);
288
251
  }
@@ -313,7 +276,7 @@ export function parseMission(value) {
313
276
  vela_version: stringAt(object.vela_version, "mission.vela_version", {
314
277
  min: 5,
315
278
  max: 32,
316
- pattern: /^0\.[0-9]+\.[0-9]+$/u,
279
+ pattern: /^0\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u,
317
280
  }),
318
281
  vela_sha256: sha256At(object.vela_sha256, "mission.vela_sha256"),
319
282
  frontier: frontierPathAt(object.frontier),
@@ -376,7 +339,6 @@ export function parseMission(value) {
376
339
  schema: MISSION_V1_SCHEMA,
377
340
  target_packet: targetPacket,
378
341
  ...(profile === undefined ? {} : { profile }),
379
- strict_baseline: parseStrictBaseline(object.strict_baseline),
380
342
  worker: parseWorker(object.worker),
381
343
  verifier: verifier,
382
344
  ...(executionBinding === undefined ? {} : { execution_binding: executionBinding }),
@@ -1,25 +1 @@
1
- export declare class ContractError extends Error {
2
- constructor(message: string);
3
- }
4
- export type JsonObject = Record<string, unknown>;
5
- export declare function objectAt(value: unknown, at: string): JsonObject;
6
- export declare function exactKeys(value: JsonObject, required: readonly string[], optional: readonly string[], at: string): void;
7
- export declare function stringAt(value: unknown, at: string, options: {
8
- min?: number;
9
- max: number;
10
- pattern?: RegExp;
11
- }): string;
12
- export declare function integerAt(value: unknown, at: string, min: number, max: number): number;
13
- export declare function enumAt<const T extends string>(value: unknown, at: string, allowed: readonly T[]): T;
14
- export declare function arrayAt<T>(value: unknown, at: string, options: {
15
- min?: number;
16
- max: number;
17
- unique?: boolean;
18
- }, parse: (item: unknown, at: string) => T): T[];
19
- export declare const GIT_OBJECT_RE: RegExp;
20
- export declare const SHA256_RE: RegExp;
21
- export declare const MISSION_ID_RE: RegExp;
22
- export declare const AGENT_RE: RegExp;
23
- export declare function relativePathAt(value: unknown, at: string): string;
24
- export declare function sha256At(value: unknown, at: string): string;
25
- export declare function gitObjectAt(value: unknown, at: string): string;
1
+ export { AGENT_RE, ContractError, GIT_OBJECT_RE, MISSION_ID_RE, SHA256_RE, arrayAt, enumAt, exactKeys, gitObjectAt, integerAt, objectAt, relativePathAt, sha256At, stringAt, type JsonObject, } from "@vela-science/protocol";