All skills

supabase-postgres-best-practices (supabase/agent-skills)

Supabase Postgres best practices: no gain over plain Codex on schema, review and index tasks (4.48 vs 4.54)

SituationalSafe

24.4 / 30

Lift over no skill: -0.06

Situational: the lift -0.06 is under +1.0. Recommended needs a total of 22/30, output quality of 3.5, and a lift of +1.0.

SOTA

Pass

Tested with Codex.

Covers Codex, Claude Code

Codex CLI 0.166 (SOTA tier): 18 graded runs. A pass here also covers Claude Code under our tier rule; it says nothing about the second tier.

Scores

Each dimension is 0 to 5 and the six add up to the total out of 30. 5 is better on every dimension. Cost 5 means cheap and fast. Install 5 means easy.

Trigger reliability
5.0
Lift over no skill
1.9
Output quality
4.5
Token and time cost
4.0
Install and portability
4.0
Safety
5.0

Security review

Safe

Static scan: 0 CRITICAL / 4 HIGH / 0 MEDIUM. Manual review: Safe to run in a no-network sandbox (scanner findings explained). A clean scan does not prove a skill safe.

Install

npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices

Source

Pinned commit: c9be0e931b7930f7d02126d04774d904c381e7d7

Tested: 2026-11-02

What we tested

  • Multi-tenant schema with RLS — Write schema.sql for a Supabase (Postgres) multi-tenant project tracker: organizations, memberships (a Supabase auth user belongs to an organization with a role), projects, tasks (assignee, status, due date) and comments on tasks. A user may only see and chang…
  • Review a schema, queries and a worker — Review schema.sql, queries.sql and worker.py (a Supabase/Postgres support-ticket app). List the problems, most serious first, each with the fix. Do not change any files. Write the list in your final answer.
  • Indexes for three slow queries — The three queries in slow_queries.md are slow on the table in schema.sql (120M rows). Write migration.sql with the index changes you would make (and nothing else), and notes.md (at most 120 words) saying which index serves which query and any caveat. (No datab…

Pros

  • The skill was picked up without being named: Skill file read in 6/6 treatment runs.

Cons

  • No measurable lift over no skill (-0.06, inside the noise).
  • A one-sentence prompt scored 4.75, against 4.48 with the skill (-0.27).
  • n=2 per task per arm; differences under about 1 point are noise.

Review

Codex CLI 0.166 (gpt-6-astra, low reasoning) on a Mac test machine, a fresh temporary project and temporary HOME/TMPDIR/caches per run, skill installed (treatment) or absent (control) or absent with the skill's core requirement squeezed into one sentence appended to the prompt (one-liner group), 2 runs per group per task, network off, /tmp writes blocked. The prompts never mention the skill (except the wording noted below). Three tasks: write a multi-tenant Supabase schema with RLS (checks: RLS on every table, auth.uid() wrapped in a select, every foreign key indexed, timestamptz, identity or uuid keys, constraints, lowercase names), review a flawed schema/queries/worker for 11 planted problems, and write index migrations plus notes for three slow queries on a 120M-row table (composite index, partial index, CONCURRENTLY, at most 4 indexes).

Three-arm comparison

ArmMean score (0-5)Tokens per run
Without the skill4.5477k
One-sentence prompt4.7580k
With the skill4.4893k

Measured with Codex CLI over 18 runs, scored by rule-based checks with no LLM judge. Total 24.4 / 30, verdict Situational.

Verdict: Situational (24.4 / 30). Source: supabase/agent-skills, skills/supabase-postgres-best-practices, pinned at commit c9be0e9. Install: npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices. Licence: MIT (repo LICENSE).

What it is

The supabase-postgres-best-practices skill from supabase/agent-skills: Postgres performance and schema guidance for Supabase projects in about 35 reference files (query performance and indexes, connection management, security and RLS, schema design, concurrency and locking, data access patterns, monitoring), with a 440-word SKILL.md that indexes them. Markdown only, no scripts.

How we tested

Codex CLI 0.166 (gpt-6-astra, low reasoning) on a Mac test machine, a fresh temporary project and temporary HOME/TMPDIR/caches per run, skill installed (treatment) or absent (control) or absent with the skill's core requirement squeezed into one sentence appended to the prompt (one-liner group), 2 runs per group per task, network off, /tmp writes blocked. The prompts never mention the skill (except the wording noted below).

Three tasks: write a multi-tenant Supabase schema with RLS (checks: RLS on every table, auth.uid() wrapped in a select, every foreign key indexed, timestamptz, identity or uuid keys, constraints, lowercase names), review a flawed schema/queries/worker for 11 planted problems, and write index migrations plus notes for three slow queries on a 120M-row table (composite index, partial index, CONCURRENTLY, at most 4 indexes).

  1. Multi-tenant schema with RLS — Write schema.sql for a Supabase (Postgres) multi-tenant project tracker: organizations, memberships (a Supabase auth user belongs to an organization with a role), projects, tasks (assignee, status, due date) and comments on tasks. A user may only see and chang…
  2. Review a schema, queries and a worker — Review schema.sql, queries.sql and worker.py (a Supabase/Postgres support-ticket app). List the problems, most serious first, each with the fix. Do not change any files. Write the list in your final answer.
  3. Indexes for three slow queries — The three queries in slow_queries.md are slow on the table in schema.sql (120M rows). Write migration.sql with the index changes you would make (and nothing else), and notes.md (at most 120 words) saying which index serves which query and any caveat. (No datab…

Scoring is programmatic (hidden tests / planted-issue recall / constraint checks / render+OCR where relevant), 0–5 per run. No LLM judge decided any score.

Results

The one-liner given to the third group: "Apply Postgres best practices: index every foreign key and every column used in WHERE/ORDER BY (composite indexes with equality columns first, partial indexes for rare statuses, CREATE INDEX CONCURRENTLY on big tables), enable RLS on every exposed table and wrap auth.uid() in (select auth.uid()), use timestamptz, identity or uuid keys and lowercase snake_case names, paginate with keyset not offset, and keep transactions short (no external calls inside)."

TaskControl scoreOne-linerWith skillControl tokensOne-liner tokensWith-skill tokensSkill read
t1-schema4.524.794.16104k121k124k2/2
t2-review4.094.464.2864k56k70k2/2
t3-indexes5.05.05.060k61k84k2/2

Mean score: 4.54 without vs 4.48 with the skill (delta -0.06). Tokens per run: 77k vs 93k (1.21x); time 78s vs 73s. One-liner group: 4.75 (the skill's gain over the one-liner: -0.27; one-liner's own gain over no skill: +0.21); one-liner tokens 80k per run, so the skill costs 1.16x the one-liner.

What happened

  • No difference: mean 4.54 control, 4.75 one-liner, 4.48 skill. The skill is -0.06 against control at 93k tokens per run against 77k (1.2x).
  • The index task is solved by every group in every run (all six indexes checks pass 6/6 in all groups, including CONCURRENTLY and keeping to 4 indexes): plain Codex already knows these basics.
  • On the schema task every group indexed all foreign keys, enabled RLS on all tables and used uuid/identity keys. Wrapping auth.uid() in (select auth.uid()) was done for only part of the policies in every group: 3 of 5-6 occurrences in control and skill runs, 5 of 5 in one one-liner run. The one-liner group was best on this rule (1.6 of 2 against 1.1 for control and skill). One skill run used a plain timestamp column (3.86), which is the lowest score of any run.
  • On the review task all groups found 8-9 of the 11 planted problems (1.5-1.7 of 2 checks); the skill did not find more.
  • The test was rerun once for the index task: the first batch gave it no input files because of a spec error (the fixture list was empty), so all 6 runs said the files were missing. We fixed the spec and re-ran those 6 runs; the earlier ones are not counted.

Dimension scores

DimensionScoreWhy
1_output_quality4.48

Mean score 4.48/5 with the skill (programmatic checks, see per-task table).

2_lift_vs_no_skill1.94

-0.06 points vs 4.54 without the skill (n=2 per task; under ~1 point is noise).

3_trigger5.0Skill file read in 6/6 treatment runs.
4_instructions_portability4Plain Markdown rule files; follows the open skills layout.
5_efficiency41.21x tokens (93k vs 77k per run) and 0.93x time (73s vs 78s).
6_safety_hygiene5

Markdown only; the scanner's four HIGH hits are the table name public.profiles matching a .profile pattern.

Limits of this test

  • Textbook problems in small files; Supabase-specific points beyond RLS (connection pooling, Realtime, storage) were not tested.
  • Items are matched by regex, so a differently worded fix counts as a miss.
  • The skill's rules on locking, monitoring and data-access patterns were not exercised.
  • n = 2 per group per task: differences under about 1 point are noise.
  • Rule-based scoring measures what the checks can see; it cannot judge taste or whether a result is the best possible.

Security

See security.md. Scan: 0 CRITICAL / 4 HIGH / 0 MEDIUM. Verdict: safe to run (no-network sandbox). A clean scan does not prove a skill safe.

Bottom line

On schema design, review and index tasks plain Codex already applies the practices this skill lists, and the skill did not improve the score while adding about 20% tokens. It may still be a useful reference for less common Supabase topics that these tests did not touch.

Transcript

Run log for supabase-postgres-best-practices: Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.
Columns: task, arm-run, score (0-5), tokens, seconds, checks passed, skill files the agent opened.

t1-schema control-1: score 4.57, tokens 105331, secs 124.5, checks 6/7, opened nothing
t1-schema control-2: score 4.46, tokens 104624, secs 130.1, checks 6/7, opened nothing
t1-schema oneliner-1: score 5.0, tokens 111153, secs 140.4, checks 7/7, opened nothing
t1-schema oneliner-2: score 4.57, tokens 132693, secs 145.1, checks 6/7, opened nothing
t1-schema treatment-1: score 3.86, tokens 123861, secs 108.3, checks 5/7, opened supabase-postgres-best-practices
t1-schema treatment-2: score 4.46, tokens 124377, secs 129.6, checks 6/7, opened supabase-postgres-best-practices
t2-review control-1: score 4.27, tokens 64780, secs 65.4, checks 2/2, opened nothing
t2-review control-2: score 3.91, tokens 64695, secs 71.5, checks 2/2, opened nothing
t2-review oneliner-1: score 3.91, tokens 45381, secs 57.3, checks 2/2, opened nothing

18 runs, 3 artifacts

Sample output

SOTA · With the skill: schema.sql (t1-schema, run 1, Codex)

-- Fresh Supabase database: run once as postgres (trusted BYPASSRLS owner). -- Members manage tenant content; admins also manage settings and memberships. -- service_role bypasses RLS: keep its credentials exclusively on the backend. begin; create schema if not exists private; revoke all on schema private from public, anon; grant usage on schema private to authenticated; -- Keep private out of the Data API's exposed schemas. create table public.organizations ( id uuid primary key default gen_random_uuid(), name text not null check (length(btrim(name)) between 1 and 200), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table public.memberships ( organization_id uuid not null references public.organizations(id) on delete cascade, user_id uuid not null references auth.users(id) on delete cascade, role text not null default 'member' check (role in ('admin', 'member')), created_at timestamptz not null default now(), primary key (organization_id, user_id) ); -- Reverse lookup for the caller's tenants, RLS, and auth user deletion. create index memberships_user_idx on public.memberships (user_id, organization_id) include (role); create table public.projects ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, name text not null check (length(btrim(name)) between 1 and 200), description text not null default '', created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id) ); create index projects_list_idx on public.projects (organization_id, created_at desc, id desc); create table public.tasks ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, project_id uuid not null, title text not null check (length(btrim(title)) between 1 and 500), description text not null default '', assignee_id uuid, status text not null default 'todo' check (status in ('todo', 'in_progress', 'done', 'cancelled')), due_date date, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id), foreign key (organization_id, project_id) references public.projects (organization_id, id) on delete cascade, -- Unassign tasks before deleting a membership/auth user. Deferred checking -- permits deleting an entire organization and all its children atomically. foreign key (organization_id, assignee_id) references public.memberships (organization_id, user_id) on delete no action deferrable initially deferred ); create index tasks_project_list_idx on public.tasks (organization_id, project_id, created_at desc, id desc); create index tasks_board_idx on public.tasks (organization_id, project_id, status, created_at desc, id desc); create index tasks_assignee_idx on public.tasks (organization_id, assignee_id, status, due_date, id) where assignee_id is not null; create index tasks_open_due_idx on public.tasks (organization_id, due_date, id) where status in ('todo', 'in_progress') [... excerpt: first 3200 of 11674 characters]

See Pro