This document describes the database schema for the async-code project, built on Supabase.
The database consists of three main tables:
users: User profiles with auto-sync from Supabase Auth (UUID PK for RLS simplicity)projects: GitHub repositories managed by users (BIGSERIAL PK for performance)tasks: AI automation tasks with chat messages, git patches, and execution status (BIGSERIAL PK for performance)
- Users table: Uses
UUIDto match Supabase Auth and simplify RLS policies (auth.uid() = user_id) - Projects & Tasks tables: Use
BIGSERIALfor optimal performance in joins, indexing, and frequent operations
Automatically synced with auth.users via database triggers.
CREATE TABLE public.users (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
email TEXT,
full_name TEXT,
avatar_url TEXT,
github_username TEXT,
github_token TEXT, -- Encrypted GitHub token
preferences JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);Features:
- ✅ Auto-sync with Supabase Auth
- ✅ Row Level Security (RLS) enabled
- ✅ Users can only access their own data
- ✅ Secure GitHub token storage
- ✅ Extensible preferences via JSONB
Represents GitHub repositories managed by users.
CREATE TABLE public.projects (
id BIGSERIAL PRIMARY KEY,
user_id UUID REFERENCES public.users(id) ON DELETE CASCADE NOT NULL,
-- GitHub repository information
repo_url TEXT NOT NULL,
repo_name TEXT NOT NULL,
repo_owner TEXT NOT NULL,
-- Project settings
name TEXT NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT true,
-- Custom settings (extensible)
settings JSONB DEFAULT '{}',
-- Metadata
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Constraints
UNIQUE(user_id, repo_url)
);Features:
- ✅ One project per user per repository
- ✅ Branch selection is task-specific, not project-specific
- ✅ Extensible settings via JSONB
- ✅ Full RLS protection
- ✅ Auto-updated timestamps
Stores AI automation tasks with full execution history.
CREATE TABLE public.tasks (
id BIGSERIAL PRIMARY KEY,
user_id UUID REFERENCES public.users(id) ON DELETE CASCADE NOT NULL,
project_id BIGINT REFERENCES public.projects(id) ON DELETE CASCADE,
-- Task information
status task_status DEFAULT 'pending', -- 'pending', 'running', 'completed', 'failed', 'cancelled'
agent TEXT DEFAULT 'claude', -- AI agent name (flexible string)
-- GitHub/Repository information
repo_url TEXT,
target_branch TEXT DEFAULT 'main', -- The branch we're targeting (e.g., 'main')
pr_branch TEXT, -- The branch we created for the PR (e.g., 'feature/optimize-readme')
-- Container and execution details
container_id TEXT,
-- Git workflow tracking
commit_hash TEXT, -- Final commit hash
pr_number INTEGER, -- Pull request number
pr_url TEXT, -- Full PR URL
-- Results and patches
git_diff TEXT,
git_patch TEXT,
changed_files JSONB DEFAULT '[]',
-- Error handling
error TEXT,
-- AI Chat Messages (stored as JSONB array)
chat_messages JSONB DEFAULT '[]', -- Array of {role, content, timestamp} objects
-- Execution metadata
execution_metadata JSONB DEFAULT '{}', -- Store execution logs, timing, etc.
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
started_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE
);Features:
- ✅ Complete task execution tracking with AI agents
- ✅ AI chat messages storage (no separate prompt field needed)
- ✅ Full Git workflow tracking (target branch, PR branch, PR number/URL)
- ✅ Git patch and diff storage
- ✅ Flexible metadata storage
- ✅ Comprehensive status tracking
- ✅ Flexible AI agent support (any string value)
- ✅ GitHub token from user settings (not per-task)
Clean and simple schema focusing on essential functionality:
- Minimal indexes for performance where needed
- Flexible agent field (TEXT instead of ENUM)
- User-level GitHub tokens (not per-task)
- Direct Supabase SDK usage (no custom functions)
All tables have RLS enabled with policies ensuring users can only access their own data:
- Users: Can view and update own profile
- Projects: Full CRUD access to own projects only
- Tasks: Full CRUD access to own tasks only
Essential indexes only for optimal performance:
-- Essential indexes only
CREATE INDEX idx_projects_user_id ON public.projects(user_id);
CREATE INDEX idx_tasks_user_id ON public.tasks(user_id);
CREATE INDEX idx_tasks_project_id ON public.tasks(project_id);
CREATE INDEX idx_tasks_status ON public.tasks(status);Run the SQL script in your Supabase dashboard:
# In Supabase SQL Editor, execute:
psql -f db/init_supabase.sqlEnsure you have authentication enabled in your Supabase project:
// In your app
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'your-project-url',
'your-anon-key'
)Set these environment variables:
SUPABASE_URL=your-project-url
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-keyNo migration functions needed - data will be handled through normal Supabase SDK operations during refactoring.
const { data, error } = await supabase
.from('projects')
.insert({
repo_url: 'https://github.com/user/repo',
repo_name: 'repo',
repo_owner: 'user',
name: 'My Project',
description: 'Project description'
});// Create a task with initial chat message
const { data, error } = await supabase
.from('tasks')
.insert({
project_id: projectId,
repo_url: 'https://github.com/user/repo',
target_branch: 'main',
agent: 'claude',
chat_messages: [
{
role: 'user',
content: 'Optimize the README file',
timestamp: Date.now() / 1000
}
]
});// Get user tasks with filtering
const { data, error } = await supabase
.from('tasks')
.select(`
*,
projects (
name,
repo_name
)
`)
.eq('status', 'completed')
.order('created_at', { ascending: false })
.limit(50);
// Get tasks by PR status
const { data: tasksWithPRs } = await supabase
.from('tasks')
.select('id, target_branch, pr_branch, pr_number, pr_url, status')
.not('pr_number', 'is', null)
.order('pr_number', { ascending: false });// Update chat messages array directly
const { data, error } = await supabase
.from('tasks')
.update({
chat_messages: [
...existingMessages,
{
role: 'assistant',
content: 'I have optimized your README file with better structure.',
timestamp: Date.now() / 1000
}
]
})
.eq('id', taskId);- Use Supabase SDK for all database operations
- Leverage JSONB fields for flexible data storage (chat_messages, settings)
- Follow RLS policies for automatic security
- Store GitHub tokens in user table (not per-task)
- Keep schema simple and add complexity only when needed
Key metrics to monitor:
- Task completion rates by model
- Average execution time per task
- Error rates and patterns
- User activity and engagement
- Database performance and query times