@mneme-ai/core 0.9.0 → 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 (153) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/insights/bus-factor.d.ts +58 -0
  6. package/dist/insights/bus-factor.d.ts.map +1 -0
  7. package/dist/insights/bus-factor.js +117 -0
  8. package/dist/insights/bus-factor.js.map +1 -0
  9. package/dist/insights/bus-factor.test.d.ts +2 -0
  10. package/dist/insights/bus-factor.test.d.ts.map +1 -0
  11. package/dist/insights/bus-factor.test.js +149 -0
  12. package/dist/insights/bus-factor.test.js.map +1 -0
  13. package/dist/insights/commit-coach.d.ts +80 -0
  14. package/dist/insights/commit-coach.d.ts.map +1 -0
  15. package/dist/insights/commit-coach.js +230 -0
  16. package/dist/insights/commit-coach.js.map +1 -0
  17. package/dist/insights/commit-coach.test.d.ts +2 -0
  18. package/dist/insights/commit-coach.test.d.ts.map +1 -0
  19. package/dist/insights/commit-coach.test.js +163 -0
  20. package/dist/insights/commit-coach.test.js.map +1 -0
  21. package/dist/insights/crystal-ball.d.ts +76 -0
  22. package/dist/insights/crystal-ball.d.ts.map +1 -0
  23. package/dist/insights/crystal-ball.js +219 -0
  24. package/dist/insights/crystal-ball.js.map +1 -0
  25. package/dist/insights/crystal-ball.test.d.ts +2 -0
  26. package/dist/insights/crystal-ball.test.d.ts.map +1 -0
  27. package/dist/insights/crystal-ball.test.js +157 -0
  28. package/dist/insights/crystal-ball.test.js.map +1 -0
  29. package/dist/insights/index.d.ts +5 -0
  30. package/dist/insights/index.d.ts.map +1 -1
  31. package/dist/insights/index.js +5 -0
  32. package/dist/insights/index.js.map +1 -1
  33. package/dist/insights/paradox.d.ts +36 -0
  34. package/dist/insights/paradox.d.ts.map +1 -0
  35. package/dist/insights/paradox.js +201 -0
  36. package/dist/insights/paradox.js.map +1 -0
  37. package/dist/insights/paradox.test.d.ts +2 -0
  38. package/dist/insights/paradox.test.d.ts.map +1 -0
  39. package/dist/insights/paradox.test.js +88 -0
  40. package/dist/insights/paradox.test.js.map +1 -0
  41. package/dist/insights/regret.d.ts +57 -0
  42. package/dist/insights/regret.d.ts.map +1 -0
  43. package/dist/insights/regret.js +137 -0
  44. package/dist/insights/regret.js.map +1 -0
  45. package/dist/insights/regret.test.d.ts +2 -0
  46. package/dist/insights/regret.test.d.ts.map +1 -0
  47. package/dist/insights/regret.test.js +153 -0
  48. package/dist/insights/regret.test.js.map +1 -0
  49. package/dist/insights/who-knows.d.ts +18 -0
  50. package/dist/insights/who-knows.d.ts.map +1 -1
  51. package/dist/insights/who-knows.js +29 -0
  52. package/dist/insights/who-knows.js.map +1 -1
  53. package/dist/insights/who-knows.test.js +63 -1
  54. package/dist/insights/who-knows.test.js.map +1 -1
  55. package/dist/quant/alpha.d.ts +87 -0
  56. package/dist/quant/alpha.d.ts.map +1 -0
  57. package/dist/quant/alpha.js +103 -0
  58. package/dist/quant/alpha.js.map +1 -0
  59. package/dist/quant/alpha.test.d.ts +2 -0
  60. package/dist/quant/alpha.test.d.ts.map +1 -0
  61. package/dist/quant/alpha.test.js +147 -0
  62. package/dist/quant/alpha.test.js.map +1 -0
  63. package/dist/quant/backtest.d.ts +57 -0
  64. package/dist/quant/backtest.d.ts.map +1 -0
  65. package/dist/quant/backtest.js +90 -0
  66. package/dist/quant/backtest.js.map +1 -0
  67. package/dist/quant/backtest.test.d.ts +2 -0
  68. package/dist/quant/backtest.test.d.ts.map +1 -0
  69. package/dist/quant/backtest.test.js +133 -0
  70. package/dist/quant/backtest.test.js.map +1 -0
  71. package/dist/quant/black-swan.d.ts +45 -0
  72. package/dist/quant/black-swan.d.ts.map +1 -0
  73. package/dist/quant/black-swan.js +112 -0
  74. package/dist/quant/black-swan.js.map +1 -0
  75. package/dist/quant/black-swan.test.d.ts +2 -0
  76. package/dist/quant/black-swan.test.d.ts.map +1 -0
  77. package/dist/quant/black-swan.test.js +131 -0
  78. package/dist/quant/black-swan.test.js.map +1 -0
  79. package/dist/quant/correlation-matrix.d.ts +54 -0
  80. package/dist/quant/correlation-matrix.d.ts.map +1 -0
  81. package/dist/quant/correlation-matrix.js +103 -0
  82. package/dist/quant/correlation-matrix.js.map +1 -0
  83. package/dist/quant/correlation-matrix.test.d.ts +2 -0
  84. package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
  85. package/dist/quant/correlation-matrix.test.js +118 -0
  86. package/dist/quant/correlation-matrix.test.js.map +1 -0
  87. package/dist/quant/drawdown.d.ts +51 -0
  88. package/dist/quant/drawdown.d.ts.map +1 -0
  89. package/dist/quant/drawdown.js +96 -0
  90. package/dist/quant/drawdown.js.map +1 -0
  91. package/dist/quant/drawdown.test.d.ts +2 -0
  92. package/dist/quant/drawdown.test.d.ts.map +1 -0
  93. package/dist/quant/drawdown.test.js +166 -0
  94. package/dist/quant/drawdown.test.js.map +1 -0
  95. package/dist/quant/greek.d.ts +55 -0
  96. package/dist/quant/greek.d.ts.map +1 -0
  97. package/dist/quant/greek.js +157 -0
  98. package/dist/quant/greek.js.map +1 -0
  99. package/dist/quant/greek.test.d.ts +2 -0
  100. package/dist/quant/greek.test.d.ts.map +1 -0
  101. package/dist/quant/greek.test.js +138 -0
  102. package/dist/quant/greek.test.js.map +1 -0
  103. package/dist/quant/implied-volatility.d.ts +65 -0
  104. package/dist/quant/implied-volatility.d.ts.map +1 -0
  105. package/dist/quant/implied-volatility.js +149 -0
  106. package/dist/quant/implied-volatility.js.map +1 -0
  107. package/dist/quant/implied-volatility.test.d.ts +2 -0
  108. package/dist/quant/implied-volatility.test.d.ts.map +1 -0
  109. package/dist/quant/implied-volatility.test.js +127 -0
  110. package/dist/quant/implied-volatility.test.js.map +1 -0
  111. package/dist/quant/index.d.ts +28 -0
  112. package/dist/quant/index.d.ts.map +1 -0
  113. package/dist/quant/index.js +28 -0
  114. package/dist/quant/index.js.map +1 -0
  115. package/dist/quant/insider-trading.d.ts +56 -0
  116. package/dist/quant/insider-trading.d.ts.map +1 -0
  117. package/dist/quant/insider-trading.js +129 -0
  118. package/dist/quant/insider-trading.js.map +1 -0
  119. package/dist/quant/insider-trading.test.d.ts +2 -0
  120. package/dist/quant/insider-trading.test.d.ts.map +1 -0
  121. package/dist/quant/insider-trading.test.js +130 -0
  122. package/dist/quant/insider-trading.test.js.map +1 -0
  123. package/dist/quant/moneyball.d.ts +48 -0
  124. package/dist/quant/moneyball.d.ts.map +1 -0
  125. package/dist/quant/moneyball.js +110 -0
  126. package/dist/quant/moneyball.js.map +1 -0
  127. package/dist/quant/moneyball.test.d.ts +2 -0
  128. package/dist/quant/moneyball.test.d.ts.map +1 -0
  129. package/dist/quant/moneyball.test.js +137 -0
  130. package/dist/quant/moneyball.test.js.map +1 -0
  131. package/dist/quant/tax-loss-harvest.d.ts +59 -0
  132. package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
  133. package/dist/quant/tax-loss-harvest.js +126 -0
  134. package/dist/quant/tax-loss-harvest.js.map +1 -0
  135. package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
  136. package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
  137. package/dist/quant/tax-loss-harvest.test.js +126 -0
  138. package/dist/quant/tax-loss-harvest.test.js.map +1 -0
  139. package/dist/retrieve/synthesize.d.ts.map +1 -1
  140. package/dist/retrieve/synthesize.js +56 -25
  141. package/dist/retrieve/synthesize.js.map +1 -1
  142. package/dist/retrieve/synthesize.test.js +26 -15
  143. package/dist/retrieve/synthesize.test.js.map +1 -1
  144. package/dist/store/schema.d.ts +2 -2
  145. package/dist/store/schema.d.ts.map +1 -1
  146. package/dist/store/schema.js +6 -2
  147. package/dist/store/schema.js.map +1 -1
  148. package/dist/store/sqlite.d.ts +2 -0
  149. package/dist/store/sqlite.d.ts.map +1 -1
  150. package/dist/store/sqlite.js +24 -0
  151. package/dist/store/sqlite.js.map +1 -1
  152. package/dist/store/sqlite.test.js +1 -1
  153. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ /**
2
+ * `mneme tax-loss-harvest` — find dead code that can be deleted to "offset"
3
+ * technical debt.
4
+ *
5
+ * The metaphor: in finance, you sell losing positions to harvest tax losses,
6
+ * which offset gains elsewhere. In a codebase, deleting dead code reduces
7
+ * cognitive surface area, freeing up budget to absorb new debt elsewhere.
8
+ *
9
+ * Scoring per file:
10
+ * • staleness — days since last touch
11
+ * • commit count — fewer commits = less load-bearing
12
+ * • entity references — files with no entities or no inbound refs
13
+ * • risk if deleted — files with incidents historically = higher risk
14
+ *
15
+ * harvest_score = staleness × (1 / max(commits, 1)) × (1 - risk)
16
+ *
17
+ * Pure data analysis. No LLM. Output is a candidate list with risk gauges.
18
+ */
19
+ export function findHarvestCandidates(store, opts = {}) {
20
+ const minStale = opts.minStaleDays ?? 180;
21
+ const maxCommits = opts.maxCommits ?? 5;
22
+ const topN = opts.topN ?? 20;
23
+ const now = opts.now ?? new Date();
24
+ // 1. Per-file commit count + last touch.
25
+ const fileRows = store.db
26
+ .prepare(`SELECT
27
+ fc.path AS path,
28
+ COUNT(*) AS commits,
29
+ MAX(c.author_date) AS last_touch
30
+ FROM file_changes fc
31
+ JOIN commits c ON c.hash = fc.commit_hash
32
+ GROUP BY fc.path`)
33
+ .all();
34
+ // 2. Entity count per file.
35
+ const entityRows = store.db
36
+ .prepare(`SELECT file_path, COUNT(*) AS n FROM entities GROUP BY file_path`)
37
+ .all();
38
+ const entityCountByFile = new Map(entityRows.map((r) => [r.file_path, r.n]));
39
+ // 3. Incident-affected files (best-effort).
40
+ const incidentRows = store.db
41
+ .prepare(`SELECT affected_files FROM incidents`)
42
+ .all();
43
+ const incidentCountByFile = new Map();
44
+ for (const r of incidentRows) {
45
+ if (!r.affected_files)
46
+ continue;
47
+ let files = [];
48
+ try {
49
+ const parsed = JSON.parse(r.affected_files);
50
+ if (Array.isArray(parsed))
51
+ files = parsed.filter((x) => typeof x === "string");
52
+ }
53
+ catch {
54
+ files = r.affected_files.split(",").map((s) => s.trim()).filter(Boolean);
55
+ }
56
+ for (const f of files)
57
+ incidentCountByFile.set(f, (incidentCountByFile.get(f) ?? 0) + 1);
58
+ }
59
+ // 4. Score each candidate.
60
+ const candidates = [];
61
+ for (const r of fileRows) {
62
+ const days = (now.getTime() - new Date(r.last_touch).getTime()) / 86_400_000;
63
+ if (days < minStale)
64
+ continue;
65
+ if (r.commits > maxCommits)
66
+ continue;
67
+ const entities = entityCountByFile.get(r.path) ?? 0;
68
+ const incidents = incidentCountByFile.get(r.path) ?? 0;
69
+ // Risk: incident history makes deletion riskier.
70
+ const risk = Math.min(1, incidents / 5);
71
+ const harvestScore = (days / 365) * (1 / Math.max(r.commits, 1)) * (1 - risk);
72
+ candidates.push({
73
+ filePath: r.path,
74
+ daysSinceTouch: Math.round(days),
75
+ commitCount: r.commits,
76
+ entityCount: entities,
77
+ incidentCount: incidents,
78
+ harvestScore: Math.round(harvestScore * 1000) / 1000,
79
+ risk: classifyHarvestRisk(incidents, entities, r.commits),
80
+ recommendation: buildHarvestRecommendation(days, entities, incidents),
81
+ });
82
+ }
83
+ candidates.sort((a, b) => b.harvestScore - a.harvestScore);
84
+ return candidates.slice(0, topN);
85
+ }
86
+ export function classifyHarvestRisk(incidents, entities, commits) {
87
+ if (incidents >= 3)
88
+ return "risky";
89
+ if (incidents >= 1 || entities >= 10)
90
+ return "moderate";
91
+ if (commits >= 4)
92
+ return "low-risk";
93
+ return "safe";
94
+ }
95
+ function buildHarvestRecommendation(days, entities, incidents) {
96
+ if (incidents >= 3) {
97
+ return "Risky to delete — past incidents associated with this file. Inspect manually.";
98
+ }
99
+ if (entities === 0 && days > 365) {
100
+ return "Likely safe to delete — no entities parsed and untouched for over a year.";
101
+ }
102
+ if (entities === 0) {
103
+ return "Probably dead — no entities. Verify no runtime imports before deleting.";
104
+ }
105
+ if (days > 730) {
106
+ return "Untouched for 2+ years. Strong candidate, but check downstream usage first.";
107
+ }
108
+ return "Stale candidate. Run static analysis to confirm no inbound references.";
109
+ }
110
+ export function summarizeHarvest(candidates) {
111
+ if (candidates.length === 0) {
112
+ return { candidateCount: 0, estimatedLinesSaved: 0, netSavings: 0, summary: "No harvestable candidates." };
113
+ }
114
+ // Approximate 50 LOC per file as a rough heuristic — refine by reading file size if needed.
115
+ const linesPerFile = 50;
116
+ const estLines = candidates.length * linesPerFile;
117
+ const avgRisk = candidates.reduce((s, c) => s + (c.risk === "risky" ? 1 : c.risk === "moderate" ? 0.5 : 0.1), 0) / candidates.length;
118
+ const netSavings = Math.round(estLines * (1 - avgRisk));
119
+ return {
120
+ candidateCount: candidates.length,
121
+ estimatedLinesSaved: estLines,
122
+ netSavings,
123
+ summary: `${candidates.length} harvestable files — ~${estLines} LOC potential, ~${netSavings} net of risk.`,
124
+ };
125
+ }
126
+ //# sourceMappingURL=tax-loss-harvest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tax-loss-harvest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.test.d.ts","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { MnemeStore } from "../store/sqlite.js";
6
+ import { findHarvestCandidates, classifyHarvestRisk, summarizeHarvest } from "./tax-loss-harvest.js";
7
+ let tmpDir;
8
+ let store;
9
+ beforeEach(() => {
10
+ tmpDir = mkdtempSync(join(tmpdir(), "mneme-tlh-"));
11
+ store = new MnemeStore(join(tmpDir, "mneme.db"));
12
+ });
13
+ afterEach(() => {
14
+ store.close();
15
+ rmSync(tmpDir, { recursive: true, force: true });
16
+ });
17
+ const cmt = (hash, date, files) => ({
18
+ hash,
19
+ shortHash: hash.slice(0, 7),
20
+ authorName: "alice",
21
+ authorEmail: "a@x",
22
+ authorDate: `${date}T00:00:00Z`,
23
+ committerDate: `${date}T00:00:00Z`,
24
+ subject: "x",
25
+ body: "",
26
+ parents: [],
27
+ files,
28
+ });
29
+ function seed(commits) {
30
+ store.upsertCommits(commits);
31
+ for (const c of commits) {
32
+ store.upsertFileChanges(c.files.map((f) => ({ commitHash: c.hash, path: f, changeKind: "M", insertions: 1, deletions: 0 })));
33
+ }
34
+ }
35
+ function seedIncident(affectedFiles) {
36
+ store.db
37
+ .prepare(`INSERT INTO incidents (id, source, title, occurred_at, severity, affected_files)
38
+ VALUES (?, 'manual', 'inc', ?, 'medium', ?)`)
39
+ .run(`inc-${Math.random()}`, "2024-01-01T00:00:00Z", JSON.stringify(affectedFiles));
40
+ }
41
+ describe("classifyHarvestRisk", () => {
42
+ it("'risky' when incidents ≥ 3", () => {
43
+ expect(classifyHarvestRisk(3, 0, 1)).toBe("risky");
44
+ });
45
+ it("'moderate' when entities ≥ 10 OR incidents ≥ 1", () => {
46
+ expect(classifyHarvestRisk(0, 12, 1)).toBe("moderate");
47
+ expect(classifyHarvestRisk(1, 0, 1)).toBe("moderate");
48
+ });
49
+ it("'low-risk' when commits ≥ 4 (some history)", () => {
50
+ expect(classifyHarvestRisk(0, 0, 4)).toBe("low-risk");
51
+ });
52
+ it("'safe' when nothing else applies", () => {
53
+ expect(classifyHarvestRisk(0, 0, 1)).toBe("safe");
54
+ });
55
+ });
56
+ describe("findHarvestCandidates — basic detection", () => {
57
+ it("returns empty when no files are stale enough", () => {
58
+ seed([cmt("a1", "2024-11-01", ["src/active.ts"])]);
59
+ expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 180 })).toEqual([]);
60
+ });
61
+ it("flags a stale solo-touched file as a candidate", () => {
62
+ seed([cmt("a1", "2023-01-01", ["src/legacy.ts"])]);
63
+ const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01") });
64
+ expect(candidates.find((c) => c.filePath === "src/legacy.ts")).toBeDefined();
65
+ });
66
+ it("respects maxCommits filter", () => {
67
+ const commits = Array.from({ length: 10 }, (_, i) => cmt(`a${i}`.padEnd(7, "x"), "2023-01-01", ["src/hot.ts"]));
68
+ seed(commits);
69
+ expect(findHarvestCandidates(store, { now: new Date("2024-12-01"), maxCommits: 5 }).find((c) => c.filePath === "src/hot.ts")).toBeUndefined();
70
+ });
71
+ it("higher harvest score for older + lower-commit files", () => {
72
+ seed([
73
+ cmt("a1", "2020-01-01", ["src/very-old.ts"]),
74
+ cmt("b1", "2023-06-01", ["src/recent-stale.ts"]),
75
+ ]);
76
+ const candidates = findHarvestCandidates(store, { now: new Date("2024-12-01"), minStaleDays: 90 });
77
+ expect(candidates[0].filePath).toBe("src/very-old.ts");
78
+ });
79
+ it("'risky' tier when file appears in 3+ incidents", () => {
80
+ seed([cmt("a1", "2023-01-01", ["src/payment.ts"])]);
81
+ seedIncident(["src/payment.ts"]);
82
+ seedIncident(["src/payment.ts"]);
83
+ seedIncident(["src/payment.ts"]);
84
+ const c = findHarvestCandidates(store, { now: new Date("2024-12-01") }).find((x) => x.filePath === "src/payment.ts");
85
+ expect(c.risk).toBe("risky");
86
+ expect(c.recommendation.toLowerCase()).toMatch(/risk|past incidents/);
87
+ });
88
+ });
89
+ describe("summarizeHarvest", () => {
90
+ it("returns zero metrics for empty input", () => {
91
+ const s = summarizeHarvest([]);
92
+ expect(s.candidateCount).toBe(0);
93
+ expect(s.estimatedLinesSaved).toBe(0);
94
+ expect(s.summary.toLowerCase()).toContain("no");
95
+ });
96
+ it("computes net-of-risk savings", () => {
97
+ const candidates = [
98
+ {
99
+ filePath: "src/a.ts",
100
+ daysSinceTouch: 500,
101
+ commitCount: 1,
102
+ entityCount: 0,
103
+ incidentCount: 0,
104
+ harvestScore: 1,
105
+ risk: "safe",
106
+ recommendation: "",
107
+ },
108
+ {
109
+ filePath: "src/b.ts",
110
+ daysSinceTouch: 600,
111
+ commitCount: 1,
112
+ entityCount: 0,
113
+ incidentCount: 4,
114
+ harvestScore: 0.5,
115
+ risk: "risky",
116
+ recommendation: "",
117
+ },
118
+ ];
119
+ const s = summarizeHarvest(candidates);
120
+ expect(s.candidateCount).toBe(2);
121
+ expect(s.estimatedLinesSaved).toBe(100);
122
+ // avg risk = (0.1 + 1) / 2 = 0.55 → net = 100 × (1 - 0.55) = 45
123
+ expect(s.netSavings).toBe(45);
124
+ });
125
+ });
126
+ //# sourceMappingURL=tax-loss-harvest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax-loss-harvest.test.js","sourceRoot":"","sources":["../../src/quant/tax-loss-harvest.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,UAAU,SAAI,GACb,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
1
+ {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,UAAU,SAAI,GACb,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
@@ -16,13 +16,19 @@
16
16
  */
17
17
  const SYNTH_SYSTEM_PROMPT = [
18
18
  "You are a senior engineer answering a question about a code repository's history.",
19
- "You are given a question and the top-K commits retrieved from the repo's git history.",
20
- "Write a 2-4 sentence answer that:",
21
- " Cites at least one commit hash (short form, 7 chars) using inline backticks.",
22
- " Says only what the evidence supports never invent a claim.",
23
- " If the evidence is thin or contradicts itself, say so plainly.",
24
- " Avoids filler ('the codebase shows', 'as we can see') and lists.",
25
- "Output the answer as plain text, no markdown lists, no headings, no preamble.",
19
+ "You receive the user's question and the top-K commits retrieved from git.",
20
+ "",
21
+ "Write a VERDICT-SHAPED answer:",
22
+ " Line 1: A single declarative sentence stating the verdict.",
23
+ " Line 2-3: 1-2 sentences of supporting evidence, citing at least one",
24
+ " commit hash using `inline backticks`.",
25
+ " Optionally line 4: A 1-line caveat IF the evidence is thin.",
26
+ "",
27
+ "Strict rules:",
28
+ " • Never invent claims. If evidence is contradictory, say \"the evidence is mixed\".",
29
+ " • No lists, no markdown headings, no preamble like \"Based on the commits...\".",
30
+ " • Don't hedge with \"Possibly\", \"Likely\", \"It seems\" in line 1 — STATE the verdict.",
31
+ " • The user already sees the evidence cards below. Don't repeat them.",
26
32
  ].join("\n");
27
33
  /**
28
34
  * Synthesize an answer from retrieval results.
@@ -127,26 +133,51 @@ function cleanLlmOutput(text) {
127
133
  return t.trim();
128
134
  }
129
135
  /**
130
- * Template-based answer — used when no LLM is configured or LLM fails.
131
- * Honest and short: "Top X matches: PR #N (author, date) subject."
136
+ * Template-based VERDICT answer — used when no LLM is configured or LLM fails.
137
+ * Aggregates results into an actionable summary, not a list of evidence rows.
138
+ *
139
+ * Shape:
140
+ * Line 1 — verdict ("X did 73% of the work" / "Most relevant commit is `abc`")
141
+ * Line 2 — supporting context (date range or top file)
142
+ * (Caveat if confidence is low.)
132
143
  */
133
144
  function extractiveAnswer(_question, results, confidence) {
134
- const top = results.slice(0, 3);
135
- const parts = top.map((r) => {
136
- const hash = r.commit.shortHash || r.commit.hash.slice(0, 7);
137
- const date = r.commit.authorDate.slice(0, 10);
138
- return `\`${hash}\` (${date}): ${r.commit.subject}`;
139
- });
140
- const prefix = confidence === "high"
141
- ? "The most likely answer is in"
142
- : confidence === "medium"
143
- ? "Possibly relevant"
144
- : "Weakly relevant please verify";
145
- if (top.length === 1)
146
- return `${prefix} ${parts[0]}.`;
147
- if (top.length === 2)
148
- return `${prefix} ${parts[0]} and ${parts[1]}.`;
149
- return `${prefix} ${parts[0]}; also ${parts[1]} and ${parts[2]}.`;
145
+ if (results.length === 0)
146
+ return "No matching commits found.";
147
+ const top = results[0];
148
+ const topHash = top.commit.shortHash || top.commit.hash.slice(0, 7);
149
+ // Aggregate author distribution.
150
+ const authors = new Map();
151
+ for (const r of results) {
152
+ const a = r.commit.authorName;
153
+ authors.set(a, (authors.get(a) ?? 0) + 1);
154
+ }
155
+ const sortedAuthors = [...authors.entries()].sort((a, b) => b[1] - a[1]);
156
+ const topAuthor = sortedAuthors[0];
157
+ const topAuthorPct = Math.round((topAuthor[1] / results.length) * 100);
158
+ // Date range.
159
+ const dates = results.map((r) => r.commit.authorDate.slice(0, 10)).sort();
160
+ const fromDate = dates[0];
161
+ const toDate = dates[dates.length - 1];
162
+ const dateRange = fromDate === toDate ? fromDate : `${fromDate} → ${toDate}`;
163
+ // Verdict line — author-aggregated when one dominates, otherwise commit-first.
164
+ let verdict;
165
+ if (topAuthorPct >= 60 && results.length >= 3) {
166
+ verdict = `${topAuthor[0]} authored ${topAuthor[1]} of ${results.length} relevant commits (${topAuthorPct}%) — likely the person to ask.`;
167
+ }
168
+ else if (results.length === 1) {
169
+ verdict = `The single relevant commit is \`${topHash}\` — ${top.commit.subject}.`;
170
+ }
171
+ else {
172
+ verdict = `${results.length} relevant commits across ${sortedAuthors.length} author(s); the strongest match is \`${topHash}\` (${top.commit.authorName}).`;
173
+ }
174
+ // Supporting line — date range + top commit subject (if not already in verdict).
175
+ const support = `Range: ${dateRange}. Top: \`${topHash}\` — ${top.commit.subject}.`;
176
+ // Caveat for thin evidence.
177
+ const caveat = confidence === "low"
178
+ ? " The evidence is thin — verify before trusting this answer."
179
+ : "";
180
+ return `${verdict}\n${support}${caveat}`;
150
181
  }
151
182
  /** Honest "no context found" — never returns a result. */
152
183
  function noContextAnswer(_question) {
@@ -1 +1 @@
1
- {"version":3,"file":"synthesize.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"synthesize.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"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"}
@@ -37,26 +37,37 @@ describe("synthesize — no LLM (extractive fallback)", () => {
37
37
  expect(r.answer).toContain("abc1234");
38
38
  expect(r.evidenceCommitHashes).toEqual(["abc1234", "def5678"]);
39
39
  });
40
- it("extractive answer uses different phrasing per confidence level", async () => {
40
+ it("extractive answer adds a caveat ONLY for low confidence", async () => {
41
41
  const high = await synthesize("q", [result("abc1234", 0.05, "subject")], "high");
42
- const medium = await synthesize("q", [result("abc1234", 0.05, "subject")], "medium");
43
42
  const low = await synthesize("q", [result("abc1234", 0.05, "subject")], "low");
44
- expect(high.answer).toContain("most likely");
45
- expect(medium.answer.toLowerCase()).toContain("possibly");
46
43
  expect(low.answer.toLowerCase()).toContain("verify");
44
+ expect(high.answer.toLowerCase()).not.toContain("verify");
47
45
  });
48
- it("extractive answer adapts to result count (1, 2, 3+)", async () => {
49
- const one = await synthesize("q", [result("abc1234", 0.05, "s1")], "high");
50
- const two = await synthesize("q", [result("abc1234", 0.05, "s1"), result("def5678", 0.04, "s2")], "high");
51
- const three = await synthesize("q", [
52
- result("abc1234", 0.05, "s1"),
53
- result("def5678", 0.04, "s2"),
54
- result("ghi9012", 0.03, "s3"),
46
+ it("extractive verdict aggregates by dominant author when ≥60%", async () => {
47
+ // 3 commits by alice, 1 by bob → alice dominates at 75%
48
+ const r = await synthesize("q", [
49
+ { commit: { ...sampleCommit("a1", "s1", "", "alice") }, score: 0.05, matchedChunks: [] },
50
+ { commit: { ...sampleCommit("a2", "s2", "", "alice") }, score: 0.04, matchedChunks: [] },
51
+ { commit: { ...sampleCommit("a3", "s3", "", "alice") }, score: 0.03, matchedChunks: [] },
52
+ { commit: { ...sampleCommit("b1", "s4", "", "bob") }, score: 0.02, matchedChunks: [] },
55
53
  ], "high");
56
- expect(one.answer).toContain("abc1234");
57
- expect(one.answer).not.toContain("and");
58
- expect(two.answer).toContain("and");
59
- expect(three.answer).toContain("also");
54
+ // verdict line names the dominant author + percentage
55
+ expect(r.answer).toContain("alice");
56
+ expect(r.answer).toMatch(/75%|75 percent/);
57
+ expect(r.answer).toContain("likely the person to ask");
58
+ });
59
+ it("extractive verdict adapts to single-result case", async () => {
60
+ const r = await synthesize("q", [result("abc1234", 0.05, "subject")], "high");
61
+ expect(r.answer.toLowerCase()).toContain("single");
62
+ expect(r.answer).toContain("abc1234");
63
+ });
64
+ it("extractive verdict reports range when authors are mixed", async () => {
65
+ const r = await synthesize("q", [
66
+ { commit: { ...sampleCommit("a1", "s1", "", "alice") }, score: 0.05, matchedChunks: [] },
67
+ { commit: { ...sampleCommit("b1", "s2", "", "bob") }, score: 0.04, matchedChunks: [] },
68
+ ], "high");
69
+ // Mixed authors at 2/2 → no dominant majority (≥60% but n<3 short-circuits)
70
+ expect(r.answer.toLowerCase()).toContain("strongest match");
60
71
  });
61
72
  });
62
73
  describe("synthesize — with LLM (enricher branch)", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"synthesize.test.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"synthesize.test.js","sourceRoot":"","sources":["../../src/retrieve/synthesize.test.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,6 @@
6
6
  * Phase 3 — incidents, correlations
7
7
  * Phase 4 — graph_snapshots (for temporal viz)
8
8
  */
9
- export declare const SCHEMA_VERSION = 2;
10
- export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS commits (\n hash TEXT PRIMARY KEY,\n short_hash TEXT NOT NULL,\n author_name TEXT NOT NULL,\n author_email TEXT NOT NULL,\n author_date TEXT NOT NULL,\n committer_date TEXT NOT NULL,\n subject TEXT NOT NULL,\n body TEXT NOT NULL,\n parents TEXT NOT NULL,\n pr_number INTEGER,\n pr_title TEXT,\n pr_body TEXT,\n issue_refs TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_commits_author_date ON commits(author_date);\nCREATE INDEX IF NOT EXISTS idx_commits_pr ON commits(pr_number);\n\nCREATE TABLE IF NOT EXISTS file_changes (\n commit_hash TEXT NOT NULL,\n path TEXT NOT NULL,\n change_kind TEXT NOT NULL,\n insertions INTEGER NOT NULL DEFAULT 0,\n deletions INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (commit_hash, path),\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(path);\n\nCREATE TABLE IF NOT EXISTS chunks (\n id TEXT PRIMARY KEY,\n commit_hash TEXT NOT NULL,\n kind TEXT NOT NULL,\n text TEXT NOT NULL,\n embedding BLOB,\n embedding_model TEXT,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);\nCREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n id UNINDEXED,\n commit_hash UNINDEXED,\n text,\n tokenize = 'porter unicode61'\n);\n\n-- Phase 2: entity graph\nCREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n signature TEXT,\n language TEXT NOT NULL,\n embedding BLOB\n);\nCREATE INDEX IF NOT EXISTS idx_entities_file ON entities(file_path);\nCREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);\n\n-- Phase 3: incidents and correlations (the moat)\nCREATE TABLE IF NOT EXISTS incidents (\n id TEXT PRIMARY KEY,\n source TEXT NOT NULL,\n external_id TEXT,\n title TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n resolved_at TEXT,\n severity TEXT NOT NULL,\n affected_files TEXT,\n stack_frames TEXT,\n url TEXT,\n metadata TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_incidents_occurred ON incidents(occurred_at);\nCREATE INDEX IF NOT EXISTS idx_incidents_source ON incidents(source);\n\nCREATE TABLE IF NOT EXISTS correlations (\n id TEXT PRIMARY KEY,\n from_kind TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_kind TEXT NOT NULL,\n to_id TEXT NOT NULL,\n weight REAL NOT NULL,\n reason TEXT NOT NULL,\n evidence TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_corr_from ON correlations(from_kind, from_id);\nCREATE INDEX IF NOT EXISTS idx_corr_to ON correlations(to_kind, to_id);\n\n-- Phase 4: temporal graph snapshots\nCREATE TABLE IF NOT EXISTS graph_snapshots (\n id TEXT PRIMARY KEY,\n taken_at TEXT NOT NULL,\n payload BLOB NOT NULL\n);\n\n-- WILD #1: AI-synthesized notes for commits with poor messages.\n-- The original commit is never modified. The synthesized note is searched\n-- alongside the original chunks but always marked as kind='synthesized'\n-- so users can verify against the underlying diff.\nCREATE TABLE IF NOT EXISTS synthesized_notes (\n commit_hash TEXT PRIMARY KEY,\n note TEXT NOT NULL,\n model TEXT NOT NULL,\n diff_chars INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\n\n-- Phase 4 - Wisdom Mutant Engine\n-- The \"always-learning\" loop. Three append-only tables that record what the\n-- system saw, what it tried, and how well it worked. Per-repo, never leaves\n-- the machine. Honest contract: every recommendation in mneme adapt and\n-- every threshold in mneme ask is traceable back to a row here.\n\n-- Every query records its result-set fingerprint. was_helpful is set later,\n-- either by the user (--feedback up/down) or by an implicit signal (the\n-- query was not re-run with a refined version within 60s, or one of the\n-- returned commits was navigated to via mneme why).\nCREATE TABLE IF NOT EXISTS wisdom_feedback (\n id TEXT PRIMARY KEY,\n query TEXT NOT NULL,\n result_hashes TEXT NOT NULL, -- JSON array of commit hashes returned\n top_score REAL, -- top RRF score at query time\n was_helpful INTEGER, -- 1=yes, 0=no, NULL=unknown\n source TEXT NOT NULL, -- 'explicit' | 'implicit-reuse' | 'implicit-revisit'\n semantic_weight REAL, -- the search config that produced this result\n min_sem_cosine REAL,\n rrf_k INTEGER,\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_feedback_created ON wisdom_feedback(created_at);\nCREATE INDEX IF NOT EXISTS idx_feedback_helpful ON wisdom_feedback(was_helpful);\n\n-- The current calibration of search/retrieval knobs. One row per knob.\n-- Updated by the auto-calibrator after evaluating against the feedback set.\nCREATE TABLE IF NOT EXISTS wisdom_calibration (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL, -- JSON\n metric_value REAL, -- the metric this calibration achieved\n metric_name TEXT, -- e.g. \"hit_rate\", \"recall_at_3\"\n calibrated_at TEXT NOT NULL,\n notes TEXT\n);\n\n-- Append-only history of self-eval runs. Lets the user (and any auditor)\n-- see whether quality is trending up or down over time.\nCREATE TABLE IF NOT EXISTS wisdom_eval_run (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ran_at TEXT NOT NULL,\n variant TEXT NOT NULL, -- 'baseline' | 'reranked' | 'calibrated' | ...\n recall_at_3 REAL NOT NULL,\n mrr REAL NOT NULL,\n ndcg_at_10 REAL,\n hit_rate REAL NOT NULL,\n semantic_weight REAL,\n min_sem_cosine REAL,\n rrf_k INTEGER,\n num_queries INTEGER NOT NULL,\n notes TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_eval_ran_at ON wisdom_eval_run(ran_at);\n";
9
+ export declare const SCHEMA_VERSION = 3;
10
+ export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS commits (\n hash TEXT PRIMARY KEY,\n short_hash TEXT NOT NULL,\n author_name TEXT NOT NULL,\n author_email TEXT NOT NULL,\n author_date TEXT NOT NULL,\n committer_date TEXT NOT NULL,\n subject TEXT NOT NULL,\n body TEXT NOT NULL,\n parents TEXT NOT NULL,\n pr_number INTEGER,\n pr_title TEXT,\n pr_body TEXT,\n issue_refs TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_commits_author_date ON commits(author_date);\nCREATE INDEX IF NOT EXISTS idx_commits_pr ON commits(pr_number);\n\nCREATE TABLE IF NOT EXISTS file_changes (\n commit_hash TEXT NOT NULL,\n path TEXT NOT NULL,\n change_kind TEXT NOT NULL,\n insertions INTEGER NOT NULL DEFAULT 0,\n deletions INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (commit_hash, path),\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_file_changes_path ON file_changes(path);\n\nCREATE TABLE IF NOT EXISTS chunks (\n id TEXT PRIMARY KEY,\n commit_hash TEXT NOT NULL,\n kind TEXT NOT NULL,\n text TEXT NOT NULL,\n embedding BLOB,\n embedding_model TEXT,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);\nCREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);\n\n-- FTS5 with trigram tokenizer \u2014 works for Thai, CJK, Arabic, and any\n-- non-space-separated language. The tradeoff vs porter is that BM25 is\n-- slightly less precise for English, but the gain in cross-language\n-- coverage is enormous. Also: trigram makes substring search free.\nCREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n id UNINDEXED,\n commit_hash UNINDEXED,\n text,\n tokenize = 'trigram'\n);\n\n-- Phase 2: entity graph\nCREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n signature TEXT,\n language TEXT NOT NULL,\n embedding BLOB\n);\nCREATE INDEX IF NOT EXISTS idx_entities_file ON entities(file_path);\nCREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);\n\n-- Phase 3: incidents and correlations (the moat)\nCREATE TABLE IF NOT EXISTS incidents (\n id TEXT PRIMARY KEY,\n source TEXT NOT NULL,\n external_id TEXT,\n title TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n resolved_at TEXT,\n severity TEXT NOT NULL,\n affected_files TEXT,\n stack_frames TEXT,\n url TEXT,\n metadata TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_incidents_occurred ON incidents(occurred_at);\nCREATE INDEX IF NOT EXISTS idx_incidents_source ON incidents(source);\n\nCREATE TABLE IF NOT EXISTS correlations (\n id TEXT PRIMARY KEY,\n from_kind TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_kind TEXT NOT NULL,\n to_id TEXT NOT NULL,\n weight REAL NOT NULL,\n reason TEXT NOT NULL,\n evidence TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_corr_from ON correlations(from_kind, from_id);\nCREATE INDEX IF NOT EXISTS idx_corr_to ON correlations(to_kind, to_id);\n\n-- Phase 4: temporal graph snapshots\nCREATE TABLE IF NOT EXISTS graph_snapshots (\n id TEXT PRIMARY KEY,\n taken_at TEXT NOT NULL,\n payload BLOB NOT NULL\n);\n\n-- WILD #1: AI-synthesized notes for commits with poor messages.\n-- The original commit is never modified. The synthesized note is searched\n-- alongside the original chunks but always marked as kind='synthesized'\n-- so users can verify against the underlying diff.\nCREATE TABLE IF NOT EXISTS synthesized_notes (\n commit_hash TEXT PRIMARY KEY,\n note TEXT NOT NULL,\n model TEXT NOT NULL,\n diff_chars INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (commit_hash) REFERENCES commits(hash) ON DELETE CASCADE\n);\n\n-- Phase 4 - Wisdom Mutant Engine\n-- The \"always-learning\" loop. Three append-only tables that record what the\n-- system saw, what it tried, and how well it worked. Per-repo, never leaves\n-- the machine. Honest contract: every recommendation in mneme adapt and\n-- every threshold in mneme ask is traceable back to a row here.\n\n-- Every query records its result-set fingerprint. was_helpful is set later,\n-- either by the user (--feedback up/down) or by an implicit signal (the\n-- query was not re-run with a refined version within 60s, or one of the\n-- returned commits was navigated to via mneme why).\nCREATE TABLE IF NOT EXISTS wisdom_feedback (\n id TEXT PRIMARY KEY,\n query TEXT NOT NULL,\n result_hashes TEXT NOT NULL, -- JSON array of commit hashes returned\n top_score REAL, -- top RRF score at query time\n was_helpful INTEGER, -- 1=yes, 0=no, NULL=unknown\n source TEXT NOT NULL, -- 'explicit' | 'implicit-reuse' | 'implicit-revisit'\n semantic_weight REAL, -- the search config that produced this result\n min_sem_cosine REAL,\n rrf_k INTEGER,\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_feedback_created ON wisdom_feedback(created_at);\nCREATE INDEX IF NOT EXISTS idx_feedback_helpful ON wisdom_feedback(was_helpful);\n\n-- The current calibration of search/retrieval knobs. One row per knob.\n-- Updated by the auto-calibrator after evaluating against the feedback set.\nCREATE TABLE IF NOT EXISTS wisdom_calibration (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL, -- JSON\n metric_value REAL, -- the metric this calibration achieved\n metric_name TEXT, -- e.g. \"hit_rate\", \"recall_at_3\"\n calibrated_at TEXT NOT NULL,\n notes TEXT\n);\n\n-- Append-only history of self-eval runs. Lets the user (and any auditor)\n-- see whether quality is trending up or down over time.\nCREATE TABLE IF NOT EXISTS wisdom_eval_run (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n ran_at TEXT NOT NULL,\n variant TEXT NOT NULL, -- 'baseline' | 'reranked' | 'calibrated' | ...\n recall_at_3 REAL NOT NULL,\n mrr REAL NOT NULL,\n ndcg_at_10 REAL,\n hit_rate REAL NOT NULL,\n semantic_weight REAL,\n min_sem_cosine REAL,\n rrf_k INTEGER,\n num_queries INTEGER NOT NULL,\n notes TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_eval_ran_at ON wisdom_eval_run(ran_at);\n";
11
11
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,UAAU,g4LA4KtB,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,UAAU,upMAgLtB,CAAC"}
@@ -6,7 +6,7 @@
6
6
  * Phase 3 — incidents, correlations
7
7
  * Phase 4 — graph_snapshots (for temporal viz)
8
8
  */
9
- export const SCHEMA_VERSION = 2;
9
+ export const SCHEMA_VERSION = 3;
10
10
  export const SCHEMA_SQL = `
11
11
  CREATE TABLE IF NOT EXISTS meta (
12
12
  key TEXT PRIMARY KEY,
@@ -54,11 +54,15 @@ CREATE TABLE IF NOT EXISTS chunks (
54
54
  CREATE INDEX IF NOT EXISTS idx_chunks_commit ON chunks(commit_hash);
55
55
  CREATE INDEX IF NOT EXISTS idx_chunks_kind ON chunks(kind);
56
56
 
57
+ -- FTS5 with trigram tokenizer — works for Thai, CJK, Arabic, and any
58
+ -- non-space-separated language. The tradeoff vs porter is that BM25 is
59
+ -- slightly less precise for English, but the gain in cross-language
60
+ -- coverage is enormous. Also: trigram makes substring search free.
57
61
  CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
58
62
  id UNINDEXED,
59
63
  commit_hash UNINDEXED,
60
64
  text,
61
- tokenize = 'porter unicode61'
65
+ tokenize = 'trigram'
62
66
  );
63
67
 
64
68
  -- Phase 2: entity graph
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4KzB,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgLzB,CAAC"}
@@ -4,6 +4,8 @@ export declare class MnemeStore {
4
4
  readonly dbPath: string;
5
5
  readonly db: Database.Database;
6
6
  constructor(dbPath: string);
7
+ /** Drop + recreate chunks_fts with the new trigram tokenizer, then rebuild from chunks. */
8
+ private migrateFtsToTrigram;
7
9
  close(): void;
8
10
  setMeta(key: string, value: string): void;
9
11
  getMeta(key: string): string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,qBAAa,UAAU;aAGO,MAAM,EAAE,MAAM;IAF1C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;gBAEH,MAAM,EAAE,MAAM;IAU1C,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAMzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOxC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IA6BtC,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IAc9C,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBlE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ3C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAOtC,YAAY,IAAI,MAAM;IAKtB,WAAW,IAAI,MAAM;IAKrB,wBAAwB,IAAI,MAAM;IAOlC,mFAAmF;IAClF,kBAAkB,IAAI,SAAS,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC;IAuBnH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAuB5H,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IA0BjE,aAAa,IAAI,MAAM;IAKvB,0BAA0B,IAAI,MAAM;IAOpC,uBAAuB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhE,oBAAoB,IAAI,SAAS,CAAC,MAAM,CAAC;IAgC1C,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA2B5C,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAU/F,kBAAkB,CAChB,UAAU,EAAE,MAAM,GACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAQjE,qBAAqB,IAAI,MAAM;IAO/B,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI;CAsBtD"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,qBAAa,UAAU;aAGO,MAAM,EAAE,MAAM;IAF1C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;gBAEH,MAAM,EAAE,MAAM;IAmB1C,2FAA2F;IAC3F,OAAO,CAAC,mBAAmB;IAiB3B,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAMzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOxC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IA6BtC,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IAc9C,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBlE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ3C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAOtC,YAAY,IAAI,MAAM;IAKtB,WAAW,IAAI,MAAM;IAKrB,wBAAwB,IAAI,MAAM;IAOlC,mFAAmF;IAClF,kBAAkB,IAAI,SAAS,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC;IAuBnH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAuB5H,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IA0BjE,aAAa,IAAI,MAAM;IAKvB,0BAA0B,IAAI,MAAM;IAOpC,uBAAuB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhE,oBAAoB,IAAI,SAAS,CAAC,MAAM,CAAC;IAgC1C,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA2B5C,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAU/F,kBAAkB,CAChB,UAAU,EAAE,MAAM,GACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAQjE,qBAAqB,IAAI,MAAM;IAO/B,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI;CAsBtD"}