@mneme-ai/core 0.14.0 → 0.16.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/guardian/guardian.d.ts +93 -0
- package/dist/guardian/guardian.d.ts.map +1 -0
- package/dist/guardian/guardian.js +170 -0
- package/dist/guardian/guardian.js.map +1 -0
- package/dist/guardian/guardian.test.d.ts +2 -0
- package/dist/guardian/guardian.test.d.ts.map +1 -0
- package/dist/guardian/guardian.test.js +154 -0
- package/dist/guardian/guardian.test.js.map +1 -0
- package/dist/guardian/index.d.ts +2 -0
- package/dist/guardian/index.d.ts.map +1 -0
- package/dist/guardian/index.js +2 -0
- package/dist/guardian/index.js.map +1 -0
- 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/indexer/index.d.ts +1 -0
- package/dist/indexer/index.d.ts.map +1 -1
- package/dist/indexer/index.js +1 -0
- package/dist/indexer/index.js.map +1 -1
- package/dist/indexer/quality.d.ts +56 -0
- package/dist/indexer/quality.d.ts.map +1 -0
- package/dist/indexer/quality.js +181 -0
- package/dist/indexer/quality.js.map +1 -0
- package/dist/indexer/quality.test.d.ts +2 -0
- package/dist/indexer/quality.test.d.ts.map +1 -0
- package/dist/indexer/quality.test.js +120 -0
- package/dist/indexer/quality.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +1 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +1 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/novel-scoring.d.ts +146 -0
- package/dist/retrieve/novel-scoring.d.ts.map +1 -0
- package/dist/retrieve/novel-scoring.js +256 -0
- package/dist/retrieve/novel-scoring.js.map +1 -0
- package/dist/retrieve/novel-scoring.test.d.ts +2 -0
- package/dist/retrieve/novel-scoring.test.d.ts.map +1 -0
- package/dist/retrieve/novel-scoring.test.js +221 -0
- package/dist/retrieve/novel-scoring.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mneme Guardian — the 24/7 self-healing engine.
|
|
3
|
+
*
|
|
4
|
+
* while (true) {
|
|
5
|
+
* diagnose();
|
|
6
|
+
* fix();
|
|
7
|
+
* learn();
|
|
8
|
+
* sleep(interval);
|
|
9
|
+
* }
|
|
10
|
+
*
|
|
11
|
+
* The Guardian is a long-running diagnostic + auto-remediation loop. It
|
|
12
|
+
* watches for weaknesses (regressions in index quality, drift between
|
|
13
|
+
* HEAD and the index, missing embeddings, schema-version mismatch) and
|
|
14
|
+
* threats (data corruption signals, token expiry on remote APIs, secret
|
|
15
|
+
* patterns slipping through redaction) and applies safe corrective
|
|
16
|
+
* actions automatically. Anything risky is *logged with a recommendation*
|
|
17
|
+
* rather than auto-applied.
|
|
18
|
+
*
|
|
19
|
+
* Pure data structures + dispatch — no side effects in this module
|
|
20
|
+
* (file I/O lives in the CLI command). Caller decides whether to
|
|
21
|
+
* actually apply the recommended action or just observe.
|
|
22
|
+
*
|
|
23
|
+
* Design principles:
|
|
24
|
+
* • SAFE BY DEFAULT — auto-apply only the actions that are demonstrably
|
|
25
|
+
* reversible (re-index, calibrate). Anything that could lose data
|
|
26
|
+
* gets recommended, not executed.
|
|
27
|
+
* • OBSERVABLE — every diagnosis + action is appended to a JSONL log so
|
|
28
|
+
* the operator can audit what the daemon did.
|
|
29
|
+
* • DETERMINISTIC — same inputs → same diagnosis. No flaky heuristics.
|
|
30
|
+
*/
|
|
31
|
+
import type { Commit } from "../types.js";
|
|
32
|
+
import type { IndexQualityReport } from "../indexer/quality.js";
|
|
33
|
+
export type WeaknessKind = "drift" | "missing-embeddings" | "low-quality" | "stale-calibration" | "schema-drift" | "token-expiry" | "redaction-gap";
|
|
34
|
+
export type ThreatKind = "tamper" | "secret-leak" | "outlier-author" | "deletion-storm";
|
|
35
|
+
export type Severity = "low" | "medium" | "high" | "critical";
|
|
36
|
+
export type ActionPolicy = "auto" | "recommended" | "observe";
|
|
37
|
+
export interface GuardianFinding {
|
|
38
|
+
kind: WeaknessKind | ThreatKind;
|
|
39
|
+
severity: Severity;
|
|
40
|
+
/** One-line summary for the operator. */
|
|
41
|
+
message: string;
|
|
42
|
+
/** Suggested CLI command to apply the fix (if any). */
|
|
43
|
+
suggestedAction?: string;
|
|
44
|
+
/** Whether the daemon should auto-apply. */
|
|
45
|
+
policy: ActionPolicy;
|
|
46
|
+
/** Free-form structured detail. */
|
|
47
|
+
detail?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
export interface GuardianInput {
|
|
50
|
+
/** Latest HEAD commits in git (most recent first). */
|
|
51
|
+
headCommits: Commit[];
|
|
52
|
+
/** Commits currently indexed (most recent first). */
|
|
53
|
+
indexedCommits: Commit[];
|
|
54
|
+
/** Most recent index quality report (if available). */
|
|
55
|
+
quality: IndexQualityReport | null;
|
|
56
|
+
/** Last-known good index quality (for regression detection). */
|
|
57
|
+
lastQualityScore: number | null;
|
|
58
|
+
/** Schema version recorded in the store. */
|
|
59
|
+
storeSchemaVersion: number;
|
|
60
|
+
/** Schema version expected by the running binary. */
|
|
61
|
+
expectedSchemaVersion: number;
|
|
62
|
+
/** Number of feedback events since last `mneme calibrate`. */
|
|
63
|
+
feedbackEventsSinceCalibrate: number;
|
|
64
|
+
/** Threshold above which calibrate is recommended. Default 25. */
|
|
65
|
+
feedbackCalibrateThreshold?: number;
|
|
66
|
+
/** Threshold below which low-quality is flagged (0..1). Default 0.55 (= grade C). */
|
|
67
|
+
qualityFloor?: number;
|
|
68
|
+
/** Quality regression threshold (drop in score). Default 0.1. */
|
|
69
|
+
qualityRegressionThreshold?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface GuardianReport {
|
|
72
|
+
generatedAt: string;
|
|
73
|
+
findings: GuardianFinding[];
|
|
74
|
+
summary: {
|
|
75
|
+
findings: number;
|
|
76
|
+
autoActions: number;
|
|
77
|
+
recommendations: number;
|
|
78
|
+
threats: number;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Run one diagnostic pass. Pure function — same input → same output.
|
|
83
|
+
*
|
|
84
|
+
* The CLI wraps this in `while (true)` with a sleep, but the diagnosis
|
|
85
|
+
* itself is stateless.
|
|
86
|
+
*/
|
|
87
|
+
export declare function diagnose(input: GuardianInput): GuardianReport;
|
|
88
|
+
/**
|
|
89
|
+
* Decide which findings the daemon should auto-apply this tick. Returns
|
|
90
|
+
* findings ordered by severity (high → low).
|
|
91
|
+
*/
|
|
92
|
+
export declare function selectAutoActions(report: GuardianReport): GuardianFinding[];
|
|
93
|
+
//# sourceMappingURL=guardian.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardian.d.ts","sourceRoot":"","sources":["../../src/guardian/guardian.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,oBAAoB,GACpB,aAAa,GACb,mBAAmB,GACnB,cAAc,GACd,cAAc,GACd,eAAe,CAAC;AAEpB,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,aAAa,GACb,gBAAgB,GAChB,gBAAgB,CAAC;AAErB,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,MAAM,EAAE,YAAY,CAAC;IACrB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,qDAAqD;IACrD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uDAAuD;IACvD,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8DAA8D;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,kEAAkE;IAClE,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA0H7D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,EAAE,CAU3E"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mneme Guardian — the 24/7 self-healing engine.
|
|
3
|
+
*
|
|
4
|
+
* while (true) {
|
|
5
|
+
* diagnose();
|
|
6
|
+
* fix();
|
|
7
|
+
* learn();
|
|
8
|
+
* sleep(interval);
|
|
9
|
+
* }
|
|
10
|
+
*
|
|
11
|
+
* The Guardian is a long-running diagnostic + auto-remediation loop. It
|
|
12
|
+
* watches for weaknesses (regressions in index quality, drift between
|
|
13
|
+
* HEAD and the index, missing embeddings, schema-version mismatch) and
|
|
14
|
+
* threats (data corruption signals, token expiry on remote APIs, secret
|
|
15
|
+
* patterns slipping through redaction) and applies safe corrective
|
|
16
|
+
* actions automatically. Anything risky is *logged with a recommendation*
|
|
17
|
+
* rather than auto-applied.
|
|
18
|
+
*
|
|
19
|
+
* Pure data structures + dispatch — no side effects in this module
|
|
20
|
+
* (file I/O lives in the CLI command). Caller decides whether to
|
|
21
|
+
* actually apply the recommended action or just observe.
|
|
22
|
+
*
|
|
23
|
+
* Design principles:
|
|
24
|
+
* • SAFE BY DEFAULT — auto-apply only the actions that are demonstrably
|
|
25
|
+
* reversible (re-index, calibrate). Anything that could lose data
|
|
26
|
+
* gets recommended, not executed.
|
|
27
|
+
* • OBSERVABLE — every diagnosis + action is appended to a JSONL log so
|
|
28
|
+
* the operator can audit what the daemon did.
|
|
29
|
+
* • DETERMINISTIC — same inputs → same diagnosis. No flaky heuristics.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Run one diagnostic pass. Pure function — same input → same output.
|
|
33
|
+
*
|
|
34
|
+
* The CLI wraps this in `while (true)` with a sleep, but the diagnosis
|
|
35
|
+
* itself is stateless.
|
|
36
|
+
*/
|
|
37
|
+
export function diagnose(input) {
|
|
38
|
+
const findings = [];
|
|
39
|
+
const feedbackThreshold = input.feedbackCalibrateThreshold ?? 25;
|
|
40
|
+
const qualityFloor = input.qualityFloor ?? 0.55;
|
|
41
|
+
const qualityRegression = input.qualityRegressionThreshold ?? 0.1;
|
|
42
|
+
// ── Weakness 1: index drift ────────────────────────────────────────
|
|
43
|
+
const indexedHashes = new Set(input.indexedCommits.map((c) => c.hash));
|
|
44
|
+
const driftingCommits = input.headCommits.filter((c) => !indexedHashes.has(c.hash));
|
|
45
|
+
if (driftingCommits.length > 0) {
|
|
46
|
+
const sev = driftingCommits.length > 50
|
|
47
|
+
? "high"
|
|
48
|
+
: driftingCommits.length > 10
|
|
49
|
+
? "medium"
|
|
50
|
+
: "low";
|
|
51
|
+
findings.push({
|
|
52
|
+
kind: "drift",
|
|
53
|
+
severity: sev,
|
|
54
|
+
message: `${driftingCommits.length} commit(s) on HEAD not yet indexed.`,
|
|
55
|
+
suggestedAction: "mneme index",
|
|
56
|
+
policy: "auto",
|
|
57
|
+
detail: {
|
|
58
|
+
driftingCount: driftingCommits.length,
|
|
59
|
+
headNewest: input.headCommits[0]?.hash,
|
|
60
|
+
indexNewest: input.indexedCommits[0]?.hash,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// ── Weakness 2: missing embeddings ─────────────────────────────────
|
|
65
|
+
if (input.quality) {
|
|
66
|
+
const embedRatio = input.quality.metrics.embedRatio;
|
|
67
|
+
if (embedRatio < 0.95 && input.quality.indexedChunks > 0) {
|
|
68
|
+
findings.push({
|
|
69
|
+
kind: "missing-embeddings",
|
|
70
|
+
severity: embedRatio < 0.5 ? "high" : "medium",
|
|
71
|
+
message: `${Math.round((1 - embedRatio) * 100)}% of chunks have no embedding (${input.quality.indexedChunks - input.quality.embeddedChunks} chunks).`,
|
|
72
|
+
suggestedAction: "mneme index",
|
|
73
|
+
policy: "auto",
|
|
74
|
+
detail: { embedRatio },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── Weakness 3: low overall quality ────────────────────────────────
|
|
79
|
+
if (input.quality && input.quality.overallScore < qualityFloor) {
|
|
80
|
+
findings.push({
|
|
81
|
+
kind: "low-quality",
|
|
82
|
+
severity: input.quality.overallScore < 0.4 ? "high" : "medium",
|
|
83
|
+
message: `Index quality grade ${input.quality.grade} (${Math.round(input.quality.overallScore * 100)}/100) is below recommended floor.`,
|
|
84
|
+
suggestedAction: "mneme heal",
|
|
85
|
+
policy: "recommended",
|
|
86
|
+
detail: {
|
|
87
|
+
score: input.quality.overallScore,
|
|
88
|
+
grade: input.quality.grade,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// ── Weakness 3b: quality regression ────────────────────────────────
|
|
93
|
+
if (input.quality &&
|
|
94
|
+
input.lastQualityScore !== null &&
|
|
95
|
+
input.lastQualityScore - input.quality.overallScore >= qualityRegression) {
|
|
96
|
+
findings.push({
|
|
97
|
+
kind: "low-quality",
|
|
98
|
+
severity: "medium",
|
|
99
|
+
message: `Index quality dropped from ${Math.round(input.lastQualityScore * 100)} to ${Math.round(input.quality.overallScore * 100)}.`,
|
|
100
|
+
suggestedAction: "mneme index --analyze",
|
|
101
|
+
policy: "observe",
|
|
102
|
+
detail: {
|
|
103
|
+
before: input.lastQualityScore,
|
|
104
|
+
after: input.quality.overallScore,
|
|
105
|
+
regression: input.lastQualityScore - input.quality.overallScore,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// ── Weakness 4: stale calibration ──────────────────────────────────
|
|
110
|
+
if (input.feedbackEventsSinceCalibrate >= feedbackThreshold) {
|
|
111
|
+
findings.push({
|
|
112
|
+
kind: "stale-calibration",
|
|
113
|
+
severity: "low",
|
|
114
|
+
message: `${input.feedbackEventsSinceCalibrate} feedback events since last calibrate — retrieval knobs may need re-tuning.`,
|
|
115
|
+
suggestedAction: "mneme calibrate",
|
|
116
|
+
policy: "auto",
|
|
117
|
+
detail: {
|
|
118
|
+
eventsSinceCalibrate: input.feedbackEventsSinceCalibrate,
|
|
119
|
+
threshold: feedbackThreshold,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// ── Weakness 5: schema drift ───────────────────────────────────────
|
|
124
|
+
if (input.storeSchemaVersion < input.expectedSchemaVersion) {
|
|
125
|
+
findings.push({
|
|
126
|
+
kind: "schema-drift",
|
|
127
|
+
severity: "high",
|
|
128
|
+
message: `Store schema is v${input.storeSchemaVersion}, binary expects v${input.expectedSchemaVersion}.`,
|
|
129
|
+
suggestedAction: "mneme index",
|
|
130
|
+
policy: "auto",
|
|
131
|
+
detail: {
|
|
132
|
+
storeVersion: input.storeSchemaVersion,
|
|
133
|
+
expectedVersion: input.expectedSchemaVersion,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const summary = {
|
|
138
|
+
findings: findings.length,
|
|
139
|
+
autoActions: findings.filter((f) => f.policy === "auto").length,
|
|
140
|
+
recommendations: findings.filter((f) => f.policy === "recommended").length,
|
|
141
|
+
threats: findings.filter((f) => isThreat(f.kind)).length,
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
generatedAt: new Date().toISOString(),
|
|
145
|
+
findings,
|
|
146
|
+
summary,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Decide which findings the daemon should auto-apply this tick. Returns
|
|
151
|
+
* findings ordered by severity (high → low).
|
|
152
|
+
*/
|
|
153
|
+
export function selectAutoActions(report) {
|
|
154
|
+
const order = {
|
|
155
|
+
critical: 0,
|
|
156
|
+
high: 1,
|
|
157
|
+
medium: 2,
|
|
158
|
+
low: 3,
|
|
159
|
+
};
|
|
160
|
+
return report.findings
|
|
161
|
+
.filter((f) => f.policy === "auto")
|
|
162
|
+
.sort((a, b) => order[a.severity] - order[b.severity]);
|
|
163
|
+
}
|
|
164
|
+
function isThreat(kind) {
|
|
165
|
+
return (kind === "tamper" ||
|
|
166
|
+
kind === "secret-leak" ||
|
|
167
|
+
kind === "outlier-author" ||
|
|
168
|
+
kind === "deletion-storm");
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=guardian.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/guardian/guardian.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardian.test.d.ts","sourceRoot":"","sources":["../../src/guardian/guardian.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { diagnose, selectAutoActions } from "./guardian.js";
|
|
3
|
+
function mkCommit(hash, date = "2024-01-01") {
|
|
4
|
+
return {
|
|
5
|
+
hash,
|
|
6
|
+
shortHash: hash.slice(0, 7),
|
|
7
|
+
authorName: "A",
|
|
8
|
+
authorEmail: "a@x.com",
|
|
9
|
+
authorDate: date,
|
|
10
|
+
committerDate: date,
|
|
11
|
+
subject: "x",
|
|
12
|
+
body: "",
|
|
13
|
+
files: [],
|
|
14
|
+
parents: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function mkQuality(score, embedRatio = 1) {
|
|
18
|
+
return {
|
|
19
|
+
indexedCommits: 10,
|
|
20
|
+
indexedChunks: 30,
|
|
21
|
+
embeddedChunks: Math.round(30 * embedRatio),
|
|
22
|
+
metrics: {
|
|
23
|
+
chunkDensity: 1,
|
|
24
|
+
embedRatio,
|
|
25
|
+
subjectQuality: 1,
|
|
26
|
+
bodyRatio: 1,
|
|
27
|
+
prRatio: 1,
|
|
28
|
+
issueRatio: 1,
|
|
29
|
+
duplicateRatio: 0,
|
|
30
|
+
tokenizerHealth: 1,
|
|
31
|
+
},
|
|
32
|
+
overallScore: score,
|
|
33
|
+
grade: score >= 0.85 ? "A" : score >= 0.7 ? "B" : score >= 0.55 ? "C" : score >= 0.4 ? "D" : "F",
|
|
34
|
+
recommendations: [],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const baseInput = {
|
|
38
|
+
headCommits: [],
|
|
39
|
+
indexedCommits: [],
|
|
40
|
+
quality: mkQuality(0.9),
|
|
41
|
+
lastQualityScore: 0.9,
|
|
42
|
+
storeSchemaVersion: 3,
|
|
43
|
+
expectedSchemaVersion: 3,
|
|
44
|
+
feedbackEventsSinceCalibrate: 0,
|
|
45
|
+
};
|
|
46
|
+
describe("diagnose", () => {
|
|
47
|
+
it("returns no findings when everything is healthy", () => {
|
|
48
|
+
const r = diagnose(baseInput);
|
|
49
|
+
expect(r.findings).toHaveLength(0);
|
|
50
|
+
expect(r.summary.autoActions).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
it("detects index drift when HEAD has commits not in index", () => {
|
|
53
|
+
const r = diagnose({
|
|
54
|
+
...baseInput,
|
|
55
|
+
headCommits: [mkCommit("new1"), mkCommit("new2"), mkCommit("indexed")],
|
|
56
|
+
indexedCommits: [mkCommit("indexed")],
|
|
57
|
+
});
|
|
58
|
+
const drift = r.findings.find((f) => f.kind === "drift");
|
|
59
|
+
expect(drift).toBeDefined();
|
|
60
|
+
expect(drift.policy).toBe("auto");
|
|
61
|
+
expect(drift.suggestedAction).toBe("mneme index");
|
|
62
|
+
});
|
|
63
|
+
it("escalates drift severity by count", () => {
|
|
64
|
+
const lots = Array.from({ length: 60 }, (_, i) => mkCommit("c" + i));
|
|
65
|
+
const r = diagnose({
|
|
66
|
+
...baseInput,
|
|
67
|
+
headCommits: lots,
|
|
68
|
+
indexedCommits: [],
|
|
69
|
+
});
|
|
70
|
+
const drift = r.findings.find((f) => f.kind === "drift");
|
|
71
|
+
expect(drift.severity).toBe("high");
|
|
72
|
+
});
|
|
73
|
+
it("detects missing embeddings", () => {
|
|
74
|
+
const r = diagnose({
|
|
75
|
+
...baseInput,
|
|
76
|
+
quality: mkQuality(0.9, 0.7),
|
|
77
|
+
});
|
|
78
|
+
const missing = r.findings.find((f) => f.kind === "missing-embeddings");
|
|
79
|
+
expect(missing).toBeDefined();
|
|
80
|
+
expect(missing.policy).toBe("auto");
|
|
81
|
+
});
|
|
82
|
+
it("flags low quality below floor", () => {
|
|
83
|
+
const r = diagnose({
|
|
84
|
+
...baseInput,
|
|
85
|
+
quality: mkQuality(0.4),
|
|
86
|
+
});
|
|
87
|
+
const low = r.findings.find((f) => f.kind === "low-quality");
|
|
88
|
+
expect(low).toBeDefined();
|
|
89
|
+
expect(low.policy).toBe("recommended");
|
|
90
|
+
});
|
|
91
|
+
it("flags quality regression even when above floor", () => {
|
|
92
|
+
const r = diagnose({
|
|
93
|
+
...baseInput,
|
|
94
|
+
quality: mkQuality(0.7),
|
|
95
|
+
lastQualityScore: 0.9,
|
|
96
|
+
});
|
|
97
|
+
const reg = r.findings.find((f) => f.kind === "low-quality" &&
|
|
98
|
+
f.message.toLowerCase().includes("dropped"));
|
|
99
|
+
expect(reg).toBeDefined();
|
|
100
|
+
});
|
|
101
|
+
it("recommends calibrate when feedback accumulates", () => {
|
|
102
|
+
const r = diagnose({
|
|
103
|
+
...baseInput,
|
|
104
|
+
feedbackEventsSinceCalibrate: 50,
|
|
105
|
+
});
|
|
106
|
+
const stale = r.findings.find((f) => f.kind === "stale-calibration");
|
|
107
|
+
expect(stale).toBeDefined();
|
|
108
|
+
expect(stale.policy).toBe("auto");
|
|
109
|
+
expect(stale.suggestedAction).toBe("mneme calibrate");
|
|
110
|
+
});
|
|
111
|
+
it("flags schema drift when store < expected", () => {
|
|
112
|
+
const r = diagnose({
|
|
113
|
+
...baseInput,
|
|
114
|
+
storeSchemaVersion: 2,
|
|
115
|
+
expectedSchemaVersion: 3,
|
|
116
|
+
});
|
|
117
|
+
const sd = r.findings.find((f) => f.kind === "schema-drift");
|
|
118
|
+
expect(sd).toBeDefined();
|
|
119
|
+
expect(sd.severity).toBe("high");
|
|
120
|
+
});
|
|
121
|
+
it("returns deterministic output for same input", () => {
|
|
122
|
+
const a = diagnose({
|
|
123
|
+
...baseInput,
|
|
124
|
+
headCommits: [mkCommit("a")],
|
|
125
|
+
indexedCommits: [],
|
|
126
|
+
});
|
|
127
|
+
const b = diagnose({
|
|
128
|
+
...baseInput,
|
|
129
|
+
headCommits: [mkCommit("a")],
|
|
130
|
+
indexedCommits: [],
|
|
131
|
+
});
|
|
132
|
+
expect(a.findings).toEqual(b.findings);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("selectAutoActions", () => {
|
|
136
|
+
it("returns only auto-policy findings, sorted by severity", () => {
|
|
137
|
+
const report = diagnose({
|
|
138
|
+
...baseInput,
|
|
139
|
+
headCommits: [mkCommit("new1")], // low/medium drift → auto
|
|
140
|
+
indexedCommits: [],
|
|
141
|
+
storeSchemaVersion: 2, // high schema-drift → auto
|
|
142
|
+
expectedSchemaVersion: 3,
|
|
143
|
+
quality: mkQuality(0.4), // medium low-quality → recommended (not auto)
|
|
144
|
+
feedbackEventsSinceCalibrate: 30, // low stale-calib → auto
|
|
145
|
+
});
|
|
146
|
+
const actions = selectAutoActions(report);
|
|
147
|
+
// High severity should come first
|
|
148
|
+
expect(actions[0].severity).toBe("high");
|
|
149
|
+
// No "recommended" findings should be in the auto list
|
|
150
|
+
for (const a of actions)
|
|
151
|
+
expect(a.policy).toBe("auto");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=guardian.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardian.test.js","sourceRoot":"","sources":["../../src/guardian/guardian.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guardian/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guardian/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,5 @@ export * as util from "./util/index.js";
|
|
|
10
10
|
export * as wisdom from "./wisdom/index.js";
|
|
11
11
|
export * as insights from "./insights/index.js";
|
|
12
12
|
export * as quant from "./quant/index.js";
|
|
13
|
+
export * as guardian from "./guardian/index.js";
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,5 @@ export * as util from "./util/index.js";
|
|
|
10
10
|
export * as wisdom from "./wisdom/index.js";
|
|
11
11
|
export * as insights from "./insights/index.js";
|
|
12
12
|
export * as quant from "./quant/index.js";
|
|
13
|
+
export * as guardian from "./guardian/index.js";
|
|
13
14
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
|
package/dist/indexer/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
package/dist/indexer/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index quality analyzer — answers "how good is the memory I just built?"
|
|
3
|
+
*
|
|
4
|
+
* The retrieval quality of Mneme is bounded by the quality of what was
|
|
5
|
+
* indexed. Garbage in, garbage out. This module computes a battery of
|
|
6
|
+
* metrics that surface index-level problems *before* they become bad
|
|
7
|
+
* answers.
|
|
8
|
+
*
|
|
9
|
+
* Pure data extraction — no LLM, no external services. CLI renders.
|
|
10
|
+
*
|
|
11
|
+
* Metrics computed:
|
|
12
|
+
* - coverage : # commits indexed vs # commits in git history
|
|
13
|
+
* - chunkDensity : average chunks per commit (more = richer signal)
|
|
14
|
+
* - embedRatio : fraction of chunks that have a vector embedding
|
|
15
|
+
* - subjectQuality : fraction of commits with subjects ≥ minWords
|
|
16
|
+
* - bodyRatio : fraction of commits with non-trivial bodies
|
|
17
|
+
* - prRatio : fraction of commits linked to a PR/MR
|
|
18
|
+
* - issueRatio : fraction of commits with issue refs
|
|
19
|
+
* - duplicateRatio : fraction of commits whose subject is a duplicate
|
|
20
|
+
* ("fix", "wip", "merge", etc.)
|
|
21
|
+
* - tokenizerHealth: estimate of how well tokenization is working
|
|
22
|
+
* (heuristic: ratio of chunks with ≥3 distinct tokens)
|
|
23
|
+
*
|
|
24
|
+
* Returns an overall A-F score plus per-metric details + concrete
|
|
25
|
+
* recommendations the user can act on.
|
|
26
|
+
*/
|
|
27
|
+
import type { Commit, CommitChunk } from "../types.js";
|
|
28
|
+
export interface IndexQualityReport {
|
|
29
|
+
/** Total commits in the index. */
|
|
30
|
+
indexedCommits: number;
|
|
31
|
+
/** Total chunks in the index. */
|
|
32
|
+
indexedChunks: number;
|
|
33
|
+
/** Chunks with non-empty embeddings. */
|
|
34
|
+
embeddedChunks: number;
|
|
35
|
+
/** Per-metric breakdown — values 0..1. */
|
|
36
|
+
metrics: {
|
|
37
|
+
chunkDensity: number;
|
|
38
|
+
embedRatio: number;
|
|
39
|
+
subjectQuality: number;
|
|
40
|
+
bodyRatio: number;
|
|
41
|
+
prRatio: number;
|
|
42
|
+
issueRatio: number;
|
|
43
|
+
duplicateRatio: number;
|
|
44
|
+
tokenizerHealth: number;
|
|
45
|
+
};
|
|
46
|
+
/** Overall 0..1 quality score (weighted average). */
|
|
47
|
+
overallScore: number;
|
|
48
|
+
/** Letter grade A-F derived from overallScore. */
|
|
49
|
+
grade: "A" | "B" | "C" | "D" | "F";
|
|
50
|
+
/** Human-actionable recommendations. */
|
|
51
|
+
recommendations: string[];
|
|
52
|
+
}
|
|
53
|
+
export declare function analyzeIndexQuality(commits: Commit[], chunks: CommitChunk[], opts?: {
|
|
54
|
+
minSubjectWords?: number;
|
|
55
|
+
}): IndexQualityReport;
|
|
56
|
+
//# sourceMappingURL=quality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/indexer/quality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,KAAK,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACnC,wCAAwC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAQD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,WAAW,EAAE,EACrB,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,kBAAkB,CAoHpB"}
|