{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://skill-map.ai/spec/v1/issue.schema.json",
  "title": "Issue",
  "description": "Deterministic finding emitted by a analyzer when evaluating the graph. Not to be confused with `Finding`, which is probabilistic (LLM-produced).",
  "type": "object",
  "required": ["analyzerId", "severity", "nodeIds", "message"],
  "additionalProperties": false,
  "properties": {
    "analyzerId": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
      "description": "Kebab-case identifier of the analyzer that emitted this issue (e.g. `name-collision`, `broken-ref`, `node-stability`)."
    },
    "severity": {
      "type": "string",
      "enum": ["error", "warn", "info"],
      "description": "Severity level. `info` is emitted by `--strict` mode for recommendations."
    },
    "nodeIds": {
      "type": "array",
      "description": "`node.path` values involved in this issue. Most analyzers emit 1 or 2; name-collision may emit N. Field name uses `id` generically to remain stable across future identifier changes.",
      "minItems": 1,
      "items": { "type": "string" }
    },
    "linkIndices": {
      "type": "array",
      "description": "Optional indices into the sibling `links[]` array (in the same scan result) that the issue refers to.",
      "items": { "type": "integer", "minimum": 0 }
    },
    "message": {
      "type": "string",
      "description": "Human-readable finding in the canonical shape: an optional backtick-quoted subject line (the offending token / path / name), then the diagnosis `L<line>: <what>; <why>`. The `L<line>: ` location prefix (`L2: ` / `L2, 5: `) appears only when the finding maps to body line(s); it is omitted for sidecar / frontmatter / abstract checks (and the subject line is omitted when there is no single offending token). The body reads `<what>; <why>` (what was detected, then why it matters), in English. Remediation hints do NOT belong here, they go in `fix.summary`. Built-in analyzers assemble this via the shared `formatFinding` helper. Multi-line via `\\n`."
    },
    "detail": {
      "type": ["string", "null"],
      "description": "Longer explanation, markdown-friendly. Optional."
    },
    "fix": {
      "type": ["object", "null"],
      "description": "Machine-readable fix hint. Stability: experimental, shape may change before v1.",
      "additionalProperties": false,
      "properties": {
        "summary": { "type": "string", "description": "Human-readable remediation hint (e.g. `Rename the file or its frontmatter.name`). This is the home for the actionable advice that used to be appended to `message`." },
        "autofixable": { "type": "boolean" }
      }
    },
    "data": {
      "type": "object",
      "description": "Analyzer-specific structured payload (e.g. the colliding trigger string, the missing target). Free-form.",
      "additionalProperties": true
    }
  }
}
