--- source: raw/schema.sql content_hash: 71b47e47b483834c887de14c76d5b16506d90ad64198d76a92a7b3731132dab8 namespace: work last_synced: 2026-04-27 confidence: 0.9 tags: [schema, postgres, database, asana, wiki] --- # Personal OS Database Schema Postgres schema (`personal_os`) backing the Asana mirror, agent annotations, file ingestion pipeline, semantic wiki, and memory store. Requires the `vector` (pgvector) and `pg_trgm` extensions. Bootstrap with `psql -U admin -d personal_os -f schema.sql`. ## Asana mirror - **`tasks`** — one row per Asana task GID, upserted on each sync. Stores due/start dates, completion, My Tasks section, assignee/creator, primary project, timestamps, full `raw_json`, plus a `source` enum: `my_tasks` (assigned to me) > `delegated` (I created, others assigned) > `following` (CC'd) > `project` (project member, fallback). Sync bookkeeping: `fetched_at`, `stories_fetched_before` cursor. - **`stories`** — raw event log per task, keyed by Asana story GID. `resource_subtype` covers comments, assignment changes, due-date edits, section moves, dependency edits, attachments, completion, etc. - **`task_edges`** — directed graph between tasks. `relation_type`: `subtask`, `dependency` (blocked-by), `dependent` (blocking), `project_sibling`, `mention` (referenced in a story). `related_gid` may not yet exist in `tasks`. - **`task_annotations`** — agent or user notes per (task, annotation_type). Types: `irrelevant`, `watching`, `needs_action`, `snoozed` (with `snoozed_until`). Never written by the fetcher. - **`sync_state`** — one row per sync stream (`my_tasks`, `following`, `delegated`, `project:{gid}`, `workspace_events`). Tracks cursor, last sync, last full sync. - **`task_embeddings`** — pgvector(1024) per task, HNSW index with cosine ops; populated separately from sync. The **`active_tasks`** view filters out completed and irrelevant tasks and tasks snoozed past today, ordered by source priority then due date then modified-at. Starting point for daily review. ## Phase 0 additions (2026-04-27) All core tables gain `namespace TEXT NOT NULL DEFAULT 'work'` so a single DB can serve work/personal/family contexts. `tasks` also gets `possibly_deleted` and `last_seen_in_full_sync` to track tasks that disappear between full syncs without explicit deletion events. ## File ingestion → wiki - **`file_references`** — files (via macOS security-scoped `bookmark_data` + cached `last_known_path`) and web URLs queued for wiki ingestion. Tracks `content_hash` (SHA256), `mime_type`, `tags`, a `modification_log` JSONB, and a `wiki_stale` flag the [[wiki-ingest-process]] consumes. Namespace-checked (`work`/`personal`/`family`). - **`wiki_pages`** — LLM-synthesised markdown, never a raw copy. Has `sources` JSONB (file_ref/url/title), `confidence` float, `superseded_by` self-FK for version chains, `stale` flag, `last_synced_hash`, and a pgvector(1536) embedding for semantic search. The optional ivfflat index is left commented; rebuild once the table has 1000+ rows. ## Memory store **`memory_store`** — semantic memory from Discord, Claude sessions, and manual entries. `type` ∈ {fact, preference, decision, person}, `source` ∈ {discord, claude, manual}, with entities JSONB, confidence, optional `expires_at` (null = permanent), and a pgvector(1536) embedding. Used by Hermes for cross-session context. ## Indexing notes - Trigram GIN on `tasks.name` enables fuzzy task search. - `tasks_due_on` is partial (only non-completed tasks). - pgvector embedding indexes for `wiki_pages` and `memory_store` are deferred until the tables have meaningful row counts. - `task_embeddings` uses HNSW; the wiki/memory stores use ivfflat (commented) — different recall/build trade-off per workload. ## Key constraints - All `namespace` columns are CHECK-constrained to `('work','personal','family')` on the new (Phase 0+) tables. - Cascade deletes flow from `tasks` → `stories`, `task_edges`, `task_annotations`, `task_embeddings`. ## Related [[personal-os-architecture]] [[wiki-ingest-process]]