{
  "$defs": {
    "DomainRating": {
      "description": "A reviewer's quality [`Rating`] for one of a case's scoring domains.",
      "properties": {
        "domain": {
          "description": "The declared domain's stable id (see [`crate::test_case::Domain::id`]).",
          "type": "string"
        },
        "rating": {
          "$ref": "#/$defs/Rating",
          "description": "The reviewer's rating for this domain."
        }
      },
      "required": ["domain", "rating"],
      "type": "object"
    },
    "Rating": {
      "description": "A reviewer's subjective quality rating for a finished implementation.\n\nAssigned by hand while playing the build, ordered best to worst. It is a\nper-run signal shown alongside a run, never an aggregate or a ranking across\nruns (see `docs/site.md`).",
      "oneOf": [
        {
          "const": "flawless",
          "description": "Implemented according to spec with no noticeable bugs.",
          "type": "string"
        },
        {
          "const": "great",
          "description": "Implemented according to spec; may have minor issues that don't impact\nplayability.",
          "type": "string"
        },
        {
          "const": "passable",
          "description": "Implemented to spec and playable, but with rough edges beyond the minor\nissues of a [`Great`](Rating::Great) run — noticeable, though not enough\nto deviate from the spec or impair playability.",
          "type": "string"
        },
        {
          "const": "scuffed",
          "description": "Mostly implemented according to spec. Playable, but deviates from the\nspec or has bugs that impact playability.",
          "type": "string"
        },
        {
          "const": "broken",
          "description": "Doesn't follow the spec, or has bugs severe enough to render the game\nunplayable.",
          "type": "string"
        }
      ]
    },
    "RatingChange": {
      "description": "One per-domain rating change between two versions of a review (see\n[`ReviewDiff`]). A newly rated domain has `from = None`; a domain whose rating\nwas dropped has `to = None`.",
      "properties": {
        "domain": {
          "description": "The scoring domain whose rating changed.",
          "type": "string"
        },
        "from": {
          "anyOf": [
            {
              "$ref": "#/$defs/Rating"
            },
            {
              "type": "null"
            }
          ],
          "description": "The rating before the edit, or `None` if the domain was newly rated."
        },
        "to": {
          "anyOf": [
            {
              "$ref": "#/$defs/Rating"
            },
            {
              "type": "null"
            }
          ],
          "description": "The rating after the edit, or `None` if the domain's rating was removed."
        }
      },
      "required": ["domain"],
      "type": "object"
    },
    "ReviewDiff": {
      "description": "The autogenerated, structured difference between two versions of a review: which\nper-domain ratings changed, which checklist verdicts flipped, and whether the\nwriteup prose changed. Computed by [`diff_reviews`] when a reviewer edits their\nreview and stored on the resulting [`ReviewRevision`], so the edit history can\nshow *what* changed alongside the reviewer's note on *why*.",
      "properties": {
        "ratings": {
          "description": "The per-domain rating changes, in the new review's domain order followed by\nany domains whose rating was removed.",
          "items": {
            "$ref": "#/$defs/RatingChange"
          },
          "type": "array"
        },
        "verdicts": {
          "description": "The checklist verdict changes, in the new review's verdict order followed by\nany verdicts that were withdrawn.",
          "items": {
            "$ref": "#/$defs/VerdictChange"
          },
          "type": "array"
        },
        "writeup": {
          "anyOf": [
            {
              "$ref": "#/$defs/WriteupChange"
            },
            {
              "type": "null"
            }
          ],
          "description": "The writeup change, or `None` when the prose was untouched."
        }
      },
      "type": "object"
    },
    "ReviewRevision": {
      "description": "One superseded version of a review, recorded when the reviewer edits it: the note\nthe reviewer wrote explaining the change, when the edit was made, and the\nautogenerated [`ReviewDiff`] from the prior content to the new. A review's\nrevisions are newest-last, so replaying their diffs from the original walks the\nreview forward to its current state.",
      "properties": {
        "diff": {
          "$ref": "#/$defs/ReviewDiff",
          "description": "The autogenerated diff from the content before this edit to the content after."
        },
        "editedAt": {
          "description": "RFC 3339 of when this edit was made.",
          "type": "string"
        },
        "note": {
          "description": "The reviewer's note explaining what changed and why. Required on every edit.",
          "type": "string"
        }
      },
      "required": ["editedAt", "note", "diff"],
      "type": "object"
    },
    "ReviewVerdict": {
      "description": "A reviewer's recorded verdict on one declared checklist item.",
      "properties": {
        "id": {
          "description": "The declared item's stable id (see [`crate::test_case::ReviewItem::id`]).",
          "type": "string"
        },
        "note": {
          "description": "An optional one-line note recording what the reviewer observed. `None`\nwhen the reviewer left no note.",
          "type": ["string", "null"]
        },
        "status": {
          "$ref": "#/$defs/VerdictStatus",
          "description": "The reviewer's verdict on the item."
        }
      },
      "required": ["id", "status"],
      "type": "object"
    },
    "VerdictChange": {
      "description": "One checklist verdict change between two versions of a review (see\n[`ReviewDiff`]). A newly recorded verdict has `from = None`; a verdict that was\nwithdrawn has `to = None`. [`note_changed`](Self::note_changed) flags a change to\nthe verdict's own note text even when its pass/fail/grade status held.",
      "properties": {
        "from": {
          "anyOf": [
            {
              "$ref": "#/$defs/VerdictStatus"
            },
            {
              "type": "null"
            }
          ],
          "description": "The status before the edit, or `None` if the verdict was newly recorded."
        },
        "id": {
          "description": "The declared item's verdict id (an item id or a composite `<item>.<sub>`).",
          "type": "string"
        },
        "noteChanged": {
          "description": "Whether the verdict's note text changed (independently of its status).",
          "type": "boolean"
        },
        "to": {
          "anyOf": [
            {
              "$ref": "#/$defs/VerdictStatus"
            },
            {
              "type": "null"
            }
          ],
          "description": "The status after the edit, or `None` if the verdict was withdrawn."
        }
      },
      "required": ["id", "noteChanged"],
      "type": "object"
    },
    "VerdictStatus": {
      "description": "A reviewer's verdict on one declared checklist item.\n\nA test case declares the checklist (see [`crate::test_case::ReviewItem`]); the\nreviewer records one of these per item while judging the build.\n\nMost case types grade an item **binary** — [`Pass`](VerdictStatus::Pass) or\n[`Fail`](VerdictStatus::Fail) — and the item earns all its weight or none. A\n[game jam](crate::test_case::TestType::GameJam) instead grades each of its\nreview categories on a five-level **graded** scale worth a fixed number of\npoints ([`Broken`](VerdictStatus::Broken) 0 → [`Incredible`](VerdictStatus::Incredible)\n10); the same graded scale carries the reviewer's whole-game\n[`OVERALL_VERDICT_ID`] mark. Which scale an item uses is declared on the item\n([`crate::test_case::ReviewItem::graded`]); the two never mix within a case.\nKeep the tiers and their point values in lockstep with the TypeScript\n`VERDICT_META` in `packages/ui/src/ratings.ts`.",
      "oneOf": [
        {
          "const": "pass",
          "description": "Binary: the item was checked and the build satisfies it. The item earns\nits weight toward the run's score.",
          "type": "string"
        },
        {
          "const": "fail",
          "description": "Binary: the item was checked and the build does not satisfy it. The item\nearns none of its weight.",
          "type": "string"
        },
        {
          "const": "broken",
          "description": "Graded 💩: broken. Worth 0 points.",
          "type": "string"
        },
        {
          "const": "poor",
          "description": "Graded 🙁: not great. Worth 2 points.",
          "type": "string"
        },
        {
          "const": "neutral",
          "description": "Graded 😐: neutral. Worth 5 points.",
          "type": "string"
        },
        {
          "const": "great",
          "description": "Graded 😀: great. Worth 8 points.",
          "type": "string"
        },
        {
          "const": "incredible",
          "description": "Graded 💎: incredible. Worth 10 points.",
          "type": "string"
        }
      ]
    },
    "WriteupChange": {
      "description": "The writeup body change between two versions of a review, present in a\n[`ReviewDiff`] only when the prose actually changed.",
      "properties": {
        "from": {
          "description": "The writeup body before the edit.",
          "type": "string"
        },
        "to": {
          "description": "The writeup body after the edit.",
          "type": "string"
        }
      },
      "required": ["from", "to"],
      "type": "object"
    }
  },
  "$id": "https://docs.testcabinet.ai/schema/backend-api/review.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "One published review on a run: the reviewer's public identity, their per-domain\nratings, the writeup, and their checklist verdicts. This is the canonical\nreview wire shape (`backend-api/review.schema.json`), referenced by the per-run\nsnapshot document.",
  "properties": {
    "checklist": {
      "items": {
        "$ref": "#/$defs/ReviewVerdict"
      },
      "type": "array"
    },
    "editedAt": {
      "description": "RFC 3339 of when the review was last edited, or `None` if it has never been\nedited since it was first submitted. The newest [`Self::revisions`] entry\ncarries the same timestamp.",
      "type": ["string", "null"]
    },
    "pictureKey": {
      "description": "The snapshot-relative object key of the reviewer's profile picture\n(`pfp/<reviewer-id>`), or `None` when the reviewer has no picture. The\nbytes are exported once per reviewer under the content-stable top-level\n`pfp/` prefix (like run media under `MEDIA_PREFIX`); the site resolves the\nkey against the snapshot base into an absolute avatar URL.",
      "type": ["string", "null"]
    },
    "ratings": {
      "description": "The reviewer's rating for each scoring domain. This review's overall\nrating is the worst across them.",
      "items": {
        "$ref": "#/$defs/DomainRating"
      },
      "type": "array"
    },
    "reviewedAt": {
      "description": "RFC 3339 of when the review was **first** submitted (unchanged by later\nedits — see [`Self::edited_at`]).",
      "type": "string"
    },
    "reviewer": {
      "description": "The reviewer's display name, shown beside their review.",
      "type": "string"
    },
    "reviewerId": {
      "description": "The reviewing account's id (stable across their reviews).",
      "type": "string"
    },
    "revisions": {
      "description": "The review's edit history, oldest first: one entry per edit, each with the\nreviewer's note and the autogenerated diff of what changed. Empty for a\nreview that has never been edited. Public, so a reader can see how a review\nevolved.",
      "items": {
        "$ref": "#/$defs/ReviewRevision"
      },
      "type": "array"
    },
    "writeup": {
      "type": "string"
    }
  },
  "required": [
    "reviewerId",
    "reviewer",
    "ratings",
    "writeup",
    "checklist",
    "reviewedAt"
  ],
  "title": "Review",
  "type": "object"
}
