{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://commonworld.net/contracts/commonworld/project.schema.json",
  "title": "CommonProject",
  "description": "Canonical public Commons record for the Commonworld globe. Presence is expressed on two independent axes (geographic and digital) inside presence; visual projections are derived from this record and are not catalog truth.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "id",
    "title",
    "summary",
    "themes",
    "actions",
    "presence",
    "activity",
    "provenance",
    "curation",
    "links"
  ],
  "properties": {
    "schema_version": {
      "type": "integer",
      "const": 4
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{2,95}$"
    },
    "title": {
      "type": "string",
      "minLength": 2,
      "maxLength": 140
    },
    "summary": {
      "type": "string",
      "minLength": 20,
      "maxLength": 500
    },
    "themes": {
      "type": "array",
      "minItems": 1,
      "maxItems": 8,
      "uniqueItems": true,
      "items": {
        "type": "string",
        "pattern": "^[a-z][a-z0-9-]{1,63}$"
      }
    },
    "actions": {
      "type": "array",
      "minItems": 1,
      "uniqueItems": true,
      "items": {
        "type": "string",
        "enum": [
          "visit",
          "use",
          "borrow",
          "learn",
          "contribute",
          "volunteer",
          "donate",
          "contact",
          "replicate"
        ]
      }
    },
    "presence": {
      "$ref": "#/$defs/presence"
    },
    "relations": {
      "type": "array",
      "uniqueItems": true,
      "items": {
        "$ref": "#/$defs/relation"
      }
    },
    "provenance": {
      "$ref": "#/$defs/provenance"
    },
    "curation": {
      "$ref": "#/$defs/curation"
    },
    "links": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/link"
      }
    },
    "handoff": {
      "$ref": "#/$defs/handoff"
    },
    "activity": {
      "$ref": "#/$defs/activity"
    },
    "languages": {
      "$ref": "#/$defs/languageEvidence"
    },
    "access": {
      "$ref": "#/$defs/accessEvidence"
    }
  },
  "$defs": {
    "presence": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "geographic",
        "digital"
      ],
      "properties": {
        "geographic": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/geographicLocation"
          }
        },
        "digital": {
          "$ref": "#/$defs/digitalPresence"
        }
      },
      "anyOf": [
        {
          "properties": {
            "geographic": {
              "minItems": 1
            }
          }
        },
        {
          "properties": {
            "digital": {
              "properties": {
                "available": {
                  "const": true
                }
              }
            }
          }
        }
      ]
    },
    "publicGeometry": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "type",
            "coordinates"
          ],
          "properties": {
            "type": {
              "const": "Point"
            },
            "coordinates": {
              "$ref": "#/$defs/position"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "type",
            "coordinates"
          ],
          "properties": {
            "type": {
              "const": "Polygon"
            },
            "coordinates": {
              "$ref": "#/$defs/polygonCoordinates"
            },
            "bbox": {
              "$ref": "#/$defs/publicExtentBbox"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "type",
            "coordinates"
          ],
          "properties": {
            "type": {
              "const": "MultiPolygon"
            },
            "coordinates": {
              "$ref": "#/$defs/multiPolygonCoordinates"
            },
            "bbox": {
              "$ref": "#/$defs/publicExtentBbox"
            }
          }
        }
      ]
    },
    "digitalPresence": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "available",
        "source_ids"
      ],
      "properties": {
        "available": {
          "type": "boolean"
        },
        "reach": {
          "type": "string",
          "enum": [
            "global",
            "regional",
            "network",
            "restricted"
          ]
        },
        "label": {
          "type": "string",
          "minLength": 2,
          "maxLength": 180
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "available": {
                "const": true
              }
            },
            "required": [
              "available"
            ]
          },
          "then": {
            "required": [
              "reach",
              "label"
            ]
          },
          "else": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "reach"
                  ]
                },
                {
                  "required": [
                    "label"
                  ]
                }
              ]
            }
          }
        }
      ]
    },
    "relation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "target_id",
        "type",
        "source_ids"
      ],
      "properties": {
        "target_id": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]{2,95}$"
        },
        "type": {
          "type": "string",
          "enum": [
            "part-of",
            "operated-by",
            "chapter-of",
            "cooperates-with",
            "depends-on",
            "local-digital-link"
          ]
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        },
        "note": {
          "type": "string",
          "maxLength": 500
        }
      }
    },
    "provenance": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "sources"
      ],
      "properties": {
        "sources": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/source"
          }
        },
        "uncertainty_note": {
          "type": "string",
          "maxLength": 500
        }
      }
    },
    "source": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "label",
        "url",
        "retrieved_at"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]{2,95}$"
        },
        "type": {
          "type": "string",
          "enum": [
            "official-source",
            "public-registry",
            "manual-curation",
            "derived"
          ]
        },
        "label": {
          "type": "string",
          "minLength": 2,
          "maxLength": 180
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "retrieved_at": {
          "type": "string",
          "format": "date"
        },
        "note": {
          "type": "string",
          "maxLength": 500
        }
      }
    },
    "curation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "state",
        "reviewed_at",
        "next_review_at"
      ],
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "candidate",
            "listed",
            "verified",
            "featured",
            "stale",
            "archived"
          ]
        },
        "catalogued_at": {
          "type": "string",
          "format": "date",
          "description": "Stable date on which the record first entered the public Commonworld catalogue. Later reviews must not change it."
        },
        "reviewed_by": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "reviewed_at": {
          "type": "string",
          "format": "date"
        },
        "next_review_at": {
          "type": "string",
          "format": "date"
        },
        "notes": {
          "type": "string",
          "maxLength": 500
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "state": {
                "enum": [
                  "listed",
                  "verified",
                  "featured",
                  "stale",
                  "archived"
                ]
              }
            },
            "required": [
              "state"
            ]
          },
          "then": {
            "required": [
              "reviewed_by"
            ]
          }
        }
      ]
    },
    "link": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "label",
        "url"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "homepage",
            "visit",
            "use",
            "borrow",
            "learn",
            "contribute",
            "volunteer",
            "donate",
            "contact",
            "replicate",
            "documentation",
            "license",
            "source"
          ]
        },
        "label": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        }
      }
    },
    "handoff": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "enabled"
      ],
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "system": {
          "type": "string",
          "const": "weltgewebe"
        },
        "project_id": {
          "type": "string",
          "minLength": 3,
          "maxLength": 160
        },
        "label": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "url": {
          "type": "string",
          "format": "uri"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "enabled": {
                "const": true
              }
            },
            "required": [
              "enabled"
            ]
          },
          "then": {
            "required": [
              "system",
              "project_id",
              "label",
              "url"
            ]
          },
          "else": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "system"
                  ]
                },
                {
                  "required": [
                    "project_id"
                  ]
                },
                {
                  "required": [
                    "label"
                  ]
                },
                {
                  "required": [
                    "url"
                  ]
                }
              ]
            }
          }
        }
      ]
    },
    "geographicLocation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "mode",
        "label",
        "source_ids"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]{2,95}$"
        },
        "mode": {
          "type": "string",
          "enum": [
            "exact",
            "approximate",
            "hidden"
          ]
        },
        "label": {
          "type": "string",
          "minLength": 2,
          "maxLength": 180
        },
        "geometry": {
          "$ref": "#/$defs/publicGeometry"
        },
        "uncertainty_meters_min": {
          "type": "integer",
          "minimum": 1
        },
        "privacy_note": {
          "type": "string",
          "minLength": 10,
          "maxLength": 500
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "mode": {
                "const": "exact"
              }
            },
            "required": [
              "mode"
            ]
          },
          "then": {
            "required": [
              "geometry"
            ],
            "not": {
              "required": [
                "uncertainty_meters_min"
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "mode": {
                "const": "approximate"
              }
            },
            "required": [
              "mode"
            ]
          },
          "then": {
            "required": [
              "geometry",
              "uncertainty_meters_min"
            ],
            "properties": {
              "geometry": {
                "properties": {
                  "type": {
                    "const": "Point"
                  }
                }
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "mode": {
                "const": "hidden"
              }
            },
            "required": [
              "mode"
            ]
          },
          "then": {
            "required": [
              "privacy_note"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "geometry"
                  ]
                },
                {
                  "required": [
                    "uncertainty_meters_min"
                  ]
                }
              ]
            }
          }
        }
      ]
    },
    "position": {
      "type": "array",
      "prefixItems": [
        {
          "type": "number",
          "minimum": -180,
          "maximum": 180
        },
        {
          "type": "number",
          "minimum": -90,
          "maximum": 90
        }
      ],
      "minItems": 2,
      "maxItems": 2
    },
    "publicExtentBbox": {
      "description": "Optional explicit focus extent for a public Polygon or MultiPolygon, ordered [west, south, east, north]. west > east explicitly denotes the short antimeridian-crossing interval; west <= east denotes the unwrapped interval, including an intended span greater than 180 degrees. The box must contain every geometry vertex. If bbox is absent, consumers preserve the legacy numeric min/max-longitude interpretation and must not infer wrapping from vertices alone.",
      "type": "array",
      "prefixItems": [
        {
          "type": "number",
          "minimum": -180,
          "maximum": 180
        },
        {
          "type": "number",
          "minimum": -90,
          "maximum": 90
        },
        {
          "type": "number",
          "minimum": -180,
          "maximum": 180
        },
        {
          "type": "number",
          "minimum": -90,
          "maximum": 90
        }
      ],
      "minItems": 4,
      "maxItems": 4
    },
    "linearRing": {
      "type": "array",
      "minItems": 4,
      "items": {
        "$ref": "#/$defs/position"
      }
    },
    "polygonCoordinates": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/linearRing"
      }
    },
    "multiPolygonCoordinates": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/polygonCoordinates"
      }
    },
    "activity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "status",
        "observed_at",
        "source_ids"
      ],
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "active",
            "paused",
            "seasonal",
            "unknown",
            "ended"
          ]
        },
        "observed_at": {
          "type": "string",
          "format": "date"
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        },
        "note": {
          "type": "string",
          "maxLength": 500
        }
      }
    },
    "languageEvidence": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "codes",
        "source_ids"
      ],
      "properties": {
        "codes": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z]{2,3}(?:-[A-Z]{2})?$"
          }
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        },
        "note": {
          "type": "string",
          "minLength": 4,
          "maxLength": 240
        }
      }
    },
    "accessEvidence": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "source_ids"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "public",
            "membership",
            "restricted"
          ]
        },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,95}$"
          }
        },
        "note": {
          "type": "string",
          "minLength": 4,
          "maxLength": 240
        }
      }
    }
  }
}
