{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://pcbastudio.com/schemas/finding.schema.json",
  "title": "PCBA Studio review report",
  "description": "Output of `pcba review --format json`. Stable contract for CI and agents.",
  "type": "object",
  "required": ["schema_version", "tool", "summary", "coverage", "findings"],
  "properties": {
    "schema_version": { "type": "integer", "const": 1 },
    "tool": {
      "type": "object",
      "required": ["name", "version"],
      "properties": {
        "name": { "type": "string" },
        "version": { "type": "string" }
      }
    },
    "project": { "type": "object" },
    "baseline": {
      "type": "object",
      "required": ["path", "suppressed", "stale"],
      "properties": {
        "path": { "type": "string" },
        "suppressed": { "type": "integer", "minimum": 0 },
        "stale": { "type": "array", "items": { "type": "string" } }
      }
    },
    "changes": {
      "type": "object",
      "required": ["base", "files", "omitted"],
      "properties": {
        "base": { "type": "string" },
        "files": { "type": "array", "items": { "type": "string" } },
        "omitted": { "type": "integer", "minimum": 0 }
      }
    },
    "summary": {
      "type": "object",
      "required": ["total"],
      "properties": {
        "total": { "type": "integer", "minimum": 0 },
        "error": { "type": "integer", "minimum": 0 },
        "warning": { "type": "integer", "minimum": 0 },
        "info": { "type": "integer", "minimum": 0 }
      }
    },
    "coverage": {
      "type": "object",
      "description": "Which rules ran and which could not. An empty `findings` list is NOT proof of a clean design — a consumer must read this before reporting a pass.",
      "properties": {
        "ran": { "type": "array", "items": { "type": "string" } },
        "skipped": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Rule id plus the reason it could not run."
        }
      }
    },
    "truncated": {
      "type": "object",
      "description": "Present only when the report was capped.",
      "properties": {
        "shown": { "type": "integer" },
        "total": { "type": "integer" },
        "note": { "type": "string" }
      }
    },
    "findings": { "type": "array", "items": { "$ref": "#/$defs/finding" } }
  },
  "$defs": {
    "finding": {
      "type": "object",
      "required": [
        "id",
        "rule_id",
        "title",
        "severity",
        "domain",
        "location",
        "evidence",
        "explanation",
        "consequence",
        "recommendation",
        "detector",
        "confidence"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Stable across runs so a baseline can suppress this exact finding. Derived from rule + location, never from the message text — rewording a message must not resurrect a waived finding."
        },
        "rule_id": { "type": "string", "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)+$" },
        "title": { "type": "string", "minLength": 1 },
        "severity": { "enum": ["error", "warning", "info"] },
        "domain": { "enum": ["schematic", "ioc", "firmware", "cross_domain"] },
        "location": { "$ref": "#/$defs/location" },
        "evidence": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/evidence" },
          "description": "At least one. A finding without evidence is an assertion, and engineers do not act on assertions."
        },
        "explanation": { "type": "string", "minLength": 1 },
        "consequence": { "type": "string", "minLength": 1 },
        "recommendation": { "type": "string", "minLength": 1 },
        "detector": {
          "enum": ["deterministic", "heuristic", "ai_inferred", "external", "manual"],
          "description": "How this was produced. `deterministic` means the files prove it. Never render a `heuristic` or `ai_inferred` finding as though it were a fact."
        },
        "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
        "rule_version": { "type": "string" }
      }
    },
    "location": {
      "type": "object",
      "required": ["artifact"],
      "description": "Where in the design. Something beyond `artifact` should always be set — a finding an engineer cannot navigate to is one they will not act on.",
      "properties": {
        "artifact": { "type": "string" },
        "sheet": { "type": "string" },
        "component": { "type": "string" },
        "pin": { "type": "string" },
        "line": { "type": "integer", "minimum": 1 },
        "key": { "type": "string" }
      }
    },
    "evidence": {
      "type": "object",
      "required": ["kind", "value"],
      "properties": {
        "kind": {
          "type": "string",
          "description": "e.g. schematic_net, ioc_assignment, firmware_reference, geometry, mcu_database"
        },
        "value": {
          "type": "string",
          "description": "What was actually read out of the file — quoted, not paraphrased, so it can be grepped for."
        },
        "source": { "type": "string" }
      }
    }
  }
}
