{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "attest verify --branch, attest badge: every tick of a branch and what its evidence comes to",
  "description": "Result of `speccoder attest verify --branch` / `speccoder attest badge`.",
  "type": "object",
  "properties": {
    "head": {
      "description": "The commit checked.",
      "type": "string"
    },
    "base": {
      "description": "The merge base with the given base, when one was given.",
      "type": [
        "string",
        "null"
      ]
    },
    "mode": {
      "description": "Which units were covered.",
      "$ref": "#/$defs/Mode"
    },
    "features": {
      "description": "The units checked, in order.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "ticks": {
      "description": "Every tick (and trailer-only task), in document order per unit.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/TickReport"
      }
    },
    "skipped": {
      "description": "Tasks skipped by a person's decision (informational).",
      "type": "array",
      "items": {
        "$ref": "#/$defs/SkippedTask"
      }
    },
    "totals": {
      "description": "Counts by outcome.",
      "$ref": "#/$defs/BranchTotals"
    },
    "conclusion": {
      "description": "The conclusion.",
      "$ref": "#/$defs/Conclusion"
    },
    "trust": {
      "description": "The trust used: the allowed-signers file and the revision it was\nread at, the Sigstore identity and issuer."
    },
    "accepted_unsigned": {
      "description": "Unsigned evidence was accepted (`--require-signed=false`).",
      "type": "boolean"
    },
    "require_verified": {
      "description": "`regression_only` and `attested` fail too.",
      "type": "boolean"
    },
    "trace": {
      "description": "Requirement → task → test → verdict, from the signed `trace[]`,\nwith a row (no task) for each requirement no tick cites.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/TraceRow"
      }
    },
    "share": {
      "description": "Evidenced (not stale) over all ticks; `None` without ticks.",
      "type": [
        "number",
        "null"
      ],
      "format": "double"
    },
    "open_no_claim": {
      "description": "Bug homes touched without a verdict commit (informational).",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "not_evaluated": {
      "description": "Rules this verifier version does not evaluate yet.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "newer_fields": {
      "description": "Predicate fields or revisions seen but unknown here (tolerated).",
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "head",
    "base",
    "mode",
    "features",
    "ticks",
    "skipped",
    "totals",
    "conclusion",
    "trust",
    "accepted_unsigned",
    "require_verified",
    "trace",
    "share",
    "open_no_claim",
    "not_evaluated",
    "newer_fields"
  ],
  "$defs": {
    "Mode": {
      "description": "Which units a check covers.",
      "oneOf": [
        {
          "description": "Every unit whose task list or intent differs between base and head.",
          "type": "string",
          "const": "touched"
        },
        {
          "description": "Every unit (the badge, the default branch).",
          "type": "string",
          "const": "all"
        }
      ]
    },
    "TickReport": {
      "description": "One tick's report.",
      "type": "object",
      "properties": {
        "tick": {
          "description": "The tick.",
          "$ref": "#/$defs/Tick"
        },
        "commit": {
          "description": "The task commit checked (the latest reachable with the task's\nverdict trailers), when one exists.",
          "type": [
            "string",
            "null"
          ]
        },
        "outcome": {
          "description": "The outcome.",
          "$ref": "#/$defs/TickOutcome"
        },
        "verdict": {
          "description": "`verified`, `regression_only` or `attested`, as the evidence claims.",
          "type": [
            "string",
            "null"
          ]
        },
        "reason": {
          "description": "For `unverifiable`.",
          "anyOf": [
            {
              "$ref": "#/$defs/Unverifiable"
            },
            {
              "type": "null"
            }
          ]
        },
        "detail": {
          "description": "More on the reason, in one line.",
          "type": [
            "string",
            "null"
          ]
        },
        "signer": {
          "description": "Who signed.",
          "anyOf": [
            {
              "$ref": "#/$defs/SignerView"
            },
            {
              "type": "null"
            }
          ]
        },
        "decider": {
          "description": "Who accepted an `attested` tick.",
          "type": [
            "string",
            "null"
          ]
        },
        "stale": {
          "description": "For `stale`: the cited requirements that changed since the tick.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/RequirementDrift"
          }
        },
        "stale_anchor": {
          "description": "For `stale`: the first changed requirement's line at the head.",
          "anyOf": [
            {
              "$ref": "#/$defs/Anchor"
            },
            {
              "type": "null"
            }
          ]
        },
        "regression": {
          "description": "A regression-only tick: why, in speccoder's wording (signed).",
          "type": [
            "string",
            "null"
          ]
        },
        "newer_fields": {
          "description": "Predicate fields or revisions this verifier does not know (kept,\nnever deciding).",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "verification": {
          "description": "`attest verify`'s object for the commit, unchanged.",
          "anyOf": [
            {
              "$ref": "#/$defs/ObjectVerification"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "tick",
        "commit",
        "outcome",
        "verdict",
        "reason",
        "detail",
        "signer",
        "decider",
        "stale",
        "stale_anchor",
        "newer_fields",
        "verification"
      ]
    },
    "Tick": {
      "description": "A ticked task at the checked head.",
      "type": "object",
      "properties": {
        "feature": {
          "description": "The unit, as the format adapter names it.",
          "type": "string"
        },
        "task": {
          "description": "Task ID (`T012`, `1.3`, `bug:<slug>`).",
          "type": "string"
        },
        "title": {
          "description": "The task's title.",
          "type": "string"
        },
        "kind": {
          "description": "A tick or a trailer-only task.",
          "$ref": "#/$defs/TickKind"
        },
        "anchor": {
          "description": "The task line (a tick) or the home's assessment (trailer-only).",
          "$ref": "#/$defs/Anchor"
        },
        "line_sha256": {
          "description": "sha256 of the task line with its box read as open: what the\npredicate's `task.line_hash` must equal. `None` for trailer-only.",
          "type": [
            "string",
            "null"
          ]
        },
        "claim_sha256": {
          "description": "For trailer-only: the claim's digest at the head (the bug's triad).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "feature",
        "task",
        "title",
        "kind",
        "anchor",
        "line_sha256",
        "claim_sha256"
      ]
    },
    "TickKind": {
      "description": "What a checked task is.",
      "oneOf": [
        {
          "description": "A `[X]` line in a task list.",
          "type": "string",
          "const": "tick"
        },
        {
          "description": "A closed task of a format home without a task list: its verdict\ncommit is the claim (a bug's `bug:<slug>`).",
          "type": "string",
          "const": "trailer_only"
        }
      ]
    },
    "Anchor": {
      "description": "Where an annotation lands: a file and a 1-based line, at the head.",
      "type": "object",
      "properties": {
        "path": {
          "description": "Repository-relative path.",
          "type": "string"
        },
        "line": {
          "description": "1-based line.",
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        }
      },
      "required": [
        "path",
        "line"
      ]
    },
    "TickOutcome": {
      "description": "A tick's outcome.",
      "oneOf": [
        {
          "description": "The evidence verifies; `verdict` says what it shows.",
          "type": "string",
          "const": "evidenced"
        },
        {
          "description": "The evidence verifies, but a requirement the task cites changed\nsince the tick: it is for the earlier wording.",
          "type": "string",
          "const": "stale"
        },
        {
          "description": "The evidence does not verify; `reason` says why.",
          "type": "string",
          "const": "unverifiable"
        },
        {
          "description": "Reserved: a later signed checkpoint marks this earlier task\nregressed. No producer yet; it fails like `unverifiable`.",
          "type": "string",
          "const": "regressed"
        }
      ]
    },
    "Unverifiable": {
      "description": "Why a tick's evidence does not verify.",
      "oneOf": [
        {
          "description": "No commit carries the task's verdict (ticked by hand).",
          "type": "string",
          "const": "ticked_without_evidence"
        },
        {
          "description": "The task commit has no attestation note.",
          "type": "string",
          "const": "missing"
        },
        {
          "description": "A line of the note does not parse, or the predicate is not v0.",
          "type": "string",
          "const": "malformed"
        },
        {
          "description": "A signature does not verify.",
          "type": "string",
          "const": "bad_signature"
        },
        {
          "description": "A statement disagrees with the commit, its trailers, the task line\nor the bundle.",
          "type": "string",
          "const": "mismatch"
        },
        {
          "description": "Signed by a key the verifier does not trust.",
          "type": "string",
          "const": "untrusted_signer"
        },
        {
          "description": "Not signed (unverifiable unless the caller accepts unsigned ticks).",
          "type": "string",
          "const": "unsigned"
        },
        {
          "description": "The commit the evidence names is not reachable from the head (a\nsquash or rebase merge).",
          "type": "string",
          "const": "commit_unreachable"
        },
        {
          "description": "The signature's tool (`ssh-keygen`, `gpg`, `cosign`) cannot run.",
          "type": "string",
          "const": "tool_missing"
        }
      ]
    },
    "SignerView": {
      "description": "Who signed.",
      "type": "object",
      "properties": {
        "kind": {
          "description": "`ssh`, `openpgp` or `sigstore`.",
          "type": "string"
        },
        "keyid": {
          "description": "The key's fingerprint (`SHA256:…`, an OpenPGP fingerprint).",
          "type": "string"
        },
        "principal": {
          "description": "The principal the verifier's trust input names for it.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "kind",
        "keyid"
      ]
    },
    "RequirementDrift": {
      "description": "One cited requirement, as the tick saw it and as the spec has it now.",
      "type": "object",
      "properties": {
        "id": {
          "description": "`FR-003`.",
          "type": "string"
        },
        "then": {
          "description": "Its line when the task was ticked; `None`: it did not exist yet.",
          "type": [
            "string",
            "null"
          ]
        },
        "now": {
          "description": "Its line now; `None`: it was removed.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "then",
        "now"
      ]
    },
    "ObjectVerification": {
      "description": "One task object's verification.",
      "type": "object",
      "properties": {
        "object": {
          "description": "The commit (or skip record) id.",
          "type": "string"
        },
        "kind": {
          "description": "`task` or `skip`.",
          "type": "string"
        },
        "task": {
          "description": "Task ID.",
          "type": "string"
        },
        "verdict": {
          "description": "The verdict the statements claim.",
          "type": [
            "string",
            "null"
          ]
        },
        "outcome": {
          "description": "The outcome.",
          "$ref": "#/$defs/VerifyOutcome"
        },
        "reasons": {
          "description": "Every reason found.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/VerifyReason"
          }
        },
        "signer": {
          "description": "Who signed, when signed.",
          "anyOf": [
            {
              "$ref": "#/$defs/SignerView"
            },
            {
              "type": "null"
            }
          ]
        },
        "statements": {
          "description": "How many statements the bundle holds.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "same_tree_as": {
          "description": "For a missing attestation: an attested commit with the same tree.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "object",
        "kind",
        "task",
        "verdict",
        "outcome",
        "reasons",
        "signer",
        "statements",
        "same_tree_as"
      ]
    },
    "VerifyOutcome": {
      "description": "What `attest verify` found for one task object.",
      "oneOf": [
        {
          "description": "Every line parses, every signature verifies against the trust input,\nand every statement agrees with the commit.",
          "type": "string",
          "const": "ok"
        },
        {
          "description": "The statements hold but are not signed.",
          "type": "string",
          "const": "unsigned"
        },
        {
          "description": "No speccoder attestation on the object.",
          "type": "string",
          "const": "missing"
        },
        {
          "description": "A signature does not verify.",
          "type": "string",
          "const": "bad_signature"
        },
        {
          "description": "A signature verifies, but its key is not trusted by the verifier.",
          "type": "string",
          "const": "untrusted_signer"
        },
        {
          "description": "A statement disagrees with the commit, its trailers, the spec or the bundle.",
          "type": "string",
          "const": "mismatch"
        },
        {
          "description": "A line does not parse.",
          "type": "string",
          "const": "malformed"
        },
        {
          "description": "The signature's tool (`ssh-keygen`, `gpg`, `cosign`) cannot run:\nnothing can be said about it.",
          "type": "string",
          "const": "tool_missing"
        }
      ]
    },
    "VerifyReason": {
      "description": "Why an object got its outcome.",
      "type": "object",
      "properties": {
        "code": {
          "description": "A stable code, e.g. `subject`, `trailer`, `line_hash`, `signature`.",
          "type": "string"
        },
        "field": {
          "description": "The field that disagrees.",
          "type": [
            "string",
            "null"
          ]
        },
        "expected": {
          "description": "What it should be.",
          "type": [
            "string",
            "null"
          ]
        },
        "found": {
          "description": "What it is.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "code"
      ]
    },
    "SkippedTask": {
      "description": "A task skipped by a person's decision: no tick, not counted.",
      "type": "object",
      "properties": {
        "feature": {
          "description": "The unit.",
          "type": "string"
        },
        "task": {
          "description": "Task ID.",
          "type": "string"
        }
      },
      "required": [
        "feature",
        "task"
      ]
    },
    "BranchTotals": {
      "description": "Counts by outcome, in the verdict bar's order.",
      "type": "object",
      "properties": {
        "verified": {
          "description": "Evidenced `verified`.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "regression_only": {
          "description": "Evidenced `regression_only`.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "attested": {
          "description": "Evidenced `attested`.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "stale": {
          "description": "Stale.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "unverifiable": {
          "description": "Unverifiable.",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "regressed": {
          "description": "Regressed (reserved).",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        },
        "skipped": {
          "description": "Skipped by a person (not ticks).",
          "type": "integer",
          "format": "uint",
          "minimum": 0
        }
      },
      "required": [
        "verified",
        "regression_only",
        "attested",
        "stale",
        "unverifiable",
        "regressed",
        "skipped"
      ]
    },
    "Conclusion": {
      "description": "The branch's conclusion.",
      "oneOf": [
        {
          "description": "Every tick carries verifying evidence.",
          "type": "string",
          "const": "success"
        },
        {
          "description": "A tick is unverifiable, stale or regressed (or not verified while\nrequired), or a claim in range cannot be checked.",
          "type": "string",
          "const": "failure"
        },
        {
          "description": "Nothing to check: never success.",
          "type": "string",
          "const": "neutral"
        }
      ]
    },
    "TraceRow": {
      "description": "One row of the requirement → scenario → test → verdict matrix.",
      "type": "object",
      "properties": {
        "requirement": {
          "description": "`FR-001`, `SC-002`.",
          "type": "string"
        },
        "scenario": {
          "description": "`US1.2`: story 1, acceptance scenario 2.",
          "type": [
            "string",
            "null"
          ]
        },
        "test": {
          "description": "The acceptance test.",
          "type": [
            "string",
            "null"
          ]
        },
        "task": {
          "description": "The task that cites it.",
          "type": [
            "string",
            "null"
          ]
        },
        "verdict": {
          "description": "That task's verdict.",
          "type": [
            "string",
            "null"
          ]
        },
        "changed": {
          "description": "The requirement changed since that task's tick: the verdict is for\nits earlier wording.",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "requirement",
        "scenario",
        "test",
        "task",
        "verdict",
        "changed"
      ]
    }
  },
  "$id": "https://hupe1980.github.io/speccoder/schemas/v2/branch_verify.json"
}
