martin-loop 0.1.4 → 1.3.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 (286) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/README.md +172 -227
  3. package/demo/seeded-workspace/README.md +35 -0
  4. package/demo/seeded-workspace/TASKS.md +29 -0
  5. package/demo/seeded-workspace/martin.config.yaml +11 -0
  6. package/demo/seeded-workspace/package.json +8 -0
  7. package/demo/seeded-workspace/src/invoice-summary.js +11 -0
  8. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
  9. package/dist/bin/martin-loop.js +0 -0
  10. package/dist/vendor/adapters/claude-cli.d.ts +19 -4
  11. package/dist/vendor/adapters/claude-cli.js +55 -24
  12. package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
  13. package/dist/vendor/adapters/cli-bridge.js +154 -28
  14. package/dist/vendor/adapters/counter.d.ts +1 -0
  15. package/dist/vendor/adapters/counter.js +4 -0
  16. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  17. package/dist/vendor/adapters/git-baseline.js +233 -0
  18. package/dist/vendor/adapters/index.d.ts +1 -0
  19. package/dist/vendor/adapters/index.js +1 -0
  20. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  21. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  22. package/dist/vendor/adapters/usage.d.ts +48 -0
  23. package/dist/vendor/adapters/usage.js +66 -0
  24. package/dist/vendor/adapters/verifier-only.d.ts +7 -0
  25. package/dist/vendor/adapters/verifier-only.js +57 -0
  26. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  27. package/dist/vendor/cli/bin/exit.js +28 -0
  28. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  29. package/dist/vendor/cli/commands/analyze.js +58 -0
  30. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  31. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  32. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  33. package/dist/vendor/cli/commands/audit.js +199 -0
  34. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  35. package/dist/vendor/cli/commands/corpus.js +60 -0
  36. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  37. package/dist/vendor/cli/commands/doctor.js +219 -0
  38. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  39. package/dist/vendor/cli/commands/explain.js +176 -0
  40. package/dist/vendor/cli/commands/export.d.ts +5 -0
  41. package/dist/vendor/cli/commands/export.js +60 -0
  42. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  43. package/dist/vendor/cli/commands/governance.js +95 -0
  44. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  45. package/dist/vendor/cli/commands/improve.js +396 -0
  46. package/dist/vendor/cli/commands/init.d.ts +8 -0
  47. package/dist/vendor/cli/commands/init.js +281 -0
  48. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  49. package/dist/vendor/cli/commands/migration.js +67 -0
  50. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  51. package/dist/vendor/cli/commands/prior.js +145 -0
  52. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  53. package/dist/vendor/cli/commands/resume.js +73 -0
  54. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  55. package/dist/vendor/cli/commands/verify.js +43 -0
  56. package/dist/vendor/cli/index.d.ts +6 -1
  57. package/dist/vendor/cli/index.js +124 -7
  58. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  59. package/dist/vendor/cli/research/public-corpus.js +151 -0
  60. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  61. package/dist/vendor/cli/ui/error-card.js +103 -0
  62. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  63. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  64. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  65. package/dist/vendor/cli/ui/summary-card.js +102 -0
  66. package/dist/vendor/contracts/audit.d.ts +46 -0
  67. package/dist/vendor/contracts/audit.js +360 -0
  68. package/dist/vendor/contracts/index.d.ts +3 -1
  69. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  70. package/dist/vendor/contracts/post-phase15.js +166 -0
  71. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  72. package/dist/vendor/core/agent/mandates.js +178 -0
  73. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  74. package/dist/vendor/core/agent/receipts.js +131 -0
  75. package/dist/vendor/core/agent/signing.d.ts +17 -0
  76. package/dist/vendor/core/agent/signing.js +91 -0
  77. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  78. package/dist/vendor/core/attestation/sign.js +216 -0
  79. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  80. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  81. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  82. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  83. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  84. package/dist/vendor/core/autonomy/envelope.js +27 -0
  85. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  86. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  87. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  88. package/dist/vendor/core/autonomy/resume.js +23 -0
  89. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  90. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  91. package/dist/vendor/core/compiler.d.ts +2 -0
  92. package/dist/vendor/core/compiler.js +10 -4
  93. package/dist/vendor/core/context-distillation.d.ts +3 -0
  94. package/dist/vendor/core/context-distillation.js +44 -0
  95. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  96. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  97. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  98. package/dist/vendor/core/context-flow/entities.js +44 -0
  99. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  101. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  102. package/dist/vendor/core/context-flow/index.js +24 -0
  103. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/labels.js +17 -0
  105. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  106. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  107. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  108. package/dist/vendor/core/context-flow/profiles.js +36 -0
  109. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  110. package/dist/vendor/core/context-flow/redaction.js +6 -0
  111. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  112. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  113. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  114. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  115. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  116. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  117. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  118. package/dist/vendor/core/context-flow/types.js +2 -0
  119. package/dist/vendor/core/context-integrity.d.ts +26 -0
  120. package/dist/vendor/core/context-integrity.js +56 -0
  121. package/dist/vendor/core/context-utility.d.ts +47 -0
  122. package/dist/vendor/core/context-utility.js +405 -0
  123. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  124. package/dist/vendor/core/cost/pipeline.js +141 -0
  125. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  126. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  127. package/dist/vendor/core/cost-governor.d.ts +2 -0
  128. package/dist/vendor/core/cost-governor.js +50 -0
  129. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  130. package/dist/vendor/core/cve/cve-check.js +172 -0
  131. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  132. package/dist/vendor/core/digital-twin/index.js +90 -0
  133. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  134. package/dist/vendor/core/drift/drift-graph.js +100 -0
  135. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  136. package/dist/vendor/core/drift/objective-lock.js +88 -0
  137. package/dist/vendor/core/drift/scope.d.ts +46 -0
  138. package/dist/vendor/core/drift/scope.js +102 -0
  139. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  140. package/dist/vendor/core/drift/signature-lock.js +202 -0
  141. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  142. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  143. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  144. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  145. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  146. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  147. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  148. package/dist/vendor/core/exit-intelligence.js +58 -0
  149. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  150. package/dist/vendor/core/explain/formatter.js +171 -0
  151. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  152. package/dist/vendor/core/explain/timeline.js +213 -0
  153. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  154. package/dist/vendor/core/failure-taxonomy.js +76 -0
  155. package/dist/vendor/core/gateway/index.d.ts +10 -0
  156. package/dist/vendor/core/gateway/index.js +12 -0
  157. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  158. package/dist/vendor/core/gateway/registry.js +97 -0
  159. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  160. package/dist/vendor/core/gateway/transport.js +82 -0
  161. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  162. package/dist/vendor/core/gateway/vault.js +29 -0
  163. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  164. package/dist/vendor/core/graph/adapters.js +91 -0
  165. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  166. package/dist/vendor/core/graph/hotspots.js +30 -0
  167. package/dist/vendor/core/graph/index.d.ts +1 -0
  168. package/dist/vendor/core/graph/index.js +2 -0
  169. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  170. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  171. package/dist/vendor/core/index.d.ts +7 -4
  172. package/dist/vendor/core/index.js +222 -64
  173. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  174. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  175. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  176. package/dist/vendor/core/learning/prior-sets.js +111 -0
  177. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  178. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  179. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  180. package/dist/vendor/core/leash/blast-radius.js +156 -0
  181. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  182. package/dist/vendor/core/leash/policy-leash.js +117 -0
  183. package/dist/vendor/core/memo/memo.d.ts +63 -0
  184. package/dist/vendor/core/memo/memo.js +97 -0
  185. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  186. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  187. package/dist/vendor/core/memory/palace.d.ts +84 -0
  188. package/dist/vendor/core/memory/palace.js +379 -0
  189. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  190. package/dist/vendor/core/merge/ast-merge.js +350 -0
  191. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  192. package/dist/vendor/core/merge/text-merge.js +182 -0
  193. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  194. package/dist/vendor/core/otel/tracer.js +116 -0
  195. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  196. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  197. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  198. package/dist/vendor/core/parallel/scorer.js +65 -0
  199. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  200. package/dist/vendor/core/pattern-detection.js +108 -0
  201. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  202. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  203. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  204. package/dist/vendor/core/persistence/cleanup.js +131 -0
  205. package/dist/vendor/core/persistence/index.d.ts +2 -0
  206. package/dist/vendor/core/persistence/index.js +1 -0
  207. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  208. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  209. package/dist/vendor/core/persistence/store.d.ts +6 -1
  210. package/dist/vendor/core/persistence/store.js +5 -0
  211. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  212. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  213. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  214. package/dist/vendor/core/policy/policy-loader.js +170 -0
  215. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  216. package/dist/vendor/core/policy/policy-schema.js +78 -0
  217. package/dist/vendor/core/policy.d.ts +6 -0
  218. package/dist/vendor/core/probe/probe.d.ts +49 -0
  219. package/dist/vendor/core/probe/probe.js +115 -0
  220. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  221. package/dist/vendor/core/proof/patch-proof.js +84 -0
  222. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  223. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  224. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  225. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  226. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  227. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  228. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  229. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  230. package/dist/vendor/core/replay/replay.d.ts +85 -0
  231. package/dist/vendor/core/replay/replay.js +109 -0
  232. package/dist/vendor/core/router/engine.d.ts +54 -0
  233. package/dist/vendor/core/router/engine.js +131 -0
  234. package/dist/vendor/core/router/index.d.ts +1 -0
  235. package/dist/vendor/core/router/index.js +2 -0
  236. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  237. package/dist/vendor/core/router/trust-calibration.js +127 -0
  238. package/dist/vendor/core/run-martin.d.ts +2 -0
  239. package/dist/vendor/core/run-martin.js +287 -0
  240. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  241. package/dist/vendor/core/security/cve-scanner.js +178 -0
  242. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  243. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  244. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  245. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  246. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  247. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  248. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  249. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  250. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  251. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  252. package/dist/vendor/core/surface-signals.d.ts +21 -0
  253. package/dist/vendor/core/surface-signals.js +139 -0
  254. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  255. package/dist/vendor/core/truth/truth-wall.js +69 -0
  256. package/dist/vendor/core/truth-spine.d.ts +26 -0
  257. package/dist/vendor/core/truth-spine.js +62 -0
  258. package/dist/vendor/core/types.d.ts +115 -0
  259. package/dist/vendor/core/types.js +2 -0
  260. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  261. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  262. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  263. package/dist/vendor/core/verifier-pyramid.js +111 -0
  264. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  265. package/dist/vendor/core/workflow-artifacts.js +668 -0
  266. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  267. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  268. package/docs/assets/cli-animated.svg +139 -0
  269. package/docs/assets/cli-static.svg +34 -0
  270. package/docs/assets/github-hero-v2.svg +23 -0
  271. package/docs/assets/martin-raplph.png.jpg +0 -0
  272. package/docs/assets/martinloop-logo.png +0 -0
  273. package/docs/assets/nvidia-inception-program-light.png +0 -0
  274. package/docs/assets/nvidia-inception-program.png +0 -0
  275. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  276. package/docs/assets/side-by-side.svg +134 -0
  277. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
  278. package/docs/oss/EXAMPLES.md +9 -1
  279. package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
  280. package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
  281. package/docs/oss/QUICKSTART.md +39 -4
  282. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
  283. package/docs/oss/README.md +7 -4
  284. package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
  285. package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
  286. package/package.json +129 -49
@@ -0,0 +1,8 @@
1
+ export interface MigrationCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function handleMigrationCommand(args: string[], options?: {
7
+ cwd?: string;
8
+ }): Promise<MigrationCommandResult>;
@@ -0,0 +1,67 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { verifyMigrationPlan } from "@martin/sdk";
4
+ export async function handleMigrationCommand(args, options = {}) {
5
+ const subcommand = args[0];
6
+ try {
7
+ if (subcommand !== "verify") {
8
+ return {
9
+ stdout: "",
10
+ stderr: [
11
+ "Usage: martin migration verify --plan <path> [options]",
12
+ "",
13
+ "Options:",
14
+ " --now <timestamp>",
15
+ " --release-windows <start/end,start/end>",
16
+ " --output <path>"
17
+ ].join("\n"),
18
+ exitCode: 1
19
+ };
20
+ }
21
+ const planPath = parseFlag(args, "--plan");
22
+ if (!planPath) {
23
+ return { stdout: "", stderr: "Missing --plan", exitCode: 1 };
24
+ }
25
+ const cwd = options.cwd ?? process.cwd();
26
+ const resolvedPlanPath = resolve(cwd, planPath);
27
+ const rawPlan = await readFile(resolvedPlanPath, "utf8");
28
+ const plan = JSON.parse(rawPlan);
29
+ const releaseWindows = parseCsvFlag(args, "--release-windows");
30
+ const now = parseFlag(args, "--now");
31
+ const verificationOptions = {
32
+ ...(now ? { now } : {}),
33
+ ...(releaseWindows.length > 0 ? { releaseWindows } : {})
34
+ };
35
+ const readiness = verifyMigrationPlan(plan, verificationOptions);
36
+ const outputPath = resolve(cwd, parseFlag(args, "--output") ?? "cutover-readiness.json");
37
+ await mkdir(dirname(outputPath), { recursive: true });
38
+ await writeFile(outputPath, JSON.stringify(readiness, null, 2), "utf8");
39
+ return {
40
+ stdout: JSON.stringify({
41
+ command: "migration.verify",
42
+ outputPath,
43
+ result: readiness
44
+ }, null, 2),
45
+ stderr: "",
46
+ exitCode: 0
47
+ };
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ return { stdout: "", stderr: message, exitCode: 1 };
52
+ }
53
+ }
54
+ function parseFlag(args, flag) {
55
+ const index = args.indexOf(flag);
56
+ return index >= 0 ? args[index + 1] : undefined;
57
+ }
58
+ function parseCsvFlag(args, flag) {
59
+ const value = parseFlag(args, flag);
60
+ return value
61
+ ? value
62
+ .split(",")
63
+ .map((entry) => entry.trim())
64
+ .filter((entry) => entry.length > 0)
65
+ : [];
66
+ }
67
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1,23 @@
1
+ export interface PriorCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function resolvePriorStoreDir(cwd?: string): string;
7
+ /**
8
+ * Handles `martin prior <subcommand> [args]`
9
+ *
10
+ * Subcommands:
11
+ * list — list all PriorSets
12
+ * status --prior-set-id <id> — show current gate for a PriorSet
13
+ * promote --prior-set-id <id> --gate <gate> — advance gate (sequential only)
14
+ * rollback --prior-set-id <id> — revert to previous gate
15
+ */
16
+ export declare function handlePriorCommand(args: string[], options?: {
17
+ cwd?: string;
18
+ }): Promise<PriorCommandResult>;
19
+ /**
20
+ * Seeds a PriorSet into the store if it doesn't already exist.
21
+ * Used by tests and the CLI init flow.
22
+ */
23
+ export declare function seedPriorSet(priorSetId: string, taskClass: string, priors: Record<string, number>, storeDir: string): Promise<void>;
@@ -0,0 +1,145 @@
1
+ import { join } from "node:path";
2
+ import { listPriorSets, getPriorSet, promotePriorSet, rollbackPriorSet, createPriorSet, savePriorStore, loadPriorStore } from "../../core/index.js";
3
+ const VALID_GATES = ["development", "backtest", "shadow", "live"];
4
+ // ─── Store path resolution ────────────────────────────────────────────────────
5
+ export function resolvePriorStoreDir(cwd) {
6
+ return join(cwd ?? process.cwd(), ".martin");
7
+ }
8
+ // ─── Command handler ──────────────────────────────────────────────────────────
9
+ /**
10
+ * Handles `martin prior <subcommand> [args]`
11
+ *
12
+ * Subcommands:
13
+ * list — list all PriorSets
14
+ * status --prior-set-id <id> — show current gate for a PriorSet
15
+ * promote --prior-set-id <id> --gate <gate> — advance gate (sequential only)
16
+ * rollback --prior-set-id <id> — revert to previous gate
17
+ */
18
+ export async function handlePriorCommand(args, options = {}) {
19
+ const storeDir = resolvePriorStoreDir(options.cwd);
20
+ const subcommand = args[0];
21
+ try {
22
+ switch (subcommand) {
23
+ case "list": {
24
+ const priorSets = await listPriorSets(storeDir);
25
+ if (priorSets.length === 0) {
26
+ return {
27
+ stdout: JSON.stringify({ priorSets: [], message: "No PriorSets found." }, null, 2),
28
+ stderr: "",
29
+ exitCode: 0
30
+ };
31
+ }
32
+ return {
33
+ stdout: JSON.stringify({ priorSets }, null, 2),
34
+ stderr: "",
35
+ exitCode: 0
36
+ };
37
+ }
38
+ case "status": {
39
+ const priorSetId = parseFlag(args, "--prior-set-id");
40
+ if (!priorSetId) {
41
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
42
+ }
43
+ const ps = await getPriorSet(priorSetId, storeDir);
44
+ if (!ps) {
45
+ return { stdout: "", stderr: `PriorSet not found: ${priorSetId}`, exitCode: 1 };
46
+ }
47
+ return {
48
+ stdout: JSON.stringify({
49
+ priorSetId: ps.priorSetId,
50
+ taskClass: ps.taskClass,
51
+ promotionGate: ps.promotionGate,
52
+ version: ps.version,
53
+ sampleCount: ps.sampleCount,
54
+ confidence: ps.confidence
55
+ }, null, 2),
56
+ stderr: "",
57
+ exitCode: 0
58
+ };
59
+ }
60
+ case "promote": {
61
+ const priorSetId = parseFlag(args, "--prior-set-id");
62
+ const gate = parseFlag(args, "--gate");
63
+ if (!priorSetId)
64
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
65
+ if (!gate)
66
+ return { stdout: "", stderr: "Missing --gate", exitCode: 1 };
67
+ if (!VALID_GATES.includes(gate)) {
68
+ return {
69
+ stdout: "",
70
+ stderr: `Invalid gate: ${gate}. Must be one of: ${VALID_GATES.join(", ")}`,
71
+ exitCode: 1
72
+ };
73
+ }
74
+ const updated = await promotePriorSet(priorSetId, gate, storeDir);
75
+ return {
76
+ stdout: JSON.stringify({
77
+ message: `PriorSet ${priorSetId} promoted to ${gate}`,
78
+ priorSetId: updated.priorSetId,
79
+ promotionGate: updated.promotionGate,
80
+ version: updated.version
81
+ }, null, 2),
82
+ stderr: "",
83
+ exitCode: 0
84
+ };
85
+ }
86
+ case "rollback": {
87
+ const priorSetId = parseFlag(args, "--prior-set-id");
88
+ if (!priorSetId)
89
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
90
+ const reverted = await rollbackPriorSet(priorSetId, storeDir);
91
+ return {
92
+ stdout: JSON.stringify({
93
+ message: `PriorSet ${priorSetId} rolled back to ${reverted.promotionGate}`,
94
+ priorSetId: reverted.priorSetId,
95
+ promotionGate: reverted.promotionGate,
96
+ version: reverted.version
97
+ }, null, 2),
98
+ stderr: "",
99
+ exitCode: 0
100
+ };
101
+ }
102
+ default:
103
+ return {
104
+ stdout: "",
105
+ stderr: [
106
+ "Usage: martin prior <subcommand> [options]",
107
+ "",
108
+ "Subcommands:",
109
+ " list List all PriorSets",
110
+ " status --prior-set-id <id> Show current gate",
111
+ " promote --prior-set-id <id> --gate <gate> Promote to next gate",
112
+ " rollback --prior-set-id <id> Revert to previous gate",
113
+ "",
114
+ "Gates (sequential): development → backtest → shadow → live"
115
+ ].join("\n"),
116
+ exitCode: 1
117
+ };
118
+ }
119
+ }
120
+ catch (err) {
121
+ const message = err instanceof Error ? err.message : String(err);
122
+ return { stdout: "", stderr: message, exitCode: 1 };
123
+ }
124
+ }
125
+ // ─── Seed helper (for testing / initial setup) ────────────────────────────────
126
+ /**
127
+ * Seeds a PriorSet into the store if it doesn't already exist.
128
+ * Used by tests and the CLI init flow.
129
+ */
130
+ export async function seedPriorSet(priorSetId, taskClass, priors, storeDir) {
131
+ const store = await loadPriorStore(storeDir);
132
+ if (store.priorSets.some(p => p.priorSetId === priorSetId))
133
+ return;
134
+ const ps = createPriorSet({ priorSetId, taskClass, priors });
135
+ store.priorSets.push(ps);
136
+ await savePriorStore(store, storeDir);
137
+ }
138
+ // ─── Internal helpers ─────────────────────────────────────────────────────────
139
+ function parseFlag(args, flag) {
140
+ const idx = args.indexOf(flag);
141
+ if (idx === -1 || idx >= args.length - 1)
142
+ return undefined;
143
+ return args[idx + 1];
144
+ }
145
+ //# sourceMappingURL=prior.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * resume.ts — SLICE-23: Resumable loops CLI handler
3
+ *
4
+ * Usage: martin resume <loop-id> [--force]
5
+ *
6
+ * 1. Reads checkpoint from ~/.martin/loops/<loop-id>/checkpoint.json
7
+ * 2. Validates workspace hashes
8
+ * 3. If modified and no --force: prints error and exits with code 1
9
+ * 4. If valid (or --force): prints resume state and exits with instructions
10
+ * (actual resume wiring into runMartin is future work — this slice ships
11
+ * the checkpoint infrastructure and the CLI entry point)
12
+ * 5. Prints: "Resuming loop <id> from attempt <N>. Cost so far: $<M>"
13
+ */
14
+ export interface ResumeCommandResult {
15
+ stdout: string;
16
+ stderr: string;
17
+ exitCode: number;
18
+ }
19
+ export declare function handleResumeCommand(args: string[], _options?: {
20
+ cwd?: string;
21
+ }): Promise<ResumeCommandResult>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * resume.ts — SLICE-23: Resumable loops CLI handler
3
+ *
4
+ * Usage: martin resume <loop-id> [--force]
5
+ *
6
+ * 1. Reads checkpoint from ~/.martin/loops/<loop-id>/checkpoint.json
7
+ * 2. Validates workspace hashes
8
+ * 3. If modified and no --force: prints error and exits with code 1
9
+ * 4. If valid (or --force): prints resume state and exits with instructions
10
+ * (actual resume wiring into runMartin is future work — this slice ships
11
+ * the checkpoint infrastructure and the CLI entry point)
12
+ * 5. Prints: "Resuming loop <id> from attempt <N>. Cost so far: $<M>"
13
+ */
14
+ export async function handleResumeCommand(args, _options = {}) {
15
+ const loopId = readPositional(args);
16
+ const force = args.includes("--force");
17
+ if (!loopId) {
18
+ return {
19
+ exitCode: 1,
20
+ stdout: "",
21
+ stderr: "Error: resume requires a loop ID. Usage: martin resume <loopId> [--force]"
22
+ };
23
+ }
24
+ const { getCheckpointStorageDir, readCheckpoint, validateWorkspaceHashes, WorkspaceModifiedError } = await import("../../core/index.js");
25
+ const storageDir = getCheckpointStorageDir();
26
+ const checkpoint = readCheckpoint(loopId, storageDir);
27
+ if (!checkpoint) {
28
+ return {
29
+ exitCode: 1,
30
+ stdout: "",
31
+ stderr: `Error: no checkpoint found for loop ${loopId}. Run has not been interrupted or checkpoint was cleaned up.`
32
+ };
33
+ }
34
+ const { valid, modifiedFiles } = validateWorkspaceHashes(checkpoint, force);
35
+ if (!valid) {
36
+ const fileList = modifiedFiles.join(", ");
37
+ return {
38
+ exitCode: 1,
39
+ stdout: "",
40
+ stderr: [
41
+ `Error: workspace has been modified since checkpoint for loop ${loopId}.`,
42
+ `Modified files: ${fileList}`,
43
+ "Use --force to resume anyway."
44
+ ].join("\n")
45
+ };
46
+ }
47
+ const resumeFromAttempt = checkpoint.lastCompletedAttempt + 1;
48
+ const costFormatted = checkpoint.costSoFar.toFixed(4);
49
+ const summary = [
50
+ `Resuming loop ${loopId} from attempt ${resumeFromAttempt}. Cost so far: $${costFormatted}`,
51
+ `Phase at checkpoint: ${checkpoint.phase}`,
52
+ `Last attempt rolled back: ${String(checkpoint.lastAttemptRolledBack)}`,
53
+ ...(force && modifiedFiles.length > 0
54
+ ? [`Warning: resuming with --force despite ${modifiedFiles.length} modified file(s).`]
55
+ : []),
56
+ "",
57
+ "Note: this command prints resume state only. Pass the loop ID to martin run to re-execute."
58
+ ].join("\n");
59
+ return {
60
+ exitCode: 0,
61
+ stdout: summary,
62
+ stderr: ""
63
+ };
64
+ }
65
+ function readPositional(args) {
66
+ for (const arg of args) {
67
+ if (!arg.startsWith("--")) {
68
+ return arg;
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ //# sourceMappingURL=resume.js.map
@@ -0,0 +1,6 @@
1
+ export interface VerifyCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function handleVerifyCommand(args: string[]): Promise<VerifyCommandResult>;
@@ -0,0 +1,43 @@
1
+ import { join } from "node:path";
2
+ import { resolveRunsRoot, verifyLoopRecordAttestation } from "../../core/index.js";
3
+ export async function handleVerifyCommand(args) {
4
+ const loopId = parseFlag(args, "--loop") ?? args[0];
5
+ if (!loopId) {
6
+ return {
7
+ stdout: "",
8
+ stderr: "Missing --loop. Usage: martin verify --loop <id>",
9
+ exitCode: 1
10
+ };
11
+ }
12
+ const runRoot = join(resolveRunsRoot(process.env), loopId);
13
+ const verification = await verifyLoopRecordAttestation(runRoot);
14
+ if (verification.ok) {
15
+ return {
16
+ stdout: JSON.stringify({
17
+ command: "verify",
18
+ status: "passed",
19
+ loopId,
20
+ file: verification.file,
21
+ keyId: verification.keyId
22
+ }, null, 2),
23
+ stderr: "",
24
+ exitCode: 0
25
+ };
26
+ }
27
+ return {
28
+ stdout: "",
29
+ stderr: JSON.stringify({
30
+ command: "verify",
31
+ status: "failed",
32
+ loopId,
33
+ file: verification.file,
34
+ reason: verification.reason
35
+ }, null, 2),
36
+ exitCode: 1
37
+ };
38
+ }
39
+ function parseFlag(args, flag) {
40
+ const index = args.indexOf(flag);
41
+ return index >= 0 ? args[index + 1] : undefined;
42
+ }
43
+ //# sourceMappingURL=verify.js.map
@@ -1,4 +1,4 @@
1
- import { type LoopBudget } from "../contracts/index.js";
1
+ import { type LoopBudget, type MutationMode } from "../contracts/index.js";
2
2
  export type RunCommandRequest = {
3
3
  workspaceId: string;
4
4
  projectId: string;
@@ -11,6 +11,7 @@ export type RunCommandRequest = {
11
11
  cwd?: string;
12
12
  model?: string;
13
13
  engine?: string;
14
+ mutationMode?: MutationMode;
14
15
  allowedPaths?: string[];
15
16
  deniedPaths?: string[];
16
17
  acceptanceCriteria?: string[];
@@ -23,6 +24,10 @@ export type ParsedCliArguments = {
23
24
  } | {
24
25
  command: "bench";
25
26
  suiteId: string;
27
+ } | {
28
+ command: "demo";
29
+ directory: string;
30
+ force: boolean;
26
31
  } | {
27
32
  command: "inspect";
28
33
  file: string;
@@ -1,7 +1,8 @@
1
- import { appendFile, mkdir, readFile } from "node:fs/promises";
1
+ import { appendFile, cp, mkdir, readFile, readdir, rm } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
- import { isAbsolute, join, resolve } from "node:path";
4
- import { createClaudeCliAdapter, createCodexCliAdapter, createStubDirectProviderAdapter } from "../adapters/index.js";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { createClaudeCliAdapter, createCodexCliAdapter, createStubDirectProviderAdapter, createVerifierOnlyAdapter } from "../adapters/index.js";
5
6
  import { runMartin } from "../core/index.js";
6
7
  import { buildPortfolioSnapshot, createLoopRecord } from "../contracts/index.js";
7
8
  export async function executeCli(args) {
@@ -30,7 +31,7 @@ export async function executeCli(args) {
30
31
  }
31
32
  };
32
33
  const workingDirectory = parsed.request.cwd ?? readOption(args, "--cwd") ?? process.cwd();
33
- const adapter = selectAdapter(args, workingDirectory, parsed.request.model, parsed.request.engine);
34
+ const adapter = selectAdapter(args, workingDirectory, parsed.request.model, parsed.request.engine, parsed.request.mutationMode);
34
35
  let result;
35
36
  try {
36
37
  result = await runMartin({
@@ -40,6 +41,7 @@ export async function executeCli(args) {
40
41
  title: resolvedRequest.title,
41
42
  objective: resolvedRequest.objective,
42
43
  verificationPlan: resolvedRequest.verificationPlan,
44
+ ...(resolvedRequest.mutationMode ? { mutationMode: resolvedRequest.mutationMode } : {}),
43
45
  repoRoot: workingDirectory,
44
46
  ...(resolvedRequest.allowedPaths?.length ? { allowedPaths: resolvedRequest.allowedPaths } : {}),
45
47
  ...(resolvedRequest.deniedPaths?.length ? { deniedPaths: resolvedRequest.deniedPaths } : {}),
@@ -58,6 +60,7 @@ export async function executeCli(args) {
58
60
  title: resolvedRequest.title,
59
61
  objective: resolvedRequest.objective,
60
62
  verificationPlan: resolvedRequest.verificationPlan,
63
+ ...(resolvedRequest.mutationMode ? { mutationMode: resolvedRequest.mutationMode } : {}),
61
64
  repoRoot: workingDirectory
62
65
  },
63
66
  budget: resolvedRequest.budget,
@@ -115,6 +118,27 @@ export async function executeCli(args) {
115
118
  stderr: "The benchmark harness remains a workspace-only RC surface and is not part of the publishable @martin/cli boundary yet. Use pnpm --filter @martin/benchmarks test or pnpm --filter @martin/benchmarks eval:phase12 from the repo root instead."
116
119
  };
117
120
  }
121
+ case "demo": {
122
+ try {
123
+ const targetDirectory = await createDemoWorkspace({
124
+ targetDirectory: parsed.directory,
125
+ force: parsed.force
126
+ });
127
+ return {
128
+ exitCode: 0,
129
+ stdout: renderDemoInstructions(targetDirectory),
130
+ stderr: ""
131
+ };
132
+ }
133
+ catch (error) {
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ return {
136
+ exitCode: 1,
137
+ stdout: "",
138
+ stderr: `Error: ${message}`
139
+ };
140
+ }
141
+ }
118
142
  case "inspect": {
119
143
  try {
120
144
  const contents = await readFile(parsed.file, "utf8");
@@ -295,6 +319,9 @@ export function parseCliArguments(args) {
295
319
  request.cwd = next;
296
320
  index += 1;
297
321
  break;
322
+ case "--verify-only":
323
+ request.mutationMode = "verify_only";
324
+ break;
298
325
  case "--allow-path":
299
326
  if (next) {
300
327
  request.allowedPaths = [...(request.allowedPaths ?? []), next];
@@ -339,6 +366,7 @@ export function parseCliArguments(args) {
339
366
  ...(request.cwd ? { cwd: request.cwd } : {}),
340
367
  ...(request.model ? { model: request.model } : {}),
341
368
  ...(request.engine ? { engine: request.engine } : {}),
369
+ ...(request.mutationMode ? { mutationMode: request.mutationMode } : {}),
342
370
  ...(request.allowedPaths?.length ? { allowedPaths: request.allowedPaths } : {}),
343
371
  ...(request.deniedPaths?.length ? { deniedPaths: request.deniedPaths } : {}),
344
372
  ...(request.acceptanceCriteria?.length ? { acceptanceCriteria: request.acceptanceCriteria } : {})
@@ -351,6 +379,13 @@ export function parseCliArguments(args) {
351
379
  suiteId: readOption(rest, "--suite") ?? "ralphy-smoke"
352
380
  };
353
381
  }
382
+ if (command === "demo") {
383
+ return {
384
+ command: "demo",
385
+ directory: resolve(readOption(rest, "--dir") ?? join(process.cwd(), "martin-loop-demo")),
386
+ force: hasFlag(rest, "--force")
387
+ };
388
+ }
354
389
  if (command === "inspect") {
355
390
  return {
356
391
  command: "inspect",
@@ -373,12 +408,14 @@ export function renderCliHelp() {
373
408
  " martin-loop run <objective> [options]",
374
409
  " martin run <objective> [options] (alias)",
375
410
  " martin-loop run --objective <text> [options]",
411
+ " martin-loop demo [--dir <path>] [--force]",
376
412
  " martin-loop inspect --file <path>",
377
413
  " martin-loop resume <loopId>",
378
414
  " martin-loop bench --suite <suiteId>",
379
415
  "",
380
416
  "Commands:",
381
417
  " run Execute a bounded Martin loop against the current repository.",
418
+ " demo Copy a safe local sandbox so you can try MartinLoop outside your own repo.",
382
419
  " inspect Read a persisted loop record and summarize its portfolio metrics.",
383
420
  " resume Load a persisted loop record by loop ID from ~/.martin/runs/.",
384
421
  " bench Redirect to the workspace-only RC benchmark harness.",
@@ -390,12 +427,19 @@ export function renderCliHelp() {
390
427
  " --cwd <path> Set the repo root used for repo-backed runs.",
391
428
  " --budget <n> Set the hard cost cap in USD (subprocess killed at limit).",
392
429
  " --budget-usd <n> Alias for --budget.",
393
- " --verify <cmd> Shell command to run as the verifier after each attempt.",
430
+ " --soft-limit-usd <n> Soft budget warning threshold in USD.",
394
431
  " --max-iterations <n> Set the maximum number of attempts.",
432
+ " --max-tokens <n> Set the maximum total token budget.",
433
+ " --verify <cmd> Shell command to run as the verifier after each attempt.",
434
+ " --verify-only Skip the coding adapter and run the verifier only.",
395
435
  " --allow-path <glob> Restrict agent writes to this path pattern (repeatable).",
396
436
  " --deny-path <glob> Block agent from this path pattern (repeatable).",
397
437
  " --accept <criterion> Add an acceptance criterion to the prompt (repeatable).",
398
- " --config <path> Path to martin.config.yaml."
438
+ " --config <path> Path to martin.config.yaml.",
439
+ "",
440
+ "Demo options:",
441
+ " --dir <path> Target directory for the copied demo sandbox.",
442
+ " --force Replace an existing non-empty demo target."
399
443
  ].join("\n");
400
444
  }
401
445
  function readOption(tokens, flag) {
@@ -418,6 +462,76 @@ function parseLoopRecords(contents) {
418
462
  return lines.map((line) => JSON.parse(line));
419
463
  }
420
464
  }
465
+ async function createDemoWorkspace(input) {
466
+ const rootDir = await findMartinPackageRoot();
467
+ const sourceDirectory = join(rootDir, "demo", "seeded-workspace");
468
+ try {
469
+ await readdir(sourceDirectory);
470
+ }
471
+ catch (error) {
472
+ if (isNodeErrorWithCode(error, "ENOENT")) {
473
+ throw new Error(`Demo assets are missing from this install: ${sourceDirectory}`);
474
+ }
475
+ throw error;
476
+ }
477
+ const targetDirectory = resolve(input.targetDirectory);
478
+ const existingEntries = await readdir(targetDirectory).catch((error) => {
479
+ if (isNodeErrorWithCode(error, "ENOENT")) {
480
+ return undefined;
481
+ }
482
+ throw error;
483
+ });
484
+ if (existingEntries) {
485
+ if (existingEntries.length > 0 && !input.force) {
486
+ throw new Error(`Demo target already exists and is not empty: ${targetDirectory}. Re-run with --force to replace it.`);
487
+ }
488
+ await rm(targetDirectory, { force: true, recursive: true });
489
+ }
490
+ await mkdir(dirname(targetDirectory), { recursive: true });
491
+ await cp(sourceDirectory, targetDirectory, { recursive: true });
492
+ return targetDirectory;
493
+ }
494
+ async function findMartinPackageRoot() {
495
+ let currentDirectory = dirname(fileURLToPath(import.meta.url));
496
+ for (let depth = 0; depth < 8; depth += 1) {
497
+ const manifestPath = join(currentDirectory, "package.json");
498
+ try {
499
+ const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
500
+ if (manifest.name === "martin-loop") {
501
+ return currentDirectory;
502
+ }
503
+ }
504
+ catch (error) {
505
+ if (!isNodeErrorWithCode(error, "ENOENT")) {
506
+ throw error;
507
+ }
508
+ }
509
+ const parentDirectory = dirname(currentDirectory);
510
+ if (parentDirectory === currentDirectory) {
511
+ break;
512
+ }
513
+ currentDirectory = parentDirectory;
514
+ }
515
+ throw new Error("Unable to resolve the martin-loop package root for demo assets.");
516
+ }
517
+ function renderDemoInstructions(targetDirectory) {
518
+ return [
519
+ `MartinLoop demo sandbox created at ${targetDirectory}`,
520
+ "",
521
+ "Next steps:",
522
+ ` cd ${targetDirectory}`,
523
+ " npm install",
524
+ " npm test",
525
+ "",
526
+ "Safe first run (no provider spend):",
527
+ ' MARTIN_LIVE=false npx martin-loop run "Summarize the demo workspace and confirm the verifier is green" --verify "npm test"',
528
+ "",
529
+ "Optional live run:",
530
+ ' npx martin-loop run "Add support for a discount percentage to summarizeInvoice and update the tests" --verify "npm test" --engine codex',
531
+ "",
532
+ `Task ideas live in ${join(targetDirectory, "TASKS.md")}`
533
+ ].join("\n");
534
+ }
421
535
  async function resolveGuardrails(request, rawArgs) {
422
536
  const tokens = rawArgs.slice(1);
423
537
  const { config, configPath } = await loadGuardrailsConfig(request.configPath);
@@ -615,7 +729,10 @@ function isNodeErrorWithCode(error, code) {
615
729
  * --engine codex — real Codex CLI subprocess
616
730
  * MARTIN_LIVE=false — stub adapter (for tests / dry-runs)
617
731
  */
618
- function selectAdapter(rawArgs, workingDirectory, modelOverride, engineOverride) {
732
+ function selectAdapter(rawArgs, workingDirectory, modelOverride, engineOverride, mutationMode) {
733
+ if (mutationMode === "verify_only") {
734
+ return createVerifierOnlyAdapter({ workingDirectory });
735
+ }
619
736
  if (process.env.MARTIN_LIVE === "false") {
620
737
  return createStubDirectProviderAdapter({
621
738
  label: "Stub adapter (MARTIN_LIVE=false)",