tactual-mcp
Analisador de custo de navegação por leitores de tela que mede o esforço real de navegação para usuários de tecnologia assistiva, construindo um grafo ponderado a partir de snapshots de acessibilidade do Playwright e pontuando cada destino sob perfis reais de tecnologia assistiva (NVDA, JAWS, VoiceOver, TalkBack, mobile genérico).
Documentação
Tactual
Analisador de custo de navegação para leitores de tela. Mede quantos toques de teclado um usuário de leitor de tela precisa para descobrir, alcançar e operar cada alvo interativo na sua página — sob um perfil de tecnologia assistiva (AT) específico (NVDA, JAWS, VoiceOver).
O que ele faz
Ferramentas de acessibilidade existentes verificam conformidade — o ARIA está correto? A taxa de contraste é suficiente?
O Tactual mede o custo de navegação — quantas ações um usuário de leitor de tela precisa para chegar ao botão de checkout? O que acontece se ele passar do ponto? Ele consegue sequer descobrir que o botão existe? O menu realmente abre com Enter, ou apenas com clique? O foco vai para o primeiro item do menu ou fica preso no gatilho?
Como funciona:
- Captura snapshots de acessibilidade do Playwright + simulação de anúncios do leitor de tela
- Opcionalmente explora ramos ocultos (menus, diálogos, abas, disclosures) e os testa com eventos reais de teclado, incluindo contratos de widgets no estilo APG e fluxos de erro de formulário
- Constrói um grafo de navegação com pontos de entrada (landmarks, títulos, Tab linear) e pontua cada alvo
- Opcionalmente valida caminhos previstos contra
@guidepup/virtual-screen-readerpara calibração
O Tactual é uma ferramenta de desenvolvedor para analisar seus próprios sites e ambientes de staging. Execute-o localmente, em CI, ou via servidor MCP no seu editor. Não é um serviço público de varredura.
Como ele se encaixa
O Tactual complementa scanners de conformidade como axe-core, Lighthouse e Pa11y. Essas ferramentas ainda são a primeira passagem correta para ampla cobertura de regras WCAG e ARIA. O Tactual visa a próxima pergunta: depois que uma página tem marcação válida, quão caro é para um usuário de AT descobrir, alcançar e operar os alvos importantes?
Use o Tactual para triagem de custo de navegação com leitor de tela, rastreamento de caminhos, evidências medidas de teclado/widget, diffs antes/depois, priorização em CI e fluxos de trabalho MCP onde um agente precisa de achados compactos com seletores de origem e candidatos a correção. Use leitores de tela reais e testes manuais para validação final de jornadas críticas, fluxos sensíveis a tempo, configurações de navegador/AT e padrões de implementação que intencionalmente diferem de um exemplo APG comum.
Caminho rápido para agentes: este README é uma visão geral do produto mais referência. Agentes devem começar com docs/AGENT-RECIPES.md para padrões de tarefas e docs/MCP-TOOLS.md para esquemas MCP completos, e depois voltar aqui apenas para contexto do produto, notas de instalação e exemplos de superfície de lançamento.
Instalação
Requer Node.js 20 ou posterior.
npm install tactual
O Tactual instala o Playwright como dependência de runtime para que comandos avulsos de npx tactual@latest ... funcionem sem instalar o Playwright separadamente no cache do npx. O SDK MCP também acompanha como dependência de runtime, então tactual-mcp funciona a partir de um pacote tactual instalado sem uma instalação separada do SDK.
Início 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
A saída do console inclui uma linha de caminho compactada para cada achado mostrando como um usuário de leitor de tela o alcança:
██████░░ 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
Onde Tab = nextItem, H = nextHeading, ; = nextLandmark, K = nextLink, B = nextButton, Enter = ativar. Etapas consecutivas com a mesma ação são colapsadas (Tab ×2).
Da Auditoria à Correção
Para trabalho de acessibilidade em um aplicativo local ou ambiente de preview, o Tactual fornece evidências para mudanças pequenas e revisáveis:
selector,penalties,suggestedFixese resumos de evidência em cada achadoissueGroupsagrupados e candidatos a correção na saída resumidaanalyze_pages.site.repeatedNavigationpara custo de navegação repetido entre rotasdiff-results/diff_resultspara verificação antes e depois
Comece com triagem ampla, depois aprofunde uma rota antes de mudar o 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
Use a seção de candidatos como ponto de partida para causas raiz repetidas, como um componente compartilhado, padrão de navegação ou contrato de widget. Confirme o componente de origem e inclua a rota, comando, evidência do achado, impacto no usuário, mudança de código e verificação em qualquer formato de issue ou PR que o projeto espera. A mudança na pontuação é evidência de apoio útil, mas a mudança deve liderar com o comportamento de acessibilidade que foi alterado.
Clientes MCP podem consumir a mesma saída compacta e manter o ciclo de revisão fundamentado em rotas, seletores, evidências, mudanças de origem e verificação antes/depois.
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 anúncios de leitor de tela — prevê o que NVDA, JAWS ou VoiceOver anunciariam para cada alvo, com informações de estado (marcado, expandido, selecionado, modal, valor, obrigatório, 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 validação e calibração — compara a saída do modelo contra execuções de validação de SR virtual ou conjuntos de dados de observação 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));
Ou pela CLI:
npx tactual transcript https://example.com --at voiceover
npx tactual calibration-report my-calibration.json --analysis example-nvda.json
O simulador é previsão heurística, não saída real de leitor de tela. O simulador em si é rápido (JavaScript puro sobre alvos capturados — sub-segundo uma vez que os alvos estão em memória), mas uma execução completa de analyze-url inclui inicialização do navegador + captura de página + pontuação e leva segundos em páginas pequenas, mais tempo com --probe (~30s+) e --explore (~1–5 min em SPAs complexos). A análise roda em navegador headless por padrão, então nada aparece enquanto você trabalha. (Use --no-headless ou --channel chrome --stealth para sites visíveis/protegidos contra bots.)
Qualidade de dados. Calibrado contra asserções em nível de token do projeto W3C ARIA-AT: 77/77 asserções de token de papel/nome/estado passam a 100% nos três ATs (NVDA, JAWS, VoiceOver), cobrindo formulação de papel/nome/estado para 36 padrões de alvo único (botão, botão de alternância, todas as variantes de botão de menu, disclosure, accordion, checkbox/estado triplo, switch, sliders, diálogo, alerta, links, abas, comboboxes, radiogroups, spin button, menubar) mais 4 cenários de landmark com múltiplos alvos. Execute npm run calibrate após npm run build para verificar contra as asserções upstream mais recentes. Isso é calibração de simulador, não prova de fidelidade completa de leitor de tela entre modos de navegação, configurações de verbosidade, tempo ou cada variante válida de widget. Overrides específicos de AT fora do conjunto calibrado são rotulados com confiança ALTA/MÉDIA/BAIXA na origem.
Servidor MCP
O Tactual inclui um 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 implantações MCP voltadas à rede, coloque o transporte HTTP atrás de um proxy TLS autenticado e mantenha-o restrito a clientes confiáveis. Veja SECURITY.md para a lista de verificação hospedada e modelo de ameaça.
Ferramentas MCP disponíveis:
| Ferramenta | Descrição |
|---|---|
analyze_url | Analisa uma página para custo de navegação SR (padrão SARIF). Suporta exploração opcional, sondas de teclado/widget/formulário, sondagem direcionada/por objetivo, stealth/channel para sites protegidos contra bots e filtragem. |
trace_path | Caminho de navegação passo a passo até um alvo com anúncios SR modelados. |
validate_url | Valida caminhos previstos contra @guidepup/virtual-screen-reader. Retorna alcançável + precisão média por estratégia (linear/semântica). Fecha o ciclo previsto-vs-validado. |
calibration_report | Executa conjuntos de dados de calibração observados contra JSON de análise completa salvo e retorna sinais de pontuação estruturados para fluxos de ajuste/revisão. |
list_profiles | Lista perfis de AT disponíveis. |
diff_results | Compara dois resultados de análise — melhorias, regressões, mudanças de severidade. |
suggest_remediations | Sugestões de correção ranqueadas por impacto. |
save_auth | Autentica e salva estado de sessão para analisar conteúdo protegido. |
analyze_pages | Triagem de site com múltiplas páginas com estatísticas agregadas e grupos de custo de navegação repetidos entre páginas. |
Referência completa de parâmetros: docs/MCP-TOOLS.md
Configuração por ferramenta de IA
Primeiro instale os pacotes necessários no seu projeto:
npm install tactual
Claude Code — adicione a .mcp.json na raiz do seu projeto:
{
"mcpServers": {
"tactual": {
"type": "stdio",
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
GitHub Copilot — adicione a .copilot/mcp.json ou ~/.copilot/mcp-config.json:
{
"mcpServers": {
"tactual": {
"type": "stdio",
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
Cursor / Windsurf / Cline — mesmo formato na configuração MCP do seu editor:
{
"mcpServers": {
"tactual": {
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
Direto (instalação global) — se preferir não usar npx:
npm install -g tactual
tactual-mcp # starts the MCP server on stdio
GitHub Actions
Use a action composta do 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"
A action instala o Tactual e os binários do navegador Chromium, executa a análise, envia SARIF para o GitHub Code Scanning e falha o build se a pontuação média estiver abaixo do limite. Defina comment-on-pr: "true" para postar um comentário resumido em pull requests (atualiza em re-execução). Saídas average-score e result-file para etapas downstream. A versão da action acompanha a versão do Tactual — aumente a linha uses: para receber patches.
Os padrões são conservadores: probe está desligado a menos que habilitado porque envia eventos reais de teclado, e verificações de ícones com cores forçadas rodam apenas para perfis que declaram visualModes como nvda-desktop-v0 e jaws-desktop-v0.
Ou use a CLI diretamente para mais controle:
- 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
Portão de regressão (falha de CI em pior-que-baseline)
Combine --baseline com --fail-on-regression para transformar o Tactual em um portão estrito de CI: salve um baseline de um build conhecidamente bom, depois falhe verificações de PR sempre que uma mudança regredir N+ achados vs o baseline. O comando diff-results também pode ser executado separadamente para relatórios legíveis de antes/depois.
# 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
Ou via action:
- uses: tactual-dev/tactual@v0.5.0
with:
url: https://pr-preview.your-app.com
baseline: tactual-baseline.json
fail-on-regression: "3"
A action espelha a superfície CLI de analyze-url para entradas de análise, e um teste de contrato CI-para-CLI mantém esses campos alinhados. Alguns controles de fluxo de trabalho são orquestração da Action em vez de flags diretas da CLI: fail-below envolve a CLI --threshold, comment-on-pr controla a etapa de comentário em PR, e o upload de SARIF é tratado pelo fluxo de trabalho. As entradas comuns que você definirá incluem 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. O padrão de invocação direta da CLI acima ainda é o caminho recomendado quando você quer uma versão diferente do Tactual daquela fixada pela action.
| Superfície | Convenção de nomenclatura | Exemplo |
|---|---|---|
| CLI | flags kebab-case | --probe-strategy modal-return-focus |
| Opções MCP e de biblioteca | campos camelCase | probeStrategy: "modal-return-focus" |
| GitHub Action | inputs kebab-case | probe-strategy: modal-return-focus |
Configuração
Flags de 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
Crie com tactual init ou 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"
}
}
A configuração é detectada automaticamente a partir do diretório de trabalho (tactual.json ou .tactualrc.json). As flags de CLI são mescladas com as configurações do arquivo e as substituem.
Perfis de AT
| Perfil | Plataforma | Descrição |
|---|---|---|
generic-mobile-web-sr-v0 | Mobile | Primitivas normalizadas de SR móvel (padrão) |
voiceover-ios-v0 | Mobile | VoiceOver no iOS Safari — navegação baseada em rotor |
talkback-android-v0 | Mobile | TalkBack no Android Chrome — controles de leitura |
nvda-desktop-v0 | Desktop | NVDA no Windows — teclas rápidas do modo de navegação |
jaws-desktop-v0 | Desktop | JAWS no Windows — cursor virtual com modo de formulários automático |
Os perfis definem o custo de cada ação de navegação, os pesos das dimensões de pontuação, costSensitivity (escala a curva de decaimento de acessibilidade) e modificadores dependentes de contexto. Consulte src/profiles/ para detalhes de implementação.
Limitação do perfil mobile. Os perfis voiceover-ios-v0 e talkback-android-v0 modelam os custos de ação e o fraseado dos anúncios de SR com precisão, mas as sondas de teclado do Tactual (--probe) testam apenas interações de desktop (Tab, Enter, Escape). Elas NÃO simulam gestos de toque (toque único, toque duplo, deslizar para a direita, deslizar com três dedos, rotação do rotor, etc.). Para perfis mobile, as dimensões de pontuação refletem o custo previsto pelo modelo do perfil — não o comportamento medido. Testes em dispositivos reais continuam sendo necessários para verificar a acessibilidade móvel.
Modos visuais. Os perfis nvda-desktop-v0 e jaws-desktop-v0 declaram uma matriz visualModes (claro/escuro × cores forçadas ligado/desligado) para que o analisador capture o contraste de cada ícone sob cada combinação. Os perfis mobile e genérico omitem isso — o Modo de Alto Contraste do Windows não é uma preocupação realista para mobile. Consulte Verificações de visibilidade abaixo.
Verificações de visibilidade
Quando o perfil ativo declara uma matriz visualModes, o Tactual re-emula cada combinação de (colorScheme, forcedColors) após a captura inicial e amostra os estilos computados de cada ícone. O construtor de achados compara o fill computado de cada ícone com o background-color do ancestral não transparente mais próximo e emite uma penalidade quando o contraste fica abaixo do limite não textual da WCAG 1.4.11 (3:1).
Quatro redações de penalidade, três níveis de pontuação:
| Penalidade | Gatilho | Impacto na operabilidade |
|---|---|---|
Icon invisible in <mode> | Contraste < 1,5:1, sem rótulo de texto adjacente | Operabilidade limitada a 60 |
Decorative icon invisible in <mode> | Contraste < 1,5:1, o controle tem rótulo de texto visível | Operabilidade −5 |
Low icon contrast in <mode> | Contraste 1,5–3,0:1, sem rótulo de texto adjacente | Operabilidade −5 |
Author-set SVG fill in <mode> | Contraste OK no Playwright (≥3:1), mas o modo é forced-colors: active e o preenchimento é um literal CSS do autor (não sistêmico, não currentColor) | Operabilidade −2 |
A verificação ignora ícones que já são seguros para HCM: fill="currentColor", fill: ButtonText (ou qualquer cor do sistema), forced-color-adjust: none (opt-out do autor) ou fill === color computado (currentColor aplicado via CSS). Ícones de baixo contraste ao lado de um rótulo de texto visível são suprimidos por completo — o rótulo identifica o controle e o ícone é apenas reforço.
Por que o nível de risco de substituição existe. Diferentes temas de HCM do usuário têm valores diferentes de Canvas/ButtonText/cores do sistema. Um preenchimento literal do autor (ex.: svg { fill: #e4e6e6 }) pode ter bom contraste contra a paleta HCM padrão do Chromium, mas ruim contra um tema específico do usuário. A renderização do literal pelo navegador é consistente entre Playwright, Chrome e Edge para forced-color-adjust: preserve-parent-color (o padrão para caminhos SVG) — a preocupação concreta é a variabilidade do tema, não uma substituição oculta de pintura do SO. O Tactual sinaliza o padrão para que você saiba verificar no Edge real com um tema HCM representativo, não porque a medição de contraste do Playwright é enganosa.
Desative explicitamente via --no-check-visibility, checkVisibility: false em tactual.json ou checkVisibility: false na ferramenta analyze_url do MCP. Force a ativação via --check-visibility mesmo quando um perfil não declara modos (sem efeito sem modos).
A verificação adiciona aproximadamente +50–200ms por modo declarado por página — re-emular mídias é barato; não há novo contexto de navegador por modo.
Predefinições de pontuação
As predefinições agrupam filtros de foco e mapeamentos de prioridade para casos de uso comuns. Elas se sobrepõem aos arquivos de configuração e às flags de CLI (predefinição → tactual.json → flags de CLI).
| Predefinição | Caso de uso | Foco | Alvos críticos |
|---|---|---|---|
ecommerce-checkout | Fluxos de compra | main | checkout, cart, payment, buy |
docs-site | Documentação | main, navigation | search, nav |
dashboard | Aplicações web | main, navigation | save, submit, create, delete, search |
form-heavy | Páginas de formulário | main | submit, save, next, continue, error |
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual presets # list all presets with details
As predefinições suprimem banners de cookies e alvos de analytics por padrão. Para substituir, use --exclude ou defina priority em tactual.json. As predefinições não se compõem — apenas uma --preset pode estar ativa.
Pontuação
Cada alvo recebe um vetor de pontuação de 5 dimensões:
| Dimensão | O que mede |
|---|---|
| Descobribilidade | O usuário consegue perceber que o alvo existe? |
| Alcance | Qual é o custo de navegação para chegar lá? |
| Operabilidade | O controle se comporta de forma previsível? |
| Recuperação | Quão difícil é se recuperar de ultrapassar o alvo? |
| Risco de interop | Qual a probabilidade de variação de suporte AT/navegador? (penalidade) |
Os pesos das dimensões variam por perfil:
| Perfil | D | R | O | Rec | costSensitivity |
|---|---|---|---|---|---|
| generic-mobile-web-sr-v0 | 0.30 | 0.40 | 0.20 | 0.10 | 1.0 |
| voiceover-ios-v0 | 0.30 | 0.35 | 0.20 | 0.15 | 1.1 |
| talkback-android-v0 | 0.25 | 0.45 | 0.20 | 0.10 | 1.3 |
| nvda-desktop-v0 | 0.35 | 0.25 | 0.30 | 0.10 | 0.7 |
| jaws-desktop-v0 | 0.30 | 0.25 | 0.35 | 0.10 | 0.6 |
Composto: Média geométrica ponderada: overall = exp(sum(w_i * ln(score_i)) / sum(w_i)) - interopRisk. Cada dimensão é limitada a um mínimo de 1 antes do log para evitar log(0). Um zero em qualquer dimensão elimina a contribuição dessa dimensão para a média geométrica, reduzindo significativamente a pontuação geral — você não pode operar o que não consegue alcançar.
Faixas de severidade:
| Pontuação | Faixa | Significado |
|---|---|---|
| 90-100 | Forte | Baixa preocupação |
| 75-89 | Aceitável | Melhorável |
| 60-74 | Moderado | Deve ser triado |
| 40-59 | Alto | Provável atrito significativo |
| 0-39 | Grave | Provavelmente bloqueante |
Diagnósticos
O Tactual emite diagnósticos para confiabilidade de captura, estrutura da página, acesso visual, evidências de runtime, validade de ARIA e padrões de custo repetidos. Avisos são sugestões de revisão, não falhas automáticas de conformidade. Muitas verificações visuais/de conteúdo são heurísticas e devem ser confirmadas em contexto antes de registrar um defeito.
| Código | Nível | Significado |
|---|---|---|
blocked-by-bot-protection | erro | Página de bot/desafio detectada; o conteúdo capturado não é a página pretendida. |
empty-page | erro | Nenhum alvo encontrado. |
ok | info | A captura produziu um conjunto de alvos sem avisos de confiabilidade. |
possibly-degraded-content | aviso | Suspeita de poucos alvos para uma página HTTP. |
sparse-content | aviso | Apenas 1-4 alvos encontrados. |
possible-login-wall | aviso | Suspeita de conteúdo restrito por autenticação ou redirecionamento de login. |
possible-cookie-wall | info | O consentimento de cookies pode obscurecer o conteúdo. |
redirect-detected | info/aviso | A captura caiu em uma URL ou domínio diferente. |
timeout-during-render | aviso | Uma espera de renderização solicitada não foi concluída antes da captura. |
framework-detected | info | Sinais de framework frontend foram detectados durante a captura. |
spa-route-changes | info | Mudanças de rota SPA aconteceram durante a análise. |
exploration-no-new-states | aviso | --explore foi executado, mas não revelou estados adicionais. |
frames-descended | info | A descida em iframes capturou ou pulou quadros filhos. |
auto-scrolled | info | A rolagem automática foi executada antes da captura e relata o que surgiu. |
banners-dismissed | info | A dispensa do banner de cookies/consentimento foi tentada. |
tab-order-walked | info/aviso | A varredura por ordem de tabulação registrou paradas de foco; avisa em tabindex positivo. |
viewport-divergence | aviso | Diferença de viewport desktop/mobile encontrou alvos, marcos ou títulos ausentes. |
no-headings | aviso | Nenhum elemento de título (heading) encontrado. |
heading-skip | aviso | A hierarquia de títulos pula um nível, como h1 -> h3. |
empty-heading | aviso | O título existe, mas não tem texto. |
numeric-heading | aviso | O texto do título é apenas dígitos, pontuação ou conteúdo trivial de um caractere. |
h1-count | info/aviso | A página não tem um H1 único útil, ou tem vários H1s que valem revisão. |
no-landmarks | aviso | Nenhuma região de marco (landmark) encontrada. |
no-main-landmark | aviso | Marco <main> ausente. |
no-banner-landmark | info | Marco <header> / banner ausente. |
no-contentinfo-landmark | info | Marco <footer> / contentinfo ausente. |
no-nav-landmark | info | Marco <nav> / navegação ausente. |
landmark-demoted | aviso | O marco HTML existe, mas é rebaixado pelo contexto de aninhamento. |
structural-summary | info | Visão geral estrutural de uma linha. |
no-skip-link | aviso | Nenhum link "pular para o conteúdo" em páginas com 5+ alvos. |
broken-skip-link | aviso | Link estilo "pular" aponta para um alvo de fragmento ausente. |
skip-link-not-first | aviso | Um link de pular existe, mas não é alcançável nas duas primeiras paradas de Tab. |
visual-order-divergence | aviso | A ordem visual parece divergir da ordem de navegação DOM/SR. |
shared-structural-issue | aviso | Uma penalidade que afeta >50% dos alvos é promovida ao nível da página. |
redundant-tab-stops | aviso | Múltiplos alvos de link criam paradas de Tab repetidas para o mesmo destino. |
data-flow-dependencies | info | Estados explorados revelam controles que só são habilitados após ação anterior. |
form-summary | info/aviso | Resume formulários e avisa quando um formulário parece não ter controle de envio. |
missing-autocomplete | aviso | Campos de formulário padrão não têm tokens autocomplete úteis ou os desabilitam. |
empty-interactive | aviso | Alvo interativo sem nome acessível. |
fake-interactive-elements | aviso | Elementos não semânticos clicáveis não são alcançáveis por teclado/SR. |
cdp-click-listeners | aviso | CDP encontrou listeners de clique em elementos não interativos. |
ambiguous-link-names | aviso | Links com o mesmo nome acessível apontam para destinos diferentes. |
media-without-controls | aviso | Áudio/vídeo sem controles e não oculto. |
duplicate-id | aviso | Valores duplicados de id podem quebrar rótulos e referências ARIA. |
nested-interactive | aviso | Controles interativos aninhados dentro de outros controles interativos. |
meta-refresh | aviso | A página atualiza ou redireciona automaticamente via meta refresh. |
missing-image-alt | aviso | Imagens sem atributos alt. |
suspicious-image-alt | aviso | Texto alternativo de imagem parece preenchimento ou placeholder tipo nome de arquivo. |
missing-iframe-title | aviso | Iframes sem title ou rótulo acessível. |
missing-html-lang | aviso | <html lang> ausente ou não parece uma tag de idioma BCP 47. |
poor-document-title | aviso | Título do documento ausente, vazio, curto demais ou genérico. |
viewport-blocks-zoom | aviso | Configurações de viewport meta restringem o zoom do usuário. |
low-contrast-text | aviso | Texto interativo ou títulos falham nos limites de contraste de texto estilo WCAG. |
color-only-conveyance | aviso | Texto parece depender apenas de cor para transmitir significado. |
color-blindness-contrast-fail | aviso | Texto perde contraste sob deficiência simulada de visão de cores. |
lang-switch-without-marker | aviso | O idioma do texto parece mudar sem um marcador lang. |
invalid-aria-role | aviso | Papel ARIA não padrão presente. |
unknown-aria-attr | aviso | Atributo aria-* desconhecido presente. |
invalid-aria-attr-value | aviso | Valor de atributo ARIA fora do conjunto de valores permitidos. |
missing-required-aria-attr | aviso | Papel ARIA sem estado ou propriedade obrigatória. |
aria-naming-prohibited | aviso | Nome aplicado a um papel que proíbe nomeação. |
unsupported-aria-attr-for-role | aviso | Atributo ARIA não suportado no papel do elemento. |
Exploração
A flag --explore ativa exploração de ramos limitada:
- Abre menus, abas, disclosures, accordions e diálogos
- Captura novos estados de acessibilidade de UI oculta
- Marca alvos descobertos como
requiresBranchOpen - Respeita orçamentos de profundidade, contagem de ações, contagem de alvos e novidade
- Política de ação segura bloqueia interações destrutivas
A exploração é útil para páginas com UI oculta significativa (ex.: menus suspensos, interfaces com abas, diálogos modais).
Candidatos à exploração são ordenados por uma chave estável (papel + nome) antes da iteração, para que o mesmo conteúdo de página produza a mesma ordem de exploração entre execuções.
Sondas
A flag --probe mede se padrões interativos importantes funcionam após aparecerem na árvore de acessibilidade. As sondas são opt-in porque enviam eventos reais de teclado e adicionam tempo de execução. Desde 0.4.0, isso inclui verificações genéricas de foco/ativação, contratos de menu, contratos de diálogo modal, fluxos de gatilho para diálogo, abas, disclosures, comboboxes, listboxes e fluxos de erro de campo obrigatório. Os achados das sondas incluem resumos de evidências para que os relatórios distingam falhas medidas de pontuação modelada ou heurística.
Controles orientados a objetivos mantêm sondas profundas úteis em SPAs complexos:
| Necessidade | CLI | Campo MCP/Ação | Efeito |
|---|---|---|---|
| Analisar uma subárvore | --scope-selector "#drawer" | scopeSelector / scope-selector | Captura, pontua e sonda apenas a(s) subárvore(s) selecionada(s). |
| Sondar uma subárvore | --probe-selector "#drawer" | probeSelector / probe-selector | Mantém a pontuação em toda a página, mas gasta o orçamento de sondas apenas dentro da(s) subárvore(s) selecionada(s). |
| Abrir um ramo primeiro | --entry-selector "[aria-controls='menu']" | entrySelector / entry-selector | Ativa o gatilho antes da captura/sonda e prioriza alvos recém-revelados. |
| Mirar em um alvo conhecido | --goal-target "checkout" | goalTarget / goal-target | Restringe a sondagem a ids, nomes, papéis, tipos ou seletores de alvo correspondentes. |
| Mirar por glob | --goal-pattern "*dialog*" | goalPattern / goal-pattern | Igual ao alvo de objetivo, com correspondência por glob. |
| Gastar orçamento por intenção | --probe-strategy modal-return-focus | probeStrategy / probe-strategy | Executa as famílias de sondas relevantes para all, overlay, composite-widget, form, navigation, modal-return-focus ou menu-pattern. |
Por exemplo, para avaliar um ramo modal sem rastrear menus não 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
Orçamentos de exploração
| Orçamento | Flag CLI | Padrão | Propósito |
|---|---|---|---|
| Profundidade | --explore-depth | 3 | Profundidade máxima de recursão |
| Ações | --explore-budget | 50 | Orçamento total de cliques em todos os ramos |
| Alvos | --explore-max-targets | 2000 | Parar se os alvos acumulados excederem isso |
| Tempo | --explore-timeout | 60000 ms | Limita o tempo total de exploração, incluindo sondas iniciais, capturas de ramos e sondas de estados revelados |
Orientação de dimensionamento:
| Tipo de página | Configurações sugeridas | Porquê |
|---|---|---|
| Site de marketing, página de documentação, blog | padrões | Superfície pequena, padrões raramente atingidos |
| Dashboard com barra lateral/menu | --explore-depth 3 --explore-budget 50 (padrões) | Captura um nível de aberturas de menu |
| Aplicativo complexo (Figma, Notion, etc.) | --explore-depth 4 --explore-budget 100 --explore-max-targets 5000 | Menus mais profundos, mais estado |
| Páginas com UI oculta muito grande (seletores de emoji, grades de cores) | --explore-max-targets 10000 mais --exclude "emoji-*" | Limitar ou filtrar o fluxo excessivo |
| Triagem rápida de página desconhecida | --explore-depth 1 --explore-budget 10 | Apenas abrir ramos óbvios, rápido |
Se a exploração atingir o tempo limite antes de abrir ramos úteis, aumente --explore-timeout e --explore-budget lentamente, ou use --entry-selector, --probe-selector e --probe-strategy para gastar o mesmo orçamento no ramo que você deseja. Se a saída tiver alvos com aparência duplicada, reduza --explore-depth (recursão profunda pode redescobrir os mesmos elementos por caminhos diferentes).
Detecção de framework SPA
O Tactual detecta quando o conteúdo SPA foi renderizado antes de capturar a árvore de acessibilidade. Frameworks detectados: React, Next.js, Vue, Nuxt, Angular, Svelte e SvelteKit. Sinais genéricos de conteúdo HTML5 (marcos, títulos, navegação, links) também são verificados. Para SPAs não cobertos pela detecção automática, use --wait-for-selector (CLI) ou waitForSelector (MCP/API) para especificar um seletor CSS que indique que seu aplicativo foi hidratado.
Após a detecção inicial do framework, o Tactual usa polling baseado em convergência — capturando repetidamente a árvore de acessibilidade até que a contagem de alvos se estabilize — o que funciona independentemente do framework.
Para aplicativos com muitos SPAs, estes auxiliares de captura opt-in são úteis:
npx tactual analyze-url https://app.example.com \
--wait-for-selector "main" \
--detect-routes \
--auto-scroll \
--descend-frames \
--diff-viewports
--detect-routesregistra eventos depushState,replaceState,popstateehashchangeque ocorrem durante a análise.--auto-scrollexpõe conteúdo preguiçoso orientado por IntersectionObserver antes da captura.--descend-framesanexa alvos de iframe com atribuição de URL do quadro. Iframes de mesma origem usam o snapshot de acessibilidade com escopo de quadro do Playwright; Chromium usa CDP como fallback para OOPIFs de origem cruzada quando o caminho normal de snapshot está inacessível. Firefox/WebKit mantêm o comportamento de pular quadros inacessíveis.--diff-viewportscaptura conteúdo de alvo, marco ou cabeçalho que desaparece entre viewports de desktop e mobile.--dismiss-banners,--probe-hovere--walk-tab-orderadicionam evidências de runtime direcionadas para bugs comuns de sobreposição em SPAs e de ordem de foco.
Benchmark de páginas conhecidas
Para evidências de lançamento contra páginas públicas complexas de SPA/biblioteca de componentes, execute:
npm run benchmark:known-pages
O script compila o pacote, executa analyze-url com a pilha de helpers de SPA
habilitada e grava run-results.json, summary.json e REPORT.md em
build/known-pages-*. Ele não é intencionalmente um gate de CI: sites públicos mudam,
bloqueiam automação e servem conteúdo diferente ao longo do tempo. Use o relatório para identificar
desvios e surpresas por categoria e, em seguida, use fixtures locais ou páginas de propriedade do
projeto para gates de regressão determinísticos. Para um smoke run limitado ou sondas de qualidade de captura APG/W3C,
chame o script diretamente, por exemplo
node scripts/known-pages-corpus.mjs --build --limit 1 ou
node scripts/known-pages-corpus.mjs --build --include-capture-probes.
Rastreamento de Regressão
Compare duas execuções de análise para detectar regressões:
# 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
O diff mostra alvos que melhoraram, regrediram ou mudaram de severidade, além de penalidades resolvidas e adicionadas. Em CI, use a entrada de ação comment-on-pr para publicar resultados automaticamente em cada pull request.
Risco de Interoperabilidade
Tactual inclui um snapshot estático de dados de suporte a papéis/atributos ARIA derivado de a11ysupport.io e do projeto ARIA-AT. Papéis com lacunas conhecidas de suporte entre AT/navegadores recebem uma penalidade de risco de interoperabilidade.
| Papel | Risco | Nota |
|---|---|---|
button, link, heading | 0 | Bem suportado |
dialog | 5 | Gerenciamento de foco varia |
combobox | 8 | Padrão mais problemático para interoperabilidade |
tree | 10 | Pouco suportado fora do JAWS |
application | 15 | Perigoso se mal utilizado |
Interpretando Descobertas
As descobertas do Tactual misturam intencionalmente vários domínios de evidência:
- Navegação por leitor de tela: marcos, cabeçalhos, rótulos, descoberta de ramificações, custo de travessia sequencial e anúncios modelados.
- Operabilidade por teclado: movimento de foco, ativação, recuperação com Escape, aprisionamento de Tab e sondas de widgets em runtime.
- Semântica estrutural: nomes ausentes, estrutura de cabeçalho/marco, marcos rebaixados, causas compartilhadas repetidas.
- Risco de interoperabilidade: papéis e estados com lacunas conhecidas de suporte entre AT/navegadores.
- Verificações adjacentes a ponteiro: problemas de tamanho de alvo e visibilidade de ícones que podem afetar usuários fora do modelo de navegação por leitor de tela.
Isso significa que uma página pode ter uma pontuação forte de navegação por leitor de tela e ainda receber avisos de link de pular, tamanho de alvo ou visibilidade. Trate-os como categorias de correção separadas, em vez de contradições.
Descobertas de APG derivadas de sondas são avisos de consistência medidos. Muitos widgets têm variantes de implementação válidas, especialmente comboboxes e padrões semelhantes a disclosure, então verifique o aviso contra o padrão pretendido antes de tratá-lo como uma substituição obrigatória. Fluxos críticos ainda devem ser verificados com a combinação de navegador/AT alvo.
Recomendações de Formato de Saída
| Formato | Tamanho típico | Melhor para |
|---|---|---|
console | ~8KB | Revisão humana no terminal |
markdown | ~11KB | PRs e comentários de issues |
json | ~18KB | Consumo programático |
sarif | ~4-40KB | GitHub Code Scanning / CI |
Todos os formatos de relatório não-SARIF emitem saída resumida por padrão: estatísticas, issues agrupadas, candidatos a remediação, resumos de evidência e piores descobertas (limitadas a 15). SARIF limita a 25 resultados. Quando a saída é truncada, uma nota aparece no topo. A API da biblioteca expõe o AnalysisResult completo; a saída do relatório CLI e MCP é intencionalmente compacta, a menos que um campo específico como includeStates seja solicitado.
Para uso via MCP, sarif é o formato padrão e recomendado. Use summaryOnly: true para uma verificação de saúde compacta com estatísticas, contagens de severidade, diagnósticos e as 3 principais issues.
Calibração
Tactual inclui uma estrutura de calibração (src/calibration/, exportada como tactual/calibration) para ajustar parâmetros de pontuação contra conjuntos de dados de verdade fundamental. Consulte docs/CALIBRATION.md para detalhes.
Observações de calibração também podem incluir feedback determinístico de anúncios de trabalho de revisão OSS: registre observedAnnouncement quando você souber a saída testada, ou observedAnnouncementTokens quando a frase exata for ruidosa, mas os tokens de papel/nome/estado forem claros. Tactual compara esses com seu anúncio modelado para o alvo correspondente e relata tokens ausentes ou inesperados. Use tactual calibration-report ou MCP calibration_report para executar um conjunto de dados contra a saída salva de analyze-url --full-json e emitir scoringSignals. Use tactual observe-announcement para gerar ou anexar observações somente de anúncio de uma análise salva, ou npm run -- nvda:vm:observe -- ... neste repositório para organizar uma pasta de captura controlada de VM NVDA. O corpus versionado do repositório vive em calibration/corpus/; execute npm run calibration:corpus para auditar gates de cobertura e npm run calibration:matrix após npm run build para classificar o trabalho de ajuste de alcance por MAE, viés, variância e desvio de plano de sequência obsoleto.
Prontidão de lançamento e limites conhecidos estão documentados em docs/RELEASE_TEST_MATRIX.md, docs/LIMITATIONS.md e docs/NVDA_VM_OBSERVER.md.
Desenvolvimento
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
Segurança
Sandboxing do navegador
Tactual sempre executa Playwright com sandboxing padrão do Chromium habilitado. Ele nunca desativa a segurança da web ou modifica o modelo de segurança do navegador. Todas as interações de página acontecem dentro do sandbox padrão do processo Chromium.
Política de ação segura
Quando a exploração está habilitada (--explore), Tactual classifica elementos interativos em três níveis antes de ativá-los:
| Nível | Ação | Exemplos |
|---|---|---|
| Seguro | Ativado | Abas, itens de menu, disclosures, accordions, âncoras da mesma página |
| Cautela | Ativado com cuidado | Links externos, botões ambíguos |
| Inseguro | Pulado | Botões de envio (fora de formulários de busca), Excluir, sair, comprar, implantar, cancelar assinatura |
Esta é uma heurística baseada em palavras-chave — ela não pode detectar engano semântico (por exemplo, um botão "Salvar" que na verdade exclui dados) ou inspecionar comportamento no servidor. Para uso em produção, sempre execute exploração contra ambientes confiáveis ou em sandbox.
Validação de URL
Todas as URLs são validadas antes da navegação. O CLI aceita esquemas http:, https: e file: para que fixtures locais funcionem; ferramentas MCP que aceitam URLs aceitam apenas http: e https: para evitar expor arquivos locais através de navegação de navegador controlada por agente. javascript:, data:, blob: e vbscript: são rejeitados. URLs com credenciais incorporadas (por exemplo, https://user:pass@host/) também são rejeitadas. Faixas de IP privadas/internas não são filtradas — executar Tactual em um ambiente com acesso a serviços internos é equivalente a permitir que qualquer outra ferramenta orientada por Playwright os alcance, então trate a entrada de URL como entrada confiável.
Licença
Apache-2.0
Atribuição
A frase de papel/estado do simulador é calibrada contra o projeto W3C ARIA-AT, que é licenciado sob CC-BY 4.0. Tactual não inclui dados ARIA-AT; o script de calibração (npm run calibrate) busca asserções do repositório upstream em tempo de execução. Se você publicar resultados de calibração do Tactual, por favor atribua o projeto W3C ARIA-AT como a fonte das asserções de verdade fundamental.
Dados de suporte a papéis/atributos ARIA referenciados na pontuação de risco de interoperabilidade são derivados de a11ysupport.io e do mesmo projeto ARIA-AT.