Tag: Playwright

  • Agentes de IA en el navegador: por qué fallan y cómo arreglarlo

    Agentes de IA en el navegador: por qué fallan y cómo arreglarlo

    Tenía un agente que hacía una cosa aburrida y la hacía bien: leer una cola, crear el recurso por API, comprobar la respuesta, seguir. Si algo fallaba, lo repetía. Lo dejé corriendo toda una tarde sin mirarlo.

    Luego apunté ese mismo agente al navegador, porque el proveedor de turno no tenía API. Misma tarea, mismo bucle, misma cabeza: así se montan hoy casi todos los agentes de IA en el navegador. Y ahí aparece el clásico. El clic de "Confirmar" parece no responder, el bucle reintenta, y al otro lado quedan dos pedidos idénticos.

    No fue un bug del modelo. Fue el bucle haciendo exactamente lo que le pedí: reintentar. Hereda una política diseñada para un mundo donde reintentar es gratis, y el navegador es justo el sitio donde deja de serlo.

    Esto no es un tutorial. Si quieres montarlo y verlo funcionando, el cómo está en automatizar la usabilidad con agentes de IA. Aquí va lo otro: por qué se rompe cuando lo pones a trabajar de verdad. Se rompe siempre por los mismos tres sitios —las esperas, el estado y las acciones que no se pueden deshacer— y ninguno de los tres es culpa del modelo.

    Los agentes de IA en el navegador heredan un bucle que no es suyo

    Un agente es un bucle: observa, decide, actúa, vuelve a observar. Lo conté con detalle en qué es el agentic loop, y todo lo que envuelve al modelo —tools, memoria, política de errores— es lo que llamamos harness.

    Ese harness lleva dentro una suposición que casi nadie hace explícita: si una acción falla, se puede volver a intentar.

    Con una API la suposición se sostiene. El POST devolvió timeout, no sabes si llegó, lo repites. Si la API es decente tienes una clave de idempotencia. Si no, tienes un staging donde da igual. El coste de un reintento es latencia.

    En el navegador se cae. La acción ya salió de tu sistema: un pedido confirmado, un correo enviado, un registro borrado, una publicación en una cuenta real. Y tu bucle no recibe ningún código de estado: recibe una página repintada. Saber si aquello se completó es un paso aparte, que tienes que pedir tú. Casi nadie lo pide.

    Y aquí está el detalle que provoca el desastre: nadie cambia el bucle al cambiar de herramienta. Sustituyes http_request por browser_click en la lista de tools y sigues con la misma política de reintentos y la misma tolerancia a errores.

    Fallo 1: las esperas. Clicable no significa listo

    Playwright, antes de actuar, comprueba que el elemento sea accionable. Son los actionability checks, y la documentación oficial los define así:

    • Visible: tiene un bounding box no vacío y no tiene visibility: hidden.
    • Stable: ha mantenido el mismo bounding box durante al menos dos frames de animación consecutivos.
    • Receives Events: es el hit target del evento de puntero en el punto de la acción.
    • Enabled: no está deshabilitado.

    Lee esas cuatro definiciones otra vez. Son geometría y DOM. Ninguna dice nada sobre si tu aplicación tiene sentido.

    Un botón "Confirmar pedido" puede estar visible, quieto durante dos frames, habilitado y ser el hit target mientras el precio final todavía se recalcula en el backend. Pasa los cuatro checks. El clic es prematuro. Playwright no ha mentido: nunca prometió esperar a que la app estuviera lista, solo a que el elemento fuera clicable.

    Hay más. No todas las acciones piden lo mismo:

    Acción Comprobaciones
    click(), dblclick(), check(), uncheck(), tap() Visible, Stable, Receives Events, Enabled
    fill(), clear() Visible, Enabled, Editable
    selectOption() Visible, Enabled
    hover(), dragTo() Visible, Stable, Receives Events
    press(), pressSequentially(), focus(), blur(), dispatchEvent(), setInputFiles() Ninguna

    Fíjate en dos cosas. fill() no espera a Stable: comprueba que el campo sea visible, esté habilitado y no sea de solo lectura, pero puedes escribir en un input que se está moviendo. Y hay un grupo entero de acciones sin ninguna comprobación, incluida dispatchEvent().

    Ese grupo importa porque es la salida de emergencia. Cuando el clic normal "no funciona", el camino que aparece es disparar el evento a mano. En código de Playwright, locator.dispatchEvent('click'). Desde MCP no existe una tool equivalente: lo que hay es browser_evaluate, que ejecuta tu JavaScript sobre el elemento, y browser_run_code_unsafe, que la propia documentación describe como equivalente a ejecución remota de código.

    Tres caminos distintos y el mismo efecto: ninguno pasa por los actionability checks.

    El agente cree que ha encontrado una solución ingeniosa. Lo que ha hecho es quitar las comprobaciones que le impedían hacer clic demasiado pronto.

    La corrección no es esperar más. Es esperar a otra cosa: a la condición de negocio, no al elemento.

    // Espera al elemento. Pasa los cuatro checks y hace clic.
    await page.getByRole('button', { name: 'Confirmar pedido' }).click();
    
    // Espera a que la aplicación esté lista, y entonces hace clic.
    await expect(page.getByTestId('gastos-envio')).toHaveText(/\d/);
    await expect(page.getByTestId('spinner-total')).toBeHidden();
    await page.getByRole('button', { name: 'Confirmar pedido' }).click();
    

    Ojo con el orden: toBeHidden() en primera posición pasaría antes de que el spinner llegue a aparecer. Solo es seguro después de una aserción positiva.

    Con Playwright MCP el equivalente es browser_wait_for sobre un texto que solo existe cuando el backend ya respondió. No "Total" —que está en el DOM desde el primer render—, sino "Envío calculado" o el importe con su formato final.

    Esperar por estado y no por píxeles es la misma disciplina que separa una suite de tests fiable de una que falla los martes. La trabajo a fondo en el curso de Testing en Angular, y se traslada tal cual a los agentes.

    Fallo 2: el estado. El agente decide sobre una foto caducada

    En el fallo anterior el elemento no estaba listo. Aquí el elemento está listo, pero el agente mira una foto de hace tres segundos. La carrera es la misma; la corrección, no.

    Playwright MCP no le manda screenshots al modelo. Le manda accessibility snapshots: un árbol estructurado de elementos accesibles con refs para interactuar, del tipo e5.

    Es la decisión correcta. Playwright documenta un coste aproximado de 200-400 tokens por snapshot frente a 3.000-5.000 de un screenshot para un modelo de visión. Barato, textual, determinista.

    Pero la letra pequeña está en la misma página: los refs son estables dentro de un único snapshot —el mismo elemento mantiene el mismo ref hasta que la página cambia— y quedan invalidados cuando la página cambia.

    Traducción: el snapshot es una fotografía. Y entre la fotografía y el clic hay una llamada a un LLM que tarda segundos.

    En esos segundos tu SPA puede recibir un evento por WebSocket, reordenar una lista, cerrar un modal o repintar una tabla. Si el nodo desapareció, el ref deja de resolver y la tool falla: molesto, pero visible.

    El caso feo es el otro. Si tu framework recicla el nodo en vez de recrearlo —listas sin key o sin trackBy, scroll virtual—, el e5 que en la foto era "Eliminar borrador" de la fila 3 sigue vivo y ahora es el de la fila 4. Resuelve, el clic sale, y borras lo que no era. El agente no actúa sobre la página: actúa sobre su recuerdo de la página.

    Un locator de Playwright se resuelve en el instante de actuar. Un ref se resolvió antes de que el modelo empezara a pensar. Toda la diferencia está ahí.

    Hay un segundo efecto del estado que casi nadie cuenta, y es el que más planes rompe.

    No puedes paralelizar agentes de navegador como paralelizas agentes de API. Playwright MCP arranca por defecto con un perfil persistente —ahí viven las sesiones y las cookies— y la documentación es tajante: un perfil persistente solo lo puede usar una instancia de navegador a la vez, así que varios clientes MCP concurrentes que compartan el mismo workspace entrarán en conflicto.

    Con la configuración por defecto, ese perfil es un singleton. Lanzar diez subagentes contra la misma cuenta no te da diez veces el rendimiento: te da un conflicto, o peor, diez agentes pisándose la sesión.

    La propia documentación da dos salidas: arrancar cada cliente extra con --isolated, o apuntarlo a un --user-data-dir distinto. La segunda te conserva la sesión guardada. La primera arranca limpia y pierde todo su storage al cerrar el navegador, así que le inyectas el estado inicial con --storage-state.

    Para un agente que reintenta, quédate con la primera. Pierdes comodidad y ganas algo más valioso: poder tirar el estado y repetir el intento desde cero.

    Fallo 3: el bucle no distingue lo que se puede deshacer

    Divide en dos columnas todo lo que hace tu agente.

    Idempotente: navegar, leer, hacer scroll, sacar un snapshot, abrir una pestaña, leer la consola. Repetirlo cien veces no cambia el mundo. Como mucho gasta tokens.

    Con efecto externo: enviar el formulario, confirmar el pago, borrar el registro, publicar el post, invitar al usuario. Repetirlo cambia el mundo, y a veces cambia el mundo de otra persona.

    Para el harness las dos son idénticas. browser_click sobre "Volver" y browser_click sobre "Confirmar pedido" son la misma tool call con distinto ref. El modelo cree que sabe la diferencia; el bucle, que es quien decide reintentar, no la sabe. Sobre los límites reales de un bucle en producción escribí en el loop del agente en producción.

    La confirmación de que esto es un problema real no la pongo yo. La pone Anthropic.

    Claude for Chrome, en beta para planes de pago, navega, hace clic y rellena formularios en tu navegador. El usuario puede pre-aprobar por sitio las acciones que le permite. Y aun así, el producto sigue preguntando antes de ciertas acciones irreversibles o potencialmente dañinas, como hacer una compra.

    Si el reintento fuera seguro, ese gate no existiría. Nadie construye una interrupción humana específica alrededor de "comprar" por gusto: la construye porque comprar dos veces no se arregla razonando mejor. Es el mismo principio de aprobación previa del que hablé en computer use con Claude Code.

    El navegador viene con tus credenciales dentro

    La documentación de Playwright MCP tiene una frase que deberías pegar en la pared antes de darle un navegador a un agente:

    Playwright MCP is not a security boundary.
    (Playwright MCP no es una frontera de seguridad.)

    No es un aviso legal. Es una descripción exacta de lo que estás montando. Ese navegador lleva las sesiones del usuario real, sus cookies, sus tokens y su banca abierta en otra pestaña. Y cualquier texto que la página muestre entra en el contexto del modelo.

    Anthropic lo midió sobre su propia extensión. En el anuncio del piloto de Claude in Chrome, de agosto de 2025: 123 casos de prueba sobre 29 escenarios de ataque, 23,6% de éxito de la inyección de prompts sin mitigaciones y 11,2% con las defensas activadas en modo autónomo.

    Fíjate en que el segundo número no es cero. Algo más de uno de cada diez intentos seguía pasando, en la configuración de quien fabrica el modelo y tiene todos los incentivos para que no pase.

    Darle un navegador a un agente no es añadir una tool más al array. Es una decisión de arquitectura sobre qué credenciales pones al alcance de un bucle que, por diseño, insiste.

    Qué hacer con esto hoy

    1. Afirma el estado, no esperes al elemento. Antes de cada acción con efecto, exige una condición de negocio verificable: un texto que solo aparece cuando el backend respondió, un importe con formato final, un contador que cuadra. Si no sabes escribir esa aserción, tu agente tampoco sabe si la app está lista.

    2. Clasifica tus acciones y cierra las pocas irreversibles con un gate. No pidas aprobación para todo: eso degenera en aprobar en automático en tres días. Pídela solo en la columna corta. En Claude Code se implementa con hooks, como lo monté en hooks como guardrails.

    3. Usa una clave de idempotencia cuando la aplicación te la dé. Un identificador de operación en el formulario, un borrador con ID estable, un endpoint que acepta la misma referencia dos veces. Si controlas la app de destino, es media hora de trabajo y elimina la clase entera de fallo.

    4. Trabaja con perfil aislado y --storage-state. Estado desechable significa reintentos limpios. Estado persistente significa que el segundo intento arranca desde la basura del primero.

    5. Verifica después de actuar, no solo antes. browser_network_requests y browser_console_messages te dicen si la petición salió y si la app se quejó. Actuar a ciegas y volver a intentar es exactamente cómo se duplican pedidos.

    Diseñar así el bucle —qué se reintenta, qué se para, qué se verifica— es lo que separa un agente de demo de uno que dejas suelto, y es lo que trabajamos en el curso Construye con IA.

    El bucle no va a distinguir por ti entre leer y comprar. Esa frontera la dibujas tú, hoy, antes de la próxima ejecución. Si quieres ver estos patrones aplicados sobre proyectos reales y con el código delante, en Dominicode Labs es donde los montamos.

    Preguntas frecuentes

    ¿Por qué mi agente funciona bien contra APIs y falla en el navegador?

    Porque el bucle está construido sobre la idea de que una acción fallida se puede repetir. Con una API eso suele ser cierto y barato. En el navegador, la acción ya produjo un efecto fuera de tu sistema —un pedido, un correo, un borrado— y confirmar si se completó exige un paso extra que el bucle no da solo. Cambiaste la herramienta, no la política de reintentos.

    Si Playwright ya espera a que el elemento sea accionable, ¿por qué hace clic antes de tiempo?

    Porque sus comprobaciones son geométricas y de DOM: visible, estable, hit target y habilitado. Estable significa que el bounding box no cambió durante dos frames de animación, no que los datos hayan cargado. Un botón puede pasar los cuatro checks mientras el backend todavía calcula. Clicable no es lo mismo que listo.

    ¿Cómo hago que un agente espere a que la aplicación esté lista de verdad?

    Espera por una condición de negocio, no por un elemento: un texto que solo se renderiza cuando llegó la respuesta, un importe con su formato final, un estado que pasó de "calculando" a un valor. Con Playwright MCP, browser_wait_for sobre ese texto. Regla práctica: si el marcador que esperas ya está en el DOM en el primer render, no te sirve.

    ¿Puedo lanzar en paralelo varios agentes de IA en el navegador?

    No con la configuración por defecto. Playwright MCP usa un perfil persistente y su documentación advierte de que solo lo puede usar una instancia de navegador a la vez, así que varios clientes MCP concurrentes en el mismo workspace entran en conflicto. La salida documentada es arrancar cada cliente extra con --isolated —con --storage-state si hace falta sesión inicial— o apuntarlo a un --user-data-dir distinto si quieres conservar la sesión. Y, normalmente, cuentas distintas.

    ¿Qué acciones de un agente de navegador deberían pedir aprobación humana?

    Solo las que no se pueden deshacer: pagos, envíos definitivos, borrados, publicaciones e invitaciones. Navegar, leer o sacar snapshots no necesitan gate. Es la línea que sigue Claude for Chrome, que pre-aprueba por sitio y aun así vuelve a preguntar antes de acciones irreversibles como una compra. Si pides confirmación para todo, en tres días la darás en automático.

    ¿Es mejor accessibility snapshot o screenshot para un agente de navegador?

    El snapshot, casi siempre. Playwright documenta un coste aproximado de 200-400 tokens por snapshot frente a 3.000-5.000 de un screenshot para un modelo de visión, y además da refs con los que interactuar. Su límite es otro: esos refs solo son estables dentro de ese snapshot y se invalidan cuando la página cambia. El screenshot sigue disponible como tool por defecto (browser_take_screenshot) para lo que el árbol de accesibilidad no expresa, como un canvas o un mapa. Lo que habilita --caps=vision no es la captura: es poder interactuar por coordenadas sobre ella.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Cómo automatizar usabilidad con agentes de IA en tu app

    Cómo automatizar usabilidad con agentes de IA en tu app

    Un cliente me enseñó su aplicación de onboarding hace unos meses. Cinco pasos. Diseño limpio. Todo funcional en los tests.

    La tasa de abandono era del 68% en el paso tres.

    Revisamos los tests de integración: pasaban todos. El formulario enviaba datos correctamente. La validación funcionaba. El CI estaba en verde. Y aun así, casi siete de cada diez usuarios salían del flujo antes de terminar.

    El problema no era que la aplicación no funcionara. Era que nadie había auditado cómo se usaba.

    Ahí está la diferencia que muchos developers no distinguen hasta que lo ven en métricas reales: una cosa es que el código haga lo que debe, y otra muy distinta es que el usuario pueda usarlo sin fricción. La primera la resuelves con tests. La segunda la resuelves con automatizar usabilidad con agentes de IA — que es exactamente lo que vamos a ver aquí.

    Automatizar usabilidad con agentes de IA consiste en delegar la auditoría de accesibilidad, flujos de usuario y fricciones de interfaz a un agente — como Claude Code con MCP de Playwright — que controla el navegador de forma programática, navega flujos reales y genera un reporte estructurado de hallazgos sin intervención manual.


    Testing funcional vs. testing de usabilidad

    Un test funcional pregunta: ¿el botón de "Enviar" llama a la función correcta?

    Un test de usabilidad pregunta: ¿el usuario sabe que tiene que hacer clic ahí? ¿Lo ve? ¿Entiende qué va a pasar después?

    Son preguntas distintas y se responden con herramientas distintas.

    Los tests funcionales los escribes tú, los corre un CI, comprueban comportamiento esperado. Eso ya lo sabes hacer. Lo que un agente de IA aporta es la capacidad de navegar tu aplicación como si fuera un usuario — explorar rutas no documentadas, detectar elementos sin etiquetas accesibles, medir cuánto tarda en responder una pantalla, o identificar que un campo de error aparece debajo del scroll y el usuario nunca lo ve.

    No te reemplaza un test de usabilidad con personas reales. Pero te da un nivel de auditoría automatizada que antes no existía — y que tú nunca harías manualmente en cada PR. Si quieres ver cómo encaja esto en un pipeline completo de desarrollo, tienes la explicación en el post sobre automatizar el proceso de desarrollo con IA.


    Qué puede detectar un agente

    Cuando conectas Claude Code con el MCP de Playwright o Chrome DevTools, el agente puede controlar el navegador de forma programática. Eso significa que puede:

    • Navegar a cualquier ruta de tu aplicación
    • Hacer clic en elementos, rellenar formularios, desplazarse por la página
    • Leer el DOM y el árbol de accesibilidad (el que usa un lector de pantalla)
    • Medir tiempos de respuesta entre interacciones
    • Ejecutar axe-core para detectar violaciones WCAG
    • Capturar screenshots en cada paso del flujo
    • Generar un reporte estructurado con los hallazgos

    Lo que un agente detecta bien:

    1. Accesibilidad técnica: imágenes sin alt, botones sin aria-label, contraste insuficiente entre texto y fondo, formularios sin etiquetas asociadas, skip navigation ausente, foco de teclado atrapado en un componente modal.
    2. Fricciones estructurales: mensajes de error fuera del viewport, campos obligatorios que no se identifican como tales hasta el submit, pasos de onboarding que no guardan el progreso si el usuario recarga.
    3. Performance percibida: cuánto tarda en aparecer el primer elemento interactivo después de una navegación, si hay loaders sin indicación de progreso, si el layout shift hace que el usuario haga clic en el elemento equivocado.
    4. Cobertura de flujos: si una ruta de error (credenciales incorrectas, sesión expirada, red caída) termina en una pantalla sin instrucciones claras.

    Cómo configurar el agente para automatizar la auditoría de usabilidad

    La combinación que funciona en producción: Claude Code + MCP de Playwright.

    Para instalarlo: npx @playwright/mcp@latest. Una vez activo en tu configuración de Claude Code, el agente tiene acceso a las herramientas de control del navegador sin que escribas una línea de Playwright.

    El MCP de Playwright expone herramientas al agente para controlar el navegador: browser_navigate, browser_click, browser_type, browser_snapshot, browser_evaluate. Claude puede encadenar esas herramientas para ejecutar flujos completos.

    Un ejemplo de instrucción al agente para auditar el onboarding de una app:

    ## Tarea: Auditoría de usabilidad — flujo de onboarding
    
    URL base: http://localhost:4200
    
    Flujo a auditar:
    1. Navega a /register
    2. Rellena el formulario con datos válidos: nombre, email, contraseña
    3. Haz clic en "Crear cuenta"
    4. Completa los pasos del onboarding hasta llegar al dashboard
    
    En cada paso:
    - Captura un screenshot
    - Extrae el árbol de accesibilidad del contenido principal
    - Identifica elementos interactivos sin aria-label o sin texto visible
    - Mide el tiempo hasta que el siguiente paso es interactivo
    - Detecta si hay mensajes de error o advertencia y si son visibles sin scroll
    
    Al terminar, genera un reporte en formato JSON con esta estructura:
    {
      "paso": string,
      "url": string,
      "tiempo_carga_ms": number,
      "violaciones_accesibilidad": [],
      "fricciones_detectadas": [],
      "screenshot": string
    }
    

    El agente ejecuta eso de forma autónoma. Navega, interactúa, observa, y vuelve con un reporte estructurado.


    Accesibilidad automática con axe-core

    Para violaciones WCAG, la integración más sólida es axe-core. El agente puede ejecutarlo sobre cualquier página activa en el navegador mediante browser_evaluate:

    // Si axe-core no está en el bundle de la app, inyectarlo primero:
    // await page.addScriptTag({ url: 'https://cdn.jsdelivr.net/npm/axe-core/axe.min.js' });
    
    // Ejecutar la auditoría en el contexto de la página
    const results = await axe.run();
    return {
      violaciones: results.violations.map(v => ({
        impacto: v.impact,
        descripcion: v.description,
        elementos: v.nodes.map(n => n.target)
      }))
    };
    

    Lo que devuelve axe-core son violaciones categorizadas por impacto: critical, serious, moderate, minor. El agente puede filtrar solo las críticas, agregar el selector del elemento afectado, y generar una lista accionable para el developer.

    Esto detecta cosas como:

    • Contraste de color insuficiente (ratio menor a 4.5:1 para texto normal)
    • Imágenes sin atributo alt o con alt vacío en imágenes informativas
    • Elementos <div> y <span> usados como botones sin rol ARIA
    • Formularios sin <label> asociado o con placeholder como único identificador
    • Encabezados fuera de jerarquía (<h4> después de <h2> sin <h3>)

    Esta es una de las capacidades que trabajamos en detalle en el curso Construye con IA: cómo delegar auditorías estructuradas al agente para que el developer se centre en las decisiones de producto, no en el checklist técnico.


    El reporte de hallazgos

    Un agente que navega y detecta problemas no sirve de nada si los hallazgos terminan en un log de consola que nadie lee.

    El formato que mejor funciona para integrar en un workflow de desarrollo es un JSON estructurado que puedas convertir en un issue de GitHub, una tarea en Linear, o un comentario en un PR.

    Estructura mínima de reporte que el agente genera:

    {
      "auditoria": {
        "fecha": "2026-06-17",
        "url_base": "https://app.ejemplo.com",
        "flujo": "onboarding",
        "duracion_total_ms": 8420
      },
      "resumen": {
        "violaciones_criticas": 3,
        "violaciones_serias": 7,
        "fricciones_detectadas": 4,
        "pasos_con_retraso": 2
      },
      "hallazgos": [
        {
          "paso": "registro",
          "tipo": "accesibilidad",
          "impacto": "critical",
          "descripcion": "Campo de contraseña sin label asociado. Solo usa placeholder.",
          "selector": "#password-input",
          "referencia_wcag": "1.3.1"
        },
        {
          "paso": "paso-2-perfil",
          "tipo": "friccion",
          "impacto": "serious",
          "descripcion": "Mensaje de validación aparece 280px por debajo del campo en mobile. No visible sin scroll.",
          "selector": ".validation-message",
          "screenshot": "paso-2-error-state.png"
        }
      ]
    }
    

    Este reporte lo puedes consumir directamente en tu pipeline de CI, enviarlo a un webhook de Slack, o procesarlo con otro agente que abra los issues correspondientes.


    Agentes IA vs Lighthouse

    Capacidad Lighthouse Agente IA + MCP Playwright
    Métricas de rendimiento (LCP, CLS, FID)
    Accesibilidad estática (axe-core) ✅ más granular
    Flujos interactivos multipaso
    Estados de error y modales
    Reporte adaptado al contexto del proyecto ✅ JSON estructurado
    Requiere código de automatización ❌ lenguaje natural
    Integración en CI/CD

    Lighthouse mide el estado de una página en un instante. Un agente mide cómo un usuario real la recorre.


    Lo que el agente no puede hacer

    Esto es importante. Un agente mide lo que puede observar en el DOM y en el comportamiento de la interfaz. No puede medir lo que ocurre dentro del usuario.

    No detecta:

    • Frustración emocional. Si el flujo es técnicamente correcto pero genera ansiedad porque el lenguaje es frío o las instrucciones son ambiguas, el agente no lo sabe.
    • Preferencias estéticas. El contraste puede pasar el ratio WCAG y aun así resultar incómodo visualmente en contextos específicos.
    • Contexto cultural. Un ícono que es intuitivo para un usuario europeo puede no serlo para un usuario latinoamericano. El agente no tiene ese mapa cultural.
    • Carga cognitiva subjetiva. Puede detectar que hay ocho campos en un formulario, pero no puede decirte si eso es demasiado para tu audiencia específica.
    • Microcopy y confianza. El texto de un CTA puede ser técnicamente legible y aun así no generar suficiente confianza para que el usuario haga clic.

    Esas decisiones siguen siendo tuyas — o del diseñador, o del researcher de UX. Lo que el agente elimina es el trabajo de auditoría técnica repetitiva que de otra forma no harías en cada ciclo de desarrollo.


    Cómo integrarlo en tu workflow

    El patrón que funciona sin complicar el pipeline:

    1. Local, bajo demanda: el developer lanza la auditoría sobre la rama antes de abrir el PR. El agente revisa el flujo afectado por el cambio.
    2. En CI, sobre entornos de preview: cada PR despliega a un entorno de preview (Vercel, Netlify, Railway), y el agente audita ese entorno de forma automática antes del merge.
    3. Semanal, sobre producción: un job programado lanza la auditoría completa sobre la app en producción y genera un reporte que llega al equipo.

    El tercer nivel es el más valioso a largo plazo: detecta regresiones de accesibilidad que se cuelan en producción sin que nadie las vea en los tests unitarios.

    El paso de code review automático antes del PR — que complementa esta auditoría de usabilidad — lo explico en detalle en el post sobre agentic code review con Claude Code.

    Si quieres ver cómo construir este tipo de pipelines con agentes desde cero, en Dominicode Labs tenemos proyectos completos que aplican exactamente este enfoque — desde la configuración del MCP hasta la generación del reporte final.


    FAQ

    ¿Necesito conocer Playwright para esto?
    No necesitas escribir código Playwright. El MCP abstrae las herramientas de control del navegador y el agente las usa directamente. Basta con que describas el flujo que quieres auditar en lenguaje natural.

    ¿axe-core cubre todos los criterios WCAG?
    Cubre los criterios que son detectables automáticamente — menos de la mitad de los criterios de WCAG 2.1. El resto requiere evaluación humana. Pero ese 30-40% incluye los problemas más comunes y los más graves.

    ¿El agente puede auditar aplicaciones con autenticación?
    Sí. Puedes darle al agente las credenciales de una cuenta de prueba, o configurar el MCP para que arranque el navegador con una sesión ya autenticada. El agente navega como un usuario real, incluyendo el flujo de login.

    ¿Qué diferencia hay entre esto y Lighthouse?
    Lighthouse audita métricas de performance, SEO básico y accesibilidad en un snapshot estático. Un agente con MCP de Playwright puede auditar flujos interactivos completos — formularios multipaso, modales, estados de error, interacciones con el teclado — y generar reportes adaptados a tu contexto específico, no a un checklist genérico.

    ¿Puedo usar esto con cualquier framework frontend?
    Sí. El agente interactúa con el navegador, no con el framework. Funciona igual con Angular, React, Vue o cualquier app renderizada en el cliente o en el servidor.

    La próxima vez que un cliente te muestre métricas de abandono con todos los tests en verde, ya sabes qué está pasando — y cómo resolverlo.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.