{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "WhipWhip whipset (authoring format)",
  "description": "A whipset written by a chat for the WhipWhip browser extension. Paste the JSON into the WhipWhip panel import field. Ids, derived fields, keys and signatures are filled in by the extension — do not write them. Variable tokens are written as <<var.key>>, never with curly braces.",
  "type": "object",
  "required": [
    "format",
    "name",
    "whips"
  ],
  "additionalProperties": false,
  "properties": {
    "format": {
      "const": "whipwhip.authoring/1"
    },
    "name": {
      "type": "string",
      "minLength": 1
    },
    "description": {
      "type": "string"
    },
    "inline_guide": {
      "type": "string",
      "description": "Usage notes shown to the person who runs the set."
    },
    "tags": {
      "type": "array",
      "description": "Roles: each tag names one conversation the whips can move between.",
      "items": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{3,10}$",
            "not": {
              "const": "main"
            }
          }
        }
      }
    },
    "variables": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key",
          "label",
          "source",
          "required"
        ],
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_]+$"
          },
          "label": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          },
          "source": {
            "enum": [
              "user_input",
              "set_by_action",
              "constant"
            ],
            "description": "user_input: asked from the person. set_by_action: written by a card. constant: fixed defaultValue."
          },
          "valueType": {
            "enum": [
              "string",
              "array",
              "json"
            ],
            "description": "Use \"array\" for variables that list cards change (append_item, upsert_item, patch_item, remove_item) or that derive_index reads."
          },
          "defaultValue": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          }
        }
      }
    },
    "whips": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": [
          "label",
          "actions"
        ],
        "additionalProperties": false,
        "properties": {
          "label": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          },
          "preflightStyle": {
            "enum": [
              "form",
              "none"
            ],
            "description": "Defaults to \"form\"."
          },
          "scopeTags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags whose conversation this whip belongs to."
          },
          "actions": {
            "type": "array",
            "minItems": 1,
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "prompt_write",
                      "description": "Write or modify text in the chat window."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "text"
                      ],
                      "properties": {
                        "text": {
                          "type": "string",
                          "description": "Prompt May contain variable tokens written as <<var.key>>."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "replace",
                            "append",
                            "prepend"
                          ],
                          "description": "Insert position"
                        },
                        "preflight": {
                          "type": "boolean",
                          "description": "Show an input form for <<var.key>> tokens in this prompt before executing"
                        },
                        "inputStyle": {
                          "type": "string",
                          "enum": [
                            "form",
                            "inline"
                          ],
                          "description": "Ask as Only used when \"preflight\" is \"true\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "submit",
                      "description": "Send the current text, and wait for the AI's reply unless you turn waiting off."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "skipWait": {
                          "type": "boolean",
                          "description": "Move on right after sending. Use this when the whip never reads the reply — e.g. broadcasting the same prompt to several tabs."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "input_prompt",
                      "description": "Ask the user to provide necessary information."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "selectedVars"
                      ],
                      "properties": {
                        "title": {
                          "type": "string",
                          "description": "Modal title"
                        },
                        "selectedVars": {
                          "type": "array",
                          "minItems": 1,
                          "items": {
                            "type": "object",
                            "required": [
                              "key"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "key": {
                                "type": "string"
                              },
                              "question": {
                                "type": "string"
                              },
                              "placeholder": {
                                "type": "string"
                              }
                            }
                          },
                          "description": "Variables to ask Keys declared in \"variables\"."
                        },
                        "inputStyle": {
                          "type": "string",
                          "enum": [
                            "form",
                            "inline"
                          ],
                          "description": "Ask as"
                        },
                        "alwaysAsk": {
                          "type": "boolean",
                          "description": "Show this input even when the variable already has a value, so a pre-filled or LLM-drafted value can be reviewed before it is committed"
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "ask_and_extract",
                      "description": "Prompt the AI and extract key information as variables automatically."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "text",
                        "varsToExtract"
                      ],
                      "properties": {
                        "text": {
                          "type": "string",
                          "description": "Prompt May contain variable tokens written as <<var.key>>."
                        },
                        "preflight": {
                          "type": "boolean",
                          "description": "Show an input form for <<var.key>> tokens in this prompt before executing"
                        },
                        "inputStyle": {
                          "type": "string",
                          "enum": [
                            "form",
                            "inline"
                          ],
                          "description": "Ask as Only used when \"preflight\" is \"true\"."
                        },
                        "varsToExtract": {
                          "type": "array",
                          "minItems": 1,
                          "items": {
                            "type": "object",
                            "required": [
                              "key"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "key": {
                                "type": "string"
                              },
                              "description": {
                                "type": "string"
                              }
                            }
                          },
                          "description": "Variables to extract Keys declared in \"variables\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "extract_response",
                      "description": "Analyze the AI response and convert it into structured data. Opens a temporary tab in the foreground while it works."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key",
                        "schema"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "The variable name to store the captured value in. A key declared in \"variables\"."
                        },
                        "schema": {
                          "type": "string",
                          "description": "Describe the shape of the JSON you expect the AI to return."
                        },
                        "promptSuffix": {
                          "type": "string",
                          "description": "Optional extra text appended to the extraction prompt."
                        },
                        "retries": {
                          "type": [
                            "number",
                            "string"
                          ],
                          "description": "How many times to re-ask if the response is not valid JSON."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "validate",
                      "description": "Check the reply against a FORMAT rule and re-ask if it does not match. Format only, never quality."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "rule"
                      ],
                      "properties": {
                        "source": {
                          "type": "string",
                          "enum": [
                            "last_assistant",
                            "var"
                          ],
                          "description": "The last reply, or a stored variable. A variable is checked only — re-asking cannot change a past value."
                        },
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Variable Only used when \"source\" is \"var\". A key declared in \"variables\"."
                        },
                        "rule": {
                          "type": "string",
                          "enum": [
                            "json",
                            "regex",
                            "non_empty",
                            "whipset"
                          ],
                          "description": "JSON: parses (optionally with required keys). Regex: matches a pattern. Not empty: any non-blank reply. Whipset: a WhipWhip whipset written by the chat — if it is wrong, the re-ask carries the list of fixes."
                        },
                        "pattern": {
                          "type": "string",
                          "description": "Pattern Only used when \"rule\" is \"regex\"."
                        },
                        "requiredKeys": {
                          "type": "string",
                          "description": "Comma-separated top-level keys that must exist. Only used when \"rule\" is \"json\"."
                        },
                        "retries": {
                          "type": [
                            "number",
                            "string"
                          ],
                          "description": "How many times to send a correction prompt before failing. 0 = check only."
                        },
                        "correction": {
                          "type": "string",
                          "description": "Sent when the format does not match. Leave empty for a default message."
                        },
                        "variable": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Store result in A key declared in \"variables\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "send_to_services",
                      "description": "Send the same text to the chosen AIs and gather every answer in one variable. Other AIs open in background tabs."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "servicesVar",
                        "text",
                        "key"
                      ],
                      "properties": {
                        "servicesVar": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Variable holding a JSON array of AI service ids. Usually filled by a Choice with the \"AI services\" source and multiple selection. A key declared in \"variables\"."
                        },
                        "text": {
                          "type": "string",
                          "description": "Sent unchanged to every AI. Variable tokens are allowed. May contain variable tokens written as <<var.key>>."
                        },
                        "tagPrefix": {
                          "type": "string",
                          "description": "When set, each AI's chat is tagged \"name_abbr\" (gpt, gem, cla, grk, ppx) so the next run can follow up. Up to 6 characters."
                        },
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Gets a heading and the answer (or why it failed) for each AI. A key declared in \"variables\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "var_save",
                      "description": "Save text from the composer or AI response into a variable."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "valueSource",
                        "key"
                      ],
                      "properties": {
                        "valueSource": {
                          "type": "string",
                          "description": "Where to read the value from. One of: builtin.date_today, builtin.date_iso, builtin.time_now, builtin.timestamp, builtin.day_of_week, builtin.page_url, builtin.page_title, builtin.page_domain, builtin.language, builtin.random_uuid, builtin.selection, composer, var.last.user, var.last.assistant, or \"var.<declared variable key>\"."
                        },
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "The variable name to store the captured value in. A key declared in \"variables\"."
                        },
                        "valueFilter": {
                          "type": "string",
                          "enum": [
                            "",
                            "code"
                          ],
                          "description": "Content filter Only used depending on \"valueSource\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "var_flush",
                      "description": "Clear all stored variable values."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {}
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "append_item",
                      "description": "Add an item to an array variable (e.g. append a record to an ADR list)."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key",
                        "fields"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "An array-typed variable to mutate. Set the variable's type to \"array\" first. A key declared in \"variables\"."
                        },
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "name",
                              "value"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "value": {
                                "type": "string",
                                "description": "May contain variable tokens written as <<var.key>>."
                              }
                            }
                          },
                          "description": "Fields of the record to append (name → value). Values may use <<var.x>> tokens; quotes and newlines are handled for you. May contain variable tokens written as <<var.key>>."
                        },
                        "autoId": {
                          "type": "boolean",
                          "description": "Give each new item a stable, incrementing id when you don't supply one yourself."
                        },
                        "idPrefix": {
                          "type": "string",
                          "description": "Prepended to the number, e.g. \"adr-\" produces adr-1, adr-2, … Only used when \"autoId\" is \"true\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "upsert_item",
                      "description": "Insert or update an item in an array variable, matched by its \"id\" field."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key",
                        "fields"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "An array-typed variable to mutate. Set the variable's type to \"array\" first. A key declared in \"variables\"."
                        },
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "name",
                              "value"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "value": {
                                "type": "string",
                                "description": "May contain variable tokens written as <<var.key>>."
                              }
                            }
                          },
                          "description": "Fields of the record to insert or update. Include an \"id\" field — it matches the existing item to replace. May contain variable tokens written as <<var.key>>."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "patch_item",
                      "description": "Update only the named fields of an item matched by id, keeping its other fields (e.g. flip status)."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key",
                        "itemId",
                        "fields"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "An array-typed variable to mutate. Set the variable's type to \"array\" first. A key declared in \"variables\"."
                        },
                        "itemId": {
                          "type": "string",
                          "description": "The \"id\" of the item to patch. Supports <<var.x>> tokens. May contain variable tokens written as <<var.key>>."
                        },
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "name",
                              "value"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "value": {
                                "type": "string",
                                "description": "May contain variable tokens written as <<var.key>>."
                              }
                            }
                          },
                          "description": "Only these fields are updated; the item's other fields are kept. Values may use <<var.x>> tokens. May contain variable tokens written as <<var.key>>."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "remove_item",
                      "description": "Remove an item from an array variable by its \"id\"."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key",
                        "itemId"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "An array-typed variable to mutate. Set the variable's type to \"array\" first. A key declared in \"variables\"."
                        },
                        "itemId": {
                          "type": "string",
                          "description": "The \"id\" of the item to remove. Supports <<var.x>> tokens. May contain variable tokens written as <<var.key>>."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "derive_index",
                      "description": "Build a summary/index string from an array variable, one line per item."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "sourceKey",
                        "key"
                      ],
                      "properties": {
                        "sourceKey": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "The array variable to summarize. A key declared in \"variables\"."
                        },
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "The variable that receives the derived index string. A key declared in \"variables\"."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "join",
                            "count"
                          ],
                          "description": "Index text (default), or just how many items are left after filtering."
                        },
                        "itemTemplate": {
                          "type": "string",
                          "description": "Rendered once per item and joined by newline. Use <<item.field>> tokens, e.g. <<item.id>> — <<item.title>>. May contain list item tokens written as <<item.field>>. Required when used. Only used depending on \"mode\"."
                        },
                        "filterField": {
                          "type": "string",
                          "description": "Keep only items matching the value below. Leave blank to include all."
                        },
                        "filterOp": {
                          "type": "string",
                          "enum": [
                            "eq",
                            "ne"
                          ],
                          "description": "A missing field counts as an empty value either way. Only used depending on \"filterField\"."
                        },
                        "filterValue": {
                          "type": "string",
                          "description": "The value the filter field is compared against. Supports <<var.x>> tokens. May contain variable tokens written as <<var.key>>. Only used depending on \"filterField\"."
                        },
                        "sortField": {
                          "type": "string",
                          "description": "Order items by this field before rendering. Numeric-aware, so adr-2 comes before adr-10. Leave blank to keep insertion order."
                        },
                        "sortDir": {
                          "type": "string",
                          "enum": [
                            "asc",
                            "desc"
                          ],
                          "description": "Sort direction Only used depending on \"sortField\"."
                        },
                        "limit": {
                          "type": [
                            "number",
                            "string"
                          ],
                          "description": "Keep at most this many items. Leave blank for all."
                        },
                        "limitFrom": {
                          "type": "string",
                          "enum": [
                            "end",
                            "start"
                          ],
                          "description": "Take from Only used depending on \"limit\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "go_to_role",
                      "description": "Move the run to the conversation a role is bound to."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "tagName": {
                          "type": "string",
                          "description": "Tag A tag name declared in \"tags\"."
                        },
                        "tagCandidates": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Pick the roles this step may route to. Leave empty for fixed routing. The run is refused unless every candidate is bound. Tag names declared in \"tags\"."
                        },
                        "tagVar": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Variable holding the chosen role name (e.g. filled by a Choice step). Only used when candidates are picked. A key declared in \"variables\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "new_chat",
                      "description": "Open a new conversation on the current service."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "isTemp": {
                          "type": "string",
                          "enum": [
                            "false",
                            "true"
                          ],
                          "description": "Temporary chat"
                        },
                        "openMode": {
                          "type": "string",
                          "enum": [
                            "current",
                            "new_tab",
                            "background"
                          ],
                          "description": "Open in"
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "open_service",
                      "description": "Switch to a different AI service (ChatGPT, Gemini, Claude…)."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "service": {
                          "type": "string",
                          "enum": [
                            "chatgpt",
                            "gemini",
                            "claude",
                            "grok",
                            "perplexity"
                          ],
                          "description": "Service"
                        },
                        "openMode": {
                          "type": "string",
                          "enum": [
                            "current",
                            "new_tab",
                            "background"
                          ],
                          "description": "Open in"
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "open_url",
                      "description": "Navigate to any https:// URL."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "target": {
                          "type": "string",
                          "description": "URL"
                        },
                        "openMode": {
                          "type": "string",
                          "enum": [
                            "current",
                            "new_tab",
                            "background"
                          ],
                          "description": "Open in"
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "tag_current_chat",
                      "description": "Assign a tag to the current conversation."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "tagName"
                      ],
                      "properties": {
                        "tagName": {
                          "type": "string",
                          "description": "Tag name A tag name declared in \"tags\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "choice_prompt",
                      "description": "Show the user a list of options and store their pick in a variable."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key"
                      ],
                      "properties": {
                        "label": {
                          "type": "string",
                          "description": "Question"
                        },
                        "optionsSource": {
                          "type": "string",
                          "enum": [
                            "static",
                            "variable",
                            "services"
                          ],
                          "description": "Where to get the choice options from. \"AI services\" fills in ChatGPT, Claude, Gemini, Grok and Perplexity."
                        },
                        "options": {
                          "type": "string",
                          "description": "Each line becomes a button. Saved to variable as the chosen line. Required when used. Only used depending on \"optionsSource\"."
                        },
                        "dynamicOptionsVar": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Variable containing options (one per line). Parsed at runtime. Required when used. Only used when \"optionsSource\" is \"variable\". A key declared in \"variables\"."
                        },
                        "optionLabel": {
                          "type": "string",
                          "description": "Per-item label template for each choice. Use <<item.field>> tokens, e.g. <<item.id>> — <<item.title>>. May contain list item tokens written as <<item.field>>. Only used when \"optionsSource\" is \"variable\"."
                        },
                        "optionValueField": {
                          "type": "string",
                          "description": "Which item field becomes the picked value (default \"id\"). Feed it to a later Patch list item or Remove from list step as the item id. Only used when \"optionsSource\" is \"variable\"."
                        },
                        "multiple": {
                          "type": "boolean",
                          "description": "Show a checklist; on OK the checked values are saved as a JSON array."
                        },
                        "lockCurrentService": {
                          "type": "boolean",
                          "description": "With multiple selection, mark the AI of the tab that ran the whip as \"current tab\" and keep it checked and locked. Only used when \"optionsSource\" is \"services\"."
                        },
                        "warnInConversation": {
                          "type": "boolean",
                          "description": "With multiple selection, note above the checklist when the running tab already has a conversation, since only its AI answers with that context. Only used when \"optionsSource\" is \"services\"."
                        },
                        "continueGroup": {
                          "type": "string",
                          "description": "With multiple selection, when the running tab belongs to the \"name_abbr\" tag group, add a \"Follow up\" row on top (on by default) and pre-check those AIs. Match the tag name in \"Send to several AIs\". Only used when \"optionsSource\" is \"services\"."
                        },
                        "rememberAs": {
                          "type": "string",
                          "description": "Give a name to remember this pick and pre-check it next run. The same memory is used from any tab or workspace. Only used when \"multiple\" is \"true\"."
                        },
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Variable key where the chosen option will be stored. A key declared in \"variables\"."
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "show_result_modal",
                      "description": "Display a result window using stored variable values."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "body"
                      ],
                      "properties": {
                        "title": {
                          "type": "string",
                          "description": "Title"
                        },
                        "body": {
                          "type": "string",
                          "description": "Body"
                        },
                        "copyButton": {
                          "type": "string",
                          "enum": [
                            "none",
                            "copy_body"
                          ],
                          "description": "Copy button"
                        }
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "card",
                    "config"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "card": {
                      "const": "import_whipset",
                      "description": "Import a chat-written whipset JSON held in a variable. You confirm it in a preview before anything is saved."
                    },
                    "config": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "key"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-zA-Z0-9_]+$",
                          "description": "Usually the variable a preceding Validate card (whipset rule) stored. A key declared in \"variables\"."
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}
