Reconcile drifted schema and guard the two provisioning paths (#205) - #218
Conversation
Reconcile drifted schema and guard the two provisioning paths (#205) The migration mechanism, baseline, db:check verification, rollback docs and trigger-order test already exist. What was missing was the thing they exist to protect: the two provisioning paths had silently diverged. The drift Commits 0823a9e and dad56b6 added 563 lines to supabase-setup.sql with no matching migration - auth challenge storage, auth rate limiting, trip invites and claim RPC, settlement_attestations, and the optimistic-concurrency RPCs (update_expense_versioned, mark_share_paid, mark_shares_paid_batch), plus several indexes. A database provisioned by running migrations was therefore missing functions that authentication and expense editing call at runtime, while reporting schema version 0002. Nothing failed until one of them was called. That is precisely the failure #205 exists to prevent, reintroduced. Two indexes drifted the other way: baseline 0001 created auth_challenges_expiration_idx and auth_rate_limits_window_idx, which supabase-setup.sql lacked. migrations/0003_reconcile_drifted_schema.sql Carries every setup-file-only object into the versioned track. Fully idempotent: IF NOT EXISTS / CREATE OR REPLACE throughout, and the three trip_invites policies now get DROP POLICY IF EXISTS guards they lacked in the setup file, so re-running does not error. No DROP TABLE, DROP COLUMN or destructive ALTER - an existing database converges with no data loss, and on a database already built from supabase-setup.sql the whole file is a no-op that only records the version row. The two drifted indexes are mirrored into supabase-setup.sql. __tests__/database/schemaDrift.test.ts Static CI guard, since the root cause is that nothing noticed. Extracts every table, function and index from both paths and fails if either side has an object the other lacks - the regression test for this specific bug. Also asserts migration hygiene: unique version numbers (so parallel contributors collide in git rather than diverging silently), a self-recording tracking insert with ON CONFLICT DO NOTHING, guarded CREATE POLICY, and no destructive statements. Policies and triggers are excluded deliberately - the setup file recreates those wholesale, so their text legitimately differs. Trigger ordering keeps its own test. docs/DATABASE_MIGRATIONS.md gains section 8 stating the rule both directions and the four-step procedure for adding a migration. Note: not executed here - this checkout has no installed node_modules. The drift comparison itself was run directly under node and reports both directions empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> @
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
||
| for (const [re, kind] of patterns) { | ||
| for (const match of clean.matchAll(re)) { | ||
| found.add(`${kind}:${match[1].replace(/^public\./, "").toLowerCase()}`); |
There was a problem hiding this comment.
Suggestion: The drift check compares only object names, so missing grants, columns, constraints, RLS, or function security settings can pass as converged schemas. [possible bug]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** __tests__/database/schemaDrift.test.ts
**Line:** 59:59
**Comment:**
*Possible Bug: The drift check compares only object names, so missing grants, columns, constraints, RLS, or function security settings can pass as converged schemas.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| } | ||
| }); | ||
|
|
||
| expect({ name, unguarded }).toEqual({ name, unguarded: [] }); |
There was a problem hiding this comment.
Suggestion: This test requires every policy to have a preceding drop, but baseline schema_migrations_read has no drop, so the new hygiene test fails on the existing migration set. [possible bug]
Assessment: 🟠 Major · 🔁 Occurrence: Often
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** __tests__/database/schemaDrift.test.ts
**Line:** 152:152
**Comment:**
*Possible Bug: This test requires every policy to have a preceding drop, but baseline `schema_migrations_read` has no drop, so the new hygiene test fails on the existing migration set.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| INSERT INTO public.auth_rate_limits (key, count, window_start, updated_at) | ||
| VALUES (p_key, 1, p_now, NOW()) | ||
| ON CONFLICT (key) DO UPDATE | ||
| SET count = 1, window_start = p_now, updated_at = NOW(); |
There was a problem hiding this comment.
Suggestion: Concurrent first requests both enter this branch; the second upsert resets the count to one, allowing requests to bypass the configured rate limit. [race condition]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** migrations/0003_reconcile_drifted_schema.sql
**Line:** 144:147
**Comment:**
*Race Condition: Concurrent first requests both enter this branch; the second upsert resets the count to one, allowing requests to bypass the configured rate limit.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| ) | ||
| FROM jsonb_array_elements(shares) AS s | ||
| ) | ||
| WHERE (id::text = ANY(v_trip.expense_ids) OR v_trip.id::text = ANY(member_wallets) OR members @> jsonb_build_array(jsonb_build_object('id', v_target_member_id))); |
There was a problem hiding this comment.
Suggestion: This predicate compares a trip UUID with expense wallet addresses, so expenses not listed in expense_ids can remain stale after an invite claim. [incorrect condition logic]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** migrations/0003_reconcile_drifted_schema.sql
**Line:** 407:407
**Comment:**
*Incorrect Condition Logic: This predicate compares a trip UUID with expense wallet addresses, so expenses not listed in `expense_ids` can remain stale after an invite claim.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| CREATE INDEX IF NOT EXISTS auth_challenges_expiration_idx ON public.auth_challenges (expiration); | ||
| CREATE INDEX IF NOT EXISTS auth_rate_limits_window_idx ON public.auth_rate_limits (window_start); |
There was a problem hiding this comment.
Suggestion: auth_rate_limits does not exist yet, so this index statement aborts fresh setup before the rate-limit table is created. [logic error]
Assessment: 🔴 Critical · 🔁 Occurrence: Often
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** supabase-setup.sql
**Line:** 112:113
**Comment:**
*Logic Error: `auth_rate_limits` does not exist yet, so this index statement aborts fresh setup before the rate-limit table is created.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
User description
close #205
The migration mechanism, baseline, db:check verification, rollback docs and trigger-order test already exist. What was missing was the thing they exist to protect: the two provisioning paths had silently diverged.
The drift
Commits 0823a9e and dad56b6 added 563 lines to supabase-setup.sql with no
matching migration - auth challenge storage, auth rate limiting, trip invites
and claim RPC, settlement_attestations, and the optimistic-concurrency RPCs
(update_expense_versioned, mark_share_paid, mark_shares_paid_batch), plus
several indexes.
A database provisioned by running migrations was therefore missing functions
that authentication and expense editing call at runtime, while reporting
schema version 0002. Nothing failed until one of them was called. That is
precisely the failure #205 exists to prevent, reintroduced.
Two indexes drifted the other way: baseline 0001 created
auth_challenges_expiration_idx and auth_rate_limits_window_idx, which
supabase-setup.sql lacked.
migrations/0003_reconcile_drifted_schema.sql
Carries every setup-file-only object into the versioned track. Fully
idempotent: IF NOT EXISTS / CREATE OR REPLACE throughout, and the three
trip_invites policies now get DROP POLICY IF EXISTS guards they lacked in the
setup file, so re-running does not error. No DROP TABLE, DROP COLUMN or
destructive ALTER - an existing database converges with no data loss, and on
a database already built from supabase-setup.sql the whole file is a no-op
that only records the version row.
The two drifted indexes are mirrored into supabase-setup.sql.
tests/database/schemaDrift.test.ts
Static CI guard, since the root cause is that nothing noticed. Extracts every
table, function and index from both paths and fails if either side has an
object the other lacks - the regression test for this specific bug. Also
asserts migration hygiene: unique version numbers (so parallel contributors
collide in git rather than diverging silently), a self-recording tracking
insert with ON CONFLICT DO NOTHING, guarded CREATE POLICY, and no destructive
statements.
Policies and triggers are excluded deliberately - the setup file recreates
those wholesale, so their text legitimately differs. Trigger ordering keeps
its own test.
docs/DATABASE_MIGRATIONS.md gains section 8 stating the rule both directions and the four-step procedure for adding a migration.
Note: not executed here - this checkout has no installed node_modules. The drift comparison itself was run directly under node and reports both directions empty.
@
CodeAnt-AI Description
Reconcile database provisioning paths and prevent future schema drift
What Changed
Impact
✅ Authentication works on migration-provisioned databases✅ Trip invites and expense updates are available after migration✅ Schema drift is caught before deployment💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.