Nicholai 40fdf48cbf
feat: add conversations, desktop (Tauri), and offline sync (#81)
* feat: add conversations, desktop (Tauri), and offline sync

Major new features:
- conversations module: Slack-like channels, threads, reactions, pins
- Tauri desktop app with local SQLite for offline-first operation
- Hybrid logical clock sync engine with conflict resolution
- DB provider abstraction (D1/Tauri/memory) with React context

Conversations:
- Text/voice/announcement channels with categories
- Message threads, reactions, attachments, pinning
- Real-time presence and typing indicators
- Full-text search across messages

Desktop (Tauri):
- Local SQLite database with sync to cloud D1
- Offline mutation queue with automatic replay
- Window management and keyboard shortcuts
- Desktop shell with offline banner

Sync infrastructure:
- Vector clock implementation for causality tracking
- Last-write-wins with semantic conflict resolution
- Delta sync via checkpoints for bandwidth efficiency
- Comprehensive test coverage

Also adds e2e test setup with Playwright and CI workflows
for desktop releases.

* fix(tests): sync engine test schema and checkpoint logic

- Add missing process_after column and sync_tombstone table to test schemas
- Fix checkpoint update to save cursor even when records array is empty
- Revert claude-code-review.yml workflow changes to match main

---------

Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
2026-02-14 19:32:14 -07:00

273 lines
8.0 KiB
SQL

-- Compass Desktop SQLite Schema
-- Mirrors the Drizzle schema for local offline storage
-- Auth and user management tables
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
first_name TEXT,
last_name TEXT,
display_name TEXT,
avatar_url TEXT,
role TEXT NOT NULL DEFAULT 'office',
google_email TEXT,
is_active INTEGER NOT NULL DEFAULT 1,
last_login_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS organizations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
type TEXT NOT NULL,
logo_url TEXT,
is_active INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS organization_members (
id TEXT PRIMARY KEY,
organization_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
joined_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS teams (
id TEXT PRIMARY KEY,
organization_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS team_members (
id TEXT PRIMARY KEY,
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
joined_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS groups (
id TEXT PRIMARY KEY,
organization_id TEXT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
color TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS group_members (
id TEXT PRIMARY KEY,
group_id TEXT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
joined_at TEXT NOT NULL
);
-- Project management tables
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'OPEN',
address TEXT,
client_name TEXT,
project_manager TEXT,
organization_id TEXT REFERENCES organizations(id),
netsuite_job_id TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS project_members (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
assigned_at TEXT NOT NULL
);
-- Schedule tables
CREATE TABLE IF NOT EXISTS schedule_tasks (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title TEXT NOT NULL,
start_date TEXT NOT NULL,
workdays INTEGER NOT NULL,
end_date_calculated TEXT NOT NULL,
phase TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'PENDING',
is_critical_path INTEGER NOT NULL DEFAULT 0,
is_milestone INTEGER NOT NULL DEFAULT 0,
percent_complete INTEGER NOT NULL DEFAULT 0,
assigned_to TEXT,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS task_dependencies (
id TEXT PRIMARY KEY,
predecessor_id TEXT NOT NULL REFERENCES schedule_tasks(id) ON DELETE CASCADE,
successor_id TEXT NOT NULL REFERENCES schedule_tasks(id) ON DELETE CASCADE,
type TEXT NOT NULL DEFAULT 'FS',
lag_days INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS workday_exceptions (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title TEXT NOT NULL,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'non_working',
category TEXT NOT NULL DEFAULT 'company_holiday',
recurrence TEXT NOT NULL DEFAULT 'one_time',
notes TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS schedule_baselines (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name TEXT NOT NULL,
snapshot_data TEXT NOT NULL,
created_at TEXT NOT NULL
);
-- Customer and vendor tables
CREATE TABLE IF NOT EXISTS customers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
company TEXT,
email TEXT,
phone TEXT,
address TEXT,
notes TEXT,
netsuite_id TEXT,
created_at TEXT NOT NULL,
updated_at TEXT
);
CREATE TABLE IF NOT EXISTS vendors (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL DEFAULT 'Subcontractor',
email TEXT,
phone TEXT,
address TEXT,
netsuite_id TEXT,
created_at TEXT NOT NULL,
updated_at TEXT
);
-- Agent memory tables
CREATE TABLE IF NOT EXISTS agent_conversations (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT,
last_message_at TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS agent_memories (
id TEXT PRIMARY KEY,
conversation_id TEXT NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL,
content TEXT NOT NULL,
embedding TEXT,
metadata TEXT,
created_at TEXT NOT NULL
);
-- Slab persistent memory
CREATE TABLE IF NOT EXISTS slab_memories (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
memory_type TEXT NOT NULL,
tags TEXT,
importance REAL NOT NULL DEFAULT 0.7,
pinned INTEGER NOT NULL DEFAULT 0,
access_count INTEGER NOT NULL DEFAULT 0,
last_accessed_at TEXT,
created_at TEXT NOT NULL
);
-- Feedback tables
CREATE TABLE IF NOT EXISTS feedback (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
message TEXT NOT NULL,
name TEXT,
email TEXT,
page_url TEXT,
user_agent TEXT,
viewport_width INTEGER,
viewport_height INTEGER,
ip_hash TEXT,
github_issue_url TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS feedback_interviews (
id TEXT PRIMARY KEY,
user_id TEXT REFERENCES users(id),
user_name TEXT NOT NULL,
user_role TEXT NOT NULL,
responses TEXT NOT NULL,
summary TEXT NOT NULL,
pain_points TEXT,
feature_requests TEXT,
overall_sentiment TEXT NOT NULL,
github_issue_url TEXT,
conversation_id TEXT,
created_at TEXT NOT NULL
);
-- Push notification tokens
CREATE TABLE IF NOT EXISTS push_tokens (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL,
platform TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
-- Sync metadata for offline-first support
CREATE TABLE IF NOT EXISTS sync_metadata (
id TEXT PRIMARY KEY,
table_name TEXT NOT NULL UNIQUE,
last_sync_at TEXT,
sync_version INTEGER NOT NULL DEFAULT 0,
pending_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT
);
CREATE TABLE IF NOT EXISTS sync_queue (
id TEXT PRIMARY KEY,
table_name TEXT NOT NULL,
record_id TEXT NOT NULL,
operation TEXT NOT NULL, -- 'create', 'update', 'delete'
data TEXT, -- JSON payload for create/update
created_at TEXT NOT NULL,
attempts INTEGER NOT NULL DEFAULT 0,
last_attempt_at TEXT,
error TEXT
);
-- Create indexes for common queries
CREATE INDEX IF NOT EXISTS idx_schedule_tasks_project ON schedule_tasks(project_id);
CREATE INDEX IF NOT EXISTS idx_task_dependencies_predecessor ON task_dependencies(predecessor_id);
CREATE INDEX IF NOT EXISTS idx_task_dependencies_successor ON task_dependencies(successor_id);
CREATE INDEX IF NOT EXISTS idx_project_members_project ON project_members(project_id);
CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members(user_id);
CREATE INDEX IF NOT EXISTS idx_agent_memories_conversation ON agent_memories(conversation_id);
CREATE INDEX IF NOT EXISTS idx_agent_memories_user ON agent_memories(user_id);
CREATE INDEX IF NOT EXISTS idx_sync_queue_table ON sync_queue(table_name);
CREATE INDEX IF NOT EXISTS idx_sync_queue_record ON sync_queue(record_id);