From 582209dcb434c6afd19bf3bf23808706c49df04c Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 30 Sep 2026 13:38:14 +0000 Subject: [PATCH] fix(db): define the legacy purge without running it The legacy cleanup migration deleted legacy results, chats, flowcharts, sparring rounds and sources the moment it was applied. Production never applied it, and the pins migration that follows it is blocked behind it. Adam decided on 2026-09-30 to keep production's legacy material. The migration now only defines private.purge_legacy_debate_material, so db push is safe on every project. Running the purge is a separate call that needs explicit authorization. The pgTAP tests still call the function directly, so its behavior stays covered. Co-Authored-By: Claude Opus 5.5 --- supabase/README.md | 2 +- .../20260926090100_legacy_debate_cleanup.sql | 29 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/supabase/README.md b/supabase/README.md index 247191bb7..1d6bf081a 100644 --- a/supabase/README.md +++ b/supabase/README.md @@ -123,7 +123,7 @@ The database gate proves migration replay and pgTAP assertions locally. It does `20260927170000_debate_context_pins.sql` is additive: it creates `debate_context_pins`, the saved results a student pins per debate, with RLS and a composite key to the owner's debate. -`20260926090100_legacy_debate_cleanup.sql` is destructive and one-time: it deletes legacy results, chats, flowcharts, sparring rounds, and sources that belong to no debate and had no activity in the 30 days before `20260926090000_current_debates.sql` recorded its rollout time in `private.current_debate_rollout`. Work created after that time is never touched, however late the cleanup runs. Keep it separate from the additive `20260926090000_current_debates.sql`, and apply it to a remote project only with explicit authorization for that environment. +`20260926090100_legacy_debate_cleanup.sql` defines `private.purge_legacy_debate_material`, which deletes legacy results, chats, flowcharts, sparring rounds, and sources that belong to no debate and had no activity in the 30 days before `20260926090000_current_debates.sql` recorded its rollout time in `private.current_debate_rollout`. The migration does not call it, so applying it deletes nothing; legacy rows stay in the Unsorted view. Running the purge on a remote project is a separate, destructive step that needs explicit authorization for that environment. Work created after the rollout is never touched, however late it runs. ## Edge Function tests diff --git a/supabase/migrations/20260926090100_legacy_debate_cleanup.sql b/supabase/migrations/20260926090100_legacy_debate_cleanup.sql index 858e5b704..6a910fcf9 100644 --- a/supabase/migrations/20260926090100_legacy_debate_cleanup.sql +++ b/supabase/migrations/20260926090100_legacy_debate_cleanup.sql @@ -1,14 +1,19 @@ --- DESTRUCTIVE, ONE-TIME: remove legacy material that predates Current Debate --- and had no activity in the 30 days before it shipped. Recent legacy material --- stays, without a debate, in the Unsorted view until the user confirms where --- it belongs. +-- One-time purge of legacy material that predates Current Debate and had no +-- activity in the 30 days before it shipped. This migration only defines the +-- function; it does not run it. Adam chose on 2026-09-30 to keep production's +-- legacy material, so any purge is a separate, explicitly authorized call: -- --- Only rows with no debate that were created before the rollout are candidates, --- so neither debate work nor later one-off work can be removed, however late --- this runs. Nothing schedules it; this migration calls it once. Activity is --- the latest edit where a table records one (flowcharts, sparring sessions) and --- the latest message for chats. Chat messages and source files go with their --- parents through ON DELETE CASCADE; evidence cards keep a null source. +-- SELECT private.purge_legacy_debate_material( +-- (SELECT started_at FROM private.current_debate_rollout) +-- ); +-- +-- Until then, legacy material stays, without a debate, in the Unsorted view. +-- Only rows with no debate that were created before the rollout are +-- candidates, so neither debate work nor later one-off work can be removed, +-- however late it runs. Activity is the latest edit where a table records one +-- (flowcharts, sparring sessions) and the latest message for chats. Chat +-- messages and source files go with their parents through ON DELETE CASCADE; +-- evidence cards keep a null source. CREATE FUNCTION private.purge_legacy_debate_material(p_rollout timestamptz) RETURNS void @@ -39,7 +44,3 @@ $$; REVOKE ALL ON FUNCTION private.purge_legacy_debate_material(timestamptz) FROM PUBLIC, anon, authenticated, service_role; - -SELECT private.purge_legacy_debate_material( - (SELECT started_at FROM private.current_debate_rollout) -);