8c054094a1
Refs #1 - peers table (peer_id, addr, cursor, last_handshake) - watch serves HTTP API: GET /v1/ping (knowledge vector), GET /v1/log?node=&after= (cursor-paged pull), POST /v1/obs/batch - anti-entropy sweep (Run): ping, pull what we lack, push own obs; echo suppressed by node_id ownership; reconcile-on-pull - config via KNOX_PEERS / KNOX_PEER_ADDR; knox gossip status - db: GossipObservation wire type, PushObservations, ObservationsAfter, KnowledgeVector, peer upsert/list - integration tests: bidirectional convergence + idempotency
152 lines
5.4 KiB
Go
152 lines
5.4 KiB
Go
package db
|
|
|
|
const Schema = `
|
|
-- IMMUTABLE LOG: every observation is append-only, never mutated
|
|
CREATE TABLE IF NOT EXISTS observations (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
fingerprint TEXT NOT NULL,
|
|
source_id TEXT NOT NULL,
|
|
source_path TEXT,
|
|
project TEXT,
|
|
content_type TEXT,
|
|
title TEXT,
|
|
summary TEXT,
|
|
collected_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
created_at TEXT,
|
|
line_start INTEGER,
|
|
line_end INTEGER,
|
|
confidence REAL DEFAULT 0.5,
|
|
ingester_version TEXT DEFAULT '1',
|
|
trigger TEXT,
|
|
provenance TEXT DEFAULT '{}',
|
|
node_id TEXT NOT NULL DEFAULT '',
|
|
hcl INTEGER,
|
|
received_at TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_obs_fingerprint ON observations(fingerprint);
|
|
CREATE INDEX IF NOT EXISTS idx_obs_collected ON observations(collected_at);
|
|
CREATE INDEX IF NOT EXISTS idx_obs_created ON observations(created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_obs_source ON observations(source_id, source_path);
|
|
CREATE INDEX IF NOT EXISTS idx_obs_project ON observations(project);
|
|
CREATE INDEX IF NOT EXISTS idx_obs_trigger ON observations(trigger);
|
|
|
|
-- MATERIALIZED CACHE: current state, reconstructable from observations
|
|
CREATE TABLE IF NOT EXISTS entries (
|
|
fingerprint TEXT PRIMARY KEY,
|
|
source_id TEXT NOT NULL,
|
|
source_path TEXT,
|
|
project TEXT,
|
|
content_type TEXT,
|
|
title TEXT,
|
|
summary TEXT,
|
|
first_seen TEXT,
|
|
last_seen TEXT,
|
|
ref_count INTEGER DEFAULT 1,
|
|
last_confidence REAL DEFAULT 0.5
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_entries_project ON entries(project);
|
|
CREATE INDEX IF NOT EXISTS idx_entries_source ON entries(source_id);
|
|
CREATE INDEX IF NOT EXISTS idx_entries_seen ON entries(last_seen);
|
|
|
|
-- PROVENANCE LINKS: multi-hop lineage between entries
|
|
CREATE TABLE IF NOT EXISTS provenance_links (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
descendant_fp TEXT NOT NULL,
|
|
ancestor_fp TEXT NOT NULL,
|
|
hop_distance INTEGER NOT NULL,
|
|
relation TEXT NOT NULL,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_pl_descendant ON provenance_links(descendant_fp);
|
|
CREATE INDEX IF NOT EXISTS idx_pl_ancestor ON provenance_links(ancestor_fp);
|
|
|
|
-- REFLECTION STATE: per-session tracking
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
session_id TEXT PRIMARY KEY,
|
|
project TEXT,
|
|
title TEXT,
|
|
status TEXT DEFAULT 'active',
|
|
agent_count INTEGER DEFAULT 0,
|
|
started_at TEXT,
|
|
ended_at TEXT,
|
|
last_seen TEXT DEFAULT (datetime('now')),
|
|
indexed INTEGER DEFAULT 0
|
|
);
|
|
|
|
-- SOURCE REGISTRY
|
|
CREATE TABLE IF NOT EXISTS sources (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
enabled INTEGER DEFAULT 1,
|
|
last_event TEXT,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
INSERT OR IGNORE INTO sources (id, name) VALUES ('opencode-session', 'Opencode Session Diffs');
|
|
INSERT OR IGNORE INTO sources (id, name) VALUES ('opencode-log', 'Opencode Log Files');
|
|
INSERT OR IGNORE INTO sources (id, name) VALUES ('git', 'Git History');
|
|
INSERT OR IGNORE INTO sources (id, name) VALUES ('filesystem', 'File System Watcher');
|
|
|
|
-- KNOWLEDGE THREADS: why observations exist, what questions they answer
|
|
CREATE TABLE IF NOT EXISTS threads (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
title TEXT NOT NULL,
|
|
motivation TEXT,
|
|
status TEXT DEFAULT 'active',
|
|
priority TEXT DEFAULT 'medium',
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
resolved_at TEXT,
|
|
tags TEXT DEFAULT '[]',
|
|
provenance TEXT DEFAULT '{}',
|
|
cluster_key TEXT
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_threads_cluster ON threads(cluster_key) WHERE cluster_key IS NOT NULL AND cluster_key != '';
|
|
|
|
-- SETTINGS: key-value store for runtime configuration
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT
|
|
);
|
|
|
|
-- PEERS: other knox nodes known to this one (gossip ring)
|
|
CREATE TABLE IF NOT EXISTS peers (
|
|
peer_id TEXT PRIMARY KEY, -- remote node_id
|
|
addr TEXT, -- e.g. http://192.168.1.20:8931
|
|
name TEXT,
|
|
last_handshake TEXT,
|
|
cursor INTEGER DEFAULT 0, -- remote's max_hcl we've seen (knowledge)
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status);
|
|
|
|
CREATE TABLE IF NOT EXISTS thread_observations (
|
|
thread_id INTEGER NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
|
|
observation_id INTEGER NOT NULL REFERENCES observations(id) ON DELETE CASCADE,
|
|
relevance TEXT,
|
|
added_at TEXT DEFAULT (datetime('now')),
|
|
PRIMARY KEY (thread_id, observation_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS thread_notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
thread_id INTEGER NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
|
|
note TEXT NOT NULL,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- TOPICS: TF-IDF clustered groups of related observations
|
|
CREATE TABLE IF NOT EXISTS topics (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
keywords TEXT,
|
|
score REAL DEFAULT 0,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
`
|