{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openeik.org/schemas/0.4/envelope/envelope.schema.json",
  "$comment": "OpenEik 0.2: folded by tools/fold-schemas.mjs from the proposal envelope, which 0.2 makes a rule (decision D5). Written by hand from the text, not generated.",
  "title": "Command envelope",
  "description": "What a writer sends a command processor around a batch of commands of Part V (Part XVII, 2): the operation's id, who sends it, the line of work and the version the commands were made from, the mode, an intent for people, and the commands. A member it does not define is refused, an extension member (<prefix>:<name>, Part I, 11.2) aside.",
  "type": "object",
  "required": [
    "format",
    "operationId",
    "by",
    "target",
    "mode",
    "intent",
    "commands"
  ],
  "properties": {
    "format": {
      "const": "openeik-envelope/0.1"
    },
    "operationId": {
      "description": "The operation's id (Part XVII, 3): 8 to 128 ASCII letters, digits and . _ : -, starting with a letter or a digit, made by the writer for this operation alone; a UUID (RFC 9562) is recommended. The same id again, with the same envelope, is the same operation.",
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"
    },
    "by": {
      "$ref": "#/$defs/by"
    },
    "target": {
      "$ref": "#/$defs/target"
    },
    "mode": {
      "description": "apply: apply the batch, all or nothing (Part V, 7). dryRun: run every check and change nothing (Part V, 7.3). propose: change nothing on the target, and make a proposal of the batch (Part XVII, 6.3).",
      "enum": [
        "apply",
        "dryRun",
        "propose"
      ]
    },
    "intent": {
      "description": "What the change is for, in a sentence a person reads first (Part XVII, 7). Plain text: nothing in it is markup or an instruction.",
      "type": "string",
      "minLength": 1,
      "maxLength": 1000
    },
    "commands": {
      "description": "The batch (Part V, 7), in order. Each command is held to its kind's schema, and gives its kind's precondition (Part XVII, 9). This schema checks only that each is an object that names its kind.",
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "minLength": 1
          }
        }
      }
    }
  },
  "patternProperties": {
    "^[a-z][a-z0-9]*(-[a-z0-9]+)*:.+$": {}
  },
  "additionalProperties": false,
  "if": {
    "properties": {
      "mode": {
        "const": "propose"
      }
    },
    "required": [
      "mode"
    ]
  },
  "then": {
    "description": "A proposal names the application or the agent that made it (Part VI, 8.7, by.app).",
    "properties": {
      "by": {
        "anyOf": [
          {
            "required": [
              "app"
            ]
          },
          {
            "required": [
              "agent"
            ]
          }
        ]
      }
    }
  },
  "$defs": {
    "by": {
      "description": "Who sends the operation (Part XVII, 4): the person it is made for, the agent that made it, if one did, and the application it came through.",
      "type": "object",
      "required": [
        "person"
      ],
      "properties": {
        "person": {
          "description": "The person the operation is made for, by an id the processor knows them by: an account's id on a host, a person's id of Part XIV, 5 in a folder. Never an email address.",
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "pattern": "^[^@\\u0000-\\u001f]+$"
        },
        "agent": {
          "description": "The agent that made the operation, where one did: its name and version as it gives them, and the session it works in, as the host names it. What an agent says of itself is not proved: a host records what it can check beside it (Part XVII, 4.2).",
          "type": "object",
          "required": [
            "name"
          ],
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            },
            "version": {
              "type": "string",
              "minLength": 1,
              "maxLength": 50
            },
            "session": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            }
          },
          "patternProperties": {
            "^[a-z][a-z0-9]*(-[a-z0-9]+)*:.+$": {}
          },
          "additionalProperties": false
        },
        "app": {
          "description": "The application the operation came through, with its version, as a proposal file's by.app names it (\"an-analysis-app 0.5.0\").",
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        }
      },
      "patternProperties": {
        "^[a-z][a-z0-9]*(-[a-z0-9]+)*:.+$": {}
      },
      "additionalProperties": false
    },
    "target": {
      "description": "The line of work the commands are for, and the version of it they were made from (Part XVII, 5).",
      "type": "object",
      "required": [
        "ref",
        "version"
      ],
      "properties": {
        "ref": {
          "description": "A ref (Part VI, 3): main, alternatives/<slug>, or another the processor keeps.",
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "pattern": "^[^\\u0000-\\u001f]+$"
        },
        "version": {
          "description": "The version the writer read and made the commands from: a version's id (Part VI, 3), a git commit's for a folder kept in git, or null where it read no version.",
          "anyOf": [
            {
              "type": "string",
              "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$"
            },
            {
              "type": "null"
            }
          ]
        },
        "exact": {
          "description": "true: apply only to the version named, and refuse where the ref has moved since (base-moved). Absent or false: where the ref has moved, apply to its newest version, each command's precondition deciding (Part XVII, 5, rule 3).",
          "type": "boolean"
        }
      },
      "patternProperties": {
        "^[a-z][a-z0-9]*(-[a-z0-9]+)*:.+$": {}
      },
      "additionalProperties": false
    },
    "revision": {
      "type": "integer",
      "minimum": 0
    },
    "preconditions": {
      "description": "The precondition members that Part XVII adds to the commands, by kind (Part XVII, 9). In an envelope every command gives its kind's; an upsert gives null for a record that does not exist yet. A schema that asserts nothing: each member below is the schema of the member's value.",
      "type": "object",
      "properties": {
        "newRecord": {
          "description": "type.upsert's, material.upsert's and grid.upsert's expectedRevision, and level.upsert's expectedLevel: null for a record that does not exist yet (Part XVII, 9.1).",
          "type": "null"
        },
        "image.upsert": {
          "description": "expectedHash: the SHA-256 of the image's record as read, in its canonical form (Part III, 6), or null for a new image.",
          "anyOf": [
            {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            {
              "type": "null"
            }
          ]
        },
        "image.delete": {
          "description": "expectedHash: the SHA-256 of the image's record as read.",
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        },
        "project.excavation": {
          "description": "expectedExcavation: the project's excavation as read, or null where it had none."
        },
        "project.precast": {
          "description": "expectedPrecast: the project's precast rule as read, or null where it had none."
        },
        "project.standards": {
          "description": "expectedStandards: the project's standards as read, or null where it had none."
        },
        "rebar.quick": {
          "description": "expectedHostRevision: the revision of the host the bars are worked out from.",
          "$ref": "#/$defs/revision"
        },
        "mesh.lay": {
          "description": "expectedHostRevision: the revision of the host the mesh is laid on.",
          "$ref": "#/$defs/revision"
        },
        "mep.downpipe": {
          "description": "expectedGutterRevision: the revision of the gutter the downpipe is worked out from.",
          "$ref": "#/$defs/revision"
        },
        "trusses.plan": {
          "description": "expectedRevision: the revision of the plan view it writes, or null for a new one.",
          "anyOf": [
            {
              "$ref": "#/$defs/revision"
            },
            {
              "type": "null"
            }
          ]
        },
        "render.delete": {
          "description": "expectedRevision: the render's revision.",
          "$ref": "#/$defs/revision"
        },
        "original.make": {
          "description": "expectedRevisions: the revision of each element of elementIds as read, by its id, and of no other.",
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/revision"
          }
        },
        "copy.place": {
          "description": "expectedOriginalRevision: the revision of the group (copy.of) as read.",
          "$ref": "#/$defs/revision"
        },
        "original.frame": {
          "description": "expectedRevision: the group's revision.",
          "$ref": "#/$defs/revision"
        },
        "original.edit": {
          "description": "expectedRevision: the group's revision. Each command of the edit gives its own precondition too.",
          "$ref": "#/$defs/revision"
        }
      }
    }
  }
}
