Supabase has open sourced Supabase Evals, its benchmark and framework for testing how well AI agents build using Supabase. It runs coding agents including Claude Code, Codex, and OpenCode against real tasks, such as building a schema, debugging a failed Edge Function, or fixing a broken RLS policy, then scores the result. It powers the public leaderboard at supabase.com/evals and an internal regression suite monitored daily.
Is it deployable?
Yes, today. supabase/evals is public under Apache-2.0 and runs locally via pnpm.
- Industries: Developer tooling, cloud infrastructure, data platforms, and regulated backends in fintech or healthcare, where an agent writing a wrong RLS policy is a security incident.
- Applications: Regression-testing docs and skill edits, gating SDK releases, and comparing agent harnesses head to head.
- Constraints: Local-stack runs need a Docker daemon, provider API keys, and ports 54321–54329 free.
How the harness works
Supabase defined three dimensions: products (database, auth, storage, edge-functions, realtime, cron, queues, vectors, data-api), topics (RLS, security, migrations, SQL, SDK, observability, self-hosting, tests, declarative-schema), and stages (build, deploy, investigate, resolve). It then picked the smallest scenario set touching each dimension once, grounded in support tickets, bug reports, and GitHub issues.
Scenarios split into two suites. Benchmark scenarios cover breadth and are published. Regression scenarios cover known failure modes, refresh daily, and do not move published scores.
Every scenario runs against a real environment. The framework boots a hosted-like stack and a local CLI project in containers, so agents call the actual MCP server and CLI. A platform-lite runtime exposes a Management API-compatible surface backed by @supabase/lite. Scoring combines deterministic checks with LLM-as-a-judge. Agents get one retry before grading.
Each eval directory holds PROMPT.md (task plus frontmatter), EVAL.ts (the scorer), and optional remote/ and local/ starting states. Shipping a local/ workspace, or declaring interface: cli, boots a Docker sandbox with the real CLI installed.
Run the pipeline</button>
</div>
<!– ANATOMY –>
<div class=”pane” id=”p2″>
<div class=”hint”>Every eval lives at <b>evals/<id>/</b>. Click a file to see what it holds.</div>
<div class=”tree” id=”tree”></div>
<div class=”detail” id=”det2″></div>
</div>
<!– RUNTIMES –>
<div class=”pane” id=”p3″>
<div class=”hint”>The harness picks a runtime <b>automatically</b>, per eval. Toggle to compare.</div>
<div class=”tog”>
<button class=”tg on” data-r=”0″>Tools evals</button>
<button class=”tg” data-r=”1″>Local-stack evals</button>
</div>
<div class=”lanes” id=”lanes”></div>
<div class=”note” id=”rnote”></div>
</div>
<!– FINDINGS –>
<div class=”pane” id=”p4″>
<div class=”hint”>Published <b>Build stage</b> pass rates. Toggle the Supabase agent skill on and off.</div>
<div class=”tog”>
<button class=”tg” data-s=”0″>No skill loaded</button>
<button class=”tg on” data-s=”1″>Skill loaded</button>
</div>
<div class=”rows” id=”rows”></div>
<div class=”note”>
<b>Also measured:</b> Codex / GPT-5.6 reads about 8 docs pages per scenario, versus roughly 2 for Claude Code, which checks the docs in under 40% of scenarios even with skills loaded. Rewriting the Postgres best-practices skill description lifted its activation from about 1 in 10 sessions to 60%.<br><br>
Figures are a snapshot from Supabase’s launch post (31 Jul 2026). Results move as models change — check the live page.
</div>
</div>
<div class=”ft”>
<span>Source: <a href=”https://supabase.com/blog/introducing-supabase-evals” target=”_blank” rel=”noopener”>Supabase blog</a> · <a href=”https://github.com/supabase/evals” target=”_blank” rel=”noopener”>supabase/evals</a> · Apache-2.0</span>
<span><b>Marktechpost</b></span>
</div>
</div>
<script>
var STAGES=[
{i:”
“,l:”Scenario”,h:”1 · A real scenario”,t:”Each eval is grounded in a real problem — a support ticket, bug report, or GitHub issue. <code>PROMPT.md</code> carries the task the agent sees plus frontmatter tagging its stage, product, and topic.”},
{i:”
“,l:”Environments”,h:”2 · Two real environments”,t:”The framework boots a hosted-like Supabase stack and a local CLI project in containers. <code>platform-lite</code> serves a Management API-compatible surface backed by <code>@supabase/lite</code>.”},
{i:”
“,l:”Agent runs”,h:”3 · The agent works”,t:”Claude Code, Codex, OpenCode, or an AI SDK agent invokes the real Supabase MCP server and CLI — not mocks. Skills load lazily: only name and description sit in the system prompt.”},
{i:”
“,l:”One retry”,h:”4 · One retry allowed”,t:”To cut false negatives while keeping runs sustainable, agents may retry once after a failure before they are graded.”},
{i:”
“,l:”Scoring”,h:”5 · Deterministic + judge”,t:”<code>EVAL.ts</code> exports the scorer. Deterministic checks confirm things like whether a user can reach certain data or an Edge Function returns the expected result; an LLM judge handles semantic calls.”},
{i:”
“,l:”Results”,h:”6 · Benchmark or regression”,t:”Benchmark scenarios go to the public site and run when assessing new changes or harnesses. Regression scenarios track known failure modes and refresh daily, without moving published scores.”}
];
var FILES=[
{n:”PROMPT.md”,d:”Task + frontmatter”,h:”PROMPT.md”,t:”Frontmatter plus the task description the agent sees. Keys drive discovery and the site filters: <code>stage</code>, <code>suite</code>, <code>product</code>, <code>topic</code>, <code>motivation</code>. <code>suite</code> is required on every eval.”},
{n:”EVAL.ts”,d:”The scorer”,h:”EVAL.ts”,t:”A default-exported scorer. Scorers check what the agent produced, never what the harness provisioned — with <code>projectRunning: true</code>, only the agent’s deltas are scored.”},
{n:”remote/”,d:”Hosted project state”,h:”remote/ — optional”,t:”The hosted project’s starting state, seeded into platform-lite: <code>project.sql</code> for the database, <code>logs.jsonl</code> for observability logs, and <code>functions/</code> for already-deployed Edge Functions.”},
{n:”local/”,d:”Agent workspace”,h:”local/ — optional”,t:”The developer’s working directory, copied into the sandbox before the agent starts. Its presence is also a runtime switch: ship a <code>local/</code> and the eval boots a Docker sandbox.”}
];
var RUN=[
{lanes:[[“PROMPT”,”Agent gets the task, with no local/ directory and no interface: cli”],[“TOOLS”,”It works through the experiment’s MCP / tool surface only — there is no filesystem”],[“SKILLS”,”A load_skill tool returns a skill’s full instructions on demand”],[“SCORE”,”The resulting project state or report is graded”]],
note:”<b>Tools evals</b> exercise the MCP surface in isolation. Because the agent has no filesystem, skills are fetched through a tool call rather than read from disk.”},
{lanes:[[“PROMPT”,”Eval ships a local/ workspace or declares interface: cli”],[“SANDBOX”,”A fresh Docker container boots per attempt, with the real Supabase CLI installed”],[“STACK”,”The agent runs supabase init / start / db / test against a live local stack”],[“EXPORT”,”The workspace is copied back to the host so scorers run vite / vitest against it”]],
note:”<b>Local-stack evals</b> need a running Docker daemon, and default ports 54321–54329 free. A <code>services:</code> list keeps stack boots fast by starting only what the scenario needs.”}
];
var SCORES=[
{n:”Opus 5″,off:100,on:100},
{n:”Kimi K3″,off:100,on:100},
{n:”GPT-5.6 Sol”,off:89,on:100},
{n:”Sonnet 5″,off:78,on:100},
{n:”GPT-5.4 mini”,off:78,on:89}
];
function $(s){return document.querySelector(s)}
function all(s){return [].slice.call(document.querySelectorAll(s))}
/* tabs */
all(‘.tab’).forEach(function(b){b.onclick=function(){
all(‘.tab’).forEach(function(x){x.classList.remove(‘on’)});
all(‘.pane’).forEach(function(x){x.classList.remove(‘on’)});
b.classList.add(‘on’);$(‘#’+b.dataset.p).classList.add(‘on’);
}});
/* pipeline */
var flow=$(‘#flow’);
STAGES.forEach(function(s,i){
var d=document.createElement(‘div’);d.className=’node’;d.dataset.i=i;
d.innerHTML='<span class=”dot”></span><div class=”nnum”>0’+(i+1)+'</div><div class=”nico”>’+s.i+'</div><div class=”nlab”>’+s.l+'</div>’;
d.onclick=function(){pick(i)};flow.appendChild(d);
});
function pick(i){
all(‘#flow .node’).forEach(function(n,j){n.classList.toggle(‘on’,j===i)});
$(‘#det’).innerHTML='<h4>’+STAGES[i].h+'</h4><p>’+STAGES[i].t+'</p>’;
}
pick(0);
var busy=false;
$(‘#run’).onclick=function(){
if(busy)return;busy=true;this.disabled=true;
var nodes=all(‘#flow .node’);nodes.forEach(function(n){n.classList.remove(‘lit’)});
var i=0;var t=setInterval(function(){
if(i>0)nodes[i-1].classList.remove(‘lit’);
if(i>=nodes.length){clearInterval(t);busy=false;$(‘#run’).disabled=false;return}
nodes[i].classList.add(‘lit’);pick(i);i++;
},900);
};
/* anatomy */
var tree=$(‘#tree’);
FILES.forEach(function(f,i){
var d=document.createElement(‘div’);d.className=’f’;
d.innerHTML='<div class=”fn”>’+f.n+'</div><div class=”fd”>’+f.d+'</div>’;
d.onclick=function(){
all(‘#tree .f’).forEach(function(x,j){x.classList.toggle(‘on’,j===i)});
$(‘#det2′).innerHTML='<h4>’+f.h+'</h4><p>’+f.t+'</p>’;
};
tree.appendChild(d);
});
all(‘#tree .f’)[0].click();
/* runtimes */
function drawRun(k){
var L=$(‘#lanes’);L.innerHTML=”;
RUN[k].lanes.forEach(function(x,i){
var d=document.createElement(‘div’);d.className=’lane’;
d.innerHTML='<span class=”lb”>’+x[0]+'</span><span class=”lt”>’+x[1]+'</span>’;
L.appendChild(d);setTimeout(function(){d.classList.add(‘act’)},120*i+60);
});
$(‘#rnote’).innerHTML=RUN[k].note;
}
all(‘[data-r]’).forEach(function(b){b.onclick=function(){
all(‘[data-r]’).forEach(function(x){x.classList.remove(‘on’)});
b.classList.add(‘on’);drawRun(+b.dataset.r);
}});
drawRun(0);
/* findings */
var rows=$(‘#rows’);
SCORES.forEach(function(s){
var d=document.createElement(‘div’);d.className=’row’;
d.innerHTML='<div class=”rn”>’+s.n+'</div><div class=”track”><div class=”bar”></div></div><div class=”rv”>—</div>’;
rows.appendChild(d);
});
function drawScores(on){
all(‘#rows .row’).forEach(function(r,i){
var v=on?SCORES[i].on:SCORES[i].off;
r.querySelector(‘.bar’).style.width=v+’%’;
r.querySelector(‘.rv’).textContent=v+’%’;
});
}
all(‘[data-s]’).forEach(function(b){b.onclick=function(){
all(‘[data-s]’).forEach(function(x){x.classList.remove(‘on’)});
b.classList.add(‘on’);drawScores(b.dataset.s===’1′);
}});
drawScores(true);
/* auto-resize */
function ping(){
try{parent.postMessage({mtpEmbed:’supabase-evals’,height:document.body.offsetHeight+40},’*’)}catch(e){}
}
window.addEventListener(‘load’,ping);
setInterval(ping,400);
document.addEventListener(‘click’,function(){setTimeout(ping,60)});
</script>
“>
Findings
Agents pass most scenarios with no skill loaded. In the Build stage, Opus 5 and Kimi K3 both scored 100% unaided. Skills closed the rest of the gap: Sonnet 5 rose from 78% to 100%, GPT-5.6 Sol from 89% to 100%, and GPT-5.4 mini from 78% to 89%.
Three weaknesses surfaced. Agents hand-write migrations instead of using declarative schemas, prompting a skill guidance update. Agents verify auth by hand rather than reaching for @supabase/server, prompting a package selection guide. And docs usage varies sharply: Codex / GPT-5.6 reads roughly 8 docs pages per scenario versus about 2 for Claude Code, which checks docs in under 40% of scenarios even with skills loaded.
Key Takeaways
- Supabase open sourced
supabase/evalsunder Apache-2.0. - Scenarios run against real containerized Supabase stacks, not mocks.
- Scoring mixes deterministic checks with LLM-as-a-judge; one retry allowed.
- Skills mattered least for top models, most for smaller ones.
Check out the Technical details and GitHub Repo. Also, feel free to follow us on Twitter and don’t forget to join our 150k+ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.
Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.? Connect with us
The post Supabase Releases Evals: an Open Source Benchmark That Scores Claude Code, Codex and OpenCode on Real Supabase Tasks appeared first on MarkTechPost.
