{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/stack-adapters.schema.json",
  "version": "1.0.0",
  "title": "Multi-Agent Pipeline  -  stack adapters",
  "description": "Shape of pipeline/schemas/stack-adapters.json: one adapter per stack, keyed by id, plus the unknown adapter every lookup falls back to. _stack-adapter.mjs enforces the same rules at load time without a schema library, and additionally compiles every pattern and requires a (?<ref>...) group in each reference pattern.",
  "type": "object",
  "additionalProperties": false,
  "required": ["version", "order", "adapters"],
  "properties": {
    "$comment": { "type": "string" },
    "version": { "type": "string", "const": "1.0.0" },
    "order": {
      "description": "Priority when a repo matches several adapters; the first match is the primary one. A manifest that names a language outranks a package.json, which is often only tooling.",
      "type": "array",
      "minItems": 1,
      "uniqueItems": true,
      "items": { "$ref": "#/$defs/adapterId" }
    },
    "adapters": {
      "type": "object",
      "required": [
        "ios",
        "android",
        "web",
        "backend-node",
        "python",
        "go",
        "rust",
        "jvm",
        "unknown"
      ],
      "propertyNames": { "$ref": "#/$defs/adapterId" },
      "additionalProperties": { "$ref": "#/$defs/adapter" }
    }
  },
  "$defs": {
    "adapterId": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
    "coarseStack": { "type": "string", "enum": ["ios", "android", "web", "backend"] },
    "marker": {
      "type": "object",
      "additionalProperties": false,
      "required": ["pattern"],
      "properties": {
        "pattern": { "type": "string", "minLength": 1, "maxLength": 200 },
        "flags": { "type": "string", "pattern": "^[im]{0,2}$" }
      }
    },
    "markerSet": {
      "type": "object",
      "additionalProperties": false,
      "required": ["success", "failure"],
      "properties": {
        "success": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/marker" } },
        "failure": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/marker" } },
        "skipped": {
          "description": "Lines the runner prints when the task did not run at all (a skipped or source-less test task). evidence-gate.mjs --stack counts a match as zero tests when the log carries no counts.",
          "type": "array",
          "items": { "$ref": "#/$defs/marker" }
        },
        "loadError": {
          "description": "Lines that mean the test did not compile or load, so it never reached an assertion. test-strength.mjs reads a red run with one of these as unjudged, not red.",
          "type": "array",
          "items": { "$ref": "#/$defs/marker" }
        }
      }
    },
    "command": {
      "description": "Either a shell template with {placeholder} slots, or a package.json script resolved through package-manager.mjs so the repo's own package manager runs it.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "template": { "type": "string", "minLength": 1 },
        "packageScript": { "type": "string", "pattern": "^[A-Za-z0-9:_-]+$" },
        "args": { "type": "array", "items": { "type": "string" } },
        "exitMarker": { "type": "string", "pattern": "^[^'\\n]+$" }
      },
      "oneOf": [{ "required": ["template"] }, { "required": ["packageScript"] }]
    },
    "commandSet": {
      "type": "object",
      "additionalProperties": false,
      "required": ["build", "test"],
      "properties": {
        "when": {
          "description": "Manifests that select this entry; an entry without it is the fallback.",
          "type": "array",
          "items": { "type": "string" }
        },
        "build": { "$ref": "#/$defs/command" },
        "test": { "$ref": "#/$defs/command" },
        "testFile": { "$ref": "#/$defs/command" },
        "lint": {
          "description": "The stack's linter, run by scaffold-gate.mjs; a skeleton must lint clean before its first commit. Optional: a stack without one reports lint not-applicable.",
          "$ref": "#/$defs/command"
        }
      }
    },
    "globList": { "type": "array", "items": { "type": "string", "minLength": 1 } },
    "patternList": { "type": "array", "items": { "type": "string", "minLength": 1 } },
    "adapter": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "label",
        "detection",
        "commands",
        "markers",
        "testSummary",
        "testFilePatterns",
        "truthSources",
        "referencePatterns"
      ],
      "properties": {
        "label": { "type": "string", "minLength": 1 },
        "applicable": { "type": "boolean" },
        "detection": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "stackDetect": { "$ref": "#/$defs/coarseStack" },
            "manifests": { "$ref": "#/$defs/globList" },
            "notWith": { "type": "array", "items": { "$ref": "#/$defs/adapterId" } }
          }
        },
        "commands": { "type": "array", "items": { "$ref": "#/$defs/commandSet" } },
        "markers": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "build": { "$ref": "#/$defs/markerSet" },
            "test": { "$ref": "#/$defs/markerSet" }
          }
        },
        "testSummary": {
          "type": "object",
          "additionalProperties": false,
          "required": ["parsers"],
          "properties": {
            "parsers": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "xcodebuild",
                  "xcresult",
                  "junit-xml",
                  "gradle",
                  "maven",
                  "pytest",
                  "jest",
                  "vitest",
                  "node-test",
                  "go-test",
                  "go-test-json",
                  "cargo-test"
                ]
              }
            },
            "resultFiles": { "$ref": "#/$defs/globList" }
          }
        },
        "testFilePatterns": { "$ref": "#/$defs/patternList" },
        "truthSources": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "localization": { "$ref": "#/$defs/globList" },
            "endpoints": { "$ref": "#/$defs/globList" },
            "generated": { "$ref": "#/$defs/globList" }
          }
        },
        "referencePatterns": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "localization": { "$ref": "#/$defs/patternList" },
            "endpoints": { "$ref": "#/$defs/patternList" }
          }
        }
      }
    }
  }
}
