{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://lernapps.net/tooling/schemas/verdict.v1.schema.json",
  "title": "Review verdict of lernapps, version 1",
  "description": "What the review agent judged on an app that asks to be listed, as YAML (verdict.yaml): the reviewed commit, the rules judged, and every finding with its rule id or a proposed new rule. Written by the agent that follows review/prompt.md of @lernapps/tooling, checked with `npm run verdict -- <file>`. A breaking change gets a new version of this schema.",
  "type": "object",
  "properties": {
    "reviewed": {
      "description": "What was reviewed: the commit of the app's repo, and where the app and its listing are.",
      "type": "object",
      "properties": {
        "commit": {
          "type": "string",
          "pattern": "^[0-9a-f]{40}$",
          "description": "The full id of the reviewed commit"
        },
        "repo": { "type": "string", "minLength": 1, "description": "The app's repo: a URL, or a directory" },
        "url": {
          "type": "string",
          "minLength": 1,
          "description": "Where the app is deployed, if it was reviewed there"
        },
        "listing": { "type": "string", "minLength": 1, "description": "The listing pull request, if there is one" }
      },
      "required": ["commit"],
      "additionalProperties": false
    },
    "report": {
      "description": "The validation report of `lernapps check` the review started from: its file, or the URL of the listing comment that holds it.",
      "type": "string",
      "minLength": 1
    },
    "archetype": {
      "description": "The archetype from the plan's front matter; null when the app has no plan file. It selects review/archetypes/<archetype>.md.",
      "enum": ["explainer", "interactive", "quiz", null]
    },
    "outcome": {
      "description": "fail when a finding has severity error, else pass. The owner decides on the listing.",
      "enum": ["pass", "fail"]
    },
    "rules": {
      "description": "The ids of the rubric items judged: those of review/rubric.md and of the archetype's file.",
      "type": "array",
      "items": { "$ref": "#/$defs/id" },
      "uniqueItems": true,
      "minItems": 1
    },
    "findings": {
      "description": "Every finding, sorted by severity and rule id. Each names a rule of the catalog (rule, link), or proposes a new rule (proposal).",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "rule": { "$ref": "#/$defs/id", "description": "The id of the broken rule, as `npm run rules` lists it" },
          "link": {
            "type": "string",
            "pattern": "^https://lernapps\\.net/tooling/rules/#[a-z][a-z0-9]*(-[a-z0-9]+)*$",
            "description": "The rule's entry on the rule page"
          },
          "proposal": {
            "description": "A new rule, when no rule of the catalog covers the finding.",
            "type": "object",
            "properties": {
              "id": { "$ref": "#/$defs/id", "description": "A stable name that is no rule id yet" },
              "title": { "type": "string", "minLength": 1 },
              "text": { "type": "string", "minLength": 1, "description": "The rule, as a skill would tell it" },
              "scope": {
                "type": "array",
                "items": { "type": "string", "pattern": "^(listing|archetype:[a-z][a-z0-9-]*)$" },
                "minItems": 1,
                "uniqueItems": true
              },
              "severity": { "enum": ["error", "warning", "hint"] },
              "actsIn": { "$ref": "#/$defs/artifacts", "description": "Where the rule would act" }
            },
            "required": ["id", "actsIn"],
            "additionalProperties": false
          },
          "severity": { "enum": ["error", "warning", "hint"] },
          "where": {
            "type": "string",
            "minLength": 1,
            "description": "File of the bundle or repo and element, page, or the plan's section"
          },
          "found": { "type": "string", "minLength": 1, "description": "What was found" },
          "fix": { "type": "string", "minLength": 1, "description": "How to fix it" },
          "moveTo": {
            "$ref": "#/$defs/artifacts",
            "description": "For a rule of the catalog: where it could be decided or prevented next time, so that it moves down from the review"
          }
        },
        "required": ["severity", "where", "found", "fix"],
        "additionalProperties": false,
        "dependentRequired": { "rule": ["link"], "link": ["rule"] },
        "oneOf": [
          { "properties": { "rule": true }, "required": ["rule"] },
          { "properties": { "proposal": true }, "required": ["proposal"] }
        ]
      }
    },
    "at": { "type": "string", "description": "ISO 8601 date of the review" }
  },
  "required": ["reviewed", "report", "archetype", "outcome", "rules", "findings"],
  "additionalProperties": false,
  "if": { "type": "object", "properties": { "outcome": { "const": "pass" } }, "required": ["outcome"] },
  "then": {
    "properties": {
      "findings": {
        "type": "array",
        "not": {
          "type": "array",
          "contains": { "type": "object", "properties": { "severity": { "const": "error" } }, "required": ["severity"] }
        }
      }
    }
  },
  "else": {
    "properties": {
      "findings": {
        "type": "array",
        "contains": { "type": "object", "properties": { "severity": { "const": "error" } }, "required": ["severity"] }
      }
    }
  },
  "$defs": {
    "id": { "type": "string", "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$" },
    "artifacts": {
      "type": "array",
      "items": { "enum": ["skill", "lint rule", "check", "rubric item"] },
      "minItems": 1,
      "uniqueItems": true
    }
  }
}
