Skip to main content

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

MicroSkills Reference

Dependency Order Planner

workflow.topological_sort

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

You send the steps and which ones must finish before which, and we return one order where every prerequisite comes first, plus the levels of steps that can run in parallel. When several orders are equally correct, the tie-breaker you choose decides: the order you declared the steps in, or alphabetical. Anything that cannot be placed — a step in a dependency loop, a step waiting on an id you never declared, or a step waiting on one of those — comes back in blocked with the reason and the ids responsible, so no step is ever silently dropped. We work out the order and nothing else: we run no step and schedule none.

Every scheduler, build tool and migration runner needs the same answer — in what order may these run — and each one re-implements it slightly differently, usually dropping the steps it cannot place. One shared answer, with the blocked steps named, keeps that from being rediscovered per service.

Reliability

Fully deterministic: the same steps, constraints and tie-breaker always give the same order. LEXICOGRAPHIC makes the answer independent of how you listed the steps, which is what you want when the input comes from an unordered source. Traversal is iterative, so a 10000-step chain is ordered rather than overflowing. Failures, which are about the request rather than the graph: WORKFLOW_NODE_LIMIT_EXCEEDED and WORKFLOW_EDGE_LIMIT_EXCEEDED mean you sent more than 10000 steps or 50000 constraints, so split the plan. WORKFLOW_IDENTIFIER_INVALID means an id is empty, longer than 128 characters, padded with whitespace, or carries a control character; fix the id and resend. WORKFLOW_OUTPUT_TOO_LARGE means the ids alone would exceed 2 MB of response, which needs a plan at the ceiling with very long ids; shorten the ids or split the plan. Loops and dangling prerequisites are never failures — they come back as complete set to false.

Context footprint

Returns ids and counts, no step payloads: a 1000-step plan is a few tens of kilobytes, and each id appears once in ordered and once in levels. A successful call costs 1 execution credit.

When to use it

When you hold tasks and prerequisites and want a run order, parallel waves, and an explicit account of anything unschedulable.

When you need the run itself, when ordering should follow cost or priority instead of dependencies, or when only a valid-or-not verdict is wanted.

Scope

Note
Compute one deterministic run order from declared prerequisites, with parallel levels, and report every step that could not be ordered and why.

Non-goals

  • Does not run or schedule anything
  • Does not resolve a dependency loop for you
  • Does not invent steps for prerequisites you never declared
  • Does not estimate durations or pick a critical path

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

data-transformation

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

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
nodesarrayYesSteps to order, 0 to 10000 closed {id} objects counted once when duplicated.
dependenciesarrayNoOrdering constraints, 0 to 50000 closed {before, after} objects; omit it so every step lands in the first level.
tieBreakerstringNoHow equal steps are ordered; omit it for INPUT_ORDER.

Output schema

FieldTypeRequiredDescription
completebooleanYesTrue only when every distinct step was placed and nothing was duplicated or dangling.
orderedarrayYesRun order of every step that could be placed, 0 to 10000 ids, safe to execute sequentially.
levelsarrayYesThe same steps grouped into parallel waves, 0 to 10000 waves; empty when nothing could be placed.
blockedarrayYesSteps that could not be placed, each a closed {id, reason, dependsOn} object in declaration order.
nodeCountintegerYesHow many distinct steps were declared.
dependencyCountintegerYesHow many distinct constraints remain after de-duplication.
tieBreakerstringYesTie-breaker actually applied, filled in with the default when you left it out.
issuesarrayYesOne closed {code, count, message} object per problem kind; empty when the order is complete.
_metaobjectYesAlways present on success, with orderedCount, blockedCount, levelCount and declaredDependencyCount.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"workflow.topological_sort","input":{"nodes":[{"id":"extract"},{"id":"load"}],"dependencies":[{"before":"extract","after":"load"}]}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

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

const { output } = await sq.execute("workflow.topological_sort", {"dependencies":[{"before":"extract","after":"transform"},{"before":"fetch-rates","after":"transform"},{"before":"transform","after":"load"}],"nodes":[{"id":"extract"},{"id":"fetch-rates"},{"id":"transform"},{"id":"load"}]});

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.
WORKFLOW_NODE_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
WORKFLOW_EDGE_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
WORKFLOW_IDENTIFIER_INVALID400NoThe request payload failed this skill's input validation.
WORKFLOW_OUTPUT_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not run or schedule anything
  • Does not resolve a dependency loop for you
  • Does not invent steps for prerequisites you never declared
  • Does not estimate durations or pick a critical path
  • Executing the plan
  • Ordering by cost, duration or priority rather than dependency
  • Graphs where you only need the validity verdict

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "workflow.topological_sort",
  "input": {
    "dependencies": [
      {
        "before": "extract",
        "after": "transform"
      },
      {
        "before": "fetch-rates",
        "after": "transform"
      },
      {
        "before": "transform",
        "after": "load"
      }
    ],
    "nodes": [
      {
        "id": "extract"
      },
      {
        "id": "fetch-rates"
      },
      {
        "id": "transform"
      },
      {
        "id": "load"
      }
    ]
  }
}

Examples

A four-step pipeline with two independent starts, ordered as declared

Inputjson
{
  "dependencies": [
    {
      "before": "extract",
      "after": "transform"
    },
    {
      "before": "fetch-rates",
      "after": "transform"
    },
    {
      "before": "transform",
      "after": "load"
    }
  ],
  "nodes": [
    {
      "id": "extract"
    },
    {
      "id": "fetch-rates"
    },
    {
      "id": "transform"
    },
    {
      "id": "load"
    }
  ]
}
Expected outputjson
{
  "blocked": [],
  "ordered": [
    "extract",
    "fetch-rates",
    "transform",
    "load"
  ],
  "nodeCount": 4,
  "levels": [
    [
      "extract",
      "fetch-rates"
    ],
    [
      "transform"
    ],
    [
      "load"
    ]
  ],
  "dependencyCount": 3,
  "issues": [],
  "tieBreaker": "INPUT_ORDER",
  "_meta": {
    "orderedCount": 4,
    "blockedCount": 0,
    "levelCount": 3,
    "declaredDependencyCount": 3
  },
  "complete": true
}

A loop and a typo: two steps strand a third, and alphabetical order settles the rest

Inputjson
{
  "nodes": [
    {
      "id": "beta"
    },
    {
      "id": "alpha"
    },
    {
      "id": "ring-a"
    },
    {
      "id": "ring-b"
    },
    {
      "id": "after-ring"
    }
  ],
  "dependencies": [
    {
      "before": "ring-a",
      "after": "ring-b"
    },
    {
      "before": "ring-b",
      "after": "ring-a"
    },
    {
      "before": "ring-b",
      "after": "after-ring"
    },
    {
      "before": "typo",
      "after": "beta"
    }
  ],
  "tieBreaker": "LEXICOGRAPHIC"
}
Expected outputjson
{
  "blocked": [
    {
      "id": "beta",
      "reason": "MISSING_DEPENDENCY",
      "dependsOn": [
        "typo"
      ]
    },
    {
      "id": "ring-a",
      "reason": "IN_CYCLE",
      "dependsOn": [
        "ring-b"
      ]
    },
    {
      "id": "ring-b",
      "reason": "IN_CYCLE",
      "dependsOn": [
        "ring-a"
      ]
    },
    {
      "id": "after-ring",
      "reason": "DEPENDS_ON_BLOCKED",
      "dependsOn": [
        "ring-b"
      ]
    }
  ],
  "ordered": [
    "alpha"
  ],
  "nodeCount": 5,
  "levels": [
    [
      "alpha"
    ]
  ],
  "dependencyCount": 4,
  "issues": [
    {
      "code": "MISSING_REFERENCE",
      "count": 1,
      "message": "A dependency references an id that no node declares."
    },
    {
      "code": "CYCLE",
      "count": 1,
      "message": "Nodes depend on each other in a loop, so no order can satisfy every dependency."
    },
    {
      "code": "BLOCKED",
      "count": 4,
      "message": "Nodes could not be placed in the order; each one reports why and which identifiers are responsible."
    }
  ],
  "tieBreaker": "LEXICOGRAPHIC",
  "_meta": {
    "orderedCount": 1,
    "blockedCount": 4,
    "levelCount": 1,
    "declaredDependencyCount": 4
  },
  "complete": false
}
  • Turn a set of tasks and prerequisites into a runnable order
  • Group independent steps into waves you can execute in parallel
  • Show an author which steps a loop or a typo has stranded

Avoid for

  • Executing the plan
  • Ordering by cost, duration or priority rather than dependency
  • Graphs where you only need the validity verdict

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

graphworkflowvalidationdag