# AirCoding Session DB Schema V1 Date: 2026-05-26 Status: Canonical schema baseline for V1.0.0 Alpha skeleton This document defines the first implementation-facing SQLite schema for per-session state. Session DB path: ```text /.air/local/sessions//session.db ``` Project-level DBs remain outside this session schema: ```text /.air/local/debug-records.db /.air/local/learned-memory.db ``` ## 1. SQLite Runtime Settings ```sql PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = OFF; ``` Rationale: - WAL supports concurrent read/write patterns needed by runtime and TUI projection. - `NORMAL` is sufficient for local session state and faster than `FULL`. - Foreign keys are disabled in MVP to reduce migration/recovery complexity. Application-level consistency checks handle references. This can be revisited after schema stabilizes. Transaction rules: - A durable event insert and its corresponding domain table update must be in the same transaction. - Artifact file writes use temp file → atomic rename → DB record. - `ui_state` is flushed periodically and on normal exit. ## 2. schema_meta ```sql CREATE TABLE schema_meta ( key TEXT PRIMARY KEY, value TEXT NOT NULL ); ``` Initial keys: ```text schema_version = 1 created_by = aircoding created_at = aircoding_version_created = aircoding_version_last_opened = ``` ## 3. sessions ```sql CREATE TABLE sessions ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL, project_root TEXT NOT NULL, title TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, exited_at TEXT, model_provider_id TEXT, model_id TEXT, metadata_json TEXT ); ``` Status values: ```text active | archived | deleted ``` ## 4. messages ```sql CREATE TABLE messages ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, role TEXT NOT NULL, canonical_format TEXT NOT NULL, content_json TEXT NOT NULL, parent_message_id TEXT, route_json TEXT, created_at TEXT NOT NULL, token_estimate INTEGER, metadata_json TEXT ); CREATE INDEX idx_messages_session_created ON messages(session_id, created_at); ``` `canonical_format` is `anthropic` in V1. ## 5. message_drafts ```sql CREATE TABLE message_drafts ( message_id TEXT PRIMARY KEY, session_id TEXT NOT NULL, role TEXT NOT NULL, canonical_format TEXT NOT NULL, partial_content_json TEXT NOT NULL, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, metadata_json TEXT ); CREATE INDEX idx_drafts_session_status ON message_drafts(session_id, status); ``` Drafts are deleted after the completed message is written to `messages`. Status values: ```text streaming | interrupted | error ``` ## 6. events ```sql CREATE TABLE events ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, type TEXT NOT NULL, version INTEGER NOT NULL, timestamp TEXT NOT NULL, source_kind TEXT NOT NULL, source_id TEXT, agent_type TEXT, task_id TEXT, agent_id TEXT, tool_run_id TEXT, command_run_id TEXT, route_json TEXT NOT NULL, route_text TEXT NOT NULL, payload_json TEXT NOT NULL ); CREATE INDEX idx_events_session_type_time ON events(session_id, type, timestamp); CREATE INDEX idx_events_task_time ON events(task_id, timestamp); CREATE INDEX idx_events_agent_time ON events(agent_id, timestamp); CREATE INDEX idx_events_route_text ON events(route_text); ``` ## 7. tasks ```sql CREATE TABLE tasks ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, type TEXT NOT NULL, status TEXT NOT NULL, title TEXT NOT NULL, task_spec_json TEXT NOT NULL, worker_result_json TEXT, assigned_agent_id TEXT, workspace_id TEXT, retry_count INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, started_at TEXT, completed_at TEXT, heartbeat_at TEXT, metadata_json TEXT ); CREATE INDEX idx_tasks_session_status ON tasks(session_id, status); ``` Task status values: ```text pending | running | completed | failed | blocked | cancelled | interrupted ``` ## 8. task_dependencies ```sql CREATE TABLE task_dependencies ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT NOT NULL, depends_on_task_id TEXT NOT NULL, dependency_type TEXT NOT NULL, reason TEXT, created_at TEXT NOT NULL ); CREATE INDEX idx_task_deps_task ON task_dependencies(task_id); CREATE INDEX idx_task_deps_depends_on ON task_dependencies(depends_on_task_id); CREATE INDEX idx_task_deps_session_type ON task_dependencies(session_id, dependency_type); ``` Dependency types: ```text hard | soft | conflict | serialization ``` ## 9. task_attempts ```sql CREATE TABLE task_attempts ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT NOT NULL, attempt_index INTEGER NOT NULL, agent_id TEXT, status TEXT NOT NULL, failure_signature TEXT, failure_summary TEXT, started_at TEXT NOT NULL, completed_at TEXT, worker_result_json TEXT, metadata_json TEXT ); CREATE INDEX idx_task_attempts_task ON task_attempts(task_id, attempt_index); CREATE INDEX idx_task_attempts_failure_signature ON task_attempts(failure_signature); ``` ## 10. agents ```sql CREATE TABLE agents ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, type TEXT NOT NULL, status TEXT NOT NULL, pid INTEGER, task_id TEXT, model_provider_id TEXT, model_id TEXT, started_at TEXT NOT NULL, completed_at TEXT, last_heartbeat_at TEXT, metadata_json TEXT ); CREATE INDEX idx_agents_session_status ON agents(session_id, status); CREATE INDEX idx_agents_task ON agents(task_id); ``` Agent status values: ```text starting | running | completed | failed | lost | cancelled ``` ## 11. tool_runs ```sql CREATE TABLE tool_runs ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT, agent_id TEXT, origin_message_id TEXT, tool_name TEXT NOT NULL, status TEXT NOT NULL, input_json TEXT NOT NULL, output_json TEXT, error_json TEXT, started_at TEXT NOT NULL, completed_at TEXT, duration_ms INTEGER, artifacts_json TEXT, evidence_refs_json TEXT, metadata_json TEXT ); CREATE INDEX idx_tool_runs_origin_message ON tool_runs(origin_message_id); CREATE INDEX idx_tool_runs_session_time ON tool_runs(session_id, started_at); CREATE INDEX idx_tool_runs_task_time ON tool_runs(task_id, started_at); CREATE INDEX idx_tool_runs_agent_time ON tool_runs(agent_id, started_at); ``` Status values: ```text running | ok | error | cancelled ``` ## 12. command_runs ```sql CREATE TABLE command_runs ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT, agent_id TEXT, origin_message_id TEXT, tool_run_id TEXT, command TEXT NOT NULL, cwd TEXT NOT NULL, exit_code INTEGER, stdout_artifact_id TEXT, stderr_artifact_id TEXT, combined_artifact_id TEXT, started_at TEXT NOT NULL, completed_at TEXT, duration_ms INTEGER, parsed_diagnostics_json TEXT, metadata_json TEXT ); CREATE INDEX idx_command_runs_origin_message ON command_runs(origin_message_id); CREATE INDEX idx_command_runs_session_time ON command_runs(session_id, started_at); CREATE INDEX idx_command_runs_task_time ON command_runs(task_id, started_at); CREATE INDEX idx_command_runs_agent_time ON command_runs(agent_id, started_at); ``` ## 13. artifacts ```sql CREATE TABLE artifacts ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, type TEXT NOT NULL, uri TEXT NOT NULL, path TEXT NOT NULL, original_name TEXT, size_bytes INTEGER, sha256 TEXT, task_id TEXT, agent_id TEXT, tool_run_id TEXT, command_run_id TEXT, associated_entity_type TEXT, associated_entity_id TEXT, created_at TEXT NOT NULL, metadata_json TEXT ); CREATE INDEX idx_artifacts_session_type_time ON artifacts(session_id, type, created_at); CREATE INDEX idx_artifacts_task ON artifacts(task_id); CREATE INDEX idx_artifacts_agent ON artifacts(agent_id); CREATE INDEX idx_artifacts_tool_run ON artifacts(tool_run_id); CREATE INDEX idx_artifacts_command_run ON artifacts(command_run_id); ``` ## 14. diagnostics ```sql CREATE TABLE diagnostics ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT, agent_id TEXT, command_run_id TEXT, artifact_id TEXT, language TEXT, toolchain TEXT, severity TEXT, file TEXT, line INTEGER, column INTEGER, code TEXT, message TEXT NOT NULL, semantic_signature TEXT NOT NULL, created_at TEXT NOT NULL, metadata_json TEXT ); CREATE INDEX idx_diagnostics_signature ON diagnostics(semantic_signature); CREATE INDEX idx_diagnostics_file ON diagnostics(file); CREATE INDEX idx_diagnostics_command ON diagnostics(command_run_id); CREATE INDEX idx_diagnostics_task ON diagnostics(task_id); ``` ## 15. evidence_refs ```sql CREATE TABLE evidence_refs ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT, agent_id TEXT, tool_run_id TEXT, command_run_id TEXT, artifact_id TEXT, diagnostic_id TEXT, message_id TEXT, kind TEXT NOT NULL, ref TEXT NOT NULL, location_json TEXT, claim TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE INDEX idx_evidence_task ON evidence_refs(task_id); CREATE INDEX idx_evidence_artifact ON evidence_refs(artifact_id); CREATE INDEX idx_evidence_diagnostic ON evidence_refs(diagnostic_id); ``` ## 16. workspaces ```sql CREATE TABLE workspaces ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, task_id TEXT, agent_id TEXT, path TEXT NOT NULL, strategy TEXT NOT NULL, status TEXT NOT NULL, base_ref TEXT, branch_name TEXT, created_at TEXT NOT NULL, merged_at TEXT, metadata_json TEXT ); CREATE INDEX idx_workspaces_task ON workspaces(task_id); CREATE INDEX idx_workspaces_status ON workspaces(session_id, status); ``` Workspace strategy values: ```text main | worktree | isolated_copy ``` Workspace status values: ```text active | merged | conflicted | abandoned | cleaned ``` ## 17. summaries ```sql CREATE TABLE summaries ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, type TEXT NOT NULL, range_start_message_id TEXT, range_end_message_id TEXT, content_json TEXT NOT NULL, created_at TEXT NOT NULL, metadata_json TEXT ); ``` ## 18. ui_state ```sql CREATE TABLE ui_state ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, scope TEXT NOT NULL, key TEXT NOT NULL, value_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE UNIQUE INDEX idx_ui_state_session_scope_key ON ui_state(session_id, scope, key); ``` `ui_state` is not a source of truth for runtime state. It stores route, active panel, layout, scroll, collapse state, selected tab, and HUD preset. ## 19. V1 Summary Frozen decisions: 1. Session DB is project-local and per-session. 2. WAL + synchronous NORMAL + foreign_keys OFF. 3. No `message_parts` source-of-truth table in V1.0.0 Alpha. 4. Canonical messages are Anthropic content JSON. 5. Draft messages exist only during streaming/incomplete assistant output. 6. Domain tables are scheduling/recovery/query source of truth. 7. JSON columns preserve full object fidelity; frequently queried relationships are extracted into columns and indexes. 8. Durable event + domain update must be transactional. 9. Artifact files are written temp → rename → DB record. 10. UI state is flushed periodically and on normal exit. ## 20. Project-Level DBs Two project-level databases live under `/.air/local/`: ### 20.1 debug-records.db ```sql PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = OFF; CREATE TABLE debug_records ( id TEXT PRIMARY KEY, task_id TEXT, failure_signature TEXT NOT NULL, summary TEXT NOT NULL, root_cause TEXT, fix_ref TEXT, evidence_json TEXT, verification_json TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, metadata_json TEXT ); CREATE INDEX idx_debug_records_signature ON debug_records(failure_signature); CREATE INDEX idx_debug_records_task ON debug_records(task_id); ``` ### 20.2 learned-memory.db ```sql PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = OFF; CREATE TABLE learned_memories ( id TEXT PRIMARY KEY, memory_type TEXT NOT NULL, summary TEXT NOT NULL, content TEXT, source_entity_type TEXT, source_entity_id TEXT, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, metadata_json TEXT ); CREATE INDEX idx_learned_memories_type ON learned_memories(memory_type); CREATE INDEX idx_learned_memories_status ON learned_memories(status); ``` ## 21. Closed Enum Inventory The following TEXT columns use closed value sets. Implementations must validate on insert/update. | Table | Column | Valid values | |---|---|---| | `sessions` | `status` | `active`, `archived`, `deleted` | | `messages` | `role` | `user`, `assistant`, `system`, `tool` | | `messages` | `canonical_format` | `anthropic` | | `tasks` | `type` | `execute`, `review`, `debug`, `compact`, `mine_experience`, `docs` | | `tasks` | `status` | `pending`, `running`, `completed`, `failed`, `blocked`, `cancelled`, `interrupted` | | `task_dependencies` | `dependency_type` | `hard`, `soft`, `conflict`, `serialization` | | `task_attempts` | `status` | `pending`, `running`, `completed`, `failed`, `cancelled` | | `agents` | `status` | `starting`, `running`, `completed`, `failed`, `lost`, `cancelled` | | `tool_runs` | `status` | `running`, `ok`, `error`, `cancelled` | | `artifacts` | `type` | `log`, `diff`, `screenshot`, `pcap`, `report`, `diagnostic`, `bundle`, `other` | | `diagnostics` | `severity` | `error`, `warning`, `info`, `hint` | | `evidence_refs` | `kind` | `build_output`, `test_output`, `log`, `screenshot`, `diff`, `metric`, `other` | | `workspaces` | `strategy` | `main`, `worktree`, `isolated_copy` | | `workspaces` | `status` | `active`, `merged`, `conflicted`, `abandoned`, `cleaned` | | `summaries` | `type` | `compaction`, `checkpoint`, `review`, `other` | | `message_drafts` | `status` | `streaming`, `interrupted`, `error` | | `learned_memories` | `memory_type` | `project_rule`, `toolchain_rule`, `skill_update`, `debug_experience` | | `learned_memories` | `status` | `candidate`, `promoted`, `archived`, `rejected` |