{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:cede:schema:exposure-codes:v0",
  "title": "Craton exposure coding answer, v0",
  "description": "The body of POST /exposure-codes: OED 5.0.0 OccupancyCode and ConstructionCode values for rows of schedule-of-values text, one building a row. A model (TypeSafe's Jev, a decision model that answers closed questions only) proposes; Craton's deterministic code adopts a proposal only when it maps to a real OED code and its confidence clears the field's gate. A field below the gate is left uncoded with the proposal beside it and its row is marked needs_review. No member carries text a model wrote: codes, numbers, enum members, OED names from Craton's own lists, and model and provider identifiers. Nothing is stored.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "run_id",
    "coded_at",
    "model",
    "question_set",
    "oed",
    "gate",
    "counts",
    "rows"
  ],
  "properties": {
    "schema_version": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
    },
    "run_id": {
      "type": "string",
      "pattern": "^exposure-codes-v0:[0-9a-f]{16}:[0-9a-f]{16}$",
      "description": "Identity of this run: the question set's digest, then a digest of the model, the providers that served it, the OED lists and every row's decided fields. Two runs that coded anything differently never share a run id."
    },
    "coded_at": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
      "description": "When the rows were coded, UTC. The model is reached through a gateway that reports no dated version, so the date, the serving provider and each row's generation_id are the version evidence."
    },
    "model": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "served_by",
        "via"
      ],
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}(/[A-Za-z0-9][A-Za-z0-9._-]{0,63})?$",
          "description": "The model as the gateway reported it, for example typesafe-ai/jev."
        },
        "served_by": {
          "type": "array",
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
          },
          "description": "The providers the gateway says served this run's rows, as it names them. Empty when it named none."
        },
        "via": {
          "type": "string",
          "enum": [
            "vercel-ai-gateway"
          ],
          "description": "How Craton reaches the model."
        }
      }
    },
    "question_set": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "version",
        "digest"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "version": {
          "type": "string",
          "minLength": 1
        },
        "digest": {
          "type": "string",
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "SHA-256 of the question set file Craton asks every row, byte for byte. The questions are Craton's, never the caller's."
        }
      }
    },
    "oed": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "version",
        "fields"
      ],
      "properties": {
        "version": {
          "type": "string",
          "const": "5.0.0"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "OccupancyCode",
              "ConstructionCode"
            ]
          },
          "minItems": 1,
          "uniqueItems": true
        }
      }
    },
    "gate": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "description": "The confidence each field needs to be adopted without review, per OED field.",
      "properties": {
        "OccupancyCode": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 1
        },
        "ConstructionCode": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 1
        }
      }
    },
    "counts": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "rows",
        "coded",
        "needs_review",
        "unanswered"
      ],
      "properties": {
        "rows": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50
        },
        "coded": {
          "type": "integer",
          "minimum": 0
        },
        "needs_review": {
          "type": "integer",
          "minimum": 0
        },
        "unanswered": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "rows": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "description": "One entry per row sent, in the order sent. The row text is not echoed.",
      "items": {
        "$ref": "#/$defs/row"
      }
    }
  },
  "$defs": {
    "row": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "row",
        "status",
        "fields",
        "generation_id"
      ],
      "properties": {
        "row": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "description": "The row's position in the request, from 1."
        },
        "status": {
          "type": "string",
          "enum": [
            "coded",
            "needs_review",
            "unanswered"
          ],
          "description": "coded: every field cleared its gate. needs_review: the model answered, and at least one field did not clear. unanswered: no usable reply came back for this row; send it again."
        },
        "fields": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "properties": {
            "OccupancyCode": {
              "$ref": "#/$defs/occupancy_field"
            },
            "ConstructionCode": {
              "$ref": "#/$defs/construction_field"
            }
          }
        },
        "generation_id": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^gen_[A-Za-z0-9]{1,64}$",
          "description": "The AI gateway's id for the call that answered this row, the trace the provider can look up. Null when the row was not answered or the gateway named none. Not part of run_id: two calls that decided the same thing are the same outcome."
        }
      }
    },
    "occupancy_field": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "code",
        "proposed",
        "name",
        "confidence",
        "cleared",
        "why",
        "alternatives"
      ],
      "properties": {
        "code": {
          "oneOf": [
            {
              "$ref": "#/$defs/occupancy_code"
            },
            {
              "type": "null"
            }
          ],
          "description": "The OED OccupancyCode this field is coded as, or null when it is not coded. Non-null exactly when cleared is true: a proposal below the gate is never written here."
        },
        "proposed": {
          "oneOf": [
            {
              "$ref": "#/$defs/occupancy_code"
            },
            {
              "type": "null"
            }
          ],
          "description": "The OED code the model's answer maps to, whether or not it cleared the gate. Null when the model gave no usable answer for this field."
        },
        "name": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 1,
          "description": "The OED name of the proposed code, from the OED 5.0.0 lists Craton holds. Null when proposed is null. Never model text."
        },
        "confidence": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0,
          "maximum": 1,
          "description": "The model's confidence in its answer, 0 to 1, four decimal places. Null when proposed is null."
        },
        "cleared": {
          "type": "boolean",
          "description": "True when the confidence is at or above this field's gate and the code is adopted."
        },
        "why": {
          "type": "string",
          "enum": [
            "cleared",
            "below_gate",
            "unknown",
            "unrecognized_answer",
            "no_answer"
          ],
          "description": "cleared: adopted. below_gate: a valid proposal whose confidence is under the gate, left for a person. unknown: the model says the text does not tell (OED's Unknown code, 1000 or 5000), which is always left for a person. unrecognized_answer: the model answered with something that is not one of the keys it was asked with. no_answer: no usable reply for this row."
        },
        "alternatives": {
          "type": "array",
          "maxItems": 2,
          "description": "Up to two other codes the model gave at least a 5% probability, most likely first. Empty when there are none.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "name",
              "probability"
            ],
            "properties": {
              "code": {
                "$ref": "#/$defs/occupancy_code"
              },
              "name": {
                "type": "string",
                "minLength": 1
              },
              "probability": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            }
          }
        }
      }
    },
    "construction_field": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "code",
        "proposed",
        "name",
        "confidence",
        "cleared",
        "why",
        "alternatives"
      ],
      "properties": {
        "code": {
          "oneOf": [
            {
              "$ref": "#/$defs/construction_code"
            },
            {
              "type": "null"
            }
          ],
          "description": "The OED ConstructionCode this field is coded as, or null when it is not coded. Non-null exactly when cleared is true: a proposal below the gate is never written here."
        },
        "proposed": {
          "oneOf": [
            {
              "$ref": "#/$defs/construction_code"
            },
            {
              "type": "null"
            }
          ],
          "description": "The OED code the model's answer maps to, whether or not it cleared the gate. Null when the model gave no usable answer for this field."
        },
        "name": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 1,
          "description": "The OED name of the proposed code, from the OED 5.0.0 lists Craton holds. Null when proposed is null. Never model text."
        },
        "confidence": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0,
          "maximum": 1,
          "description": "The model's confidence in its answer, 0 to 1, four decimal places. Null when proposed is null."
        },
        "cleared": {
          "type": "boolean",
          "description": "True when the confidence is at or above this field's gate and the code is adopted."
        },
        "why": {
          "type": "string",
          "enum": [
            "cleared",
            "below_gate",
            "unknown",
            "unrecognized_answer",
            "no_answer"
          ],
          "description": "cleared: adopted. below_gate: a valid proposal whose confidence is under the gate, left for a person. unknown: the model says the text does not tell (OED's Unknown code, 1000 or 5000), which is always left for a person. unrecognized_answer: the model answered with something that is not one of the keys it was asked with. no_answer: no usable reply for this row."
        },
        "alternatives": {
          "type": "array",
          "maxItems": 2,
          "description": "Up to two other codes the model gave at least a 5% probability, most likely first. Empty when there are none.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "name",
              "probability"
            ],
            "properties": {
              "code": {
                "$ref": "#/$defs/construction_code"
              },
              "name": {
                "type": "string",
                "minLength": 1
              },
              "probability": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            }
          }
        }
      }
    },
    "occupancy_code": {
      "type": "integer",
      "description": "An OED 5.0.0 OccupancyCode.",
      "enum": [
        1000,
        1050,
        1051,
        1052,
        1053,
        1054,
        1055,
        1056,
        1057,
        1058,
        1070,
        1071,
        1072,
        1073,
        1100,
        1101,
        1102,
        1103,
        1104,
        1105,
        1106,
        1107,
        1108,
        1109,
        1110,
        1111,
        1112,
        1113,
        1114,
        1115,
        1116,
        1117,
        1118,
        1119,
        1120,
        1121,
        1122,
        1123,
        1124,
        1125,
        1150,
        1151,
        1152,
        1153,
        1154,
        1155,
        1156,
        1157,
        1158,
        1159,
        1200,
        1201,
        1210,
        1211,
        1212,
        1213,
        1214,
        1215,
        1220,
        1230,
        1231,
        1250,
        1251,
        1252,
        1253,
        1254,
        1255,
        1256,
        1260,
        1300,
        1301,
        1302,
        1303,
        1304,
        1305,
        1350,
        1351,
        1352,
        1353,
        1360,
        1370,
        1400,
        1401,
        1402,
        1403,
        1404,
        1405,
        1406,
        1407,
        1408,
        1409,
        1410,
        1411,
        1412,
        2000,
        2050,
        2051,
        2052,
        2053,
        2054,
        2055,
        2056,
        2057,
        2058,
        2100,
        2101,
        2102,
        2103,
        2104,
        2105,
        2106,
        2107,
        2108,
        2109,
        2110,
        2111,
        2150,
        2151,
        2152,
        2153,
        2154,
        2155,
        2200,
        2201,
        2202,
        2203,
        2204,
        2205,
        2206,
        2207,
        2208,
        2250,
        2251,
        2252,
        2253,
        2300,
        2301,
        2302,
        2303,
        2304,
        2305,
        2306,
        2350,
        2351,
        2352,
        2400,
        2401,
        2402,
        2403,
        2404,
        2450,
        2460,
        2461,
        2470,
        2500,
        2505,
        2510,
        2515,
        2520,
        2521,
        2530,
        2531,
        2541,
        2542,
        2543,
        2550,
        2560,
        2600,
        2610,
        2611,
        2612,
        2613,
        2620,
        2650,
        2651,
        2700,
        2750,
        2760,
        2770,
        2780,
        3000,
        3001,
        3002,
        3003,
        3004,
        3005,
        3006,
        3007,
        3008,
        3009,
        3010,
        3011,
        3012,
        3013,
        3014,
        3015,
        3016,
        3017,
        3018,
        3019,
        3020,
        3021,
        3022,
        3023,
        3024,
        3025,
        3026,
        3027,
        3028,
        3029,
        3030,
        3031,
        3032,
        3033,
        3034,
        3035,
        3036
      ]
    },
    "construction_code": {
      "type": "integer",
      "description": "An OED 5.0.0 ConstructionCode.",
      "enum": [
        5000,
        5050,
        5051,
        5052,
        5053,
        5054,
        5055,
        5056,
        5057,
        5100,
        5101,
        5102,
        5103,
        5104,
        5105,
        5106,
        5107,
        5108,
        5109,
        5110,
        5150,
        5151,
        5152,
        5153,
        5154,
        5155,
        5156,
        5157,
        5158,
        5159,
        5200,
        5201,
        5202,
        5203,
        5204,
        5205,
        5206,
        5207,
        5208,
        5209,
        5251,
        5252,
        5253,
        5254,
        5255,
        5256,
        5257,
        5258,
        5259,
        5260,
        5261,
        5262,
        5263,
        5264,
        5265,
        5301,
        5302,
        5303,
        5304,
        5305,
        5306,
        5309,
        5310,
        5350,
        5351,
        5352,
        5353,
        5354,
        5400,
        5401,
        5402,
        5403,
        5404,
        5405,
        5406,
        5407,
        5408,
        5409,
        5410,
        5451,
        5452,
        5453,
        5454,
        5455,
        5456,
        5457,
        5458,
        5459,
        5460,
        5461,
        5463,
        5464,
        5465,
        5466,
        5467,
        5501,
        5502,
        5503,
        5550,
        5551,
        5552,
        5553,
        5554,
        5555,
        5556,
        5557,
        5600,
        5601,
        5602,
        5603,
        5604,
        5605,
        5606,
        5607,
        5608,
        5609,
        5610,
        5611,
        5612,
        5613,
        5614,
        5650,
        5651,
        5652,
        5653,
        5654,
        5655,
        5656,
        5657,
        5658,
        5659,
        5660,
        5661,
        5662,
        5701,
        5702,
        5703,
        5751,
        5752,
        5753,
        5754,
        5755,
        5756,
        5801,
        5802,
        5803,
        5804,
        5805,
        5806,
        5807,
        5850,
        5851,
        5852,
        5853,
        5854,
        5855,
        5856,
        5857,
        5858,
        5859,
        5860,
        5861,
        5862,
        5863,
        5864,
        5865,
        5866,
        5867,
        5868,
        5869,
        5870,
        5871,
        5872,
        5873,
        5874,
        5875,
        5900,
        5901,
        5902,
        5951,
        5952,
        5953,
        5954,
        5955,
        5956,
        5957,
        5958,
        5959,
        5960,
        5961,
        6000,
        6051,
        6052,
        6053,
        6054,
        6055,
        6056,
        6100,
        6101,
        6102,
        6103,
        6104,
        6105,
        6106,
        6150,
        6151,
        6152,
        6153,
        6154,
        6155,
        6156,
        6200,
        6201,
        6202,
        6203,
        6204,
        6205,
        6206,
        6207,
        6208,
        6209,
        6210,
        6211,
        6212,
        6213,
        6214,
        6215,
        6216,
        6217,
        6218,
        6219,
        7000,
        7001,
        7002,
        7003,
        7004,
        7005,
        7006,
        7007,
        7008,
        7009,
        7010,
        7011,
        7012,
        7013,
        7014,
        7015,
        7016,
        7017,
        7018,
        7019,
        7020,
        7021,
        7022,
        7023,
        7500,
        7501,
        7502,
        7503,
        7504,
        7505,
        7506,
        7507,
        7550,
        8000,
        8010,
        8011,
        8012,
        8013,
        8014,
        8015,
        8016,
        8017,
        8018,
        8019,
        8030,
        8031,
        8032,
        8033,
        8034,
        8035,
        8050,
        8051,
        8052,
        8053,
        8054,
        8055,
        8056,
        8057,
        8070,
        8071,
        8072,
        8073,
        8090,
        8091,
        8092,
        8093,
        8094,
        8100,
        8101,
        8102,
        8103,
        8110,
        8111,
        8112,
        8113,
        8114,
        8115,
        8116,
        8117,
        8118,
        8119,
        8120,
        8130,
        8131,
        8132,
        8133,
        8134,
        8140,
        8141,
        8142,
        8143,
        8144,
        8145,
        8146,
        8300,
        8301,
        8302,
        8303,
        8304,
        8305,
        8306,
        8307,
        8308,
        8309,
        8310,
        8311,
        8312,
        8313,
        8314,
        8315,
        8316,
        8317,
        8318,
        8319,
        8320,
        8321,
        8322,
        8323,
        8324,
        8325,
        8326,
        8327,
        8400,
        8401,
        8402,
        8403,
        8404,
        8405,
        8406,
        8407,
        8408,
        8409,
        8410,
        8411,
        8412,
        8413,
        8414,
        8415,
        8416,
        8417,
        8418,
        8419,
        8420,
        8421,
        8422
      ]
    }
  }
}
