assertledger 1.0.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 (203) hide show
  1. package/CONTRIBUTING.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.fr.md +236 -0
  4. package/README.md +224 -0
  5. package/SECURITY.md +51 -0
  6. package/benchmarks/agentic-profile/README.md +15 -0
  7. package/benchmarks/agentic-profile/public/README.md +5 -0
  8. package/benchmarks/self-hosted-core/README.md +113 -0
  9. package/benchmarks/self-hosted-core/adapter.mjs +293 -0
  10. package/benchmarks/self-hosted-core/builder.ts +193 -0
  11. package/benchmarks/self-hosted-core/campaign.ts +233 -0
  12. package/benchmarks/self-hosted-core/existing-tests-builder.ts +217 -0
  13. package/benchmarks/self-hosted-core/existing-tests.ts +146 -0
  14. package/benchmarks/self-hosted-core/liveness.test.mjs +8 -0
  15. package/conformance/v1/bundle.json +104 -0
  16. package/conformance/v1/expected/canonical-order-a.json +4 -0
  17. package/conformance/v1/expected/canonical-order-b.json +4 -0
  18. package/conformance/v1/expected/create-benchmark-v1-measured.json +575 -0
  19. package/conformance/v1/expected/create-profile-v1-qualified.json +280 -0
  20. package/conformance/v1/expected/decide-collection-failure-non-kill.json +192 -0
  21. package/conformance/v1/expected/decide-compile-failure-non-kill.json +192 -0
  22. package/conformance/v1/expected/decide-infra-error-non-kill.json +192 -0
  23. package/conformance/v1/expected/decide-no-test-discovered-non-kill.json +192 -0
  24. package/conformance/v1/expected/decide-process-crash-non-kill.json +192 -0
  25. package/conformance/v1/expected/decide-timeout-non-kill.json +192 -0
  26. package/conformance/v1/expected/decide-verified.json +192 -0
  27. package/conformance/v1/expected/replay-benchmark-v1-resealed-summary-forgery.json +12 -0
  28. package/conformance/v1/expected/replay-evidence-raw-tamper.json +6 -0
  29. package/conformance/v1/expected/replay-evidence-resealed-semantic-forgery.json +6 -0
  30. package/conformance/v1/inputs/canonical-order-a.json +8 -0
  31. package/conformance/v1/inputs/canonical-order-b.json +8 -0
  32. package/conformance/v1/inputs/create-benchmark-v1-measured.json +459 -0
  33. package/conformance/v1/inputs/create-profile-v1-qualified.json +228 -0
  34. package/conformance/v1/inputs/decide-collection-failure-non-kill.json +143 -0
  35. package/conformance/v1/inputs/decide-compile-failure-non-kill.json +143 -0
  36. package/conformance/v1/inputs/decide-infra-error-non-kill.json +143 -0
  37. package/conformance/v1/inputs/decide-no-test-discovered-non-kill.json +143 -0
  38. package/conformance/v1/inputs/decide-process-crash-non-kill.json +143 -0
  39. package/conformance/v1/inputs/decide-timeout-non-kill.json +143 -0
  40. package/conformance/v1/inputs/decide-verified.json +143 -0
  41. package/conformance/v1/inputs/replay-benchmark-v1-resealed-summary-forgery.json +575 -0
  42. package/conformance/v1/inputs/replay-evidence-raw-tamper.json +201 -0
  43. package/conformance/v1/inputs/replay-evidence-resealed-semantic-forgery.json +192 -0
  44. package/conformance/v1/schemas/expected-digests.json +175 -0
  45. package/dist/cli.d.ts +9 -0
  46. package/dist/cli.d.ts.map +1 -0
  47. package/dist/cli.js +951 -0
  48. package/dist/cli.js.map +1 -0
  49. package/dist/contracts/diagnostics.d.ts +18 -0
  50. package/dist/contracts/diagnostics.d.ts.map +1 -0
  51. package/dist/contracts/diagnostics.js +13 -0
  52. package/dist/contracts/diagnostics.js.map +1 -0
  53. package/dist/contracts/index.d.ts +3908 -0
  54. package/dist/contracts/index.d.ts.map +1 -0
  55. package/dist/contracts/index.js +2569 -0
  56. package/dist/contracts/index.js.map +1 -0
  57. package/dist/contracts/runtime-doctor.d.ts +107 -0
  58. package/dist/contracts/runtime-doctor.d.ts.map +1 -0
  59. package/dist/contracts/runtime-doctor.js +91 -0
  60. package/dist/contracts/runtime-doctor.js.map +1 -0
  61. package/dist/core/index.d.ts +200 -0
  62. package/dist/core/index.d.ts.map +1 -0
  63. package/dist/core/index.js +2587 -0
  64. package/dist/core/index.js.map +1 -0
  65. package/dist/diagnostics.d.ts +7 -0
  66. package/dist/diagnostics.d.ts.map +1 -0
  67. package/dist/diagnostics.js +252 -0
  68. package/dist/diagnostics.js.map +1 -0
  69. package/dist/engine/adapters/node-test-profile.d.ts +14 -0
  70. package/dist/engine/adapters/node-test-profile.d.ts.map +1 -0
  71. package/dist/engine/adapters/node-test-profile.js +14 -0
  72. package/dist/engine/adapters/node-test-profile.js.map +1 -0
  73. package/dist/engine/adapters/node-test-runtime.d.ts +39 -0
  74. package/dist/engine/adapters/node-test-runtime.d.ts.map +1 -0
  75. package/dist/engine/adapters/node-test-runtime.js +173 -0
  76. package/dist/engine/adapters/node-test-runtime.js.map +1 -0
  77. package/dist/engine/adapters/runtime-facts.d.ts +26 -0
  78. package/dist/engine/adapters/runtime-facts.d.ts.map +1 -0
  79. package/dist/engine/adapters/runtime-facts.js +73 -0
  80. package/dist/engine/adapters/runtime-facts.js.map +1 -0
  81. package/dist/engine/connection.d.ts +22 -0
  82. package/dist/engine/connection.d.ts.map +1 -0
  83. package/dist/engine/connection.js +343 -0
  84. package/dist/engine/connection.js.map +1 -0
  85. package/dist/engine/git-regression.d.ts +25 -0
  86. package/dist/engine/git-regression.d.ts.map +1 -0
  87. package/dist/engine/git-regression.js +803 -0
  88. package/dist/engine/git-regression.js.map +1 -0
  89. package/dist/engine/index.d.ts +55 -0
  90. package/dist/engine/index.d.ts.map +1 -0
  91. package/dist/engine/index.js +2782 -0
  92. package/dist/engine/index.js.map +1 -0
  93. package/dist/engine/node-test-reporter.d.ts +2 -0
  94. package/dist/engine/node-test-reporter.d.ts.map +1 -0
  95. package/dist/engine/node-test-reporter.js +70 -0
  96. package/dist/engine/node-test-reporter.js.map +1 -0
  97. package/dist/engine/runtime-doctor.d.ts +16 -0
  98. package/dist/engine/runtime-doctor.d.ts.map +1 -0
  99. package/dist/engine/runtime-doctor.js +100 -0
  100. package/dist/engine/runtime-doctor.js.map +1 -0
  101. package/dist/evaluation/agentic-corpus.d.ts +161 -0
  102. package/dist/evaluation/agentic-corpus.d.ts.map +1 -0
  103. package/dist/evaluation/agentic-corpus.js +710 -0
  104. package/dist/evaluation/agentic-corpus.js.map +1 -0
  105. package/dist/index.d.ts +8 -0
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.js +8 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/mcp/index.d.ts +13 -0
  110. package/dist/mcp/index.d.ts.map +1 -0
  111. package/dist/mcp/index.js +391 -0
  112. package/dist/mcp/index.js.map +1 -0
  113. package/dist/mcp/stdio.d.ts +3 -0
  114. package/dist/mcp/stdio.d.ts.map +1 -0
  115. package/dist/mcp/stdio.js +13 -0
  116. package/dist/mcp/stdio.js.map +1 -0
  117. package/dist/sdk/index.d.ts +52 -0
  118. package/dist/sdk/index.d.ts.map +1 -0
  119. package/dist/sdk/index.js +224 -0
  120. package/dist/sdk/index.js.map +1 -0
  121. package/dist/version.d.ts +2 -0
  122. package/dist/version.d.ts.map +1 -0
  123. package/dist/version.js +10 -0
  124. package/dist/version.js.map +1 -0
  125. package/docs/adapter-protocol.md +196 -0
  126. package/docs/agentic-benchmark.md +118 -0
  127. package/docs/agentic-corpus-experiment-h3.md +89 -0
  128. package/docs/agentic-corpus-plan.md +105 -0
  129. package/docs/agentic-corpus-provenance.md +59 -0
  130. package/docs/agentic-test-profile-pilot.md +57 -0
  131. package/docs/agentic-test-profile-v2.md +116 -0
  132. package/docs/agentic-test-profile.md +274 -0
  133. package/docs/architecture.md +157 -0
  134. package/docs/ci.md +37 -0
  135. package/docs/client-connections.md +61 -0
  136. package/docs/conformance-v1.md +72 -0
  137. package/docs/decisions/0001-typescript-runtime.md +24 -0
  138. package/docs/developer-experience.md +55 -0
  139. package/docs/diagnostics.md +35 -0
  140. package/docs/distribution.md +40 -0
  141. package/docs/git-regression.md +39 -0
  142. package/docs/migration-repository-validation-order.md +35 -0
  143. package/docs/migration-testforge-to-assertledger.md +64 -0
  144. package/docs/project-intent.md +173 -0
  145. package/docs/proof-model.md +116 -0
  146. package/docs/reference.md +336 -0
  147. package/docs/release-1.0.md +63 -0
  148. package/docs/repository-audit.md +52 -0
  149. package/docs/repository-init.md +60 -0
  150. package/docs/research-basis.md +27 -0
  151. package/docs/roadmap.md +74 -0
  152. package/docs/runtime-doctor.md +65 -0
  153. package/docs/testexplora-calibration.md +71 -0
  154. package/examples/agentic-benchmark/benchmark-request.mjs +19 -0
  155. package/examples/agentic-benchmark/structured-phase-adapter-fixture.mjs +35 -0
  156. package/examples/agentic-profile/profile-benchmark.mjs +34 -0
  157. package/examples/agentic-profile/profile-manifest.mjs +28 -0
  158. package/examples/git-history/README.md +44 -0
  159. package/examples/git-history/create-demo.mjs +128 -0
  160. package/examples/git-history/escape-string-regexp/LICENSE +9 -0
  161. package/examples/git-history/escape-string-regexp/before.cjs.txt +11 -0
  162. package/examples/git-history/escape-string-regexp/fixed.cjs.txt +13 -0
  163. package/examples/git-history/escape-string-regexp/provenance.json +28 -0
  164. package/examples/node-test/repository/package.json +5 -0
  165. package/examples/node-test/repository/src/is-even.js +3 -0
  166. package/examples/node-test/repository/tests/base.test.js +6 -0
  167. package/examples/node-test/request.json +93 -0
  168. package/integrations/skill/SKILL.md +51 -0
  169. package/package.json +88 -0
  170. package/schemas/agentic-benchmark-acquisition-replay-result.v1.json +70 -0
  171. package/schemas/agentic-benchmark-acquisition-request.v1.json +564 -0
  172. package/schemas/agentic-benchmark-acquisition-result.v1.json +1409 -0
  173. package/schemas/agentic-benchmark-artifact.v1.json +1251 -0
  174. package/schemas/agentic-benchmark-replay-result.v1.json +84 -0
  175. package/schemas/agentic-benchmark-request.v1.json +1034 -0
  176. package/schemas/agentic-corpus-allocation-commitment-replay-result.v1.json +58 -0
  177. package/schemas/agentic-corpus-allocation-commitment.v1.json +141 -0
  178. package/schemas/agentic-corpus-allocation-replay-result.v1.json +34 -0
  179. package/schemas/agentic-corpus-allocation-request.v1.json +65 -0
  180. package/schemas/agentic-corpus-allocation-reveal.v1.json +66 -0
  181. package/schemas/agentic-corpus-allocation.v1.json +167 -0
  182. package/schemas/agentic-corpus-experiment-artifact.v1.json +329 -0
  183. package/schemas/agentic-corpus-experiment-plan-replay-result.v1.json +50 -0
  184. package/schemas/agentic-corpus-experiment-plan.v1.json +424 -0
  185. package/schemas/agentic-corpus-experiment-replay-request.v1.json +336 -0
  186. package/schemas/agentic-corpus-experiment-replay-result.v1.json +106 -0
  187. package/schemas/agentic-corpus-experiment-request.v1.json +204 -0
  188. package/schemas/agentic-corpus-provenance.v1.json +143 -0
  189. package/schemas/agentic-corpus-trust-policy.v1.json +133 -0
  190. package/schemas/agentic-profile-replay-result.v1.json +56 -0
  191. package/schemas/agentic-profile-replay-result.v2.json +63 -0
  192. package/schemas/agentic-profile-report.v1.json +961 -0
  193. package/schemas/agentic-profile-report.v2.json +1674 -0
  194. package/schemas/agentic-profile-request.v1.json +671 -0
  195. package/schemas/agentic-profile-request.v2.json +1338 -0
  196. package/schemas/evidence-manifest.v1.json +636 -0
  197. package/schemas/replay-result.v1.json +49 -0
  198. package/schemas/repository-analysis.v1.json +119 -0
  199. package/schemas/repository-audit.v1.json +811 -0
  200. package/schemas/repository-init-config.v1.json +183 -0
  201. package/schemas/repository-init-lock.v1.json +162 -0
  202. package/schemas/repository-init-result.v1.json +212 -0
  203. package/schemas/verification-request.v1.json +389 -0
package/dist/cli.js ADDED
@@ -0,0 +1,951 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, realpath, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
6
+ import { ContractError } from "./contracts/index.js";
7
+ import { renderDiagnostics } from "./diagnostics.js";
8
+ import { connectClient, disconnectClient } from "./engine/connection.js";
9
+ import { renderGitRegressionSummary } from "./engine/git-regression.js";
10
+ import { AgenticCorpusError, evaluateAgenticCorpusHoldout, evaluateAgenticCorpusPublic, inspectAgenticCorpus, } from "./evaluation/agentic-corpus.js";
11
+ import { createAssertLedgerServer } from "./mcp/index.js";
12
+ import { AssertLedger } from "./sdk/index.js";
13
+ import { ASSERTLEDGER_VERSION } from "./version.js";
14
+ const USAGE = `Usage: assertledger <command> [arguments] [--json]
15
+ (legacy alias: testforge <command> [arguments] [--json])
16
+
17
+ Commands:
18
+ explain CODE [CODE ...] [--json] Explain reason codes and safe next actions
19
+ check [repository] --before REF [--after REF] --neutral REF --neutral-reason TEXT
20
+ --test PATH --base-test PATH [--base-test PATH ...] --out RELATIVE_DIRECTORY
21
+ --allow-unsafe-execution Qualify one committed node:test regression
22
+ doctor [repository] [--json] Inspect static repository readiness without writing
23
+ doctor [repository] --runtime --allow-unsafe-execution [--json]
24
+ Run controlled trusted-local runtime probes
25
+ connect [repository] --client <codex|claude-code> [--write]
26
+ Preview or install project-local client integration
27
+ connect [repository] --client mcp Emit a generic stdio descriptor as JSON
28
+ disconnect [repository] --client <codex|claude-code> [--write]
29
+ Preview or remove exact AssertLedger-owned artifacts
30
+ analyze [repository] Analyze a repository
31
+ init [repository] [--dry-run] [--adapter-config PATH] [--package-manager ID]
32
+ [--framework ID] [--test-command-json PATH]
33
+ Detect and write portable initialization files
34
+ audit [repository] [--verification-request PATH] [--emit-verification-request] [--no-git]
35
+ Produce a static audit and campaign cost projection
36
+ schema <verification-request|repository-analysis|repository-audit|repository-init-config|
37
+ repository-init-lock|repository-init-result|evidence-manifest|replay-result|
38
+ agentic-profile-request|agentic-profile-report|agentic-profile-replay-result|
39
+ agentic-profile-request-v2|agentic-profile-report-v2|agentic-profile-replay-result-v2|
40
+ agentic-benchmark-request|agentic-benchmark-artifact|agentic-benchmark-replay-result|
41
+ agentic-benchmark-acquisition-request|agentic-benchmark-acquisition-result|
42
+ agentic-benchmark-acquisition-replay-result|agentic-corpus-allocation-request|
43
+ agentic-corpus-allocation|agentic-corpus-allocation-replay-result|
44
+ agentic-corpus-allocation-commitment|agentic-corpus-allocation-reveal|
45
+ agentic-corpus-allocation-commitment-replay-result|agentic-corpus-experiment-plan|
46
+ agentic-corpus-experiment-plan-replay-result|
47
+ agentic-corpus-experiment-request|agentic-corpus-experiment-artifact|
48
+ agentic-corpus-experiment-replay-request|agentic-corpus-experiment-replay-result>
49
+ Print a JSON Schema
50
+ verify [request.json|-] --allow-unsafe-execution
51
+ Execute a trusted-local campaign
52
+ replay [manifest.json|-] Verify an evidence digest
53
+ profile [request.json|-] Derive an Agentic Test Profile
54
+ profile-replay [report.json|-] Replay an Agentic Test Profile
55
+ profile-v2 [request.json|-] Derive a benchmark-backed Agentic Test Profile v2
56
+ profile-v2-replay [report.json|-] Replay an Agentic Test Profile v2
57
+ benchmark [request.json|-] Derive an Agentic Benchmark Artifact
58
+ benchmark-replay [artifact.json|-] Replay an Agentic Benchmark Artifact
59
+ benchmark-acquire [request.json|-] --allow-unsafe-execution
60
+ Run fresh verification and phase-aware acquisition
61
+ benchmark-acquire-replay [result.json|-] Replay benchmark acquisition evidence
62
+ corpus-allocate [request.json|-] Create a deterministic corpus allocation
63
+ corpus-allocation-replay [allocation.json|-] Replay a corpus allocation
64
+ corpus-experiment-replay [artifact.json|-] --trust-policy PATH --trust-policy-digest DIGEST
65
+ --allocation-commitment PATH --allocation-commitment-digest DIGEST --allocation-reveal PATH
66
+ --allocation PATH --experiment-plan PATH --experiment-plan-digest DIGEST
67
+ --subject-evidence PATH --evidence-contents PATH
68
+ Replay externally anchored H3 evidence
69
+ corpus-status [corpus-root] Check H1-H4 corpus readiness
70
+ corpus-evaluate-public [corpus-root] Evaluate the public corpus with feedback
71
+ corpus-evaluate-holdout [corpus-root] Evaluate holdout aggregates without leakage
72
+ mcp [--root PATH] [--allow-unsafe-execution] Serve MCP v2 over stdio (read-only by default)
73
+ `;
74
+ const MAXIMUM_JSON_INPUT_BYTES = 16 * 1024 * 1024;
75
+ function writeJson(io, value) {
76
+ io.writeStdout(`${JSON.stringify(value)}\n`);
77
+ }
78
+ function errorMessage(error) {
79
+ return error instanceof Error ? error.message : String(error);
80
+ }
81
+ function isRecord(value) {
82
+ return typeof value === "object" && value !== null && !Array.isArray(value);
83
+ }
84
+ function authorizeTrustedLocalExecution(request) {
85
+ if (!isRecord(request) || !isRecord(request.isolation))
86
+ return request;
87
+ return {
88
+ ...request,
89
+ isolation: {
90
+ ...request.isolation,
91
+ acknowledgedUnsafeExecution: true,
92
+ },
93
+ };
94
+ }
95
+ function authorizeBenchmarkAcquisition(request) {
96
+ if (!isRecord(request) || !isRecord(request.verificationRequest))
97
+ return request;
98
+ return {
99
+ ...request,
100
+ verificationRequest: authorizeTrustedLocalExecution(request.verificationRequest),
101
+ };
102
+ }
103
+ async function readJsonInput(argument, io) {
104
+ let text;
105
+ if (argument === undefined || argument === "-") {
106
+ text = await io.readStdin();
107
+ }
108
+ else {
109
+ const inputPath = path.resolve(io.cwd, argument);
110
+ if ((await stat(inputPath)).size > MAXIMUM_JSON_INPUT_BYTES) {
111
+ throw new TypeError("JSON_INPUT_TOO_LARGE");
112
+ }
113
+ text = await readFile(inputPath, "utf8");
114
+ }
115
+ if (Buffer.byteLength(text, "utf8") > MAXIMUM_JSON_INPUT_BYTES) {
116
+ throw new TypeError("JSON_INPUT_TOO_LARGE");
117
+ }
118
+ if (text.trim().length === 0)
119
+ throw new SyntaxError("JSON input is required");
120
+ return JSON.parse(text);
121
+ }
122
+ function requiredFlag(argv, name) {
123
+ const index = argv.indexOf(name);
124
+ const value = index < 0 ? undefined : argv[index + 1];
125
+ if (value === undefined || value.startsWith("--"))
126
+ throw new TypeError(`MISSING_${name.slice(2).toUpperCase().replaceAll("-", "_")}`);
127
+ return value;
128
+ }
129
+ function optionalFlag(argv, name) {
130
+ const index = argv.indexOf(name);
131
+ if (index < 0)
132
+ return undefined;
133
+ const value = argv[index + 1];
134
+ if (value === undefined || value.startsWith("--"))
135
+ throw new TypeError(`MISSING_${name.slice(2).toUpperCase().replaceAll("-", "_")}`);
136
+ return value;
137
+ }
138
+ function parseClientArguments(argv, cwd, operation) {
139
+ let client;
140
+ let rootArgument = ".";
141
+ let rootSeen = false;
142
+ let clientSeen = false;
143
+ let writeSeen = false;
144
+ for (let index = 1; index < argv.length; index += 1) {
145
+ const argument = argv[index] ?? "";
146
+ if (argument === "--client") {
147
+ if (clientSeen)
148
+ return undefined;
149
+ const value = argv[index + 1];
150
+ if (value === undefined || value.startsWith("-"))
151
+ return undefined;
152
+ client = value;
153
+ clientSeen = true;
154
+ index += 1;
155
+ }
156
+ else if (argument === "--write") {
157
+ if (writeSeen)
158
+ return undefined;
159
+ writeSeen = true;
160
+ }
161
+ else if (argument.startsWith("-") || rootSeen) {
162
+ return undefined;
163
+ }
164
+ else {
165
+ rootArgument = argument;
166
+ rootSeen = true;
167
+ }
168
+ }
169
+ if (!["codex", "claude-code", "mcp"].includes(client ?? ""))
170
+ return undefined;
171
+ if (operation === "disconnect" && client === "mcp")
172
+ return undefined;
173
+ if (client === "mcp" && writeSeen)
174
+ return undefined;
175
+ return {
176
+ root: path.resolve(cwd, rootArgument),
177
+ client: client,
178
+ write: writeSeen,
179
+ };
180
+ }
181
+ function parseCheckArguments(argv, cwd) {
182
+ const values = new Map();
183
+ let repository = ".";
184
+ let repositorySeen = false;
185
+ const valueFlags = new Set([
186
+ "--before",
187
+ "--after",
188
+ "--neutral",
189
+ "--neutral-reason",
190
+ "--test",
191
+ "--base-test",
192
+ "--out",
193
+ ]);
194
+ const booleanFlags = new Set(["--allow-unsafe-execution", "--json"]);
195
+ for (let index = 1; index < argv.length; index += 1) {
196
+ const argument = argv[index] ?? "";
197
+ if (valueFlags.has(argument)) {
198
+ const value = argv[index + 1];
199
+ if (value === undefined || value.startsWith("--"))
200
+ throw new TypeError(`MISSING_${argument.slice(2).toUpperCase().replaceAll("-", "_")}`);
201
+ const existing = values.get(argument) ?? [];
202
+ existing.push(value);
203
+ values.set(argument, existing);
204
+ index += 1;
205
+ }
206
+ else if (!booleanFlags.has(argument) && !argument.startsWith("--") && !repositorySeen) {
207
+ repository = argument;
208
+ repositorySeen = true;
209
+ }
210
+ else if (!booleanFlags.has(argument)) {
211
+ throw new TypeError("GIT_REGRESSION_ARGUMENT_INVALID");
212
+ }
213
+ }
214
+ const exactlyOne = (name) => {
215
+ const found = values.get(name) ?? [];
216
+ if (found.length !== 1)
217
+ throw new TypeError(found.length === 0
218
+ ? `MISSING_${name.slice(2).toUpperCase().replaceAll("-", "_")}`
219
+ : "GIT_REGRESSION_ARGUMENT_INVALID");
220
+ return found[0];
221
+ };
222
+ const afterValues = values.get("--after") ?? [];
223
+ if (afterValues.length > 1)
224
+ throw new TypeError("GIT_REGRESSION_ARGUMENT_INVALID");
225
+ const baseTests = values.get("--base-test") ?? [];
226
+ if (baseTests.length === 0)
227
+ throw new TypeError("MISSING_BASE_TEST");
228
+ return {
229
+ repository: path.resolve(cwd, repository),
230
+ before: exactlyOne("--before"),
231
+ ...(afterValues[0] === undefined ? {} : { after: afterValues[0] }),
232
+ neutral: exactlyOne("--neutral"),
233
+ neutralReason: exactlyOne("--neutral-reason"),
234
+ test: exactlyOne("--test"),
235
+ baseTests,
236
+ out: exactlyOne("--out"),
237
+ };
238
+ }
239
+ function evidenceContentMap(value) {
240
+ if (!Array.isArray(value))
241
+ throw new TypeError("EVIDENCE_CONTENTS_INVALID");
242
+ const entries = value.map((entry) => {
243
+ if (!isRecord(entry) ||
244
+ typeof entry.digest !== "string" ||
245
+ entry.encoding !== "BASE64URL" ||
246
+ typeof entry.content !== "string") {
247
+ throw new TypeError("EVIDENCE_CONTENTS_INVALID");
248
+ }
249
+ return [entry.digest, new Uint8Array(Buffer.from(entry.content, "base64url"))];
250
+ });
251
+ if (new Set(entries.map(([digest]) => digest)).size !== entries.length)
252
+ throw new TypeError("EVIDENCE_CONTENTS_INVALID");
253
+ return new Map(entries);
254
+ }
255
+ function decisionExitCode(result) {
256
+ if (!isRecord(result) || !isRecord(result.decision))
257
+ return 5;
258
+ switch (result.decision.status) {
259
+ case "VERIFIED":
260
+ return 0;
261
+ case "REJECTED":
262
+ return 2;
263
+ case "INCONCLUSIVE":
264
+ return 3;
265
+ default:
266
+ return 5;
267
+ }
268
+ }
269
+ function profileExitCode(result) {
270
+ if (!isRecord(result))
271
+ return 5;
272
+ switch (result.status) {
273
+ case "QUALIFIED":
274
+ return 0;
275
+ case "NOT_QUALIFIED":
276
+ case "BUDGET_MISSED":
277
+ return 2;
278
+ case "INSUFFICIENT_TIMING_EVIDENCE":
279
+ return 3;
280
+ case "OBSERVED_BENCHMARK_FAILURE":
281
+ case "COMPARISON_SCOPE_MISMATCH":
282
+ return 4;
283
+ default:
284
+ return 5;
285
+ }
286
+ }
287
+ function benchmarkExitCode(result) {
288
+ if (!isRecord(result) || !Array.isArray(result.summaries))
289
+ return 5;
290
+ const statuses = result.summaries.map((summary) => isRecord(summary) ? summary.status : undefined);
291
+ if (statuses.every((status) => status === "MEASURED"))
292
+ return 0;
293
+ if (statuses.some((status) => status === "OBSERVED_RUN_FAILURE"))
294
+ return 2;
295
+ if (statuses.some((status) => status === "INSUFFICIENT_SAMPLES"))
296
+ return 3;
297
+ return 5;
298
+ }
299
+ function benchmarkAcquisitionExitCode(result) {
300
+ if (!isRecord(result))
301
+ return 5;
302
+ switch (result.status) {
303
+ case "COMPLETE":
304
+ return 0;
305
+ case "SOURCE_NOT_VERIFIED":
306
+ case "OBSERVED_RUN_FAILURE":
307
+ return 2;
308
+ case "INSUFFICIENT_SAMPLES":
309
+ return 3;
310
+ default:
311
+ return 5;
312
+ }
313
+ }
314
+ async function runCorpusCommand(action, root, io) {
315
+ try {
316
+ const result = action === "STATUS"
317
+ ? await inspectAgenticCorpus(root)
318
+ : action === "PUBLIC"
319
+ ? await evaluateAgenticCorpusPublic(root)
320
+ : await evaluateAgenticCorpusHoldout(root);
321
+ writeJson(io, result);
322
+ return result.status === "NOT_READY" ? 3 : 0;
323
+ }
324
+ catch (error) {
325
+ writeJson(io, {
326
+ schemaVersion: "1.0.0",
327
+ status: "INVALID_CORPUS",
328
+ reasonCodes: [error instanceof AgenticCorpusError ? error.code : "CORPUS_IO_ERROR"],
329
+ });
330
+ return 4;
331
+ }
332
+ }
333
+ const VALIDATION_ERROR_CODES = new Set([
334
+ "DIAGNOSTIC_CODES_INVALID",
335
+ "CANDIDATE_BUDGET_EXCEEDED",
336
+ "CANDIDATE_BYTES_EXCEEDED",
337
+ "DUPLICATE_BASE_TEST_FILE",
338
+ "DUPLICATE_CANDIDATE_FILE_PATH",
339
+ "DUPLICATE_CANDIDATE_ID",
340
+ "DUPLICATE_CANDIDATE_ROOT",
341
+ "DUPLICATE_ENVIRONMENT_ALLOWLIST_ENTRY",
342
+ "DUPLICATE_WORLD_FILE_PATH",
343
+ "DUPLICATE_WORLD_ID",
344
+ "EXECUTION_BUDGET_EXCEEDED",
345
+ "FORBIDDEN_CANDIDATE_PATH",
346
+ "FORBIDDEN_WORLD_PATH",
347
+ "INVALID_ACCEPTED_TARGET_OUTCOMES",
348
+ "INVALID_ADAPTER",
349
+ "INVALID_ADAPTER_ARGUMENTS",
350
+ "INVALID_ADAPTER_EXECUTABLE",
351
+ "INVALID_BASE_TEST_FILES",
352
+ "INVALID_BUDGETS",
353
+ "INVALID_CANDIDATE",
354
+ "INVALID_CAMPAIGN_COLLECTIONS",
355
+ "INVALID_CANDIDATE_FILES",
356
+ "INVALID_CANDIDATE_ID",
357
+ "INVALID_CANDIDATE_ROOTS",
358
+ "INVALID_ENVIRONMENT_ALLOWLIST",
359
+ "INVALID_ENVIRONMENT_ALLOWLIST_ENTRY",
360
+ "INVALID_FILE_OVERLAY",
361
+ "INVALID_ISOLATION",
362
+ "INVALID_MAXIMUM_CANDIDATE_BYTES",
363
+ "INVALID_MAXIMUM_CANDIDATES",
364
+ "INVALID_MAXIMUM_EXECUTIONS",
365
+ "INVALID_MAXIMUM_OUTPUT_BYTES",
366
+ "INVALID_MAXIMUM_REPOSITORY_BYTES",
367
+ "INVALID_MAXIMUM_REPOSITORY_FILES",
368
+ "INVALID_MAXIMUM_SELECTED_CANDIDATES",
369
+ "INVALID_MAXIMUM_TOTAL_CANDIDATE_BYTES",
370
+ "INVALID_MAXIMUM_WORLD_OVERLAY_BYTES",
371
+ "INVALID_MAXIMUM_WORLDS",
372
+ "INVALID_MINIMUM_TARGET_WEIGHT_PERMILLE",
373
+ "INVALID_OVERLAY_CONTENT",
374
+ "INVALID_OVERLAY_PATH",
375
+ "INVALID_POLICY",
376
+ "INVALID_POLICY_VERSION",
377
+ "INVALID_REPOSITORY",
378
+ "INVALID_REPOSITORY_EXCLUDE",
379
+ "INVALID_REPOSITORY_ROOT",
380
+ "INVALID_REQUIRED_ATTEMPTS",
381
+ "INVALID_SCHEMA_VERSION",
382
+ "INVALID_TIMEOUT_MS_PER_EXECUTION",
383
+ "INVALID_WORLD",
384
+ "INVALID_WORLD_FILES",
385
+ "INVALID_WORLD_ID",
386
+ "INVALID_WORLD_KIND",
387
+ "INVALID_WORLD_REQUIRED",
388
+ "INVALID_WORLD_WEIGHT",
389
+ "INVALID_WORLDS",
390
+ "JSON_INPUT_TOO_LARGE",
391
+ "AGENTIC_PROFILE_SOURCE_INVALID",
392
+ "AGENTIC_BENCHMARK_REFERENCE_WORLD_INVALID",
393
+ "AGENTIC_BENCHMARK_SOURCE_INVALID",
394
+ "AGENTIC_BENCHMARK_SOURCE_BINDING_INVALID",
395
+ "AGENTIC_BENCHMARK_ACQUISITION_REQUEST_INVALID",
396
+ "AGENTIC_BENCHMARK_ACQUISITION_IDENTITY_INVALID",
397
+ "BENCHMARK_PHASE_ACQUISITION_UNSUPPORTED_NODE_TEST",
398
+ "AGENTIC_PROFILE_V2_BENCHMARK_INVALID",
399
+ "AGENTIC_PROFILE_V2_COHORT_INVALID",
400
+ "NODE_TEST_EXECUTABLE_PROBE_FAILED",
401
+ "NODE_TEST_VERSION_UNSUPPORTED",
402
+ "PORTABLE_PATH_COLLISION",
403
+ "REPOSITORY_BYTES_BUDGET_EXCEEDED",
404
+ "REPOSITORY_FILE_BUDGET_EXCEEDED",
405
+ "RESERVED_ENVIRONMENT_VARIABLE",
406
+ "SYMLINK_OVERLAY_PATH",
407
+ "TOTAL_CANDIDATE_BYTES_EXCEEDED",
408
+ "UNSAFE_LOCAL_EXECUTION_NOT_ACKNOWLEDGED",
409
+ "UNSAFE_NODE_TEST_ARGUMENT",
410
+ "UNSUPPORTED_ADAPTER",
411
+ "UNSUPPORTED_ISOLATION",
412
+ "UNSUPPORTED_REPOSITORY_SYMLINK",
413
+ "VERIFICATION_REQUEST_REPOSITORY_MISMATCH",
414
+ "WORLD_BUDGET_EXCEEDED",
415
+ "WORLD_OVERLAY_BYTES_EXCEEDED",
416
+ ]);
417
+ const BOUNDARY_VALIDATION_MESSAGES = new Set([
418
+ "Absolute paths and Windows ADS are forbidden",
419
+ "Ambiguous Unicode path segment must use NFC",
420
+ "Ambiguous Windows path segment",
421
+ "At least one safe root is required",
422
+ "Control or Windows-forbidden filename character",
423
+ "Path escapes the allowed roots",
424
+ "Path must be a non-empty relative path",
425
+ "Path traversal or ambiguous segments are forbidden",
426
+ "Reserved Windows device name",
427
+ "Safe roots must be strings",
428
+ ]);
429
+ function classifyError(error) {
430
+ if (error instanceof SyntaxError || error instanceof ContractError) {
431
+ return 4;
432
+ }
433
+ if (error instanceof Error &&
434
+ (VALIDATION_ERROR_CODES.has(error.message) ||
435
+ BOUNDARY_VALIDATION_MESSAGES.has(error.message) ||
436
+ error.message.startsWith("MISSING_") ||
437
+ error.message.startsWith("GIT_") ||
438
+ error.message === "EVIDENCE_CONTENTS_INVALID" ||
439
+ error.message === "SUBJECT_EVIDENCE_INVALID")) {
440
+ return 4;
441
+ }
442
+ return 5;
443
+ }
444
+ export async function runCli(argv, io) {
445
+ const ledger = new AssertLedger();
446
+ const positional = argv.filter((argument) => !argument.startsWith("--"));
447
+ const command = ["--help", "-h", "--version", "-v"].includes(argv[0] ?? "")
448
+ ? argv[0]
449
+ : positional[0];
450
+ try {
451
+ switch (command) {
452
+ case "explain": {
453
+ const codes = argv.slice(1).filter((argument) => argument !== "--json");
454
+ if (codes.length === 0 ||
455
+ codes.some((code) => code.startsWith("-")) ||
456
+ argv.filter((argument) => argument === "--json").length > 1) {
457
+ io.writeStderr(USAGE);
458
+ return 64;
459
+ }
460
+ const report = ledger.explain(codes);
461
+ if (argv.includes("--json"))
462
+ writeJson(io, report);
463
+ else
464
+ io.writeStdout(`${renderDiagnostics(codes)}\n`);
465
+ return 0;
466
+ }
467
+ case "help":
468
+ case "--help":
469
+ case "-h":
470
+ if (argv.length !== 1) {
471
+ io.writeStderr(USAGE);
472
+ return 64;
473
+ }
474
+ io.writeStdout(USAGE);
475
+ return 0;
476
+ case "version":
477
+ case "--version":
478
+ case "-v":
479
+ if (argv.length !== 1) {
480
+ io.writeStderr(USAGE);
481
+ return 64;
482
+ }
483
+ io.writeStdout(`assertledger ${ASSERTLEDGER_VERSION}\n`);
484
+ return 0;
485
+ case "doctor": {
486
+ const allowedFlags = new Set(["--json", "--runtime", "--allow-unsafe-execution"]);
487
+ const rootArguments = argv.slice(1).filter((argument) => !argument.startsWith("-"));
488
+ const runtime = argv.includes("--runtime");
489
+ const allowUnsafeExecution = argv.includes("--allow-unsafe-execution");
490
+ if (rootArguments.length > 1 ||
491
+ [...allowedFlags].some((flag) => argv.filter((argument) => argument === flag).length > 1) ||
492
+ (allowUnsafeExecution && !runtime) ||
493
+ argv.slice(1).some((argument) => argument.startsWith("-") && !allowedFlags.has(argument))) {
494
+ io.writeStderr(USAGE);
495
+ return 64;
496
+ }
497
+ const root = path.resolve(io.cwd, rootArguments[0] ?? ".");
498
+ if (runtime) {
499
+ const runtimeResult = await ledger.doctorRuntime(root, { allowUnsafeExecution });
500
+ if (argv.includes("--json")) {
501
+ writeJson(io, runtimeResult);
502
+ }
503
+ else {
504
+ const reasons = runtimeResult.reasonCodes.length === 0
505
+ ? "none"
506
+ : runtimeResult.reasonCodes.join(", ");
507
+ io.writeStdout([
508
+ `Runtime status: ${runtimeResult.status}`,
509
+ `Execution mode: ${runtimeResult.executionMode}`,
510
+ `Adapter: ${runtimeResult.adapter ?? "not checked"}`,
511
+ `Node.js: ${runtimeResult.nodeVersion ?? "not checked"}`,
512
+ `Reason codes: ${reasons}`,
513
+ ...runtimeResult.checks.map((check) => `${check.status} ${check.id}: ${check.summary}${check.nextAction ? ` Next: ${check.nextAction}` : ""}`),
514
+ ...runtimeResult.limitations.map((limitation) => `Limit: ${limitation}`),
515
+ "",
516
+ ].join("\n"));
517
+ }
518
+ return runtimeResult.status === "READY" ? 0 : 3;
519
+ }
520
+ const result = await ledger.doctor(root);
521
+ if (argv.includes("--json")) {
522
+ writeJson(io, result);
523
+ }
524
+ else {
525
+ const reasons = result.reasonCodes.length === 0 ? "none" : result.reasonCodes.join(", ");
526
+ io.writeStdout([
527
+ `Status: ${result.status}`,
528
+ `Reason codes: ${reasons}`,
529
+ `Required operator inputs: ${result.requiredOperatorInputs.join(", ")}`,
530
+ renderDiagnostics(result.reasonCodes),
531
+ `Next safe action: ${result.nextCommands[0]?.executable ?? "assertledger"} ${(result.nextCommands[0]?.arguments ?? []).join(" ")}`,
532
+ "Execution limit: verification remains UNSANDBOXED trusted-local and requires explicit operator authorization.",
533
+ "This static diagnostic does not prove campaign evidence or MCP connectivity.",
534
+ "",
535
+ ].join("\n"));
536
+ }
537
+ if (result.status === "BLOCKED")
538
+ return 3;
539
+ if (result.status === "CONFLICT")
540
+ return 4;
541
+ return 0;
542
+ }
543
+ case "connect": {
544
+ const parsed = parseClientArguments(argv, io.cwd, "connect");
545
+ if (parsed === undefined) {
546
+ io.writeStderr(USAGE);
547
+ return 64;
548
+ }
549
+ const currentEntry = fileURLToPath(import.meta.url);
550
+ if (path.basename(currentEntry) !== "cli.js" ||
551
+ path.basename(path.dirname(currentEntry)) !== "dist") {
552
+ io.writeStderr("CONNECT_BUILD_REQUIRED: run `pnpm build` and invoke dist/cli.js.\n");
553
+ return 3;
554
+ }
555
+ const result = await connectClient(parsed.root, currentEntry, parsed.client, parsed.write);
556
+ if (parsed.client === "mcp") {
557
+ io.writeStdout(result.artifacts[0]?.content ?? "");
558
+ return 0;
559
+ }
560
+ if (result.status === "CONFLICT") {
561
+ io.writeStderr(`CONFLICT: ${result.artifacts.map((artifact) => artifact.path).join(", ")} includes different operator-owned content; no files changed.\n`);
562
+ return 4;
563
+ }
564
+ for (const artifact of result.artifacts) {
565
+ io.writeStdout(`${result.status}: ${artifact.path}\n`);
566
+ if (result.status === "EMITTED") {
567
+ const label = artifact.kind.toUpperCase();
568
+ io.writeStdout(`--- BEGIN ASSERTLEDGER ${label} ---\n`);
569
+ io.writeStdout(artifact.content);
570
+ if (!artifact.content.endsWith("\n"))
571
+ io.writeStdout("\n");
572
+ io.writeStdout(`--- END ASSERTLEDGER ${label} ---\n`);
573
+ }
574
+ }
575
+ if (result.status === "EMITTED") {
576
+ io.writeStdout("No files changed. Re-run with --write to install these artifacts.\n");
577
+ }
578
+ return 0;
579
+ }
580
+ case "disconnect": {
581
+ const parsed = parseClientArguments(argv, io.cwd, "disconnect");
582
+ if (parsed === undefined || parsed.client === "mcp") {
583
+ io.writeStderr(USAGE);
584
+ return 64;
585
+ }
586
+ const currentEntry = fileURLToPath(import.meta.url);
587
+ if (path.basename(currentEntry) !== "cli.js" ||
588
+ path.basename(path.dirname(currentEntry)) !== "dist") {
589
+ io.writeStderr("CONNECT_BUILD_REQUIRED: run `pnpm build` and invoke dist/cli.js.\n");
590
+ return 3;
591
+ }
592
+ const result = await disconnectClient(parsed.root, currentEntry, parsed.client, parsed.write);
593
+ if (result.status === "CONFLICT") {
594
+ io.writeStderr(`CONFLICT: ${result.artifacts.map((artifact) => artifact.path).join(", ")} includes content AssertLedger does not own byte-for-byte; no files changed.\n`);
595
+ return 4;
596
+ }
597
+ for (const artifact of result.artifacts) {
598
+ io.writeStdout(`${result.status}: ${artifact.path}\n`);
599
+ }
600
+ if (result.status === "EMITTED") {
601
+ io.writeStdout("No files changed. Re-run with --write to remove only byte-identical AssertLedger artifacts.\n");
602
+ }
603
+ return 0;
604
+ }
605
+ case "analyze": {
606
+ const root = path.resolve(io.cwd, positional[1] ?? ".");
607
+ writeJson(io, await ledger.analyze(root));
608
+ return 0;
609
+ }
610
+ case "init": {
611
+ const valueFlags = new Set([
612
+ "--adapter-config",
613
+ "--package-manager",
614
+ "--framework",
615
+ "--test-command-json",
616
+ ]);
617
+ const booleanFlags = new Set(["--dry-run", "--json"]);
618
+ let rootArgument = ".";
619
+ let rootSeen = false;
620
+ for (let index = 1; index < argv.length; index += 1) {
621
+ const argument = argv[index] ?? "";
622
+ if (valueFlags.has(argument)) {
623
+ const value = argv[index + 1];
624
+ if (value === undefined || value.startsWith("--")) {
625
+ io.writeStderr(USAGE);
626
+ return 64;
627
+ }
628
+ index += 1;
629
+ }
630
+ else if (!booleanFlags.has(argument)) {
631
+ if (!argument.startsWith("--") && !rootSeen) {
632
+ rootArgument = argument;
633
+ rootSeen = true;
634
+ }
635
+ else {
636
+ io.writeStderr(USAGE);
637
+ return 64;
638
+ }
639
+ }
640
+ }
641
+ const root = path.resolve(io.cwd, rootArgument);
642
+ const commandPath = optionalFlag(argv, "--test-command-json");
643
+ const adapterConfigPath = optionalFlag(argv, "--adapter-config");
644
+ const packageManager = optionalFlag(argv, "--package-manager");
645
+ const framework = optionalFlag(argv, "--framework");
646
+ const result = await ledger.init(root, {
647
+ dryRun: argv.includes("--dry-run"),
648
+ ...(adapterConfigPath === undefined ? {} : { adapterConfigPath }),
649
+ ...(packageManager === undefined ? {} : { packageManager }),
650
+ ...(framework === undefined ? {} : { framework }),
651
+ ...(commandPath === undefined
652
+ ? {}
653
+ : {
654
+ testCommand: (await readJsonInput(commandPath, io)),
655
+ }),
656
+ });
657
+ writeJson(io, result);
658
+ if (result.status === "BLOCKED")
659
+ return 3;
660
+ if (result.status === "CONFLICT")
661
+ return 4;
662
+ return 0;
663
+ }
664
+ case "audit": {
665
+ const rootArgument = argv[1] && !argv[1].startsWith("--") ? argv[1] : ".";
666
+ const root = path.resolve(io.cwd, rootArgument);
667
+ const explicitRequest = optionalFlag(argv, "--verification-request");
668
+ let request;
669
+ if (explicitRequest !== undefined)
670
+ request = await readJsonInput(explicitRequest, io);
671
+ else {
672
+ try {
673
+ request = JSON.parse(await readFile(path.join(root, "assertledger.request.json"), "utf8"));
674
+ }
675
+ catch (error) {
676
+ const code = error.code;
677
+ if (code !== "ENOENT")
678
+ throw error;
679
+ }
680
+ }
681
+ const result = await ledger.audit(root, {
682
+ noGit: argv.includes("--no-git"),
683
+ verificationRequest: request,
684
+ });
685
+ if (argv.includes("--emit-verification-request")) {
686
+ if (result.verificationRequest === null)
687
+ return 3;
688
+ writeJson(io, result.verificationRequest);
689
+ return 0;
690
+ }
691
+ writeJson(io, result);
692
+ return 0;
693
+ }
694
+ case "check": {
695
+ if (!argv.includes("--allow-unsafe-execution")) {
696
+ io.writeStderr("Refusing trusted-local execution without --allow-unsafe-execution.\n");
697
+ return 4;
698
+ }
699
+ const options = parseCheckArguments(argv, io.cwd);
700
+ const result = await ledger.checkGitRegression({
701
+ ...options,
702
+ allowUnsafeExecution: true,
703
+ });
704
+ if (argv.includes("--json"))
705
+ writeJson(io, result);
706
+ else
707
+ io.writeStdout(renderGitRegressionSummary(result, { ...options, allowUnsafeExecution: true }));
708
+ return decisionExitCode(result);
709
+ }
710
+ case "schema": {
711
+ const name = positional[1];
712
+ if (name !== "verification-request" &&
713
+ name !== "repository-analysis" &&
714
+ name !== "repository-audit" &&
715
+ name !== "repository-init-config" &&
716
+ name !== "repository-init-lock" &&
717
+ name !== "repository-init-result" &&
718
+ name !== "evidence-manifest" &&
719
+ name !== "replay-result" &&
720
+ name !== "agentic-benchmark-request" &&
721
+ name !== "agentic-benchmark-artifact" &&
722
+ name !== "agentic-benchmark-replay-result" &&
723
+ name !== "agentic-benchmark-acquisition-request" &&
724
+ name !== "agentic-benchmark-acquisition-result" &&
725
+ name !== "agentic-benchmark-acquisition-replay-result" &&
726
+ name !== "agentic-profile-request" &&
727
+ name !== "agentic-profile-report" &&
728
+ name !== "agentic-profile-replay-result" &&
729
+ name !== "agentic-profile-request-v2" &&
730
+ name !== "agentic-profile-report-v2" &&
731
+ name !== "agentic-profile-replay-result-v2" &&
732
+ name !== "agentic-corpus-allocation-request" &&
733
+ name !== "agentic-corpus-allocation" &&
734
+ name !== "agentic-corpus-allocation-replay-result" &&
735
+ name !== "agentic-corpus-allocation-commitment" &&
736
+ name !== "agentic-corpus-allocation-reveal" &&
737
+ name !== "agentic-corpus-allocation-commitment-replay-result" &&
738
+ name !== "agentic-corpus-experiment-plan" &&
739
+ name !== "agentic-corpus-experiment-plan-replay-result" &&
740
+ name !== "agentic-corpus-experiment-request" &&
741
+ name !== "agentic-corpus-experiment-artifact" &&
742
+ name !== "agentic-corpus-experiment-replay-request" &&
743
+ name !== "agentic-corpus-experiment-replay-result") {
744
+ io.writeStderr(USAGE);
745
+ return 64;
746
+ }
747
+ writeJson(io, ledger.schema(name));
748
+ return 0;
749
+ }
750
+ case "verify": {
751
+ const request = await readJsonInput(positional[1], io);
752
+ if (!argv.includes("--allow-unsafe-execution")) {
753
+ io.writeStderr("Refusing trusted-local execution without --allow-unsafe-execution.\n");
754
+ return 4;
755
+ }
756
+ const result = await ledger.verify(authorizeTrustedLocalExecution(request));
757
+ writeJson(io, result);
758
+ return decisionExitCode(result);
759
+ }
760
+ case "replay": {
761
+ const result = ledger.replay(await readJsonInput(positional[1], io));
762
+ writeJson(io, result);
763
+ return result.valid ? 0 : 4;
764
+ }
765
+ case "profile": {
766
+ const result = ledger.profile(await readJsonInput(positional[1], io));
767
+ writeJson(io, result);
768
+ return profileExitCode(result);
769
+ }
770
+ case "profile-replay": {
771
+ const result = ledger.replayProfile(await readJsonInput(positional[1], io));
772
+ writeJson(io, result);
773
+ return result.valid ? 0 : 4;
774
+ }
775
+ case "profile-v2": {
776
+ const result = ledger.profileV2(await readJsonInput(positional[1], io));
777
+ writeJson(io, result);
778
+ return profileExitCode(result);
779
+ }
780
+ case "profile-v2-replay": {
781
+ const result = ledger.replayProfileV2(await readJsonInput(positional[1], io));
782
+ writeJson(io, result);
783
+ return result.valid ? 0 : 4;
784
+ }
785
+ case "benchmark": {
786
+ const result = ledger.benchmark(await readJsonInput(positional[1], io));
787
+ writeJson(io, result);
788
+ return benchmarkExitCode(result);
789
+ }
790
+ case "benchmark-replay": {
791
+ const result = ledger.replayBenchmark(await readJsonInput(positional[1], io));
792
+ writeJson(io, result);
793
+ return result.valid ? 0 : 4;
794
+ }
795
+ case "benchmark-acquire": {
796
+ const request = await readJsonInput(positional[1], io);
797
+ if (!argv.includes("--allow-unsafe-execution")) {
798
+ io.writeStderr("Refusing trusted-local execution without --allow-unsafe-execution.\n");
799
+ return 4;
800
+ }
801
+ const result = await ledger.acquireBenchmark(authorizeBenchmarkAcquisition(request));
802
+ writeJson(io, result);
803
+ return benchmarkAcquisitionExitCode(result);
804
+ }
805
+ case "benchmark-acquire-replay": {
806
+ const result = ledger.replayBenchmarkAcquisition(await readJsonInput(positional[1], io));
807
+ writeJson(io, result);
808
+ return result.valid ? 0 : 4;
809
+ }
810
+ case "corpus-allocate": {
811
+ const result = ledger.allocateCorpus(await readJsonInput(positional[1], io));
812
+ writeJson(io, result);
813
+ return 0;
814
+ }
815
+ case "corpus-allocation-replay": {
816
+ const result = ledger.replayCorpusAllocation(await readJsonInput(positional[1], io));
817
+ writeJson(io, result);
818
+ return result.valid ? 0 : 4;
819
+ }
820
+ case "corpus-experiment-replay": {
821
+ const artifact = await readJsonInput(positional[1], io);
822
+ const trustPolicy = await readJsonInput(requiredFlag(argv, "--trust-policy"), io);
823
+ const allocationCommitment = await readJsonInput(requiredFlag(argv, "--allocation-commitment"), io);
824
+ const allocationReveal = await readJsonInput(requiredFlag(argv, "--allocation-reveal"), io);
825
+ const allocation = await readJsonInput(requiredFlag(argv, "--allocation"), io);
826
+ const experimentPlan = await readJsonInput(requiredFlag(argv, "--experiment-plan"), io);
827
+ const subjectEvidence = await readJsonInput(requiredFlag(argv, "--subject-evidence"), io);
828
+ if (!Array.isArray(subjectEvidence))
829
+ throw new TypeError("SUBJECT_EVIDENCE_INVALID");
830
+ const evidenceContents = evidenceContentMap(await readJsonInput(requiredFlag(argv, "--evidence-contents"), io));
831
+ const result = ledger.replayCorpusExperiment(artifact, {
832
+ expectedTrustPolicyDigest: requiredFlag(argv, "--trust-policy-digest"),
833
+ expectedAllocationCommitmentDigest: requiredFlag(argv, "--allocation-commitment-digest"),
834
+ expectedExperimentPlanDigest: requiredFlag(argv, "--experiment-plan-digest"),
835
+ trustPolicy,
836
+ allocationCommitment,
837
+ allocationReveal,
838
+ allocation,
839
+ experimentPlan,
840
+ subjectEvidence,
841
+ evidenceContents,
842
+ });
843
+ writeJson(io, result);
844
+ if (!result.valid)
845
+ return 4;
846
+ const parsedArtifact = artifact;
847
+ switch (parsedArtifact.result?.status) {
848
+ case "SUPPORTED":
849
+ return 0;
850
+ case "NOT_SUPPORTED":
851
+ return 2;
852
+ case "INSUFFICIENT":
853
+ return 3;
854
+ default:
855
+ return 4;
856
+ }
857
+ }
858
+ case "corpus-status":
859
+ return runCorpusCommand("STATUS", path.resolve(io.cwd, positional[1] ?? "benchmarks/agentic-profile"), io);
860
+ case "corpus-evaluate-public":
861
+ return runCorpusCommand("PUBLIC", path.resolve(io.cwd, positional[1] ?? "benchmarks/agentic-profile"), io);
862
+ case "corpus-evaluate-holdout":
863
+ return runCorpusCommand("HOLDOUT", path.resolve(io.cwd, positional[1] ?? "benchmarks/agentic-profile"), io);
864
+ case "mcp": {
865
+ const allowedFlags = new Set(["--allow-unsafe-execution", "--root"]);
866
+ let rootSeen = false;
867
+ let unsafeSeen = false;
868
+ for (let index = 1; index < argv.length; index += 1) {
869
+ const argument = argv[index] ?? "";
870
+ if (!allowedFlags.has(argument)) {
871
+ io.writeStderr(USAGE);
872
+ return 64;
873
+ }
874
+ if (argument === "--root") {
875
+ if (rootSeen) {
876
+ io.writeStderr(USAGE);
877
+ return 64;
878
+ }
879
+ const value = argv[index + 1];
880
+ if (value === undefined || value.startsWith("-")) {
881
+ io.writeStderr(USAGE);
882
+ return 64;
883
+ }
884
+ rootSeen = true;
885
+ index += 1;
886
+ }
887
+ else if (unsafeSeen) {
888
+ io.writeStderr(USAGE);
889
+ return 64;
890
+ }
891
+ else {
892
+ unsafeSeen = true;
893
+ }
894
+ }
895
+ const requestedRoot = optionalFlag(argv, "--root");
896
+ const root = requestedRoot === undefined
897
+ ? io.cwd
898
+ : await realpath(path.resolve(io.cwd, requestedRoot));
899
+ if (!(await stat(root)).isDirectory())
900
+ throw new Error("MCP_ROOT_NOT_DIRECTORY");
901
+ serveStdio(() => createAssertLedgerServer({
902
+ allowUnsafeExecution: argv.includes("--allow-unsafe-execution"),
903
+ allowedRepositoryRoots: [root],
904
+ }), {
905
+ onerror(error) {
906
+ io.writeStderr(`AssertLedger MCP error: ${error.message}\n`);
907
+ },
908
+ });
909
+ return 0;
910
+ }
911
+ default:
912
+ io.writeStderr(USAGE);
913
+ return 64;
914
+ }
915
+ }
916
+ catch (error) {
917
+ io.writeStderr(`${errorMessage(error)}\n`);
918
+ return classifyError(error);
919
+ }
920
+ }
921
+ const defaultIo = {
922
+ cwd: process.cwd(),
923
+ readStdin: async () => {
924
+ const chunks = [];
925
+ let bytes = 0;
926
+ for await (const chunk of process.stdin) {
927
+ const buffer = Buffer.from(chunk);
928
+ bytes += buffer.length;
929
+ if (bytes > MAXIMUM_JSON_INPUT_BYTES)
930
+ throw new TypeError("JSON_INPUT_TOO_LARGE");
931
+ chunks.push(buffer);
932
+ }
933
+ return Buffer.concat(chunks).toString("utf8");
934
+ },
935
+ writeStdout: (text) => process.stdout.write(text),
936
+ writeStderr: (text) => process.stderr.write(text),
937
+ };
938
+ async function isDirectInvocation(moduleUrl, entryPath) {
939
+ if (entryPath === undefined)
940
+ return false;
941
+ try {
942
+ return (await realpath(fileURLToPath(moduleUrl))) === (await realpath(path.resolve(entryPath)));
943
+ }
944
+ catch {
945
+ return false;
946
+ }
947
+ }
948
+ if (await isDirectInvocation(import.meta.url, process.argv[1])) {
949
+ process.exitCode = await runCli(process.argv.slice(2), defaultIo);
950
+ }
951
+ //# sourceMappingURL=cli.js.map