{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:showpapers:paper-record:1",
  "title": "Paper Record",
  "$comment": "papers/<id>.json: everything about one paper except its original bytes and title. paperVersion is required; other members are optional: an omitted member is not described (a merge leaves the library's value unchanged; a new paper uses the app default). The runtime also checks detail ID uniqueness, origin-specific rules, evidence against the reading, reference resolution against the manifest, the native analysis budget (50,000 nodes, 2 MiB) and the type rule (custom needs selected generic).",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "paperVersion"
  ],
  "properties": {
    "paperVersion": {
      "const": 1,
      "type": "integer"
    },
    "createdAt": {
      "$ref": "#/$defs/timestamp"
    },
    "automaticTitle": {
      "type": "boolean"
    },
    "type": {
      "$comment": "detected/suggested come from the reading (detected null means the paper was never read); selected and custom are the person's choice; agentSuggestion is an external agent's proposal awaiting review.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "detected",
        "suggested",
        "selected",
        "custom",
        "agentSuggestion"
      ],
      "properties": {
        "detected": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/$defs/kind"
            }
          ]
        },
        "suggested": {
          "type": "array",
          "maxItems": 13,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/kind"
          }
        },
        "selected": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/$defs/kind"
            }
          ]
        },
        "custom": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/$defs/label"
            }
          ]
        },
        "agentSuggestion": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "agentId",
                "kind",
                "custom",
                "confidence"
              ],
              "properties": {
                "agentId": {
                  "$ref": "#/$defs/id"
                },
                "kind": {
                  "$ref": "#/$defs/kind"
                },
                "custom": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "$ref": "#/$defs/label"
                    }
                  ]
                },
                "confidence": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "$ref": "#/$defs/unit"
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "details": {
      "type": "array",
      "maxItems": 562,
      "items": {
        "$ref": "#/$defs/detail"
      }
    },
    "removedSuggestions": {
      "type": "array",
      "maxItems": 512,
      "items": {
        "$ref": "#/$defs/removed"
      }
    },
    "personIds": {
      "type": "array",
      "maxItems": 64,
      "uniqueItems": true,
      "items": {
        "$ref": "#/$defs/id"
      }
    },
    "builtInFolder": {
      "anyOf": [
        {
          "type": "null"
        },
        {
          "enum": [
            "identity",
            "visas",
            "notices",
            "applications",
            "residency",
            "work-authorization",
            "travel-entry",
            "study",
            "home",
            "finance",
            "health",
            "education",
            "work",
            "family",
            "legal",
            "other"
          ]
        }
      ]
    },
    "reminder": {
      "anyOf": [
        {
          "type": "null"
        },
        {
          "$comment": "source detail names a date detail of this paper with one of the keys expiration-date, valid-until, program-end-date, admit-until, coverage-end-date; source date has detailId null.",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "source",
            "detailId",
            "date",
            "leadDays",
            "enabled"
          ],
          "properties": {
            "source": {
              "enum": [
                "detail",
                "date"
              ]
            },
            "detailId": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 96
                }
              ]
            },
            "date": {
              "$ref": "#/$defs/isoDate"
            },
            "leadDays": {
              "type": "integer",
              "enum": [
                7,
                14,
                30
              ]
            },
            "enabled": {
              "type": "boolean"
            }
          }
        }
      ]
    }
  },
  "$defs": {
    "id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
    },
    "timestamp": {
      "type": "string",
      "minLength": 1,
      "maxLength": 19,
      "pattern": "^(0|[1-9][0-9]{0,18})$"
    },
    "isoDate": {
      "type": "string",
      "minLength": 10,
      "maxLength": 10,
      "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
    },
    "unit": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "kind": {
      "enum": [
        "generic",
        "i20",
        "i94",
        "passport",
        "visa",
        "i797",
        "employment-authorization",
        "drivers-license",
        "social-security",
        "birth-certificate",
        "marriage-certificate",
        "tax",
        "insurance"
      ]
    },
    "fieldKey": {
      "enum": [
        "program-start-date",
        "program-end-date",
        "sevis-id",
        "admission-date",
        "admit-until",
        "class-of-admission",
        "admission-record-number",
        "given-name",
        "family-name",
        "date-of-birth",
        "country-of-birth",
        "country-of-citizenship",
        "nationality",
        "document-number",
        "passport-number",
        "issuing-country",
        "issue-date",
        "expiration-date",
        "place-of-birth",
        "sex",
        "full-name",
        "receipt-number",
        "case-type",
        "notice-type",
        "notice-date",
        "received-date",
        "priority-date",
        "petitioner",
        "beneficiary",
        "classification",
        "valid-from",
        "valid-until",
        "visa-class",
        "visa-entries",
        "visa-control-number",
        "issuing-post",
        "uscis-number",
        "card-number",
        "authorization-category",
        "license-number",
        "license-class",
        "license-restrictions",
        "license-endorsements",
        "social-security-number",
        "certificate-number",
        "registration-number",
        "registration-date",
        "mother-name",
        "father-name",
        "child-name",
        "marriage-date",
        "marriage-place",
        "spouse-one",
        "spouse-two",
        "policy-number",
        "member-id",
        "group-number",
        "insured-name",
        "insurer",
        "coverage-start-date",
        "coverage-end-date",
        "tax-form",
        "tax-year",
        "employer-name",
        "employer-identification-number",
        "taxpayer-name",
        "school-name",
        "school-code",
        "education-level",
        "major",
        "second-major",
        "estimated-total-cost",
        "student-personal-funds",
        "school-funds",
        "other-funds",
        "on-campus-employment-funds",
        "funding-period",
        "other"
      ]
    },
    "valueType": {
      "enum": [
        "text",
        "date",
        "duration-of-status"
      ]
    },
    "label": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "pattern": "^(?![ \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000])(?![\\s\\S]*[ \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000]$)[^\\u0000-\\u001f\\u007f-\\u009f\\ud800-\\udfff]+$"
    },
    "region": {
      "$comment": "Normalized to the rendered page (0..1). box is [left, top, right, bottom] with left <= right and top <= bottom.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "box",
        "polygon"
      ],
      "properties": {
        "box": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "type": "array",
              "minItems": 4,
              "maxItems": 4,
              "items": {
                "$ref": "#/$defs/unit"
              }
            }
          ]
        },
        "polygon": {
          "type": "array",
          "maxItems": 8,
          "items": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "$ref": "#/$defs/unit"
            }
          }
        }
      }
    },
    "evidence": {
      "oneOf": [
        {
          "$comment": "A line of this paper's reading (readings/<id>.json must exist and contain it).",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "page",
            "block",
            "line"
          ],
          "properties": {
            "page": {
              "type": "integer",
              "minimum": 0,
              "maximum": 199
            },
            "block": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1999
            },
            "line": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9999
            }
          }
        },
        {
          "$comment": "Text quoted from the original page by an external agent, with an optional location. Not an OCR line.",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "page",
            "quote",
            "region"
          ],
          "properties": {
            "page": {
              "type": "integer",
              "minimum": 0,
              "maximum": 199
            },
            "quote": {
              "type": "string",
              "minLength": 1,
              "maxLength": 8192,
              "pattern": "^[^\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f\\ud800-\\udfff]*$"
            },
            "region": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "$ref": "#/$defs/region"
                }
              ]
            }
          }
        }
      ]
    },
    "detail": {
      "$comment": "origin reading: found by the app's on-device reading; id [a-z][a-z0-9_]{0,95}, 1-16 reading-line evidence, review required, confidence and agentId null. origin person: a custom detail the person typed; key other, label 0-80 scalars, value 0-500 scalars, valueType text, no evidence, concerns, confidence, agentId or review. origin agent: proposed by an external AI agent; id is a UUID, agentId names manifest.agents, value non-blank, label required for key other, optional confidence and 0-16 evidence items, review required (pending until confirmed). Values are limited to 4096 UTF-8 bytes.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "origin",
        "key",
        "label",
        "value",
        "normalizedValue",
        "valueType",
        "concerns",
        "evidence",
        "confidence",
        "agentId",
        "review"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 96,
          "pattern": "^([a-z][a-z0-9_]{0,95}|[A-Za-z][A-Za-z0-9_-]{0,95}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$"
        },
        "origin": {
          "enum": [
            "reading",
            "person",
            "agent"
          ]
        },
        "key": {
          "$ref": "#/$defs/fieldKey"
        },
        "label": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "type": "string",
              "minLength": 0,
              "maxLength": 80,
              "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f\\ud800-\\udfff]*$"
            }
          ]
        },
        "value": {
          "type": "string",
          "minLength": 0,
          "maxLength": 4096
        },
        "normalizedValue": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "type": "string",
              "minLength": 0,
              "maxLength": 4096
            }
          ]
        },
        "valueType": {
          "$ref": "#/$defs/valueType"
        },
        "concerns": {
          "type": "array",
          "maxItems": 3,
          "uniqueItems": true,
          "items": {
            "enum": [
              "invalid-date",
              "ambiguous-date",
              "conflicting-values",
              "read-by-ai"
            ]
          }
        },
        "evidence": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "$ref": "#/$defs/evidence"
          }
        },
        "confidence": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/$defs/unit"
            }
          ]
        },
        "agentId": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/$defs/id"
            }
          ]
        },
        "review": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "confirmed",
                "correctedValue"
              ],
              "properties": {
                "confirmed": {
                  "type": "boolean"
                },
                "correctedValue": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "string",
                      "minLength": 0,
                      "maxLength": 4096
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "removed": {
      "oneOf": [
        {
          "$comment": "A reading suggestion the person removed, remembered so a re-read does not bring it back. pages ascending; sdkFieldId and sdkDocumentType are both null or both set.",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "origin",
            "id",
            "identity"
          ],
          "properties": {
            "origin": {
              "const": "reading"
            },
            "id": {
              "type": "string",
              "minLength": 1,
              "maxLength": 96,
              "pattern": "^[a-z][a-z0-9_]{0,95}$"
            },
            "identity": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "key",
                    "valueType",
                    "pages",
                    "sdkFieldId",
                    "sdkDocumentType"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/$defs/fieldKey"
                    },
                    "valueType": {
                      "$ref": "#/$defs/valueType"
                    },
                    "pages": {
                      "type": "array",
                      "minItems": 1,
                      "maxItems": 16,
                      "uniqueItems": true,
                      "items": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 199
                      }
                    },
                    "sdkFieldId": {
                      "anyOf": [
                        {
                          "type": "null"
                        },
                        {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 128,
                          "pattern": "^[A-Za-z][A-Za-z0-9_.:-]{0,127}$"
                        }
                      ]
                    },
                    "sdkDocumentType": {
                      "anyOf": [
                        {
                          "type": "null"
                        },
                        {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 160,
                          "pattern": "^[A-Za-z][A-Za-z0-9_.-]{0,159}$"
                        }
                      ]
                    }
                  }
                }
              ]
            }
          }
        },
        {
          "$comment": "An agent suggestion the person rejected, remembered so importing the same proposal again does not bring it back.",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "origin",
            "id",
            "agentId",
            "key",
            "label",
            "value"
          ],
          "properties": {
            "origin": {
              "const": "agent"
            },
            "id": {
              "$ref": "#/$defs/id"
            },
            "agentId": {
              "$ref": "#/$defs/id"
            },
            "key": {
              "$ref": "#/$defs/fieldKey"
            },
            "label": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "string",
                  "minLength": 0,
                  "maxLength": 80,
                  "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f\\ud800-\\udfff]*$"
                }
              ]
            },
            "value": {
              "type": "string",
              "minLength": 0,
              "maxLength": 4096
            }
          }
        }
      ]
    }
  }
}
