Перейти к содержимому

Rotación en JSON

Actualizado el 18/10/2026

El JSON es la descripción completa de una rotación avanzada: todo lo que tiene el texto KRC, más los ID de hechizo, las marcas de ranura y los ajustes del perfil. El constructor lo muestra en la página «Texto y JSON», y KeyRota lo admite como archivo («Nuevo» — «Desde un archivo…», «Importar»). El formato se llama keyrota-rotation, versión 1. Hay un JSON Schema para validarlo.

Archivo de ejemplo#

La rotación útil más corta: Ruptura con 5 puntos de combo o más cuando no está en el objetivo y, si no, Mutilar.

minimal.json
{
  "format": "keyrota-rotation",
  "version": 1,
  "title": "Asesinato: mínimo",
  "specID": 259,
  "resources": ["PlayerHP", "TargetHP", "Energy", "Combo_Points"],
  "auras": [{"n": 1, "alias": "Rupture_DoT", "label": "Ruptura en el objetivo [1943]", "spell": 1943}],
  "slots": [
    {"n": 2, "alias": "Rupture", "label": "Ruptura", "spell": 1943},
    {"n": 4, "alias": "Mutilate", "label": "Mutilar", "spell": 1329}
  ],
  "rules": [
    {
      "use": "Rupture",
      "name": "Ruptura",
      "when": [{"all": ["Combo_Points >= 70", "Rupture_DoT.down"], "comment": "5+ puntos de combo, sin sangrado"}]
    },
    {"use": "Mutilate", "name": "Mutilar"}
  ]
}

Todo lo que el archivo omite toma su valor predeterminado: las ranuras se muestran «Con objetivo» y no tienen marcas, el contador de enemigos es de 8 metros y solo cuenta enemigos en combate. Una regla sin when se cumple siempre.

Campos de primer nivel#

CampoTipoObligatorioPredeterminadoQué es
formatcadenasí—Siempre "keyrota-rotation".
versionenterosí—Siempre 1. Una versión más nueva se rechaza: «actualízala».
titlecadenano""Nombre de la rotación: la primera línea del texto, el nombre en el registro. Sin comillas dobles.
specIDentero 0–100000no0Especialización de WoW; 0 = cualquiera. Necesario para el perfil de Better Assistant.
ignoreMainIcontrue / falsenotrueActivado: pulsar solo según las reglas. Desactivado: si no se cumple ninguna regla, pulsar la recomendación del juego.
enemiesobjetono{"range": 8, "combatOnly": true}El contador de enemigos de la condición enemies.
notescadena o lista de cadenasno[]Descripción: líneas # al principio del texto.
resourceslista de cadenas y nullno[]Nombres de resource1…N, en orden.
auraslistano[]Auras buff1…12.
slotslistano[]Ranuras slot1…12.
ruleslistano[]Reglas por orden de prioridad.
footercadena o lista de cadenasno[]Comentarios después de la última regla.

Cualquier otro campo es un error, «campo sobrante»: las erratas no se pasan por alto. Por eso no añadas "$schema" al archivo; asocia el esquema en tu editor o validador.

Casi en cualquier sitio, null puede ocupar el lugar de un valor; equivale a omitir el campo.

format y version#

"format": "keyrota-rotation" y "version": 1 son obligatorios. Si version es mayor que 1, KeyRota la rechaza: «la versión 2 es más nueva de lo que entiende esta KeyRota: actualízala». Sin version: «se necesita la versión 1».

title#

Una cadena sin comillas dobles ". Los saltos de línea se convierten en espacios. En el texto KRC es rotation "…". Es un buen sitio para la versión: "Asesinato v1.3".

specID#

El ID de la especialización en el juego. Determina los recursos de la rotación (cuando se crea en el constructor) y la clase y especialización del perfil de Better Assistant. 0 significa cualquier especialización: la rotación funciona, pero el perfil de Better Assistant no se crea («Elige una especialización: el perfil de Better Assistant la necesita»).

ClaseEspecializaciónspecIDRecursos de la especialización (resource3…)
GuerreroArmas71Rage
GuerreroFuria72Rage
GuerreroProtección73Rage
PaladínSagrado65Mana, Holy_Power
PaladínProtección66Mana, Holy_Power
PaladínReprensión70Mana, Holy_Power
CazadorBestias253Focus
CazadorPuntería254Focus
CazadorSupervivencia255Focus
PícaroAsesinato259Energy, Combo_Points
PícaroForajido260Energy, Combo_Points
PícaroSutileza261Energy, Combo_Points
SacerdoteDisciplina256Mana, Insanity
SacerdoteSagrado257Mana, Insanity
SacerdoteSombra258Mana, Insanity
Caballero de la MuerteSangre250Runes, Runic_Power
Caballero de la MuerteEscarcha251Runes, Runic_Power
Caballero de la MuerteProfano252Runes, Runic_Power
ChamánElemental262Mana, Maelstrom
ChamánMejora263Mana, Maelstrom
ChamánRestauración264Mana, Maelstrom
MagoArcano62Mana, Arcane_Charges
MagoFuego63Mana, Arcane_Charges
MagoEscarcha64Mana, Arcane_Charges
BrujoAflicción265Mana, Soul_Shards
BrujoDemonología266Mana, Soul_Shards
BrujoDestrucción267Mana, Soul_Shards
MonjeMaestro cervecero268Energy, Stagger
MonjeTejedor de niebla270Mana
MonjeViajero del viento269Energy, Chi
DruidaEquilibrio102Mana, Astral_Power
DruidaFeral103Energy, Combo_Points
DruidaGuardián104Rage
DruidaRestauración105Mana, Astral_Power
Cazador de demoniosDevastación577Fury
Cazador de demoniosVenganza581Fury
Cazador de demoniosDevorador1480Fury
EvocadorDevastación1467Mana, Essence
EvocadorPreservación1468Mana, Essence
EvocadorAumento1473Mana, Essence

ignoreMainIcon#

true (predeterminado): KeyRota solo pulsa según las reglas; si no se cumple ninguna, espera. false: en ese caso pulsa la recomendación del juego, como el perfil de Blizzard. La recomendación es el último recurso, después de todas las reglas; nunca se adelanta a una regla.

enemies#

JSON
"enemies": {"range": 8, "combatOnly": true}
CampoTipoPredeterminadoQué es
rangeentero 1–1008El radio del contador de enemigos, en metros.
combatOnlytrue / falsetrueContar solo los enemigos que ya están en combate.

La condición enemies lee este contador (consulta Enemigos cerca). En el constructor son «Radio del contador de enemigos, metros» y «Solo enemigos en combate», en la página «General». Estos valores también pasan al perfil de Better Assistant.

notes y footer#

La descripción de la rotación (notes) y los comentarios después de la última regla (footer). Una lista de cadenas o una sola cadena con \n. Una cadena vacía "" es una línea en blanco en el texto. Las líneas en blanco de los extremos se descartan.

JSON
"notes": ["Basada en una guía y en los mejores logs, parche 12.1", "", "v1.1: Envenenar antes con 4+ objetivos"]

En el texto KRC son líneas # … antes de las declaraciones. No afectan al funcionamiento de la rotación.

resources#

Los nombres de los recursos en orden: el primer elemento es resource1, el segundo resource2, etc. El número determina qué recurso es; el nombre solo es cómo lo escribes en las condiciones:

  • resource1 es la salud del jugador y resource2, la salud del objetivo;
  • de resource3 en adelante son los recursos de la especialización, en el orden de la tabla de specID: para un pícaro, resource3 es la energía y resource4, los puntos de combo.
JSON
"resources": ["PlayerHP", "TargetHP", "Energy", "Combo_Points"]

Usa los nombres estándar de la tabla: el constructor los muestra con palabras («Salud del jugador», «Energía») y otras personas y los agentes de IA entienden las condiciones a la primera. null se salta un número: ["PlayerHP", null, "Energy"] solo declara resource1 y resource3. Un recurso no declarado no se puede leer: «resource2 no declarado».

Todos los recursos son porcentajes del máximo, de 0 a 100. Cómo pasar a porcentaje los puntos de combo, la energía y las runas: Recursos, en Condiciones.

auras#

Las auras son tus beneficios y tus perjuicios sobre el objetivo. El aura n de la rotación es el aura n de la lista de Better Assistant.

CampoTipoObligatorioPredeterminadoQué es
nentero 1–12sí—Número del aura (buffN). Cada número, una sola vez.
aliascadenanoa partir de labelEl nombre en las condiciones. Consulta Nombres.
labelcadenano""Nombre visible. Sin comillas dobles ". Resulta práctico con el ID entre corchetes: "Garrote en el objetivo [703]".
spellentero 0–100000000no0El ID del aura en Wowhead. 0 = sin definir: el aura no estará en el perfil de Better Assistant y será desconocida.
notecadenano""Un comentario al final de la línea de declaración.
JSON
"auras": [
  {"n": 1, "alias": "Garrote_DoT", "label": "Garrote en el objetivo [703]", "spell": 703},
  {"n": 2, "alias": "Rupture_DoT", "label": "Ruptura en el objetivo [1943]", "spell": 1943}
]

El orden de la lista no importa; importa n. Puede haber huecos en la numeración, pero para el perfil de Better Assistant las auras con ID deben ir seguidas desde la primera.

slots#

Las ranuras son los botones de la rotación. La ranura n de la rotación es la ranura n de Better Assistant.

CampoTipoObligatorioPredeterminadoQué es
nentero 1–12sí—Número de ranura (slotN).
aliascadenanoa partir de labelEl nombre en las reglas y las condiciones.
labelcadenano""El nombre del hechizo. Sin comillas dobles ".
spellentero 0–100000000no0El ID del hechizo en Wowhead. Better Assistant lo usa para poner el hechizo en la ranura.
notecadenano""Un comentario al final de la línea de declaración.
show"target" / "combat"no"target"Cuándo muestra Better Assistant la ranura.
manualtrue / falsenofalseMarca M.
channeltrue / falsenofalseMarca C.
offGcdtrue / falsenofalseMarca O.
queuetrue / falsenofalseMarca Q, solo con offGcd.

show#

  • "target": la ranura se muestra cuando tienes un objetivo. Es lo habitual en casi todos los ataques.
  • "combat": la ranura se muestra en combate aunque no haya objetivo. Para sanación y habilidades defensivas: Vial carmesí debe funcionar cuando no hay objetivo.

Una ranura solo está lista cuando Better Assistant la muestra. Una ranura "target" sin objetivo no está lista y sus reglas se omiten.

Marcas de ranura#

Las marcas indican cómo pulsar una ranura una vez elegida su regla. En el constructor son los interruptores de «Cómo pulsar».

MarcasEn JSONCómo se pulsaEjemplo
ningunatodas falseEn la ventana de cola, al final del GCD: el hechizo se lanza en cuanto termina el GCD.Mutilar, Envenenar
M"manual": trueNunca. Las reglas de la ranura se omiten. Si pulsas tú su tecla, la rotación se pausa.Una habilidad que pulsas tú
C"channel": trueComo un hechizo normal; mientras dura la canalización, no se pulsa nada. Tras la pulsación, la ranura no se vuelve a pulsar hasta que empieza la canalización.Asesinato múltiple (51690)
O"offGcd": trueAl momento, incluso en mitad del GCD.Suerte prolongada (381989)
O + Q"offGcd": true, "queue": trueFuera del GCD, pero solo en la ventana de cola al final del GCD.La macro Subidón de adrenalina + Asesinato múltiple (13750); Esfumarse (1856) tras Perdición de Reyes
C + O"channel": true, "offGcd": trueUna canalización que empieza fuera del GCD: al momento, y después nada hasta que termina la canalización.Poco habitual
C + O + Qlas tresLo mismo, en la ventana de cola.Poco habitual
Q sin O"queue": trueError: «queue solo va junto con offGcd».—
M + otras"manual": true, …Manda M: la ranura no se pulsa y las demás marcas dan igual.—

Por qué O + Q y no solo O: un botón solo con O se pulsa al momento, en mitad del GCD, antes de que haya llegado el golpe anterior. Esfumarse tras Perdición de Reyes solo con O se pulsaría antes de que lleguen los puntos de combo de Perdición de Reyes, y la regla de Esfumarse (Combo_Points < 65) comprobaría el valor antiguo. Con Q, el botón espera al final del GCD.

Prioridad estricta: una regla fuera del GCD que esté más abajo en la lista no se adelanta a una regla superior que espera al GCD.

JSON
"slots": [
  {"n": 1, "alias": "Adrenaline_Rush", "label": "Subidón de adrenalina (macro con Asesinato múltiple)", "spell": 13750, "offGcd": true, "queue": true},
  {"n": 4, "alias": "Keep_It_Rolling", "label": "Suerte prolongada", "spell": 381989, "offGcd": true},
  {"n": 8, "alias": "Killing_Spree", "label": "Asesinato múltiple", "spell": 51690, "channel": true},
  {"n": 11, "alias": "Crimson_Vial", "label": "Vial carmesí", "spell": 185311, "show": "combat"}
]

rules#

Las reglas por orden de prioridad: la primera de la lista se comprueba primero.

CampoTipoObligatorioPredeterminadoQué es
usecadenasí—La ranura que se pulsa: su alias o slotN.
namecadenano""El nombre de la acción, que aparece en el registro. En el texto, el comentario de la línea use.
notecadena o lista de cadenasno[]Notas sobre la regla (líneas # antes de use).
whenlista de gruposno[]Grupos O. Vacío o ausente: una regla sin condiciones, que se pulsa siempre que el hechizo se pueda usar.

when: grupos y condiciones#

CampoTipoObligatorioQué es
alllista de cadenas no vacíasíLas condiciones del grupo; deben cumplirse todas (Y).
commentcadenanoEl comentario del grupo (en el texto, al final de su línea).
JSON
{
  "use": "Garrote",
  "name": "Garrote",
  "note": ["Renovar en la ventana de pandemia"],
  "when": [
    {"all": ["Garrote_DoT.down"], "comment": "sin sangrado"},
    {"all": ["Garrote_DoT.up", "Garrote_DoT.duration <= 30"], "comment": "queda un 30 % o menos"}
  ]
}

Una misma ranura puede aparecer en varias reglas: por ejemplo, Garrote desde Sigilo arriba en la lista y el mantenimiento normal de Garrote más abajo.

Condiciones en JSON#

Cada condición es una cadena con sintaxis KRC: Garrote_DoT.down, Rupture_DoT.duration <= 25, Kingsbane.cd <= 3, Combo_Points >= 70, enemies >= 2, target.distance <= 8, cd == 1. La lista completa está en Condiciones.

  • Se hace referencia por nombre (Garrote_DoT) o por número: buff1, slot3, resource4.
  • and dentro de una cadena la divide en dos condiciones: "Combo_Points >= 70 and Energy >= 70" son dos condiciones, y ambas cuentan para el límite de 16.
  • Sin # ni saltos de línea dentro de una cadena.
  • Las condiciones las lee el propio motor de KeyRota, así que el JSON y el texto siempre significan lo mismo.

Nombres#

Un nombre (alias) es cómo se llaman las auras, las ranuras y los recursos en las condiciones y en use.

  • El primer carácter es una letra (de cualquier alfabeto) o _; después, letras, dígitos 0–9 y _. Se admiten letras con tilde, la ñ y otros alfabetos, como el cirílico; espacios, guiones y puntos no: Fan_of_Knives, no Fan-of-Knives.
  • Un nombre es único entre todas las auras, ranuras y recursos.
  • Reservados: use, when, and, or, rotation, gcd, enemies, target, aoe, st, cd, defensives, ground_spells, movement, y los nombres del tipo buffN, slotN, resourceN.
  • Las palabras clave van solo en minúsculas: use, or, .up. Use, OR y .UP son errores.

Un nombre a partir de la etiqueta. Sin alias, KeyRota crea uno a partir de label: los corchetes y su contenido se descartan, las letras y los dígitos se quedan y todo lo demás pasa a ser _. Por ejemplo, "Garrote en el objetivo [703]" se convierte en Garrote_en_el_objetivo. Un nombre vacío o inservible pasa a ser Aura_N o Slot_N; uno ya ocupado recibe _2, _3. Si una regla hace referencia a buffN o slotN que no están en las listas, se declaran automáticamente como Aura_N y Slot_N.

Campos obligatorios y valores predeterminados#

DóndeObligatorioPredeterminados
Archivoformat, versiontitle "", specID 0, ignoreMainIcon true, enemies {"range": 8, "combatOnly": true}, listas vacías
Auranalias a partir de label, label "", spell 0
Ranuranshow "target", todas las marcas false, spell 0
Reglausename "", note [], when [] (sin condiciones)
Grupoall (no vacía)comment ""

Límites#

QuéLímite
Auras12 (n 1–12)
Ranuras12 (n 1–12)
Grupos O en una regla16
Condiciones Y en un grupo16
Reglassin límite
Número en una condiciónentero 0–100
enemies.range1–100
spell0–100000000
specID0–100000
Recursoshasta 99

Exactamente 16 grupos de 16 condiciones está permitido; 17 es un error.

Errores#

Una rotación con un error no se carga en absoluto: no hay ejecución parcial. Un error siempre indica el lugar. En JSON es una ruta: rules[3].when[1]: nombre desconocido Foo (contando desde cero). En el constructor, «Regla 4, grupo 2: nombre desconocido Foo» (contando desde uno). En el texto KRC, un número de línea.

Estructura del JSON:

MensajeCausa
campo sobrante «offgcd»Un campo mal escrito o desconocido (importan las mayúsculas: offGcd).
falta el campo «format»Falta un campo obligatorio.
format: se esperaba «keyrota-rotation»Otro valor en format.
version: se necesita la versión 1Falta version o es menor que 1.
slots[0]: falta el número n (1–12)Una ranura o un aura sin n.
slots[2].n: número fuera de 1..12n fuera de rango.
slots[3]: el número 4 ya está ocupadoDos ranuras (o dos auras) con el mismo n.
slots[1].show: se esperaba «target» o «combat»Otro valor en show.
slots[0]: queue solo va junto con offGcdqueue: true sin offGcd: true.
rules[2].use: no existe la ranura «Garotte»use hace referencia a una ranura que no existe.
rules[0].when[1].all: se necesita una lista de condiciones no vacíaUn grupo vacío o sin all.
rules[0].when[0].all[2]: condición vacía o con # o salto de líneaUna cadena de condición vacía o con #.
….spell: se esperaba un número enteroTipo incorrecto: una cadena en lugar de un número, true en lugar de un número, etc.

Condiciones y declaraciones (igual que en el texto KRC):

CódigoMensaje de ejemploCausa
unknown_refnombre desconocido FooNo hay ningún aura, ranura ni recurso con ese nombre.
unknown_refresource5 no declaradoEl recurso no está declarado en resources.
wrong_kindGarrote es slot, pero se necesita buffUna propiedad de aura en una ranura (Garrote.up) o al revés.
bad_conditionno se entiende la condición "Energy => 50"Una errata en la condición o en el operador.
bad_condition"Garrote.distance <= 8": distance solo existe para targetSolo el objetivo tiene distancia.
value_rangevalor 120 fuera de 0..100Un número mayor que 100. No hay números negativos ni decimales.
toggle_value"cd >= 1": el interruptor solo admite == 0 o == 1Un interruptor solo se compara con == y 0 o 1.
bad_aliasnombre no válido Fan-of-KnivesCaracteres no válidos en un nombre.
reserved_aliasel nombre cd está reservadoUn nombre reservado.
duplicate_aliasel nombre Garrote ya está declaradoDos auras, ranuras o recursos con el mismo nombre.
index_rangebuff13: número fuera de 1..12Una referencia a un número mayor que 12.
too_many_groupsmás de 16 grupos ORMás de 16 grupos en una regla.
too_many_conditions17 condiciones en el grupo (máximo 16)Más de 16 condiciones en un grupo.

El constructor también indica «el grupo no tiene condiciones» (un grupo vacío), «no se ha elegido ranura» y «el nombre no puede llevar comillas».

Texto KRC#

El texto KRC es la misma rotación en forma de líneas. La página «Texto y JSON» lo muestra y puedes guardarlo como archivo .txt. El texto y el JSON se convierten el uno en el otro sin perder significado.

Texto KRC
rotation "Asesinato: mínimo"

buff1	Rupture_DoT = "Ruptura en el objetivo [1943]"
slot2	Rupture = "Ruptura"
slot4	Mutilate = "Mutilar"
resource1	PlayerHP
resource2	TargetHP
resource3	Energy
resource4	Combo_Points

use Rupture when  # Ruptura
	Combo_Points >= 70 and Rupture_DoT.down  # 5+ puntos de combo, sin sangrado

use Mutilate when  # Mutilar

Líneas#

  • # fuera de comillas dobles inicia un comentario hasta el final de la línea. Dentro de una "etiqueta", # es un carácter normal.
  • Las líneas en blanco y las que solo tienen un comentario se omiten: así se escriben las descripciones y los separadores.
  • Cualquier otra línea es una declaración, use, or o una línea de condiciones. Si no, es un error stray_line: «línea fuera de una regla: "…" (el texto libre debe empezar con #)».
  • Se admite cualquier cantidad de espacios y tabulaciones entre las partes. La forma canónica usa una tabulación después de buffN/slotN y al principio de las líneas de condiciones.

Declaraciones#

Solo antes del primer use:

Texto KRC
rotation "Nombre"                        # opcional, como mucho una vez
buff1	Garrote_DoT = "Garrote en el objetivo [703]"   # aura 1
slot1	Garrote = "Garrote"                         # ranura 1
resource1	PlayerHP                               # recurso 1

buffN y slotN van de 1 a 12; resourceN, de 1 a 99. La etiqueta entre comillas puede estar vacía: slot5 Filler = "". Una declaración después del primer use es un error late_declaration.

Reglas#

Texto KRC
use Rupture when  # Ruptura
	Combo_Points >= 70 and Rupture_DoT.down  # sin sangrado
	or
	Combo_Points >= 70 and Rupture_DoT.up and Rupture_DoT.duration <= 30  # pandemia
  • use Ranura when abre una regla; un comentario en esa línea es el nombre de la acción.
  • Cada línea de condiciones es un grupo (condiciones unidas con and). Los grupos se separan con una línea que solo contiene la palabra or.
  • Una regla sin líneas de condiciones no tiene condiciones.
  • Dos líneas de condiciones seguidas sin or son un error split_group; or al principio, al final o dos veces seguidas es empty_group.

Texto y JSON: correspondencia#

En el textoEn JSON
rotation "…"title
Líneas # … antes del primer usenotes
Un comentario al final de una línea buffN / slotNnote del aura o de la ranura
Líneas # … justo antes de use (y entre los grupos de la regla)note de la regla
Un comentario en la línea usename de la regla
Un comentario al final de una línea de condicionescomment del grupo
Líneas # … después de la última reglafooter
— (no está en el texto)specID, ignoreMainIcon, enemies, spell, show, marcas

Las líneas en blanco de los extremos de los bloques de comentarios se descartan; las del interior se conservan como "".

Importación y almacenamiento#

  • KeyRota guarda la rotación dentro del perfil. «Exportar» en la ventana principal guarda el perfil de KeyRota (JSON) con su rotación; «Importar» admite un perfil de KeyRota y un JSON de rotación suelto.
  • «Nuevo» — «Desde un archivo…» admite .txt con texto KRC, JSON de rotación y perfiles de KeyRota; «Nuevo» — «Desde el portapapeles», lo mismo desde el portapapeles.
  • En el constructor: «Texto y JSON» — «Pegar» o «Abrir…» sustituyen la rotación; las ranuras y auras con el mismo número conservan sus ID, «Con objetivo» / «En combate» y sus marcas si la versión nueva no los trae.
  • Una rotación en texto (sin JSON) toma los ID y la visibilidad de las ranuras del perfil de Better Assistant ya guardado en el perfil de KeyRota, y los ID de las auras también de las marcas [703] de sus etiquetas.

El perfil de Better Assistant a partir del JSON#

«Crear desde ranuras y auras», en el constructor, genera la cadena del perfil de Better Assistant a partir del JSON de la rotación:

  • 12 ranuras: la ranura N es slots[n = N].spell, mostrada según show. Una ranura sin ID queda vacía.
  • Auras, en orden desde la primera: auras[n = 1].spell, auras[n = 2].spell… Un aura sin ID en medio es un error.
  • Clase y especialización, a partir de specID.
  • Contador de enemigos, a partir de enemies.range y enemies.combatOnly.
  • Si el perfil de KeyRota ya contiene un perfil de Better Assistant de la misma especialización, se conservan sus demás ajustes y las ranuras y auras que coinciden.

En el juego: /ba — Profile — pega — Apply.

JSON Schema#

El esquema del formato: keyrota-rotation.schema.json (draft 2020-12). Comprueba los campos, los tipos, los rangos, queue sin offGcd y la forma de las cadenas de condición. No comprueba que n y los nombres sean únicos, que existan los nombres usados en las condiciones ni el límite de 16 tras dividir las cadenas por and : eso lo hace KeyRota al cargar.

Para validar un archivo:

Texto
npx ajv-cli validate --spec=draft2020 -s keyrota-rotation.schema.json -d my-rotation.json

No pongas un campo "$schema" en un archivo de rotación: KeyRota lo toma por un campo sobrante.