env | grep -i supabase | sed 's/=.*/=<set>/'
SUPABASE_DB_PASSWORD=<set> SUPABASE_DB_URL=<set> SUPABASE_URL=<set> SUPABASE_SERVICE_ROLE_KEY=<set> SUPABASE_PROJECT_REF=<set> SUPABASE_ANON_KEY=<set>
which psql pg_dump python3; psql --version 2>/dev/null
/usr/bin/psql /usr/bin/pg_dump /usr/bin/python3 psql (PostgreSQL) 18.4 (Debian 18.4-1.pgdg12+1)
cd /tmp && psql "$SUPABASE_DB_URL" -c "select current_database(), current_user, version();" 2>&1 | head -20
current_database | current_user | version ------------------+--------------+------------------------------------------------------------------------------ postgres | postgres | PostgreSQL 17.6 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 15.2.0, 64-bit (1 row)
echo "$SUPABASE_DB_URL" | sed -E 's#(://[^:]+:)[^@]+(@)#\1***\2#'
postgresql://postgres.<ref>:***@aws-0-us-east-1.pooler.supabase.com:5432/postgres
cd /tmp && psql "$SUPABASE_DB_URL" -c "\d public.orders" -c "select count(*) as rows from public.orders;" 2>&1
Table "public.orders"
Column | Type | Collation | Nullable | Default
----------------+--------------------------+-----------+----------+---------
id | integer | | not null |
customer_name | text | | not null |
customer_email | text | | not null |
item | text | | not null |
amount_cents | integer | | not null |
created_at | timestamp with time zone | | not null |
Indexes:
"orders_pkey" PRIMARY KEY, btree (id)
rows
------
120
(1 row)
cd /tmp && psql "$SUPABASE_DB_URL" -c "select to_regclass('public.customers') as customers_exists;" -c "select count(distinct customer_email) as distinct_emails, count(distinct (customer_name, customer_email)) as distinct_pairs, count(*) filter (where customer_email is null) as null_email, count(*) filter (where customer_name is null) as null_name from public.orders;" 2>&1 customers_exists
------------------
(1 row)
distinct_emails | distinct_pairs | null_email | null_name
-----------------+----------------+------------+-----------
40 | 40 | 0 | 0
(1 row)
cd /tmp && psql "$SUPABASE_DB_URL" -c "
-- would case-insensitive or trimmed collapsing change the distinct count? (informational)
select count(distinct customer_email) as exact,
count(distinct lower(customer_email)) as lower_ci,
count(distinct btrim(customer_email)) as trimmed,
count(distinct lower(btrim(customer_email))) as lower_trimmed
from public.orders;" -c "
-- any email tied to more than one name?
select customer_email, count(distinct customer_name) n, string_agg(distinct customer_name,' | ') names
from public.orders group by 1 having count(distinct customer_name)>1;" 2>&1 exact | lower_ci | trimmed | lower_trimmed
-------+----------+---------+---------------
40 | 40 | 40 | 40
(1 row)
customer_email | n | names
----------------+---+-------
(0 rows)
cd /tmp && psql "$SUPABASE_DB_URL" -c "select id, customer_name, customer_email, item, amount_cents, created_at from public.orders order by id limit 8;" -c "select min(id), max(id), count(*) from public.orders;" 2>&1
id | customer_name | customer_email | item | amount_cents | created_at ----+---------------------+--------------------------+--------------+--------------+------------------------ 1 | Customer-2869aff5cb | 66f4f3939508@example.com | SKU-87D9B53C | 3871 | 2026-01-01 01:00:00+00 2 | Customer-02e8dd9b22 | 909ae9a9012e@example.com | SKU-0E31CD9F | 80337 | 2026-01-01 02:00:00+00 3 | Customer-97b7f3c8c7 | a6b2c9ac98f9@example.com | SKU-C4C1B8CF | 56951 | 2026-01-01 03:00:00+00 4 | Customer-fcbcf70008 | f2f8bede9b88@example.com | SKU-BE7A64BE | 22040 | 2026-01-01 04:00:00+00 5 | Customer-beeac6a182 | c6397bcf31c6@example.com | SKU-7C3D3496 | 5665 | 2026-01-01 05:00:00+00 6 | Customer-a0ea908d6a | 1d4a857f6a1c@example.com | SKU-25AEB703 | 42266 | 2026-01-01 06:00:00+00 7 | Customer-3e03962dc8 | e2616d8facdb@example.com | SKU-5040D99A | 75256 | 2026-01-01 07:00:00+00 8 | Customer-a0aaa81ec2 | 7ff8d006b700@example.com | SKU-5835B4E7 | 14020 | 2026-01-01 08:00:00+00 (8 rows) min | max | count -----+-----+------- 1 | 120 | 120 (1 row)
cd /tmp && psql "$SUPABASE_DB_URL" -c " select c.relname, c.relkind, c.relrowsecurity from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname='public';" -c " -- views/matviews depending on orders columns select distinct dependent.relname, dependent.relkind from pg_depend d join pg_rewrite r on r.oid=d.objid join pg_class dependent on dependent.oid=r.ev_class join pg_class src on src.oid=d.refobjid join pg_namespace n on n.oid=src.relnamespace where src.relname='orders' and n.nspname='public' and dependent.relname<>'orders';" -c " select tgname from pg_trigger where tgrelid='public.orders'::regclass and not tgisinternal;" -c " select polname, cmd from pg_policies where schemaname='public' and tablename='orders';" 2>&1
relname | relkind | relrowsecurity
-------------+---------+----------------
orders | r | f
orders_pkey | i | f
(2 rows)
relname | relkind
---------+---------
(0 rows)
tgname
--------
(0 rows)
ERROR: column "polname" does not exist
LINE 2: select polname, cmd from pg_policies where schemaname='publi...
^
HINT: Perhaps you meant to reference the column "pg_policies.policyname".
cd /tmp && pg_dump "$SUPABASE_DB_URL" -t public.orders --data-only --column-inserts -f /tmp/orders_backup_data.sql 2>&1; pg_dump "$SUPABASE_DB_URL" -t public.orders --schema-only -f /tmp/orders_backup_schema.sql 2>&1; wc -l /tmp/orders_backup_data.sql /tmp/orders_backup_schema.sql; grep -c "^INSERT" /tmp/orders_backup_data.sql
153 /tmp/orders_backup_data.sql 64 /tmp/orders_backup_schema.sql 217 total 120
cd /tmp && psql "$SUPABASE_DB_URL" -c "select policyname, cmd from pg_policies where schemaname='public' and tablename='orders';" 2>&1
policyname | cmd ------------+----- (0 rows)
cd /tmp && psql "$SUPABASE_DB_URL" -At -F$'\t' -c "select id, customer_name, customer_email, item, amount_cents, created_at from public.orders order by id;" > /tmp/orders_before.tsv 2>&1; wc -l /tmp/orders_before.tsv; md5sum /tmp/orders_before.tsv; psql "$SUPABASE_DB_URL" -At -c "select md5(string_agg(id||'|'||item||'|'||amount_cents||'|'||created_at, E'\n' order by id)) from public.orders;"
120 /tmp/orders_before.tsv ddea190ff5bd81df44620a28b810a186 /tmp/orders_before.tsv 4cc79b18b98cacc61ebf058301def1df
cat > /tmp/migrate.sql <<'SQL'
\set ON_ERROR_STOP on
BEGIN;
-- Serialize against concurrent writers for the duration of the migration.
LOCK TABLE public.orders IN SHARE ROW EXCLUSIVE MODE;
-- Snapshot pre-state for assertions.
CREATE TEMP TABLE _pre ON COMMIT DROP AS
SELECT id, customer_name, customer_email, item, amount_cents, created_at
FROM public.orders;
-- 1) customers: one row per distinct email, carrying that customer's name.
CREATE TABLE public.customers (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
email text NOT NULL UNIQUE
);
INSERT INTO public.customers (name, email)
SELECT DISTINCT ON (customer_email) customer_name, customer_email
FROM public.orders
ORDER BY customer_email, id;
-- Guard: email->name must be unambiguous, else the "carrying that name" mapping is lossy.
DO $$
DECLARE bad int;
BEGIN
SELECT count(*) INTO bad FROM (
SELECT customer_email FROM public.orders
GROUP BY customer_email HAVING count(DISTINCT customer_name) > 1
) t;
IF bad > 0 THEN RAISE EXCEPTION 'ambiguous name for % email(s)', bad; END IF;
END $$;
-- 2) orders.customer_id, backfilled on exact (name, email) match.
ALTER TABLE public.orders ADD COLUMN customer_id bigint;
UPDATE public.orders o
SET customer_id = c.id
FROM public.customers c
WHERE o.customer_email = c.email
AND o.customer_name = c.name;
-- Assert every order matched before enforcing NOT NULL.
DO $$
DECLARE unmatched int;
BEGIN
SELECT count(*) INTO unmatched FROM public.orders WHERE customer_id IS NULL;
IF unmatched > 0 THEN RAISE EXCEPTION '% order(s) unmatched by (name,email)', unmatched; END IF;
END $$;
ALTER TABLE public.orders
ALTER COLUMN customer_id SET NOT NULL,
ADD CONSTRAINT orders_customer_id_fkey
FOREIGN KEY (customer_id) REFERENCES public.customers(id);
CREATE INDEX orders_customer_id_idx ON public.orders (customer_id);
-- Assert the new FK reproduces the original identity for EVERY row, then row count.
DO $$
DECLARE bad int; n int;
BEGIN
SELECT count(*) INTO bad
FROM _pre p JOIN public.orders o ON o.id = p.id JOIN public.customers c ON c.id = o.customer_id
WHERE c.name <> p.customer_name OR c.email <> p.customer_email;
IF bad > 0 THEN RAISE EXCEPTION 'identity mismatch on % row(s)', bad; END IF;
SELECT count(*) INTO n FROM public.orders;
IF n <> (SELECT count(*) FROM _pre) THEN RAISE EXCEPTION 'order row count changed: %', n; END IF;
-- other columns must be byte-identical
SELECT count(*) INTO bad
FROM _pre p JOIN public.orders o ON o.id = p.id
WHERE o.item <> p.item OR o.amount_cents <> p.amount_cents OR o.created_at <> p.created_at;
IF bad > 0 THEN RAISE EXCEPTION 'immutable column drift on % row(s)', bad; END IF;
-- no customer left behind / no phantom customers
IF (SELECT count(*) FROM public.customers) <> (SELECT count(DISTINCT customer_email) FROM _pre) THEN
RAISE EXCEPTION 'customers count != distinct emails';
END IF;
IF (SELECT count(DISTINCT customer_id) FROM public.orders) <> (SELECT count(*) FROM public.customers) THEN
RAISE EXCEPTION 'some customers unreferenced';
END IF;
END $$;
-- 3) Drop the denormalized columns only after all assertions passed.
ALTER TABLE public.orders DROP COLUMN customer_name, DROP COLUMN customer_email;
COMMIT;
SQL
echo builtbuilt
cd /tmp && sed 's/^COMMIT;$/ROLLBACK;/' /tmp/migrate.sql > /tmp/rehearse.sql && tail -3 /tmp/rehearse.sql && psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 -f /tmp/rehearse.sql 2>&1 | tail -20
ALTER TABLE public.orders DROP COLUMN customer_name, DROP COLUMN customer_email; ROLLBACK; BEGIN LOCK TABLE SELECT 120 CREATE TABLE INSERT 0 40 DO ALTER TABLE UPDATE 120 DO ALTER TABLE CREATE INDEX DO ALTER TABLE ROLLBACK
cd /tmp && psql "$SUPABASE_DB_URL" -c "select to_regclass('public.customers') as customers, count(*) as orders from public.orders;" 2>&1 customers | orders
-----------+--------
| 120
(1 row)
cd /tmp && psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 -f /tmp/migrate.sql 2>&1 | tail -20; echo "EXIT=${PIPESTATUS[0]}"BEGIN LOCK TABLE SELECT 120 CREATE TABLE INSERT 0 40 DO ALTER TABLE UPDATE 120 DO ALTER TABLE CREATE INDEX DO ALTER TABLE COMMIT EXIT=0
cd /tmp && psql "$SUPABASE_DB_URL" -c "\d public.orders" -c "\d public.customers" 2>&1
Table "public.orders"
Column | Type | Collation | Nullable | Default
--------------+--------------------------+-----------+----------+---------
id | integer | | not null |
item | text | | not null |
amount_cents | integer | | not null |
created_at | timestamp with time zone | | not null |
customer_id | bigint | | not null |
Indexes:
"orders_pkey" PRIMARY KEY, btree (id)
"orders_customer_id_idx" btree (customer_id)
Foreign-key constraints:
"orders_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customers(id)
Table "public.customers"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+------------------------------
id | bigint | | not null | generated always as identity
name | text | | not null |
email | text | | not null |
Indexes:
"customers_pkey" PRIMARY KEY, btree (id)
"customers_email_key" UNIQUE CONSTRAINT, btree (email)
Referenced by:
TABLE "orders" CONSTRAINT "orders_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customers(id)
cd /tmp && psql "$SUPABASE_DB_URL" -c "select count(*) as orders from public.orders;" -c "select count(*) as customers from public.customers;" -c "select count(*) as null_customer_id from public.orders where customer_id is null;" -c "select md5(string_agg(id||'|'||item||'|'||amount_cents||'|'||created_at, E'\n' order by id)) as immutable_checksum from public.orders;" 2>&1
orders
--------
120
(1 row)
customers
-----------
40
(1 row)
null_customer_id
------------------
0
(1 row)
immutable_checksum
----------------------------------
4cc79b18b98cacc61ebf058301def1df
(1 row)
cd /tmp && psql "$SUPABASE_DB_URL" -At -F$'\t' -c "select o.id, c.name, c.email, o.item, o.amount_cents, o.created_at from public.orders o join public.customers c on c.id=o.customer_id order by o.id;" > /tmp/orders_after.tsv 2>&1; wc -l /tmp/orders_after.tsv; md5sum /tmp/orders_before.tsv /tmp/orders_after.tsv; if diff -q /tmp/orders_before.tsv /tmp/orders_after.tsv >/dev/null; then echo "ZERO LOSS: reconstructed rows identical to pre-migration snapshot"; else echo "DIFF FOUND"; diff /tmp/orders_before.tsv /tmp/orders_after.tsv | head; fi
120 /tmp/orders_after.tsv ddea190ff5bd81df44620a28b810a186 /tmp/orders_before.tsv ddea190ff5bd81df44620a28b810a186 /tmp/orders_after.tsv ZERO LOSS: reconstructed rows identical to pre-migration snapshot
cd /tmp && psql "$SUPABASE_DB_URL" -c "select count(*) as unreferenced_customers from public.customers c where not exists (select 1 from public.orders o where o.customer_id=c.id);" -c "select count(*) as dup_emails from (select email from public.customers group by email having count(*)>1) t;" -c "select c.name, c.email, count(o.id) as orders from public.customers c join public.orders o on o.customer_id=c.id group by 1,2 order by orders desc, c.email limit 5;" 2>&1
unreferenced_customers
------------------------
0
(1 row)
dup_emails
------------
0
(1 row)
name | email | orders
---------------------+--------------------------+--------
Customer-a0ea908d6a | 1d4a857f6a1c@example.com | 3
Customer-0af94ec5e2 | 1e60620a670c@example.com | 3
Customer-fcd83070ad | 202bb5897646@example.com | 3
Customer-9ac5211a4a | 25e610157424@example.com | 3
Customer-e8fab3b763 | 26e048a730f7@example.com | 3
(5 rows)
12 earlier narration turn(s) are in the raw transcript.
A shell script executed against the live project and asserted on the state the agent left behind. No model judged another model.
Raw transcript · Task definition and verifier · Every attempt