{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://pipelinexlab.com/contracts/component-descriptor.v2.schema.json",
  "title": "PipelineXLab ComponentDescriptor v2 authoring schema",
  "$comment": "Contract-only authoring metadata for deterministic Component container V2 publication. It is not emitted by the current SDK.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "$schema",
    "schemaVersion",
    "componentKey",
    "componentContractVersion",
    "label",
    "description",
    "classification",
    "category",
    "typeDefinitions",
    "inputs",
    "results",
    "executorEntry",
    "presentation"
  ],
  "properties": {
    "$schema": {
      "const": "https://pipelinexlab.com/contracts/component-descriptor.v2.schema.json"
    },
    "schemaVersion": {
      "const": 2
    },
    "componentKey": {
      "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
    },
    "componentContractVersion": {
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "label": {
      "$ref": "#/$defs/label"
    },
    "description": {
      "$ref": "#/$defs/description"
    },
    "classification": {
      "const": "flow-node"
    },
    "category": {
      "$ref": "#/$defs/primaryCategory"
    },
    "subcategory": {
      "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
    },
    "typeDefinitions": {
      "$ref": "./pxflow-direct.v1.schema.json#/$defs/typeDefinitions"
    },
    "inputs": {
      "type": "array",
      "maxItems": 8,
      "items": {
        "$ref": "#/$defs/inputPort"
      }
    },
    "results": {
      "type": "array",
      "maxItems": 8,
      "items": {
        "$ref": "#/$defs/resultPort"
      }
    },
    "settings": {
      "$comment": "The instance settings this Component declares, absent when it declares none. Eight is the same bound the renderer draws ports with, for the same reason: a panel that needs a scrollbar to show what a Node is configured with is a Node whose configuration belongs in a record the Flow supplies.",
      "type": "array",
      "maxItems": 8,
      "items": {
        "$ref": "#/$defs/settingDeclaration"
      }
    },
    "execution": {
      "$ref": "#/$defs/executionPolicy"
    },
    "executorEntry": {
      "$ref": "#/$defs/executorEntry"
    },
    "presentation": {
      "$ref": "#/$defs/presentation"
    }
  },
  "$defs": {
    "nonEmptyString": {
      "type": "string",
      "minLength": 1,
      "$comment": "Publisher-facing text is NFC-normalized by semantic admission. The schema keeps it trimmed, single-line and free of C0/C1, bidi-formatting and invisible layout controls.",
      "pattern": "^(?!\\s)(?!.*\\s$)[^\\u0000-\\u001F\\u007F-\\u009F\\u00AD\\u061C\\u200B\\u200E\\u200F\\u2028-\\u202E\\u2060-\\u206F\\uFEFF]+$"
    },
    "label": {
      "allOf": [
        {
          "$ref": "#/$defs/nonEmptyString"
        },
        {
          "type": "string",
          "maxLength": 120
        }
      ]
    },
    "description": {
      "allOf": [
        {
          "$ref": "#/$defs/nonEmptyString"
        },
        {
          "type": "string",
          "maxLength": 2000
        }
      ]
    },
    "primaryCategory": {
      "enum": [
        "input",
        "transform",
        "calculate",
        "review",
        "deliver",
        "control"
      ]
    },
    "portUnit": {
      "$comment": "The shared V1 unit grammar admits only the RFC-003 approved UCUM subset; this V2-local presentation bound does not widen it. The presentation resolution port bounds a projected unit at 64 Unicode scalar values, so a V2 declaration admitted above that bound could not project a schema-valid response. It is refused here rather than truncated later, because a truncated unit is a different unit.",
      "allOf": [
        {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/unitCode"
        },
        {
          "type": "string",
          "maxLength": 64
        }
      ]
    },
    "portDescription": {
      "allOf": [
        {
          "$ref": "#/$defs/nonEmptyString"
        },
        {
          "type": "string",
          "maxLength": 500
        }
      ]
    },
    "inputPort": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "portKey",
        "type",
        "description",
        "required",
        "nullable"
      ],
      "properties": {
        "portKey": {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
        },
        "type": {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/typeDescriptor"
        },
        "description": {
          "$ref": "#/$defs/portDescription"
        },
        "unit": {
          "$ref": "#/$defs/portUnit"
        },
        "required": {
          "type": "boolean"
        },
        "nullable": {
          "type": "boolean"
        }
      }
    },
    "resultPort": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "portKey",
        "type",
        "description",
        "nullable"
      ],
      "properties": {
        "portKey": {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
        },
        "type": {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/typeDescriptor"
        },
        "description": {
          "$ref": "#/$defs/portDescription"
        },
        "unit": {
          "$ref": "#/$defs/portUnit"
        },
        "nullable": {
          "type": "boolean"
        }
      }
    },
    "executorEntry": {
      "type": "string",
      "maxLength": 512,
      "allOf": [
        {
          "pattern": "^content/(?:[A-Za-z0-9][A-Za-z0-9._-]*/)*[A-Za-z0-9][A-Za-z0-9._-]*\\.py$"
        },
        {
          "not": {
            "pattern": "^content/components/"
          }
        }
      ]
    },
    "presentation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "icon",
        "summaryResultKeys"
      ],
      "properties": {
        "icon": {
          "type": "string",
          "maxLength": 64,
          "pattern": "^[a-z][a-z0-9-]*$"
        },
        "summaryResultKeys": {
          "type": "array",
          "maxItems": 4,
          "uniqueItems": true,
          "items": {
            "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
          }
        }
      }
    },
    "settingDeclaration": {
      "$comment": "One instance setting: a value the placing user chooses for one Node, where a port is a value another Node supplies. The declared kinds are exactly the ones a host control can offer a person -- a record, table, list or artifact has no control, so declaring one would ship a panel field nobody could fill. `default` is what the host writes when the placing user changes nothing; it is judged against this setting's own type at admission.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "settingKey",
        "type",
        "description",
        "required",
        "nullable"
      ],
      "properties": {
        "settingKey": {
          "$ref": "./pxflow-direct.v1.schema.json#/$defs/semanticKey"
        },
        "type": {
          "type": "object",
          "allOf": [
            {
              "$ref": "./pxflow-direct.v1.schema.json#/$defs/typeDescriptor"
            }
          ],
          "properties": {
            "kind": {
              "enum": [
                "boolean",
                "int64",
                "float64",
                "decimal",
                "string",
                "enum",
                "date",
                "timestamp",
                "duration"
              ]
            }
          }
        },
        "description": {
          "$ref": "#/$defs/portDescription"
        },
        "unit": {
          "$ref": "#/$defs/portUnit"
        },
        "required": {
          "type": "boolean"
        },
        "nullable": {
          "type": "boolean"
        },
        "default": {
          "$comment": "Any JSON literal the declared type admits."
        }
      }
    },
    "executionPolicy": {
      "$comment": "What one Component says about re-running it. Nothing structural guarantees determinism -- an executor is ordinary Python and may read a clock or a random source -- so the runtime cannot infer this, and inferring it wrongly would serve a stale answer as a fresh one. An absent member is a declaration that made no promise and reuses nothing; it is never read as a default that admits reuse. `cache: content` requires `deterministic: true`.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "deterministic",
        "cache"
      ],
      "properties": {
        "deterministic": {
          "type": "boolean"
        },
        "cache": {
          "enum": [
            "none",
            "content"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "type": "object",
            "properties": {
              "cache": {
                "const": "content"
              }
            },
            "required": [
              "cache"
            ]
          },
          "then": {
            "type": "object",
            "properties": {
              "deterministic": {
                "const": true
              }
            }
          }
        }
      ]
    }
  }
}
