{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://yarrasys.dev/schema/yarramate-operations.schema.json",
  "title": "YarraMate operations document",
  "description": "An atomic batch of validated writes: every operation applies or none does. Documents are addressed by their workspace-manifest paths.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "format",
    "operations"
  ],
  "properties": {
    "format": {
      "const": "yarramate/operations/v1"
    },
    "operations": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/operation"
      }
    }
  },
  "$defs": {
    "nonEmptyText": {
      "type": "string",
      "pattern": "\\S"
    },
    "identifiedReference": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "ref"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "ref": {
          "$ref": "#/$defs/nonEmptyText"
        }
      }
    },
    "constraintReference": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "ref"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "ref": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "expects": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "provider",
            "key",
            "value"
          ],
          "properties": {
            "provider": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
            },
            "key": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]*$"
            },
            "value": {
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        }
      }
    },
    "attestation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "topic",
        "by",
        "on",
        "recordedBy"
      ],
      "properties": {
        "topic": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]*$"
        },
        "by": {
          "description": "The authority whose judgment this records, resolved against the model as a subject reference once the write lands.",
          "$ref": "#/$defs/nonEmptyText"
        },
        "recordedBy": {
          "description": "Who is writing this record. Required here and optional in a document: a hand-written sign-off already has a committer, whereas a batch names the agent holding the pen, so an authority's judgment and a machine's transcription of one are never the same bytes.",
          "$ref": "#/$defs/nonEmptyText"
        },
        "on": {
          "type": "string",
          "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
        }
      }
    },
    "conceptFields": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "kind": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "name": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "description": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "aka": {
          "description": "Alternative labels appended to the subject's aka list.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/nonEmptyText"
          }
        },
        "status": {
          "enum": [
            "planned",
            "current",
            "retired"
          ]
        },
        "owner": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "distinctFrom": {
          "description": "Subjects this one has been judged genuinely different from, dismissing a near-duplicate question permanently (ADR 0077).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/nonEmptyText"
          }
        },
        "supersedes": {
          "description": "Subjects whose responsibility this one took over, recording a rename, split, or merge (ADR 0080).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/nonEmptyText"
          }
        },
        "constraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/constraintReference"
          }
        },
        "references": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/identifiedReference"
          }
        },
        "presentIn": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/nonEmptyText"
          }
        },
        "parts": {
          "description": "The subjects this instance binds into its pattern's slots (ADR 0123), keyed by part name. Without this an agent cannot instantiate a pattern over apply at all, so the whole pattern mechanism was reachable only from raw YAML (#448). On update-concept it MERGES by slot: a named slot rebinds, an unnamed one is untouched, because a write enriches what is there and never silently shrinks it (ADR 0062). Retraction is coarse: remove: [\"parts\"] unbinds the whole map, and there is no slot-granular spelling.",
          "type": "object",
          "minProperties": 1,
          "additionalProperties": false,
          "patternProperties": {
            "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$": {
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        },
        "attestations": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/attestation"
          }
        }
      }
    },
    "relationshipFields": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "kind": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "from": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "to": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "name": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "description": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "status": {
          "enum": [
            "planned",
            "current",
            "retired"
          ]
        },
        "mode": {
          "enum": [
            "read",
            "write",
            "read-write",
            "unspecified"
          ]
        },
        "content": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "references": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/identifiedReference"
          }
        },
        "presentIn": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/nonEmptyText"
          }
        }
      }
    },
    "subjectTarget": {
      "description": "A subject already declared in the named document, addressed by its local id. Deletes and renames name what is there rather than restating it.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/nonEmptyText"
        }
      }
    },
    "observationTarget": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "subject": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "claim": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "key": {
          "$ref": "#/$defs/nonEmptyText"
        }
      },
      "oneOf": [
        {
          "required": ["subject"],
          "not": {
            "required": ["claim"]
          }
        },
        {
          "required": ["claim"],
          "not": {
            "required": ["subject"]
          }
        }
      ]
    },
    "observationFields": {
      "type": "object",
      "additionalProperties": false,
      "dependentRequired": {
        "key": ["value"],
        "value": ["key"]
      },
      "properties": {
        "subject": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "claim": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "key": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "value": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "result": {
          "enum": ["confirmed", "contradicted", "unknown", "not-observed"]
        },
        "evidence": {
          "type": "object",
          "additionalProperties": false,
          "required": ["uri"],
          "properties": {
            "uri": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "message": {
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        }
      },
      "oneOf": [
        {
          "required": ["subject"],
          "not": {
            "required": ["claim"]
          }
        },
        {
          "required": ["claim"],
          "not": {
            "required": ["subject"]
          }
        }
      ]
    },
    "observationChange": {
      "type": "object",
      "additionalProperties": false,
      "dependentRequired": {
        "value": ["key"]
      },
      "properties": {
        "subject": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "claim": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "key": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "value": {
          "$ref": "#/$defs/nonEmptyText"
        },
        "result": {
          "enum": ["confirmed", "contradicted", "unknown", "not-observed"]
        },
        "evidence": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "uri": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "message": {
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        }
      },
      "oneOf": [
        {
          "required": ["subject"],
          "not": {
            "required": ["claim"]
          }
        },
        {
          "required": ["claim"],
          "not": {
            "required": ["subject"]
          }
        }
      ]
    },
    "operation": {
      "description": "A discriminated union: the `op` tag selects exactly one branch, so a malformed operation is reported against the shape its author named rather than against all nine.",
      "type": "object",
      "required": [
        "op"
      ],
      "discriminator": {
        "propertyName": "op"
      },
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "concept"
          ],
          "properties": {
            "op": {
              "const": "add-concept"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "concept": {
              "allOf": [
                {
                  "$ref": "#/$defs/conceptFields"
                },
                {
                  "required": [
                    "id",
                    "kind",
                    "name"
                  ],
                  "type": "object"
                }
              ]
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "relationship"
          ],
          "properties": {
            "op": {
              "const": "add-relationship"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "relationship": {
              "allOf": [
                {
                  "$ref": "#/$defs/relationshipFields"
                },
                {
                  "required": [
                    "id",
                    "kind",
                    "from",
                    "to"
                  ],
                  "type": "object"
                }
              ]
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "concept"
          ],
          "properties": {
            "op": {
              "const": "update-concept"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "concept": {
              "allOf": [
                {
                  "$ref": "#/$defs/conceptFields"
                },
                {
                  "required": [
                    "id"
                  ],
                  "type": "object"
                }
              ]
            },
            "remove": {
              "type": "array",
              "minItems": 1,
              "items": {
                "enum": [
                  "name",
                  "description",
                  "aka",
                  "status",
                  "owner",
                  "distinctFrom",
                  "supersedes",
                  "constraints",
                  "references",
                  "presentIn",
                  "parts",
                  "attestations"
                ]
              }
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "relationship"
          ],
          "properties": {
            "op": {
              "const": "update-relationship"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "relationship": {
              "allOf": [
                {
                  "$ref": "#/$defs/relationshipFields"
                },
                {
                  "required": [
                    "id"
                  ],
                  "type": "object"
                }
              ]
            },
            "remove": {
              "type": "array",
              "minItems": 1,
              "items": {
                "enum": [
                  "name",
                  "description",
                  "status",
                  "mode",
                  "content",
                  "references",
                  "presentIn"
                ]
              }
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "concept"
          ],
          "properties": {
            "op": {
              "const": "delete-concept"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "concept": {
              "$ref": "#/$defs/subjectTarget"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "relationship"
          ],
          "properties": {
            "op": {
              "const": "delete-relationship"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "relationship": {
              "$ref": "#/$defs/subjectTarget"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "concept",
            "to"
          ],
          "properties": {
            "op": {
              "const": "rename-concept"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "concept": {
              "$ref": "#/$defs/subjectTarget"
            },
            "to": {
              "description": "The local id this subject should have had. Every declarative reference to the old address is re-pointed in the same batch; prose is left exactly as written.",
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "op",
            "document",
            "relationship",
            "to"
          ],
          "properties": {
            "op": {
              "const": "rename-relationship"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "relationship": {
              "$ref": "#/$defs/subjectTarget"
            },
            "to": {
              "description": "The local id this subject should have had. Sits at the operation level because a relationship's own `to` field is an endpoint.",
              "$ref": "#/$defs/nonEmptyText"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["op", "document", "observation"],
          "properties": {
            "op": {
              "const": "add-observation"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "observation": {
              "allOf": [
                {
                  "$ref": "#/$defs/observationFields"
                },
                {
                  "type": "object",
                  "required": ["result", "evidence"]
                }
              ]
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["op", "document", "observation"],
          "properties": {
            "op": {
              "const": "update-observation"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "observation": {
              "$ref": "#/$defs/observationChange"
            },
            "remove": {
              "type": "array",
              "minItems": 1,
              "items": {
                "enum": ["message"]
              }
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["op", "document", "observation"],
          "properties": {
            "op": {
              "const": "delete-observation"
            },
            "document": {
              "$ref": "#/$defs/nonEmptyText"
            },
            "observation": {
              "$ref": "#/$defs/observationTarget"
            }
          }
        }
      ]
    }
  }
}
