@theglitchking/semantic-memory 1.0.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 (68) hide show
  1. package/.claude-plugin/marketplace.json +71 -0
  2. package/.claude-plugin/plugin.json +34 -0
  3. package/CHANGELOG.md +233 -0
  4. package/LICENSE +21 -0
  5. package/README.md +91 -0
  6. package/bin/semantic-memory +2 -0
  7. package/bin/semantic-sidekick +2 -0
  8. package/commands/healthcheck.md +6 -0
  9. package/commands/mode.md +65 -0
  10. package/commands/normalize-config.md +6 -0
  11. package/commands/policy.md +16 -0
  12. package/commands/relink.md +6 -0
  13. package/commands/status.md +6 -0
  14. package/commands/update.md +6 -0
  15. package/commands/vault.md +21 -0
  16. package/dist/chunk-25EIQWXL.js +651 -0
  17. package/dist/chunk-25EIQWXL.js.map +1 -0
  18. package/dist/chunk-CAN2GWUG.js +88 -0
  19. package/dist/chunk-CAN2GWUG.js.map +1 -0
  20. package/dist/chunk-CXTRTCIK.js +762 -0
  21. package/dist/chunk-CXTRTCIK.js.map +1 -0
  22. package/dist/chunk-E2Z4J4MK.js +180 -0
  23. package/dist/chunk-E2Z4J4MK.js.map +1 -0
  24. package/dist/chunk-HODDRXJK.js +169 -0
  25. package/dist/chunk-HODDRXJK.js.map +1 -0
  26. package/dist/chunk-JPQHNG3R.js +147 -0
  27. package/dist/chunk-JPQHNG3R.js.map +1 -0
  28. package/dist/chunk-PZ5AY32C.js +10 -0
  29. package/dist/chunk-PZ5AY32C.js.map +1 -0
  30. package/dist/cli/index.d.ts +1 -0
  31. package/dist/cli/index.js +505 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/core/embed-worker.d.ts +2 -0
  34. package/dist/core/embed-worker.js +121 -0
  35. package/dist/core/embed-worker.js.map +1 -0
  36. package/dist/core/index.d.ts +486 -0
  37. package/dist/core/index.js +62 -0
  38. package/dist/core/index.js.map +1 -0
  39. package/dist/indexer-KD6UTXZB.js +8 -0
  40. package/dist/indexer-KD6UTXZB.js.map +1 -0
  41. package/dist/lint-GMXQF4RW.js +11 -0
  42. package/dist/lint-GMXQF4RW.js.map +1 -0
  43. package/dist/log-LQXIJXIA.js +10 -0
  44. package/dist/log-LQXIJXIA.js.map +1 -0
  45. package/dist/mcp/server.d.ts +16 -0
  46. package/dist/mcp/server.js +4925 -0
  47. package/dist/mcp/server.js.map +1 -0
  48. package/dist/schema-F3RTY6SQ.js +12 -0
  49. package/dist/schema-F3RTY6SQ.js.map +1 -0
  50. package/dist/search-27WYAI24.js +202 -0
  51. package/dist/search-27WYAI24.js.map +1 -0
  52. package/docs/compat-matrix.md +127 -0
  53. package/docs/corpora-json.md +150 -0
  54. package/docs/regression-snapshot.md +122 -0
  55. package/docs/smart-middle-activation.md +97 -0
  56. package/hooks/hooks.json +39 -0
  57. package/hooks/reconcile.js +171 -0
  58. package/hooks/session-start.js +81 -0
  59. package/hooks/vault-context.js +503 -0
  60. package/package.json +97 -0
  61. package/scripts/link-skills.js +25 -0
  62. package/scripts/pre-commit-lint.sh +42 -0
  63. package/skills/outage-silence/SKILL.md +72 -0
  64. package/skills/research-mode/SKILL.md +84 -0
  65. package/skills/semantic-first/SKILL.md +117 -0
  66. package/skills/semantic-first/evals/evals.json +37 -0
  67. package/skills/semantic-first/references/vault-frontmatter.md +133 -0
  68. package/skills/vault-first/SKILL.md +100 -0
@@ -0,0 +1,12 @@
1
+ import {
2
+ installDefaultSchema,
3
+ loadSchema,
4
+ validateNote
5
+ } from "./chunk-E2Z4J4MK.js";
6
+ import "./chunk-PZ5AY32C.js";
7
+ export {
8
+ installDefaultSchema,
9
+ loadSchema,
10
+ validateNote
11
+ };
12
+ //# sourceMappingURL=schema-F3RTY6SQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,202 @@
1
+ import {
2
+ Embedder,
3
+ GraphBuilder,
4
+ TextSearch,
5
+ VectorIndex
6
+ } from "./chunk-25EIQWXL.js";
7
+ import {
8
+ Indexer
9
+ } from "./chunk-HODDRXJK.js";
10
+ import "./chunk-PZ5AY32C.js";
11
+
12
+ // src/cli/search.ts
13
+ import { join, resolve } from "path";
14
+ import { existsSync } from "fs";
15
+ import { readFile } from "fs/promises";
16
+ async function runSearch(opts) {
17
+ const notesPath = resolve(opts.notes);
18
+ if (!existsSync(notesPath)) {
19
+ throw new Error(`notes directory not found: ${notesPath}`);
20
+ }
21
+ const indexer = new Indexer(notesPath);
22
+ const indexPath = join(notesPath, ".semantic-sidekick-index");
23
+ let documents = existsSync(indexPath) ? await indexer.loadDocsCache(indexPath) : null;
24
+ if (!documents) {
25
+ documents = await indexer.indexAll();
26
+ }
27
+ if (documents.length === 0) return [];
28
+ const limit = opts.limit ?? 8;
29
+ if (opts.textOnly) {
30
+ const textSearch = new TextSearch();
31
+ textSearch.setDocuments(documents);
32
+ const keywords = extractKeywords(opts.query);
33
+ if (keywords.length === 0) return [];
34
+ const scoreByPath = /* @__PURE__ */ new Map();
35
+ for (const kw of keywords) {
36
+ const results = textSearch.search({ pattern: kw, limit: 50 });
37
+ for (const r of results) {
38
+ const existing = scoreByPath.get(r.path);
39
+ const doc = documents.find((d) => d.path === r.path);
40
+ if (existing) {
41
+ existing.hits += 1;
42
+ } else {
43
+ scoreByPath.set(r.path, {
44
+ hits: 1,
45
+ firstSnippet: r.snippet,
46
+ title: r.title,
47
+ mtime: doc?.mtime ?? ""
48
+ });
49
+ }
50
+ }
51
+ }
52
+ const hits = Array.from(scoreByPath.entries()).map(([path, v]) => ({
53
+ path,
54
+ title: v.title,
55
+ score: v.hits / keywords.length,
56
+ snippet: v.firstSnippet,
57
+ mtime: v.mtime
58
+ })).sort((a, b) => b.score - a.score).slice(0, limit);
59
+ return hits;
60
+ }
61
+ if (!existsSync(indexPath)) {
62
+ throw new Error(`index not built at ${indexPath} \u2014 run: semantic-sidekick --notes ${notesPath} --reindex`);
63
+ }
64
+ const embedder = new Embedder();
65
+ await embedder.init();
66
+ const metaPath = join(indexPath, "meta.json");
67
+ if (existsSync(metaPath)) {
68
+ try {
69
+ const meta = JSON.parse(await readFile(metaPath, "utf-8"));
70
+ if (meta.model && meta.model !== embedder.getModel()) {
71
+ throw new Error(`model mismatch (cached=${meta.model}, current=${embedder.getModel()}) \u2014 run --reindex`);
72
+ }
73
+ } catch (e) {
74
+ if (e instanceof Error && e.message.startsWith("model mismatch")) throw e;
75
+ }
76
+ }
77
+ const vectorIndex = new VectorIndex(embedder.getDimensions());
78
+ const loaded = await vectorIndex.load(indexPath);
79
+ if (!loaded) {
80
+ throw new Error(`could not load vector index at ${indexPath} \u2014 run --reindex`);
81
+ }
82
+ const graph = new GraphBuilder();
83
+ await graph.load(indexPath);
84
+ const docByPath = new Map(documents.map((d) => [d.path, d]));
85
+ const queryEmbed = await embedder.embed(opts.query);
86
+ const semantic = vectorIndex.search(queryEmbed, limit * 3);
87
+ const graphResults = graph.searchGraph(opts.query, 2);
88
+ const graphPaths = new Set(graphResults.map((r) => r.path));
89
+ const hybrid = semantic.map((r) => {
90
+ const doc = docByPath.get(r.path);
91
+ let score = graphPaths.has(r.path) ? r.score * 1.3 : r.score;
92
+ if (doc?.loadPriority !== void 0) {
93
+ score *= 1 + (doc.loadPriority - 5) * 0.04;
94
+ }
95
+ return { ...r, score };
96
+ }).sort((a, b) => b.score - a.score).slice(0, limit);
97
+ return hybrid.map((r) => {
98
+ const doc = docByPath.get(r.path);
99
+ return {
100
+ path: r.path,
101
+ title: r.title ?? doc?.title,
102
+ score: r.score,
103
+ snippet: r.snippet,
104
+ mtime: doc?.mtime
105
+ };
106
+ });
107
+ }
108
+ var STOPWORDS = /* @__PURE__ */ new Set([
109
+ "a",
110
+ "an",
111
+ "and",
112
+ "are",
113
+ "as",
114
+ "at",
115
+ "be",
116
+ "by",
117
+ "can",
118
+ "do",
119
+ "does",
120
+ "for",
121
+ "from",
122
+ "how",
123
+ "i",
124
+ "if",
125
+ "in",
126
+ "is",
127
+ "it",
128
+ "its",
129
+ "of",
130
+ "on",
131
+ "or",
132
+ "our",
133
+ "should",
134
+ "that",
135
+ "the",
136
+ "this",
137
+ "to",
138
+ "was",
139
+ "we",
140
+ "what",
141
+ "when",
142
+ "where",
143
+ "which",
144
+ "why",
145
+ "will",
146
+ "with",
147
+ "you",
148
+ "your",
149
+ "have",
150
+ "has",
151
+ "had",
152
+ "but",
153
+ "not",
154
+ "so",
155
+ "any",
156
+ "all",
157
+ "about",
158
+ "there",
159
+ "their",
160
+ "they",
161
+ "them",
162
+ "these",
163
+ "those",
164
+ "would",
165
+ "could",
166
+ "might",
167
+ "may",
168
+ "really",
169
+ "just",
170
+ "like",
171
+ "some",
172
+ "more",
173
+ "than",
174
+ "into",
175
+ "out",
176
+ "up",
177
+ "down",
178
+ "need",
179
+ "want",
180
+ "get",
181
+ "got",
182
+ "also",
183
+ "very",
184
+ "much"
185
+ ]);
186
+ function extractKeywords(query) {
187
+ const tokens = query.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).filter((t) => t.length > 2 && !STOPWORDS.has(t));
188
+ const seen = /* @__PURE__ */ new Set();
189
+ const out = [];
190
+ for (const t of tokens) {
191
+ if (!seen.has(t)) {
192
+ seen.add(t);
193
+ out.push(t);
194
+ }
195
+ if (out.length >= 6) break;
196
+ }
197
+ return out;
198
+ }
199
+ export {
200
+ runSearch
201
+ };
202
+ //# sourceMappingURL=search-27WYAI24.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/search.ts"],"sourcesContent":["import { join, resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { Indexer } from \"../core/indexer.js\";\nimport { Embedder } from \"../core/embedder.js\";\nimport { GraphBuilder } from \"../core/graph.js\";\nimport { VectorIndex } from \"../core/vector.js\";\nimport { TextSearch } from \"../core/search-text.js\";\n\nexport interface SearchCliOptions {\n notes: string;\n query: string;\n limit?: number;\n textOnly?: boolean;\n json?: boolean;\n}\n\nexport interface SearchHit {\n path: string;\n title?: string;\n score: number;\n snippet?: string;\n mtime?: string;\n}\n\nexport async function runSearch(opts: SearchCliOptions): Promise<SearchHit[]> {\n const notesPath = resolve(opts.notes);\n if (!existsSync(notesPath)) {\n throw new Error(`notes directory not found: ${notesPath}`);\n }\n\n const indexer = new Indexer(notesPath);\n const indexPath = join(notesPath, \".semantic-sidekick-index\");\n\n // Prefer the parsed-doc cache written by reindex — avoids re-parsing 500+ files\n // on every hook call. Falls back to a full parse if the cache is missing.\n let documents = existsSync(indexPath) ? await indexer.loadDocsCache(indexPath) : null;\n if (!documents) {\n documents = await indexer.indexAll();\n }\n if (documents.length === 0) return [];\n\n const limit = opts.limit ?? 8;\n\n if (opts.textOnly) {\n const textSearch = new TextSearch();\n textSearch.setDocuments(documents);\n const keywords = extractKeywords(opts.query);\n if (keywords.length === 0) return [];\n const scoreByPath = new Map<string, { hits: number; firstSnippet?: string; title?: string; mtime: string }>();\n for (const kw of keywords) {\n const results = textSearch.search({ pattern: kw, limit: 50 });\n for (const r of results) {\n const existing = scoreByPath.get(r.path);\n const doc = documents.find((d) => d.path === r.path);\n if (existing) {\n existing.hits += 1;\n } else {\n scoreByPath.set(r.path, {\n hits: 1,\n firstSnippet: r.snippet,\n title: r.title,\n mtime: doc?.mtime ?? \"\",\n });\n }\n }\n }\n const hits: SearchHit[] = Array.from(scoreByPath.entries())\n .map(([path, v]) => ({\n path,\n title: v.title,\n score: v.hits / keywords.length,\n snippet: v.firstSnippet,\n mtime: v.mtime,\n }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n return hits;\n }\n\n // Hybrid path: load cached index, embed query, search + graph rerank.\n if (!existsSync(indexPath)) {\n throw new Error(`index not built at ${indexPath} — run: semantic-sidekick --notes ${notesPath} --reindex`);\n }\n\n const embedder = new Embedder();\n await embedder.init();\n\n const metaPath = join(indexPath, \"meta.json\");\n if (existsSync(metaPath)) {\n try {\n const meta = JSON.parse(await readFile(metaPath, \"utf-8\")) as { model?: string };\n if (meta.model && meta.model !== embedder.getModel()) {\n throw new Error(`model mismatch (cached=${meta.model}, current=${embedder.getModel()}) — run --reindex`);\n }\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"model mismatch\")) throw e;\n }\n }\n\n const vectorIndex = new VectorIndex(embedder.getDimensions());\n const loaded = await vectorIndex.load(indexPath);\n if (!loaded) {\n throw new Error(`could not load vector index at ${indexPath} — run --reindex`);\n }\n\n const graph = new GraphBuilder();\n await graph.load(indexPath);\n\n const docByPath = new Map(documents.map((d) => [d.path, d]));\n const queryEmbed = await embedder.embed(opts.query);\n const semantic = vectorIndex.search(queryEmbed, limit * 3);\n const graphResults = graph.searchGraph(opts.query, 2);\n const graphPaths = new Set(graphResults.map((r) => r.path));\n\n const hybrid = semantic\n .map((r) => {\n const doc = docByPath.get(r.path);\n let score = graphPaths.has(r.path) ? r.score * 1.3 : r.score;\n if (doc?.loadPriority !== undefined) {\n score *= 1 + (doc.loadPriority - 5) * 0.04;\n }\n return { ...r, score };\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n\n return hybrid.map((r) => {\n const doc = docByPath.get(r.path);\n return {\n path: r.path,\n title: (r as any).title ?? doc?.title,\n score: r.score,\n snippet: (r as any).snippet,\n mtime: doc?.mtime,\n };\n });\n}\n\nconst STOPWORDS = new Set([\n \"a\", \"an\", \"and\", \"are\", \"as\", \"at\", \"be\", \"by\", \"can\", \"do\", \"does\", \"for\", \"from\",\n \"how\", \"i\", \"if\", \"in\", \"is\", \"it\", \"its\", \"of\", \"on\", \"or\", \"our\", \"should\", \"that\",\n \"the\", \"this\", \"to\", \"was\", \"we\", \"what\", \"when\", \"where\", \"which\", \"why\", \"will\",\n \"with\", \"you\", \"your\", \"have\", \"has\", \"had\", \"but\", \"not\", \"so\", \"any\", \"all\",\n \"about\", \"there\", \"their\", \"they\", \"them\", \"these\", \"those\", \"would\", \"could\",\n \"might\", \"may\", \"really\", \"just\", \"like\", \"some\", \"more\", \"than\", \"into\", \"out\",\n \"up\", \"down\", \"need\", \"want\", \"get\", \"got\", \"also\", \"very\", \"much\",\n]);\n\nfunction extractKeywords(query: string): string[] {\n const tokens = query\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \" \")\n .split(/\\s+/)\n .filter((t) => t.length > 2 && !STOPWORDS.has(t));\n const seen = new Set<string>();\n const out: string[] = [];\n for (const t of tokens) {\n if (!seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n if (out.length >= 6) break;\n }\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,127 @@
1
+ ---
2
+ title: TheGlitchKing stack compatibility matrix
3
+ tier: reference
4
+ domains:
5
+ - architecture
6
+ - distribution
7
+ status: active
8
+ last_updated: 2026-05-07
9
+ version: 1.0.0
10
+ audience:
11
+ - developers
12
+ - admin
13
+ purpose: Canonical compatibility matrix for the TheGlitchKing memory-layer stack. Defines which combinations of underlying plugin versions are tested together and ship as a coherent set under each `dev-stack` meta-plugin version. This is the single source of truth referenced by every plugin's README.
14
+ ---
15
+
16
+ # TheGlitchKing stack compatibility matrix
17
+
18
+ This is **the** compatibility matrix for the TheGlitchKing memory-layer + planning + docs + translation stack. It documents which combinations of underlying plugin versions are tested together and ship as a coherent set under each `dev-stack` meta-plugin version.
19
+
20
+ The matrix lives in `semantic-memory` (this repo) because semantic-memory is the runtime that consumes the other plugins as either:
21
+ - producers writing into corpora (persistent-planning → `plans`, babel-fish → `project-map`, hit-em-with-the-docs → `docs`)
22
+ - a frontmatter standard the runtime validates against (HEWTD)
23
+
24
+ Every other plugin's README cross-references this doc as the canonical source.
25
+
26
+ ## Active matrix
27
+
28
+ ### `dev-stack@1.0.x` (current)
29
+
30
+ | Plugin | Required range | Tested at |
31
+ |---|---|---|
32
+ | `@theglitchking/semantic-memory` | `^1.0.0` | 1.0.0 (Phase 2.0.0a — rebrand mechanics, runtime equivalent to semantic-sidekick 0.2.5) |
33
+ | `@theglitchking/hit-em-with-the-docs` | `~2.2.0` | 2.2.0 (Phase 1.1.0 — `tier: "plan"` + conditional `version`) |
34
+ | `@theglitchking/persistent-planning` | `^3.0.0` | 3.0.0 (Phase 1.2.0/1.3.0 — sm/lg modes + layered phase/task/atom/notes) |
35
+ | `@theglitchking/babel-fish` | `^2.0.0` | 2.0.0 (existing release; integration with semantic-memory project-map corpus + glossary side-channel ships in Phase 3.1.0) |
36
+
37
+ #### Why these ranges
38
+ - **semantic-memory `^1.0.0`**: any 1.x is API-compatible. Multi-corpus refactor (Phase 2.0.0b) lands as 1.1.0; will not break the 1.0 tool surface (regression-snapshot suite gates this).
39
+ - **hit-em-with-the-docs `~2.2.0`**: the `tier: "plan"` extension is the precise feature persistent-planning depends on. Tilde-range allows patch-level updates (2.2.x) without accepting a 2.3.0 that might re-shape the schema. Will widen to `^2.2.0` once 2.3 is known to be additive.
40
+ - **persistent-planning `^3.0.0`**: any 3.x preserves the lg-mode contract (phase / task / atom / notes layers, HEWTD frontmatter, sm-mode preservation).
41
+ - **babel-fish `^2.0.0`**: existing 2.0 release. Integration uses babel-fish's existing 19-section project-map output unchanged — semantic-memory consumes the artifact, doesn't drive babel-fish runtime.
42
+
43
+ #### What's tested together
44
+ - Each underlying plugin has its own test suite (preserved bit-for-bit through the rebrand)
45
+ - semantic-memory regression-snapshot suite (`test/regression/`) gates the 33-tool surface across all of these
46
+ - Phase 4.4.0 (persistent-planning testing) verifies sm + lg flows with semantic-memory present and absent
47
+ - Phase 4.5.0 (babel-fish testing) verifies the glossary contract end-to-end with semantic-memory's translation verbs
48
+
49
+ #### What's NOT tested together (documented gaps)
50
+ - Multi-corpus runtime is preview-documented in semantic-memory 1.0.0a but not implemented; full integration tests with all six corpora active land in Phase 4.2.0 once 2.0.0b ships
51
+ - Drift detection and translation verbs are not part of the 1.0 surface; they ship in Phase 3.1.0 / 3.3.0
52
+ - pages-compat mode (1:1 surface mapping for semantic-pages migrators) ships in 2.0.0b
53
+
54
+ ## Future matrix (planned)
55
+
56
+ ### `dev-stack@1.1.x` (after semantic-memory 1.1.0 ships with multi-corpus)
57
+
58
+ | Plugin | Range |
59
+ |---|---|
60
+ | `semantic-memory` | `^1.1.0` |
61
+ | `hit-em-with-the-docs` | `~2.2.0` (unchanged) |
62
+ | `persistent-planning` | `^3.0.0` (unchanged) |
63
+ | `babel-fish` | `^2.0.0` (unchanged) |
64
+
65
+ When 1.1 ships, dev-stack pin tightens to `^1.1.0` to ensure consumers get the multi-corpus runtime.
66
+
67
+ ### `dev-stack@1.2.x` (after Phase 3.1.0 — babel-fish glossary integration)
68
+
69
+ When semantic-memory adds the `project-map` corpus + translation verbs (3.1.0 of the meta-plan = a minor of semantic-memory), babel-fish constraint may tighten to a specific minor that ships the glossary side-channel contract changes (TBD — could be 2.1.0 or 2.0.0 if the existing 19-section output already meets the contract).
70
+
71
+ ### `dev-stack@2.0.x` (after Phase 3.2.0 / 3.3.0 — code corpus + drift)
72
+
73
+ Major bump if the drift-detection feature changes the way users author HEWTD docs (e.g. requires a new field). Otherwise stays at 1.x.
74
+
75
+ ## Coexistence with deprecated plugins
76
+
77
+ | Plugin | Status | Coexists with dev-stack? |
78
+ |---|---|---|
79
+ | `semantic-sidekick` | Renamed to `semantic-memory` at 1.0.0a (PR #9) | Don't install both — semantic-memory IS the new name. Migrate via uninstall + install. |
80
+ | `semantic-pages` | Deprecated as of 2026-05-07 (PR #5) | Yes — different MCP key (`semantic-vault` vs `semantic-memory`). Both can run side-by-side during the 12-month sunset. |
81
+
82
+ ## How to verify your install matches this matrix
83
+
84
+ ```bash
85
+ # Check the meta-plugin version
86
+ /plugin list --installed | grep dev-stack
87
+
88
+ # Check each underlying plugin's version
89
+ /plugin list --installed | grep -E "semantic-memory|hit-em-with-the-docs|persistent-planning|babel-fish"
90
+ ```
91
+
92
+ Or via npm:
93
+
94
+ ```bash
95
+ npm ls @theglitchking/semantic-memory @theglitchking/hit-em-with-the-docs @theglitchking/persistent-planning @theglitchking/babel-fish
96
+ ```
97
+
98
+ If any version falls outside the range listed above for your `dev-stack` version, you've drifted — either bump dev-stack or pin the underlying plugin manually in your `.mcp.json` / `package.json`.
99
+
100
+ ## Inter-PR coordination history
101
+
102
+ This matrix was derived from the unified memory-layer plan tracked in `~/workspace/the-glitch-kingdom/persistent-planning/.planning/layered-planning-with-mcp-and-hewtd-frontmatter/task_plan.md`. Initial entries reflect the PRs landing in the May 2026 launch window:
103
+
104
+ | PR | Repo | Status | Lands |
105
+ |---|---|---|---|
106
+ | #7, #8 | semantic-sidekick | Merged to main | offline packaging, regression baseline |
107
+ | #3 | hit-em-with-the-docs | Open | HEWTD 2.2.0 — `tier: "plan"` + conditional version |
108
+ | #1 | persistent-planning | Open | persistent-planning 3.0.0 — sm/lg + layered model |
109
+ | #9 | semantic-sidekick | Open | semantic-memory 1.0.0a rebrand mechanics |
110
+ | #5 | semantic-pages | Open | deprecation announcement |
111
+ | (new repo) | dev-stack | Created private; pending visibility flip + publish | meta-plugin 1.0.0 |
112
+
113
+ ## Updating this matrix
114
+
115
+ When a new minor of any underlying plugin ships:
116
+ 1. Verify it satisfies the existing range (e.g. semantic-memory 1.1.0 satisfies `^1.0.0`)
117
+ 2. If it does AND the integration is tested → no matrix change needed
118
+ 3. If it changes behavior in a way users should opt into → bump dev-stack minor and tighten the constraint
119
+ 4. If it requires breaking changes in another plugin → bump dev-stack major
120
+
121
+ The matrix is hand-maintained. Automation (a CI job that pins from npm latest) is a future improvement.
122
+
123
+ ## See also
124
+
125
+ - `~/workspace/the-glitch-kingdom/dev-stack/README.md` — meta-plugin README with install / upgrade flow
126
+ - `~/workspace/the-glitch-kingdom/persistent-planning/.planning/layered-planning-with-mcp-and-hewtd-frontmatter/task_plan.md` — Phase 4.14.0 (cross-repo coordination)
127
+ - Each underlying plugin's CHANGELOG for what shipped in the bumps reflected here
@@ -0,0 +1,150 @@
1
+ ---
2
+ title: corpora.json schema (preview, ships in 2.0.0b)
3
+ tier: standard
4
+ domains:
5
+ - architecture
6
+ - configuration
7
+ status: draft
8
+ last_updated: 2026-05-07
9
+ version: 1.0.0
10
+ audience:
11
+ - developers
12
+ purpose: Preview the multi-corpus configuration file that semantic-memory will read in v2.0.0b. Documents the schema, defaults, and smart-middle activation contract before the implementation lands.
13
+ ---
14
+
15
+ # `corpora.json` schema (preview)
16
+
17
+ > **Status: planned for 2.0.0b.** This file documents the design of `corpora.json` so consumers and downstream plugins (persistent-planning, babel-fish) can plan against it. The implementation lands in the next release. v1.0.0 (this release) preserves the legacy single-vault behavior unchanged.
18
+
19
+ `corpora.json` is the configuration file that tells semantic-memory which corpora to index and how. It lives at `<project-root>/.semantic/corpora.json` and is auto-bootstrapped on first run by the **smart-middle activation** heuristic.
20
+
21
+ ## Schema
22
+
23
+ ```json
24
+ {
25
+ "schema_version": "1.0",
26
+ "corpora": [
27
+ {
28
+ "name": "vault",
29
+ "root": "./.claude/.vault",
30
+ "glob": "**/*.md",
31
+ "chunker": "markdown",
32
+ "enabled": true
33
+ },
34
+ {
35
+ "name": "code",
36
+ "root": "./src",
37
+ "glob": "**/*.{ts,tsx,js,jsx}",
38
+ "chunker": "tree-sitter-ts",
39
+ "enabled": true
40
+ },
41
+ {
42
+ "name": "plans",
43
+ "root": "./.planning",
44
+ "glob": "**/*.md",
45
+ "chunker": "markdown",
46
+ "enabled": true
47
+ },
48
+ {
49
+ "name": "docs",
50
+ "root": "./.documentation",
51
+ "glob": "**/*.md",
52
+ "chunker": "markdown",
53
+ "enabled": true
54
+ },
55
+ {
56
+ "name": "research",
57
+ "root": "./.research",
58
+ "glob": "**/*.md",
59
+ "chunker": "markdown",
60
+ "enabled": true
61
+ },
62
+ {
63
+ "name": "project-map",
64
+ "root": "./.babel-fish",
65
+ "glob": "**/*.md",
66
+ "chunker": "markdown",
67
+ "enabled": true,
68
+ "extras": {
69
+ "glossary_extractor": "babel-fish-vocabulary"
70
+ }
71
+ }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ ## Field reference
77
+
78
+ ### Top level
79
+
80
+ | Field | Type | Required | Description |
81
+ |---|---|---|---|
82
+ | `schema_version` | string | yes | Schema version of this file. Currently `"1.0"`. |
83
+ | `corpora` | array of corpus objects | yes | One entry per corpus to register. |
84
+
85
+ ### Per corpus
86
+
87
+ | Field | Type | Required | Description |
88
+ |---|---|---|---|
89
+ | `name` | string | yes | Corpus identifier. Used as the suffix for per-corpus search verbs (`search_<name>`). Reserved names: `vault`, `code`, `plans`, `docs`, `research`, `project-map`. Custom names allowed. |
90
+ | `root` | string | yes | Directory to scan for content. Relative to project root. |
91
+ | `glob` | string | yes | File pattern (glob syntax) for files to index within `root`. |
92
+ | `chunker` | string | yes | Chunker to use. Built-in: `markdown`, `tree-sitter-ts`, `fixed-window`. |
93
+ | `enabled` | boolean | no (default `true`) | If `false`, the corpus is registered but not indexed; search verbs return `{status: "no_index", results: []}`. |
94
+ | `extras` | object | no | Chunker-specific or corpus-specific configuration. E.g. `glossary_extractor: "babel-fish-vocabulary"` activates the babel-fish glossary side-channel for the project-map corpus. |
95
+
96
+ ## Smart-middle activation
97
+
98
+ On first run (when `<project-root>/.semantic/` doesn't exist), semantic-memory scans the project root for known markers:
99
+
100
+ | Marker | Activates corpus |
101
+ |---|---|
102
+ | `.claude/.vault/` | `vault` |
103
+ | `src/` (or detected source dir) with TS/JS files | `code` |
104
+ | `.planning/` with `.meta/workspace.json` (lg mode) | `plans` |
105
+ | `.documentation/` | `docs` |
106
+ | `.research/` | `research` |
107
+ | `.babel-fish/` with `01-vocabulary.md` | `project-map` |
108
+
109
+ For each marker found, the corresponding corpus is added to `corpora.json` with `enabled: true`. Markers not found result in the corpus being omitted (or added with `enabled: false` for visibility — TBD).
110
+
111
+ A decision banner is printed on first activation listing what got enabled and why.
112
+
113
+ ## Backwards compatibility
114
+
115
+ For projects that have an existing `.claude/.vault/` and never ran v1.0.0+ before, the smart-middle activation creates a `corpora.json` with just `vault` enabled, pointing at the existing `.claude/.vault/` directory. **The existing `.semantic-sidekick-index/` directory continues to be the index location** (preserved for backwards compatibility).
116
+
117
+ For projects that ARE v1.0.0a-only (no `corpora.json`), the legacy single-vault flow continues to work without any `corpora.json`. The auto-default reads `notes_path` from CLI args or environment, exactly as v0.x did.
118
+
119
+ ## Per-corpus search verbs (planned for 2.0.0b)
120
+
121
+ Each registered corpus produces a corresponding `search_<name>` MCP verb:
122
+
123
+ - `search_vault(query, limit?, ...filters)` — equivalent to today's `search_semantic` for the vault corpus
124
+ - `search_code(query, language?, symbol_kind?, pathGlob?)` — when code corpus is active
125
+ - `search_plans(query, ...filters)` — when plans corpus is active
126
+ - `search_docs(query, ...filters)` — when docs corpus is active
127
+ - `search_research(query, ...filters)` — when research corpus is active
128
+ - `search_project_map(query, ...filters)` — when project-map corpus is active
129
+
130
+ Plus a cross-corpus verb:
131
+
132
+ - `search_all(query, corpus_filter?, limit?)` — searches across all enabled corpora and merges results
133
+
134
+ Empty / missing corpora always return `{status: "no_index", results: []}` rather than erroring (the `no_index` contract). This means the agent can always attempt a search and learn from the empty result.
135
+
136
+ ## Conditional tool registration
137
+
138
+ Workflow tools register only when their target corpus has data:
139
+
140
+ - Plan workflow tools (`next_atom`, `update_atom_status`, ...) register iff `plans` corpus is active AND has data
141
+ - Drift detection tools (`detect_drift`) register iff `code` corpus + at least one doc-bearing corpus are both active
142
+ - Translation tools (`translate`, `reverse_translate`, `list_vocabulary`) register iff `project-map` corpus is active AND `glossary.json` is present
143
+
144
+ Search tools always register and use the `no_index` contract for empty corpora.
145
+
146
+ ## See also
147
+
148
+ - `docs/smart-middle-activation.md` — full smart-middle resolution rules
149
+ - `docs/regression-snapshot.md` — the gate that protects existing tool surface during the multi-corpus refactor
150
+ - `~/workspace/the-glitch-kingdom/persistent-planning/.planning/layered-planning-with-mcp-and-hewtd-frontmatter/task_plan.md` — the meta-plan
@@ -0,0 +1,122 @@
1
+ ---
2
+ title: Regression-Snapshot Suite
3
+ tier: standard
4
+ domains:
5
+ - testing
6
+ - operational
7
+ status: active
8
+ last_updated: 2026-05-07
9
+ version: 1.0.0
10
+ audience:
11
+ - developers
12
+ purpose: Document the regression-snapshot suite, when it fails, how to interpret a failure, and the policy for updating snapshots intentionally.
13
+ ---
14
+
15
+ # Regression-Snapshot Suite
16
+
17
+ The regression suite under `test/regression/` is the **structural stability gate** for semantic-sidekick. It captures the shape of the MCP tool surface — every tool's name, description, input schema, and representative output structure — as vitest snapshots that fail loudly on any unintentional change.
18
+
19
+ This is the load-bearing baseline for the **semantic-memory rebrand + multi-corpus refactor** (Phase 1.0.0 of the unified memory layer plan). The contract: every snapshot in this suite must remain unchanged through the rebrand unless an intentional break is documented and signed off.
20
+
21
+ ## What the suite covers
22
+
23
+ ### `test/regression/tool-surface.test.ts` — input contract
24
+
25
+ Three snapshots:
26
+
27
+ | Snapshot | Captures | Size |
28
+ |---|---|---|
29
+ | `tool-surface 1` | Full list of registered tools — name, description, complete JSON Schema for inputs | ~1000 lines |
30
+ | `tool-name list (compact) 1` | Sorted list of tool names in default mode (33 entries) | ~35 lines |
31
+ | `tool-name list in read-only mode 1` | Sorted list of tool names in `--read-only` mode (21 entries) | ~22 lines |
32
+
33
+ Plus structural assertions:
34
+ - Default mode exposes exactly 33 tools
35
+ - `--read-only` mode exposes exactly 21 tools
36
+ - Read-only mode never exposes any of the 12 write tools (explicit name list)
37
+
38
+ ### `test/regression/tool-outputs.test.ts` — output contract
39
+
40
+ 16 snapshots covering response shapes for:
41
+ - **Deterministic read tools**: `read_note`, `read_multiple_notes`, `list_notes`, `get_frontmatter`, `backlinks`, `forwardlinks`, `graph_path`, `graph_statistics`, `get_stats`
42
+ - **Lint tools**: `lint_vault` (rule-name structure)
43
+ - **Embedding-dependent tools** (structural only — see below): `search_semantic`, `search_text`, `search_hybrid`, `search_graph`
44
+
45
+ ## Embedding-dependent vs deterministic snapshots
46
+
47
+ Search tools that route through the embedder produce slightly different scores across runs (within a tolerance). Snapshotting their full output would create false-positive failures.
48
+
49
+ The suite handles this with two strategies:
50
+
51
+ 1. **Strip volatile fields**: `mtime`, `lastIndexed`, embedding scores are removed (or reduced to presence-checks) before snapshotting.
52
+ 2. **Shape-only snapshots**: For `search_semantic` / `search_hybrid` / `search_graph`, the snapshot captures only the *keys present* in a result, not the values. The shape regresses if a field is added or removed; embedding score drift never breaks the test.
53
+
54
+ ## When a snapshot fails
55
+
56
+ A regression-snapshot failure means the tool surface or output shape changed since the snapshot was last written. There are three responses:
57
+
58
+ ### 1. The change is unintentional → fix the regression
59
+
60
+ This is the default assumption during the semantic-memory rebrand. Investigate what changed, restore the previous behavior. Do not update the snapshot.
61
+
62
+ ### 2. The change is intentional and documented
63
+
64
+ Examples:
65
+ - Adding a new MCP tool (the surface snapshot will gain an entry)
66
+ - Extending an existing tool's input schema (the surface snapshot will show the new field)
67
+ - Adding a field to a tool's output (the output snapshot will show the new key)
68
+
69
+ Process:
70
+ 1. Confirm the change is intentional and matches the published plan
71
+ 2. Update snapshots: `npm test -- -u test/regression/`
72
+ 3. Review the diff in the snapshot file as part of the PR
73
+ 4. Document the intentional break in the relevant CHANGELOG entry (semantic-memory CHANGELOG, probably under the version that introduces it)
74
+ 5. Sign-off in the PR description: explicit acknowledgment that this is a deliberate break
75
+
76
+ ### 3. The change reflects an upgrade to the embedding model or chunker
77
+
78
+ If the embedding model changes (e.g., MiniLM-L6 → BGE-m3) or the chunker changes (markdown → tree-sitter for some corpus), the deterministic snapshots may legitimately need to update. Treat as case 2 — explicit, documented, signed off. Note the model/chunker change in the CHANGELOG.
79
+
80
+ ## Updating snapshots
81
+
82
+ ```bash
83
+ # Update only the regression suite
84
+ npm test -- -u test/regression/
85
+
86
+ # Update everything (use sparingly)
87
+ npm test -- -u
88
+ ```
89
+
90
+ Always inspect the resulting diff before committing. A snapshot diff that touches more than the file you changed is a signal that something else regressed.
91
+
92
+ ## Capture process (how the baseline was created)
93
+
94
+ Captured 2026-05-07 BEFORE any sidekick code changes for the semantic-memory rebrand. Three commits established the baseline:
95
+
96
+ 1. `test(regression): golden snapshots for MCP tool surface` — input contracts (3 snapshots, 6 tests)
97
+ 2. `test(regression): golden snapshots for MCP tool output shapes` — output contracts (16 snapshots, 16 tests)
98
+ 3. `docs(regression): document the snapshot suite` — this file
99
+
100
+ Combined: 19 snapshots / 22 regression tests / 0 failures at baseline.
101
+
102
+ The full sidekick suite at baseline: 211 tests across 26 files, 21.85s.
103
+
104
+ ## Performance baseline (separate, deferred)
105
+
106
+ A performance baseline (query latency p50/p95, index size per chunk count) is mentioned in the regression plan but is captured separately by `scripts/regression-perf-baseline.js` (TBD). It is **informational, not gated** — a 10% performance budget will be enforced in Phase 4.2.0, not now.
107
+
108
+ ## Suite lifecycle
109
+
110
+ The regression suite is a **gate during refactoring**. After the semantic-memory rebrand and multi-corpus refactor are complete, the suite should be re-evaluated:
111
+
112
+ - Snapshots that captured `--read-only` 21-tool surface may need to evolve to reflect new conditional tool registration (workflow / drift / translation verbs).
113
+ - Output snapshots may need to evolve as new corpora come online and result shapes grow corpus tags.
114
+ - The suite itself should evolve from "lock current behavior" to "verify multi-corpus behavior remains stable as new corpora are added."
115
+
116
+ That evolution happens at Phase 4.2.0 (per the plan). Until then: this suite is the immutable baseline.
117
+
118
+ ## Related
119
+
120
+ - Plan: `~/workspace/the-glitch-kingdom/persistent-planning/.planning/layered-planning-with-mcp-and-hewtd-frontmatter/task_plan.md`
121
+ - Companion behavior tests: `test/integration/mcp-server.test.ts`, `test/e2e/stdio-server.test.ts`
122
+ - Vitest snapshot docs: https://vitest.dev/guide/snapshot.html