diff --git a/.env.example b/.env.example index 19a0696f..a7cb97a9 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,9 @@ # AI SDK Core direct-provider credentials. ANTHROPIC_API_KEY= OPENAI_API_KEY= + +# Vercel AI Gateway — one key for every vendor. Direct keys above stay the +# default; set RUN_THROUGH_GATEWAY=true (the eval-refresh workflow's +# run_through_gateway input) to route the whole run through the gateway. +AI_GATEWAY_API_KEY= +RUN_THROUGH_GATEWAY= \ No newline at end of file diff --git a/.github/workflows/eval-refresh.yml b/.github/workflows/eval-refresh.yml index 42d5e25a..5c7d2847 100644 --- a/.github/workflows/eval-refresh.yml +++ b/.github/workflows/eval-refresh.yml @@ -27,6 +27,11 @@ on: description: "Timeout per attempt in seconds" required: true default: "720" + run_through_gateway: + description: "Route model traffic through the Vercel AI Gateway instead of per-vendor API keys" + type: boolean + required: false + default: false merge: description: "Merge into existing results instead of overwriting (graft new experiment/eval pairs)" type: boolean @@ -62,11 +67,13 @@ jobs: github.event_name == 'schedule' || (github.event_name == 'pull_request' && (contains(github.event.pull_request.labels.*.name, 'run-evals') || - contains(github.event.pull_request.labels.*.name, 'run-evals-changed')) && + contains(github.event.pull_request.labels.*.name, 'run-evals-changed') || + contains(github.event.pull_request.labels.*.name, 'run-evals-through-gateway')) && github.event.pull_request.head.repo.full_name == github.repository && (github.event.action != 'labeled' || github.event.label.name == 'run-evals' || - github.event.label.name == 'run-evals-changed')) + github.event.label.name == 'run-evals-changed' || + github.event.label.name == 'run-evals-through-gateway')) runs-on: ubuntu-latest outputs: pairs: ${{ steps.discover.outputs.pairs }} @@ -74,6 +81,7 @@ jobs: timeout_sec: ${{ steps.inputs.outputs.timeout_sec }} filter_changed: ${{ steps.inputs.outputs.filter_changed }} do_merge: ${{ steps.inputs.outputs.do_merge }} + run_through_gateway: ${{ steps.inputs.outputs.run_through_gateway }} steps: - name: Prepare inputs id: inputs @@ -123,6 +131,17 @@ jobs: do_merge="true" fi + # Route model traffic through the Vercel AI Gateway instead of the + # per-vendor keys: the run_through_gateway dispatch input, or the + # run-evals-through-gateway PR label. + run_through_gateway="false" + if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.run_through_gateway }}" = "true" ]; then + run_through_gateway="true" + elif [ "${{ github.event_name }}" = "pull_request" ] && \ + [ "${{ contains(github.event.pull_request.labels.*.name, 'run-evals-through-gateway') }}" = "true" ]; then + run_through_gateway="true" + fi + { echo "experiments_override=$experiments_override" echo "eval=$eval_id" @@ -132,6 +151,7 @@ jobs: echo "timeout_sec=$timeout_sec" echo "filter_changed=$filter_changed" echo "do_merge=$do_merge" + echo "run_through_gateway=$run_through_gateway" } >> "$GITHUB_OUTPUT" - name: Checkout @@ -259,6 +279,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + AI_GATEWAY_API_KEY: ${{ secrets.AI_GATEWAY_API_KEY }} steps: - name: Checkout uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3 @@ -283,9 +304,18 @@ jobs: run: | set -euo pipefail + # In gateway mode the vendor keys are withheld from the eval run — + # agent traffic authenticates with AI_GATEWAY_API_KEY alone, so a + # passing run is proof the gateway carried it. OPENAI_API_KEY stays: + # the judge intentionally runs direct in both modes so scores remain + # comparable across routing paths. { - echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}" + if [ "${{ needs.prepare.outputs.run_through_gateway }}" != "true" ]; then + echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}" + fi echo "OPENAI_API_KEY=${OPENAI_API_KEY}" + echo "AI_GATEWAY_API_KEY=${AI_GATEWAY_API_KEY}" + echo "RUN_THROUGH_GATEWAY=${{ needs.prepare.outputs.run_through_gateway }}" } > .env - name: Run evals diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index a891a817..55680f33 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -313,11 +313,13 @@ function formatOpenAiModel(modelId: string) { } function formatModel(display: ExperimentDisplay) { + // AI Gateway model ids are `vendor/model` slugs; format just the model part. + const modelId = display.modelId.replace(/^[a-z-]+\//, "") switch (display.modelProvider) { case "anthropic": - return formatAnthropicModel(display.modelId) + return formatAnthropicModel(modelId) case "openai": - return formatOpenAiModel(display.modelId) + return formatOpenAiModel(modelId) } } diff --git a/apps/web/src/data/eval-results.json b/apps/web/src/data/eval-results.json index 556790c2..67ba450e 100644 --- a/apps/web/src/data/eval-results.json +++ b/apps/web/src/data/eval-results.json @@ -165,7 +165,7 @@ { "name": "process-tasks function drains the queue", "passed": true, - "notes": "function removed the seeded message (id 5) from the queue" + "notes": "function removed the seeded message (id 7) from the queue" } ], "skills": { @@ -181,59 +181,58 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"pg_cron schedule job invoke edge function net.http_post queue\", limit: 6) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"pgmq queues send read pop delete message SQL\", limit: 6) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" + "url": "https://supabase.com/docs/guides/queues/pgmq", + "title": "PGMQ Extension" }, { - "url": "https://supabase.com/docs/guides/cron/quickstart", + "url": "https://supabase.com/docs/guides/queues/quickstart", "title": "Quickstart" }, { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/webhook-debugging-guide-M8sk47", - "title": "Webhook debugging guide" + "url": "https://supabase.com/docs/guides/queues", + "title": "Supabase Queues" }, { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" + "url": "https://supabase.com/docs/guides/queues/api", + "title": "API" } ], - "resultChars": 62880 + "resultChars": 36950 }, { "source": "search_docs", - "query": "{ searchDocs(query: \"queues read pop delete messages consume edge function pgmq_public rpc\", limit: 4) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"pg_cron schedule job every minute cron.schedule SQL\", limit: 3) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" + "url": "https://supabase.com/docs/guides/database/extensions/pg_net", + "title": "pg_net: Async Networking" }, { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" + "url": "https://supabase.com/docs/guides/auth/auth-hooks/send-sms-hook", + "title": "Send SMS Hook" }, { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - }, + "url": "https://supabase.com/docs/guides/auth/auth-hooks/send-email-hook", + "title": "Send Email Hook" + } + ], + "resultChars": 79601 + }, + { + "source": "web_fetch", + "query": "How do you create a scheduled cron job that runs every minute using cron.schedule in SQL? Show the exact SQL syntax including job name, schedule string, and command. Also how to unschedule/replace an existing job by name.", + "hasContent": true, + "pages": [ { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" + "url": "https://supabase.com/docs/guides/cron.md" } ], - "resultChars": 60050 + "resultChars": 1666 } ] }, @@ -408,17 +407,17 @@ { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"d6d5fe0c-99bd-4131-bf20-272b4ea4e9fd\",\"metric\":\"steps_a_mro0usf9\",\"value\":111}]" + "notes": "status 200: [{\"user_id\":\"efadb677-23ea-4038-9da1-da13b2fad52c\",\"metric\":\"steps_a_mrz1qmeh\",\"value\":111}]" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: [{\"user_id\":\"d6d5fe0c-99bd-4131-bf20-272b4ea4e9fd\",\"metric\":\"steps_a_mro0usf9\",\"value\":111}]" + "notes": "status 200: [{\"user_id\":\"efadb677-23ea-4038-9da1-da13b2fad52c\",\"metric\":\"steps_a_mrz1qmeh\",\"value\":111}]" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"dca2d817-6fa6-47ce-9655-c733eb8a7c63\",\"metric\":\"steps_b_mro0usf9\",\"value\":222}]" + "notes": "status 200: [{\"user_id\":\"01ddb0db-d292-4485-905f-92d487c88d3e\",\"metric\":\"steps_b_mrz1qmeh\",\"value\":222}]" }, { "name": "non-service key is not granted service access", @@ -454,60 +453,92 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"edge function service role key apikey header authenticate user getUser\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"edge function authentication service role verify_jwt apikey header\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/reference/javascript/auth-admin-deleteuser" + "url": "https://supabase.com/docs/guides/getting-started/api-keys", + "title": "Understanding API keys" }, { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" }, { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" + "url": "https://supabase.com/docs/guides/functions/auth-headers", + "title": "Authorization headers" }, { - "url": "https://supabase.com/docs/reference/javascript/auth-admin-listusers" + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" }, { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", + "title": "Envoy API Gateway" } ], - "resultChars": 34937 + "resultChars": 86244 }, { - "source": "search_docs", - "query": "{ searchDocs(query: \"withSupabase @supabase/server edge function auth publishable secret authMode ctx supabaseAdmin getUser\", limit: 6) { nodes { title href content } } }", + "source": "web_fetch", + "query": "How does an edge function authenticate callers? How to verify a user's JWT and get their user id. How does verify_jwt work and how to disable it per-function in config.toml. What environment variables are available (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, new publishable/secret keys). How does the apikey header vs Authorization header work.", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, + "url": "https://supabase.com/docs/guides/functions/auth.md" + } + ], + "resultChars": 2437 + }, + { + "source": "web_fetch", + "query": "What are the new publishable (sb_publishable_) and secret (sb_secret_) API keys? How do they replace anon and service_role keys? What environment variables are exposed to edge functions for these keys? Does the secret key bypass RLS like service_role?", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/api/api-keys.md" + } + ], + "resultChars": 1568 + }, + { + "source": "web_fetch", + "query": "Show the full API of withSupabase from @supabase/server. Can the auth option accept multiple modes at once (e.g. both 'user' and 'secret') for one function that serves two different caller types? How is ctx.supabase vs ctx.supabaseAdmin populated in each mode? Show complete code examples including imports and the exact jsr/npm specifier and version.", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/functions/auth" + } + ], + "resultChars": 3169 + }, + { + "source": "web_search", + "query": "@supabase/server withSupabase auth modes array user secret dual auth edge function", + "hasContent": false, + "pages": [ { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" + "url": "https://supabase.com/docs/guides/functions/auth", + "title": "Securing Edge Functions | Supabase Docs" }, { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" + "url": "https://supabase.com/blog/introducing-supabase-server", + "title": "Introducing @supabase/server" }, { - "url": "https://supabase.com/docs/reference/javascript/auth-getuser" + "url": "https://supabase.com/docs/reference/server/types-supabaseenv", + "title": "Server API Reference | Supabase Docs" }, { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" + "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", + "title": "Integrating With Supabase Auth | Supabase Docs" }, { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" + "url": "https://supabase.com/docs/guides/functions", + "title": "Edge Functions | Supabase Docs" } ], - "resultChars": 46083 + "resultChars": 5886 } ] }, @@ -554,7 +585,7 @@ { "name": "user A lists only own files", "passed": true, - "notes": "saw: 019f6c9d-f613-743e-92ba-d858089774ab/receipt-alpha.pdf, 019f6c9d-f613-743e-92ba-d858089774ab/receipt-beta.pdf" + "notes": "saw: 019f9488-8085-7530-bf9c-013b13e5a0c3/receipt-alpha.pdf, 019f9488-8085-7530-bf9c-013b13e5a0c3/receipt-beta.pdf" }, { "name": "user B cannot read user A files", @@ -575,7 +606,7 @@ { "name": "configured private per-user storage access", "passed": true, - "judgeNotes": "Creates private user-files bucket, owner-scoped SELECT and INSERT policies on storage.objects for authenticated users using first folder = auth.uid(), keeps RLS enabled (does not disable it), and provides supabase-js createSignedUrl code with expiry. No public bucket/getPublicUrl/service-role misuse." + "judgeNotes": "Creates a private user-files bucket, defines authenticated owner-scoped SELECT and INSERT policies (plus update/delete) on storage.objects using the user ID path segment, does not disable RLS or use public access, and provides supabase-js createSignedUrl code with an expiry." } ], "skills": { @@ -591,44 +622,27 @@ "calls": [ { "source": "search_docs", - "query": "{ storage: searchDocs(query: \"storage bucket RLS policy user folder path auth.uid\", limit: 5) { nodes { title href content } } signed: searchDocs(query: \"createSignedUrl temporary link expires storage\", limit: 4) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"storage RLS policy restrict users to own folder user id path\", limit: 4) { nodes { title href content } } }", "hasContent": true, "pages": [ + { + "url": "https://supabase.com/docs/guides/storage/security/ownership", + "title": "Ownership" + }, { "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", "title": "Storage Helper Functions" }, { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs", - "title": "Build a User Management App with Next.js" + "url": "https://supabase.com/docs/guides/resources/glossary", + "title": "Glossary" }, { "url": "https://supabase.com/docs/guides/storage/security/access-control", "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurl" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurls" - }, - { - "url": "https://supabase.com/docs/reference/dart/file-buckets-createsignedurl" } ], - "resultChars": 68372 + "resultChars": 18826 } ] }, @@ -667,12 +681,12 @@ { "name": "pgTAP isolation tests ran and pass", "passed": true, - "notes": "3 passed, 2 failed" + "notes": "6 passed, 2 failed" }, { "name": "agent correctly identifies the posts isolation bug from test results", "passed": true, - "judgeNotes": "Correctly identifies `posts` as having a broken tenant isolation SELECT policy, grounded in pgTAP failure showing org1 member can read org2 posts. Does not blame `notes` and treats test results as authoritative." + "judgeNotes": "Correctly identifies `posts` as the broken tenant isolation policy, explains the missing org predicate, treats pgTAP failures as authoritative, and notes that `notes` passes/is correctly scoped." } ], "skills": { @@ -723,12 +737,12 @@ { "name": "HNSW index on the embedding column", "passed": true, - "notes": "CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" + "notes": "CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" }, { "name": "index operator class matches the search operator", "passed": true, - "notes": "function operators: <=>\nindexes: CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" + "notes": "function operators: <#>\nindexes: CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" }, { "name": "user A search returns only own sections, best match first", @@ -760,37 +774,37 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"semantic search gte-small embeddings match_document_sections RLS\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"semantic search pgvector match_documents function gte-small embedding column RLS\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" + "url": "https://supabase.com/docs/guides/ai/hybrid-search", + "title": "Hybrid search" }, { "url": "https://supabase.com/docs/guides/ai/semantic-search", "title": "Semantic search" }, { - "url": "https://supabase.com/docs/guides/database/extensions/pgvector", - "title": "pgvector: Embeddings and vector similarity" + "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", + "title": "RAG with Permissions" }, { - "url": "https://supabase.com/docs/guides/ai/choosing-compute-addon", - "title": "Choosing your Compute Add-on" + "url": "https://supabase.com/docs/guides/ai/vector-columns", + "title": "Vector columns" }, { - "url": "https://supabase.com/docs/guides/functions/examples/semantic-search", - "title": "Semantic Search" + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" } ], - "resultChars": 57915 + "resultChars": 78090 } ] }, "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", - "attempts": 2, + "attempts": 1, "sourcePath": "claude-code-opus-4.8/build-vectors-001-rag-with-permissions.json" }, { @@ -820,12 +834,12 @@ { "name": "configured the Supabase Metrics API scrape correctly", "passed": true, - "judgeNotes": "Meets requirements: preserves app scrape, adds Supabase HTTPS scrape with correct metrics_path, Basic Auth using password_file, project target on .supabase.co:443, and docker-compose mounts the secrets directory containing the password file read-only." + "judgeNotes": "Supabase scrape uses HTTPS, correct metrics path, Basic Auth with password_file, target on .supabase.co, app scrape is preserved, and docker-compose mounts the secret file into Prometheus." }, { "name": "documented live deployment and verification steps", "passed": true, - "judgeNotes": "README includes concrete live setup steps: replace project ref, create a Supabase Secret API key, write it to the expected secret file mounted by Compose, and reload/start the Compose stack. It also provides verification via curl, Prometheus targets, and PromQL/Grafana guidance. No hardcoded real secret or mismatched setup detected." + "judgeNotes": "README includes steps to set project ref, create and place the Secret API key in observability/supabase.secret, restart/reload the Compose/Prometheus stack, and verify via curl and Prometheus targets." } ], "skills": { @@ -841,13 +855,9 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"Prometheus metrics endpoint observability Grafana integration\", limit: 6) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"Prometheus metrics endpoint project observability scrape\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, { "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", "title": "Metrics API with Prometheus & Grafana (self-hosted)" @@ -861,22 +871,26 @@ "title": "Vendor-agnostic Metrics API setup" }, { - "url": "https://supabase.com/docs/guides/troubleshooting/how-to-view-database-metrics-uqf2z_", - "title": "How to View Database Metrics" + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", + "title": "Metrics API with Grafana Cloud" + }, + { + "url": "https://supabase.com/docs/guides/platform/read-replicas", + "title": "Read Replicas" } ], - "resultChars": 24393 + "resultChars": 32656 }, { "source": "web_fetch", - "query": "What is the exact Supabase project metrics endpoint URL, what authentication does it use (username/password), what is the recommended Prometheus scrape config (job, scrape_interval, metrics_path, basic_auth, scheme), and any Grafana dashboard details? Quote exact config snippets.", + "query": "What is the exact Prometheus metrics endpoint URL for a Supabase project, what authentication does it require (username/password), and what is the recommended Prometheus scrape_config (metrics_path, scheme, basic_auth, scrape_interval)? Quote the example config if present.", "hasContent": true, "pages": [ { "url": "https://supabase.com/docs/guides/telemetry/metrics.md" } ], - "resultChars": 1190 + "resultChars": 1166 } ] }, @@ -935,7 +949,40 @@ ] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"edge function secrets environment variables deploy\", limit: 6) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/troubleshooting/inspecting-edge-function-environment-variables-wg5qOQ", + "title": "Inspecting edge function environment variables" + }, + { + "url": "https://supabase.com/docs/guides/functions/secrets", + "title": "Environment Variables" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + }, + { + "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech", + "title": "Transcription Telegram Bot" + }, + { + "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream", + "title": "Streaming Speech with ElevenLabs" + }, + { + "url": "https://supabase.com/docs/guides/deployment/branching/configuration", + "title": "Configuration" + } + ], + "resultChars": 51315 + } + ] }, "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", @@ -987,33 +1034,20 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [ { "source": "web_fetch", - "query": "List the exact steps to self-host Supabase with Docker. Include: which files/directories to copy (docker-compose.yml, .env.example, volumes/), the exact commands to obtain them, the full list of secrets/env vars that must be set in .env (POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, DASHBOARD_USERNAME, DASHBOARD_PASSWORD, SECRET_KEY_BASE, VAULT_ENC_KEY, pooler tenant/keys, etc.), how to generate JWT anon and service_role keys, and any security notes about changing default credentials.", + "query": "Extract the full step-by-step instructions for self-hosting Supabase with Docker. Specifically: which files/folders are needed (docker-compose.yml, .env, volumes/), the exact git commands to obtain them, which secrets must be generated and changed from defaults (JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, POSTGRES_PASSWORD, DASHBOARD_USERNAME/PASSWORD, SECRET_KEY_BASE, VAULT_ENC_KEY, pooler keys, etc.), how to generate the API keys (anon/service role JWTs), and any security guidance about securing your services before going to production.", "hasContent": true, "pages": [ { "url": "https://supabase.com/docs/guides/self-hosting/docker" } ], - "resultChars": 4558 - }, - { - "source": "web_fetch", - "query": "List any recent breaking-change entries related to self-hosting, Docker, docker-compose, env vars, JWT keys, API keys (anon/service_role/publishable/secret), or the analytics/logflare/vector/pooler services.", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ], - "resultChars": 1453 + "resultChars": 4475 } ] }, @@ -1073,7 +1107,7 @@ { "name": "diagnosed and explained session revocation", "passed": true, - "judgeNotes": "Identifies soft-delete-only cause and lack of session revocation; implements meaningful revocation by banning auth user and deleting sessions/refresh tokens plus RLS enforcement of deleted flag. Explains JWTs remain valid until expiry for local validation, while DB path is closed immediately due to RLS checking active account, which is consistent with the implemented fix. Correctly distinguishes publishable/frontend/RLS-enforced keys from secret/server-only/RLS-bypassing keys." + "judgeNotes": "Diagnoses soft-delete-only flow, implements real session/refresh revocation via auth.sessions deletion and banning auth user, adds RLS deleted checks to close data-path window, accurately caveats stateless JWT validity for local validation/unguarded endpoints, and correctly distinguishes publishable frontend key with RLS from secret server-only RLS-bypassing key." } ], "skills": { @@ -1139,7 +1173,7 @@ { "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "The assistant correctly diagnosed that the channel can be SUBSCRIBED while orders INSERT events are silent because public.orders was missing from the supabase_realtime publication. It fixed exactly that with ALTER PUBLICATION supabase_realtime ADD TABLE public.orders, verified publication membership, and did not alter RLS, policies, courier_locations, or client code." + "judgeNotes": "The assistant correctly identified that orders was missing from the supabase_realtime publication despite the channel reaching SUBSCRIBED, added only public.orders to the existing publication, verified membership, and did not alter RLS/policies or disrupt courier_locations." } ], "skills": { @@ -1183,17 +1217,17 @@ { "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "judgeNotes": "Identified image-transform as the affected function and explicitly listed the recurring 503 gateway failures across the morning of 2026-04-28, covering all 8 failures from 07:00Z–12:00Z." + "judgeNotes": "Identified image-transform as the main affected function and described the recurring pattern of eight HTTP 503 gateway failures across the morning of 2026-04-28, while correctly distinguishing unrelated billing-webhook 503s." }, { "name": "attributed recurring 503s to gateway/platform layer, not function code", "passed": true, - "judgeNotes": "Attributes the 503s to the gateway/platform layer before function execution, grounded in the mismatch between API/gateway 503s and clean edge-function runtime 200 logs, and distinguishes them from avatar-upload's function-level 500." + "judgeNotes": "Attributes image-transform 503s to gateway/worker boot layer before handler, not application code, and grounds it in absent execution logs for 503s plus unchanged deployment/nearby successful invocations. It also distinguishes avatar-upload’s logged 500 as a separate in-function error." }, { "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "The assistant recommended concrete next steps, including pulling detailed edge-function metrics/boot logs around specific 503 timestamps, checking for worker/resource limit errors, reducing invocation resource use, adding retries, and increasing compute/limits if capacity-related." + "judgeNotes": "The assistant recommended concrete next steps: checking Edge Function boot reasons/metrics for specific 503 timestamps, hardening deployment/dependency boot path, adding retries, and investigating the specific avatar-upload invocation error." } ], "skills": { @@ -1210,7 +1244,7 @@ }, "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "claude-code-opus-4.8/investigate-reliability-003-edge-function-5xx-correlation.json" }, { @@ -1264,7 +1298,7 @@ { "name": "diagnosed RLS and added owner-scoped policies", "passed": true, - "judgeNotes": "Diagnosed deny-all RLS due to no policies and added authenticated SELECT and INSERT owner-scoped policies using auth.uid(), without disabling RLS." + "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies causing deny-all Data API results, kept RLS enabled, and created authenticated-only SELECT and INSERT policies scoped to user_id = auth.uid(), with WITH CHECK for inserts." } ], "skills": { @@ -1328,7 +1362,7 @@ { "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "Avatar migration was applied through `supabase db push` (#14), which shows `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` The orphan bio migration history was reconciled by adding the missing local file `supabase/migrations/20240115000000_add_profile_bio.sql` (#12), after which `supabase migration list` showed local and remote aligned (#13) and the successful push proceeded. No disallowed workaround or direct mutation was used; psql commands were read-only inspection." + "judgeNotes": "Avatar migration was applied through `supabase db push` in #12, with output showing `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` History was reconciled by adding the missing local migration file `20240115000000_add_profile_bio.sql` in #10, after which `supabase migration list` in #11 showed local and remote aligned. No prohibited workaround observed; psql usage was read-only inspection." } ], "skills": { @@ -1385,7 +1419,7 @@ { "name": "query plan uses an index and avoids sequential scan", "passed": true, - "notes": "Limit (cost=55.55..55.61 rows=25 width=88)\n -> Sort (cost=55.55..55.61 rows=25 width=88)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=4.48..54.97 rows=25 width=88)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_desc_idx (cost=0.00..4.47 rows=25 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + "notes": "Limit (cost=55.55..55.61 rows=25 width=88)\n -> Sort (cost=55.55..55.61 rows=25 width=88)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=4.48..54.97 rows=25 width=88)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_idx (cost=0.00..4.47 rows=25 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" }, { "name": "inserts still work", @@ -1637,12 +1671,12 @@ { "name": "cron command enqueues to the 'tasks' queue", "passed": true, - "notes": "queue depth 1 -> 2" + "notes": "queue depth 0 -> 1" }, { "name": "process-tasks function drains the queue", "passed": true, - "notes": "function removed the seeded message (id 7) from the queue" + "notes": "function removed the seeded message (id 6) from the queue" } ], "skills": { @@ -1650,32 +1684,7 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"pg_cron schedule job send message to queue pgmq\", limit: 4) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - } - ], - "resultChars": 75189 - } - ] + "calls": [] }, "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", @@ -1733,7 +1742,7 @@ }, "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", - "attempts": 2, + "attempts": 1, "sourcePath": "claude-code-opus-4.8-no-skills/build-database-001-migrate-postgres-to-supabase.json" }, { @@ -1838,17 +1847,17 @@ { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"14f44167-7395-4098-9ee4-65835041dd08\",\"metric\":\"steps_a_mrnzcw4x\",\"value\":111}]" + "notes": "status 200: [{\"user_id\":\"b604e5f3-fa3f-4696-aaf1-20f4984d4b39\",\"metric\":\"steps_a_mrz23lfp\",\"value\":111}]" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: [{\"user_id\":\"14f44167-7395-4098-9ee4-65835041dd08\",\"metric\":\"steps_a_mrnzcw4x\",\"value\":111}]" + "notes": "status 200: [{\"user_id\":\"b604e5f3-fa3f-4696-aaf1-20f4984d4b39\",\"metric\":\"steps_a_mrz23lfp\",\"value\":111}]" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"1b50150f-6ab2-42bd-8008-f0c40d3404f9\",\"metric\":\"steps_b_mrnzcw4x\",\"value\":222}]" + "notes": "status 200: [{\"user_id\":\"5d64c93b-2226-4cc3-8d48-8a946ff8cd0d\",\"metric\":\"steps_b_mrz23lfp\",\"value\":222}]" }, { "name": "non-service key is not granted service access", @@ -1876,72 +1885,7 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"edge function dual authentication service role key user token verify jwt\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - } - ], - "resultChars": 65709 - }, - { - "source": "search_docs", - "query": "{ securing: searchDocs(query: \"Securing Edge Functions service role bypass RLS API key apikey header pattern\", limit: 3) { nodes { title href content } } server: searchDocs(query: \"@supabase/server createClient edge function new API keys secret publishable\", limit: 3) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", - "title": "Why is my service role key client getting RLS errors or not returning data?" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - } - ], - "resultChars": 46583 - } - ] + "calls": [] }, "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", @@ -1986,7 +1930,7 @@ { "name": "user A lists only own files", "passed": true, - "notes": "saw: 019f6c9d-4b62-7791-8378-3d0bd8ab5048/receipt-alpha.pdf, 019f6c9d-4b62-7791-8378-3d0bd8ab5048/receipt-beta.pdf" + "notes": "saw: 019f9488-4b60-76bd-a764-1e8d1a69adb5/receipt-alpha.pdf, 019f9488-4b60-76bd-a764-1e8d1a69adb5/receipt-beta.pdf" }, { "name": "user B cannot read user A files", @@ -2007,7 +1951,7 @@ { "name": "configured private per-user storage access", "passed": true, - "judgeNotes": "Meets all requirements: private user-files bucket, authenticated owner-scoped SELECT and INSERT policies with WITH CHECK, no RLS disabling or public access, and uses createSignedUrl with expiry for sharing." + "judgeNotes": "Meets all rubric requirements: private bucket, owner-scoped SELECT and INSERT RLS policies for authenticated users, RLS kept enabled, and supabase-js createSignedUrl with expiry for temporary sharing." } ], "skills": { @@ -2015,40 +1959,7 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"storage RLS policy private bucket user folder owner access control\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/security/product-security", - "title": "Secure configuration of Supabase products" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/ownership", - "title": "Ownership" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/design", - "title": "The Storage Schema" - } - ], - "resultChars": 22916 - } - ] + "calls": [] }, "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", @@ -2085,12 +1996,12 @@ { "name": "pgTAP isolation tests ran and pass", "passed": true, - "notes": "6 passed, 2 failed" + "notes": "5 passed, 3 failed" }, { "name": "agent correctly identifies the posts isolation bug from test results", "passed": true, - "judgeNotes": "The agent correctly identifies `posts` as the table with the broken tenant isolation policy, explains that authenticated members can read posts from organizations they are not members of, and grounds the conclusion in the pgTAP failures. It also distinguishes `notes` as correctly isolated." + "judgeNotes": "The agent correctly identifies `posts` as the table with the tenant isolation flaw, explains that authenticated members can read posts from organizations they do not belong to, and grounds the conclusion in pgTAP failures. It also distinguishes `notes` as correctly isolated and treats the test results as authoritative." } ], "skills": { @@ -2199,12 +2110,12 @@ { "name": "configured the Supabase Metrics API scrape correctly", "passed": true, - "judgeNotes": "Meets requirements: preserves app scrape, adds HTTPS Supabase metrics scrape with correct path, Basic Auth using password_file, project target on supabase.co, and docker-compose mounts the secrets directory containing the password file." + "judgeNotes": "Prometheus preserves the app job and adds a Supabase scrape using HTTPS, the required metrics path, basic_auth with password_file, and a supabase.co target. docker-compose mounts the secrets directory containing the password_file." }, { "name": "documented live deployment and verification steps", "passed": true, - "judgeNotes": "README includes concrete steps to replace project ref, create a Supabase Secret API key, place it in the mounted secrets/supabase_metrics_key file, reload/start the Compose stack, and verify via curl plus Prometheus targets." + "judgeNotes": "README includes concrete go-live steps: project ref replacement, Secret API key creation, secret file placement, Compose start/reload, and verification via curl, Prometheus targets, and Grafana dashboard." } ], "skills": { @@ -2215,17 +2126,17 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"Prometheus metrics endpoint scrape project monitoring\", limit: 6) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"Prometheus metrics endpoint project observability scrape\", limit: 6) { nodes { title href content } } }", "hasContent": true, "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, { "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", "title": "Metrics API with Prometheus & Grafana (self-hosted)" }, + { + "url": "https://supabase.com/docs/guides/telemetry/metrics", + "title": "Metrics API" + }, { "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", "title": "Vendor-agnostic Metrics API setup" @@ -2233,9 +2144,13 @@ { "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", "title": "Metrics API with Grafana Cloud" + }, + { + "url": "https://supabase.com/docs/guides/platform/read-replicas", + "title": "Read Replicas" } ], - "resultChars": 23542 + "resultChars": 32656 } ] }, @@ -2368,7 +2283,7 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { "name": "victim session active before delete-account", @@ -2389,7 +2304,8 @@ }, { "name": "deleted user cannot sign back in", - "passed": true + "passed": false, + "notes": "deleted account can still sign in" }, { "name": "other users keep their sessions and access", @@ -2398,7 +2314,7 @@ { "name": "diagnosed and explained session revocation", "passed": true, - "judgeNotes": "Meets all rubric requirements: identifies soft-delete-only bug, implements real auth user deletion/session revocation, correctly explains stateless JWT residual window and aligns it with added RLS live-profile mitigation while caveating local validation, and accurately distinguishes publishable vs secret keys including RLS behavior and frontend/server placement." + "judgeNotes": "Diagnoses soft-delete-only flow, implements auth-layer revocation via ban plus session/refresh token deletion, adds RLS checks to close Data API stale-JWT access, explains JWT expiry caveat consistently, and correctly distinguishes publishable frontend/RLS-enforced vs secret backend/RLS-bypassing keys." } ], "skills": { @@ -2406,40 +2322,11 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"publishable key secret key API keys migration anon service_role RLS\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs", - "title": "Build a User Management App with RedwoodJS" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/quickstarts/with-expo-react-native-social-auth", - "title": "Build a Social Auth App with Expo React Native" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-sveltekit", - "title": "Build a User Management App with SvelteKit" - } - ], - "resultChars": 169611 - } - ] + "calls": [] }, "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "claude-code-opus-4.8-no-skills/investigate-auth-001-deleted-user-access.json" }, { @@ -2488,7 +2375,7 @@ { "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "The assistant correctly identifies that the channel reaches SUBSCRIBED but INSERT events do not arrive because public.orders is missing from the supabase_realtime publication. It applies exactly the required fix via ALTER PUBLICATION supabase_realtime ADD TABLE public.orders, verifies the publication, and explicitly leaves RLS/policies and courier_locations intact without blaming or weakening them." + "judgeNotes": "The assistant correctly identifies that orders was missing from the supabase_realtime publication despite the channel reaching SUBSCRIBED, applies exactly ALTER PUBLICATION supabase_realtime ADD TABLE public.orders, verifies it, and does not weaken RLS/policies or disrupt courier_locations." } ], "skills": { @@ -2527,17 +2414,17 @@ { "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "judgeNotes": "Identified image-transform as affected and described the recurring HTTP 503 gateway failures throughout the morning of 2026-04-28, covering the 8 failures from ~07:00Z to 12:00Z, while ruling out billing-webhook noise." + "judgeNotes": "Identified image-transform as the affected function and described the recurring HTTP 503 pattern across the morning of 2026-04-28, covering most/all gateway failures and distinguishing unrelated billing-webhook errors." }, { "name": "attributed recurring 503s to gateway/platform layer, not function code", "passed": true, - "judgeNotes": "Attributes recurring image-transform 503s to the gateway/platform layer before the function, grounded in valid observations: 503s appear in gateway logs with no corresponding function execution 503s, executions that reached the function were 200s, and distinguishes the avatar-upload 500 as a separate function-level error." + "judgeNotes": "Attributes the 503s to the API/gateway/platform layer before function code runs, grounded in valid observations: gateway-only 503s with no edge-function execution logs, nearby successful invocations, unchanged deployment version, and distinction from avatar-upload's function-level 500." }, { "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "The assistant recommended concrete next steps including checking Edge Function resource limits/concurrency, correlating 503 timestamps with traffic spikes, and opening a Supabase support ticket referencing gateway 503s." + "judgeNotes": "The assistant recommended specific actionable next steps, including checking Edge Function CPU/memory/wall-clock limits, reviewing resource-heavy transform code, adding input guards/retries, and offloading heavy transforms." } ], "skills": { @@ -2603,7 +2490,7 @@ { "name": "diagnosed RLS and added owner-scoped policies", "passed": true, - "judgeNotes": "Diagnosed RLS enabled with no policies causing deny-all Data API results, kept RLS enabled, and created authenticated SELECT and INSERT policies scoped to user_id = auth.uid() with WITH CHECK for inserts." + "judgeNotes": "The answer correctly diagnoses RLS enabled with no policies as deny-all for Data API, keeps RLS enabled, and creates authenticated SELECT and INSERT policies scoped to user_id = auth.uid() using USING/WITH CHECK. It does not use permissive public/anon policies or disable RLS." } ], "skills": { @@ -2662,7 +2549,7 @@ { "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "Applied the pending avatar_url migration with `supabase db push` in step #18; output shows `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled the orphan bio migration by adding the local file `supabase/migrations/20240115000000_add_bio.sql` in step #16, after which migration list matched local/remote in step #17 and db push succeeded. No disallowed workaround observed." + "judgeNotes": "Applied pending avatar_url migration with `supabase db push` in #14; output shows `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled orphan bio migration by adding local file `supabase/migrations/20240115000000_add_profile_bio.sql` in #12, after which `supabase migration list` in #13 showed local and remote aligned. Only read-only psql inspection was used; no disallowed workaround seen." } ], "skills": { @@ -2839,7 +2726,7 @@ { "name": "todos table exists with at least 2 seeded rows", "passed": true, - "notes": "found 2 rows" + "notes": "found 3 rows" }, { "name": "row level security is enabled on todos", @@ -2857,7 +2744,7 @@ { "name": "REST API returns the todos to authenticated requests", "passed": true, - "notes": "2 rows" + "notes": "3 rows" } ], "skills": { @@ -2870,7 +2757,36 @@ ] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"row level security policy authenticated select only no insert update delete\", limit: 5) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", + "title": "Row Level Security" + }, + { + "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", + "title": "Column Level Security" + }, + { + "url": "https://supabase.com/docs/guides/api/securing-your-api", + "title": "Securing your API" + }, + { + "url": "https://supabase.com/docs/guides/resources/glossary", + "title": "Glossary" + }, + { + "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", + "title": "Advanced pgTAP Testing" + } + ], + "resultChars": 85626 + } + ] }, "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", @@ -2930,7 +2846,7 @@ }, "prompt": "Add a description text column to the `products` table in my local Supabase stack", "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "claude-code-sonnet-5/build-cli-002-declarative-schema.json" }, { @@ -2966,12 +2882,12 @@ { "name": "cron command enqueues to the 'tasks' queue", "passed": true, - "notes": "queue depth 1 -> 2" + "notes": "queue depth 0 -> 1" }, { "name": "process-tasks function drains the queue", "passed": true, - "notes": "function removed the seeded message (id 5) from the queue" + "notes": "function removed the seeded message (id 6) from the queue" } ], "skills": { @@ -2987,13 +2903,85 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"pg_cron schedule pgmq send queue example\", limit: 5) { nodes { title href content } } }", + "query": "{\n searchDocs(query: \"pg_cron schedule job cron.schedule extension\", limit: 5) {\n nodes {\n title\n href\n content\n }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/database/extensions/pg_net", + "title": "pg_net: Async Networking" + }, + { + "url": "https://supabase.com/docs/guides/troubleshooting/pgcron-debugging-guide-n1KTaz", + "title": "pg_cron debugging guide" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" + }, + { + "url": "https://supabase.com/docs/guides/cron/install", + "title": "Install" + } + ], + "resultChars": 56092 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"Queues pgmq send read pop archive delete\", limit: 5) {\n nodes {\n title\n href\n content\n }\n }\n}", "hasContent": true, "pages": [ { "url": "https://supabase.com/docs/guides/queues/pgmq", "title": "PGMQ Extension" }, + { + "url": "https://supabase.com/docs/guides/queues/quickstart", + "title": "Quickstart" + }, + { + "url": "https://supabase.com/docs/guides/queues", + "title": "Supabase Queues" + } + ], + "resultChars": 34511 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"Supabase Cron pg_cron schedule database function local development enable extension\", limit: 5) {\n nodes {\n title\n href\n content\n }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" + }, + { + "url": "https://supabase.com/docs/guides/cron/install", + "title": "Install" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/auth/auth-hooks/send-email-hook", + "title": "Send Email Hook" + }, + { + "url": "https://supabase.com/docs/guides/cron", + "title": "Cron" + } + ], + "resultChars": 78302 + }, + { + "source": "search_docs", + "query": "{\n a: searchDocs(query: \"cron.schedule syntax examples cron.unschedule pg_cron\", limit: 4) {\n nodes { title href content }\n }\n b: searchDocs(query: \"invoke edge function from database webhooks cron pg_net http_post local development\", limit: 4) {\n nodes { title href content }\n }\n}", + "hasContent": true, + "pages": [ { "url": "https://supabase.com/docs/guides/database/extensions/pg_net", "title": "pg_net: Async Networking" @@ -3003,15 +2991,47 @@ "title": "pg_cron debugging guide" }, { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" + "url": "https://supabase.com/docs/guides/cron/quickstart", + "title": "Quickstart" }, { "url": "https://supabase.com/docs/guides/cron", "title": "Cron" + }, + { + "url": "https://supabase.com/docs/guides/database/webhooks", + "title": "Database Webhooks" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + } + ], + "resultChars": 60485 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"Edge Functions environment variables SUPABASE_URL SUPABASE_SERVICE_ROLE_KEY automatically injected local\", limit: 4) {\n nodes { title href content }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/functions/secrets", + "title": "Environment Variables" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", + "title": "Self-Hosted Functions" + }, + { + "url": "https://supabase.com/docs/guides/functions/auth", + "title": "Securing Edge Functions" } ], - "resultChars": 46500 + "resultChars": 35932 } ] }, @@ -3117,7 +3137,7 @@ { "name": "reads only with the caller's JWT", "passed": true, - "notes": "bearer_tokens=2, all_match=true" + "notes": "bearer_tokens=1, all_match=true" }, { "name": "user A cannot force-read user B note", @@ -3135,9 +3155,7 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [] @@ -3181,22 +3199,22 @@ { "name": "rejects request with no credentials", "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + "notes": "status 401: {\"msg\":\"Error: Missing authorization header\"}" }, { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"6f3de9df-d36e-4fe3-afa6-585831ee3ab6\",\"metric\":\"steps_a_mrnza8oa\",\"value\":111}]}" + "notes": "status 200: {\"data\":[{\"user_id\":\"f37eefeb-3afa-4282-bfea-de9d5bd8f051\",\"metric\":\"steps_a_mrz1qlq6\",\"value\":111}]}" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"6f3de9df-d36e-4fe3-afa6-585831ee3ab6\",\"metric\":\"steps_a_mrnza8oa\",\"value\":111}]}" + "notes": "status 200: {\"data\":[{\"user_id\":\"f37eefeb-3afa-4282-bfea-de9d5bd8f051\",\"metric\":\"steps_a_mrz1qlq6\",\"value\":111}]}" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"547d02a8-3075-460e-b74b-3566ca328be3\",\"metric\":\"steps_b_mrnza8oa\",\"value\":222}]}" + "notes": "status 200: {\"data\":[{\"user_id\":\"d4fb0043-23ae-423b-99a7-940e9de5921a\",\"metric\":\"steps_b_mrz1qlq6\",\"value\":222}]}" }, { "name": "non-service key is not granted service access", @@ -3206,7 +3224,7 @@ { "name": "rejects an unverified (forged) user token", "passed": true, - "notes": "status 504: { \"message\":\"The upstream server is timing out\" }" + "notes": "status 401: {\"msg\":\"Invalid JWT\"}" }, { "name": "a user token in the apikey slot is not treated as the service key", @@ -3232,44 +3250,80 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"Edge Functions environment variables SUPABASE_URL SUPABASE_SERVICE_ROLE_KEY secret key default secrets\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"Edge Functions verify_jwt apikey header service_role secret key authorization\", limit: 10) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" + "url": "https://supabase.com/docs/guides/getting-started/api-keys", + "title": "Understanding API keys" }, { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" + }, + { + "url": "https://supabase.com/docs/guides/functions/auth", + "title": "Securing Edge Functions" }, { "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", "title": "Migrating to publishable and secret API keys" }, + { + "url": "https://supabase.com/docs/guides/functions/auth-headers", + "title": "Authorization headers" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", + "title": "Envoy API Gateway" + }, { "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", "title": "Integrating With Supabase Auth" }, { - "url": "https://supabase.com/docs/guides/functions/storage-caching", - "title": "Integrating with Supabase Storage" - } - ], - "resultChars": 36932 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"verify_jwt config.toml edge functions per function\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", + "title": "Self-Hosted Functions" + }, { "url": "https://supabase.com/docs/guides/functions/function-configuration", "title": "Function Configuration" }, { - "url": "https://supabase.com/docs/guides/functions/examples/mcp-server-mcp-lite", - "title": "Building an MCP Server with mcp-lite" + "url": "https://supabase.com/docs/guides/functions/error-codes", + "title": "Error codes" + } + ], + "resultChars": 120827 + }, + { + "source": "search_docs", + "query": "{ searchDocs(query: \"Edge Function verify JWT manually service role apikey header\", limit: 3) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", + "title": "New API Keys and Asymmetric Authentication" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", + "title": "Envoy API Gateway" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + } + ], + "resultChars": 53787 + }, + { + "source": "search_docs", + "query": "{ searchDocs(query: \"Edge Function disable verify_jwt config.toml per function\", limit: 3) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/functions/function-configuration", + "title": "Function Configuration" }, { "url": "https://supabase.com/docs/guides/functions/deploy", @@ -3278,47 +3332,69 @@ { "url": "https://supabase.com/docs/guides/functions/development-tips", "title": "Development tips" + } + ], + "resultChars": 15383 + }, + { + "source": "search_docs", + "query": "{ searchDocs(query: \"edge runtime policy per_worker main worker entrypoint local development\", limit: 3) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/functions/examples/mcp-server-mcp-lite", + "title": "Building an MCP Server with mcp-lite" }, { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" + "url": "https://supabase.com/docs/guides/functions/background-tasks", + "title": "Background Tasks" + }, + { + "url": "https://supabase.com/docs/guides/functions/websockets", + "title": "Handling WebSockets" } ], - "resultChars": 27882 + "resultChars": 21386 }, { "source": "search_docs", - "query": "{ searchDocs(query: \"Securing Edge Functions auth modes user secret combine multiple auth same function\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"SUPABASE_SECRET_KEYS SUPABASE_PUBLISHABLE_KEYS edge function environment variables new api keys @supabase/server\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" }, { "url": "https://supabase.com/docs/guides/functions/auth", "title": "Securing Edge Functions" }, { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" + "url": "https://supabase.com/docs/guides/functions/secrets", + "title": "Environment Variables" }, { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" + "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream", + "title": "Streaming Speech with ElevenLabs" }, { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", + "title": "Self-Hosted Functions" } ], - "resultChars": 33453 + "resultChars": 45031 + }, + { + "source": "web_search", + "query": "\"failed to determine entrypoint\" supabase edge-runtime worker boot error", + "pages": [], + "resultChars": 3487 } ] }, "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", - "attempts": 2, + "attempts": 1, "sourcePath": "claude-code-sonnet-5/build-functions-005-dual-auth-user-secret.json" }, { @@ -3359,7 +3435,7 @@ { "name": "user A lists only own files", "passed": true, - "notes": "saw: 019f6c9d-2621-71dc-bf54-b0321882d158/receipt-alpha.pdf, 019f6c9d-2621-71dc-bf54-b0321882d158/receipt-beta.pdf" + "notes": "saw: 019f9488-91b4-708f-a750-645153434b22/receipt-alpha.pdf, 019f9488-91b4-708f-a750-645153434b22/receipt-beta.pdf" }, { "name": "user B cannot read user A files", @@ -3380,7 +3456,7 @@ { "name": "configured private per-user storage access", "passed": true, - "judgeNotes": "Creates a private user-files bucket, owner-scoped SELECT and INSERT policies on storage.objects for authenticated users using the user-id path prefix, does not disable RLS or make the bucket public, and provides supabase-js createSignedUrl code with an expiry for temporary sharing." + "judgeNotes": "Meets rubric: private user-files bucket, owner-scoped SELECT and INSERT storage.objects policies for authenticated users using user-id folder prefix, no RLS disabling or public access, and supabase-js createSignedUrl with expiry for temporary sharing." } ], "skills": { @@ -3396,20 +3472,12 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"storage RLS policy folder path user id owner\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"storage RLS policy restrict access to a folder owner user id\", limit: 5) { nodes { ... on Guide { title href content } } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", - "title": "Storage Helper Functions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs", - "title": "Build a User Management App with Next.js" - }, - { - "url": "https://supabase.com/docs/guides/auth/third-party/clerk", - "title": "Clerk" + "url": "https://supabase.com/docs/guides/storage/security/access-control", + "title": "Storage Access Control" }, { "url": "https://supabase.com/docs/guides/storage/security/ownership", @@ -3418,13 +3486,21 @@ { "url": "https://supabase.com/docs/guides/resources/glossary", "title": "Glossary" + }, + { + "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", + "title": "Column Level Security" + }, + { + "url": "https://supabase.com/docs/guides/storage/debugging/error-codes", + "title": "Error Codes" } ], - "resultChars": 64123 + "resultChars": 35750 }, { "source": "search_docs", - "query": "{ searchDocs(query: \"createSignedUrl expiring link\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"createSignedUrl temporary share link expires storage\", limit: 5) { nodes { ... on ClientLibraryFunctionReference { title href content language methodName } ... on Guide { title href content } } } }", "hasContent": true, "pages": [ { @@ -3433,18 +3509,18 @@ { "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurls" }, - { - "url": "https://supabase.com/docs/reference/dart/file-buckets-createsignedurl" - }, { "url": "https://supabase.com/docs/guides/storage/serving/downloads", "title": "Serving assets from Storage" }, + { + "url": "https://supabase.com/docs/reference/dart/file-buckets-createsignedurl" + }, { "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsigneduploadurl" } ], - "resultChars": 7426 + "resultChars": 7891 } ] }, @@ -3478,17 +3554,17 @@ { "name": "pgTAP test file(s) written under supabase/tests/", "passed": true, - "notes": "4 file(s): supabase/tests/database/memberships_exposure.test.sql, supabase/tests/database/posts_tenant_isolation.test.sql, supabase/tests/database/00_rls_enabled.test.sql, supabase/tests/database/notes_tenant_isolation.test.sql" + "notes": "1 file(s): supabase/tests/database/tenant_isolation.sql" }, { "name": "pgTAP isolation tests ran and pass", "passed": true, - "notes": "2 passed, 1 failed" + "notes": "6 passed, 0 failed" }, { "name": "agent correctly identifies the posts isolation bug from test results", "passed": true, - "judgeNotes": "The agent correctly identifies `posts` as having the broken tenant isolation policy, explains that authenticated members can read posts from other organizations due to the missing `org_id` match, and grounds the conclusion in the pgTAP failures while noting `notes` passed." + "judgeNotes": "The agent correctly identifies `posts` as having the tenant isolation flaw: its SELECT policy allows any authenticated member to read posts from other orgs. They ground this in pgTAP test results, explicitly state `notes` was correctly isolated, and do not dismiss the tests or blame `notes`." } ], "skills": { @@ -3501,36 +3577,7 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"pgTAP testing RLS policies auth.uid() local development\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/testing/overview", - "title": "Testing Overview" - }, - { - "url": "https://supabase.com/docs/guides/database/testing", - "title": "Testing Your Database" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - } - ], - "resultChars": 75875 - } - ] + "calls": [] }, "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", @@ -3568,12 +3615,12 @@ { "name": "HNSW index on the embedding column", "passed": true, - "notes": "CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" + "notes": "CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" }, { "name": "index operator class matches the search operator", "passed": true, - "notes": "function operators: <=>\nindexes: CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" + "notes": "function operators: <#>\nindexes: CREATE INDEX document_sections_embedding_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" }, { "name": "user A search returns only own sections, best match first", @@ -3597,61 +3644,10 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"match_document_sections semantic search gte-small embedding\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/examples/semantic-search", - "title": "Semantic Search" - }, - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/storage/vector/querying-vectors", - "title": "Querying Vectors" - }, - { - "url": "https://supabase.com/docs/guides/ai/choosing-compute-addon", - "title": "Choosing your Compute Add-on" - }, - { - "url": "https://supabase.com/docs/guides/ai/concepts", - "title": "Concepts" - } - ], - "resultChars": 65363 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"gte-small model dimensions Supabase.ai Session embedding edge function\", limit: 3) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/semantic-search", - "title": "Semantic Search" - }, - { - "url": "https://supabase.com/docs/guides/functions/ai-models", - "title": "Running AI Models" - } - ], - "resultChars": 37366 - } - ] + "calls": [] }, "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", @@ -3685,12 +3681,12 @@ { "name": "configured the Supabase Metrics API scrape correctly", "passed": true, - "judgeNotes": "Supabase scrape uses HTTPS, correct metrics path, Basic Auth with password_file, preserves the app job, and docker-compose mounts the secrets directory containing the password_file." + "judgeNotes": "Meets requirements: app scrape preserved; Supabase scrape uses HTTPS, correct metrics path, project-ref target on supabase.co, HTTP Basic Auth with password_file; docker-compose mounts the secrets directory containing that password file." }, { "name": "documented live deployment and verification steps", "passed": true, - "judgeNotes": "README includes Secret API key creation, matching secret file placement, Prometheus reload, and concrete verification via Prometheus targets, PromQL, and Grafana." + "judgeNotes": "README includes correct hosted Supabase metrics endpoint/auth, steps to create a Secret API key, place it in the mounted password_file location, restart or reload the Compose stack, and verify via Prometheus targets/PromQL or direct curl." } ], "skills": { @@ -3705,60 +3701,15 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "{ searchDocs(query: \"project metrics endpoint prometheus\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/database/replication/manual-replication-monitoring", - "title": "Manual replication monitoring" - } - ], - "resultChars": 27147 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"metrics customer/v1/privileged/metrics\", limit: 5) { nodes { title href content } } }", + "source": "web_fetch", + "query": "Explain how to access Supabase project metrics in Prometheus format: what is the exact metrics endpoint URL (for both hosted projects and local CLI dev), what authentication is required, and what format the response is in. Include any details about self-hosted setups and scrape config examples.", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/reports", - "title": "Reports" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" + "url": "https://supabase.com/docs/guides/telemetry/metrics" } ], - "resultChars": 54724 + "resultChars": 2185 } ] }, @@ -3800,7 +3751,7 @@ { "name": "the weather function reads WEATHER_API_KEY from the environment", "passed": true, - "judgeNotes": "Reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." }, { "name": "WEATHER_API_KEY value is not committed to the repo", @@ -3817,7 +3768,36 @@ ] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"Edge Functions environment variables secrets set\", limit: 5) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/troubleshooting/inspecting-edge-function-environment-variables-wg5qOQ", + "title": "Inspecting edge function environment variables" + }, + { + "url": "https://supabase.com/docs/guides/functions/secrets", + "title": "Environment Variables" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + }, + { + "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech", + "title": "Transcription Telegram Bot" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", + "title": "Self-Hosted Functions" + } + ], + "resultChars": 41731 + } + ] }, "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", @@ -3876,30 +3856,58 @@ "docs": { "calls": [ { - "source": "web_fetch", - "query": "List the exact steps to set up self-hosted Supabase with Docker: which repo/files to clone or copy, the docker-compose.yml structure, which env vars need to be set in .env (list all of them with descriptions), and how JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, POSTGRES_PASSWORD, DASHBOARD credentials, SECRET_KEY_BASE, VAULT_ENC_KEY are generated/used.", + "source": "search_docs", + "query": "{ searchDocs(query: \"self-hosting with Docker\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/self-hosting/docker" - } - ], - "resultChars": 3336 - } - ] - }, - "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", - "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", - "attempts": 1, - "sourcePath": "claude-code-sonnet-5/deploy-self-hosting-001-docker-compose.json" - }, - { - "experiment": "claude-code-sonnet-5", - "experimentSuite": "benchmark", - "experimentDisplay": { - "agent": "claude-code", - "modelProvider": "anthropic", - "modelId": "claude-sonnet-5", + "url": "https://supabase.com/docs/guides/self-hosting", + "title": "Self-Hosting" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/docker", + "title": "Self-Hosting with Docker" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform", + "title": "Restore a Platform Project to Self-Hosted" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17", + "title": "Upgrade to Postgres 17" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https", + "title": "Configure Reverse Proxy and HTTPS" + } + ], + "resultChars": 70233 + }, + { + "source": "web_fetch", + "query": "Extract the full step by step instructions for self-hosting Supabase with Docker, including which files to download, the docker-compose structure, and how secrets/env vars (JWT_SECRET, POSTGRES_PASSWORD, ANON_KEY, SERVICE_ROLE_KEY, dashboard credentials, etc.) should be generated and set.", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/self-hosting/docker.md" + } + ], + "resultChars": 2820 + } + ] + }, + "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", + "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-sonnet-5/deploy-self-hosting-001-docker-compose.json" + }, + { + "experiment": "claude-code-sonnet-5", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-sonnet-5", "reasoningEffort": "high" }, "eval": "investigate-auth-001-deleted-user-access", @@ -3913,7 +3921,7 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { "name": "victim session active before delete-account", @@ -3934,7 +3942,8 @@ }, { "name": "deleted user cannot sign back in", - "passed": true + "passed": false, + "notes": "deleted account can still sign in" }, { "name": "other users keep their sessions and access", @@ -3943,7 +3952,7 @@ { "name": "diagnosed and explained session revocation", "passed": true, - "judgeNotes": "Meets all rubric requirements: correctly diagnoses soft-delete-only flow, implements hard auth user deletion/session revocation, accurately explains residual JWT access window consistent with the implemented fix and names mitigations, and correctly distinguishes publishable vs secret keys and RLS behavior." + "judgeNotes": "Diagnoses the soft-delete-only root cause, implements real session revocation and blocks future sign-in, adds RLS checks to close the data-access path for stale JWTs, explains the remaining stateless JWT/local-validation caveat consistently, and correctly distinguishes publishable/anon vs secret/service_role key behavior." } ], "skills": { @@ -3956,7 +3965,36 @@ ] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"publishable key secret key anon service_role migration difference\", limit: 5) { nodes { title href ... on Guide { content } } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + }, + { + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", + "title": "New API Keys and Asymmetric Authentication" + }, + { + "url": "https://supabase.com/docs/guides/integrations/supabase-for-platforms", + "title": "Supabase for Platforms" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs", + "title": "Build a User Management App with RedwoodJS" + } + ], + "resultChars": 107177 + } + ] }, "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", @@ -4009,7 +4047,7 @@ { "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "Diagnosed the missing orders table in supabase_realtime publication, applied ALTER PUBLICATION ADD TABLE public.orders, preserved courier_locations/RLS/policies, and did not blame or alter unrelated components." + "judgeNotes": "Identifies orders missing from supabase_realtime as root cause despite SUBSCRIBED status, applies ALTER PUBLICATION supabase_realtime ADD TABLE public.orders, preserves courier_locations and RLS/policies, and does not blame or weaken unrelated components." } ], "skills": { @@ -4053,17 +4091,17 @@ { "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "judgeNotes": "The assistant named image-transform as the affected function and described the recurring HTTP 503 pattern across the morning of 2026-04-28, covering the eight gateway failures from 07:00Z through 12:00Z. It also distinguished this from the older billing-webhook issue." + "judgeNotes": "Identified image-transform as the affected upstream function and described the recurring HTTP 503 pattern across the morning of 2026-04-28, including most/all 8 gateway failures from 07:00Z-12:00Z. Also correctly distinguished old billing-webhook 503s as unrelated." }, { "name": "attributed recurring 503s to gateway/platform layer, not function code", "passed": true, - "judgeNotes": "Attributes the recurring image-transform 503s to the API gateway/platform layer before function code ran, grounded in the observation that 503 entries appear only in gateway logs with no execution_time_ms/deployment_id/version while nearby 200s succeeded. Also distinguishes these gateway 503s from avatar-upload's function-level 500." + "judgeNotes": "The assistant clearly attributes the recurring image-transform 503s to the gateway/platform layer rather than function code, and grounds this in valid observations: the 503s appear only in gateway logs with no runtime fields/invocation evidence, while retries later succeed. It also distinguishes these gateway 503s from avatar-upload's function-level 500." }, { "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "Recommended concrete next steps including adding retry/backoff, reducing cold-start frequency, improving alerting by gateway vs runtime failures, and investigating slow initialization in the private package." + "judgeNotes": "The assistant recommended concrete next steps: add retry/backoff, investigate gateway-level rejections, check concurrency/rate limits and scheduled jobs, and add 5xx alerting." } ], "skills": { @@ -4080,7 +4118,7 @@ }, "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "claude-code-sonnet-5/investigate-reliability-003-edge-function-5xx-correlation.json" }, { @@ -4134,7 +4172,7 @@ { "name": "diagnosed RLS and added owner-scoped policies", "passed": true, - "judgeNotes": "Diagnosed RLS enabled with no policies causing deny-all Data API results; kept RLS enabled; created authenticated SELECT policy owner-scoped by user_id = auth.uid(); created authenticated INSERT policy with WITH CHECK enforcing user_id = auth.uid()." + "judgeNotes": "Diagnoses RLS enabled with no policies causing Data API empty results, keeps RLS enabled, and creates authenticated SELECT and INSERT policies scoped to user_id = auth.uid() with WITH CHECK for inserts." } ], "skills": { @@ -4198,7 +4236,7 @@ { "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "Applied avatar_url via `supabase db push` in #25, which showed `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled orphan bio migration by adding local file `supabase/migrations/20240115000000_add_profile_bio.sql` in #23, after which `supabase migration list` showed local and remote aligned in #24. Direct `psql` commands were read-only inspection; no prohibited workaround seen." + "judgeNotes": "Avatar migration applied through Supabase CLI with `supabase db push` in #10, showing `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` The orphan bio migration history was reconciled by adding the missing local migration file `20240115000000_add_profile_bio.sql` in #8, after which `supabase migration list` showed local and remote aligned in #9. Only read-only psql inspection was used; no prohibited workaround seen." } ], "skills": { @@ -4344,9 +4382,7 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [] @@ -4512,7 +4548,7 @@ { "name": "process-tasks function drains the queue", "passed": true, - "notes": "function removed the seeded message (id 36) from the queue" + "notes": "function removed the seeded message (id 40) from the queue" } ], "skills": { @@ -4520,7 +4556,116 @@ "loaded": [] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{\n a: searchDocs(query: \"schedule edge function invocation with pg_cron and pg_net\", limit: 5) {\n nodes { title href content }\n }\n b: searchDocs(query: \"cron job secure service_role key vault secret\", limit: 5) {\n nodes { title href content }\n }\n c: searchDocs(query: \"pgmq queue create send read delete edge function example\", limit: 5) {\n nodes { title href content }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/database/extensions/pg_net", + "title": "pg_net: Async Networking" + }, + { + "url": "https://supabase.com/docs/guides/cron/quickstart", + "title": "Quickstart" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" + }, + { + "url": "https://supabase.com/docs/guides/troubleshooting/pgcron-debugging-guide-n1KTaz", + "title": "pg_cron debugging guide" + }, + { + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", + "title": "Migrating to publishable and secret API keys" + }, + { + "url": "https://supabase.com/docs/guides/database/vault", + "title": "Vault" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/api-keys", + "title": "Understanding API keys" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin", + "title": "Build a Product Management Android App with Jetpack Compose" + }, + { + "url": "https://supabase.com/docs/guides/queues/pgmq", + "title": "PGMQ Extension" + }, + { + "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", + "title": "Consuming Supabase Queue Messages with Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/features", + "title": "Features" + }, + { + "url": "https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore", + "title": "Backup and Restore using the CLI" + }, + { + "url": "https://supabase.com/docs/guides/platform/manage-your-usage/egress", + "title": "Manage Egress usage" + } + ], + "resultChars": 263539 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"invoke edge function from pg_cron using pg_net vault secret authorization header\", limit: 3) {\n nodes { title href content }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" + }, + { + "url": "https://supabase.com/docs/guides/database/extensions/pg_net", + "title": "pg_net: Async Networking" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + } + ], + "resultChars": 47854 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"edge function cron trigger config.toml schedule declarative\", limit: 3) {\n nodes { title href content }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/database/extensions/pg_net", + "title": "pg_net: Async Networking" + }, + { + "url": "https://supabase.com/docs/guides/functions/schedule-functions", + "title": "Scheduling Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/cron/quickstart", + "title": "Quickstart" + } + ], + "resultChars": 25078 + } + ] }, "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", @@ -4683,17 +4828,17 @@ { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"470e6d3d-5da0-4456-ac30-a58f189b2226\",\"metric\":\"steps_a_mrnyzk12\",\"value\":111}]}" + "notes": "status 200: [{\"user_id\":\"52c2a24c-3a0a-4998-b3a0-c122dd533504\",\"metric\":\"steps_a_mrz1jrqf\",\"value\":111}]" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"470e6d3d-5da0-4456-ac30-a58f189b2226\",\"metric\":\"steps_a_mrnyzk12\",\"value\":111}]}" + "notes": "status 200: [{\"user_id\":\"52c2a24c-3a0a-4998-b3a0-c122dd533504\",\"metric\":\"steps_a_mrz1jrqf\",\"value\":111}]" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"d5cf03db-1898-4d2d-8721-801cafe8f82a\",\"metric\":\"steps_b_mrnyzk12\",\"value\":222}]}" + "notes": "status 200: [{\"user_id\":\"d8bf30d5-8080-47c8-b9d2-9c5219b42870\",\"metric\":\"steps_b_mrz1jrqf\",\"value\":222}]" }, { "name": "non-service key is not granted service access", @@ -4724,64 +4869,16 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"@supabase/server edge function new API keys secret key publishable key\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"Edge Functions verify_jwt apikey header service_role authorization JWT\", limit: 10) { nodes { title href content } } }", "hasContent": true, "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, { "url": "https://supabase.com/docs/guides/getting-started/api-keys", "title": "Understanding API keys" - } - ], - "resultChars": 55720 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"@supabase/server edge function sb-api-key header createClient\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-createclient" - }, - { - "url": "https://supabase.com/docs/guides/auth/passkeys", - "title": "Passkey authentication" }, { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-getclient" - }, - { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-updateclient" - }, - { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-deleteclient" - } - ], - "resultChars": 13437 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"edge functions authentication verify_jwt service_role apikey header user JWT\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" }, { "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", @@ -4792,8 +4889,8 @@ "title": "Authorization headers" }, { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", + "title": "Envoy API Gateway" }, { "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", @@ -4802,9 +4899,21 @@ { "url": "https://supabase.com/docs/guides/functions/auth", "title": "Securing Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/functions/error-codes", + "title": "Error codes" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", + "title": "Self-Hosted Functions" + }, + { + "url": "https://supabase.com/docs/guides/functions/status-codes", + "title": "Status codes" } ], - "resultChars": 75098 + "resultChars": 121746 } ] }, @@ -4851,7 +4960,7 @@ { "name": "user A lists only own files", "passed": true, - "notes": "saw: 019f6c9c-ff09-73dd-9a80-8ae97a305e9f/receipt-alpha.pdf, 019f6c9c-ff09-73dd-9a80-8ae97a305e9f/receipt-beta.pdf" + "notes": "saw: 019f9488-12a4-75c3-b318-d87f3704955f/receipt-alpha.pdf, 019f9488-12a4-75c3-b318-d87f3704955f/receipt-beta.pdf" }, { "name": "user B cannot read user A files", @@ -4872,7 +4981,7 @@ { "name": "configured private per-user storage access", "passed": true, - "judgeNotes": "Meets requirements: private user-files bucket, authenticated owner-scoped SELECT and INSERT policies with WITH CHECK, no RLS disabling or public access, and signed URL sharing via createSignedUrl with expiry." + "judgeNotes": "Creates private user-files bucket, owner-scoped SELECT and INSERT policies for authenticated users using foldername/auth.uid with WITH CHECK on insert, keeps RLS enabled, and provides supabase-js createSignedUrl with expiry for temporary sharing. No public bucket/getPublicUrl/service-role client usage." } ], "skills": { @@ -4912,17 +5021,17 @@ { "name": "pgTAP test file(s) written under supabase/tests/", "passed": true, - "notes": "1 file(s): supabase/tests/rls_tenant_isolation_test.sql" + "notes": "1 file(s): supabase/tests/tenant_isolation_test.sql" }, { "name": "pgTAP isolation tests ran and pass", "passed": true, - "notes": "9 passed, 3 failed" + "notes": "7 passed, 3 failed" }, { "name": "agent correctly identifies the posts isolation bug from test results", "passed": true, - "judgeNotes": "The agent correctly identifies posts as having the broken tenant isolation policy, specifically that members of any org can read posts from other orgs because the policy checks only membership existence and not org_id. It grounds this in the pgTAP failure (test 8) and treats the test results as authoritative. It also discusses memberships, but does not blame notes and explicitly says notes isolation passes." + "judgeNotes": "The agent correctly identifies `posts` as having a broken tenant isolation policy, specifically that authenticated members can read posts from orgs they are not members of, and grounds this in pgTAP results. It does not blame `notes`; it says `notes` passes. It also notes an additional `memberships` issue, which does not undermine the required conclusion." } ], "skills": { @@ -4997,21 +5106,50 @@ "loaded": [] }, "docs": { - "calls": [] - }, - "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", - "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", - "attempts": 1, - "sourcePath": "claude-code-sonnet-5-no-skills/build-vectors-001-rag-with-permissions.json" - }, - { - "experiment": "claude-code-sonnet-5-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "claude-code", - "modelProvider": "anthropic", - "modelId": "claude-sonnet-5", - "reasoningEffort": "high" + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"hybrid search pgvector match_documents function RLS embeddings edge function\", limit: 5) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/ai/hybrid-search", + "title": "Hybrid search" + }, + { + "url": "https://supabase.com/docs/guides/ai/vector-columns", + "title": "Vector columns" + }, + { + "url": "https://supabase.com/docs/guides/ai/semantic-search", + "title": "Semantic search" + }, + { + "url": "https://supabase.com/docs/guides/functions/examples/semantic-search", + "title": "Semantic Search" + }, + { + "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", + "title": "Automatic embeddings" + } + ], + "resultChars": 71630 + } + ] + }, + "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", + "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-sonnet-5-no-skills/build-vectors-001-rag-with-permissions.json" + }, + { + "experiment": "claude-code-sonnet-5-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-sonnet-5", + "reasoningEffort": "high" }, "eval": "deploy-database-001-prometheus-metrics", "stage": "deploy", @@ -5031,12 +5169,12 @@ { "name": "configured the Supabase Metrics API scrape correctly", "passed": true, - "judgeNotes": "Meets requirements: preserves app scrape, adds HTTPS Supabase metrics endpoint with correct path and project target, uses HTTP Basic Auth with password_file, and docker-compose mounts the secrets directory containing that password file." + "judgeNotes": "Supabase scrape uses HTTPS, correct metrics path, Basic Auth with password_file, target on supabase.co, preserves app job, and docker-compose mounts /run/secrets containing the password file." }, { "name": "documented live deployment and verification steps", "passed": true, - "judgeNotes": "README includes required live setup steps: create Secret API key, place it in the mounted secret file, replace project ref, and restart/reload Compose/Prometheus. It also provides concrete verification via Prometheus Targets, direct curl, and Grafana dashboard." + "judgeNotes": "README includes concrete live setup: create/copy a Supabase Secret API key, replace project ref, write it to observability/secrets/supabase_metrics_key matching password_file, and reload/restart the Compose stack. Verification is concrete via curl, Prometheus Status → Targets, and Grafana dashboard panels." } ], "skills": { @@ -5047,7 +5185,7 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"project metrics endpoint prometheus\", limit: 5) { nodes { title href content } } }", + "query": "{\n searchDocs(query: \"Prometheus metrics endpoint project observability\", limit: 5) {\n nodes {\n title\n href\n content\n }\n }\n}", "hasContent": true, "pages": [ { @@ -5067,11 +5205,31 @@ "title": "Metrics API with Grafana Cloud" }, { - "url": "https://supabase.com/docs/guides/database/replication/manual-replication-monitoring", - "title": "Manual replication monitoring" + "url": "https://supabase.com/docs/guides/platform/read-replicas", + "title": "Read Replicas" + } + ], + "resultChars": 32656 + }, + { + "source": "search_docs", + "query": "{\n searchDocs(query: \"supabase_project_ref supabase_identifier metric labels\", limit: 3) {\n nodes {\n title\n href\n content\n }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/telemetry/metrics", + "title": "Metrics API" + }, + { + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", + "title": "Metrics API with Prometheus & Grafana (self-hosted)" + }, + { + "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", + "title": "Vendor-agnostic Metrics API setup" } ], - "resultChars": 27147 + "resultChars": 17449 } ] }, @@ -5216,12 +5374,13 @@ }, { "name": "delete-account revokes the user's sessions", - "passed": true, - "notes": "sessions left: 0" + "passed": false, + "notes": "sessions left: 1" }, { "name": "deleted user's refresh token is rejected", - "passed": true + "passed": false, + "notes": "refresh token still produces a session" }, { "name": "deleted user cannot sign back in", @@ -5235,7 +5394,7 @@ { "name": "diagnosed and explained session revocation", "passed": true, - "judgeNotes": "The answer diagnoses the soft-delete-only bug, implements real session/refresh-token revocation and blocks future sign-in, explains JWT expiry and the remaining/local-validation window consistently with its RLS mitigation, and correctly states publishable keys are client-safe while secret/service-role keys are server-only and bypass RLS. There is one slightly contradictory closing phrase about RLS being the enforcement layer regardless of key, but the surrounding explanation clearly says secret keys bypass RLS." + "judgeNotes": "Diagnoses the soft-delete-only bug, updates auth state to block sign-in/refresh, adds RLS liveness checks to close the Data API stale-JWT window for covered tables, correctly caveats stateless JWT validity/local validation until expiry, and correctly distinguishes publishable vs secret keys/RLS behavior." } ], "skills": { @@ -5296,7 +5455,7 @@ { "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "Diagnosed the missing orders table in supabase_realtime publication, added public.orders to the existing publication, preserved courier_locations and RLS/policies, and did not blame or alter unrelated areas." + "judgeNotes": "Identified the missing orders table in the supabase_realtime publication as the root cause, added only public.orders to the existing publication, and preserved courier_locations, RLS, and policies." } ], "skills": { @@ -5335,17 +5494,17 @@ { "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "judgeNotes": "The assistant identified `image-transform` as the main affected function and described a recurring morning pattern of gateway HTTP 503s on 2026-04-28 with timestamps spread from 07:00Z to 12:00Z. Although it listed five rather than all eight failures, it recognized the correct function and recurring pattern, satisfying the pass criteria." + "judgeNotes": "The assistant clearly identified `image-transform` as the main affected function and described the recurring pattern of 8 HTTP 503 gateway failures across the morning of 2026-04-28 from 07:00Z to 12:00Z. It also correctly distinguished the older billing-webhook 503s as unrelated." }, { "name": "attributed recurring 503s to gateway/platform layer, not function code", "passed": true, - "judgeNotes": "Attributes recurring image-transform 503s to gateway/infrastructure layer, grounded in observation that 503s appear only in API gateway logs with no corresponding edge-function execution logs, while nearby requests succeeded. It also distinguishes avatar-upload's function-level 500 from the gateway 503s." + "judgeNotes": "Attributes image-transform 503s to gateway/dispatch/platform layer, grounded in missing invocation logs while reached invocations succeeded and contrasting with avatar-upload's function-level 500." }, { "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "The assistant recommended concrete next steps: checking bundle/init cost, adding warm-up pings and retries, investigating the separate function error, and adding monitoring/alerting. These are specific actionable steps beyond vague log checking." + "judgeNotes": "Recommended concrete next steps including checking Supabase status/opening a support ticket with timestamps, adding retries, investigating the separate function error, and considering redeploy if gateway/routing issues persist." } ], "skills": { @@ -5411,7 +5570,7 @@ { "name": "diagnosed RLS and added owner-scoped policies", "passed": true, - "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies as deny-all for the Data API, kept RLS enabled, and created authenticated SELECT and INSERT policies scoped to user_id = auth.uid(), using WITH CHECK for INSERT." + "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies causing Data API deny-all behavior, kept RLS enabled, and created authenticated SELECT and INSERT policies scoped to auth.uid() = user_id using USING and WITH CHECK respectively." } ], "skills": { @@ -5470,7 +5629,7 @@ { "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "Avatar migration was applied by `supabase db push` in #19, which shows `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` The orphan bio migration was reconciled by adding the matching local migration file `20240115000000_add_profile_bio.sql` in #17, after which `supabase migration list` in #18 showed local and remote aligned. Only read-only psql inspection was used; no direct SQL mutation or prepared-statement workaround was seen." + "judgeNotes": "Applied avatar_url via `supabase db push` (#16), which shows `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled the orphan bio migration by adding the missing local migration file `20240115000000_add_profile_bio.sql` (#14), after which `supabase migration list` showed local and remote aligned (#15), allowing the CLI push to proceed. Read-only psql inspection was used; no disallowed workaround or direct mutation observed." } ], "skills": { @@ -5679,907 +5838,181 @@ }, "docs": { "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"local development migrations RLS insert policies seed data Data API exposed table\", limit: 5) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#add-rls-policies", - "title": "Add RLS policies" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#grant-access-explicitly", - "title": "Grant access explicitly" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#use-a-dedicated-api-schema", - "title": "Use a dedicated API schema" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#default-privileges-for-new-tables-and-functions", - "title": "Default privileges for new tables and functions" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#accessing-request-information", - "title": "Accessing request information" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#enforce-additional-rules-on-each-request", - "title": "Enforce additional rules on each request" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#disable-the-data-api", - "title": "Disable the Data API" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-2-start-the-local-stack", - "title": "Step 2: Start the local stack" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-3-create-your-schema", - "title": "Step 3: Create your schema" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-4-add-seed-data", - "title": "Step 4: Add seed data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-5-verify", - "title": "Step 5: Verify" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-6-commit", - "title": "Step 6: Commit" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#the-daily-workflow", - "title": "The daily workflow" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#making-schema-changes", - "title": "Making schema changes" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#generating-types", - "title": "Generating types" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#staying-in-sync-with-your-team", - "title": "Staying in sync with your team" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#pushing-to-a-remote-project", - "title": "Pushing to a remote project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#resetting-a-remote-dev-or-staging-project", - "title": "Resetting a remote dev or staging project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#key-commands-at-a-glance", - "title": "Key commands at a glance" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#cleaning-up-generated-migrations", - "title": "Cleaning up generated migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#grants", - "title": "Grants" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#revokere-grant-patterns", - "title": "Revoke/re-grant patterns" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#extension-statements", - "title": "Extension statements" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#known-limitations-of-db-diff", - "title": "Known limitations of db diff" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#the-supabase-directory", - "title": "The ./supabase directory" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#move-an-existing-project-to-local-development", - "title": "Move an existing project to local development" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-1-initialize", - "title": "Step 1: Initialize" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-2-authenticate", - "title": "Step 2: Authenticate" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-3-link-to-your-remote-project", - "title": "Step 3: Link to your remote project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-4-pull-the-remote-schema", - "title": "Step 4: Pull the remote schema" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-5-create-seed-data", - "title": "Step 5: Create seed data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-6-verify", - "title": "Step 6: Verify" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-7-commit", - "title": "Step 7: Commit" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#start-a-new-project-from-scratch", - "title": "Start a new project from scratch" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-1-initialize-1", - "title": "Step 1: Initialize" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#more-information", - "title": "More information" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#frontend-access", - "title": "Frontend access" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#direct-database-connections", - "title": "Direct database connections" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#edge-functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#data-api", - "title": "Data API" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#connecting-your-app-securely", - "title": "Connecting your app securely" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres", - "title": "Connect to your database" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#direct-connection", - "title": "Direct connection" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#data-apis-and-client-libraries", - "title": "Data APIs and client libraries" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#quickstarts", - "title": "Quickstarts" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#how-to-connect-to-your-postgres-databases", - "title": "How to connect to your Postgres databases" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#what-is-the-max-pooler-clients-limit", - "title": "What is the max pooler clients limit?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#how-to-choose-the-right-connection-method", - "title": "How to choose the right connection method?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#does-connection-pooling-affect-latency", - "title": "Does connection pooling affect latency?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#why-do-connection-strings-have-different-ports", - "title": "Why do connection strings have different ports?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#why-are-there-active-connections-when-the-app-is-idle", - "title": "Why are there active connections when the app is idle?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#where-can-you-see-current-connection-usage", - "title": "Where can you see current connection usage?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#what-is-the-difference-between-client-connections-and-backend-connections", - "title": "What is the difference between client connections and backend connections?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#how-does-the-default-pool-size-work", - "title": "How does the default pool size work?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#can-you-use-supavisor-and-pgbouncer-together", - "title": "Can you use Supavisor and PgBouncer together?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#where-is-the-postgres-connection-string-in-supabase", - "title": "Where is the Postgres connection string in Supabase?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#how-do-you-connect-using-ipv4", - "title": "How do you connect using IPv4?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#what-is-the-fatal-password-authentication-failed-error", - "title": "What is the “FATAL: Password authentication failed” error?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#what-is-a-connection-refused-error", - "title": "What is a “connection refused” error?" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#troubleshooting-and-postgres-connection-string-faqs", - "title": "Troubleshooting and Postgres connection string FAQs" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#connecting-with-ssl", - "title": "Connecting with SSL" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#server-side-poolers", - "title": "Server-side poolers" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#application-side-poolers", - "title": "Application-side poolers" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#more-about-connection-pooling", - "title": "More about connection pooling" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#dedicated-pooler", - "title": "Dedicated pooler" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#pooler-transaction-mode", - "title": "Pooler transaction mode" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#pooler-session-mode", - "title": "Pooler session mode" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres#poolers", - "title": "Poolers" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling", - "title": "Storage Optimizations" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#egress", - "title": "Egress" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#optimizing-rls", - "title": "Optimizing RLS" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#optimize-listing-objects", - "title": "Optimize listing objects" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#smart-cdn", - "title": "Smart CDN" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#limit-the-upload-size", - "title": "Limit the upload size" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#set-a-high-cache-control-value", - "title": "Set a high cache-control value" - }, - { - "url": "https://supabase.com/docs/guides/storage/production/scaling#resize-images", - "title": "Resize images" - } - ], - "resultChars": 180379 - }, { "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] + "query": "site:supabase.com/changelog.md Supabase changelog markdown", + "pages": [] }, { "source": "web_search", - "query": "site:supabase.com changelog.md supabase", + "query": "site:supabase.com changelog.md Supabase breaking-change local development RLS Data API exposed table", "pages": [] }, { "source": "web_search", - "query": "\"https://supabase.com/changelog.md\"", + "query": "site:supabase.com/changelog.md Supabase changelog breaking-change local dev API RLS", "pages": [] - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase CLI init migration new seed local development\", limit: 10) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#the-supabase-directory", - "title": "The ./supabase directory" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#move-an-existing-project-to-local-development", - "title": "Move an existing project to local development" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-1-initialize", - "title": "Step 1: Initialize" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-2-authenticate", - "title": "Step 2: Authenticate" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-3-link-to-your-remote-project", - "title": "Step 3: Link to your remote project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-4-pull-the-remote-schema", - "title": "Step 4: Pull the remote schema" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-5-create-seed-data", - "title": "Step 5: Create seed data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-6-verify", - "title": "Step 6: Verify" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-7-commit", - "title": "Step 7: Commit" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#start-a-new-project-from-scratch", - "title": "Start a new project from scratch" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-1-initialize-1", - "title": "Step 1: Initialize" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-2-start-the-local-stack", - "title": "Step 2: Start the local stack" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-3-create-your-schema", - "title": "Step 3: Create your schema" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-4-add-seed-data", - "title": "Step 4: Add seed data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-5-verify", - "title": "Step 5: Verify" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#step-6-commit", - "title": "Step 6: Commit" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#the-daily-workflow", - "title": "The daily workflow" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#making-schema-changes", - "title": "Making schema changes" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#generating-types", - "title": "Generating types" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#staying-in-sync-with-your-team", - "title": "Staying in sync with your team" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#pushing-to-a-remote-project", - "title": "Pushing to a remote project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#resetting-a-remote-dev-or-staging-project", - "title": "Resetting a remote dev or staging project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#key-commands-at-a-glance", - "title": "Key commands at a glance" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#cleaning-up-generated-migrations", - "title": "Cleaning up generated migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#grants", - "title": "Grants" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#revokere-grant-patterns", - "title": "Revoke/re-grant patterns" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#extension-statements", - "title": "Extension statements" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#known-limitations-of-db-diff", - "title": "Known limitations of db diff" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#add-sample-data", - "title": "Add sample data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#deploy-your-project", - "title": "Deploy your project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#diffing-changes", - "title": "Diffing changes" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#log-in-to-the-supabase-cli", - "title": "Log in to the Supabase CLI" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#link-your-project", - "title": "Link your project" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#deploy-database-changes", - "title": "Deploy database changes" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#deploy-edge-functions", - "title": "Deploy Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#use-auth-locally", - "title": "Use Auth locally" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#sync-storage-buckets", - "title": "Sync storage buckets" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#sync-any-schema-with---schema", - "title": "Sync any schema with --schema" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#limitations-and-considerations", - "title": "Limitations and considerations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations#database-migrations", - "title": "Database migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/seeding-your-database", - "title": "Seeding your database" - }, - { - "url": "https://supabase.com/docs/guides/local-development/seeding-your-database#generating-seed-data", - "title": "Generating seed data" - }, - { - "url": "https://supabase.com/docs/guides/local-development/seeding-your-database#what-is-seed-data", - "title": "What is seed data?" - }, - { - "url": "https://supabase.com/docs/guides/local-development/seeding-your-database#using-seed-files", - "title": "Using seed files" - }, - { - "url": "https://supabase.com/docs/guides/local-development/seeding-your-database#splitting-up-your-seed-file", - "title": "Splitting up your seed file" - }, - { - "url": "https://supabase.com/docs/guides/cli", - "title": "Local Dev with CLI" - }, - { - "url": "https://supabase.com/docs/guides/cli#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech", - "title": "Transcription Telegram Bot" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#create-a-supabase-edge-function-to-handle-telegram-webhook-requests", - "title": "Create a Supabase Edge Function to handle Telegram webhook requests" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#set-up-the-environment-variables", - "title": "Set up the environment variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#dependencies", - "title": "Dependencies" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#code-the-telegram-bot", - "title": "Code the Telegram bot" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#deploy-to-supabase", - "title": "Deploy to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#apply-the-database-migrations", - "title": "Apply the database migrations" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#set-up-the-webhook", - "title": "Set up the webhook" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#set-the-function-secrets", - "title": "Set the function secrets" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#test-the-bot", - "title": "Test the bot" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#introduction", - "title": "Introduction" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#requirements", - "title": "Requirements" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#setup", - "title": "Setup" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#register-a-telegram-bot", - "title": "Register a Telegram bot" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#create-a-supabase-project-locally", - "title": "Create a Supabase project locally" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-transcribe-speech#create-a-database-table-to-log-the-transcription-results", - "title": "Create a database table to log the transcription results" - }, + } + ] + }, + "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", + "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/build-cli-001-bootstrap-app.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-cli-002-declarative-schema", + "stage": "build", + "product": [ + "database" + ], + "topic": [ + "declarative-schema", + "migrations" + ], + "suite": "benchmark", + "interface": "cli", + "passed": true, + "checks": [ + { + "name": "supabase db diff used to generate the migration", + "passed": true + }, + { + "name": "schema file updated to include description column", + "passed": true + }, + { + "name": "a new migration was generated for the change", + "passed": true + }, + { + "name": "description column exists in the live database", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "Add a description text column to the `products` table in my local Supabase stack", + "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini/build-cli-002-declarative-schema.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-cli-003-pg-cron-queue-workflow", + "stage": "build", + "product": [ + "database", + "edge-functions", + "cron", + "queues" + ], + "topic": [ + "sql", + "sdk" + ], + "suite": "benchmark", + "interface": "cli", + "passed": true, + "checks": [ + { + "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", + "passed": true, + "notes": "schedule='* * * * *', active=true" + }, + { + "name": "cron command enqueues to the 'tasks' queue", + "passed": true, + "notes": "queue depth 1 -> 2" + }, + { + "name": "process-tasks function drains the queue", + "passed": true, + "notes": "function removed the seeded message (id 5) from the queue" + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com changelog pg_cron queue edge function pgmq dequeue archive supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", + "pages": [ { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream", - "title": "Streaming Speech with ElevenLabs" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#configure-background-tasks-for-supabase-edge-functions", - "title": "Configure background tasks for Supabase Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#configure-the-storage-bucket", - "title": "Configure the storage bucket" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#create-a-supabase-project-locally", - "title": "Create a Supabase project locally" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#setup", - "title": "Setup" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#requirements", - "title": "Requirements" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#try-it-out", - "title": "Try it out" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#run-locally", - "title": "Run locally" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#set-the-function-secrets", - "title": "Set the function secrets" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#test-the-function", - "title": "Test the function" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#deploy-to-supabase", - "title": "Deploy to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#code-the-supabase-edge-function", - "title": "Code the Supabase Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#dependencies", - "title": "Dependencies" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#set-up-the-environment-variables", - "title": "Set up the environment variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#create-a-supabase-edge-function-for-speech-generation", - "title": "Create a Supabase Edge Function for speech generation" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream#introduction", - "title": "Introduction" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration", - "title": "GitHub integration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#preparing-your-git-repository", - "title": "Preparing your Git repository" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#set-the-working-directory", - "title": "Set the working directory" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#installation", - "title": "Installation" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#email-notifications", - "title": "Email notifications" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#preventing-migration-failures", - "title": "Preventing migration failures" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#deploying-changes-to-production", - "title": "Deploying changes to production" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#seeding", - "title": "Seeding" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#migrations", - "title": "Migrations" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#configuration", - "title": "Configuration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/github-integration#syncing-github-branches", - "title": "Syncing GitHub branches" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started", - "title": "Supabase CLI" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#installing-the-supabase-cli", - "title": "Installing the Supabase CLI" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#beta-channel", - "title": "Beta channel" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#learn-more", - "title": "Learn more" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#how-to-opt-out", - "title": "How to opt out" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#telemetry", - "title": "Telemetry" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#stopping-local-services", - "title": "Stopping local services" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#access-your-projects-services", - "title": "Access your project's services" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#running-supabase-locally", - "title": "Running Supabase locally" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/getting-started#updating-the-supabase-cli", - "title": "Updating the Supabase CLI" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments", - "title": "Managing Environments" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#set-up-a-local-environment", - "title": "Set up a local environment" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#release-to-production", - "title": "Release to production" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#open-a-pr-with-new-migration", - "title": "Open a PR with new migration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#configure-github-actions", - "title": "Configure GitHub Actions" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#rebasing-new-migrations", - "title": "Rebasing new migrations" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#permission-denied-on-db-push", - "title": "Permission denied on db push" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#permission-denied-on-db-pull", - "title": "Permission denied on db pull" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#sync-production-project-to-staging", - "title": "Sync production project to staging" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#deploy-a-migration", - "title": "Deploy a migration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#auto-schema-diff", - "title": "Auto schema diff" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#manual-migration", - "title": "Manual migration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/managing-environments#create-a-new-migration", - "title": "Create a new migration" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches", - "title": "Working with branches" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#managing-branch-environments", - "title": "Managing branch environments" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#subscribing-to-notifications", - "title": "Subscribing to notifications" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#migration-and-seeding-behavior", - "title": "Migration and seeding behavior" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#using-orm-or-custom-seed-scripts", - "title": "Using ORM or custom seed scripts" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#rolling-back-migrations", - "title": "Rolling back migrations" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#seeding-behavior", - "title": "Seeding behavior" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#developing-with-branches", - "title": "Developing with branches" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#local-development-workflow", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#remote-development-workflow", - "title": "Remote development workflow" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#switching-between-branches", - "title": "Switching between branches" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#accessing-branch-credentials", - "title": "Accessing branch credentials" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#branch-isolation", - "title": "Branch isolation" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/working-with-branches#next-steps", - "title": "Next steps" + "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions" } - ], - "resultChars": 396779 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs cron install pg_cron Supabase docs md", + "pages": [] + }, + { + "source": "web_search", + "query": "'cron.schedule' in https://supabase.com/docs/guides/database/extensions/pg_cron", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs queues consuming messages edge functions supabase docs", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs \"cron.schedule(\" Supabase docs", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs pg_cron schedule Supabase cron.schedule every minute example", + "pages": [] } ] }, - "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", - "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", + "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", + "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-cli-001-bootstrap-app.json" + "sourcePath": "codex-gpt-5.4-mini/build-cli-003-pg-cron-queue-workflow.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -6590,13 +6023,12 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-cli-002-declarative-schema", + "eval": "build-database-001-migrate-postgres-to-supabase", "stage": "build", "product": [ "database" ], "topic": [ - "declarative-schema", "migrations" ], "suite": "benchmark", @@ -6604,19 +6036,23 @@ "passed": true, "checks": [ { - "name": "supabase db diff used to generate the migration", + "name": "all 3 tables exist (teams, members, tasks)", "passed": true }, { - "name": "schema file updated to include description column", + "name": "row counts match (teams=5, members=10, tasks=13)", "passed": true }, { - "name": "a new migration was generated for the change", + "name": "foreign key constraints survived the restore", "passed": true }, { - "name": "description column exists in the live database", + "name": "tasks_team_status_idx index survived the restore", + "passed": true + }, + { + "name": "sequences synced (next insert won't conflict with existing IDs)", "passed": true } ], @@ -6630,41 +6066,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"declarative database schemas supabase local development alter table add column\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/local-development/declarative-database-schemas", - "title": "Declarative database schemas" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" - }, - { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/guides/database/tables", - "title": "Tables and Data" - } - ], - "resultChars": 96255 - } - ] + "calls": [] }, - "prompt": "Add a description text column to the `products` table in my local Supabase stack", - "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini/build-cli-002-declarative-schema.json" + "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", + "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini/build-database-001-migrate-postgres-to-supabase.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -6675,36 +6082,46 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-cli-003-pg-cron-queue-workflow", + "eval": "build-functions-004-service-role-bypass", "stage": "build", "product": [ - "database", "edge-functions", - "cron", - "queues" + "auth", + "database" ], "topic": [ - "sql", + "rls", + "security", "sdk" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", + "name": "rejects missing auth", "passed": true, - "notes": "schedule='* * * * *', active=true" + "notes": "status=401" }, { - "name": "cron command enqueues to the 'tasks' queue", + "name": "user A reads own note", "passed": true, - "notes": "queue depth 0 -> 1" + "notes": "status=200" }, { - "name": "process-tasks function drains the queue", - "passed": true, - "notes": "function removed the seeded message (id 36) from the queue" + "name": "reads only with the caller's JWT", + "passed": false, + "notes": "bearer_tokens=1, all_match=false" + }, + { + "name": "user A cannot force-read user B note", + "passed": false, + "notes": "status=200" + }, + { + "name": "user B cannot force-read user A note", + "passed": false, + "notes": "status=200" } ], "skills": { @@ -6717,337 +6134,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"cron jobs queue edge function tasks schedule background worker\", limit: 5) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } ... on ClientLibraryFunctionReference { language methodName } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#invoke-an-edge-function-every-minute", - "title": "Invoke an Edge Function every minute" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#delete-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#enable-the-extension", - "title": "Enable the extension" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-signature", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_post", - "title": "http_post" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-signature", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#call-an-endpoint-every-minute-with-pg_cron", - "title": "Call an endpoint every minute with pg_cron" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#execute-pg_net-in-a-trigger", - "title": "Execute pg_net in a trigger" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#send-multiple-table-rows-in-one-request", - "title": "Send multiple table rows in one request" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#limitations", - "title": "Limitations" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#debugging-requests", - "title": "Debugging requests" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#analyzing-responses", - "title": "Analyzing responses" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_get", - "title": "http_get" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_delete", - "title": "http_delete" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-signature-1", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#inspecting-request-data", - "title": "Inspecting request data" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#inspecting-failed-requests", - "title": "Inspecting failed requests" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#configuration", - "title": "Configuration" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-current-settings", - "title": "Get current settings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#alter-settings", - "title": "Alter settings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#invoke-a-supabase-edge-function", - "title": "Invoke a Supabase Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/functions/background-tasks", - "title": "Background Tasks" - }, - { - "url": "https://supabase.com/docs/guides/functions/background-tasks#overview", - "title": "Overview" - }, - { - "url": "https://supabase.com/docs/guides/functions/background-tasks#handling-errors", - "title": "Handling errors" - }, - { - "url": "https://supabase.com/docs/guides/functions/background-tasks#testing-background-tasks-locally", - "title": "Testing background tasks locally" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#see-also", - "title": "See also" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#why-not-one-request-per-row", - "title": "Why not one request per row?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#is-10-seconds-a-good-interval-for-processing", - "title": "Is 10 seconds a good interval for processing?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#step-4-create-the-edge-function", - "title": "Step 4: Create the Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#how-do-visibility-timeouts-work", - "title": "How do visibility timeouts work?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#why-queue-requests-instead-of-processing-them-immediately", - "title": "Why queue requests instead of processing them immediately?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#why-not-generate-all-embeddings-in-a-single-edge-function-request", - "title": "Why not generate all embeddings in a single Edge Function request?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#step-3-create-queue-and-triggers", - "title": "Step 3: Create queue and triggers" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#step-2-create-utility-functions", - "title": "Step 2: Create utility functions" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#step-1-enable-extensions", - "title": "Step 1: Enable extensions" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#implementation", - "title": "Implementation" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#understanding-the-architecture", - "title": "Understanding the architecture" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#understanding-the-challenge", - "title": "Understanding the challenge" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#conclusion", - "title": "Conclusion" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#how-do-we-handle-retries", - "title": "How do we handle retries?" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#usage", - "title": "Usage" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#1-create-table-to-store-documents-with-embeddings", - "title": "1. Create table to store documents with embeddings" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#2-create-triggers-to-enqueue-embedding-jobs", - "title": "2. Create triggers to enqueue embedding jobs" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#optional-clearing-embeddings-on-update", - "title": "(Optional) Clearing embeddings on update" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings#3-insert-and-update-documents", - "title": "3. Insert and update documents" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#inspecting-job-runs", - "title": "Inspecting job runs" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#caution-scheduling-system-maintenance", - "title": "Caution: Scheduling system maintenance" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#invoke-supabase-edge-function-every-30-seconds", - "title": "Invoke Supabase Edge Function every 30 seconds" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#call-a-database-stored-procedure", - "title": "Call a database stored procedure" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#call-a-database-function-every-5-minutes", - "title": "Call a database function every 5 minutes" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#run-a-vacuum-every-day", - "title": "Run a vacuum every day" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#delete-data-every-week", - "title": "Delete data every week" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#unschedule-a-job", - "title": "Unschedule a job" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#activatedeactivate-a-job", - "title": "Activate/Deactivate a job" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#edit-a-job", - "title": "Edit a job" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart#schedule-a-job", - "title": "Schedule a job" - } - ], - "resultChars": 142727 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"pgmq create queue pop delete read Supabase queues create queue\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues", - "title": "Expose Queues for local and self-hosted Supabase" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgmq", - "title": "pgmq: Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase", - "title": "Migrating to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes", - "title": "Subscribing to Database Changes" - } - ], - "resultChars": 50907 - } - ] + "calls": [] }, - "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", - "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-cli-003-pg-cron-queue-workflow.json" + "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", + "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/build-functions-004-service-role-bypass.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7058,37 +6150,67 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-database-001-migrate-postgres-to-supabase", + "eval": "build-functions-005-dual-auth-user-secret", "stage": "build", "product": [ + "edge-functions", + "auth", "database" ], "topic": [ - "migrations" + "sdk", + "rls", + "security" ], "suite": "benchmark", "interface": "cli", - "passed": true, + "cliVersion": "2.109.1", + "passed": false, "checks": [ { - "name": "all 3 tables exist (teams, members, tasks)", - "passed": true + "name": "seed rows present", + "passed": true, + "notes": "found 2/2 seeded rows" }, { - "name": "row counts match (teams=5, members=10, tasks=13)", - "passed": true + "name": "rejects request with no credentials", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "foreign key constraints survived the restore", - "passed": true + "name": "user with JWT reads only their own rows", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tasks_team_status_idx index survived the restore", - "passed": true + "name": "user cannot read another user's rows by passing user_id", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "sequences synced (next insert won't conflict with existing IDs)", - "passed": true + "name": "service key bypasses RLS to read the target user's rows", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" + }, + { + "name": "non-service key is not granted service access", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" + }, + { + "name": "rejects an unverified (forged) user token", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" + }, + { + "name": "a user token in the apikey slot is not treated as the service key", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" + }, + { + "name": "implementation uses @supabase/server", + "passed": true, + "notes": "imports @supabase/server / withSupabase" } ], "skills": { @@ -7101,62 +6223,191 @@ ] }, "docs": { - "calls": [] - }, - "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", - "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini/build-database-001-migrate-postgres-to-supabase.json" - }, - { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" - }, - "eval": "build-functions-004-service-role-bypass", - "stage": "build", - "product": [ - "edge-functions", - "auth", + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com changelog edge functions auth service_role apikey header access token user auth.getUser official docs", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com changelog.md Supabase Edge Functions apikey header auth getUser current docs", + "pages": [] + }, + { + "source": "web_search", + "query": "'auth: 'secret'' in https://supabase.com/docs/guides/functions/auth", + "pages": [] + }, + { + "source": "web_search", + "query": "'verify_jwt = false' in https://supabase.com/docs/guides/functions/auth", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs edge functions @supabase/server withSupabase auth user secret", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/functions/auth", + "pages": [ + { + "url": "https://supabase.com/docs/guides/functions/auth" + } + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/changelog.md supabase edge function withSupabase auth secret user @supabase/server", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com docs supabase functions entrypoint verify_jwt config.toml user-stats", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com @supabase/server withSupabase auth secret user docs", + "pages": [] + } + ] + }, + "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", + "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/build-functions-005-dual-auth-user-secret.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-storage-001-private-bucket-access", + "stage": "build", + "product": [ + "storage", "database" ], "topic": [ "rls", - "security", "sdk" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "rejects missing auth", - "passed": true, - "notes": "status=401" - }, - { - "name": "user A reads own note", - "passed": true, - "notes": "status=200" - }, + "name": "bucket user-files exists", + "passed": false, + "notes": "no row in storage.buckets with id or name 'user-files'" + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com/docs storage bucket RLS storage.objects policy path prefix auth.uid signed url createSignedUrl official docs", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/reference/javascript createSignedUrl v2 supabase-js official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage.foldername helper storage.objects official docs", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage/serving/downloads createSignedUrl signedUrl supabase-js v2 official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com changelog storage signed URL policy bucket RLS official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage.objects path prefix auth.uid folder policy official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/changelog.md storage signed url changelog", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage buckets sql insert into storage.buckets official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs createSignedUrl download option supabase js official", + "pages": [] + } + ] + }, + "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", + "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/build-storage-001-private-bucket-access.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-tests-001-rls-tenant-isolation", + "stage": "build", + "product": [ + "database" + ], + "topic": [ + "tests", + "rls" + ], + "suite": "benchmark", + "interface": "cli", + "passed": true, + "checks": [ { - "name": "reads only with the caller's JWT", + "name": "pgTAP test file(s) written under supabase/tests/", "passed": true, - "notes": "bearer_tokens=2, all_match=true" + "notes": "1 file(s): supabase/tests/tenant_isolation_test.sql" }, { - "name": "user A cannot force-read user B note", + "name": "pgTAP isolation tests ran and pass", "passed": true, - "notes": "status=403" + "notes": "4 passed, 0 failed" }, { - "name": "user B cannot force-read user A note", + "name": "agent correctly identifies the posts isolation bug from test results", "passed": true, - "notes": "status=403" + "judgeNotes": "The agent correctly identifies `posts` as the table with the tenant isolation flaw, specifically that its policy checked membership in any org rather than the row's `org_id`, allowing cross-org reads. It does not blame `notes` and uses pgTAP test results as validation after fixing the policy." } ], "skills": { @@ -7171,39 +6422,16 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "{ searchDocs(query: \"Supabase Edge Functions get user auth createClient Authorization header anon key\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs", - "title": "Build a User Management App with Next.js" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - } - ], - "resultChars": 84907 + "source": "web_search", + "query": "site:supabase.com/changelog.md breaking-change RLS policies Supabase CLI 2026", + "pages": [] } ] }, - "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", - "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", + "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", + "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-functions-004-service-role-bypass.json" + "sourcePath": "codex-gpt-5.4-mini/build-tests-001-rls-tenant-isolation.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7214,67 +6442,52 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-functions-005-dual-auth-user-secret", + "eval": "build-vectors-001-rag-with-permissions", "stage": "build", "product": [ - "edge-functions", - "auth", - "database" + "database", + "vectors" ], "topic": [ - "sdk", - "rls", - "security" + "sql", + "rls" ], "suite": "benchmark", - "interface": "cli", - "cliVersion": "2.109.1", + "interface": "mcp", "passed": false, "checks": [ { - "name": "seed rows present", - "passed": true, - "notes": "found 2/2 seeded rows" - }, - { - "name": "rejects request with no credentials", - "passed": true, - "notes": "status 503: {\"message\":\"name resolution failed\"}" - }, - { - "name": "user with JWT reads only their own rows", + "name": "document_sections.embedding is vector(384)", "passed": false, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "notes": "no embedding column" }, { - "name": "user cannot read another user's rows by passing user_id", + "name": "HNSW index on the embedding column", "passed": false, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "notes": "no index on embedding column" }, { - "name": "service key bypasses RLS to read the target user's rows", + "name": "index operator class matches the search operator", "passed": false, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "notes": "match_document_sections not found" }, { - "name": "non-service key is not granted service access", - "passed": true, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "name": "user A search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" }, { - "name": "rejects an unverified (forged) user token", - "passed": true, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "name": "user B search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" }, { - "name": "a user token in the apikey slot is not treated as the service key", - "passed": true, - "notes": "status 503: {\"message\":\"name resolution failed\"}" + "name": "user A reads only own sections through the API", + "passed": false }, { - "name": "implementation uses @supabase/server", - "passed": true, - "notes": "imports @supabase/server / withSupabase" + "name": "user A reads only own documents through the API", + "passed": false } ], "skills": { @@ -7287,137 +6500,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"Edge Functions authentication service_role getUser verify JWT supabase-js\", limit: 5) {\n nodes {\n __typename\n ... on Guide {\n title\n href\n content\n }\n ... on ClientLibraryFunctionReference {\n title\n href\n content\n language\n methodName\n }\n ... on TroubleshootingGuide {\n title\n href\n content\n }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs", - "title": "Build a User Management App with Next.js" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwts", - "title": "JSON Web Token (JWT)" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - } - ], - "resultChars": 84447 - }, - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"verify_jwt edge functions auth secret user combined modes withSupabase\", limit: 10) {\n nodes {\n __typename\n ... on Guide { title href content }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwts", - "title": "JSON Web Token (JWT)" - }, - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/auth-mfa", - "title": "Multi-Factor Authentication" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/functions/websockets", - "title": "Handling WebSockets" - } - ], - "resultChars": 120341 - }, - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"supabase functions config.toml entrypoint import_map local serve\", limit: 10) {\n nodes {\n __typename\n ... on Guide { title href content }\n ... on CLICommandReference { title href content }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-tips", - "title": "Development tips" - }, - { - "url": "https://supabase.com/docs/guides/functions/dependencies", - "title": "Managing dependencies" - }, - { - "url": "https://supabase.com/docs/guides/local-development/managing-config", - "title": "Managing config and secrets" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/elevenlabs-generate-speech-stream", - "title": "Streaming Speech with ElevenLabs" - }, - { - "url": "https://supabase.com/docs/guides/auth/third-party/clerk", - "title": "Clerk" - }, - { - "url": "https://supabase.com/docs/guides/deployment", - "title": "Deployment & Branching" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-functions-serve", - "title": "Serve all Functions locally" - } - ], - "resultChars": 79104 - } - ] + "calls": [] }, - "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", - "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", + "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini/build-functions-005-dual-auth-user-secret.json" + "sourcePath": "codex-gpt-5.4-mini/build-vectors-001-rag-with-permissions.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7428,57 +6516,30 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-storage-001-private-bucket-access", - "stage": "build", + "eval": "deploy-database-001-prometheus-metrics", + "stage": "deploy", "product": [ - "storage", "database" ], "topic": [ - "rls", - "sdk" + "observability" ], "suite": "benchmark", - "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "bucket user-files exists", - "passed": true - }, - { - "name": "bucket user-files is private", - "passed": true - }, - { - "name": "RLS still enabled on storage.objects", - "passed": true - }, - { - "name": "user A lists only own files", - "passed": true, - "notes": "saw: 019f6c9d-1652-7625-a693-586f70ac5f7c/receipt-alpha.pdf, 019f6c9d-1652-7625-a693-586f70ac5f7c/receipt-beta.pdf" - }, - { - "name": "user B cannot read user A files", - "passed": true - }, - { - "name": "anon reads no files", - "passed": true - }, - { - "name": "user A can upload into own folder", - "passed": true + "name": "preserved existing app scrape job", + "passed": false }, { - "name": "user B cannot upload into user A folder", - "passed": true + "name": "configured the Supabase Metrics API scrape correctly", + "passed": false, + "judgeNotes": "Fails: observability/prometheus.yml is empty, Supabase auth uses inline basic_auth password from an environment variable instead of password_file, and docker-compose.yml does not mount the password_file via a volume or Compose secret." }, { - "name": "configured private per-user storage access", - "passed": true, - "judgeNotes": "Configured a private user-files bucket, owner-scoped authenticated SELECT and INSERT policies with WITH CHECK on storage.objects while keeping RLS intact, and provided supabase-js createSignedUrl code with an expiry. No fail conditions present." + "name": "documented live deployment and verification steps", + "passed": false, + "judgeNotes": "README includes a concrete Prometheus targets verification step, but it does not require placing the Secret API key in a matching secret file and restarting/reloading the Compose stack. It uses exported environment variables instead, so the required secret setup is missing/mismatched." } ], "skills": { @@ -7493,93 +6554,45 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Storage policy restrict file uploads to authenticated user's folder path RLS bucket storage.objects signed upload signed URL\", limit: 5) { nodes { __typename ... on Guide { title href content } ... on TroubleshootingGuide { title href content } ... on ClientLibraryFunctionReference { title href language methodName content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/storage-error-403-forbidden-new-row-violates-row-level-security-policy-on-upload-a94384", - "title": "Storage error: 403 Forbidden: 'new row violates row-level security policy' on upload" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", - "title": "Storage Helper Functions" - } - ], - "resultChars": 23119 + "source": "web_search", + "query": "site:supabase.com/docs Supabase metrics Prometheus scrape project metrics endpoint", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"supabase-js storage from createSignedUrl signed url method syntax JavaScript\", limit: 5) { nodes { __typename ... on ClientLibraryFunctionReference { title href language methodName content } ... on Guide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurl" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurls" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads", - "title": "Resumable Uploads" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-saml-sso", - "title": "Configure SAML SSO" - } - ], - "resultChars": 47474 + "source": "web_search", + "query": "site:supabase.com/docs metrics Prometheus Supabase project observability metrics endpoint Basic Auth", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"storage.objects create policy authenticated foldername auth.uid select insert update delete\", limit: 5) { nodes { __typename ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href language methodName content } } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", "pages": [ { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/storage/management/copy-move-objects", - "title": "Copy Objects" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/custom-roles", - "title": "Custom Roles" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage/quickstart", - "title": "Storage Quickstart" + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted" } - ], - "resultChars": 29933 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs Supabase metrics Prometheus privileged metrics secret project ref", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs customer/v1/privileged/metrics Supabase Prometheus metrics secret", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com docs metrics prometheus customer/v1/privileged/metrics Supabase", + "pages": [] } ] }, - "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", - "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-storage-001-private-bucket-access.json" + "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", + "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/deploy-database-001-prometheus-metrics.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7590,33 +6603,35 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-tests-001-rls-tenant-isolation", - "stage": "build", + "eval": "deploy-functions-001-edge-function-secrets", + "stage": "deploy", "product": [ - "database" + "edge-functions" ], "topic": [ - "tests", - "rls" + "security" ], "suite": "benchmark", "interface": "cli", "passed": true, "checks": [ { - "name": "pgTAP test file(s) written under supabase/tests/", - "passed": true, - "notes": "1 file(s): supabase/tests/tenant_isolation_test.sql" + "name": "WEATHER_API_KEY is set as a Function secret on the project", + "passed": true }, { - "name": "pgTAP isolation tests ran and pass", + "name": "the weather function is deployed to the project", "passed": true, - "notes": "9 passed, 0 failed" + "notes": "status ACTIVE" }, { - "name": "agent correctly identifies the posts isolation bug from test results", + "name": "the weather function reads WEATHER_API_KEY from the environment", "passed": true, - "judgeNotes": "The agent correctly identifies `posts` as having a tenant isolation flaw, specifically exposing posts to authenticated members outside the post's org, and treats pgTAP testing as verification. It does mention other tables, including `memberships` and `notes`, but does not blame `notes` instead of `posts`." + "judgeNotes": "Reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + }, + { + "name": "WEATHER_API_KEY value is not committed to the repo", + "passed": true } ], "skills": { @@ -7631,39 +6646,26 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"row level security testing local auth.uid pgtap policies\", limit: 5) {\n nodes {\n title\n href\n content\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/testing/overview", - "title": "Testing Overview" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/database/testing", - "title": "Testing Your Database" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgtap", - "title": "pgTAP: Unit Testing" - } - ], - "resultChars": 56902 + "source": "web_search", + "query": "site:supabase.com/changelog.md Supabase changelog Edge Functions secrets env vars", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com docs edge functions invoke deployed function URL apikey Authorization bearer supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com docs edge functions invoke URL functions/v1 project ref apikey authorization", + "pages": [] } ] }, - "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", - "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", + "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", + "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-tests-001-rls-tenant-isolation.json" + "sourcePath": "codex-gpt-5.4-mini/deploy-functions-001-edge-function-secrets.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7674,49 +6676,34 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-vectors-001-rag-with-permissions", - "stage": "build", + "eval": "deploy-self-hosting-001-docker-compose", + "stage": "deploy", "product": [ "database", - "vectors" + "auth", + "storage" ], "topic": [ - "sql", - "rls" + "self-hosting" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "document_sections.embedding is vector(384)", - "passed": true, - "notes": "vector(384)" - }, - { - "name": "HNSW index on the embedding column", - "passed": true, - "notes": "CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops) WHERE (embedding IS NOT NULL)" - }, - { - "name": "index operator class matches the search operator", - "passed": true, - "notes": "function operators: <=>\nindexes: CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops) WHERE (embedding IS NOT NULL)" - }, - { - "name": "user A search returns only own sections, best match first", + "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", "passed": true }, { - "name": "user B search returns only own sections, best match first", + "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", "passed": true }, { - "name": "user A reads only own sections through the API", + "name": "secrets rotated off the shipped defaults", "passed": true }, { - "name": "user A reads only own documents through the API", + "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", "passed": true } ], @@ -7726,137 +6713,16 @@ "supabase-postgres-best-practices" ], "loaded": [ - "supabase", - "supabase-postgres-best-practices" + "supabase" ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"pgvector semantic search row level security documents owned by user\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/ai/hybrid-search", - "title": "Hybrid search" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - } - ], - "resultChars": 81105 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase vector index hnsw cosine ops syntax extensions.vector\", limit: 10) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-indexes/hnsw-indexes", - "title": "HNSW indexes" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM", - "title": "Increase vector lookup speeds by applying an HSNW index" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-indexes/ivf-indexes", - "title": "IVFFlat indexes" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-indexes", - "title": "Vector indexes" - }, - { - "url": "https://supabase.com/docs/guides/storage/vector/working-with-indexes", - "title": "Working with Vector Indexes" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-columns", - "title": "Vector columns" - }, - { - "url": "https://supabase.com/docs/guides/storage/vector/querying-vectors", - "title": "Querying Vectors" - }, - { - "url": "https://supabase.com/docs/guides/ai/going-to-prod", - "title": "Going to Production" - }, - { - "url": "https://supabase.com/docs/guides/ai/vecs-python-client", - "title": "Python client" - } - ], - "resultChars": 89986 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Project not specified edge functions endpoint supabase functions host project ref\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/log-drains", - "title": "Log Drains" - }, - { - "url": "https://supabase.com/docs/guides/storage/analytics/connecting-to-analytics-bucket", - "title": "Iceberg Catalog" - }, - { - "url": "https://supabase.com/docs/guides/functions/quickstart-dashboard", - "title": "Getting Started with Edge Functions (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/functions/architecture", - "title": "Edge Functions Architecture" - } - ], - "resultChars": 42668 - }, - { - "source": "web_search", - "query": "site:supabase.com \"Project not specified\" \"functions.supabase.co\" supabase", - "pages": [] - }, - { - "source": "web_search", - "query": "\"Project not specified\" \"Supabase\" \"functions\"", - "pages": [] - }, - { - "source": "web_search", - "query": "site:supabase.com/docs \"functions/v1\" \"Project not specified\"", - "pages": [] - } - ] + "calls": [] }, - "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", - "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", + "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/build-vectors-001-rag-with-permissions.json" + "sourcePath": "codex-gpt-5.4-mini/deploy-self-hosting-001-docker-compose.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7867,30 +6733,50 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "deploy-database-001-prometheus-metrics", - "stage": "deploy", + "eval": "investigate-auth-001-deleted-user-access", + "stage": "investigate", "product": [ - "database" + "auth" ], "topic": [ - "observability" + "security", + "sdk" ], "suite": "benchmark", + "interface": "mcp", "passed": false, "checks": [ { - "name": "preserved existing app scrape job", + "name": "victim session active before delete-account", "passed": true }, { - "name": "configured the Supabase Metrics API scrape correctly", + "name": "delete_account flow ran for the victim", + "passed": true + }, + { + "name": "delete-account revokes the user's sessions", "passed": false, - "judgeNotes": "Fails because the Supabase scrape uses basic_auth.password with an injected Secret API key instead of basic_auth.password_file, and docker-compose.yml does not mount that password_file via a volume or Compose secret. The app scrape is preserved and endpoint/HTTPS target are otherwise correct." + "notes": "sessions left: 1" }, { - "name": "documented live deployment and verification steps", + "name": "deleted user's refresh token is rejected", + "passed": false, + "notes": "refresh token still produces a session" + }, + { + "name": "deleted user cannot sign back in", + "passed": false, + "notes": "deleted account can still sign in" + }, + { + "name": "other users keep their sessions and access", + "passed": true + }, + { + "name": "diagnosed and explained session revocation", "passed": false, - "judgeNotes": "README mentions creating a Supabase Secret API key and setting env vars, but it does not instruct placing a matching secret file, lacks a concrete compose restart/reload command, and does not include concrete verification via Prometheus targets, PromQL, Grafana, or equivalent." + "judgeNotes": "The answer only states an intention to inspect and patch. It does not diagnose the soft-delete-only issue, does not provide a real revocation/auth-user deletion fix, does not explain JWT/access-token residual validity or mitigations, and does not clarify publishable vs secret key usage." } ], "skills": { @@ -7898,46 +6784,15 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"metrics prometheus supabase project metrics endpoint observability\", limit: 5) { nodes { __typename ... on Guide { title href content } ... on CLICommandReference { title href content } ... on ClientLibraryFunctionReference { title href content } ... on ManagementApiReference { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas", - "title": "Read Replicas" - } - ], - "resultChars": 32819 - } - ] + "calls": [] }, - "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", - "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", + "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini/deploy-database-001-prometheus-metrics.json" + "sourcePath": "codex-gpt-5.4-mini/investigate-auth-001-deleted-user-access.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -7948,35 +6803,96 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "deploy-functions-001-edge-function-secrets", - "stage": "deploy", + "eval": "investigate-realtime-001-subscribed-no-events", + "stage": "investigate", "product": [ - "edge-functions" + "realtime", + "database" ], "topic": [ - "security" + "sdk" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "WEATHER_API_KEY is set as a Function secret on the project", + "name": "orders table added to supabase_realtime publication", + "passed": false + }, + { + "name": "courier_locations still in supabase_realtime publication", "passed": true }, { - "name": "the weather function is deployed to the project", - "passed": true, - "notes": "status ACTIVE" + "name": "publication still publishes INSERT events", + "passed": true }, { - "name": "the weather function reads WEATHER_API_KEY from the environment", + "name": "RLS still enabled on orders", + "passed": true + }, + { + "name": "staff can still read orders through RLS", "passed": true, - "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + "notes": "authenticated sees 2 of 2 orders" }, { - "name": "WEATHER_API_KEY value is not committed to the repo", - "passed": true + "name": "diagnosed missing publication membership", + "passed": false, + "judgeNotes": "The assistant only states an intent to inspect and does not diagnose the missing orders table from the supabase_realtime publication or provide the required ALTER PUBLICATION fix." + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", + "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/investigate-realtime-001-subscribed-no-events.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "investigate-reliability-003-edge-function-5xx-correlation", + "stage": "investigate", + "product": [ + "edge-functions" + ], + "topic": [ + "observability" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "identified image-transform and the recurring 503 pattern", + "passed": false, + "judgeNotes": "The assistant did not identify image-transform, did not mention the 2026-04-28 morning recurring HTTP 503 pattern, and provided only process/status updates without the required conclusion." + }, + { + "name": "attributed recurring 503s to gateway/platform layer, not function code", + "passed": false, + "judgeNotes": "The assistant does not provide any layer attribution or conclusion about the 503s; it only describes searching for logs/tools. It neither attributes the recurring 503s to the gateway/platform layer nor grounds that attribution in observations from the data." + }, + { + "name": "recommended a concrete next step", + "passed": true, + "judgeNotes": "The assistant proposed concrete actions: querying/tracing project logs for a specific failure window, correlating upload errors with server-side messages, locating incident/log sources, and checking Supabase MCP tools." } ], "skills": { @@ -7989,41 +6905,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions secrets deploy WEATHER_API_KEY runtime environment variables\", limit: 5) { nodes { ... on Guide { title href content } ... on CLICommandReference { title href content } ... on ManagementApiReference { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/integrations/supabase-for-platforms", - "title": "Supabase for Platforms" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - } - ], - "resultChars": 51647 - } - ] + "calls": [] }, - "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", - "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/deploy-functions-001-edge-function-secrets.json" + "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", + "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/investigate-reliability-003-edge-function-5xx-correlation.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -8034,35 +6921,114 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "deploy-self-hosting-001-docker-compose", - "stage": "deploy", + "eval": "resolve-dataapi-001-empty-results", + "stage": "resolve", "product": [ + "data-api", "database", - "auth", - "storage" + "auth" ], "topic": [ - "self-hosting" + "rls", + "sdk" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "RLS still enabled on bookmarks", + "passed": true + }, + { + "name": "user A reads own bookmarks", + "passed": false + }, + { + "name": "user B cannot read user A bookmarks", + "passed": true + }, + { + "name": "anon reads no bookmarks", + "passed": true + }, + { + "name": "user A can save a new bookmark", + "passed": false, + "notes": "new row violates row-level security policy for table \"bookmarks\"" + }, + { + "name": "user B cannot insert a bookmark as user A", + "passed": true + }, + { + "name": "diagnosed RLS and added owner-scoped policies", + "passed": false, + "judgeNotes": "The assistant did not diagnose RLS deny-all as the cause or create authenticated owner-scoped SELECT and INSERT policies with user_id = auth.uid(). It only inspected files and made no fix." + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", + "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/resolve-dataapi-001-empty-results.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "resolve-database-001-migration-history-mismatch", + "stage": "resolve", + "product": [ + "database" + ], + "topic": [ + "migrations" ], "suite": "benchmark", "interface": "cli", "passed": true, "checks": [ { - "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "name": "the avatar_url column is applied on the hosted profiles table", "passed": true }, { - "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "name": "migration 20240220000000 is recorded in the remote history", "passed": true }, { - "name": "secrets rotated off the shipped defaults", + "name": "remote migration history matches local migration files", "passed": true }, { - "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "name": "local migrations are a valid reconciled sequence", + "passed": true + }, + { + "name": "production profile data is intact (not reset)", "passed": true + }, + { + "name": "the avatar migration and history reconciliation were done via the Supabase CLI", + "passed": true, + "judgeNotes": "PASS: avatar_url was applied through `supabase db push --db-url \"$(cat supabase/.temp/pooler-url)\" --yes` (#21), which output `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` The orphan bio migration was reconciled by adding the missing local migration file `supabase/migrations/20240115000000_add_profile_bio.sql` (#19), after which `supabase db push` no longer blocked. Read-only psql inspections were used; no disallowed direct SQL mutation or prepared-statement workaround was seen." } ], "skills": { @@ -8077,919 +7043,98 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"self-hosting docker compose Supabase official docker compose env secrets\", limit: 5) { nodes { title href ... on Guide { content subsections { nodes { title href content } } } ... on CLICommandReference { content } ... on TroubleshootingGuide { content } } totalCount } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com changelog.md Supabase breaking-change migration deploy", + "pages": [] + } + ] + }, + "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", + "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini/resolve-database-001-migration-history-mismatch.json" + }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "resolve-performance-001-slow-query-cpu-spike", + "stage": "resolve", + "product": [ + "database" + ], + "topic": [ + "observability", + "sql" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "inspected pg_stat_statements for query performance", + "passed": false + }, + { + "name": "ran EXPLAIN on the expensive query", + "passed": false + }, + { + "name": "created index covering user_id and created_at", + "passed": false + }, + { + "name": "query plan uses an index and avoids sequential scan", + "passed": false, + "notes": "Limit (cost=118.52..118.57 rows=20 width=88)\n -> Sort (cost=118.52..118.57 rows=20 width=88)\n Sort Key: created_at DESC\n -> Seq Scan on events (cost=0.00..118.09 rows=20 width=88)\n Filter: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + }, + { + "name": "inserts still work", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase", + "supabase-postgres-best-practices" + ] + }, + "docs": { + "calls": [ + { + "source": "web_search", + "query": "https://supabase.com/changelog.md", "pages": [ { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#how-it-works", - "title": "How it works" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#what-client-sdk-sends", - "title": "What client SDK sends" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#kong-api-gateway-routing", - "title": "Kong API gateway routing" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#request-flows", - "title": "Request flows" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#unauthenticated-requests-api-key-only-no-user-session-jwt", - "title": "Unauthenticated requests (API key only, no user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#authenticated-requests-user-session-jwt", - "title": "Authenticated requests (user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#adding-the-new-keys", - "title": "Adding the new keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#new-api-keys-format", - "title": "New API keys format" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#verifying-the-setup", - "title": "Verifying the setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#environment-variables-configuration", - "title": "Environment variables configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#differences-from-the-supabase-platform", - "title": "Differences from the Supabase platform" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#backward-compatibility", - "title": "Backward compatibility" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#rotating-the-new-api-keys", - "title": "Rotating the new API keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#regenerating-asymmetric-key-pair", - "title": "Regenerating asymmetric key pair" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker", - "title": "Self-Hosting with Docker" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-secrets", - "title": "Configuring secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-database-password", - "title": "Setting database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#changing-database-password", - "title": "Changing database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-services", - "title": "Configuring Supabase services" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-social-login-oauth-providers", - "title": "Configuring social login (OAuth) providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-phone-login-sms-and-mfa", - "title": "Configuring phone login, SMS, and MFA" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-an-email-server", - "title": "Configuring an email server" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-s3-storage", - "title": "Configuring S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#using-file-backend-in-storage-on-macos", - "title": "Using file backend in Storage on macOS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-ai-assistant", - "title": "Configuring Supabase AI Assistant" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres-through-supavisor", - "title": "Accessing Postgres through Supavisor" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#exposing-your-postgres-database", - "title": "Exposing your Postgres database" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-log_min_messages-in-postgres", - "title": "Setting log_min_messages in Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-your-secrets", - "title": "Managing your secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#demo", - "title": "Demo" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#contents", - "title": "Contents" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#system-requirements", - "title": "System requirements" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#installing-supabase", - "title": "Installing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#quick-start-linux", - "title": "Quick start (Linux)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#manual-installation", - "title": "Manual installation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-and-securing-supabase", - "title": "Configuring and securing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#generate-keys-and-secrets", - "title": "Generate keys and secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configure-supabase-urls", - "title": "Configure Supabase URLs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#where-to-find-your-credentials", - "title": "Where to find your credentials" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#studio-authentication", - "title": "Studio authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#starting-and-stopping", - "title": "Starting and stopping" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-supabase-studio-dashboard", - "title": "Accessing Supabase Studio (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres", - "title": "Accessing Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-edge-functions", - "title": "Accessing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-apis", - "title": "Accessing APIs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#enabling-analytics", - "title": "Enabling analytics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-https", - "title": "Configuring HTTPS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-the-stack", - "title": "Managing the stack" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#updating", - "title": "Updating" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#uninstalling", - "title": "Uninstalling" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#advanced-topics", - "title": "Advanced topics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting", - "title": "Self-Hosting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#get-started", - "title": "Get started" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#community-driven-projects", - "title": "Community-driven projects" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#about-self-hosting", - "title": "About self-hosting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#how-self-hosted-supabase-differs", - "title": "How self-hosted Supabase differs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#your-responsibilities-when-self-hosting", - "title": "Your responsibilities when self-hosting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#telemetry", - "title": "Telemetry" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#support-and-community", - "title": "Support and community" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting#enterprise-self-hosting", - "title": "Enterprise self-hosting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#test-with-the-aws-cli", - "title": "Test with the AWS CLI" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#how-to-configure-an-s3-backend", - "title": "How to configure an S3 backend" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#using-rustfs", - "title": "Using RustFS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#using-minio", - "title": "Using MinIO" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#using-aws-s3", - "title": "Using AWS S3" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#s3-compatible-providers", - "title": "S3-compatible providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#verify", - "title": "Verify" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#session-token", - "title": "Session token" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#signature-mismatch-errors", - "title": "Signature mismatch errors" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#enable-the-s3-protocol-endpoint", - "title": "Enable the S3 protocol endpoint" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#tus-upload-errors-on-cloudflare-r2", - "title": "TUS upload errors on Cloudflare R2" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#permission-denied-on-uploads", - "title": "Permission denied on uploads" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#upload-urls-point-to-localhost", - "title": "Upload URLs point to localhost" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3#test-with-rclone", - "title": "Test with rclone" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth", - "title": "Configure Social Login (OAuth) Providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#nonce-check-failure-on-mobile-google-sign-in", - "title": "Nonce check failure on mobile (Google Sign In)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#oauth-request-flow", - "title": "OAuth request flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-environment-variables", - "title": "Auth environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-by-step-configuration", - "title": "Step-by-step configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-1-register-your-app-with-the-provider", - "title": "Step 1: Register your app with the provider" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-2-configure-environment-variables", - "title": "Step 2: Configure environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-3-enable-the-matching-lines-in-docker-compose-configuration", - "title": "Step 3: Enable the matching lines in Docker Compose configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-4-restart-the-auth-service", - "title": "Step 4: Restart the auth service" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-5-verify-the-configuration", - "title": "Step 5: Verify the configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-specific-setup", - "title": "Provider-specific setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#other-supported-providers", - "title": "Other supported providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-service-fails-to-start", - "title": "Auth service fails to start" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#environment-variable-reference", - "title": "Environment variable reference" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#variables-added-to-the-environment-but-provider-still-not-working", - "title": "Variables added to the environment but provider still not working" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#test-the-login-flow", - "title": "Test the login flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#site-url-or-redirect-url-errors-after-login", - "title": "Site URL or redirect URL errors after login" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-not-enabled-or-provider-seen-as-false-in-settings" + "url": "https://supabase.com/changelog.md" } - ], - "resultChars": 341620 + ] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"Self-Hosting with Docker quick start Linux generate keys env example docker compose\", limit: 5) { nodes { title href ... on Guide { content subsections { nodes { title href content } } } } totalCount } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/self-hosting/docker", - "title": "Self-Hosting with Docker" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#enabling-analytics", - "title": "Enabling analytics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-an-email-server", - "title": "Configuring an email server" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-s3-storage", - "title": "Configuring S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#using-file-backend-in-storage-on-macos", - "title": "Using file backend in Storage on macOS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-edge-functions", - "title": "Accessing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-apis", - "title": "Accessing APIs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-phone-login-sms-and-mfa", - "title": "Configuring phone login, SMS, and MFA" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-https", - "title": "Configuring HTTPS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-the-stack", - "title": "Managing the stack" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#updating", - "title": "Updating" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#uninstalling", - "title": "Uninstalling" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#advanced-topics", - "title": "Advanced topics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-database-password", - "title": "Setting database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#changing-database-password", - "title": "Changing database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-secrets", - "title": "Configuring secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-services", - "title": "Configuring Supabase services" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-social-login-oauth-providers", - "title": "Configuring social login (OAuth) providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#demo", - "title": "Demo" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-your-secrets", - "title": "Managing your secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-log_min_messages-in-postgres", - "title": "Setting log_min_messages in Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#exposing-your-postgres-database", - "title": "Exposing your Postgres database" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres-through-supavisor", - "title": "Accessing Postgres through Supavisor" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-ai-assistant", - "title": "Configuring Supabase AI Assistant" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#contents", - "title": "Contents" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#system-requirements", - "title": "System requirements" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#installing-supabase", - "title": "Installing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#quick-start-linux", - "title": "Quick start (Linux)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#manual-installation", - "title": "Manual installation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-and-securing-supabase", - "title": "Configuring and securing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#generate-keys-and-secrets", - "title": "Generate keys and secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configure-supabase-urls", - "title": "Configure Supabase URLs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#where-to-find-your-credentials", - "title": "Where to find your credentials" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#studio-authentication", - "title": "Studio authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#starting-and-stopping", - "title": "Starting and stopping" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-supabase-studio-dashboard", - "title": "Accessing Supabase Studio (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres", - "title": "Accessing Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#verifying-the-setup", - "title": "Verifying the setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#what-client-sdk-sends", - "title": "What client SDK sends" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#how-it-works", - "title": "How it works" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#regenerating-asymmetric-key-pair", - "title": "Regenerating asymmetric key pair" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#rotating-the-new-api-keys", - "title": "Rotating the new API keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#backward-compatibility", - "title": "Backward compatibility" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#differences-from-the-supabase-platform", - "title": "Differences from the Supabase platform" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#environment-variables-configuration", - "title": "Environment variables configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#kong-api-gateway-routing", - "title": "Kong API gateway routing" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#new-api-keys-format", - "title": "New API keys format" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#adding-the-new-keys", - "title": "Adding the new keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#authenticated-requests-user-session-jwt", - "title": "Authenticated requests (user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#unauthenticated-requests-api-key-only-no-user-session-jwt", - "title": "Unauthenticated requests (API key only, no user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#request-flows", - "title": "Request flows" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth", - "title": "Configure Social Login (OAuth) Providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-4-restart-the-auth-service", - "title": "Step 4: Restart the auth service" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#environment-variable-reference", - "title": "Environment variable reference" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-service-fails-to-start", - "title": "Auth service fails to start" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#nonce-check-failure-on-mobile-google-sign-in", - "title": "Nonce check failure on mobile (Google Sign In)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#site-url-or-redirect-url-errors-after-login", - "title": "Site URL or redirect URL errors after login" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#variables-added-to-the-environment-but-provider-still-not-working", - "title": "Variables added to the environment but provider still not working" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#oauth-request-flow", - "title": "OAuth request flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-environment-variables", - "title": "Auth environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-by-step-configuration", - "title": "Step-by-step configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-1-register-your-app-with-the-provider", - "title": "Step 1: Register your app with the provider" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-2-configure-environment-variables", - "title": "Step 2: Configure environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-3-enable-the-matching-lines-in-docker-compose-configuration", - "title": "Step 3: Enable the matching lines in Docker Compose configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-5-verify-the-configuration", - "title": "Step 5: Verify the configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-specific-setup", - "title": "Provider-specific setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#other-supported-providers", - "title": "Other supported providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#test-the-login-flow", - "title": "Test the login flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#how-the-configuration-is-rendered-at-startup", - "title": "How the configuration is rendered at startup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#routes", - "title": "Routes" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#authentication", - "title": "Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#dashboard-basic-auth", - "title": "Dashboard basic auth" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#api-key-enforcement-on-protected-routes", - "title": "API key enforcement on protected routes" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#opaque-key-translation", - "title": "Opaque key translation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#forwarded-headers-and-cors", - "title": "Forwarded headers and CORS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#x-forwarded-headers", - "title": "X-Forwarded headers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#security-hardening", - "title": "Security hardening" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#cors", - "title": "CORS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#customizing-the-configuration", - "title": "Customizing the configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#admin-interface", - "title": "Admin interface" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#logs", - "title": "Logs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#common-issues", - "title": "Common issues" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#see-also", - "title": "See also" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#enabling-the-envoy-gateway", - "title": "Enabling the Envoy gateway" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#verify", - "title": "Verify" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#configuration-file-structure", - "title": "Configuration file structure" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#changes-to-function-code-not-reflected-after-editing", - "title": "Changes to function code not reflected after editing" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#copying-functions-from-supabase-platform", - "title": "Copying functions from Supabase platform" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#deploying-functions-to-a-remote-server", - "title": "Deploying functions to a remote server" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#managing-functions-via-dashboard", - "title": "Managing functions via dashboard" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#internal-vs-external-urls", - "title": "Internal vs external URLs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#500-error-on-invocation", - "title": "500 error on invocation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#calling-supabase-services-from-functions", - "title": "Calling Supabase services from functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#accessing-variables-in-functions", - "title": "Accessing variables in functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#using-inline-environment-variables", - "title": "Using inline environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#using-an-env-file-recommended", - "title": "Using an env file (recommended)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#custom-environment-variables", - "title": "Custom environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-3-invoke-your-function", - "title": "Step 3: Invoke your function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-2-restart-the-functions-service-to-pick-up-the-new-function", - "title": "Step 2: Restart the functions service to pick up the new function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-1-add-a-new-function-directory-and-the-function-code", - "title": "Step 1: Add a new function directory and the function code" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#create-a-new-function", - "title": "Create a new function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#invoke-the-default-function", - "title": "Invoke the default function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#memory-or-timeout-errors", - "title": "Memory or timeout errors" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#custom-env-vars-not-available-in-functions", - "title": "Custom env vars not available in functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-not-enabled-or-provider-seen-as-false-in-settings" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#401-invalid-jwt" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#400-missing-function-name-in-request" - } - ], - "resultChars": 323616 + "source": "web_search", + "query": "site:supabase.com/changelog.md Supabase changelog md", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com changelog.md Supabase breaking change row level security performance query optimization", + "pages": [] } ] }, - "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", - "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/deploy-self-hosting-001-docker-compose.json" + "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", + "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/resolve-performance-001-slow-query-cpu-spike.json" }, { "experiment": "codex-gpt-5.4-mini", @@ -9000,48 +7145,55 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "investigate-auth-001-deleted-user-access", - "stage": "investigate", + "eval": "resolve-security-002-rls-cross-tenant-leak", + "stage": "resolve", "product": [ + "database", "auth" ], "topic": [ - "security", - "sdk" + "rls", + "security" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "victim session active before delete-account", + "name": "RLS enabled on notes", "passed": true }, { - "name": "delete_account flow ran for the victim", + "name": "tenant A sees only org A notes", + "passed": false + }, + { + "name": "tenant B cannot read org A notes", + "passed": false + }, + { + "name": "tenant A author can update own note", "passed": true }, { - "name": "delete-account revokes the user's sessions", - "passed": true, - "notes": "sessions left: 0" + "name": "tenant B cannot update org A note", + "passed": true }, { - "name": "deleted user's refresh token is rejected", + "name": "tenant B author can delete own note", "passed": true }, { - "name": "deleted user cannot sign back in", + "name": "tenant B cannot delete org A note", "passed": true }, { - "name": "other users keep their sessions and access", + "name": "tenant A can insert note in own org", "passed": true }, { - "name": "diagnosed and explained session revocation", - "passed": true, - "judgeNotes": "Meets rubric: identifies soft-delete-only delete_account as cause, changes flow to delete auth.users and closes RLS data path for stale JWTs, explains JWT expiry caveat consistently, and correctly distinguishes publishable vs secret keys." + "name": "tenant B cannot insert into org A", + "passed": true } ], "skills": { @@ -9054,1181 +7206,206 @@ ] }, "docs": { - "calls": [ - { - "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] - }, - { - "source": "web_search", - "query": "site:supabase.com/changelog.md supabase changelog md", - "pages": [] - }, - { - "source": "web_search", - "query": "Supabase changelog breaking change auth delete user sessions publishable secret key", - "pages": [] - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"delete user revoke sessions auth.admin.deleteUser auth.sessions\", limit: 5) { nodes { __typename title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data", - "title": "User Management" - }, - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data#accessing-user-data-via-api", - "title": "Accessing user data via API" - }, - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data#adding-and-retrieving-user-metadata", - "title": "Adding and retrieving user metadata" - }, - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data#exporting-users", - "title": "Exporting users" - }, - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data#deleting-users", - "title": "Deleting users" - }, - { - "url": "https://supabase.com/docs/reference/javascript/auth-admin-deleteuser" - }, - { - "url": "https://supabase.com/docs/reference/dart/auth-admin-deleteuser" - }, - { - "url": "https://supabase.com/docs/reference/swift/auth-admin-deleteuser", - "title": "deleteUser()" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary", - "title": "Glossary" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#row-level-security-policies-rls", - "title": "Row level security policies (RLS)" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#access-token", - "title": "Access token" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#authentication", - "title": "Authentication" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#authenticator-app", - "title": "Authenticator app" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#authorization", - "title": "Authorization" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#identity-provider", - "title": "Identity provider" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#json-web-token-jwt", - "title": "JSON Web Token (JWT)" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#jwt-signing-secret", - "title": "JWT signing secret" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#multi-factor-authentication-mfa-or-2fa", - "title": "Multi-factor authentication (MFA or 2FA)" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#nonce", - "title": "Nonce" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#oauth", - "title": "OAuth" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#oidc", - "title": "OIDC" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#one-time-password-otp", - "title": "One-time password (OTP)" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#password-hashing-function", - "title": "Password hashing function" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#password-strength", - "title": "Password strength" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#pkce", - "title": "PKCE" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#provider-refresh-token", - "title": "Provider refresh token" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#provider-tokens", - "title": "Provider tokens" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#refresh-token", - "title": "Refresh token" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#refresh-token-flow", - "title": "Refresh token flow" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#replay-attack", - "title": "Replay attack" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#saml", - "title": "SAML" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#session", - "title": "Session" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#single-sign-on-sso", - "title": "Single-sign on (SSO)" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary#time-based-one-time-password-totp", - "title": "Time-based one-time password (TOTP)" - } - ], - "resultChars": 35929 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"migrating to new API keys publishable secret frontend RLS\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs", - "title": "Build a User Management App with RedwoodJS" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/getting-started", - "title": "Getting Started with OAuth 2.1 Server" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - } - ], - "resultChars": 109403 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"revoke sessions delete user auth.sessions sign out current user delete account\", limit: 10) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data", - "title": "User Management" - }, - { - "url": "https://supabase.com/docs/reference/javascript/auth-admin-signout" - }, - { - "url": "https://supabase.com/docs/reference/javascript/auth-signout" - }, - { - "url": "https://supabase.com/docs/guides/auth/signout", - "title": "Signing out" - }, - { - "url": "https://supabase.com/docs/guides/auth/sessions", - "title": "User sessions" - }, - { - "url": "https://supabase.com/docs/reference/dart/auth-signout" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary", - "title": "Glossary" - }, - { - "url": "https://supabase.com/docs/reference/swift/auth-signout", - "title": "signOut()" - }, - { - "url": "https://supabase.com/docs/guides/auth/auth-anonymous", - "title": "Anonymous Sign-Ins" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/oauth-flows", - "title": "OAuth 2.1 Flows" - } - ], - "resultChars": 84801 - }, - { - "source": "web_search", - "query": "site:supabase.com/docs deleting a user does not automatically sign out a user JWT remain valid until it has expired Supabase", - "pages": [] - } - ] + "calls": [] }, - "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", - "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/investigate-auth-001-deleted-user-access.json" + "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", + "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini/resolve-security-002-rls-cross-tenant-leak.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "investigate-realtime-001-subscribed-no-events", - "stage": "investigate", + "eval": "build-cli-001-bootstrap-app", + "stage": "build", "product": [ - "realtime", - "database" + "database", + "data-api" ], "topic": [ - "sdk" + "migrations", + "rls" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "orders table added to supabase_realtime publication", + "name": "supabase project initialised (supabase/config.toml exists)", "passed": true }, { - "name": "courier_locations still in supabase_realtime publication", + "name": "todos table is created by a migration file", "passed": true }, { - "name": "publication still publishes INSERT events", + "name": "todos table exists with at least 2 seeded rows", + "passed": true, + "notes": "found 2 rows" + }, + { + "name": "row level security is enabled on todos", "passed": true }, { - "name": "RLS still enabled on orders", + "name": "a SELECT policy targets the authenticated role", "passed": true }, { - "name": "staff can still read orders through RLS", + "name": "REST API returns no todos to anonymous requests", "passed": true, - "notes": "authenticated sees 2 of 2 orders" + "notes": "0 rows" }, { - "name": "diagnosed missing publication membership", + "name": "REST API returns the todos to authenticated requests", "passed": true, - "judgeNotes": "The assistant correctly identified that subscriptions could reach SUBSCRIBED while INSERT events for orders were silent because public.orders was missing from the supabase_realtime publication. It applied the narrow fix `alter publication supabase_realtime add table public.orders;`, verified both orders and courier_locations remained in the existing publication, and did not alter RLS/policies or recreate/drop the publication." + "notes": "2 rows" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"supabase_realtime publication postgres_changes table not receiving events\", limit: 5) {\n nodes {\n title\n href\n content\n ... on Guide {\n subsections {\n nodes {\n title\n href\n content\n }\n }\n }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes", - "title": "Subscribing to Database Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#streaming-inserts", - "title": "Streaming inserts" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#using-broadcast", - "title": "Using Broadcast" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#broadcast-authorization", - "title": "Broadcast authorization" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#create-a-trigger-function", - "title": "Create a trigger function" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#create-a-trigger", - "title": "Create a trigger" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#listening-on-client-side", - "title": "Listening on client side" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#using-postgres-changes", - "title": "Using Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#enable-postgres-changes", - "title": "Enable Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#streaming-updates", - "title": "Streaming updates" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes", - "title": "Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#less-than-lt", - "title": "Less than (lt)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#quick-start", - "title": "Quick start" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#usage", - "title": "Usage" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-specific-schemas", - "title": "Listening to specific schemas" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-insert-events", - "title": "Listening to INSERT events" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-update-events", - "title": "Listening to UPDATE events" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-delete-events", - "title": "Listening to DELETE events" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-specific-tables", - "title": "Listening to specific tables" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#listening-to-multiple-changes", - "title": "Listening to multiple changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#filtering-for-specific-changes", - "title": "Filtering for specific changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#available-filters", - "title": "Available filters" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#equal-to-eq", - "title": "Equal to (eq)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#not-equal-to-neq", - "title": "Not equal to (neq)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#less-than-or-equal-to-lte", - "title": "Less than or equal to (lte)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#greater-than-gt", - "title": "Greater than (gt)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#greater-than-or-equal-to-gte", - "title": "Greater than or equal to (gte)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#contained-in-list-in", - "title": "Contained in list (in)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#receiving-old-records", - "title": "Receiving old records" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#private-schemas", - "title": "Private schemas" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#custom-tokens", - "title": "Custom tokens" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#refreshed-tokens", - "title": "Refreshed tokens" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#limitations", - "title": "Limitations" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#delete-events-are-not-filterable", - "title": "Delete events are not filterable" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#spaces-in-table-names", - "title": "Spaces in table names" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes#database-instance-and-realtime-performance", - "title": "Database instance and realtime performance" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#methodology", - "title": "Methodology" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#workloads", - "title": "Workloads" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#results", - "title": "Results" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#broadcast-using-websockets", - "title": "Broadcast: Using WebSockets" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#broadcast-using-the-database", - "title": "Broadcast: Using the database" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#broadcast-impact-of-payload-size", - "title": "Broadcast: Impact of payload size" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#1kb-payload", - "title": "1KB payload" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#10kb-payload", - "title": "10KB payload" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#50kb-payload", - "title": "50KB payload" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#broadcast-scalability-scenarios", - "title": "Broadcast: Scalability scenarios" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#realtime-auth", - "title": "Realtime Auth" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks#postgres-changes", - "title": "Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol", - "title": "Realtime Protocol" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#presence-errors", - "title": "Presence errors" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#access-token-refresh", - "title": "Access token refresh" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#join-errors", - "title": "Join errors" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#websocket-connection-setup", - "title": "WebSocket connection setup" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#protocol-messages", - "title": "Protocol messages" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#100", - "title": "1.0.0" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#200", - "title": "2.0.0" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#text-frames", - "title": "Text frames" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#binary-frames", - "title": "Binary frames" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#user-broadcast-push", - "title": "User Broadcast Push" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#user-broadcast", - "title": "User Broadcast" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#event-types", - "title": "Event types" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#client-sent-events", - "title": "Client sent events" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#phx_join", - "title": "phx_join" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#phx_leave", - "title": "phx_leave" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#heartbeat", - "title": "heartbeat" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#access_token", - "title": "access_token" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#broadcast-text-frame", - "title": "broadcast (text frame)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#broadcast-binary-frame", - "title": "broadcast (binary frame)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#presence", - "title": "presence" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#server-sent-events", - "title": "Server sent events" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#phx_close", - "title": "phx_close" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#phx_error", - "title": "phx_error" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#phx_reply", - "title": "phx_reply" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#system", - "title": "system" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#broadcast-text-frame-1", - "title": "broadcast (text frame)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#broadcast-binary-frame-1", - "title": "broadcast (binary frame)" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#postgres_changes", - "title": "postgres_changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#presence_state", - "title": "presence_state" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#presence_diff", - "title": "presence_diff" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#reconnection", - "title": "Reconnection" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#channel-level-system-errors", - "title": "Channel-level system errors" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#error-handling", - "title": "Error handling" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#postgres-changes-subscription-errors", - "title": "Postgres Changes subscription errors" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol#broadcast-errors", - "title": "Broadcast errors" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started", - "title": "Getting Started with Realtime" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#next-steps", - "title": "Next steps" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#framework-examples", - "title": "Framework examples" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#integration-guides", - "title": "Integration guides" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#advanced-topics", - "title": "Advanced topics" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#security--configuration", - "title": "Security & configuration" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#core-features", - "title": "Core features" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-postgres-changes", - "title": "When to use Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-presence", - "title": "When to use Presence" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-broadcast", - "title": "When to use Broadcast" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#choose-the-right-feature", - "title": "Choose the right feature" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#clean-up-subscriptions", - "title": "Clean up subscriptions" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#follow-naming-conventions", - "title": "Follow naming conventions" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#use-private-channels", - "title": "Use private channels" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#essential-best-practices", - "title": "Essential best practices" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#53-using-database-triggers", - "title": "5.3 using database triggers" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#52-using-httprest-api", - "title": "5.2 using HTTP/REST API" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#51-using-client-libraries", - "title": "5.1 using client libraries" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#5-send-and-receive-messages", - "title": "5. Send and receive messages" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#4-set-up-authorization", - "title": "4. Set up authorization" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#3-create-your-first-channel", - "title": "3. Create your first Channel" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#get-api-details", - "title": "Get API details" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#2-initialize-the-client", - "title": "2. Initialize the client" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#1-install-the-client-library", - "title": "1. Install the client library" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#quick-start", - "title": "Quick start" - } - ], - "resultChars": 335915 - } - ] + "calls": [] }, - "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", - "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", + "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", + "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/investigate-realtime-001-subscribed-no-events.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-001-bootstrap-app.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "investigate-reliability-003-edge-function-5xx-correlation", - "stage": "investigate", + "eval": "build-cli-002-declarative-schema", + "stage": "build", "product": [ - "edge-functions" + "database" ], "topic": [ - "observability" + "declarative-schema", + "migrations" ], "suite": "benchmark", - "interface": "mcp", - "passed": true, + "interface": "cli", + "passed": false, "checks": [ { - "name": "identified image-transform and the recurring 503 pattern", - "passed": true, - "judgeNotes": "The assistant named `image-transform` as the affected function and explicitly identified the recurring 503 pattern across 2026-04-28 from 07:00Z to 12:00Z, listing all 8 gateway failures. It did not misattribute the issue to old billing-webhook errors." + "name": "supabase db diff used to generate the migration", + "passed": false }, { - "name": "attributed recurring 503s to gateway/platform layer, not function code", - "passed": true, - "judgeNotes": "Attributes recurring image-transform 503s to gateway/Functions-layer instability rather than function code, and grounds this in gateway 503 pattern, successful 200s between failures on same deployed version, and distinction from avatar-upload's isolated function-level 500." + "name": "schema file updated to include description column", + "passed": true }, { - "name": "recommended a concrete next step", - "passed": true, - "judgeNotes": "The assistant recommended concrete next steps, including checking for a platform incident/rollout during the affected time window, adding explicit function logging, adding retry/backoff, reviewing dependency changes, and collecting exact request IDs to distinguish gateway from handler failures." + "name": "a new migration was generated for the change", + "passed": false, + "notes": "found 1 migration file(s)" + }, + { + "name": "description column exists in the live database", + "passed": false } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [] }, - "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", - "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/investigate-reliability-003-edge-function-5xx-correlation.json" + "prompt": "Add a description text column to the `products` table in my local Supabase stack", + "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-002-declarative-schema.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "resolve-dataapi-001-empty-results", - "stage": "resolve", + "eval": "build-cli-003-pg-cron-queue-workflow", + "stage": "build", "product": [ - "data-api", "database", - "auth" + "edge-functions", + "cron", + "queues" ], "topic": [ - "rls", + "sql", "sdk" ], "suite": "benchmark", - "interface": "mcp", - "passed": true, + "interface": "cli", + "passed": false, "checks": [ { - "name": "RLS still enabled on bookmarks", - "passed": true + "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", + "passed": true, + "notes": "schedule='* * * * *', active=true" }, { - "name": "user A reads own bookmarks", - "passed": true - }, - { - "name": "user B cannot read user A bookmarks", - "passed": true - }, - { - "name": "anon reads no bookmarks", - "passed": true - }, - { - "name": "user A can save a new bookmark", - "passed": true - }, - { - "name": "user B cannot insert a bookmark as user A", - "passed": true + "name": "cron command enqueues to the 'tasks' queue", + "passed": true, + "notes": "queue depth 0 -> 1" }, { - "name": "diagnosed RLS and added owner-scoped policies", - "passed": true, - "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies causing deny-all/zero Data API rows, kept RLS enabled, and created authenticated SELECT and INSERT policies scoped to user_id = auth.uid(), with INSERT using WITH CHECK. Extra index and verification are acceptable." + "name": "process-tasks function drains the queue", + "passed": false, + "notes": "HTTP 404: Function not found" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Exposing a Table to the Data API RLS grants anon authenticated bookmarks\", limit: 5) { nodes { __typename title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#enforce-additional-rules-on-each-request", - "title": "Enforce additional rules on each request" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#add-rls-policies", - "title": "Add RLS policies" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#disable-the-data-api", - "title": "Disable the Data API" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#accessing-request-information", - "title": "Accessing request information" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#grant-access-explicitly", - "title": "Grant access explicitly" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#default-privileges-for-new-tables-and-functions", - "title": "Default privileges for new tables and functions" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api#use-a-dedicated-api-schema", - "title": "Use a dedicated API schema" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#more-information", - "title": "More information" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#frontend-access", - "title": "Frontend access" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#direct-database-connections", - "title": "Direct database connections" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#edge-functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#data-api", - "title": "Data API" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data#connecting-your-app-securely", - "title": "Connecting your app securely" - }, - { - "url": "https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0013_rls_disabled_in_public", - "title": "Database Advisor: Lint 0013_rls_disabled_in_public" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#authuid", - "title": "auth.uid()" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#authjwt", - "title": "auth.jwt()" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#mfa", - "title": "MFA" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#bypassing-row-level-security", - "title": "Bypassing Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#rls-performance-recommendations", - "title": "RLS performance recommendations" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#add-indexes", - "title": "Add indexes" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#benchmarks", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#call-functions-with-select", - "title": "Call functions with select" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#benchmarks-1", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#add-filters-to-every-query", - "title": "Add filters to every query" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#benchmarks-2", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#use-security-definer-functions", - "title": "Use security definer functions" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#minimize-joins", - "title": "Minimize joins" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#benchmarks-3", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#specify-roles-in-your-policies", - "title": "Specify roles in your policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#benchmarks-4", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#more-resources", - "title": "More resources" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#row-level-security-in-supabase", - "title": "Row Level Security in Supabase" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#policies", - "title": "Policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#enabling-row-level-security", - "title": "Enabling Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#auto-enable-rls-for-new-tables", - "title": "Auto-enable RLS for new tables" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#authenticated-and-unauthenticated-roles", - "title": "Authenticated and unauthenticated roles" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#creating-policies", - "title": "Creating policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#select-policies", - "title": "SELECT policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#insert-policies", - "title": "INSERT policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#update-policies", - "title": "UPDATE policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#delete-policies", - "title": "DELETE policies" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#views", - "title": "Views" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security#helper-functions", - "title": "Helper functions" - }, - { - "url": "https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api", - "title": "Database Advisor: Lint 0017_foreign_table_in_api" - } - ], - "resultChars": 203452 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"row level security insert select update delete policies auth.uid bookmarks\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", - "title": "Column Level Security" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary", - "title": "Glossary" - } - ], - "resultChars": 60041 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"auth.uid request.jwt.claims current_setting Supabase Postgres JWT claims\", limit: 5) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/realtime/authorization", - "title": "Realtime Authorization" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/rls-performance-and-best-practices-Z5Jjwv", - "title": "RLS Performance and Best Practices" - }, - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/auth/third-party/firebase-auth", - "title": "Firebase Auth" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes", - "title": "Postgres Changes" - } - ], - "resultChars": 103093 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"auth.uid implementation request.jwt.claim.sub current_setting\", limit: 3) { nodes { __typename title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/docs/guides/cron", "pages": [ { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/realtime/authorization", - "title": "Realtime Authorization" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/oauth-flows", - "title": "OAuth 2.1 Flows" + "url": "https://supabase.com/docs/guides/cron" } - ], - "resultChars": 61357 + ] } ] }, - "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", - "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/resolve-dataapi-001-empty-results.json" + "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", + "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-003-pg-cron-queue-workflow.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "resolve-database-001-migration-history-mismatch", - "stage": "resolve", + "eval": "build-database-001-migrate-postgres-to-supabase", + "stage": "build", "product": [ "database" ], @@ -10237,218 +7414,212 @@ ], "suite": "benchmark", "interface": "cli", - "passed": false, + "passed": true, "checks": [ { - "name": "the avatar_url column is applied on the hosted profiles table", - "passed": false, - "notes": "avatar_url not found on public.profiles" - }, - { - "name": "migration 20240220000000 is recorded in the remote history", + "name": "all 3 tables exist (teams, members, tasks)", "passed": true }, { - "name": "remote migration history matches local migration files", + "name": "row counts match (teams=5, members=10, tasks=13)", "passed": true }, { - "name": "local migrations are a valid reconciled sequence", + "name": "foreign key constraints survived the restore", "passed": true }, { - "name": "production profile data is intact (not reset)", + "name": "tasks_team_status_idx index survived the restore", "passed": true }, { - "name": "the avatar migration and history reconciliation were done via the Supabase CLI", - "passed": true, - "judgeNotes": "Applied via `supabase db push --db-url \"$DB_URL\"` (#22), which showed `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled the orphan bio migration by adding local `supabase/migrations/20240115000000_add_profile_bio.sql` (#20) and then using `supabase db push`/migration workflow; final `supabase migration list --db-url` showed local and remote histories aligned (#23). No disallowed direct SQL mutation or prepared-statement reset workaround seen." + "name": "sequences synced (next insert won't conflict with existing IDs)", + "passed": true } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [] }, - "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", - "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini/resolve-database-001-migration-history-mismatch.json" + "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", + "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-database-001-migrate-postgres-to-supabase.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "resolve-performance-001-slow-query-cpu-spike", - "stage": "resolve", + "eval": "build-functions-004-service-role-bypass", + "stage": "build", "product": [ - "database" + "edge-functions", + "auth", + "database" ], "topic": [ - "observability", - "sql" + "rls", + "security", + "sdk" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "inspected pg_stat_statements for query performance", - "passed": true + "name": "rejects missing auth", + "passed": true, + "notes": "status=401" }, { - "name": "ran EXPLAIN on the expensive query", - "passed": true + "name": "user A reads own note", + "passed": true, + "notes": "status=200" }, { - "name": "created index covering user_id and created_at", - "passed": true + "name": "reads only with the caller's JWT", + "passed": false, + "notes": "bearer_tokens=1, all_match=false" }, { - "name": "query plan uses an index and avoids sequential scan", - "passed": true, - "notes": "Limit (cost=55.55..55.61 rows=25 width=88)\n -> Sort (cost=55.55..55.61 rows=25 width=88)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=4.48..54.97 rows=25 width=88)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_desc_idx (cost=0.00..4.47 rows=25 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + "name": "user A cannot force-read user B note", + "passed": false, + "notes": "status=200" }, { - "name": "inserts still work", - "passed": true + "name": "user B cannot force-read user A note", + "passed": false, + "notes": "status=200" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [] }, - "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", - "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/resolve-performance-001-slow-query-cpu-spike.json" + "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", + "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-functions-004-service-role-bypass.json" }, { - "experiment": "codex-gpt-5.4-mini", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "resolve-security-002-rls-cross-tenant-leak", - "stage": "resolve", + "eval": "build-functions-005-dual-auth-user-secret", + "stage": "build", "product": [ - "database", - "auth" + "edge-functions", + "auth", + "database" ], "topic": [ + "sdk", "rls", "security" ], "suite": "benchmark", - "interface": "mcp", - "passed": true, + "interface": "cli", + "cliVersion": "2.109.1", + "passed": false, "checks": [ { - "name": "RLS enabled on notes", - "passed": true + "name": "seed rows present", + "passed": true, + "notes": "found 2/2 seeded rows" }, { - "name": "tenant A sees only org A notes", - "passed": true + "name": "rejects request with no credentials", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant B cannot read org A notes", - "passed": true + "name": "user with JWT reads only their own rows", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant A author can update own note", - "passed": true + "name": "user cannot read another user's rows by passing user_id", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant B cannot update org A note", - "passed": true + "name": "service key bypasses RLS to read the target user's rows", + "passed": false, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant B author can delete own note", - "passed": true + "name": "non-service key is not granted service access", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant B cannot delete org A note", - "passed": true + "name": "rejects an unverified (forged) user token", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant A can insert note in own org", - "passed": true + "name": "a user token in the apikey slot is not treated as the service key", + "passed": true, + "notes": "status 503: {\"message\":\"name resolution failed\"}" }, { - "name": "tenant B cannot insert into org A", - "passed": true + "name": "implementation uses @supabase/server", + "passed": false, + "notes": "hand-rolled (raw supabase-js or other) — this eval requires @supabase/server" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"row level security multi tenant notes workspace\", limit: 5) { nodes { __typename ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } ... on TroubleshootingGuide { title href content } ... on ManagementApiReference { title href content } } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com edge functions entrypoint index.ts config.toml entrypoint Supabase functions serve determine entrypoint", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs edge functions entrypoint index.ts deno.json Supabase CLI failed to determine entrypoint", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/local-development/cli/config", "pages": [ { - "url": "https://supabase.com/docs/guides/auth/enterprise-sso/auth-sso-saml", - "title": "Single Sign-On with SAML 2.0 for Projects" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/deployment/maturity-model", - "title": "Maturity Model" + "url": "https://supabase.com/docs/guides/local-development/cli/config" } - ], - "resultChars": 97790 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com docs edge functions entrypoint config.toml verify_jwt supabase functions user-stats", + "pages": [] } ] }, - "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", - "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini/resolve-security-002-rls-cross-tenant-leak.json" + "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", + "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-functions-005-dual-auth-user-secret.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10459,50 +7630,24 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-cli-001-bootstrap-app", + "eval": "build-storage-001-private-bucket-access", "stage": "build", "product": [ - "database", - "data-api" + "storage", + "database" ], "topic": [ - "migrations", - "rls" + "rls", + "sdk" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "supabase project initialised (supabase/config.toml exists)", - "passed": true - }, - { - "name": "todos table is created by a migration file", - "passed": true - }, - { - "name": "todos table exists with at least 2 seeded rows", - "passed": true, - "notes": "found 2 rows" - }, - { - "name": "row level security is enabled on todos", - "passed": true - }, - { - "name": "a SELECT policy targets the authenticated role", - "passed": true - }, - { - "name": "REST API returns no todos to anonymous requests", - "passed": true, - "notes": "0 rows" - }, - { - "name": "REST API returns the todos to authenticated requests", - "passed": true, - "notes": "2 rows" + "name": "bucket user-files exists", + "passed": false, + "notes": "no row in storage.buckets with id or name 'user-files'" } ], "skills": { @@ -10510,12 +7655,27 @@ "loaded": [] }, "docs": { - "calls": [] + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com/docs storage signed url createSignedUrl supabase-js official", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/storage/schema/design", + "pages": [ + { + "url": "https://supabase.com/docs/guides/storage/schema/design" + } + ] + } + ] }, - "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", - "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-001-bootstrap-app.json" + "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", + "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-storage-001-private-bucket-access.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10526,34 +7686,33 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-cli-002-declarative-schema", + "eval": "build-tests-001-rls-tenant-isolation", "stage": "build", "product": [ "database" ], "topic": [ - "declarative-schema", - "migrations" + "tests", + "rls" ], "suite": "benchmark", "interface": "cli", - "passed": false, + "passed": true, "checks": [ { - "name": "supabase db diff used to generate the migration", - "passed": false - }, - { - "name": "schema file updated to include description column", - "passed": true + "name": "pgTAP test file(s) written under supabase/tests/", + "passed": true, + "notes": "1 file(s): supabase/tests/tenant_isolation_test.sql" }, { - "name": "a new migration was generated for the change", - "passed": true + "name": "pgTAP isolation tests ran and pass", + "passed": true, + "notes": "4 passed, 0 failed" }, { - "name": "description column exists in the live database", - "passed": true + "name": "agent correctly identifies the posts isolation bug from test results", + "passed": true, + "judgeNotes": "The agent correctly identifies `posts` as the table with the tenant isolation flaw, states that authenticated members could read posts across orgs because the policy checked membership in any org rather than `posts.org_id`, and grounds this in the pgTAP failure/passing after fix. It does not blame `notes` or dismiss the tests." } ], "skills": { @@ -10563,10 +7722,10 @@ "docs": { "calls": [] }, - "prompt": "Add a description text column to the `products` table in my local Supabase stack", - "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-002-declarative-schema.json" + "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", + "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-tests-001-rls-tenant-isolation.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10577,36 +7736,99 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-cli-003-pg-cron-queue-workflow", + "eval": "build-vectors-001-rag-with-permissions", "stage": "build", "product": [ "database", - "edge-functions", - "cron", - "queues" + "vectors" ], "topic": [ "sql", - "sdk" + "rls" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", - "passed": true, - "notes": "schedule='* * * * *', active=true" + "name": "document_sections.embedding is vector(384)", + "passed": false, + "notes": "no embedding column" }, { - "name": "cron command enqueues to the 'tasks' queue", - "passed": true, - "notes": "queue depth 0 -> 1" + "name": "HNSW index on the embedding column", + "passed": false, + "notes": "no index on embedding column" }, { - "name": "process-tasks function drains the queue", - "passed": true, - "notes": "function removed the seeded message (id 4) from the queue" + "name": "index operator class matches the search operator", + "passed": false, + "notes": "match_document_sections not found" + }, + { + "name": "user A search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user B search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user A reads only own sections through the API", + "passed": false + }, + { + "name": "user A reads only own documents through the API", + "passed": false + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", + "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-vectors-001-rag-with-permissions.json" + }, + { + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "deploy-database-001-prometheus-metrics", + "stage": "deploy", + "product": [ + "database" + ], + "topic": [ + "observability" + ], + "suite": "benchmark", + "passed": false, + "checks": [ + { + "name": "preserved existing app scrape job", + "passed": false + }, + { + "name": "configured the Supabase Metrics API scrape correctly", + "passed": false, + "judgeNotes": "Fails: Supabase scrape is generated with basic_auth.password from an environment secret instead of basic_auth.password_file, and docker-compose.yml does not mount a password_file via volume or Compose secret. This also exposes/injects the secret API key directly. App scrape is preserved and endpoint/HTTPS/target are otherwise correct." + }, + { + "name": "documented live deployment and verification steps", + "passed": false, + "judgeNotes": "README includes restart and Prometheus targets verification, but it does not instruct how to create a Secret API key or place a matching secret file. The Compose setup uses environment variables rather than a secret file, so it fails the required secret setup." } ], "skills": { @@ -10616,67 +7838,53 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"pgmq create queue send pop delete archive read Supabase cron schedule\", limit: 5) {\n totalCount\n nodes {\n __typename\n title\n href\n content\n }\n }\n}", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/docs metrics prometheus supabase project endpoint", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", "pages": [ { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues", - "title": "Expose Queues for local and self-hosted Supabase" - }, - { - "url": "https://supabase.com/docs/guides/cron/install", - "title": "Install" + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted" } - ], - "resultChars": 28867 + ] }, { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"pg_cron schedule cron.schedule cron.unschedule Supabase SQL job name\", limit: 5) {\n totalCount\n nodes {\n __typename\n title\n href\n content\n }\n }\n}", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/docs project metrics prometheus endpoint supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", "pages": [ { - "url": "https://supabase.com/docs/guides/troubleshooting/pgcron-debugging-guide-n1KTaz", - "title": "pg_cron debugging guide" - }, - { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" - }, - { - "url": "https://supabase.com/docs/guides/cron/install", - "title": "Install" - }, - { - "url": "https://supabase.com/docs/guides/cron/quickstart", - "title": "Quickstart" - }, + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted" + } + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs Supabase metrics endpoint Prometheus scrape metrics project metrics", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", + "pages": [ { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted" } - ], - "resultChars": 33057 + ] } ] }, - "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", - "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-cli-003-pg-cron-queue-workflow.json" + "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", + "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-database-001-prometheus-metrics.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10687,36 +7895,34 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-database-001-migrate-postgres-to-supabase", - "stage": "build", + "eval": "deploy-functions-001-edge-function-secrets", + "stage": "deploy", "product": [ - "database" + "edge-functions" ], "topic": [ - "migrations" + "security" ], "suite": "benchmark", "interface": "cli", "passed": true, "checks": [ { - "name": "all 3 tables exist (teams, members, tasks)", - "passed": true - }, - { - "name": "row counts match (teams=5, members=10, tasks=13)", + "name": "WEATHER_API_KEY is set as a Function secret on the project", "passed": true }, { - "name": "foreign key constraints survived the restore", - "passed": true + "name": "the weather function is deployed to the project", + "passed": true, + "notes": "status ACTIVE" }, { - "name": "tasks_team_status_idx index survived the restore", - "passed": true + "name": "the weather function reads WEATHER_API_KEY from the environment", + "passed": true, + "judgeNotes": "Reads WEATHER_API_KEY from the runtime environment using Deno.env.get." }, { - "name": "sequences synced (next insert won't conflict with existing IDs)", + "name": "WEATHER_API_KEY value is not committed to the repo", "passed": true } ], @@ -10727,10 +7933,10 @@ "docs": { "calls": [] }, - "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", - "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", + "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", + "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-database-001-migrate-postgres-to-supabase.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-functions-001-edge-function-secrets.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10741,46 +7947,35 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-functions-004-service-role-bypass", - "stage": "build", + "eval": "deploy-self-hosting-001-docker-compose", + "stage": "deploy", "product": [ - "edge-functions", + "database", "auth", - "database" + "storage" ], "topic": [ - "rls", - "security", - "sdk" + "self-hosting" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "rejects missing auth", - "passed": true, - "notes": "status=401" - }, - { - "name": "user A reads own note", - "passed": true, - "notes": "status=200" + "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "passed": true }, { - "name": "reads only with the caller's JWT", - "passed": true, - "notes": "bearer_tokens=2, all_match=true" + "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "passed": true }, { - "name": "user A cannot force-read user B note", - "passed": true, - "notes": "status=200" + "name": "secrets rotated off the shipped defaults", + "passed": true }, { - "name": "user B cannot force-read user A note", - "passed": true, - "notes": "status=200" + "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "passed": true } ], "skills": { @@ -10790,39 +7985,21 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions environment variable SUPABASE_ANON_KEY auth user in Deno\", limit: 5) { nodes { title href content ... on Guide { subsections { totalCount } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli/testing-and-linting", - "title": "Testing and linting" - } - ], - "resultChars": 42100 - } - ] - }, - "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", - "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", + "source": "web_search", + "query": "site:supabase.com/docs self-hosting docker compose Supabase official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:github.com supabase docker-compose self-hosting compose env JWT_SECRET official", + "pages": [] + } + ] + }, + "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", + "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-functions-004-service-role-bypass.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-self-hosting-001-docker-compose.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10833,27 +8010,50 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-functions-005-dual-auth-user-secret", - "stage": "build", + "eval": "investigate-auth-001-deleted-user-access", + "stage": "investigate", "product": [ - "edge-functions", - "auth", - "database" + "auth" ], "topic": [ - "sdk", - "rls", - "security" + "security", + "sdk" ], "suite": "benchmark", - "interface": "cli", - "cliVersion": "2.109.1", + "interface": "mcp", "passed": false, "checks": [ { - "name": "read stack config from `supabase status`", + "name": "victim session active before delete-account", + "passed": true + }, + { + "name": "delete_account flow ran for the victim", + "passed": true + }, + { + "name": "delete-account revokes the user's sessions", + "passed": false, + "notes": "sessions left: 1" + }, + { + "name": "deleted user's refresh token is rejected", + "passed": false, + "notes": "refresh token still produces a session" + }, + { + "name": "deleted user cannot sign back in", + "passed": false, + "notes": "deleted account can still sign in" + }, + { + "name": "other users keep their sessions and access", + "passed": true + }, + { + "name": "diagnosed and explained session revocation", "passed": false, - "notes": "missing API_URL/SECRET_KEY/PUBLISHABLE_KEY — new API keys are required for @supabase/server; is the stack running on a new-enough CLI? got keys: ANON_KEY, DB_URL, JWT_SECRET, PUBLISHABLE_KEY, SECRET_KEY, SERVICE_ROLE_KEY" + "judgeNotes": "The answer does not actually fix the delete-account flow, and it does not clearly identify the specific bug as soft-deleting only the profile row while leaving the auth user/sessions intact. It gives some correct general JWT/RLS/key guidance, but no real revocation implementation is shipped." } ], "skills": { @@ -10861,117 +8061,12 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Edge Functions verifyJWT apikey service role auth user access token Deno.env.get SUPABASE_SERVICE_ROLE_KEY\", limit: 5) { edges { node { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href language methodName content } ... on TroubleshootingGuide { title href content } ... on CLICommandReference { title href content } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwts", - "title": "JSON Web Token (JWT)" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - } - ], - "resultChars": 55962 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions failed to determine entrypoint index.ts config.toml entrypoint\", limit: 5) { edges { node { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-tips", - "title": "Development tips" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - }, - { - "url": "https://supabase.com/docs/guides/functions/status-codes", - "title": "Status codes" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore", - "title": "Backup and Restore using the CLI" - } - ], - "resultChars": 58296 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"failed to determine entrypoint supabase functions serve local function layout index.ts\", limit: 10) { edges { node { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-tips", - "title": "Development tips" - }, - { - "url": "https://supabase.com/docs/guides/functions/quickstart", - "title": "Getting Started with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - }, - { - "url": "https://supabase.com/docs/guides/functions/status-codes", - "title": "Status codes" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/unable-to-deploy-edge-function", - "title": "Unable to deploy Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/issues-serving-edge-functions-locally", - "title": "Issues serving Edge Functions locally" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/unit-test", - "title": "Testing your Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-environment", - "title": "Development Environment" - } - ], - "resultChars": 60734 - } - ] + "calls": [] }, - "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", - "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", + "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-functions-005-dual-auth-user-secret.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-auth-001-deleted-user-access.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -10982,57 +8077,93 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-storage-001-private-bucket-access", - "stage": "build", + "eval": "investigate-realtime-001-subscribed-no-events", + "stage": "investigate", "product": [ - "storage", + "realtime", "database" ], "topic": [ - "rls", "sdk" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "bucket user-files exists", - "passed": true + "name": "orders table added to supabase_realtime publication", + "passed": false }, { - "name": "bucket user-files is private", + "name": "courier_locations still in supabase_realtime publication", "passed": true }, { - "name": "RLS still enabled on storage.objects", + "name": "publication still publishes INSERT events", "passed": true }, { - "name": "user A lists only own files", - "passed": true, - "notes": "saw: 019f6c9d-552b-73d4-b26e-66eca00b3f0c/receipt-alpha.pdf, 019f6c9d-552b-73d4-b26e-66eca00b3f0c/receipt-beta.pdf" - }, - { - "name": "user B cannot read user A files", + "name": "RLS still enabled on orders", "passed": true }, { - "name": "anon reads no files", - "passed": true + "name": "staff can still read orders through RLS", + "passed": true, + "notes": "authenticated sees 2 of 2 orders" }, { - "name": "user A can upload into own folder", - "passed": true + "name": "diagnosed missing publication membership", + "passed": false, + "judgeNotes": "The answer does not identify the missing orders table in the supabase_realtime publication or provide the required ALTER PUBLICATION fix. It only states an intent to inspect and trace." + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", + "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-realtime-001-subscribed-no-events.json" + }, + { + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "investigate-reliability-003-edge-function-5xx-correlation", + "stage": "investigate", + "product": [ + "edge-functions" + ], + "topic": [ + "observability" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "identified image-transform and the recurring 503 pattern", + "passed": false, + "judgeNotes": "The assistant did not identify image-transform as the affected function or recognize the recurring HTTP 503 pattern on 2026-04-28; it only reported missing logs/artifacts." }, { - "name": "user B cannot upload into user A folder", - "passed": true + "name": "attributed recurring 503s to gateway/platform layer, not function code", + "passed": false, + "judgeNotes": "The assistant gives no layer attribution for the 503s and does not ground the cause in the provided observations. It instead says it cannot determine the cause due to missing logs." }, { - "name": "configured private per-user storage access", + "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "Meets rubric: private user-files bucket, owner-scoped authenticated SELECT and INSERT policies with WITH CHECK, RLS kept enabled, and supabase-js createSignedUrl with short expiry." + "judgeNotes": "The assistant recommended concrete next steps, including providing relevant logs, inspecting 500 upload errors in the failing window, correlating with deploys/config changes, and checking clustering by file size/MIME/route." } ], "skills": { @@ -11040,232 +8171,12 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"storage.objects policy foldername signed url supabase-js createSignedUrl\", limit: 5) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads#signing-urls", - "title": "Signing URLs" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads#public-buckets", - "title": "Public buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads#downloading", - "title": "Downloading" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads#programmatic-downloads-with-query-parameters", - "title": "Programmatic downloads with query parameters" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads#private-buckets", - "title": "Private buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads", - "title": "Resumable Uploads" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads#overwriting-files", - "title": "Overwriting files" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads#presigned-uploads", - "title": "Presigned uploads" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads#uppy-example", - "title": "Uppy example" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads#concurrency", - "title": "Concurrency" - }, - { - "url": "https://supabase.com/docs/guides/storage/uploads/resumable-uploads#upload-url", - "title": "Upload URL" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations", - "title": "Storage Image Transformations" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#storage-api-configuration", - "title": "Storage API configuration:" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#imgproxy-configuration", - "title": "imgproxy configuration:" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#self-hosting", - "title": "Self hosting" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#supported-image-formats", - "title": "Supported image formats" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#limits", - "title": "Limits" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#modes", - "title": "Modes" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#resizing", - "title": "Resizing" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#optimizing", - "title": "Optimizing" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#transformation-options", - "title": "Transformation options" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#nextjs-loader", - "title": "Next.js loader" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#automatic-image-optimization-webp", - "title": "Automatic image optimization (WebP)" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#downloading-images", - "title": "Downloading images" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#signing-urls-with-transformation-options", - "title": "Signing URLs with transformation options" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#get-a-public-url-for-a-transformed-image", - "title": "Get a public URL for a transformed image" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations#manage-image-transformations", - "title": "Manage image transformations" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations", - "title": "Manage Storage Image Transformations usage" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#exceeding-quotas", - "title": "Exceeding Quotas" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#optimize-usage", - "title": "Optimize usage" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#view-usage", - "title": "View usage" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#billing-examples", - "title": "Billing examples" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#exceeding-quota", - "title": "Exceeding quota" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#usage-on-your-invoice", - "title": "Usage on your invoice" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#what-you-are-charged-for", - "title": "What you are charged for" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#example", - "title": "Example" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#how-charges-are-calculated", - "title": "How charges are calculated" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#example-1", - "title": "Example" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations#within-quota", - "title": "Within quota" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurl" - } - ], - "resultChars": 103502 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"storage.foldername storage.objects policy path tokens auth.uid\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/security/product-security", - "title": "Secure configuration of Supabase products" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", - "title": "Storage Helper Functions" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/storage/management/copy-move-objects", - "title": "Copy Objects" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/why-cant-i-uploadlistetc-my-public-bucket-Z6CmGt", - "title": "Why can't I upload/list/etc my public bucket?" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/quickstart", - "title": "Storage Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/design", - "title": "The Storage Schema" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - } - ], - "resultChars": 50894 - } - ] + "calls": [] }, - "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", - "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-storage-001-private-bucket-access.json" + "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", + "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-reliability-003-edge-function-5xx-correlation.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -11276,33 +8187,50 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-tests-001-rls-tenant-isolation", - "stage": "build", + "eval": "resolve-dataapi-001-empty-results", + "stage": "resolve", "product": [ - "database" + "data-api", + "database", + "auth" ], "topic": [ - "tests", - "rls" + "rls", + "sdk" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "pgTAP test file(s) written under supabase/tests/", - "passed": true, - "notes": "1 file(s): supabase/tests/tenant_isolation_test.sql" + "name": "RLS still enabled on bookmarks", + "passed": true }, { - "name": "pgTAP isolation tests ran and pass", - "passed": true, - "notes": "3 passed, 1 failed" + "name": "user A reads own bookmarks", + "passed": false }, { - "name": "agent correctly identifies the posts isolation bug from test results", - "passed": true, - "judgeNotes": "The agent correctly identifies `posts` as having a tenant isolation flaw, specifically that members can read cross-org posts, and grounds the conclusion in the pgTAP result where the cross-org `posts` check returned 1 row instead of 0. It also distinguishes that `notes` passed." + "name": "user B cannot read user A bookmarks", + "passed": true + }, + { + "name": "anon reads no bookmarks", + "passed": true + }, + { + "name": "user A can save a new bookmark", + "passed": false, + "notes": "new row violates row-level security policy for table \"bookmarks\"" + }, + { + "name": "user B cannot insert a bookmark as user A", + "passed": true + }, + { + "name": "diagnosed RLS and added owner-scoped policies", + "passed": false, + "judgeNotes": "The assistant did not diagnose RLS deny-all or create the required authenticated SELECT and INSERT owner-scoped policies. It only reported that the repository was not mounted and made no fix." } ], "skills": { @@ -11312,10 +8240,10 @@ "docs": { "calls": [] }, - "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", - "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-tests-001-rls-tenant-isolation.json" + "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", + "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-dataapi-001-empty-results.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -11326,50 +8254,46 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "build-vectors-001-rag-with-permissions", - "stage": "build", + "eval": "resolve-database-001-migration-history-mismatch", + "stage": "resolve", "product": [ - "database", - "vectors" + "database" ], "topic": [ - "sql", - "rls" + "migrations" ], "suite": "benchmark", - "interface": "mcp", - "passed": true, + "interface": "cli", + "passed": false, "checks": [ { - "name": "document_sections.embedding is vector(384)", - "passed": true, - "notes": "vector(384)" - }, - { - "name": "HNSW index on the embedding column", - "passed": true, - "notes": "CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops) WHERE (embedding IS NOT NULL)" + "name": "the avatar_url column is applied on the hosted profiles table", + "passed": false, + "notes": "avatar_url not found on public.profiles" }, { - "name": "index operator class matches the search operator", - "passed": true, - "notes": "function operators: <=>\nindexes: CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops) WHERE (embedding IS NOT NULL)" + "name": "migration 20240220000000 is recorded in the remote history", + "passed": false, + "notes": "remote history versions: [\"20240101000000\",\"20240115000000\"]" }, { - "name": "user A search returns only own sections, best match first", - "passed": true + "name": "remote migration history matches local migration files", + "passed": false, + "notes": "remote-only versions still unreconciled: [\"20240115000000\"] (remote: [\"20240101000000\",\"20240115000000\"], local: [\"20240101000000\",\"20240220000000\"])" }, { - "name": "user B search returns only own sections, best match first", - "passed": true + "name": "local migrations are a valid reconciled sequence", + "passed": false, + "notes": "expected 20240101000000_create_profiles → → 20240220000000_add_avatar_url, strictly ascending; got [20240101000000_create_profiles.sql, 20240220000000_add_avatar_url.sql]" }, { - "name": "user A reads only own sections through the API", + "name": "production profile data is intact (not reset)", "passed": true }, { - "name": "user A reads only own documents through the API", - "passed": true + "name": "the avatar migration and history reconciliation were done via the Supabase CLI", + "passed": false, + "judgeNotes": "No qualifying remote fix occurred. `supabase db push` attempts to the linked remote failed with IPv6/DNS errors (#7, #20, #33). The only later push was `supabase db push --db-url 'postgresql://postgres:postgres@127.0.0.1:54322/postgres' --yes` (#60), which targeted a local Docker database and reported `Remote database is up to date`, with no `Applying migration`/`Finished supabase db push`. No `supabase migration repair`, `supabase db pull`, or local orphan bio migration reconciliation was performed. Read-only `psql` inspection was used; no prohibited mutation workaround was shown." } ], "skills": { @@ -11379,10 +8303,10 @@ "docs": { "calls": [] }, - "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", - "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", + "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/build-vectors-001-rag-with-permissions.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-database-001-migration-history-mismatch.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -11393,30 +8317,39 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "deploy-database-001-prometheus-metrics", - "stage": "deploy", + "eval": "resolve-performance-001-slow-query-cpu-spike", + "stage": "resolve", "product": [ "database" ], "topic": [ - "observability" + "observability", + "sql" ], "suite": "benchmark", + "interface": "mcp", "passed": false, "checks": [ { - "name": "preserved existing app scrape job", - "passed": true + "name": "inspected pg_stat_statements for query performance", + "passed": false }, { - "name": "configured the Supabase Metrics API scrape correctly", - "passed": false, - "judgeNotes": "Fails: Supabase scrape uses basic_auth.password instead of password_file, docker-compose.yml does not mount the password file via volume or Compose secret, and README instructs replacing the value with a Secret API key in prometheus.yml. Existing app scrape and HTTPS metrics path are present." + "name": "ran EXPLAIN on the expensive query", + "passed": false }, { - "name": "documented live deployment and verification steps", + "name": "created index covering user_id and created_at", + "passed": false + }, + { + "name": "query plan uses an index and avoids sequential scan", "passed": false, - "judgeNotes": "README gives basic Supabase scrape setup and restart guidance, but fails required criteria: it instructs replacing the password inline rather than placing a matching secret file, does not configure/use a secret file, and lacks concrete verification steps via Prometheus targets, PromQL/Grafana, or equivalent." + "notes": "Limit (cost=118.52..118.57 rows=20 width=88)\n -> Sort (cost=118.52..118.57 rows=20 width=88)\n Sort Key: created_at DESC\n -> Seq Scan on events (cost=0.00..118.09 rows=20 width=88)\n Filter: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + }, + { + "name": "inserts still work", + "passed": true } ], "skills": { @@ -11424,49 +8357,12 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"metrics Prometheus project metrics\", limit: 10) { edges { node { __typename title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/database/replication/manual-replication-monitoring", - "title": "Manual replication monitoring" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/data-deletion", - "title": "Deleting data and dropping objects safely" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/how-to-view-database-metrics-uqf2z_", - "title": "How to View Database Metrics" - } - ], - "resultChars": 35779 - } - ] + "calls": [] }, - "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", - "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", + "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-database-001-prometheus-metrics.json" + "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-performance-001-slow-query-cpu-spike.json" }, { "experiment": "codex-gpt-5.4-mini-no-skills", @@ -11477,143 +8373,54 @@ "modelId": "gpt-5.4-mini", "reasoningEffort": "medium" }, - "eval": "deploy-functions-001-edge-function-secrets", - "stage": "deploy", + "eval": "resolve-security-002-rls-cross-tenant-leak", + "stage": "resolve", "product": [ - "edge-functions" + "database", + "auth" ], "topic": [ + "rls", "security" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "WEATHER_API_KEY is set as a Function secret on the project", + "name": "RLS enabled on notes", "passed": true }, { - "name": "the weather function is deployed to the project", - "passed": true, - "notes": "status ACTIVE" + "name": "tenant A sees only org A notes", + "passed": false }, { - "name": "the weather function reads WEATHER_API_KEY from the environment", - "passed": true, - "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + "name": "tenant B cannot read org A notes", + "passed": false }, { - "name": "WEATHER_API_KEY value is not committed to the repo", + "name": "tenant A author can update own note", "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"Edge Functions secrets set env-file deploy cli project-ref\", limit: 5) {\n nodes {\n ... on Guide {\n title\n href\n content\n }\n ... on CLICommandReference {\n title\n href\n content\n }\n ... on TroubleshootingGuide {\n title\n href\n content\n }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/push-notifications", - "title": "Sending Push Notifications" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/inspecting-edge-function-environment-variables-wg5qOQ", - "title": "Inspecting edge function environment variables" - }, - { - "url": "https://supabase.com/docs/guides/deployment/branching/troubleshooting", - "title": "Troubleshooting" - } - ], - "resultChars": 40692 - }, - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"functions deploy no-verify-jwt config.toml verify_jwt false\", limit: 5) {\n nodes {\n ... on Guide { title href content }\n ... on CLICommandReference { title href content }\n ... on TroubleshootingGuide { title href content }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/functions/deploy", - "title": "Deploy to Production" - }, - { - "url": "https://supabase.com/docs/guides/ai/examples/openai", - "title": "Generating OpenAI GPT3 completions" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/upstash-redis", - "title": "Upstash Redis" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-tips", - "title": "Development tips" - } - ], - "resultChars": 22399 - } - ] - }, - "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", - "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-functions-001-edge-function-secrets.json" - }, - { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" - }, - "eval": "deploy-self-hosting-001-docker-compose", - "stage": "deploy", - "product": [ - "database", - "auth", - "storage" - ], - "topic": [ - "self-hosting" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ + }, { - "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "name": "tenant B cannot update org A note", "passed": true }, { - "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "name": "tenant B author can delete own note", "passed": true }, { - "name": "secrets rotated off the shipped defaults", + "name": "tenant B cannot delete org A note", "passed": true }, { - "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "name": "tenant A can insert note in own org", + "passed": true + }, + { + "name": "tenant B cannot insert into org A", "passed": true } ], @@ -11624,468 +8431,410 @@ "docs": { "calls": [] }, - "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", - "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/deploy-self-hosting-001-docker-compose.json" + "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", + "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-security-002-rls-cross-tenant-leak.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "investigate-auth-001-deleted-user-access", - "stage": "investigate", + "eval": "build-cli-001-bootstrap-app", + "stage": "build", "product": [ - "auth" + "database", + "data-api" ], "topic": [ - "security", - "sdk" + "migrations", + "rls" ], "suite": "benchmark", - "interface": "mcp", - "passed": false, + "interface": "cli", + "passed": true, "checks": [ { - "name": "victim session active before delete-account", + "name": "supabase project initialised (supabase/config.toml exists)", "passed": true }, { - "name": "delete_account flow ran for the victim", + "name": "todos table is created by a migration file", "passed": true }, { - "name": "delete-account revokes the user's sessions", + "name": "todos table exists with at least 2 seeded rows", "passed": true, - "notes": "sessions left: 0" + "notes": "found 2 rows" }, { - "name": "deleted user's refresh token is rejected", + "name": "row level security is enabled on todos", "passed": true }, { - "name": "deleted user cannot sign back in", - "passed": false, - "notes": "deleted account can still sign in" + "name": "a SELECT policy targets the authenticated role", + "passed": true }, { - "name": "other users keep their sessions and access", - "passed": true + "name": "REST API returns no todos to anonymous requests", + "passed": true, + "notes": "0 rows" }, { - "name": "diagnosed and explained session revocation", - "passed": false, - "judgeNotes": "Fails because it does not explain that existing access tokens are stateless JWTs that remain cryptographically valid until expiry for purely local validation such as getClaims() or custom JWT middleware. It instead says the only remaining race is an in-flight request, which omits the required caveat. The diagnosis, RLS/data-path fix, session/refresh-token revocation, and publishable vs secret key explanation are otherwise largely correct." + "name": "REST API returns the todos to authenticated requests", + "passed": true, + "notes": "2 rows" } ], "skills": { - "available": [], - "loaded": [] + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query {\n searchDocs(query: \"publishable secret keys RLS anon service_role\", limit: 5) {\n nodes {\n __typename\n ... on Guide { title href content }\n ... on TroubleshootingGuide { title href content }\n ... on ClientLibraryFunctionReference { title href methodName language content }\n ... on ManagementApiReference { title href content }\n ... on CLICommandReference { title href content }\n }\n }\n}", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/server-side/creating-a-client", - "title": "Creating a Supabase client for SSR" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - } - ], - "resultChars": 112269 - } - ] + "calls": [] }, - "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", - "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-auth-001-deleted-user-access.json" + "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", + "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6/build-cli-001-bootstrap-app.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "investigate-realtime-001-subscribed-no-events", - "stage": "investigate", + "eval": "build-cli-002-declarative-schema", + "stage": "build", "product": [ - "realtime", "database" ], "topic": [ - "sdk" + "declarative-schema", + "migrations" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "orders table added to supabase_realtime publication", + "name": "supabase db diff used to generate the migration", "passed": true }, { - "name": "courier_locations still in supabase_realtime publication", + "name": "schema file updated to include description column", "passed": true }, { - "name": "publication still publishes INSERT events", + "name": "a new migration was generated for the change", "passed": true }, { - "name": "RLS still enabled on orders", + "name": "description column exists in the live database", "passed": true - }, - { - "name": "staff can still read orders through RLS", - "passed": true, - "notes": "authenticated sees 2 of 2 orders" - }, - { - "name": "diagnosed missing publication membership", - "passed": true, - "judgeNotes": "Identified orders missing from supabase_realtime publication as root cause, added public.orders with ALTER PUBLICATION via migration, and preserved courier_locations/RLS/policies." } ], "skills": { - "available": [], - "loaded": [] + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] }, "docs": { "calls": [] }, - "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", - "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", + "prompt": "Add a description text column to the `products` table in my local Supabase stack", + "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-realtime-001-subscribed-no-events.json" + "sourcePath": "codex-gpt-5.6/build-cli-002-declarative-schema.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "investigate-reliability-003-edge-function-5xx-correlation", - "stage": "investigate", + "eval": "build-cli-003-pg-cron-queue-workflow", + "stage": "build", "product": [ - "edge-functions" + "database", + "edge-functions", + "cron", + "queues" ], "topic": [ - "observability" + "sql", + "sdk" ], "suite": "benchmark", - "interface": "mcp", - "passed": false, + "interface": "cli", + "passed": true, "checks": [ { - "name": "identified image-transform and the recurring 503 pattern", + "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", "passed": true, - "judgeNotes": "The assistant identified `image-transform` as the affected function and described a recurring morning pattern of gateway-level HTTP 503s on 2026-04-28, including alternating/intermittent failures across roughly 06:00–12:00 UTC. This satisfies the rubric despite mentioning an additional avatar-upload issue." + "notes": "schedule='* * * * *', active=true" }, { - "name": "attributed recurring 503s to gateway/platform layer, not function code", - "passed": false, - "judgeNotes": "The assistant notes that the 503s lack function-side execution details and likely failed before runtime, but it also frames this as a function-level/edge-function issue and recommends redeploying the functions. The rubric explicitly fails answers that recommend fixing or redeploying the function as remediation." + "name": "cron command enqueues to the 'tasks' queue", + "passed": true, + "notes": "queue depth 0 -> 1" }, { - "name": "recommended a concrete next step", + "name": "process-tasks function drains the queue", "passed": true, - "judgeNotes": "The assistant recommended multiple concrete next steps: redeploying functions, adding retry/backoff, instrumenting packages, correlating a specific 500 with request payload/time, and escalating to Supabase support if 503s continue." + "notes": "function removed the seeded message (id 3) from the queue" } ], "skills": { - "available": [], + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], "loaded": [] }, "docs": { "calls": [] }, - "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", - "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.4-mini-no-skills/investigate-reliability-003-edge-function-5xx-correlation.json" + "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", + "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6/build-cli-003-pg-cron-queue-workflow.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "resolve-dataapi-001-empty-results", - "stage": "resolve", + "eval": "build-database-001-migrate-postgres-to-supabase", + "stage": "build", "product": [ - "data-api", - "database", - "auth" + "database" ], "topic": [ - "rls", - "sdk" + "migrations" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "RLS still enabled on bookmarks", - "passed": true - }, - { - "name": "user A reads own bookmarks", + "name": "all 3 tables exist (teams, members, tasks)", "passed": true }, { - "name": "user B cannot read user A bookmarks", + "name": "row counts match (teams=5, members=10, tasks=13)", "passed": true }, { - "name": "anon reads no bookmarks", + "name": "foreign key constraints survived the restore", "passed": true }, { - "name": "user A can save a new bookmark", + "name": "tasks_team_status_idx index survived the restore", "passed": true }, { - "name": "user B cannot insert a bookmark as user A", + "name": "sequences synced (next insert won't conflict with existing IDs)", "passed": true - }, - { - "name": "diagnosed RLS and added owner-scoped policies", - "passed": true, - "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies as deny-all, kept RLS enabled, and created authenticated-only SELECT and INSERT policies scoped to user_id = auth.uid() using USING and WITH CHECK." } ], "skills": { - "available": [], - "loaded": [] + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] }, "docs": { "calls": [] }, - "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", - "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", + "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", + "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-dataapi-001-empty-results.json" + "sourcePath": "codex-gpt-5.6/build-database-001-migrate-postgres-to-supabase.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "resolve-database-001-migration-history-mismatch", - "stage": "resolve", + "eval": "build-functions-004-service-role-bypass", + "stage": "build", "product": [ + "edge-functions", + "auth", "database" ], "topic": [ - "migrations" + "rls", + "security", + "sdk" ], "suite": "benchmark", - "interface": "cli", + "interface": "mcp", "passed": true, "checks": [ { - "name": "the avatar_url column is applied on the hosted profiles table", - "passed": true - }, - { - "name": "migration 20240220000000 is recorded in the remote history", - "passed": true + "name": "rejects missing auth", + "passed": true, + "notes": "status=401" }, { - "name": "remote migration history matches local migration files", - "passed": true + "name": "user A reads own note", + "passed": true, + "notes": "status=200" }, { - "name": "local migrations are a valid reconciled sequence", - "passed": true + "name": "reads only with the caller's JWT", + "passed": true, + "notes": "bearer_tokens=2, all_match=true" }, { - "name": "production profile data is intact (not reset)", - "passed": true + "name": "user A cannot force-read user B note", + "passed": true, + "notes": "status=200" }, { - "name": "the avatar migration and history reconciliation were done via the Supabase CLI", + "name": "user B cannot force-read user A note", "passed": true, - "judgeNotes": "Applied avatar_url via `supabase db push --db-url \"$(cat supabase/.temp/pooler-url)\" --include-all` (#35), which showed `Applying migration 20240220000000_add_avatar_url.sql...` and `Finished supabase db push.` Reconciled the orphan bio migration by adding local migration file `supabase/migrations/20240115000000_add_profile_bio.sql` (#34) and then letting the successful `supabase db push` align local/remote history; `supabase migration list` (#36) showed all three migrations matched. Read-only psql inspections were used; no forbidden direct SQL mutation or prepared-statement workaround observed." + "notes": "status=200" } ], "skills": { - "available": [], - "loaded": [] + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] }, "docs": { "calls": [] }, - "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", - "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", + "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", + "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-database-001-migration-history-mismatch.json" + "sourcePath": "codex-gpt-5.6/build-functions-004-service-role-bypass.json" }, { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" }, - "eval": "resolve-performance-001-slow-query-cpu-spike", - "stage": "resolve", + "eval": "build-functions-005-dual-auth-user-secret", + "stage": "build", "product": [ + "edge-functions", + "auth", "database" ], "topic": [ - "observability", - "sql" + "sdk", + "rls", + "security" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", + "cliVersion": "2.109.1", "passed": true, "checks": [ { - "name": "inspected pg_stat_statements for query performance", - "passed": true + "name": "seed rows present", + "passed": true, + "notes": "found 2/2 seeded rows" }, { - "name": "ran EXPLAIN on the expensive query", - "passed": true + "name": "rejects request with no credentials", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" }, { - "name": "created index covering user_id and created_at", - "passed": true + "name": "user with JWT reads only their own rows", + "passed": true, + "notes": "status 200: [{\"user_id\":\"82f9e431-8225-4820-94b8-530f52191baa\",\"metric\":\"steps_a_mrz1v0oc\",\"value\":111}]" }, { - "name": "query plan uses an index and avoids sequential scan", + "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "Limit (cost=55.55..55.61 rows=25 width=88)\n -> Sort (cost=55.55..55.61 rows=25 width=88)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=4.48..54.97 rows=25 width=88)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_idx (cost=0.00..4.47 rows=25 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + "notes": "status 200: [{\"user_id\":\"82f9e431-8225-4820-94b8-530f52191baa\",\"metric\":\"steps_a_mrz1v0oc\",\"value\":111}]" }, { - "name": "inserts still work", - "passed": true + "name": "service key bypasses RLS to read the target user's rows", + "passed": true, + "notes": "status 200: [{\"user_id\":\"7ab07e73-1f78-4524-a47f-728463959050\",\"metric\":\"steps_b_mrz1v0oc\",\"value\":222}]" + }, + { + "name": "non-service key is not granted service access", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "rejects an unverified (forged) user token", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "a user token in the apikey slot is not treated as the service key", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "implementation uses @supabase/server", + "passed": true, + "notes": "imports @supabase/server / withSupabase" } ], "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [] - }, - "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", - "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-performance-001-slow-query-cpu-spike.json" - }, - { - "experiment": "codex-gpt-5.4-mini-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.4-mini", - "reasoningEffort": "medium" - }, - "eval": "resolve-security-002-rls-cross-tenant-leak", - "stage": "resolve", - "product": [ - "database", - "auth" - ], - "topic": [ - "rls", - "security" - ], - "suite": "benchmark", - "interface": "mcp", - "passed": true, - "checks": [ - { - "name": "RLS enabled on notes", - "passed": true - }, - { - "name": "tenant A sees only org A notes", - "passed": true - }, - { - "name": "tenant B cannot read org A notes", - "passed": true - }, - { - "name": "tenant A author can update own note", - "passed": true - }, - { - "name": "tenant B cannot update org A note", - "passed": true - }, - { - "name": "tenant B author can delete own note", - "passed": true - }, - { - "name": "tenant B cannot delete org A note", - "passed": true - }, - { - "name": "tenant A can insert note in own org", - "passed": true - }, - { - "name": "tenant B cannot insert into org A", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] }, "docs": { "calls": [] }, - "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", - "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.4-mini-no-skills/resolve-security-002-rls-cross-tenant-leak.json" + "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", + "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/build-functions-005-dual-auth-user-secret.json" }, { "experiment": "codex-gpt-5.6", @@ -12096,50 +8845,24 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-cli-001-bootstrap-app", + "eval": "build-storage-001-private-bucket-access", "stage": "build", "product": [ - "database", - "data-api" + "storage", + "database" ], "topic": [ - "migrations", - "rls" + "rls", + "sdk" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "supabase project initialised (supabase/config.toml exists)", - "passed": true - }, - { - "name": "todos table is created by a migration file", - "passed": true - }, - { - "name": "todos table exists with at least 2 seeded rows", - "passed": true, - "notes": "found 2 rows" - }, - { - "name": "row level security is enabled on todos", - "passed": true - }, - { - "name": "a SELECT policy targets the authenticated role", - "passed": true - }, - { - "name": "REST API returns no todos to anonymous requests", - "passed": true, - "notes": "0 rows" - }, - { - "name": "REST API returns the todos to authenticated requests", - "passed": true, - "notes": "2 rows" + "name": "bucket user-files exists", + "passed": false, + "notes": "no row in storage.buckets with id or name 'user-files'" } ], "skills": { @@ -12154,59 +8877,79 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Row Level Security authenticated role SELECT policy anon authenticated Data API grants local development migrations seed.sql\", limit: 5) { nodes { title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/changelog.md", "pages": [ { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/roles", - "title": "Postgres Roles" + "url": "https://supabase.com/changelog.md" } - ], - "resultChars": 104061 + ] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"supabase db diff local generate migration imperative migrations\", limit: 3) { nodes { title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/docs storage access control foldername auth.uid policy objects", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage access control foldername auth.uid create policy objects signed URL createSignedUrl", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/changelog.md", "pages": [ { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/declarative-database-schemas", - "title": "Declarative database schemas" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" + "url": "https://supabase.com/changelog.md" } - ], - "resultChars": 45083 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage security access control foldername storage.objects policy", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage access control foldername create policy authenticated user id", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/reference/javascript/storage-from-createsignedurl \"data.signedUrl\"", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage/security/access-control foldername auth.uid storage.objects policy", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/reference/javascript/storage-from-createsignedurl \"data.signedUrl\"", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage security access-control foldername create policy storage.objects", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/reference/javascript/storage-from-createsignedurl \"Create a signed URL\"", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage security access-control foldername createSignedUrl private bucket Supabase", + "pages": [] } ] }, - "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", - "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-cli-001-bootstrap-app.json" + "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", + "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/build-storage-001-private-bucket-access.json" }, { "experiment": "codex-gpt-5.6", @@ -12217,34 +8960,33 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-cli-002-declarative-schema", + "eval": "build-tests-001-rls-tenant-isolation", "stage": "build", "product": [ "database" ], "topic": [ - "declarative-schema", - "migrations" + "tests", + "rls" ], "suite": "benchmark", "interface": "cli", "passed": true, "checks": [ { - "name": "supabase db diff used to generate the migration", - "passed": true - }, - { - "name": "schema file updated to include description column", - "passed": true + "name": "pgTAP test file(s) written under supabase/tests/", + "passed": true, + "notes": "1 file(s): supabase/tests/database/tenant_isolation.test.sql" }, { - "name": "a new migration was generated for the change", - "passed": true + "name": "pgTAP isolation tests ran and pass", + "passed": true, + "notes": "9 passed, 0 failed" }, { - "name": "description column exists in the live database", - "passed": true + "name": "agent correctly identifies the posts isolation bug from test results", + "passed": true, + "judgeNotes": "The agent correctly identifies `posts` as having the tenant isolation flaw: its policy allowed users with any membership to read posts without matching `posts.org_id`. It also correctly states `notes` is isolated and cites pgTAP/test verification results rather than blaming tests or migration comments." } ], "skills": { @@ -12257,33 +8999,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"declarative database schemas local development schema_paths db diff migration\", limit: 3) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/declarative-database-schemas", - "title": "Declarative database schemas" - }, - { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-db-diff", - "title": "Diffs the local database for schema changes" - } - ], - "resultChars": 34329 - } - ] + "calls": [] }, - "prompt": "Add a description text column to the `products` table in my local Supabase stack", - "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", + "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", + "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-cli-002-declarative-schema.json" + "sourcePath": "codex-gpt-5.6/build-tests-001-rls-tenant-isolation.json" }, { "experiment": "codex-gpt-5.6", @@ -12294,36 +9015,52 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-cli-003-pg-cron-queue-workflow", + "eval": "build-vectors-001-rag-with-permissions", "stage": "build", "product": [ "database", - "edge-functions", - "cron", - "queues" + "vectors" ], "topic": [ "sql", - "sdk" + "rls" ], "suite": "benchmark", - "interface": "cli", - "passed": true, + "interface": "mcp", + "passed": false, "checks": [ { - "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", - "passed": true, - "notes": "schedule='* * * * *', active=true" + "name": "document_sections.embedding is vector(384)", + "passed": false, + "notes": "no embedding column" }, { - "name": "cron command enqueues to the 'tasks' queue", - "passed": true, - "notes": "queue depth 0 -> 1" + "name": "HNSW index on the embedding column", + "passed": false, + "notes": "no index on embedding column" }, { - "name": "process-tasks function drains the queue", - "passed": true, - "notes": "function removed the seeded message (id 3) from the queue" + "name": "index operator class matches the search operator", + "passed": false, + "notes": "match_document_sections not found" + }, + { + "name": "user A search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user B search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user A reads only own sections through the API", + "passed": false + }, + { + "name": "user A reads only own documents through the API", + "passed": false } ], "skills": { @@ -12332,681 +9069,47 @@ "supabase-postgres-best-practices" ], "loaded": [ - "supabase" + "supabase", + "supabase-postgres-best-practices" ] }, "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Queues pgmq create queue send read delete message Edge Function Cron pg_cron local\", limit: 8) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/docs semantic search pgvector match_documents gte-small 384 Supabase Edge Functions", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/ai \"match_documents\"", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/changelog.md vector breaking change Supabase pgvector", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/functions/examples/semantic-search", "pages": [ { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues#features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/queues#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions#invoke-an-edge-function-every-minute", - "title": "Invoke an Edge Function every minute" - }, - { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" - }, - { - "url": "https://supabase.com/docs/guides/cron#how-does-cron-work", - "title": "How does Cron work?" - }, - { - "url": "https://supabase.com/docs/guides/cron#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicsendqueue_name-message-sleep_seconds", - "title": "pgmq_public.send(queue_name, message, sleep_seconds)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicpopqueue_name", - "title": "pgmq_public.pop(queue_name)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicsend_batchqueue_name-messages-sleep_seconds", - "title": "pgmq_public.send_batch(queue_name, messages, sleep_seconds)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicarchivequeue_name-message_id", - "title": "pgmq_public.archive(queue_name, message_id)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicdeletequeue_name-message_id", - "title": "pgmq_public.delete(queue_name, message_id)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicreadqueue_name-sleep_seconds-n", - "title": "pgmq_public.read(queue_name, sleep_seconds, n)" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enable-rls-on-your-tables-in-pgmq-schema", - "title": "Enable RLS on your tables in pgmq schema" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#create-queues", - "title": "Create Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#queue-types", - "title": "Queue types" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#message", - "title": "Message" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#pull-based-queue", - "title": "Pull-Based Queue" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enqueueing-and-dequeueing-messages", - "title": "Enqueueing and dequeueing messages" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#grant-permissions-to-pgmq_public-database-functions", - "title": "Grant permissions to pgmq_public database functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#expose-queues-to-client-side-consumers", - "title": "Expose Queues to client-side consumers" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#what-happens-when-you-create-a-queue", - "title": "What happens when you create a queue?" - }, - { - "url": "https://supabase.com/docs/guides/cron/install", - "title": "Install" - }, - { - "url": "https://supabase.com/docs/guides/cron/install#uninstall", - "title": "Uninstall" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#consuming-messages-in-an-edge-function", - "title": "Consuming messages in an Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-signature", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_post", - "title": "http_post" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-signature", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_get", - "title": "http_get" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#enable-the-extension", - "title": "Enable the extension" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#http_delete", - "title": "http_delete" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#post-signature-1", - "title": "Signature " - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#inspecting-request-data", - "title": "Inspecting request data" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#inspecting-failed-requests", - "title": "Inspecting failed requests" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#configuration", - "title": "Configuration" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#get-current-settings", - "title": "Get current settings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#alter-settings", - "title": "Alter settings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#examples", - "title": "Examples" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#invoke-a-supabase-edge-function", - "title": "Invoke a Supabase Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#call-an-endpoint-every-minute-with-pg_cron", - "title": "Call an endpoint every minute with pg_cron" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#execute-pg_net-in-a-trigger", - "title": "Execute pg_net in a trigger" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#send-multiple-table-rows-in-one-request", - "title": "Send multiple table rows in one request" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#limitations", - "title": "Limitations" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#debugging-requests", - "title": "Debugging requests" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#analyzing-responses", - "title": "Analyzing responses" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net#delete-usage", - "title": "Usage " + "url": "https://supabase.com/docs/guides/functions/examples/semantic-search" } - ], - "resultChars": 116291 + ] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"Queues API pgmq_public.read delete send create queue SQL\", limit: 10) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#expose-queues-to-client-side-consumers", - "title": "Expose Queues to client-side consumers" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enqueueing-and-dequeueing-messages", - "title": "Enqueueing and dequeueing messages" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#pull-based-queue", - "title": "Pull-Based Queue" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#message", - "title": "Message" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#queue-types", - "title": "Queue types" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#create-queues", - "title": "Create Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#what-happens-when-you-create-a-queue", - "title": "What happens when you create a queue?" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enable-rls-on-your-tables-in-pgmq-schema", - "title": "Enable RLS on your tables in pgmq schema" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#grant-permissions-to-pgmq_public-database-functions", - "title": "Grant permissions to pgmq_public database functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicreadqueue_name-sleep_seconds-n", - "title": "pgmq_public.read(queue_name, sleep_seconds, n)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicdeletequeue_name-message_id", - "title": "pgmq_public.delete(queue_name, message_id)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicarchivequeue_name-message_id", - "title": "pgmq_public.archive(queue_name, message_id)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicsend_batchqueue_name-messages-sleep_seconds", - "title": "pgmq_public.send_batch(queue_name, messages, sleep_seconds)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicsendqueue_name-message-sleep_seconds", - "title": "pgmq_public.send(queue_name, message, sleep_seconds)" - }, - { - "url": "https://supabase.com/docs/guides/queues/api#pgmq_publicpopqueue_name", - "title": "pgmq_public.pop(queue_name)" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#drop_queue", - "title": "drop_queue" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#deletingarchiving-messages", - "title": "Deleting/Archiving messages" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#delete-single", - "title": "delete (single)" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#delete-batch", - "title": "delete (batch)" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#purge_queue", - "title": "purge_queue" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#archive-single", - "title": "archive (single)" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#archive-batch", - "title": "archive (batch)" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#utilities", - "title": "Utilities" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#set_vt", - "title": "set_vt" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#list_queues", - "title": "list_queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#metrics", - "title": "metrics" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#metrics_all", - "title": "metrics_all" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#types", - "title": "Types" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#message_record", - "title": "message_record" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#enable-the-extension", - "title": "Enable the extension" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#get-usage", - "title": "Usage " - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#queue-management", - "title": "Queue management" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#create", - "title": "create" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#create_unlogged", - "title": "create_unlogged" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#detach_archive", - "title": "detach_archive" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#sending-messages", - "title": "Sending messages" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#send", - "title": "send" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#send_batch", - "title": "send_batch" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#reading-messages", - "title": "Reading messages" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#read", - "title": "read" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#read_with_poll", - "title": "read_with_poll" - }, - { - "url": "https://supabase.com/docs/guides/queues/pgmq#pop", - "title": "pop" - }, - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/queues#features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues", - "title": "Expose Queues for local and self-hosted Supabase" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues#stop-exposing-queues", - "title": "Stop exposing queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues#expose-queues-with-docker-compose", - "title": "Expose queues with Docker compose" - }, - { - "url": "https://supabase.com/docs/guides/queues/expose-self-hosted-queues#expose-queues-with-supabase-cli", - "title": "Expose Queues with Supabase CLI" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#consuming-messages-in-an-edge-function", - "title": "Consuming messages in an Edge Function" - } - ], - "resultChars": 278303 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"site:supabase.com/docs/guides/queues/api delete message_id read sleep_seconds send messages\", limit: 6) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/extensions/pgmq", - "title": "pgmq: Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#consuming-messages-in-an-edge-function", - "title": "Consuming messages in an Edge Function" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enable-rls-on-your-tables-in-pgmq-schema", - "title": "Enable RLS on your tables in pgmq schema" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#concepts", - "title": "Concepts" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#pull-based-queue", - "title": "Pull-Based Queue" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#message", - "title": "Message" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#queue-types", - "title": "Queue types" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#create-queues", - "title": "Create Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#what-happens-when-you-create-a-queue", - "title": "What happens when you create a queue?" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#expose-queues-to-client-side-consumers", - "title": "Expose Queues to client-side consumers" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#grant-permissions-to-pgmq_public-database-functions", - "title": "Grant permissions to pgmq_public database functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart#enqueueing-and-dequeueing-messages", - "title": "Enqueueing and dequeueing messages" - }, - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues#features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/queues#resources", - "title": "Resources" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started", - "title": "Getting Started with Realtime" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#framework-examples", - "title": "Framework examples" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#2-initialize-the-client", - "title": "2. Initialize the client" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#1-install-the-client-library", - "title": "1. Install the client library" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#quick-start", - "title": "Quick start" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#get-api-details", - "title": "Get API details" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#3-create-your-first-channel", - "title": "3. Create your first Channel" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#4-set-up-authorization", - "title": "4. Set up authorization" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#5-send-and-receive-messages", - "title": "5. Send and receive messages" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#51-using-client-libraries", - "title": "5.1 using client libraries" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#52-using-httprest-api", - "title": "5.2 using HTTP/REST API" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#53-using-database-triggers", - "title": "5.3 using database triggers" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#use-private-channels", - "title": "Use private channels" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#essential-best-practices", - "title": "Essential best practices" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#follow-naming-conventions", - "title": "Follow naming conventions" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#clean-up-subscriptions", - "title": "Clean up subscriptions" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#choose-the-right-feature", - "title": "Choose the right feature" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-broadcast", - "title": "When to use Broadcast" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-presence", - "title": "When to use Presence" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#when-to-use-postgres-changes", - "title": "When to use Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#next-steps", - "title": "Next steps" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#core-features", - "title": "Core features" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#security--configuration", - "title": "Security & configuration" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#advanced-topics", - "title": "Advanced topics" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started#integration-guides", - "title": "Integration guides" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_cron", - "title": "pg_cron: Schedule Recurring Jobs with Cron Syntax in Postgres" - } - ], - "resultChars": 110913 + "source": "web_search", + "query": "site:supabase.com/docs/guides/functions/examples/semantic-search query_embeddings embeddings vector(384)", + "pages": [] } ] }, - "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", - "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-cli-003-pg-cron-queue-workflow.json" + "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", + "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/build-vectors-001-rag-with-permissions.json" }, { "experiment": "codex-gpt-5.6", @@ -13017,37 +9120,30 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-database-001-migrate-postgres-to-supabase", - "stage": "build", + "eval": "deploy-database-001-prometheus-metrics", + "stage": "deploy", "product": [ "database" ], "topic": [ - "migrations" + "observability" ], "suite": "benchmark", - "interface": "cli", "passed": true, "checks": [ { - "name": "all 3 tables exist (teams, members, tasks)", - "passed": true - }, - { - "name": "row counts match (teams=5, members=10, tasks=13)", - "passed": true - }, - { - "name": "foreign key constraints survived the restore", + "name": "preserved existing app scrape job", "passed": true }, { - "name": "tasks_team_status_idx index survived the restore", - "passed": true + "name": "configured the Supabase Metrics API scrape correctly", + "passed": true, + "judgeNotes": "Meets requirements: preserves app scrape, adds HTTPS Supabase metrics scrape with correct path and basic_auth password_file, targets supabase.co with project ref placeholder, and docker-compose wires the matching password file via a Compose secret." }, { - "name": "sequences synced (next insert won't conflict with existing IDs)", - "passed": true + "name": "documented live deployment and verification steps", + "passed": true, + "judgeNotes": "README includes Secret API key creation, matching Compose secret file placement, render/start/reload/restart steps, and concrete verification via Prometheus targets, PromQL, and Grafana." } ], "skills": { @@ -13060,41 +9156,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"restore existing Postgres pg_dump custom format local Supabase CLI\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/postgres", - "title": "Migrate from Postgres to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/local-development/restoring-downloaded-backup", - "title": "Restoring a downloaded backup locally" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform", - "title": "Restore a Platform Project to Self-Hosted" - }, - { - "url": "https://supabase.com/docs/guides/database/inspect", - "title": "Debugging and monitoring" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/vercel-postgres", - "title": "Migrate from Vercel Postgres to Supabase" - } - ], - "resultChars": 52540 - } - ] + "calls": [] }, - "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", - "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-database-001-migrate-postgres-to-supabase.json" + "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", + "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/deploy-database-001-prometheus-metrics.json" }, { "experiment": "codex-gpt-5.6", @@ -13105,46 +9172,35 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-functions-004-service-role-bypass", - "stage": "build", + "eval": "deploy-functions-001-edge-function-secrets", + "stage": "deploy", "product": [ - "edge-functions", - "auth", - "database" + "edge-functions" ], "topic": [ - "rls", - "security", - "sdk" + "security" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "rejects missing auth", - "passed": true, - "notes": "status=401" + "name": "WEATHER_API_KEY is set as a Function secret on the project", + "passed": true }, { - "name": "user A reads own note", + "name": "the weather function is deployed to the project", "passed": true, - "notes": "status=200" + "notes": "status ACTIVE" }, { - "name": "reads only with the caller's JWT", - "passed": true, - "notes": "bearer_tokens=2, all_match=true" - }, - { - "name": "user A cannot force-read user B note", + "name": "the weather function reads WEATHER_API_KEY from the environment", "passed": true, - "notes": "status=200" + "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." }, { - "name": "user B cannot force-read user A note", - "passed": true, - "notes": "status=200" + "name": "WEATHER_API_KEY value is not committed to the repo", + "passed": true } ], "skills": { @@ -13157,88 +9213,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions JWT verification authenticate user getClaims Authorization header createClient RLS\", limit: 8) { nodes { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/getting-started", - "title": "Getting Started with OAuth 2.1 Server" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", - "title": "Why is my service role key client getting RLS errors or not returning data?" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" - }, - { - "url": "https://supabase.com/docs/reference/javascript/auth-getclaims" - } - ], - "resultChars": 79335 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions verify_jwt asymmetric signing keys recommended authentication getClaims 2026\", limit: 8) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/oauth-flows", - "title": "OAuth 2.1 Flows" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" - } - ], - "resultChars": 96675 - } - ] + "calls": [] }, - "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", - "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", + "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", + "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-functions-004-service-role-bypass.json" + "sourcePath": "codex-gpt-5.6/deploy-functions-001-edge-function-secrets.json" }, { "experiment": "codex-gpt-5.6", @@ -13249,67 +9229,107 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-functions-005-dual-auth-user-secret", - "stage": "build", + "eval": "deploy-self-hosting-001-docker-compose", + "stage": "deploy", "product": [ - "edge-functions", + "database", "auth", - "database" + "storage" ], "topic": [ - "sdk", - "rls", - "security" + "self-hosting" ], "suite": "benchmark", "interface": "cli", - "cliVersion": "2.109.1", "passed": true, "checks": [ { - "name": "seed rows present", - "passed": true, - "notes": "found 2/2 seeded rows" + "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "passed": true }, { - "name": "rejects request with no credentials", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "passed": true }, { - "name": "user with JWT reads only their own rows", - "passed": true, - "notes": "status 200: [{\"user_id\":\"9d427975-63c5-47ed-b72b-5b56288341f8\",\"metric\":\"steps_a_mrpdsmvt\",\"value\":111}]" + "name": "secrets rotated off the shipped defaults", + "passed": true }, { - "name": "user cannot read another user's rows by passing user_id", - "passed": true, - "notes": "status 200: [{\"user_id\":\"9d427975-63c5-47ed-b72b-5b56288341f8\",\"metric\":\"steps_a_mrpdsmvt\",\"value\":111}]" + "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", + "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6/deploy-self-hosting-001-docker-compose.json" + }, + { + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" + }, + "eval": "investigate-auth-001-deleted-user-access", + "stage": "investigate", + "product": [ + "auth" + ], + "topic": [ + "security", + "sdk" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "victim session active before delete-account", + "passed": true }, { - "name": "service key bypasses RLS to read the target user's rows", - "passed": true, - "notes": "status 200: [{\"user_id\":\"a28cbd5f-63ce-4a79-aabe-16254a9f2708\",\"metric\":\"steps_b_mrpdsmvt\",\"value\":222}]" + "name": "delete_account flow ran for the victim", + "passed": true }, { - "name": "non-service key is not granted service access", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + "name": "delete-account revokes the user's sessions", + "passed": false, + "notes": "sessions left: 1" }, { - "name": "rejects an unverified (forged) user token", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + "name": "deleted user's refresh token is rejected", + "passed": false, + "notes": "refresh token still produces a session" }, { - "name": "a user token in the apikey slot is not treated as the service key", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + "name": "deleted user cannot sign back in", + "passed": false, + "notes": "deleted account can still sign in" }, { - "name": "implementation uses @supabase/server", - "passed": true, - "notes": "imports @supabase/server / withSupabase" + "name": "other users keep their sessions and access", + "passed": true + }, + { + "name": "diagnosed and explained session revocation", + "passed": false, + "judgeNotes": "Fails the diagnosis requirement: it does not identify that the existing delete_account flow only soft-deletes the profile row and never deletes the auth user or revokes sessions. The proposed fix, token-window explanation, and publishable vs secret key clarification are broadly consistent, but the required root-cause diagnosis is missing." } ], "skills": { @@ -13324,138 +9344,89 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions authentication JWT verification apikey secret key service role getUser createClient Deno environment SUPABASE_SERVICE_ROLE_KEY\", limit: 8) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - } - ], - "resultChars": 76653 + "source": "web_search", + "query": "site:supabase.com/docs delete user access token session invalidate auth admin deleteUser", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"@supabase/server verifyCredentials apikey auth user secret key Edge Functions createContextClient createAdminClient\", limit: 10) { nodes { __typename title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/changelog auth sessions delete user access token invalidation session_id", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/auth/sessions", "pages": [ { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-createclient" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/auth/passkeys", - "title": "Passkey authentication" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, - { - "url": "https://supabase.com/docs/guides/functions/websockets", - "title": "Handling WebSockets" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/performing-administration-tasks-on-the-server-side-with-the-servicerole-secret-BYM4Fa", - "title": "Performing administration tasks on the server side with a secret key" - }, - { - "url": "https://supabase.com/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers", - "title": "Migrating to the SSR package from Auth Helpers" + "url": "https://supabase.com/docs/guides/auth/sessions" } - ], - "resultChars": 71941 + ] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions auth secret legacy service_role JWT apikey @supabase/server INVALID_CREDENTIALS\", limit: 8) { nodes { __typename title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/changelog auth sessions delete user JWT breaking change", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs delete user JWT remains valid until expiry sign out sessions", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/reference/javascript/auth-admin-deleteuser delete user JWT valid until expiry", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/auth sessions delete user access token not revoked", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/auth/signout access token valid until expiry sessions revoked", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/api/securing-your-api", "pages": [ { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" + "url": "https://supabase.com/docs/guides/api/securing-your-api" } - ], - "resultChars": 79252 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs auth sessions session_id JWT sign out access token remains valid until expiry", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs auth sessions delete user JWT remains valid session_id", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/api/api-keys publishable secret key Authorization RLS", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs auth sessions session_id JWT sign out access token remains valid until expiry", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs delete user auth.users delete account database function sessions", + "pages": [] } ] }, - "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", - "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-functions-005-dual-auth-user-secret.json" + "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", + "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/investigate-auth-001-deleted-user-access.json" }, { "experiment": "codex-gpt-5.6", @@ -13466,57 +9437,44 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-storage-001-private-bucket-access", - "stage": "build", + "eval": "investigate-realtime-001-subscribed-no-events", + "stage": "investigate", "product": [ - "storage", + "realtime", "database" ], "topic": [ - "rls", "sdk" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "bucket user-files exists", - "passed": true - }, - { - "name": "bucket user-files is private", - "passed": true - }, - { - "name": "RLS still enabled on storage.objects", - "passed": true - }, - { - "name": "user A lists only own files", - "passed": true, - "notes": "saw: 019f71b9-0677-7412-9c1e-57a03e47da0e/receipt-alpha.pdf, 019f71b9-0677-7412-9c1e-57a03e47da0e/receipt-beta.pdf" + "name": "orders table added to supabase_realtime publication", + "passed": false }, { - "name": "user B cannot read user A files", + "name": "courier_locations still in supabase_realtime publication", "passed": true }, { - "name": "anon reads no files", + "name": "publication still publishes INSERT events", "passed": true }, { - "name": "user A can upload into own folder", + "name": "RLS still enabled on orders", "passed": true }, { - "name": "user B cannot upload into user A folder", - "passed": true + "name": "staff can still read orders through RLS", + "passed": true, + "notes": "authenticated sees 2 of 2 orders" }, { - "name": "configured private per-user storage access", + "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "Creates private user-files bucket, keeps RLS policies on storage.objects scoped to authenticated users and auth.uid() folder ownership for SELECT and INSERT (plus update/delete), and provides supabase-js createSignedUrl with expiry. No public bucket/getPublicUrl/service-role client-side issue." + "judgeNotes": "Identifies missing orders table in supabase_realtime publication as root cause and adds only public.orders via ALTER PUBLICATION, preserving existing tables/RLS/policies." } ], "skills": { @@ -13531,97 +9489,21 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Storage access control policy foldername auth.uid private bucket createSignedUrl JavaScript\", limit: 5) { nodes { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage", - "title": "Storage" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/firebase-storage", - "title": "Migrated from Firebase Storage to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - } - ], - "resultChars": 18276 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Storage access control RLS policy storage.foldername(name) auth.uid upload select update delete\", limit: 6) { nodes { ... on Guide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/security/product-security", - "title": "Secure configuration of Supabase products" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", - "title": "Storage Helper Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/design", - "title": "The Storage Schema" - } - ], - "resultChars": 25062 + "source": "web_search", + "query": "site:supabase.com/docs postgres_changes add table supabase_realtime publication", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"JavaScript createSignedUrl storage expiresIn private bucket supabase-js\", limit: 5) { nodes { ... on ClientLibraryFunctionReference { title href content language methodName } ... on Guide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/guides/storage/serving/image-transformations", - "title": "Storage Image Transformations" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsigneduploadurl" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurl" - }, - { - "url": "https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations", - "title": "Manage Storage Image Transformations usage" - } - ], - "resultChars": 32239 + "source": "web_search", + "query": "site:supabase.com/docs/guides/realtime/postgres-changes replication publication add table supabase_realtime", + "pages": [] } ] }, - "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", - "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", + "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", + "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", "attempts": 2, - "sourcePath": "codex-gpt-5.6/build-storage-001-private-bucket-access.json" + "sourcePath": "codex-gpt-5.6/investigate-realtime-001-subscribed-no-events.json" }, { "experiment": "codex-gpt-5.6", @@ -13632,33 +9514,32 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-tests-001-rls-tenant-isolation", - "stage": "build", + "eval": "investigate-reliability-003-edge-function-5xx-correlation", + "stage": "investigate", "product": [ - "database" + "edge-functions" ], "topic": [ - "tests", - "rls" + "observability" ], "suite": "benchmark", - "interface": "cli", + "interface": "mcp", "passed": true, "checks": [ { - "name": "pgTAP test file(s) written under supabase/tests/", + "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "notes": "1 file(s): supabase/tests/database/tenant_isolation.test.sql" + "judgeNotes": "Identified image-transform as affected and described recurring HTTP 503s across 07:00–12:00 UTC on 2026-04-28, including the 8 gateway failures." }, { - "name": "pgTAP isolation tests ran and pass", + "name": "attributed recurring 503s to gateway/platform layer, not function code", "passed": true, - "notes": "8 passed, 0 failed" + "judgeNotes": "The assistant clearly attributes the recurring image-transform 503s to the gateway/platform layer, not function code, and grounds this in valid observations: gateway logs without corresponding Edge Function logs, successful nearby invocations on the same deployment, and distinction from a separate function-level avatar-upload 500." }, { - "name": "agent correctly identifies the posts isolation bug from test results", + "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "The agent correctly identified `posts` as having a tenant isolation flaw, specifically that organization members could read posts across tenants, and grounded this in pgTAP failures. It also correctly stated `notes` isolation was enforced. Extra mention of `memberships` does not undermine the required conclusion." + "judgeNotes": "Recommended concrete next steps, including opening a Supabase support ticket with project, region, time window, and API event IDs, plus specific retry/alerting/logging actions." } ], "skills": { @@ -13671,41 +9552,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"row level security testing auth.uid tenant isolation policies pgTAP\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/overview", - "title": "Testing Overview" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgtap", - "title": "pgTAP: Unit Testing" - }, - { - "url": "https://supabase.com/docs/guides/database/testing", - "title": "Testing Your Database" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - } - ], - "resultChars": 56902 - } - ] + "calls": [] }, - "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", - "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", + "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", + "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-tests-001-rls-tenant-isolation.json" + "sourcePath": "codex-gpt-5.6/investigate-reliability-003-edge-function-5xx-correlation.json" }, { "experiment": "codex-gpt-5.6", @@ -13716,50 +9568,49 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-vectors-001-rag-with-permissions", - "stage": "build", + "eval": "resolve-dataapi-001-empty-results", + "stage": "resolve", "product": [ + "data-api", "database", - "vectors" + "auth" ], "topic": [ - "sql", - "rls" + "rls", + "sdk" ], "suite": "benchmark", "interface": "mcp", "passed": true, "checks": [ { - "name": "document_sections.embedding is vector(384)", - "passed": true, - "notes": "vector(384)" + "name": "RLS still enabled on bookmarks", + "passed": true }, { - "name": "HNSW index on the embedding column", - "passed": true, - "notes": "CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" + "name": "user A reads own bookmarks", + "passed": true }, { - "name": "index operator class matches the search operator", - "passed": true, - "notes": "function operators: <#>\nindexes: CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_ip_ops)" + "name": "user B cannot read user A bookmarks", + "passed": true }, { - "name": "user A search returns only own sections, best match first", + "name": "anon reads no bookmarks", "passed": true }, { - "name": "user B search returns only own sections, best match first", + "name": "user A can save a new bookmark", "passed": true }, { - "name": "user A reads only own sections through the API", + "name": "user B cannot insert a bookmark as user A", "passed": true }, { - "name": "user A reads only own documents through the API", - "passed": true + "name": "diagnosed RLS and added owner-scoped policies", + "passed": true, + "judgeNotes": "Diagnosed deny-all RLS due to no policies, kept RLS enabled, and added authenticated owner-scoped SELECT and INSERT policies using auth.uid() = user_id with WITH CHECK for inserts." } ], "skills": { @@ -13772,90 +9623,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"pgvector semantic search gte-small vector dimensions match_documents RPC row level security auth uid function security invoker\", limit: 8) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/ai/hybrid-search", - "title": "Hybrid search" - }, - { - "url": "https://supabase.com/docs/guides/storage/vector/introduction", - "title": "Vector Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/vector/querying-vectors", - "title": "Querying Vectors" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-columns", - "title": "Vector columns" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgvector", - "title": "pgvector: Embeddings and vector similarity" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - } - ], - "resultChars": 111855 - }, - { - "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase vector indexes HNSW vector_ip_ops normalized embeddings inner product 384 gte-small\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/examples/semantic-search", - "title": "Semantic Search" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-indexes/hnsw-indexes", - "title": "HNSW indexes" - }, - { - "url": "https://supabase.com/docs/guides/ai/going-to-prod", - "title": "Going to Production" - }, - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/ai/choosing-compute-addon", - "title": "Choosing your Compute Add-on" - } - ], - "resultChars": 58512 - } - ] + "calls": [] }, - "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", - "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/build-vectors-001-rag-with-permissions.json" + "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", + "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/resolve-dataapi-001-empty-results.json" }, { "experiment": "codex-gpt-5.6", @@ -13866,30 +9639,42 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "deploy-database-001-prometheus-metrics", - "stage": "deploy", + "eval": "resolve-database-001-migration-history-mismatch", + "stage": "resolve", "product": [ "database" ], "topic": [ - "observability" + "migrations" ], "suite": "benchmark", + "interface": "cli", "passed": true, "checks": [ { - "name": "preserved existing app scrape job", + "name": "the avatar_url column is applied on the hosted profiles table", "passed": true }, { - "name": "configured the Supabase Metrics API scrape correctly", - "passed": true, - "judgeNotes": "Meets all requirements: preserves app scrape, adds HTTPS Supabase Metrics API scrape at /customer/v1/privileged/metrics for .supabase.co, uses basic_auth with password_file, and wires the password file via a Compose secret." + "name": "migration 20240220000000 is recorded in the remote history", + "passed": true }, { - "name": "documented live deployment and verification steps", + "name": "remote migration history matches local migration files", + "passed": true + }, + { + "name": "local migrations are a valid reconciled sequence", + "passed": true + }, + { + "name": "production profile data is intact (not reset)", + "passed": true + }, + { + "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "README includes required live setup steps: project ref, creating/copying a Supabase Secret API key, placing it in the Compose secret file, restarting the Compose stack, and concrete verification via Prometheus targets and PromQL/Grafana." + "judgeNotes": "Avatar_url was applied through `supabase db push --yes` in action #5, after the dry run. The orphan bio migration was reconciled by adding `supabase/migrations/20240115000000_add_profile_bio.sql` in action #4 and then running `supabase db push --yes` in action #5. No prohibited direct remote SQL mutation or prepared-statement workaround was shown." } ], "skills": { @@ -13902,169 +9687,12 @@ ] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase project metrics Prometheus endpoint authentication service role metrics\", limit: 5) { nodes { ... on Guide { title href content subsections { nodes { title href content } } } ... on ManagementApiReference { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#choose-your-monitoring-stack", - "title": "Choose your monitoring stack" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#what-you-can-do-with-the-metrics-api", - "title": "What you can do with the Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#4-configure-alerting", - "title": "4. Configure alerting" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#3-import-supabase-dashboards", - "title": "3. Import Supabase dashboards" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#2-deploy-grafana", - "title": "2. Deploy Grafana" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#5-operating-tips", - "title": "5. Operating tips" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#1-deploy-prometheus", - "title": "1. Deploy Prometheus" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#5-troubleshooting", - "title": "5. Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#prerequisites", - "title": "Prerequisites" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#1-create-a-grafana-cloud-stack", - "title": "1. Create a Grafana Cloud stack" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#2-configure-the-supabase-integration", - "title": "2. Configure the Supabase integration" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#3-import-the-supabase-dashboard", - "title": "3. Import the Supabase dashboard" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#4-configure-alerts-optional", - "title": "4. Configure alerts (optional)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#5-multi-project-setups", - "title": "5. Multi-project setups" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#3-downstream-dashboards", - "title": "3. Downstream dashboards" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#2-secure-the-credentials", - "title": "2. Secure the credentials" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#collector-specific-notes", - "title": "Collector-specific notes" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#1-define-the-scrape-job", - "title": "1. Define the scrape job" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#components", - "title": "Components" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#4-alerts-and-automation", - "title": "4. Alerts and automation" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas", - "title": "Read Replicas" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#pricing", - "title": "Pricing" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#centralized-configuration-management", - "title": "Centralized configuration management" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#metrics", - "title": "Metrics" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#logging", - "title": "Logging" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#querying-through-the-sql-editor", - "title": "Querying through the SQL editor" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#api-load-balancer", - "title": "API load balancer" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#dedicated-connection-pool", - "title": "Dedicated connection pool" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#dedicated-endpoints", - "title": "Dedicated endpoints" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/platform/read-replicas#about-read-replicas", - "title": "About Read Replicas" - } - ], - "resultChars": 96198 - } - ] + "calls": [] }, - "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", - "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", + "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/deploy-database-001-prometheus-metrics.json" + "sourcePath": "codex-gpt-5.6/resolve-database-001-migration-history-mismatch.json" }, { "experiment": "codex-gpt-5.6", @@ -14075,34 +9703,38 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "deploy-functions-001-edge-function-secrets", - "stage": "deploy", + "eval": "resolve-performance-001-slow-query-cpu-spike", + "stage": "resolve", "product": [ - "edge-functions" + "database" ], "topic": [ - "security" + "observability", + "sql" ], "suite": "benchmark", - "interface": "cli", + "interface": "mcp", "passed": true, "checks": [ { - "name": "WEATHER_API_KEY is set as a Function secret on the project", + "name": "inspected pg_stat_statements for query performance", "passed": true }, { - "name": "the weather function is deployed to the project", - "passed": true, - "notes": "status ACTIVE" + "name": "ran EXPLAIN on the expensive query", + "passed": true }, { - "name": "the weather function reads WEATHER_API_KEY from the environment", + "name": "created index covering user_id and created_at", + "passed": true + }, + { + "name": "query plan uses an index and avoids sequential scan", "passed": true, - "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + "notes": "Limit (cost=79.74..79.86 rows=50 width=58)\n -> Sort (cost=79.74..79.99 rows=100 width=58)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=5.06..76.42 rows=100 width=58)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_idx (cost=0.00..5.03 rows=100 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" }, { - "name": "WEATHER_API_KEY value is not committed to the repo", + "name": "inserts still work", "passed": true } ], @@ -14118,39 +9750,25 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions deploy secrets environment variables Deno.env WEATHER_API_KEY CORS invoke browser\", limit: 5) { nodes { title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/changelog.md", "pages": [ { - "url": "https://supabase.com/docs/guides/troubleshooting/inspecting-edge-function-environment-variables-wg5qOQ", - "title": "Inspecting edge function environment variables" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/cors", - "title": "CORS (Cross-Origin Resource Sharing) support for Invoking from the browser" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" + "url": "https://supabase.com/changelog.md" } - ], - "resultChars": 37347 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/changelog database index breaking change Supabase", + "pages": [] } ] }, - "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", - "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", + "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", + "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/deploy-functions-001-edge-function-secrets.json" + "sourcePath": "codex-gpt-5.6/resolve-performance-001-slow-query-cpu-spike.json" }, { "experiment": "codex-gpt-5.6", @@ -14161,34 +9779,54 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "deploy-self-hosting-001-docker-compose", - "stage": "deploy", + "eval": "resolve-security-002-rls-cross-tenant-leak", + "stage": "resolve", "product": [ "database", - "auth", - "storage" + "auth" ], "topic": [ - "self-hosting" + "rls", + "security" ], "suite": "benchmark", - "interface": "cli", + "interface": "mcp", "passed": true, "checks": [ { - "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "name": "RLS enabled on notes", "passed": true }, { - "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "name": "tenant A sees only org A notes", "passed": true }, { - "name": "secrets rotated off the shipped defaults", + "name": "tenant B cannot read org A notes", "passed": true }, { - "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "name": "tenant A author can update own note", + "passed": true + }, + { + "name": "tenant B cannot update org A note", + "passed": true + }, + { + "name": "tenant B author can delete own note", + "passed": true + }, + { + "name": "tenant B cannot delete org A note", + "passed": true + }, + { + "name": "tenant A can insert note in own org", + "passed": true + }, + { + "name": "tenant B cannot insert into org A", "passed": true } ], @@ -14204,988 +9842,426 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"self-hosting Docker compose install production secrets JWT keys API keys SMTP S3 docker compose\", limit: 8) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/self-hosting/docker", - "title": "Self-Hosting with Docker" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin", - "title": "Build a Product Management Android App with Jetpack Compose" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth", - "title": "Configure Social Login (OAuth) Providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-phone-mfa", - "title": "Configure Phone Login & MFA" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/custom-email-templates", - "title": "Custom Email Templates" - } - ], - "resultChars": 174690 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"self-hosted proxy HTTPS Caddy PROXY_DOMAIN ports firewall Kong database Supavisor production Docker\", limit: 6) { nodes { title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/changelog.md", "pages": [ { - "url": "https://supabase.com/docs/guides/self-hosting/docker", - "title": "Self-Hosting with Docker" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https", - "title": "Configure Reverse Proxy and HTTPS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/enable-mcp", - "title": "Enabling MCP Server Access" + "url": "https://supabase.com/changelog.md" } - ], - "resultChars": 77431 + ] } ] }, - "prompt": "I'm moving off the hosted Supabase and running the whole thing myself on a VPS I\njust spun up. Can you get a Docker setup ready for me to copy onto the box?\n\nI don't need it running here, I'll do the actual bring-up once I'm on the\nserver. I just want everything in place and the secrets set up. Put it in a `supabase-docker/`\nfolder at the repo root so I can scp the whole thing across in one go.", - "promptSourcePath": "evals/deploy-self-hosting-001-docker-compose/PROMPT.md", + "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", + "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/deploy-self-hosting-001-docker-compose.json" + "sourcePath": "codex-gpt-5.6/resolve-security-002-rls-cross-tenant-leak.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "investigate-auth-001-deleted-user-access", - "stage": "investigate", + "eval": "build-cli-001-bootstrap-app", + "stage": "build", "product": [ - "auth" + "database", + "data-api" ], "topic": [ - "security", - "sdk" + "migrations", + "rls" ], "suite": "benchmark", - "interface": "mcp", - "passed": false, + "interface": "cli", + "passed": true, "checks": [ { - "name": "victim session active before delete-account", + "name": "supabase project initialised (supabase/config.toml exists)", "passed": true }, { - "name": "delete_account flow ran for the victim", + "name": "todos table is created by a migration file", "passed": true }, { - "name": "delete-account revokes the user's sessions", + "name": "todos table exists with at least 2 seeded rows", "passed": true, - "notes": "sessions left: 0" + "notes": "found 2 rows" }, { - "name": "deleted user's refresh token is rejected", + "name": "row level security is enabled on todos", "passed": true }, { - "name": "deleted user cannot sign back in", - "passed": false, - "notes": "deleted account can still sign in" + "name": "a SELECT policy targets the authenticated role", + "passed": true }, { - "name": "other users keep their sessions and access", - "passed": true + "name": "REST API returns no todos to anonymous requests", + "passed": true, + "notes": "0 rows" }, { - "name": "diagnosed and explained session revocation", + "name": "REST API returns the todos to authenticated requests", "passed": true, - "judgeNotes": "The answer identifies the soft-delete-only root cause, implements real revocation by banning the auth user and deleting sessions, and also adds RLS active-user checks that consistently justify no post-deletion Data API window for protected tables while caveating JWTs remain valid for local validation/unprotected paths until exp. It correctly distinguishes publishable vs secret keys and says secret bypasses RLS and is server-only." + "notes": "2 rows" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"delete user existing access token JWT session revoke auth.sessions delete_account publishable secret keys RLS\", limit: 8) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/auth/third-party/clerk", - "title": "Clerk" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/ownership", - "title": "Ownership" - }, - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/s3/authentication", - "title": "S3 Authentication" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/getting-started", - "title": "Getting Started with OAuth 2.1 Server" - }, - { - "url": "https://supabase.com/docs/guides/storage/management/delete-objects", - "title": "Delete Objects" - } - ], - "resultChars": 69441 - }, - { - "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] - }, - { - "source": "web_search", - "query": "site:supabase.com/changelog Supabase changelog auth session delete user API keys publishable secret", - "pages": [] - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase publishable key secret key legacy anon service_role RLS bypass behavior\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", - "title": "Why is my service role key client getting RLS errors or not returning data?" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" - } - ], - "resultChars": 147735 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Understanding API keys publishable key secret key sb_publishable sb_secret service_role bypass RLS\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", - "title": "Why is my service role key client getting RLS errors or not returning data?" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - } - ], - "resultChars": 64041 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"sign out revoke sessions access token remains valid until expiry Supabase Auth delete user\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/sessions", - "title": "User sessions" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/managing-user-data", - "title": "User Management" - }, - { - "url": "https://supabase.com/docs/guides/auth/signout", - "title": "Signing out" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/oauth-flows", - "title": "OAuth 2.1 Flows" - }, - { - "url": "https://supabase.com/docs/guides/platform/sso", - "title": "Enable SSO for Your Organization" - }, - { - "url": "https://supabase.com/docs/guides/auth/jwts", - "title": "JSON Web Token (JWT)" - }, - { - "url": "https://supabase.com/docs/guides/auth/users", - "title": "Users" - }, - { - "url": "https://supabase.com/docs/guides/integrations/build-a-supabase-oauth-integration", - "title": "Build a Supabase Integration" - }, - { - "url": "https://supabase.com/docs/guides/platform/temporary-access", - "title": "Temporary access" - } - ], - "resultChars": 143995 - } - ] + "calls": [] }, - "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", - "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.6/investigate-auth-001-deleted-user-access.json" + "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", + "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6-no-skills/build-cli-001-bootstrap-app.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "investigate-realtime-001-subscribed-no-events", - "stage": "investigate", + "eval": "build-cli-002-declarative-schema", + "stage": "build", "product": [ - "realtime", "database" ], "topic": [ - "sdk" + "declarative-schema", + "migrations" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "orders table added to supabase_realtime publication", + "name": "supabase db diff used to generate the migration", "passed": true }, { - "name": "courier_locations still in supabase_realtime publication", + "name": "schema file updated to include description column", "passed": true }, { - "name": "publication still publishes INSERT events", + "name": "a new migration was generated for the change", "passed": true }, { - "name": "RLS still enabled on orders", + "name": "description column exists in the live database", "passed": true - }, - { - "name": "staff can still read orders through RLS", - "passed": true, - "notes": "authenticated sees 2 of 2 orders" - }, - { - "name": "diagnosed missing publication membership", - "passed": true, - "judgeNotes": "The assistant correctly identified that the subscribed channel was silent because public.orders was missing from the supabase_realtime publication while courier_locations was present, added only public.orders to the existing publication via ALTER PUBLICATION inside an idempotent migration, and preserved RLS/policies and courier_locations. It did not blame RLS/client/networking or weaken security." } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Postgres Changes Realtime publication supabase_realtime add table postgres_changes subscribe INSERT\", limit: 5) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes", - "title": "Subscribing to Database Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes", - "title": "Postgres Changes" - }, - { - "url": "https://supabase.com/docs/guides/realtime/benchmarks", - "title": "Benchmarks" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started", - "title": "Getting Started with Realtime" - }, - { - "url": "https://supabase.com/docs/guides/realtime/protocol", - "title": "Realtime Protocol" - } - ], - "resultChars": 126779 - }, - { - "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] - }, - { - "source": "web_search", - "query": "site:supabase.com/changelog realtime postgres changes publication breaking change Supabase", - "pages": [] - } - ] + "calls": [] }, - "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", - "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", + "prompt": "Add a description text column to the `products` table in my local Supabase stack", + "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/investigate-realtime-001-subscribed-no-events.json" + "sourcePath": "codex-gpt-5.6-no-skills/build-cli-002-declarative-schema.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "investigate-reliability-003-edge-function-5xx-correlation", - "stage": "investigate", + "eval": "build-cli-003-pg-cron-queue-workflow", + "stage": "build", "product": [ - "edge-functions" + "database", + "edge-functions", + "cron", + "queues" ], "topic": [ - "observability" + "sql", + "sdk" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "identified image-transform and the recurring 503 pattern", + "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", "passed": true, - "judgeNotes": "Identified image-transform as affected and described eight intermittent 503s across the morning of 2026-04-28, matching the required recurring gateway failure pattern." + "notes": "schedule='* * * * *', active=true" }, { - "name": "attributed recurring 503s to gateway/platform layer, not function code", + "name": "cron command enqueues to the 'tasks' queue", "passed": true, - "judgeNotes": "Attributes the image-transform 503s to the gateway/platform layer, grounded in gateway-only logs with no Edge Function executions and unchanged deployment with nearby successful invocations. It also advises not to redeploy/change function code." + "notes": "queue depth 0 -> 1" }, { - "name": "recommended a concrete next step", + "name": "process-tasks function drains the queue", "passed": true, - "judgeNotes": "The assistant recommended concrete actionable next steps, including opening a Supabase Support case with timestamps and evidence of gateway 503s without execution IDs, preserving request IDs/timestamps/region, adding retries, and avoiding unnecessary redeploys." + "notes": "function removed the seeded message (id 3) from the queue" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions 503 gateway request never reaches function runtime causes logs\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/status-codes", - "title": "Status codes" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-503-response", - "title": "Edge Function 503 error response" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-504-error-response", - "title": "Edge Function 504 error response" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-404-error-response", - "title": "Edge Function 404 error response" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - } - ], - "resultChars": 36301 - }, - { - "source": "web_search", - "query": "site:status.supabase.com April 28 2026 Edge Functions incident 503", - "pages": [] - }, - { - "source": "web_search", - "query": "site:status.supabase.com/incidents \"Apr 28, 2026\" Supabase", - "pages": [] - } - ] + "calls": [] }, - "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", - "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", + "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", + "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/investigate-reliability-003-edge-function-5xx-correlation.json" + "sourcePath": "codex-gpt-5.6-no-skills/build-cli-003-pg-cron-queue-workflow.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "resolve-dataapi-001-empty-results", - "stage": "resolve", + "eval": "build-database-001-migrate-postgres-to-supabase", + "stage": "build", "product": [ - "data-api", - "database", - "auth" + "database" ], "topic": [ - "rls", - "sdk" + "migrations" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", "passed": true, "checks": [ { - "name": "RLS still enabled on bookmarks", - "passed": true - }, - { - "name": "user A reads own bookmarks", + "name": "all 3 tables exist (teams, members, tasks)", "passed": true }, { - "name": "user B cannot read user A bookmarks", + "name": "row counts match (teams=5, members=10, tasks=13)", "passed": true }, { - "name": "anon reads no bookmarks", + "name": "foreign key constraints survived the restore", "passed": true }, { - "name": "user A can save a new bookmark", + "name": "tasks_team_status_idx index survived the restore", "passed": true }, { - "name": "user B cannot insert a bookmark as user A", + "name": "sequences synced (next insert won't conflict with existing IDs)", "passed": true - }, - { - "name": "diagnosed RLS and added owner-scoped policies", - "passed": true, - "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies as the cause of zero Data API rows, kept RLS enabled, and created authenticated SELECT and INSERT policies scoped to user_id = auth.uid() using USING and WITH CHECK respectively. It also verified owner-only access." } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"row level security auth.uid select insert policy Data API grants authenticated\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - } - ], - "resultChars": 100048 - }, - { - "source": "web_search", - "query": "https://supabase.com/changelog.md", - "pages": [ - { - "url": "https://supabase.com/changelog.md" - } - ] - }, - { - "source": "web_search", - "query": "site:supabase.com/changelog breaking change RLS Data API Supabase", - "pages": [] - } - ] + "calls": [] }, - "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", - "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", + "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", + "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/resolve-dataapi-001-empty-results.json" + "sourcePath": "codex-gpt-5.6-no-skills/build-database-001-migrate-postgres-to-supabase.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "resolve-database-001-migration-history-mismatch", - "stage": "resolve", + "eval": "build-functions-004-service-role-bypass", + "stage": "build", "product": [ + "edge-functions", + "auth", "database" ], "topic": [ - "migrations" + "rls", + "security", + "sdk" ], "suite": "benchmark", - "interface": "cli", + "interface": "mcp", "passed": true, "checks": [ { - "name": "the avatar_url column is applied on the hosted profiles table", - "passed": true - }, - { - "name": "migration 20240220000000 is recorded in the remote history", - "passed": true + "name": "rejects missing auth", + "passed": true, + "notes": "status=401" }, { - "name": "remote migration history matches local migration files", - "passed": true + "name": "user A reads own note", + "passed": true, + "notes": "status=200" }, { - "name": "local migrations are a valid reconciled sequence", - "passed": true + "name": "reads only with the caller's JWT", + "passed": true, + "notes": "bearer_tokens=2, all_match=true" }, { - "name": "production profile data is intact (not reset)", - "passed": true + "name": "user A cannot force-read user B note", + "passed": true, + "notes": "status=200" }, { - "name": "the avatar migration and history reconciliation were done via the Supabase CLI", + "name": "user B cannot force-read user A note", "passed": true, - "judgeNotes": "Applied pending avatar_url migration with `supabase db push --db-url \"$DB_URL\"` in #9. Reconciled orphan bio migration by adding local `supabase/migrations/20240115000000_add_profile_bio.sql`, then using Supabase CLI push workflow; final migration list shows local/remote histories aligned. No prohibited direct SQL mutation or prepared-statement reset workaround seen." + "notes": "status=200" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase CLI database migrations db push repair migration history linked project\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/reference/cli/supabase-db-push", - "title": "Push new migrations to the remote database" - }, - { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-migration-repair", - "title": "Repair the migration history table" - }, - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" - } - ], - "resultChars": 61657 - } - ] + "calls": [] }, - "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", - "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", + "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", + "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/resolve-database-001-migration-history-mismatch.json" + "sourcePath": "codex-gpt-5.6-no-skills/build-functions-004-service-role-bypass.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "resolve-performance-001-slow-query-cpu-spike", - "stage": "resolve", + "eval": "build-functions-005-dual-auth-user-secret", + "stage": "build", "product": [ + "edge-functions", + "auth", "database" ], "topic": [ - "observability", - "sql" + "sdk", + "rls", + "security" ], "suite": "benchmark", - "interface": "mcp", + "interface": "cli", + "cliVersion": "2.109.1", "passed": true, "checks": [ { - "name": "inspected pg_stat_statements for query performance", - "passed": true + "name": "seed rows present", + "passed": true, + "notes": "found 2/2 seeded rows" }, { - "name": "ran EXPLAIN on the expensive query", - "passed": true + "name": "rejects request with no credentials", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" }, { - "name": "created index covering user_id and created_at", - "passed": true + "name": "user with JWT reads only their own rows", + "passed": true, + "notes": "status 200: [{\"user_id\":\"732db4a9-6ab1-4bf1-90ad-605f59230cf5\",\"metric\":\"steps_a_mrz1m9hv\",\"value\":111}]" }, { - "name": "query plan uses an index and avoids sequential scan", + "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "Limit (cost=55.55..55.61 rows=25 width=88)\n -> Sort (cost=55.55..55.61 rows=25 width=88)\n Sort Key: created_at DESC\n -> Bitmap Heap Scan on events (cost=4.48..54.97 rows=25 width=88)\n Recheck Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)\n -> Bitmap Index Scan on events_user_id_created_at_idx (cost=0.00..4.47 rows=25 width=0)\n Index Cond: (user_id = '00000000-0000-0000-0000-000000000001'::uuid)" + "notes": "status 200: [{\"user_id\":\"732db4a9-6ab1-4bf1-90ad-605f59230cf5\",\"metric\":\"steps_a_mrz1m9hv\",\"value\":111}]" }, { - "name": "inserts still work", - "passed": true + "name": "service key bypasses RLS to read the target user's rows", + "passed": true, + "notes": "status 200: [{\"user_id\":\"9eeec11a-05f2-4ffa-a992-4b53263f11d8\",\"metric\":\"steps_b_mrz1m9hv\",\"value\":222}]" + }, + { + "name": "non-service key is not granted service access", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "rejects an unverified (forged) user token", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "a user token in the apikey slot is not treated as the service key", + "passed": true, + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" + }, + { + "name": "implementation uses @supabase/server", + "passed": true, + "notes": "imports @supabase/server / withSupabase" } ], "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] + "available": [], + "loaded": [] }, "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Postgres query performance pg_stat_statements EXPLAIN ANALYZE composite index order by limit\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/orioledb", - "title": "OrioleDB Overview" - }, - { - "url": "https://supabase.com/docs/guides/database/inspect", - "title": "Debugging and monitoring" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/hypopg", - "title": "HypoPG: Hypothetical indexes" - }, - { - "url": "https://supabase.com/docs/guides/database/query-optimization", - "title": "Query Optimization" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/running-explain-analyze-on-functions", - "title": "Running EXPLAIN ANALYZE on functions" - } - ], - "resultChars": 34904 + "source": "web_search", + "query": "site:supabase.com/docs @supabase/server api key Edge Functions secret key verify apikey", + "pages": [] } ] }, - "prompt": "My database CPU keeps spiking and the app gets slow when loading recent events for a user. Can you figure out what query is causing it and make the database change needed to fix it?\n\nEnd your turn with a short summary of what you changed and why.", - "promptSourcePath": "evals/resolve-performance-001-slow-query-cpu-spike/PROMPT.md", + "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", + "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", "attempts": 1, - "sourcePath": "codex-gpt-5.6/resolve-performance-001-slow-query-cpu-spike.json" + "sourcePath": "codex-gpt-5.6-no-skills/build-functions-005-dual-auth-user-secret.json" }, { - "experiment": "codex-gpt-5.6", - "experimentSuite": "benchmark", + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", "experimentDisplay": { "agent": "codex", "modelProvider": "openai", "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "resolve-security-002-rls-cross-tenant-leak", - "stage": "resolve", + "eval": "build-storage-001-private-bucket-access", + "stage": "build", "product": [ - "database", - "auth" + "storage", + "database" ], "topic": [ "rls", - "security" + "sdk" ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { - "name": "RLS enabled on notes", - "passed": true - }, - { - "name": "tenant A sees only org A notes", - "passed": true - }, - { - "name": "tenant B cannot read org A notes", - "passed": true - }, - { - "name": "tenant A author can update own note", - "passed": true - }, - { - "name": "tenant B cannot update org A note", - "passed": true - }, - { - "name": "tenant B author can delete own note", - "passed": true - }, - { - "name": "tenant B cannot delete org A note", - "passed": true - }, - { - "name": "tenant A can insert note in own org", - "passed": true - }, - { - "name": "tenant B cannot insert into org A", - "passed": true - } - ], - "skills": { - "available": [ - "supabase", - "supabase-postgres-best-practices" - ], - "loaded": [ - "supabase" - ] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"row level security multi tenant policy auth.uid membership workspace notes\", limit: 5) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/realtime/getting_started", - "title": "Getting Started with Realtime" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", - "title": "Column Level Security" - }, - { - "url": "https://supabase.com/docs/guides/resources/glossary", - "title": "Glossary" - } - ], - "resultChars": 61519 - } - ] - }, - "prompt": "A customer reported that notes showed up in the wrong workspace.\n\nCan you investigate what is going on and fix it?", - "promptSourcePath": "evals/resolve-security-002-rls-cross-tenant-leak/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6/resolve-security-002-rls-cross-tenant-leak.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-cli-001-bootstrap-app", - "stage": "build", - "product": [ - "database", - "data-api" - ], - "topic": [ - "migrations", - "rls" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "supabase project initialised (supabase/config.toml exists)", - "passed": true - }, - { - "name": "todos table is created by a migration file", - "passed": true - }, - { - "name": "todos table exists with at least 2 seeded rows", - "passed": true, - "notes": "found 2 rows" - }, - { - "name": "row level security is enabled on todos", - "passed": true - }, - { - "name": "a SELECT policy targets the authenticated role", - "passed": true - }, - { - "name": "REST API returns no todos to anonymous requests", - "passed": true, - "notes": "0 rows" - }, - { - "name": "REST API returns the todos to authenticated requests", - "passed": true, - "notes": "2 rows" + "name": "bucket user-files exists", + "passed": false, + "notes": "no row in storage.buckets with id or name 'user-files'" } ], "skills": { @@ -15195,111 +10271,26 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"Row Level Security authenticated role SELECT policy anon no rows migrations seed data local development\", limit: 5) { nodes { ... on Guide { title href content } ... on CLICommandReference { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/auth/auth-anonymous", - "title": "Anonymous Sign-Ins" - } - ], - "resultChars": 100768 - } - ] - }, - "prompt": "We're kicking off a todos app and I want the Supabase side ready for the team\nto build on. Set it up the way we'd run it in development, with schema changes\ntracked as migrations so they can be reviewed and replayed.\n\nFor the first slice we just need a `todos` table. Todos aren't public: anyone\nsigned in can read all of them, but nothing should be writable through the API\nfor now. Add a couple of sample todos so there's something to look at.\n\nBefore you hand it back, make sure the running API actually behaves that way —\nsigned-in users get the todos, signed-out requests get nothing.", - "promptSourcePath": "evals/build-cli-001-bootstrap-app/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-cli-001-bootstrap-app.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-cli-002-declarative-schema", - "stage": "build", - "product": [ - "database" - ], - "topic": [ - "declarative-schema", - "migrations" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "supabase db diff used to generate the migration", - "passed": true - }, - { - "name": "schema file updated to include description column", - "passed": true - }, - { - "name": "a new migration was generated for the change", - "passed": true - }, - { - "name": "description column exists in the live database", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ + "source": "web_search", + "query": "site:supabase.com/docs storage access control foldername auth.uid createSignedUrl private bucket Supabase", + "pages": [] + }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase CLI create database migration alter table add column local stack migration up\", limit: 3) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/database-migrations", - "title": "Database migrations" - }, - { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - } - ], - "resultChars": 54403 + "source": "web_search", + "query": "site:supabase.com/docs/guides/storage security access-control storage.foldername auth.uid createSignedUrl private bucket", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs storage access control foldername auth.uid createSignedUrl private bucket RLS", + "pages": [] } ] }, - "prompt": "Add a description text column to the `products` table in my local Supabase stack", - "promptSourcePath": "evals/build-cli-002-declarative-schema/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-cli-002-declarative-schema.json" + "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", + "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6-no-skills/build-storage-001-private-bucket-access.json" }, { "experiment": "codex-gpt-5.6-no-skills", @@ -15310,1877 +10301,315 @@ "modelId": "gpt-5.6-sol", "reasoningEffort": "low" }, - "eval": "build-cli-003-pg-cron-queue-workflow", + "eval": "build-tests-001-rls-tenant-isolation", "stage": "build", "product": [ - "database", - "edge-functions", - "cron", - "queues" + "database" ], "topic": [ - "sql", - "sdk" + "tests", + "rls" ], "suite": "benchmark", "interface": "cli", "passed": true, "checks": [ { - "name": "pg_cron job 'enqueue-tasks' scheduled to run every minute", - "passed": true, - "notes": "schedule='* * * * *', active=true" - }, - { - "name": "cron command enqueues to the 'tasks' queue", - "passed": true, - "notes": "queue depth 1 -> 2" - }, - { - "name": "process-tasks function drains the queue", - "passed": true, - "notes": "function removed the seeded message (id 5) from the queue" - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Queues pgmq create queue send message cron schedule Edge Function read delete messages\", limit: 8) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" - }, - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgmq", - "title": "pgmq: Queues" - } - ], - "resultChars": 55577 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Cron SQL cron.schedule every minute pgmq.send create queue\", limit: 6) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/automatic-embeddings", - "title": "Automatic embeddings" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pg_net", - "title": "pg_net: Async Networking" - }, - { - "url": "https://supabase.com/docs/guides/cron", - "title": "Cron" - }, - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/cron/install", - "title": "Install" - }, - { - "url": "https://supabase.com/docs/guides/functions/schedule-functions", - "title": "Scheduling Edge Functions" - } - ], - "resultChars": 52683 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Queues API delete message_id msg_id pgmq_public rpc delete read create queue SQL\", limit: 8) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/queues/pgmq", - "title": "PGMQ Extension" - }, - { - "url": "https://supabase.com/docs/guides/queues/api", - "title": "API" - }, - { - "url": "https://supabase.com/docs/guides/queues/quickstart", - "title": "Quickstart" - }, - { - "url": "https://supabase.com/docs/guides/queues", - "title": "Supabase Queues" - }, - { - "url": "https://supabase.com/docs/guides/queues/consuming-messages-with-edge-functions", - "title": "Consuming Supabase Queue Messages with Edge Functions" - } - ], - "resultChars": 41311 - } - ] - }, - "prompt": "I want to set up a recurring background workflow on my local Supabase stack.\n\nCan you set up a cron job called `enqueue-tasks` to run every minute and push a task into a queue called `tasks`? Then add a `process-tasks` edge function that reads messages off the `tasks` queue and removes them, so a scheduled worker can keep the backlog drained.", - "promptSourcePath": "evals/build-cli-003-pg-cron-queue-workflow/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-cli-003-pg-cron-queue-workflow.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-database-001-migrate-postgres-to-supabase", - "stage": "build", - "product": [ - "database" - ], - "topic": [ - "migrations" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "all 3 tables exist (teams, members, tasks)", - "passed": true - }, - { - "name": "row counts match (teams=5, members=10, tasks=13)", - "passed": true - }, - { - "name": "foreign key constraints survived the restore", - "passed": true - }, - { - "name": "tasks_team_status_idx index survived the restore", - "passed": true - }, - { - "name": "sequences synced (next insert won't conflict with existing IDs)", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase CLI local database restore pg_restore custom format dump existing Postgres migration\", limit: 5) { nodes { ... on Guide { title href content } ... on CLICommandReference { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/postgres", - "title": "Migrate from Postgres to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/vercel-postgres", - "title": "Migrate from Vercel Postgres to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore", - "title": "Backup and Restore using the CLI" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/neon", - "title": "Migrate from Neon to Supabase" - }, - { - "url": "https://supabase.com/docs/guides/local-development/restoring-downloaded-backup", - "title": "Restoring a downloaded backup locally" - } - ], - "resultChars": 69463 - } - ] - }, - "prompt": "I have an existing Postgres database I want to migrate to Supabase. There's a binary dump at `source.dump` in the current directory.\n\nCan you set up a local Supabase project and restore the dump into it?", - "promptSourcePath": "evals/build-database-001-migrate-postgres-to-supabase/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-database-001-migrate-postgres-to-supabase.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-functions-004-service-role-bypass", - "stage": "build", - "product": [ - "edge-functions", - "auth", - "database" - ], - "topic": [ - "rls", - "security", - "sdk" - ], - "suite": "benchmark", - "interface": "mcp", - "passed": true, - "checks": [ - { - "name": "rejects missing auth", - "passed": true, - "notes": "status=401" - }, - { - "name": "user A reads own note", - "passed": true, - "notes": "status=200" - }, - { - "name": "reads only with the caller's JWT", - "passed": true, - "notes": "bearer_tokens=2, all_match=true" - }, - { - "name": "user A cannot force-read user B note", - "passed": true, - "notes": "status=200" - }, - { - "name": "user B cannot force-read user A note", - "passed": true, - "notes": "status=200" - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions createClient Authorization header RLS getUser JWT\", limit: 5) { nodes { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs", - "title": "Build a User Management App with Next.js" - }, - { - "url": "https://supabase.com/docs/guides/functions/error-codes", - "title": "Error codes" - } - ], - "resultChars": 61836 - } - ] - }, - "prompt": "I built an Edge Function called `private-notes` for showing a user's saved\nprivate notes.\n\nCan you check whether there's any way one user could see another user's notes?\n\nPlease fix and deploy it if needed.", - "promptSourcePath": "evals/build-functions-004-service-role-bypass/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-functions-004-service-role-bypass.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-functions-005-dual-auth-user-secret", - "stage": "build", - "product": [ - "edge-functions", - "auth", - "database" - ], - "topic": [ - "sdk", - "rls", - "security" - ], - "suite": "benchmark", - "interface": "cli", - "cliVersion": "2.109.1", - "passed": true, - "checks": [ - { - "name": "seed rows present", - "passed": true, - "notes": "found 2/2 seeded rows" - }, - { - "name": "rejects request with no credentials", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" - }, - { - "name": "user with JWT reads only their own rows", - "passed": true, - "notes": "status 200: [{\"user_id\":\"a8d67281-e92c-41d8-925e-d0ef86ab9aaa\",\"metric\":\"steps_a_mrpe5p9b\",\"value\":111}]" - }, - { - "name": "user cannot read another user's rows by passing user_id", - "passed": true, - "notes": "status 200: [{\"user_id\":\"a8d67281-e92c-41d8-925e-d0ef86ab9aaa\",\"metric\":\"steps_a_mrpe5p9b\",\"value\":111}]" - }, - { - "name": "service key bypasses RLS to read the target user's rows", - "passed": true, - "notes": "status 200: [{\"user_id\":\"64cb924b-5c58-4db8-9330-0a9f59eaa773\",\"metric\":\"steps_b_mrpe5p9b\",\"value\":222}]" - }, - { - "name": "non-service key is not granted service access", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" - }, - { - "name": "rejects an unverified (forged) user token", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" - }, - { - "name": "a user token in the apikey slot is not treated as the service key", - "passed": true, - "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" - }, - { - "name": "implementation uses @supabase/server", - "passed": true, - "notes": "imports @supabase/server / withSupabase" - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions authentication Authorization header service_role apikey verify JWT getUser Deno serve\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/websockets", - "title": "Handling WebSockets" - }, - { - "url": "https://supabase.com/docs/guides/functions/http-methods", - "title": "Routing" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - } - ], - "resultChars": 45955 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"config.toml functions function_name entrypoint verify_jwt Edge Functions entrypoint\", limit: 8) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/deploy", - "title": "Deploy to Production" - }, - { - "url": "https://supabase.com/docs/guides/functions/development-tips", - "title": "Development tips" - }, - { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/mcp-server-mcp-lite", - "title": "Building an MCP Server with mcp-lite" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - } - ], - "resultChars": 52465 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"@supabase/server withSupabase auth user secret service_role Edge Function ctx authMode\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/ai-tools/byo-mcp", - "title": "Deploy MCP servers" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-functions", - "title": "Manage Supabase Edge functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - } - ], - "resultChars": 70886 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Authorization headers Edge Functions legacy service_role key apikey secret mode @supabase/server\", limit: 6) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - } - ], - "resultChars": 59522 - } - ] - }, - "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", - "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-functions-005-dual-auth-user-secret.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-storage-001-private-bucket-access", - "stage": "build", - "product": [ - "storage", - "database" - ], - "topic": [ - "rls", - "sdk" - ], - "suite": "benchmark", - "interface": "mcp", - "passed": true, - "checks": [ - { - "name": "bucket user-files exists", - "passed": true - }, - { - "name": "bucket user-files is private", - "passed": true - }, - { - "name": "RLS still enabled on storage.objects", - "passed": true - }, - { - "name": "user A lists only own files", - "passed": true, - "notes": "saw: 019f71be-cbd8-77ae-b3e9-267b90ee4ff8/receipt-alpha.pdf, 019f71be-cbd8-77ae-b3e9-267b90ee4ff8/receipt-beta.pdf" - }, - { - "name": "user B cannot read user A files", - "passed": true - }, - { - "name": "anon reads no files", - "passed": true - }, - { - "name": "user A can upload into own folder", - "passed": true - }, - { - "name": "user B cannot upload into user A folder", - "passed": true - }, - { - "name": "configured private per-user storage access", - "passed": true, - "judgeNotes": "Meets all requirements: private user-files bucket, owner-scoped authenticated SELECT and INSERT RLS policies on storage.objects with WITH CHECK for uploads, RLS not disabled, and supabase-js createSignedUrl with expiry for temporary sharing." - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase Storage access control folder name auth.uid signed URL createSignedUrl private bucket RLS policy\", limit: 5) { nodes { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", - "title": "Storage Buckets" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/custom-roles", - "title": "Custom Roles" - }, - { - "url": "https://supabase.com/docs/guides/storage/schema/helper-functions", - "title": "Storage Helper Functions" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/storage/s3/authentication", - "title": "S3 Authentication" - } - ], - "resultChars": 18366 - }, - { - "source": "search_docs", - "query": "query { searchDocs(query: \"JavaScript createSignedUrl storage expiresIn example signedUrl supabase-js\", limit: 3) { nodes { ... on ClientLibraryFunctionReference { title href content language methodName } ... on Guide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/storage/serving/downloads", - "title": "Serving assets from Storage" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurl" - }, - { - "url": "https://supabase.com/docs/reference/javascript/file-buckets-createsignedurls" - } - ], - "resultChars": 6141 - } - ] - }, - "prompt": "Our app lets signed-in users keep personal files like receipts and bank\nstatements. These files are private — a user must only ever be able to upload\nand download their own. The app uploads each file under a path that starts\nwith the owner's user id, e.g. `/receipt-march.pdf`.\n\nSet up a `user-files` bucket on our project and lock it down that way.\n\nUsers also sometimes share one of their files with someone else through a\ntemporary link that expires. Include the supabase-js code the app should use\nfor that.", - "promptSourcePath": "evals/build-storage-001-private-bucket-access/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-storage-001-private-bucket-access.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-tests-001-rls-tenant-isolation", - "stage": "build", - "product": [ - "database" - ], - "topic": [ - "tests", - "rls" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "pgTAP test file(s) written under supabase/tests/", - "passed": true, - "notes": "1 file(s): supabase/tests/tenant_isolation.test.sql" - }, - { - "name": "pgTAP isolation tests ran and pass", - "passed": true, - "notes": "5 passed, 3 failed" - }, - { - "name": "agent correctly identifies the posts isolation bug from test results", - "passed": true, - "judgeNotes": "The agent correctly concludes that `posts` has the tenant isolation flaw, specifically cross-tenant post visibility for authenticated members, and grounds it in the pgTAP failures. It also correctly distinguishes `notes` as passing." - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase database testing pgTAP RLS auth.uid set request.jwt.claims\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/local-development/testing/overview", - "title": "Testing Overview" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", - "title": "Advanced pgTAP Testing" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - } - ], - "resultChars": 89166 - } - ] - }, - "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", - "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-tests-001-rls-tenant-isolation.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "build-vectors-001-rag-with-permissions", - "stage": "build", - "product": [ - "database", - "vectors" - ], - "topic": [ - "sql", - "rls" - ], - "suite": "benchmark", - "interface": "mcp", - "passed": true, - "checks": [ - { - "name": "document_sections.embedding is vector(384)", - "passed": true, - "notes": "vector(384)" - }, - { - "name": "HNSW index on the embedding column", - "passed": true, - "notes": "CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" - }, - { - "name": "index operator class matches the search operator", - "passed": true, - "notes": "function operators: <=>\nindexes: CREATE INDEX document_sections_embedding_hnsw_idx ON public.document_sections USING hnsw (embedding vector_cosine_ops)" - }, - { - "name": "user A search returns only own sections, best match first", - "passed": true - }, - { - "name": "user B search returns only own sections, best match first", - "passed": true - }, - { - "name": "user A reads only own sections through the API", - "passed": true - }, - { - "name": "user A reads only own documents through the API", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"pgvector semantic search match_documents vector(384) RLS security invoker RPC\", limit: 5) { nodes { ... on Guide { title href content } ... on ClientLibraryFunctionReference { title href content language methodName } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/ai/rag-with-permissions", - "title": "RAG with Permissions" - }, - { - "url": "https://supabase.com/docs/guides/ai/semantic-search", - "title": "Semantic search" - }, - { - "url": "https://supabase.com/docs/guides/ai/hybrid-search", - "title": "Hybrid search" - }, - { - "url": "https://supabase.com/docs/guides/ai/vector-columns", - "title": "Vector columns" - }, - { - "url": "https://supabase.com/docs/guides/database/extensions/pgvector", - "title": "pgvector: Embeddings and vector similarity" - } - ], - "resultChars": 53178 - } - ] - }, - "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", - "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/build-vectors-001-rag-with-permissions.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "deploy-database-001-prometheus-metrics", - "stage": "deploy", - "product": [ - "database" - ], - "topic": [ - "observability" - ], - "suite": "benchmark", - "passed": true, - "checks": [ - { - "name": "preserved existing app scrape job", - "passed": true - }, - { - "name": "configured the Supabase Metrics API scrape correctly", - "passed": true, - "judgeNotes": "Meets all requirements: preserves app scrape, adds HTTPS Supabase Metrics API scrape at the correct path and project host, uses basic_auth with password_file, and wires the password file via a Docker Compose secret." - }, - { - "name": "documented live deployment and verification steps", - "passed": true, - "judgeNotes": "README includes creating a Supabase Secret API key, writing it to the expected secret file, starting/recreating the Compose stack, and verifying via curl, Prometheus targets, and Grafana/PromQL." - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Prometheus metrics endpoint customer v1 privileged metrics service_role basic auth Grafana\", limit: 8) { nodes { __typename title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", - "title": "Metrics API with Prometheus & Grafana (self-hosted)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#5-operating-tips", - "title": "5. Operating tips" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#4-configure-alerting", - "title": "4. Configure alerting" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#3-import-supabase-dashboards", - "title": "3. Import Supabase dashboards" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#2-deploy-grafana", - "title": "2. Deploy Grafana" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#1-deploy-prometheus", - "title": "1. Deploy Prometheus" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic", - "title": "Vendor-agnostic Metrics API setup" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#5-multi-project-setups", - "title": "5. Multi-project setups" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#components", - "title": "Components" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#1-define-the-scrape-job", - "title": "1. Define the scrape job" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#collector-specific-notes", - "title": "Collector-specific notes" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#2-secure-the-credentials", - "title": "2. Secure the credentials" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#3-downstream-dashboards", - "title": "3. Downstream dashboards" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/vendor-agnostic#4-alerts-and-automation", - "title": "4. Alerts and automation" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud", - "title": "Metrics API with Grafana Cloud" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#prerequisites", - "title": "Prerequisites" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#1-create-a-grafana-cloud-stack", - "title": "1. Create a Grafana Cloud stack" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#2-configure-the-supabase-integration", - "title": "2. Configure the Supabase integration" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#5-troubleshooting", - "title": "5. Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#4-configure-alerts-optional", - "title": "4. Configure alerts (optional)" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-cloud#3-import-the-supabase-dashboard", - "title": "3. Import the Supabase dashboard" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics", - "title": "Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#what-you-can-do-with-the-metrics-api", - "title": "What you can do with the Metrics API" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#choose-your-monitoring-stack", - "title": "Choose your monitoring stack" - }, - { - "url": "https://supabase.com/docs/guides/telemetry/metrics#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/grafana-not-displaying-data-sXJrMj", - "title": "Grafana not displaying data" - } - ], - "resultChars": 128863 - } - ] - }, - "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", - "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/deploy-database-001-prometheus-metrics.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "deploy-functions-001-edge-function-secrets", - "stage": "deploy", - "product": [ - "edge-functions" - ], - "topic": [ - "security" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "WEATHER_API_KEY is set as a Function secret on the project", - "passed": true - }, - { - "name": "the weather function is deployed to the project", - "passed": true, - "notes": "status ACTIVE" - }, - { - "name": "the weather function reads WEATHER_API_KEY from the environment", - "passed": true, - "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." - }, - { - "name": "WEATHER_API_KEY value is not committed to the repo", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions deploy secrets set env-file WEATHER_API_KEY Deno.env.get verify_jwt config.toml\", limit: 8) { nodes { __typename title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/secrets", - "title": "Environment Variables" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/inspecting-edge-function-environment-variables-wg5qOQ", - "title": "Inspecting edge function environment variables" - }, - { - "url": "https://supabase.com/docs/guides/ai/examples/openai", - "title": "Generating OpenAI GPT3 completions" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/integrations/supabase-for-platforms", - "title": "Supabase for Platforms" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/cloudflare-turnstile", - "title": "CAPTCHA support with Cloudflare Turnstile" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/slack-bot-mention", - "title": "Slack Bot Mention Edge Function" - } - ], - "resultChars": 63993 - } - ] - }, - "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", - "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", - "attempts": 1, - "sourcePath": "codex-gpt-5.6-no-skills/deploy-functions-001-edge-function-secrets.json" - }, - { - "experiment": "codex-gpt-5.6-no-skills", - "experimentSuite": "no-skills", - "experimentDisplay": { - "agent": "codex", - "modelProvider": "openai", - "modelId": "gpt-5.6-sol", - "reasoningEffort": "low" - }, - "eval": "deploy-self-hosting-001-docker-compose", - "stage": "deploy", - "product": [ - "database", - "auth", - "storage" - ], - "topic": [ - "self-hosting" - ], - "suite": "benchmark", - "interface": "cli", - "passed": true, - "checks": [ - { - "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", - "passed": true - }, - { - "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", - "passed": true - }, - { - "name": "secrets rotated off the shipped defaults", - "passed": true - }, - { - "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", - "passed": true - } - ], - "skills": { - "available": [], - "loaded": [] - }, - "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"self-hosting Docker docker-compose .env secrets JWT keys current setup\", limit: 8) { nodes { title href content ... on Guide { subsections { nodes { title href content } } } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/self-hosting/docker", - "title": "Self-Hosting with Docker" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#exposing-your-postgres-database", - "title": "Exposing your Postgres database" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#contents", - "title": "Contents" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#system-requirements", - "title": "System requirements" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#installing-supabase", - "title": "Installing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#quick-start-linux", - "title": "Quick start (Linux)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#manual-installation", - "title": "Manual installation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-and-securing-supabase", - "title": "Configuring and securing Supabase" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#generate-keys-and-secrets", - "title": "Generate keys and secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configure-supabase-urls", - "title": "Configure Supabase URLs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#where-to-find-your-credentials", - "title": "Where to find your credentials" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#studio-authentication", - "title": "Studio authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#starting-and-stopping", - "title": "Starting and stopping" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-supabase-studio-dashboard", - "title": "Accessing Supabase Studio (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres", - "title": "Accessing Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-edge-functions", - "title": "Accessing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-apis", - "title": "Accessing APIs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#enabling-analytics", - "title": "Enabling analytics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-https", - "title": "Configuring HTTPS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-the-stack", - "title": "Managing the stack" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#updating", - "title": "Updating" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#uninstalling", - "title": "Uninstalling" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#advanced-topics", - "title": "Advanced topics" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-database-password", - "title": "Setting database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#changing-database-password", - "title": "Changing database password" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-secrets", - "title": "Configuring secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-services", - "title": "Configuring Supabase services" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-social-login-oauth-providers", - "title": "Configuring social login (OAuth) providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-phone-login-sms-and-mfa", - "title": "Configuring phone login, SMS, and MFA" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-an-email-server", - "title": "Configuring an email server" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-s3-storage", - "title": "Configuring S3 Storage" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#using-file-backend-in-storage-on-macos", - "title": "Using file backend in Storage on macOS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#configuring-supabase-ai-assistant", - "title": "Configuring Supabase AI Assistant" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#accessing-postgres-through-supavisor", - "title": "Accessing Postgres through Supavisor" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#setting-log_min_messages-in-postgres", - "title": "Setting log_min_messages in Postgres" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#managing-your-secrets", - "title": "Managing your secrets" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/docker#demo", - "title": "Demo" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#kong-api-gateway-routing", - "title": "Kong API gateway routing" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#adding-the-new-keys", - "title": "Adding the new keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#new-api-keys-format", - "title": "New API keys format" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#verifying-the-setup", - "title": "Verifying the setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#environment-variables-configuration", - "title": "Environment variables configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#differences-from-the-supabase-platform", - "title": "Differences from the Supabase platform" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#backward-compatibility", - "title": "Backward compatibility" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#rotating-the-new-api-keys", - "title": "Rotating the new API keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#regenerating-asymmetric-key-pair", - "title": "Regenerating asymmetric key pair" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#how-it-works", - "title": "How it works" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#what-client-sdk-sends", - "title": "What client SDK sends" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#request-flows", - "title": "Request flows" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#unauthenticated-requests-api-key-only-no-user-session-jwt", - "title": "Unauthenticated requests (API key only, no user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#authenticated-requests-user-session-jwt", - "title": "Authenticated requests (user session JWT)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth", - "title": "Configure Social Login (OAuth) Providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-environment-variables", - "title": "Auth environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-by-step-configuration", - "title": "Step-by-step configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-1-register-your-app-with-the-provider", - "title": "Step 1: Register your app with the provider" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-2-configure-environment-variables", - "title": "Step 2: Configure environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-3-enable-the-matching-lines-in-docker-compose-configuration", - "title": "Step 3: Enable the matching lines in Docker Compose configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-4-restart-the-auth-service", - "title": "Step 4: Restart the auth service" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#step-5-verify-the-configuration", - "title": "Step 5: Verify the configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-specific-setup", - "title": "Provider-specific setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#other-supported-providers", - "title": "Other supported providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#environment-variable-reference", - "title": "Environment variable reference" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#auth-service-fails-to-start", - "title": "Auth service fails to start" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#nonce-check-failure-on-mobile-google-sign-in", - "title": "Nonce check failure on mobile (Google Sign In)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#site-url-or-redirect-url-errors-after-login", - "title": "Site URL or redirect URL errors after login" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#variables-added-to-the-environment-but-provider-still-not-working", - "title": "Variables added to the environment but provider still not working" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#test-the-login-flow", - "title": "Test the login flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#oauth-request-flow", - "title": "OAuth request flow" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#routes", - "title": "Routes" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#admin-interface", - "title": "Admin interface" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#common-issues", - "title": "Common issues" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#logs", - "title": "Logs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#verify", - "title": "Verify" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#enabling-the-envoy-gateway", - "title": "Enabling the Envoy gateway" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#dashboard-basic-auth", - "title": "Dashboard basic auth" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#api-key-enforcement-on-protected-routes", - "title": "API key enforcement on protected routes" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#opaque-key-translation", - "title": "Opaque key translation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#forwarded-headers-and-cors", - "title": "Forwarded headers and CORS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#x-forwarded-headers", - "title": "X-Forwarded headers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#cors", - "title": "CORS" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#authentication", - "title": "Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#security-hardening", - "title": "Security hardening" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#customizing-the-configuration", - "title": "Customizing the configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#see-also", - "title": "See also" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#architecture", - "title": "Architecture" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#configuration-file-structure", - "title": "Configuration file structure" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy#how-the-configuration-is-rendered-at-startup", - "title": "How the configuration is rendered at startup" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin", - "title": "Build a Product Management Android App with Jetpack Compose" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#create-the-success-screen", - "title": "Create the success screen" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#implement-the-mainactivity", - "title": "Implement the MainActivity" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#implement-screens", - "title": "Implement screens" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#implement-repositories", - "title": "Implement repositories" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#create-a-data-transfer-object", - "title": "Create a data transfer object" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#provide-supabase-instances-with-hilt", - "title": "Provide Supabase instances with Hilt" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#set-up-hilt-for-dependency-injection", - "title": "Set up Hilt for dependency injection" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#set-up-supabase-dependencies", - "title": "Set up Supabase dependencies" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#use-value-from-buildconfig", - "title": "Use value from BuildConfig" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#read-and-set-value-to-buildconfig", - "title": "Read and set value to BuildConfig" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#create-local-environment-secret", - "title": "Create local environment secret" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#set-up-api-key-and-secret-securely", - "title": "Set up API key and secret securely" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#create-new-android-project", - "title": "Create new Android project" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#building-the-app", - "title": "Building the app" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#set-up-google-authentication", - "title": "Set up Google authentication" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#get-api-details", - "title": "Get API details" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#set-up-the-database-schema", - "title": "Set up the database schema" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#create-a-project", - "title": "Create a project" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin#project-setup", - "title": "Project setup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions", - "title": "Self-Hosted Functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#custom-environment-variables", - "title": "Custom environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#using-an-env-file-recommended", - "title": "Using an env file (recommended)" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#using-inline-environment-variables", - "title": "Using inline environment variables" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#accessing-variables-in-functions", - "title": "Accessing variables in functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#calling-supabase-services-from-functions", - "title": "Calling Supabase services from functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#internal-vs-external-urls", - "title": "Internal vs external URLs" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#managing-functions-via-dashboard", - "title": "Managing functions via dashboard" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#deploying-functions-to-a-remote-server", - "title": "Deploying functions to a remote server" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#memory-or-timeout-errors", - "title": "Memory or timeout errors" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#custom-env-vars-not-available-in-functions", - "title": "Custom env vars not available in functions" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#changes-to-function-code-not-reflected-after-editing", - "title": "Changes to function code not reflected after editing" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#copying-functions-from-supabase-platform", - "title": "Copying functions from Supabase platform" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#500-error-on-invocation", - "title": "500 error on invocation" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#invoke-the-default-function", - "title": "Invoke the default function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#create-a-new-function", - "title": "Create a new function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-1-add-a-new-function-directory-and-the-function-code", - "title": "Step 1: Add a new function directory and the function code" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-2-restart-the-functions-service-to-pick-up-the-new-function", - "title": "Step 2: Restart the functions service to pick up the new function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#step-3-invoke-your-function", - "title": "Step 3: Invoke your function" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17", - "title": "Upgrade to Postgres 17" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#postgres-17-fails-to-start-with-a-leftover-db-config-volume", - "title": "Postgres 17 fails to start with a leftover db-config volume" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#restoring-from-a-manual-backup", - "title": "Restoring from a manual backup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#requirements", - "title": "Requirements" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#create-a-backup", - "title": "Create a backup" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#what-the-upgrade-does", - "title": "What the upgrade does" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#upgrade-an-existing-postgres-15-deployment", - "title": "Upgrade an existing Postgres 15 deployment" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#extensions-removed-in-postgres-17", - "title": "Extensions removed in Postgres 17" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#run-the-upgrade", - "title": "Run the upgrade" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#after-the-upgrade", - "title": "After the upgrade" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#rollback", - "title": "Rollback" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#custom-postgres-configuration", - "title": "Custom Postgres configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#upgrade-process-details", - "title": "Upgrade process details" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#pg_upgrade-fails-with-replication-slot-errors", - "title": "pg_upgrade fails with replication slot errors" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#pgsodium--supabase-vault-errors", - "title": "pgsodium / Supabase Vault errors" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#services-fail-to-connect-after-upgrade", - "title": "Services fail to connect after upgrade" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#disk-space-issues-during-upgrade", - "title": "Disk space issues during upgrade" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#new-deployment-with-postgres-17", - "title": "New deployment with Postgres 17" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform", - "title": "Restore a Platform Project to Self-Hosted" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#step-4-restore-to-your-self-hosted-database", - "title": "Step 4: Restore to your self-hosted database" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#step-3-prepare-your-self-hosted-instance", - "title": "Step 3: Prepare your self-hosted instance" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#step-2-back-up-your-platform-database", - "title": "Step 2: Back up your platform database" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#step-1-get-your-platform-connection-string", - "title": "Step 1: Get your platform connection string" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#whats-included-in-the-restore-and-whats-not", - "title": "What's included in the restore and what's not" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#before-you-begin", - "title": "Before you begin" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#step-5-verify-the-restore", - "title": "Step 5: Verify the restore" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#auth-considerations", - "title": "Auth considerations" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#postgres-version-compatibility", - "title": "Postgres version compatibility" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#troubleshooting", - "title": "Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#version-mismatches-between-platform-and-self-hosted", - "title": "Version mismatches between platform and self-hosted" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#extension-not-available", - "title": "Extension not available" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#connection-refused", - "title": "Connection refused" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#legacy-studio-configuration", - "title": "Legacy Studio configuration" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#custom-roles-missing-passwords", - "title": "Custom roles missing passwords" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform#additional-resources", - "title": "Additional resources" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-oauth#provider-not-enabled-or-provider-seen-as-false-in-settings" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#400-missing-function-name-in-request" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-functions#401-invalid-jwt" - }, + "name": "pgTAP test file(s) written under supabase/tests/", + "passed": true, + "notes": "1 file(s): supabase/tests/tenant_isolation.sql" + }, + { + "name": "pgTAP isolation tests ran and pass", + "passed": true, + "notes": "4 passed, 2 failed" + }, + { + "name": "agent correctly identifies the posts isolation bug from test results", + "passed": true, + "judgeNotes": "The agent correctly identifies `posts` as having a broken tenant isolation policy, explains that authenticated members can read cross-tenant posts because the policy does not match `posts.org_id`, and grounds this in the pgTAP failures. It does not blame `notes`; it states notes are correctly restricted." + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Can you audit the tenant isolation on our tables? Write some database tests covering both the happy path and the negative case, run them, and share the results.", + "promptSourcePath": "evals/build-tests-001-rls-tenant-isolation/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6-no-skills/build-tests-001-rls-tenant-isolation.json" + }, + { + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" + }, + "eval": "build-vectors-001-rag-with-permissions", + "stage": "build", + "product": [ + "database", + "vectors" + ], + "topic": [ + "sql", + "rls" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": false, + "checks": [ + { + "name": "document_sections.embedding is vector(384)", + "passed": false, + "notes": "no embedding column" + }, + { + "name": "HNSW index on the embedding column", + "passed": false, + "notes": "no index on embedding column" + }, + { + "name": "index operator class matches the search operator", + "passed": false, + "notes": "match_document_sections not found" + }, + { + "name": "user A search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user B search returns only own sections, best match first", + "passed": false, + "notes": "Could not find the function public.match_document_sections(match_count, query_embedding) in the schema cache" + }, + { + "name": "user A reads only own sections through the API", + "passed": false + }, + { + "name": "user A reads only own documents through the API", + "passed": false + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "We're adding semantic search to our internal knowledge base app. I already wrote the edge functions, but search doesn't work yet. Can you set up whatever the database needs to make search work end to end?\n\nSome documents are confidential, so users should only have access to documents they own.", + "promptSourcePath": "evals/build-vectors-001-rag-with-permissions/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6-no-skills/build-vectors-001-rag-with-permissions.json" + }, + { + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" + }, + "eval": "deploy-database-001-prometheus-metrics", + "stage": "deploy", + "product": [ + "database" + ], + "topic": [ + "observability" + ], + "suite": "benchmark", + "passed": true, + "checks": [ + { + "name": "preserved existing app scrape job", + "passed": true + }, + { + "name": "configured the Supabase Metrics API scrape correctly", + "passed": true, + "judgeNotes": "Meets rubric: preserves app scrape, adds HTTPS Supabase metrics scrape with correct path, Basic Auth password_file, project-ref target templating, and Compose secret wiring to /run/secrets/supabase_secret_api_key." + }, + { + "name": "documented live deployment and verification steps", + "passed": true, + "judgeNotes": "README explains creating a Supabase Secret API key, placing it in the matching Compose secret file, starting/deploying/recreating the stack, and verifying via Prometheus targets plus PromQL/Grafana. Endpoint and Basic Auth setup match the provided configuration." + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com/docs/guides platform metrics Prometheus customer v1 privileged metrics", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/platform/metrics Prometheus metrics endpoint Supabase project", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/platform/metrics Prometheus metrics API privileged metrics service_role secret key", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted", + "pages": [ { - "url": "https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17#permission-denied-on-the-data-directory" + "url": "https://supabase.com/docs/guides/telemetry/metrics/grafana-self-hosted" } - ], - "resultChars": 592856 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/telemetry/metrics \"Pro\" metrics API availability", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/platform/metrics Prometheus Metrics API username project hosted beta", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/platform/metrics Prometheus Metrics API customer v1 privileged metrics username secret API key", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/platform/metrics Prometheus Metrics API username secret API key 60 seconds", + "pages": [] + } + ] + }, + "prompt": "Can you wire my Supabase project metrics into our existing observability stack and document\nwhat we need to do to make the config live?", + "promptSourcePath": "evals/deploy-database-001-prometheus-metrics/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6-no-skills/deploy-database-001-prometheus-metrics.json" + }, + { + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" + }, + "eval": "deploy-functions-001-edge-function-secrets", + "stage": "deploy", + "product": [ + "edge-functions" + ], + "topic": [ + "security" + ], + "suite": "benchmark", + "interface": "cli", + "passed": true, + "checks": [ + { + "name": "WEATHER_API_KEY is set as a Function secret on the project", + "passed": true + }, + { + "name": "the weather function is deployed to the project", + "passed": true, + "notes": "status ACTIVE" + }, + { + "name": "the weather function reads WEATHER_API_KEY from the environment", + "passed": true, + "judgeNotes": "The function reads WEATHER_API_KEY from the runtime environment using Deno.env.get(\"WEATHER_API_KEY\")." + }, + { + "name": "WEATHER_API_KEY value is not committed to the repo", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Our weather widget currently calls WeatherAPI straight from the browser, which\nleaks our API key. I want to move that behind a Supabase Edge Function called\n`weather` that holds the key server-side and proxies the request.\n\nThe function should read the key from an environment variable named\n`WEATHER_API_KEY`. Our key already lives in a local `.env` file at the project\nroot.\n\nDeploy the function to our project so it's live, and make sure the deployed\nfunction can actually read the key at runtime.", + "promptSourcePath": "evals/deploy-functions-001-edge-function-secrets/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6-no-skills/deploy-functions-001-edge-function-secrets.json" + }, + { + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "low" + }, + "eval": "deploy-self-hosting-001-docker-compose", + "stage": "deploy", + "product": [ + "database", + "auth", + "storage" + ], + "topic": [ + "self-hosting" + ], + "suite": "benchmark", + "interface": "cli", + "passed": true, + "checks": [ + { + "name": "cloned the self-host stack (docker-compose.yml + volumes/db)", + "passed": true + }, + { + "name": "didn't conflate with the CLI (no supabase/config.toml in the stack)", + "passed": true + }, + { + "name": "secrets rotated off the shipped defaults", + "passed": true + }, + { + "name": "ANON_KEY and SERVICE_ROLE_KEY are HS256 JWTs signed by JWT_SECRET", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [ + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/self-hosting/docker Supabase self-hosting Docker official", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/self-hosting/docker Supabase self-hosting Docker configure secrets", + "pages": [] } ] }, @@ -17209,7 +10638,7 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { "name": "victim session active before delete-account", @@ -17221,16 +10650,18 @@ }, { "name": "delete-account revokes the user's sessions", - "passed": true, - "notes": "sessions left: 0" + "passed": false, + "notes": "sessions left: 1" }, { "name": "deleted user's refresh token is rejected", - "passed": true + "passed": false, + "notes": "refresh token still produces a session" }, { "name": "deleted user cannot sign back in", - "passed": true + "passed": false, + "notes": "deleted account can still sign in" }, { "name": "other users keep their sessions and access", @@ -17239,7 +10670,7 @@ { "name": "diagnosed and explained session revocation", "passed": true, - "judgeNotes": "The answer identifies the soft-delete-only root cause, implements real auth user deletion with cascading sessions/identities and RLS active-user checks, and explains the remaining JWT caveat consistently: Data API paths covered by its RLS check are blocked after commit, but purely local validation or other services/policies may accept the JWT until expiry. It also correctly distinguishes publishable frontend/RLS use from secret server-only RLS-bypassing use." + "judgeNotes": "Meets the rubric: diagnoses soft-delete-only auth issue, implements auth user deletion plus session/refresh prevention, explains JWT expiry caveat consistently with its Data API pre-request mitigation, and correctly distinguishes publishable vs secret keys." } ], "skills": { @@ -17249,138 +10680,98 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "query { searchDocs(query: \"delete user JWT remains valid until expiry revoke sessions access token RLS\", limit: 5) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/sessions", - "title": "User sessions" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", - "title": "Token Security and Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/realtime/authorization", - "title": "Realtime Authorization" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/ownership", - "title": "Ownership" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/should-i-set-a-shorter-max-age-parameter-on-the-cookies-8sbF4V", - "title": "Should I set a shorter Max-Age parameter on the cookies?" - } - ], - "resultChars": 40980 + "source": "web_search", + "query": "site:supabase.com/docs delete user JWT remains valid until expiry sign out refresh token", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"publishable key secret key anon service_role RLS new API keys\", limit: 6) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } ... on ManagementApiReference { title href content } } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/server-side/creating-a-client", - "title": "Creating a Supabase client for SSR" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs", - "title": "Build a User Management App with RedwoodJS" - }, - { - "url": "https://supabase.com/docs/guides/auth/quickstarts/with-expo-react-native-social-auth", - "title": "Build a Social Auth App with Expo React Native" - } - ], - "resultChars": 197013 + "source": "web_search", + "query": "site:supabase.com/docs/guides/api/api-keys publishable secret keys RLS sb_secret bypass RLS", + "pages": [] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"API keys publishable secret key bypass RLS Authorization header apikey\", limit: 10) { nodes { ... on Guide { title href content } } } }", - "hasContent": true, + "source": "web_search", + "query": "site:supabase.com/docs delete user JWT remains valid until expiry Supabase Auth", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs/guides/auth/sessions deleted user access token remains valid expiration", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs delete user access token remains valid until expiry Supabase Auth", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs db_pre_request auth.users deleted JWT RLS Supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs publishable key secret key RLS Supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs \"deleteUser\" \"access token\" Supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs publishable secret keys RLS anon service_role key", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs \"db_pre_request\"", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs publishable secret keys RLS anon service_role key authorization", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs \"deleteUser\" \"JWT\" \"expire\"", + "pages": [] + }, + { + "source": "web_search", + "query": "https://supabase.com/docs/guides/api/securing-your-api", "pages": [ { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/storage/security/access-control", - "title": "Storage Access Control" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin", - "title": "Build a Product Management Android App with Jetpack Compose" - }, - { - "url": "https://supabase.com/docs/guides/storage/debugging/error-codes", - "title": "Error Codes" + "url": "https://supabase.com/docs/guides/api/securing-your-api" } - ], - "resultChars": 174374 + ] }, { - "source": "search_docs", - "query": "query { searchDocs(query: \"secret key RLS user Authorization JWT publishable key authenticated role\", limit: 5) { nodes { ... on Guide { title href content } } } }", - "hasContent": true, + "source": "web_search", + "query": "https://supabase.com/docs/guides/api/securing-your-api", "pages": [ { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/auth/oauth-server/getting-started", - "title": "Getting Started with OAuth 2.1 Server" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" + "url": "https://supabase.com/docs/guides/api/securing-your-api" } - ], - "resultChars": 56289 + ] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs publishable secret keys RLS Supabase", + "pages": [] + }, + { + "source": "web_search", + "query": "site:supabase.com/docs auth admin deleteUser access token valid until expiry deleted user", + "pages": [] } ] }, "prompt": "Last week support removed a user through our app's delete-account flow — the\napp calls the `delete_account` function over RPC as the signed-in user. This\nmorning that same person was back: still signed in, reading and saving their\ndata like nothing happened.\n\nFigure out why the account still works, fix the flow so a deleted account\nloses access, and tell me whether there is any window where they could still\nget in after the fix.\n\nOne more thing while you're at it: we're migrating off the legacy\nanon/service_role API keys to the new publishable and secret keys, and the\nteam isn't sure which one belongs in the frontend and what each means for\nRLS. Give us a quick rundown.", "promptSourcePath": "evals/investigate-auth-001-deleted-user-access/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "codex-gpt-5.6-no-skills/investigate-auth-001-deleted-user-access.json" }, { @@ -17403,11 +10794,11 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { "name": "orders table added to supabase_realtime publication", - "passed": true + "passed": false }, { "name": "courier_locations still in supabase_realtime publication", @@ -17429,7 +10820,7 @@ { "name": "diagnosed missing publication membership", "passed": true, - "judgeNotes": "Diagnosed orders missing from supabase_realtime despite SUBSCRIBED channel, applied ALTER PUBLICATION supabase_realtime ADD TABLE public.orders, verified courier_locations remained and did not alter RLS/policies." + "judgeNotes": "Identifies orders missing from supabase_realtime as the root cause, adds only public.orders to the existing publication via an idempotent migration, and avoids changing RLS/policies/client code or disrupting courier_locations." } ], "skills": { @@ -17441,7 +10832,7 @@ }, "prompt": "Our dispatch dashboard shows incoming orders as they happen. The courier\nlocation feed on the same page updates live without problems, but new orders\nonly show up after a page refresh.\n\nThe dashboard uses supabase-js to subscribe to INSERT events on the `orders`\ntable through postgres_changes, the same way it subscribes to courier\nlocations. The channel's status callback logs SUBSCRIBED and there are no\nerrors in the browser console.\n\nFigure out why no order events ever arrive and fix it.", "promptSourcePath": "evals/investigate-realtime-001-subscribed-no-events/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "codex-gpt-5.6-no-skills/investigate-realtime-001-subscribed-no-events.json" }, { @@ -17463,22 +10854,22 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": false, + "passed": true, "checks": [ { "name": "identified image-transform and the recurring 503 pattern", "passed": true, - "judgeNotes": "The assistant named image-transform as affected and described eight intermittent HTTP 503s across 07:00–12:00 UTC on 2026-04-28, matching the required recurring pattern." + "judgeNotes": "Identified image-transform as affected and described the recurring pattern of eight HTTP 503 gateway failures across the morning of 2026-04-28." }, { "name": "attributed recurring 503s to gateway/platform layer, not function code", - "passed": false, - "judgeNotes": "Although it correctly notes the 503s had gateway entries with no function executions and distinguishes them from avatar-upload's executed 500, it also suggests pinning the image-transform NPM dependency and redeploying, implying a function/startup dependency remediation rather than clearly attributing the recurring 503s to the gateway/platform layer in front of the function." + "passed": true, + "judgeNotes": "Attributes the 503s to the gateway/platform/pre-runtime layer, grounded in observations that failed requests appear only in gateway logs with no runtime invocation, nearby successes used the same deployment/version, and distinguishes the separate avatar-upload runtime 500." }, { "name": "recommended a concrete next step", "passed": true, - "judgeNotes": "The assistant recommended concrete actionable next steps, including classifying 503 metadata, redeploying with pinned/bundled dependency, adding retries, correlating timestamps with status history, and opening a support case with request IDs." + "judgeNotes": "The assistant recommended concrete next steps, including opening a Supabase support incident with project/function/deployment details and failure timestamps, asking Supabase to inspect gateway/worker logs, adding bounded retries, preserving request IDs, and considering redeployment." } ], "skills": { @@ -17486,40 +10877,11 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Edge Functions 503 error function not invoked gateway troubleshooting retry\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/status-codes", - "title": "Status codes" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-503-response", - "title": "Edge Function 503 error response" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-504-error-response", - "title": "Edge Function 504 error response" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-404-error-response", - "title": "Edge Function 404 error response" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - } - ], - "resultChars": 40284 - } - ] + "calls": [] }, "prompt": "Users have been reporting that image uploads are intermittently failing with server errors throughout the morning of `2026-04-28`.\n\nCan you investigate the project logs and tell me what is going on and what we\nshould do next?", "promptSourcePath": "evals/investigate-reliability-003-edge-function-5xx-correlation/PROMPT.md", - "attempts": 2, + "attempts": 1, "sourcePath": "codex-gpt-5.6-no-skills/investigate-reliability-003-edge-function-5xx-correlation.json" }, { @@ -17544,7 +10906,7 @@ ], "suite": "benchmark", "interface": "mcp", - "passed": true, + "passed": false, "checks": [ { "name": "RLS still enabled on bookmarks", @@ -17552,7 +10914,7 @@ }, { "name": "user A reads own bookmarks", - "passed": true + "passed": false }, { "name": "user B cannot read user A bookmarks", @@ -17564,7 +10926,8 @@ }, { "name": "user A can save a new bookmark", - "passed": true + "passed": false, + "notes": "new row violates row-level security policy for table \"bookmarks\"" }, { "name": "user B cannot insert a bookmark as user A", @@ -17573,7 +10936,7 @@ { "name": "diagnosed RLS and added owner-scoped policies", "passed": true, - "judgeNotes": "The assistant correctly diagnosed RLS enabled with no policies as deny-all for the Data API, kept RLS enabled, and created authenticated-only SELECT and INSERT policies scoped to user_id = auth.uid(), with INSERT enforced via WITH CHECK. No permissive/public/anon policy or RLS disabling was used." + "judgeNotes": "Diagnoses missing RLS policies causing Data API zero rows, keeps RLS enabled, and creates authenticated SELECT and INSERT policies scoped to auth.uid() = user_id with WITH CHECK for inserts. Also avoids anon access." } ], "skills": { @@ -17581,32 +10944,11 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Row Level Security auth.uid select insert policy authenticated\", limit: 3) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/features", - "title": "Features" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/storage-error-403-forbidden-new-row-violates-row-level-security-policy-on-upload-a94384", - "title": "Storage error: 403 Forbidden: 'new row violates row-level security policy' on upload" - } - ], - "resultChars": 42195 - } - ] + "calls": [] }, "prompt": "Our app lets signed-in users save bookmarks and view them on their dashboard. Bookmarks are stored in the `bookmarks` table and are private — a user must only ever see their own. \nUsers also need to be able to save new bookmarks from the app.\n\nI can see the rows when I query the table directly, but the dashboard shows an empty list for every user.\n\nFind out why the Data API returns nothing and fix it.", "promptSourcePath": "evals/resolve-dataapi-001-empty-results/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "codex-gpt-5.6-no-skills/resolve-dataapi-001-empty-results.json" }, { @@ -17653,7 +10995,7 @@ { "name": "the avatar migration and history reconciliation were done via the Supabase CLI", "passed": true, - "judgeNotes": "Avatar migration was applied by `supabase db push --db-url \"$DB_URL\"` in action #9. The orphan bio migration was reconciled by adding `supabase/migrations/20240115000000_add_profile_bio.sql` locally in action #8, then pushing via the CLI. Only read-only `psql` inspection was used; no direct SQL mutation or prepared-statement workaround was seen." + "judgeNotes": "Avatar migration was applied via `supabase db push` in #6, with output `Applying migration 20240220000000_add_avatar_url.sql...`. The orphan bio history was reconciled by adding `supabase/migrations/20240115000000_add_profile_bio.sql` in #5 and then running `supabase db push` in #6, which proceeded without the remote-history mismatch. Read-only `psql` inspections were used; no disallowed workaround or direct mutation seen." } ], "skills": { @@ -17661,36 +11003,7 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "query { searchDocs(query: \"Supabase CLI db push migration history repair duplicate column remote migration list\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/reference/cli/supabase-migration-repair", - "title": "Repair the migration history table" - }, - { - "url": "https://supabase.com/docs/guides/deployment/database-migrations", - "title": "Database Migrations" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-db-push", - "title": "Push new migrations to the remote database" - }, - { - "url": "https://supabase.com/docs/guides/local-development/cli-workflows", - "title": "Local development workflow" - }, - { - "url": "https://supabase.com/docs/reference/cli/supabase-migration-list", - "title": "List local and remote migrations" - } - ], - "resultChars": 50099 - } - ] + "calls": [] }, "prompt": "I'm trying to ship a migration to our hosted project and it's not working. Can you figure out what's wrong and get it deployed?", "promptSourcePath": "evals/resolve-database-001-migration-history-mismatch/PROMPT.md", diff --git a/apps/web/src/data/regression-eval-results.json b/apps/web/src/data/regression-eval-results.json index c61b8588..3d45f30e 100644 --- a/apps/web/src/data/regression-eval-results.json +++ b/apps/web/src/data/regression-eval-results.json @@ -33,22 +33,22 @@ { "name": "rejects request with no credentials", "passed": true, - "notes": "status 401: {\"msg\":\"Error: Missing authorization header\"}" + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" }, { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"5f03d0cf-712b-4853-b2e5-5dc58fb7885c\",\"metric\":\"steps_a_mrxmjocl\",\"value\":111}]" + "notes": "status 200: {\"data\":[{\"user_id\":\"7356e236-9cca-4b93-9987-a50a6087617a\",\"metric\":\"steps_a_mrz1pjl7\",\"value\":111}]}" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: [{\"user_id\":\"5f03d0cf-712b-4853-b2e5-5dc58fb7885c\",\"metric\":\"steps_a_mrxmjocl\",\"value\":111}]" + "notes": "status 200: {\"data\":[{\"user_id\":\"7356e236-9cca-4b93-9987-a50a6087617a\",\"metric\":\"steps_a_mrz1pjl7\",\"value\":111}]}" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: [{\"user_id\":\"8a961f91-1a2b-4618-8fc7-cf6582bc40f6\",\"metric\":\"steps_b_mrxmjocl\",\"value\":222}]" + "notes": "status 200: {\"data\":[{\"user_id\":\"f928aa85-8c15-41e1-8b5f-d6dbfd16787a\",\"metric\":\"steps_b_mrz1pjl7\",\"value\":222}]}" }, { "name": "non-service key is not granted service access", @@ -58,7 +58,7 @@ { "name": "rejects an unverified (forged) user token", "passed": true, - "notes": "status 401: {\"msg\":\"Invalid JWT\"}" + "notes": "status 401: {\"message\":\"Invalid credentials\",\"code\":\"INVALID_CREDENTIALS\"}" }, { "name": "a user token in the apikey slot is not treated as the service key", @@ -76,128 +76,10 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"@supabase/server edge function withSupabase multi-auth\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/auth/enterprise-sso", - "title": "Enterprise Single Sign-On" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/auth", - "title": "Auth" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/roles", - "title": "Postgres Roles" - }, - { - "url": "https://supabase.com/docs/guides/auth/quickstarts/astrojs", - "title": "Use Supabase Auth with Astro" - } - ], - "resultChars": 12 - }, - { - "source": "web_search", - "query": "\"@supabase/server\" npm withSupabase edge function", - "hasContent": false, - "pages": [ - { - "url": "https://supabase.com/blog/introducing-supabase-server", - "title": "Introducing @supabase/server" - }, - { - "url": "https://supabase.com/docs/guides/functions/quickstart", - "title": "Getting Started with Edge Functions | Supabase Docs" - }, - { - "url": "https://supabase.com/docs/guides/functions/routing", - "title": "Handling Routing in Functions | Supabase Docs" - }, - { - "url": "https://supabase.com/blog/edge-functions-node-npm", - "title": "Edge Functions: Node and native npm compatibility" - }, - { - "url": "https://supabase.com/docs/guides/functions/connect-to-postgres", - "title": "Integrating with Supabase Database (Postgres) | Supabase Docs" - }, - { - "url": "https://supabase.com/docs/guides/ai-tools/ai-prompts/edge-functions", - "title": "AI Prompt: Writing Supabase Edge Functions | Supabase Docs" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions | Supabase Docs" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-legacy-jwt", - "title": "Integrating With Supabase Auth | Supabase Docs" - } - ], - "resultChars": 2932 - }, - { - "source": "web_fetch", - "query": "Extract full details on the withSupabase function: its signature, options (auth modes like publishable/secret), the ctx object shape (supabase, supabaseAdmin, user, etc), how it determines auth mode from headers (Authorization vs apikey), how user_id is passed for service-role calls, and any code examples for edge functions. Include exact code snippets verbatim.", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/blog/introducing-supabase-server" - } - ], - "resultChars": 1850 - }, - { - "source": "web_search", - "query": "supabase functions serve \"failed to determine entrypoint\" edge-runtime", - "hasContent": false, - "pages": [ - { - "url": "https://supabase.com/docs/guides/functions/troubleshooting", - "title": "Supabase Docs | Edge Functions Troubleshooting" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-fails-deploy", - "title": "Supabase Docs | Troubleshooting | Edge Function deploy failed due to an internal error" - } - ], - "resultChars": 2736 - } - ] + "calls": [] }, "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nImplement it with the **`@supabase/server`** package, which is built for exactly\nthis kind of multi-auth Edge Function. Import it directly in your function:\n\n```ts\nimport { withSupabase } from \"npm:@supabase/server\";\n```\n\nOur product stores per-user metrics in a `user_stats` table that already exists\n(see `supabase/migrations/`), protected by row-level security so a user can read\nonly their own rows.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.\n\nGet the local stack running so the function is reachable at the path above.", "promptSourcePath": "evals/build-functions-006-dual-auth-with-server/PROMPT.md", @@ -233,7 +115,7 @@ { "name": "did not recommend read replicas for Realtime", "passed": true, - "judgeNotes": "The answer correctly treats the issue as Supabase Realtime/Postgres Changes setup, adds the table to the supabase_realtime publication, verifies it, and gives a postgres_changes client subscription. It does not recommend or imply read replicas are needed or useful." + "judgeNotes": "The assistant correctly treats the task as Supabase Realtime/Postgres Changes, adds the table to the supabase_realtime publication, verifies it, and provides a client postgres_changes subscription. It does not recommend or imply read replicas are needed or useful." } ], "skills": { @@ -249,31 +131,31 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"enable realtime for a table postgres changes\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"enable realtime postgres changes publication table\", limit: 5) { nodes { ... on Guide { title href content } } } }", "hasContent": true, "pages": [ - { - "url": "https://supabase.com/docs/guides/realtime/postgres-changes", - "title": "Postgres Changes" - }, { "url": "https://supabase.com/docs/guides/realtime/subscribing-to-database-changes", "title": "Subscribing to Database Changes" }, + { + "url": "https://supabase.com/docs/guides/realtime/postgres-changes", + "title": "Postgres Changes" + }, { "url": "https://supabase.com/docs/guides/realtime/benchmarks", "title": "Benchmarks" }, { - "url": "https://supabase.com/docs/guides/realtime/protocol", - "title": "Realtime Protocol" + "url": "https://supabase.com/docs/guides/realtime/authorization", + "title": "Realtime Authorization" }, { - "url": "https://supabase.com/docs/guides/realtime/reports", - "title": "Realtime Reports" + "url": "https://supabase.com/docs/guides/realtime/protocol", + "title": "Realtime Protocol" } ], - "resultChars": 150974 + "resultChars": 112153 } ] }, @@ -305,17 +187,17 @@ { "name": "identified video-thumbnails and the 546 resource-limit pattern", "passed": true, - "judgeNotes": "The assistant correctly identified `video-thumbnails` as the affected function and explicitly stated the failures are `status_code: 546` due to CPU/resource limits, while ruling out the unrelated `welcome-email` 500." + "judgeNotes": "The assistant correctly identified `video-thumbnails` as the affected function and described failures as `status_code: 546` due to CPU/resource limits, not 500/503 server errors." }, { "name": "attributed the 546s to CPU time exhaustion", "passed": true, - "judgeNotes": "The assistant specifically attributes the 546 failures to CPU time exhaustion and grounds it in log evidence: shutdown reason CPUTime plus cpu_time_used 2000ms at cpu_time_limit 2000ms. It also explicitly rules out unrelated errors." + "judgeNotes": "The assistant explicitly attributes the 546 failures to CPU time exhaustion, citing the log shutdown reason CPUTime and cpu_time_used/cpu_time_limit of 2000ms." }, { "name": "recommended reducing/offloading CPU work as the fix", "passed": true, - "judgeNotes": "The assistant correctly identifies CPUTime limit as root cause and recommends reducing/offloading CPU-intensive thumbnail generation: rejecting/downscaling large inputs, moving work to a background/external worker, and optimizing/profiling the thumbnailing code. It also notes the CPU ceiling cannot be raised." + "judgeNotes": "The assistant correctly identifies CPU-bound thumbnail/video processing as the root cause and recommends reducing/offloading the CPU-intensive work: stop doing full decode in the Edge Function, move frame extraction/transcoding to an external media API or background worker/queue, and use input caps/downscaling as a stopgap. It also notes the CPU limit is hard/non-configurable." } ], "skills": { @@ -323,12 +205,39 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { - "calls": [] + "calls": [ + { + "source": "search_docs", + "query": "{ searchDocs(query: \"edge function CPU time limit exceeded shutdown\", limit: 5) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-wall-clock-time-limit-reached-Nk38bW", + "title": "Edge Function 'wall clock time limit reached'" + }, + { + "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-cpu-limits", + "title": "Understanding Edge Function CPU limits" + }, + { + "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-shutdown-reasons-explained", + "title": "Edge Function shutdown reasons explained" + }, + { + "url": "https://supabase.com/docs/guides/troubleshooting/edge-function-546-error-response", + "title": "546 - WORKER_RESOURCE_LIMIT Exceeded / WORKER_LIMIT Exceeded" + }, + { + "url": "https://supabase.com/docs/guides/functions/status-codes", + "title": "Status codes" + } + ], + "resultChars": 31353 + } + ] }, "prompt": "Our `video-thumbnails` edge function has been failing intermittently since this morning. It generates a thumbnail from a user-uploaded video, and about half the calls are erroring out.\n\nCan you investigate the project logs and tell me what's going on and what we should do about it?", "promptSourcePath": "evals/investigate-functions-001-546-resource-limit/PROMPT.md", @@ -402,7 +311,7 @@ { "name": "diagnosed secure default grants without weakening RLS", "passed": true, - "judgeNotes": "Diagnosed missing Data API/table grants separately from RLS, preserved owner-scoped RLS, granted only SELECT/INSERT on public.journal_entries to authenticated, did not grant anon/public or weaken/disable RLS, and verified behavior." + "judgeNotes": "The answer correctly diagnoses missing authenticated table grants / secure-by-default Data API exposure, distinguishes grants from RLS, preserves owner-scoped RLS, grants only SELECT and INSERT on public.journal_entries to authenticated, and does not weaken policies or grant anon/public." } ], "skills": { @@ -410,9 +319,7 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [] @@ -450,7 +357,7 @@ { "name": "user A's update actually checks off their own task", "passed": true, - "notes": "saw: [{\"id\":\"8326f375-f2b1-45eb-9a76-8484d1f56545\",\"is_done\":true}]" + "notes": "saw: [{\"id\":\"58a3dfff-fdea-426b-8da6-defa70ac3faa\",\"is_done\":true}]" }, { "name": "user B cannot update user A's task", @@ -459,7 +366,7 @@ { "name": "diagnosed the missing USING clause and added it", "passed": true, - "judgeNotes": "Diagnosed the zero-row UPDATE as caused by the tasks UPDATE RLS policy missing a USING clause while only having WITH CHECK, explained the USING vs WITH CHECK behavior, and fixed it with a policy scoped to authenticated users using (user_id = auth.uid()) while preserving with check (user_id = auth.uid()) and keeping RLS intact." + "judgeNotes": "Diagnosed the zero-row UPDATE as an UPDATE RLS policy missing a USING clause, explained WITH CHECK vs USING semantics, and fixed it with USING (user_id = auth.uid()) while preserving WITH CHECK (user_id = auth.uid()) and authenticated/owner scoping." } ], "skills": { @@ -467,39 +374,37 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"row level security policy update missing USING clause WITH CHECK\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"row level security policy UPDATE USING WITH CHECK default\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ - { - "url": "https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0024_permissive_rls_policy", - "title": "Database Advisor: Lint 0024_permissive_rls_policy" - }, { "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", "title": "Row Level Security" }, { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" + "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", + "title": "Token Security and Row Level Security" }, { "url": "https://supabase.com/docs/guides/troubleshooting/rls-simplified-BJTcS8", "title": "RLS Simplified" }, { - "url": "https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0007_policy_exists_rls_disabled", - "title": "Database Advisor: Lint 0007_policy_exists_rls_disabled" + "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", + "title": "Advanced pgTAP Testing" + }, + { + "url": "https://supabase.com/docs/guides/api/securing-your-api", + "title": "Securing your API" } ], - "resultChars": 59228 + "resultChars": 87538 } ] }, @@ -531,7 +436,7 @@ { "name": "answered unhealthy project recovery question safely", "passed": true, - "judgeNotes": "The answer gives safe, docs-grounded recovery guidance: it explicitly says pause/restore is not the right step, recommends restart first, distinguishes restart from heavier backup/reactivation restore behavior, and suggests checking resource pressure and contacting support if unresolved." + "judgeNotes": "Answers with safe guidance: restart first, not pause/restore; distinguishes restore/pause from restart; recommends scaling/tuning and support if unresolved. Concrete first step is provided." } ], "skills": { @@ -546,60 +451,43 @@ "docs": { "calls": [ { - "source": "search_docs", - "query": "{ searchDocs(query: \"restart project unhealthy pause restore\", limit: 5) { nodes { title href content } } }", - "hasContent": true, + "source": "web_search", + "query": "Supabase project unhealthy dashboard restart vs pause restore", + "hasContent": false, "pages": [ { - "url": "https://supabase.com/docs/guides/platform/upgrading", - "title": "Upgrading" + "url": "https://supabase.com/docs/guides/troubleshooting/restore-project-after-90-days-pause", + "title": "Supabase Docs | Troubleshooting | How To Restore Project After 90-Day Pause" }, { - "url": "https://supabase.com/docs/guides/platform/free-project-pausing", - "title": "Project Pausing" + "url": "https://supabase.com/docs/guides/troubleshooting/project-status-reports-unhealthy-services", + "title": "Supabase Docs | Troubleshooting | Project Status reports unhealthy services" }, { - "url": "https://supabase.com/docs/guides/platform/delete-project", - "title": "Deleting Your Project" + "url": "https://supabase.com/docs/guides/troubleshooting/pausing-pro-projects-vNL-2a", + "title": "Supabase Docs | Troubleshooting | Pausing Pro-Projects" }, { - "url": "https://supabase.com/docs/guides/local-development/restoring-downloaded-backup", - "title": "Restoring a downloaded backup locally" + "url": "https://supabase.com/docs/guides/platform/free-project-pausing", + "title": "Project Pausing | Supabase Docs" }, { - "url": "https://supabase.com/docs/guides/platform/backups", - "title": "Database Backups" + "url": "https://supabase.com/docs/guides/troubleshooting", + "title": "Supabase Docs | Troubleshooting" } ], - "resultChars": 32704 + "resultChars": 4654 }, { - "source": "search_docs", - "query": "{ searchDocs(query: \"project unhealthy restart dashboard unresponsive troubleshooting\", limit: 5) { nodes { title href content } } }", + "source": "web_fetch", + "query": "What does this page recommend doing when a project shows unhealthy services? Does it recommend restart or pause/restore, and what are the risks of each?", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/troubleshooting/project-status-reports-unhealthy-services", - "title": "Project Status reports unhealthy services" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/failed-to-run-sql-query-connection-terminated-due-to-connection-timeout", - "title": "Error: Failed to run sql query: Connection terminated due to connection timeout" - }, - { - "url": "https://supabase.com/docs/guides/troubleshooting/supabase-dashboard-not-loading-project-not-loading-on-dashboard-LfMq9F", - "title": "Supabase dashboard not loading/ Project not loading on dashboard" - }, - { - "url": "https://supabase.com/docs/guides/platform/sso/multiple-providers", - "title": "Multiple SSO Providers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", - "title": "Configure S3 Storage" + "url": "https://supabase.com/docs/guides/troubleshooting/project-status-reports-unhealthy-services" } ], - "resultChars": 34604 + "resultChars": 2155 } ] }, @@ -649,7 +537,7 @@ { "name": "user A can replace their own avatar via upsert", "passed": true, - "notes": "saw: [{\"name\":\"019f8f6b-0da1-73fd-b22e-a8ebfbcd53e6/avatar.png\",\"metadata\":{\"version\":\"replacement\"}}]" + "notes": "saw: [{\"name\":\"019f9488-1036-7633-8c70-b9e3b90eda77/avatar.png\",\"metadata\":{\"version\":\"replacement\"}}]" }, { "name": "user B cannot overwrite user A's avatar", @@ -658,7 +546,7 @@ { "name": "added an owner-scoped UPDATE policy without weakening public reads", "passed": true, - "judgeNotes": "Diagnosed missing UPDATE RLS policy for upsert replacement, noted public bucket only affects reads, kept public-read/RLS intact, and added authenticated owner-scoped UPDATE policy with USING and WITH CHECK." + "judgeNotes": "The answer correctly diagnoses missing UPDATE RLS policy for upsert replacement, notes public bucket only affects reads, adds an authenticated owner-scoped UPDATE policy with USING and WITH CHECK, and keeps public read/RLS intact." } ], "skills": { @@ -666,9 +554,7 @@ "supabase", "supabase-postgres-best-practices" ], - "loaded": [ - "supabase" - ] + "loaded": [] }, "docs": { "calls": [] @@ -717,17 +603,17 @@ { "name": "user with JWT reads only their own rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"4cbcfda6-5bde-4706-aadc-d0c3d58f1c19\",\"metric\":\"steps_a_mrxmh45d\",\"value\":111}]}" + "notes": "status 200: [{\"user_id\":\"b2f476da-e64d-4d4e-ad97-57cbf0511e66\",\"metric\":\"steps_a_mrz1ja7o\",\"value\":111}]" }, { "name": "user cannot read another user's rows by passing user_id", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"4cbcfda6-5bde-4706-aadc-d0c3d58f1c19\",\"metric\":\"steps_a_mrxmh45d\",\"value\":111}]}" + "notes": "status 200: [{\"user_id\":\"b2f476da-e64d-4d4e-ad97-57cbf0511e66\",\"metric\":\"steps_a_mrz1ja7o\",\"value\":111}]" }, { "name": "service key bypasses RLS to read the target user's rows", "passed": true, - "notes": "status 200: {\"data\":[{\"user_id\":\"45751830-2003-4463-bb10-512c6c1527da\",\"metric\":\"steps_b_mrxmh45d\",\"value\":222}]}" + "notes": "status 200: [{\"user_id\":\"a26435fd-7c34-47bc-812b-3d59fd652da3\",\"metric\":\"steps_b_mrz1ja7o\",\"value\":222}]" }, { "name": "non-service key is not granted service access", @@ -755,150 +641,7 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"@supabase/server withSupabase edge function\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions", - "title": "Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/connect-to-postgres", - "title": "Integrating with Supabase Database (Postgres)" - }, - { - "url": "https://supabase.com/docs/guides/functions/quickstart-dashboard", - "title": "Getting Started with Edge Functions (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/functions/recursive-functions", - "title": "Recursive / Nested Function Calls" - }, - { - "url": "https://supabase.com/docs/guides/functions/architecture", - "title": "Edge Functions Architecture" - }, - { - "url": "https://supabase.com/docs/guides/database/connecting-to-postgres/serverless-drivers", - "title": "Serverless Drivers" - }, - { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" - } - ], - "resultChars": 84300 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"publishable key secret key edge function apikey header\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/api-keys", - "title": "Understanding API keys" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-envoy", - "title": "Envoy API Gateway" - }, - { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth-headers", - "title": "Authorization headers" - }, - { - "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys", - "title": "New API Keys and Asymmetric Authentication" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-kotlin", - "title": "Build a Product Management Android App with Jetpack Compose" - }, - { - "url": "https://supabase.com/docs/guides/functions/quickstart-dashboard", - "title": "Getting Started with Edge Functions (Dashboard)" - }, - { - "url": "https://supabase.com/docs/guides/database/secure-data", - "title": "Securing your data" - } - ], - "resultChars": 172535 - }, - { - "source": "search_docs", - "query": "{ searchDocs(query: \"@supabase/server reference withSupabase auth mode service admin client requireUser optional\", limit: 10) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys", - "title": "Migrating to publishable and secret API keys" - }, - { - "url": "https://supabase.com/docs/guides/functions/auth", - "title": "Securing Edge Functions" - }, - { - "url": "https://supabase.com/docs/guides/auth/choosing-a-server-package", - "title": "Which package to use" - }, - { - "url": "https://supabase.com/docs/guides/auth/passkeys", - "title": "Passkey authentication" - }, - { - "url": "https://supabase.com/docs/reference/javascript/oauth-admin-createclient" - }, - { - "url": "https://supabase.com/docs/guides/platform/migrating-to-supabase/auth0", - "title": "Migrate from Auth0 to Supabase Auth" - }, - { - "url": "https://supabase.com/docs/guides/getting-started/tutorials/with-nuxt-3", - "title": "Build a User Management App with Nuxt 3" - }, - { - "url": "https://supabase.com/docs/guides/database/postgres/roles", - "title": "Postgres Roles" - }, - { - "url": "https://supabase.com/docs/reference/javascript/auth-getuser" - }, - { - "url": "https://supabase.com/docs/guides/auth", - "title": "Auth" - } - ], - "resultChars": 12 - } - ] + "calls": [] }, "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nImplement it with the **`@supabase/server`** package, which is built for exactly\nthis kind of multi-auth Edge Function. Import it directly in your function:\n\n```ts\nimport { withSupabase } from \"npm:@supabase/server\";\n```\n\nOur product stores per-user metrics in a `user_stats` table that already exists\n(see `supabase/migrations/`), protected by row-level security so a user can read\nonly their own rows.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.\n\nGet the local stack running so the function is reachable at the path above.", "promptSourcePath": "evals/build-functions-006-dual-auth-with-server/PROMPT.md", @@ -934,7 +677,7 @@ { "name": "did not recommend read replicas for Realtime", "passed": true, - "judgeNotes": "The answer correctly treats the task as Supabase Realtime/Postgres Changes, adds the messages table to the supabase_realtime publication, and provides a postgres_changes subscription example. It does not recommend or imply read replicas are needed/useful for live updates, nor confuse them with logical replication/publications." + "judgeNotes": "The answer correctly treats the issue as Supabase Realtime/Postgres Changes setup, adds the messages table to the supabase_realtime publication, provides a postgres_changes client subscription, and does not recommend or imply read replicas are needed." } ], "skills": { @@ -972,17 +715,17 @@ { "name": "identified video-thumbnails and the 546 resource-limit pattern", "passed": true, - "judgeNotes": "Identified `video-thumbnails` as the affected function and correctly recognized HTTP 546 worker/resource/CPU limit errors, not 500/503 server errors." + "judgeNotes": "The assistant correctly identified `video-thumbnails` as the affected function and explicitly recognized failures as HTTP `546` due to a resource/CPU time limit, not 500s/503s or a generic server error." }, { "name": "attributed the 546s to CPU time exhaustion", "passed": true, - "judgeNotes": "The assistant specifically attributes the 546 failures to CPU time exhaustion, citing log evidence including `shutdown (reason: CPUTime, cpu_time_used: 2000ms, cpu_time_limit: 2000ms)` and explicitly distinguishing it from wall-clock timeout." + "judgeNotes": "The assistant explicitly attributes the 546 failures to CPU time exhaustion, citing log evidence: shutdown reason CPUTime with cpu_time_used 2000ms and cpu_time_limit 2000ms. It distinguishes this from wall-clock timeout and avoids blaming memory, bugs, or unrelated errors." }, { "name": "recommended reducing/offloading CPU work as the fix", "passed": true, - "judgeNotes": "The assistant explicitly recommends offloading thumbnail generation to a background worker/external container and reducing per-call CPU cost, rather than retrying or raising limits." + "judgeNotes": "The assistant clearly identifies CPUTime limit and recommends offloading thumbnail extraction to a background job/external compute service, with optional guards to reduce inline work. This directly reduces/offloads CPU-intensive work rather than suggesting timeout/resource ceiling changes." } ], "skills": { @@ -1064,7 +807,7 @@ { "name": "diagnosed secure default grants without weakening RLS", "passed": true, - "judgeNotes": "Diagnoses missing table grants vs RLS, preserves owner-scoped RLS, grants SELECT/INSERT to authenticated only for client access, and keeps RLS enabled. Also recognizes secure-by-default/non-auto-exposed behavior implicitly via missing underlying PostgREST grants, without granting anon/public or weakening policies." + "judgeNotes": "Diagnoses secure-by-default missing table grants, distinguishes grants from RLS, preserves owner-scoped RLS, grants only SELECT/INSERT on public.journal_entries to authenticated, and does not weaken RLS or grant anon/public." } ], "skills": { @@ -1107,7 +850,7 @@ { "name": "user A's update actually checks off their own task", "passed": true, - "notes": "saw: [{\"id\":\"0857b42f-c7bb-4778-ba57-278fde808b35\",\"is_done\":true}]" + "notes": "saw: [{\"id\":\"7a02851e-f80a-4dac-835a-4746d5244369\",\"is_done\":true}]" }, { "name": "user B cannot update user A's task", @@ -1116,7 +859,7 @@ { "name": "diagnosed the missing USING clause and added it", "passed": true, - "judgeNotes": "Diagnosed the silent zero-row UPDATE as an UPDATE RLS policy missing a USING clause, explained WITH CHECK vs USING, and fixed it with a policy scoped to authenticated users and user_id = auth.uid(), preserving WITH CHECK and RLS." + "judgeNotes": "The assistant correctly identified the missing USING clause on the UPDATE RLS policy, explained why WITH CHECK alone causes zero-row updates, and fixed it with USING (user_id = auth.uid()) while preserving WITH CHECK and authenticated/user ownership scoping." } ], "skills": { @@ -1127,31 +870,31 @@ "calls": [ { "source": "search_docs", - "query": "{ searchDocs(query: \"RLS UPDATE policy USING WITH CHECK missing USING clause\", limit: 5) { nodes { title href content } } }", + "query": "{ searchDocs(query: \"RLS policy UPDATE using with check default\", limit: 5) { nodes { title href content } } }", "hasContent": true, "pages": [ { - "url": "https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0024_permissive_rls_policy", - "title": "Database Advisor: Lint 0024_permissive_rls_policy" + "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", + "title": "Row Level Security" }, { - "url": "https://supabase.com/docs/guides/troubleshooting/rls-simplified-BJTcS8", - "title": "RLS Simplified" + "url": "https://supabase.com/docs/guides/auth/oauth-server/token-security", + "title": "Token Security and Row Level Security" }, { - "url": "https://supabase.com/docs/guides/api/securing-your-api", - "title": "Securing your API" + "url": "https://supabase.com/docs/guides/storage/security/access-control", + "title": "Storage Access Control" }, { - "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", - "title": "Row Level Security" + "url": "https://supabase.com/docs/guides/api/securing-your-api", + "title": "Securing your API" }, { - "url": "https://supabase.com/docs/guides/self-hosting/restore-from-platform", - "title": "Restore a Platform Project to Self-Hosted" + "url": "https://supabase.com/docs/guides/troubleshooting/rls-simplified-BJTcS8", + "title": "RLS Simplified" } ], - "resultChars": 67418 + "resultChars": 68481 } ] }, @@ -1183,7 +926,7 @@ { "name": "answered unhealthy project recovery question safely", "passed": true, - "judgeNotes": "The answer clearly says pause/restore is not the right first recovery step, distinguishes restart from restore/pause, recommends restart plus logs/advisors and checking overload/sizing, and suggests contacting support if unable to restart. It is concrete and safe, though it could mention waiting/reducing workload more explicitly." + "judgeNotes": "The answer safely says restart first, distinguishes restart from pause/restore, warns against jumping to pause/restore, and gives concrete recovery steps including Management API restart and contacting support." } ], "skills": { @@ -1191,40 +934,11 @@ "loaded": [] }, "docs": { - "calls": [ - { - "source": "search_docs", - "query": "{ searchDocs(query: \"project unhealthy restart pause restore dashboard unusable\", limit: 5) { nodes { title href content } } }", - "hasContent": true, - "pages": [ - { - "url": "https://supabase.com/docs/guides/troubleshooting/project-status-reports-unhealthy-services", - "title": "Project Status reports unhealthy services" - }, - { - "url": "https://supabase.com/docs/guides/platform/backups", - "title": "Database Backups" - }, - { - "url": "https://supabase.com/docs/guides/platform/upgrading", - "title": "Upgrading" - }, - { - "url": "https://supabase.com/docs/guides/platform/free-project-pausing", - "title": "Project Pausing" - }, - { - "url": "https://supabase.com/docs/guides/local-development/restoring-downloaded-backup", - "title": "Restoring a downloaded backup locally" - } - ], - "resultChars": 28996 - } - ] + "calls": [] }, "prompt": "My Supabase dashboard says my project is unhealthy, and the dashboard is unusable.\n\nWould restart or pause/restore be better?", "promptSourcePath": "evals/resolve-reliability-001-unhealthy-project-recovery/PROMPT.md", - "attempts": 1, + "attempts": 2, "sourcePath": "claude-code-sonnet-5-no-skills/resolve-reliability-001-unhealthy-project-recovery.json" }, { @@ -1268,7 +982,7 @@ { "name": "user A can replace their own avatar via upsert", "passed": true, - "notes": "saw: [{\"name\":\"019f8f6a-de4a-72d5-b12a-29589a17a64c/avatar.png\",\"metadata\":{\"version\":\"replacement\"}}]" + "notes": "saw: [{\"name\":\"019f9487-fa91-71ee-944d-9e7fdd84d49e/avatar.png\",\"metadata\":{\"version\":\"replacement\"}}]" }, { "name": "user B cannot overwrite user A's avatar", @@ -1277,7 +991,7 @@ { "name": "added an owner-scoped UPDATE policy without weakening public reads", "passed": true, - "judgeNotes": "The answer correctly diagnoses missing UPDATE RLS policy on storage.objects for Supabase Storage upsert, notes public bucket only affects reads, keeps public-read/RLS intact, and adds an authenticated owner-scoped UPDATE policy with USING and WITH CHECK." + "judgeNotes": "Diagnosed missing UPDATE RLS policy for upsert on storage.objects, noted public bucket only affects reads, and added an authenticated owner-scoped UPDATE policy with USING and WITH CHECK while keeping public read/RLS intact." } ], "skills": { diff --git a/packages/core/src/agents/claude-code/index.ts b/packages/core/src/agents/claude-code/index.ts index edde756e..dec49551 100644 --- a/packages/core/src/agents/claude-code/index.ts +++ b/packages/core/src/agents/claude-code/index.ts @@ -5,29 +5,34 @@ * to parse Claude Code transcripts. */ -import type { Model as AnthropicModel } from '@anthropic-ai/sdk/resources/messages'; import type { AgentHarness } from '../../index.js'; import type { ReasoningEffortLevel } from '../../eval-metadata.js'; import { createCliAgent } from '../engine.js'; import type { AgentDefinition } from '../types.js'; -import { claudeCodeRunner } from './runner.js'; +import { claudeCodeRunner, type ClaudeCodeModel } from './runner.js'; import { claudeCodeParser } from './parser.js'; /** Claude Code as an `AgentHarness`. */ export function claudeCodeAgent( options: { - /** Anthropic model id (typed from `@anthropic-ai/sdk`). Defaults to Sonnet. */ - model?: AnthropicModel; + /** + * Anthropic model id (typed from `@anthropic-ai/sdk`). Defaults to Sonnet. + * With `gateway`, any AI Gateway `vendor/model` slug is accepted. + */ + model?: ClaudeCodeModel; /** Reasoning effort (`--effort`). Omit to use Claude Code's own default. */ reasoningEffort?: ReasoningEffortLevel; /** Override the pinned CLI version. */ cliVersion?: string; + /** Route through the Vercel AI Gateway instead of the Anthropic API. */ + gateway?: boolean; } = {} ): AgentHarness { return createCliAgent(claudeCodeRunner, claudeCodeParser, { model: options.model ?? claudeCodeRunner.defaultModel, reasoningEffort: options.reasoningEffort, cliVersion: options.cliVersion, + gateway: options.gateway, }); } diff --git a/packages/core/src/agents/claude-code/runner.test.ts b/packages/core/src/agents/claude-code/runner.test.ts index 07450363..3bb6e0fc 100644 --- a/packages/core/src/agents/claude-code/runner.test.ts +++ b/packages/core/src/agents/claude-code/runner.test.ts @@ -36,6 +36,43 @@ function streamJson(subtype: string, isError = false): string { ].join('\n'); } +describe('claudeCodeRunner.exec env', () => { + async function execEnv(gateway: boolean): Promise> { + let env: Record = {}; + await claudeCodeRunner.exec({ + sandbox: { + workspace: '/w', + exec: async (_cmd, options) => { + if (options?.env) env = options.env; + return { ...ok, stdout: streamJson('success') }; + }, + readFile: async () => '', + }, + model: 'anthropic/claude-sonnet-5', + apiKey: 'test-key', + gateway, + systemPromptPath: '/s', + userPromptPath: '/u', + mcpServers: {}, + timeoutSec: 1, + }); + return env; + } + + it('routes through the AI Gateway when gateway is set', async () => { + expect(await execEnv(true)).toEqual({ + ANTHROPIC_BASE_URL: 'https://ai-gateway.vercel.sh', + ANTHROPIC_AUTH_TOKEN: 'test-key', + // Must be empty: Claude Code prefers ANTHROPIC_API_KEY when non-empty. + ANTHROPIC_API_KEY: '', + }); + }); + + it('keeps the direct Anthropic env otherwise', async () => { + expect(await execEnv(false)).toEqual({ ANTHROPIC_API_KEY: 'test-key' }); + }); +}); + describe('claudeCodeRunner.deriveStopReason', () => { const derive = claudeCodeRunner.deriveStopReason!; diff --git a/packages/core/src/agents/claude-code/runner.ts b/packages/core/src/agents/claude-code/runner.ts index 37129ca1..cb3b2ead 100644 --- a/packages/core/src/agents/claude-code/runner.ts +++ b/packages/core/src/agents/claude-code/runner.ts @@ -8,6 +8,7 @@ import type { Model as AnthropicModel } from '@anthropic-ai/sdk/resources/messag import type { McpServerConfig } from '../../index.js'; import { parseJsonlRecords } from '../../json.js'; import type { AgentRunner } from '../types.js'; +import { AI_GATEWAY, type GatewayModelId } from '../gateway.js'; import { npmGlobalBin, npmInstallGlobal, @@ -18,7 +19,10 @@ import { const MCP_CONFIG_PATH = '"$HOME/.eval/mcp.json"'; -export const claudeCodeRunner: AgentRunner = { +/** Anthropic ids for the direct path; typed gateway slugs for gateway mode. */ +export type ClaudeCodeModel = AnthropicModel | GatewayModelId; + +export const claudeCodeRunner: AgentRunner = { id: 'claude-code', displayName: 'Claude Code', apiKeyEnvVar: 'ANTHROPIC_API_KEY', @@ -40,6 +44,7 @@ export const claudeCodeRunner: AgentRunner = { sandbox, model, apiKey, + gateway, systemPromptPath, userPromptPath, mcpServers, @@ -78,11 +83,21 @@ export const claudeCodeRunner: AgentRunner = { ].join(' '); // Prompt on stdin: `claude -p` with no positional reads it from stdin. + // Gateway mode swaps the endpoint + auth env only (Vercel's documented + // recipe: the key goes in ANTHROPIC_AUTH_TOKEN, and ANTHROPIC_API_KEY must + // be the empty string or Claude Code prefers it over the auth token). + const env: Record = gateway + ? { + ANTHROPIC_BASE_URL: AI_GATEWAY.baseUrl, + ANTHROPIC_AUTH_TOKEN: apiKey, + ANTHROPIC_API_KEY: '', + } + : { ANTHROPIC_API_KEY: apiKey }; const command = await sandbox.exec( `cat ${userPromptPath} | ${claude} ${flags}`, { timeoutMs: timeoutSec * 1000, - env: { ANTHROPIC_API_KEY: apiKey }, + env, } ); return { command, raw: command.stdout }; diff --git a/packages/core/src/agents/codex/index.ts b/packages/core/src/agents/codex/index.ts index 6e26eb7c..5d4f4b70 100644 --- a/packages/core/src/agents/codex/index.ts +++ b/packages/core/src/agents/codex/index.ts @@ -21,12 +21,15 @@ export function codexAgent( reasoningEffort?: ReasoningEffortLevel; /** Override the pinned CLI version. */ cliVersion?: string; + /** Route through the Vercel AI Gateway instead of the OpenAI API. */ + gateway?: boolean; } = {} ): AgentHarness { return createCliAgent(codexRunner, codexParser, { model: options.model ?? codexRunner.defaultModel, reasoningEffort: options.reasoningEffort, cliVersion: options.cliVersion, + gateway: options.gateway, }); } diff --git a/packages/core/src/agents/codex/runner.test.ts b/packages/core/src/agents/codex/runner.test.ts new file mode 100644 index 00000000..517039a3 --- /dev/null +++ b/packages/core/src/agents/codex/runner.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from 'vitest'; +import { codexRunner } from './runner.js'; +import type { AgentSandbox } from '../types.js'; +import type { CommandResult } from '../../index.js'; + +const ok: CommandResult = { ok: true, exitCode: 0, stdout: '', stderr: '' }; + +/** Records every exec call (command + env) and every file written. */ +function recordingSandbox() { + const calls: Array<{ command: string; env?: Record }> = []; + const sandbox: AgentSandbox = { + workspace: '/w', + exec: async (command, options) => { + calls.push({ command, env: options?.env }); + return ok; + }, + readFile: async () => '', + }; + return { sandbox, calls }; +} + +describe('codexRunner gateway mode', () => { + it('skips `codex login` on install (env_key auth handles it)', async () => { + const { sandbox, calls } = recordingSandbox(); + await codexRunner.install(sandbox, '0.138.0', 'gw-key', true); + expect(calls.some((c) => c.command.includes('login'))).toBe(false); + }); + + it('still logs in with the OpenAI key on direct install', async () => { + const { sandbox, calls } = recordingSandbox(); + await codexRunner.install(sandbox, '0.138.0', 'sk-openai', false); + const login = calls.find((c) => c.command.includes('login --with-api-key')); + expect(login?.env).toEqual({ OPENAI_API_KEY: 'sk-openai' }); + }); + + it('writes the Vercel provider config and passes the gateway key', async () => { + const { sandbox, calls } = recordingSandbox(); + await codexRunner.exec({ + sandbox, + model: 'openai/gpt-5.4-mini', + apiKey: 'gw-key', + gateway: true, + systemPromptPath: '/s', + userPromptPath: '/u', + mcpServers: {}, + timeoutSec: 1, + }); + + // Config is staged base64-encoded (writeSandboxFile) into ~/.codex/config.toml. + const configWrite = calls.find((c) => c.command.includes('config.toml')); + const encoded = /printf %s '([^']+)'/.exec(configWrite?.command ?? '')?.[1]; + const toml = Buffer.from(encoded ?? '', 'base64').toString('utf8'); + expect(toml).toContain('model_provider = "vercel"'); + expect(toml).toContain('base_url = "https://ai-gateway.vercel.sh/v1"'); + expect(toml).toContain('env_key = "AI_GATEWAY_API_KEY"'); + expect(toml).toContain('wire_api = "responses"'); + + const run = calls.find((c) => c.command.includes(' exec ')); + expect(run?.env).toEqual({ AI_GATEWAY_API_KEY: 'gw-key' }); + }); + + it('writes no config and keeps the OpenAI env when direct', async () => { + const { sandbox, calls } = recordingSandbox(); + await codexRunner.exec({ + sandbox, + model: 'gpt-5.4-mini', + apiKey: 'sk-openai', + gateway: false, + systemPromptPath: '/s', + userPromptPath: '/u', + mcpServers: {}, + timeoutSec: 1, + }); + expect(calls.some((c) => c.command.includes('config.toml'))).toBe(false); + const run = calls.find((c) => c.command.includes(' exec ')); + expect(run?.env).toEqual({ OPENAI_API_KEY: 'sk-openai' }); + }); +}); diff --git a/packages/core/src/agents/codex/runner.ts b/packages/core/src/agents/codex/runner.ts index f4e3837e..3530161f 100644 --- a/packages/core/src/agents/codex/runner.ts +++ b/packages/core/src/agents/codex/runner.ts @@ -11,6 +11,7 @@ import type { ChatModel } from 'openai/resources/shared'; import type { McpServerConfig } from '../../index.js'; import { parseJsonlRecords } from '../../json.js'; import type { AgentRunner } from '../types.js'; +import { AI_GATEWAY, type GatewayModelId } from '../gateway.js'; import { npmGlobalBin, npmInstallGlobal, @@ -19,8 +20,9 @@ import { writeSandboxFile, } from '../shared.js'; -// ChatModel is a closed union; widen so newer/codex-specific ids still type. -export type CodexModel = ChatModel | (string & {}); +// ChatModel is a closed union; widen with the typed gateway slugs (which carry +// their own `(string & {})` fallback, so newer/codex-specific ids still type). +export type CodexModel = ChatModel | GatewayModelId; const CODEX_CONFIG_PATH = '"$HOME/.codex/config.toml"'; @@ -34,12 +36,15 @@ export const codexRunner: AgentRunner = { defaultCliVersion: '0.138.0', defaultModel: 'gpt-5.4', - async install(sandbox, version, apiKey) { + async install(sandbox, version, apiKey, gateway) { await npmInstallGlobal( sandbox, `${this.cliPackage}@${version}`, this.displayName ); + // Gateway mode authenticates via the custom provider's `env_key` at exec + // time; there is no OpenAI account to log in to. + if (gateway) return; // Persist API-key auth to ~/.codex/auth.json (read the key from stdin so it // never lands in argv or the process table). const codex = npmGlobalBin('codex'); @@ -58,6 +63,7 @@ export const codexRunner: AgentRunner = { sandbox, model, apiKey, + gateway, systemPromptPath, userPromptPath, mcpServers, @@ -65,15 +71,20 @@ export const codexRunner: AgentRunner = { timeoutSec, }) { const codex = npmGlobalBin('codex'); - if (Object.keys(mcpServers).length > 0) { + if (gateway || Object.keys(mcpServers).length > 0) { await sandbox.exec(`mkdir -p "$HOME/.codex"`); await writeSandboxFile( sandbox, CODEX_CONFIG_PATH, - buildCodexConfig(mcpServers) + buildCodexConfig(mcpServers, { gateway }) ); } + // The gateway's catalog is slug-addressed; a bare id (an env-flipped + // direct experiment, e.g. "gpt-5.4-mini") is an OpenAI id by construction. + const resolvedModel = + gateway && !model.includes('/') ? `openai/${model}` : model; + const flags = [ 'exec', '--json', @@ -81,7 +92,7 @@ export const codexRunner: AgentRunner = { '--skip-git-repo-check', // The sandbox is the isolation boundary — let Codex run commands freely. '--dangerously-bypass-approvals-and-sandbox', - `-m ${shellQuote(model)}`, + `-m ${shellQuote(resolvedModel)}`, // Reasoning effort via config override; omitted leaves Codex's default. // The value is parsed as TOML, so pass it as a quoted TOML string. ...(reasoningEffort @@ -95,7 +106,14 @@ export const codexRunner: AgentRunner = { // both staged as files, fed on stdin. const command = await sandbox.exec( `{ cat ${systemPromptPath}; printf '\\n\\n'; cat ${userPromptPath}; } | ${codex} ${flags}`, - { timeoutMs: timeoutSec * 1000, env: { OPENAI_API_KEY: apiKey } } + { + timeoutMs: timeoutSec * 1000, + // Gateway mode: the config.toml provider block reads the gateway key + // from AI_GATEWAY_API_KEY (its `env_key`). + env: gateway + ? { [AI_GATEWAY.apiKeyEnvVar]: apiKey } + : { OPENAI_API_KEY: apiKey }, + } ); return { command, raw: command.stdout }; }, @@ -132,14 +150,33 @@ function terminalOutcome( } /** - * Codex's `~/.codex/config.toml` MCP schema: + * Codex's `~/.codex/config.toml`. Gateway mode prepends a custom model + * provider pointing at the AI Gateway's OpenAI-compatible endpoint (Vercel's + * documented recipe — Responses API wire format, key via `env_key`). MCP + * servers follow, in Codex's schema: * [mcp_servers.] * command = "npx" * args = ["…"] * env = { KEY = "val" } */ -function buildCodexConfig(servers: Record): string { +function buildCodexConfig( + servers: Record, + options: { gateway?: boolean } = {} +): string { const blocks: string[] = []; + if (options.gateway) { + blocks.push( + [ + `model_provider = "vercel"`, + ``, + `[model_providers.vercel]`, + `name = "Vercel AI Gateway"`, + `base_url = ${tomlString(AI_GATEWAY.openAiBaseUrl)}`, + `env_key = ${tomlString(AI_GATEWAY.apiKeyEnvVar)}`, + `wire_api = "responses"`, + ].join('\n') + ); + } for (const [name, server] of Object.entries(servers)) { const lines = [ `[mcp_servers.${tomlKey(name)}]`, diff --git a/packages/core/src/agents/engine.ts b/packages/core/src/agents/engine.ts index 36dffe39..be081445 100644 --- a/packages/core/src/agents/engine.ts +++ b/packages/core/src/agents/engine.ts @@ -24,6 +24,11 @@ import type { ModelProvider, ReasoningEffortLevel } from '../eval-metadata.js'; import { adaptTranscript } from '../parsers/adapt.js'; import type { AgentTranscriptParser } from '../parsers/types.js'; import type { AgentRunner } from './types.js'; +import { + gatewayModelProvider, + requireGatewayApiKey, + runThroughGateway, +} from './gateway.js'; import { SCRATCH, SYSTEM_PROMPT_PATH, @@ -52,15 +57,25 @@ export function createCliAgent( model: M; cliVersion?: string; reasoningEffort?: ReasoningEffortLevel; + /** + * Route through the Vercel AI Gateway (see `./gateway.ts`). Omitted, the + * RUN_THROUGH_GATEWAY env flag decides; an explicit value pins the path. + */ + gateway?: boolean; } ): AgentHarness { const version = options.cliVersion ?? runner.defaultCliVersion; + const useGateway = options.gateway ?? runThroughGateway(); return { id: runner.id, modelId: options.model, metadata: { agent: runner.id, - modelProvider: modelProviderForAgent(runner.id), + // Through the gateway the model may be any vendor's; derive the vendor + // from the model slug instead of from the agent. + modelProvider: useGateway + ? gatewayModelProvider(options.model) + : modelProviderForAgent(runner.id), modelId: options.model, ...(options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } @@ -68,10 +83,10 @@ export function createCliAgent( }, runsInSandbox: true, assertReady() { - requireApiKey(runner); + requireApiKey(runner, useGateway); }, async run(args): Promise { - const apiKey = requireApiKey(runner); + const apiKey = requireApiKey(runner, useGateway); const sandbox = args.sandbox; if (!sandbox) { throw new Error( @@ -79,7 +94,7 @@ export function createCliAgent( ); } - await runner.install(sandbox, version, apiKey); + await runner.install(sandbox, version, apiKey, useGateway); // Stage the prompts into the sandbox scratch dir (outside the workspace). await sandbox.exec(`mkdir -p ${SCRATCH}`); @@ -90,6 +105,7 @@ export function createCliAgent( sandbox, model: options.model, apiKey, + gateway: useGateway, systemPromptPath: SYSTEM_PROMPT_PATH, userPromptPath: USER_PROMPT_PATH, // Rewrite loopback hosts so in-container MCP servers can reach host-side @@ -116,7 +132,8 @@ export function createCliAgent( }; } -function requireApiKey(runner: AgentRunner): string { +function requireApiKey(runner: AgentRunner, gateway = false): string { + if (gateway) return requireGatewayApiKey(runner.displayName); const apiKey = process.env[runner.apiKeyEnvVar]; if (!apiKey) { throw new Error( diff --git a/packages/core/src/agents/gateway.test.ts b/packages/core/src/agents/gateway.test.ts new file mode 100644 index 00000000..802de0f7 --- /dev/null +++ b/packages/core/src/agents/gateway.test.ts @@ -0,0 +1,71 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { + RUN_THROUGH_GATEWAY_ENV, + gatewayModelProvider, + runThroughGateway, + toGatewaySlug, +} from './gateway.js'; + +describe('gatewayModelProvider', () => { + it('parses the vendor from a gateway slug', () => { + expect(gatewayModelProvider('anthropic/claude-sonnet-5')).toBe('anthropic'); + expect(gatewayModelProvider('openai/gpt-5.4-mini')).toBe('openai'); + }); + + it('infers the vendor for bare Anthropic/OpenAI ids', () => { + expect(gatewayModelProvider('claude-sonnet-4-6')).toBe('anthropic'); + expect(gatewayModelProvider('gpt-5.4')).toBe('openai'); + expect(gatewayModelProvider('o4-mini')).toBe('openai'); + }); + + it("rejects vendors outside the benchmark's provider enum", () => { + expect(() => gatewayModelProvider('mistral/mistral-large')).toThrow( + /unsupported AI Gateway model vendor/ + ); + }); + + it('rejects bare ids it cannot attribute to a vendor', () => { + expect(() => gatewayModelProvider('mistral-large')).toThrow( + /vendor\/model slug/ + ); + }); +}); + +describe('toGatewaySlug', () => { + it('prefixes the vendor and swaps version dashes for dots', () => { + expect(toGatewaySlug('anthropic', 'claude-opus-4-8')).toBe( + 'anthropic/claude-opus-4.8' + ); + expect(toGatewaySlug('anthropic', 'claude-sonnet-5')).toBe( + 'anthropic/claude-sonnet-5' + ); + expect(toGatewaySlug('openai', 'gpt-5.4-mini')).toBe('openai/gpt-5.4-mini'); + // Non-version dashes (digit-letter) are untouched. + expect(toGatewaySlug('openai', 'gpt-4-turbo')).toBe('openai/gpt-4-turbo'); + }); + + it('passes existing slugs through unchanged', () => { + expect(toGatewaySlug('openai', 'openai/gpt-5.4-mini')).toBe( + 'openai/gpt-5.4-mini' + ); + }); +}); + +describe('runThroughGateway', () => { + afterEach(() => { + delete process.env[RUN_THROUGH_GATEWAY_ENV]; + }); + + it('is off by default and on for truthy values only', () => { + expect(runThroughGateway()).toBe(false); + for (const [value, expected] of [ + ['true', true], + ['1', true], + ['false', false], + ['', false], + ] as const) { + process.env[RUN_THROUGH_GATEWAY_ENV] = value; + expect(runThroughGateway()).toBe(expected); + } + }); +}); diff --git a/packages/core/src/agents/gateway.ts b/packages/core/src/agents/gateway.ts new file mode 100644 index 00000000..f1e9e4a9 --- /dev/null +++ b/packages/core/src/agents/gateway.ts @@ -0,0 +1,91 @@ +/** + * Vercel AI Gateway — the opt-in alternative to per-vendor API keys. + * + * The gateway lives side by side with the direct-provider path: every agent + * factory takes `gateway: true` to route through it, and nothing changes when + * the flag is off. All gateway knowledge (endpoints, key env var, model-slug + * conventions) lives in this one module so the whole feature can be removed — + * or the per-vendor path retired — by touching only the small call sites that + * import from here. + * + * Gateway model ids use `vendor/model` slugs (e.g. `anthropic/claude-sonnet-5`, + * `openai/gpt-5.4-mini`) with dots, not hyphens, in versions. The catalog is + * public: GET https://ai-gateway.vercel.sh/v1/models + */ + +import type { GatewayModelId } from 'ai'; +import type { ModelProvider } from '../eval-metadata.js'; +import { modelProviderSchema } from '../eval-metadata.js'; + +/** + * Typed gateway model slugs (`vendor/model`), from `@ai-sdk/gateway` via `ai`. + * The union is a snapshot of the catalog at the pinned SDK version — newer + * models (e.g. `anthropic/claude-sonnet-5`) don't autocomplete yet but still + * typecheck through the union's `(string & {})` fallback, mirroring how + * `CodexModel` widens OpenAI's `ChatModel`. + */ +export type { GatewayModelId }; + +export const AI_GATEWAY = { + /** One key for every vendor; also read by `@ai-sdk/gateway` automatically. */ + apiKeyEnvVar: 'AI_GATEWAY_API_KEY', + /** Anthropic-compatible surface (Claude Code's `ANTHROPIC_BASE_URL`). */ + baseUrl: 'https://ai-gateway.vercel.sh', + /** OpenAI-compatible surface (Codex's custom `model_providers` entry). */ + openAiBaseUrl: 'https://ai-gateway.vercel.sh/v1', +} as const; + +/** + * Env flag mirroring the eval-refresh workflow's `run_through_gateway` input + * (and the `run-evals-through-gateway` PR label): when truthy, every harness + * defaults to gateway routing without touching experiment files. An explicit + * per-experiment `gateway:` option still wins, so `gateway: false` pins an + * experiment to the direct path even under the flag. + */ +export const RUN_THROUGH_GATEWAY_ENV = 'RUN_THROUGH_GATEWAY'; + +export function runThroughGateway(): boolean { + const value = process.env[RUN_THROUGH_GATEWAY_ENV]?.trim().toLowerCase(); + return value === '1' || value === 'true' || value === 'yes'; +} + +/** + * Translate a direct vendor model id to its gateway slug: prefix the vendor + * and swap version dashes for dots (`claude-opus-4-8` → `anthropic/claude-opus-4.8`). + * Ids that are already slugs pass through unchanged. + */ +export function toGatewaySlug(vendor: ModelProvider, modelId: string): string { + if (modelId.includes('/')) return modelId; + return `${vendor}/${modelId.replace(/(\d)-(?=\d)/g, '$1.')}`; +} + +export function requireGatewayApiKey(displayName: string): string { + const apiKey = process.env[AI_GATEWAY.apiKeyEnvVar]; + if (!apiKey) { + throw new Error( + `Missing AI Gateway credentials for ${displayName}. Set ${AI_GATEWAY.apiKeyEnvVar} before running gateway evals.` + ); + } + return apiKey; +} + +/** + * The model's vendor, parsed from a gateway `vendor/model` slug. Bare + * Anthropic/OpenAI ids (e.g. `claude-sonnet-5`) are also accepted — the + * gateway's compat endpoints resolve them — so the prefix is optional there. + */ +export function gatewayModelProvider(model: string): ModelProvider { + const slash = model.indexOf('/'); + if (slash > 0) { + const vendor = modelProviderSchema.safeParse(model.slice(0, slash)); + if (vendor.success) return vendor.data; + throw new Error( + `unsupported AI Gateway model vendor in "${model}" (expected one of: ${modelProviderSchema.options.join(', ')})` + ); + } + if (model.startsWith('claude-')) return 'anthropic'; + if (model.startsWith('gpt-') || model.startsWith('o')) return 'openai'; + throw new Error( + `cannot infer vendor for AI Gateway model "${model}"; use a vendor/model slug (e.g. "anthropic/claude-sonnet-5")` + ); +} diff --git a/packages/core/src/agents/types.ts b/packages/core/src/agents/types.ts index aad716ed..69ca0858 100644 --- a/packages/core/src/agents/types.ts +++ b/packages/core/src/agents/types.ts @@ -54,6 +54,12 @@ export interface RunnerExecArgs { sandbox: AgentSandbox; model: M; apiKey: string; + /** + * Route through the Vercel AI Gateway instead of the vendor API. When set, + * `apiKey` is the gateway key (see `../gateway.ts`) and `model` may be a + * gateway `vendor/model` slug. Off = the unchanged per-vendor path. + */ + gateway?: boolean; /** Shell path to a file holding the system prompt (skills + task framing). */ systemPromptPath: string; /** Shell path to a file holding the user prompt (the task). */ @@ -92,7 +98,8 @@ export interface AgentRunner { install( sandbox: AgentSandbox, version: string, - apiKey: string + apiKey: string, + gateway?: boolean ): Promise; /** Run the CLI to completion and return the process result + raw transcript. */ exec(args: RunnerExecArgs): Promise; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c3ccfc0e..e881e0f2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,6 +15,7 @@ import { Experimental_StdioMCPTransport as StdioMCPTransport } from '@ai-sdk/mcp import { openai } from '@ai-sdk/openai'; import { Output, + gateway, generateText, stepCountIs, type JSONValue, @@ -44,6 +45,12 @@ import type { } from './eval-metadata.js'; import { reasoningEffortSchema } from './eval-metadata.js'; import type { AgentMetadata, AgentSandbox } from './agents/types.js'; +import { + AI_GATEWAY, + gatewayModelProvider, + runThroughGateway, + toGatewaySlug, +} from './agents/gateway.js'; import { isRecord } from './json.js'; // Resolved lazily on first use, not at module load: `import.meta.resolve` is a @@ -103,6 +110,8 @@ export type { DocsResultSandbox } from './docs-results.js'; export { createCliAgent } from './agents/engine.js'; export { claudeCodeAgent } from './agents/claude-code/index.js'; export { codexAgent } from './agents/codex/index.js'; +// Vercel AI Gateway (opt-in alternative to per-vendor keys; see agents/gateway.ts). +export { AI_GATEWAY, type GatewayModelId } from './agents/gateway.js'; export type { AgentMetadata, AgentSandbox, @@ -589,6 +598,11 @@ export async function judge(args: JudgeInput): Promise { } function getModelProvider(provider: string, modelId: string): ModelProvider { + // AI Gateway models (`gateway("vendor/model")`) carry the vendor in the id. + if (provider.startsWith('gateway')) { + return gatewayModelProvider(modelId); + } + if (provider.startsWith('anthropic') || modelId.startsWith('claude-')) { return 'anthropic'; } @@ -608,21 +622,33 @@ export function aiSdkAgent(options: { const configuredEffort = po?.anthropic?.effort ?? po?.openai?.reasoningEffort; const reasoningEffort = reasoningEffortSchema.safeParse(configuredEffort).data; - const modelId = options.model.modelId; + // RUN_THROUGH_GATEWAY reroutes direct-provider models through the AI + // Gateway by rebuilding them as gateway models under the equivalent slug. + // Models that are already gateway models pass through untouched. + const model = + runThroughGateway() && !options.model.provider.startsWith('gateway') + ? gateway( + toGatewaySlug( + getModelProvider(options.model.provider, options.model.modelId), + options.model.modelId + ) + ) + : options.model; + const modelId = model.modelId; return { id: 'ai-sdk', modelId, metadata: { agent: 'ai-sdk', - modelProvider: getModelProvider(options.model.provider, modelId), + modelProvider: getModelProvider(model.provider, modelId), modelId, ...(reasoningEffort ? { reasoningEffort } : {}), }, assertReady() { - assertProviderReady(options.model.provider); + assertProviderReady(model.provider); }, async run(args) { - assertProviderReady(options.model.provider); + assertProviderReady(model.provider); const mcpHandles = args.mcpServers ? await createAiSdkTools(args.mcpServers) : []; @@ -638,7 +664,7 @@ export function aiSdkAgent(options: { try { const result = await generateText({ - model: options.model, + model, system: args.systemPrompt, prompt: args.userPrompt, tools, @@ -646,7 +672,7 @@ export function aiSdkAgent(options: { maxOutputTokens: MAX_OUTPUT_TOKENS, timeout: { totalMs: args.timeoutSec * 1000 }, providerOptions: withProviderDefaults( - options.model.provider, + model.provider, options.providerOptions ), experimental_onToolCallFinish: (event) => { @@ -1195,6 +1221,11 @@ const MAX_OUTPUT_TOKENS = 4096; const RUNTIME_URL = 'http://supabase-evals.local'; function assertProviderReady(provider: string): void { + if (provider.startsWith('gateway') && !process.env[AI_GATEWAY.apiKeyEnvVar]) { + throw new Error( + `Missing AI Gateway credentials. Set ${AI_GATEWAY.apiKeyEnvVar} before running gateway evals.` + ); + } if (provider.startsWith('openai') && !process.env.OPENAI_API_KEY) { throw new Error( 'Missing OpenAI credentials. Set OPENAI_API_KEY before running OpenAI evals.'