tactual-mcp

Analizador de costos de navegación con lectores de pantalla que mide el esfuerzo real de navegación para usuarios de tecnología de asistencia mediante la construcción de un gráfico ponderado a partir de instantáneas de accesibilidad de Playwright y puntuando cada objetivo bajo perfiles reales de tecnología de asistencia (NVDA, JAWS, VoiceOver, TalkBack, móvil genérico).

Documentación

Tactual

Tactual logo

Analizador de coste de navegación para lectores de pantalla. Mide cuántas pulsaciones de teclas necesita un usuario de lector de pantalla para descubrir, alcanzar y operar cada objetivo interactivo de tu página — bajo un perfil de tecnología de asistencia (AT) específico (NVDA, JAWS, VoiceOver).

Qué hace

Las herramientas de accesibilidad existentes comprueban la conformidad — ¿es correcto el ARIA? ¿Es suficiente la relación de contraste?

Tactual mide el coste de navegación — ¿cuántas acciones necesita un usuario de lector de pantalla para llegar al botón de pago? ¿Qué ocurre si se pasa de largo? ¿Puede siquiera descubrir que existe? ¿Se abre el menú realmente con Enter, o solo con clic? ¿El foco cae en el primer elemento del menú o se queda atascado en el disparador?

Cómo funciona:

  • Captura instantáneas de accesibilidad de Playwright + simulación de anuncios del lector de pantalla
  • Explora opcionalmente ramas ocultas (menús, diálogos, pestañas, divulgaciones) y las prueba con eventos de teclado reales, incluidos contratos de widgets estilo APG y flujos de errores de formularios
  • Construye un grafo de navegación con puntos de entrada (hitos, encabezados, Tab lineal) y puntúa cada objetivo
  • Valida opcionalmente las rutas predichas contra @guidepup/virtual-screen-reader para calibración

Tactual es una herramienta de desarrollo para analizar tus propios sitios y entornos de preparación. Ejecútalo localmente, en CI, o mediante el servidor MCP en tu editor. No es un servicio público de escaneo.

Cómo encaja

Tactual complementa los escáneres de conformidad como axe-core, Lighthouse y Pa11y. Esas herramientas siguen siendo la primera pasada correcta para una amplia cobertura de reglas WCAG y ARIA. Tactual está orientado a la siguiente pregunta: después de que una página tenga un marcado válido, ¿cuán costoso es para un usuario de AT descubrir, alcanzar y operar los objetivos importantes?

Usa Tactual para el triaje de coste de navegación con lector de pantalla, trazado de rutas, evidencia medida de teclado/widget, diferencias antes/después, priorización en CI y flujos de trabajo MCP donde un agente necesita hallazgos compactos con selectores de origen y candidatos de remediación. Usa lectores de pantalla reales y pruebas manuales para la validación final de recorridos críticos, flujos sensibles al tiempo, configuraciones de navegador/AT y patrones de implementación que difieren intencionalmente de un ejemplo APG común.

Ruta rápida para agentes: este README es una visión general del producto más una referencia. Los agentes deberían comenzar con docs/AGENT-RECIPES.md para patrones de tareas y docs/MCP-TOOLS.md para esquemas MCP completos, y luego volver aquí solo para contexto del producto, notas de instalación y ejemplos de superficie de lanzamiento.

Instalación

Requiere Node.js 20 o posterior.

npm install tactual

Tactual instala Playwright como dependencia de ejecución para que los comandos npx tactual@latest ... puntuales funcionen sin instalar Playwright por separado en la caché de npx. El SDK de MCP también se incluye como dependencia de ejecución, por lo que tactual-mcp funciona desde un paquete tactual instalado sin una instalación separada del SDK.

Inicio rápido

CLI

# Analyze a URL (default profile: generic-mobile-web-sr-v0)
npx tactual analyze-url https://example.com

# Analyze with a specific AT profile
npx tactual analyze-url https://example.com --profile voiceover-ios-v0

# Explore hidden UI (menus, tabs, dialogs, disclosures)
npx tactual analyze-url https://example.com --explore

# Use a scoring preset for your use case
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual analyze-url https://docs.example.com --preset docs-site

# Output as JSON, Markdown, or SARIF
npx tactual analyze-url https://example.com --format json --output report.json
npx tactual analyze-url https://example.com --format sarif --output report.sarif

# Compare two analysis runs
npx tactual diff-results baseline.json candidate.json
npx tactual diff-results baseline.json candidate.json --format json

# Print what NVDA would say as you Tab through the page
npx tactual transcript https://example.com
npx tactual transcript https://example.com --at voiceover

# List available AT profiles and scoring presets
npx tactual profiles
npx tactual presets

# Run benchmark suites
npx tactual benchmark
npx tactual benchmark --suite all

Benchmark fixtures ship with the npm package, so the benchmark command works from a fresh install and does not require cloning the repository fixtures into your current directory.

# Validate predicted paths against a virtual screen reader (reachability + step count)
# Requires optional deps, installed by default with tactual
npx tactual validate-url https://example.com --max-targets 10 --strategy semantic

# Initialize a tactual.json config file
npx tactual init

# Analyze a bot-protected site with stealth + real Chrome
npx tactual analyze-url https://www.npmjs.com/ --stealth --channel chrome

# Deep keyboard probing including revealed widgets and form-error flows
npx tactual analyze-url https://docs.example.com --probe --explore --probe-mode deep

# Focus probing on one opened branch, such as a dialog trigger
npx tactual analyze-url https://app.example.com/settings \
  --probe \
  --entry-selector "[aria-controls='profile-dialog']" \
  --probe-strategy modal-return-focus

# Analyze + inline virtual-SR validation in one command (predicted vs validated steps)
npx tactual analyze-url https://example.com --validate --validate-max-targets 10

La salida de consola incluye una línea de ruta compactada para cada hallazgo que muestra cómo lo alcanza un usuario de lector de pantalla:

  ██████░░ 70  link:reference structural
               D:47 R:71 O:100 Rec:100
               getByRole('link', { name: 'Reference' })
               ↪ Tab ×2 "v19.2" → K "Learn" → Tab "Reference"
               → Target is not efficiently reachable via heading or landmark navigation

Donde Tab = nextItem, H = nextHeading, ; = nextLandmark, K = nextLink, B = nextButton, Enter = activar. Los pasos consecutivos con la misma acción se colapsan (Tab ×2).

De la Auditoría a la Corrección

Para trabajo de accesibilidad en una aplicación local o entorno de vista previa, Tactual proporciona evidencia para cambios pequeños y revisables:

  • selector, penalties, suggestedFixes y resúmenes de evidencia en cada hallazgo
  • issueGroups agrupados y candidatos de remediación en la salida resumida
  • analyze_pages.site.repeatedNavigation para coste de navegación repetido entre rutas
  • diff-results / diff_results para verificación antes y después

Comienza con un triaje amplio, luego profundiza en una ruta antes de cambiar el código:

# Site-level triage. Redirect JSON for tool consumption.
npx tactual analyze-pages \
  https://app.example.com/ \
  https://app.example.com/docs \
  https://app.example.com/settings \
  --profile nvda-desktop-v0 \
  --format json > tactual-site.json

# Deepen one route and produce a reviewable markdown report.
npx tactual analyze-url https://app.example.com/docs \
  --profile nvda-desktop-v0 \
  --explore --probe --probe-mode standard \
  --format markdown --output tactual-report.md

# When one branch is the target, open it first and spend probe budget there.
npx tactual analyze-url https://app.example.com/docs \
  --profile nvda-desktop-v0 \
  --probe \
  --entry-selector "[aria-controls='search-panel']" \
  --probe-selector "#search-panel" \
  --probe-strategy composite-widget \
  --format markdown --output tactual-search-panel.md

# Save a baseline before editing, then verify the patch.
npx tactual analyze-url https://app.example.com/docs --explore --probe --format json --output baseline.json
# Edit one root cause in the local repo, rebuild/restart the preview, then re-run:
npx tactual analyze-url https://app.example.com/docs --explore --probe --format json --output candidate.json
npx tactual diff-results baseline.json candidate.json

Usa la sección de candidatos como punto de partida para causas raíz repetidas, como un componente compartido, patrón de navegación o contrato de widget. Confirma el componente de origen e incluye la ruta, el comando, la evidencia del hallazgo, el impacto para el usuario, el cambio de código y la verificación en el formato de issue o PR que el proyecto espere. El movimiento de la puntuación es evidencia de apoyo útil, pero el cambio debería liderar con el comportamiento de accesibilidad que cambió.

Los clientes MCP pueden consumir la misma salida compacta y mantener el bucle de revisión anclado en rutas, selectores, evidencia, cambios de origen y verificación antes/después.

API de Biblioteca

import { analyze, getProfile } from "tactual";
import { captureState } from "tactual/playwright";
import { chromium } from "playwright";

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com");

const state = await captureState(page);
await browser.close();

const profile = getProfile("generic-mobile-web-sr-v0");
const result = analyze([state], profile);

for (const finding of result.findings) {
  console.log(finding.targetId, finding.scores.overall, finding.severity);
}

Simulador de anuncios de lector de pantalla — predice qué anunciaría NVDA, JAWS o VoiceOver para cada objetivo, con información de estado (marcado, expandido, seleccionado, modal, valor, requerido, inválido, etc.):

import {
  simulateScreenReader,
  buildAnnouncement,
  buildMultiATAnnouncement,
  buildTranscript,
} from "tactual/playwright";

const report = await simulateScreenReader(page, state.targets);

for (const a of report.formFields) {
  console.log(a.announcement);
  // → "Subscribe, check box, checked"
  // → "Country, combo box, collapsed"
  // → "Email, edit, invalid entry, required, you must use a work address"
}

// Compare across screen readers
const tx = state.targets[5];
buildAnnouncement(tx, "nvda"); // → "Country, combo box, collapsed"
buildAnnouncement(tx, "voiceover"); // → "Country, popup button"

// All three at once
buildMultiATAnnouncement(tx);
// → { nvda: "...", jaws: "...", voiceover: "..." }

// Linear navigation transcript — what an SR user hears Tabbing through
const transcript = buildTranscript(state.targets, "nvda");
// → [{ step: 1, kind: "landmark", announcement: "Main, main landmark" }, ...]

// Multi-target navigation modes (linear, by-heading, by-landmark, by-form-control)
import { buildNavigationTranscript } from "tactual/playwright";

// Heading-only navigation (NVDA: H key)
const headings = buildNavigationTranscript(state.targets, { mode: "by-heading" });

// Navigate from one element to another
const path = buildNavigationTranscript(state.targets, {
  from: "link:before-main",
  to: "heading:welcome",
  mode: "linear",
});

// Demoted landmarks (in DOM but stripped by HTML rules, e.g. <header> in <section>)
for (const d of report.demotedLandmarks) {
  console.warn(d.demotionReason);
}

APIs de validación y calibración — compara la salida del modelo contra ejecuciones de validación de SR virtual o conjuntos de datos de observación humana:

import { validateFindingsInJsdom } from "tactual/validation";
import { runCalibration, formatCalibrationReport } from "tactual/calibration";

// Given a JSDOM instance, PageState, AnalysisResult, and calibration dataset:
const validation = await validateFindingsInJsdom(dom, state, result.findings, {
  maxTargets: 10,
  strategy: "semantic",
});

const calibration = runCalibration(dataset, new Map([[state.url, result]]));
console.log(validation, formatCalibrationReport(calibration));

O desde la CLI:

npx tactual transcript https://example.com --at voiceover
npx tactual calibration-report my-calibration.json --analysis example-nvda.json

El simulador es una predicción heurística, no una salida real de lector de pantalla. El simulador en sí es rápido (JavaScript puro sobre objetivos capturados — menos de un segundo una vez que los objetivos están en memoria), pero una ejecución completa de analyze-url incluye lanzamiento del navegador + captura de página + puntuación y tarda segundos en páginas pequeñas, más con --probe (~30s+) y --explore (~1–5 min en SPA complejas). El análisis se ejecuta en un navegador sin interfaz por defecto, por lo que nada aparece mientras trabajas. (Usa --no-headless o --channel chrome --stealth para sitios visibles/protegidos contra bots.)

Calidad de datos. Calibrado contra aserciones a nivel de token del proyecto W3C ARIA-AT: 77/77 aserciones de tokens de rol/nombre/estado pasan al 100% en los tres AT (NVDA, JAWS, VoiceOver), cubriendo la redacción de rol/nombre/estado para 36 patrones de objetivo único (botón, botón de alternancia, todas las variantes de botón de menú, divulgación, acordeón, casilla de verificación/tri-estado, interruptor, deslizadores, diálogo, alerta, enlaces, pestañas, cuadros combinados, grupos de radio, botón giratorio, barra de menú) más 4 escenarios de hitos multi-objetivo. Ejecuta npm run calibrate después de npm run build para verificar contra las últimas aserciones ascendentes. Esto es calibración del simulador, no prueba de fidelidad completa del lector de pantalla en modos de exploración, configuraciones de verbosidad, tiempos o cada variante válida de widget. Las anulaciones específicas de AT fuera del conjunto calibrado están etiquetadas con confianza ALTA/MEDIA/BAJA en el código fuente.

Servidor MCP

Tactual incluye un servidor MCP para consumo por agentes de IA:

# Start the MCP server (stdio transport — default)
npx tactual-mcp

# Start with HTTP transport (for hosted platforms, remote clients)
npx tactual-mcp --http              # listens on http://127.0.0.1:8787/mcp
npx tactual-mcp --http --port=3000  # custom port (or set PORT env var)
npx tactual-mcp --http --port 3000  # space-separated form is also supported
npx tactual-mcp --http --host=0.0.0.0  # bind to all interfaces (default: 127.0.0.1)

Para despliegues MCP orientados a red, coloca el transporte HTTP detrás de un proxy TLS autenticado y mantenlo limitado a clientes de confianza. Consulta SECURITY.md para la lista de verificación alojada y el modelo de amenazas.

Herramientas MCP disponibles:

HerramientaDescripción
analyze_urlAnaliza una página para coste de navegación SR (SARIF por defecto). Admite exploración opcional, sondeos de teclado/widget/formulario, sondeos dirigidos/por objetivos, modo sigiloso/canal para sitios protegidos contra bots y filtrado.
trace_pathRuta de navegación paso a paso hacia un objetivo con anuncios SR modelados.
validate_urlValida rutas predichas contra @guidepup/virtual-screen-reader. Devuelve alcanzable + precisión media por estrategia (lineal/semántica). Cierra el bucle predicho-vs-validado.
calibration_reportEjecuta conjuntos de datos de calibración observados contra JSON de análisis completo guardado y devuelve señales de puntuación estructuradas para flujos de ajuste/revisión.
list_profilesLista los perfiles AT disponibles.
diff_resultsCompara dos resultados de análisis — mejoras, regresiones, cambios de severidad.
suggest_remediationsSugerencias de corrección clasificadas por impacto.
save_authAutentica y guarda el estado de sesión para analizar contenido protegido.
analyze_pagesTriaje de sitio multipágina con estadísticas agregadas y grupos de coste de navegación repetido entre páginas.

Referencia completa de parámetros: docs/MCP-TOOLS.md

Configuración por herramienta de IA

Primero instala los paquetes requeridos en tu proyecto:

npm install tactual

Claude Code — añade a .mcp.json en la raíz de tu proyecto:

{
  "mcpServers": {
    "tactual": {
      "type": "stdio",
      "command": "npx",
      "args": ["tactual-mcp"]
    }
  }
}

GitHub Copilot — añade a .copilot/mcp.json o ~/.copilot/mcp-config.json:

{
  "mcpServers": {
    "tactual": {
      "type": "stdio",
      "command": "npx",
      "args": ["tactual-mcp"]
    }
  }
}

Cursor / Windsurf / Cline — mismo formato en la configuración MCP de tu editor:

{
  "mcpServers": {
    "tactual": {
      "command": "npx",
      "args": ["tactual-mcp"]
    }
  }
}

Directo (instalación global) — si prefieres no usar npx:

npm install -g tactual
tactual-mcp  # starts the MCP server on stdio

GitHub Actions

Usa la acción compuesta del GitHub Actions Marketplace:

jobs:
  a11y:
    runs-on: ubuntu-latest
    permissions:
      security-events: write # for SARIF upload
      pull-requests: write # for comment-on-pr
    steps:
      - name: Analyze accessibility
        uses: tactual-dev/tactual@v0.5.0
        with:
          url: https://your-app.com
          profile: nvda-desktop-v0
          explore: "true"
          probe: "true"
          probe-mode: standard
          fail-below: "70"
          comment-on-pr: "true"

La acción instala Tactual y los binarios del navegador Chromium, ejecuta el análisis, sube SARIF a GitHub Code Scanning y falla la compilación si la puntuación media está por debajo del umbral. Establece comment-on-pr: "true" para publicar un comentario resumen en las pull requests (se actualiza al re-ejecutar). Produce average-score y result-file para pasos posteriores. La versión de la acción sigue la versión de Tactual — sube la línea uses: para recoger parches.

Los valores por defecto son conservadores: probe está desactivado a menos que se habilite porque envía eventos de teclado reales, y las comprobaciones de iconos con colores forzados solo se ejecutan para perfiles que declaran visualModes como nvda-desktop-v0 y jaws-desktop-v0.

O usa la CLI directamente para más control:

- name: Install Tactual
  run: npm install tactual

- name: Install browsers
  run: npx playwright install chromium --with-deps

- name: Run accessibility analysis
  run: npx tactual analyze-url https://your-app.com --format sarif --output results.sarif --threshold 70

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

Puerta de regresión (CI falla en peor-que-línea-base)

Combina --baseline con --fail-on-regression para convertir Tactual en una puerta estricta de CI: guarda una línea base de una compilación conocida-buena, luego falla las comprobaciones de PR siempre que un cambio regrese N+ hallazgos frente a la línea base. El comando diff-results también puede ejecutarse por separado para informes legibles antes/después.

# One-time: snapshot main as the baseline
- name: Snapshot baseline
  if: github.ref == 'refs/heads/main'
  run: |
    npx tactual analyze-url https://preview.your-app.com \
      --format json --output tactual-baseline.json

- name: Upload baseline
  if: github.ref == 'refs/heads/main'
  uses: actions/upload-artifact@v4
  with:
    name: tactual-baseline
    path: tactual-baseline.json

# On PRs: compare against the baseline, fail on regressions
- name: Fetch baseline
  uses: actions/download-artifact@v4
  with:
    name: tactual-baseline

- name: Analyze + gate on regressions
  run: |
    npx tactual analyze-url https://pr-preview-${{ github.event.number }}.your-app.com \
      --format sarif --output results.sarif \
      --baseline tactual-baseline.json \
      --fail-on-regression 3     # fail if 3+ findings regressed

O mediante la acción:

- uses: tactual-dev/tactual@v0.5.0
  with:
    url: https://pr-preview.your-app.com
    baseline: tactual-baseline.json
    fail-on-regression: "3"

La acción refleja la superficie CLI de analyze-url para entradas de análisis, y una prueba de contrato CI-a-CLI mantiene esos campos alineados. Algunos controles de flujo de trabajo son orquestación de Action en lugar de banderas CLI directas: fail-below envuelve la CLI --threshold, comment-on-pr controla el paso de comentario de PR, y la subida de SARIF la maneja el flujo de trabajo. Las entradas comunes que establecerás incluyen profile, explore, explore-depth, explore-budget, explore-timeout, probe, probe-mode, probe-strategy, scope-selector, probe-selector, entry-selector, goal-target, goal-pattern, stealth, channel, wait-for-selector, exclude, exclude-selector, focus, min-severity, max-findings, baseline, fail-on-regression, fail-below, validate, storage-state, summary-only. El patrón de invocación CLI directa anterior sigue siendo la ruta recomendada cuando quieres una versión de Tactual diferente a la que fija la acción.

SuperficieConvención de nombresEjemplo
CLIbanderas kebab-case--probe-strategy modal-return-focus
Opciones de MCP y bibliotecacampos camelCaseprobeStrategy: "modal-return-focus"
GitHub Actionentradas kebab-caseprobe-strategy: modal-return-focus

Configuración

Banderas CLI

Options:
  -p, --profile <id>              AT profile (default: generic-mobile-web-sr-v0)
  -f, --format <format>           json | markdown | console | sarif (default: console)
  -o, --output <path>             Write to file instead of stdout
  -d, --device <name>             Playwright device emulation
  -e, --explore                   Explore hidden branches
  --explore-depth <n>             Max exploration depth (default: 3)
  --explore-budget <n>            Max exploration actions (default: 50)
  --explore-timeout <ms>          Total exploration timeout; includes probe time when combined with --probe (default: 60000)
  --explore-max-targets <n>       Max accumulated targets before stopping (default: 2000)
  --allow-action <patterns...>    Allow exploring controls matching these patterns (overrides safety)
  --exclude <patterns...>         Exclude targets by name/role glob
  --exclude-selector <css...>     Exclude elements by CSS selector
  --scope-selector <css...>       Capture, score, and probe only these subtrees
  --focus <landmarks...>          Only analyze within these landmarks
  --suppress <codes...>           Suppress diagnostic codes
  --top <n>                       Show only worst N findings
  --min-severity <level>          Minimum severity to report
  --threshold <n>                 Exit non-zero if avg score < N
  --preset <name>                 Scoring preset (ecommerce-checkout, docs-site, dashboard, form-heavy)
  --config <path>                 Path to tactual.json
  --no-headless                   Headed browser (for bot-blocked sites)
  --channel <name>                Browser channel: chrome, chrome-beta, msedge (uses installed browser; bypasses most bot detection)
  --stealth                       Anti-detection defaults: realistic UA, override navigator.webdriver, spoof plugins/languages
  --user-agent <ua>               Override User-Agent string
  --timeout <ms>                  Page load timeout (default: 30000)
  --probe                         Opt-in runtime keyboard probes for interactive targets
                                    (focus, activation, Escape, Tab).
                                    Also probes menu, dialog, tab, disclosure, combobox/listbox,
                                    and form-error patterns.
                                    When combined with --explore, probes revealed-state targets too
                                    (menu items, dialog bodies, expanded widgets).
  --probe-budget <n>              Override generic-probe budget (default: per --probe-mode)
  --probe-mode <mode>             fast | standard (default) | deep.
                                    fast=5 generic/5 menu/3 modal/5 widget;
                                    standard=20/20/10/20; deep=50/40/20/40.
                                    Budget is shared across initial + all revealed states.
  --probe-selector <css...>       Probe only these subtrees without changing capture/scoring
  --entry-selector <css>          Activate this trigger before capture/probe
  --goal-target <target>          Exact-ish target id/name/role/kind/selector hint
  --goal-pattern <pattern>        Glob target id/name/role/kind/selector hint
  --probe-strategy <strategy>     all | overlay | composite-widget | form |
                                    navigation | modal-return-focus | menu-pattern
  --validate                      Run the virtual screen reader over the captured DOM and include
                                    a predicted-vs-validated step comparison in the output.
                                    Requires optional deps: jsdom + @guidepup/virtual-screen-reader.
                                    Installed by default unless optional deps were omitted.
  --validate-max-targets <n>      Max findings to validate (default: 10)
  --validate-strategy <mode>      Virtual-SR nav strategy: linear | semantic (default: semantic)
  --check-visibility              Force per-icon contrast check across the profile's visualModes
  --no-check-visibility           Disable per-icon contrast check even if profile declares modes
  --detect-routes                 Record SPA route changes during analysis
  --descend-frames                Include iframe accessibility targets; Chromium can recover many cross-origin OOPIFs via CDP
  --auto-scroll                   Scroll before capture to surface lazy/infinite-scroll content
  --dismiss-banners               Best-effort dismissal of safe cookie/consent banners
  --probe-hover                   Hover likely triggers to expose hover-only popup content
  --walk-tab-order                Record Tab traversal to detect focus-order/focus-trap issues
  --diff-viewports                Compare desktop and mobile captures for hidden content
  --wait-for-selector <css>       Wait for selector before capturing (for SPAs)
  --wait-time <ms>                Additional wait after page load
  --storage-state <path>          Playwright storageState JSON for authenticated pages
  --also-json <path>              Also write JSON to this path (single analysis run for CI)
  --summary-only                  Return only summary stats, no individual findings
  -q, --quiet                     Suppress info diagnostics

tactual.json

Crea con tactual init o manualmente:

{
  "preset": "ecommerce-checkout",
  "profile": "voiceover-ios-v0",
  "exclude": ["easter*", "admin*", "debug*"],
  "excludeSelectors": ["#easter-egg", ".admin-only", ".third-party-widget"],
  "scopeSelectors": ["main"],
  "probeSelectors": [".checkout-dialog"],
  "probeStrategy": "modal-return-focus",
  "focus": ["main"],
  "suppress": ["possible-cookie-wall"],
  "threshold": 70,
  "priority": {
    "checkout*": "critical",
    "footer*": "low",
    "analytics*": "ignore"
  }
}

La configuración se detecta automáticamente desde el directorio de trabajo (tactual.json o .tactualrc.json). Las banderas CLI se combinan con la configuración y la sobrescriben.

Perfiles de AT

PerfilPlataformaDescripción
generic-mobile-web-sr-v0MóvilPrimitivas SR móviles normalizadas (predeterminado)
voiceover-ios-v0MóvilVoiceOver en iOS Safari: navegación basada en rotor
talkback-android-v0MóvilTalkBack en Android Chrome: controles de lectura
nvda-desktop-v0EscritorioNVDA en Windows: teclas rápidas del modo exploración
jaws-desktop-v0EscritorioJAWS en Windows: cursor virtual con modo formularios automático

Los perfiles definen el costo de cada acción de navegación, los pesos de las dimensiones de puntuación, costSensitivity (escala la curva de decaimiento de accesibilidad) y modificadores dependientes del contexto. Consulta src/profiles/ para los detalles de implementación.

Limitación del perfil móvil. Los perfiles voiceover-ios-v0 y talkback-android-v0 modelan con precisión los costos de acción y la redacción de los anuncios del lector de pantalla, pero las sondas de teclado de Tactual (--probe) solo prueban interacciones de escritorio (Tab, Enter, Escape). NO simulan gestos táctiles (toque simple, doble toque, deslizar a la derecha, deslizar con tres dedos, rotación del rotor, etc.). Para perfiles móviles, las dimensiones de puntuación reflejan el costo previsto del modelo de perfil, no el comportamiento medido. Sigue siendo necesaria la prueba en dispositivos reales para verificar la accesibilidad móvil.

Modos visuales. Los perfiles nvda-desktop-v0 y jaws-desktop-v0 declaran una matriz visualModes (claro/oscuro × colores forzados activados/desactivados) para que el analizador capture el contraste por icono bajo cada combinación. Los perfiles móviles y genéricos omiten esto: el modo de alto contraste de Windows no es una preocupación realista en móviles. Consulta Comprobaciones de visibilidad a continuación.

Comprobaciones de visibilidad

Cuando el perfil activo declara una matriz visualModes, Tactual re-emula cada combinación (colorScheme, forcedColors) después de la captura inicial y muestrea los estilos calculados por icono. El generador de hallazgos compara el fill calculado de cada icono con el background-color del ancestro no transparente más cercano y emite una penalización cuando el contraste cae por debajo del umbral de no texto WCAG 1.4.11 (3:1).

Cuatro redacciones de penalización, tres niveles de puntuación:

PenalizaciónDisparadorImpacto en operabilidad
Icon invisible in <mode>Contraste < 1.5:1, sin etiqueta de texto adyacenteOperabilidad limitada a 60
Decorative icon invisible in <mode>Contraste < 1.5:1, el control tiene etiqueta de texto visibleOperabilidad −5
Low icon contrast in <mode>Contraste 1.5–3.0:1, sin etiqueta de texto adyacenteOperabilidad −5
Author-set SVG fill in <mode>Contraste correcto en Playwright (≥3:1) pero el modo es forced-colors: active y el relleno es un literal CSS del autor (no de sistema, no currentColor)Operabilidad −2

La comprobación omite los iconos que ya son seguros para HCM: fill="currentColor", fill: ButtonText (o cualquier color de sistema), forced-color-adjust: none (exclusión voluntaria del autor) o fill === color calculado (currentColor aplicado por CSS). Los iconos de bajo contraste junto a una etiqueta de texto visible se suprimen por completo: la etiqueta identifica el control y el icono es un refuerzo.

Por qué existe el nivel de riesgo de sustitución. Los diferentes temas HCM de usuario tienen diferentes valores de Canvas/ButtonText/color de sistema. Un relleno literal del autor (p. ej., svg { fill: #e4e6e6 }) puede contrastar bien con la paleta HCM predeterminada de Chromium, pero mal con un tema de usuario específico. El renderizado del literal por parte del navegador es consistente en Playwright, Chrome y Edge para forced-color-adjust: preserve-parent-color (el valor predeterminado para rutas SVG): la preocupación concreta es la variabilidad del tema, no una sustitución oculta de pintura del sistema operativo. Tactual marca el patrón para que sepas que debes verificar en Edge real con un tema HCM representativo, no porque la medición de contraste de Playwright sea engañosa.

Desactívalo explícitamente mediante --no-check-visibility, checkVisibility: false en tactual.json, o checkVisibility: false en la herramienta analyze_url de MCP. Fuerza su activación mediante --check-visibility incluso cuando un perfil no declara modos (sin efecto si no hay modos).

La comprobación añade aproximadamente +50–200 ms por modo declarado por página: re-emular los medios es barato; no hay un nuevo contexto de navegador por modo.

Ajustes predefinidos de puntuación

Los ajustes predefinidos agrupan filtros de enfoque y asignaciones de prioridad para casos de uso comunes. Se superponen bajo los archivos de configuración y las banderas CLI (ajuste predefinido → tactual.json → banderas CLI).

Ajuste predefinidoCaso de usoEnfoqueObjetivos críticos
ecommerce-checkoutFlujos de compramaincheckout, cart, payment, buy
docs-siteDocumentaciónmain, navigationsearch, nav
dashboardAplicaciones webmain, navigationsave, submit, create, delete, search
form-heavyPáginas de formulariosmainsubmit, save, next, continue, error
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual presets  # list all presets with details

Los ajustes predefinidos suprimen los banners de cookies y los objetivos de analítica de forma predeterminada. Para anularlo, usa --exclude o establece priority en tactual.json. Los ajustes predefinidos no se componen: solo puede estar activo un --preset.

Puntuación

Cada objetivo recibe un vector de puntuación de 5 dimensiones:

DimensiónQué mide
Descubribilidad¿Puede el usuario saber que el objetivo existe?
Accesibilidad¿Cuál es el costo de navegación para llegar allí?
Operabilidad¿El control se comporta de manera predecible?
Recuperación¿Qué tan difícil es recuperarse de pasarse del objetivo?
Riesgo de interoperabilidad¿Qué tan probable es la variación de soporte AT/navegador? (penalización)

Los pesos de las dimensiones varían según el perfil:

PerfilDROReccostSensitivity
generic-mobile-web-sr-v00.300.400.200.101.0
voiceover-ios-v00.300.350.200.151.1
talkback-android-v00.250.450.200.101.3
nvda-desktop-v00.350.250.300.100.7
jaws-desktop-v00.300.250.350.100.6

Compuesto: Media geométrica ponderada: overall = exp(sum(w_i * ln(score_i)) / sum(w_i)) - interopRisk. Cada dimensión se limita a un mínimo de 1 antes del logaritmo para evitar log(0). Un cero en cualquier dimensión elimina la contribución de esa dimensión a la media geométrica, lo que reduce significativamente la puntuación general: no puedes operar lo que no puedes alcanzar.

Bandas de severidad:

PuntuaciónBandaSignificado
90-100FuertePreocupación baja
75-89AceptableMejorable
60-74ModeradaDebe priorizarse
40-59AltaProbable fricción significativa
0-39SeveraProbablemente bloqueante

Diagnósticos

Tactual emite diagnósticos para la fiabilidad de la captura, la estructura de la página, el acceso visual, la evidencia en tiempo de ejecución, la validez de ARIA y los patrones de costo repetidos. Las advertencias son avisos de revisión, no fallos automáticos de conformidad. Muchas comprobaciones visuales/de contenido son heurísticas y deben confirmarse en contexto antes de reportar un defecto.

CódigoNivelSignificado
blocked-by-bot-protectionerrorSe detectó una página de bot/desafío; el contenido capturado no es la página prevista.
empty-pageerrorNo se encontraron objetivos en absoluto.
okinfoLa captura produjo un conjunto de objetivos sin advertencias de fiabilidad.
possibly-degraded-contentwarningSospechosamente pocos objetivos para una página HTTP.
sparse-contentwarningSolo se encontraron 1-4 objetivos.
possible-login-wallwarningSe sospecha contenido restringido por autenticación o redirección de inicio de sesión.
possible-cookie-wallinfoEl consentimiento de cookies puede ocultar contenido.
redirect-detectedinfo/warningLa captura aterrizó en una URL o dominio diferente.
timeout-during-renderwarningUna espera de renderizado solicitada no se completó antes de la captura.
framework-detectedinfoSe detectaron señales de frameworks frontend durante la captura.
spa-route-changesinfoOcurrieron cambios de ruta SPA durante el análisis.
exploration-no-new-stateswarning--explore se ejecutó pero no reveló estados adicionales.
frames-descendedinfoEl descenso de iframes capturó o omitió marcos secundarios.
auto-scrolledinfoEl desplazamiento automático se ejecutó antes de la captura e informa lo que reveló.
banners-dismissedinfoSe intentó descartar el banner de cookies/consentimiento.
tab-order-walkedinfo/warningEl recorrido por orden de tabulación registró paradas de enfoque; advierte sobre tabindex positivo.
viewport-divergencewarningLa diferencia de viewport escritorio/móvil encontró objetivos, puntos de referencia o encabezados faltantes.
no-headingswarningNo se encontraron elementos de encabezado.
heading-skipwarningLa jerarquía de encabezados omite un nivel, como h1 -> h3.
empty-headingwarningExiste un encabezado pero no tiene texto.
numeric-headingwarningEl texto del encabezado es solo dígitos, puntuación o contenido trivial de un solo carácter.
h1-countinfo/warningLa página no tiene un H1 único útil, o tiene múltiples H1 que vale la pena revisar.
no-landmarkswarningNo se encontraron regiones de puntos de referencia.
no-main-landmarkwarningFalta el punto de referencia <main>.
no-banner-landmarkinfoFalta el punto de referencia <header> / banner.
no-contentinfo-landmarkinfoFalta el punto de referencia <footer> / contentinfo.
no-nav-landmarkinfoFalta el punto de referencia <nav> / navegación.
landmark-demotedwarningEl punto de referencia HTML existe pero está degradado por el contexto de anidamiento.
structural-summaryinfoResumen estructural de una línea.
no-skip-linkwarningNo hay enlace de salto al contenido en páginas con 5+ objetivos.
broken-skip-linkwarningEl enlace de estilo salto apunta a un objetivo de fragmento faltante.
skip-link-not-firstwarningExiste un enlace de salto pero no es alcanzable en las dos primeras paradas de Tab.
visual-order-divergencewarningEl orden visual parece divergir del orden de navegación DOM/SR.
shared-structural-issuewarningUna penalización que afecta a >50% de los objetivos se promueve al nivel de página.
redundant-tab-stopswarningMúltiples objetivos de enlace crean paradas de Tab repetidas al mismo destino.
data-flow-dependenciesinfoLos estados explorados revelan controles que solo se habilitan después de una acción previa.
form-summaryinfo/warningResume formularios y advierte cuando un formulario parece carecer de un control de envío.
missing-autocompletewarningLos campos de formulario estándar carecen de tokens autocomplete útiles o los deshabilitan.
empty-interactivewarningEl objetivo interactivo no tiene un nombre accesible.
fake-interactive-elementswarningLos elementos no semánticos clicables no son alcanzables por teclado/SR.
cdp-click-listenerswarningCDP encontró listeners de tipo clic en elementos no interactivos.
ambiguous-link-nameswarningLos enlaces con el mismo nombre accesible apuntan a destinos diferentes.
media-without-controlswarningEl audio/video carece de controles y no está oculto.
duplicate-idwarningLos valores duplicados de id pueden romper etiquetas y referencias ARIA.
nested-interactivewarningLos controles interactivos están anidados dentro de otros controles interactivos.
meta-refreshwarningLa página se actualiza o redirige automáticamente mediante meta refresh.
missing-image-altwarningLas imágenes carecen de atributos alt.
suspicious-image-altwarningEl texto alternativo de la imagen parece relleno o un marcador de posición similar a un nombre de archivo.
missing-iframe-titlewarningLos iframes carecen de un title o etiqueta accesible.
missing-html-langwarning<html lang> falta o no parece una etiqueta de idioma BCP 47.
poor-document-titlewarningEl título del documento falta, está vacío, es demasiado corto o genérico.
viewport-blocks-zoomwarningLa configuración de viewport meta restringe el zoom del usuario.
low-contrast-textwarningEl texto interactivo o los encabezados no cumplen los umbrales de contraste de texto estilo WCAG.
color-only-conveyancewarningEl texto parece depender solo del color para transmitir significado.
color-blindness-contrast-failwarningEl texto pierde contraste bajo deficiencia simulada de visión de color.
lang-switch-without-markerwarningEl idioma del texto parece cambiar sin un marcador lang.
invalid-aria-rolewarningEstá presente un rol ARIA no estándar.
unknown-aria-attrwarningEstá presente un atributo aria-* desconocido.
invalid-aria-attr-valuewarningEl valor del atributo ARIA está fuera del conjunto de valores permitidos.
missing-required-aria-attrwarningAl rol ARIA le falta un estado o propiedad requerido.
aria-naming-prohibitedwarningEl nombre se aplica a un rol que prohíbe el nombrado.
unsupported-aria-attr-for-rolewarningEl atributo ARIA no es compatible con el rol del elemento.

Exploración

La bandera --explore activa la exploración de ramas acotada:

  • Abre menús, pestañas, divulgaciones, acordeones y diálogos
  • Captura nuevos estados de accesibilidad de la interfaz oculta
  • Marca los objetivos descubiertos como requiresBranchOpen
  • Respeta los presupuestos de profundidad, recuento de acciones, recuento de objetivos y novedad
  • La política de acciones seguras bloquea interacciones destructivas

La exploración es útil para páginas con interfaz oculta significativa (por ejemplo, menús desplegables, interfaces de pestañas, diálogos modales).

Los candidatos de exploración se ordenan por una clave estable (rol + nombre) antes de iterar, de modo que el mismo contenido de página produce el mismo orden de exploración entre ejecuciones.

Sondas

La bandera --probe mide si los patrones interactivos importantes funcionan después de aparecer en el árbol de accesibilidad. Las sondas son opcionales porque envían eventos de teclado reales y añaden tiempo de ejecución. Desde 0.4.0 esto incluye comprobaciones genéricas de enfoque/activación, contratos de menú, contratos de diálogo modal, flujos de disparador a diálogo, pestañas, divulgaciones, comboboxes, listboxes y flujos de error de campos requeridos. Los hallazgos de las sondas incluyen resúmenes de evidencia para que los informes distingan fallos medidos de puntuación modelada o heurística.

Los controles dirigidos por objetivos mantienen útiles las sondas profundas en SPA complejas:

NecesidadCLICampo MCP/AcciónEfecto
Analizar un subárbol--scope-selector "#drawer"scopeSelector / scope-selectorCaptura, puntúa y sondea solo los subárboles seleccionados.
Sondear un subárbol--probe-selector "#drawer"probeSelector / probe-selectorMantiene la puntuación de toda la página pero gasta el presupuesto de sondas solo dentro de los subárboles seleccionados.
Abrir una rama primero--entry-selector "[aria-controls='menu']"entrySelector / entry-selectorActiva el disparador antes de la captura/sonda y prioriza los objetivos recién revelados.
Apuntar a un objetivo conocido--goal-target "checkout"goalTarget / goal-targetReduce la sondas a ids, nombres, roles, tipos o selectores de objetivos coincidentes.
Apuntar por glob--goal-pattern "*dialog*"goalPattern / goal-patternIgual que el objetivo de meta, con coincidencia glob.
Gastar presupuesto por intención--probe-strategy modal-return-focusprobeStrategy / probe-strategyEjecuta las familias de sondas relevantes para all, overlay, composite-widget, form, navigation, modal-return-focus o menu-pattern.

Por ejemplo, para evaluar una rama modal sin rastrear menús no relacionados:

npx tactual analyze-url https://app.example.com/settings \
  --profile nvda-desktop-v0 \
  --probe \
  --entry-selector "[aria-controls='profile-dialog']" \
  --probe-strategy modal-return-focus \
  --format markdown

Presupuestos de exploración

PresupuestoBandera CLIPredeterminadoPropósito
Profundidad--explore-depth3Profundidad máxima de recursión
Acciones--explore-budget50Presupuesto total de clics en todas las ramas
Objetivos--explore-max-targets2000Detener si los objetivos acumulados superan esto
Tiempo--explore-timeout60000 msLimita el tiempo total de exploración, incluyendo sondas iniciales, capturas de ramas y sondas de estados revelados

Guía de dimensionamiento:

Tipo de páginaConfiguración sugeridaPor qué
Sitio de marketing, página de documentación, blogpredeterminadosSuperficie pequeña, los predeterminados rara vez se alcanzan
Panel con barra lateral/menú--explore-depth 3 --explore-budget 50 (predeterminados)Captura un nivel de aperturas de menú
Aplicación compleja (Figma, Notion, etc.)--explore-depth 4 --explore-budget 100 --explore-max-targets 5000Menús más profundos, más estado
Páginas con interfaz oculta muy grande (selectores de emoji, cuadrículas de color)--explore-max-targets 10000 más --exclude "emoji-*"Limitar o filtrar el flujo masivo
Triage rápido de página desconocida--explore-depth 1 --explore-budget 10Solo abrir ramas obvias, rápido

Si la exploración alcanza el tiempo de espera antes de abrir ramas útiles, aumenta --explore-timeout y --explore-budget lentamente, o usa --entry-selector, --probe-selector y --probe-strategy para gastar el mismo presupuesto en la rama que te interesa. Si la salida tiene objetivos de apariencia duplicada, baja --explore-depth (la recursión profunda puede redescubrir los mismos elementos a través de diferentes rutas).

Detección de frameworks SPA

Tactual detecta cuándo el contenido SPA se ha renderizado antes de capturar el árbol de accesibilidad. Frameworks detectados: React, Next.js, Vue, Nuxt, Angular, Svelte y SvelteKit. También se comprueban señales genéricas de contenido HTML5 (puntos de referencia, encabezados, navegación, enlaces). Para SPA no cubiertas por la detección automática, usa --wait-for-selector (CLI) o waitForSelector (MCP/API) para especificar un selector CSS que indique que tu aplicación se ha hidratado.

Después de la detección inicial del framework, Tactual usa sondeo basado en convergencia — capturando repetidamente el árbol de accesibilidad hasta que el recuento de objetivos se estabilice — que funciona independientemente del framework.

Para aplicaciones con mucho SPA, estos ayudantes de captura opcionales son útiles:

npx tactual analyze-url https://app.example.com \
  --wait-for-selector "main" \
  --detect-routes \
  --auto-scroll \
  --descend-frames \
  --diff-viewports
  • --detect-routes registra los eventos pushState, replaceState, popstate y hashchange que ocurren durante el análisis.
  • --auto-scroll expone el contenido diferido impulsado por IntersectionObserver antes de la captura.
  • --descend-frames añade objetivos de iframe con atribución de URL del marco. Los iframes del mismo origen usan la instantánea de accesibilidad específica del marco de Playwright; Chromium recurre a CDP para OOPIFs de origen cruzado cuando la ruta de instantánea normal es inaccesible. Firefox/WebKit mantienen el comportamiento de omisión existente para marcos inaccesibles.
  • --diff-viewports detecta contenido de destino, punto de referencia o encabezado que desaparece entre las vistas de escritorio y móvil.
  • --dismiss-banners, --probe-hover y --walk-tab-order añaden evidencia de tiempo de ejecución dirigida para superposiciones comunes de SPA y errores de orden de enfoque.

Benchmark de páginas conocidas

Para evidencia de lanzamiento contra páginas públicas complejas de SPA/biblioteca de componentes, ejecuta:

npm run benchmark:known-pages

El script construye el paquete, ejecuta analyze-url con la pila de ayudantes de SPA habilitada y escribe run-results.json, summary.json y REPORT.md bajo build/known-pages-*. Intencionalmente no es una puerta de CI: los sitios públicos cambian, bloquean la automatización y sirven contenido diferente con el tiempo. Usa el informe para detectar desviaciones y sorpresas a nivel de categoría, luego usa fixtures locales o páginas propiedad del proyecto para puertas de regresión deterministas. Para una ejecución de humo limitada o sondas de calidad de captura APG/W3C, llama al script directamente, por ejemplo node scripts/known-pages-corpus.mjs --build --limit 1 o node scripts/known-pages-corpus.mjs --build --include-capture-probes.

Seguimiento de regresiones

Compara dos ejecuciones de análisis para detectar regresiones:

# Save a baseline
npx tactual analyze-url https://your-app.com --format json --output baseline.json

# After changes, run again and diff
npx tactual analyze-url https://your-app.com --format json --output candidate.json
npx tactual diff-results baseline.json candidate.json

El diff muestra objetivos que mejoraron, retrocedieron o cambiaron de severidad, además de penalizaciones resueltas y añadidas. En CI, usa la entrada de acción comment-on-pr para publicar resultados en cada pull request automáticamente.

Riesgo de interoperabilidad

Tactual incluye una instantánea estática de datos de soporte de roles/atributos ARIA derivada de a11ysupport.io y del proyecto ARIA-AT. Los roles con brechas conocidas de soporte entre AT/navegadores reciben una penalización de riesgo de interoperabilidad.

RolRiesgoNota
button, link, heading0Bien soportado
dialog5La gestión de enfoque varía
combobox8Patrón más problemático para interoperabilidad
tree10Pobremente soportado fuera de JAWS
application15Peligroso si se usa mal

Interpretación de hallazgos

Los hallazgos de Tactual mezclan intencionalmente varios dominios de evidencia:

  • Navegación por lector de pantalla: puntos de referencia, encabezados, etiquetas, descubrimiento de ramas, costo de recorrido secuencial y anuncios modelados.
  • Operabilidad con teclado: movimiento de enfoque, activación, recuperación con Escape, atrapamiento de Tab y sondas de widgets en tiempo de ejecución.
  • Semántica estructural: nombres faltantes, estructura de encabezados/puntos de referencia, puntos de referencia degradados, causas compartidas repetidas.
  • Riesgo de interoperabilidad: roles y estados con brechas conocidas de soporte entre AT/navegadores.
  • Verificaciones adyacentes al puntero: problemas de tamaño de objetivo y visibilidad de iconos que pueden afectar a usuarios fuera del modelo de navegación por lector de pantalla.

Eso significa que una página puede tener una puntuación sólida de navegación por lector de pantalla y aún así recibir advertencias de enlace de salto, tamaño de objetivo o visibilidad. Trátalas como categorías de corrección separadas en lugar de contradicciones.

Los hallazgos APG derivados de sondas son advertencias de consistencia medidas. Muchos widgets tienen variantes de implementación válidas, especialmente comboboxes y patrones similares a disclosure, así que verifica la advertencia contra el patrón previsto antes de tratarla como un reemplazo obligatorio. Los flujos críticos aún deben verificarse con la combinación de navegador/AT objetivo.

Recomendaciones de formato de salida

FormatoTamaño típicoMejor para
console~8KBRevisión humana en terminal
markdown~11KBPRs y comentarios de issues
json~18KBConsumo programático
sarif~4-40KBGitHub Code Scanning / CI

Todos los formatos de reporter no SARIF emiten salida resumida por defecto: estadísticas, issues agrupados, candidatos de remediación, resúmenes de evidencia y peores hallazgos (máximo 15). SARIF limita a 25 resultados. Cuando la salida se trunca, aparece una nota en la parte superior. La API de la biblioteca expone el AnalysisResult completo; la salida del reporter CLI y MCP es intencionalmente compacta a menos que se solicite un campo específico como includeStates.

Para uso con MCP, sarif es el formato predeterminado y recomendado. Usa summaryOnly: true para una verificación de salud compacta con estadísticas, conteos de severidad, diagnósticos y los 3 issues principales.

Calibración

Tactual incluye un marco de calibración (src/calibration/, exportado como tactual/calibration) para ajustar parámetros de puntuación contra conjuntos de datos de verdad fundamental. Consulta docs/CALIBRATION.md para más detalles.

Las observaciones de calibración también pueden incluir retroalimentación determinista de anuncios del trabajo de revisión OSS: registra observedAnnouncement cuando conozcas la salida probada, o observedAnnouncementTokens cuando la redacción exacta sea ruidosa pero los tokens de rol/nombre/estado sean claros. Tactual compara esos contra su anuncio modelado para el objetivo coincidente e informa tokens faltantes o inesperados. Usa tactual calibration-report o MCP calibration_report para ejecutar un conjunto de datos contra la salida guardada de analyze-url --full-json y emitir scoringSignals. Usa tactual observe-announcement para generar o añadir observaciones solo de anuncios desde un análisis guardado, o npm run -- nvda:vm:observe -- ... en este repositorio para organizar una carpeta de captura controlada de VM NVDA. El corpus versionado del repositorio vive bajo calibration/corpus/; ejecuta npm run calibration:corpus para auditar puertas de cobertura y npm run calibration:matrix después de npm run build para clasificar el trabajo de ajuste de alcanzabilidad por MAE, sesgo, varianza y deriva de plan de secuencia obsoleto.

La preparación para el lanzamiento y los límites conocidos están documentados en docs/RELEASE_TEST_MATRIX.md, docs/LIMITATIONS.md y docs/NVDA_VM_OBSERVER.md.

Desarrollo

npm install                    # Install dependencies
npm run build                  # Build with tsup
npm run test                   # Run unit + integration tests
npm run test:shard -- --list   # List bounded Vitest release shards
npm run test:shard -- capture  # Run one bounded Vitest shard
npm run test:shards            # Run all bounded Vitest shards
npm run test:benchmark         # Run benchmark suites
npm run typecheck              # TypeScript type checking
npm run lint                   # ESLint
npm run test:release           # Full split release gate

Seguridad

Sandboxing del navegador

Tactual siempre ejecuta Playwright con el sandboxing predeterminado de Chromium habilitado. Nunca desactiva la seguridad web ni modifica el modelo de seguridad del navegador. Todas las interacciones de página ocurren dentro del sandbox de procesos estándar de Chromium.

Política de acciones seguras

Cuando la exploración está habilitada (--explore), Tactual clasifica los elementos interactivos en tres niveles antes de activarlos:

NivelAcciónEjemplos
SeguroActivadoPestañas, elementos de menú, disclosures, acordeones, anclas de la misma página
PrecauciónActivado con cuidadoEnlaces externos, botones ambiguos
InseguroOmitidoBotones de envío (fuera de formularios de búsqueda), Eliminar, cerrar sesión, comprar, implementar, cancelar suscripción

Esto es una heurística basada en palabras clave: no puede detectar engaños semánticos (por ejemplo, un botón "Guardar" que realmente elimina datos) ni inspeccionar el comportamiento del lado del servidor. Para uso en producción, siempre ejecuta la exploración contra entornos confiables o sandboxed.

Validación de URL

Todas las URLs se validan antes de la navegación. El CLI acepta esquemas http:, https: y file: para que los fixtures locales funcionen; las herramientas MCP que toman URLs aceptan solo http: y https: para evitar exponer archivos locales a través de la navegación del navegador controlada por agentes. javascript:, data:, blob: y vbscript: son rechazadas. Las URLs con credenciales incrustadas (por ejemplo, https://user:pass@host/) también son rechazadas. Los rangos de IP privados/internos no se filtran: ejecutar Tactual en un entorno con acceso a servicios internos es equivalente a permitir que cualquier otra herramienta impulsada por Playwright los alcance, así que trata la entrada de URL como entrada confiable.

Licencia

Apache-2.0

Atribución

La redacción de roles/estados del simulador está calibrada contra el proyecto W3C ARIA-AT, que está licenciado bajo CC-BY 4.0. Tactual no incluye datos de ARIA-AT; el script de calibración (npm run calibrate) obtiene aserciones del repositorio upstream en tiempo de ejecución. Si publicas resultados de calibración de Tactual, por favor atribuye al proyecto W3C ARIA-AT como fuente de las aserciones de verdad fundamental.

Los datos de soporte de roles/atributos ARIA referenciados en la puntuación de riesgo de interoperabilidad se derivan de a11ysupport.io y del mismo proyecto ARIA-AT.