8.4 KiB
Supabase Skills Eval Scenarios
Scenario 1: auth-rls-new-project
Description: Set up a new Supabase project from scratch and add authentication with RLS. The agent must initialize the project with the CLI, start the local Supabase stack, then create a tasks table with proper security (RLS policies, auth FK, indexes) in a single idempotent migration.
Setup: The workspace starts empty (no supabase/ directory). The agent is
expected to run npx supabase init and npx supabase start before creating
the migration.
Expected skill files read:
SKILL.md(skill body with reference file index)references/dev-getting-started.mdreferences/db-rls-mandatory.mdreferences/db-rls-policy-types.mdreferences/db-rls-common-mistakes.mdreferences/db-schema-auth-fk.mdreferences/db-schema-timestamps.mdreferences/db-migrations-idempotent.md
Expected result:
The agent initializes a Supabase project and creates a migration file that:
- Creates tasks table with
timestamptzcolumns - Has
user_idFK toauth.users(id)withON DELETE CASCADE - Enables RLS (
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY) - Creates per-operation policies using
(select auth.uid())withTO authenticated - Creates index on
user_id - Uses
IF NOT EXISTSfor idempotency
Scorer: Binary pass/fail (12 vitest assertions)
| Test | What it checks |
|---|---|
| supabase project initialized | supabase/config.toml exists after agent runs |
| migration file exists | Agent created a .sql file in supabase/migrations/ |
| creates tasks table | SQL contains CREATE TABLE ... tasks |
| enables RLS | ALTER TABLE tasks ENABLE ROW LEVEL SECURITY |
| FK to auth.users | REFERENCES auth.users |
| ON DELETE CASCADE | Cascade delete on auth FK |
| (select auth.uid()) | Subselect form in policies (performance) |
| TO authenticated | Policies scoped to authenticated role |
| timestamptz | No plain timestamp for time columns |
| index on user_id | CREATE INDEX on the FK column |
| IF NOT EXISTS | Idempotent migration |
| overall quality | At least 4/5 best-practice signals present |
Scenario 2: team-rls-security-definer
Description: Create a SQL migration for a team-based project management app
where users belong to organizations via a membership table. The migration must
define tables for organizations, memberships, and projects, then secure them
with RLS policies that use a security definer helper function in a private
schema to efficiently resolve team membership without per-row joins.
Setup: The workspace starts with a pre-initialized Supabase project
(supabase/config.toml exists, empty supabase/migrations/ directory). The
agent creates migration files within this structure.
Expected skill files read:
SKILL.md(skill body with reference file index)references/db-rls-mandatory.mdreferences/db-rls-policy-types.mdreferences/db-rls-common-mistakes.mdreferences/db-rls-performance.mdreferences/db-security-functions.mdreferences/db-schema-auth-fk.mdreferences/db-schema-timestamps.mdreferences/db-perf-indexes.mdreferences/db-migrations-idempotent.md
Expected result:
The agent creates a migration file that:
- Creates organizations, memberships, and projects tables with
timestamptzcolumns - Has
user_idFK toauth.users(id)withON DELETE CASCADEon memberships - Has
org_idFK on projects referencing organizations - Enables RLS on all three tables
- Creates a private schema with a
security definerhelper function (SET search_path = '') - Creates RLS policies using
(select auth.uid())withTO authenticated - Creates indexes on membership lookup columns (user_id, org_id)
- Has a delete policy on projects restricted to owner role
- Uses
IF NOT EXISTSfor idempotency
Scorer: Binary pass/fail (16 vitest assertions)
| Test | What it checks |
|---|---|
| migration file exists | A .sql file exists in supabase/migrations/ |
| creates organizations table | SQL contains CREATE TABLE for organizations |
| creates memberships table | SQL contains CREATE TABLE for memberships |
| creates projects table | SQL contains CREATE TABLE for projects |
| enables RLS on all tables | ALTER TABLE ... ENABLE ROW LEVEL SECURITY for all three tables |
| FK to auth.users with ON DELETE CASCADE | memberships references auth.users with cascade |
| org_id FK on projects | projects references organizations |
| private schema created | CREATE SCHEMA ... private present |
| security_definer helper function | Function in private schema with SECURITY DEFINER and SET search_path = '' |
| policies use (select auth.uid()) | Subselect form in all policies referencing auth.uid() |
| policies use TO authenticated | All policies scoped to authenticated role |
| index on membership lookup columns | CREATE INDEX on user_id and/or org_id in memberships |
| uses timestamptz | No plain timestamp for time columns |
| idempotent DDL | Uses IF NOT EXISTS or DROP ... IF EXISTS patterns |
| delete policy restricted to owner role | A delete policy on projects checks for owner/admin role |
| overall quality score | At least 10/14 best-practice signals present |
Scenario 3: storage-rls-user-folders
Description: Create a SQL migration that sets up Supabase Storage buckets with RLS policies for user-content. An avatars bucket (public reads, authenticated uploads restricted to user folders) and a documents bucket (fully private, user-isolated), with file type restrictions, storage helper functions in policies, and a file_metadata tracking table secured with RLS.
Setup: Pre-initialized Supabase project (supabase/config.toml exists)
with an empty supabase/migrations/ directory. The agent creates migration
files within this structure.
Expected skill files read:
SKILL.md(skill body with reference file index)references/storage-access-control.mdreferences/db-rls-mandatory.mdreferences/db-rls-common-mistakes.mdreferences/db-rls-performance.mdreferences/db-schema-auth-fk.mdreferences/db-schema-timestamps.mdreferences/db-perf-indexes.mdreferences/db-migrations-idempotent.md
Expected result:
The agent creates a migration file that:
- Inserts avatars bucket into
storage.bucketswithpublic = true, MIME type restrictions, and file size limit - Inserts documents bucket with
public = false - Creates RLS policies on
storage.objectsusingstorage.foldername(name)withauth.uid()::text - Scopes upload policies
TO authenticatedand avatars SELECT policyTO public - Creates
file_metadatatable with FK toauth.userswithON DELETE CASCADE - Enables RLS on
file_metadatawith policies using(select auth.uid()) - Uses
timestamptzfor time columns, indexesuser_id, andIF NOT EXISTSfor idempotency
Scorer: Binary pass/fail (17 vitest assertions)
| Test | What it checks |
|---|---|
| migration file exists | A .sql file exists in supabase/migrations/ |
| creates avatars bucket | SQL inserts into storage.buckets with id 'avatars' and public = true |
| creates documents bucket | SQL inserts into storage.buckets with id 'documents' and public = false |
| avatars bucket has mime type restriction | allowed_mime_types includes image types (jpeg, png, webp) |
| avatars bucket has file size limit | file_size_limit set (around 2MB / 2097152 bytes) |
| storage policy uses foldername or path for user isolation | Policy references storage.foldername(name) with auth.uid()::text |
| storage policy uses TO authenticated | Storage upload/delete policies scoped to TO authenticated |
| public read policy for avatars | A SELECT policy on storage.objects for avatars allows public/anon access |
| documents bucket is fully private | Policies for documents restrict all operations to authenticated owner |
| creates file_metadata table | SQL contains CREATE TABLE for file_metadata |
| file_metadata has FK to auth.users with CASCADE | REFERENCES auth.users with ON DELETE CASCADE |
| RLS enabled on file_metadata | ALTER TABLE file_metadata ENABLE ROW LEVEL SECURITY |
| file_metadata policies use (select auth.uid()) | Subselect form in policies |
| uses timestamptz for time columns | No plain timestamp in file_metadata |
| index on file_metadata user_id | CREATE INDEX on user_id column |
| idempotent DDL | Uses IF NOT EXISTS patterns |
| overall quality score | At least 11/15 best-practice signals present |