| 1 | -- Folios: what people call artifacts. Artifacts mode is one mode for docs, |
| 2 | -- slides, designs and dashboards (docs/ARTIFACTS_MODE.md, section 2.2). |
| 3 | -- A folio's live content is a Yjs document in its room (FolioRoom, |
| 4 | -- src/folios/room.ts); these tables keep everything around it and the |
| 5 | -- text rendition the room saves after each burst of edits. |
| 6 | -- |
| 7 | -- This migration only adds tables. Docs' pages keep working on theirs |
| 8 | -- until Phase 7 drops them. Keys are as in Docs: `user:<id>`, |
| 9 | -- `agent:<id>`, `team:<slug>`. |
| 10 | |
| 11 | CREATE TABLE folios ( |
| 12 | id TEXT PRIMARY KEY, |
| 13 | workspace_id TEXT NOT NULL, |
| 14 | kind TEXT NOT NULL CHECK (kind IN ('doc', 'slides', 'design', 'dashboard')), |
| 15 | title TEXT NOT NULL DEFAULT '', |
| 16 | icon TEXT, |
| 17 | cover TEXT, |
| 18 | -- Always a person: `user:<id>`. An agent's folio is owned by whoever it acted for. |
| 19 | owner TEXT NOT NULL, |
| 20 | -- NULL: the owner's Private section. |
| 21 | space_id TEXT REFERENCES spaces (id), |
| 22 | -- Only a doc is ever a parent. Children share their parent's space. |
| 23 | parent_id TEXT REFERENCES folios (id), |
| 24 | position REAL NOT NULL, |
| 25 | -- 1: follows its parent (or, at the top, its space). 0: "Only people invited". |
| 26 | inherit INTEGER NOT NULL DEFAULT 1, |
| 27 | -- The nearest of itself and its ancestors with inherit = 0 or no parent. |
| 28 | acl_root TEXT NOT NULL, |
| 29 | -- '/<top id>/…/<id>/', for subtree updates. |
| 30 | path TEXT NOT NULL, |
| 31 | general_access TEXT NOT NULL DEFAULT 'none' CHECK (general_access IN ('none', 'workspace', 'link')), |
| 32 | general_role TEXT CHECK (general_role IN ('view', 'comment', 'edit')), |
| 33 | -- NULL: the space's, or 'suggest' in Private. |
| 34 | agent_mode TEXT CHECK (agent_mode IN ('suggest', 'edit')), |
| 35 | -- The kind's text rendition: search, recall, the read view, export. |
| 36 | text TEXT NOT NULL DEFAULT '', |
| 37 | excerpt TEXT NOT NULL DEFAULT '', |
| 38 | -- Small JSON a card draws; never data values. |
| 39 | preview TEXT, |
| 40 | -- JSON { title, href }: where it was written up from. |
| 41 | source TEXT, |
| 42 | -- People already told they were mentioned in it. |
| 43 | mentioned TEXT NOT NULL DEFAULT '[]', |
| 44 | created_by TEXT NOT NULL, |
| 45 | created_at TEXT NOT NULL, |
| 46 | -- Any change: rename, move, share. |
| 47 | updated_by TEXT, |
| 48 | updated_at TEXT NOT NULL, |
| 49 | -- Content changes: "Edited 45m ago". |
| 50 | edited_by TEXT, |
| 51 | edited_at TEXT NOT NULL, |
| 52 | trashed_at TEXT, |
| 53 | trashed_by TEXT |
| 54 | ); |
| 55 | CREATE INDEX folios_tree ON folios (workspace_id, space_id, parent_id, position); |
| 56 | CREATE INDEX folios_owner ON folios (owner, edited_at) WHERE trashed_at IS NULL; |
| 57 | CREATE INDEX folios_recent ON folios (workspace_id, edited_at) WHERE trashed_at IS NULL; |
| 58 | CREATE INDEX folios_trashed ON folios (workspace_id, trashed_at) WHERE trashed_at IS NOT NULL; |
| 59 | CREATE INDEX folios_root ON folios (acl_root); |
| 60 | CREATE INDEX folios_path ON folios (path); |
| 61 | CREATE INDEX folios_parent ON folios (parent_id); |
| 62 | |
| 63 | -- Explicit shares, as they were set. |
| 64 | CREATE TABLE folio_grants ( |
| 65 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 66 | principal TEXT NOT NULL, |
| 67 | role TEXT NOT NULL CHECK (role IN ('view', 'comment', 'edit', 'manage')), |
| 68 | granted_by TEXT NOT NULL, |
| 69 | granted_at TEXT NOT NULL, |
| 70 | PRIMARY KEY (folio_id, principal) |
| 71 | ); |
| 72 | |
| 73 | -- Effective explicit access, per folio, for list and search SQL: the |
| 74 | -- owner, the owners of ancestors it inherits from (as `manage`), and every |
| 75 | -- grant from the folio up to its acl_root, highest role each. Rebuilt for |
| 76 | -- a subtree (by `path`) on a grant, move, restriction or ownership change |
| 77 | -- (src/folios/access-store.ts). |
| 78 | CREATE TABLE folio_access ( |
| 79 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 80 | principal TEXT NOT NULL, |
| 81 | role TEXT NOT NULL, |
| 82 | -- The folio whose grant or owner this is (itself or an ancestor). |
| 83 | via TEXT NOT NULL, |
| 84 | -- For "Shared with you" ordering. |
| 85 | since TEXT NOT NULL, |
| 86 | PRIMARY KEY (folio_id, principal) |
| 87 | ); |
| 88 | CREATE INDEX folio_access_principal ON folio_access (principal, since); |
| 89 | |
| 90 | -- Recent, and link access once opened. |
| 91 | CREATE TABLE folio_visits ( |
| 92 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 93 | user_id TEXT NOT NULL, |
| 94 | first_at TEXT NOT NULL, |
| 95 | last_at TEXT NOT NULL, |
| 96 | PRIMARY KEY (folio_id, user_id) |
| 97 | ); |
| 98 | CREATE INDEX folio_visits_user ON folio_visits (user_id, last_at); |
| 99 | |
| 100 | CREATE TABLE folio_favorites ( |
| 101 | user_id TEXT NOT NULL, |
| 102 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 103 | position REAL NOT NULL, |
| 104 | created_at TEXT NOT NULL, |
| 105 | PRIMARY KEY (user_id, folio_id) |
| 106 | ); |
| 107 | |
| 108 | -- Open spaces a member shows in their sidebar. |
| 109 | CREATE TABLE space_joins ( |
| 110 | space_id TEXT NOT NULL REFERENCES spaces (id) ON DELETE CASCADE, |
| 111 | user_id TEXT NOT NULL, |
| 112 | position REAL NOT NULL, |
| 113 | joined_at TEXT NOT NULL, |
| 114 | PRIMARY KEY (space_id, user_id) |
| 115 | ); |
| 116 | CREATE INDEX space_joins_user ON space_joins (user_id); |
| 117 | |
| 118 | -- History: the Yjs state (an exact restore) and the text (reading, diffs). |
| 119 | CREATE TABLE folio_versions ( |
| 120 | id TEXT PRIMARY KEY, |
| 121 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 122 | created_at TEXT NOT NULL, |
| 123 | kind TEXT NOT NULL CHECK (kind IN ('created', 'edit', 'agent', 'suggestion', 'proposal', 'restore')), |
| 124 | authors TEXT NOT NULL DEFAULT '[]', |
| 125 | note TEXT, |
| 126 | text TEXT NOT NULL, |
| 127 | -- The Yjs state when it is at most 1.5 MB. |
| 128 | state BLOB, |
| 129 | -- Else its key in the file store. |
| 130 | state_key TEXT |
| 131 | ); |
| 132 | CREATE INDEX folio_versions_folio ON folio_versions (folio_id, created_at); |
| 133 | |
| 134 | -- A doc's agents' tracked changes, as `suggestions` for pages. |
| 135 | CREATE TABLE folio_suggestions ( |
| 136 | id TEXT PRIMARY KEY, |
| 137 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 138 | author TEXT NOT NULL, |
| 139 | asked_by TEXT, |
| 140 | target TEXT NOT NULL, |
| 141 | before_markdown TEXT NOT NULL, |
| 142 | after_markdown TEXT NOT NULL, |
| 143 | note TEXT, |
| 144 | status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'accepted', 'rejected', 'stale')), |
| 145 | created_at TEXT NOT NULL, |
| 146 | decided_by TEXT, |
| 147 | decided_at TEXT, |
| 148 | marks_current INTEGER NOT NULL DEFAULT 0 |
| 149 | ); |
| 150 | CREATE INDEX folio_suggestions_folio ON folio_suggestions (folio_id, status); |
| 151 | |
| 152 | -- Other kinds: an agent's whole change as a Yjs update, previewed and applied or rejected. |
| 153 | CREATE TABLE folio_proposals ( |
| 154 | id TEXT PRIMARY KEY, |
| 155 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 156 | author TEXT NOT NULL, |
| 157 | asked_by TEXT, |
| 158 | note TEXT, |
| 159 | base_vector BLOB NOT NULL, |
| 160 | update_blob BLOB, |
| 161 | update_key TEXT, |
| 162 | summary TEXT NOT NULL, |
| 163 | status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'accepted', 'rejected', 'stale')), |
| 164 | created_at TEXT NOT NULL, |
| 165 | decided_by TEXT, |
| 166 | decided_at TEXT |
| 167 | ); |
| 168 | CREATE INDEX folio_proposals_folio ON folio_proposals (folio_id, status); |
| 169 | |
| 170 | -- The workspace's own templates; the built-in ones are in code. |
| 171 | CREATE TABLE folio_templates ( |
| 172 | id TEXT PRIMARY KEY, |
| 173 | workspace_id TEXT NOT NULL, |
| 174 | kind TEXT NOT NULL CHECK (kind IN ('doc', 'slides', 'design', 'dashboard')), |
| 175 | name TEXT NOT NULL, |
| 176 | description TEXT NOT NULL DEFAULT '', |
| 177 | icon TEXT, |
| 178 | -- Markdown (doc, slides) or a JSON spec (design, dashboard). |
| 179 | body TEXT NOT NULL, |
| 180 | created_by TEXT NOT NULL, |
| 181 | created_at TEXT NOT NULL |
| 182 | ); |
| 183 | CREATE INDEX folio_templates_ws ON folio_templates (workspace_id, kind); |
| 184 | |
| 185 | -- Files put in folios, kept in the file store under `docs/<key>` like pages' files. |
| 186 | CREATE TABLE folio_files ( |
| 187 | id TEXT PRIMARY KEY, |
| 188 | workspace_id TEXT NOT NULL, |
| 189 | folio_id TEXT REFERENCES folios (id) ON DELETE SET NULL, |
| 190 | key TEXT NOT NULL UNIQUE, |
| 191 | name TEXT NOT NULL, |
| 192 | content_type TEXT NOT NULL, |
| 193 | bytes INTEGER NOT NULL, |
| 194 | created_by TEXT NOT NULL, |
| 195 | created_at TEXT NOT NULL |
| 196 | ); |
| 197 | CREATE INDEX folio_files_folio ON folio_files (folio_id); |
| 198 | |
| 199 | -- Links from one folio to another, rebuilt on each save: backlinks. |
| 200 | CREATE TABLE folio_links ( |
| 201 | from_folio TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 202 | to_folio TEXT NOT NULL, |
| 203 | PRIMARY KEY (from_folio, to_folio) |
| 204 | ); |
| 205 | CREATE INDEX folio_links_to ON folio_links (to_folio); |
| 206 | |
| 207 | -- Projects (repositories, `owner/name` lowercased) a folio is about. |
| 208 | CREATE TABLE folio_projects ( |
| 209 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 210 | repo TEXT NOT NULL, |
| 211 | PRIMARY KEY (folio_id, repo) |
| 212 | ); |
| 213 | CREATE INDEX folio_projects_repo ON folio_projects (repo); |
| 214 | |
| 215 | -- Code a folio cites, as `citations` for pages. |
| 216 | CREATE TABLE folio_citations ( |
| 217 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 218 | repo TEXT NOT NULL, |
| 219 | path TEXT NOT NULL, |
| 220 | kind TEXT NOT NULL CHECK (kind IN ('path', 'symbol', 'endpoint', 'env')), |
| 221 | label TEXT NOT NULL DEFAULT '', |
| 222 | ref TEXT, |
| 223 | source TEXT NOT NULL CHECK (source IN ('body', 'header')), |
| 224 | PRIMARY KEY (folio_id, source, repo, path, kind, label) |
| 225 | ); |
| 226 | CREATE INDEX folio_citations_repo ON folio_citations (repo); |
| 227 | |
| 228 | -- Changes that touched code a folio cites, as `page_changes`. |
| 229 | CREATE TABLE folio_changes ( |
| 230 | folio_id TEXT NOT NULL REFERENCES folios (id) ON DELETE CASCADE, |
| 231 | repo TEXT NOT NULL, |
| 232 | repo_id TEXT NOT NULL, |
| 233 | commit_sha TEXT NOT NULL, |
| 234 | pull_number INTEGER, |
| 235 | pull_title TEXT, |
| 236 | paths TEXT NOT NULL DEFAULT '[]', |
| 237 | detected_at TEXT NOT NULL, |
| 238 | cleared_at TEXT, |
| 239 | cleared_by TEXT, |
| 240 | PRIMARY KEY (folio_id, repo, commit_sha) |
| 241 | ); |
| 242 | CREATE INDEX folio_changes_open ON folio_changes (folio_id) WHERE cleared_at IS NULL; |
| 243 | CREATE INDEX folio_changes_recent ON folio_changes (detected_at) WHERE cleared_at IS NULL; |
| 244 | |
| 245 | -- Full text over titles and text renditions; rebuilt for a folio on each save. |
| 246 | CREATE VIRTUAL TABLE folios_fts USING fts5 (folio_id UNINDEXED, kind UNINDEXED, title, body, tokenize = 'unicode61 remove_diacritics 2'); |
| 247 | |
| 248 | -- Folios' passages for the semantic index (Vectorize `g1t-folios`) and |
| 249 | -- recall. `scope` is 'space:<id>' when the folio's access is exactly its |
| 250 | -- space's, else 'folio:<acl_root>'; every hit is checked again against |
| 251 | -- these tables before anyone sees it. Projects' docs keep `doc_chunks`. |
| 252 | CREATE TABLE folio_chunks ( |
| 253 | id TEXT PRIMARY KEY, |
| 254 | workspace_id TEXT NOT NULL, |
| 255 | folio_id TEXT NOT NULL, |
| 256 | kind TEXT NOT NULL, |
| 257 | scope TEXT NOT NULL, |
| 258 | seq INTEGER NOT NULL, |
| 259 | heading TEXT, |
| 260 | text TEXT NOT NULL, |
| 261 | hash TEXT NOT NULL, |
| 262 | vector_hash TEXT, |
| 263 | updated_at TEXT NOT NULL |
| 264 | ); |
| 265 | CREATE INDEX folio_chunks_folio ON folio_chunks (folio_id, seq); |
| 266 | CREATE INDEX folio_chunks_workspace ON folio_chunks (workspace_id); |
| 267 | CREATE INDEX folio_chunks_unembedded ON folio_chunks (workspace_id) WHERE vector_hash IS NULL OR vector_hash <> hash; |
| 268 | |
| 269 | CREATE VIRTUAL TABLE folio_chunks_fts USING fts5 (chunk_id UNINDEXED, scope UNINDEXED, folio_id UNINDEXED, heading, text, tokenize = 'unicode61 remove_diacritics 2'); |