# Database Migration Rollout

Plans safe Drizzle migrations with online indexes, backfills and idempotent SQL.

- Repository: https://github.com/lobehub/lobehub (83676 stars)
- Category: DevOps
- License: see repository
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add lobehub/lobehub --skill db-migrations -g -a claude-code -y`
Codex: `npx skills add lobehub/lobehub --skill db-migrations -g -a codex -y`
Cursor: `npx skills add lobehub/lobehub --skill db-migrations -g -a cursor -y`
Gemini CLI: `npx skills add lobehub/lobehub --skill db-migrations -g -a gemini-cli -y`

## Description

Use for Drizzle migration rollout, online indexes, backfills, idempotent SQL and migration regeneration or rebase conflicts.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Database Migrations Guide

## Schema conventions

Apply these before generating any migration, they change what the schema file looks like, not just the SQL.

- **No pg enums (and no fixed value sets) for growing domains.** For columns whose value set will keep expanding (resource types, statuses, providers, …), use a plain `text` column typed via `.$type<UnionType>()`. `pgEnum` requires an `ALTER TYPE ... ADD VALUE` migration for every new literal, and even the Drizzle `text('col', { enum: [...] })` option hardwires the value list into the schema file. With `.$type<>()`, onboarding a new value is a type-only change, no migration at all.

  ```ts
  // ✅ Good, type lives in @lobechat/types, column stays plain text
  resourceType: text('resource_type').$type<TransferResourceType>().notNull(),

  // ❌ Bad, pg enum, needs ALTER TYPE per new value
  resourceType: resourceTypeEnum('resource_type').notNull(),

  // ❌ Avoid, value list hardwired into the schema file
  resourceType: text('resource_type', { enum: TRANSFER_RESOURCE_TYPES }).notNull(),
  ```

- **Keep domain constants out of schema files.** In new or modified schema files under `packages/database/src/schemas/`, shared domain literal arrays, union types, and option interfaces belong in `@lobechat/types` (one module per domain, re-exported from its `index.ts`); both the schema (`.$type<>()`) and consumers (routers via `z.enum(...)`, services, UI) import from there. This rule targets domain constants only, table objects, inferred row types, Drizzle relation objects, and zod insert/select schemas (`insertAgentSchema`, …) are the schema file's job and stay put. Existing schema files that already export such constants (e.g. `resourcePermission.ts`) are grandfathered; migrate them opportunistically when the file is next touched, not in bulk.

The rest: https://github.com/lobehub/lobehub/blob/HEAD/.agents/skills/db-migrations/SKILL.md
