gobby 0.2.5__py3-none-any.whl
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.
- gobby/__init__.py +3 -0
- gobby/adapters/__init__.py +30 -0
- gobby/adapters/base.py +93 -0
- gobby/adapters/claude_code.py +276 -0
- gobby/adapters/codex.py +1292 -0
- gobby/adapters/gemini.py +343 -0
- gobby/agents/__init__.py +37 -0
- gobby/agents/codex_session.py +120 -0
- gobby/agents/constants.py +112 -0
- gobby/agents/context.py +362 -0
- gobby/agents/definitions.py +133 -0
- gobby/agents/gemini_session.py +111 -0
- gobby/agents/registry.py +618 -0
- gobby/agents/runner.py +968 -0
- gobby/agents/session.py +259 -0
- gobby/agents/spawn.py +916 -0
- gobby/agents/spawners/__init__.py +77 -0
- gobby/agents/spawners/base.py +142 -0
- gobby/agents/spawners/cross_platform.py +266 -0
- gobby/agents/spawners/embedded.py +225 -0
- gobby/agents/spawners/headless.py +226 -0
- gobby/agents/spawners/linux.py +125 -0
- gobby/agents/spawners/macos.py +277 -0
- gobby/agents/spawners/windows.py +308 -0
- gobby/agents/tty_config.py +319 -0
- gobby/autonomous/__init__.py +32 -0
- gobby/autonomous/progress_tracker.py +447 -0
- gobby/autonomous/stop_registry.py +269 -0
- gobby/autonomous/stuck_detector.py +383 -0
- gobby/cli/__init__.py +67 -0
- gobby/cli/__main__.py +8 -0
- gobby/cli/agents.py +529 -0
- gobby/cli/artifacts.py +266 -0
- gobby/cli/daemon.py +329 -0
- gobby/cli/extensions.py +526 -0
- gobby/cli/github.py +263 -0
- gobby/cli/init.py +53 -0
- gobby/cli/install.py +614 -0
- gobby/cli/installers/__init__.py +37 -0
- gobby/cli/installers/antigravity.py +65 -0
- gobby/cli/installers/claude.py +363 -0
- gobby/cli/installers/codex.py +192 -0
- gobby/cli/installers/gemini.py +294 -0
- gobby/cli/installers/git_hooks.py +377 -0
- gobby/cli/installers/shared.py +737 -0
- gobby/cli/linear.py +250 -0
- gobby/cli/mcp.py +30 -0
- gobby/cli/mcp_proxy.py +698 -0
- gobby/cli/memory.py +304 -0
- gobby/cli/merge.py +384 -0
- gobby/cli/projects.py +79 -0
- gobby/cli/sessions.py +622 -0
- gobby/cli/tasks/__init__.py +30 -0
- gobby/cli/tasks/_utils.py +658 -0
- gobby/cli/tasks/ai.py +1025 -0
- gobby/cli/tasks/commits.py +169 -0
- gobby/cli/tasks/crud.py +685 -0
- gobby/cli/tasks/deps.py +135 -0
- gobby/cli/tasks/labels.py +63 -0
- gobby/cli/tasks/main.py +273 -0
- gobby/cli/tasks/search.py +178 -0
- gobby/cli/tui.py +34 -0
- gobby/cli/utils.py +513 -0
- gobby/cli/workflows.py +927 -0
- gobby/cli/worktrees.py +481 -0
- gobby/config/__init__.py +129 -0
- gobby/config/app.py +551 -0
- gobby/config/extensions.py +167 -0
- gobby/config/features.py +472 -0
- gobby/config/llm_providers.py +98 -0
- gobby/config/logging.py +66 -0
- gobby/config/mcp.py +346 -0
- gobby/config/persistence.py +247 -0
- gobby/config/servers.py +141 -0
- gobby/config/sessions.py +250 -0
- gobby/config/tasks.py +784 -0
- gobby/hooks/__init__.py +104 -0
- gobby/hooks/artifact_capture.py +213 -0
- gobby/hooks/broadcaster.py +243 -0
- gobby/hooks/event_handlers.py +723 -0
- gobby/hooks/events.py +218 -0
- gobby/hooks/git.py +169 -0
- gobby/hooks/health_monitor.py +171 -0
- gobby/hooks/hook_manager.py +856 -0
- gobby/hooks/hook_types.py +575 -0
- gobby/hooks/plugins.py +813 -0
- gobby/hooks/session_coordinator.py +396 -0
- gobby/hooks/verification_runner.py +268 -0
- gobby/hooks/webhooks.py +339 -0
- gobby/install/claude/commands/gobby/bug.md +51 -0
- gobby/install/claude/commands/gobby/chore.md +51 -0
- gobby/install/claude/commands/gobby/epic.md +52 -0
- gobby/install/claude/commands/gobby/eval.md +235 -0
- gobby/install/claude/commands/gobby/feat.md +49 -0
- gobby/install/claude/commands/gobby/nit.md +52 -0
- gobby/install/claude/commands/gobby/ref.md +52 -0
- gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
- gobby/install/claude/hooks/hook_dispatcher.py +364 -0
- gobby/install/claude/hooks/validate_settings.py +102 -0
- gobby/install/claude/hooks-template.json +118 -0
- gobby/install/codex/hooks/hook_dispatcher.py +153 -0
- gobby/install/codex/prompts/forget.md +7 -0
- gobby/install/codex/prompts/memories.md +7 -0
- gobby/install/codex/prompts/recall.md +7 -0
- gobby/install/codex/prompts/remember.md +13 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
- gobby/install/gemini/hooks-template.json +138 -0
- gobby/install/shared/plugins/code_guardian.py +456 -0
- gobby/install/shared/plugins/example_notify.py +331 -0
- gobby/integrations/__init__.py +10 -0
- gobby/integrations/github.py +145 -0
- gobby/integrations/linear.py +145 -0
- gobby/llm/__init__.py +40 -0
- gobby/llm/base.py +120 -0
- gobby/llm/claude.py +578 -0
- gobby/llm/claude_executor.py +503 -0
- gobby/llm/codex.py +322 -0
- gobby/llm/codex_executor.py +513 -0
- gobby/llm/executor.py +316 -0
- gobby/llm/factory.py +34 -0
- gobby/llm/gemini.py +258 -0
- gobby/llm/gemini_executor.py +339 -0
- gobby/llm/litellm.py +287 -0
- gobby/llm/litellm_executor.py +303 -0
- gobby/llm/resolver.py +499 -0
- gobby/llm/service.py +236 -0
- gobby/mcp_proxy/__init__.py +29 -0
- gobby/mcp_proxy/actions.py +175 -0
- gobby/mcp_proxy/daemon_control.py +198 -0
- gobby/mcp_proxy/importer.py +436 -0
- gobby/mcp_proxy/lazy.py +325 -0
- gobby/mcp_proxy/manager.py +798 -0
- gobby/mcp_proxy/metrics.py +609 -0
- gobby/mcp_proxy/models.py +139 -0
- gobby/mcp_proxy/registries.py +215 -0
- gobby/mcp_proxy/schema_hash.py +381 -0
- gobby/mcp_proxy/semantic_search.py +706 -0
- gobby/mcp_proxy/server.py +549 -0
- gobby/mcp_proxy/services/__init__.py +0 -0
- gobby/mcp_proxy/services/fallback.py +306 -0
- gobby/mcp_proxy/services/recommendation.py +224 -0
- gobby/mcp_proxy/services/server_mgmt.py +214 -0
- gobby/mcp_proxy/services/system.py +72 -0
- gobby/mcp_proxy/services/tool_filter.py +231 -0
- gobby/mcp_proxy/services/tool_proxy.py +309 -0
- gobby/mcp_proxy/stdio.py +565 -0
- gobby/mcp_proxy/tools/__init__.py +27 -0
- gobby/mcp_proxy/tools/agents.py +1103 -0
- gobby/mcp_proxy/tools/artifacts.py +207 -0
- gobby/mcp_proxy/tools/hub.py +335 -0
- gobby/mcp_proxy/tools/internal.py +337 -0
- gobby/mcp_proxy/tools/memory.py +543 -0
- gobby/mcp_proxy/tools/merge.py +422 -0
- gobby/mcp_proxy/tools/metrics.py +283 -0
- gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
- gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
- gobby/mcp_proxy/tools/orchestration/review.py +736 -0
- gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
- gobby/mcp_proxy/tools/session_messages.py +1056 -0
- gobby/mcp_proxy/tools/task_dependencies.py +219 -0
- gobby/mcp_proxy/tools/task_expansion.py +591 -0
- gobby/mcp_proxy/tools/task_github.py +393 -0
- gobby/mcp_proxy/tools/task_linear.py +379 -0
- gobby/mcp_proxy/tools/task_orchestration.py +77 -0
- gobby/mcp_proxy/tools/task_readiness.py +522 -0
- gobby/mcp_proxy/tools/task_sync.py +351 -0
- gobby/mcp_proxy/tools/task_validation.py +843 -0
- gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
- gobby/mcp_proxy/tools/tasks/_context.py +112 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
- gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
- gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
- gobby/mcp_proxy/tools/tasks/_search.py +215 -0
- gobby/mcp_proxy/tools/tasks/_session.py +125 -0
- gobby/mcp_proxy/tools/workflows.py +973 -0
- gobby/mcp_proxy/tools/worktrees.py +1264 -0
- gobby/mcp_proxy/transports/__init__.py +0 -0
- gobby/mcp_proxy/transports/base.py +95 -0
- gobby/mcp_proxy/transports/factory.py +44 -0
- gobby/mcp_proxy/transports/http.py +139 -0
- gobby/mcp_proxy/transports/stdio.py +213 -0
- gobby/mcp_proxy/transports/websocket.py +136 -0
- gobby/memory/backends/__init__.py +116 -0
- gobby/memory/backends/mem0.py +408 -0
- gobby/memory/backends/memu.py +485 -0
- gobby/memory/backends/null.py +111 -0
- gobby/memory/backends/openmemory.py +537 -0
- gobby/memory/backends/sqlite.py +304 -0
- gobby/memory/context.py +87 -0
- gobby/memory/manager.py +1001 -0
- gobby/memory/protocol.py +451 -0
- gobby/memory/search/__init__.py +66 -0
- gobby/memory/search/text.py +127 -0
- gobby/memory/viz.py +258 -0
- gobby/prompts/__init__.py +13 -0
- gobby/prompts/defaults/expansion/system.md +119 -0
- gobby/prompts/defaults/expansion/user.md +48 -0
- gobby/prompts/defaults/external_validation/agent.md +72 -0
- gobby/prompts/defaults/external_validation/external.md +63 -0
- gobby/prompts/defaults/external_validation/spawn.md +83 -0
- gobby/prompts/defaults/external_validation/system.md +6 -0
- gobby/prompts/defaults/features/import_mcp.md +22 -0
- gobby/prompts/defaults/features/import_mcp_github.md +17 -0
- gobby/prompts/defaults/features/import_mcp_search.md +16 -0
- gobby/prompts/defaults/features/recommend_tools.md +32 -0
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
- gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
- gobby/prompts/defaults/features/server_description.md +20 -0
- gobby/prompts/defaults/features/server_description_system.md +6 -0
- gobby/prompts/defaults/features/task_description.md +31 -0
- gobby/prompts/defaults/features/task_description_system.md +6 -0
- gobby/prompts/defaults/features/tool_summary.md +17 -0
- gobby/prompts/defaults/features/tool_summary_system.md +6 -0
- gobby/prompts/defaults/research/step.md +58 -0
- gobby/prompts/defaults/validation/criteria.md +47 -0
- gobby/prompts/defaults/validation/validate.md +38 -0
- gobby/prompts/loader.py +346 -0
- gobby/prompts/models.py +113 -0
- gobby/py.typed +0 -0
- gobby/runner.py +488 -0
- gobby/search/__init__.py +23 -0
- gobby/search/protocol.py +104 -0
- gobby/search/tfidf.py +232 -0
- gobby/servers/__init__.py +7 -0
- gobby/servers/http.py +636 -0
- gobby/servers/models.py +31 -0
- gobby/servers/routes/__init__.py +23 -0
- gobby/servers/routes/admin.py +416 -0
- gobby/servers/routes/dependencies.py +118 -0
- gobby/servers/routes/mcp/__init__.py +24 -0
- gobby/servers/routes/mcp/hooks.py +135 -0
- gobby/servers/routes/mcp/plugins.py +121 -0
- gobby/servers/routes/mcp/tools.py +1337 -0
- gobby/servers/routes/mcp/webhooks.py +159 -0
- gobby/servers/routes/sessions.py +582 -0
- gobby/servers/websocket.py +766 -0
- gobby/sessions/__init__.py +13 -0
- gobby/sessions/analyzer.py +322 -0
- gobby/sessions/lifecycle.py +240 -0
- gobby/sessions/manager.py +563 -0
- gobby/sessions/processor.py +225 -0
- gobby/sessions/summary.py +532 -0
- gobby/sessions/transcripts/__init__.py +41 -0
- gobby/sessions/transcripts/base.py +125 -0
- gobby/sessions/transcripts/claude.py +386 -0
- gobby/sessions/transcripts/codex.py +143 -0
- gobby/sessions/transcripts/gemini.py +195 -0
- gobby/storage/__init__.py +21 -0
- gobby/storage/agents.py +409 -0
- gobby/storage/artifact_classifier.py +341 -0
- gobby/storage/artifacts.py +285 -0
- gobby/storage/compaction.py +67 -0
- gobby/storage/database.py +357 -0
- gobby/storage/inter_session_messages.py +194 -0
- gobby/storage/mcp.py +680 -0
- gobby/storage/memories.py +562 -0
- gobby/storage/merge_resolutions.py +550 -0
- gobby/storage/migrations.py +860 -0
- gobby/storage/migrations_legacy.py +1359 -0
- gobby/storage/projects.py +166 -0
- gobby/storage/session_messages.py +251 -0
- gobby/storage/session_tasks.py +97 -0
- gobby/storage/sessions.py +817 -0
- gobby/storage/task_dependencies.py +223 -0
- gobby/storage/tasks/__init__.py +42 -0
- gobby/storage/tasks/_aggregates.py +180 -0
- gobby/storage/tasks/_crud.py +449 -0
- gobby/storage/tasks/_id.py +104 -0
- gobby/storage/tasks/_lifecycle.py +311 -0
- gobby/storage/tasks/_manager.py +889 -0
- gobby/storage/tasks/_models.py +300 -0
- gobby/storage/tasks/_ordering.py +119 -0
- gobby/storage/tasks/_path_cache.py +110 -0
- gobby/storage/tasks/_queries.py +343 -0
- gobby/storage/tasks/_search.py +143 -0
- gobby/storage/workflow_audit.py +393 -0
- gobby/storage/worktrees.py +547 -0
- gobby/sync/__init__.py +29 -0
- gobby/sync/github.py +333 -0
- gobby/sync/linear.py +304 -0
- gobby/sync/memories.py +284 -0
- gobby/sync/tasks.py +641 -0
- gobby/tasks/__init__.py +8 -0
- gobby/tasks/build_verification.py +193 -0
- gobby/tasks/commits.py +633 -0
- gobby/tasks/context.py +747 -0
- gobby/tasks/criteria.py +342 -0
- gobby/tasks/enhanced_validator.py +226 -0
- gobby/tasks/escalation.py +263 -0
- gobby/tasks/expansion.py +626 -0
- gobby/tasks/external_validator.py +764 -0
- gobby/tasks/issue_extraction.py +171 -0
- gobby/tasks/prompts/expand.py +327 -0
- gobby/tasks/research.py +421 -0
- gobby/tasks/tdd.py +352 -0
- gobby/tasks/tree_builder.py +263 -0
- gobby/tasks/validation.py +712 -0
- gobby/tasks/validation_history.py +357 -0
- gobby/tasks/validation_models.py +89 -0
- gobby/tools/__init__.py +0 -0
- gobby/tools/summarizer.py +170 -0
- gobby/tui/__init__.py +5 -0
- gobby/tui/api_client.py +281 -0
- gobby/tui/app.py +327 -0
- gobby/tui/screens/__init__.py +25 -0
- gobby/tui/screens/agents.py +333 -0
- gobby/tui/screens/chat.py +450 -0
- gobby/tui/screens/dashboard.py +377 -0
- gobby/tui/screens/memory.py +305 -0
- gobby/tui/screens/metrics.py +231 -0
- gobby/tui/screens/orchestrator.py +904 -0
- gobby/tui/screens/sessions.py +412 -0
- gobby/tui/screens/tasks.py +442 -0
- gobby/tui/screens/workflows.py +289 -0
- gobby/tui/screens/worktrees.py +174 -0
- gobby/tui/widgets/__init__.py +21 -0
- gobby/tui/widgets/chat.py +210 -0
- gobby/tui/widgets/conductor.py +104 -0
- gobby/tui/widgets/menu.py +132 -0
- gobby/tui/widgets/message_panel.py +160 -0
- gobby/tui/widgets/review_gate.py +224 -0
- gobby/tui/widgets/task_tree.py +99 -0
- gobby/tui/widgets/token_budget.py +166 -0
- gobby/tui/ws_client.py +258 -0
- gobby/utils/__init__.py +3 -0
- gobby/utils/daemon_client.py +235 -0
- gobby/utils/git.py +222 -0
- gobby/utils/id.py +38 -0
- gobby/utils/json_helpers.py +161 -0
- gobby/utils/logging.py +376 -0
- gobby/utils/machine_id.py +135 -0
- gobby/utils/metrics.py +589 -0
- gobby/utils/project_context.py +182 -0
- gobby/utils/project_init.py +263 -0
- gobby/utils/status.py +256 -0
- gobby/utils/validation.py +80 -0
- gobby/utils/version.py +23 -0
- gobby/workflows/__init__.py +4 -0
- gobby/workflows/actions.py +1310 -0
- gobby/workflows/approval_flow.py +138 -0
- gobby/workflows/artifact_actions.py +103 -0
- gobby/workflows/audit_helpers.py +110 -0
- gobby/workflows/autonomous_actions.py +286 -0
- gobby/workflows/context_actions.py +394 -0
- gobby/workflows/definitions.py +130 -0
- gobby/workflows/detection_helpers.py +208 -0
- gobby/workflows/engine.py +485 -0
- gobby/workflows/evaluator.py +669 -0
- gobby/workflows/git_utils.py +96 -0
- gobby/workflows/hooks.py +169 -0
- gobby/workflows/lifecycle_evaluator.py +613 -0
- gobby/workflows/llm_actions.py +70 -0
- gobby/workflows/loader.py +333 -0
- gobby/workflows/mcp_actions.py +60 -0
- gobby/workflows/memory_actions.py +272 -0
- gobby/workflows/premature_stop.py +164 -0
- gobby/workflows/session_actions.py +139 -0
- gobby/workflows/state_actions.py +123 -0
- gobby/workflows/state_manager.py +104 -0
- gobby/workflows/stop_signal_actions.py +163 -0
- gobby/workflows/summary_actions.py +344 -0
- gobby/workflows/task_actions.py +249 -0
- gobby/workflows/task_enforcement_actions.py +901 -0
- gobby/workflows/templates.py +52 -0
- gobby/workflows/todo_actions.py +84 -0
- gobby/workflows/webhook.py +223 -0
- gobby/workflows/webhook_executor.py +399 -0
- gobby/worktrees/__init__.py +5 -0
- gobby/worktrees/git.py +690 -0
- gobby/worktrees/merge/__init__.py +20 -0
- gobby/worktrees/merge/conflict_parser.py +177 -0
- gobby/worktrees/merge/resolver.py +485 -0
- gobby-0.2.5.dist-info/METADATA +351 -0
- gobby-0.2.5.dist-info/RECORD +383 -0
- gobby-0.2.5.dist-info/WHEEL +5 -0
- gobby-0.2.5.dist-info/entry_points.txt +2 -0
- gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
- gobby-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
"""Database migrations for local storage.
|
|
2
|
+
|
|
3
|
+
This module handles schema migrations for the Gobby database.
|
|
4
|
+
|
|
5
|
+
For new databases (version == 0):
|
|
6
|
+
The BASELINE_SCHEMA is applied directly, jumping to version 60.
|
|
7
|
+
|
|
8
|
+
For existing databases (0 < version < 60):
|
|
9
|
+
Legacy migrations are imported from migrations_legacy.py and run incrementally.
|
|
10
|
+
|
|
11
|
+
For all databases:
|
|
12
|
+
Any migrations in MIGRATIONS (v61+) are applied after the baseline/legacy path.
|
|
13
|
+
|
|
14
|
+
To add a new migration:
|
|
15
|
+
1. Add it to the MIGRATIONS list below with version = 61, 62, etc.
|
|
16
|
+
2. Use SQL strings for schema changes, callables for data migrations.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
|
|
22
|
+
from gobby.storage.database import LocalDatabase
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Migration can be SQL string or a callable that takes LocalDatabase
|
|
27
|
+
MigrationAction = str | Callable[[LocalDatabase], None]
|
|
28
|
+
|
|
29
|
+
# Baseline version - the schema state after all legacy migrations
|
|
30
|
+
BASELINE_VERSION = 60
|
|
31
|
+
|
|
32
|
+
# Baseline schema - applied directly for new databases
|
|
33
|
+
# This represents the final schema state after migrations 1-60
|
|
34
|
+
BASELINE_SCHEMA = """
|
|
35
|
+
-- Schema version tracking
|
|
36
|
+
CREATE TABLE schema_version (
|
|
37
|
+
version INTEGER PRIMARY KEY,
|
|
38
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
-- Projects
|
|
42
|
+
CREATE TABLE projects (
|
|
43
|
+
id TEXT PRIMARY KEY,
|
|
44
|
+
name TEXT NOT NULL UNIQUE,
|
|
45
|
+
repo_path TEXT,
|
|
46
|
+
github_url TEXT,
|
|
47
|
+
github_repo TEXT,
|
|
48
|
+
linear_team_id TEXT,
|
|
49
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
50
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX idx_projects_name ON projects(name);
|
|
53
|
+
|
|
54
|
+
-- Placeholder projects for orphaned/migrated data
|
|
55
|
+
INSERT INTO projects (id, name, repo_path, created_at, updated_at)
|
|
56
|
+
VALUES ('00000000-0000-0000-0000-000000000000', '_orphaned', NULL, datetime('now'), datetime('now'));
|
|
57
|
+
INSERT INTO projects (id, name, repo_path, created_at, updated_at)
|
|
58
|
+
VALUES ('00000000-0000-0000-0000-000000000001', '_migrated', NULL, datetime('now'), datetime('now'));
|
|
59
|
+
|
|
60
|
+
-- MCP Servers
|
|
61
|
+
CREATE TABLE mcp_servers (
|
|
62
|
+
id TEXT PRIMARY KEY,
|
|
63
|
+
name TEXT NOT NULL,
|
|
64
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
65
|
+
transport TEXT NOT NULL,
|
|
66
|
+
url TEXT,
|
|
67
|
+
command TEXT,
|
|
68
|
+
args TEXT,
|
|
69
|
+
env TEXT,
|
|
70
|
+
headers TEXT,
|
|
71
|
+
enabled INTEGER DEFAULT 1,
|
|
72
|
+
description TEXT,
|
|
73
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
74
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
75
|
+
);
|
|
76
|
+
CREATE INDEX idx_mcp_servers_name ON mcp_servers(name);
|
|
77
|
+
CREATE INDEX idx_mcp_servers_project_id ON mcp_servers(project_id);
|
|
78
|
+
CREATE INDEX idx_mcp_servers_enabled ON mcp_servers(enabled);
|
|
79
|
+
CREATE UNIQUE INDEX idx_mcp_servers_name_project ON mcp_servers(name, project_id);
|
|
80
|
+
|
|
81
|
+
-- Tools
|
|
82
|
+
CREATE TABLE tools (
|
|
83
|
+
id TEXT PRIMARY KEY,
|
|
84
|
+
mcp_server_id TEXT NOT NULL REFERENCES mcp_servers(id) ON DELETE CASCADE,
|
|
85
|
+
name TEXT NOT NULL,
|
|
86
|
+
description TEXT,
|
|
87
|
+
input_schema TEXT,
|
|
88
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
89
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
90
|
+
UNIQUE(mcp_server_id, name)
|
|
91
|
+
);
|
|
92
|
+
CREATE INDEX idx_tools_server_id ON tools(mcp_server_id);
|
|
93
|
+
CREATE INDEX idx_tools_name ON tools(name);
|
|
94
|
+
|
|
95
|
+
-- Tool embeddings for semantic search
|
|
96
|
+
CREATE TABLE tool_embeddings (
|
|
97
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
98
|
+
tool_id TEXT NOT NULL REFERENCES tools(id) ON DELETE CASCADE,
|
|
99
|
+
server_name TEXT NOT NULL,
|
|
100
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
101
|
+
embedding BLOB NOT NULL,
|
|
102
|
+
embedding_model TEXT NOT NULL,
|
|
103
|
+
embedding_dim INTEGER NOT NULL,
|
|
104
|
+
text_hash TEXT NOT NULL,
|
|
105
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
106
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
107
|
+
UNIQUE(tool_id)
|
|
108
|
+
);
|
|
109
|
+
CREATE INDEX idx_tool_embeddings_tool ON tool_embeddings(tool_id);
|
|
110
|
+
CREATE INDEX idx_tool_embeddings_server ON tool_embeddings(server_name);
|
|
111
|
+
CREATE INDEX idx_tool_embeddings_project ON tool_embeddings(project_id);
|
|
112
|
+
CREATE INDEX idx_tool_embeddings_hash ON tool_embeddings(text_hash);
|
|
113
|
+
|
|
114
|
+
-- Tool schema hashes for incremental re-indexing
|
|
115
|
+
CREATE TABLE tool_schema_hashes (
|
|
116
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
117
|
+
server_name TEXT NOT NULL,
|
|
118
|
+
tool_name TEXT NOT NULL,
|
|
119
|
+
project_id TEXT NOT NULL,
|
|
120
|
+
schema_hash TEXT NOT NULL,
|
|
121
|
+
last_verified_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
122
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
123
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
124
|
+
UNIQUE(project_id, server_name, tool_name)
|
|
125
|
+
);
|
|
126
|
+
CREATE INDEX idx_schema_hashes_server ON tool_schema_hashes(server_name);
|
|
127
|
+
CREATE INDEX idx_schema_hashes_project ON tool_schema_hashes(project_id);
|
|
128
|
+
CREATE INDEX idx_schema_hashes_verified ON tool_schema_hashes(last_verified_at);
|
|
129
|
+
|
|
130
|
+
-- Tool metrics
|
|
131
|
+
CREATE TABLE tool_metrics (
|
|
132
|
+
id TEXT PRIMARY KEY,
|
|
133
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
134
|
+
server_name TEXT NOT NULL,
|
|
135
|
+
tool_name TEXT NOT NULL,
|
|
136
|
+
call_count INTEGER NOT NULL DEFAULT 0,
|
|
137
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
138
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
139
|
+
total_latency_ms REAL NOT NULL DEFAULT 0,
|
|
140
|
+
avg_latency_ms REAL,
|
|
141
|
+
last_called_at TEXT,
|
|
142
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
143
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
144
|
+
UNIQUE(project_id, server_name, tool_name)
|
|
145
|
+
);
|
|
146
|
+
CREATE INDEX idx_tool_metrics_project ON tool_metrics(project_id);
|
|
147
|
+
CREATE INDEX idx_tool_metrics_server ON tool_metrics(server_name);
|
|
148
|
+
CREATE INDEX idx_tool_metrics_tool ON tool_metrics(tool_name);
|
|
149
|
+
CREATE INDEX idx_tool_metrics_call_count ON tool_metrics(call_count DESC);
|
|
150
|
+
CREATE INDEX idx_tool_metrics_last_called ON tool_metrics(last_called_at);
|
|
151
|
+
|
|
152
|
+
-- Tool metrics daily aggregates
|
|
153
|
+
CREATE TABLE tool_metrics_daily (
|
|
154
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
155
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
156
|
+
server_name TEXT NOT NULL,
|
|
157
|
+
tool_name TEXT NOT NULL,
|
|
158
|
+
date TEXT NOT NULL,
|
|
159
|
+
call_count INTEGER NOT NULL DEFAULT 0,
|
|
160
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
161
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
162
|
+
total_latency_ms REAL NOT NULL DEFAULT 0,
|
|
163
|
+
avg_latency_ms REAL,
|
|
164
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
165
|
+
UNIQUE(project_id, server_name, tool_name, date)
|
|
166
|
+
);
|
|
167
|
+
CREATE INDEX idx_tool_metrics_daily_project ON tool_metrics_daily(project_id);
|
|
168
|
+
CREATE INDEX idx_tool_metrics_daily_date ON tool_metrics_daily(date);
|
|
169
|
+
CREATE INDEX idx_tool_metrics_daily_server ON tool_metrics_daily(server_name);
|
|
170
|
+
|
|
171
|
+
-- Agent runs
|
|
172
|
+
CREATE TABLE agent_runs (
|
|
173
|
+
id TEXT PRIMARY KEY,
|
|
174
|
+
parent_session_id TEXT NOT NULL REFERENCES sessions(id),
|
|
175
|
+
child_session_id TEXT REFERENCES sessions(id),
|
|
176
|
+
workflow_name TEXT,
|
|
177
|
+
provider TEXT NOT NULL,
|
|
178
|
+
model TEXT,
|
|
179
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
180
|
+
prompt TEXT NOT NULL,
|
|
181
|
+
result TEXT,
|
|
182
|
+
error TEXT,
|
|
183
|
+
tool_calls_count INTEGER DEFAULT 0,
|
|
184
|
+
turns_used INTEGER DEFAULT 0,
|
|
185
|
+
started_at TEXT,
|
|
186
|
+
completed_at TEXT,
|
|
187
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
188
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
189
|
+
);
|
|
190
|
+
CREATE INDEX idx_agent_runs_parent_session ON agent_runs(parent_session_id);
|
|
191
|
+
CREATE INDEX idx_agent_runs_child_session ON agent_runs(child_session_id);
|
|
192
|
+
CREATE INDEX idx_agent_runs_status ON agent_runs(status);
|
|
193
|
+
CREATE INDEX idx_agent_runs_provider ON agent_runs(provider);
|
|
194
|
+
|
|
195
|
+
-- Sessions
|
|
196
|
+
CREATE TABLE sessions (
|
|
197
|
+
id TEXT PRIMARY KEY,
|
|
198
|
+
external_id TEXT NOT NULL,
|
|
199
|
+
machine_id TEXT NOT NULL,
|
|
200
|
+
source TEXT NOT NULL,
|
|
201
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
202
|
+
title TEXT,
|
|
203
|
+
status TEXT DEFAULT 'active',
|
|
204
|
+
jsonl_path TEXT,
|
|
205
|
+
summary_path TEXT,
|
|
206
|
+
summary_markdown TEXT,
|
|
207
|
+
compact_markdown TEXT,
|
|
208
|
+
git_branch TEXT,
|
|
209
|
+
parent_session_id TEXT REFERENCES sessions(id),
|
|
210
|
+
transcript_processed BOOLEAN DEFAULT FALSE,
|
|
211
|
+
agent_depth INTEGER DEFAULT 0,
|
|
212
|
+
spawned_by_agent_id TEXT,
|
|
213
|
+
workflow_name TEXT,
|
|
214
|
+
agent_run_id TEXT REFERENCES agent_runs(id) ON DELETE SET NULL,
|
|
215
|
+
context_injected INTEGER DEFAULT 0,
|
|
216
|
+
original_prompt TEXT,
|
|
217
|
+
usage_input_tokens INTEGER DEFAULT 0,
|
|
218
|
+
usage_output_tokens INTEGER DEFAULT 0,
|
|
219
|
+
usage_cache_creation_tokens INTEGER DEFAULT 0,
|
|
220
|
+
usage_cache_read_tokens INTEGER DEFAULT 0,
|
|
221
|
+
usage_total_cost_usd REAL DEFAULT 0.0,
|
|
222
|
+
terminal_context TEXT,
|
|
223
|
+
seq_num INTEGER,
|
|
224
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
225
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
226
|
+
);
|
|
227
|
+
CREATE INDEX idx_sessions_external_id ON sessions(external_id);
|
|
228
|
+
CREATE INDEX idx_sessions_machine_id ON sessions(machine_id);
|
|
229
|
+
CREATE INDEX idx_sessions_source ON sessions(source);
|
|
230
|
+
CREATE INDEX idx_sessions_status ON sessions(status);
|
|
231
|
+
CREATE INDEX idx_sessions_project_id ON sessions(project_id);
|
|
232
|
+
CREATE INDEX idx_sessions_pending_transcript ON sessions(status, transcript_processed)
|
|
233
|
+
WHERE status = 'expired' AND transcript_processed = FALSE;
|
|
234
|
+
CREATE INDEX idx_sessions_agent_depth ON sessions(agent_depth);
|
|
235
|
+
CREATE INDEX idx_sessions_spawned_by ON sessions(spawned_by_agent_id);
|
|
236
|
+
CREATE INDEX idx_sessions_workflow ON sessions(workflow_name);
|
|
237
|
+
CREATE INDEX idx_sessions_agent_run ON sessions(agent_run_id);
|
|
238
|
+
CREATE UNIQUE INDEX idx_sessions_seq_num ON sessions(seq_num);
|
|
239
|
+
CREATE UNIQUE INDEX idx_sessions_unique ON sessions(external_id, machine_id, source, project_id);
|
|
240
|
+
|
|
241
|
+
-- Session messages
|
|
242
|
+
CREATE TABLE session_messages (
|
|
243
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
244
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
245
|
+
message_index INTEGER NOT NULL,
|
|
246
|
+
role TEXT NOT NULL,
|
|
247
|
+
content TEXT NOT NULL,
|
|
248
|
+
content_type TEXT DEFAULT 'text',
|
|
249
|
+
tool_name TEXT,
|
|
250
|
+
tool_input TEXT,
|
|
251
|
+
tool_result TEXT,
|
|
252
|
+
timestamp TEXT NOT NULL,
|
|
253
|
+
raw_json TEXT,
|
|
254
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
255
|
+
UNIQUE(session_id, message_index)
|
|
256
|
+
);
|
|
257
|
+
CREATE INDEX idx_session_messages_session ON session_messages(session_id);
|
|
258
|
+
CREATE INDEX idx_session_messages_role ON session_messages(role);
|
|
259
|
+
CREATE INDEX idx_session_messages_timestamp ON session_messages(timestamp);
|
|
260
|
+
CREATE INDEX idx_session_messages_tool ON session_messages(tool_name);
|
|
261
|
+
|
|
262
|
+
-- Session message processing state
|
|
263
|
+
CREATE TABLE session_message_state (
|
|
264
|
+
session_id TEXT PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE,
|
|
265
|
+
last_byte_offset INTEGER DEFAULT 0,
|
|
266
|
+
last_message_index INTEGER DEFAULT 0,
|
|
267
|
+
last_processed_at TEXT,
|
|
268
|
+
processing_errors INTEGER DEFAULT 0,
|
|
269
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
-- Session artifacts with FTS
|
|
273
|
+
CREATE TABLE session_artifacts (
|
|
274
|
+
id TEXT PRIMARY KEY,
|
|
275
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
276
|
+
artifact_type TEXT NOT NULL,
|
|
277
|
+
content TEXT NOT NULL,
|
|
278
|
+
metadata_json TEXT,
|
|
279
|
+
source_file TEXT,
|
|
280
|
+
line_start INTEGER,
|
|
281
|
+
line_end INTEGER,
|
|
282
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
283
|
+
);
|
|
284
|
+
CREATE INDEX idx_session_artifacts_session ON session_artifacts(session_id);
|
|
285
|
+
CREATE INDEX idx_session_artifacts_type ON session_artifacts(artifact_type);
|
|
286
|
+
CREATE INDEX idx_session_artifacts_created ON session_artifacts(created_at);
|
|
287
|
+
CREATE VIRTUAL TABLE session_artifacts_fts USING fts5(id UNINDEXED, content);
|
|
288
|
+
|
|
289
|
+
-- Session stop signals for autonomous stop
|
|
290
|
+
CREATE TABLE session_stop_signals (
|
|
291
|
+
session_id TEXT PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE,
|
|
292
|
+
source TEXT NOT NULL,
|
|
293
|
+
reason TEXT,
|
|
294
|
+
requested_at TEXT NOT NULL,
|
|
295
|
+
acknowledged_at TEXT
|
|
296
|
+
);
|
|
297
|
+
CREATE INDEX idx_stop_signals_pending ON session_stop_signals(acknowledged_at)
|
|
298
|
+
WHERE acknowledged_at IS NULL;
|
|
299
|
+
|
|
300
|
+
-- Loop progress for autonomous progress tracking
|
|
301
|
+
CREATE TABLE loop_progress (
|
|
302
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
303
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
304
|
+
progress_type TEXT NOT NULL,
|
|
305
|
+
tool_name TEXT,
|
|
306
|
+
details TEXT,
|
|
307
|
+
recorded_at TEXT NOT NULL,
|
|
308
|
+
is_high_value INTEGER NOT NULL DEFAULT 0
|
|
309
|
+
);
|
|
310
|
+
CREATE INDEX idx_loop_progress_session ON loop_progress(session_id, recorded_at DESC);
|
|
311
|
+
CREATE INDEX idx_loop_progress_high_value ON loop_progress(session_id, is_high_value, recorded_at DESC)
|
|
312
|
+
WHERE is_high_value = 1;
|
|
313
|
+
|
|
314
|
+
-- Tasks
|
|
315
|
+
CREATE TABLE tasks (
|
|
316
|
+
id TEXT PRIMARY KEY,
|
|
317
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
318
|
+
parent_task_id TEXT REFERENCES tasks(id),
|
|
319
|
+
created_in_session_id TEXT REFERENCES sessions(id),
|
|
320
|
+
closed_in_session_id TEXT REFERENCES sessions(id),
|
|
321
|
+
closed_commit_sha TEXT,
|
|
322
|
+
closed_at TEXT,
|
|
323
|
+
title TEXT NOT NULL,
|
|
324
|
+
description TEXT,
|
|
325
|
+
status TEXT DEFAULT 'open',
|
|
326
|
+
priority INTEGER DEFAULT 2,
|
|
327
|
+
task_type TEXT DEFAULT 'task',
|
|
328
|
+
assignee TEXT,
|
|
329
|
+
labels TEXT,
|
|
330
|
+
closed_reason TEXT,
|
|
331
|
+
compacted_at TEXT,
|
|
332
|
+
summary TEXT,
|
|
333
|
+
validation_status TEXT CHECK(validation_status IN ('pending', 'valid', 'invalid')),
|
|
334
|
+
validation_feedback TEXT,
|
|
335
|
+
validation_override_reason TEXT,
|
|
336
|
+
original_instruction TEXT,
|
|
337
|
+
details TEXT,
|
|
338
|
+
category TEXT,
|
|
339
|
+
complexity_score INTEGER,
|
|
340
|
+
estimated_subtasks INTEGER,
|
|
341
|
+
expansion_context TEXT,
|
|
342
|
+
validation_criteria TEXT,
|
|
343
|
+
use_external_validator INTEGER DEFAULT 0,
|
|
344
|
+
validation_fail_count INTEGER DEFAULT 0,
|
|
345
|
+
workflow_name TEXT,
|
|
346
|
+
verification TEXT,
|
|
347
|
+
sequence_order INTEGER,
|
|
348
|
+
commits TEXT,
|
|
349
|
+
escalated_at TEXT,
|
|
350
|
+
escalation_reason TEXT,
|
|
351
|
+
github_issue_number INTEGER,
|
|
352
|
+
github_pr_number INTEGER,
|
|
353
|
+
github_repo TEXT,
|
|
354
|
+
linear_issue_id TEXT,
|
|
355
|
+
linear_team_id TEXT,
|
|
356
|
+
seq_num INTEGER,
|
|
357
|
+
path_cache TEXT,
|
|
358
|
+
agent_name TEXT,
|
|
359
|
+
reference_doc TEXT,
|
|
360
|
+
is_expanded INTEGER DEFAULT 0,
|
|
361
|
+
is_tdd_applied INTEGER DEFAULT 0,
|
|
362
|
+
requires_user_review INTEGER DEFAULT 0,
|
|
363
|
+
accepted_by_user INTEGER DEFAULT 0,
|
|
364
|
+
created_at TEXT NOT NULL,
|
|
365
|
+
updated_at TEXT NOT NULL
|
|
366
|
+
);
|
|
367
|
+
CREATE INDEX idx_tasks_project ON tasks(project_id);
|
|
368
|
+
CREATE INDEX idx_tasks_status ON tasks(status);
|
|
369
|
+
CREATE INDEX idx_tasks_parent ON tasks(parent_task_id);
|
|
370
|
+
CREATE INDEX idx_tasks_workflow ON tasks(workflow_name);
|
|
371
|
+
CREATE INDEX idx_tasks_sequence ON tasks(workflow_name, sequence_order);
|
|
372
|
+
CREATE INDEX idx_tasks_created_session ON tasks(created_in_session_id);
|
|
373
|
+
CREATE INDEX idx_tasks_closed_session ON tasks(closed_in_session_id);
|
|
374
|
+
CREATE UNIQUE INDEX idx_tasks_seq_num ON tasks(project_id, seq_num);
|
|
375
|
+
CREATE INDEX idx_tasks_path_cache ON tasks(path_cache);
|
|
376
|
+
|
|
377
|
+
-- Task dependencies
|
|
378
|
+
CREATE TABLE task_dependencies (
|
|
379
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
380
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
381
|
+
depends_on TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
382
|
+
dep_type TEXT NOT NULL,
|
|
383
|
+
created_at TEXT NOT NULL,
|
|
384
|
+
UNIQUE(task_id, depends_on, dep_type)
|
|
385
|
+
);
|
|
386
|
+
CREATE INDEX idx_deps_task ON task_dependencies(task_id);
|
|
387
|
+
CREATE INDEX idx_deps_depends_on ON task_dependencies(depends_on);
|
|
388
|
+
|
|
389
|
+
-- Session-task linkages
|
|
390
|
+
CREATE TABLE session_tasks (
|
|
391
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
392
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
393
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
394
|
+
action TEXT NOT NULL,
|
|
395
|
+
created_at TEXT NOT NULL,
|
|
396
|
+
UNIQUE(session_id, task_id, action)
|
|
397
|
+
);
|
|
398
|
+
CREATE INDEX idx_session_tasks_session ON session_tasks(session_id);
|
|
399
|
+
CREATE INDEX idx_session_tasks_task ON session_tasks(task_id);
|
|
400
|
+
|
|
401
|
+
-- Task validation history
|
|
402
|
+
CREATE TABLE task_validation_history (
|
|
403
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
404
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
405
|
+
iteration INTEGER NOT NULL,
|
|
406
|
+
status TEXT NOT NULL,
|
|
407
|
+
feedback TEXT,
|
|
408
|
+
issues TEXT,
|
|
409
|
+
context_type TEXT,
|
|
410
|
+
context_summary TEXT,
|
|
411
|
+
validator_type TEXT,
|
|
412
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
413
|
+
);
|
|
414
|
+
CREATE INDEX idx_validation_history_task ON task_validation_history(task_id);
|
|
415
|
+
|
|
416
|
+
-- Task selection history for stuck detection
|
|
417
|
+
CREATE TABLE task_selection_history (
|
|
418
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
419
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
420
|
+
task_id TEXT NOT NULL,
|
|
421
|
+
selected_at TEXT NOT NULL,
|
|
422
|
+
context TEXT
|
|
423
|
+
);
|
|
424
|
+
CREATE INDEX idx_task_selection_session ON task_selection_history(session_id, selected_at DESC);
|
|
425
|
+
CREATE INDEX idx_task_selection_task ON task_selection_history(session_id, task_id, selected_at DESC);
|
|
426
|
+
|
|
427
|
+
-- Workflow states
|
|
428
|
+
CREATE TABLE workflow_states (
|
|
429
|
+
session_id TEXT PRIMARY KEY,
|
|
430
|
+
workflow_name TEXT NOT NULL,
|
|
431
|
+
step TEXT NOT NULL,
|
|
432
|
+
step_entered_at TEXT,
|
|
433
|
+
step_action_count INTEGER DEFAULT 0,
|
|
434
|
+
total_action_count INTEGER DEFAULT 0,
|
|
435
|
+
artifacts TEXT,
|
|
436
|
+
observations TEXT,
|
|
437
|
+
reflection_pending INTEGER DEFAULT 0,
|
|
438
|
+
context_injected INTEGER DEFAULT 0,
|
|
439
|
+
variables TEXT,
|
|
440
|
+
task_list TEXT,
|
|
441
|
+
current_task_index INTEGER DEFAULT 0,
|
|
442
|
+
files_modified_this_task INTEGER DEFAULT 0,
|
|
443
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
444
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
445
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
-- Workflow audit log
|
|
449
|
+
CREATE TABLE workflow_audit_log (
|
|
450
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
451
|
+
session_id TEXT NOT NULL,
|
|
452
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
453
|
+
step TEXT NOT NULL,
|
|
454
|
+
event_type TEXT NOT NULL,
|
|
455
|
+
tool_name TEXT,
|
|
456
|
+
rule_id TEXT,
|
|
457
|
+
condition TEXT,
|
|
458
|
+
result TEXT NOT NULL,
|
|
459
|
+
reason TEXT,
|
|
460
|
+
context TEXT,
|
|
461
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
462
|
+
);
|
|
463
|
+
CREATE INDEX idx_audit_session ON workflow_audit_log(session_id);
|
|
464
|
+
CREATE INDEX idx_audit_timestamp ON workflow_audit_log(timestamp);
|
|
465
|
+
CREATE INDEX idx_audit_event_type ON workflow_audit_log(event_type);
|
|
466
|
+
CREATE INDEX idx_audit_result ON workflow_audit_log(result);
|
|
467
|
+
|
|
468
|
+
-- Memories
|
|
469
|
+
CREATE TABLE memories (
|
|
470
|
+
id TEXT PRIMARY KEY,
|
|
471
|
+
project_id TEXT REFERENCES projects(id),
|
|
472
|
+
memory_type TEXT NOT NULL,
|
|
473
|
+
content TEXT NOT NULL,
|
|
474
|
+
source_type TEXT,
|
|
475
|
+
source_session_id TEXT REFERENCES sessions(id),
|
|
476
|
+
importance REAL DEFAULT 0.5,
|
|
477
|
+
access_count INTEGER DEFAULT 0,
|
|
478
|
+
last_accessed_at TEXT,
|
|
479
|
+
tags TEXT,
|
|
480
|
+
media TEXT,
|
|
481
|
+
created_at TEXT NOT NULL,
|
|
482
|
+
updated_at TEXT NOT NULL
|
|
483
|
+
);
|
|
484
|
+
CREATE INDEX idx_memories_project ON memories(project_id);
|
|
485
|
+
CREATE INDEX idx_memories_type ON memories(memory_type);
|
|
486
|
+
CREATE INDEX idx_memories_importance ON memories(importance DESC);
|
|
487
|
+
|
|
488
|
+
-- Session-memory linkages
|
|
489
|
+
CREATE TABLE session_memories (
|
|
490
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
491
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
492
|
+
memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
493
|
+
action TEXT NOT NULL,
|
|
494
|
+
created_at TEXT NOT NULL,
|
|
495
|
+
UNIQUE(session_id, memory_id, action)
|
|
496
|
+
);
|
|
497
|
+
CREATE INDEX idx_session_memories_session ON session_memories(session_id);
|
|
498
|
+
CREATE INDEX idx_session_memories_memory ON session_memories(memory_id);
|
|
499
|
+
|
|
500
|
+
-- Memory cross-references
|
|
501
|
+
CREATE TABLE memory_crossrefs (
|
|
502
|
+
source_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
503
|
+
target_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
504
|
+
similarity REAL NOT NULL,
|
|
505
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
506
|
+
PRIMARY KEY (source_id, target_id)
|
|
507
|
+
);
|
|
508
|
+
CREATE INDEX idx_crossrefs_source ON memory_crossrefs(source_id);
|
|
509
|
+
CREATE INDEX idx_crossrefs_target ON memory_crossrefs(target_id);
|
|
510
|
+
CREATE INDEX idx_crossrefs_similarity ON memory_crossrefs(similarity DESC);
|
|
511
|
+
|
|
512
|
+
-- Worktrees
|
|
513
|
+
CREATE TABLE worktrees (
|
|
514
|
+
id TEXT PRIMARY KEY,
|
|
515
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
516
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
517
|
+
branch_name TEXT NOT NULL,
|
|
518
|
+
worktree_path TEXT NOT NULL,
|
|
519
|
+
base_branch TEXT DEFAULT 'main',
|
|
520
|
+
agent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
521
|
+
status TEXT DEFAULT 'active',
|
|
522
|
+
merge_state TEXT,
|
|
523
|
+
merged_at TEXT,
|
|
524
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
525
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
526
|
+
);
|
|
527
|
+
CREATE INDEX idx_worktrees_project ON worktrees(project_id);
|
|
528
|
+
CREATE INDEX idx_worktrees_status ON worktrees(status);
|
|
529
|
+
CREATE INDEX idx_worktrees_task ON worktrees(task_id);
|
|
530
|
+
CREATE INDEX idx_worktrees_session ON worktrees(agent_session_id);
|
|
531
|
+
CREATE UNIQUE INDEX idx_worktrees_branch ON worktrees(project_id, branch_name);
|
|
532
|
+
CREATE UNIQUE INDEX idx_worktrees_path ON worktrees(worktree_path);
|
|
533
|
+
|
|
534
|
+
-- Merge resolutions
|
|
535
|
+
CREATE TABLE merge_resolutions (
|
|
536
|
+
id TEXT PRIMARY KEY,
|
|
537
|
+
worktree_id TEXT NOT NULL REFERENCES worktrees(id) ON DELETE CASCADE,
|
|
538
|
+
source_branch TEXT NOT NULL,
|
|
539
|
+
target_branch TEXT NOT NULL,
|
|
540
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
541
|
+
tier_used TEXT,
|
|
542
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
543
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
544
|
+
);
|
|
545
|
+
CREATE INDEX idx_merge_resolutions_worktree ON merge_resolutions(worktree_id);
|
|
546
|
+
CREATE INDEX idx_merge_resolutions_status ON merge_resolutions(status);
|
|
547
|
+
CREATE INDEX idx_merge_resolutions_source_branch ON merge_resolutions(source_branch);
|
|
548
|
+
CREATE INDEX idx_merge_resolutions_target_branch ON merge_resolutions(target_branch);
|
|
549
|
+
|
|
550
|
+
-- Merge conflicts
|
|
551
|
+
CREATE TABLE merge_conflicts (
|
|
552
|
+
id TEXT PRIMARY KEY,
|
|
553
|
+
resolution_id TEXT NOT NULL REFERENCES merge_resolutions(id) ON DELETE CASCADE,
|
|
554
|
+
file_path TEXT NOT NULL,
|
|
555
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
556
|
+
ours_content TEXT,
|
|
557
|
+
theirs_content TEXT,
|
|
558
|
+
resolved_content TEXT,
|
|
559
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
560
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
561
|
+
);
|
|
562
|
+
CREATE INDEX idx_merge_conflicts_resolution ON merge_conflicts(resolution_id);
|
|
563
|
+
CREATE INDEX idx_merge_conflicts_file_path ON merge_conflicts(file_path);
|
|
564
|
+
CREATE INDEX idx_merge_conflicts_status ON merge_conflicts(status);
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
# Future migrations (v61+)
|
|
568
|
+
# Add new migrations here. Do not modify the baseline schema above.
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _migrate_test_strategy_to_category(db: LocalDatabase) -> None:
|
|
572
|
+
"""Rename test_strategy column to category if it exists.
|
|
573
|
+
|
|
574
|
+
This is a no-op for fresh databases that already have category in the baseline schema.
|
|
575
|
+
Only runs the rename for databases upgraded from versions before the rename.
|
|
576
|
+
"""
|
|
577
|
+
# Check if test_strategy column exists
|
|
578
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
579
|
+
if row and "test_strategy" in row["sql"].lower():
|
|
580
|
+
db.execute("ALTER TABLE tasks RENAME COLUMN test_strategy TO category")
|
|
581
|
+
logger.info("Renamed test_strategy column to category")
|
|
582
|
+
else:
|
|
583
|
+
logger.debug("test_strategy column not found (fresh database), skipping rename")
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _migrate_add_agent_name(db: LocalDatabase) -> None:
|
|
587
|
+
"""Add agent_name column to tasks table for agent configuration."""
|
|
588
|
+
# Check if agent_name column already exists (fresh database)
|
|
589
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
590
|
+
if row and "agent_name" not in row["sql"].lower():
|
|
591
|
+
db.execute("ALTER TABLE tasks ADD COLUMN agent_name TEXT")
|
|
592
|
+
logger.info("Added agent_name column to tasks table")
|
|
593
|
+
else:
|
|
594
|
+
logger.debug("agent_name column already exists, skipping")
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _migrate_add_reference_doc(db: LocalDatabase) -> None:
|
|
598
|
+
"""Add reference_doc column to tasks table for spec traceability."""
|
|
599
|
+
# Check if reference_doc column already exists (fresh database)
|
|
600
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
601
|
+
if row and "reference_doc" not in row["sql"].lower():
|
|
602
|
+
db.execute("ALTER TABLE tasks ADD COLUMN reference_doc TEXT")
|
|
603
|
+
logger.info("Added reference_doc column to tasks table")
|
|
604
|
+
else:
|
|
605
|
+
logger.debug("reference_doc column already exists, skipping")
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def _migrate_add_boolean_columns(db: LocalDatabase) -> None:
|
|
609
|
+
"""Add is_enriched, is_expanded, is_tdd_applied columns to tasks table."""
|
|
610
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
611
|
+
if not row:
|
|
612
|
+
return
|
|
613
|
+
|
|
614
|
+
sql_lower = row["sql"].lower()
|
|
615
|
+
|
|
616
|
+
# Add each column if it doesn't exist
|
|
617
|
+
if "is_enriched" not in sql_lower:
|
|
618
|
+
db.execute("ALTER TABLE tasks ADD COLUMN is_enriched INTEGER DEFAULT 0")
|
|
619
|
+
logger.info("Added is_enriched column to tasks table")
|
|
620
|
+
|
|
621
|
+
if "is_expanded" not in sql_lower:
|
|
622
|
+
db.execute("ALTER TABLE tasks ADD COLUMN is_expanded INTEGER DEFAULT 0")
|
|
623
|
+
logger.info("Added is_expanded column to tasks table")
|
|
624
|
+
|
|
625
|
+
if "is_tdd_applied" not in sql_lower:
|
|
626
|
+
db.execute("ALTER TABLE tasks ADD COLUMN is_tdd_applied INTEGER DEFAULT 0")
|
|
627
|
+
logger.info("Added is_tdd_applied column to tasks table")
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def _migrate_add_review_columns(db: LocalDatabase) -> None:
|
|
631
|
+
"""Add requires_user_review and accepted_by_user columns for review status support."""
|
|
632
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
633
|
+
if not row:
|
|
634
|
+
return
|
|
635
|
+
|
|
636
|
+
sql_lower = row["sql"].lower()
|
|
637
|
+
|
|
638
|
+
if "requires_user_review" not in sql_lower:
|
|
639
|
+
db.execute("ALTER TABLE tasks ADD COLUMN requires_user_review INTEGER DEFAULT 0")
|
|
640
|
+
logger.info("Added requires_user_review column to tasks table")
|
|
641
|
+
|
|
642
|
+
if "accepted_by_user" not in sql_lower:
|
|
643
|
+
db.execute("ALTER TABLE tasks ADD COLUMN accepted_by_user INTEGER DEFAULT 0")
|
|
644
|
+
logger.info("Added accepted_by_user column to tasks table")
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def _migrate_drop_is_enriched(db: LocalDatabase) -> None:
|
|
648
|
+
"""Drop deprecated is_enriched column from tasks table.
|
|
649
|
+
|
|
650
|
+
The is_enriched flag is no longer used after the Task Expansion V3 simplification.
|
|
651
|
+
SQLite 3.35.0+ supports ALTER TABLE DROP COLUMN.
|
|
652
|
+
"""
|
|
653
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'")
|
|
654
|
+
if not row:
|
|
655
|
+
return
|
|
656
|
+
|
|
657
|
+
if "is_enriched" in row["sql"].lower():
|
|
658
|
+
try:
|
|
659
|
+
db.execute("ALTER TABLE tasks DROP COLUMN is_enriched")
|
|
660
|
+
logger.info("Dropped is_enriched column from tasks table")
|
|
661
|
+
except Exception as e:
|
|
662
|
+
# SQLite < 3.35.0 doesn't support DROP COLUMN
|
|
663
|
+
# Column will remain but be unused - not a problem
|
|
664
|
+
logger.warning(f"Could not drop is_enriched column (SQLite < 3.35?): {e}")
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _migrate_add_inter_session_messages(db: LocalDatabase) -> None:
|
|
668
|
+
"""Add inter_session_messages table for parent-child agent communication.
|
|
669
|
+
|
|
670
|
+
This table enables asynchronous messaging between agent sessions,
|
|
671
|
+
allowing parent agents to send instructions to child agents and
|
|
672
|
+
receive status updates back.
|
|
673
|
+
"""
|
|
674
|
+
# Check if table already exists (fresh database with baseline)
|
|
675
|
+
row = db.fetchone(
|
|
676
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='inter_session_messages'"
|
|
677
|
+
)
|
|
678
|
+
if row:
|
|
679
|
+
logger.debug("inter_session_messages table already exists, skipping")
|
|
680
|
+
return
|
|
681
|
+
|
|
682
|
+
# Create the table
|
|
683
|
+
db.execute("""
|
|
684
|
+
CREATE TABLE inter_session_messages (
|
|
685
|
+
id TEXT PRIMARY KEY,
|
|
686
|
+
from_session TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
687
|
+
to_session TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
688
|
+
content TEXT NOT NULL,
|
|
689
|
+
priority TEXT NOT NULL DEFAULT 'normal',
|
|
690
|
+
sent_at TEXT NOT NULL,
|
|
691
|
+
read_at TEXT
|
|
692
|
+
)
|
|
693
|
+
""")
|
|
694
|
+
|
|
695
|
+
# Create indexes for efficient querying
|
|
696
|
+
db.execute(
|
|
697
|
+
"CREATE INDEX idx_inter_session_messages_from_session ON inter_session_messages(from_session)"
|
|
698
|
+
)
|
|
699
|
+
db.execute(
|
|
700
|
+
"CREATE INDEX idx_inter_session_messages_to_session ON inter_session_messages(to_session)"
|
|
701
|
+
)
|
|
702
|
+
db.execute(
|
|
703
|
+
"CREATE INDEX idx_inter_session_messages_unread ON inter_session_messages(to_session, read_at) "
|
|
704
|
+
"WHERE read_at IS NULL"
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
logger.info("Created inter_session_messages table with indexes")
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _migrate_add_media_column(db: LocalDatabase) -> None:
|
|
711
|
+
"""Add media column to memories table for multimodal support."""
|
|
712
|
+
# Check if media column already exists (fresh database from baseline)
|
|
713
|
+
row = db.fetchone("SELECT sql FROM sqlite_master WHERE type='table' AND name='memories'")
|
|
714
|
+
if row and "media" not in row["sql"].lower():
|
|
715
|
+
db.execute("ALTER TABLE memories ADD COLUMN media TEXT")
|
|
716
|
+
logger.info("Added media column to memories table")
|
|
717
|
+
else:
|
|
718
|
+
logger.debug("media column already exists, skipping")
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
MIGRATIONS: list[tuple[int, str, MigrationAction]] = [
|
|
722
|
+
# TDD Expansion Restructure: Rename test_strategy to category
|
|
723
|
+
(61, "Rename test_strategy to category", _migrate_test_strategy_to_category),
|
|
724
|
+
# TDD Expansion Restructure: Add agent_name column
|
|
725
|
+
(62, "Add agent_name column to tasks", _migrate_add_agent_name),
|
|
726
|
+
# TDD Expansion Restructure: Add reference_doc column
|
|
727
|
+
(63, "Add reference_doc column to tasks", _migrate_add_reference_doc),
|
|
728
|
+
# TDD Expansion Restructure: Add boolean columns for idempotent operations
|
|
729
|
+
(64, "Add boolean columns to tasks", _migrate_add_boolean_columns),
|
|
730
|
+
# Review status: Add columns for HITL review workflow
|
|
731
|
+
(65, "Add review columns to tasks", _migrate_add_review_columns),
|
|
732
|
+
# Task Expansion V3: Drop unused is_enriched column
|
|
733
|
+
(66, "Drop is_enriched column from tasks", _migrate_drop_is_enriched),
|
|
734
|
+
# Inter-session messaging: Add table for parent-child agent communication
|
|
735
|
+
(67, "Add inter_session_messages table", _migrate_add_inter_session_messages),
|
|
736
|
+
# Memory V3 Phase 2: Add media column for multimodal support
|
|
737
|
+
(68, "Add media column to memories", _migrate_add_media_column),
|
|
738
|
+
]
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def get_current_version(db: LocalDatabase) -> int:
|
|
742
|
+
"""Get current schema version from database."""
|
|
743
|
+
try:
|
|
744
|
+
row = db.fetchone("SELECT MAX(version) as version FROM schema_version")
|
|
745
|
+
return row["version"] if row and row["version"] else 0
|
|
746
|
+
except Exception:
|
|
747
|
+
return 0
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def _apply_baseline(db: LocalDatabase) -> None:
|
|
751
|
+
"""Apply baseline schema for new databases."""
|
|
752
|
+
logger.info("Applying baseline schema (v60)")
|
|
753
|
+
|
|
754
|
+
# Execute baseline schema
|
|
755
|
+
for statement in BASELINE_SCHEMA.strip().split(";"):
|
|
756
|
+
statement = statement.strip()
|
|
757
|
+
if statement:
|
|
758
|
+
db.execute(statement)
|
|
759
|
+
|
|
760
|
+
# Record baseline version
|
|
761
|
+
db.execute(
|
|
762
|
+
"INSERT INTO schema_version (version) VALUES (?)",
|
|
763
|
+
(BASELINE_VERSION,),
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
logger.info(f"Baseline schema applied, now at version {BASELINE_VERSION}")
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def _run_migration_list(
|
|
770
|
+
db: LocalDatabase,
|
|
771
|
+
current_version: int,
|
|
772
|
+
migrations: list[tuple[int, str, MigrationAction]],
|
|
773
|
+
) -> int:
|
|
774
|
+
"""
|
|
775
|
+
Run migrations from a list.
|
|
776
|
+
|
|
777
|
+
Args:
|
|
778
|
+
db: LocalDatabase instance
|
|
779
|
+
current_version: Current schema version
|
|
780
|
+
migrations: List of (version, description, action) tuples
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
Number of migrations applied
|
|
784
|
+
"""
|
|
785
|
+
applied = 0
|
|
786
|
+
last_version = current_version
|
|
787
|
+
|
|
788
|
+
for version, description, action in migrations:
|
|
789
|
+
if version > current_version:
|
|
790
|
+
logger.debug(f"Applying migration {version}: {description}")
|
|
791
|
+
try:
|
|
792
|
+
if callable(action):
|
|
793
|
+
# Python data migration
|
|
794
|
+
action(db)
|
|
795
|
+
else:
|
|
796
|
+
# SQL migration (may contain multiple statements)
|
|
797
|
+
for statement in action.strip().split(";"):
|
|
798
|
+
statement = statement.strip()
|
|
799
|
+
if statement:
|
|
800
|
+
db.execute(statement)
|
|
801
|
+
|
|
802
|
+
# Record migration
|
|
803
|
+
db.execute(
|
|
804
|
+
"INSERT INTO schema_version (version) VALUES (?)",
|
|
805
|
+
(version,),
|
|
806
|
+
)
|
|
807
|
+
applied += 1
|
|
808
|
+
last_version = version
|
|
809
|
+
except Exception as e:
|
|
810
|
+
logger.error(f"Migration {version} failed: {e}")
|
|
811
|
+
raise
|
|
812
|
+
|
|
813
|
+
if applied > 0:
|
|
814
|
+
logger.debug(f"Applied {applied} migration(s), now at version {last_version}")
|
|
815
|
+
|
|
816
|
+
return applied
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def run_migrations(db: LocalDatabase) -> int:
|
|
820
|
+
"""
|
|
821
|
+
Run pending migrations.
|
|
822
|
+
|
|
823
|
+
For new databases (version == 0):
|
|
824
|
+
Applies baseline schema directly, jumping to version 60.
|
|
825
|
+
|
|
826
|
+
For existing databases (0 < version < 60):
|
|
827
|
+
Imports and runs legacy migrations incrementally.
|
|
828
|
+
|
|
829
|
+
For all databases:
|
|
830
|
+
Runs any new migrations (v61+) after baseline/legacy path.
|
|
831
|
+
|
|
832
|
+
Args:
|
|
833
|
+
db: LocalDatabase instance
|
|
834
|
+
|
|
835
|
+
Returns:
|
|
836
|
+
Number of migrations applied
|
|
837
|
+
"""
|
|
838
|
+
current_version = get_current_version(db)
|
|
839
|
+
total_applied = 0
|
|
840
|
+
|
|
841
|
+
if current_version == 0:
|
|
842
|
+
# New database: apply baseline schema directly
|
|
843
|
+
_apply_baseline(db)
|
|
844
|
+
total_applied = 1
|
|
845
|
+
current_version = BASELINE_VERSION
|
|
846
|
+
elif current_version < BASELINE_VERSION:
|
|
847
|
+
# Existing database needing legacy migrations
|
|
848
|
+
# Lazy import to avoid loading legacy code for new databases
|
|
849
|
+
from gobby.storage.migrations_legacy import LEGACY_MIGRATIONS
|
|
850
|
+
|
|
851
|
+
applied = _run_migration_list(db, current_version, LEGACY_MIGRATIONS)
|
|
852
|
+
total_applied += applied
|
|
853
|
+
current_version = get_current_version(db)
|
|
854
|
+
|
|
855
|
+
# Run any new migrations (v61+)
|
|
856
|
+
if MIGRATIONS:
|
|
857
|
+
applied = _run_migration_list(db, current_version, MIGRATIONS)
|
|
858
|
+
total_applied += applied
|
|
859
|
+
|
|
860
|
+
return total_applied
|