Tenant Migrate
Published by joshuashepherd in my-skills
What this skill does
Audit and execute multi-tenant platform migration — determines what's needed when cloning a fully-built Movemental tenant (e.g. alan-hirsch) to bootstrap a new one (e.g. brad-brisco). Inspects Supabase via MCP, audits tenant config, env vars, feature flags, storage buckets, database org row, codebase leaks, middleware, and Vercel deployment. Does NOT handle visual theming (globals.css) or image assets — those are separate concerns. Use when onboarding a new thought leader onto the platform.
Add Tenant Migrate to your agent
Review the source and files first. When you are ready, copy the prompt instruction or use the CLI command supported by your environment.
Install with a prompt
Paste this into a compatible coding agent:
add this skill "tenant-migrate" from https://github.com/joshuashepherd/my-skillsInstall with the CLI
Run this command in a controlled environment after reviewing the repository:
npx skills add https://github.com/joshuashepherd/my-skills --skill tenant-migrateSkill instructions
Tenant Migration Skill
Audit and execute the full migration checklist when cloning an existing Movemental tenant platform (the source, e.g. alan-hirsch) to create a new tenant deployment (the target, e.g. brad-brisco).
Context
This is a multi-tenant thought leader platform. Each tenant is:
- A separate Git repo/deployment sharing the same codebase structure
- Scoped to one
organizationsrow in a shared Supabase database viaTENANT_ORG_ID - Visually differentiated via
globals.cssCSS variables andtenant.config.ts
The source repo (alan-hirsch) is the reference implementation. New tenants clone the repo, then must change every tenant-specific surface. This skill audits what's been done and what remains.
Execution Steps
Phase 1: Database & Org Identity (Supabase MCP)
Use the Supabase MCP (project vhaiiiykcukrlyvwlgip, public schema) to verify:
-
Organization row exists:
SELECT id, name, slug, description FROM organizations WHERE slug = '<target-slug>';If missing, report that the org must be created first. Do NOT create it automatically.
-
TENANT_ORG_ID matches:
- Read
.env.local.examplefor the documentedTENANT_ORG_ID - Confirm it matches the org row's
id
- Read
-
Content scoped to this org:
SELECT 'books' AS type, count(*) FROM books WHERE organization_id = '<org-id>' UNION ALL SELECT 'articles', count(*) FROM articles WHERE organization_id = '<org-id>' UNION ALL SELECT 'courses', count(*) FROM courses WHERE organization_id = '<org-id>' UNION ALL SELECT 'podcast_episodes', count(*) FROM podcast_episodes WHERE organization_id = '<org-id>' UNION ALL SELECT 'videos', count(*) FROM videos WHERE organization_id = '<org-id>';Report counts. Zero counts for enabled content types are warnings.
-
Storage buckets: Check if the tenant has a storage folder:
SELECT name FROM storage.buckets WHERE name LIKE '%media%';Note: tenant images typically live under
media-library/<tenant-slug>/in Supabase Storage. -
Vector store (if AI features enabled): Check if
OPENAI_VECTOR_STORE_IDis documented or if the org has corpus content:SELECT count(*) FROM book_chapters WHERE book_id IN (SELECT id FROM books WHERE organization_id = '<org-id>');
Phase 2: Tenant Config Audit
Read and audit src/lib/config/tenant.config.ts:
-
Name/identity fields — Must NOT reference the source tenant:
name,tagline,description,copyrightlogo.text,logo.imageUrl,logo.markLightUrl,logo.markDarkUrlabout.heading,about.leadSentence,about.body,about.credentialscontact.email,contact.speakingNotesearch.placeholder(should not say "Search Alan's...")newsletter.headline,newsletter.subline,newsletter.leadMagnetquote.text,quote.cite
-
Feature flags —
features.*should reflect what the new tenant actually has:- If
features.books === true, there must be books in the DB for this org - If
features.courses === true, there must be courses - If
features.chat === true, AI/agent infrastructure must be configured - If
features.assessments === true, assessment records must exist - If
features.podcasts === true, podcast episodes must exist
- If
-
Chat config — All chat strings reference source tenant's name/voice:
chat.welcomeMessage,chat.disclaimer,chat.featuredSublinechat.firstMessageHost,chat.assistantLabelchat.floatingDocsAriaLabel
-
Themes/pathways — These are deeply tenant-specific:
themes[]slugs, titles, descriptions, coverImagesframeworks.items[]slugs and descriptionspathwayCta.*contenthome.router.options[]andhome.router.ctas[]
-
Organizations/partners —
organizations.items[]must be the new tenant's partners, not the source's -
Pricing —
pricing.plans[]may differ per tenant -
Author profile (EEAT) —
authorProfile.*must reflect the new author -
Hero —
hero.*heading, subheading, CTAs, imageUrl, backgroundImageUrl -
Content type descriptions —
contentTypes.*descriptions reference source tenant's domain -
Home page sections —
home.*section copy
Phase 3: Environment Variables
Read .env.local.example and verify:
TENANT_ORG_ID— Must be the new tenant's org UUIDDATABASE_URL— Same shared Supabase instanceNEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_ANON_KEY— Same projectOPENAI_API_KEY/OPENAI_MODEL— If chat enabledOPENAI_VECTOR_STORE_ID— Tenant-specific vector store for RAGAI_LAB_AGENT_URL/AI_LAB_AGENT_API_KEY— If using external agentSTRIPE_SECRET_KEY/NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY— Tenant-specific Stripe accountSENTRY_DSN/SENTRY_PROJECT— Tenant-specific Sentry projectCOURSE_PREVIEW_HMAC_SECRET— Must match studio if using course preview
Phase 4: Codebase Grep for Source Tenant Leaks
Search the entire src/ directory for hardcoded references to the source tenant:
Grep for: "Alan Hirsch", "alan-hirsch", "alanhirsch", "Alan's", "mDNA", "APEST", "Forgotten Ways", "5Q", "Reframation"
Exclude:
tenant.config.ts(that's the config file itself)node_modules/,.next/,_docs/- Comments in generated files
Any hits in components, pages, or services are violations that must be fixed.
Phase 5: Middleware & Auth
Read src/middleware.ts and verify:
- Protected route patterns still make sense for the new tenant
- No hardcoded paths specific to the source tenant
Phase 6: Vercel Deployment
Check for Vercel project linkage:
.vercel/project.json— Should reference the new tenant's Vercel project- Environment variables must be set in Vercel dashboard for all required vars
Output Format
Generate a structured migration report:
# Tenant Migration Report: [source] → [target]
## Status Summary
- Database org: [PASS/FAIL/MISSING]
- Tenant config: [X/Y fields migrated]
- Env vars: [CONFIGURED/NEEDS ATTENTION]
- Source tenant leaks: [N violations found]
- Middleware/auth: [PASS/NEEDS REVIEW]
- Vercel deployment: [LINKED/NOT LINKED]
## Critical (must fix before launch)
1. ...
## Warnings (should fix)
1. ...
## Passed
1. ...
## Recommended Next Steps
1. ...
Important Rules
- Never modify the Drizzle schema for migration purposes
- Never create database rows without explicit user approval
- Use Supabase MCP for all database queries — do not guess structure
- If MCP is unavailable, report what you can from code inspection alone and note which DB checks were skipped
- Visual theming (globals.css) and image assets are out of scope — handle those separately via
/color-audit,/asset-match, or manual design work - The tenant.config.ts + .env.local are the two files that MUST change for every migration — everything else is structural
- Run
/tenant-checkafter migration to verify no hardcoded strings leaked through
Files included
- SKILL.md

