@jamesaphoenix/tx-core 0.5.8 → 0.5.10

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/README.md +7 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/migrations-embedded.d.ts +9 -0
  7. package/dist/migrations-embedded.d.ts.map +1 -0
  8. package/dist/migrations-embedded.js +143 -0
  9. package/dist/migrations-embedded.js.map +1 -0
  10. package/dist/services/index.d.ts +1 -1
  11. package/dist/services/index.d.ts.map +1 -1
  12. package/dist/services/index.js +1 -1
  13. package/dist/services/index.js.map +1 -1
  14. package/dist/services/migration-service.d.ts +8 -2
  15. package/dist/services/migration-service.d.ts.map +1 -1
  16. package/dist/services/migration-service.js +35 -15
  17. package/dist/services/migration-service.js.map +1 -1
  18. package/migrations/001_initial.sql +57 -0
  19. package/migrations/002_learnings.sql +64 -0
  20. package/migrations/003_file_learnings.sql +19 -0
  21. package/migrations/004_attempts.sql +18 -0
  22. package/migrations/005_runs.sql +34 -0
  23. package/migrations/006_events.sql +33 -0
  24. package/migrations/007_sync_config.sql +18 -0
  25. package/migrations/008_learning_anchors.sql +28 -0
  26. package/migrations/009_learning_edges.sql +43 -0
  27. package/migrations/010_learning_candidates.sql +30 -0
  28. package/migrations/011_processed_hashes.sql +35 -0
  29. package/migrations/012_invalidation.sql +32 -0
  30. package/migrations/013_content_preview.sql +13 -0
  31. package/migrations/014_daemon_tracked_projects.sql +20 -0
  32. package/migrations/015_worker_orchestration.sql +64 -0
  33. package/migrations/016_tracing_io_paths.sql +13 -0
  34. package/migrations/017_span_metric_events.sql +47 -0
  35. package/migrations/018_compaction_learnings.sql +15 -0
  36. package/migrations/019_tasks_updated_at_index.sql +11 -0
  37. package/migrations/020_task_id_check.sql +32 -0
  38. package/migrations/021_agent_outbox.sql +34 -0
  39. package/migrations/022_docs_as_primitives.sql +66 -0
  40. package/migrations/023_task_labels.sql +29 -0
  41. package/migrations/024_task_assignment.sql +27 -0
  42. package/migrations/025_run_heartbeat_state.sql +21 -0
  43. package/migrations/026_task_order_composite_indexes.sql +17 -0
  44. package/migrations/027_runs_events_hot_query_indexes.sql +35 -0
  45. package/migrations/028_task_group_context.sql +13 -0
  46. package/package.json +4 -8
@@ -0,0 +1,57 @@
1
+ -- Version: 001
2
+ -- Migration: initial
3
+
4
+ -- Core tasks table
5
+ CREATE TABLE IF NOT EXISTS tasks (
6
+ id TEXT PRIMARY KEY,
7
+ title TEXT NOT NULL,
8
+ description TEXT DEFAULT '',
9
+ status TEXT NOT NULL DEFAULT 'backlog'
10
+ CHECK (status IN (
11
+ 'backlog', 'ready', 'planning', 'active',
12
+ 'blocked', 'review', 'human_needs_to_review', 'done'
13
+ )),
14
+ parent_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
15
+ score INTEGER NOT NULL DEFAULT 0,
16
+ created_at TEXT NOT NULL,
17
+ updated_at TEXT NOT NULL,
18
+ completed_at TEXT,
19
+ metadata TEXT DEFAULT '{}'
20
+ );
21
+
22
+ -- Dependency relationships
23
+ CREATE TABLE IF NOT EXISTS task_dependencies (
24
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
25
+ blocker_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
26
+ blocked_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
27
+ created_at TEXT NOT NULL,
28
+ UNIQUE(blocker_id, blocked_id),
29
+ CHECK (blocker_id != blocked_id)
30
+ );
31
+
32
+ -- Compaction history
33
+ CREATE TABLE IF NOT EXISTS compaction_log (
34
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
35
+ compacted_at TEXT NOT NULL,
36
+ task_count INTEGER NOT NULL,
37
+ summary TEXT NOT NULL,
38
+ task_ids TEXT NOT NULL,
39
+ learnings_exported_to TEXT
40
+ );
41
+
42
+ -- Schema version tracking
43
+ CREATE TABLE IF NOT EXISTS schema_version (
44
+ version INTEGER PRIMARY KEY,
45
+ applied_at TEXT NOT NULL
46
+ );
47
+
48
+ -- Indexes
49
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
50
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
51
+ CREATE INDEX IF NOT EXISTS idx_tasks_score ON tasks(score DESC);
52
+ CREATE INDEX IF NOT EXISTS idx_tasks_created ON tasks(created_at);
53
+ CREATE INDEX IF NOT EXISTS idx_deps_blocker ON task_dependencies(blocker_id);
54
+ CREATE INDEX IF NOT EXISTS idx_deps_blocked ON task_dependencies(blocked_id);
55
+
56
+ -- Record this migration
57
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (1, datetime('now'));
@@ -0,0 +1,64 @@
1
+ -- Version: 002
2
+ -- Migration: Learnings table with FTS5 for BM25 search
3
+
4
+ -- Learnings table (append-only event log)
5
+ CREATE TABLE IF NOT EXISTS learnings (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ content TEXT NOT NULL,
8
+ source_type TEXT NOT NULL CHECK (source_type IN ('compaction', 'run', 'manual', 'claude_md')),
9
+ source_ref TEXT,
10
+ created_at TEXT NOT NULL,
11
+ keywords TEXT,
12
+ category TEXT,
13
+ usage_count INTEGER NOT NULL DEFAULT 0,
14
+ last_used_at TEXT,
15
+ outcome_score REAL,
16
+ embedding BLOB
17
+ );
18
+
19
+ -- FTS5 for BM25 keyword search
20
+ CREATE VIRTUAL TABLE IF NOT EXISTS learnings_fts USING fts5(
21
+ content, keywords, category,
22
+ content='learnings', content_rowid='id',
23
+ tokenize='porter unicode61'
24
+ );
25
+
26
+ -- Triggers to sync FTS on insert
27
+ CREATE TRIGGER IF NOT EXISTS learnings_ai AFTER INSERT ON learnings BEGIN
28
+ INSERT INTO learnings_fts(rowid, content, keywords, category)
29
+ VALUES (new.id, new.content, new.keywords, new.category);
30
+ END;
31
+
32
+ -- Triggers to sync FTS on delete
33
+ CREATE TRIGGER IF NOT EXISTS learnings_ad AFTER DELETE ON learnings BEGIN
34
+ INSERT INTO learnings_fts(learnings_fts, rowid, content, keywords, category)
35
+ VALUES ('delete', old.id, old.content, old.keywords, old.category);
36
+ END;
37
+
38
+ -- Triggers to sync FTS on update
39
+ CREATE TRIGGER IF NOT EXISTS learnings_au AFTER UPDATE ON learnings BEGIN
40
+ INSERT INTO learnings_fts(learnings_fts, rowid, content, keywords, category)
41
+ VALUES ('delete', old.id, old.content, old.keywords, old.category);
42
+ INSERT INTO learnings_fts(rowid, content, keywords, category)
43
+ VALUES (new.id, new.content, new.keywords, new.category);
44
+ END;
45
+
46
+ -- Config for retrieval weights
47
+ CREATE TABLE IF NOT EXISTS learnings_config (
48
+ key TEXT PRIMARY KEY,
49
+ value TEXT NOT NULL
50
+ );
51
+ INSERT OR IGNORE INTO learnings_config (key, value) VALUES
52
+ ('bm25_weight', '0.4'),
53
+ ('vector_weight', '0.4'),
54
+ ('recency_weight', '0.2');
55
+
56
+ -- Indexes for common queries
57
+ CREATE INDEX IF NOT EXISTS idx_learnings_created ON learnings(created_at DESC);
58
+ CREATE INDEX IF NOT EXISTS idx_learnings_source_type ON learnings(source_type);
59
+ CREATE INDEX IF NOT EXISTS idx_learnings_usage ON learnings(usage_count DESC);
60
+ CREATE INDEX IF NOT EXISTS idx_learnings_outcome ON learnings(outcome_score DESC);
61
+ CREATE INDEX IF NOT EXISTS idx_learnings_category ON learnings(category);
62
+
63
+ -- Record this migration
64
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (2, datetime('now'));
@@ -0,0 +1,19 @@
1
+ -- Version: 003
2
+ -- Migration: File learnings table for path-based knowledge
3
+
4
+ -- File learnings table (path-based knowledge storage)
5
+ CREATE TABLE IF NOT EXISTS file_learnings (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ file_pattern TEXT NOT NULL,
8
+ note TEXT NOT NULL,
9
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
10
+ created_at TEXT NOT NULL
11
+ );
12
+
13
+ -- Index for file pattern lookups
14
+ CREATE INDEX IF NOT EXISTS idx_file_learnings_pattern ON file_learnings(file_pattern);
15
+ CREATE INDEX IF NOT EXISTS idx_file_learnings_created ON file_learnings(created_at DESC);
16
+ CREATE INDEX IF NOT EXISTS idx_file_learnings_task ON file_learnings(task_id);
17
+
18
+ -- Record this migration
19
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (3, datetime('now'));
@@ -0,0 +1,18 @@
1
+ -- Version: 004
2
+ -- Migration: Attempts table for tracking task attempt outcomes
3
+
4
+ -- Attempts table (track failed/succeeded approaches per task)
5
+ CREATE TABLE IF NOT EXISTS attempts (
6
+ id INTEGER PRIMARY KEY,
7
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
8
+ approach TEXT NOT NULL,
9
+ outcome TEXT NOT NULL CHECK (outcome IN ('failed', 'succeeded')),
10
+ reason TEXT,
11
+ created_at TEXT NOT NULL
12
+ );
13
+
14
+ -- Index for task_id lookups
15
+ CREATE INDEX IF NOT EXISTS idx_attempts_task_id ON attempts(task_id);
16
+
17
+ -- Record this migration
18
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (4, datetime('now'));
@@ -0,0 +1,34 @@
1
+ -- Version: 005
2
+ -- Migration: Runs table for tracking Claude agent sessions
3
+
4
+ -- Runs table (track each Claude agent session)
5
+ CREATE TABLE IF NOT EXISTS runs (
6
+ id TEXT PRIMARY KEY, -- run-<sha8>
7
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
8
+ agent TEXT NOT NULL, -- tx-implementer, tx-decomposer, etc.
9
+ started_at TEXT NOT NULL,
10
+ ended_at TEXT,
11
+ status TEXT NOT NULL DEFAULT 'running'
12
+ CHECK (status IN ('running', 'completed', 'failed', 'timeout', 'cancelled')),
13
+ exit_code INTEGER,
14
+ pid INTEGER, -- OS process ID
15
+ transcript_path TEXT, -- Path to conversation .jsonl file
16
+ context_injected TEXT, -- Path to context.md that was injected
17
+ summary TEXT, -- LLM-generated summary of what happened
18
+ error_message TEXT, -- If failed, why
19
+ metadata TEXT DEFAULT '{}' -- JSON: git_sha, branch, iteration, etc.
20
+ );
21
+
22
+ -- Link learnings to runs (which run produced this learning?)
23
+ -- Note: learnings table already exists, just adding the column
24
+ ALTER TABLE learnings ADD COLUMN run_id TEXT REFERENCES runs(id) ON DELETE SET NULL;
25
+
26
+ -- Indexes for common queries
27
+ CREATE INDEX IF NOT EXISTS idx_runs_task ON runs(task_id);
28
+ CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
29
+ CREATE INDEX IF NOT EXISTS idx_runs_started ON runs(started_at DESC);
30
+ CREATE INDEX IF NOT EXISTS idx_runs_agent ON runs(agent);
31
+ CREATE INDEX IF NOT EXISTS idx_learnings_run ON learnings(run_id);
32
+
33
+ -- Record this migration
34
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (5, datetime('now'));
@@ -0,0 +1,33 @@
1
+ -- Version: 006
2
+ -- Migration: Events table for append-only activity tracking
3
+
4
+ -- Events table (append-only log of all activity for replay/analysis)
5
+ CREATE TABLE IF NOT EXISTS events (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ timestamp TEXT NOT NULL,
8
+ event_type TEXT NOT NULL CHECK (event_type IN (
9
+ 'run_started', 'run_completed', 'run_failed',
10
+ 'task_created', 'task_updated', 'task_completed',
11
+ 'tool_call', 'tool_result',
12
+ 'user_message', 'assistant_message',
13
+ 'error', 'learning_captured',
14
+ 'commit', 'review'
15
+ )),
16
+ run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,
17
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
18
+ agent TEXT,
19
+ tool_name TEXT,
20
+ content TEXT,
21
+ metadata TEXT DEFAULT '{}',
22
+ duration_ms INTEGER
23
+ );
24
+
25
+ -- Indexes for efficient querying
26
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp DESC);
27
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
28
+ CREATE INDEX IF NOT EXISTS idx_events_run ON events(run_id);
29
+ CREATE INDEX IF NOT EXISTS idx_events_task ON events(task_id);
30
+ CREATE INDEX IF NOT EXISTS idx_events_agent ON events(agent);
31
+
32
+ -- Record this migration
33
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (6, datetime('now'));
@@ -0,0 +1,18 @@
1
+ -- Version: 007
2
+ -- Migration: Sync config table for JSONL sync daemon settings
3
+
4
+ -- Sync config table (key-value store for sync settings)
5
+ CREATE TABLE IF NOT EXISTS sync_config (
6
+ key TEXT PRIMARY KEY,
7
+ value TEXT NOT NULL,
8
+ updated_at TEXT NOT NULL
9
+ );
10
+
11
+ -- Default values
12
+ INSERT OR IGNORE INTO sync_config (key, value, updated_at) VALUES
13
+ ('auto_sync', 'false', datetime('now')),
14
+ ('last_export', '', datetime('now')),
15
+ ('last_import', '', datetime('now'));
16
+
17
+ -- Record this migration
18
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (7, datetime('now'));
@@ -0,0 +1,28 @@
1
+ -- Version: 008
2
+ -- Migration: Learning anchors table for file-code associations
3
+
4
+ -- Learning anchors table (file/code associations for learnings)
5
+ CREATE TABLE IF NOT EXISTS learning_anchors (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ learning_id INTEGER NOT NULL REFERENCES learnings(id) ON DELETE CASCADE,
8
+ anchor_type TEXT NOT NULL CHECK (anchor_type IN ('glob', 'hash', 'symbol', 'line_range')),
9
+ anchor_value TEXT NOT NULL,
10
+ file_path TEXT NOT NULL,
11
+ symbol_fqname TEXT,
12
+ line_start INTEGER,
13
+ line_end INTEGER,
14
+ content_hash TEXT,
15
+ status TEXT NOT NULL DEFAULT 'valid' CHECK (status IN ('valid', 'invalid', 'drifted')),
16
+ verified_at TEXT,
17
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
18
+ );
19
+
20
+ -- Indexes for graph traversal and common queries
21
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_learning_id ON learning_anchors(learning_id);
22
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_file_path ON learning_anchors(file_path);
23
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_status ON learning_anchors(status);
24
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_anchor_type ON learning_anchors(anchor_type);
25
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_symbol ON learning_anchors(symbol_fqname) WHERE symbol_fqname IS NOT NULL;
26
+
27
+ -- Record this migration
28
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (8, datetime('now'));
@@ -0,0 +1,43 @@
1
+ -- Version: 009
2
+ -- Migration: Learning edges table for graph relationships
3
+
4
+ -- Learning edges table (graph relationships between nodes)
5
+ -- Supports edge types: ANCHORED_TO, DERIVED_FROM, IMPORTS, CO_CHANGES_WITH, SIMILAR_TO, LINKS_TO, USED_IN_RUN, INVALIDATED_BY
6
+ -- Node types: learning, file, task, run
7
+ CREATE TABLE IF NOT EXISTS learning_edges (
8
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9
+ edge_type TEXT NOT NULL CHECK (edge_type IN (
10
+ 'ANCHORED_TO',
11
+ 'DERIVED_FROM',
12
+ 'IMPORTS',
13
+ 'CO_CHANGES_WITH',
14
+ 'SIMILAR_TO',
15
+ 'LINKS_TO',
16
+ 'USED_IN_RUN',
17
+ 'INVALIDATED_BY'
18
+ )),
19
+ source_type TEXT NOT NULL CHECK (source_type IN ('learning', 'file', 'task', 'run')),
20
+ source_id TEXT NOT NULL,
21
+ target_type TEXT NOT NULL CHECK (target_type IN ('learning', 'file', 'task', 'run')),
22
+ target_id TEXT NOT NULL,
23
+ weight REAL NOT NULL DEFAULT 1.0 CHECK (weight >= 0.0 AND weight <= 1.0),
24
+ metadata TEXT NOT NULL DEFAULT '{}',
25
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
26
+ invalidated_at TEXT
27
+ );
28
+
29
+ -- Indexes for graph traversal
30
+ -- Source-based lookups (outgoing edges)
31
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_source ON learning_edges(source_type, source_id);
32
+ -- Target-based lookups (incoming edges)
33
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_target ON learning_edges(target_type, target_id);
34
+ -- Edge type filtering
35
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_type ON learning_edges(edge_type);
36
+ -- Active edges only (not invalidated) - partial index for efficient traversal
37
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_active ON learning_edges(source_type, source_id) WHERE invalidated_at IS NULL;
38
+ -- Composite index for bidirectional traversal with edge type filtering
39
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_source_type ON learning_edges(source_type, source_id, edge_type) WHERE invalidated_at IS NULL;
40
+ CREATE INDEX IF NOT EXISTS idx_learning_edges_target_type ON learning_edges(target_type, target_id, edge_type) WHERE invalidated_at IS NULL;
41
+
42
+ -- Record this migration
43
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (9, datetime('now'));
@@ -0,0 +1,30 @@
1
+ -- Version: 010
2
+ -- Migration: Learning candidates table for telemetry extraction pipeline
3
+
4
+ -- Learning candidates awaiting promotion to learnings table
5
+ -- See PRD-015: JSONL Telemetry Daemon and Knowledge Promotion Pipeline
6
+ CREATE TABLE IF NOT EXISTS learning_candidates (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ content TEXT NOT NULL,
9
+ confidence TEXT NOT NULL CHECK (confidence IN ('high', 'medium', 'low')),
10
+ category TEXT,
11
+ source_file TEXT NOT NULL,
12
+ source_run_id TEXT,
13
+ source_task_id TEXT,
14
+ extracted_at TEXT NOT NULL DEFAULT (datetime('now')),
15
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'promoted', 'rejected', 'merged')),
16
+ reviewed_at TEXT,
17
+ reviewed_by TEXT, -- 'auto' or user identifier
18
+ promoted_learning_id INTEGER,
19
+ rejection_reason TEXT,
20
+ FOREIGN KEY (promoted_learning_id) REFERENCES learnings(id) ON DELETE SET NULL
21
+ );
22
+
23
+ -- Indexes for efficient queries
24
+ CREATE INDEX IF NOT EXISTS idx_learning_candidates_status ON learning_candidates(status);
25
+ CREATE INDEX IF NOT EXISTS idx_learning_candidates_confidence ON learning_candidates(confidence);
26
+ CREATE INDEX IF NOT EXISTS idx_learning_candidates_source ON learning_candidates(source_file);
27
+ CREATE INDEX IF NOT EXISTS idx_learning_candidates_extracted ON learning_candidates(extracted_at);
28
+
29
+ -- Record this migration
30
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (10, datetime('now'));
@@ -0,0 +1,35 @@
1
+ -- Version: 011
2
+ -- Migration: Processed hashes table for JSONL line deduplication
3
+
4
+ -- Track processed JSONL line hashes for deduplication
5
+ -- Each unique line (by content hash) is recorded once to avoid re-processing
6
+ CREATE TABLE IF NOT EXISTS processed_hashes (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ content_hash TEXT NOT NULL UNIQUE, -- SHA256 of the JSONL line content
9
+ source_file TEXT NOT NULL, -- First file where this line was seen
10
+ source_line INTEGER NOT NULL, -- Line number in source file (1-indexed)
11
+ processed_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ );
13
+
14
+ -- Index for fast hash lookups (most common operation)
15
+ CREATE INDEX IF NOT EXISTS idx_processed_hashes_hash ON processed_hashes(content_hash);
16
+ -- Index for querying by source file
17
+ CREATE INDEX IF NOT EXISTS idx_processed_hashes_file ON processed_hashes(source_file);
18
+
19
+ -- Track file processing progress for incremental processing
20
+ -- Allows resuming from last position when a file changes
21
+ CREATE TABLE IF NOT EXISTS file_progress (
22
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ file_path TEXT NOT NULL UNIQUE, -- Absolute path to the JSONL file
24
+ last_line_processed INTEGER DEFAULT 0, -- Last line number processed (1-indexed)
25
+ last_byte_offset INTEGER DEFAULT 0, -- Byte offset for streaming resume
26
+ file_size INTEGER, -- Size at last processing time
27
+ file_checksum TEXT, -- SHA256 of file content at last processing
28
+ last_processed_at TEXT NOT NULL DEFAULT (datetime('now'))
29
+ );
30
+
31
+ -- Index for file path lookups
32
+ CREATE INDEX IF NOT EXISTS idx_file_progress_path ON file_progress(file_path);
33
+
34
+ -- Record this migration
35
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (11, datetime('now'));
@@ -0,0 +1,32 @@
1
+ -- Version: 012
2
+ -- Migration: Invalidation tracking and pinned anchors for PRD-017
3
+
4
+ -- Add pinned column to learning_anchors
5
+ ALTER TABLE learning_anchors ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;
6
+
7
+ -- Invalidation audit log
8
+ CREATE TABLE IF NOT EXISTS invalidation_log (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ anchor_id INTEGER NOT NULL,
11
+ old_status TEXT NOT NULL,
12
+ new_status TEXT NOT NULL,
13
+ reason TEXT NOT NULL,
14
+ detected_by TEXT NOT NULL CHECK (detected_by IN ('periodic', 'lazy', 'manual', 'agent', 'git_hook')),
15
+ old_content_hash TEXT,
16
+ new_content_hash TEXT,
17
+ similarity_score REAL,
18
+ invalidated_at TEXT NOT NULL DEFAULT (datetime('now')),
19
+ FOREIGN KEY (anchor_id) REFERENCES learning_anchors(id) ON DELETE CASCADE
20
+ );
21
+
22
+ -- Indexes for invalidation_log
23
+ CREATE INDEX IF NOT EXISTS idx_invalidation_anchor ON invalidation_log(anchor_id);
24
+ CREATE INDEX IF NOT EXISTS idx_invalidation_time ON invalidation_log(invalidated_at);
25
+ CREATE INDEX IF NOT EXISTS idx_invalidation_status ON invalidation_log(new_status);
26
+ CREATE INDEX IF NOT EXISTS idx_invalidation_detected_by ON invalidation_log(detected_by);
27
+
28
+ -- Index for pinned anchors
29
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_pinned ON learning_anchors(pinned) WHERE pinned = 1;
30
+
31
+ -- Record this migration
32
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (12, datetime('now'));
@@ -0,0 +1,13 @@
1
+ -- Version: 013
2
+ -- Migration: Add content_preview for self-healing comparison (PRD-017)
3
+
4
+ -- Add content_preview column to store original content snippet for Jaccard similarity
5
+ -- Used by self-healing to compare old vs new content when anchors drift
6
+ ALTER TABLE learning_anchors ADD COLUMN content_preview TEXT;
7
+
8
+ -- Index for faster lookups when content_preview is present
9
+ CREATE INDEX IF NOT EXISTS idx_learning_anchors_has_preview
10
+ ON learning_anchors(content_hash) WHERE content_preview IS NOT NULL;
11
+
12
+ -- Record this migration
13
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (13, datetime('now'));
@@ -0,0 +1,20 @@
1
+ -- Version: 014
2
+ -- Migration: Daemon tracked projects for opt-in project monitoring (PRD-015)
3
+
4
+ -- Tracked projects for the daemon to watch for JSONL transcript processing
5
+ -- Users explicitly opt-in projects via `tx daemon track <path>`
6
+ CREATE TABLE IF NOT EXISTS daemon_tracked_projects (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ project_path TEXT NOT NULL UNIQUE,
9
+ project_id TEXT,
10
+ source_type TEXT DEFAULT 'claude' CHECK (source_type IN ('claude', 'cursor', 'windsurf', 'other')),
11
+ added_at TEXT NOT NULL DEFAULT (datetime('now')),
12
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1))
13
+ );
14
+
15
+ -- Index for efficient enabled project queries
16
+ CREATE INDEX IF NOT EXISTS idx_daemon_tracked_projects_enabled
17
+ ON daemon_tracked_projects(enabled) WHERE enabled = 1;
18
+
19
+ -- Record this migration
20
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (14, datetime('now'));
@@ -0,0 +1,64 @@
1
+ -- Version: 015
2
+ -- Migration: Worker orchestration system (PRD-018)
3
+ -- Implements k8s-style worker orchestration with heartbeats, leases, and reconciliation
4
+
5
+ -- Workers table: registered worker processes
6
+ CREATE TABLE IF NOT EXISTS workers (
7
+ id TEXT PRIMARY KEY,
8
+ name TEXT NOT NULL,
9
+ hostname TEXT NOT NULL,
10
+ pid INTEGER NOT NULL,
11
+ status TEXT NOT NULL DEFAULT 'starting'
12
+ CHECK (status IN ('starting', 'idle', 'busy', 'stopping', 'dead')),
13
+ registered_at TEXT NOT NULL DEFAULT (datetime('now')),
14
+ last_heartbeat_at TEXT NOT NULL DEFAULT (datetime('now')),
15
+ current_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
16
+ capabilities TEXT NOT NULL DEFAULT '[]',
17
+ metadata TEXT NOT NULL DEFAULT '{}'
18
+ );
19
+
20
+ -- Task claims table: lease-based task ownership
21
+ CREATE TABLE IF NOT EXISTS task_claims (
22
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
24
+ worker_id TEXT NOT NULL REFERENCES workers(id) ON DELETE CASCADE,
25
+ claimed_at TEXT NOT NULL DEFAULT (datetime('now')),
26
+ lease_expires_at TEXT NOT NULL,
27
+ renewed_count INTEGER NOT NULL DEFAULT 0,
28
+ status TEXT NOT NULL DEFAULT 'active'
29
+ CHECK (status IN ('active', 'released', 'expired', 'completed'))
30
+ );
31
+
32
+ -- Orchestrator state table: singleton state for the orchestrator
33
+ -- Uses CHECK constraint to enforce singleton (only id=1 allowed)
34
+ CREATE TABLE IF NOT EXISTS orchestrator_state (
35
+ id INTEGER PRIMARY KEY CHECK (id = 1),
36
+ status TEXT NOT NULL DEFAULT 'stopped'
37
+ CHECK (status IN ('stopped', 'starting', 'running', 'stopping')),
38
+ pid INTEGER,
39
+ started_at TEXT,
40
+ last_reconcile_at TEXT,
41
+ worker_pool_size INTEGER NOT NULL DEFAULT 1,
42
+ reconcile_interval_seconds INTEGER NOT NULL DEFAULT 60,
43
+ heartbeat_interval_seconds INTEGER NOT NULL DEFAULT 30,
44
+ lease_duration_minutes INTEGER NOT NULL DEFAULT 30,
45
+ metadata TEXT NOT NULL DEFAULT '{}'
46
+ );
47
+
48
+ -- Indexes for workers
49
+ CREATE INDEX IF NOT EXISTS idx_workers_status ON workers(status);
50
+ CREATE INDEX IF NOT EXISTS idx_workers_last_heartbeat ON workers(last_heartbeat_at);
51
+ CREATE INDEX IF NOT EXISTS idx_workers_current_task ON workers(current_task_id) WHERE current_task_id IS NOT NULL;
52
+
53
+ -- Indexes for task_claims
54
+ CREATE INDEX IF NOT EXISTS idx_claims_task_id ON task_claims(task_id);
55
+ CREATE INDEX IF NOT EXISTS idx_claims_worker_id ON task_claims(worker_id);
56
+ CREATE INDEX IF NOT EXISTS idx_claims_status ON task_claims(status);
57
+ CREATE INDEX IF NOT EXISTS idx_claims_expires ON task_claims(lease_expires_at) WHERE status = 'active';
58
+ CREATE INDEX IF NOT EXISTS idx_claims_active_task ON task_claims(task_id, status) WHERE status = 'active';
59
+
60
+ -- Insert initial orchestrator state (singleton row)
61
+ INSERT OR IGNORE INTO orchestrator_state (id, status) VALUES (1, 'stopped');
62
+
63
+ -- Record this migration
64
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (15, datetime('now'));
@@ -0,0 +1,13 @@
1
+ -- Version: 016
2
+ -- Migration: Add stderr_path and stdout_path to runs table for PRD-019 execution tracing
3
+
4
+ -- Add stderr capture path (optional, orchestrator decides whether to capture)
5
+ ALTER TABLE runs ADD COLUMN stderr_path TEXT;
6
+
7
+ -- Add stdout capture path (optional, orchestrator decides whether to capture)
8
+ ALTER TABLE runs ADD COLUMN stdout_path TEXT;
9
+
10
+ -- Note: transcript_path already exists from migration 005
11
+
12
+ -- Record this migration
13
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (16, datetime('now'));
@@ -0,0 +1,47 @@
1
+ -- Version: 017
2
+ -- Migration: Add 'span' and 'metric' event types for PRD-019 execution tracing
3
+
4
+ -- SQLite doesn't support ALTER CHECK, so we need to recreate the table
5
+ -- with the updated constraint that includes 'span' and 'metric' event types
6
+
7
+ -- Step 1: Create new events table with updated CHECK constraint
8
+ CREATE TABLE events_new (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ timestamp TEXT NOT NULL,
11
+ event_type TEXT NOT NULL CHECK (event_type IN (
12
+ 'run_started', 'run_completed', 'run_failed',
13
+ 'task_created', 'task_updated', 'task_completed',
14
+ 'tool_call', 'tool_result',
15
+ 'user_message', 'assistant_message',
16
+ 'error', 'learning_captured',
17
+ 'commit', 'review',
18
+ 'span', 'metric'
19
+ )),
20
+ run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,
21
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
22
+ agent TEXT,
23
+ tool_name TEXT,
24
+ content TEXT,
25
+ metadata TEXT DEFAULT '{}',
26
+ duration_ms INTEGER
27
+ );
28
+
29
+ -- Step 2: Copy existing data
30
+ INSERT INTO events_new (id, timestamp, event_type, run_id, task_id, agent, tool_name, content, metadata, duration_ms)
31
+ SELECT id, timestamp, event_type, run_id, task_id, agent, tool_name, content, metadata, duration_ms FROM events;
32
+
33
+ -- Step 3: Drop old table
34
+ DROP TABLE events;
35
+
36
+ -- Step 4: Rename new table
37
+ ALTER TABLE events_new RENAME TO events;
38
+
39
+ -- Step 5: Recreate indexes (dropped with old table)
40
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp DESC);
41
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
42
+ CREATE INDEX IF NOT EXISTS idx_events_run ON events(run_id);
43
+ CREATE INDEX IF NOT EXISTS idx_events_task ON events(task_id);
44
+ CREATE INDEX IF NOT EXISTS idx_events_agent ON events(agent);
45
+
46
+ -- Record this migration
47
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (17, datetime('now'));
@@ -0,0 +1,15 @@
1
+ -- Version: 018
2
+ -- Migration: Add learnings column to compaction_log for data loss prevention
3
+ --
4
+ -- Fixes CRITICAL bug: learnings were only exported to file, not stored in DB.
5
+ -- If file export failed (disk full, permissions, etc.), learnings were lost
6
+ -- forever because the original tasks were already deleted.
7
+ --
8
+ -- Now learnings are stored in the database as the primary source of truth,
9
+ -- ensuring they're never lost even if file export fails.
10
+
11
+ -- Add learnings column to store actual learning content
12
+ ALTER TABLE compaction_log ADD COLUMN learnings TEXT;
13
+
14
+ -- Record this migration
15
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (18, datetime('now'));
@@ -0,0 +1,11 @@
1
+ -- Version: 019
2
+ -- Migration: Add index on tasks.updated_at for sync dirty detection
3
+ --
4
+ -- The sync status dirty check scans all tasks to find any updated after
5
+ -- the last export. Without an index on updated_at, this is O(n).
6
+ -- With the index, a SQL query can find changed tasks in O(log n).
7
+
8
+ CREATE INDEX IF NOT EXISTS idx_tasks_updated ON tasks(updated_at DESC);
9
+
10
+ -- Record this migration
11
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (19, datetime('now'));
@@ -0,0 +1,32 @@
1
+ -- Version: 020
2
+ -- Migration: Add database-level task ID format validation
3
+ --
4
+ -- Problem: tasks.id has no format constraint. ID validation (tx-[a-z0-9]{6,})
5
+ -- only exists in the app layer. JSONL import and direct SQL can insert malformed IDs.
6
+ --
7
+ -- Approach: Use BEFORE INSERT/UPDATE triggers instead of CHECK constraint because
8
+ -- SQLite does not support ALTER TABLE ADD CONSTRAINT, and recreating the tasks table
9
+ -- would cascade-delete all data in referencing tables (task_dependencies, attempts,
10
+ -- runs, events, task_claims, workers, file_learnings) when foreign_keys is ON.
11
+ -- Triggers provide identical database-level enforcement without table recreation.
12
+
13
+ -- Validate task ID format on INSERT
14
+ CREATE TRIGGER IF NOT EXISTS validate_task_id_insert
15
+ BEFORE INSERT ON tasks
16
+ FOR EACH ROW
17
+ WHEN NEW.id NOT GLOB 'tx-[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*'
18
+ BEGIN
19
+ SELECT RAISE(ABORT, 'Task ID must match format: tx-[a-z0-9]{6,}');
20
+ END;
21
+
22
+ -- Validate task ID format on UPDATE (prevents changing id to malformed value)
23
+ CREATE TRIGGER IF NOT EXISTS validate_task_id_update
24
+ BEFORE UPDATE OF id ON tasks
25
+ FOR EACH ROW
26
+ WHEN NEW.id NOT GLOB 'tx-[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*'
27
+ BEGIN
28
+ SELECT RAISE(ABORT, 'Task ID must match format: tx-[a-z0-9]{6,}');
29
+ END;
30
+
31
+ -- Record this migration
32
+ INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (20, datetime('now'));