{
  "$comment": "Canonical definition of the DrawSQL schema patch format. Used by: PHP tool schema (ModifySchema.php), AI system prompt (DiagramAssistant.php), JS validation (schema-patch.js), and external AI documentation.",
  "properties": {
    "strategy": {
      "type": "string",
      "description": "Patch strategy: \"merge\" (add/update/delete) or \"additive\" (add only)",
      "enum": ["merge", "additive"]
    },
    "tables": {
      "type": "array",
      "description": "Tables to add or update",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "required": true, "description": "Table name" },
          "old_name": { "type": "string", "description": "Previous name when renaming a table" },
          "comment": { "type": "string", "description": "Table comment/description" },
          "parent_uuid": { "type": "string", "description": "UUID of an existing group to nest this table inside" },
          "parent_name": { "type": "string", "description": "Name of a group (existing or in this patch) to nest this table inside" },
          "left": { "type": "integer", "description": "Canvas x-coordinate in pixels. Use the value returned by PlaceNear; omit otherwise." },
          "top": { "type": "integer", "description": "Canvas y-coordinate in pixels. Use the value returned by PlaceNear; omit otherwise." },
          "columns": {
            "type": "array",
            "description": "Columns to add or update on this table",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string", "required": true, "description": "Column name" },
                "type": { "type": "string", "required": true, "description": "Must be an exact data type from the available list" },
                "length": { "type": "integer", "description": "Type length (e.g. varchar(255))" },
                "is_primary_key": { "type": "boolean", "description": "Whether this is a primary key column" },
                "is_auto_increment": { "type": "boolean", "description": "Whether this column auto-increments" },
                "is_nullable": { "type": "boolean", "description": "Whether this column allows NULL values" },
                "is_unique_key": { "type": "boolean", "description": "Whether this column has a unique constraint" },
                "is_index": { "type": "boolean", "description": "Whether this column has a single-column index" },
                "is_unsigned": { "type": "boolean", "description": "Whether this numeric column is unsigned" },
                "default": { "type": "string", "description": "Default value for the column" },
                "enum_values": { "type": "array", "description": "Allowed values for enum/set columns", "items": { "type": "string" } }
              }
            }
          },
          "indexes": {
            "type": "array",
            "description": "Multi-column indexes and keys only. Use type 'index' for a composite index, 'unique' for a composite unique key, or 'primary' for a composite primary key. For single-column indexes and keys, use the column flags instead.",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string", "required": true, "description": "Index name" },
                "type": { "type": "string", "required": true, "description": "Index type", "enum": ["primary", "unique", "index"] },
                "columns": { "type": "array", "required": true, "description": "Column names in the index", "items": { "type": "string" } }
              }
            }
          }
        }
      }
    },
    "relationships": {
      "type": "array",
      "description": "Relationships to add or update",
      "items": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "required": true, "description": "Relationship type", "enum": ["one-to-one", "one-to-many", "many-to-one"] },
          "source_table": { "type": "string", "required": true, "description": "Source table name" },
          "source_column": { "type": "string", "required": true, "description": "Source column name" },
          "target_table": { "type": "string", "required": true, "description": "Target table name" },
          "target_column": { "type": "string", "required": true, "description": "Target column name" }
        }
      }
    },
    "groups": {
      "type": "array",
      "description": "Groups to add or update. Each table/sticky_note that belongs to a group MUST set parent_name to the group's name.",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "required": true, "description": "Group name" }
        }
      }
    },
    "sticky_notes": {
      "type": "array",
      "description": "Sticky notes to add or update",
      "items": {
        "type": "object",
        "properties": {
          "content": { "type": "string", "required": true, "description": "Sticky note content" },
          "parent_uuid": { "type": "string", "description": "UUID of an existing group to nest this sticky note inside" },
          "parent_name": { "type": "string", "description": "Name of a group (existing or in this patch) to nest this sticky note inside" }
        }
      }
    },
    "deletions": {
      "type": "object",
      "description": "Entities to delete (merge strategy only)",
      "properties": {
        "tables": { "type": "array", "description": "Table names to delete", "items": { "type": "string" } },
        "columns": {
          "type": "array",
          "description": "Columns to delete, grouped by table. 'table' accepts a table name or table UUID string.",
          "items": {
            "type": "object",
            "properties": {
              "table": { "type": "string", "required": true, "description": "Table name or UUID string" },
              "columns": {
                "type": "array",
                "required": true,
                "description": "Column names to delete from that table",
                "items": { "type": "string" }
              }
            }
          }
        },
        "indexes": {
          "type": "array",
          "description": "Indexes to delete, grouped by table. 'table' accepts a table name or table UUID string.",
          "items": {
            "type": "object",
            "properties": {
              "table": { "type": "string", "required": true, "description": "Table name or UUID string" },
              "indexes": {
                "type": "array",
                "required": true,
                "description": "Index names to delete from that table",
                "items": { "type": "string" }
              }
            }
          }
        },
        "relationships": {
          "type": "array",
          "description": "Relationships to delete (identified by endpoints)",
          "items": {
            "type": "object",
            "properties": {
              "source_table": { "type": "string", "required": true },
              "source_column": { "type": "string", "required": true },
              "target_table": { "type": "string", "required": true },
              "target_column": { "type": "string", "required": true }
            }
          }
        },
        "groups": {
          "type": "array",
          "description": "Groups to delete. Children (tables/sticky notes) are kept by default. Set delete_children: true to also delete all children.",
          "items": {
            "type": "object",
            "properties": {
              "name": { "type": "string", "required": true, "description": "Group name" },
              "delete_children": { "type": "boolean", "description": "If true, also delete all tables and sticky notes inside the group. Default false (children become ungrouped)." }
            }
          }
        },
        "sticky_notes": { "type": "array", "description": "Sticky note UUIDs to delete", "items": { "type": "string" } }
      }
    }
  },
  "examples": {
    "add_table": {
      "description": "Add table",
      "patch": { "strategy": "merge", "tables": [{ "name": "posts", "comment": "Blog posts", "columns": [{ "name": "id", "type": "bigint", "is_primary_key": true, "is_auto_increment": true }, { "name": "user_id", "type": "bigint", "is_index": true }, { "name": "title", "type": "varchar", "length": 255 }, { "name": "body", "type": "text", "is_nullable": true }, { "name": "created_at", "type": "timestamp", "is_nullable": true }, { "name": "updated_at", "type": "timestamp", "is_nullable": true }] }] }
    },
    "delete": {
      "description": "Delete",
      "patch": { "strategy": "merge", "deletions": { "tables": ["tmp"], "columns": [{ "table": "users", "columns": ["legacy"] }], "relationships": [{ "source_table": "users", "source_column": "id", "target_table": "posts", "target_column": "user_id" }] } }
    },
    "group_with_tables": {
      "description": "Multiple groups with nested tables (every table sets parent_name)",
      "patch": { "strategy": "merge", "groups": [{ "name": "Auth" }, { "name": "Content" }], "tables": [{ "name": "users", "parent_name": "Auth", "columns": [{ "name": "id", "type": "bigint", "is_primary_key": true, "is_auto_increment": true }] }, { "name": "roles", "parent_name": "Auth", "columns": [{ "name": "id", "type": "bigint", "is_primary_key": true, "is_auto_increment": true }] }, { "name": "posts", "parent_name": "Content", "columns": [{ "name": "id", "type": "bigint", "is_primary_key": true, "is_auto_increment": true }] }] }
    },
    "rename": {
      "description": "Rename table and column (use old_name only when renaming)",
      "patch": { "strategy": "merge", "tables": [{ "name": "articles", "old_name": "posts", "columns": [{ "name": "content", "old_name": "body" }] }] }
    },
    "enum_column": {
      "description": "Column with enum values (use enum_values on the column, not sticky notes)",
      "patch": { "strategy": "merge", "tables": [{ "name": "users", "columns": [{ "name": "role", "type": "enum", "enum_values": ["admin", "instructor", "student"] }] }] }
    }
  }
}
