{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://diditwork.org/schema/outcome_record.schema.json",
  "title": "Better World outcome ledger record (\"Did it work?\") v0",
  "description": "One append-only record in data/ledger/records.json. A claim_snapshot freezes what a named evaluator published (a MODELED estimate, not a guaranteed outcome) and schedules a follow-up check. A follow_up_result records what a later cited evaluator source said about that snapshot. Records are hash-chained; never edit a record in place, append a new one.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "record_id",
    "seq",
    "record_kind",
    "recorded_on",
    "org_id",
    "evaluator",
    "evidence",
    "honesty",
    "provenance"
  ],
  "properties": {
    "schema_version": {
      "const": "outcome_record/v0"
    },
    "record_id": {
      "type": "string",
      "pattern": "^ldg_[a-z0-9_]+$",
      "maxLength": 96
    },
    "seq": {
      "type": "integer",
      "minimum": 0
    },
    "record_kind": {
      "enum": [
        "claim_snapshot",
        "follow_up_result"
      ]
    },
    "recorded_on": {
      "type": "string",
      "format": "date",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
    },
    "org_id": {
      "type": "string",
      "pattern": "^org_[a-z0-9_]+$"
    },
    "intervention_ids": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^int_[a-z0-9_]+$"
      },
      "uniqueItems": true
    },
    "evaluator": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name"
      ],
      "properties": {
        "id": {
          "enum": [
            "givewell",
            "hli",
            "ace",
            "who",
            "ihme"
          ]
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120
        }
      }
    },
    "claim": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "claim_id",
        "evaluation_id",
        "label",
        "value",
        "unit",
        "period",
        "verified",
        "modeled",
        "guaranteed"
      ],
      "properties": {
        "claim_id": {
          "type": "string",
          "pattern": "^[a-z0-9_]+$"
        },
        "evaluation_id": {
          "type": "string",
          "pattern": "^eval_[a-z0-9_]+$"
        },
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "value": {
          "type": [
            "number",
            "null"
          ]
        },
        "unit": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_]+$",
          "maxLength": 80
        },
        "period": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 120
        },
        "verified": {
          "type": "boolean"
        },
        "modeled": {
          "const": true
        },
        "guaranteed": {
          "const": false
        }
      }
    },
    "subject_record_id": {
      "type": "string",
      "pattern": "^ldg_[a-z0-9_]+$"
    },
    "supersedes": {
      "type": "string",
      "pattern": "^ldg_[a-z0-9_]+$"
    },
    "evidence": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "accessed_on"
        ],
        "properties": {
          "url": {
            "type": "string",
            "pattern": "^https://",
            "maxLength": 500
          },
          "accessed_on": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "note": {
            "type": "string",
            "maxLength": 300
          }
        }
      }
    },
    "follow_up": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "check_after",
        "status"
      ],
      "properties": {
        "check_after": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        },
        "status": {
          "enum": [
            "pending",
            "confirmed",
            "contradicted",
            "unverifiable"
          ]
        },
        "checked_on": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        },
        "notes": {
          "type": [
            "string",
            "null"
          ],
          "maxLength": 500
        }
      }
    },
    "seeded": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "seed_kind",
        "seeded_from",
        "source_claim_sha256"
      ],
      "properties": {
        "seed_kind": {
          "const": "existing_evaluator_citation"
        },
        "seeded_from": {
          "const": "data/claims_index.json"
        },
        "source_claim_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        }
      }
    },
    "honesty": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "modeled",
        "guaranteed",
        "statement"
      ],
      "properties": {
        "modeled": {
          "const": true
        },
        "guaranteed": {
          "const": false
        },
        "statement": {
          "type": "string",
          "minLength": 10,
          "maxLength": 400
        }
      }
    },
    "provenance": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "prev_record_sha256",
        "record_sha256"
      ],
      "properties": {
        "prev_record_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        },
        "record_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "record_kind": {
            "const": "claim_snapshot"
          }
        }
      },
      "then": {
        "required": [
          "claim",
          "follow_up"
        ],
        "not": {
          "required": [
            "subject_record_id"
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "record_kind": {
            "const": "follow_up_result"
          }
        }
      },
      "then": {
        "required": [
          "subject_record_id",
          "follow_up"
        ],
        "properties": {
          "follow_up": {
            "required": [
              "checked_on",
              "notes"
            ],
            "properties": {
              "status": {
                "enum": [
                  "confirmed",
                  "contradicted",
                  "unverifiable"
                ]
              },
              "checked_on": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  ]
}
