theaio/theaio-boards

Laneway, team task boards you can run from Claude or ChatGPT. FastAPI + DataK3 + appid + Models + MCP.

A remix template · 0 remixes · Remix theaio/theaio-boards

schema.sql

-- Created once by the remix, in the bucket it makes. Natural keys only: DataK3 has no sequences.
-- Every table has a primary key. An existing starter deployment upgrades with
-- migrations/001_board_upgrade.sql (ADD COLUMN + the new tables).
CREATE TABLE IF NOT EXISTS boards (
  id TEXT PRIMARY KEY,
  title TEXT NOT NULL,
  owner_email TEXT NOT NULL,
  created_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS memberships (
  id TEXT PRIMARY KEY,            -- board_id + ':' + email
  board_id TEXT NOT NULL,
  email TEXT NOT NULL,
  role TEXT NOT NULL              -- member | viewer (the owner is boards.owner_email)
);
CREATE TABLE IF NOT EXISTS lists (
  id TEXT PRIMARY KEY,
  board_id TEXT NOT NULL,
  name TEXT NOT NULL,
  pos BIGINT NOT NULL             -- order on the board; the last list counts as done
);
CREATE TABLE IF NOT EXISTS cards (
  id TEXT PRIMARY KEY,            -- board_id + ':' + num: a plain INSERT claims the number
  board_id TEXT NOT NULL,
  list_name TEXT NOT NULL,        -- the starter's fixed list; written '' now, read once to upgrade
  title TEXT NOT NULL,
  assignee TEXT,
  due_date DATE,
  updated_at TIMESTAMP,
  num BIGINT,                     -- the card's number on its board ("card 12")
  list_id TEXT,                   -- lists.id
  description TEXT,
  labels TEXT,                    -- JSON array of short strings
  pos BIGINT,                     -- order inside its list
  created_by TEXT,
  created_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS activity (
  id TEXT PRIMARY KEY,            -- board-level events: members, lists
  board_id TEXT NOT NULL,
  card_id TEXT,
  actor TEXT NOT NULL,
  action TEXT NOT NULL,
  event_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS card_history (
  id TEXT PRIMARY KEY,            -- append-only: one row per field that changed
  board_id TEXT NOT NULL,
  card_id TEXT NOT NULL,
  actor TEXT NOT NULL,
  changed_at TIMESTAMP NOT NULL,
  field_name TEXT NOT NULL,       -- created | title | description | assignee | due_date | labels
                                  -- | list | position | comment | blocked by
  old_value TEXT,
  new_value TEXT
);
CREATE TABLE IF NOT EXISTS card_links (
  id TEXT PRIMARY KEY,            -- card_id + '<' + blocked_by
  board_id TEXT NOT NULL,
  card_id TEXT NOT NULL,          -- this card waits on...
  blocked_by TEXT NOT NULL        -- ...this one
);
CREATE TABLE IF NOT EXISTS card_embeddings (
  card_id TEXT PRIMARY KEY,
  board_id TEXT NOT NULL,
  digest TEXT NOT NULL,           -- hash of the text embedded: re-embed only when it changes
  embedding VECTOR(2048)          -- jina-embeddings-v4; searched with <=> (cosine distance)
);