# Send Ecto Migrator to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "ecto-migrator",
    "name": "Ecto Migrator",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/gchapim/ecto-migrator",
    "canonicalUrl": "https://clawhub.ai/gchapim/ecto-migrator",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/ecto-migrator",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=ecto-migrator",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "references/index-patterns.md",
      "references/column-types.md",
      "SKILL.md"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "ecto-migrator",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-01T07:39:41.242Z",
      "expiresAt": "2026-05-08T07:39:41.242Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=ecto-migrator",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=ecto-migrator",
        "contentDisposition": "attachment; filename=\"ecto-migrator-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "ecto-migrator"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/ecto-migrator"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/ecto-migrator",
    "downloadUrl": "https://openagent3.xyz/downloads/ecto-migrator",
    "agentUrl": "https://openagent3.xyz/skills/ecto-migrator/agent",
    "manifestUrl": "https://openagent3.xyz/skills/ecto-migrator/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/ecto-migrator/agent.md"
  }
}
```
## Documentation

### From Natural Language

Parse the user's description and generate a migration file. Common patterns:

User SaysMigration Action"Create users table with email and name"create table(:users) with columns"Add phone to users"alter table(:users), add :phone"Make email unique on users"create unique_index(:users, [:email])"Add tenant_id to all tables"Multiple alter table with index"Rename status to state on orders"rename table(:orders), :status, to: :state"Remove the legacy_id column from users"alter table(:users), remove :legacy_id"Add a check constraint on orders amount > 0"create constraint(:orders, ...)

### File Naming

mix ecto.gen.migration <name>
# Generates: priv/repo/migrations/YYYYMMDDHHMMSS_<name>.exs

Name conventions: create_<table>, add_<column>_to_<table>, create_<table>_<column>_index, alter_<table>_add_<columns>.

### Migration Template

defmodule MyApp.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :email, :string, null: false
      add :name, :string, null: false
      add :role, :string, null: false, default: "member"
      add :metadata, :map, default: %{}
      add :tenant_id, :binary_id, null: false

      add :team_id, references(:teams, type: :binary_id, on_delete: :delete_all)

      timestamps(type: :utc_datetime_usec)
    end

    create unique_index(:users, [:tenant_id, :email])
    create index(:users, [:tenant_id])
    create index(:users, [:team_id])
  end
end

### Column Types

See references/column-types.md for complete type mapping and guidance.

Key decisions:

IDs: Use :binary_id (UUID) — set primary_key: false on table, add :id manually.
Money: Use :integer (cents) or :decimal — never :float.
Timestamps: Always timestamps(type: :utc_datetime_usec).
Enums: Use :string with app-level Ecto.Enum — avoid Postgres enums (hard to migrate).
JSON: Use :map (maps to jsonb).
Arrays: Use {:array, :string} etc.

### Index Strategies

See references/index-patterns.md for detailed index guidance.

### When to Add Indexes

Always index:

Foreign keys (_id columns)
tenant_id (first column in composite indexes)
Columns used in WHERE clauses
Columns used in ORDER BY
Unique constraints

### Index Types

# Standard B-tree
create index(:users, [:tenant_id])

# Unique
create unique_index(:users, [:tenant_id, :email])

# Partial (conditional)
create index(:orders, [:status], where: "status != 'completed'", name: :orders_active_status_idx)

# GIN for JSONB
create index(:events, [:metadata], using: :gin)

# GIN for array columns
create index(:posts, [:tags], using: :gin)

# Composite
create index(:orders, [:tenant_id, :status, :inserted_at])

# Concurrent (no table lock — use in separate migration)
@disable_ddl_transaction true
@disable_migration_lock true

def change do
  create index(:users, [:email], concurrently: true)
end

### Constraints

# Check constraint
create constraint(:orders, :amount_must_be_positive, check: "amount > 0")

# Exclusion constraint (requires btree_gist extension)
execute "CREATE EXTENSION IF NOT EXISTS btree_gist", ""
create constraint(:reservations, :no_overlapping_bookings,
  exclude: ~s|gist (room_id WITH =, tstzrange(starts_at, ends_at) WITH &&)|
)

# Unique constraint (same as unique_index for most purposes)
create unique_index(:accounts, [:slug])

### References (Foreign Keys)

add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false
add :team_id, references(:teams, type: :binary_id, on_delete: :nilify_all)
add :parent_id, references(:categories, type: :binary_id, on_delete: :nothing)

on_deleteUse When:delete_allChild can't exist without parent (memberships, line items):nilify_allChild should survive parent deletion (optional association):nothingHandle in application code (default):restrictPrevent parent deletion if children exist

### Every Table Gets tenant_id

def change do
  create table(:items, primary_key: false) do
    add :id, :binary_id, primary_key: true
    add :name, :string, null: false
    add :tenant_id, :binary_id, null: false
    timestamps(type: :utc_datetime_usec)
  end

  # Always composite index with tenant_id first
  create index(:items, [:tenant_id])
  create unique_index(:items, [:tenant_id, :name])
end

### Adding tenant_id to Existing Tables

def change do
  alter table(:items) do
    add :tenant_id, :binary_id
  end

  # Backfill in a separate data migration, then:
  # alter table(:items) do
  #   modify :tenant_id, :binary_id, null: false
  # end
end

### Data Migrations

Rule: Never mix schema changes and data changes in the same migration.

### Safe Data Migration Pattern

defmodule MyApp.Repo.Migrations.BackfillUserRoles do
  use Ecto.Migration

  # Don't use schema modules — they may change after this migration runs
  def up do
    execute """
    UPDATE users SET role = 'member' WHERE role IS NULL
    """
  end

  def down do
    # Data migrations may not be reversible
    :ok
  end
end

### Batched Data Migration (large tables)

def up do
  execute """
  UPDATE users SET role = 'member'
  WHERE id IN (
    SELECT id FROM users WHERE role IS NULL LIMIT 10000
  )
  """

  # For very large tables, use a Task or Oban job instead
end

### Reversible (use change)

These are auto-reversible:

create table ↔ drop table
add column ↔ remove column
create index ↔ drop index
rename ↔ rename

### Irreversible (use up/down)

Must define both directions:

modify column type — Ecto can't infer the old type
execute raw SQL
Data backfills
Dropping columns with data

def up do
  alter table(:users) do
    modify :email, :citext, from: :string  # from: helps reversibility
  end
end

def down do
  alter table(:users) do
    modify :email, :string, from: :citext
  end
end

### Using modify with from:

Phoenix 1.7+ supports from: for reversible modify:

def change do
  alter table(:users) do
    modify :email, :citext, null: false, from: {:string, null: true}
  end
end

### PostgreSQL Extensions

def change do
  execute "CREATE EXTENSION IF NOT EXISTS citext", "DROP EXTENSION IF EXISTS citext"
  execute "CREATE EXTENSION IF NOT EXISTS pgcrypto", "DROP EXTENSION IF EXISTS pgcrypto"
  execute "CREATE EXTENSION IF NOT EXISTS pg_trgm", "DROP EXTENSION IF EXISTS pg_trgm"
end

### Enum Types (PostgreSQL native — use sparingly)

Prefer Ecto.Enum with :string columns. If you must use Postgres enums:

def up do
  execute "CREATE TYPE order_status AS ENUM ('pending', 'confirmed', 'shipped', 'delivered')"

  alter table(:orders) do
    add :status, :order_status, null: false, default: "pending"
  end
end

def down do
  alter table(:orders) do
    remove :status
  end

  execute "DROP TYPE order_status"
end

Warning: Adding values to Postgres enums requires ALTER TYPE ... ADD VALUE which cannot run inside a transaction. Prefer :string + Ecto.Enum.

### Checklist

Primary key: primary_key: false + add :id, :binary_id, primary_key: true
 null: false on required columns
 timestamps(type: :utc_datetime_usec)
 Foreign keys with appropriate on_delete
 Index on every foreign key column
 tenant_id indexed (composite with lookup fields)
 Unique constraints where needed
 Concurrent indexes in separate migration with @disable_ddl_transaction true
 Data migrations in separate files from schema migrations
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: gchapim
- Version: 1.0.0
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-05-01T07:39:41.242Z
- Expires at: 2026-05-08T07:39:41.242Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/ecto-migrator)
- [Send to Agent page](https://openagent3.xyz/skills/ecto-migrator/agent)
- [JSON manifest](https://openagent3.xyz/skills/ecto-migrator/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/ecto-migrator/agent.md)
- [Download page](https://openagent3.xyz/downloads/ecto-migrator)