@mneme-ai/core 0.8.4 → 0.10.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 (272) hide show
  1. package/dist/entities/go-parser.d.ts +47 -0
  2. package/dist/entities/go-parser.d.ts.map +1 -0
  3. package/dist/entities/go-parser.js +315 -0
  4. package/dist/entities/go-parser.js.map +1 -0
  5. package/dist/entities/go-parser.test.d.ts +2 -0
  6. package/dist/entities/go-parser.test.d.ts.map +1 -0
  7. package/dist/entities/go-parser.test.js +147 -0
  8. package/dist/entities/go-parser.test.js.map +1 -0
  9. package/dist/entities/index.d.ts +1 -0
  10. package/dist/entities/index.d.ts.map +1 -1
  11. package/dist/entities/index.js +1 -0
  12. package/dist/entities/index.js.map +1 -1
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/indexer/indexer.d.ts +12 -0
  18. package/dist/indexer/indexer.d.ts.map +1 -1
  19. package/dist/indexer/indexer.js +28 -1
  20. package/dist/indexer/indexer.js.map +1 -1
  21. package/dist/insights/bus-factor.d.ts +58 -0
  22. package/dist/insights/bus-factor.d.ts.map +1 -0
  23. package/dist/insights/bus-factor.js +117 -0
  24. package/dist/insights/bus-factor.js.map +1 -0
  25. package/dist/insights/bus-factor.test.d.ts +2 -0
  26. package/dist/insights/bus-factor.test.d.ts.map +1 -0
  27. package/dist/insights/bus-factor.test.js +149 -0
  28. package/dist/insights/bus-factor.test.js.map +1 -0
  29. package/dist/insights/commit-coach.d.ts +80 -0
  30. package/dist/insights/commit-coach.d.ts.map +1 -0
  31. package/dist/insights/commit-coach.js +230 -0
  32. package/dist/insights/commit-coach.js.map +1 -0
  33. package/dist/insights/commit-coach.test.d.ts +2 -0
  34. package/dist/insights/commit-coach.test.d.ts.map +1 -0
  35. package/dist/insights/commit-coach.test.js +163 -0
  36. package/dist/insights/commit-coach.test.js.map +1 -0
  37. package/dist/insights/crystal-ball.d.ts +76 -0
  38. package/dist/insights/crystal-ball.d.ts.map +1 -0
  39. package/dist/insights/crystal-ball.js +219 -0
  40. package/dist/insights/crystal-ball.js.map +1 -0
  41. package/dist/insights/crystal-ball.test.d.ts +2 -0
  42. package/dist/insights/crystal-ball.test.d.ts.map +1 -0
  43. package/dist/insights/crystal-ball.test.js +157 -0
  44. package/dist/insights/crystal-ball.test.js.map +1 -0
  45. package/dist/insights/decisions.d.ts +38 -0
  46. package/dist/insights/decisions.d.ts.map +1 -0
  47. package/dist/insights/decisions.js +125 -0
  48. package/dist/insights/decisions.js.map +1 -0
  49. package/dist/insights/decisions.test.d.ts +2 -0
  50. package/dist/insights/decisions.test.d.ts.map +1 -0
  51. package/dist/insights/decisions.test.js +141 -0
  52. package/dist/insights/decisions.test.js.map +1 -0
  53. package/dist/insights/dream.d.ts +71 -0
  54. package/dist/insights/dream.d.ts.map +1 -0
  55. package/dist/insights/dream.js +235 -0
  56. package/dist/insights/dream.js.map +1 -0
  57. package/dist/insights/dream.test.d.ts +2 -0
  58. package/dist/insights/dream.test.d.ts.map +1 -0
  59. package/dist/insights/dream.test.js +127 -0
  60. package/dist/insights/dream.test.js.map +1 -0
  61. package/dist/insights/index.d.ts +21 -0
  62. package/dist/insights/index.d.ts.map +1 -0
  63. package/dist/insights/index.js +21 -0
  64. package/dist/insights/index.js.map +1 -0
  65. package/dist/insights/obsidian.d.ts +42 -0
  66. package/dist/insights/obsidian.d.ts.map +1 -0
  67. package/dist/insights/obsidian.js +263 -0
  68. package/dist/insights/obsidian.js.map +1 -0
  69. package/dist/insights/obsidian.test.d.ts +2 -0
  70. package/dist/insights/obsidian.test.d.ts.map +1 -0
  71. package/dist/insights/obsidian.test.js +241 -0
  72. package/dist/insights/obsidian.test.js.map +1 -0
  73. package/dist/insights/paradox.d.ts +36 -0
  74. package/dist/insights/paradox.d.ts.map +1 -0
  75. package/dist/insights/paradox.js +201 -0
  76. package/dist/insights/paradox.js.map +1 -0
  77. package/dist/insights/paradox.test.d.ts +2 -0
  78. package/dist/insights/paradox.test.d.ts.map +1 -0
  79. package/dist/insights/paradox.test.js +88 -0
  80. package/dist/insights/paradox.test.js.map +1 -0
  81. package/dist/insights/regret.d.ts +57 -0
  82. package/dist/insights/regret.d.ts.map +1 -0
  83. package/dist/insights/regret.js +137 -0
  84. package/dist/insights/regret.js.map +1 -0
  85. package/dist/insights/regret.test.d.ts +2 -0
  86. package/dist/insights/regret.test.d.ts.map +1 -0
  87. package/dist/insights/regret.test.js +153 -0
  88. package/dist/insights/regret.test.js.map +1 -0
  89. package/dist/insights/stack-trace.d.ts +40 -0
  90. package/dist/insights/stack-trace.d.ts.map +1 -0
  91. package/dist/insights/stack-trace.js +127 -0
  92. package/dist/insights/stack-trace.js.map +1 -0
  93. package/dist/insights/stack-trace.test.d.ts +2 -0
  94. package/dist/insights/stack-trace.test.d.ts.map +1 -0
  95. package/dist/insights/stack-trace.test.js +103 -0
  96. package/dist/insights/stack-trace.test.js.map +1 -0
  97. package/dist/insights/story.d.ts +34 -0
  98. package/dist/insights/story.d.ts.map +1 -0
  99. package/dist/insights/story.js +100 -0
  100. package/dist/insights/story.js.map +1 -0
  101. package/dist/insights/story.test.d.ts +2 -0
  102. package/dist/insights/story.test.d.ts.map +1 -0
  103. package/dist/insights/story.test.js +99 -0
  104. package/dist/insights/story.test.js.map +1 -0
  105. package/dist/insights/suggest.d.ts +29 -0
  106. package/dist/insights/suggest.d.ts.map +1 -0
  107. package/dist/insights/suggest.js +93 -0
  108. package/dist/insights/suggest.js.map +1 -0
  109. package/dist/insights/suggest.test.d.ts +2 -0
  110. package/dist/insights/suggest.test.d.ts.map +1 -0
  111. package/dist/insights/suggest.test.js +71 -0
  112. package/dist/insights/suggest.test.js.map +1 -0
  113. package/dist/insights/who-knows.d.ts +66 -0
  114. package/dist/insights/who-knows.d.ts.map +1 -0
  115. package/dist/insights/who-knows.js +125 -0
  116. package/dist/insights/who-knows.js.map +1 -0
  117. package/dist/insights/who-knows.test.d.ts +2 -0
  118. package/dist/insights/who-knows.test.d.ts.map +1 -0
  119. package/dist/insights/who-knows.test.js +109 -0
  120. package/dist/insights/who-knows.test.js.map +1 -0
  121. package/dist/quant/alpha.d.ts +87 -0
  122. package/dist/quant/alpha.d.ts.map +1 -0
  123. package/dist/quant/alpha.js +103 -0
  124. package/dist/quant/alpha.js.map +1 -0
  125. package/dist/quant/alpha.test.d.ts +2 -0
  126. package/dist/quant/alpha.test.d.ts.map +1 -0
  127. package/dist/quant/alpha.test.js +147 -0
  128. package/dist/quant/alpha.test.js.map +1 -0
  129. package/dist/quant/backtest.d.ts +57 -0
  130. package/dist/quant/backtest.d.ts.map +1 -0
  131. package/dist/quant/backtest.js +90 -0
  132. package/dist/quant/backtest.js.map +1 -0
  133. package/dist/quant/backtest.test.d.ts +2 -0
  134. package/dist/quant/backtest.test.d.ts.map +1 -0
  135. package/dist/quant/backtest.test.js +133 -0
  136. package/dist/quant/backtest.test.js.map +1 -0
  137. package/dist/quant/black-swan.d.ts +45 -0
  138. package/dist/quant/black-swan.d.ts.map +1 -0
  139. package/dist/quant/black-swan.js +112 -0
  140. package/dist/quant/black-swan.js.map +1 -0
  141. package/dist/quant/black-swan.test.d.ts +2 -0
  142. package/dist/quant/black-swan.test.d.ts.map +1 -0
  143. package/dist/quant/black-swan.test.js +131 -0
  144. package/dist/quant/black-swan.test.js.map +1 -0
  145. package/dist/quant/correlation-matrix.d.ts +54 -0
  146. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  147. package/dist/quant/correlation-matrix.js +103 -0
  148. package/dist/quant/correlation-matrix.js.map +1 -0
  149. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  150. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  151. package/dist/quant/correlation-matrix.test.js +118 -0
  152. package/dist/quant/correlation-matrix.test.js.map +1 -0
  153. package/dist/quant/drawdown.d.ts +51 -0
  154. package/dist/quant/drawdown.d.ts.map +1 -0
  155. package/dist/quant/drawdown.js +96 -0
  156. package/dist/quant/drawdown.js.map +1 -0
  157. package/dist/quant/drawdown.test.d.ts +2 -0
  158. package/dist/quant/drawdown.test.d.ts.map +1 -0
  159. package/dist/quant/drawdown.test.js +166 -0
  160. package/dist/quant/drawdown.test.js.map +1 -0
  161. package/dist/quant/greek.d.ts +55 -0
  162. package/dist/quant/greek.d.ts.map +1 -0
  163. package/dist/quant/greek.js +157 -0
  164. package/dist/quant/greek.js.map +1 -0
  165. package/dist/quant/greek.test.d.ts +2 -0
  166. package/dist/quant/greek.test.d.ts.map +1 -0
  167. package/dist/quant/greek.test.js +138 -0
  168. package/dist/quant/greek.test.js.map +1 -0
  169. package/dist/quant/implied-volatility.d.ts +65 -0
  170. package/dist/quant/implied-volatility.d.ts.map +1 -0
  171. package/dist/quant/implied-volatility.js +149 -0
  172. package/dist/quant/implied-volatility.js.map +1 -0
  173. package/dist/quant/implied-volatility.test.d.ts +2 -0
  174. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  175. package/dist/quant/implied-volatility.test.js +127 -0
  176. package/dist/quant/implied-volatility.test.js.map +1 -0
  177. package/dist/quant/index.d.ts +28 -0
  178. package/dist/quant/index.d.ts.map +1 -0
  179. package/dist/quant/index.js +28 -0
  180. package/dist/quant/index.js.map +1 -0
  181. package/dist/quant/insider-trading.d.ts +56 -0
  182. package/dist/quant/insider-trading.d.ts.map +1 -0
  183. package/dist/quant/insider-trading.js +129 -0
  184. package/dist/quant/insider-trading.js.map +1 -0
  185. package/dist/quant/insider-trading.test.d.ts +2 -0
  186. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  187. package/dist/quant/insider-trading.test.js +130 -0
  188. package/dist/quant/insider-trading.test.js.map +1 -0
  189. package/dist/quant/moneyball.d.ts +48 -0
  190. package/dist/quant/moneyball.d.ts.map +1 -0
  191. package/dist/quant/moneyball.js +110 -0
  192. package/dist/quant/moneyball.js.map +1 -0
  193. package/dist/quant/moneyball.test.d.ts +2 -0
  194. package/dist/quant/moneyball.test.d.ts.map +1 -0
  195. package/dist/quant/moneyball.test.js +137 -0
  196. package/dist/quant/moneyball.test.js.map +1 -0
  197. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  198. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  199. package/dist/quant/tax-loss-harvest.js +126 -0
  200. package/dist/quant/tax-loss-harvest.js.map +1 -0
  201. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  202. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  203. package/dist/quant/tax-loss-harvest.test.js +126 -0
  204. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  205. package/dist/retrieve/index.d.ts +2 -0
  206. package/dist/retrieve/index.d.ts.map +1 -1
  207. package/dist/retrieve/index.js +2 -0
  208. package/dist/retrieve/index.js.map +1 -1
  209. package/dist/retrieve/intent.d.ts +32 -0
  210. package/dist/retrieve/intent.d.ts.map +1 -0
  211. package/dist/retrieve/intent.js +104 -0
  212. package/dist/retrieve/intent.js.map +1 -0
  213. package/dist/retrieve/intent.test.d.ts +2 -0
  214. package/dist/retrieve/intent.test.d.ts.map +1 -0
  215. package/dist/retrieve/intent.test.js +106 -0
  216. package/dist/retrieve/intent.test.js.map +1 -0
  217. package/dist/retrieve/search.d.ts +30 -0
  218. package/dist/retrieve/search.d.ts.map +1 -1
  219. package/dist/retrieve/search.js +48 -0
  220. package/dist/retrieve/search.js.map +1 -1
  221. package/dist/retrieve/search.test.js +84 -1
  222. package/dist/retrieve/search.test.js.map +1 -1
  223. package/dist/retrieve/synthesize.d.ts +57 -0
  224. package/dist/retrieve/synthesize.d.ts.map +1 -0
  225. package/dist/retrieve/synthesize.js +191 -0
  226. package/dist/retrieve/synthesize.js.map +1 -0
  227. package/dist/retrieve/synthesize.test.d.ts +2 -0
  228. package/dist/retrieve/synthesize.test.d.ts.map +1 -0
  229. package/dist/retrieve/synthesize.test.js +127 -0
  230. package/dist/retrieve/synthesize.test.js.map +1 -0
  231. package/dist/store/schema.d.ts +2 -2
  232. package/dist/store/schema.d.ts.map +1 -1
  233. package/dist/store/schema.js +60 -2
  234. package/dist/store/schema.js.map +1 -1
  235. package/dist/store/sqlite.d.ts +2 -0
  236. package/dist/store/sqlite.d.ts.map +1 -1
  237. package/dist/store/sqlite.js +24 -0
  238. package/dist/store/sqlite.js.map +1 -1
  239. package/dist/store/sqlite.test.js +1 -1
  240. package/dist/util/index.d.ts +2 -0
  241. package/dist/util/index.d.ts.map +1 -1
  242. package/dist/util/index.js +2 -1
  243. package/dist/util/index.js.map +1 -1
  244. package/dist/util/redact.d.ts +58 -0
  245. package/dist/util/redact.d.ts.map +1 -0
  246. package/dist/util/redact.js +129 -0
  247. package/dist/util/redact.js.map +1 -0
  248. package/dist/util/redact.test.d.ts +2 -0
  249. package/dist/util/redact.test.d.ts.map +1 -0
  250. package/dist/util/redact.test.js +148 -0
  251. package/dist/util/redact.test.js.map +1 -0
  252. package/dist/wisdom/calibrator.d.ts +43 -0
  253. package/dist/wisdom/calibrator.d.ts.map +1 -0
  254. package/dist/wisdom/calibrator.js +120 -0
  255. package/dist/wisdom/calibrator.js.map +1 -0
  256. package/dist/wisdom/feedback.d.ts +45 -0
  257. package/dist/wisdom/feedback.d.ts.map +1 -0
  258. package/dist/wisdom/feedback.js +116 -0
  259. package/dist/wisdom/feedback.js.map +1 -0
  260. package/dist/wisdom/index.d.ts +15 -0
  261. package/dist/wisdom/index.d.ts.map +1 -0
  262. package/dist/wisdom/index.js +15 -0
  263. package/dist/wisdom/index.js.map +1 -0
  264. package/dist/wisdom/types.d.ts +67 -0
  265. package/dist/wisdom/types.d.ts.map +1 -0
  266. package/dist/wisdom/types.js +20 -0
  267. package/dist/wisdom/types.js.map +1 -0
  268. package/dist/wisdom/wisdom.test.d.ts +2 -0
  269. package/dist/wisdom/wisdom.test.d.ts.map +1 -0
  270. package/dist/wisdom/wisdom.test.js +144 -0
  271. package/dist/wisdom/wisdom.test.js.map +1 -0
  272. package/package.json +1 -1
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { scoreCandidate, tierOf, whoKnowsVerdict } from "./who-knows.js";
3
+ const today = new Date("2026-05-04T00:00:00Z");
4
+ const daysAgo = (n) => new Date(today.getTime() - n * 86_400_000).toISOString();
5
+ describe("scoreCandidate — pure scoring function", () => {
6
+ it("returns 0 for zero commits", () => {
7
+ expect(scoreCandidate(0, daysAgo(0), today)).toBe(0);
8
+ });
9
+ it("recent commits score higher than old commits with the same count", () => {
10
+ const recent = scoreCandidate(10, daysAgo(5), today);
11
+ const old = scoreCandidate(10, daysAgo(300), today);
12
+ expect(recent).toBeGreaterThan(old);
13
+ });
14
+ it("more commits score higher than fewer with the same recency", () => {
15
+ const more = scoreCandidate(50, daysAgo(10), today);
16
+ const fewer = scoreCandidate(5, daysAgo(10), today);
17
+ expect(more).toBeGreaterThan(fewer);
18
+ });
19
+ it("uses log-scale volume — 100 commits is not 100× more than 1 commit", () => {
20
+ const one = scoreCandidate(1, daysAgo(0), today);
21
+ const hundred = scoreCandidate(100, daysAgo(0), today);
22
+ // log2(101) ≈ 6.66, log2(2) = 1 → hundred / one ≈ 6.66, NOT 100.
23
+ expect(hundred / one).toBeLessThan(10);
24
+ });
25
+ it("recency floors at 0.3 even for very old commits", () => {
26
+ const ancient = scoreCandidate(10, daysAgo(2000), today);
27
+ const oneYear = scoreCandidate(10, daysAgo(365), today);
28
+ // Both should be at or near the floor.
29
+ expect(ancient).toBeGreaterThan(0);
30
+ expect(oneYear).toBeGreaterThan(0);
31
+ });
32
+ });
33
+ describe("tierOf — readable expert tier", () => {
34
+ it('"definitive" for many recent commits', () => {
35
+ expect(tierOf(20, daysAgo(10), today)).toBe("definitive");
36
+ });
37
+ it('"active" for moderate recent commits', () => {
38
+ expect(tierOf(5, daysAgo(20), today)).toBe("active");
39
+ });
40
+ it('"stale" for any volume but very old', () => {
41
+ expect(tierOf(100, daysAgo(300), today)).toBe("stale");
42
+ });
43
+ it('"occasional" — middle ground (≥1 commit, < ACTIVE_DAYS old, but few)', () => {
44
+ expect(tierOf(1, daysAgo(40), today)).toBe("occasional");
45
+ });
46
+ });
47
+ describe("whoKnowsVerdict — turns a list of candidates into a single verdict", () => {
48
+ const cand = (overrides) => ({
49
+ name: "alice",
50
+ email: "alice@x",
51
+ commitCount: 10,
52
+ lastTouch: daysAgo(5),
53
+ filesTouched: 20,
54
+ score: 3.0,
55
+ tier: "active",
56
+ ...overrides,
57
+ });
58
+ it("returns empty verdict for empty candidate list", () => {
59
+ const v = whoKnowsVerdict([]);
60
+ expect(v.topExpert).toBeUndefined();
61
+ expect(v.confidencePct).toBe(0);
62
+ expect(v.totalCommits).toBe(0);
63
+ });
64
+ it("picks the highest-score candidate as topExpert", () => {
65
+ const v = whoKnowsVerdict([
66
+ cand({ name: "alice", score: 4.0, commitCount: 15 }),
67
+ cand({ name: "bob", score: 2.0, commitCount: 5 }),
68
+ ]);
69
+ expect(v.topExpert?.name).toBe("alice");
70
+ expect(v.backup?.name).toBe("bob");
71
+ });
72
+ it("computes confidencePct as share of total commits", () => {
73
+ const v = whoKnowsVerdict([
74
+ cand({ name: "alice", score: 4.0, commitCount: 15 }),
75
+ cand({ name: "bob", score: 2.0, commitCount: 5 }),
76
+ ]);
77
+ expect(v.confidencePct).toBe(75);
78
+ });
79
+ it("flags risk when top expert is stale", () => {
80
+ const v = whoKnowsVerdict([
81
+ cand({ name: "alice", tier: "stale", lastTouch: daysAgo(300), commitCount: 20 }),
82
+ cand({ name: "bob", tier: "active", lastTouch: daysAgo(5), commitCount: 5 }),
83
+ ]);
84
+ expect(v.risk?.toLowerCase()).toContain("stale");
85
+ expect(v.risk).toContain("bob"); // suggests backup
86
+ });
87
+ it("flags risk when no dominant expert (well-distributed)", () => {
88
+ const v = whoKnowsVerdict([
89
+ cand({ name: "alice", commitCount: 4, score: 1.5 }),
90
+ cand({ name: "bob", commitCount: 4, score: 1.4 }),
91
+ cand({ name: "carol", commitCount: 4, score: 1.3 }),
92
+ cand({ name: "dave", commitCount: 4, score: 1.2 }),
93
+ ]);
94
+ expect(v.confidencePct).toBe(25);
95
+ expect(v.risk?.toLowerCase()).toContain("distributed");
96
+ });
97
+ it("flags risk when top expert touched once", () => {
98
+ const v = whoKnowsVerdict([cand({ name: "alice", commitCount: 1 })]);
99
+ expect(v.risk?.toLowerCase()).toContain("once");
100
+ });
101
+ it("no risk when top expert is definitive + recent", () => {
102
+ const v = whoKnowsVerdict([
103
+ cand({ name: "alice", tier: "definitive", commitCount: 20, lastTouch: daysAgo(5), score: 5 }),
104
+ cand({ name: "bob", commitCount: 2, score: 1, tier: "occasional" }),
105
+ ]);
106
+ expect(v.risk).toBeUndefined();
107
+ });
108
+ });
109
+ //# sourceMappingURL=who-knows.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-knows.test.js","sourceRoot":"","sources":["../../src/insights/who-knows.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `mneme alpha` — Kelly criterion for technical debt allocation.
3
+ *
4
+ * Each refactor/TD item is a "bet" with (expected payoff, variance).
5
+ * Kelly optimal fraction maximizes long-run growth: f* = edge / variance.
6
+ * In practice, we use FRACTIONAL Kelly (×0.25) to limit blow-up risk —
7
+ * the same lever Edward Thorp used to win Wall Street.
8
+ *
9
+ * Pure math + small heuristics for estimating edge/variance from history.
10
+ * No LLM. The CLI takes a JSON list of items OR auto-extracts from
11
+ * `mneme regret` + `mneme bus-factor` + `mneme paradox` data.
12
+ */
13
+ export interface DebtItem {
14
+ /** Stable id — drives sorting + reporting. */
15
+ id: string;
16
+ /** Human-readable name shown in output. */
17
+ name: string;
18
+ /**
19
+ * Edge — expected return as a fraction. Positive = improves codebase
20
+ * (smaller diff sizes, fewer regrets, better velocity). Negative =
21
+ * outright wasteful (the item costs more than it returns).
22
+ */
23
+ edge: number;
24
+ /**
25
+ * Variance — squared volatility of the outcome. Higher = riskier bet,
26
+ * more dispersion in possible outcomes. Tiny number (0..1) — interpret
27
+ * as fraction-squared.
28
+ */
29
+ variance: number;
30
+ /** Estimated dev-days to complete. */
31
+ effortDays: number;
32
+ }
33
+ export interface KellyAllocation extends DebtItem {
34
+ /** Raw Kelly fraction f* = edge / variance. May be > 1 or negative. */
35
+ rawKelly: number;
36
+ /** Fractional Kelly (clamped to [0, 0.5] after multiplier). */
37
+ kellyFraction: number;
38
+ /** Allocated dev-days for this item out of the budget. */
39
+ allocatedDays: number;
40
+ /** Sort tier for display. */
41
+ tier: "skip" | "small" | "core" | "outsized";
42
+ }
43
+ export interface KellyResult {
44
+ items: KellyAllocation[];
45
+ totalAllocated: number;
46
+ budgetDays: number;
47
+ reserveDays: number;
48
+ /** Kelly multiplier used. 0.25 by default — conservative. */
49
+ kellyMultiplier: number;
50
+ }
51
+ /**
52
+ * Compute Kelly-optimal allocation for a list of debt items.
53
+ *
54
+ * Algorithm:
55
+ * 1. raw_kelly_i = edge_i / variance_i (the canonical Kelly formula)
56
+ * 2. fractional_kelly_i = raw_kelly_i × multiplier (default 0.25)
57
+ * 3. clamp to [0, 0.5] — never bet > 50% of budget on one item
58
+ * 4. zero out negative-edge items (don't hold a losing bet)
59
+ * 5. normalize so allocations sum to ≤ budgetDays (leave reserve)
60
+ * 6. allocate dev-days proportionally
61
+ */
62
+ export declare function kellyAllocate(items: DebtItem[], opts?: {
63
+ budgetDays: number;
64
+ multiplier?: number;
65
+ reserveFraction?: number;
66
+ }): KellyResult;
67
+ export declare function classifyTier(kellyFraction: number, edge: number): KellyAllocation["tier"];
68
+ /**
69
+ * Estimate edge from historical data: a refactor's edge is the difference
70
+ * in (regret rate, bug rate, churn velocity) between BEFORE and AFTER
71
+ * similar past refactors in the same module.
72
+ *
73
+ * The CLI calls this with regret data + commit-coach signals; here we
74
+ * just expose the algorithm so it's testable in isolation.
75
+ */
76
+ export declare function estimateEdge(opts: {
77
+ pastRegretRate: number;
78
+ pastChurnPerDay: number;
79
+ postRefactorRegretRate: number;
80
+ postRefactorChurnPerDay: number;
81
+ }): number;
82
+ /**
83
+ * Estimate variance — historical variance of payoff across similar past
84
+ * refactors. Higher when past outcomes were inconsistent.
85
+ */
86
+ export declare function estimateVariance(historicalPayoffs: number[]): number;
87
+ //# sourceMappingURL=alpha.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.d.ts","sourceRoot":"","sources":["../../src/quant/alpha.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ;IAC/C,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EAAE,EACjB,IAAI,GAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAuB,GAC/F,WAAW,CA6Cb;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAMzF;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;CACjC,GAAG,MAAM,CAKT;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,EAAE,GAAG,MAAM,CAMpE"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `mneme alpha` — Kelly criterion for technical debt allocation.
3
+ *
4
+ * Each refactor/TD item is a "bet" with (expected payoff, variance).
5
+ * Kelly optimal fraction maximizes long-run growth: f* = edge / variance.
6
+ * In practice, we use FRACTIONAL Kelly (×0.25) to limit blow-up risk —
7
+ * the same lever Edward Thorp used to win Wall Street.
8
+ *
9
+ * Pure math + small heuristics for estimating edge/variance from history.
10
+ * No LLM. The CLI takes a JSON list of items OR auto-extracts from
11
+ * `mneme regret` + `mneme bus-factor` + `mneme paradox` data.
12
+ */
13
+ /**
14
+ * Compute Kelly-optimal allocation for a list of debt items.
15
+ *
16
+ * Algorithm:
17
+ * 1. raw_kelly_i = edge_i / variance_i (the canonical Kelly formula)
18
+ * 2. fractional_kelly_i = raw_kelly_i × multiplier (default 0.25)
19
+ * 3. clamp to [0, 0.5] — never bet > 50% of budget on one item
20
+ * 4. zero out negative-edge items (don't hold a losing bet)
21
+ * 5. normalize so allocations sum to ≤ budgetDays (leave reserve)
22
+ * 6. allocate dev-days proportionally
23
+ */
24
+ export function kellyAllocate(items, opts = { budgetDays: 25 }) {
25
+ const multiplier = opts.multiplier ?? 0.25;
26
+ const reserveFrac = opts.reserveFraction ?? 0.2;
27
+ const budget = Math.max(0, opts.budgetDays);
28
+ // 1. Raw + fractional Kelly per item.
29
+ const computed = items.map((item) => {
30
+ // Avoid division-by-zero — variance of 0 is unrealistic; treat as floor.
31
+ const v = Math.max(item.variance, 1e-6);
32
+ const rawKelly = item.edge / v;
33
+ let frac = rawKelly * multiplier;
34
+ if (frac < 0)
35
+ frac = 0; // never bet on a losing item
36
+ if (frac > 0.5)
37
+ frac = 0.5; // never bet > 50% on one item
38
+ return { ...item, rawKelly, kellyFraction: frac };
39
+ });
40
+ // 2. Normalize fractions to sum ≤ (1 - reserveFrac).
41
+ const totalFrac = computed.reduce((s, c) => s + c.kellyFraction, 0);
42
+ const targetTotal = 1 - reserveFrac;
43
+ const scale = totalFrac > targetTotal ? targetTotal / totalFrac : 1;
44
+ // 3. Allocate dev-days.
45
+ let allocatedSum = 0;
46
+ const allocations = computed.map((c) => {
47
+ const adjusted = c.kellyFraction * scale;
48
+ const allocatedDays = Math.round(budget * adjusted * 10) / 10;
49
+ allocatedSum += allocatedDays;
50
+ return {
51
+ ...c,
52
+ kellyFraction: adjusted,
53
+ allocatedDays,
54
+ tier: classifyTier(adjusted, c.edge),
55
+ };
56
+ });
57
+ // 4. Sort by allocated days desc — biggest bets first.
58
+ allocations.sort((a, b) => b.allocatedDays - a.allocatedDays);
59
+ return {
60
+ items: allocations,
61
+ totalAllocated: Math.round(allocatedSum * 10) / 10,
62
+ budgetDays: budget,
63
+ reserveDays: Math.round((budget - allocatedSum) * 10) / 10,
64
+ kellyMultiplier: multiplier,
65
+ };
66
+ }
67
+ export function classifyTier(kellyFraction, edge) {
68
+ if (edge < 0)
69
+ return "skip";
70
+ if (kellyFraction >= 0.2)
71
+ return "outsized";
72
+ if (kellyFraction >= 0.1)
73
+ return "core";
74
+ if (kellyFraction > 0)
75
+ return "small";
76
+ return "skip";
77
+ }
78
+ /**
79
+ * Estimate edge from historical data: a refactor's edge is the difference
80
+ * in (regret rate, bug rate, churn velocity) between BEFORE and AFTER
81
+ * similar past refactors in the same module.
82
+ *
83
+ * The CLI calls this with regret data + commit-coach signals; here we
84
+ * just expose the algorithm so it's testable in isolation.
85
+ */
86
+ export function estimateEdge(opts) {
87
+ const regretImprovement = opts.pastRegretRate - opts.postRefactorRegretRate;
88
+ const churnImprovement = (opts.pastChurnPerDay - opts.postRefactorChurnPerDay) / Math.max(opts.pastChurnPerDay, 0.001);
89
+ // Weighted: regret matters more (bugs hurt more than churn).
90
+ return 0.7 * regretImprovement + 0.3 * churnImprovement;
91
+ }
92
+ /**
93
+ * Estimate variance — historical variance of payoff across similar past
94
+ * refactors. Higher when past outcomes were inconsistent.
95
+ */
96
+ export function estimateVariance(historicalPayoffs) {
97
+ if (historicalPayoffs.length < 2)
98
+ return 0.1; // unknown — assume mid-range
99
+ const mean = historicalPayoffs.reduce((s, x) => s + x, 0) / historicalPayoffs.length;
100
+ const variance = historicalPayoffs.reduce((s, x) => s + (x - mean) * (x - mean), 0) / historicalPayoffs.length;
101
+ return Math.max(variance, 1e-6);
102
+ }
103
+ //# sourceMappingURL=alpha.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.js","sourceRoot":"","sources":["../../src/quant/alpha.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=alpha.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.test.d.ts","sourceRoot":"","sources":["../../src/quant/alpha.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,147 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { kellyAllocate, classifyTier, estimateEdge, estimateVariance } from "./alpha.js";
3
+ const item = (overrides) => ({
4
+ id: "x",
5
+ name: "X",
6
+ edge: 0.1,
7
+ variance: 0.05,
8
+ effortDays: 5,
9
+ ...overrides,
10
+ });
11
+ describe("kellyAllocate — basic invariants", () => {
12
+ it("never allocates negative days", () => {
13
+ const r = kellyAllocate([
14
+ item({ id: "good", edge: 0.2, variance: 0.05 }),
15
+ item({ id: "bad", edge: -0.1, variance: 0.05 }),
16
+ ], { budgetDays: 20 });
17
+ for (const a of r.items)
18
+ expect(a.allocatedDays).toBeGreaterThanOrEqual(0);
19
+ expect(r.items.find((a) => a.id === "bad").allocatedDays).toBe(0);
20
+ expect(r.items.find((a) => a.id === "bad").tier).toBe("skip");
21
+ });
22
+ it("raw Kelly is edge / variance", () => {
23
+ const r = kellyAllocate([item({ id: "x", edge: 0.2, variance: 0.05 })], { budgetDays: 100 });
24
+ expect(r.items[0].rawKelly).toBeCloseTo(4, 6);
25
+ });
26
+ it("applies fractional Kelly multiplier (default 0.25)", () => {
27
+ const r = kellyAllocate([item({ id: "x", edge: 0.2, variance: 0.1 })], { budgetDays: 100 });
28
+ // raw kelly = 2.0, × 0.25 = 0.5 (clamped to ceiling)
29
+ expect(r.items[0].kellyFraction).toBeLessThanOrEqual(0.5);
30
+ });
31
+ it("clamps any single item to ≤ 50% of budget", () => {
32
+ const r = kellyAllocate([item({ edge: 1.0, variance: 0.001 })], { budgetDays: 100, multiplier: 1 });
33
+ expect(r.items[0].kellyFraction).toBeLessThanOrEqual(0.5);
34
+ });
35
+ it("totalAllocated never exceeds budgetDays", () => {
36
+ const items = [];
37
+ for (let i = 0; i < 10; i++) {
38
+ items.push(item({ id: `i${i}`, edge: 0.3, variance: 0.05 }));
39
+ }
40
+ const r = kellyAllocate(items, { budgetDays: 25 });
41
+ expect(r.totalAllocated).toBeLessThanOrEqual(25);
42
+ });
43
+ it("reserves at least reserveFraction of the budget by default", () => {
44
+ const items = Array.from({ length: 5 }, (_, i) => item({ id: `i${i}`, edge: 0.5, variance: 0.05 }));
45
+ const r = kellyAllocate(items, { budgetDays: 25, reserveFraction: 0.2 });
46
+ expect(r.totalAllocated).toBeLessThanOrEqual(25 * 0.8 + 0.5); // float wiggle
47
+ expect(r.reserveDays).toBeGreaterThanOrEqual(25 * 0.2 - 0.5);
48
+ });
49
+ });
50
+ describe("kellyAllocate — sorting + tiers", () => {
51
+ it("sorts by allocated days desc", () => {
52
+ const r = kellyAllocate([
53
+ item({ id: "small", edge: 0.05, variance: 0.05 }),
54
+ item({ id: "big", edge: 0.5, variance: 0.05 }),
55
+ item({ id: "mid", edge: 0.2, variance: 0.05 }),
56
+ ], { budgetDays: 25 });
57
+ expect(r.items[0].id).toBe("big");
58
+ expect(r.items[r.items.length - 1].id).toBe("small");
59
+ });
60
+ it("classifies tiers — outsized > core > small > skip", () => {
61
+ expect(classifyTier(0.3, 0.2)).toBe("outsized");
62
+ expect(classifyTier(0.15, 0.2)).toBe("core");
63
+ expect(classifyTier(0.05, 0.2)).toBe("small");
64
+ expect(classifyTier(0.0, 0.2)).toBe("skip");
65
+ expect(classifyTier(0.5, -0.1)).toBe("skip"); // negative edge always skip
66
+ });
67
+ });
68
+ describe("kellyAllocate — edge cases", () => {
69
+ it("handles empty input", () => {
70
+ const r = kellyAllocate([], { budgetDays: 25 });
71
+ expect(r.items).toEqual([]);
72
+ expect(r.totalAllocated).toBe(0);
73
+ expect(r.reserveDays).toBe(25);
74
+ });
75
+ it("handles zero variance (avoids division by zero)", () => {
76
+ const r = kellyAllocate([item({ id: "x", edge: 0.1, variance: 0 })], { budgetDays: 100 });
77
+ expect(Number.isFinite(r.items[0].allocatedDays)).toBe(true);
78
+ });
79
+ it("zero-budget case allocates nothing", () => {
80
+ const r = kellyAllocate([item({ edge: 0.5, variance: 0.05 })], { budgetDays: 0 });
81
+ expect(r.totalAllocated).toBe(0);
82
+ expect(r.items[0].allocatedDays).toBe(0);
83
+ });
84
+ it("more permissive multiplier (0.5) allocates more aggressively than default (0.25)", () => {
85
+ // Use a low raw-Kelly so neither multiplier hits the per-item 0.5 ceiling.
86
+ const items = [item({ edge: 0.05, variance: 0.1 })];
87
+ const conservative = kellyAllocate(items, { budgetDays: 100, multiplier: 0.25 });
88
+ const aggressive = kellyAllocate(items, { budgetDays: 100, multiplier: 0.5 });
89
+ expect(aggressive.totalAllocated).toBeGreaterThan(conservative.totalAllocated);
90
+ });
91
+ });
92
+ describe("estimateEdge — derives expected return from historical signals", () => {
93
+ it("positive edge when post-refactor improves regret + churn", () => {
94
+ const e = estimateEdge({
95
+ pastRegretRate: 0.3,
96
+ pastChurnPerDay: 5,
97
+ postRefactorRegretRate: 0.1,
98
+ postRefactorChurnPerDay: 3,
99
+ });
100
+ expect(e).toBeGreaterThan(0);
101
+ });
102
+ it("negative edge when post-refactor is worse", () => {
103
+ const e = estimateEdge({
104
+ pastRegretRate: 0.1,
105
+ pastChurnPerDay: 5,
106
+ postRefactorRegretRate: 0.3,
107
+ postRefactorChurnPerDay: 8,
108
+ });
109
+ expect(e).toBeLessThan(0);
110
+ });
111
+ it("regret improvement weighted higher than churn improvement", () => {
112
+ const regretOnly = estimateEdge({
113
+ pastRegretRate: 0.5,
114
+ pastChurnPerDay: 5,
115
+ postRefactorRegretRate: 0,
116
+ postRefactorChurnPerDay: 5,
117
+ });
118
+ const churnOnly = estimateEdge({
119
+ pastRegretRate: 0.3,
120
+ pastChurnPerDay: 10,
121
+ postRefactorRegretRate: 0.3,
122
+ postRefactorChurnPerDay: 5,
123
+ });
124
+ expect(regretOnly).toBeGreaterThan(churnOnly);
125
+ });
126
+ });
127
+ describe("estimateVariance — sample variance of payoffs", () => {
128
+ it("zero (floor) for unknown / single sample", () => {
129
+ expect(estimateVariance([])).toBe(0.1);
130
+ expect(estimateVariance([0.2])).toBe(0.1);
131
+ });
132
+ it("computes variance correctly for multiple payoffs", () => {
133
+ const v = estimateVariance([0.1, 0.2, 0.3]);
134
+ // mean = 0.2, deviations: -0.1, 0, 0.1; variance = (0.01 + 0 + 0.01) / 3
135
+ expect(v).toBeCloseTo(0.00667, 4);
136
+ });
137
+ it("non-negative for any input", () => {
138
+ const inputs = [
139
+ [0, 0, 0],
140
+ [-0.5, 0.5],
141
+ [1, 1, 1, 1],
142
+ ];
143
+ for (const arr of inputs)
144
+ expect(estimateVariance(arr)).toBeGreaterThanOrEqual(0);
145
+ });
146
+ });
147
+ //# sourceMappingURL=alpha.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.test.js","sourceRoot":"","sources":["../../src/quant/alpha.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `mneme backtest` — validate insight commands retroactively.
3
+ *
4
+ * The killer property: every prediction Mneme makes ("this is risky") can
5
+ * be replayed against actual history to compute precision, recall, F1, and
6
+ * lift over a random baseline. This turns "we have an AI tool" into
7
+ * "we have an AI tool with measured edge against the past".
8
+ *
9
+ * Backtest works for any binary predictor: given a set of (commit,
10
+ * prediction) pairs and a window in which to count "trouble" outcomes,
11
+ * compute the standard classification metrics.
12
+ *
13
+ * Pure data analysis — no LLM. The actual replay (re-running a command at
14
+ * a frozen point in history) lives in the CLI command, but the metric
15
+ * math is here and unit-testable.
16
+ */
17
+ export interface BacktestSample {
18
+ /** The thing being predicted on — a commit, file, etc. */
19
+ id: string;
20
+ /** Did the predictor say "trouble incoming"? */
21
+ predicted: boolean;
22
+ /** Did trouble actually happen within the validation window? */
23
+ actual: boolean;
24
+ }
25
+ export interface BacktestResult {
26
+ /** Total samples evaluated. */
27
+ n: number;
28
+ /** Confusion matrix counts. */
29
+ truePositives: number;
30
+ falsePositives: number;
31
+ trueNegatives: number;
32
+ falseNegatives: number;
33
+ /** Standard metrics. */
34
+ precision: number;
35
+ recall: number;
36
+ f1: number;
37
+ /** Base rate of positive outcomes (how often trouble happens randomly). */
38
+ baseRate: number;
39
+ /** Lift over base rate — precision / baseRate. */
40
+ lift: number;
41
+ /** Verdict label for the report. */
42
+ verdict: "no-edge" | "weak" | "real-edge" | "strong-edge";
43
+ /** Plain-English conclusion. */
44
+ conclusion: string;
45
+ }
46
+ /**
47
+ * Compute classification metrics + verdict from a list of (predicted,
48
+ * actual) samples. Pure math — no I/O, deterministic.
49
+ */
50
+ export declare function backtest(samples: BacktestSample[]): BacktestResult;
51
+ export declare function classifyVerdict(lift: number, precision: number, recall: number, n: number): BacktestResult["verdict"];
52
+ /**
53
+ * Aggregate a backtest result into a one-line markdown badge for the
54
+ * README / docs. Format: "F1 = 0.67 · 2.4× lift · n=14".
55
+ */
56
+ export declare function badge(result: BacktestResult): string;
57
+ //# sourceMappingURL=backtest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtest.d.ts","sourceRoot":"","sources":["../../src/quant/backtest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC;IAC1D,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,CAkClE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,GACR,cAAc,CAAC,SAAS,CAAC,CAM3B;AAsBD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `mneme backtest` — validate insight commands retroactively.
3
+ *
4
+ * The killer property: every prediction Mneme makes ("this is risky") can
5
+ * be replayed against actual history to compute precision, recall, F1, and
6
+ * lift over a random baseline. This turns "we have an AI tool" into
7
+ * "we have an AI tool with measured edge against the past".
8
+ *
9
+ * Backtest works for any binary predictor: given a set of (commit,
10
+ * prediction) pairs and a window in which to count "trouble" outcomes,
11
+ * compute the standard classification metrics.
12
+ *
13
+ * Pure data analysis — no LLM. The actual replay (re-running a command at
14
+ * a frozen point in history) lives in the CLI command, but the metric
15
+ * math is here and unit-testable.
16
+ */
17
+ /**
18
+ * Compute classification metrics + verdict from a list of (predicted,
19
+ * actual) samples. Pure math — no I/O, deterministic.
20
+ */
21
+ export function backtest(samples) {
22
+ const n = samples.length;
23
+ let tp = 0;
24
+ let fp = 0;
25
+ let tn = 0;
26
+ let fn = 0;
27
+ for (const s of samples) {
28
+ if (s.predicted && s.actual)
29
+ tp += 1;
30
+ else if (s.predicted && !s.actual)
31
+ fp += 1;
32
+ else if (!s.predicted && !s.actual)
33
+ tn += 1;
34
+ else
35
+ fn += 1;
36
+ }
37
+ const precision = tp + fp === 0 ? 0 : tp / (tp + fp);
38
+ const recall = tp + fn === 0 ? 0 : tp / (tp + fn);
39
+ const f1 = precision + recall === 0 ? 0 : (2 * precision * recall) / (precision + recall);
40
+ const baseRate = n === 0 ? 0 : (tp + fn) / n;
41
+ const lift = baseRate === 0 ? 0 : precision / baseRate;
42
+ const verdict = classifyVerdict(lift, precision, recall, n);
43
+ return {
44
+ n,
45
+ truePositives: tp,
46
+ falsePositives: fp,
47
+ trueNegatives: tn,
48
+ falseNegatives: fn,
49
+ precision,
50
+ recall,
51
+ f1,
52
+ baseRate,
53
+ lift,
54
+ verdict,
55
+ conclusion: buildConclusion(verdict, n, precision, recall, lift),
56
+ };
57
+ }
58
+ export function classifyVerdict(lift, precision, recall, n) {
59
+ if (n < 5)
60
+ return "no-edge"; // sample too small
61
+ if (lift >= 2.5 && precision >= 0.6 && recall >= 0.5)
62
+ return "strong-edge";
63
+ if (lift >= 1.5 && precision >= 0.4)
64
+ return "real-edge";
65
+ if (lift >= 1.1)
66
+ return "weak";
67
+ return "no-edge";
68
+ }
69
+ function buildConclusion(verdict, n, precision, recall, lift) {
70
+ switch (verdict) {
71
+ case "no-edge":
72
+ if (n < 5)
73
+ return `Sample size too small (n=${n}) to draw conclusions.`;
74
+ return `No detectable edge over random — precision ${(precision * 100).toFixed(0)}%, lift ${lift.toFixed(2)}×.`;
75
+ case "weak":
76
+ return `Weak edge — beats random by ${((lift - 1) * 100).toFixed(0)}% but precision is still ${(precision * 100).toFixed(0)}%. Use as a soft prior.`;
77
+ case "real-edge":
78
+ return `Real predictive edge — precision ${(precision * 100).toFixed(0)}%, ${lift.toFixed(1)}× over random.`;
79
+ case "strong-edge":
80
+ return `Strong edge — precision ${(precision * 100).toFixed(0)}%, recall ${(recall * 100).toFixed(0)}%, ${lift.toFixed(1)}× over random. Trust this predictor.`;
81
+ }
82
+ }
83
+ /**
84
+ * Aggregate a backtest result into a one-line markdown badge for the
85
+ * README / docs. Format: "F1 = 0.67 · 2.4× lift · n=14".
86
+ */
87
+ export function badge(result) {
88
+ return `F1 = ${result.f1.toFixed(2)} · ${result.lift.toFixed(1)}× lift · n=${result.n}`;
89
+ }
90
+ //# sourceMappingURL=backtest.js.map