supabase-postgres-best-practices (supabase/agent-skills)
Guide la conception de schémas Postgres, migrations, RLS, index et performances des requêtes selon Supabase.
Supabase Postgres best practices : aucun gain par rapport à Codex seul pour les tâches de schéma, de revue et d'index (4.48 contre 4.54)
Codex CLI · gpt-6-astra
24.4 / 30
Gain par rapport à sans skill : -0.06 · Selon les cas · 18 exécutions
Selon les cas : le gain -0.06 est inférieur à +1.0. Pour être recommandé, il faut un total de 22/30, une qualité de sortie de 3.5 et un gain de +1.0.
SOTA
RéussiTesté avec Codex.
Couvre Codex, Claude Code
Codex CLI 0.166 (palier SOTA) : 18 exécutions notées. Une réussite ici couvre aussi Claude Code selon notre règle de palier ; elle ne dit rien du second palier.
Scores
Chaque dimension va de 0 à 5 et les six forment le total sur 30. 5 est la meilleure note dans chaque dimension. Coût 5 signifie bon marché et rapide. Installation 5 signifie facile.
- Fiabilité du déclenchement
- 5.0
- Gain par rapport à sans skill
- 1.9
- Qualité de sortie
- 4.5
- Coût en tokens et en temps
- 4.0
- Installation et portabilité
- 4.0
- Sécurité
- 5.0
Revue de sécurité
SûrScan statique : 0 CRITIQUE / 4 ÉLEVÉ / 0 MOYEN. Revue manuelle : sûr à exécuter dans un bac à sable sans réseau (résultats du scanner expliqués). Un scan propre ne prouve pas qu'un skill est sûr.
Installer
npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices
Commit épinglé: c9be0e931b7930f7d02126d04774d904c381e7d7
Testé: 2026-11-02
Ce que nous avons testé
- Schéma multi-tenant avec RLS — Écrivez schema.sql pour un tracker de projet Supabase (Postgres) multi-tenant : organizations, memberships (un utilisateur auth Supabase appartient à une organisation avec un rôle), projects, tasks (assigné, statut, échéance) et comments sur les tâches. Un utilisateur ne peut voir et modifier…
- Relire un schéma, des requêtes et un worker — Relisez schema.sql, queries.sql et worker.py (une application Supabase/Postgres de tickets de support). Listez les problèmes, du plus grave au moins grave, avec la correction pour chacun. Ne modifiez aucun fichier. Écrivez la liste dans votre réponse finale.
- Index pour trois requêtes lentes — Les trois requêtes de slow_queries.md sont lentes sur la table de schema.sql (120M de lignes). Écrivez migration.sql avec les changements d'index que vous feriez (et rien d'autre), et notes.md (120 mots maximum) indiquant quel index sert quelle requête et toute réserve. (Pas de base de données…
Points forts
- Le skill a été détecté sans être nommé : son fichier a été lu dans 6/6 exécutions avec skill.
Points faibles
- Aucun gain mesurable par rapport à sans skill (-0.06, dans le bruit).
- Un prompt d'une phrase obtient 4.75, contre 4.48 avec le skill (-0.27).
- n=2 par tâche et par bras ; les écarts inférieurs à environ 1 point sont du bruit.
Avis
Codex CLI 0.166 (gpt-6-astra, low reasoning) sur un Mac de test, un projet temporaire neuf et des HOME/TMPDIR/caches temporaires à chaque exécution, skill installé (traitement) ou absent (contrôle) ou absent avec l'exigence centrale du skill compressée en une phrase ajoutée au prompt (groupe « prompt d'une phrase »), 2 exécutions par groupe et par tâche, réseau coupé, écritures dans /tmp bloquées. Les prompts ne mentionnent jamais le skill (sauf la formulation indiquée ci-dessous). Trois tâches : écrire un schéma Supabase multi-tenant avec RLS (contrôles : RLS sur chaque table, auth.uid() enveloppé dans un select, chaque clé étrangère indexée, timestamptz, clés identity ou uuid, contraintes, noms en minuscules), relire un schéma/des requêtes/un worker défaillants pour 11 problèmes plantés, et écrire des migrations d'index plus des notes pour trois requêtes lentes sur une table de 120M de lignes (index composite, index partiel, CONCURRENTLY, 4 index maximum).
Comparaison à trois bras
| Bras | Score moyen (0-5) | Tokens par exécution |
|---|---|---|
| Sans le skill | 4.54 | 77k |
| Prompt d'une phrase | 4.75 | 80k |
| Avec le skill | 4.48 | 93k |
Mesuré avec Codex CLI sur 18 exécutions, noté par des contrôles basés sur des règles, sans juge LLM. Total 24.4 / 30, verdict Selon les cas.
Verdict : situationnel (24.4 / 30). Source : supabase/agent-skills, skills/supabase-postgres-best-practices, épinglé au commit c9be0e9. Installation : npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices. Licence : MIT (LICENSE du dépôt).
Ce que c'est
La compétence supabase-postgres-best-practices de supabase/agent-skills : des conseils de performance et de schéma Postgres pour les projets Supabase dans environ 35 fichiers de référence (performance des requêtes et index, gestion des connexions, sécurité et RLS, conception de schéma, concurrence et verrouillage, modèles d'accès aux données, supervision), avec un SKILL.md de 440 mots qui les indexe. Markdown uniquement, aucun script.
Comment nous avons testé
Codex CLI 0.166 (gpt-6-astra, raisonnement faible) sur une machine de test Mac, un projet temporaire neuf et des HOME/TMPDIR/caches temporaires à chaque exécution, compétence installée (avec compétence), absente (témoin) ou absente avec l'exigence essentielle de la compétence condensée en une phrase ajoutée au prompt (groupe prompt d'une phrase), 2 exécutions par groupe et par tâche, réseau désactivé, écritures dans /tmp bloquées. Les prompts ne mentionnent jamais la compétence (sauf la formulation indiquée ci-dessous).
Trois tâches : écrire un schéma Supabase multi-tenant avec RLS (contrôles : RLS sur chaque table, auth.uid() enveloppé dans un select, chaque clé étrangère indexée, clés timestamptz, identity ou uuid, contraintes, noms en minuscules), relire un schéma, des requêtes et un worker défectueux pour 11 problèmes implantés, et écrire des migrations d'index plus des notes pour trois requêtes lentes sur une table de 120M lignes (index composite, index partiel, CONCURRENTLY, 4 index au maximum).
- Schéma multi-tenant avec RLS — Écris schema.sql pour un gestionnaire de projet Supabase (Postgres) multi-tenant : organisations, appartenances (un utilisateur auth Supabase appartient à une organisation avec un rôle), projets, tâches (assigné, statut, échéance) et commentaires sur les tâches. Un utilisateur ne peut voir et modifier…
- Relire un schéma, des requêtes et un worker — Relis schema.sql, queries.sql et worker.py (une application Supabase/Postgres de tickets de support). Liste les problèmes, du plus grave au moins grave, avec la correction pour chacun. Ne modifie aucun fichier. Rédige la liste dans ta réponse finale.
- Index pour trois requêtes lentes — Les trois requêtes de slow_queries.md sont lentes sur la table de schema.sql (120M lignes). Écris migration.sql avec les changements d'index que tu ferais (et rien d'autre), ainsi que notes.md (120 mots maximum) indiquant quel index sert quelle requête et toute réserve. (Aucune base de données…
Le score est calculé de manière programmatique (tests masqués / détection des problèmes implantés / vérifications de contraintes / rendu + OCR le cas échéant), avec une note de 0–5 par exécution. Aucun juge LLM n'a attribué de score.
Résultats
Le prompt d'une phrase fourni au troisième groupe : « Applique les bonnes pratiques Postgres : indexe chaque clé étrangère et chaque colonne utilisée dans WHERE/ORDER BY (index composites avec les colonnes d'égalité en premier, index partiels pour les statuts rares, CREATE INDEX CONCURRENTLY sur les grosses tables), active RLS sur chaque table exposée et enveloppe auth.uid() dans (select auth.uid()), utilise des clés timestamptz, identity ou uuid et des noms snake_case en minuscules, pagine par keyset plutôt que par offset, et garde les transactions courtes (aucun appel externe à l'intérieur). »
| Tâche | Score témoin | Prompt d'une phrase | Avec compétence | Tokens témoin | Tokens du prompt d'une phrase | Tokens avec compétence | Compétence lue |
|---|---|---|---|---|---|---|---|
| t1-schema | 4.52 | 4.79 | 4.16 | 104k | 121k | 124k | 2/2 |
| t2-review | 4.09 | 4.46 | 4.28 | 64k | 56k | 70k | 2/2 |
| t3-indexes | 5.0 | 5.0 | 5.0 | 60k | 61k | 84k | 2/2 |
Score moyen : 4.54 sans contre 4.48 avec la compétence (écart -0.06). Tokens par exécution : 77k contre 93k (1.21x) ; temps 78s contre 73s. Groupe prompt d'une phrase : 4.75 (gain de la compétence par rapport au prompt d'une phrase : -0.27 ; gain propre du prompt d'une phrase par rapport à l'absence de compétence : +0.21) ; tokens du prompt d'une phrase : 80k par exécution, la compétence coûte donc 1.16x le prompt d'une phrase.
Ce qui s'est passé
- Aucune différence : moyenne 4.54 pour le témoin, 4.75 pour le prompt d'une phrase, 4.48 pour la compétence. La compétence est à -0.06 par rapport au témoin, pour 93k tokens par exécution contre 77k (1.2x).
- La tâche d'index est résolue par tous les groupes à toutes les exécutions (les six contrôles d'index passent 6/6 dans tous les groupes, y compris CONCURRENTLY et le respect de la limite de 4 index) : Codex seul connaît déjà ces bases.
- Sur la tâche de schéma, chaque groupe a indexé toutes les clés étrangères, activé RLS sur toutes les tables et utilisé des clés uuid/identity. L'enveloppement de auth.uid() dans (select auth.uid()) n'a été fait que pour une partie des politiques dans chaque groupe : 3 occurrences sur 5-6 pour le témoin et la compétence, 5 sur 5 dans une exécution du prompt d'une phrase. Le groupe prompt d'une phrase était le meilleur sur cette règle (1.6 sur 2 contre 1.1 pour le témoin et la compétence). Une exécution avec compétence a utilisé une simple colonne timestamp (3.86), le score le plus bas de toutes les exécutions.
- Sur la tâche de relecture, tous les groupes ont trouvé 8 à 9 des 11 problèmes implantés (1.5-1.7 sur 2 contrôles) ; la compétence n'en a pas trouvé plus.
- Le test a été relancé une fois pour la tâche d'index : la première série ne lui fournissait aucun fichier d'entrée à cause d'une erreur de spec (la liste des fixtures était vide), si bien que les 6 exécutions ont signalé des fichiers manquants. Nous avons corrigé la spec et relancé ces 6 exécutions ; les précédentes ne sont pas comptées.
Scores par dimension
| Dimension | Score | Pourquoi |
|---|---|---|
| 1_output_quality | 4.48 | Score moyen de 4.48/5 avec la compétence (contrôles programmatiques, voir le tableau par tâche). |
| 2_lift_vs_no_skill | 1.94 | -0.06 point par rapport à 4.54 sans la compétence (n=2 par tâche ; moins d'environ 1 point relève du bruit). |
| 3_trigger | 5.0 | Fichier de compétence lu dans 6/6 exécutions avec compétence. |
| 4_instructions_portability | 4 | Fichiers de règles en Markdown simple ; suit la disposition ouverte des compétences. |
| 5_efficiency | 4 | 1.21x de tokens (93k contre 77k par exécution) et 0.93x de temps (73s contre 78s). |
| 6_safety_hygiene | 5 | Markdown uniquement ; les quatre détections HIGH du scanner concernent le nom de table
|
Limites de ce test
- Des problèmes de manuel dans de petits fichiers ; les points propres à Supabase au-delà de RLS (pooling de connexions, Realtime, stockage) n'ont pas été testés.
- Les éléments sont détectés par expression régulière, une correction formulée différemment compte donc comme manquée.
- Les règles de la compétence sur le verrouillage, la supervision et les modèles d'accès aux données n'ont pas été exercées.
- n = 2 par groupe et par tâche : les écarts inférieurs à environ 1 point relèvent du bruit.
- Le score basé sur des règles mesure ce que les contrôles peuvent voir ; il ne peut juger du goût ni du caractère optimal d'un résultat.
Sécurité
Voir security.md. Analyse : 0 CRITICAL / 4 HIGH / 0 MEDIUM. Verdict : sûr à exécuter (sandbox sans réseau). Une analyse propre ne prouve pas qu'une compétence est sûre.
En bref
Sur les tâches de conception de schéma, de relecture et d'index, Codex seul applique déjà les pratiques listées par cette compétence, et celle-ci n'a pas amélioré le score tout en ajoutant environ 20 % de tokens. Elle peut rester une référence utile pour des sujets Supabase moins courants que ces tests n'ont pas abordés.
Transcription
Journal d'exécution : supabase-postgres-best-practices (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.) Colonnes : tâche, groupe-exécution, score (0-5), tokens, secondes, vérifications réussies, fichiers de la skill ouverts par l'agent. t1-schema control-1: score 4.57, tokens 105331, s 124.5, vérifications 6/7, rien ouvert t1-schema control-2: score 4.46, tokens 104624, s 130.1, vérifications 6/7, rien ouvert t1-schema oneliner-1: score 5.0, tokens 111153, s 140.4, vérifications 7/7, rien ouvert t1-schema oneliner-2: score 4.57, tokens 132693, s 145.1, vérifications 6/7, rien ouvert t1-schema treatment-1: score 3.86, tokens 123861, s 108.3, vérifications 5/7, ouvert supabase-postgres-best-practices t1-schema treatment-2: score 4.46, tokens 124377, s 129.6, vérifications 6/7, ouvert supabase-postgres-best-practices t2-review control-1: score 4.27, tokens 64780, s 65.4, vérifications 2/2, rien ouvert t2-review control-2: score 3.91, tokens 64695, s 71.5, vérifications 2/2, rien ouvert t2-review oneliner-1: score 3.91, tokens 45381, s 57.3, vérifications 2/2, rien ouvert
18 exécutions, 3 artefacts
Exemple de sortie
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]