Un agente de QA puede devolverte un análisis impecable sin haber leído una sola de tus reglas.
Lo encontré en mi propio harness, revisando la versión que estás a punto de instalar. En una de las herramientas soportadas, el agente cargaba las skills, corría el análisis y devolvía un resultado correcto, con el archivo de reglas sin instalar en ningún lado. Nadie lo habría notado nunca. El resultado se veía bien.
Por eso esta guía no termina cuando el instalador dice que terminó. Termina dos pasos después: cuando compruebas que las reglas te llegaron, y cuando ves al gate bloquear algo delante de ti.
Vamos a instalar QA Harness Pro, el arnés que rodea a tu agente de IA y le da tus herramientas, tu método y tus límites. En veinte minutos lo tienes vivo y analizando un ticket.
Esta es la guía de instalación, no la del método. Al terminar vas a tener el
harness vivo en tu máquina y un primer análisis corriendo sobre un ticket de
ejemplo. Necesitas una de las cuatro herramientas soportadas instalada
(Claude Code, Cursor, Antigravity o Codex) y python3, porque los hooks son
scripts de Python. Si vas a instalar para Cursor, Antigravity o Codex,
también jq.
Qué estás instalando, en una pantalla
Antes de correr nada, el mapa. El harness separa tres cosas que casi siempre viven mezcladas:
- El método (
skills/): cómo trabajas como QA. Es portable, no cambia cuando cambias de empresa. - La config (
companies/<empresa>.json): los datos de tu empresa. Se cambia en un archivo. - Tu perfil (
profile/profile.json): quién eres, tu nombre y tu tono. Es el que firma los cierres.
Esa separación es la razón por la que el día que cambies de trabajo copias un JSON y no tocas ni una skill.
El repo se prueba a sí mismo, y conviene saber cómo antes de instalarlo. Los 276 tests unitarios revisan cada regla por separado. Las 431 comprobaciones de humo instalan el harness entero, una vez por cada herramienta soportada, y después revisan archivo por archivo que quedó donde tenía que quedar. Todo corre en GitHub con cada cambio que se sube.
Y corre en carpetas temporales que se crean y se borran. Tu configuración real nunca se toca, ni siquiera cuando las pruebas fallan.
Los tres gates son deterministas: no dependen de que el modelo recuerde obedecer una regla escrita. Uno bloquea comandos destructivos antes de cada ejecución de shell. Otro valida el archivo después de cada edición. El tercero revisa el contenido antes de escribir en Jira, Confluence o Notion, y a ese lo vas a poner a prueba en el paso 8.
Prerrequisitos: por qué son esos dos y no más
python3. Los hooks están escritos en Python, así que sin él no hay gates. El validador tampoco arranca si falta:
❌ Necesito python3 para validar los JSON (viene con macOS/Linux). Instálalo y reintenta.jq. Este es condicional, y la condición importa. El instalador lo usa para fusionar tu JSON sin pisarlo cuando instala en Cursor, en Antigravity o en Codex, porque en esos tres casos toca archivos de configuración que ya son tuyos (~/.cursor/hooks.json, ~/.gemini/config/, ~/.codex/hooks.json) y que pueden tener cosas que tú pusiste antes. Con --agent claude no se toca ningún JSON ajeno, así que ni te lo pide.
Y si vas a instalar para Codex, python3 tiene que ser 3.11 o más nuevo: el instalador lo usa para validar tu ~/.codex/config.toml antes de tocarlo, y si no lo tienes para sin cambiar nada.
Si te falta, el instalador para en seco y te da el comando:
❌ Falta jq: lo necesita la instalación de 'cursor' para fusionar tu config JSON sin pisarla. macOS: brew install jq · Debian/Ubuntu: sudo apt install jqPaso 1 — Clona, y déjalo donde se va a quedar
git clone https://github.com/adrianagit87/qa-harness-pro.git ~/Proyectos/qa-harness-procd ~/Proyectos/qa-harness-proElige bien la carpeta. La instalación crea enlaces y rutas absolutas que apuntan acá: si después mueves el repo o lo borras, el harness deja de funcionar. No es irreversible: reinstalar desde la ruta nueva lo arregla, y el instalador reemplaza las entradas de la instalación anterior en vez de sumarles otras. Pero es una tarde perdida que puedes ahorrarte eligiendo bien la carpeta de una vez.
Paso 2 — Tu empresa, en un JSON
cp companies/_template.json companies/miempresa.jsonAbre el archivo que acabas de crear y complétalo. Lo mínimo que tiene que quedar resuelto:
tracker.hostytracker.cloudId: tu Jira. El host es solo el nombre del servidor, sinhttps://y sin rutas:tuempresa.atlassian.net. El cloudId es el identificador interno de tu instancia, y lo sacas abriendo en el navegadorhttps://tuempresa.atlassian.net/_edge/tenant_info: te devuelve un JSON y ahí está. El template deja en su lugar el textoPON-AQUI-TU-CLOUD-ID-DE-JIRA, que es el mismo tipo de marcador que el gate de publicación bloquea. Volvemos a eso en el paso 8.environments: los ambientes en los que tu equipo cierra ciclos, con sudefault. No están hardcodeados en las skills. Si tu equipo solo prueba en staging, declaras solo staging y listo.docs.backend: dónde quieres que el agente documente los análisis y los casos de prueba. Hay tres opciones:"jira"documenta dentro de los propios tickets,"confluence"crea páginas y"notion"también. Más abajo en el archivo hay un bloque de configuración para cada una, con los datos que cada herramienta necesita: el proyecto de QA y los tipos de issue para Jira, el espacio para Confluence, la base de datos para Notion. Completa solo el bloque de la opción que elegiste y deja los otros dos tal como vinieron.automation: dónde vive tu suite de tests automatizados y con qué framework está escrita. Es opcional, y conviene saber qué te pierdes si lo dejas vacío. El harness va a poder decirte igual si un caso vale la pena automatizar, pero no va a poder escribirte el código, porque no sabe dónde ponerlo ni qué convenciones sigue tu suite.
Cada campo está explicado en docs/CONFIG.md dentro del repo.
Ni companies/*.json ni profile/profile.json se versionan: están en el
.gitignore del repo. Lo que viaja versionado es el template vacío. Esto
importa más de lo que parece el día que quieras compartir el harness con
alguien del equipo.
Paso 3 — Tu perfil
cp profile/profile.example.json profile/profile.jsonEl ejemplo te muestra la forma exacta:
{ "name": "Tu Nombre", "role": "QA Engineer", "activeCompany": "acme", "language": "es", "tone": "directo, técnico, sin humo", "signature": "Tu Nombre — QA"}Dos campos que no son relleno. name es el nombre que va a firmar los cierres en Jira, así que pon el tuyo real. Y activeCompany tiene que coincidir con el nombre del archivo que creaste en el paso 2: si tu archivo es companies/miempresa.json, acá va "miempresa", sin la extensión.
Paso 4 — Valida antes de instalar
./validate-config.shEste script es el gate del propio harness, y hace bastante más que revisar que los JSON parseen.
Primero mira tu configuración: que el tracker sea coherente y que el browseUrlPattern apunte a tu host real y no al del template.
Después mira la del harness, que es la parte que no esperas. Comprueba que la transición de tickets de Jira siga estando denegada, que las escrituras hacia afuera sigan pidiendo confirmación, que los tres hooks sigan enganchados y que los servidores MCP sigan siendo los oficiales. Son las reglas que el repo promete: el validador confirma que siguen puestas y no solo escritas en el README. Si alguien las borró, o un merge se las llevó puestas, te enteras acá y no el día que el agente publique algo sin preguntarte.
Lo que no cubre es igual de importante que lo que cubre:
Leer el semáforo sin autoengañarte
El validador cierra con una de estas tres líneas:
❌ N error(es), M aviso(s). Corrige los errores antes de usar el harness.🟡 Configuración usable con N aviso(s).🟢 Configuración lista. Prueba la demo: abre Claude Code y escribe 'Analiza el ticket de demo/ticket-ejemplo.md'.Lo que tiene que quedar limpio son los ❌, no el color. El 🟡 significa “usable, con avisos”, y hay avisos perfectamente sanos.
Los dos avisos que vas a ver aunque hayas hecho todo bien
El primero aparece si no instalaste Claude Code. El validador revisa
claude siempre, porque .mcp.json y .claude/settings.json viajan
versionados en el repo. Así que instalando solo Cursor, solo Antigravity o
solo Codex te va a avisar que falta ~/.claude/CLAUDE.md y que no hay skills enlazadas.
Son avisos de algo que no instalaste, no fallas.
El segundo aparece si dejaste automation sin completar. También es
aviso, no error: la skill de automatización va a poder evaluar si vale la
pena automatizar, pero no generar código integrado a tu suite.
Pasarle tu runtime con --agent saca del medio los avisos de los otros
(./validate-config.sh --agent cursor, --agent all, --help), pero no los
de tu config: el 🟢 recién aparece cuando no queda ningún aviso pendiente.
Vale la pena correrlo otra vez después de instalar: en esa segunda pasada también verifica que las skills quedaron enlazadas de verdad, con symlinks que resuelven a este repo y no rotos ni apuntando a otro lado.
Paso 5 — Instala, y elige tu herramienta a mano
Hay un solo instalador, y le dices para qué herramienta con --agent:
./install.sh --agent claude # Claude Code./install.sh --agent cursor # Cursor./install.sh --agent antigravity # Antigravity (Gemini)./install.sh --agent codex # Codex CLI./install.sh --agent all # las cuatro, en ese orden./install.sh --help # la ayuda./install.sh a secas no instala nada. Imprime la ayuda y sale con error:
❌ Falta --agent: no adivino para qué herramienta quieres instalar.Esa decisión de diseño es la que más me gusta de todo el instalador, y la explico porque es una lección de QA disfrazada de detalle técnico. Si --agent tuviera un valor por default, digamos Claude Code, alguien que llegó buscando Cursor correría ./install.sh, vería un mensaje de éxito y se iría sin reglas. Un éxito falso. El instalador lo dice con todas las letras en su propio código: elegir por default sería un éxito ambiguo, y un fallo ruidoso es mejor.
Un instalador que adivina por ti no te está ahorrando un flag. Te está quitando la única oportunidad de que el error se vea temprano.
Puedes instalar más de una herramienta: corres el comando una vez por cada una, o usas --agent all. Comparten las mismas skills y la misma config, así que una edición del método se ve desde todas. Y con all, si una falla, las otras quedan instaladas igual y el comando te dice cuál falló.
Si ya tienes skills propias que se llaman igual que las del harness (en ~/.claude/skills o en ~/.agents/skills), el instalador se frena antes de tocar nada y te las lista. Para reemplazarlas vuelves a correrlo con --reemplazar-skills, y cada una queda respaldada como <nombre>.bak-<fecha>. Si tienes otras skills qa-*, también te avisa: sus triggers pueden pisarse con los del harness.
Qué deja cada instalación exactamente
--agent claude enlaza las skills en ~/.claude/skills y, si todavía no
tienes uno, instala el CLAUDE.md base del harness en ~/.claude/CLAUDE.md
resolviendo la ruta absoluta del repo. Si ya tienes el tuyo no lo toca:
te imprime la línea exacta que tienes que agregarle, un @ seguido de la
ruta a AGENTS.md, para que las reglas te lleguen igual. Los servers MCP ya
vienen definidos en .mcp.json, versionado en el repo: no contiene ningún
secreto, la autenticación es OAuth en el navegador.
--agent cursor fusiona los hooks en ~/.cursor/hooks.json y los
servers MCP en ~/.cursor/mcp.json sin pisar lo que ya tengas (hace backup
con timestamp de todo lo que toca), y sincroniza la rule en
.cursor/rules/qa-harness.mdc dentro del repo, porque Cursor lee las
rules del proyecto y no de tu HOME.
--agent antigravity fusiona hooks y servers MCP en ~/.gemini/config/,
también con backup, copia cada skill en ~/.gemini/config/skills/ y deja la
rule en .agents/rules/qa-harness.md del repo. Las skills van como copias y
no como enlaces porque la política de workspace de Antigravity no deja leerlas
a través de un symlink: si editas una skill, vuelve a correr el instalador, y
./validate-config.sh te avisa si alguna copia quedó vieja. Tu
~/.gemini/GEMINI.md no se toca. Después de reiniciar hay que comprobar en la
UI que la rule quede activa (Always on): la sintaxis para fijar el modo de
activación desde el archivo no está documentada, así que el harness no la
adivina.
--agent codex agrega los hooks del harness al final de cada evento
en ~/.codex/hooks.json (los tuyos quedan iguales y primero), suma a
~/.codex/config.toml un bloque entre marcas con el server de Atlassian, la
transición de tickets fuera de la lista de tools y aprobación obligatoria en
cada escritura, y agrega a ~/.codex/AGENTS.md un bloque corto que apunta al
AGENTS.md del repo. Las skills quedan enlazadas en ~/.agents/skills, donde
Codex las lee de forma nativa. Todo con backup, y tu contenido intacto.
Paso 6 — Reinicia tu herramienta y ábrela en la raíz del repo
Hablo de la herramienta para la que acabas de instalar: Claude Code, Cursor, Antigravity o Codex. Reiniciarla no es opcional. Las reglas y los hooks se leen al arrancar, así que la ventana que tenías abierta mientras instalabas no los tiene, por más que el instalador haya terminado bien.
Ciérrala del todo, vuelve a abrirla, y ábrela en la carpeta donde clonaste el harness. Esa carpeta también importa: los permisos, los hooks y las reglas aplican desde ahí. En Claude Code, .claude/settings.json vale en toda sesión abierta desde ahí; en Cursor y Antigravity, la rule vive en el scope del proyecto; en Codex, el AGENTS.md de la raíz se lee solo como instrucciones del proyecto.
La primera vez, Claude Code te va a pedir dos cosas: confiar en el workspace y aprobar los servers de .mcp.json. Acepta ambas. Sin el trust, Claude Code ignora los permisos allow de .claude/settings.json y el harness pierde parte de su configuración de seguridad. Después autenticas en el navegador, sin pegar tokens en ningún lado.
En Codex, aprueba los hooks con /hooks
Si instalaste para Codex, este paso no se puede saltar. Codex no corre un hook que no aprobaste, y no te avisa: sin este paso el harness queda instalado y no hay ningún gate.
- Abre Codex (
codex). - Escribe
/hooks. - Revisa los cinco hooks del harness y confía en ellos. Son cinco y no tres porque el gate de lo que el agente escribe por la terminal usa un par: uno saca una foto del disco antes del comando y el otro revisa lo que cambió. Aprueba los dos, porque con uno solo ese gate se abstiene en silencio.
Si algún día reinstalas desde otra carpeta, Codex te los vuelve a pedir. Y ./validate-config.sh --agent codex te avisa si los hooks todavía no pasaron por /hooks.
Conecta tu Jira, una sola vez
Los servidores que conectan tu Jira y tu Confluence ya vienen declarados en el repo, así que no tienes que copiar ninguna credencial ni pegar ningún token en ningún archivo. Lo único que falta es que autorices tu cuenta.
La primera vez que el agente use una herramienta de Atlassian se abre tu navegador con el login de Atlassian. Entras con tu cuenta de siempre, aceptas el acceso y vuelves a la herramienta. Queda conectado para las sesiones que vienen.
Si prefieres dejarlo hecho antes de necesitarlo, en Claude Code escribe /mcp: te lista los servidores conectados y te deja autenticar atlassian desde ahí. En Codex se hace desde la terminal con codex mcp login atlassian. En Cursor y en Antigravity se dispara con el primer uso, así que pídele algo simple para forzarlo, por ejemplo que te lea un ticket tuyo por su código.
Paso 7 — Comprueba que las reglas te llegaron
Acá viene la parte que quiero que no te saltees.
Abre un chat nuevo y escribe esto como única palabra:
PING-HARNESSEl contrato está escrito en el AGENTS.md del repo, y es literal:
PONG <activeCompany> <docs.backend> <docs.jira.qaProject o el destino que corresponda al backend>Leyendo esos valores de tu config real. Nada más: sin explicación, sin preámbulo. Si tu empresa es acme, tu backend es jira y tu proyecto de QA es PROJ, la respuesta correcta es PONG acme jira PROJ. Si tu docs.backend es confluence o notion, el tercer valor es el destino de ese backend, no un proyecto de Jira.
Si te contesta cualquier otra cosa, el método te llegó pero las reglas no. Y esta es la falla más traicionera del harness, porque el análisis igual sale, y sale bien. Lo que se pierde en silencio es que te muestre el borrador antes de publicar, que no toque estados de Jira y que no invente datos. Un análisis correcto te da la falsa confirmación de que todo está conectado.
Si falla, la causa casi siempre es una de dos: no reiniciaste de verdad, o no la abriste en la raíz del repo. Vuelve al paso 6.
Este ping comprueba dos cosas a la vez: que el archivo de reglas le llegó al agente, y que además pudo leer tu configuración. Si te contesta PONG pero con la empresa o el proyecto de otra, las reglas llegaron y los datos no: revisa el activeCompany de tu perfil.
Paso 8 — Comprueba que el gate muerde
El paso anterior comprobó que el agente leyó tus reglas. Pero las reglas son texto, y un modelo puede saltárselas. Los hooks no son texto: son programas que se ejecutan solos antes de cada acción y pueden frenarla.
El problema es que un hook mal enganchado no protesta. No tira error, no avisa, no aparece en ningún lado: deja pasar todo, exactamente igual que si no existiera. La única forma de saber si el tuyo está puesto es intentar romperlo a propósito una vez.
Pídele al agente que publique un comentario en Jira que contenga el texto:
PON-AQUI-EL-IDTiene que bloquearlo. Ese texto no es un ejemplo arbitrario: es uno de los patrones de placeholder que el gate de publicación reconoce, junto con las llaves dobles de plantilla, los TODO: y TBD:, los marcadores en ángulo tipo <TICKET>, y el host de ejemplo tuempresa.atlassian.net que trae el template de configuración. Si se te quedó el host del template a medio cambiar, el gate también te frena.
Si lo publica, el hook no está enganchado. Los hooks se leen al arrancar, igual que las reglas: reinicia y vuelve a probar. En Codex, revisa además que los hayas aprobado en /hooks.
Esta prueba necesita tu Jira ya conectado, porque el gate corre justo antes de una escritura real. Si te lo saltaste, vuelve al final del paso 6: es la única verificación de esta guía que no se puede hacer en seco.
El gate de publicación comprueba si el payload está listo para publicarse: rechaza publicaciones vacías, demasiado cortas o con placeholders sin resolver. No comprueba si tú autorizas la escritura. Esa decisión sigue siendo tuya y el harness no te la quita: son dos preguntas distintas y el harness no mezcla una con la otra.
Paso 9 — Tu primer análisis, sin tocar nada real
Recién ahora, la demo. Y va en este orden a propósito: si la corres antes de los pasos 7 y 8, sale bien igual y no te dice nada.
Analiza el ticket de demo/ticket-ejemplo.mdEl ticket de ejemplo es una historia de usuario de registro con verificación por email, y está incompleto a propósito. Lo que deberías ver:
- Un gate ⚠️ APROBADA CON OBSERVACIONES: el ticket tiene título, descripción, criterios y definición de Done, pero con huecos.
- Que detecte que “el formulario debe validar los campos” no es un criterio medible.
- Que detecte que “la política estándar de contraseñas” no está definida en ningún lado del ticket.
- Que convierta esos huecos en preguntas concretas para el PO y el dev, no en suposiciones.
- Que tome los comentarios del dev como instrucciones autoritativas: la expiración de 24 horas del link y el rate limit de 3 reenvíos por hora tienen que aparecer en los casos.
Después de esas observaciones viene la tabla de casos de prueba. Suelen salir entre diez y veinticinco, según cuánto alcance tenga el ticket, y cada uno lleva su prioridad.
Entre todos cubren cinco tipos de escenario: el camino feliz, los casos de error, los valores límite, la integración con otras partes del sistema y la seguridad.
Y cierra con una matriz de riesgos, que es un resumen aparte de por dónde puede romperse esto: las áreas críticas, las dependencias de las que depende y no controlas, los datos sensibles que toca y lo que pueda pegarle a la performance.
No publica nada en ningún lado. No hay Jira conectado en la demo y el agente no intenta inventarlo.
Fíjate en lo que acaba de pasar, porque es lo que se lleva tu líder y no solo tú: el gate del ticket es una decisión documentada sobre si ese requerimiento está listo para probarse. Cuando devuelves cuatro preguntas concretas antes del sprint en vez de veinte bugs después, no estás siendo más lento. Estás moviendo el hallazgo al momento en que todavía es barato.
Los límites, declarados
Todo harness honesto dice qué no hace. Estos tres los quiero explícitos antes de que los descubras en uso real:
1. Documenta, no mueve estados. Las transiciones de Jira las haces tú, a mano, siempre. No es una limitación técnica pendiente de resolver: es una decisión de diseño, y está reforzada con un deny sobre transitionJiraIssue en los permisos. El agente redacta el cierre, tú decides que el ticket cambia de estado. Un agente que mueve tickets convierte un error de lectura en un estado equivocado en el tablero de todo el equipo.
2. En Cursor y en Antigravity, el gate post-edición no bloquea en el momento. Esto es una diferencia real entre runtimes y conviene tenerla clara. En Claude Code y en Codex, el chequeo posterior a una edición devuelve el error al agente en el acto (en Codex el archivo ya quedó escrito: el gate no puede impedir la edición, pero el agente se entera y lo corrige). En Cursor, el hook afterFileEdit no puede bloquear: deja una marca y la levanta como ask en el siguiente comando de shell. En Antigravity el PostToolUse no tiene canal de feedback, así que un segundo hook levanta la marca en la llamada siguiente. En los cuatro casos el chequeo existe y te llega; en dos de ellos te llega un turno después.
Lo que el agente escribe por la terminal, con una redirección o un sed -i, pasa por el mismo chequeo en Claude Code y en Codex. En Cursor está enganchado según su documentación, pero todavía no está probado en una sesión real. En Antigravity no está cubierto.
3. En Cursor y en Codex, la confirmación interactiva no la pone el hook. En los dos, el hook de MCP solo puede denegar, no preguntar. En Cursor la confirmación la pone el allowlist de herramientas MCP, así que no marques las herramientas de escritura como “siempre permitir”: si lo haces, el hook queda como única defensa. En Codex la pone el bloque del harness en config.toml, que pide aprobación en cada escritura y ni siquiera le ofrece al modelo la herramienta de transicionar tickets.
Y una limitación conocida de Antigravity que conviene saber antes
Antigravity tiene un límite documentado de 12.000 caracteres por archivo de
reglas, y las dos skills más grandes del método lo superaban: se observó una
listada por nombre que no llegó a cargarse. En la v2.2.0 el detalle de cada
paso se movió a references/, dentro de la carpeta de la skill, y las dos
quedan por debajo del límite. Eso todavía no está verificado en una
instalación real. Claude Code, Cursor y Codex no están afectados. La medición
y lo que falta probar están en adapters/antigravity/README.md.
Hay además un detalle que hay que cerrar a mano la primera vez: los nombres
de las tools MCP de Antigravity no están documentados, así que los hooks las
reconocen por patrón y anotan en un log cualquier tool que huela a Atlassian
o Notion y no haya matcheado. Pides una lectura y un comentario en Jira,
miras el log y, si aparece algo, lo agregas al catálogo compartido en
core/gates/catalogo.py. Es el único lugar donde se tocan: todos los runtimes
heredan el cambio.
Qué tienes ahora que no tenías hace veinte minutos
Un agente de QA con tu método escrito, tus herramientas conectadas y tres límites que no dependen de que el modelo se acuerde de respetarlos. Y, más importante, la capacidad de comprobar que sigue vivo: dos verificaciones de treinta segundos que puedes repetir cada vez que actualices el repo, cambies de máquina o sumes a alguien del equipo.
Esa es la diferencia entre un harness que funciona y uno que solo lo parece. La segunda categoría existe, es silenciosa, y es exactamente la razón por la que los pasos 7 y 8 no son opcionales.
El repo es de código abierto bajo licencia MIT: github.com/adrianagit87/qa-harness-pro. Si algo no te funciona o quieres proponer una mejora, abre un issue.
Y cuando salga una versión nueva: git pull en la carpeta del repo, vuelves a correr el instalador de tu herramienta, reinicias y repites los pasos 7 y 8. En Codex, pasa antes por /hooks: te muestra solo los hooks nuevos para aprobar. Nunca des por hecho que una actualización dejó el gate donde estaba.