{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/mikeparcewski/wicked-testing/schemas/evidence.json",
  "title": "wicked-testing evidence manifest",
  "description": "Public manifest schema for .wicked-testing/evidence/<run-id>/manifest.json. See docs/EVIDENCE.md. ADDITIVE-FIELD POLICY: this schema (and its nested objects) sets `additionalProperties: false` for producer integrity. The manifest grows additively under `manifest_version` semver — minor bumps add optional fields only (docs/EVIDENCE.md §7). Consumers validating against a vendored copy of this schema MUST gate on `manifest_version` and ignore unknown fields rather than reject them; do not enforce `additionalProperties: false` against payloads carrying a higher minor `manifest_version` than the schema copy you hold.",
  "type": "object",
  "required": [
    "manifest_version",
    "run_id",
    "project_id",
    "scenario_id",
    "scenario_name",
    "started_at",
    "finished_at",
    "duration_ms",
    "status",
    "verdict",
    "environment",
    "artifacts"
  ],
  "properties": {
    "manifest_version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Semver for the manifest schema."
    },
    "run_id":      { "type": "string", "format": "uuid" },
    "project_id":  { "type": "string", "format": "uuid" },
    "scenario_id": { "type": "string", "format": "uuid" },
    "scenario_name": { "type": "string", "minLength": 1 },
    "scenario_path": { "type": "string" },
    "started_at":  { "type": "string", "format": "date-time" },
    "finished_at": { "type": "string", "format": "date-time" },
    "duration_ms": { "type": "integer", "minimum": 0 },
    "status": {
      "type": "string",
      "enum": ["passed", "failed", "partial", "inconclusive", "errored", "skipped"]
    },
    "verdict": {
      "type": "object",
      "description": "The recorded verdict. `additionalProperties: false` here is a PRODUCER-INTEGRITY guard, not a consumer contract: it stops wicked-testing from silently emitting unknown keys. The manifest evolves ADDITIVELY under `manifest_version` (semver) — minor bumps add OPTIONAL fields and never change or remove existing ones (see docs/EVIDENCE.md §7). STRICT external validators MUST NOT vendor this `additionalProperties: false` verbatim against payloads from a newer producer; gate on `manifest_version` and ignore unknown fields instead, or a future additive field (as `equivalence` was in 1.1.0) will be falsely rejected.",
      "required": ["value", "reviewer", "recorded_at"],
      "properties": {
        "value":    { "type": "string", "enum": ["PASS", "FAIL", "PARTIAL", "CONDITIONAL", "INCONCLUSIVE", "N-A", "SKIP"] },
        "reviewer": { "type": "string", "minLength": 1 },
        "reason":   { "type": "string" },
        "recorded_at": { "type": "string", "format": "date-time" },
        "equivalence": {
          "type": "object",
          "description": "Optional baseline-match facet. Present when a behavior-preserving change was judged against a captured baseline (golden-master / contract / data-reconciliation / perceptual). A green run is not sufficient evidence of a faithful reproduction — this names the baseline-match as a typed, queryable fact.",
          "required": ["method", "matched"],
          "properties": {
            "baseline_ref":  { "type": "string", "minLength": 1, "description": "Path or identifier of the captured baseline artifact." },
            "baseline_sha":  { "type": "string", "pattern": "^[a-f0-9]{64}$", "description": "Lowercase hex SHA-256 of the baseline, for provenance." },
            "method":        { "type": "string", "enum": ["golden-master", "contract", "reconciliation", "perceptual"] },
            "diff_count":    { "type": "integer", "minimum": 0, "description": "Number of differences found vs. the baseline." },
            "tolerance":     { "type": "number", "minimum": 0, "description": "Allowed diff threshold; matched is true when diff_count <= tolerance." },
            "matched":       { "type": "boolean", "description": "Whether the fresh output matched the baseline within tolerance." }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "environment": {
      "type": "object",
      "required": ["os", "wicked_testing_version"],
      "properties": {
        "os":   { "type": "string" },
        "node": { "type": "string" },
        "cli":  { "type": "string" },
        "wicked_testing_version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+" }
      },
      "additionalProperties": true
    },
    "artifacts": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["name", "kind", "path", "bytes", "sha256", "captured_at"],
        "properties": {
          "name":  { "type": "string", "minLength": 1 },
          "kind": {
            "type": "string",
            "enum": [
              "screenshot",
              "video",
              "http-response",
              "http-request",
              "log",
              "stack-trace",
              "metric",
              "trace",
              "coverage",
              "diff",
              "misc"
            ]
          },
          "path":   { "type": "string", "minLength": 1, "description": "Relative to manifest.json." },
          "bytes":  { "type": "integer", "minimum": 0 },
          "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
          "captured_at": { "type": "string", "format": "date-time" }
        },
        "additionalProperties": false
      }
    },
    "assertions": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "description", "passed"],
        "properties": {
          "id":          { "type": "string", "minLength": 1 },
          "description": { "type": "string", "minLength": 1 },
          "passed":      { "type": "boolean" },
          "expected":    { "type": "string" },
          "actual":      { "type": "string" }
        },
        "additionalProperties": false
      }
    }
  },
  "additionalProperties": false
}
