{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://thruput.se/agents/schemas/review/ledger.schema.json",
  "title": "Ledger",
  "description": "The completeness record of a review, of which there is one: one row per rule probed, naming what was opened, the verdict reached, and [[Evidence]]. Each probe writes its rows to the file its instructions name; the reporting script reads those files, never what a probe says when it returns, and merges them into the one ledger that travels with the review. A row is complete only with its examined and evidence filled; a statement that a rule was considered is not a row",
  "type": "object",
  "required": [
    "headCommit",
    "rows"
  ],
  "properties": {
    "headCommit": {
      "$ref": "change-set.schema.json#/$defs/Commit"
    },
    "rows": {
      "description": "One row per rule probed: the rows of each probe in the order of its instructions",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Row"
      }
    }
  },
  "$defs": {
    "Row": {
      "description": "One probe's result on one rule: a rule of ours, or a rule of a ruleset from outside ours",
      "$ref": "#/$defs/Verdict",
      "oneOf": [
        {
          "description": "The verdict reached on one of our rules by [[Id]]",
          "type": "object",
          "required": [
            "rule"
          ],
          "properties": {
            "rule": {
              "$ref": "../governance/rules.schema.json#/$defs/RuleId"
            }
          }
        },
        {
          "description": "The verdict the escalation probe reached on one rule of the ruleset it selected",
          "type": "object",
          "required": [
            "bookRule"
          ],
          "properties": {
            "bookRule": {
              "description": "A rule from outside ours, referenced by where it is read: the reporting script renders the link, the probe writes none into its observation",
              "type": "object",
              "required": [
                "ruleset",
                "heading"
              ],
              "properties": {
                "ruleset": {
                  "$ref": "#/$defs/RulesetUrl"
                },
                "heading": {
                  "description": "The heading of a rule in its ruleset",
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          }
        }
      ]
    },
    "RulesetUrl": {
      "description": "A ruleset from outside our rules, by its canonical URL",
      "type": "string",
      "format": "uri"
    },
    "Verdict": {
      "description": "What a probe reached on one rule; the verdict decides what else is carried",
      "type": "object",
      "required": [
        "examined",
        "evidence"
      ],
      "properties": {
        "examined": {
          "description": "What was actually opened to reach the verdict. Context the probe needed and was not handed is named here rather than silently dropped",
          "type": "array",
          "minItems": 1,
          "items": {
            "description": "One thing opened to reach the verdict: a file, a symbol, a call site, a test file including the case where none exists, or a search with its query",
            "type": "string",
            "minLength": 1
          }
        },
        "evidence": {
          "description": "Verifiable [[Evidence]] that the verdict rests on; the verdict it sits in says what it has to name",
          "type": "string",
          "minLength": 1
        }
      },
      "oneOf": [
        {
          "description": "The rule is violated: [[Evidence]] names file and line inside the surface, and every finding is carried as a violation",
          "type": "object",
          "required": [
            "verdict",
            "violations"
          ],
          "properties": {
            "verdict": {
              "const": "violation"
            },
            "violations": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "violation.schema.json"
              }
            }
          }
        },
        {
          "description": "The rule holds: [[Evidence]] names what was checked that would have exposed a violation; it carries no violations",
          "type": "object",
          "required": [
            "verdict"
          ],
          "properties": {
            "verdict": {
              "const": "clean"
            },
            "violations": false
          }
        },
        {
          "description": "The rule cannot apply to what is under review: [[Evidence]] says why. No findings is not a reason; it carries no violations",
          "type": "object",
          "required": [
            "verdict"
          ],
          "properties": {
            "verdict": {
              "const": "not-applicable"
            },
            "violations": false
          }
        }
      ]
    }
  },
  "examples": [
    {
      "headCommit": "e2bde7afe2f2b1ffc23cdb6f0dc00601b4868eb7",
      "rows": [
        {
          "rule": "Scripts abort on error",
          "examined": [
            "scripts/verify.sh",
            "build.sh"
          ],
          "verdict": "clean",
          "evidence": "Both scripts run under set -euo pipefail, and no step is wrapped in a construct that would let the script continue."
        },
        {
          "rule": "No suppressed exit status",
          "examined": [
            "scripts/verify.sh"
          ],
          "verdict": "violation",
          "evidence": "scripts/verify.sh:21 pipes the check into tail, so its status is lost.",
          "violations": [
            {
              "observation": "Losing the check's exit status in a pipe into tail",
              "anchor": {
                "kind": "surface",
                "path": "scripts/verify.sh",
                "side": "head",
                "lines": {
                  "21": "npx --yes @sourcemeta/jsonschema@17.0.0 metaschema \"$PROJECT_ROOT/schemas\" || true"
                }
              }
            }
          ]
        },
        {
          "bookRule": {
            "ruleset": "https://github.com/ciembor/agent-rules-books/blob/main/domain-driven-design/domain-driven-design.md",
            "heading": "Make implicit concepts explicit"
          },
          "examined": [
            "scripts/verify.sh"
          ],
          "verdict": "clean",
          "evidence": "The change adds one check to a script and introduces no domain concept that could be left implicit."
        }
      ]
    }
  ]
}
