Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Docs: a workspace knowledge base people and agents write together | 1 | -- Docs: spaces, pages, history, suggestions, templates and search |
| 2 | -- (docs/WORKSPACE.md, "Docs"). A page's live content is a Yjs document in | |
| 3 | -- its Durable Object (src/room.ts); this database keeps everything around | |
| 4 | -- it and the Markdown rendition the room saves after each burst of edits. | |
| 5 | -- Workspaces are kept by id; members are keys: `user:<id>`, `agent:<id>`, | |
| 6 | -- `team:<slug>`. | |
| 7 | ||
| 8 | CREATE TABLE spaces ( | |
| 9 | id TEXT PRIMARY KEY, | |
| 10 | workspace_id TEXT NOT NULL, | |
| 11 | slug TEXT NOT NULL, | |
| 12 | name TEXT NOT NULL, | |
| 13 | description TEXT, | |
| 14 | icon TEXT, | |
| 15 | -- workspace: every member gets default_role; team: the team's members do; | |
| 16 | -- private: only listed members. | |
| 17 | kind TEXT NOT NULL CHECK (kind IN ('workspace', 'team', 'private')), | |
| 18 | team TEXT, | |
| 19 | default_role TEXT CHECK (default_role IN ('view', 'comment', 'edit', 'manage')), | |
| 20 | agent_mode TEXT NOT NULL DEFAULT 'suggest' CHECK (agent_mode IN ('suggest', 'edit')), | |
| 21 | is_default INTEGER NOT NULL DEFAULT 0, | |
| 22 | created_by TEXT NOT NULL, | |
| 23 | created_at TEXT NOT NULL, | |
| 24 | archived_at TEXT, | |
| 25 | UNIQUE (workspace_id, slug) | |
| 26 | ); | |
| 27 | ||
| 28 | -- One General space per workspace, made the first time Docs is opened. | |
| 29 | CREATE UNIQUE INDEX spaces_default ON spaces (workspace_id) WHERE is_default = 1; | |
| 30 | ||
| 31 | CREATE TABLE space_members ( | |
| 32 | space_id TEXT NOT NULL REFERENCES spaces (id) ON DELETE CASCADE, | |
| 33 | principal TEXT NOT NULL, | |
| 34 | role TEXT NOT NULL CHECK (role IN ('view', 'comment', 'edit', 'manage')), | |
| 35 | added_by TEXT NOT NULL, | |
| 36 | added_at TEXT NOT NULL, | |
| 37 | PRIMARY KEY (space_id, principal) | |
| 38 | ); | |
| 39 | ||
| 40 | -- Projects (repositories, `owner/name` lowercased) a space is about. | |
| 41 | CREATE TABLE space_projects ( | |
| 42 | space_id TEXT NOT NULL REFERENCES spaces (id) ON DELETE CASCADE, | |
| 43 | repo TEXT NOT NULL, | |
| 44 | PRIMARY KEY (space_id, repo) | |
| 45 | ); | |
| 46 | CREATE INDEX space_projects_repo ON space_projects (repo); | |
| 47 | ||
| 48 | CREATE TABLE pages ( | |
| 49 | id TEXT PRIMARY KEY, | |
| 50 | workspace_id TEXT NOT NULL, | |
| 51 | space_id TEXT NOT NULL REFERENCES spaces (id) ON DELETE CASCADE, | |
| 52 | parent_id TEXT, | |
| 53 | position REAL NOT NULL, | |
| 54 | title TEXT NOT NULL DEFAULT '', | |
| 55 | icon TEXT, | |
| 56 | cover TEXT, | |
| 57 | -- As the room last saved it: the read view, search, agents and export. | |
| 58 | markdown TEXT NOT NULL DEFAULT '', | |
| 59 | created_by TEXT NOT NULL, | |
| 60 | created_at TEXT NOT NULL, | |
| 61 | updated_by TEXT, | |
| 62 | updated_at TEXT NOT NULL, | |
| 63 | archived_at TEXT, | |
| 64 | archived_by TEXT, | |
| 65 | -- People already told they were mentioned in the page, so a save tells only the new ones. | |
| 66 | mentioned TEXT NOT NULL DEFAULT '[]' | |
| 67 | ); | |
| 68 | CREATE INDEX pages_tree ON pages (space_id, parent_id, position); | |
| 69 | CREATE INDEX pages_recent ON pages (workspace_id, updated_at); | |
| 70 | ||
| 71 | CREATE TABLE page_owners ( | |
| 72 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 73 | principal TEXT NOT NULL, | |
| 74 | PRIMARY KEY (page_id, principal) | |
| 75 | ); | |
| 76 | CREATE INDEX page_owners_principal ON page_owners (principal); | |
| 77 | ||
| 78 | CREATE TABLE page_projects ( | |
| 79 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 80 | repo TEXT NOT NULL, | |
| 81 | PRIMARY KEY (page_id, repo) | |
| 82 | ); | |
| 83 | CREATE INDEX page_projects_repo ON page_projects (repo); | |
| 84 | ||
| 85 | -- Links from one page to another, rebuilt on each save: backlinks. | |
| 86 | CREATE TABLE page_links ( | |
| 87 | from_page TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 88 | to_page TEXT NOT NULL, | |
| 89 | PRIMARY KEY (from_page, to_page) | |
| 90 | ); | |
| 91 | CREATE INDEX page_links_to ON page_links (to_page); | |
| 92 | ||
| 93 | CREATE TABLE page_views ( | |
| 94 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 95 | user_id TEXT NOT NULL, | |
| 96 | viewed_at TEXT NOT NULL, | |
| 97 | PRIMARY KEY (page_id, user_id) | |
| 98 | ); | |
| 99 | CREATE INDEX page_views_user ON page_views (user_id, viewed_at); | |
| 100 | ||
| 101 | CREATE TABLE favorites ( | |
| 102 | user_id TEXT NOT NULL, | |
| 103 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 104 | created_at TEXT NOT NULL, | |
| 105 | PRIMARY KEY (user_id, page_id) | |
| 106 | ); | |
| 107 | ||
| 108 | -- History: the Yjs state (for an exact restore) and its Markdown (for | |
| 109 | -- reading and diffs). `state` is null when it was too large to keep; a | |
| 110 | -- restore then rebuilds the page from the Markdown. | |
| 111 | CREATE TABLE page_versions ( | |
| 112 | id TEXT PRIMARY KEY, | |
| 113 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 114 | created_at TEXT NOT NULL, | |
| 115 | kind TEXT NOT NULL CHECK (kind IN ('created', 'edit', 'agent', 'suggestion', 'restore')), | |
| 116 | authors TEXT NOT NULL DEFAULT '[]', | |
| 117 | note TEXT, | |
| 118 | markdown TEXT NOT NULL, | |
| 119 | state BLOB | |
| 120 | ); | |
| 121 | CREATE INDEX page_versions_page ON page_versions (page_id, created_at); | |
| 122 | ||
| 123 | -- Agents' tracked changes, waiting for a person. | |
| 124 | CREATE TABLE suggestions ( | |
| 125 | id TEXT PRIMARY KEY, | |
| 126 | page_id TEXT NOT NULL REFERENCES pages (id) ON DELETE CASCADE, | |
| 127 | author TEXT NOT NULL, | |
| 128 | asked_by TEXT, | |
| 129 | target TEXT NOT NULL, | |
| 130 | before_markdown TEXT NOT NULL, | |
| 131 | after_markdown TEXT NOT NULL, | |
| 132 | note TEXT, | |
| 133 | status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'accepted', 'rejected', 'stale')), | |
| 134 | created_at TEXT NOT NULL, | |
| 135 | decided_by TEXT, | |
| 136 | decided_at TEXT | |
| 137 | ); | |
| 138 | CREATE INDEX suggestions_page ON suggestions (page_id, status); | |
| 139 | ||
| 140 | -- The workspace's own templates; the built-in ones are in src/templates.ts. | |
| 141 | CREATE TABLE templates ( | |
| 142 | id TEXT PRIMARY KEY, | |
| 143 | workspace_id TEXT NOT NULL, | |
| 144 | name TEXT NOT NULL, | |
| 145 | description TEXT NOT NULL DEFAULT '', | |
| 146 | icon TEXT NOT NULL DEFAULT '📄', | |
| 147 | markdown TEXT NOT NULL, | |
| 148 | created_by TEXT NOT NULL, | |
| 149 | created_at TEXT NOT NULL | |
| 150 | ); | |
| 151 | CREATE INDEX templates_workspace ON templates (workspace_id); | |
| 152 | ||
| 153 | -- Files put in pages, kept in the files bucket under `key`. | |
| 154 | CREATE TABLE files ( | |
| 155 | id TEXT PRIMARY KEY, | |
| 156 | workspace_id TEXT NOT NULL, | |
| 157 | page_id TEXT, | |
| 158 | key TEXT NOT NULL UNIQUE, | |
| 159 | name TEXT NOT NULL, | |
| 160 | content_type TEXT NOT NULL, | |
| 161 | bytes INTEGER NOT NULL, | |
| 162 | created_by TEXT NOT NULL, | |
| 163 | created_at TEXT NOT NULL | |
| 164 | ); | |
| 165 | ||
| 166 | -- Full text over titles and Markdown; rebuilt for a page on each save. | |
| 167 | CREATE VIRTUAL TABLE pages_fts USING fts5 (page_id UNINDEXED, title, body, tokenize = 'unicode61 remove_diacritics 2'); |