{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "created_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    },
    "edges": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "source": { "type": "string" },
          "source_handle": { "type": "string" },
          "target": { "type": "string" }
        },
        "required": ["id", "source", "source_handle", "target"],
        "additionalProperties": false,
        "description": "One directed edge as returned to the client (ADR 0020 V8) — a connection\nfrom `source`'s `source_handle` output into `target`."
      }
    },
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "max_concurrent_runs": {
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991,
          "description": "Issue #110: per-workflow concurrency cap; `None` = unlimited."
        },
        { "type": "null" }
      ]
    },
    "name": { "type": "string" },
    "nodes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "config": {
            "type": "object",
            "propertyNames": { "type": "string" },
            "additionalProperties": {}
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "key": { "type": "string" },
          "kind": {
            "type": "string",
            "enum": [
              "agent",
              "gate",
              "human",
              "end",
              "code",
              "db_query",
              "db_execute",
              "http_request",
              "transform",
              "notify_telegram",
              "notify_email",
              "notify_webhook",
              "notify_discord",
              "loop",
              "wait_event",
              "llm_classify",
              "imap_ack",
              "app",
              "connector"
            ],
            "description": "The kind of a workflow node.\n\nDoubles as the OpenAPI spec mirror for the closed `kind` string on the\nworkflow node/step + run-node DTOs: `StepResponse.kind` / `NodeResponse.kind`\nnarrow to this via `#[schema(value_type = NodeKindSpec)]`. The variant set is\nthe `workflow_nodes.kind` CHECK (migration 157); the serde `snake_case`\nrename matches `as_db_str` one-for-one, so the spec enum equals what the\nhandlers serialize."
          },
          "ordinal": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "parent_node_key": {
            "anyOf": [
              {
                "type": "string",
                "description": "WP V16: when set, this node lives INSIDE the named loop frame's\nsub-graph. The V8 builder wires up `parentId` / `extent: 'parent'`\non Svelte Flow nodes carrying this field."
              },
              { "type": "null" }
            ]
          },
          "position": {
            "type": "object",
            "properties": {
              "x": { "type": "number" },
              "y": { "type": "number" }
            },
            "required": ["x", "y"],
            "additionalProperties": false,
            "description": "A node's canvas position in a response body."
          }
        },
        "required": ["config", "id", "key", "kind", "ordinal", "position"],
        "additionalProperties": false,
        "description": "One typed node as returned to the client (ADR 0020 V8). Carries the node's\n`kind`, its kind-specific `config` blob and its canvas `position` -- the\nshape the Svelte Flow builder renders directly."
      }
    },
    "project_id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "agent_type": { "type": "string" },
          "depends_on": { "type": "array", "items": { "type": "string" } },
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "kind": {
            "type": "string",
            "enum": [
              "agent",
              "gate",
              "human",
              "end",
              "code",
              "notify_telegram",
              "notify_email",
              "notify_webhook",
              "notify_discord",
              "loop",
              "db_query",
              "db_execute",
              "http_request",
              "transform",
              "wait_event",
              "llm_classify",
              "imap_ack",
              "app",
              "connector"
            ],
            "description": "The kind of a workflow node.\n\nDoubles as the OpenAPI spec mirror for the closed `kind` string on the\nworkflow node/step + run-node DTOs: `StepResponse.kind` / `NodeResponse.kind`\nnarrow to this via `#[schema(value_type = NodeKindSpec)]`. The variant set is\nthe `workflow_nodes.kind` CHECK (migration 157); the serde `snake_case`\nrename matches `as_db_str` one-for-one, so the spec enum equals what the\nhandlers serialize."
          },
          "on_failure": { "type": "string" },
          "ordinal": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "prompt_template": { "type": "string" },
          "step_key": { "type": "string" }
        },
        "required": [
          "agent_type",
          "depends_on",
          "id",
          "kind",
          "on_failure",
          "ordinal",
          "prompt_template",
          "step_key"
        ],
        "additionalProperties": false,
        "description": "A workflow step (an `agent` node) as returned to the client.\n\nADR 0020 reshapes the schema to typed nodes + edges; this DTO keeps the v1\nagent-step wire shape (`step_key` + `depends_on`) so the existing web canvas\nstays functional — `depends_on` is rebuilt from the node's incoming edges."
      }
    }
  },
  "required": [
    "created_at",
    "edges",
    "id",
    "name",
    "nodes",
    "project_id",
    "steps"
  ],
  "additionalProperties": false,
  "$id": "https://supacloud.run/schemas/workflow.json",
  "title": "SupaCloud Workflow",
  "description": "A SupaCloud workflow: typed nodes wired into a DAG (plus the v1 agent-step view). Per-node `config` is currently an open object; per-kind sub-schemas are a follow-up (ADR 0044 §4)."
}
