How I Set Up a Preview Environment to Test Agent-Written PRs Without Pulling Locally

Assigning issues to agents that actually create the entire PR is great, until you need to test what they wrote. Here's how we configured an end-to-end preview environment for Jellypod using Supabase, WorkOS, and Vercel to test agent-written PRs without ever testing the code locally.


Everyone knows AI coding agents can actually write code and open PRs. But when you have a bunch of agents all working in parallel, the bottleneck becomes testing the code.

You can review the diff on GitHub, but code review alone doesn't tell you if the feature works or if the UX is broken. To validate an agent's PR, you probably need to run it and for most teams, that means pulling the branch locally, setting up the environment, running migrations, and testing manually. Do that once and it's fine. Do it for every PR across multiple agents working in parallel, and it becomes the bottleneck that kills the entire workflow.

So instead we built a completely async workflow where we assign a ticket in Linear, an AI agent picks it up, opens a PR, and we can validate both the code and the UX from the browser — without ever pulling code locally. Every PR gets its own fully functional, isolated preview environment that we can click into and test immediately.

Here's the workflow we landed on:

  1. We create a ticket in Linear describing a bug fix or feature
  2. An AI coding agent (Claude Code or Cursor) picks up the ticket, writes the code, and opens a PR
  3. GitHub Actions fire: Vercel deploys a preview, Supabase creates a preview branch with an isolated database, and our workflow syncs auth config
  4. We click the preview link and test the changes on a fully functional, isolated environment
  5. We review the code, leave comments, and merge — or ask the agent to iterate

The key: we never pull the code down locally. We don't spin up a local database, don't run migrations, don't configure environment variables. The preview environment is the testing environment.

Why This Matters

Isolation without overhead. Every PR gets its own database. An agent experimenting with schema changes or inserting test data can't corrupt your staging database or affect another PR's testing.

Parallelism. We can have multiple agents working on different features simultaneously. Each one gets its own isolated environment. There's no contention over a shared staging database.

Speed of review. When an agent opens a PR, the reviewer's job is to read the diff and click a link. If it works, merge it. If it doesn't, leave a comment and the agent iterates. The feedback loop is minutes, not hours.

No local environment drift. "Works on my machine" becomes "works on the preview URL that everyone can see." The preview environment runs the same infrastructure as production — just with an isolated database branch.

Our Stack

To make this work, we use:

The Problem: 3rd Party Auth Doesn't Carry Over to Preview Branches

In theory, Supabase branching should give you a fully functional copy of your environment for each PR. If you're using Supabase Auth directly, it probably does. But if you're using a third-party auth provider like WorkOS, it doesn't.

Supabase preview branches clone your database schema, migrations, and RLS policies from the parent project. What they don't clone is your third-party auth configuration. The preview branch has no idea that WorkOS exists, so it rejects every authentication attempt. The result: a completely useless preview environment. You can see the deployment, but you can't actually sign in and use it.

The Fix: A GitHub Action That Syncs Auth on Every PR

When a PR is opened, we run a GitHub Action that:

  1. Waits for the Supabase preview branch to exist (it takes a few seconds after the PR is created)
  2. Checks if WorkOS is already configured on the preview branch
  3. Configures WorkOS as a third-party auth provider if it isn't

Here's the workflow:

name: Sync WorkOS to Supabase Preview Branch

on:
  pull_request:
    types: [opened, synchronize, reopened]
  workflow_dispatch:
    inputs:
      pr_branch:
        description: 'Git branch name to resolve to a Supabase preview branch'
        required: true

jobs:
  sync_workos:
    runs-on: ubuntu-latest
    env:
      SUPABASE_API_URL: https://api.supabase.com/v1
      SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
      SUPABASE_PROJECT_REF: ${{ secrets.SUPABASE_PROJECT_REF }}
      WORKOS_ISSUER_URL: ${{ secrets.WORKOS_ISSUER_URL }}

    steps:
      - name: Resolve Supabase preview branch ref
        id: resolve
        run: |
          PR_BRANCH="${{ github.event.pull_request.head.ref }}"
          branch_ref=''
          for attempt in 1 2 3 4 5; do
            response="$(curl -fsS \
              -H "Authorization: Bearer ${SUPABASE_ACCESS_TOKEN}" \
              "${SUPABASE_API_URL}/projects/${SUPABASE_PROJECT_REF}/branches")"

            branch_ref="$(echo "$response" | jq -r --arg b "$PR_BRANCH" '
              map(select(.git_branch == $b or .name == $b))
              | first | .project_ref // empty')"

            [ -n "$branch_ref" ] && break
            sleep 10
          done
          echo "branch_ref=$branch_ref" >> "$GITHUB_OUTPUT"

      - name: Configure WorkOS third-party auth
        if: steps.resolve.outputs.branch_ref != ''
        run: |
          BRANCH_REF="${{ steps.resolve.outputs.branch_ref }}"

          # Check if already configured
          existing="$(curl -fsS \
            -H "Authorization: Bearer ${SUPABASE_ACCESS_TOKEN}" \
            "${SUPABASE_API_URL}/projects/${BRANCH_REF}/config/auth/third-party-auth")"

          has_workos="$(echo "$existing" | jq '[.[] | select(.type == "workos")] | length')"
          [ "$has_workos" -gt 0 ] && exit 0

          # Add WorkOS as third-party auth provider
          curl -fsS -X POST \
            "${SUPABASE_API_URL}/projects/${BRANCH_REF}/config/auth/third-party-auth" \
            -H "Authorization: Bearer ${SUPABASE_ACCESS_TOKEN}" \
            -H 'Content-Type: application/json' \
            -d "$(jq -n --arg issuer "$WORKOS_ISSUER_URL" '{
              type: "workos",
              oidc_issuer_url: $issuer
            }')"

The key insight is that Supabase's Management API lets you configure third-party auth on any project ref — including preview branch refs. The API endpoint is:

POST /v1/projects/{ref}/config/auth/third-party-auth

You just need to pass the OIDC issuer URL for your auth provider.

Why the Retry Loop?

Supabase preview branches aren't created instantly. When a PR is opened, Supabase's GitHub integration detects it and starts provisioning a branch, but there's a lag before it shows up in the API. The workflow retries up to 5 times with 10-second intervals. There's no webhook or event to tell us when the preview branch is ready so polling is the only option. It's not elegant but it works.

Idempotency

The workflow checks whether WorkOS is already configured before making any changes. This means it's safe to re-run on reopened events or trigger manually via workflow_dispatch without duplicating configuration.

Adapting This for Your Auth Provider

This approach isn't specific to WorkOS. Supabase's third-party auth supports any OIDC-compliant provider. The Management API endpoint accepts a type and issuer URL:

{
  "type": "workos",
  "oidc_issuer_url": "https://api.workos.com/sso/oidc/..."
}

If you're using Auth0, Clerk, or any other provider with an OIDC discovery endpoint, you can adapt the same workflow — just change the type and oidc_issuer_url values.

I didn't test this to confirm, you may need to check variables, etc.

Setup Checklist

If you want to replicate this:

  1. Enable Supabase Branching on your project (requires a Pro plan or higher)
  2. Connect your GitHub repo to Supabase so preview branches are auto-created on PRs
  3. Configure third-party auth on your main Supabase project. If you're using Supabase Auth directly, it may already work on preview branches and you can skip the GitHub Action workflow entirely.
  4. Add GitHub Secrets:
    • SUPABASE_ACCESS_TOKEN — a Supabase Management API token
    • SUPABASE_PROJECT_REF — your main project's ref
    • WORKOS_ISSUER_URL — your OIDC issuer URL (or equivalent for your provider)
  5. Add the GitHub Action to .github/workflows/
  6. Open a PR and verify the workflow runs, the preview deploys, and you can sign in

One missing API call was the difference between broken preview environments and a fully async agent workflow. Now we assign tickets, agents ship PRs, and we can test everything from the browser without ever needing to pull the code locally.