Skip to content

Reconcile drifted schema and guard the two provisioning paths (#205) - #218

Merged
Soumen1080 merged 1 commit into
mainfrom
Samanway2405
Aug 31, 2026
Merged

Reconcile drifted schema and guard the two provisioning paths (#205)#218
Soumen1080 merged 1 commit into
mainfrom
Samanway2405

Conversation

@Soumen1080

@Soumen1080 Soumen1080 commented Aug 31, 2026

Copy link
Copy Markdown
Owner

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

  • Migration-based databases now include authentication challenge and rate-limit storage, trip invites and claiming, settlement protection, expense concurrency and payment updates, and related indexes.
  • Existing databases converge without data loss, while rerunning the migration remains safe.
  • Setup-based provisioning now includes the authentication indexes that were previously missing.
  • CI checks that setup SQL and migrations define the same tables, functions, and indexes, and validates migration numbering, tracking, rerun safety, policy guards, and non-destructive changes.
  • Migration guidance now documents the matching-schema rule and how to add future migrations.

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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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.

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

codeant-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR edba52a Aug 31, 2026 · 12:00 12:04

@codeant-ai

codeant-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stellar-star Error Error Aug 31, 2026 12:00pm

@Soumen1080
Soumen1080 merged commit e77f2e2 into main Aug 31, 2026
3 of 5 checks passed
@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Aug 31, 2026

for (const [re, kind] of patterns) {
for (const match of clean.matchAll(re)) {
found.add(`${kind}:${match[1].replace(/^public\./, "").toLowerCase()}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

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: [] });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

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
👍 | 👎

Comment on lines +144 to +147
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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

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)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

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
👍 | 👎

Comment thread supabase-setup.sql
Comment on lines +112 to +113
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

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
👍 | 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Give the app a real database migration story

1 participant