agentleFS
gabriellstgabriellst/codm/.claude/skills/migrate/SKILL.md

migrate

Skill4 starsChanged 2 months agoMITView on GitHub

Generate SQLite migrations after schema design is complete. Use this skill AFTER `/db-modelling` to turn Drizzle schema changes into versioned SQL, mirror them into the Go embed, and verify they apply.

We split this skill into the rules it gives an agent

2 rules. 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 45 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    After /db-modelling updated packages/contracts/src/db/sqlite/*.ts and you need the SQL

    line 49
  2. Distinctive:

    CREATE/ALTER correctness, and data-loss risk (column drops, type changes).

    line 78

“Common” is measured: kept by at least 6 in 10 of the 45 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

BEFORE USING: Schema design must be done first with /db-modelling. This skill only authors the migration and gets it into both runtimes.

Database Migration

Turns schema file changes into versioned SQL. The database is one SQLite file at $CODM_DATA_DIR/codm.db, shared by the TS daemon and the Go gateway.

The one thing to internalise: ONE ledger

The invariant is one ledger, one SQL source — not "no script".

Two processes open the same file, so migrations are applied at boot, by two idempotent migrators over the SAME ledger (_sqlite_migrations, keyed by filename):

ProcessMigratorSource of the SQL
TS daemonLibsqlDriverpackages/contracts/src/db/sqlite/migrations/*.sql
Go gatewaySqliteStorepackages/api/go/core/db/sqlite/migrations/*.sql (//go:embed)

Whoever boots first applies; the second finds the ledger rows and no-ops. Both split each file on --> statement-breakpoint and derive the set to apply from readdir | filter .sql | sort — never from meta/_journal.json.

Convention: the schema directory name is the DIALECT, never the deployment. codm runs TWO independent trunks (ADR 0005) — packages/contracts/src/db/sqlite/ (the desktop daemon + Go gateway trunk, this skill's concern) and packages/contracts/src/db/pg/ (the cloud trunk for auth/owner). Each directory names the dialect its drizzle.config.ts declares (sqlite, postgresql) — never a deployment label like schema (historical, dialect-neutral) or cloud. A fork that swaps SQLite for a different embedded dialect renames the directory alongside the config; the two are never allowed to disagree.

A third applier carrying a ledger of its own is the failure mode this design exists to prevent — which is why drizzle-kit migrate (it writes __drizzle_migrations) has no script here.

bun migrate:dev exists and is safe because it is not a third applier: it calls migrateEmbeddedDatabase() — the same function src/boot/migrate-embedded.ts calls — so it is the TS row of the table above, run without booting the server. Reach for it to prepare a cold data dir for a test or a script; bun dev still migrates on its own and needs no help. It creates the Drizzle tables only: whatsmeow_* belongs to the gateway and appears on first connect.

When to Use This Skill

  • After /db-modelling updated packages/contracts/src/db/sqlite/*.ts and you need the SQL
  • Reviewing generated migration SQL before it reaches a runtime
  • Getting a new migration into the Go //go:embed copy

When NOT to Use This Skill

  • Designing tables, columns, indexes, relationships — use /db-modelling first
  • Deciding how to persist value objects — use /db-modelling
  • Writing repository code — use /repository

Prerequisites

  • Schema files updated via /db-modelling under packages/contracts/src/db/sqlite/
  • New table exported from packages/contracts/src/db/sqlite/index.ts

Process

Step 1: Generate the migration

bun migrate:create

drizzle-kit generate with packages/contracts/src/db/sqlite/drizzle.config.ts, writing SQL + snapshot into packages/contracts/src/db/sqlite/migrations/. With no schema change it prints No schema changes, nothing to generate and writes nothing — the command is idempotent.

Step 2: Review the generated SQL

  • CREATE/ALTER correctness, and data-loss risk (column drops, type changes).
  • SQLite has no ALTER COLUMN. Changing a column's type or constraints makes drizzle emit a table REBUILD (create new → copy → drop → rename). Read those migrations line by line; they are the ones that lose data quietly.
  • Closed sets are CHECK constraints, not native enums: text().$type<Enum>() + enumCheck(...). A rebuild has to carry the CHECK across, or the value-set stops being enforced in the database.
  • Namespacing is by table-name prefix (terminal_*, shared_*) — the flat dialect has no CREATE SCHEMA, so there is nothing to make idempotent at the schema level.

Step 3: Mirror the SQL into the Go embed

bun run --cwd packages/contracts db:sync-go

The //go:embed directory is a derived copy, never hand-edited, and must stay byte-identical to the contracts source: the shared ledger is keyed by filename, so a copy that drifted in content while keeping its name is silently skipped by whichever process boots second — the two processes then disagree about the shape of the database, with nothing in the logs.

Step 4: Verify

bun run --cwd packages/contracts db:check-go   # byte-equality gate (also inside bun test:tooling)
bun tsc
( cd packages/api/typescript && bun test core/src/db/drivers/LibsqlDriver.test.ts )
( cd packages/api/go && go test ./core/db/sqlite/... )

The driver/store tests are what actually prove the SQL applies: they migrate a temp file from cold and assert the second pass applies zero migrations.

Troubleshooting

The gateway and the daemon disagree about a table

Almost always a drifted embed copy. Run db:check-go. If it reports content, re-run db:sync-go and rebuild the Go binary — an already-compiled gateway carries the OLD bytes.

"table already exists" on boot

Two migrations with the same filename, or a file renamed after it was applied. The ledger keys on filename, so renaming applied SQL makes it look new. Never rename an applied migration.

Migration history

-- against $CODM_DATA_DIR/codm.db
SELECT * FROM _sqlite_migrations ORDER BY name;

Checklist

  • Schema files updated via /db-modelling, table exported from sqlite/index.ts
  • Migration generated: bun migrate:create
  • SQL reviewed — rebuild statements checked for data loss, CHECK constraints carried across
  • Go embed mirrored: bun run --cwd packages/contracts db:sync-go
  • Byte-equality gate green: bun run --cwd packages/contracts db:check-go
  • Applies from cold and no-ops on a second pass (driver + store tests)
  • Types compile: bun tsc

Bad Practices

bp-01: Destructive Migration Without Plan

Severidade: 🔴 Crítico

Dropping columns or tables without a safety plan risks data loss — and on SQLite the drop is often a full table rebuild, so "just the column" is not what actually happens.

-- ❌ WRONG — dropping directly
ALTER TABLE owner_users DROP COLUMN phone_number;
-- ✅ CORRECT — phased
-- Step 1: remove code references to the column
-- Step 2: ship the code without the column usage
-- Step 3: drop the column in a separate migration after verification
ALTER TABLE owner_users DROP COLUMN phone_number;

bp-02: Hand-editing the Go embed copy

Severidade: 🔴 Crítico

packages/api/go/core/db/sqlite/migrations/ is DERIVED. Editing it makes the two migrators apply different DDL under the same ledger key — a divergence that raises no error, only wrong reads. Edit the contracts source and re-run db:sync-go.

bp-03: Reintroducing a second LEDGER

Severidade: 🔴 Crítico

drizzle-kit migrate and an ad-hoc drizzle-kit push each bring their own ledger (__drizzle_migrations) and re-apply DDL the boot migrators already applied — a divergence that raises no error, only wrong reads.

The test is the ledger, not the entry point. bun migrate:dev is fine because it delegates to migrateEmbeddedDatabase() and therefore writes _sqlite_migrations like the boot path; a script that reaches for drizzle-kit to do the same job is not. If you add another way to apply migrations, it must end up in _sqlite_migrations, keyed by filename, reading the contracts migrations dir.

References

  • .claude/skills/db-modelling/SKILL.md — schema design (tables, columns, indexes, VO persistence)
  • scripts/db/sync-sqlite-migrations.ts — the source→derived copy gate, with the full rationale
  • packages/contracts/src/db/sqlite/drizzle.config.ts — why there is no drizzle:migrate
  • Drizzle ORM Documentation: https://orm.drizzle.team/docs
  • docs/BACKEND.md — architecture principles

Download now (ZIP)

  • Other versions58 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 58

    1. garrytan/gbrain★ 31ksame name
    2. tuist/tuist★ 5.9ksame name
    3. parcadei/Continuous-Claude★ 3.9ksame name
    4. parcadei/Continuous-Claude-v2★ 3.9ksame name
    5. parcadei/Continuous-Claude-v3★ 3.9ksame name
    6. pipecat-ai/gradient-bang★ 420same name
    7. govctl-org/govctl★ 272same name
    8. AsiaOstrich/universal-dev-standards★ 82same name
    9. jackmusick/bifrost★ 64same name
    10. atomicinnovation/accelerator★ 34same name

    48 more not listed

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • Used alongsideProjects with it also use incident, init and 9 more.

    Other files people use alongside this one.

    Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
    • JkudjoSkill

      incident

      Production incident response — structured triage, hypothesis tree, safe remediation, incident record

      Also in 3 projects that use this

    • MicrosoftSkill

      init

      Also in 8 projects that use this

    • liatrio-labsSkill

      sdd

      Execute the Liatrio Spec-Driven Development (SDD) workflow when explicitly invoked by the user. NOTE: this skill is NOT intended to be dynamically loaded or automatically triggered; it should only ever be explicitly called by the user.

      Also in 3 projects that use this

    • AnthropicSkill

      update

      Sync tasks and refresh memory from your current activity. Use when pulling new assignments from your project tracker into TASKS.md, triaging stale or overdue tasks, filling memory gaps for unknown people or projects, or running a comprehensive scan to catch todos buried in chat and email.

      Also in 4 projects that use this

    • GitHubSkill

      refactor

      Surgical code refactoring to improve maintainability without changing behavior. Covers extracting functions, renaming variables, breaking down god functions, improving type safety, eliminating code smells, and applying design patterns. Less drastic than repo-rebuilder; use for gradual improvements.

      Also in 8 projects that use this

    • sunnyclinches44Skill

      explain

      Explains code using real-world analogies and ASCII diagrams

      Also in 5 projects that use this

  • In this repository45 other files it gives its agents.

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.