Migration Order Planner
database.migration_order_plan
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
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
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
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.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| migrations | array | Yes | Bounded 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. |
| versionScheme | string | No | Version parsing scheme; omit it for lexical. |
| chainModel | string | No | Dependency-chain model; omit it for explicit dependencies. |
| gapDetection | string | No | Version-gap policy; omit it for disabled. |
| tieBreaker | string | No | Stable tie-break policy; omit it for declaration order. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| ordered | array | Yes | Bounded 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. |
| levels | array | Yes | Bounded array of parallelizable migration levels. The item shape is defined by the nested schema and the array contains from 0 through 10000 items. |
| blocked | array | Yes | Bounded array of blocked migration receipts. The item shape is defined by the nested schema and the array contains from 0 through 10000 items. |
| conflicts | array | Yes | Bounded array of ordering conflict receipts. The item shape is defined by the nested schema and the array contains from 0 through 10000 items. |
| applicable | boolean | Yes | Whether the migration set has a safe complete order. |
| summary | object | Yes | Closed migration-plan summary receipt. Its documented receipt properties are bounded to 128 entries — unconstrained RFC 8259 JSON items. |
API contract
POST /v1/executecurl -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"]}]}}'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
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| MIGRATION_INPUT_INVALID | 400 | No | The request payload failed this skill's input validation. |
| MIGRATION_IDENTIFIER_INVALID | 400 | No | The request payload failed this skill's input validation. |
| MIGRATION_VERSION_UNORDERABLE | 400 | No | The request payload failed this skill's input validation. |
| MIGRATION_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| MIGRATION_EDGE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| MIGRATION_OPTION_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| MIGRATION_OUTPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| DATABASE_CREDENTIAL_FIELD_REJECTED | 400 | No | The skill does not support the requested variant. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 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)
{
"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
{
"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"
]
}
]
}{
"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
{
"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"
]
}
]
}{
"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"
}
]
}Recommended use cases
- 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
Related skills
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.