Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Migration Order Planner

database.migration_order_plan

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

You send the migrations you hold — an id, a version, and whichever prerequisites each one has — and we return the order they may be applied in, the waves that can run together, and every migration we could not place with the reason. On top of the ordering we check what a dependency sort cannot see: a hole in the version series, two migrations claiming one version, two migrations forking one chain, a prerequisite that ships later than the migration needing it, and a prerequisite nothing declares. We read no migration body, open no connection, and apply nothing: the answer is about the set you described.

Every migration runner orders pending changes slightly differently, and most of them stay quiet about the ones they could not place — a duplicate version or a forked chain then surfaces halfway through a release. You get one order, and every objection named up front.

Reliability

Same set in, same order out: ties break by the rule you chose, and traversal is iterative, so a 10000-migration chain is ordered rather than overflowing. Loops, gaps, duplicates and dangling prerequisites are answers rather than errors — they come back in conflicts with applicable set to false. What does stop the call: MIGRATION_INPUT_INVALID — trigger: migrations is empty, or an entry has no version or a non-string one; retryable: no; next: fix the entry and resend. MIGRATION_IDENTIFIER_INVALID — trigger: an id, prerequisite or parent is empty, longer than 128 characters, padded with whitespace, or carries a control character; retryable: no; next: correct the id. MIGRATION_VERSION_UNORDERABLE — trigger: a version does not fit the scheme you asked for, such as 19 digits under NUMERIC or letters under TIMESTAMP_14; retryable: no; next: fix the version or pick the scheme that matches your series. MIGRATION_LIMIT_EXCEEDED — trigger: more than 10000 migrations, or more than 128 dependsOn entries on one of them; retryable: no; next: split the set. MIGRATION_EDGE_LIMIT_EXCEEDED — trigger: more than 50000 prerequisite references across the array, counted before we build anything; retryable: no; next: split the set or drop redundant prerequisites. MIGRATION_OPTION_UNSUPPORTED — trigger: an option value we do not offer, CONTIGUOUS_INTEGER together with TIMESTAMP_14 or LEXICOGRAPHIC, or a parent under DAG; retryable: no; next: use one of the published values. MIGRATION_OUTPUT_TOO_LARGE — trigger: the ids alone would exceed 2 MB of response, which takes a set at the ceiling with very long ids; retryable: no; next: shorten the ids or split the set. DATABASE_CREDENTIAL_FIELD_REJECTED — trigger: a field such as password, token or dsn arrived in the request; retryable: no; next: remove it, because we describe structure and never hold a secret.

Context footprint

Ids, versions and counts come back, never a migration body, so a 1000-migration plan stays in the tens of kilobytes. A successful call costs 2 execution credits.

When to use it

When you hold a set of migrations with versions and prerequisites and want the order plus an honest account of what would block it.

When you want the migrations applied, when your nodes carry no version series, or when the question is whether a change is safe for stored rows.

Scope

Note
Turns declared migrations into one application order with parallel waves, and reports every migration-level reason the set would not apply as written.

Non-goals

  • Does not connect to a database or read a live catalog
  • Does not read, parse or rewrite a migration body
  • Does not apply, roll back or schedule anything
  • Does not accept a connection string, user name or password
  • Does not order generic tasks — dependency ordering with no version series is what workflow.topological_sort is for
  • Does not read a clock, so a migration set has no notion of now

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

data-transformationvalidation

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call2 credits

File handling

Accepts file_refProduces file_refMax input1 MB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
migrationsarrayYesBounded array of 1 through 10,000 migration objects with id, version, and dependency properties. The item shape is defined by the nested schema and the array contains from 1 through 10000 items.
versionSchemestringNoVersion parsing scheme; omit it for lexical.
chainModelstringNoDependency-chain model; omit it for explicit dependencies.
gapDetectionstringNoVersion-gap policy; omit it for disabled.
tieBreakerstringNoStable tie-break policy; omit it for declaration order.

Output schema

FieldTypeRequiredDescription
orderedarrayYesBounded array of applicable migration identifiers in execution order. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
levelsarrayYesBounded array of parallelizable migration levels. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
blockedarrayYesBounded array of blocked migration receipts. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
conflictsarrayYesBounded array of ordering conflict receipts. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
applicablebooleanYesWhether the migration set has a safe complete order.
summaryobjectYesClosed migration-plan summary receipt. Its documented receipt properties are bounded to 128 entries — unconstrained RFC 8259 JSON items.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"database.migration_order_plan","input":{"migrations":[{"id":"0001_create_accounts","version":"1"},{"id":"0002_add_email","version":"2","dependsOn":["0001_create_accounts"]}]}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("database.migration_order_plan", {"migrations":[{"id":"0001_create_accounts","version":"1"},{"id":"0002_add_email","version":"2","dependsOn":["0001_create_accounts"]},{"id":"0003_backfill_email","version":"3","dependsOn":["0002_add_email"]},{"id":"0004_add_email_index","version":"4","dependsOn":["0003_backfill_email"]}]});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
MIGRATION_INPUT_INVALID400NoThe request payload failed this skill's input validation.
MIGRATION_IDENTIFIER_INVALID400NoThe request payload failed this skill's input validation.
MIGRATION_VERSION_UNORDERABLE400NoThe request payload failed this skill's input validation.
MIGRATION_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
MIGRATION_EDGE_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
MIGRATION_OPTION_UNSUPPORTED400NoThe skill does not support the requested variant.
MIGRATION_OUTPUT_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.
DATABASE_CREDENTIAL_FIELD_REJECTED400NoThe skill does not support the requested variant.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not connect to a database or read a live catalog
  • Does not read, parse or rewrite a migration body
  • Does not apply, roll back or schedule anything
  • Does not accept a connection string, user name or password
  • Does not order generic tasks — dependency ordering with no version series is what workflow.topological_sort is for
  • Does not read a clock, so a migration set has no notion of now
  • Applying migrations or checking whether they already ran
  • Ordering tasks that carry no version, where a plain dependency sort fits better
  • Deciding whether a migration is safe for the data already stored

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "database.migration_order_plan",
  "input": {
    "migrations": [
      {
        "id": "0001_create_accounts",
        "version": "1"
      },
      {
        "id": "0002_add_email",
        "version": "2",
        "dependsOn": [
          "0001_create_accounts"
        ]
      },
      {
        "id": "0003_backfill_email",
        "version": "3",
        "dependsOn": [
          "0002_add_email"
        ]
      },
      {
        "id": "0004_add_email_index",
        "version": "4",
        "dependsOn": [
          "0003_backfill_email"
        ]
      }
    ]
  }
}

Examples

A four-migration chain ordered by version and prerequisite

Inputjson
{
  "migrations": [
    {
      "id": "0001_create_accounts",
      "version": "1"
    },
    {
      "id": "0002_add_email",
      "version": "2",
      "dependsOn": [
        "0001_create_accounts"
      ]
    },
    {
      "id": "0003_backfill_email",
      "version": "3",
      "dependsOn": [
        "0002_add_email"
      ]
    },
    {
      "id": "0004_add_email_index",
      "version": "4",
      "dependsOn": [
        "0003_backfill_email"
      ]
    }
  ]
}
Expected outputjson
{
  "ordered": [
    "0001_create_accounts",
    "0002_add_email",
    "0003_backfill_email",
    "0004_add_email_index"
  ],
  "summary": {
    "migrationCount": 4,
    "orderedCount": 4,
    "blockedCount": 0,
    "levelCount": 4,
    "edgeCount": 3,
    "declaredEdgeCount": 3,
    "versionScheme": "NUMERIC",
    "chainModel": "LINEAR_CHAIN",
    "gapDetection": "CONTIGUOUS_INTEGER",
    "tieBreaker": "VERSION_THEN_ID",
    "conflictCount": 0,
    "truncated": false
  },
  "levels": [
    [
      "0001_create_accounts"
    ],
    [
      "0002_add_email"
    ],
    [
      "0003_backfill_email"
    ],
    [
      "0004_add_email_index"
    ]
  ],
  "applicable": true,
  "blocked": [],
  "conflicts": []
}

A gap in the series and a prerequisite that ships later than its dependent

Inputjson
{
  "chainModel": "DAG",
  "migrations": [
    {
      "id": "0001_create_orders",
      "version": "1"
    },
    {
      "id": "0002_add_status",
      "version": "2",
      "dependsOn": [
        "0005_split_totals"
      ]
    },
    {
      "id": "0005_split_totals",
      "version": "5",
      "dependsOn": [
        "0001_create_orders"
      ]
    }
  ]
}
Expected outputjson
{
  "ordered": [
    "0001_create_orders",
    "0005_split_totals",
    "0002_add_status"
  ],
  "summary": {
    "migrationCount": 3,
    "orderedCount": 3,
    "blockedCount": 0,
    "levelCount": 3,
    "edgeCount": 2,
    "declaredEdgeCount": 2,
    "versionScheme": "NUMERIC",
    "chainModel": "DAG",
    "gapDetection": "CONTIGUOUS_INTEGER",
    "tieBreaker": "VERSION_THEN_ID",
    "conflictCount": 2,
    "truncated": false
  },
  "levels": [
    [
      "0001_create_orders"
    ],
    [
      "0005_split_totals"
    ],
    [
      "0002_add_status"
    ]
  ],
  "applicable": false,
  "blocked": [],
  "conflicts": [
    {
      "code": "VERSION_GAP",
      "severity": "warning",
      "migrationIds": [
        "0002_add_status"
      ],
      "relatedIds": [
        "0005_split_totals"
      ],
      "detail": "version 2 is followed by version 5, which skips 2 value(s)"
    },
    {
      "code": "DEPENDS_ON_LATER_VERSION",
      "severity": "error",
      "migrationIds": [
        "0002_add_status"
      ],
      "relatedIds": [
        "0005_split_totals"
      ],
      "detail": "prerequisite version 5 is greater than dependent version 2"
    }
  ]
}
  • Decide the order a pending migration set may be applied in
  • Catch a duplicate version or a forked chain before a release goes out
  • Show which migrations a missing prerequisite or a loop has stranded

Avoid for

  • Applying migrations or checking whether they already ran
  • Ordering tasks that carry no version, where a plain dependency sort fits better
  • Deciding whether a migration is safe for the data already stored

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

migrationgraphvalidationdatabase