Skip to content

feat(migrations): cleanup funnel-duplicate roster leftovers (chat#1859)#45

Merged
sweetmantech merged 1 commit into
mainfrom
feat/cleanup-funnel-dupe-leftovers
Jul 8, 2026
Merged

feat(migrations): cleanup funnel-duplicate roster leftovers (chat#1859)#45
sweetmantech merged 1 commit into
mainfrom
feat/cleanup-funnel-dupe-leftovers

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What

The cleanup-delete — second of the two backfill follow-ups on chat#1859. Completes the deletion that database#43 deliberately deferred ("Duplicate accounts are NOT deleted — orphaning them is enough; deletion is a follow-up").

Adds 20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql, which removes the two leftovers the repoint migrations cannot:

  • Step 1 — redundant roster rows (Category B). When the account already held the canonical (api#768 claim-attach or a prior link), the repoint skipped the duplicate row to avoid a second (account, canonical) link — leaving a redundant row pointing at the song-less duplicate that no re-run touches. Deletes those.
  • Step 2 — orphaned duplicate artist accounts. Every repoint leaves the duplicate account behind (song-less, catalog-less, still carrying its scraped Spotify social, polluting artist search). Deletes the fully-orphaned ones.

Scope (measured 2026-07-08 on prod, read-only)

Safety (destructive — tightly guarded, idempotent)

  • Step 1 removes a roster row only when the same account also holds the canonical twin — so it is never an account's only link (Category A rows, where the duplicate is the sole link, are untouched here; the pass-2 repoint fixes those).
  • Step 2 deletes an artist account only when it is a pure funnel artifact: zero song_artists (never a canonical or any real artist), zero account_catalogs, not in artist_organization_ids, carries a Spotify social, has exactly one song-owning same-name twin, and is fully orphaned (zero account_artist_ids refs). Deletion cascades only its own scraped account_socials.
  • Post-condition RAISE EXCEPTION if any redundant (dupe + canonical on the same account) row remains. Re-running finds nothing.

Verification

  • Read-only preview against prod confirms the exact target sets above (5 rows / 16+ accounts); no row with song_artists, account_catalogs, or an org link is in scope.
  • The Supabase Preview check validates it parses + runs against a branch.
  • Acceptance: after the pass-2 repoint and this migration run, the #1859 health-check query returns 0 rows — no funnel-duplicate roster artist referenced, and no orphaned funnel-dupe account left.

Merge order

Merge pass-2 repoint (#44) first, then this. This file's timestamp is later so they apply repoint → cleanup; merging in that order keeps the migration history in sequence. Running this before the repoint is safe but leaves Category A unfixed (that's the repoint's job).

Links

🤖 Generated with Claude Code


Summary by cubic

Adds 20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql to remove redundant roster rows and delete fully orphaned funnel-duplicate artist accounts left by the repoint, completing cleanup for chat#1859.

  • Migration
    • Deletes redundant account_artist_ids where the account already links to the canonical twin.
    • Deletes duplicate artist accounts only if: no song_artists, no account_catalogs, no org link, has a Spotify social, exactly one same‑name song‑owning twin, and no roster refs; cascades only its own account_socials.
    • Runs after the pass‑2 repoint; idempotent with a post‑condition check that raises if any redundant rows remain.

Written for commit 66c4f9c. Summary will update on new commits.

Review in cubic

Complete the deferred cleanup after the repoint migrations. Step 1 removes
redundant duplicate roster rows where the account already holds the canonical
twin (Category B — the repoint skips these; 5 rows on prod 2026-07-08). Step 2
deletes fully-orphaned funnel-duplicate artist accounts (song-less, catalog-less,
Spotify-social, one song-owning same-name twin, zero roster refs; 16 already
orphaned from the original repoint plus those newly orphaned). Runs after the
pass-2 repoint; both are needed for the #1859 health-check query to return 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 8, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6cfbfda8-25c3-4ea3-a7d2-01cc6985b3ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe5af4 and 66c4f9c.

📒 Files selected for processing (1)
  • supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cleanup-funnel-dupe-leftovers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabase Bot commented Jul 8, 2026

Copy link
Copy Markdown

Updates to Preview Branch (feat/cleanup-funnel-dupe-leftovers) ↗︎

Deployments Status Updated
Database Wed, 08 Jul 2026 17:25:51 UTC
Services Wed, 08 Jul 2026 17:25:51 UTC
APIs Wed, 08 Jul 2026 17:25:51 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Wed, 08 Jul 2026 17:26:03 UTC
Migrations Wed, 08 Jul 2026 17:26:09 UTC
Seeding Wed, 08 Jul 2026 17:26:09 UTC
Edge Functions Wed, 08 Jul 2026 17:26:10 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql">

<violation number="1" location="supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql:88">
P1: Step 2 only checks whether the duplicate account is referenced as `artist_id`, so it can still delete an account that owns `account_artist_ids` rows and cascade those rows away. The guard needs to exclude both sides of the join before deleting the account.</violation>

<violation number="2" location="supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql:101">
P1: Deleting these orphan duplicate accounts can cascade more than the intended scraped account_socials when a qualifying account still has artist-scoped records such as funnel_analytics, campaigns, segment_reports, memories, or scheduled_actions. Consider either guarding orphan_dupe_accounts against every cascading dependent table or deleting only after explicitly verifying those dependents are absent.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

WHERE c.name = a.name AND c.id <> a.id
AND EXISTS (SELECT 1 FROM public.song_artists sa WHERE sa.artist = c.id)) = 1;

DELETE FROM public.accounts a

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Deleting these orphan duplicate accounts can cascade more than the intended scraped account_socials when a qualifying account still has artist-scoped records such as funnel_analytics, campaigns, segment_reports, memories, or scheduled_actions. Consider either guarding orphan_dupe_accounts against every cascading dependent table or deleting only after explicitly verifying those dependents are absent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql, line 101:

<comment>Deleting these orphan duplicate accounts can cascade more than the intended scraped account_socials when a qualifying account still has artist-scoped records such as funnel_analytics, campaigns, segment_reports, memories, or scheduled_actions. Consider either guarding orphan_dupe_accounts against every cascading dependent table or deleting only after explicitly verifying those dependents are absent.</comment>

<file context>
@@ -0,0 +1,119 @@
+       WHERE c.name = a.name AND c.id <> a.id
+         AND EXISTS (SELECT 1 FROM public.song_artists sa WHERE sa.artist = c.id)) = 1;
+
+DELETE FROM public.accounts a
+USING orphan_dupe_accounts o
+WHERE a.id = o.id;
</file context>

CREATE TEMP TABLE orphan_dupe_accounts ON COMMIT DROP AS
SELECT a.id
FROM public.accounts a
WHERE NOT EXISTS (SELECT 1 FROM public.account_artist_ids aai WHERE aai.artist_id = a.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.

P1: Step 2 only checks whether the duplicate account is referenced as artist_id, so it can still delete an account that owns account_artist_ids rows and cascade those rows away. The guard needs to exclude both sides of the join before deleting the account.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260708170500_cleanup_funnel_duplicate_roster_leftovers.sql, line 88:

<comment>Step 2 only checks whether the duplicate account is referenced as `artist_id`, so it can still delete an account that owns `account_artist_ids` rows and cascade those rows away. The guard needs to exclude both sides of the join before deleting the account.</comment>

<file context>
@@ -0,0 +1,119 @@
+CREATE TEMP TABLE orphan_dupe_accounts ON COMMIT DROP AS
+SELECT a.id
+FROM public.accounts a
+WHERE NOT EXISTS (SELECT 1 FROM public.account_artist_ids aai WHERE aai.artist_id = a.id)
+  AND NOT EXISTS (SELECT 1 FROM public.song_artists sa WHERE sa.artist = a.id)
+  AND NOT EXISTS (SELECT 1 FROM public.account_catalogs ac WHERE ac.account = a.id)
</file context>

@sweetmantech sweetmantech merged commit 8188eed into main Jul 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant