grove-account-deletion について
Grove の運用担当者向けに、テナントと関連データの削除手順を示すスキルです。事前確認やサブスクリプションの確認、データベースと R2 の削除検証を重視します。
主な機能
- 01テナントの特定と確認
- 02テナントと関連レコードの削除
- 03R2 メディアオブジェクトの削除
- 04削除結果の検証
- 05サブスクリプションと複数テナントの確認
活用シーン
- 01デモやテストアカウントの削除
- 02テナント削除依頼への対応
- 03テナントの残存データの整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ autumnsgrove/ lattice --skill grove-account-deletionインストール後、Claude Code で/grove-account-deletionを入力して読み込まれたか確認します。
詳細
Grove アカウントの削除
wrangler CLI コマンドを使用して、Grove プラットフォームからテナントアカウントと関連するすべてのデータを安全に削除します。
実行するタイミング
- ユーザーが「Alice のアカウントを削除して」や「Alice はもう使いません」のようなことを言った場合
- テストアカウントを削除したいと言った場合
- ウォークスルーやデモの後片付けを依頼された場合
/grove-account-deletionを明示的に呼び出した場合- 「そのアカウントを消し去って」や「データを全部消して」と言った場合
手順
Identify → Verify → Snapshot → Delete Tenant → Clean Orphans → Purge R2 → Verify
Step 1: テナントを特定する
ユーザーからユーザー名(サブドメイン)、メールアドレス、または表示名が指定されます。次のコマンドで検索します。
# By username/subdomain (most common)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE subdomain = 'USERNAME';"
# By email (if username unknown)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE email = 'EMAIL';"
# By display name (fuzzy match)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE display_name LIKE '%NAME%';"
これらのコマンドはプロジェクトルート(/Users/mini/Documents/Projects/Lattice)から実行してください。
Step 2: ユーザーに確認する
何かを削除する前に、見つかった情報をユーザーに示し、明示的な確認を求めてください。
Found tenant:
ID: abc-123-def
Username: alice
Name: Alice Wonderland
Email: [email protected]
Plan: seedling
Created: 2026-01-24
This will permanently delete:
- All blog posts and pages
- All media files (R2 storage)
- All sessions and settings
- Commerce data (orders, products, customers)
- Curio configurations (timeline, journey, gallery)
- Onboarding and billing records
Proceed with deletion?
この確認手順を絶対に省略しないでください。 テストアカウントの場合も同様です。
Step 3: 削除前のスナップショット(任意ですが推奨)
テストアカウント以外の場合、削除対象の件数を手早く記録します。
npx wrangler d1 execute grove-engine-db --remote --command="
SELECT
(SELECT COUNT(*) FROM posts WHERE tenant_id = 'TENANT_ID') as posts,
(SELECT COUNT(*) FROM pages WHERE tenant_id = 'TENANT_ID') as pages,
(SELECT COUNT(*) FROM media WHERE tenant_id = 'TENANT_ID') as media_files,
(SELECT COUNT(*) FROM sessions WHERE tenant_id = 'TENANT_ID') as sessions;
"
Step 4: テナントを削除する(CASCADE により 29 個のテーブルを処理)
この DELETE を実行すると、次のテーブルにも削除が連鎖します:posts、pages、media records、tenant_settings、sessions、products、product_variants、customers、orders、order_line_items、subscriptions、connect_accounts、platform_billing、refunds、discount_codes、post_views、timeline_curio_config、timeline_summaries、timeline_activity、timeline_ai_usage、journey_curio_config、journey_snapshots、journey_summaries、journey_jobs、gallery_curio_config、gallery_images、gallery_tags、gallery_collections、git_dashboard_config。
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM tenants WHERE id = 'TENANT_ID';"
Step 5: 孤立レコードをクリーンアップする
次のテーブルは tenants からの CASCADE 削除に含まれないため、手動でクリーンアップする必要があります。
# Remove onboarding record (linked by username, not FK)
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM user_onboarding WHERE username = 'USERNAME';"
# Remove email verification codes for that user (cascades from user_onboarding via FK, but verify)
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM email_verification_codes WHERE user_id IN (SELECT id FROM user_onboarding WHERE username = 'USERNAME');"
レガシーの users テーブルをクリーンアップします。
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM users WHERE LOWER(email) = LOWER('USER_EMAIL') AND tenant_id = 'TENANT_ID';"
Step 5b: Heartwood の認証レコードをクリーンアップする
重要: これを行わないと、Heartwood の ba_user レコードが残るため、ユーザーは再登録時に「おかえりなさい」というメッセージを目にします。
# Check if user has other tenants first
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain FROM tenants WHERE email = 'USER_EMAIL';"
# If no other tenants exist, clean Heartwood auth records:
# Delete sessions
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_session WHERE user_id IN (SELECT id FROM ba_user WHERE email = 'USER_EMAIL');"
# Delete OAuth account linkages (Google, etc.)
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_account WHERE user_id IN (SELECT id FROM ba_user WHERE email = 'USER_EMAIL');"
# Delete verification records
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_verification WHERE identifier = 'USER_EMAIL';"
# Delete the auth user record
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_user WHERE email = 'USER_EMAIL';"
Step 6: R2 メディアを削除する(メディアが存在する場合)
R2 のキーは {tenant_id}/{filename} の形式です。D1 の media テーブルのレコードはすでに削除されています(CASCADE)が、実際の R2 オブジェクトは残っています。
# List objects with tenant prefix
npx wrangler r2 object list grove-media --prefix="TENANT_ID/" --remote
# Delete each object (wrangler doesn't support bulk delete, so iterate)
# For a small number of files:
npx wrangler r2 object delete grove-media --remote "TENANT_ID/filename1.webp"
npx wrangler r2 object delete grove-media --remote "TENANT_ID/filename2.webp"
ファイルが多数ある場合は、削除スクリプトを生成します。
# List all keys, then delete in a loop
npx wrangler r2 object list grove-media --prefix="TENANT_ID/" --remote 2>/dev/null | jq -r '.[] .key' | while read key; do
echo "Deleting: $key"
npx wrangler r2 object delete grove-media --remote "$key"
done
削除前のスナップショットでメディアファイルが 0 件だった場合は、この手順を省略してください。
Step 7: 削除後の確認
テナントが完全に削除されたことを確認します。
# Verify tenant deleted
npx wrangler d1 execute grove-engine-db --remote --command="SELECT COUNT(*) as remaining FROM tenants WHERE subdomain = 'USERNAME';"
# Verify cascade worked (spot-check a few tables)
npx wrangler d1 execute grove-engine-db --remote --command="
SELECT
(SELECT COUNT(*) FROM posts WHERE tenant_id = 'TENANT_ID') as posts,
(SELECT COUNT(*) FROM media WHERE tenant_id = 'TENANT_ID') as media,
(SELECT COUNT(*) FROM sessions WHERE tenant_id = 'TENANT_ID') as sessions;
"
# Verify onboarding cleaned
npx wrangler d1 execute grove-engine-db --remote --command="SELECT COUNT(*) as remaining FROM user_onboarding WHERE username = 'USERNAME';"
Step 8: 報告
ユーザーに分かりやすく要約して報告します。
Tenant "alice" (Alice Wonderland) has been fully deleted.
Cleaned up:
- Tenant record + 29 cascaded tables
- Onboarding record
- 3 R2 media files
- Email verification codes
The subdomain "alice.grove.place" is now available for reuse.
安全ルール
- 削除前に必ず確認する - ユーザーが確信しているように見えても、まずテナントの詳細を示してください
- ID だけで削除しない - 確認のため、必ずサブドメインやメールアドレスなど、人が読める識別子に照合してください
- 有効なサブスクリプションを確認する -
platform_billing.status = 'active'または LemonSqueezy のサブスクリプションがある場合は警告してください - テストアカウントも通常のアカウントです - 同じ手順、同じ確認、同じ検証を行ってください
- R2 のデータは復元できません - R2 オブジェクトを削除すると、元に戻せません。キーを保持していた D1 の media レコードはすでに CASCADE で削除されているため、保存内容を追跡できなくなる前に R2 をクリーンアップしてください
クイックモード(テストアカウント)
ユーザーが明示的に「テストアカウント」と呼ぶアカウント、または現在のセッション中に作成されたアカウントの場合:
- ユーザー名またはメールアドレスは引き続き確認する
- 削除前のスナップショットは省略する
- メディアがアップロードされていない場合は R2 のクリーンアップを省略する
- 削除が完了したことは引き続き検証する
例外的なケース
ユーザーが複数のテナントを持っている
1 つのメールアドレスで複数のサブドメインを所有できます。指定されたテナントだけを削除してください。他のテナントには触れないでください。
テナントが見つからない
サブドメインまたはメールアドレスに一致するテナントがない場合は、ユーザーに伝えてください。推測で判断しないでください。
Heartwood のセッションクリーンアップ
Heartwood のセッションは D1 ではなく KV に保存されます。D1 の sessions テーブルは、レガシーまたはローカルのセッション用です。Heartwood のセッションは自然に期限切れになります(TTL は 24h)。緊急対応を指定された場合を除き、手動でのクリーンアップは不要です。
LemonSqueezy のサブスクリプションが有効な場合
user_onboarding.lemonsqueezy_subscription_id が設定されている場合は、LemonSqueezy のダッシュボードでサブスクリプションを別途解約する必要があることをユーザーに伝えてください。D1 の削除では、外部の課金は解約されません。
環境
すべてのコマンドはプロジェクトのルートから実行します:/Users/mini/Documents/Projects/Lattice
データベース:grove-engine-db(D1)
メディアバケット:grove-media(R2)
きれいに削除することは、優しい別れです。孤児を残さないでください。