본문으로 건너뛰기
cccskills
무료GitHub에 공개한국어 소개

db-migrations

Drizzle 데이터베이스 변경의 설계와 마이그레이션 생성, SQL 검토, 배포 절차를 정리합니다. 온라인 인덱스 생성과 재실행 가능한 데이터 백필, 개발 중 초안 재생성, rebase 이후 충돌 처리도 다룹니다.

원문원문 설명 보기

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

lobehub/lobehub스타 83,504(GitHub 83,504 + cccskills 0)
연동 도구PostgreSQL

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • 스키마 변경 배포 계획
  • 대형 테이블에 인덱스 추가
  • 기존 데이터 백필 설계
  • rebase 후 마이그레이션 재생성

한국어 소개

할 수 있는 일

이 스킬은 Drizzle을 사용하는 데이터베이스 변경의 구조 설계, 마이그레이션 생성, SQL 검토, 배포 방식을 정리합니다. 일반적인 구조 변경과 쓰기 영향을 줄이는 온라인 인덱스 생성, 기존 데이터를 갱신하는 백필을 구분해 각각의 진행 방법을 안내합니다. 생성된 SQL을 다시 실행할 수 있게 만드는 방법, 파일 이름과 저널 기록을 맞추는 방법, 개발 중 초안 마이그레이션을 재생성하는 절차, rebase 후 충돌 처리도 다룹니다. 배포 방식을 정할 때는 실제 개발 데이터베이스에서 작업을 측정하고, 관찰한 결과와 운영 환경에 대한 추론을 구분하도록 안내합니다.

이럴 때 유용합니다

테이블이나 열을 추가하거나 기존 데이터를 채울 때, 대형 테이블에 인덱스를 추가할 때 활용할 수 있습니다. 개발 중 스키마가 바뀌었거나 rebase 후 여러 마이그레이션이 충돌해 생성 파일을 다시 맞춰야 할 때도 참고할 수 있습니다.

사용 예시

  • “이 스키마 변경의 배포 방식을 정리해 주세요.”
  • “기존 데이터 갱신을 반복 실행할 수 있는 백필로 분리해 주세요.”
  • “rebase 후 스키마를 기준으로 마이그레이션을 다시 생성해 주세요.”

주의 사항

Drizzle 생성 명령으로 bun run db:generate를 안내합니다. CONCURRENTLY 인덱스 생성은 트랜잭션 안에서 실행하지 않습니다. 이미 배포되었거나 기본 브랜치에 들어간 마이그레이션은 수정하지 말고 후속 마이그레이션으로 변경합니다.

이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.

문서 점검

AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.

안전성전송·삭제·명령 실행 등을 명시하고 있는가
B일부 부족임시 데이터베이스 객체를 제거하도록 하지만 초안 마이그레이션 파일 삭제 절차도 안내합니다.
도입의 용이성필요한 도구나 API 키가 적고 적혀 있는가
B일부 부족Drizzle과 `bun run db:generate`, 승인된 데이터베이스 접근 도구가 필요합니다.

점수에는 넣지 않았습니다. 2026년 10월 26일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.

평가가 다르다고 생각되면 재검토를 요청하기

어느 항목이 왜 다르다고 생각하는지 적어 주세요. 로그인은 필요 없습니다. SKILL.md와 대조해 평가를 다시 만들고 이곳에 반영합니다.

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

포함된 파일(1)

  • SKILL.md13.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

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.

    // ✅ 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.

  • Prefixed ids only for rows that are addressed one at a time. An idGenerator prefix (task_, cmt_, brf_) earns its keep when the id turns up somewhere a person reads it — a URL segment, an API argument, a tool-call payload, a support ticket. A child table whose rows are only ever fetched in bulk for their parent takes a plain uuid instead, like task_dependencies / task_documents / task_topics; there the prefix costs a registry entry in idGenerator.ts and buys nothing back.

    // ✅ Addressed individually — deleteComment(commentId), /task/:id
    id: text('id')
      .primaryKey()
      .$defaultFn(() => idGenerator('taskComments'))
      .notNull(),
    
    // ✅ Only ever read in bulk for one parent
    id: uuid('id').defaultRandom().primaryKey().notNull(),
    
  • Column order: identity → scope → the table's own columns → visibility → timestamps. Put id and the parent FK first, then the ownership columns (user_id, workspace_id), then whatever this table is actually for, then visibility, then the ...timestamps / createdAt() spread. A reader scanning an unfamiliar table then finds the same field in the same place. visibility sitting at the end is the deliberate slot, not an accident of history: of the 22 tables that carry the column today, 17 place it in the last three columns and none place it directly after id. A new table that hoists it to the front becomes the one outlier — if the placement is ever worth changing, change it repo-wide and update this rule, not one table at a time.

Choose the rollout strategy

Classify every database change into one of these three rollout paths before generating or editing a migration.

Validate rollout assumptions on the actual Dev database

Do not choose a rollout path from hypothetical claims such as “this migration might be slow” or “installing these triggers could block deployment.” Before deciding that a schema change needs a manual production step, deferred installation, or a dedicated backfill, test the relevant operation against the project's actual Dev database.

  • Classify the database target first using the project's approved database-access tooling; never read secret-bearing .env files directly.
  • Measure the real operation or the closest safe equivalent, such as creating an identically defined probe index under a temporary name or installing temporary triggers inside a transaction that is rolled back.
  • Record the tested SQL or operation, representative row count and table size, elapsed time, and cleanup verification.
  • Keep probes reversible and remove every temporary database object after the measurement.
  • Treat a single Dev result as evidence about the observed Dev scale, not proof of production behavior. State material differences in production scale, load, cache state, and lock contention explicitly, and label any resulting production claim as an inference.

Rollout decisions must combine repository deployment facts with these measurements. Do not add operational tables, delayed activation paths, or manual release steps solely to guard against unmeasured performance concerns.

1. Regular Drizzle migration

Use the normal Drizzle workflow for schema changes that are safe to execute during deployment, such as creating a small table or adding a nullable column:

  1. Update the Drizzle schema.
  2. Run bun run db:generate.
  3. Review and harden the generated artifacts using the steps below.

Before listing a manual migration command as a release step, inspect the target repository's build and deployment scripts. If its deployment pipeline already applies migrations automatically, do not require a redundant manual run.

2. Online index creation

Creating an index normally can block writes and queries on a large or frequently accessed table, and a long-running statement can also stall the deployment. In that case:

  1. Before deploying the application, execute the index creation manually in the target database's SQL editor using CONCURRENTLY:

    CREATE INDEX CONCURRENTLY IF NOT EXISTS "table_column_idx"
    ON "table" USING btree ("column");
    
  2. Keep an idempotent, non-CONCURRENTLY version in the Drizzle migration:

    CREATE INDEX IF NOT EXISTS "table_column_idx"
    ON "table" USING btree ("column");
    

The manual online operation avoids blocking production traffic. When the deployment later runs the migration, IF NOT EXISTS makes the statement a no-op, while new or self-hosted databases can still converge through normal migration replay. Do not place CREATE INDEX CONCURRENTLY inside a transaction.

3. Data backfill

Backfills and historical-data reconciliation must run as dedicated, idempotent scripts rather than inside a Drizzle migration. Keep the schema change in Drizzle, but move row-by-row or batch data processing into a separate script so it does not block deployment.

Decide whether to run the script before or after the application deployment based on compatibility:

  • Run it before deployment when the new code or a new constraint requires existing rows to be populated immediately.
  • Run it after deployment when the application safely handles both old and new row shapes and the backfill can converge gradually.

Backfill scripts should be resumable, safe to retry, processed in bounded batches, and observable. Treat optional cleanup or eager reconciliation as optional rather than as a release blocker.

Development-stage schema changes

Schema changes churn during feature development. When the schema changes before the migration has shipped, do not hand-edit the existing migration SQL to chase the new schema shape. Delete the draft migration artifacts added by this branch (SQL file, matching snapshot, and matching journal entry), then run the generator again and re-apply the normal migration review steps below.

For example, if this branch's draft migration is NNNN_add_verify_tables (substitute the branch's real number — never an already-shipped migration):

# 1. Delete the draft SQL and its snapshot
rm packages/database/migrations/NNNN_add_verify_tables.sql
rm packages/database/migrations/meta/NNNN_snapshot.json

# 2. Remove the matching NNNN entry from the journal's "entries" array
#    packages/database/migrations/meta/_journal.json

# 3. Regenerate from the current schema
bun run db:generate

This keeps the generated SQL, snapshot, and journal aligned with the actual schema. Manual SQL edits are reserved for review-time hardening such as idempotent clauses, custom extension SQL, and meaningful filename/tag updates.

Before release, if a feature branch accumulated multiple development-only migrations, consolidate them into one migration when possible. Production does not need to replay every intermediate draft shape, and fewer migrations reduce deploy-time risk.

For example, if this branch added three drafts AAAA, BBBB, and CCCC, delete all three and regenerate a single migration:

# 1. Delete every draft SQL and snapshot this branch added
rm packages/database/migrations/{AAAA,BBBB,CCCC}_*.sql
rm packages/database/migrations/meta/{AAAA,BBBB,CCCC}_snapshot.json

# 2. Remove those entries from the journal's "entries" array
#    packages/database/migrations/meta/_journal.json

# 3. Regenerate one migration covering the full schema delta
bun run db:generate

Do not make a migration compatible with earlier development-only versions of the same branch. While the migration has not shipped, there is no production history to preserve. Fix local/dev databases directly with whatever SQL is simplest (drop the draft table, rename a column, delete draft rows), then regenerate the branch migration from the current schema.

For example, if an earlier draft on this branch created signup_attempt_id and you have since renamed it to user_signup_log_id, do not add a compatibility ALTER ... RENAME to the migration. Just fix the dev DB directly, then regenerate:

# Fix the dev DB to match the new schema (simplest SQL wins)
set -a && source .env && set +a && bun -e '
import pg from "pg";
const client = new pg.Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
await client.query("ALTER TABLE user_signup_logs DROP COLUMN signup_attempt_id");
await client.end();
'

# Regenerate so the migration reflects only the final shape
bun run db:generate

After a migration has reached production or the target default branch, treat it as immutable: add a follow-up migration instead of rewriting it.

Rebase conflicts

When a rebase conflicts in migration files, keep the upstream/default-branch migrations and remove all migrations introduced by the current feature branch. Complete the rebase, then regenerate this branch's migration from the rebased schema. This avoids merging two independent snapshots or hand-splicing journal entries.

Step 1: Generate Migrations

bun run db:generate

This generates:

  • packages/database/migrations/0046_meaningless_file_name.sql

And updates:

  • packages/database/migrations/meta/_journal.json
  • docs/development/database-schema.dbml

Custom Migrations (e.g. CREATE EXTENSION)

For migrations that don't involve Drizzle schema changes (e.g. enabling PostgreSQL extensions), use the --custom flag:

bunx drizzle-kit generate --custom --name=enable_pg_search

This generates an empty SQL file and properly updates _journal.json and snapshot. Then edit the generated SQL file to add your custom SQL:

-- Custom SQL migration file, put your code below! --
CREATE EXTENSION IF NOT EXISTS pg_search;

Do NOT manually create migration files or edit _journal.json — always use drizzle-kit generate to ensure correct journal entries and snapshots.

Step 2: Optimize Migration SQL Filename

Rename auto-generated filename to be meaningful:

0046_meaningless_file_name.sql → 0046_user_add_avatar_column.sql

Step 3: Use Idempotent Clauses (Defensive Programming)

Always use defensive clauses to make migrations idempotent (safe to re-run):

CREATE TABLE

-- ✅ Good
CREATE TABLE IF NOT EXISTS "agent_eval_runs" (
  "id" text PRIMARY KEY NOT NULL,
  "name" text,
  "created_at" timestamp with time zone DEFAULT now() NOT NULL
);

-- ❌ Bad
CREATE TABLE "agent_eval_runs" (...);

ALTER TABLE - Columns

-- ✅ Good
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "avatar" text;
ALTER TABLE "posts" DROP COLUMN IF EXISTS "deprecated_field";

-- ❌ Bad
ALTER TABLE "users" ADD COLUMN "avatar" text;

ALTER TABLE - Foreign Key Constraints

PostgreSQL has no ADD CONSTRAINT IF NOT EXISTS. Use DROP IF EXISTS + ADD:

-- ✅ Good: Drop first, then add (idempotent)
ALTER TABLE "agent_eval_datasets" DROP CONSTRAINT IF EXISTS "agent_eval_datasets_user_id_users_id_fk";
ALTER TABLE "agent_eval_datasets" ADD CONSTRAINT "agent_eval_datasets_user_id_users_id_fk"
  FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;

-- ❌ Bad: Will fail if constraint already exists
ALTER TABLE "agent_eval_datasets" ADD CONSTRAINT "agent_eval_datasets_user_id_users_id_fk"
  FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;

DROP TABLE / INDEX

-- ✅ Good
DROP TABLE IF EXISTS "old_table";
CREATE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
CREATE UNIQUE INDEX IF NOT EXISTS "users_email_unique" ON "users" USING btree ("email");

-- ❌ Bad
DROP TABLE "old_table";
CREATE INDEX "users_email_idx" ON "users" ("email");

Step 4: Update Journal Tag

After renaming the migration SQL file in Step 2, update the tag field in packages/database/migrations/meta/_journal.json to match the new filename (without .sql extension).

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

acceptance

무료요약 있음

실제 제품을 적절한 조작 환경에서 실행해 변경 사항을 검증하고, 사용자가 확인할 수 있는 증거를 모아 인수 보고서로 게시합니다. 기존 검증 계획을 확인하거나 점검 항목을 만들고, `lh` CLI로 결과를 게시할 수 있습니다.

  • 변경된 제품 동작을 실제로 확인하기
  • 스크린샷 등 검증 증거 수집하기
  • 검증 계획과 보고서 작성하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

add-model-provider

무료요약 있음

LobeHub에 AI 모델 제공자를 추가하는 과정을 안내합니다. 런타임 연동, 모델 정보와 설정, 브랜드 표시, 문서화 및 인수 확인을 다루며, 기존 제공자에 모델만 추가하는 경우와 새 연동이 필요한 경우를 구분합니다.

  • LobeHub에 새 AI 모델 제공자 추가하기
  • 제공자의 설정 안내나 설명 갱신하기
  • 모델 추가에 새 제공자 연동이 필요한지 판단하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

add-setting-env

무료요약 있음

서버 환경 변수로 사용자 설정의 기본값을 지정하도록 구현하는 절차를 안내합니다. 변수 검증, 서버 설정 구성, 사용자 저장소 반영부터 예시 환경 파일과 셀프 호스팅 문서 갱신까지 다룹니다.

  • 환경 변수로 사용자 설정 기본값 지정
  • 설정을 서버 구성과 사용자 저장소에 연결
  • 환경 변수 예시와 문서 갱신
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-runtime-hooks

무료요약 있음

에이전트 실행 단계, 도구 호출, 사람의 승인, 컨텍스트 압축, 하위 에이전트 호출에 훅을 등록하는 방법을 안내합니다. 이벤트를 관찰하고 로컬 도구 결과를 모의하거나 설정에 따라 웹훅으로 도구 호출을 허용 또는 거부할 수 있습니다.

  • 에이전트 단계와 오류 기록
  • 테스트 중 도구 결과 모의
  • 웹훅으로 도구 호출 허용 또는 거부
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-signal

무료요약 있음

에이전트의 이벤트 기반 백그라운드 작업을 설계하고 구현하도록 돕습니다. 소스, 시그널, 액션, 정책의 역할을 나누고 비동기 처리, 중복 실행 방지, 완료 기록과 관측 기능을 다룹니다.

  • 이벤트로 시작되는 백그라운드 작업 추가
  • 대화 요청과 작업을 분리
  • 액션의 중복 실행 방지
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-testing-bot

무료요약 있음

실제 데스크톱 채팅 앱에서 봇 채널의 인수 테스트를 진행합니다. Discord, Slack, Telegram, WeChat, Lark, QQ에서 메시지를 보내고 응답을 기다린 뒤 결과 화면을 캡처합니다. iMessage는 BlueBubbles 브리지를 사용합니다.

  • 실제 Discord 채널에서 봇 응답 확인하기
  • Slack이나 Telegram 대화 경로 테스트하기
  • macOS 앱의 작업 결과 화면으로 기록하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

lobehub의 스킬 모두 보기

이 스킬의 문제 신고