ralph-hero-knowledge-index 0.1.15 → 0.1.17
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/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/dist/db.d.ts +1 -0
- package/dist/db.js +35 -0
- package/dist/db.js.map +1 -1
- package/dist/graph-builder.d.ts +2 -2
- package/dist/graph-builder.js +2 -1
- package/dist/graph-builder.js.map +1 -1
- package/dist/reindex.js +11 -14
- package/dist/reindex.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/db.test.ts +189 -0
- package/src/__tests__/reindex.test.ts +42 -0
- package/src/db.ts +36 -0
- package/src/graph-builder.ts +5 -2
- package/src/reindex.ts +13 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-knowledge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Knowledge graph for ralph-hero: semantic search, relationship traversal, and document indexing across thoughts/ documents. Optional companion to ralph-hero.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Chad Dubiel",
|
package/.mcp.json
CHANGED
package/dist/db.d.ts
CHANGED
|
@@ -110,6 +110,7 @@ export declare class KnowledgeDB {
|
|
|
110
110
|
upsertSyncRecord(path: string, mtime: number): void;
|
|
111
111
|
deleteSyncRecord(path: string): void;
|
|
112
112
|
getAllSyncPaths(): string[];
|
|
113
|
+
documentExists(id: string): boolean;
|
|
113
114
|
deleteDocument(id: string): void;
|
|
114
115
|
clearAll(): void;
|
|
115
116
|
close(): void;
|
package/dist/db.js
CHANGED
|
@@ -67,6 +67,37 @@ export class KnowledgeDB {
|
|
|
67
67
|
indexed_at INTEGER NOT NULL
|
|
68
68
|
);
|
|
69
69
|
`);
|
|
70
|
+
// Migration: add is_stub column for databases created before it existed.
|
|
71
|
+
// SQLite has no IF NOT EXISTS for ALTER TABLE ADD COLUMN, so we catch the
|
|
72
|
+
// "duplicate column" error and ignore it.
|
|
73
|
+
try {
|
|
74
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN is_stub INTEGER DEFAULT 0");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Column already exists — expected for new databases
|
|
78
|
+
}
|
|
79
|
+
// Migration: rebuild relationships table for databases created before the
|
|
80
|
+
// context column, post_mortem/untyped CHECK types, and target_id FK were added.
|
|
81
|
+
// SQLite cannot ALTER CHECK constraints, so a full table rebuild is required.
|
|
82
|
+
try {
|
|
83
|
+
this.db.prepare("SELECT context FROM relationships LIMIT 0").get();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
this.db.exec(`
|
|
87
|
+
CREATE TABLE relationships_new (
|
|
88
|
+
source_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
89
|
+
target_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
90
|
+
type TEXT CHECK(type IN ('builds_on', 'tensions', 'superseded_by', 'post_mortem', 'untyped')),
|
|
91
|
+
context TEXT,
|
|
92
|
+
PRIMARY KEY (source_id, target_id, type)
|
|
93
|
+
);
|
|
94
|
+
INSERT INTO relationships_new (source_id, target_id, type)
|
|
95
|
+
SELECT source_id, target_id, type FROM relationships;
|
|
96
|
+
DROP TABLE relationships;
|
|
97
|
+
ALTER TABLE relationships_new RENAME TO relationships;
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_rel_target ON relationships(target_id, type);
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
70
101
|
}
|
|
71
102
|
upsertDocument(doc) {
|
|
72
103
|
this.db.prepare(`
|
|
@@ -251,6 +282,10 @@ export class KnowledgeDB {
|
|
|
251
282
|
getAllSyncPaths() {
|
|
252
283
|
return this.db.prepare("SELECT path FROM sync").all().map(r => r.path);
|
|
253
284
|
}
|
|
285
|
+
documentExists(id) {
|
|
286
|
+
const row = this.db.prepare("SELECT 1 FROM documents WHERE id = ?").get(id);
|
|
287
|
+
return row !== undefined;
|
|
288
|
+
}
|
|
254
289
|
deleteDocument(id) {
|
|
255
290
|
this.db.prepare("DELETE FROM documents WHERE id = ?").run(id);
|
|
256
291
|
}
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuFpC,MAAM,OAAO,WAAW;IACb,EAAE,CAAe;IAE1B,YAAY,MAAc;QACxB,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuDZ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/graph-builder.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MultiDirectedGraph } from "graphology";
|
|
1
|
+
import type { MultiDirectedGraph as MultiDirectedGraphType } from "graphology";
|
|
2
2
|
import type { KnowledgeDB } from "./db.js";
|
|
3
3
|
export interface NodeAttributes {
|
|
4
4
|
title: string;
|
|
@@ -9,7 +9,7 @@ export interface NodeAttributes {
|
|
|
9
9
|
export interface EdgeAttributes {
|
|
10
10
|
type: string;
|
|
11
11
|
}
|
|
12
|
-
export type KnowledgeGraph =
|
|
12
|
+
export type KnowledgeGraph = MultiDirectedGraphType<NodeAttributes, EdgeAttributes>;
|
|
13
13
|
export declare class GraphBuilder {
|
|
14
14
|
private readonly db;
|
|
15
15
|
constructor(db: KnowledgeDB);
|
package/dist/graph-builder.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-builder.js","sourceRoot":"","sources":["../src/graph-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"graph-builder.js","sourceRoot":"","sources":["../src/graph-builder.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAIpC,MAAM,EAAE,kBAAkB,EAAE,GAAG,UAAU,CAAC;AAe1C,MAAM,OAAO,YAAY;IACN,EAAE,CAAc;IAEjC,YAAY,EAAe;QACzB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,UAAU;QACR,MAAM,KAAK,GAAmB,IAAI,kBAAkB,EAAkC,CAAC;QAEvF,sCAAsC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE;aACpB,OAAO,CAAC,0FAA0F,CAAC;aACnG,GAAG,EAMJ,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE;gBACpB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;QAED,mDAAmD;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE;aACpB,OAAO,CAAC,sDAAsD,CAAC;aAC/D,GAAG,EAIJ,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,oEAAoE;YACpE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnE,SAAS;YACX,CAAC;YACD,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
package/dist/reindex.js
CHANGED
|
@@ -84,10 +84,16 @@ export async function reindex(dirs, dbPath, generate = false) {
|
|
|
84
84
|
}
|
|
85
85
|
// Delete old relationships before re-inserting so context updates propagate
|
|
86
86
|
db.db.prepare("DELETE FROM relationships WHERE source_id = ?").run(parsed.id);
|
|
87
|
+
// Ensure relationship targets exist before insertion (better-sqlite3 enables
|
|
88
|
+
// PRAGMA foreign_keys by default, so inserting a relationship to a non-existent
|
|
89
|
+
// document throws). upsertStubDocument uses INSERT OR IGNORE, so it's a no-op
|
|
90
|
+
// when the target is already a real document.
|
|
87
91
|
for (const rel of parsed.relationships) {
|
|
92
|
+
db.upsertStubDocument(rel.targetId);
|
|
88
93
|
db.addRelationship(rel.sourceId, rel.targetId, rel.type);
|
|
89
94
|
}
|
|
90
95
|
for (const edge of parsed.untypedEdges) {
|
|
96
|
+
db.upsertStubDocument(edge.targetId);
|
|
91
97
|
db.addRelationship(edge.sourceId, edge.targetId, "untyped", edge.context);
|
|
92
98
|
}
|
|
93
99
|
const text = prepareTextForEmbedding(parsed.title, parsed.content);
|
|
@@ -106,22 +112,13 @@ export async function reindex(dirs, dbPath, generate = false) {
|
|
|
106
112
|
}
|
|
107
113
|
// Phase 3: Rebuild FTS index from scratch (required — FTS5 content tables don't support partial sync)
|
|
108
114
|
fts.rebuildIndex();
|
|
109
|
-
// Collect all
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
for (const parsed of parsedDocs) {
|
|
114
|
-
for (const rel of parsed.relationships) {
|
|
115
|
-
allTargetIds.add(rel.targetId);
|
|
116
|
-
}
|
|
117
|
-
for (const edge of parsed.untypedEdges) {
|
|
118
|
-
allTargetIds.add(edge.targetId);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// Create stub documents for unresolved wikilink targets
|
|
115
|
+
// Collect all relationship targets from the database (covers both current batch and prior runs)
|
|
116
|
+
const allTargetIds = new Set(db.db.prepare("SELECT DISTINCT target_id FROM relationships").all()
|
|
117
|
+
.map(r => r.target_id));
|
|
118
|
+
// Create stub documents for targets that don't exist as real documents
|
|
122
119
|
let stubCount = 0;
|
|
123
120
|
for (const targetId of allTargetIds) {
|
|
124
|
-
if (!
|
|
121
|
+
if (!db.documentExists(targetId)) {
|
|
125
122
|
db.upsertStubDocument(targetId);
|
|
126
123
|
stubCount++;
|
|
127
124
|
}
|
package/dist/reindex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reindex.js","sourceRoot":"","sources":["../src/reindex.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"reindex.js","sourceRoot":"","sources":["../src/reindex.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/__tests__/db.test.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import { mkdtempSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
2
6
|
import { KnowledgeDB } from "../db.js";
|
|
3
7
|
|
|
4
8
|
let db: KnowledgeDB;
|
|
@@ -354,3 +358,188 @@ describe("deleteDocument", () => {
|
|
|
354
358
|
expect(db.getRelationshipsFrom("doc-a")).toEqual([]);
|
|
355
359
|
});
|
|
356
360
|
});
|
|
361
|
+
|
|
362
|
+
describe("Schema migration: is_stub column", () => {
|
|
363
|
+
it("adds is_stub column to a database created without it", () => {
|
|
364
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-migration-"));
|
|
365
|
+
const dbPath = join(dir, "legacy.db");
|
|
366
|
+
|
|
367
|
+
// Create a DB with the old schema (no is_stub column)
|
|
368
|
+
const rawDb = new Database(dbPath);
|
|
369
|
+
rawDb.exec(`
|
|
370
|
+
CREATE TABLE documents (
|
|
371
|
+
id TEXT PRIMARY KEY,
|
|
372
|
+
path TEXT,
|
|
373
|
+
title TEXT,
|
|
374
|
+
date TEXT,
|
|
375
|
+
type TEXT,
|
|
376
|
+
status TEXT,
|
|
377
|
+
github_issue INTEGER,
|
|
378
|
+
content TEXT
|
|
379
|
+
);
|
|
380
|
+
`);
|
|
381
|
+
rawDb.close();
|
|
382
|
+
|
|
383
|
+
// Opening via KnowledgeDB should migrate the schema
|
|
384
|
+
const migrated = new KnowledgeDB(dbPath);
|
|
385
|
+
expect(() => migrated.upsertStubDocument("stub-1")).not.toThrow();
|
|
386
|
+
const doc = migrated.getDocument("stub-1");
|
|
387
|
+
expect(doc).toBeTruthy();
|
|
388
|
+
expect(doc!.isStub).toBe(1);
|
|
389
|
+
migrated.close();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("is idempotent on a database that already has is_stub", () => {
|
|
393
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-migration-"));
|
|
394
|
+
const dbPath = join(dir, "current.db");
|
|
395
|
+
|
|
396
|
+
// First open creates the full schema including is_stub
|
|
397
|
+
const db1 = new KnowledgeDB(dbPath);
|
|
398
|
+
db1.upsertStubDocument("stub-1");
|
|
399
|
+
db1.close();
|
|
400
|
+
|
|
401
|
+
// Second open should not error (migration is a no-op)
|
|
402
|
+
const db2 = new KnowledgeDB(dbPath);
|
|
403
|
+
const doc = db2.getDocument("stub-1");
|
|
404
|
+
expect(doc).toBeTruthy();
|
|
405
|
+
expect(doc!.isStub).toBe(1);
|
|
406
|
+
db2.close();
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe("Schema migration: relationships table rebuild", () => {
|
|
411
|
+
it("rebuilds old schema, preserves data, and enables new features", () => {
|
|
412
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-rel-migration-"));
|
|
413
|
+
const dbPath = join(dir, "legacy.db");
|
|
414
|
+
|
|
415
|
+
// Create a DB with the old relationships schema (no context, narrow CHECK, no target_id FK)
|
|
416
|
+
const rawDb = new Database(dbPath);
|
|
417
|
+
rawDb.exec(`
|
|
418
|
+
CREATE TABLE documents (
|
|
419
|
+
id TEXT PRIMARY KEY, path TEXT, title TEXT, date TEXT, type TEXT,
|
|
420
|
+
status TEXT, github_issue INTEGER, content TEXT, is_stub INTEGER DEFAULT 0
|
|
421
|
+
);
|
|
422
|
+
CREATE TABLE tags (doc_id TEXT REFERENCES documents(id) ON DELETE CASCADE, tag TEXT, PRIMARY KEY (doc_id, tag));
|
|
423
|
+
CREATE TABLE relationships (
|
|
424
|
+
source_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
425
|
+
target_id TEXT,
|
|
426
|
+
type TEXT CHECK(type IN ('builds_on', 'tensions', 'superseded_by')),
|
|
427
|
+
PRIMARY KEY (source_id, target_id, type)
|
|
428
|
+
);
|
|
429
|
+
CREATE TABLE outcome_events (
|
|
430
|
+
id TEXT PRIMARY KEY, event_type TEXT NOT NULL, issue_number INTEGER NOT NULL,
|
|
431
|
+
session_id TEXT, timestamp TEXT NOT NULL, duration_ms INTEGER, verdict TEXT,
|
|
432
|
+
component_area TEXT, estimate TEXT, drift_count INTEGER, model TEXT,
|
|
433
|
+
agent_type TEXT, iteration_count INTEGER, payload TEXT DEFAULT '{}'
|
|
434
|
+
);
|
|
435
|
+
CREATE TABLE sync (path TEXT PRIMARY KEY, mtime INTEGER NOT NULL, indexed_at INTEGER NOT NULL);
|
|
436
|
+
`);
|
|
437
|
+
// Insert test data
|
|
438
|
+
rawDb.exec(`
|
|
439
|
+
INSERT INTO documents (id, path, title, content) VALUES ('a', 'a.md', 'Doc A', '');
|
|
440
|
+
INSERT INTO documents (id, path, title, content) VALUES ('b', 'b.md', 'Doc B', '');
|
|
441
|
+
INSERT INTO relationships (source_id, target_id, type) VALUES ('a', 'b', 'builds_on');
|
|
442
|
+
`);
|
|
443
|
+
rawDb.close();
|
|
444
|
+
|
|
445
|
+
// Opening via KnowledgeDB should migrate the schema
|
|
446
|
+
const migrated = new KnowledgeDB(dbPath);
|
|
447
|
+
|
|
448
|
+
// Existing data preserved with context = null
|
|
449
|
+
const rels = migrated.getRelationshipsFrom("a");
|
|
450
|
+
expect(rels).toHaveLength(1);
|
|
451
|
+
expect(rels[0].targetId).toBe("b");
|
|
452
|
+
expect(rels[0].type).toBe("builds_on");
|
|
453
|
+
expect(rels[0].context).toBeNull();
|
|
454
|
+
|
|
455
|
+
// New context column is writable
|
|
456
|
+
migrated.addRelationship("b", "a", "tensions", "disagreement on approach");
|
|
457
|
+
const relsBack = migrated.getRelationshipsFrom("b");
|
|
458
|
+
expect(relsBack[0].context).toBe("disagreement on approach");
|
|
459
|
+
|
|
460
|
+
// New CHECK types accepted
|
|
461
|
+
expect(() => migrated.addRelationship("a", "b", "post_mortem")).not.toThrow();
|
|
462
|
+
expect(() => migrated.addRelationship("a", "b", "untyped")).not.toThrow();
|
|
463
|
+
|
|
464
|
+
migrated.close();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("is idempotent on a database that already has the context column", () => {
|
|
468
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-rel-migration-"));
|
|
469
|
+
const dbPath = join(dir, "current.db");
|
|
470
|
+
|
|
471
|
+
const db1 = new KnowledgeDB(dbPath);
|
|
472
|
+
db1.upsertDocument({ id: "a", path: "a.md", title: "A", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
473
|
+
db1.upsertDocument({ id: "b", path: "b.md", title: "B", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
474
|
+
db1.addRelationship("a", "b", "builds_on", "some context");
|
|
475
|
+
db1.close();
|
|
476
|
+
|
|
477
|
+
// Second open should not error and data should be intact
|
|
478
|
+
const db2 = new KnowledgeDB(dbPath);
|
|
479
|
+
const rels = db2.getRelationshipsFrom("a");
|
|
480
|
+
expect(rels).toHaveLength(1);
|
|
481
|
+
expect(rels[0].context).toBe("some context");
|
|
482
|
+
db2.close();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("traverse works after migration from old schema", async () => {
|
|
486
|
+
const { Traverser } = await import("../traverse.js");
|
|
487
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-rel-migration-"));
|
|
488
|
+
const dbPath = join(dir, "legacy.db");
|
|
489
|
+
|
|
490
|
+
// Create old-schema DB with test data
|
|
491
|
+
const rawDb = new Database(dbPath);
|
|
492
|
+
rawDb.exec(`
|
|
493
|
+
CREATE TABLE documents (
|
|
494
|
+
id TEXT PRIMARY KEY, path TEXT, title TEXT, date TEXT, type TEXT,
|
|
495
|
+
status TEXT, github_issue INTEGER, content TEXT, is_stub INTEGER DEFAULT 0
|
|
496
|
+
);
|
|
497
|
+
CREATE TABLE tags (doc_id TEXT REFERENCES documents(id) ON DELETE CASCADE, tag TEXT, PRIMARY KEY (doc_id, tag));
|
|
498
|
+
CREATE TABLE relationships (
|
|
499
|
+
source_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
500
|
+
target_id TEXT,
|
|
501
|
+
type TEXT CHECK(type IN ('builds_on', 'tensions', 'superseded_by')),
|
|
502
|
+
PRIMARY KEY (source_id, target_id, type)
|
|
503
|
+
);
|
|
504
|
+
CREATE TABLE outcome_events (
|
|
505
|
+
id TEXT PRIMARY KEY, event_type TEXT NOT NULL, issue_number INTEGER NOT NULL,
|
|
506
|
+
session_id TEXT, timestamp TEXT NOT NULL, duration_ms INTEGER, verdict TEXT,
|
|
507
|
+
component_area TEXT, estimate TEXT, drift_count INTEGER, model TEXT,
|
|
508
|
+
agent_type TEXT, iteration_count INTEGER, payload TEXT DEFAULT '{}'
|
|
509
|
+
);
|
|
510
|
+
CREATE TABLE sync (path TEXT PRIMARY KEY, mtime INTEGER NOT NULL, indexed_at INTEGER NOT NULL);
|
|
511
|
+
INSERT INTO documents (id, path, title, content) VALUES ('a', 'a.md', 'Doc A', '');
|
|
512
|
+
INSERT INTO documents (id, path, title, content) VALUES ('b', 'b.md', 'Doc B', '');
|
|
513
|
+
INSERT INTO relationships (source_id, target_id, type) VALUES ('a', 'b', 'builds_on');
|
|
514
|
+
`);
|
|
515
|
+
rawDb.close();
|
|
516
|
+
|
|
517
|
+
// Open via KnowledgeDB (triggers migration), then traverse
|
|
518
|
+
const migrated = new KnowledgeDB(dbPath);
|
|
519
|
+
const traverser = new Traverser(migrated);
|
|
520
|
+
|
|
521
|
+
const results = traverser.traverse("a");
|
|
522
|
+
expect(results).toHaveLength(1);
|
|
523
|
+
expect(results[0].targetId).toBe("b");
|
|
524
|
+
expect(results[0].context).toBeNull();
|
|
525
|
+
expect(results[0].doc?.title).toBe("Doc B");
|
|
526
|
+
|
|
527
|
+
migrated.close();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
describe("documentExists", () => {
|
|
532
|
+
it("returns true for an existing document", () => {
|
|
533
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
534
|
+
expect(db.documentExists("doc-1")).toBe(true);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("returns false for a non-existent document", () => {
|
|
538
|
+
expect(db.documentExists("nonexistent")).toBe(false);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("returns true for stub documents", () => {
|
|
542
|
+
db.upsertStubDocument("stub-1");
|
|
543
|
+
expect(db.documentExists("stub-1")).toBe(true);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
@@ -158,4 +158,46 @@ describe("incremental reindex", () => {
|
|
|
158
158
|
// All files should be re-embedded since sync table was cleared
|
|
159
159
|
expect(mockedEmbed).toHaveBeenCalledTimes(2);
|
|
160
160
|
});
|
|
161
|
+
|
|
162
|
+
it("scenario 6: stub created for unresolved wikilink target, not for real documents", async () => {
|
|
163
|
+
// File A references file B via wikilink; both exist on disk
|
|
164
|
+
writeFileSync(join(dir, "doc-a.md"), `---\ndate: 2026-03-24\ntype: research\nstatus: draft\n---\n\n# Doc A\n\nSee also builds_on:: [[doc-b]]\n`);
|
|
165
|
+
writeFileSync(join(dir, "doc-b.md"), makeDoc("Doc B"));
|
|
166
|
+
|
|
167
|
+
await reindex([dir], dbPath);
|
|
168
|
+
|
|
169
|
+
const db = new KnowledgeDB(dbPath);
|
|
170
|
+
// doc-b is a real document — should NOT be a stub
|
|
171
|
+
const docB = db.getDocument("doc-b");
|
|
172
|
+
expect(docB).toBeTruthy();
|
|
173
|
+
expect(docB!.isStub).toBe(0);
|
|
174
|
+
db.close();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("scenario 7: stub survives incremental reindex when referencing file is skipped", async () => {
|
|
178
|
+
// File A references non-existent target "phantom"
|
|
179
|
+
writeFileSync(join(dir, "doc-a.md"), `---\ndate: 2026-03-24\ntype: research\nstatus: draft\n---\n\n# Doc A\n\nSee builds_on:: [[phantom]]\n`);
|
|
180
|
+
|
|
181
|
+
await reindex([dir], dbPath);
|
|
182
|
+
|
|
183
|
+
// Verify stub was created
|
|
184
|
+
const db1 = new KnowledgeDB(dbPath);
|
|
185
|
+
expect(db1.documentExists("phantom")).toBe(true);
|
|
186
|
+
const phantomDoc = db1.getDocument("phantom");
|
|
187
|
+
expect(phantomDoc!.isStub).toBe(1);
|
|
188
|
+
db1.close();
|
|
189
|
+
|
|
190
|
+
mockedEmbed.mockClear();
|
|
191
|
+
|
|
192
|
+
// Add a new file (doc-a is unchanged and will be skipped)
|
|
193
|
+
writeFileSync(join(dir, "doc-c.md"), makeDoc("Doc C"));
|
|
194
|
+
await reindex([dir], dbPath);
|
|
195
|
+
|
|
196
|
+
// phantom stub should still exist even though doc-a was skipped
|
|
197
|
+
const db2 = new KnowledgeDB(dbPath);
|
|
198
|
+
expect(db2.documentExists("phantom")).toBe(true);
|
|
199
|
+
const phantomDoc2 = db2.getDocument("phantom");
|
|
200
|
+
expect(phantomDoc2!.isStub).toBe(1);
|
|
201
|
+
db2.close();
|
|
202
|
+
});
|
|
161
203
|
});
|
package/src/db.ts
CHANGED
|
@@ -156,6 +156,37 @@ export class KnowledgeDB {
|
|
|
156
156
|
indexed_at INTEGER NOT NULL
|
|
157
157
|
);
|
|
158
158
|
`);
|
|
159
|
+
|
|
160
|
+
// Migration: add is_stub column for databases created before it existed.
|
|
161
|
+
// SQLite has no IF NOT EXISTS for ALTER TABLE ADD COLUMN, so we catch the
|
|
162
|
+
// "duplicate column" error and ignore it.
|
|
163
|
+
try {
|
|
164
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN is_stub INTEGER DEFAULT 0");
|
|
165
|
+
} catch {
|
|
166
|
+
// Column already exists — expected for new databases
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Migration: rebuild relationships table for databases created before the
|
|
170
|
+
// context column, post_mortem/untyped CHECK types, and target_id FK were added.
|
|
171
|
+
// SQLite cannot ALTER CHECK constraints, so a full table rebuild is required.
|
|
172
|
+
try {
|
|
173
|
+
this.db.prepare("SELECT context FROM relationships LIMIT 0").get();
|
|
174
|
+
} catch {
|
|
175
|
+
this.db.exec(`
|
|
176
|
+
CREATE TABLE relationships_new (
|
|
177
|
+
source_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
178
|
+
target_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
179
|
+
type TEXT CHECK(type IN ('builds_on', 'tensions', 'superseded_by', 'post_mortem', 'untyped')),
|
|
180
|
+
context TEXT,
|
|
181
|
+
PRIMARY KEY (source_id, target_id, type)
|
|
182
|
+
);
|
|
183
|
+
INSERT INTO relationships_new (source_id, target_id, type)
|
|
184
|
+
SELECT source_id, target_id, type FROM relationships;
|
|
185
|
+
DROP TABLE relationships;
|
|
186
|
+
ALTER TABLE relationships_new RENAME TO relationships;
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_rel_target ON relationships(target_id, type);
|
|
188
|
+
`);
|
|
189
|
+
}
|
|
159
190
|
}
|
|
160
191
|
|
|
161
192
|
upsertDocument(doc: Omit<DocumentRow, "isStub"> & { isStub?: number }): void {
|
|
@@ -388,6 +419,11 @@ export class KnowledgeDB {
|
|
|
388
419
|
return (this.db.prepare("SELECT path FROM sync").all() as Array<{ path: string }>).map(r => r.path);
|
|
389
420
|
}
|
|
390
421
|
|
|
422
|
+
documentExists(id: string): boolean {
|
|
423
|
+
const row = this.db.prepare("SELECT 1 FROM documents WHERE id = ?").get(id);
|
|
424
|
+
return row !== undefined;
|
|
425
|
+
}
|
|
426
|
+
|
|
391
427
|
deleteDocument(id: string): void {
|
|
392
428
|
this.db.prepare("DELETE FROM documents WHERE id = ?").run(id);
|
|
393
429
|
}
|
package/src/graph-builder.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import graphology from "graphology";
|
|
2
|
+
import type { MultiDirectedGraph as MultiDirectedGraphType } from "graphology";
|
|
2
3
|
import type { KnowledgeDB } from "./db.js";
|
|
3
4
|
|
|
5
|
+
const { MultiDirectedGraph } = graphology;
|
|
6
|
+
|
|
4
7
|
export interface NodeAttributes {
|
|
5
8
|
title: string;
|
|
6
9
|
type: string | null;
|
|
@@ -12,7 +15,7 @@ export interface EdgeAttributes {
|
|
|
12
15
|
type: string;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export type KnowledgeGraph =
|
|
18
|
+
export type KnowledgeGraph = MultiDirectedGraphType<NodeAttributes, EdgeAttributes>;
|
|
16
19
|
|
|
17
20
|
export class GraphBuilder {
|
|
18
21
|
private readonly db: KnowledgeDB;
|
package/src/reindex.ts
CHANGED
|
@@ -95,11 +95,17 @@ export async function reindex(dirs: string[], dbPath: string, generate: boolean
|
|
|
95
95
|
// Delete old relationships before re-inserting so context updates propagate
|
|
96
96
|
db.db.prepare("DELETE FROM relationships WHERE source_id = ?").run(parsed.id);
|
|
97
97
|
|
|
98
|
+
// Ensure relationship targets exist before insertion (better-sqlite3 enables
|
|
99
|
+
// PRAGMA foreign_keys by default, so inserting a relationship to a non-existent
|
|
100
|
+
// document throws). upsertStubDocument uses INSERT OR IGNORE, so it's a no-op
|
|
101
|
+
// when the target is already a real document.
|
|
98
102
|
for (const rel of parsed.relationships) {
|
|
103
|
+
db.upsertStubDocument(rel.targetId);
|
|
99
104
|
db.addRelationship(rel.sourceId, rel.targetId, rel.type);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
for (const edge of parsed.untypedEdges) {
|
|
108
|
+
db.upsertStubDocument(edge.targetId);
|
|
103
109
|
db.addRelationship(edge.sourceId, edge.targetId, "untyped", edge.context);
|
|
104
110
|
}
|
|
105
111
|
|
|
@@ -122,24 +128,16 @@ export async function reindex(dirs: string[], dbPath: string, generate: boolean
|
|
|
122
128
|
// Phase 3: Rebuild FTS index from scratch (required — FTS5 content tables don't support partial sync)
|
|
123
129
|
fts.rebuildIndex();
|
|
124
130
|
|
|
125
|
-
// Collect all
|
|
126
|
-
const
|
|
131
|
+
// Collect all relationship targets from the database (covers both current batch and prior runs)
|
|
132
|
+
const allTargetIds = new Set<string>(
|
|
133
|
+
(db.db.prepare("SELECT DISTINCT target_id FROM relationships").all() as Array<{ target_id: string }>)
|
|
134
|
+
.map(r => r.target_id)
|
|
135
|
+
);
|
|
127
136
|
|
|
128
|
-
//
|
|
129
|
-
const allTargetIds = new Set<string>();
|
|
130
|
-
for (const parsed of parsedDocs) {
|
|
131
|
-
for (const rel of parsed.relationships) {
|
|
132
|
-
allTargetIds.add(rel.targetId);
|
|
133
|
-
}
|
|
134
|
-
for (const edge of parsed.untypedEdges) {
|
|
135
|
-
allTargetIds.add(edge.targetId);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Create stub documents for unresolved wikilink targets
|
|
137
|
+
// Create stub documents for targets that don't exist as real documents
|
|
140
138
|
let stubCount = 0;
|
|
141
139
|
for (const targetId of allTargetIds) {
|
|
142
|
-
if (!
|
|
140
|
+
if (!db.documentExists(targetId)) {
|
|
143
141
|
db.upsertStubDocument(targetId);
|
|
144
142
|
stubCount++;
|
|
145
143
|
}
|