linksee-memory 0.7.2 → 0.8.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/README.md +111 -7
- package/dist/bin/declare-anchor.d.ts +2 -0
- package/dist/bin/declare-anchor.js +146 -0
- package/dist/bin/detect-drift.d.ts +2 -0
- package/dist/bin/detect-drift.js +91 -0
- package/dist/db/migrate.js +52 -28
- package/dist/db/schema.sql +348 -232
- package/dist/lib/drift-anchors.d.ts +78 -0
- package/dist/lib/drift-anchors.js +224 -0
- package/dist/lib/drift-detection.d.ts +62 -0
- package/dist/lib/drift-detection.js +416 -0
- package/dist/lib/drift-view.d.ts +62 -0
- package/dist/lib/drift-view.js +120 -0
- package/dist/lib/truth-engine.d.ts +84 -0
- package/dist/lib/truth-engine.js +417 -0
- package/dist/mcp/server.js +168 -3
- package/package.json +5 -3
package/dist/db/schema.sql
CHANGED
|
@@ -1,232 +1,348 @@
|
|
|
1
|
-
-- linksee-memory schema v0.0.4
|
|
2
|
-
-- Single-file SQLite store for cross-agent structured memory.
|
|
3
|
-
-- Layers: 1=facts (entities), 2=associations (edges), 3=patterns (meanings), 4=events (time-series), 5=file-state (diff cache).
|
|
4
|
-
-- v2 adds: FTS5 full-text search, consolidations audit, momentum cache on entities.
|
|
5
|
-
-- v6 adds: 3-axis generated columns (altitude/mem_type/mem_state) for queryable classification.
|
|
6
|
-
-- v7 adds: thread_id for decision chains, memory_edges for memory→memory relationships.
|
|
7
|
-
|
|
8
|
-
--
|
|
9
|
-
|
|
10
|
-
-- ============================================================
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
CREATE INDEX IF NOT EXISTS
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-- ============================================================
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
CREATE INDEX IF NOT EXISTS
|
|
55
|
-
CREATE INDEX IF NOT EXISTS
|
|
56
|
-
CREATE INDEX IF NOT EXISTS
|
|
57
|
-
CREATE INDEX IF NOT EXISTS
|
|
58
|
-
CREATE INDEX IF NOT EXISTS
|
|
59
|
-
CREATE INDEX IF NOT EXISTS
|
|
60
|
-
|
|
61
|
-
CREATE
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
--
|
|
71
|
-
--
|
|
72
|
-
--
|
|
73
|
-
--
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-- ============================================================
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
CREATE INDEX IF NOT EXISTS
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
--
|
|
118
|
-
--
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
CREATE INDEX IF NOT EXISTS
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
-- ============================================================
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
CREATE INDEX IF NOT EXISTS
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
-- ============================================================
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
CREATE
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
-- ============================================================
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
--
|
|
188
|
-
--
|
|
189
|
-
--
|
|
190
|
-
--
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
CREATE INDEX IF NOT EXISTS
|
|
205
|
-
CREATE INDEX IF NOT EXISTS
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
-- ============================================================
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
-- ============================================================
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
1
|
+
-- linksee-memory schema v0.0.4
|
|
2
|
+
-- Single-file SQLite store for cross-agent structured memory.
|
|
3
|
+
-- Layers: 1=facts (entities), 2=associations (edges), 3=patterns (meanings), 4=events (time-series), 5=file-state (diff cache).
|
|
4
|
+
-- v2 adds: FTS5 full-text search, consolidations audit, momentum cache on entities.
|
|
5
|
+
-- v6 adds: 3-axis generated columns (altitude/mem_type/mem_state) for queryable classification.
|
|
6
|
+
-- v7 adds: thread_id for decision chains, memory_edges for memory→memory relationships.
|
|
7
|
+
-- v8 adds: drift observability — drift_anchors (declared intent) + drift_edges (intent×reality verdicts).
|
|
8
|
+
-- Purely ADDITIVE: new tables only, no ALTER. Revert = DROP the two tables (no migrate.ts hook).
|
|
9
|
+
|
|
10
|
+
-- ============================================================
|
|
11
|
+
-- Layer 1: Facts — entities (people / companies / projects / concepts)
|
|
12
|
+
-- ============================================================
|
|
13
|
+
CREATE TABLE IF NOT EXISTS entities (
|
|
14
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15
|
+
kind TEXT NOT NULL CHECK (kind IN ('person', 'company', 'project', 'concept', 'file', 'other')),
|
|
16
|
+
name TEXT NOT NULL,
|
|
17
|
+
normalized_name TEXT, -- lowercased, separator-normalized for dedup matching
|
|
18
|
+
canonical_key TEXT UNIQUE,
|
|
19
|
+
attributes TEXT,
|
|
20
|
+
momentum_score REAL NOT NULL DEFAULT 0.0, -- 0-10 cached; refreshed on event insert
|
|
21
|
+
momentum_at INTEGER, -- when momentum was last computed
|
|
22
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
23
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_entities_kind ON entities(kind);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_entities_key ON entities(canonical_key);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_entities_normalized ON entities(kind, normalized_name);
|
|
29
|
+
|
|
30
|
+
-- ============================================================
|
|
31
|
+
-- Layer 3: Meanings — 6-layer structured memory per entity
|
|
32
|
+
-- ============================================================
|
|
33
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
34
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
+
entity_id INTEGER NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
|
|
36
|
+
layer TEXT NOT NULL CHECK (layer IN (
|
|
37
|
+
'goal', 'context', 'emotion', 'implementation', 'caveat', 'learning'
|
|
38
|
+
)),
|
|
39
|
+
content TEXT NOT NULL,
|
|
40
|
+
importance REAL NOT NULL DEFAULT 0.5,
|
|
41
|
+
protected INTEGER NOT NULL DEFAULT 0,
|
|
42
|
+
source TEXT,
|
|
43
|
+
thread_id TEXT, -- groups related memories (e.g. same session, same decision chain)
|
|
44
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
45
|
+
last_accessed_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
46
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
47
|
+
-- 3-axis classification (v0.5.0): auto-extracted from content JSON.
|
|
48
|
+
-- NULL when content is plain text (non-JSON). VIRTUAL = computed on read, indexed.
|
|
49
|
+
altitude TEXT GENERATED ALWAYS AS (CASE WHEN json_valid(content) THEN json_extract(content, '$.altitude') ELSE NULL END) VIRTUAL,
|
|
50
|
+
mem_type TEXT GENERATED ALWAYS AS (CASE WHEN json_valid(content) THEN json_extract(content, '$.type') ELSE NULL END) VIRTUAL,
|
|
51
|
+
mem_state TEXT GENERATED ALWAYS AS (CASE WHEN json_valid(content) THEN json_extract(content, '$.state') ELSE NULL END) VIRTUAL
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_memories_entity ON memories(entity_id);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_memories_layer ON memories(layer);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance DESC);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_memories_protected ON memories(protected);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_memories_altitude ON memories(altitude);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_memories_mem_type ON memories(mem_type);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_memories_mem_state ON memories(mem_state);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_memories_thread ON memories(thread_id);
|
|
62
|
+
|
|
63
|
+
CREATE TRIGGER IF NOT EXISTS trg_protect_caveat
|
|
64
|
+
AFTER INSERT ON memories
|
|
65
|
+
WHEN NEW.layer = 'caveat'
|
|
66
|
+
BEGIN
|
|
67
|
+
UPDATE memories SET protected = 1 WHERE id = NEW.id;
|
|
68
|
+
END;
|
|
69
|
+
|
|
70
|
+
-- ============================================================
|
|
71
|
+
-- FTS5 full-text search over memory content (Day 2)
|
|
72
|
+
-- BM25-ranked retrieval; combined with heat + momentum at query time.
|
|
73
|
+
-- ============================================================
|
|
74
|
+
-- trigram tokenizer indexes 3-character substrings — works for both English (case-insensitive
|
|
75
|
+
-- via remove_diacritics) AND Japanese/CJK (no word boundaries needed).
|
|
76
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
77
|
+
content,
|
|
78
|
+
content='memories',
|
|
79
|
+
content_rowid='id',
|
|
80
|
+
tokenize='trigram remove_diacritics 1'
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
CREATE TRIGGER IF NOT EXISTS trg_memories_fts_ai
|
|
84
|
+
AFTER INSERT ON memories BEGIN
|
|
85
|
+
INSERT INTO memories_fts(rowid, content) VALUES (NEW.id, NEW.content);
|
|
86
|
+
END;
|
|
87
|
+
|
|
88
|
+
CREATE TRIGGER IF NOT EXISTS trg_memories_fts_ad
|
|
89
|
+
AFTER DELETE ON memories BEGIN
|
|
90
|
+
INSERT INTO memories_fts(memories_fts, rowid, content) VALUES('delete', OLD.id, OLD.content);
|
|
91
|
+
END;
|
|
92
|
+
|
|
93
|
+
CREATE TRIGGER IF NOT EXISTS trg_memories_fts_au
|
|
94
|
+
AFTER UPDATE ON memories BEGIN
|
|
95
|
+
INSERT INTO memories_fts(memories_fts, rowid, content) VALUES('delete', OLD.id, OLD.content);
|
|
96
|
+
INSERT INTO memories_fts(rowid, content) VALUES (NEW.id, NEW.content);
|
|
97
|
+
END;
|
|
98
|
+
|
|
99
|
+
-- ============================================================
|
|
100
|
+
-- Layer 2: Associations — graph edges between entities
|
|
101
|
+
-- ============================================================
|
|
102
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
103
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
|
+
from_id INTEGER NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
|
|
105
|
+
to_id INTEGER NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
|
|
106
|
+
relation TEXT NOT NULL,
|
|
107
|
+
weight REAL NOT NULL DEFAULT 1.0,
|
|
108
|
+
attributes TEXT,
|
|
109
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
110
|
+
UNIQUE(from_id, to_id, relation)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_id);
|
|
114
|
+
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_id);
|
|
115
|
+
CREATE INDEX IF NOT EXISTS idx_edges_rel ON edges(relation);
|
|
116
|
+
|
|
117
|
+
-- ============================================================
|
|
118
|
+
-- Memory edges — directed relationships between individual memories
|
|
119
|
+
-- Enables: decision→implementation→outcome chains, supersedes tracking.
|
|
120
|
+
-- ============================================================
|
|
121
|
+
CREATE TABLE IF NOT EXISTS memory_edges (
|
|
122
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
+
from_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
124
|
+
to_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
125
|
+
relation TEXT NOT NULL CHECK (relation IN (
|
|
126
|
+
'supersedes', 'resolves', 'implements', 'contradicts', 'extends'
|
|
127
|
+
)),
|
|
128
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
129
|
+
UNIQUE(from_memory_id, to_memory_id, relation)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_medge_from ON memory_edges(from_memory_id);
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_medge_to ON memory_edges(to_memory_id);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_medge_rel ON memory_edges(relation);
|
|
135
|
+
|
|
136
|
+
-- ============================================================
|
|
137
|
+
-- Layer 4: Events — time-series log with importance markers
|
|
138
|
+
-- ============================================================
|
|
139
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
140
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
141
|
+
entity_id INTEGER REFERENCES entities(id) ON DELETE CASCADE,
|
|
142
|
+
kind TEXT NOT NULL,
|
|
143
|
+
payload TEXT,
|
|
144
|
+
occurred_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
CREATE INDEX IF NOT EXISTS idx_events_entity ON events(entity_id);
|
|
148
|
+
CREATE INDEX IF NOT EXISTS idx_events_occurred ON events(occurred_at DESC);
|
|
149
|
+
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);
|
|
150
|
+
|
|
151
|
+
-- ============================================================
|
|
152
|
+
-- Layer 5: File snapshots — diff cache for read_smart (Day 3)
|
|
153
|
+
-- ============================================================
|
|
154
|
+
CREATE TABLE IF NOT EXISTS file_snapshots (
|
|
155
|
+
path TEXT PRIMARY KEY,
|
|
156
|
+
content_hash TEXT NOT NULL,
|
|
157
|
+
mtime INTEGER NOT NULL,
|
|
158
|
+
size_bytes INTEGER,
|
|
159
|
+
chunks TEXT,
|
|
160
|
+
last_read_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
161
|
+
read_count INTEGER NOT NULL DEFAULT 1
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE INDEX IF NOT EXISTS idx_file_mtime ON file_snapshots(mtime);
|
|
165
|
+
|
|
166
|
+
CREATE TABLE IF NOT EXISTS file_facts (
|
|
167
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
168
|
+
file_path TEXT NOT NULL REFERENCES file_snapshots(path) ON DELETE CASCADE,
|
|
169
|
+
chunk_hash TEXT,
|
|
170
|
+
fact TEXT NOT NULL,
|
|
171
|
+
layer TEXT CHECK (layer IN ('goal', 'context', 'emotion', 'implementation', 'caveat', 'learning')),
|
|
172
|
+
extracted_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_file_facts_path ON file_facts(file_path);
|
|
176
|
+
|
|
177
|
+
-- ============================================================
|
|
178
|
+
-- Sessions — track which agent / conversation produced memories
|
|
179
|
+
-- ============================================================
|
|
180
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
181
|
+
id TEXT PRIMARY KEY,
|
|
182
|
+
agent_kind TEXT,
|
|
183
|
+
started_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
184
|
+
last_seen_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
-- ============================================================
|
|
188
|
+
-- Session file edits (v3) — conversation↔file linkage.
|
|
189
|
+
-- Each row: "during session S, at turn T, memory M mentions editing file F".
|
|
190
|
+
-- This is the table that breaks the Mem0 "flat metatag" wall:
|
|
191
|
+
-- memories describe WHY, file_edits tie the WHY to concrete filesystem changes.
|
|
192
|
+
-- ============================================================
|
|
193
|
+
CREATE TABLE IF NOT EXISTS session_file_edits (
|
|
194
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
195
|
+
session_id TEXT NOT NULL, -- Claude Code session uuid
|
|
196
|
+
memory_id INTEGER REFERENCES memories(id) ON DELETE SET NULL,
|
|
197
|
+
file_path TEXT NOT NULL,
|
|
198
|
+
operation TEXT NOT NULL CHECK (operation IN ('read', 'edit', 'write', 'bash', 'other')),
|
|
199
|
+
turn_uuid TEXT,
|
|
200
|
+
context_snippet TEXT, -- 1-2 line distilled "why this edit"
|
|
201
|
+
occurred_at INTEGER NOT NULL
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
CREATE INDEX IF NOT EXISTS idx_sfe_session ON session_file_edits(session_id);
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_sfe_file ON session_file_edits(file_path);
|
|
206
|
+
CREATE INDEX IF NOT EXISTS idx_sfe_memory ON session_file_edits(memory_id);
|
|
207
|
+
CREATE INDEX IF NOT EXISTS idx_sfe_when ON session_file_edits(occurred_at DESC);
|
|
208
|
+
|
|
209
|
+
-- ============================================================
|
|
210
|
+
-- Consolidations audit (Day 2) — trail of what got compressed into what
|
|
211
|
+
-- ============================================================
|
|
212
|
+
CREATE TABLE IF NOT EXISTS consolidations (
|
|
213
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
214
|
+
learning_id INTEGER REFERENCES memories(id) ON DELETE SET NULL,
|
|
215
|
+
replaced_ids TEXT NOT NULL, -- JSON array of deleted memory ids
|
|
216
|
+
replaced_count INTEGER NOT NULL,
|
|
217
|
+
entity_id INTEGER REFERENCES entities(id) ON DELETE CASCADE,
|
|
218
|
+
original_layer TEXT NOT NULL,
|
|
219
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
CREATE INDEX IF NOT EXISTS idx_consolidations_entity ON consolidations(entity_id);
|
|
223
|
+
|
|
224
|
+
-- ============================================================
|
|
225
|
+
-- v8: Drift observability — declared intent vs. actual reality.
|
|
226
|
+
-- Positioned ABOVE the memory engine (Sentry/Datadog-style): surface where a
|
|
227
|
+
-- DECLARED constraint/decision (intent) diverges from what the code actually did
|
|
228
|
+
-- (reality = session_file_edits). Deductive 照合, NOT 推測.
|
|
229
|
+
--
|
|
230
|
+
-- declare-don't-mine: anchors come ONLY from explicit declaration
|
|
231
|
+
-- (CLI declare / curation / CLAUDE.md), NEVER from the session pattern-extractor.
|
|
232
|
+
-- The tier CHECK ('human','explicit') enforces this at the schema level — an
|
|
233
|
+
-- agent/inferred memory physically cannot become an anchor.
|
|
234
|
+
-- ============================================================
|
|
235
|
+
CREATE TABLE IF NOT EXISTS drift_anchors (
|
|
236
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
237
|
+
kind TEXT NOT NULL CHECK (kind IN ('prohibition', 'decision', 'constraint')),
|
|
238
|
+
statement TEXT NOT NULL, -- the normative claim, verbatim
|
|
239
|
+
rationale TEXT, -- WHY (entrenchment context)
|
|
240
|
+
affects TEXT NOT NULL DEFAULT '[]', -- JSON array of path globs that scope reality
|
|
241
|
+
detect_terms TEXT NOT NULL DEFAULT '[]', -- JSON array of topical terms (FTS/overlap scoping)
|
|
242
|
+
violation_signal TEXT NOT NULL DEFAULT '[]', -- JSON array: terms whose PRESENCE in scope = violation
|
|
243
|
+
tier TEXT NOT NULL DEFAULT 'human' CHECK (tier IN ('human', 'explicit')),
|
|
244
|
+
source TEXT NOT NULL DEFAULT 'declare' CHECK (source IN ('declare', 'curate', 'claude_md')),
|
|
245
|
+
source_memory_id INTEGER REFERENCES memories(id) ON DELETE SET NULL, -- provenance if curated; anchor outlives it
|
|
246
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'retired')),
|
|
247
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
248
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
249
|
+
-- v9: ProjectCoreNode extension (Current Truth Map). ADDITIVE columns only — NO CHECK rebuild.
|
|
250
|
+
-- The existing status(active/retired) STAYS the coarse scan-gate (existing detector unaffected);
|
|
251
|
+
-- `lifecycle` carries the rich state. Values validated in the lib (no CHECK on new cols).
|
|
252
|
+
node_type TEXT, -- north_star|active_strategy|business_model|product_architecture|operational_commitment|source_of_truth|experiment|metric|asset|risk|retired_decision
|
|
253
|
+
domain TEXT, -- strategy|monetization|product|engineering|growth|operations|security|roadmap|memory
|
|
254
|
+
decision_mode TEXT, -- constraint|commitment|hypothesis|preference|source_of_truth|metric (the ROUTER → which detector + card behavior)
|
|
255
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
256
|
+
lifecycle TEXT NOT NULL DEFAULT 'active', -- active|experiment|paused|superseded|deprecated|unknown
|
|
257
|
+
validity_scope TEXT NOT NULL DEFAULT '{}', -- JSON {applies_to[], does_not_apply_to[]}
|
|
258
|
+
card_policy TEXT NOT NULL DEFAULT '{}', -- JSON {enabled, severity_if_broken, require_review_before_alert, cooldown_days}
|
|
259
|
+
reality_manifestations TEXT NOT NULL DEFAULT '[]', -- JSON RealityManifestation[]
|
|
260
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]', -- JSON EvidenceRef[]
|
|
261
|
+
review_after INTEGER,
|
|
262
|
+
last_confirmed_at INTEGER,
|
|
263
|
+
owner TEXT
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
CREATE INDEX IF NOT EXISTS idx_drift_anchors_status ON drift_anchors(status);
|
|
267
|
+
CREATE INDEX IF NOT EXISTS idx_drift_anchors_kind ON drift_anchors(kind);
|
|
268
|
+
|
|
269
|
+
-- Drift edges connect an ANCHOR (drift_anchors) to a REALITY unit (session_file_edits).
|
|
270
|
+
-- NOT reusable as memory_edges: that table FKs both ends to memories(id); these ends don't.
|
|
271
|
+
-- edit_id is NULL for a pure 'absent' verdict (decided-but-no-reality).
|
|
272
|
+
CREATE TABLE IF NOT EXISTS drift_edges (
|
|
273
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
274
|
+
anchor_id INTEGER NOT NULL REFERENCES drift_anchors(id) ON DELETE CASCADE,
|
|
275
|
+
edit_id INTEGER REFERENCES session_file_edits(id) ON DELETE CASCADE,
|
|
276
|
+
verdict TEXT NOT NULL CHECK (verdict IN ('contradicts', 'implements', 'absent')),
|
|
277
|
+
confidence REAL NOT NULL DEFAULT 0.0, -- min(tier_anchor, tier_reality) × match_strength
|
|
278
|
+
evidence TEXT NOT NULL DEFAULT '{}', -- JSON: file_path, context_snippet, shared_terms, hit_term, occurred_at
|
|
279
|
+
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'ack', 'dismissed', 'resolved')),
|
|
280
|
+
detected_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
281
|
+
UNIQUE(anchor_id, edit_id, verdict)
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
CREATE INDEX IF NOT EXISTS idx_drift_edges_anchor ON drift_edges(anchor_id);
|
|
285
|
+
CREATE INDEX IF NOT EXISTS idx_drift_edges_verdict ON drift_edges(verdict, status);
|
|
286
|
+
-- 'absent' has edit_id NULL, which UNIQUE treats as distinct — enforce one open absence per anchor.
|
|
287
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_drift_absent ON drift_edges(anchor_id) WHERE verdict = 'absent';
|
|
288
|
+
|
|
289
|
+
-- ============================================================
|
|
290
|
+
-- v9: Reality events — agent-AGNOSTIC reality signals (the uniform "floor").
|
|
291
|
+
-- Generalizes session_file_edits beyond Claude-Code transcripts: git commits, npm
|
|
292
|
+
-- publishes, deploys, etc. captured uniformly across ALL agents (git/files = WHAT).
|
|
293
|
+
-- ============================================================
|
|
294
|
+
CREATE TABLE IF NOT EXISTS reality_events (
|
|
295
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
296
|
+
scope TEXT, -- project / path token
|
|
297
|
+
source_type TEXT NOT NULL CHECK (source_type IN (
|
|
298
|
+
'git_commit','git_diff','file_change','npm','github','vercel','railway','article','strategy_doc','agent_summary','other'
|
|
299
|
+
)),
|
|
300
|
+
summary TEXT NOT NULL,
|
|
301
|
+
file_path TEXT,
|
|
302
|
+
raw_ref TEXT,
|
|
303
|
+
hash TEXT,
|
|
304
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]', -- JSON EvidenceRef[]
|
|
305
|
+
occurred_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
306
|
+
captured_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
CREATE INDEX IF NOT EXISTS idx_reality_events_scope ON reality_events(scope);
|
|
310
|
+
CREATE INDEX IF NOT EXISTS idx_reality_events_src ON reality_events(source_type);
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_reality_events_when ON reality_events(occurred_at DESC);
|
|
312
|
+
|
|
313
|
+
-- ============================================================
|
|
314
|
+
-- v9: Memory write candidates — the candidate→review→node staging.
|
|
315
|
+
-- Distillation/agents NEVER write the truth-map directly: they propose candidates.
|
|
316
|
+
-- Hard facts auto_accept; Soft interpretations stay pending_review (anti-AI-runaway).
|
|
317
|
+
-- ============================================================
|
|
318
|
+
CREATE TABLE IF NOT EXISTS memory_write_candidates (
|
|
319
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
320
|
+
scope TEXT,
|
|
321
|
+
candidate_type TEXT NOT NULL CHECK (candidate_type IN (
|
|
322
|
+
'create_node','update_node','pause_node','supersede_node','deprecate_node','create_card','no_write'
|
|
323
|
+
)),
|
|
324
|
+
target_node_id INTEGER REFERENCES drift_anchors(id) ON DELETE SET NULL,
|
|
325
|
+
proposed_node TEXT, -- JSON Partial<ProjectCoreNode>
|
|
326
|
+
rationale TEXT NOT NULL,
|
|
327
|
+
confidence REAL NOT NULL DEFAULT 0.0,
|
|
328
|
+
evidence_refs TEXT NOT NULL DEFAULT '[]',
|
|
329
|
+
status TEXT NOT NULL DEFAULT 'pending_review' CHECK (status IN (
|
|
330
|
+
'pending_review','auto_accepted','accepted','rejected'
|
|
331
|
+
)),
|
|
332
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
CREATE INDEX IF NOT EXISTS idx_mwc_status ON memory_write_candidates(status);
|
|
336
|
+
CREATE INDEX IF NOT EXISTS idx_mwc_scope ON memory_write_candidates(scope);
|
|
337
|
+
|
|
338
|
+
-- ============================================================
|
|
339
|
+
-- Meta — schema version tracking
|
|
340
|
+
-- ============================================================
|
|
341
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
342
|
+
key TEXT PRIMARY KEY,
|
|
343
|
+
value TEXT NOT NULL
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
INSERT OR IGNORE INTO meta (key, value) VALUES ('schema_version', '9');
|
|
347
|
+
INSERT OR IGNORE INTO meta (key, value) VALUES ('created_at', CAST(unixepoch() AS TEXT));
|
|
348
|
+
UPDATE meta SET value = '9' WHERE key = 'schema_version' AND value IN ('1', '2', '3', '4', '5', '6', '7', '8');
|