{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://abicheck.github.io/abicheck/reference/schemas/v1/compare_report.schema.json",
  "title": "abicheck compare report",
  "description": "Schema for the JSON document produced by `abicheck compare --format json` (full report mode). See docs/user-guide/output-formats.md for stability guarantees. The schema is versioned via report_schema_version; additive changes (new optional keys, new enum members) bump the MINOR component, breaking changes bump the MAJOR component.",
  "type": "object",
  "required": [
    "report_schema_version",
    "verdict"
  ],
  "additionalProperties": true,
  "allOf": [
    {
      "description": "A real compatibility comparison (the five Verdict enum values) carries the full compare-report shape.",
      "if": {
        "properties": {
          "verdict": {
            "enum": [
              "NO_CHANGE",
              "COMPATIBLE",
              "COMPATIBLE_WITH_RISK",
              "API_BREAK",
              "BREAKING"
            ]
          }
        },
        "required": [
          "verdict"
        ]
      },
      "then": {
        "required": [
          "library",
          "old_version",
          "new_version",
          "old_file",
          "new_file",
          "summary",
          "policy",
          "changes",
          "suppression",
          "detectors",
          "confidence",
          "evidence_tier",
          "evidence_tiers"
        ]
      }
    },
    {
      "description": "Schema 2.17 (ADR-050 D2). A null verdict (the comparability gate rejected the pair) must always carry its reason -- the property being defined does not by itself make it required.",
      "if": {
        "properties": {
          "verdict": {
            "type": "null"
          }
        },
        "required": [
          "verdict"
        ]
      },
      "then": {
        "required": [
          "reason"
        ]
      }
    }
  ],
  "properties": {
    "report_schema_version": {
      "type": "string",
      "description": "SemVer-style version of this report schema (e.g. \"1.0\"). Consumers should accept any version with the same MAJOR component.",
      "pattern": "^[0-9]+\\.[0-9]+$"
    },
    "library": {
      "type": "string"
    },
    "old_version": {
      "type": "string"
    },
    "new_version": {
      "type": "string"
    },
    "verdict": {
      "type": [
        "string",
        "null"
      ],
      "description": "The five real Verdict enum values for a computed compatibility comparison, plus two sentinel values for a report that never got that far: \"ERROR\" (an operational/infrastructure failure -- e.g. a library that failed to dump/extract/compare in the per-library release fan-out, or a resolve-baseline failure in actions/check-target; pre-dates check-target, see aggregate.py's _OPERATIONAL_ERROR_VERDICT) and \"NO_BASELINE\" (actions/check-target's bootstrap \"no baseline published yet\" advisory pass, ADR-047 \u00a76 -- never a compatibility verdict). Only the five real values require the full compare-report shape below (see allOf); ERROR/NO_BASELINE reports carry whatever operational_errors/check_evidence_coverage context is available instead. Schema 2.17 (ADR-050 D2): `null` is a third class -- the comparability precondition gate rejected the pair before any diff ran (old/new were not extracted under a comparable contract); see the sibling `reason` property, always present when `verdict` is `null`.",
      "enum": [
        "NO_CHANGE",
        "COMPATIBLE",
        "COMPATIBLE_WITH_RISK",
        "API_BREAK",
        "BREAKING",
        "ERROR",
        "NO_BASELINE",
        null
      ]
    },
    "reason": {
      "type": "object",
      "description": "Schema 2.17 (ADR-050 D2). Present only when verdict is null: why old/new could not be compared. `kind` distinguishes which ExtractionContract fingerprint mismatched.",
      "required": [
        "kind",
        "message"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "profile_mismatch",
            "scope_mismatch"
          ]
        },
        "message": {
          "type": "string",
          "description": "Human-readable explanation (the raised exception's message)."
        }
      }
    },
    "contract_coverage": {
      "type": [
        "string",
        "null"
      ],
      "description": "Schema 2.17 (ADR-050 D2). Set to \"partial\" when exactly one side of an otherwise-successful comparison carried a given ExtractionContract fingerprint (profile or scope) and the other didn't -- e.g. a freshly-produced snapshot compared against a pre-ADR-050 stored baseline. Report-level metadata, never a Change/ChangeKind finding, so it cannot be reached by --severity-* promotion. Omitted (or null) when both sides had equal fingerprint coverage.",
      "enum": [
        "partial",
        null
      ]
    },
    "assurance": {
      "type": [
        "string",
        "null"
      ],
      "description": "Schema 2.17 (ADR-050 D2). Set to \"none\" when this report was produced via --diagnostic-comparison after a genuine contract mismatch -- the diff ran anyway, but every finding shares the same reduced trust the mismatch implies. Omitted (or null) for an ordinary comparable diff.",
      "enum": [
        "none",
        null
      ]
    },
    "full_verdict": {
      "type": "string",
      "description": "Schema 2.9. The full-library verdict, present alongside verdict only when a --used-by/--required-symbol(s) scoped compare's gating verdict differs from the unscoped library-wide result (verdict becomes the scoped, gate-relevant one; full_verdict preserves the original).",
      "enum": [
        "NO_CHANGE",
        "COMPATIBLE",
        "COMPATIBLE_WITH_RISK",
        "API_BREAK",
        "BREAKING"
      ]
    },
    "check_id": {
      "type": "string",
      "description": "Schema 2.12 (ADR-047 report-identity envelope, G30 P0.3). This check's full identity: \"target@profile#baseline_channel@requested_depth\". Nothing in abicheck's CLI/service layer sets this yet -- reserved for the GitHub Actions integration-model primitives planned in G30 P1 (resolve-baseline, check-target). Each of the four components is constrained to ADR-047 \u00a77's safe identifier charset (no further '@'/'#' inside a component) so the delimiter-joined form stays unambiguous.",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*@[A-Za-z0-9][A-Za-z0-9._-]*#[A-Za-z0-9][A-Za-z0-9._-]*@(binary|headers|build|source)$"
    },
    "profile_id": {
      "type": "string",
      "description": "Schema 2.12 (ADR-047 \u00a77). The build/environment profile identifier this check ran under, e.g. \"linux-x86_64-gcc13-release\". Reserved for G30 P1; not yet populated."
    },
    "requested_depth": {
      "type": "string",
      "description": "Schema 2.12 (ADR-047 \u00a77). The evidence depth the caller asked for. Reserved for G30 P1; not yet populated.",
      "enum": [
        "binary",
        "headers",
        "build",
        "source"
      ]
    },
    "effective_depth": {
      "type": "string",
      "description": "Schema 2.12 (ADR-047 \u00a77). The evidence depth actually achieved -- may be shallower than requested_depth when the requested evidence wasn't available. Reserved for G30 P1; not yet populated.",
      "enum": [
        "binary",
        "headers",
        "build",
        "source"
      ]
    },
    "baseline_channel": {
      "type": "string",
      "description": "Schema 2.12 (ADR-047 \u00a77). Which baseline channel (e.g. \"accepted-main\", a release tag) old_file/old_version was resolved from. Reserved for G30 P1; not yet populated."
    },
    "compatibility_verdict": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). Mirrors verdict's exact casing/enum -- a separate field name so aggregate.py's existing verdict/severity parsing stays untouched while richer consumers (PR comment, SARIF, humans) read this one. Populated by actions/check-target; omitted when the underlying report has no verdict in Verdict's five-value enum (e.g. an operational-failure or bootstrap report, which stay verdict: \"ERROR\"/\"NO_BASELINE\" instead).",
      "enum": [
        "NO_CHANGE",
        "COMPATIBLE",
        "COMPATIBLE_WITH_RISK",
        "API_BREAK",
        "BREAKING"
      ]
    },
    "policy_gate_decision": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). \"pass\" or \"fail\" -- this check's own real gate decision, computed from the report's real severity/exit_code before any gate-mode: advisory neutralization of the legacy severity block. New vocabulary, not a Verdict mirror.",
      "enum": [
        "pass",
        "fail"
      ]
    },
    "check_evidence_coverage": {
      "type": "object",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). Not layer_coverage (a different, existing per-L0-L5-layer array) -- this is check-target's own state/reasons summary of whether requested_depth was actually achieved.",
      "additionalProperties": true,
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "complete",
            "degraded",
            "bootstrap",
            "unknown"
          ]
        },
        "reasons": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "operational_errors": {
      "type": "array",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). Non-empty exactly when this check hit an infrastructure/config problem (e.g. a resolve-baseline failure) rather than -- or in addition to -- a compatibility finding. Empty means clean.",
      "items": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "kind",
          "message"
        ],
        "properties": {
          "kind": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      }
    },
    "publication": {
      "type": "object",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). Whether/where this report was actually published -- distinct from whether it was computed, so a downstream consumer can tell \"no publication happened\" apart from \"no report was produced at all.\"",
      "additionalProperties": true,
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "published",
            "skipped",
            "failed"
          ]
        },
        "channels": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "baseline_bootstrap": {
      "type": "boolean",
      "description": "Schema 2.13 (ADR-047 \u00a76/\u00a77, G30 P1.3). True only on a bootstrap \"no baseline published yet\" advisory pass (required: false with no baseline set yet) -- the explicit report field ADR-047 \u00a76 requires for that case."
    },
    "project": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). The project identifier (e.g. \"owner/repo\") this check ran for."
    },
    "head_sha": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). The candidate commit SHA this check analyzed."
    },
    "base_ref": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). The base ref this check's candidate was compared against, when known (e.g. a PR's target branch)."
    },
    "tool_version": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). The abicheck version that produced this report."
    },
    "action_version": {
      "type": "string",
      "description": "Schema 2.13 (ADR-047 \u00a77, G30 P1.3). The abicheck/abicheck Action ref (e.g. \"abicheck/abicheck@v1\") that ran this check."
    },
    "old_file": {
      "$ref": "#/$defs/fileMetadata"
    },
    "new_file": {
      "$ref": "#/$defs/fileMetadata"
    },
    "summary": {
      "$ref": "#/$defs/summary"
    },
    "full_summary": {
      "description": "Schema 2.9. The full-library summary counts, present alongside summary only for a scoped (--used-by/--required-symbol(s)) compare: summary is recomputed from the complete (post-scoping) changes array so it never contradicts it, and full_summary preserves the original pre-scoping counts (audit finding: a scoped run gated only by a scoped-only synthetic finding could report a non-zero verdict next to summary.total_changes: 0).",
      "$ref": "#/$defs/summary"
    },
    "policy": {
      "type": "string"
    },
    "show_only_filter": {
      "type": "string"
    },
    "filtered_summary": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "breaking": {
          "type": "integer",
          "minimum": 0
        },
        "source_breaks": {
          "type": "integer",
          "minimum": 0
        },
        "risk_changes": {
          "type": "integer",
          "minimum": 0
        },
        "total_changes": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "severity": {
      "$ref": "#/$defs/severity"
    },
    "full_severity": {
      "description": "Schema 2.9. The full-library severity gate result, present alongside severity only for a scoped (--used-by/--required-symbol(s)) compare whose gating result was recomputed for the scoped changes array -- mirrors the verdict/full_verdict split.",
      "$ref": "#/$defs/severity"
    },
    "changes": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/change"
      }
    },
    "redundant_count": {
      "type": "integer",
      "minimum": 0
    },
    "root_causes": {
      "type": "array",
      "description": "G29 Phase 3 (ADR-052 slices 3-4, schema 2.16): present only under `--report-mode root-cause`. Groups `changes` (also present, flat, for backward compatibility) by Change.caused_by_type (falling back to the change's own symbol for an ungrouped, singleton finding); the same grouping also renders for --format markdown (the default rendered text output; slice 4) -- the full RootCauseCorrelator (G29 Phase 6) will additionally correlate consumer-overlay findings that don't share a caused_by_type today.",
      "items": {
        "type": "object",
        "required": [
          "root_cause_id",
          "root",
          "finding_count",
          "findings"
        ],
        "additionalProperties": true,
        "properties": {
          "root_cause_id": {
            "type": "string",
            "description": "Stable hash of the grouping key -- not the eventual RootCauseCorrelator's own identifier scheme."
          },
          "root": {
            "type": "string"
          },
          "finding_count": {
            "type": "integer",
            "minimum": 1
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/$defs/change"
            }
          }
        }
      }
    },
    "root_cause_count": {
      "type": "integer",
      "minimum": 0,
      "description": "G29 Phase 3 (ADR-052 slice 3, schema 2.16): present only under `--report-mode root-cause`. Length of `root_causes`."
    },
    "suppression": {
      "type": "object",
      "required": [
        "file_provided",
        "suppressed_count",
        "suppressed_changes"
      ],
      "additionalProperties": true,
      "properties": {
        "file_provided": {
          "type": "boolean"
        },
        "suppressed_count": {
          "type": "integer",
          "minimum": 0
        },
        "suppressed_changes": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "kind",
              "symbol",
              "description"
            ],
            "additionalProperties": true,
            "properties": {
              "kind": {
                "type": "string"
              },
              "symbol": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "reachability_state": {
                "type": "string",
                "enum": [
                  "reachable",
                  "unreachable",
                  "unknown"
                ],
                "description": "G29 Phase 3 slice 1 (ADR-052, schema 2.15): always present. This is the one call site that passes suppressed=True to assess_change, so decision.state below is always \"suppressed\"."
              },
              "impact_assessment": {
                "$ref": "#/$defs/change/properties/impact_assessment"
              }
            }
          }
        }
      }
    },
    "suppression_audit": {
      "type": "object",
      "description": "Schema 2.24: present only when the caller opts into `compare --audit-suppressions` (requires --suppress). An audit of the suppression rule file against this run's findings -- stale rules (matched nothing), high-risk matches (matched a BREAKING change), and expired/near-expiry rules. Advisory only; never affects verdict, severity, or any exit code.",
      "required": [
        "total_rules",
        "stale_rules",
        "high_risk_matches",
        "expired_rules",
        "near_expiry_rules"
      ],
      "additionalProperties": true,
      "properties": {
        "total_rules": {
          "type": "integer",
          "minimum": 0
        },
        "stale_rules": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Identifier (label, reason, or a matching selector like \"symbol=foo\") of each rule that matched zero changes this run."
        },
        "high_risk_matches": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "rule",
              "kind",
              "symbol"
            ],
            "additionalProperties": true,
            "properties": {
              "rule": {
                "type": "string"
              },
              "kind": {
                "type": "string"
              },
              "symbol": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "description": "Each suppression rule that matched a BREAKING-kind change this run, with the change it matched."
        },
        "expired_rules": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "near_expiry_rules": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "contract_context": {
      "type": "object",
      "description": "ADR-049 Phase 4 (schema 2.25): present only when the caller opts into `compare --contract-evaluation`. The three sibling blocks ADR-049 plan Section 5.1 persists together, so a decision can be replayed (from the receipt alone) or re-evaluated under a different contract mode (from the observations alone) without re-reading any binary or header. Advisory only; never affects verdict, severity, or any exit code.",
      "required": [
        "contract_evidence",
        "evaluation_context",
        "decision_receipt"
      ],
      "additionalProperties": true,
      "properties": {
        "contract_evidence": {
          "type": "object",
          "description": "Observed, policy-independent provider evidence: one entry per (provider, side) with its own EvidenceSearchRecord (status/completeness/identity coverage/the spellings it found ambiguous/requested-vs-searched scope/input digest), the declarations it rooted, any manifests it read, and the raw record/enum/typedef/declaration type graph. Independent of which contract mode was selected -- that is what makes it valid input to a later re-evaluation under a different one.",
          "required": [
            "providers",
            "schema_version",
            "identity_algorithm_version"
          ],
          "additionalProperties": true,
          "properties": {
            "schema_version": {
              "type": "integer",
              "minimum": 1
            },
            "identity_algorithm_version": {
              "type": "integer",
              "minimum": 1
            },
            "providers": {
              "type": "array",
              "items": {
                "type": "object"
              }
            }
          }
        },
        "evaluation_context": {
          "type": "object",
          "description": "The resolved effective configuration that produced the decisions (contract/evidence/surface/assurance/policy/gate/suppressions), plus field-level provenance and the evaluator/identity-algorithm versions active at decision time.",
          "required": [
            "resolved_config",
            "schema_version",
            "evaluator_version",
            "identity_algorithm_version"
          ],
          "additionalProperties": true,
          "properties": {
            "schema_version": {
              "type": "integer",
              "minimum": 1
            },
            "evaluator_version": {
              "type": "integer",
              "minimum": 1
            },
            "identity_algorithm_version": {
              "type": "integer",
              "minimum": 1
            },
            "resolved_config": {
              "type": "object"
            },
            "field_provenance": {
              "type": "object"
            }
          }
        },
        "decision_receipt": {
          "type": "object",
          "description": "The mode/root-dependent result computed *from* the evidence block: the evaluated contract roots, the type closure walked from them, and each finding's recorded relevance (keyed by the same finding_id the report's own changes carry). Not observed evidence -- which is why it is a separate block a re-evaluation ignores. Versioned independently of its siblings: its keys are their own contract, so its shape can change while observations and configuration do not.",
          "required": [
            "schema_version",
            "evaluated_contract_roots",
            "evaluated_type_closure",
            "relevance_by_finding"
          ],
          "additionalProperties": true,
          "properties": {
            "schema_version": {
              "type": "integer",
              "minimum": 1
            },
            "evaluated_contract_roots": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "evaluated_type_closure": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "relevance_by_finding": {
              "type": "object"
            }
          }
        }
      }
    },
    "release_recommendation": {
      "type": "object",
      "description": "Recommended next release: a semantic-version bump and a SONAME action derived from the policy-aware verdict and change set. See docs/user-guide/output-formats.md.",
      "required": [
        "version_bump",
        "soname_action",
        "rationale"
      ],
      "additionalProperties": true,
      "properties": {
        "version_bump": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "major",
            "minor",
            "patch",
            "none",
            null
          ],
          "description": "'null' added in schema 2.20: when 'state' is 'unavailable', abicheck cannot confirm any bump with real evidence, so this is null instead of a plausible-looking literal automation could act on blindly \u2014 the still-likely bump, if any, is now readable from 'possible_impact' below (previously only in 'rationale' prose)."
        },
        "possible_impact": {
          "type": "string",
          "enum": [
            "major",
            "minor",
            "patch",
            "none"
          ],
          "description": "Added in schema 2.22: the bump abicheck would recommend if its evidence were sufficient to confirm one \u2014 always a non-null string, unlike 'version_bump'. Equals 'version_bump' whenever 'state' is 'actionable'; the two diverge only for 'review'/'unavailable', where 'version_bump' is deliberately withheld. Never gate an automated release action on this field alone \u2014 check 'state' first, the same rule as for 'version_bump'."
        },
        "soname_action": {
          "type": "string",
          "enum": [
            "bump_required",
            "bump_performed",
            "bump_missing",
            "no_bump_needed",
            "not_determined"
          ],
          "description": "'not_determined' added in schema 2.14: a BREAKING verdict whose comparison either carried no ELF/PE/Mach-O/DWARF evidence at all, or also raised an evidence-coherence RISK finding (compile_context_conflict / source_surface_dso_mismatch) that leaves the analysis's own build/source context internally inconsistent."
        },
        "rationale": {
          "type": "string"
        },
        "state": {
          "type": "string",
          "enum": [
            "actionable",
            "review",
            "unavailable"
          ],
          "description": "Schema 2.14. How much weight the recommendation itself can bear, independent of what it recommends: 'actionable' (act on it), 'review' (a source/API break was found but no binary evidence confirms a SONAME action either way), or 'unavailable' (a BREAKING verdict with no ELF/PE/Mach-O/DWARF evidence at all, or either a BREAKING or API_BREAK verdict co-occurring with a compile_context_conflict/source_surface_dso_mismatch coherence finding \u2014 abicheck cannot confirm a binary action, or vouch that the analysis's own evidence is self-consistent)."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "state": {
                "const": "unavailable"
              }
            },
            "required": [
              "state"
            ]
          },
          "then": {
            "properties": {
              "version_bump": {
                "const": null
              }
            },
            "required": [
              "version_bump"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "version_bump": {
                "const": null
              }
            },
            "required": [
              "version_bump"
            ]
          },
          "then": {
            "properties": {
              "state": {
                "const": "unavailable"
              }
            },
            "required": [
              "state"
            ]
          }
        }
      ]
    },
    "layer_coverage": {
      "type": "array",
      "description": "Optional source/build evidence coverage (ADR-028 D7). One row per layer (L0 binary, L1 debug, L2 header AST, L3 build context, L4 source ABI, L5 source graph), present only when an BuildSourcePack was supplied to compare. Lets consumers distinguish artifact-proven from build-context-only findings.",
      "items": {
        "type": "object",
        "required": [
          "layer",
          "status"
        ],
        "additionalProperties": true,
        "properties": {
          "layer": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "present",
              "partial",
              "not_collected"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "high",
              "reduced",
              "unknown"
            ]
          },
          "detail": {
            "type": "string"
          }
        }
      }
    },
    "evidence_metrics": {
      "type": "object",
      "description": "Optional evidence-collection timing and observability metrics (ADR-033 D6/D9). Present only when build-info/source facts were involved in the compare. Keys follow the D9 metric names so CI can tune evidence-mode selection.",
      "additionalProperties": true,
      "properties": {
        "extractor.duration_seconds": {
          "type": "number",
          "minimum": 0
        },
        "coverage.build_context.present": {
          "type": "boolean"
        },
        "coverage.source_abi.mode": {
          "type": "string"
        },
        "coverage.graph.mode": {
          "type": "string"
        },
        "findings.artifact_backed.count": {
          "type": "integer",
          "minimum": 0
        },
        "findings.source_only.count": {
          "type": "integer",
          "minimum": 0
        },
        "findings.build_context_drift.count": {
          "type": "integer",
          "minimum": 0
        },
        "findings.evidence_required_missing.count": {
          "type": "integer",
          "minimum": 0
        },
        "findings.demoted_by_surface.count": {
          "type": "integer",
          "minimum": 0
        },
        "findings.suppressed_with_reason.count": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "detectors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "changes_count",
          "enabled",
          "coverage_gap"
        ],
        "additionalProperties": true,
        "properties": {
          "name": {
            "type": "string"
          },
          "changes_count": {
            "type": "integer",
            "minimum": 0
          },
          "enabled": {
            "type": "boolean"
          },
          "coverage_gap": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    },
    "confidence": {
      "type": "string",
      "enum": [
        "high",
        "medium",
        "low"
      ]
    },
    "evidence_tier": {
      "type": "string",
      "description": "Canonical, ordered analysis depth. Key trust decisions off this scalar.",
      "enum": [
        "elf_only",
        "dwarf_aware",
        "header_aware"
      ]
    },
    "evidence_tiers": {
      "type": "array",
      "description": "Raw data sources that were available. Retained for backward compatibility; prefer evidence_tier.",
      "items": {
        "type": "string",
        "enum": [
          "elf",
          "dwarf",
          "dwarf_advanced",
          "header",
          "pe",
          "macho"
        ]
      }
    },
    "coverage_warnings": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "policy_overrides": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    },
    "policy_file": {
      "type": "string"
    },
    "scope": {
      "type": "object",
      "description": "Public-header scoping outcome (ADR-024, issue #235). Only present when --scope-public-headers was requested. When 'resolved' is false the public surface could not be determined and analysis fell back to the full export table, so 'manual_review_required' is true and compatibility is UNCONFIRMED.",
      "required": [
        "public_headers_applied",
        "resolved",
        "fell_back",
        "manual_review_required"
      ],
      "additionalProperties": true,
      "properties": {
        "public_headers_applied": {
          "type": "boolean"
        },
        "resolved": {
          "type": "boolean"
        },
        "fell_back": {
          "type": "boolean"
        },
        "manual_review_required": {
          "type": "boolean"
        },
        "public_additions": {
          "type": "integer",
          "minimum": 0
        },
        "filtered_internal_count": {
          "type": "integer",
          "minimum": 0
        },
        "filtered_internal_changes": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "kind",
              "symbol",
              "description"
            ],
            "additionalProperties": true,
            "properties": {
              "kind": {
                "type": "string"
              },
              "symbol": {
                "type": "string"
              },
              "description": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "contract_coverage_failures": {
      "type": "array",
      "description": "ADR-049 Phase 5 (schema 2.26): present only when the caller opts into `compare --contract-evaluation`. The sibling contract-coverage ledger of ADR-049 plan Section 6.1 \u2014 one entry per provider/domain coverage failure for the contract domain this run selected. Deliberately not change rows: a coverage failure carries no ChangeKind and never enters `changes`, which is what makes it unsuppressible ('ordinary change suppressions ... cannot suppress a provider/domain coverage failure', Section 6.2). Derived from `contract_context`, so the same provider record is a failure under one domain and advisory under another. An empty array is a real value meaning the selected domain closed. Never affects verdict or severity. It does affect the process exit status: ADR-049 Phase 7 applies the sibling `contract_coverage_exit_contribution` as an orthogonal floor (folded with max, so it never lowers a 2/4).",
      "items": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "The evidence provider that failed, e.g. 'public_header' or 'export_table'."
          },
          "side": {
            "type": "string",
            "description": "'old' or 'new' \u2014 coverage is observed per side."
          },
          "record_id": {
            "type": "string",
            "description": "The contract_context.contract_evidence record this was derived from."
          },
          "reason": {
            "type": "string",
            "description": "Why it counts as a failure: 'provider_unavailable'/'provider_failed'/'provider_unsupported'/'provider_stale' (the search did not deliver), 'search_incomplete'/'search_not_started' (it delivered but did not cover everything), or 'identity_coverage_incomplete' (complete search, ambiguous identities)."
          },
          "status": {
            "type": "string",
            "description": "The provider record's own status value."
          },
          "completeness": {
            "type": "string",
            "description": "The provider record's own completeness value."
          },
          "mode": {
            "type": "string",
            "description": "The contract domain this failure was derived for ('public'/'exports'/'all'). An entry that did not name its domain would be unattributable."
          },
          "suppressible": {
            "type": "boolean",
            "description": "Always false. Stated per entry so a consumer reading one in isolation knows it cannot be waived by a --suppress rule."
          }
        },
        "required": [
          "provider",
          "side",
          "record_id",
          "reason",
          "status",
          "completeness",
          "mode",
          "suppressible"
        ]
      }
    },
    "contract_coverage_exit_contribution": {
      "type": "integer",
      "enum": [
        0,
        1
      ],
      "description": "ADR-049 (schema 2.26): what `contract_coverage_failures` contributes to the exit code \u2014 1 when non-empty, 0 otherwise, and 0 when `contract.unresolved=warn` accepted the failures. **Applied** since ADR-049 Phase 7: the process exit status is floored to this value (folded with max, so it never lowers a 2/4 from the compatibility axis, and never rewrites a finding's decision or gate contribution). This is the number that actually gated the run, derived by the same function the exit path uses. Present only alongside `contract_coverage_failures`."
    }
  },
  "$defs": {
    "summary": {
      "type": "object",
      "required": [
        "breaking",
        "source_breaks",
        "risk_changes",
        "compatible_additions",
        "total_changes",
        "binary_compatibility_pct",
        "affected_pct"
      ],
      "additionalProperties": true,
      "properties": {
        "breaking": {
          "type": "integer",
          "minimum": 0
        },
        "source_breaks": {
          "type": "integer",
          "minimum": 0
        },
        "risk_changes": {
          "type": "integer",
          "minimum": 0
        },
        "compatible_additions": {
          "type": "integer",
          "minimum": 0
        },
        "total_changes": {
          "type": "integer",
          "minimum": 0
        },
        "binary_compatibility_pct": {
          "type": "number",
          "minimum": 0,
          "maximum": 100
        },
        "affected_pct": {
          "type": "number",
          "minimum": 0,
          "maximum": 100
        }
      }
    },
    "severity": {
      "type": "object",
      "description": "Present only when --severity-* configuration was active. config/categories mirror the resolved SeverityConfig and per-category counts; blocking/blocking_categories (schema 2.3) are a typed gate summary mirroring SARIF's severityGate block.",
      "required": [
        "config",
        "categories",
        "exit_code",
        "blocking",
        "blocking_categories"
      ],
      "additionalProperties": true,
      "properties": {
        "config": {
          "type": "object"
        },
        "categories": {
          "type": "object"
        },
        "exit_code": {
          "type": "integer",
          "minimum": 0
        },
        "blocking": {
          "type": "boolean"
        },
        "blocking_categories": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "abi_breaking",
              "potential_breaking",
              "quality_issues",
              "addition"
            ]
          }
        }
      }
    },
    "fileMetadata": {
      "type": [
        "object",
        "null"
      ],
      "description": "File metadata for the compared library, or null when unavailable.",
      "required": [
        "path",
        "sha256",
        "size_bytes"
      ],
      "additionalProperties": true,
      "properties": {
        "path": {
          "type": "string"
        },
        "sha256": {
          "type": "string"
        },
        "size_bytes": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "change": {
      "type": "object",
      "required": [
        "kind",
        "symbol",
        "description",
        "old_value",
        "new_value",
        "severity"
      ],
      "additionalProperties": true,
      "properties": {
        "kind": {
          "type": "string"
        },
        "symbol": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "old_value": {
          "type": [
            "string",
            "null"
          ]
        },
        "new_value": {
          "type": [
            "string",
            "null"
          ]
        },
        "severity": {
          "type": "string",
          "enum": [
            "breaking",
            "api_break",
            "risk",
            "compatible",
            "unknown"
          ]
        },
        "evidence_status": {
          "type": "string",
          "description": "The epistemic status of this finding, deliberately policy-independent (unlike severity, it is NOT derived from the active --policy): artifact_proven = the finding's kind is intrinsically a BREAKING_KINDS member AND this comparison's evidence_tiers positively confirm a real binary (ELF/PE/Mach-O/DWARF) was examined; unattributed = the finding's kind is intrinsically a BREAKING_KINDS member but this comparison's evidence_tiers show only 'header' -- no real binary was ever examined, so 'artifact_proven' would overclaim (e.g. a Python-API caller comparing hand-built/loaded snapshots); source_contract = intrinsically API_BREAK_KINDS, a source-level break needing a recompile/policy decision; contextual_risk = intrinsically RISK_KINDS, build/source/deployment context suggests risk without proving a break (may still read 'severity':'breaking' under a policy like plugin_abi that escalates risk kinds for gating); consumer_proven = set explicitly for appcompat/plugin-check findings where runtime evidence demonstrated a specific consumer depends on this change; not_checkable = this finding IS the missing-evidence signal (evidence_required_missing), not a break. Absent for COMPATIBLE/NO_CHANGE findings, which need no epistemic status.",
          "enum": [
            "artifact_proven",
            "unattributed",
            "source_contract",
            "contextual_risk",
            "consumer_proven",
            "not_checkable"
          ]
        },
        "impact": {
          "type": "string"
        },
        "source_location": {
          "type": "string"
        },
        "affected_symbols": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "caused_by_type": {
          "type": "string"
        },
        "caused_count": {
          "type": "integer",
          "minimum": 0
        },
        "operation": {
          "type": "string",
          "description": "Structured classification of the finding derived from its kind's own suffix (the same classification --show-only's added/removed/changed tokens use): 'added'/'removed' kinds map directly, everything else (parameter/type/layout changes, renames, etc.) is 'modified'.",
          "enum": [
            "added",
            "removed",
            "modified"
          ]
        },
        "finding_id": {
          "type": "string",
          "description": "Stable, deterministic fingerprint (sha256 of kind/symbol/old_value/new_value/source_location/description, truncated to 16 hex chars) \u2014 lets a consumer correlate the same finding across two report runs without relying on array order. description disambiguates otherwise-identical findings on the same symbol (e.g. the same pointer-depth change on two different parameters). Excludes policy-derived fields (severity, evidence_status) so the same underlying finding hashes identically under any --policy."
        },
        "recommended_action": {
          "type": "string",
          "description": "Structured, machine-readable next step, derived from the same effective verdict/category resolution 'severity'/'operation' already use.",
          "enum": [
            "recompile_and_relink_required",
            "recompile_required",
            "verify_deployment_compatibility",
            "review_recommended",
            "no_action_required"
          ]
        },
        "correlated_change_kind": {
          "type": "string",
          "description": "ADR-041 P0 roadmap item 2: for a public_api_internal_dependency_added finding correlated with the same public entry's own body/type-hash change this version, the correlated finding's ChangeKind value (e.g. 'inline_body_changed') \u2014 the structured sibling to the correlation already carried in the description prose."
        },
        "reviewer_action": {
          "type": "string",
          "description": "Finer-grained reviewer guidance, present only when recommended_action is 'no_action_required' (a COMPATIBLE addition). recommended_action alone only answers whether the old binary consumer needs to do anything (no); this answers whether a human reviewing the change has anything to check.",
          "enum": [
            "review_exhaustive_switches",
            "document_stable_replacement",
            "confirm_public_api_intent"
          ]
        },
        "public_reachable": {
          "type": "boolean",
          "description": "ADR-044: true when this finding's subject was tagged public-reachable by the suppression reachability walk (layout, direct public-header origin, or L5 call-graph evidence) \u2014 the same signal a broad namespace/source_location suppression rule's reachability gate consults. Omitted (not false) when the tag was never computed."
        },
        "reachability_kind": {
          "type": "string",
          "description": "ADR-044: how public_reachable was established. 'direct_public_symbol' \u2014 the subject is itself public (ScopeOrigin.PUBLIC_HEADER, or a late detector's own Visibility.PUBLIC-filtered construction). 'value_embedding'/'pointer_or_signature' \u2014 layout/type-graph reachability from a public entry (by-value field/base vs. pointer/reference/template-argument only). 'symbol_availability' \u2014 reachable only via an L5 DECL_CALLS_DECL/DECL_REFERENCES_DECL call/reference edge (ADR-044 P1 items 1-3), with no layout evidence at all. 'consumer_proven' \u2014 set on the consumer_required_symbol_removed/consumer_runtime_load_failed overlays compare --used-by synthesizes (ADR-044 P2): always consumer-verified real (a real consumer binary's own requirement, or an actual dynamic-linker failure) rather than established by the public-surface walk the other values describe. 'public_source_abi_surface' \u2014 the finding's kind (e.g. public_typedef_removed) is public by construction, an L4/L5 source-graph finding never established by the public-surface walk (impact-analysis-layer P0, schema 2.10). Present only when public_reachable is true.",
          "enum": [
            "direct_public_symbol",
            "value_embedding",
            "pointer_or_signature",
            "symbol_availability",
            "consumer_proven",
            "public_source_abi_surface"
          ]
        },
        "reachability_proof_path": {
          "type": "string",
          "description": "ADR-044: human-readable rendering of the shortest matched reachability path (e.g. \"fn:pub \u2192 base:detail::Base\", or, for a call-graph path, \"pubFn --[DECL_CALLS_DECL]--> ns::detail::helper\"). Present only when public_reachable is true and a path was found (never set for the 'direct_public_symbol' kind, which has no path to render)."
        },
        "affected_public_roots": {
          "type": "array",
          "description": "G31 Phase B3 (ADR-048, schema 2.11): labels of the public entry node(s) an embedded L5 graph walk proved reach this finding's internal target. Enrichment on an existing finding (never a standalone new finding); present only when the graph has relevant reachability data for it.",
          "items": {
            "type": "string"
          }
        },
        "impact_proof_path": {
          "type": "array",
          "description": "G31 Phase B3 (ADR-048, schema 2.11): the structured node/edge-list counterpart of reachability_proof_path's prose rendering \u2014 the shortest matched path from a public root to this finding's subject, as an ordered list of {node/edge reference} objects. Present only when affected_public_roots is present.",
          "items": {
            "type": "object"
          }
        },
        "impact_is_direct": {
          "type": "boolean",
          "description": "G31 Phase B3 (ADR-048, schema 2.11): true when impact_proof_path is a single hop (the public root directly references the finding's subject), false when transitive. Present only when affected_public_roots is present."
        },
        "reachability_state": {
          "type": "string",
          "description": "G29 Phase 3 slice 1 (ADR-052, schema 2.15): the tri-state refinement of public_reachable (Change.reachability_state, in memory since PR #607 but never serialized before this). 'reachable' when public_reachable is true. 'unreachable' when the walk positively found this change not part of the effective public ABI. 'unknown' when no walk reached a verdict, or the only available evidence (the optional L5 call/type graph) is itself flagged narrowed/degraded \u2014 distinct from 'unreachable', since 'unknown' is not proof of absence. Always present (the enum's own default is 'unknown', an honest answer, not an absent key).",
          "enum": [
            "reachable",
            "unreachable",
            "unknown"
          ]
        },
        "impact_assessment": {
          "type": "object",
          "description": "G29 Phase 3 slice 1 (ADR-052, schema 2.15): a unified read view over this finding's reachability/impact fields above (reachability_state, public_reachable, reachability_kind, the proof path, decision state, evidence_category, correlated_change_kind) -- a single object a consumer can query instead of stitching several independently-nullable keys together. Intentionally duplicates data already published at the top level (see the fields above); present only when it carries information beyond the all-defaults case (a proof path exists, reachability_state is not 'unknown', public_reachable is true, confidence is not 'high', the decision state is not 'kept', a modulation/verdict-override fired, correlated_change_kind/evidence_category is set, or root_cause_id is set).",
          "properties": {
            "reachability_state": {
              "type": "string",
              "enum": [
                "reachable",
                "unreachable",
                "unknown"
              ]
            },
            "public_reachable": {
              "type": "boolean"
            },
            "reachability_kind": {
              "type": "string"
            },
            "confidence": {
              "type": "string",
              "enum": [
                "high",
                "medium",
                "low"
              ]
            },
            "evidence_category": {
              "type": "string"
            },
            "correlated_change_kind": {
              "type": "string"
            },
            "root_cause_id": {
              "type": "string",
              "description": "G29 Phase 3 follow-up (ADR-052, schema 2.19): a stable hash of this finding's root-cause grouping key -- the same computation `--report-mode root-cause`'s own `root_causes[].root_cause_id` uses, surfaced per-finding regardless of report_mode. Present only when this finding has a real correlation signal (a `caused_by_type`, or its own symbol is referenced by another finding's `caused_by_type`); absent for an uncorrelated singleton finding."
            },
            "root_cause_display": {
              "type": "string",
              "description": "G29 Phase 3 follow-up (ADR-052, schema 2.19): the human-readable root paired with root_cause_id. Present iff root_cause_id is."
            },
            "impact_group_id": {
              "type": "string",
              "description": "G29 Phase 3 follow-up (ADR-052, schema 2.19): currently always identical to root_cause_id -- a placeholder alias until Phase 6's RootCauseCorrelator gives it independent meaning (e.g. bucketing several distinct root causes under one broader consumer-visible event). Present iff root_cause_id is."
            },
            "proof_path": {
              "type": "object",
              "description": "The dataclass counterpart of impact_proof_path/reachability_proof_path above, packaged as one object.",
              "properties": {
                "target": {
                  "type": "string"
                },
                "root": {
                  "type": "string"
                },
                "is_direct": {
                  "type": "boolean"
                },
                "steps": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                },
                "prose": {
                  "type": "string"
                },
                "alternative_paths": {
                  "type": "array",
                  "description": "ADR-046 D6 (schema 2.16): up to 3 runner-up candidate paths not selected as primary, each shaped like this same proof_path object minus alternative_paths/discarded_path_count itself (an alternative never nests its own alternatives). Absent for the common single-candidate case.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "target": {
                        "type": "string"
                      },
                      "root": {
                        "type": "string"
                      },
                      "is_direct": {
                        "type": "boolean"
                      },
                      "steps": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      "prose": {
                        "type": "string"
                      },
                      "occurrence_id": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "target"
                    ]
                  }
                },
                "discarded_path_count": {
                  "type": "integer",
                  "description": "ADR-046 D6 (schema 2.16): count of further candidate paths beyond alternative_paths' cap. 0 (and therefore omitted) when every candidate fit."
                },
                "occurrence_id": {
                  "type": "string",
                  "description": "ADR-052's stable occurrence_id follow-up (schema 2.16, built on ADR-046 D1): a hash over this path's edges' own graph occurrences, independent of description text. Absent whenever no edge on the path carries occurrence-level attrs -- still the common case, since no producer populates them by default."
                }
              },
              "required": [
                "target"
              ]
            },
            "decision": {
              "type": "object",
              "description": "Whether this finding was kept or suppressed, and why. suppression_rule (G29 Phase 3 slice 2, ADR-052 follow-up) is the label (falling back to reason) of the Suppression rule that actually suppressed the change -- present only for a suppressed entry whose matching rule set either field. verdict_override carries Change.effective_verdict's value verbatim when set -- an override can raise a finding's category (e.g. STDLIB_IMPLEMENTATION_CHANGED promoted to BREAKING) as well as lower it, so this is not always a downgrade despite the name of the underlying mechanism.",
              "properties": {
                "state": {
                  "type": "string",
                  "enum": [
                    "kept",
                    "suppressed"
                  ]
                },
                "reason_code": {
                  "type": "string"
                },
                "suppression_rule": {
                  "type": "string"
                },
                "verdict_override": {
                  "type": "string"
                }
              },
              "required": [
                "state"
              ]
            }
          },
          "required": [
            "reachability_state",
            "public_reachable",
            "confidence",
            "decision"
          ]
        },
        "contract_relevance": {
          "type": "string",
          "description": "ADR-049 (schema 2.27, authoritative since Phase 7): present only when the caller opted into compare(..., contract_evaluation=True). Classified BEFORE compatibility policy (D9's pipeline order), and it decides whether policy runs at all: see compatibility_evaluation_status. Whether this finding's subject is confirmed within the evaluated contract's evidence domain (IN_CONTRACT), confirmed outside it (PROVEN_OUT_OF_CONTRACT), not about a contract entity at all (NOT_APPLICABLE, e.g. SONAME/RPATH/DT_NEEDED changes), or the evaluator could not resolve it either way (UNKNOWN_UNRESOLVED -- the only unresolved value this evaluator emits; see contract_reason_code). UNKNOWN_UNPROVEN is a fifth ADR-049 value this evaluator never emits (see contract_evaluation.py's own module docstring), kept in the enum because the closed-world claim it makes is a provider-completeness question, not an evaluator one.",
          "enum": [
            "IN_CONTRACT",
            "UNKNOWN_UNRESOLVED",
            "UNKNOWN_UNPROVEN",
            "PROVEN_OUT_OF_CONTRACT",
            "NOT_APPLICABLE"
          ]
        },
        "contract_reason_code": {
          "type": "string",
          "description": "ADR-049 Phase 3 (schema 2.23): the stable reason code backing contract_relevance -- e.g. 'public_root_membership' (in_contract, confirmed public-root/closure membership), 'required_evidence_incomplete' (unknown_unresolved, an unresolvable or identity-ambiguous surface), 'non_entity_finding' (not_applicable), 'terminal_authoritative_exclusion' (proven_out_of_contract), or 'all_mode_normalized_entity' (in_contract under contract=all, which makes no closed-world evidence claim). Present iff contract_relevance is."
        },
        "contract_assurance": {
          "type": "string",
          "description": "ADR-049 Phase 3 (schema 2.23): how much confidence backs contract_relevance -- 'complete' (a terminal or positively-confirmed determination), 'partial' (a weak surface-exclusion reason with no stronger corroboration), or 'unavailable' (the required surface was unresolvable). Present only alongside contract_relevance, and only when the evaluator itself set a value (this field is None internally only in unreached code paths).",
          "enum": [
            "complete",
            "partial",
            "unavailable"
          ]
        },
        "compatibility_evaluation_status": {
          "type": "string",
          "description": "ADR-049 D1 (schema 2.27): whether compatibility policy scored this finding. EVALUATED for IN_CONTRACT and NOT_APPLICABLE relevance; NOT_EVALUATED for PROVEN_OUT_OF_CONTRACT, UNKNOWN_UNPROVEN and UNKNOWN_UNRESOLVED. A NOT_EVALUATED finding is still a conserved detector fact -- it stays in `changes` and in every ledger -- but it contributes nothing to the verdict or the gate. Present only alongside contract_relevance.",
          "enum": [
            "EVALUATED",
            "NOT_EVALUATED"
          ]
        },
        "compatibility_decision": {
          "type": [
            "string",
            "null"
          ],
          "description": "ADR-049 D1 (schema 2.27): this finding's own compatibility verdict under the effective policy, or null when compatibility_evaluation_status is NOT_EVALUATED. null records that policy never ran -- it is not a sixth verdict and must not be read as COMPATIBLE. Present only alongside contract_relevance.",
          "enum": [
            "NO_CHANGE",
            "COMPATIBLE",
            "COMPATIBLE_WITH_RISK",
            "API_BREAK",
            "BREAKING",
            null
          ]
        },
        "gate_contribution": {
          "type": "integer",
          "description": "ADR-049 D1 (schema 2.27): what this finding contributed to the process exit code, under whichever gate scheme the run resolved (severity-aware category exit code when its category is configured `error`, else the legacy verdict-to-exit mapping). 0 for a NOT_EVALUATED finding, and 0 for every audit-ledger entry (out-of-surface, suppressed, reconciled, redundant), none of which reach a gate. Orthogonal to contract_coverage_exit_contribution, which is a run-level axis rather than a per-finding one. Present only alongside contract_relevance.",
          "minimum": 0
        },
        "contract_evidence_refs": {
          "type": "array",
          "description": "ADR-049 Phase 3 (schema 2.25): the ids of the top-level contract_context.contract_evidence provider records this finding's contract decision rests on (e.g. 'public_header:old', 'export_table:new'). An empty array is a real value, not a missing one: a non-entity finding's relevance follows from its ChangeKind alone and consults no provider. A run-level reference ('run:explicit_consumer_or_required_symbol') appears instead when the decision was made by --used-by/--required-symbol scoping after the comparison returned, which holds no evidence block. Present only alongside contract_relevance.",
          "items": {
            "type": "string"
          }
        }
      }
    }
  }
}
