通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
- 实际检查更改后的产品行为
- 收集截图或其他验证证据
- 编写验证计划和报告
整理 Drizzle 数据库变更的设计、迁移生成、SQL 审查与发布步骤,涵盖在线建索引、可重试的数据回填、开发中重新生成草稿迁移及 rebase 后处理冲突。
Use for Drizzle migration rollout, online indexes, backfills, idempotent SQL and migration regeneration or rebase conflicts.
实测评分 未实测
查看安装方法此技能帮助整理 Drizzle 数据库变更的方案,包括结构设计、迁移生成、SQL 审查和发布安排。它区分常规结构变更、尽量减少写入影响的在线索引创建,以及更新既有数据的回填任务。内容说明如何让生成的 SQL 可以安全重跑、保持文件名与日志记录一致、在开发期间重新生成草稿迁移,以及处理 rebase 后的迁移冲突。制定发布方案时,指南要求在实际开发数据库上测量相关操作,并区分已观察到的结果与对生产环境的推断。
添加表或列、补充既有数据,或考虑为大型表创建索引时,可以参考此技能。开发期间结构发生变化,需要重新生成迁移文件时,以及 rebase 后多个迁移产生冲突时,也能按指南处理。
Drizzle 的生成命令为 bun run db:generate。使用 CONCURRENTLY 创建索引时,不要放在事务中执行。已经发布或进入默认分支的迁移不要改写,应通过后续迁移提交变更。
本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。
AI 根据 SKILL.md 的内容检查了需要注意的操作和使用前提。评分只依据实际运行的结果(实测对比)。
不计入评分。 由 AI(OpenAI Codex)于 2026年10月26日 评估。仅供参考,安装前请同时查看原文。
安装前可以查看交给 AI 代理的指令内容。
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.
Classify every database change into one of these three rollout paths before generating or editing a migration.
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.
.env files directly.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.
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:
bun run db:generate.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.
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:
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");
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.
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:
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.
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.
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.
bun run db:generate
This generates:
packages/database/migrations/0046_meaningless_file_name.sqlAnd updates:
packages/database/migrations/meta/_journal.jsondocs/development/database-schema.dbmlFor 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.
Rename auto-generated filename to be meaningful:
0046_meaningless_file_name.sql → 0046_user_add_avatar_column.sql
Always use defensive clauses to make migrations idempotent (safe to re-run):
-- ✅ 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" (...);
-- ✅ 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;
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;
-- ✅ 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");
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).
还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
本技能指导在 LobeHub 中接入 AI 模型提供商,涵盖运行时、模型信息、设置、品牌展示、文档和验收,并帮助判断新增模型是否需要新的提供商集成。
本技能指导如何用服务器端环境变量设置用户偏好默认值,涵盖变量校验、服务器配置、写入用户状态,以及更新 `.env.example` 和自托管文档。
说明如何在智能体执行步骤、工具调用、人工审批、上下文压缩和子智能体调用时注册钩子。钩子可观察事件、模拟本地工具结果,也可通过 Webhook 按配置策略允许或拒绝工具调用。
此技能用于为智能体设计事件驱动的后台工作流,划分来源、信号、动作和策略的职责,并处理异步交接、去重、执行范围、完成记录与运行观测。
本技能在 macOS 桌面聊天应用中测试机器人频道:打开应用、进入频道或联系人、发送消息、等待回复并截图。支持 Discord、Slack、Telegram、微信、飞书和 QQ;iMessage 使用 BlueBubbles 桥接。