Skip to main content

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

MicroSkills Reference

Workflow Graph Validator

workflow.validate_dag

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

You send the steps of a workflow and the edges between them, and we tell you whether that graph can actually be run: no step depending on itself, no loop, no edge pointing at a step you never declared, no step declared twice. When something is wrong you get the offending ids back, not just a verdict — the loops, the dangling references, the duplicates, and the independent groups the graph splits into. A broken graph is a normal answer here, so structural problems come back as a report with valid set to false rather than as an error. We only read the graph you send: we run nothing, schedule nothing, and store nothing.

A workflow whose graph has a loop or a dangling edge fails somewhere in the middle of a run, after side effects have already happened. Checking the shape before anything executes turns that into a rejected submission with the offending ids in hand.

Reliability

Fully deterministic: the same graph always produces the same report, and every list comes back in declaration order, so two callers who describe one graph identically get identical bytes. Traversal is iterative, so a 10000-node chain is answered 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 nodes or 50000 edges, so split the graph. 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, since none of these can be corrected for you without changing which steps match. WORKFLOW_OUTPUT_TOO_LARGE means the ids alone would exceed 2 MB of response, which needs a graph at the ceiling with very long ids; shorten the ids, lower options.maxReportedCycles, or split the graph.

Context footprint

The report holds ids and counts, no step payloads. A clean 1000-step graph answers in a few tens of kilobytes; report arrays stop at 1000 entries each and say so in _meta. A successful call costs 1 execution credit.

When to use it

When you accept, generate or merge a pipeline definition and want the structural verdict plus the exact ids to show the author.

When you need the execution order rather than the verdict, when loops are part of the design, or when you want the workflow actually run.

Scope

Note
Report whether a declared node-and-edge workflow graph is acyclic, fully referenced and unambiguous, naming every structure that breaks it.

Non-goals

  • Does not run, schedule, or store the workflow
  • Does not order the steps — workflow.topological_sort does that
  • Does not invent steps for edges that reference unknown ids
  • Does not judge what a step does, only how it is wired

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

validation

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
nodesarrayYesWorkflow steps, 0 to 10000 closed {id} objects with unique identifiers.
edgesarrayNoDependencies, 0 to 50000 closed {from, to} objects; omit it for a graph with no edges.
optionsobjectNoClosed reporting options; omit the object for allowDisconnected true and maxReportedCycles 100.

Output schema

FieldTypeRequiredDescription
validbooleanYesTrue when the graph has no duplicate ids, dangling references, loops, or a disallowed split.
nodeCountintegerYesHow many distinct steps remain after duplicates are counted once.
edgeCountintegerYesHow many distinct edges remain after identical ones are de-duplicated.
rootsarrayYesIds of steps nothing depends on, in declaration order, 0 to 10000 strings.
leavesarrayYesIds of steps no other step waits for, in declaration order, 0 to 10000 strings.
missingNodesarrayYesIds an edge references that no node declares, in first-reference order, 0 to 10000 strings.
duplicateNodesarrayYesIds declared more than once, reported once each, 0 to 10000 strings.
duplicateEdgesarrayYesEdges declared more than once, each a closed {from, to} object, informational only.
selfLoopsarrayYesIds of steps that depend on themselves, 0 through 10000 strings in first-seen order.
cyclesarrayYesLoops as groups of ids in declaration order, capped by maxReportedCycles, 0 to 1000 groups.
disconnectedComponentsarrayYesIndependent groups when the graph splits, members in declaration order; empty when connected.
issuesarrayYesOne closed {code, count, message} object per problem kind; empty for a clean graph.
_metaobjectYesAlways present on success, with declaredEdgeCount, componentCount, cyclesTruncated and issuesTruncated.

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.validate_dag","input":{"nodes":[{"id":"extract"},{"id":"load"}],"edges":[{"from":"extract","to":"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.validate_dag", {"edges":[{"from":"extract","to":"transform"},{"from":"fetch-rates","to":"transform"},{"from":"transform","to":"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, schedule, or store the workflow
  • Does not order the steps — workflow.topological_sort does that
  • Does not invent steps for edges that reference unknown ids
  • Does not judge what a step does, only how it is wired
  • Executing or scheduling the workflow
  • Graphs where cycles are legitimate, such as state machines with returns
  • Ordering steps, which is a separate skill

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "workflow.validate_dag",
  "input": {
    "edges": [
      {
        "from": "extract",
        "to": "transform"
      },
      {
        "from": "fetch-rates",
        "to": "transform"
      },
      {
        "from": "transform",
        "to": "load"
      }
    ],
    "nodes": [
      {
        "id": "extract"
      },
      {
        "id": "fetch-rates"
      },
      {
        "id": "transform"
      },
      {
        "id": "load"
      }
    ]
  }
}

Examples

A clean four-step pipeline: two roots, one join, one leaf

Inputjson
{
  "edges": [
    {
      "from": "extract",
      "to": "transform"
    },
    {
      "from": "fetch-rates",
      "to": "transform"
    },
    {
      "from": "transform",
      "to": "load"
    }
  ],
  "nodes": [
    {
      "id": "extract"
    },
    {
      "id": "fetch-rates"
    },
    {
      "id": "transform"
    },
    {
      "id": "load"
    }
  ]
}
Expected outputjson
{
  "roots": [
    "extract",
    "fetch-rates"
  ],
  "cycles": [],
  "duplicateNodes": [],
  "valid": true,
  "missingNodes": [],
  "_meta": {
    "issuesTruncated": false,
    "cyclesTruncated": false,
    "declaredEdgeCount": 3,
    "componentCount": 1
  },
  "disconnectedComponents": [],
  "edgeCount": 3,
  "nodeCount": 4,
  "issues": [],
  "selfLoops": [],
  "duplicateEdges": [],
  "leaves": [
    "load"
  ]
}

A broken graph: a two-step loop, a self-loop and an edge to a step that was never declared

Inputjson
{
  "edges": [
    {
      "from": "a",
      "to": "b"
    },
    {
      "from": "b",
      "to": "a"
    },
    {
      "from": "c",
      "to": "c"
    },
    {
      "from": "c",
      "to": "d"
    }
  ],
  "nodes": [
    {
      "id": "a"
    },
    {
      "id": "b"
    },
    {
      "id": "c"
    }
  ]
}
Expected outputjson
{
  "roots": [],
  "cycles": [
    [
      "a",
      "b"
    ],
    [
      "c"
    ]
  ],
  "duplicateNodes": [],
  "valid": false,
  "missingNodes": [
    "d"
  ],
  "_meta": {
    "issuesTruncated": false,
    "cyclesTruncated": false,
    "declaredEdgeCount": 4,
    "componentCount": 2
  },
  "disconnectedComponents": [
    [
      "a",
      "b"
    ],
    [
      "c"
    ]
  ],
  "edgeCount": 4,
  "nodeCount": 3,
  "issues": [
    {
      "code": "MISSING_REFERENCE",
      "count": 1,
      "message": "An edge references an id that no node declares, so that dependency cannot be honoured."
    },
    {
      "code": "SELF_LOOP",
      "count": 1,
      "message": "A node depends on itself, which can never be satisfied. Self-loops are also counted as cycles."
    },
    {
      "code": "CYCLE",
      "count": 2,
      "message": "Nodes depend on each other in a loop, so no order can satisfy every edge."
    },
    {
      "code": "DISCONNECTED",
      "count": 2,
      "message": "The graph splits into independent groups. You allowed that, so it does not make the graph invalid."
    }
  ],
  "selfLoops": [
    "c"
  ],
  "duplicateEdges": [],
  "leaves": []
}
  • Reject a broken pipeline definition at submission time instead of mid-run
  • Show an author exactly which steps form a loop
  • Confirm a generated or merged graph is still executable

Avoid for

  • Executing or scheduling the workflow
  • Graphs where cycles are legitimate, such as state machines with returns
  • Ordering steps, which is a separate skill

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