@mneme-ai/core 0.9.0 → 0.11.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.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/insights/bus-factor.d.ts +58 -0
- package/dist/insights/bus-factor.d.ts.map +1 -0
- package/dist/insights/bus-factor.js +117 -0
- package/dist/insights/bus-factor.js.map +1 -0
- package/dist/insights/bus-factor.test.d.ts +2 -0
- package/dist/insights/bus-factor.test.d.ts.map +1 -0
- package/dist/insights/bus-factor.test.js +149 -0
- package/dist/insights/bus-factor.test.js.map +1 -0
- package/dist/insights/commit-coach.d.ts +80 -0
- package/dist/insights/commit-coach.d.ts.map +1 -0
- package/dist/insights/commit-coach.js +230 -0
- package/dist/insights/commit-coach.js.map +1 -0
- package/dist/insights/commit-coach.test.d.ts +2 -0
- package/dist/insights/commit-coach.test.d.ts.map +1 -0
- package/dist/insights/commit-coach.test.js +163 -0
- package/dist/insights/commit-coach.test.js.map +1 -0
- package/dist/insights/crystal-ball.d.ts +76 -0
- package/dist/insights/crystal-ball.d.ts.map +1 -0
- package/dist/insights/crystal-ball.js +219 -0
- package/dist/insights/crystal-ball.js.map +1 -0
- package/dist/insights/crystal-ball.test.d.ts +2 -0
- package/dist/insights/crystal-ball.test.d.ts.map +1 -0
- package/dist/insights/crystal-ball.test.js +157 -0
- package/dist/insights/crystal-ball.test.js.map +1 -0
- package/dist/insights/ghost.d.ts +80 -0
- package/dist/insights/ghost.d.ts.map +1 -0
- package/dist/insights/ghost.js +138 -0
- package/dist/insights/ghost.js.map +1 -0
- package/dist/insights/ghost.test.d.ts +2 -0
- package/dist/insights/ghost.test.d.ts.map +1 -0
- package/dist/insights/ghost.test.js +141 -0
- package/dist/insights/ghost.test.js.map +1 -0
- package/dist/insights/index.d.ts +8 -0
- package/dist/insights/index.d.ts.map +1 -1
- package/dist/insights/index.js +8 -0
- package/dist/insights/index.js.map +1 -1
- package/dist/insights/paradox.d.ts +36 -0
- package/dist/insights/paradox.d.ts.map +1 -0
- package/dist/insights/paradox.js +201 -0
- package/dist/insights/paradox.js.map +1 -0
- package/dist/insights/paradox.test.d.ts +2 -0
- package/dist/insights/paradox.test.d.ts.map +1 -0
- package/dist/insights/paradox.test.js +88 -0
- package/dist/insights/paradox.test.js.map +1 -0
- package/dist/insights/premortem.d.ts +73 -0
- package/dist/insights/premortem.d.ts.map +1 -0
- package/dist/insights/premortem.js +209 -0
- package/dist/insights/premortem.js.map +1 -0
- package/dist/insights/premortem.test.d.ts +2 -0
- package/dist/insights/premortem.test.d.ts.map +1 -0
- package/dist/insights/premortem.test.js +169 -0
- package/dist/insights/premortem.test.js.map +1 -0
- package/dist/insights/regret.d.ts +57 -0
- package/dist/insights/regret.d.ts.map +1 -0
- package/dist/insights/regret.js +137 -0
- package/dist/insights/regret.js.map +1 -0
- package/dist/insights/regret.test.d.ts +2 -0
- package/dist/insights/regret.test.d.ts.map +1 -0
- package/dist/insights/regret.test.js +153 -0
- package/dist/insights/regret.test.js.map +1 -0
- package/dist/insights/time-machine.d.ts +70 -0
- package/dist/insights/time-machine.d.ts.map +1 -0
- package/dist/insights/time-machine.js +177 -0
- package/dist/insights/time-machine.js.map +1 -0
- package/dist/insights/time-machine.test.d.ts +2 -0
- package/dist/insights/time-machine.test.d.ts.map +1 -0
- package/dist/insights/time-machine.test.js +141 -0
- package/dist/insights/time-machine.test.js.map +1 -0
- package/dist/insights/who-knows.d.ts +18 -0
- package/dist/insights/who-knows.d.ts.map +1 -1
- package/dist/insights/who-knows.js +29 -0
- package/dist/insights/who-knows.js.map +1 -1
- package/dist/insights/who-knows.test.js +63 -1
- package/dist/insights/who-knows.test.js.map +1 -1
- package/dist/quant/alpha.d.ts +87 -0
- package/dist/quant/alpha.d.ts.map +1 -0
- package/dist/quant/alpha.js +103 -0
- package/dist/quant/alpha.js.map +1 -0
- package/dist/quant/alpha.test.d.ts +2 -0
- package/dist/quant/alpha.test.d.ts.map +1 -0
- package/dist/quant/alpha.test.js +147 -0
- package/dist/quant/alpha.test.js.map +1 -0
- package/dist/quant/backtest.d.ts +57 -0
- package/dist/quant/backtest.d.ts.map +1 -0
- package/dist/quant/backtest.js +90 -0
- package/dist/quant/backtest.js.map +1 -0
- package/dist/quant/backtest.test.d.ts +2 -0
- package/dist/quant/backtest.test.d.ts.map +1 -0
- package/dist/quant/backtest.test.js +133 -0
- package/dist/quant/backtest.test.js.map +1 -0
- package/dist/quant/black-swan.d.ts +45 -0
- package/dist/quant/black-swan.d.ts.map +1 -0
- package/dist/quant/black-swan.js +112 -0
- package/dist/quant/black-swan.js.map +1 -0
- package/dist/quant/black-swan.test.d.ts +2 -0
- package/dist/quant/black-swan.test.d.ts.map +1 -0
- package/dist/quant/black-swan.test.js +131 -0
- package/dist/quant/black-swan.test.js.map +1 -0
- package/dist/quant/correlation-matrix.d.ts +54 -0
- package/dist/quant/correlation-matrix.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.js +103 -0
- package/dist/quant/correlation-matrix.js.map +1 -0
- package/dist/quant/correlation-matrix.test.d.ts +2 -0
- package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.test.js +118 -0
- package/dist/quant/correlation-matrix.test.js.map +1 -0
- package/dist/quant/drawdown.d.ts +51 -0
- package/dist/quant/drawdown.d.ts.map +1 -0
- package/dist/quant/drawdown.js +96 -0
- package/dist/quant/drawdown.js.map +1 -0
- package/dist/quant/drawdown.test.d.ts +2 -0
- package/dist/quant/drawdown.test.d.ts.map +1 -0
- package/dist/quant/drawdown.test.js +166 -0
- package/dist/quant/drawdown.test.js.map +1 -0
- package/dist/quant/greek.d.ts +55 -0
- package/dist/quant/greek.d.ts.map +1 -0
- package/dist/quant/greek.js +157 -0
- package/dist/quant/greek.js.map +1 -0
- package/dist/quant/greek.test.d.ts +2 -0
- package/dist/quant/greek.test.d.ts.map +1 -0
- package/dist/quant/greek.test.js +138 -0
- package/dist/quant/greek.test.js.map +1 -0
- package/dist/quant/implied-volatility.d.ts +65 -0
- package/dist/quant/implied-volatility.d.ts.map +1 -0
- package/dist/quant/implied-volatility.js +149 -0
- package/dist/quant/implied-volatility.js.map +1 -0
- package/dist/quant/implied-volatility.test.d.ts +2 -0
- package/dist/quant/implied-volatility.test.d.ts.map +1 -0
- package/dist/quant/implied-volatility.test.js +127 -0
- package/dist/quant/implied-volatility.test.js.map +1 -0
- package/dist/quant/index.d.ts +28 -0
- package/dist/quant/index.d.ts.map +1 -0
- package/dist/quant/index.js +28 -0
- package/dist/quant/index.js.map +1 -0
- package/dist/quant/insider-trading.d.ts +56 -0
- package/dist/quant/insider-trading.d.ts.map +1 -0
- package/dist/quant/insider-trading.js +129 -0
- package/dist/quant/insider-trading.js.map +1 -0
- package/dist/quant/insider-trading.test.d.ts +2 -0
- package/dist/quant/insider-trading.test.d.ts.map +1 -0
- package/dist/quant/insider-trading.test.js +130 -0
- package/dist/quant/insider-trading.test.js.map +1 -0
- package/dist/quant/moneyball.d.ts +48 -0
- package/dist/quant/moneyball.d.ts.map +1 -0
- package/dist/quant/moneyball.js +110 -0
- package/dist/quant/moneyball.js.map +1 -0
- package/dist/quant/moneyball.test.d.ts +2 -0
- package/dist/quant/moneyball.test.d.ts.map +1 -0
- package/dist/quant/moneyball.test.js +137 -0
- package/dist/quant/moneyball.test.js.map +1 -0
- package/dist/quant/tax-loss-harvest.d.ts +59 -0
- package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.js +126 -0
- package/dist/quant/tax-loss-harvest.js.map +1 -0
- package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
- package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.test.js +126 -0
- package/dist/quant/tax-loss-harvest.test.js.map +1 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -1
- package/dist/retrieve/synthesize.js +56 -25
- package/dist/retrieve/synthesize.js.map +1 -1
- package/dist/retrieve/synthesize.test.js +26 -15
- package/dist/retrieve/synthesize.test.js.map +1 -1
- package/dist/store/schema.d.ts +2 -2
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +6 -2
- package/dist/store/schema.js.map +1 -1
- package/dist/store/sqlite.d.ts +2 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +24 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sqlite.test.js +1 -1
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +26 -0
- package/dist/util/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { scoreTone, impliedVolatility, summarizeVolatility } from "./implied-volatility.js";
|
|
3
|
+
const cmt = (hash, date, subject, body = "") => ({
|
|
4
|
+
hash,
|
|
5
|
+
shortHash: hash.slice(0, 7),
|
|
6
|
+
authorName: "alice",
|
|
7
|
+
authorEmail: "a@x",
|
|
8
|
+
authorDate: `${date}T00:00:00Z`,
|
|
9
|
+
committerDate: `${date}T00:00:00Z`,
|
|
10
|
+
subject,
|
|
11
|
+
body,
|
|
12
|
+
parents: [],
|
|
13
|
+
files: [],
|
|
14
|
+
});
|
|
15
|
+
describe("scoreTone — calm vs frustrated commits", () => {
|
|
16
|
+
it("calm commit scores low tone", () => {
|
|
17
|
+
const t = scoreTone(cmt("a1", "2024-01-01", "feat: add new endpoint"));
|
|
18
|
+
expect(t.exclamationScore).toBe(0);
|
|
19
|
+
expect(t.allCapsScore).toBe(0);
|
|
20
|
+
expect(t.emojiScore).toBe(0);
|
|
21
|
+
expect(t.frictionScore).toBe(0);
|
|
22
|
+
expect(t.toneScore).toBeLessThan(0.1);
|
|
23
|
+
});
|
|
24
|
+
it("frustrated commit scores high tone", () => {
|
|
25
|
+
const t = scoreTone(cmt("a1", "2024-01-01", "OMG WTF this is BROKEN!!!", "kinda hopefully fixed??"));
|
|
26
|
+
expect(t.exclamationScore).toBeGreaterThan(0);
|
|
27
|
+
expect(t.allCapsScore).toBeGreaterThan(0);
|
|
28
|
+
expect(t.frictionScore).toBeGreaterThan(0);
|
|
29
|
+
expect(t.hedgeScore).toBeGreaterThan(0);
|
|
30
|
+
expect(t.toneScore).toBeGreaterThan(0.3);
|
|
31
|
+
});
|
|
32
|
+
it("emoji-laden commit raises emoji score", () => {
|
|
33
|
+
const t = scoreTone(cmt("a1", "2024-01-01", "fix prod 🔥💀⚠️"));
|
|
34
|
+
expect(t.emojiScore).toBeGreaterThan(0);
|
|
35
|
+
});
|
|
36
|
+
it("hedge words detected", () => {
|
|
37
|
+
const t = scoreTone(cmt("a1", "2024-01-01", "maybe fix race", "should work probably"));
|
|
38
|
+
expect(t.hedgeScore).toBeGreaterThanOrEqual(2);
|
|
39
|
+
});
|
|
40
|
+
it("toneScore is bounded [0, 1]", () => {
|
|
41
|
+
const samples = [
|
|
42
|
+
"feat: add",
|
|
43
|
+
"OMG WTF BROKEN!!!! 🔥💀 wtf wtf wtf maybe maybe maybe",
|
|
44
|
+
"",
|
|
45
|
+
"AAAAAAAA",
|
|
46
|
+
];
|
|
47
|
+
for (const subj of samples) {
|
|
48
|
+
const t = scoreTone(cmt("x", "2024-01-01", subj));
|
|
49
|
+
expect(t.toneScore).toBeGreaterThanOrEqual(0);
|
|
50
|
+
expect(t.toneScore).toBeLessThanOrEqual(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("impliedVolatility — weekly aggregation", () => {
|
|
55
|
+
it("returns empty for empty input", () => {
|
|
56
|
+
expect(impliedVolatility([])).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
it("buckets by ISO week + computes IV in 0-100 range", () => {
|
|
59
|
+
const commits = [
|
|
60
|
+
cmt("a1", "2024-01-01", "feat: A"),
|
|
61
|
+
cmt("a2", "2024-01-02", "feat: B"),
|
|
62
|
+
cmt("a3", "2024-01-08", "OMG BROKEN!!!"),
|
|
63
|
+
cmt("a4", "2024-01-09", "wtf this is hopeless"),
|
|
64
|
+
];
|
|
65
|
+
const windows = impliedVolatility(commits);
|
|
66
|
+
expect(windows.length).toBeGreaterThanOrEqual(2);
|
|
67
|
+
for (const w of windows) {
|
|
68
|
+
expect(w.iv).toBeGreaterThanOrEqual(0);
|
|
69
|
+
expect(w.iv).toBeLessThanOrEqual(100);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
it("higher tone week shows higher IV", () => {
|
|
73
|
+
const commits = [
|
|
74
|
+
cmt("a1", "2024-01-01", "feat: A"),
|
|
75
|
+
cmt("a2", "2024-01-02", "feat: B"),
|
|
76
|
+
cmt("a3", "2024-01-08", "OMG WTF BROKEN!!!"),
|
|
77
|
+
cmt("a4", "2024-01-09", "ARGH crashed!!! ugh"),
|
|
78
|
+
cmt("a5", "2024-01-10", "fucking 🔥🔥🔥"),
|
|
79
|
+
];
|
|
80
|
+
const windows = impliedVolatility(commits);
|
|
81
|
+
expect(windows.length).toBe(2);
|
|
82
|
+
expect(windows[1].iv).toBeGreaterThan(windows[0].iv);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("summarizeVolatility — trend detection", () => {
|
|
86
|
+
it("returns 'insufficient-data' for fewer than 4 weeks", () => {
|
|
87
|
+
const commits = [cmt("a", "2024-01-01", "feat: x")];
|
|
88
|
+
expect(summarizeVolatility(commits).trend).toBe("insufficient-data");
|
|
89
|
+
});
|
|
90
|
+
it("'rising' when last 4 weeks ascend", () => {
|
|
91
|
+
const commits = [
|
|
92
|
+
// Week 1: calm
|
|
93
|
+
cmt("a1", "2024-01-01", "feat: A"),
|
|
94
|
+
// Week 2: warming up
|
|
95
|
+
cmt("a2", "2024-01-08", "fix bug !!"),
|
|
96
|
+
// Week 3: hot
|
|
97
|
+
cmt("a3", "2024-01-15", "WTF BROKEN!!!"),
|
|
98
|
+
// Week 4: very hot
|
|
99
|
+
cmt("a4", "2024-01-22", "OMG WTF FUCKED 🔥💀 maybe hopefully fix?"),
|
|
100
|
+
];
|
|
101
|
+
expect(summarizeVolatility(commits).trend).toBe("rising");
|
|
102
|
+
});
|
|
103
|
+
it("'falling' when last 4 weeks descend", () => {
|
|
104
|
+
const commits = [
|
|
105
|
+
cmt("a1", "2024-01-01", "OMG WTF FUCKED 🔥💀!!!!!"),
|
|
106
|
+
cmt("a2", "2024-01-08", "WTF still BROKEN!!!"),
|
|
107
|
+
cmt("a3", "2024-01-15", "kinda fix maybe"),
|
|
108
|
+
cmt("a4", "2024-01-22", "feat: stable release"),
|
|
109
|
+
];
|
|
110
|
+
expect(summarizeVolatility(commits).trend).toBe("falling");
|
|
111
|
+
});
|
|
112
|
+
it("'flat' when 4 weeks are similar", () => {
|
|
113
|
+
const commits = [
|
|
114
|
+
cmt("a1", "2024-01-01", "feat: stable"),
|
|
115
|
+
cmt("a2", "2024-01-08", "feat: stable"),
|
|
116
|
+
cmt("a3", "2024-01-15", "feat: stable"),
|
|
117
|
+
cmt("a4", "2024-01-22", "feat: stable"),
|
|
118
|
+
];
|
|
119
|
+
expect(summarizeVolatility(commits).trend).toBe("flat");
|
|
120
|
+
});
|
|
121
|
+
it("interpretation includes IV number + trend label", () => {
|
|
122
|
+
const commits = Array.from({ length: 4 }, (_, i) => cmt(`a${i}`, `2024-01-${String(i * 7 + 1).padStart(2, "0")}`, "feat: x"));
|
|
123
|
+
const r = summarizeVolatility(commits);
|
|
124
|
+
expect(r.interpretation).toMatch(/IV is/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=implied-volatility.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implied-volatility.test.js","sourceRoot":"","sources":["../../src/quant/implied-volatility.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint 5 — Wall Street meets Git.
|
|
3
|
+
*
|
|
4
|
+
* Quantitative analysis of codebase as if it were a market:
|
|
5
|
+
* • drawdown — worst losing streaks (firefighting periods)
|
|
6
|
+
* • alpha — Kelly criterion for technical-debt allocation
|
|
7
|
+
* • backtest — validate any predictor against actual outcomes
|
|
8
|
+
* • black-swan — rare-but-catastrophic file patterns
|
|
9
|
+
* • insider-trading — authors who fix bugs they introduced
|
|
10
|
+
* • moneyball — undervalued contributors (high impact, low LOC)
|
|
11
|
+
* • greek — sensitivity analysis (Δ Γ Θ)
|
|
12
|
+
* • correlation-matrix— hidden behavioral coupling between files
|
|
13
|
+
* • implied-volatility— project chaos predicted from commit message tone
|
|
14
|
+
* • tax-loss-harvest — dead code candidates that "offset" technical debt
|
|
15
|
+
*
|
|
16
|
+
* All pure analysis. No LLM. Each module is fully unit-tested.
|
|
17
|
+
*/
|
|
18
|
+
export * from "./drawdown.js";
|
|
19
|
+
export * from "./alpha.js";
|
|
20
|
+
export * from "./backtest.js";
|
|
21
|
+
export * from "./black-swan.js";
|
|
22
|
+
export * from "./insider-trading.js";
|
|
23
|
+
export * from "./moneyball.js";
|
|
24
|
+
export * from "./greek.js";
|
|
25
|
+
export * from "./correlation-matrix.js";
|
|
26
|
+
export * from "./implied-volatility.js";
|
|
27
|
+
export * from "./tax-loss-harvest.js";
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/quant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint 5 — Wall Street meets Git.
|
|
3
|
+
*
|
|
4
|
+
* Quantitative analysis of codebase as if it were a market:
|
|
5
|
+
* • drawdown — worst losing streaks (firefighting periods)
|
|
6
|
+
* • alpha — Kelly criterion for technical-debt allocation
|
|
7
|
+
* • backtest — validate any predictor against actual outcomes
|
|
8
|
+
* • black-swan — rare-but-catastrophic file patterns
|
|
9
|
+
* • insider-trading — authors who fix bugs they introduced
|
|
10
|
+
* • moneyball — undervalued contributors (high impact, low LOC)
|
|
11
|
+
* • greek — sensitivity analysis (Δ Γ Θ)
|
|
12
|
+
* • correlation-matrix— hidden behavioral coupling between files
|
|
13
|
+
* • implied-volatility— project chaos predicted from commit message tone
|
|
14
|
+
* • tax-loss-harvest — dead code candidates that "offset" technical debt
|
|
15
|
+
*
|
|
16
|
+
* All pure analysis. No LLM. Each module is fully unit-tested.
|
|
17
|
+
*/
|
|
18
|
+
export * from "./drawdown.js";
|
|
19
|
+
export * from "./alpha.js";
|
|
20
|
+
export * from "./backtest.js";
|
|
21
|
+
export * from "./black-swan.js";
|
|
22
|
+
export * from "./insider-trading.js";
|
|
23
|
+
export * from "./moneyball.js";
|
|
24
|
+
export * from "./greek.js";
|
|
25
|
+
export * from "./correlation-matrix.js";
|
|
26
|
+
export * from "./implied-volatility.js";
|
|
27
|
+
export * from "./tax-loss-harvest.js";
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/quant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mneme insider-trading` — find authors who repeatedly fix bugs they
|
|
3
|
+
* themselves introduced.
|
|
4
|
+
*
|
|
5
|
+
* The pattern tells you two things at once:
|
|
6
|
+
* 1. This person has the deepest domain knowledge (only they understand
|
|
7
|
+
* this code well enough to BOTH ship and break it).
|
|
8
|
+
* 2. They have an execution gap — knowing more isn't translating to
|
|
9
|
+
* better quality.
|
|
10
|
+
*
|
|
11
|
+
* Concrete coaching opportunity: pair them with someone who has zero
|
|
12
|
+
* defects on the same files.
|
|
13
|
+
*
|
|
14
|
+
* Pure data. Detects pairs of (commit, follow-up commit) by SAME author
|
|
15
|
+
* touching the SAME files where the follow-up is a fix.
|
|
16
|
+
*/
|
|
17
|
+
import type { Commit } from "../types.js";
|
|
18
|
+
export interface InsiderPattern {
|
|
19
|
+
/** Original commit that introduced the bug. */
|
|
20
|
+
shipped: Commit;
|
|
21
|
+
/** Same-author commit that fixed it. */
|
|
22
|
+
fixed: Commit;
|
|
23
|
+
/** Days between ship and fix. */
|
|
24
|
+
daysToFix: number;
|
|
25
|
+
/** The shared file path that triggered the match. */
|
|
26
|
+
sharedFile: string;
|
|
27
|
+
}
|
|
28
|
+
export interface InsiderProfile {
|
|
29
|
+
authorName: string;
|
|
30
|
+
authorEmail: string;
|
|
31
|
+
/** Number of self-introduced + self-fixed bug pairs. */
|
|
32
|
+
patternCount: number;
|
|
33
|
+
/** Files where the pattern repeats. */
|
|
34
|
+
affectedFiles: string[];
|
|
35
|
+
/** Sample patterns (up to 3) for display. */
|
|
36
|
+
samples: InsiderPattern[];
|
|
37
|
+
/** Risk tier — used for output labeling. */
|
|
38
|
+
tier: "high-pattern" | "elevated" | "watch" | "low";
|
|
39
|
+
/** Suggested pairing partner from another author (if any). */
|
|
40
|
+
pairSuggestion?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface InsiderTradingOptions {
|
|
43
|
+
/** Maximum days between ship and fix to count as a pattern. */
|
|
44
|
+
windowDays?: number;
|
|
45
|
+
/** Minimum patterns for an author to be flagged. */
|
|
46
|
+
minPatterns?: number;
|
|
47
|
+
/** Top-N profiles to return. */
|
|
48
|
+
topN?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect insider patterns across commits + suggest a pairing partner per
|
|
52
|
+
* flagged author. Pure analysis — caller must pre-load commits.
|
|
53
|
+
*/
|
|
54
|
+
export declare function detectInsiderTrading(commits: Commit[], opts?: InsiderTradingOptions): InsiderProfile[];
|
|
55
|
+
export declare function classifyInsiderTier(count: number): InsiderProfile["tier"];
|
|
56
|
+
//# sourceMappingURL=insider-trading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insider-trading.d.ts","sourceRoot":"","sources":["../../src/quant/insider-trading.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,6CAA6C;IAC7C,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,4CAA4C;IAC5C,IAAI,EAAE,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;IACpD,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,qBAA0B,GAC/B,cAAc,EAAE,CAkFlB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAKzE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mneme insider-trading` — find authors who repeatedly fix bugs they
|
|
3
|
+
* themselves introduced.
|
|
4
|
+
*
|
|
5
|
+
* The pattern tells you two things at once:
|
|
6
|
+
* 1. This person has the deepest domain knowledge (only they understand
|
|
7
|
+
* this code well enough to BOTH ship and break it).
|
|
8
|
+
* 2. They have an execution gap — knowing more isn't translating to
|
|
9
|
+
* better quality.
|
|
10
|
+
*
|
|
11
|
+
* Concrete coaching opportunity: pair them with someone who has zero
|
|
12
|
+
* defects on the same files.
|
|
13
|
+
*
|
|
14
|
+
* Pure data. Detects pairs of (commit, follow-up commit) by SAME author
|
|
15
|
+
* touching the SAME files where the follow-up is a fix.
|
|
16
|
+
*/
|
|
17
|
+
const FIX_RE = /\b(fix(?:es|ed)?|hotfix|bug|crashed?|broken|regression)\b/i;
|
|
18
|
+
/**
|
|
19
|
+
* Detect insider patterns across commits + suggest a pairing partner per
|
|
20
|
+
* flagged author. Pure analysis — caller must pre-load commits.
|
|
21
|
+
*/
|
|
22
|
+
export function detectInsiderTrading(commits, opts = {}) {
|
|
23
|
+
const windowDays = opts.windowDays ?? 14;
|
|
24
|
+
const minPatterns = opts.minPatterns ?? 2;
|
|
25
|
+
const topN = opts.topN ?? 10;
|
|
26
|
+
const windowMs = windowDays * 86_400_000;
|
|
27
|
+
const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
|
|
28
|
+
// Step 1: collect all patterns across ALL authors.
|
|
29
|
+
const allPatterns = [];
|
|
30
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
31
|
+
const shipped = sorted[i];
|
|
32
|
+
if (FIX_RE.test(shipped.subject))
|
|
33
|
+
continue; // skip if shipped IS itself a fix
|
|
34
|
+
const shippedTime = new Date(shipped.authorDate).getTime();
|
|
35
|
+
const shippedAuthor = `${shipped.authorName}|${shipped.authorEmail}`;
|
|
36
|
+
const shippedFiles = new Set(shipped.files ?? []);
|
|
37
|
+
if (shippedFiles.size === 0)
|
|
38
|
+
continue;
|
|
39
|
+
for (let j = i + 1; j < sorted.length; j++) {
|
|
40
|
+
const fixed = sorted[j];
|
|
41
|
+
const dt = new Date(fixed.authorDate).getTime() - shippedTime;
|
|
42
|
+
if (dt > windowMs)
|
|
43
|
+
break;
|
|
44
|
+
// Same author requirement — that's the "insider" part.
|
|
45
|
+
if (`${fixed.authorName}|${fixed.authorEmail}` !== shippedAuthor)
|
|
46
|
+
continue;
|
|
47
|
+
// Must look like a fix.
|
|
48
|
+
if (!FIX_RE.test(fixed.subject))
|
|
49
|
+
continue;
|
|
50
|
+
// Must touch a shared file.
|
|
51
|
+
const shared = (fixed.files ?? []).find((f) => shippedFiles.has(f));
|
|
52
|
+
if (!shared)
|
|
53
|
+
continue;
|
|
54
|
+
allPatterns.push({
|
|
55
|
+
shipped,
|
|
56
|
+
fixed,
|
|
57
|
+
daysToFix: Number((dt / 86_400_000).toFixed(1)),
|
|
58
|
+
sharedFile: shared,
|
|
59
|
+
});
|
|
60
|
+
break; // one pattern per shipped commit
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const byAuthor = new Map();
|
|
64
|
+
for (const p of allPatterns) {
|
|
65
|
+
const key = `${p.shipped.authorName}|${p.shipped.authorEmail}`;
|
|
66
|
+
if (!byAuthor.has(key)) {
|
|
67
|
+
byAuthor.set(key, {
|
|
68
|
+
authorName: p.shipped.authorName,
|
|
69
|
+
authorEmail: p.shipped.authorEmail,
|
|
70
|
+
patterns: [],
|
|
71
|
+
files: new Set(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const b = byAuthor.get(key);
|
|
75
|
+
b.patterns.push(p);
|
|
76
|
+
b.files.add(p.sharedFile);
|
|
77
|
+
}
|
|
78
|
+
// Step 3: build per-author profiles + pairing suggestions.
|
|
79
|
+
const profiles = [];
|
|
80
|
+
for (const b of byAuthor.values()) {
|
|
81
|
+
if (b.patterns.length < minPatterns)
|
|
82
|
+
continue;
|
|
83
|
+
const tier = classifyInsiderTier(b.patterns.length);
|
|
84
|
+
const pairSuggestion = findPairPartner(commits, b.authorName, [...b.files]);
|
|
85
|
+
profiles.push({
|
|
86
|
+
authorName: b.authorName,
|
|
87
|
+
authorEmail: b.authorEmail,
|
|
88
|
+
patternCount: b.patterns.length,
|
|
89
|
+
affectedFiles: [...b.files],
|
|
90
|
+
samples: b.patterns.slice(0, 3),
|
|
91
|
+
tier,
|
|
92
|
+
pairSuggestion,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
profiles.sort((a, b) => b.patternCount - a.patternCount);
|
|
96
|
+
return profiles.slice(0, topN);
|
|
97
|
+
}
|
|
98
|
+
export function classifyInsiderTier(count) {
|
|
99
|
+
if (count >= 5)
|
|
100
|
+
return "high-pattern";
|
|
101
|
+
if (count >= 3)
|
|
102
|
+
return "elevated";
|
|
103
|
+
if (count >= 2)
|
|
104
|
+
return "watch";
|
|
105
|
+
return "low";
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Find a pairing partner — the author who has touched the same files
|
|
109
|
+
* with ZERO insider patterns themselves. Returns the first match.
|
|
110
|
+
*/
|
|
111
|
+
function findPairPartner(commits, avoidAuthor, files) {
|
|
112
|
+
if (files.length === 0)
|
|
113
|
+
return undefined;
|
|
114
|
+
const fileSet = new Set(files);
|
|
115
|
+
// Count touches per author on the affected files.
|
|
116
|
+
const otherAuthors = new Map();
|
|
117
|
+
for (const c of commits) {
|
|
118
|
+
if (c.authorName === avoidAuthor)
|
|
119
|
+
continue;
|
|
120
|
+
if (!c.files?.some((f) => fileSet.has(f)))
|
|
121
|
+
continue;
|
|
122
|
+
otherAuthors.set(c.authorName, (otherAuthors.get(c.authorName) ?? 0) + 1);
|
|
123
|
+
}
|
|
124
|
+
if (otherAuthors.size === 0)
|
|
125
|
+
return undefined;
|
|
126
|
+
// Pick the most active OTHER author.
|
|
127
|
+
return [...otherAuthors.entries()].sort((a, b) => b[1] - a[1])[0][0];
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=insider-trading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insider-trading.js","sourceRoot":"","sources":["../../src/quant/insider-trading.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insider-trading.test.d.ts","sourceRoot":"","sources":["../../src/quant/insider-trading.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { detectInsiderTrading, classifyInsiderTier } from "./insider-trading.js";
|
|
3
|
+
const cmt = (hash, author, date, subject, files) => ({
|
|
4
|
+
hash,
|
|
5
|
+
shortHash: hash.slice(0, 7),
|
|
6
|
+
authorName: author,
|
|
7
|
+
authorEmail: `${author}@x`,
|
|
8
|
+
authorDate: `${date}T00:00:00Z`,
|
|
9
|
+
committerDate: `${date}T00:00:00Z`,
|
|
10
|
+
subject,
|
|
11
|
+
body: "",
|
|
12
|
+
parents: [],
|
|
13
|
+
files,
|
|
14
|
+
});
|
|
15
|
+
describe("classifyInsiderTier", () => {
|
|
16
|
+
it("high-pattern at 5+", () => expect(classifyInsiderTier(5)).toBe("high-pattern"));
|
|
17
|
+
it("elevated at 3-4", () => expect(classifyInsiderTier(3)).toBe("elevated"));
|
|
18
|
+
it("watch at 2", () => expect(classifyInsiderTier(2)).toBe("watch"));
|
|
19
|
+
it("low at <2", () => expect(classifyInsiderTier(1)).toBe("low"));
|
|
20
|
+
});
|
|
21
|
+
describe("detectInsiderTrading — same author + same files + fix follow-up", () => {
|
|
22
|
+
it("flags a single insider pattern (alice ships then alice fixes)", () => {
|
|
23
|
+
const commits = [
|
|
24
|
+
cmt("a1", "alice", "2024-08-01", "feat(stripe): add webhook", ["src/stripe.ts"]),
|
|
25
|
+
cmt("a2", "alice", "2024-08-03", "fix: stripe webhook crashed", ["src/stripe.ts"]),
|
|
26
|
+
];
|
|
27
|
+
const profiles = detectInsiderTrading(commits, { minPatterns: 1 });
|
|
28
|
+
expect(profiles).toHaveLength(1);
|
|
29
|
+
expect(profiles[0].authorName).toBe("alice");
|
|
30
|
+
expect(profiles[0].patternCount).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
it("does NOT flag when author is different", () => {
|
|
33
|
+
const commits = [
|
|
34
|
+
cmt("a1", "alice", "2024-08-01", "feat(stripe): add webhook", ["src/stripe.ts"]),
|
|
35
|
+
cmt("b1", "bob", "2024-08-03", "fix: stripe webhook crashed", ["src/stripe.ts"]),
|
|
36
|
+
];
|
|
37
|
+
expect(detectInsiderTrading(commits, { minPatterns: 1 })).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
it("does NOT flag when files do not overlap", () => {
|
|
40
|
+
const commits = [
|
|
41
|
+
cmt("a1", "alice", "2024-08-01", "feat: stripe", ["src/stripe.ts"]),
|
|
42
|
+
cmt("a2", "alice", "2024-08-03", "fix: orders broken", ["src/orders.ts"]),
|
|
43
|
+
];
|
|
44
|
+
expect(detectInsiderTrading(commits, { minPatterns: 1 })).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
it("respects windowDays", () => {
|
|
47
|
+
const commits = [
|
|
48
|
+
cmt("a1", "alice", "2024-08-01", "feat: stripe", ["src/x.ts"]),
|
|
49
|
+
cmt("a2", "alice", "2024-09-15", "fix: stripe broke", ["src/x.ts"]), // 45 days later
|
|
50
|
+
];
|
|
51
|
+
expect(detectInsiderTrading(commits, { windowDays: 14, minPatterns: 1 })).toEqual([]);
|
|
52
|
+
expect(detectInsiderTrading(commits, { windowDays: 60, minPatterns: 1 })).toHaveLength(1);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("detectInsiderTrading — minPatterns threshold and aggregation", () => {
|
|
56
|
+
it("requires ≥ minPatterns to flag (default 2)", () => {
|
|
57
|
+
const commits = [
|
|
58
|
+
cmt("a1", "alice", "2024-08-01", "feat: A", ["src/x.ts"]),
|
|
59
|
+
cmt("a2", "alice", "2024-08-02", "fix: A", ["src/x.ts"]),
|
|
60
|
+
];
|
|
61
|
+
expect(detectInsiderTrading(commits)).toEqual([]); // only 1 pattern, default min = 2
|
|
62
|
+
});
|
|
63
|
+
it("aggregates all patterns by author", () => {
|
|
64
|
+
const commits = [
|
|
65
|
+
cmt("a1", "alice", "2024-08-01", "feat: A", ["src/x.ts"]),
|
|
66
|
+
cmt("a2", "alice", "2024-08-02", "fix: A", ["src/x.ts"]),
|
|
67
|
+
cmt("a3", "alice", "2024-08-10", "feat: B", ["src/y.ts"]),
|
|
68
|
+
cmt("a4", "alice", "2024-08-11", "fix: B", ["src/y.ts"]),
|
|
69
|
+
cmt("a5", "alice", "2024-08-20", "feat: C", ["src/z.ts"]),
|
|
70
|
+
cmt("a6", "alice", "2024-08-22", "fix: C broke", ["src/z.ts"]),
|
|
71
|
+
];
|
|
72
|
+
const profiles = detectInsiderTrading(commits, { minPatterns: 2 });
|
|
73
|
+
expect(profiles).toHaveLength(1);
|
|
74
|
+
expect(profiles[0].patternCount).toBe(3);
|
|
75
|
+
expect(profiles[0].affectedFiles.sort()).toEqual(["src/x.ts", "src/y.ts", "src/z.ts"]);
|
|
76
|
+
});
|
|
77
|
+
it("samples are capped at 3", () => {
|
|
78
|
+
const commits = [];
|
|
79
|
+
for (let i = 0; i < 10; i++) {
|
|
80
|
+
commits.push(cmt(`f${i}`, "alice", `2024-08-${String((i * 2) + 1).padStart(2, "0")}`, `feat: ${i}`, [`f${i}.ts`]));
|
|
81
|
+
commits.push(cmt(`x${i}`, "alice", `2024-08-${String((i * 2) + 2).padStart(2, "0")}`, `fix: ${i}`, [`f${i}.ts`]));
|
|
82
|
+
}
|
|
83
|
+
const profiles = detectInsiderTrading(commits, { minPatterns: 2 });
|
|
84
|
+
expect(profiles[0].samples).toHaveLength(3);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("detectInsiderTrading — pairing suggestion", () => {
|
|
88
|
+
it("suggests another author who has touched the same files", () => {
|
|
89
|
+
const commits = [
|
|
90
|
+
cmt("a1", "alice", "2024-08-01", "feat: A", ["src/x.ts"]),
|
|
91
|
+
cmt("a2", "alice", "2024-08-02", "fix: A", ["src/x.ts"]),
|
|
92
|
+
cmt("a3", "alice", "2024-08-10", "feat: B", ["src/x.ts"]),
|
|
93
|
+
cmt("a4", "alice", "2024-08-11", "fix: B", ["src/x.ts"]),
|
|
94
|
+
cmt("b1", "bob", "2024-09-01", "refactor", ["src/x.ts"]),
|
|
95
|
+
cmt("b2", "bob", "2024-09-05", "refactor 2", ["src/x.ts"]),
|
|
96
|
+
];
|
|
97
|
+
const profile = detectInsiderTrading(commits, { minPatterns: 2 })[0];
|
|
98
|
+
expect(profile?.pairSuggestion).toBe("bob");
|
|
99
|
+
});
|
|
100
|
+
it("returns undefined pair when nobody else has touched the files", () => {
|
|
101
|
+
const commits = [
|
|
102
|
+
cmt("a1", "alice", "2024-08-01", "feat: A", ["src/solo.ts"]),
|
|
103
|
+
cmt("a2", "alice", "2024-08-02", "fix: A", ["src/solo.ts"]),
|
|
104
|
+
cmt("a3", "alice", "2024-08-10", "feat: B", ["src/solo.ts"]),
|
|
105
|
+
cmt("a4", "alice", "2024-08-11", "fix: B", ["src/solo.ts"]),
|
|
106
|
+
];
|
|
107
|
+
const profile = detectInsiderTrading(commits, { minPatterns: 2 })[0];
|
|
108
|
+
expect(profile?.pairSuggestion).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("detectInsiderTrading — sort order", () => {
|
|
112
|
+
it("sorts profiles by patternCount desc", () => {
|
|
113
|
+
const commits = [];
|
|
114
|
+
// alice — 3 patterns
|
|
115
|
+
for (let i = 0; i < 3; i++) {
|
|
116
|
+
commits.push(cmt(`a${i}`, "alice", `2024-0${i + 1}-01`, `feat: ${i}`, [`a${i}.ts`]));
|
|
117
|
+
commits.push(cmt(`af${i}`, "alice", `2024-0${i + 1}-03`, `fix: ${i}`, [`a${i}.ts`]));
|
|
118
|
+
}
|
|
119
|
+
// bob — 5 patterns
|
|
120
|
+
for (let i = 0; i < 5; i++) {
|
|
121
|
+
commits.push(cmt(`b${i}`, "bob", `2024-0${i + 1}-15`, `feat: ${i}`, [`b${i}.ts`]));
|
|
122
|
+
commits.push(cmt(`bf${i}`, "bob", `2024-0${i + 1}-17`, `fix: ${i}`, [`b${i}.ts`]));
|
|
123
|
+
}
|
|
124
|
+
const profiles = detectInsiderTrading(commits, { minPatterns: 2 });
|
|
125
|
+
expect(profiles[0].authorName).toBe("bob");
|
|
126
|
+
expect(profiles[0].patternCount).toBe(5);
|
|
127
|
+
expect(profiles[1].authorName).toBe("alice");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=insider-trading.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insider-trading.test.js","sourceRoot":"","sources":["../../src/quant/insider-trading.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mneme moneyball` — find undervalued contributors.
|
|
3
|
+
*
|
|
4
|
+
* The Billy Beane angle on engineering: in baseball, big-volume hitters
|
|
5
|
+
* get the contracts but on-base-percentage wins games. In codebases:
|
|
6
|
+
* loud LOC-per-day contributors get promoted, but the people whose
|
|
7
|
+
* commits *unblock everyone else* are the real value.
|
|
8
|
+
*
|
|
9
|
+
* Metric: each commit is "valuable" by how many SUBSEQUENT commits touch
|
|
10
|
+
* the same files. People whose commits frequently appear upstream of
|
|
11
|
+
* many others' work are unblocking. People with high LOC volume but few
|
|
12
|
+
* downstream touches are noise generators.
|
|
13
|
+
*
|
|
14
|
+
* value_score = log(downstream_commits + 1) × distinct_collaborators
|
|
15
|
+
* over_undervalued = value_score / commit_count (per-commit ROI)
|
|
16
|
+
*
|
|
17
|
+
* Pure analysis. No LLM.
|
|
18
|
+
*/
|
|
19
|
+
import type { Commit } from "../types.js";
|
|
20
|
+
export interface ContributorScore {
|
|
21
|
+
authorName: string;
|
|
22
|
+
authorEmail: string;
|
|
23
|
+
/** Total commits authored. */
|
|
24
|
+
commitCount: number;
|
|
25
|
+
/** Total downstream commits — sum of how many later commits touched the same files. */
|
|
26
|
+
downstreamReach: number;
|
|
27
|
+
/** Number of DISTINCT other authors whose work followed theirs on the same files. */
|
|
28
|
+
collaborators: number;
|
|
29
|
+
/** Aggregate value score (see formula in module doc). */
|
|
30
|
+
valueScore: number;
|
|
31
|
+
/** Per-commit ROI = valueScore / commitCount — the moneyball metric. */
|
|
32
|
+
perCommitROI: number;
|
|
33
|
+
/** Tier label. */
|
|
34
|
+
tier: "moneyball" | "balanced" | "loud" | "passive";
|
|
35
|
+
/** A 1-line interpretation. */
|
|
36
|
+
interpretation: string;
|
|
37
|
+
}
|
|
38
|
+
export interface MoneyballOptions {
|
|
39
|
+
/** Maximum days to count "downstream" relationships. Default 90. */
|
|
40
|
+
downstreamWindowDays?: number;
|
|
41
|
+
/** Minimum commits for a contributor to be ranked. Default 2. */
|
|
42
|
+
minCommits?: number;
|
|
43
|
+
/** Top-N to return. */
|
|
44
|
+
topN?: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function moneyball(commits: Commit[], opts?: MoneyballOptions): ContributorScore[];
|
|
47
|
+
export declare function classifyMoneyballTier(commitCount: number, valueScore: number, perCommitROI: number): ContributorScore["tier"];
|
|
48
|
+
//# sourceMappingURL=moneyball.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moneyball.d.ts","sourceRoot":"","sources":["../../src/quant/moneyball.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,uFAAuF;IACvF,eAAe,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB;IAClB,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,gBAAqB,GAAG,gBAAgB,EAAE,CA2E5F;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,gBAAgB,CAAC,MAAM,CAAC,CAS1B"}
|