{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://designsystem.porsche.com/schemas/pds-audit-deprecations-angular/1.0.0.json",
  "title": "Porsche Design System deprecation audit report (angular)",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "audit",
    "project",
    "scope",
    "summary",
    "coverage",
    "findings",
    "manualFollowUps"
  ],
  "properties": {
    "schemaVersion": {
      "const": "1.0.0"
    },
    "audit": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "runId",
        "generatedAt",
        "skillName",
        "auditKind",
        "framework",
        "pdsVersion"
      ],
      "properties": {
        "runId": {
          "type": "string",
          "description": "Filesystem-safe UTC timestamp identifying the run, e.g. 2026-07-23T09-21-27Z."
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "skillName": {
          "const": "pds-audit-deprecations-angular"
        },
        "auditKind": {
          "const": "deprecations",
          "description": "What this report audits, for a consumer reading a run directory of several."
        },
        "framework": {
          "const": "angular"
        },
        "pdsVersion": {
          "type": "string",
          "description": "The installed PDS version the audit was run against."
        }
      }
    },
    "project": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "root",
        "pdsPackages"
      ],
      "properties": {
        "root": {
          "type": "string"
        },
        "pdsPackages": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "name",
              "version"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "scope": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "includedPaths",
        "excludedPaths"
      ],
      "description": "What the run covered, as a list rather than as a claim about the file walk. A package that was never discovered is a package nobody can see was missed, so `completed` is checked against these two arrays.",
      "properties": {
        "includedPaths": {
          "type": "array",
          "description": "Every path the run audited, project-relative: one entry per discovered PDS-dependent package root, narrowed by any user-supplied include paths. `.` when the project is a single package.",
          "items": {
            "type": "string"
          }
        },
        "excludedPaths": {
          "type": "array",
          "description": "Every path deliberately left out, each with why — user-supplied excludes, default exclusions such as tests or build output, and any discovered package the run chose not to audit. A choice belongs here and not in `coverage.limitations`, which is for what the audit could not do.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "path",
              "reason"
            ],
            "properties": {
              "path": {
                "type": "string"
              },
              "reason": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "summary": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "result"
      ],
      "description": "Execution state and nothing else. Counts are deliberately absent: each one would be the length of an array in this same file, and a written number is a number that can disagree with the list beside it.",
      "properties": {
        "result": {
          "enum": [
            "completed",
            "partial",
            "failed"
          ],
          "description": "Execution state only, never project quality: completed = every discovered PDS-dependent package audited and every eligible file in it checked; partial = a discovered package went unaudited or something eligible could not be checked; failed = no meaningful audit was possible."
        }
      }
    },
    "coverage": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "skippedFiles",
        "limitations"
      ],
      "description": "Deliberately thin. Anything here is the audit grading its own work, which no consumer can check, so it carries only what a reader can act on: paths they can open, and gaps stated in words. Without it a `partial` result names no gap at all.",
      "properties": {
        "skippedFiles": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "path",
              "reason"
            ],
            "properties": {
              "path": {
                "type": "string"
              },
              "reason": {
                "type": "string"
              }
            }
          }
        },
        "limitations": {
          "type": "array",
          "description": "Anything that prevented a complete check, disclosed in full. Only what the audit could not do — anything it chose to leave out is a decision and belongs in `scope.excludedPaths` with its reason.",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "findings": {
      "type": "array",
      "description": "Every deprecated usage found, ordered cheapest first: effective effort ascending, then confidence, then occurrence count descending, then ruleId. That order is the recommended action plan.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "ruleId",
          "usageKind",
          "title",
          "confidence",
          "baselineEffort",
          "remediation",
          "evidence",
          "sources"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Run-unique finding id."
          },
          "ruleId": {
            "type": "string",
            "description": "The deprecation index entry id, e.g. prop/p-accordion/heading, copied verbatim. Stable across releases, so findings stay comparable between runs."
          },
          "usageKind": {
            "enum": [
              "component",
              "prop",
              "propValue",
              "event",
              "slot",
              "cssCustomProperty",
              "cssClass",
              "scssVariable",
              "scssMixin",
              "jsExport"
            ],
            "description": "The rule id's first segment."
          },
          "title": {
            "type": "string",
            "description": "One line naming the deprecated API, e.g. \"Deprecated prop heading on p-accordion\"."
          },
          "deprecationMessage": {
            "type": "string",
            "description": "The index entry’s message, carried verbatim when it has one."
          },
          "confidence": {
            "enum": [
              "high",
              "medium"
            ],
            "description": "Derived, not judged: the lowest confidence across this finding’s evidence locations."
          },
          "baselineEffort": {
            "enum": [
              "trivial",
              "small",
              "medium",
              "large"
            ],
            "description": "Derived from usageKind, one level higher when the index documents no replacement."
          },
          "observedEffort": {
            "enum": [
              "trivial",
              "small",
              "medium",
              "large"
            ],
            "description": "Only when this project implements the API in a way that changes the real cost."
          },
          "effortRationale": {
            "type": "string",
            "description": "Required whenever observedEffort is present, and must point at evidence already listed on this finding — otherwise the deviation is unreviewable."
          },
          "remediation": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "instruction"
            ],
            "properties": {
              "replacement": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "from",
                  "to"
                ],
                "description": "The exact edit, in this framework’s spelling. Present only when the index documents an unambiguous replacement, so a fix never has to re-derive it from prose.",
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "instruction": {
                "type": "string",
                "description": "What to do, in words. When no replacement is documented, say so plainly rather than inventing one."
              }
            }
          },
          "evidence": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "path",
                "line",
                "snippet",
                "detection"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Project-relative path."
                },
                "line": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "The usage itself — the attribute, tag, property or alias reference. Never the import, @use, @theme or declaration line that anchored or resolved it: those belong in `anchor`, and counting them as locations makes two runs of the same project disagree on how many there are."
                },
                "snippet": {
                  "type": "string",
                  "description": "The source line, quoted verbatim."
                },
                "detection": {
                  "enum": [
                    "direct",
                    "wrapper-forwarded",
                    "wrapper-transformed",
                    "fallback-search"
                  ],
                  "description": "How this location was reached. Determines its confidence."
                },
                "valueResolution": {
                  "enum": [
                    "literal",
                    "same-file-constant",
                    "imported-constant"
                  ],
                  "description": "How the deprecated value was resolved. Only for usageKind `propValue`."
                },
                "anchor": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "path",
                    "line",
                    "snippet"
                  ],
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Project-relative path."
                    },
                    "line": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "The anchoring line, which is frequently in a different file from the usage."
                    },
                    "snippet": {
                      "type": "string",
                      "description": "The source line, quoted verbatim."
                    }
                  },
                  "description": "The line that proves this usage is PDS — the import, the resolved SCSS @use including one injected by build config, the Tailwind @theme import, or the PDS root at the far end of a wrapper chain. The only truthful home for the vite.config.ts line that anchors a stylesheet naming PDS nowhere. Omitted only when the usage anchors itself, as a --p- custom property does."
                }
              }
            }
          },
          "sources": {
            "type": "array",
            "minItems": 1,
            "description": "Where the fact came from, so a reader or a fix skill can re-ground the finding.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "reference",
                "pdsVersion"
              ],
              "properties": {
                "reference": {
                  "type": "string",
                  "description": "Knowledge-skill reference the fact comes from, as a path relative to that skill’s root — the same form the knowledge skill uses for its own links, so a consumer can resolve it without guessing the base directory."
                },
                "pdsVersion": {
                  "type": "string"
                }
              }
            }
          }
        },
        "dependentRequired": {
          "observedEffort": [
            "effortRationale"
          ]
        }
      }
    },
    "manualFollowUps": {
      "type": "array",
      "description": "Deprecated usage that was detected but could not be resolved. Kept apart from findings because a follow-up has no fix to apply — anything acting on this report leaves these to a human.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "subject",
          "reason",
          "evidence"
        ],
        "properties": {
          "subject": {
            "type": "string",
            "description": "What could not be resolved, e.g. \"p-text size\". Free text, since a follow-up often spans several index entries."
          },
          "ruleId": {
            "type": "string",
            "description": "Only when the follow-up maps to exactly one index entry, and then copied verbatim. Omit it rather than inventing one: an id that is not in the index cannot be looked up or compared across runs."
          },
          "reason": {
            "type": "string",
            "description": "Why it could not become a finding — dynamic value, unanchored match, spread props."
          },
          "evidence": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "path",
                "line",
                "snippet"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Project-relative path."
                },
                "line": {
                  "type": "integer",
                  "minimum": 1
                },
                "snippet": {
                  "type": "string",
                  "description": "The source line, quoted verbatim."
                }
              }
            }
          }
        }
      }
    }
  }
}
