martin-loop 0.1.5 → 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 (274) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/LICENSE +21 -21
  3. package/README.md +307 -398
  4. package/demo/seeded-workspace/README.md +35 -35
  5. package/demo/seeded-workspace/TASKS.md +29 -29
  6. package/demo/seeded-workspace/martin.config.yaml +11 -11
  7. package/demo/seeded-workspace/package.json +8 -8
  8. package/demo/seeded-workspace/src/invoice-summary.js +11 -11
  9. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
  10. package/dist/bin/martin-loop.js +0 -0
  11. package/dist/vendor/adapters/counter.d.ts +1 -0
  12. package/dist/vendor/adapters/counter.js +4 -0
  13. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  14. package/dist/vendor/adapters/git-baseline.js +233 -0
  15. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  16. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  17. package/dist/vendor/adapters/usage.d.ts +48 -0
  18. package/dist/vendor/adapters/usage.js +66 -0
  19. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  20. package/dist/vendor/cli/bin/exit.js +28 -0
  21. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  22. package/dist/vendor/cli/commands/analyze.js +58 -0
  23. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  24. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  25. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  26. package/dist/vendor/cli/commands/audit.js +199 -0
  27. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  28. package/dist/vendor/cli/commands/corpus.js +60 -0
  29. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  30. package/dist/vendor/cli/commands/doctor.js +219 -0
  31. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  32. package/dist/vendor/cli/commands/explain.js +176 -0
  33. package/dist/vendor/cli/commands/export.d.ts +5 -0
  34. package/dist/vendor/cli/commands/export.js +60 -0
  35. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  36. package/dist/vendor/cli/commands/governance.js +95 -0
  37. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  38. package/dist/vendor/cli/commands/improve.js +396 -0
  39. package/dist/vendor/cli/commands/init.d.ts +8 -0
  40. package/dist/vendor/cli/commands/init.js +281 -0
  41. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  42. package/dist/vendor/cli/commands/migration.js +67 -0
  43. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  44. package/dist/vendor/cli/commands/prior.js +145 -0
  45. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  46. package/dist/vendor/cli/commands/resume.js +73 -0
  47. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  48. package/dist/vendor/cli/commands/verify.js +43 -0
  49. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  50. package/dist/vendor/cli/research/public-corpus.js +151 -0
  51. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  52. package/dist/vendor/cli/ui/error-card.js +103 -0
  53. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  54. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  55. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  56. package/dist/vendor/cli/ui/summary-card.js +102 -0
  57. package/dist/vendor/contracts/audit.d.ts +46 -0
  58. package/dist/vendor/contracts/audit.js +360 -0
  59. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  60. package/dist/vendor/contracts/post-phase15.js +166 -0
  61. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  62. package/dist/vendor/core/agent/mandates.js +178 -0
  63. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  64. package/dist/vendor/core/agent/receipts.js +131 -0
  65. package/dist/vendor/core/agent/signing.d.ts +17 -0
  66. package/dist/vendor/core/agent/signing.js +91 -0
  67. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  68. package/dist/vendor/core/attestation/sign.js +216 -0
  69. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  70. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  71. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  72. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  73. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  74. package/dist/vendor/core/autonomy/envelope.js +27 -0
  75. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  76. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  77. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  78. package/dist/vendor/core/autonomy/resume.js +23 -0
  79. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  80. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  81. package/dist/vendor/core/context-distillation.d.ts +3 -0
  82. package/dist/vendor/core/context-distillation.js +44 -0
  83. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  84. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  85. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  86. package/dist/vendor/core/context-flow/entities.js +44 -0
  87. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  88. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  89. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  90. package/dist/vendor/core/context-flow/index.js +24 -0
  91. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  92. package/dist/vendor/core/context-flow/labels.js +17 -0
  93. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  94. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  95. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  96. package/dist/vendor/core/context-flow/profiles.js +36 -0
  97. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  98. package/dist/vendor/core/context-flow/redaction.js +6 -0
  99. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  101. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  102. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  103. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  105. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  106. package/dist/vendor/core/context-flow/types.js +2 -0
  107. package/dist/vendor/core/context-utility.d.ts +47 -0
  108. package/dist/vendor/core/context-utility.js +405 -0
  109. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  110. package/dist/vendor/core/cost/pipeline.js +141 -0
  111. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  112. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  113. package/dist/vendor/core/cost-governor.d.ts +2 -0
  114. package/dist/vendor/core/cost-governor.js +50 -0
  115. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  116. package/dist/vendor/core/cve/cve-check.js +172 -0
  117. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  118. package/dist/vendor/core/digital-twin/index.js +90 -0
  119. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  120. package/dist/vendor/core/drift/drift-graph.js +100 -0
  121. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  122. package/dist/vendor/core/drift/objective-lock.js +88 -0
  123. package/dist/vendor/core/drift/scope.d.ts +46 -0
  124. package/dist/vendor/core/drift/scope.js +102 -0
  125. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  126. package/dist/vendor/core/drift/signature-lock.js +202 -0
  127. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  128. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  129. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  130. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  131. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  132. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  133. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  134. package/dist/vendor/core/exit-intelligence.js +58 -0
  135. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  136. package/dist/vendor/core/explain/formatter.js +171 -0
  137. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  138. package/dist/vendor/core/explain/timeline.js +213 -0
  139. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  140. package/dist/vendor/core/failure-taxonomy.js +76 -0
  141. package/dist/vendor/core/gateway/index.d.ts +10 -0
  142. package/dist/vendor/core/gateway/index.js +12 -0
  143. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  144. package/dist/vendor/core/gateway/registry.js +97 -0
  145. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  146. package/dist/vendor/core/gateway/transport.js +82 -0
  147. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  148. package/dist/vendor/core/gateway/vault.js +29 -0
  149. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  150. package/dist/vendor/core/graph/adapters.js +91 -0
  151. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  152. package/dist/vendor/core/graph/hotspots.js +30 -0
  153. package/dist/vendor/core/graph/index.d.ts +1 -0
  154. package/dist/vendor/core/graph/index.js +2 -0
  155. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  156. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  157. package/dist/vendor/core/index.d.ts +2 -2
  158. package/dist/vendor/core/index.js +38 -12
  159. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  160. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  161. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  162. package/dist/vendor/core/learning/prior-sets.js +111 -0
  163. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  164. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  165. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  166. package/dist/vendor/core/leash/blast-radius.js +156 -0
  167. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  168. package/dist/vendor/core/leash/policy-leash.js +117 -0
  169. package/dist/vendor/core/memo/memo.d.ts +63 -0
  170. package/dist/vendor/core/memo/memo.js +97 -0
  171. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  172. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  173. package/dist/vendor/core/memory/palace.d.ts +84 -0
  174. package/dist/vendor/core/memory/palace.js +379 -0
  175. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  176. package/dist/vendor/core/merge/ast-merge.js +350 -0
  177. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  178. package/dist/vendor/core/merge/text-merge.js +182 -0
  179. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  180. package/dist/vendor/core/otel/tracer.js +116 -0
  181. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  182. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  183. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  184. package/dist/vendor/core/parallel/scorer.js +65 -0
  185. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  186. package/dist/vendor/core/pattern-detection.js +108 -0
  187. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  188. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  189. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  190. package/dist/vendor/core/persistence/cleanup.js +131 -0
  191. package/dist/vendor/core/persistence/index.d.ts +2 -0
  192. package/dist/vendor/core/persistence/index.js +1 -0
  193. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  194. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  195. package/dist/vendor/core/persistence/store.d.ts +6 -1
  196. package/dist/vendor/core/persistence/store.js +5 -0
  197. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  198. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  199. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  200. package/dist/vendor/core/policy/policy-loader.js +170 -0
  201. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  202. package/dist/vendor/core/policy/policy-schema.js +78 -0
  203. package/dist/vendor/core/probe/probe.d.ts +49 -0
  204. package/dist/vendor/core/probe/probe.js +115 -0
  205. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  206. package/dist/vendor/core/proof/patch-proof.js +84 -0
  207. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  208. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  209. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  210. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  211. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  212. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  213. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  214. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  215. package/dist/vendor/core/replay/replay.d.ts +85 -0
  216. package/dist/vendor/core/replay/replay.js +109 -0
  217. package/dist/vendor/core/router/engine.d.ts +54 -0
  218. package/dist/vendor/core/router/engine.js +131 -0
  219. package/dist/vendor/core/router/index.d.ts +1 -0
  220. package/dist/vendor/core/router/index.js +2 -0
  221. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  222. package/dist/vendor/core/router/trust-calibration.js +127 -0
  223. package/dist/vendor/core/run-martin.d.ts +2 -0
  224. package/dist/vendor/core/run-martin.js +287 -0
  225. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  226. package/dist/vendor/core/security/cve-scanner.js +178 -0
  227. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  228. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  229. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  230. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  231. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  232. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  233. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  234. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  235. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  236. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  237. package/dist/vendor/core/surface-signals.d.ts +21 -0
  238. package/dist/vendor/core/surface-signals.js +139 -0
  239. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  240. package/dist/vendor/core/truth/truth-wall.js +69 -0
  241. package/dist/vendor/core/truth-spine.d.ts +26 -0
  242. package/dist/vendor/core/truth-spine.js +62 -0
  243. package/dist/vendor/core/types.d.ts +115 -0
  244. package/dist/vendor/core/types.js +2 -0
  245. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  246. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  247. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  248. package/dist/vendor/core/verifier-pyramid.js +111 -0
  249. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  250. package/dist/vendor/core/workflow-artifacts.js +668 -0
  251. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  252. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  253. package/docs/assets/cli-animated.svg +139 -0
  254. package/docs/assets/cli-static.svg +34 -0
  255. package/docs/assets/github-hero-v2.svg +23 -0
  256. package/docs/assets/martin-raplph.png.jpg +0 -0
  257. package/docs/assets/martinloop-logo.png +0 -0
  258. package/docs/assets/nvidia-inception-program-light.png +0 -0
  259. package/docs/assets/nvidia-inception-program.png +0 -0
  260. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  261. package/docs/assets/side-by-side.svg +134 -0
  262. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
  263. package/docs/oss/EXAMPLES.md +134 -134
  264. package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
  265. package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
  266. package/docs/oss/QUICKSTART.md +170 -165
  267. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
  268. package/docs/oss/README.md +96 -96
  269. package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
  270. package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
  271. package/package.json +130 -58
  272. package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
  273. package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
  274. package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
@@ -0,0 +1,117 @@
1
+ import { evaluate as evaluateOpaPolicy } from "@martin/policy";
2
+ export async function applyPolicyToLeashDecision(decision, inputs, options = {}) {
3
+ const policyVerdict = await evaluatePolicyInputs(inputs, options);
4
+ if (policyVerdict.allow) {
5
+ return {
6
+ ...decision,
7
+ policyVerdict
8
+ };
9
+ }
10
+ const policyViolations = policyVerdict.deniedInputs.flatMap(toPolicyViolations);
11
+ const policyBlockedCommands = policyVerdict.deniedInputs
12
+ .map((input) => input.command)
13
+ .filter((command) => typeof command === "string" && command.length > 0);
14
+ return {
15
+ ...decision,
16
+ allowed: false,
17
+ blocked: true,
18
+ riskLevel: "blocked",
19
+ blockedCommands: [...new Set([...decision.blockedCommands, ...policyBlockedCommands])],
20
+ violations: [...decision.violations, ...policyViolations],
21
+ reason: joinReasons([
22
+ decision.reason,
23
+ `OPA policy denied ${policyVerdict.deniedInputs.length} safety input(s): ${policyVerdict.reasons.join("; ")}`
24
+ ]),
25
+ policyVerdict
26
+ };
27
+ }
28
+ async function evaluatePolicyInputs(inputs, options) {
29
+ if (inputs.length === 0) {
30
+ return {
31
+ allow: true,
32
+ reasons: [],
33
+ deniedInputs: [],
34
+ inputsEvaluated: 0
35
+ };
36
+ }
37
+ const deniedInputs = [];
38
+ const reasons = new Set();
39
+ let source;
40
+ let policyPath;
41
+ for (const input of inputs) {
42
+ try {
43
+ const result = await withTimeout(evaluateOpaPolicy(input, options.policyWasmPath ?? options.policyPath, options.repoRoot ? { repoRoot: options.repoRoot } : undefined), options.timeoutMs ?? 2_000);
44
+ source ??= result.source;
45
+ policyPath ??= result.policyPath;
46
+ if (!result.allow) {
47
+ for (const reason of result.reasons) {
48
+ reasons.add(reason);
49
+ }
50
+ deniedInputs.push({
51
+ surface: input.surface,
52
+ ...(input.command !== undefined ? { command: input.command } : {}),
53
+ ...(input.path !== undefined ? { path: input.path } : {}),
54
+ ...(input.value !== undefined ? { value: input.value } : {}),
55
+ reasons: result.reasons
56
+ });
57
+ }
58
+ }
59
+ catch (error) {
60
+ const message = error instanceof Error ? error.message : String(error);
61
+ reasons.add(message);
62
+ deniedInputs.push({
63
+ surface: input.surface,
64
+ ...(input.command !== undefined ? { command: input.command } : {}),
65
+ ...(input.path !== undefined ? { path: input.path } : {}),
66
+ ...(input.value !== undefined ? { value: input.value } : {}),
67
+ reasons: [message]
68
+ });
69
+ return {
70
+ allow: false,
71
+ reasons: [...reasons],
72
+ deniedInputs,
73
+ inputsEvaluated: inputs.length,
74
+ ...(source ? { source } : {}),
75
+ ...(policyPath ? { policyPath } : {}),
76
+ error: message
77
+ };
78
+ }
79
+ }
80
+ return {
81
+ allow: deniedInputs.length === 0,
82
+ reasons: [...reasons],
83
+ deniedInputs,
84
+ inputsEvaluated: inputs.length,
85
+ ...(source ? { source } : {}),
86
+ ...(policyPath ? { policyPath } : {})
87
+ };
88
+ }
89
+ function toPolicyViolations(input) {
90
+ return input.reasons.map((reason) => ({
91
+ kind: "policy_denied",
92
+ message: `OPA policy denied ${input.surface}: ${reason}`,
93
+ ...(input.command ? { command: input.command } : {}),
94
+ ...(input.path ? { file: input.path } : {}),
95
+ ...(input.value ? { match: input.value } : {})
96
+ }));
97
+ }
98
+ function joinReasons(values) {
99
+ return values.filter((value) => Boolean(value && value.length > 0)).join(" ");
100
+ }
101
+ async function withTimeout(promise, timeoutMs) {
102
+ let timeout;
103
+ try {
104
+ return await Promise.race([
105
+ promise,
106
+ new Promise((_, reject) => {
107
+ timeout = setTimeout(() => reject(new Error(`OPA policy evaluation timed out after ${timeoutMs}ms.`)), timeoutMs);
108
+ })
109
+ ]);
110
+ }
111
+ finally {
112
+ if (timeout) {
113
+ clearTimeout(timeout);
114
+ }
115
+ }
116
+ }
117
+ //# sourceMappingURL=policy-leash.js.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * memo.ts — SLICE-03
3
+ *
4
+ * Attempt memoization via objective + verifier + file hash key.
5
+ *
6
+ * On a cache HIT: the caller replays the stored patch with zero model calls.
7
+ * On a cache MISS: the caller runs the normal attempt path, then calls storeMemo.
8
+ *
9
+ * Cache key: SHA-256 of (objective, verifierCommand, sorted file path → hash pairs)
10
+ * Cache location: <memoRoot>/<key>/ (default: ~/.martin/memo/)
11
+ * Invalidation: any file hash change produces a different key → automatic miss
12
+ * TTL: keys never expire (deterministic inputs → deterministic output is safe to reuse)
13
+ */
14
+ export interface MemoInput {
15
+ /** Exact objective text. */
16
+ objective: string;
17
+ /** The verifier command string (e.g. "pnpm test"). */
18
+ verifierCommand: string;
19
+ /**
20
+ * Map of relevant file paths → their SHA-256 (or similar) hashes.
21
+ * Any change in any hash invalidates the cache key.
22
+ */
23
+ fileHashes: Record<string, string>;
24
+ }
25
+ export interface MemoEntry {
26
+ /** The SHA-256 cache key that produced this entry. */
27
+ key: string;
28
+ /** Unified diff text that was produced by the original attempt. */
29
+ patch: string;
30
+ /** Raw verifier output from the original successful attempt. */
31
+ verifierOutput: string;
32
+ /** ISO timestamp when the entry was stored. */
33
+ storedAt: string;
34
+ }
35
+ /**
36
+ * Computes the deterministic SHA-256 cache key for a given MemoInput.
37
+ *
38
+ * File hashes are sorted by path before hashing so that insertion order
39
+ * does not affect the key (same files → same key).
40
+ *
41
+ * verificationPlan step order IS significant (pass-order matters for verifiers).
42
+ */
43
+ export declare function computeMemoKey(input: MemoInput): string;
44
+ /**
45
+ * Returns the stored MemoEntry for the given inputs, or null on a cache miss.
46
+ *
47
+ * A miss occurs when:
48
+ * - No entry exists for this key (never been run)
49
+ * - Any file hash changed (different key → automatic miss)
50
+ */
51
+ export declare function lookupMemo(input: MemoInput, memoRoot?: string): Promise<MemoEntry | null>;
52
+ /**
53
+ * Persists a successful attempt result to the memo cache.
54
+ *
55
+ * Writes two files under <memoRoot>/<key>/:
56
+ * - entry.json — the full MemoEntry (patch + verifier output)
57
+ * - cache-hit.json — lightweight provenance artifact for audit trail
58
+ *
59
+ * The `entry.key` is set to the computed key before writing.
60
+ */
61
+ export declare function storeMemo(input: MemoInput, entry: Omit<MemoEntry, "key"> & {
62
+ key?: string;
63
+ }, memoRoot?: string): Promise<string>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * memo.ts — SLICE-03
3
+ *
4
+ * Attempt memoization via objective + verifier + file hash key.
5
+ *
6
+ * On a cache HIT: the caller replays the stored patch with zero model calls.
7
+ * On a cache MISS: the caller runs the normal attempt path, then calls storeMemo.
8
+ *
9
+ * Cache key: SHA-256 of (objective, verifierCommand, sorted file path → hash pairs)
10
+ * Cache location: <memoRoot>/<key>/ (default: ~/.martin/memo/)
11
+ * Invalidation: any file hash change produces a different key → automatic miss
12
+ * TTL: keys never expire (deterministic inputs → deterministic output is safe to reuse)
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
16
+ import { join } from "node:path";
17
+ import { homedir } from "node:os";
18
+ // ---------------------------------------------------------------------------
19
+ // Key computation
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Computes the deterministic SHA-256 cache key for a given MemoInput.
23
+ *
24
+ * File hashes are sorted by path before hashing so that insertion order
25
+ * does not affect the key (same files → same key).
26
+ *
27
+ * verificationPlan step order IS significant (pass-order matters for verifiers).
28
+ */
29
+ export function computeMemoKey(input) {
30
+ const sortedFiles = Object.entries(input.fileHashes)
31
+ .sort(([a], [b]) => a.localeCompare(b));
32
+ const canonical = JSON.stringify({
33
+ objective: input.objective,
34
+ verifierCommand: input.verifierCommand,
35
+ fileHashes: Object.fromEntries(sortedFiles)
36
+ });
37
+ return createHash("sha256").update(canonical, "utf8").digest("hex");
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Lookup
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Returns the stored MemoEntry for the given inputs, or null on a cache miss.
44
+ *
45
+ * A miss occurs when:
46
+ * - No entry exists for this key (never been run)
47
+ * - Any file hash changed (different key → automatic miss)
48
+ */
49
+ export async function lookupMemo(input, memoRoot = defaultMemoRoot()) {
50
+ const key = computeMemoKey(input);
51
+ const entryPath = join(memoRoot, key, "entry.json");
52
+ try {
53
+ const raw = await readFile(entryPath, "utf8");
54
+ return JSON.parse(raw);
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Store
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * Persists a successful attempt result to the memo cache.
65
+ *
66
+ * Writes two files under <memoRoot>/<key>/:
67
+ * - entry.json — the full MemoEntry (patch + verifier output)
68
+ * - cache-hit.json — lightweight provenance artifact for audit trail
69
+ *
70
+ * The `entry.key` is set to the computed key before writing.
71
+ */
72
+ export async function storeMemo(input, entry, memoRoot = defaultMemoRoot()) {
73
+ const key = computeMemoKey(input);
74
+ const keyDir = join(memoRoot, key);
75
+ await mkdir(keyDir, { recursive: true });
76
+ const fullEntry = { ...entry, key };
77
+ // Write the main entry
78
+ await writeFile(join(keyDir, "entry.json"), `${JSON.stringify(fullEntry, null, 2)}\n`, "utf8");
79
+ // Write the provenance artifact (lightweight, safe to read without loading full patch)
80
+ const provenance = {
81
+ key,
82
+ objective: input.objective,
83
+ verifierCommand: input.verifierCommand,
84
+ fileCount: Object.keys(input.fileHashes).length,
85
+ storedAt: entry.storedAt
86
+ };
87
+ await writeFile(join(keyDir, "cache-hit.json"), `${JSON.stringify(provenance, null, 2)}\n`, "utf8");
88
+ return key;
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // Internals
92
+ // ---------------------------------------------------------------------------
93
+ function defaultMemoRoot() {
94
+ return process.env["MARTIN_MEMO_DIR"]?.trim()
95
+ ?? join(homedir(), ".martin", "memo");
96
+ }
97
+ //# sourceMappingURL=memo.js.map
@@ -0,0 +1,154 @@
1
+ /**
2
+ * learning-pipeline.ts
3
+ *
4
+ * Guarded promotion pipeline for self-improving memory heuristics.
5
+ *
6
+ * Flow: candidate → shadow test → holdout validation → approval gate → promoted
7
+ *
8
+ * A learning candidate NEVER becomes an active heuristic without passing:
9
+ * 1. Shadow tests (runs in parallel with existing logic, results compared)
10
+ * 2. Holdout validation (tested on a held-out set, not the training data)
11
+ * 3. Approval gate (auto-approved if confidence >= threshold, else requires manual sign-off)
12
+ *
13
+ * Fail-open: any pipeline error keeps the existing heuristic active untouched.
14
+ */
15
+ export type CandidateStatus = "pending_shadow" | "shadow_passed" | "shadow_failed" | "holdout_passed" | "holdout_failed" | "approved" | "manual_review" | "promoted" | "blocked_protected_surface" | "rejected" | "rolled_back";
16
+ export interface LearningCandidateProvenance {
17
+ sourceType: "trace-intelligence" | "manual" | "memory-palace";
18
+ patternId?: string;
19
+ evidenceRunIds?: string[];
20
+ generatedAt: string;
21
+ confidenceScore?: number;
22
+ }
23
+ export interface LearningCandidateRollbackPlan {
24
+ strategy: "restore_last_known_good" | "disable_candidate";
25
+ note?: string;
26
+ }
27
+ export interface LearningCandidate {
28
+ candidateId: string;
29
+ sourceRunId: string;
30
+ heuristicFamily: string;
31
+ description: string;
32
+ payload: Record<string, unknown>;
33
+ status: CandidateStatus;
34
+ provenance?: LearningCandidateProvenance;
35
+ proposedPaths?: string[];
36
+ rollbackPlan?: LearningCandidateRollbackPlan;
37
+ shadowPassRate?: number;
38
+ holdoutPassRate?: number;
39
+ approvedAt?: string;
40
+ rejectedAt?: string;
41
+ rejectedReason?: string;
42
+ createdAt: string;
43
+ }
44
+ export interface ShadowTestResult {
45
+ candidateId: string;
46
+ totalCases: number;
47
+ passedCases: number;
48
+ passRate: number;
49
+ passed: boolean;
50
+ }
51
+ export interface HoldoutResult {
52
+ candidateId: string;
53
+ holdoutSize: number;
54
+ passedCases: number;
55
+ passRate: number;
56
+ passed: boolean;
57
+ }
58
+ export interface ApprovalGateConfig {
59
+ /** Auto-approve when holdout pass rate meets or exceeds this threshold (0–1). Default: 0.85 */
60
+ autoApproveThreshold: number;
61
+ /** If true, always require manual sign-off regardless of pass rate */
62
+ requireManualApproval: boolean;
63
+ }
64
+ export interface PromotionResult {
65
+ candidateId: string;
66
+ promoted: boolean;
67
+ reason: string;
68
+ }
69
+ export interface ActiveLearningHeuristics {
70
+ schemaVersion: "1.0";
71
+ updatedAt: string;
72
+ recallScoring: {
73
+ failureClassWeight: number;
74
+ interventionWeight: number;
75
+ tokenOverlapWeight: number;
76
+ completionWeight: number;
77
+ };
78
+ }
79
+ export interface LearningPipelineSummaryEntry {
80
+ candidateId: string;
81
+ heuristicFamily: string;
82
+ status: CandidateStatus;
83
+ sourceRunId: string;
84
+ createdAt: string;
85
+ confidenceScore?: number;
86
+ summary: string;
87
+ patternId?: string;
88
+ }
89
+ export interface LearningPipelineSummary {
90
+ counts: {
91
+ proposed: number;
92
+ shadowPassed: number;
93
+ holdoutPassed: number;
94
+ approved: number;
95
+ promoted: number;
96
+ rejected: number;
97
+ blockedProtectedSurface: number;
98
+ rolledBack: number;
99
+ };
100
+ protectedPaths: string[];
101
+ entries: LearningPipelineSummaryEntry[];
102
+ }
103
+ export declare const DEFAULT_PROTECTED_LEARNING_PATHS: readonly ["packages/core/src/leash.ts", "packages/core/src/leash/**", "packages/core/src/policy.ts", "packages/core/src/grounding.ts", "packages/core/src/security/**", "packages/core/src/attestation/**", "packages/contracts/src/**", "packages/mcp/src/**", "packages/cli/src/index.ts", "docs/release/**", "docs/security/**", "docs/handoffs/**", ".planning/**", "scripts/**"];
104
+ export declare const DEFAULT_ACTIVE_LEARNING_HEURISTICS: ActiveLearningHeuristics;
105
+ /**
106
+ * Runs the candidate heuristic in shadow mode alongside the existing logic.
107
+ * The shadow result is compared but never used to change actual behaviour.
108
+ *
109
+ * `existingScores` and `candidateScores` are parallel arrays — one per recall query.
110
+ * A case is "passed" when the candidate score >= existing score (does not regress).
111
+ */
112
+ export declare function runShadowTest(candidateId: string, existingScores: number[], candidateScores: number[]): ShadowTestResult;
113
+ /**
114
+ * Validates the candidate against a held-out set (data not used to build the candidate).
115
+ * The holdout set is scored by both the existing and candidate heuristic;
116
+ * the candidate passes if it meets or exceeds the existing on >= 80% of cases.
117
+ */
118
+ export declare function runHoldoutValidation(candidateId: string, holdoutExistingScores: number[], holdoutCandidateScores: number[]): HoldoutResult;
119
+ /**
120
+ * Determines whether a candidate can be promoted based on holdout results and
121
+ * the configured approval policy.
122
+ *
123
+ * Auto-approves when:
124
+ * - holdout passed AND holdoutPassRate >= autoApproveThreshold
125
+ * - AND requireManualApproval is false
126
+ *
127
+ * In all other cases, the candidate is held at "holdout_passed" status and
128
+ * awaits manual sign-off (future: webhook / dashboard approval flow).
129
+ */
130
+ export declare function evaluateApprovalGate(holdout: HoldoutResult, config?: ApprovalGateConfig): {
131
+ approved: boolean;
132
+ reason: string;
133
+ };
134
+ export declare function persistCandidate(candidate: LearningCandidate, pipelineRoot: string): Promise<void>;
135
+ export declare function loadCandidate(candidateId: string, pipelineRoot: string): Promise<LearningCandidate | undefined>;
136
+ export declare function loadActiveLearningHeuristics(pipelineRoot: string): Promise<ActiveLearningHeuristics>;
137
+ export declare function summarizeLearningPipeline(pipelineRoot: string): Promise<LearningPipelineSummary>;
138
+ export interface PipelineInput {
139
+ candidate: LearningCandidate;
140
+ existingScores: number[];
141
+ candidateScores: number[];
142
+ holdoutExistingScores: number[];
143
+ holdoutCandidateScores: number[];
144
+ approvalConfig?: ApprovalGateConfig;
145
+ pipelineRoot: string;
146
+ }
147
+ /**
148
+ * Runs the full guarded promotion pipeline:
149
+ * shadow → holdout → approval gate → persist result.
150
+ *
151
+ * Returns whether the candidate was promoted to "approved" status.
152
+ * Never throws — any error is caught and candidate is marked rejected.
153
+ */
154
+ export declare function runPromotionPipeline(input: PipelineInput): Promise<PromotionResult>;