nlm-memory 0.15.0 → 0.18.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 (46) hide show
  1. package/dist/cli/nlm.js +39 -1
  2. package/dist/cli/nlm.js.map +1 -1
  3. package/dist/core/classifier/hierarchical-classify.d.ts +5 -0
  4. package/dist/core/classifier/hierarchical-classify.js +53 -0
  5. package/dist/core/classifier/hierarchical-classify.js.map +1 -0
  6. package/dist/core/hook/pointer-block.d.ts +7 -1
  7. package/dist/core/hook/pointer-block.js +15 -3
  8. package/dist/core/hook/pointer-block.js.map +1 -1
  9. package/dist/core/ingest/reclassify-oversized.d.ts +39 -0
  10. package/dist/core/ingest/reclassify-oversized.js +104 -0
  11. package/dist/core/ingest/reclassify-oversized.js.map +1 -0
  12. package/dist/core/recall/recall-service.d.ts +7 -0
  13. package/dist/core/recall/recall-service.js +31 -0
  14. package/dist/core/recall/recall-service.js.map +1 -1
  15. package/dist/core/recall/related-exemplars.d.ts +14 -0
  16. package/dist/core/recall/related-exemplars.js +25 -0
  17. package/dist/core/recall/related-exemplars.js.map +1 -0
  18. package/dist/core/storage/pg-code-exemplar-store.d.ts +2 -1
  19. package/dist/core/storage/pg-code-exemplar-store.js +28 -2
  20. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  21. package/dist/core/storage/sqlite-code-exemplar-store.d.ts +2 -1
  22. package/dist/core/storage/sqlite-code-exemplar-store.js +28 -2
  23. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  24. package/dist/hook/prompt-recall-hook.d.ts +2 -1
  25. package/dist/hook/prompt-recall-hook.js +1 -1
  26. package/dist/hook/prompt-recall-hook.js.map +1 -1
  27. package/dist/hook/recall-over-http.d.ts +5 -1
  28. package/dist/hook/recall-over-http.js +14 -5
  29. package/dist/hook/recall-over-http.js.map +1 -1
  30. package/dist/http/app.js +40 -2
  31. package/dist/http/app.js.map +1 -1
  32. package/dist/mcp/server.js +22 -0
  33. package/dist/mcp/server.js.map +1 -1
  34. package/dist/ports/code-exemplar-store.d.ts +15 -1
  35. package/dist/shared/types.d.ts +30 -0
  36. package/migrations/023_code_exemplars_supersedence.sql +10 -0
  37. package/migrations/pg/001_initial.sql +3 -1
  38. package/nlm/index.js +24 -11
  39. package/package.json +1 -1
  40. package/plugin/.codex-plugin/plugin.json +1 -1
  41. package/plugin/scripts/prompt-recall-hook.mjs +24 -11
  42. package/plugin/scripts/session-start-hook.mjs +12 -5
  43. package/migrations/pg/018_repair_self_supersede.sql +0 -16
  44. package/migrations/pg/020_collapse_duplicate_active_facts.sql +0 -46
  45. package/migrations/pg/021_code_exemplars.sql +0 -45
  46. package/migrations/pg/022_facts_retired_at.sql +0 -12
@@ -7,7 +7,15 @@
7
7
  * vec lane is optional — inserts succeed without an embedding; search
8
8
  * requires one.
9
9
  */
10
- import type { CodeExemplar, CodeExemplarHit, CodeExemplarInput } from "../shared/types.js";
10
+ import type { CodeExemplar, CodeExemplarHit, CodeExemplarInput, CodeExemplarOutcome } from "../shared/types.js";
11
+ export type ExemplarVerdictSource = "llm" | "human";
12
+ export interface ExemplarVerdictPatch {
13
+ readonly retired?: boolean;
14
+ readonly outcome?: CodeExemplarOutcome;
15
+ }
16
+ export interface ExemplarVerdictResult {
17
+ readonly status: "applied" | "not_found" | "human_locked";
18
+ }
11
19
  export interface CodeExemplarSearchFilter {
12
20
  readonly installScope: string;
13
21
  readonly repo?: string;
@@ -42,4 +50,10 @@ export interface CodeExemplarStore {
42
50
  pruneReverted(installScope: string): Promise<number>;
43
51
  /** Delete exemplars with ts < olderThanTs (optional clock-based escape hatch). */
44
52
  pruneOlderThan(olderThanTs: string): Promise<number>;
53
+ /**
54
+ * Apply an operator/LLM verdict (retire/un-retire and/or relabel outcome).
55
+ * Human-wins: a `source: "llm"` call is a no-op when the row is already
56
+ * `label_source: "human"`. Returns the outcome so callers can surface it.
57
+ */
58
+ setVerdict(id: string, patch: ExemplarVerdictPatch, source: ExemplarVerdictSource): Promise<ExemplarVerdictResult>;
45
59
  }
@@ -77,6 +77,14 @@ export interface RecallQuery {
77
77
  * via NLM_HOOK_INJECT_FACTS=false. Spec G.2.
78
78
  */
79
79
  readonly withRelatedFacts?: boolean;
80
+ /**
81
+ * If true, RecallService attaches a `relatedExemplars` array of nearest
82
+ * code exemplars from the CodeRankEmbed space. Allows the recall block
83
+ * to include concrete code examples as a third dimension alongside
84
+ * session pointers and facts. Off by default; the HTTP hook caller may
85
+ * set this true to inject code examples into the pointer block.
86
+ */
87
+ readonly withRelatedExemplars?: boolean;
80
88
  /**
81
89
  * If true, superseded sessions are included in recall results, down-ranked
82
90
  * and carrying a `supersededBy` pointer to their successor. Replaced
@@ -125,6 +133,13 @@ export interface RecallResult {
125
133
  * "Known facts" section beneath the session pointer list.
126
134
  */
127
135
  readonly relatedFacts?: ReadonlyArray<RelatedFact>;
136
+ /**
137
+ * Optional array of nearest code exemplars matched via CodeRankEmbed.
138
+ * Present only when the caller requested `withRelatedExemplars`.
139
+ * Provides concrete code examples as a third dimension of context
140
+ * alongside session pointers and facts.
141
+ */
142
+ readonly relatedExemplars?: ReadonlyArray<RelatedExemplar>;
128
143
  }
129
144
  /**
130
145
  * Fact — a single normalized claim derived from a session.
@@ -262,6 +277,8 @@ export interface CodeExemplarInput {
262
277
  export interface CodeExemplar extends CodeExemplarInput {
263
278
  readonly id: string;
264
279
  readonly createdAt: string;
280
+ readonly retiredAt: string | null;
281
+ readonly labelSource: "llm" | "human";
265
282
  }
266
283
  /** One result row from recall_code. */
267
284
  export interface CodeExemplarHit {
@@ -276,3 +293,16 @@ export interface CodeExemplarHit {
276
293
  readonly gitSha: string | null;
277
294
  readonly distance: number;
278
295
  }
296
+ /**
297
+ * Lean pointer to a related code exemplar, returned by `pickRelatedExemplars`.
298
+ * Carries just the metadata needed for the recall block to reference the example
299
+ * without embedding the full code (which is retrieved on demand via `recall_code`).
300
+ */
301
+ export interface RelatedExemplar {
302
+ readonly id: string;
303
+ readonly outcome: CodeExemplarOutcome;
304
+ readonly lang: string | null;
305
+ readonly repo: string;
306
+ readonly taskContext: string;
307
+ readonly distance: number;
308
+ }
@@ -0,0 +1,10 @@
1
+ -- Migration 023: code_exemplars supersedence — operator/LLM verdict on exemplars.
2
+ --
3
+ -- retired_at: non-null = excluded from recall (the "verdict"), like facts.retired_at.
4
+ -- label_source: who last set the verdict/outcome ('llm' at capture; 'human' on
5
+ -- operator override). A human verdict is sticky — see CodeExemplarStore.setVerdict.
6
+
7
+ ALTER TABLE code_exemplars ADD COLUMN retired_at TEXT;
8
+ ALTER TABLE code_exemplars ADD COLUMN label_source TEXT NOT NULL DEFAULT 'llm';
9
+
10
+ INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (23, '023_code_exemplars_supersedence');
@@ -237,7 +237,9 @@ CREATE TABLE IF NOT EXISTS code_exemplars (
237
237
  git_sha TEXT,
238
238
  survived INTEGER,
239
239
  ts TEXT NOT NULL,
240
- created_at TEXT NOT NULL DEFAULT (now()::text)
240
+ created_at TEXT NOT NULL DEFAULT (now()::text),
241
+ retired_at TEXT,
242
+ label_source TEXT NOT NULL DEFAULT 'llm'
241
243
  );
242
244
  CREATE INDEX IF NOT EXISTS idx_exemplars_scope_repo ON code_exemplars(install_scope, repo, outcome);
243
245
  CREATE INDEX IF NOT EXISTS idx_exemplars_ts ON code_exemplars(ts);
package/nlm/index.js CHANGED
@@ -193,9 +193,9 @@ var RECALL_LIMIT = 5;
193
193
  var RECALL_TIMEOUT_MS = 2e3;
194
194
  async function recallOverHttp(prompt, runtime, conversationId) {
195
195
  const query = extractRecallQuery(prompt);
196
- if (query === null) return { hits: [], facts: [] };
196
+ if (query === null) return { hits: [], facts: [], exemplars: [] };
197
197
  const portValue = process.env["NLM_PORT"] ?? "3940";
198
- const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
198
+ const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true&withExemplars=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
199
199
  const controller = new AbortController();
200
200
  const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
201
201
  try {
@@ -205,12 +205,12 @@ async function recallOverHttp(prompt, runtime, conversationId) {
205
205
  headers: hookAuthHeaders(extra),
206
206
  signal: controller.signal
207
207
  });
208
- if (!res.ok) return { hits: [], facts: [] };
208
+ if (!res.ok) return { hits: [], facts: [], exemplars: [] };
209
209
  let body;
210
210
  try {
211
211
  body = await res.json();
212
212
  } catch {
213
- return { hits: [], facts: [] };
213
+ return { hits: [], facts: [], exemplars: [] };
214
214
  }
215
215
  const hits = (body.results ?? []).map((r) => ({
216
216
  id: r.id,
@@ -225,7 +225,13 @@ async function recallOverHttp(prompt, runtime, conversationId) {
225
225
  value: f.value,
226
226
  corroborationCount: f.corroborationCount
227
227
  }));
228
- return { hits, facts };
228
+ const exemplars = (body.relatedExemplars ?? []).map((e) => ({
229
+ outcome: e.outcome,
230
+ lang: e.lang,
231
+ repo: e.repo,
232
+ taskContext: e.taskContext
233
+ }));
234
+ return { hits, facts, exemplars };
229
235
  } finally {
230
236
  clearTimeout(timer);
231
237
  }
@@ -295,8 +301,8 @@ function recordSurfaced(conversationId, ids) {
295
301
  }
296
302
 
297
303
  // src/core/hook/pointer-block.ts
298
- function formatPointerBlock(hits, facts = []) {
299
- if (hits.length === 0 && facts.length === 0) return "";
304
+ function formatPointerBlock(hits, facts = [], exemplars = []) {
305
+ if (hits.length === 0 && facts.length === 0 && exemplars.length === 0) return "";
300
306
  const out = [];
301
307
  if (hits.length > 0) {
302
308
  out.push("## Possibly-relevant prior sessions (nlm-memory)");
@@ -317,9 +323,16 @@ function formatPointerBlock(hits, facts = []) {
317
323
  out.push(`- ${f.subject} ${f.predicate}: ${f.value}${tag}`);
318
324
  }
319
325
  }
320
- out.push(
321
- "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved)."
322
- );
326
+ if (exemplars.length > 0) {
327
+ if (out.length > 0) out.push("");
328
+ out.push("## Related code exemplars (nlm-memory)");
329
+ for (const e of exemplars) {
330
+ const langPart = e.lang ? `${e.lang} \xB7 ` : "";
331
+ out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext.slice(0, 120)}`);
332
+ }
333
+ }
334
+ const tools = exemplars.length > 0 ? "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved), recall_code (pull the full code for a related exemplar)." : "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved).";
335
+ out.push(tools);
323
336
  return out.join("\n");
324
337
  }
325
338
 
@@ -378,7 +391,7 @@ async function runHook(input, deps) {
378
391
  perFireCap: PER_FIRE_CAP,
379
392
  perConversationCap: PER_CONVERSATION_CAP
380
393
  });
381
- const block = formatPointerBlock(selected, fetched.facts);
394
+ const block = formatPointerBlock(selected, fetched.facts, fetched.exemplars);
382
395
  const estTokens = Math.ceil(block.length / 4);
383
396
  appendHookLog({
384
397
  ts: (/* @__PURE__ */ new Date()).toISOString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlm-memory",
3
- "version": "0.15.0",
3
+ "version": "0.18.0",
4
4
  "description": "Local-first non-linear memory operating system for AI operators.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlm-memory",
3
- "version": "0.15.0",
3
+ "version": "0.18.0",
4
4
  "description": "Local-first persistent memory for AI coding agents. Recalls relevant prior sessions on every prompt; learns which sessions actually get cited. Owner-edited timeline beats append-only memory layers.",
5
5
  "author": {
6
6
  "name": "Edward Chalupa",
@@ -64,8 +64,8 @@ function recordSurfaced(conversationId, ids) {
64
64
  }
65
65
 
66
66
  // src/core/hook/pointer-block.ts
67
- function formatPointerBlock(hits, facts = []) {
68
- if (hits.length === 0 && facts.length === 0) return "";
67
+ function formatPointerBlock(hits, facts = [], exemplars = []) {
68
+ if (hits.length === 0 && facts.length === 0 && exemplars.length === 0) return "";
69
69
  const out = [];
70
70
  if (hits.length > 0) {
71
71
  out.push("## Possibly-relevant prior sessions (nlm-memory)");
@@ -86,9 +86,16 @@ function formatPointerBlock(hits, facts = []) {
86
86
  out.push(`- ${f.subject} ${f.predicate}: ${f.value}${tag}`);
87
87
  }
88
88
  }
89
- out.push(
90
- "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved)."
91
- );
89
+ if (exemplars.length > 0) {
90
+ if (out.length > 0) out.push("");
91
+ out.push("## Related code exemplars (nlm-memory)");
92
+ for (const e of exemplars) {
93
+ const langPart = e.lang ? `${e.lang} \xB7 ` : "";
94
+ out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext.slice(0, 120)}`);
95
+ }
96
+ }
97
+ const tools = exemplars.length > 0 ? "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved), recall_code (pull the full code for a related exemplar)." : "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved).";
98
+ out.push(tools);
92
99
  return out.join("\n");
93
100
  }
94
101
 
@@ -298,9 +305,9 @@ var RECALL_LIMIT = 5;
298
305
  var RECALL_TIMEOUT_MS = 2e3;
299
306
  async function recallOverHttp(prompt, runtime, conversationId) {
300
307
  const query = extractRecallQuery(prompt);
301
- if (query === null) return { hits: [], facts: [] };
308
+ if (query === null) return { hits: [], facts: [], exemplars: [] };
302
309
  const portValue = process.env["NLM_PORT"] ?? "3940";
303
- const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
310
+ const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true&withExemplars=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
304
311
  const controller = new AbortController();
305
312
  const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
306
313
  try {
@@ -310,12 +317,12 @@ async function recallOverHttp(prompt, runtime, conversationId) {
310
317
  headers: hookAuthHeaders(extra),
311
318
  signal: controller.signal
312
319
  });
313
- if (!res.ok) return { hits: [], facts: [] };
320
+ if (!res.ok) return { hits: [], facts: [], exemplars: [] };
314
321
  let body;
315
322
  try {
316
323
  body = await res.json();
317
324
  } catch {
318
- return { hits: [], facts: [] };
325
+ return { hits: [], facts: [], exemplars: [] };
319
326
  }
320
327
  const hits = (body.results ?? []).map((r) => ({
321
328
  id: r.id,
@@ -330,7 +337,13 @@ async function recallOverHttp(prompt, runtime, conversationId) {
330
337
  value: f.value,
331
338
  corroborationCount: f.corroborationCount
332
339
  }));
333
- return { hits, facts };
340
+ const exemplars = (body.relatedExemplars ?? []).map((e) => ({
341
+ outcome: e.outcome,
342
+ lang: e.lang,
343
+ repo: e.repo,
344
+ taskContext: e.taskContext
345
+ }));
346
+ return { hits, facts, exemplars };
334
347
  } finally {
335
348
  clearTimeout(timer);
336
349
  }
@@ -380,7 +393,7 @@ async function runHook(input, deps) {
380
393
  perFireCap: PER_FIRE_CAP,
381
394
  perConversationCap: PER_CONVERSATION_CAP
382
395
  });
383
- const block = formatPointerBlock(selected, fetched.facts);
396
+ const block = formatPointerBlock(selected, fetched.facts, fetched.exemplars);
384
397
  const estTokens = Math.ceil(block.length / 4);
385
398
  appendHookLog({
386
399
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -53,8 +53,8 @@ function recordSurfaced(conversationId, ids) {
53
53
  }
54
54
 
55
55
  // src/core/hook/pointer-block.ts
56
- function formatPointerBlock(hits, facts = []) {
57
- if (hits.length === 0 && facts.length === 0) return "";
56
+ function formatPointerBlock(hits, facts = [], exemplars = []) {
57
+ if (hits.length === 0 && facts.length === 0 && exemplars.length === 0) return "";
58
58
  const out = [];
59
59
  if (hits.length > 0) {
60
60
  out.push("## Possibly-relevant prior sessions (nlm-memory)");
@@ -75,9 +75,16 @@ function formatPointerBlock(hits, facts = []) {
75
75
  out.push(`- ${f.subject} ${f.predicate}: ${f.value}${tag}`);
76
76
  }
77
77
  }
78
- out.push(
79
- "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved)."
80
- );
78
+ if (exemplars.length > 0) {
79
+ if (out.length > 0) out.push("");
80
+ out.push("## Related code exemplars (nlm-memory)");
81
+ for (const e of exemplars) {
82
+ const langPart = e.lang ? `${e.lang} \xB7 ` : "";
83
+ out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext.slice(0, 120)}`);
84
+ }
85
+ }
86
+ const tools = exemplars.length > 0 ? "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved), recall_code (pull the full code for a related exemplar)." : "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved).";
87
+ out.push(tools);
81
88
  return out.join("\n");
82
89
  }
83
90
 
@@ -1,16 +0,0 @@
1
- -- PG parity for SQLite migration 018: repair self-supersedence damage.
2
- --
3
- -- One-shot repair, applied manually by an operator against a PG canonical
4
- -- store (PgStorage.init only runs 001_initial.sql, there is no version-gated
5
- -- runner on the PG side). Mirrors migrations/018_repair_self_supersede.sql.
6
- --
7
- -- Removes self-loop 'supersedes' edges (id -> id) written by the unguarded
8
- -- scan path, then restores sessions wrongly marked superseded — those left
9
- -- without any real incoming 'supersedes' edge.
10
-
11
- DELETE FROM session_edges WHERE from_session = to_session AND kind = 'supersedes';
12
-
13
- UPDATE sessions
14
- SET status = 'closed', updated_at = NOW()
15
- WHERE status = 'superseded'
16
- AND id NOT IN (SELECT to_session FROM session_edges WHERE kind = 'supersedes');
@@ -1,46 +0,0 @@
1
- -- PG parity for SQLite migration 020: collapse duplicate active facts (#301).
2
- --
3
- -- One-shot repair, applied manually by an operator against a PG canonical store
4
- -- (PgStorage.init only runs 001_initial.sql; there is no version-gated runner on
5
- -- the PG side). Mirrors migrations/020_collapse_duplicate_active_facts.sql.
6
- --
7
- -- Within each conflicting (subject, predicate) group, the newest active fact
8
- -- (created_at, then id) stays active; the older actives get superseded_by set
9
- -- to that newest fact's id. The PG ingest path already collapsed all priors
10
- -- set-wise, so this only repairs rows the SQLite-era single-prior loop stranded
11
- -- in a corpus migrated to PG.
12
-
13
- BEGIN;
14
-
15
- WITH winners AS (
16
- SELECT subject, predicate, id AS winner_id
17
- FROM (
18
- SELECT
19
- subject, predicate, id,
20
- ROW_NUMBER() OVER (
21
- PARTITION BY subject, predicate
22
- ORDER BY created_at DESC, id DESC
23
- ) AS rn
24
- FROM facts
25
- WHERE superseded_by IS NULL
26
- ) ranked
27
- WHERE rn = 1
28
- ),
29
- conflicts AS (
30
- SELECT subject, predicate
31
- FROM facts
32
- WHERE superseded_by IS NULL
33
- GROUP BY subject, predicate
34
- HAVING COUNT(*) > 1
35
- )
36
- UPDATE facts f
37
- SET superseded_by = w.winner_id
38
- FROM winners w, conflicts c
39
- WHERE f.subject = w.subject
40
- AND f.predicate = w.predicate
41
- AND f.subject = c.subject
42
- AND f.predicate = c.predicate
43
- AND f.superseded_by IS NULL
44
- AND f.id != w.winner_id;
45
-
46
- COMMIT;
@@ -1,45 +0,0 @@
1
- -- PG parity for SQLite migration 021: code_exemplars lane.
2
- --
3
- -- Mirrors migrations/021_code_exemplars.sql. Uses pgvector `vector(768)`
4
- -- instead of sqlite-vec `vec0`. Applied manually by an operator.
5
-
6
- BEGIN;
7
-
8
- CREATE TABLE IF NOT EXISTS code_exemplars (
9
- id TEXT PRIMARY KEY,
10
- install_scope TEXT NOT NULL,
11
- signal_id TEXT,
12
- session_id TEXT,
13
- repo TEXT NOT NULL,
14
- model TEXT NOT NULL,
15
- lang TEXT,
16
- task_context TEXT NOT NULL,
17
- code TEXT NOT NULL,
18
- code_hash TEXT NOT NULL,
19
- outcome TEXT NOT NULL CHECK (outcome IN ('pass','fail','fix','exhausted')),
20
- git_sha TEXT,
21
- survived SMALLINT,
22
- ts TEXT NOT NULL,
23
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
24
- );
25
-
26
- CREATE INDEX IF NOT EXISTS idx_exemplars_scope_repo
27
- ON code_exemplars(install_scope, repo, outcome);
28
-
29
- CREATE INDEX IF NOT EXISTS idx_exemplars_ts
30
- ON code_exemplars(ts);
31
-
32
- CREATE INDEX IF NOT EXISTS idx_exemplars_code_hash
33
- ON code_exemplars(install_scope, code_hash);
34
-
35
- -- Vector lane. 768 dims matches CodeRankEmbed-137M output.
36
- CREATE TABLE IF NOT EXISTS code_exemplars_vec (
37
- exemplar_id TEXT PRIMARY KEY,
38
- embedding vector(768) NOT NULL
39
- );
40
-
41
- CREATE INDEX IF NOT EXISTS code_exemplars_vec_embedding_idx
42
- ON code_exemplars_vec USING ivfflat (embedding vector_l2_ops)
43
- WITH (lists = 10);
44
-
45
- COMMIT;
@@ -1,12 +0,0 @@
1
- -- PG parity for SQLite migration 022: facts.retired_at.
2
- --
3
- -- Mirrors migrations/022_facts_retired_at.sql. Distinct nullable marker for
4
- -- operator retirement of incorrect facts (NLM #326). Recall excludes retired
5
- -- facts; getHistory / includeSuperseded still surface them. Applied manually
6
- -- by an operator.
7
-
8
- BEGIN;
9
-
10
- ALTER TABLE facts ADD COLUMN IF NOT EXISTS retired_at TEXT;
11
-
12
- COMMIT;