{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://www.anguskit.com/schema/anguscicd/pipeline-v1.json",
  "title": "AngusCICD Pipeline Configuration",
  "description": "JSON Schema for the AngusCICD pipeline YAML. The authoritative source is maintained by the xcan-anguscicd.spec module (kept in sync with the spec models) and is shared by the Monaco Editor (completion/validation) and the server side. Multi-word fields accept both camelCase and kebab-case (aliases registered by FluentPropertyUtils), e.g. fail-fast≡failFast, max-parallel≡maxParallel, continue-on-error≡continueOnError; the condition field if≡condition. Composite actions (the action.yml referenced by step.uses) are defined inline under definitions.ActionConfig (with ActionInput / ActionRuns; sub-steps reuse StepConfig). A localized Chinese variant is available as anguscicd-pipeline-v1_zh_CN.json.",
  "type": "object",
  "required": [
    "name",
    "pipeline"
  ],
  "additionalProperties": false,
  "properties": {
    "version": {
      "type": "string",
      "description": "Schema version",
      "enum": [
        "1",
        "1.0"
      ],
      "default": "1"
    },
    "name": {
      "type": "string",
      "description": "Pipeline name",
      "minLength": 1,
      "maxLength": 200
    },
    "checkContext": {
      "type": "string",
      "description": "Git status check name written to PR checks. Must match branch protection requiredStatusCheckContexts exactly. Defaults to the pipeline entity name.",
      "minLength": 1,
      "maxLength": 200
    },
    "check-context": {
      "type": "string",
      "description": "Kebab-case alias of checkContext",
      "minLength": 1,
      "maxLength": 200
    },
    "env": {
      "type": "object",
      "description": "Pipeline-level environment variables inherited by all jobs (overridable by job-level env)",
      "additionalProperties": {
        "type": "string"
      }
    },
    "triggers": {
      "type": "array",
      "description": "List of trigger configurations",
      "items": {
        "$ref": "#/definitions/TriggerConfig"
      }
    },
    "smart": {
      "$ref": "#/definitions/SmartConfig"
    },
    "permissions": {
      "type": "object",
      "description": "Pipeline token permission declaration. Key is a scope (e.g. contents, packages, pull-requests); value is read/write/none.",
      "additionalProperties": {
        "type": "string",
        "enum": [
          "read",
          "write",
          "none"
        ]
      }
    },
    "concurrency": {
      "$ref": "#/definitions/ConcurrencyConfig"
    },
    "pipeline": {
      "type": "object",
      "description": "Job definitions, keyed by job name",
      "minProperties": 1,
      "additionalProperties": {
        "$ref": "#/definitions/JobConfig"
      }
    }
  },
  "definitions": {
    "TriggerConfig": {
      "type": "object",
      "description": "Trigger configuration",
      "properties": {
        "on": {
          "type": "string",
          "description": "Trigger event type",
          "enum": [
            "push",
            "pull_request",
            "tag",
            "schedule",
            "workflow_dispatch",
            "webhook",
            "manual"
          ]
        },
        "branches": {
          "type": "array",
          "description": "Branch patterns to trigger on (glob wildcards supported)",
          "items": {
            "type": "string"
          }
        },
        "paths": {
          "type": "array",
          "description": "Path filters (only changes matching these paths trigger the pipeline)",
          "items": {
            "type": "string"
          }
        },
        "cron": {
          "type": "string",
          "description": "Cron expression (only effective when on=schedule)",
          "pattern": "^[0-9*/,-]+\\s+[0-9*/,-]+\\s+[0-9*/,-]+\\s+[0-9*/,-]+\\s+[0-9*/,-]+$"
        },
        "inputs": {
          "type": "array",
          "description": "Manual trigger input definitions (only effective when on=workflow_dispatch)",
          "items": {
            "$ref": "#/definitions/WorkflowDispatchInput"
          }
        },
        "events": {
          "type": "array",
          "description": "Webhook event type filter (only effective when on=webhook). Empty means accept any inbound event.",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "on"
      ]
    },
    "ConcurrencyConfig": {
      "type": "object",
      "description": "Concurrency group: at most one in-progress run per group. cancel-in-progress=true cancels the previous run.",
      "required": [
        "group"
      ],
      "properties": {
        "group": {
          "type": "string",
          "description": "Concurrency group id. Supports ${{ }} templates (e.g. per-branch).",
          "minLength": 1,
          "maxLength": 500
        },
        "cancelInProgress": {
          "type": "boolean",
          "description": "When true, a new run cancels in-progress runs in the same group",
          "default": false
        },
        "cancel-in-progress": {
          "type": "boolean",
          "description": "Kebab-case alias of cancelInProgress",
          "default": false
        }
      }
    },
    "WorkflowDispatchInput": {
      "type": "object",
      "description": "Manual trigger input parameter",
      "required": [
        "name",
        "type"
      ],
      "properties": {
        "name": {
          "type": "string",
          "description": "Parameter name"
        },
        "type": {
          "type": "string",
          "description": "Parameter type",
          "enum": [
            "string",
            "boolean",
            "choice",
            "number"
          ]
        },
        "required": {
          "type": "boolean",
          "description": "Whether the parameter is required",
          "default": false
        },
        "default": {
          "type": "string",
          "description": "Default value"
        },
        "description": {
          "type": "string",
          "description": "Parameter description"
        },
        "options": {
          "type": "array",
          "description": "Allowed values (used when type=choice)",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "SmartConfig": {
      "type": "object",
      "description": "Smart feature configuration",
      "properties": {
        "changeAnalysis": {
          "type": "boolean",
          "description": "Enable monorepo change-impact analysis; only run jobs for affected modules",
          "default": false
        },
        "cacheStrategy": {
          "type": "string",
          "description": "Cache strategy",
          "enum": [
            "aggressive",
            "normal",
            "minimal",
            "none"
          ],
          "default": "normal"
        },
        "predictiveScaling": {
          "type": "boolean",
          "description": "Experimental reserved flag for predictive runner auto-scaling. The scheduler does not consume this field; setting it has no runtime effect.",
          "default": false
        }
      }
    },
    "JobConfig": {
      "type": "object",
      "description": "Job configuration. Provide either steps or uses (workflow_call), not both.",
      "properties": {
        "runner": {
          "$ref": "#/definitions/RunnerConfig"
        },
        "steps": {
          "type": "array",
          "description": "List of steps (executed in order). Required unless uses is set.",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/definitions/StepConfig"
          }
        },
        "uses": {
          "type": "string",
          "description": "Reusable workflow reference (workflow_call): ./.anguscicd/deploy.yml or a pipeline name. When set, this job must not declare steps."
        },
        "with": {
          "type": "object",
          "description": "Inputs passed to the reusable workflow (only with uses)",
          "additionalProperties": {
            "type": "string"
          }
        },
        "needs": {
          "type": "array",
          "description": "Names of jobs this job depends on (DAG dependencies)",
          "items": {
            "type": "string"
          }
        },
        "parallel": {
          "type": "boolean",
          "description": "Whether this job may run in parallel with other dependency-free jobs",
          "default": true
        },
        "continueOnError": {
          "type": "boolean",
          "description": "Whether to continue with subsequent jobs when this job fails (kebab-case continue-on-error also accepted)",
          "default": false
        },
        "continue-on-error": {
          "type": "boolean",
          "description": "Kebab-case alias of continueOnError",
          "default": false
        },
        "if": {
          "type": "string",
          "description": "Job-level conditional expression (JEXL; alias of condition). When false the job is marked SKIPPED. The canonical template syntax is ${{ ... }} (legacy {{ ... }} is still accepted but discouraged); the wrapper may be omitted. Supports status functions always()/success()/failure()/cancelled() over upstream needs, and needs.<job>.result. Note: specifying if overrides the implicit success() gate (the job is scheduled even if upstream failed, and this expression alone decides)."
        },
        "condition": {
          "type": "string",
          "description": "Job-level conditional expression (JEXL; equivalent to if)"
        },
        "secrets": {
          "type": "array",
          "description": "List of secret names to inject (least privilege: only listed secrets are injected)",
          "items": {
            "type": "string"
          }
        },
        "environment": {
          "type": "string",
          "description": "Deployment environment label (e.g. staging, production). Not a GitHub Environment: no protection rules, wait timer, or auto-approval. Write approval: separately if a gate is required."
        },
        "env": {
          "type": "object",
          "description": "Job-level environment variables",
          "additionalProperties": {
            "type": "string"
          }
        },
        "cache": {
          "type": "array",
          "description": "List of cache configurations",
          "items": {
            "$ref": "#/definitions/CacheConfig"
          }
        },
        "strategy": {
          "$ref": "#/definitions/MatrixConfig"
        },
        "services": {
          "type": "object",
          "description": "Service (sidecar) container configurations, keyed by service name",
          "additionalProperties": {
            "$ref": "#/definitions/ServiceContainerConfig"
          }
        },
        "canaryWeight": {
          "type": "integer",
          "description": "Script convention for canary weight (0-100). The platform validates the range only and does not split traffic.",
          "minimum": 0,
          "maximum": 100
        },
        "approval": {
          "$ref": "#/definitions/ApprovalConfig"
        },
        "modules": {
          "type": "array",
          "description": "Module path prefixes associated with change-impact analysis (effective when smart.changeAnalysis=true)",
          "items": {
            "type": "string"
          }
        },
        "outputs": {
          "type": "object",
          "description": "Job-level outputs exposed to downstream jobs via needs.<job>.outputs.<key>. Each value is a template expression resolved after the job succeeds, e.g. ${{ steps.<step-id>.outputs.<key> }}",
          "additionalProperties": {
            "type": "string"
          }
        }
      },
      "oneOf": [
        {
          "required": [
            "steps"
          ],
          "not": {
            "required": [
              "uses"
            ]
          }
        },
        {
          "required": [
            "uses"
          ],
          "not": {
            "required": [
              "steps"
            ]
          }
        }
      ]
    },
    "StepConfig": {
      "type": "object",
      "description": "Step configuration",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "description": "Step name",
          "minLength": 1
        },
        "id": {
          "type": "string",
          "description": "Logical step id used to reference this step's outputs from job-level outputs (steps.<id>.outputs.<key>)"
        },
        "run": {
          "type": "string",
          "description": "Shell command (mutually exclusive with uses)"
        },
        "uses": {
          "type": "string",
          "description": "Referenced action (mutually exclusive with run), format: owner/repo@version or ./local/path"
        },
        "with": {
          "type": "object",
          "description": "Parameters passed to the action (effective with uses)",
          "additionalProperties": {
            "type": "string"
          }
        },
        "env": {
          "type": "object",
          "description": "Step-level environment variables",
          "additionalProperties": {
            "type": "string"
          }
        },
        "timeout": {
          "type": "integer",
          "description": "Step timeout (seconds)",
          "minimum": 1,
          "maximum": 86400
        },
        "shell": {
          "type": "string",
          "description": "Shell for run (bash/sh/pwsh/powershell/cmd). Default is platform-specific.",
          "enum": [
            "bash",
            "sh",
            "pwsh",
            "powershell",
            "cmd"
          ]
        },
        "workingDirectory": {
          "type": "string",
          "description": "Working directory relative to the job workspace (kebab-case working-directory also accepted)"
        },
        "working-directory": {
          "type": "string",
          "description": "Kebab-case alias of workingDirectory"
        },
        "continueOnError": {
          "type": "boolean",
          "description": "When true, a failed step does not fail the job (kebab-case continue-on-error also accepted)",
          "default": false
        },
        "continue-on-error": {
          "type": "boolean",
          "description": "Kebab-case alias of continueOnError",
          "default": false
        },
        "retry": {
          "type": "integer",
          "description": "Extra retries after a step failure (0 = no retry)",
          "minimum": 0,
          "maximum": 10
        },
        "if": {
          "type": "string",
          "description": "Step-level conditional expression (JEXL; alias of condition). Evaluated on the runner before the step runs; when false the step is marked SKIPPED and subsequent steps continue. The canonical template syntax is ${{ ... }} (legacy {{ ... }} still accepted but discouraged); the wrapper may be omitted. Supports status functions always()/success()/failure()/cancelled(). When omitted, the step runs only if all prior steps succeeded (implicit success() gate)."
        },
        "condition": {
          "type": "string",
          "description": "Step-level conditional expression (JEXL; equivalent to if)"
        }
      },
      "oneOf": [
        {
          "required": [
            "run"
          ]
        },
        {
          "required": [
            "uses"
          ]
        }
      ]
    },
    "RunnerConfig": {
      "type": "object",
      "description": "Runner configuration",
      "properties": {
        "mode": {
          "type": "string",
          "description": "Runner execution mode",
          "enum": [
            "DOCKER",
            "NATIVE"
          ]
        },
        "image": {
          "type": "string",
          "description": "Docker image (required when mode=DOCKER)"
        },
        "executor": {
          "type": "string",
          "description": "Executor type"
        },
        "labels": {
          "type": "array",
          "description": "Runner label matching (schedule to runners that have these labels)",
          "items": {
            "type": "string"
          }
        },
        "resources": {
          "$ref": "#/definitions/ResourceConfig"
        }
      }
    },
    "ResourceConfig": {
      "type": "object",
      "description": "Resource limit configuration",
      "properties": {
        "cpu": {
          "type": "integer",
          "description": "Number of CPU cores",
          "minimum": 1
        },
        "memory": {
          "type": "integer",
          "description": "Memory (MB)",
          "minimum": 128
        },
        "disk": {
          "type": "string",
          "description": "Disk space (e.g. 10G)"
        },
        "timeout": {
          "type": "integer",
          "description": "Job-level timeout (seconds)",
          "minimum": 1
        }
      }
    },
    "CacheConfig": {
      "type": "object",
      "description": "Cache configuration",
      "required": [
        "key",
        "paths"
      ],
      "properties": {
        "key": {
          "type": "string",
          "description": "Cache key (supports ${{ }} variable substitution)"
        },
        "paths": {
          "type": "array",
          "description": "List of file paths to cache",
          "items": {
            "type": "string"
          },
          "minItems": 1
        }
      }
    },
    "MatrixConfig": {
      "type": "object",
      "description": "Matrix strategy: expands automatically into multiple job instances",
      "properties": {
        "matrix": {
          "type": "object",
          "description": "Matrix dimension definitions, key=variable name, value=list of values",
          "additionalProperties": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "fail-fast": {
          "type": "boolean",
          "description": "Whether to cancel other combinations immediately when one matrix job fails (camelCase failFast also accepted)",
          "default": true
        },
        "failFast": {
          "type": "boolean",
          "description": "CamelCase alias of fail-fast",
          "default": true
        },
        "max-parallel": {
          "type": "integer",
          "description": "Maximum number of parallel matrix jobs (camelCase maxParallel also accepted)",
          "minimum": 1
        },
        "maxParallel": {
          "type": "integer",
          "description": "CamelCase alias of max-parallel",
          "minimum": 1
        },
        "exclude": {
          "type": "array",
          "description": "Matrix combinations to exclude",
          "items": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "include": {
          "type": "array",
          "description": "Additional matrix combinations to append",
          "items": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      }
    },
    "ServiceContainerConfig": {
      "type": "object",
      "description": "Service (sidecar) container configuration",
      "required": [
        "image"
      ],
      "properties": {
        "image": {
          "type": "string",
          "description": "Docker image (required)"
        },
        "env": {
          "type": "object",
          "description": "Environment variables",
          "additionalProperties": {
            "type": "string"
          }
        },
        "ports": {
          "type": "array",
          "description": "Port mappings (format: hostPort:containerPort)",
          "items": {
            "type": "string"
          }
        },
        "command": {
          "type": "string",
          "description": "Custom startup command"
        },
        "healthCheck": {
          "type": "string",
          "description": "Health check command"
        },
        "healthCheckTimeout": {
          "type": "integer",
          "description": "Health check timeout (seconds)",
          "default": 60,
          "minimum": 1
        }
      }
    },
    "ApprovalConfig": {
      "type": "object",
      "description": "Manual approval configuration",
      "properties": {
        "required": {
          "type": "boolean",
          "description": "Whether approval is required"
        },
        "approvers": {
          "type": "array",
          "description": "List of approver user IDs",
          "items": {
            "type": "integer"
          }
        },
        "timeout": {
          "type": "integer",
          "description": "Approval timeout (seconds)",
          "default": 3600
        },
        "policy": {
          "type": "string",
          "description": "YAML approval policy: any / all / quorum",
          "enum": [
            "any",
            "all",
            "quorum"
          ],
          "default": "any"
        },
        "requiredApprovals": {
          "type": "integer",
          "description": "Quorum threshold (only when policy=quorum)",
          "minimum": 1
        }
      }
    },
    "ActionConfig": {
      "type": "object",
      "description": "Composite action definition (the action.yml referenced by a pipeline step.uses). Uses the GitHub Actions standard composite form (runs.using: composite + runs.steps); top-level type/run compatibility fields are not supported. Kept in sync with the spec ActionConfig model.",
      "required": [
        "name",
        "runs"
      ],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "description": "Action name",
          "minLength": 1,
          "maxLength": 200
        },
        "description": {
          "type": "string",
          "description": "Action description",
          "maxLength": 1024
        },
        "inputs": {
          "type": "object",
          "description": "Input parameter definitions, key=input name",
          "additionalProperties": {
            "$ref": "#/definitions/ActionInput"
          }
        },
        "runs": {
          "$ref": "#/definitions/ActionRuns"
        }
      }
    },
    "ActionInput": {
      "type": "object",
      "description": "Composite action input parameter definition",
      "additionalProperties": false,
      "properties": {
        "description": {
          "type": "string",
          "description": "Parameter description"
        },
        "required": {
          "type": "boolean",
          "description": "Whether the parameter is required",
          "default": false
        },
        "default": {
          "type": "string",
          "description": "Default value (used when the caller does not provide it via with)"
        }
      }
    },
    "ActionRuns": {
      "type": "object",
      "description": "Composite action execution definition (using + steps); steps reuse this schema's StepConfig definition",
      "required": [
        "steps"
      ],
      "properties": {
        "using": {
          "type": "string",
          "description": "Execution type; fixed to composite for composite actions",
          "default": "composite"
        },
        "steps": {
          "type": "array",
          "description": "Sub-steps that make up this composite action (executed in order; sub-steps may reference other actions)",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/StepConfig"
          }
        }
      }
    }
  }
}
