memory / projects.py — ProjectStore
Multi-session project & task store. SQLite-only; no ORM. Designed for cross-process access from API, Slack bot, dream consolidator.
Storage
SQLite at memory_dir/projects.db; threading.RLock; PRAGMA foreign_keys=ON.
Tables
projects
| Column | Type / values |
|---|---|
| id | TEXT PK (12-char hex) |
| title / kind / goal / status | TEXT (kind ∈ CODING/GENERAL · status ∈ ACTIVE/PAUSED/DONE/ARCHIVED) |
| workspace_dir | TEXT |
| metadata_json | TEXT |
| created_at / updated_at | REAL |
tasks
| Column | Notes |
|---|---|
| id, project_id, parent_id | FKs (parent_id NULL = root). |
| description | TEXT. |
| status | PENDING / IN_PROGRESS / DONE / BLOCKED. |
| dependency_type | ALL / ANY (BEST not persisted). |
| alternatives_json / postconditions_json | list[str]. |
| result_summary / failure_reason / actual_tool_used | TEXT. |
| revision_count, estimated_cost, actual_cost, depth, position | numeric. |
task_artifacts
id · task_id · project_id · kind ∈ {file, url, note, tool_call} · payload · created_at.
project_events
id (auto PK) · project_id · task_id · type · payload_json · ts (REAL).
Public API
Projects
create_project(title, kind, goal, metadata, workspace_dir) -> strlist_projects(status_filter)/get_project(id)update_project(id, **fields)delete_project(id, hard=False)— soft = ARCHIVED, hard = cascade delete.ensure_workspace(project_id)— create / return workspace path.
Tasks
add_task(...) -> strget_task(id)/list_tasks(project_id, status_filter)update_task(id, **fields)delete_task(id)— cascade delete descendants.
Artifacts & events
add_artifact(task_id, kind, payload) -> strlist_artifacts(project_id=None, task_id=None)log_event(project_id, task_id, event_type, payload) -> intlist_events(project_id, limit, event_type)
No automatic eviction; archiving via status='ARCHIVED' preserves history. Hard delete cascades via FK ON DELETE CASCADE.