agent-engineering-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +249 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/cli.js +223 -0
- package/docs/agent-integration.md +200 -0
- package/docs/philosophy.md +131 -0
- package/docs/reference-authoring.md +117 -0
- package/docs/skill-authoring.md +126 -0
- package/examples/authorization-bypass.md +191 -0
- package/examples/frontend-review.md +244 -0
- package/examples/race-condition.md +128 -0
- package/examples/xp-reward-loop.md +123 -0
- package/package.json +45 -0
- package/references/engineering.yaml +88 -0
- package/references/frontend.yaml +139 -0
- package/references/product.yaml +54 -0
- package/references/research.yaml +88 -0
- package/references/security.yaml +85 -0
- package/references/ux.yaml +37 -0
- package/scripts/validate.py +454 -0
- package/skills/audit/adversarial-review/SKILL.md +190 -0
- package/skills/audit/business-logic-audit/SKILL.md +182 -0
- package/skills/audit/edge-case-hunter/SKILL.md +159 -0
- package/skills/audit/error-flow-audit/SKILL.md +184 -0
- package/skills/audit/state-consistency-audit/SKILL.md +174 -0
- package/skills/audit/user-flow-audit/SKILL.md +161 -0
- package/skills/frontend/accessibility-review/SKILL.md +186 -0
- package/skills/frontend/animation-review/SKILL.md +171 -0
- package/skills/frontend/interaction-design/SKILL.md +162 -0
- package/skills/frontend/ux-review/SKILL.md +172 -0
- package/skills/frontend/visual-quality-review/SKILL.md +160 -0
- package/skills/meta/research-router/SKILL.md +184 -0
- package/skills/meta/skill-router/SKILL.md +206 -0
- package/skills/product/gamification-audit/SKILL.md +213 -0
- package/skills/reliability/data-integrity-audit/SKILL.md +187 -0
- package/skills/reliability/idempotency-audit/SKILL.md +191 -0
- package/skills/reliability/race-condition-hunter/SKILL.md +181 -0
- package/skills/research/github-reference-research/SKILL.md +197 -0
- package/skills/research/implementation-research/SKILL.md +181 -0
- package/skills/research/market-research/SKILL.md +202 -0
- package/skills/research/reference-research/SKILL.md +186 -0
- package/skills/security/api-abuse-audit/SKILL.md +178 -0
- package/skills/security/authorization-audit/SKILL.md +176 -0
- package/skills/security/input-trust-audit/SKILL.md +178 -0
- package/templates/audit-report.md +89 -0
- package/templates/bug-report.md +107 -0
- package/templates/design-review.md +122 -0
- package/templates/research-report.md +96 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: state-consistency-audit
|
|
3
|
+
description: Compares state across database, API, server state, cache, client state, and URL state, and looks for divergences where the layers disagree about what is true.
|
|
4
|
+
category: audit
|
|
5
|
+
triggers:
|
|
6
|
+
- "audit state consistency"
|
|
7
|
+
- "find cache and database divergence"
|
|
8
|
+
- "client and server state out of sync"
|
|
9
|
+
- "url state vs server state"
|
|
10
|
+
- "stale cache bugs"
|
|
11
|
+
priority: high
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# State Consistency Audit
|
|
15
|
+
|
|
16
|
+
## Objective
|
|
17
|
+
|
|
18
|
+
Ensinar o agente a tratar o "estado do sistema" não como uma coisa única, mas como
|
|
19
|
+
**várias cópias que devem concordar** — e a procurar os pontos onde elas divergem.
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
database API response server (in-memory) cache client state URL state
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Um bug de consistência acontece quando duas camadas afirmam coisas diferentes sobre o
|
|
26
|
+
messe fato (ex: cache diz "saldo 100", banco diz "50") e o sistema age sobre a errada.
|
|
27
|
+
|
|
28
|
+
## When to Use
|
|
29
|
+
|
|
30
|
+
* Quando o sistema tem cache (CDN, Redis, in-memory, SWR/React Query no cliente).
|
|
31
|
+
* Quando o estado é refletido na URL (filtros, paginação, tabs, modais via querystring).
|
|
32
|
+
* Quando o cliente mantém estado (otimistic UI, estado local vs servidor).
|
|
33
|
+
* Quando há read replicas, eventual consistency, ou mensagens assíncronas.
|
|
34
|
+
* Quando o pedido menciona "stale", "out of sync", "cache", "refresh shows wrong",
|
|
35
|
+
"desync".
|
|
36
|
+
* **Composição:** pareia com `user-flow-audit` (refresh/back-button causam desync),
|
|
37
|
+
`data-integrity-audit` (o banco como fonte de verdade), `error-flow-audit` (estado
|
|
38
|
+
parcial após falha), `edge-case-hunter` (stale data como edge).
|
|
39
|
+
|
|
40
|
+
## Mental Model
|
|
41
|
+
|
|
42
|
+
O "estado" não é uma variável — é um **conjunto de representações** que o sistema
|
|
43
|
+
mantém em diferentes latências e locais por performance/UX. Cada representação tem um
|
|
44
|
+
TTL, um caminho de invalidação, e um caminho de leitura. Bugs nascem quando:
|
|
45
|
+
|
|
46
|
+
1. **Invalidação ausente** — o estado muda no banco mas o cache não é invalidado.
|
|
47
|
+
2. **Ordem de invalidação errada** — escreve no cache antes do banco (e falha), ou
|
|
48
|
+
invalida depois de servir a leitura stale.
|
|
49
|
+
3. **Otimistic UI não revertida** — o cliente assume sucesso, atualiza a UI, o servidor
|
|
50
|
+
falha, a UI fica inconsistente com o servidor.
|
|
51
|
+
4. **URL como fonte de verdade sem servidor** — a URL diz um estado que o servidor não
|
|
52
|
+
conhece (deep link para estado que expirou/foi revogado).
|
|
53
|
+
5. **Read replica lag** — escreve no primário, lê da replica antes da replicação, vê
|
|
54
|
+
estado antigo.
|
|
55
|
+
|
|
56
|
+
A pergunta central para cada fato do sistema: **qual camada é a fonte de verdade, e
|
|
57
|
+
todas as outras convergiram para ela?**
|
|
58
|
+
|
|
59
|
+
## Investigation Procedure
|
|
60
|
+
|
|
61
|
+
1. **Inventariar as camadas de estado** para o componente/fluxo. Nem todos os fluxos
|
|
62
|
+
usam todas as seis; liste só as que existem.
|
|
63
|
+
2. **Para cada fato relevante** (saldo, status do recurso, permissão, contador), rotule
|
|
64
|
+
a **fonte de verdade** (geralmente o banco) e as **cópias** (cache, cliente, URL).
|
|
65
|
+
3. **Traçar o caminho de escrita** — onde o fato é mutado, e em que ordem as camadas
|
|
66
|
+
são atualizadas.
|
|
67
|
+
4. **Traçar o caminho de leitura** — qual camada é lida em cada ponto, e se há fallback.
|
|
68
|
+
5. **Procurar invalidação ausente ou tardia** — quando o fato muda, cada cópia é
|
|
69
|
+
invalidada/atualizada? Antes ou depois de servir leituras?
|
|
70
|
+
6. **Testar desyncs concretos:**
|
|
71
|
+
* Mutar + ler imediatamente de cache (stale read).
|
|
72
|
+
* Mutar no primário + ler de replica (lag).
|
|
73
|
+
* Otimistic update + falha de servidor (UI não revertida).
|
|
74
|
+
* Deep link via URL para estado revogado (URL ≠ servidor).
|
|
75
|
+
* Duas abas / dois clientes mutando (um vê estado do outro?).
|
|
76
|
+
7. **Confirmar com evidência** — mostre as duas camadas discordando.
|
|
77
|
+
8. **Reportar** via `templates/audit-report.md`.
|
|
78
|
+
|
|
79
|
+
## Questions to Ask
|
|
80
|
+
|
|
81
|
+
* Quais camadas de estado existem neste fluxo? Qual é a fonte de verdade?
|
|
82
|
+
* Quando o fato muda no banco, o cache é invalidado? Quando — antes ou depois de servir?
|
|
83
|
+
* Há read replicas? A leitura após escrita vai para a replica ou o primário?
|
|
84
|
+
* A UI atualiza otimisticamente? Se o servidor falha, a UI reverte?
|
|
85
|
+
* A URL reflete estado? Esse estado é validado contra o servidor no carregamento?
|
|
86
|
+
* Dois clientes (duas abas, dois dispositivos) mutam o mesmo recurso — um vê a mudança
|
|
87
|
+
do outro? Quando?
|
|
88
|
+
* Há TTL de cache maior que a janela de mutação esperada?
|
|
89
|
+
* O cache é populado por quem? E invalidado por quem? (populador ≠ invalidador = bug)
|
|
90
|
+
|
|
91
|
+
## Attack Patterns
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
stale cache read
|
|
95
|
+
write db: balance 50 (was 100)
|
|
96
|
+
read cache: balance 100 ← invalidação faltou ou é assíncrona
|
|
97
|
+
→ age sobre 100, permite gastar além do real
|
|
98
|
+
|
|
99
|
+
read replica lag
|
|
100
|
+
write primary: status "paid"
|
|
101
|
+
read replica (imediatamente): status "pending" ← replicação não convergiu
|
|
102
|
+
→ trata como pendente, reprocessa, duplica efeito
|
|
103
|
+
|
|
104
|
+
optimistic UI not reverted
|
|
105
|
+
user clicks "like" → UI: liked ✓ (optimistic)
|
|
106
|
+
server: 401/500 ← falha
|
|
107
|
+
UI permanece liked ✓ ← não reverteu
|
|
108
|
+
→ UI ≠ servidor
|
|
109
|
+
|
|
110
|
+
URL ≠ server state
|
|
111
|
+
url: /doc/123?mode=edit
|
|
112
|
+
server: doc 123 was deleted / permission revoked
|
|
113
|
+
→ carrega modo edit de recurso inacessível?
|
|
114
|
+
|
|
115
|
+
two-client divergence
|
|
116
|
+
client A: edits resource, saves → server updated
|
|
117
|
+
client B: still showing old version (no realtime/poll)
|
|
118
|
+
→ B edita sobre estado antigo, sobrescreve A
|
|
119
|
+
|
|
120
|
+
cache populated by A, invalidated by nobody
|
|
121
|
+
service A writes cache on read (populate-on-miss)
|
|
122
|
+
service B writes db directly, never invalidates cache
|
|
123
|
+
→ cache perpetuamente stale até TTL
|
|
124
|
+
|
|
125
|
+
in-memory server state across instances
|
|
126
|
+
instance 1: local cache of "rate limit count"
|
|
127
|
+
instance 2: separate local cache
|
|
128
|
+
→ limit bypassable by rotating instance (round-robin)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Evidence Requirements
|
|
132
|
+
|
|
133
|
+
* **Nomear as duas camadas que discordam** e o fato específico.
|
|
134
|
+
* **Mostrar o estado em cada camada** (valor no banco vs valor no cache/cliente/URL).
|
|
135
|
+
* **Mostrar o mecanismo da divergência** — qual caminho de escrita não invalidou, qual
|
|
136
|
+
lag, qual otimistic não revertido.
|
|
137
|
+
* **Escalar confiança:**
|
|
138
|
+
* `CONFIRMED` — reproduziu e capturou os dois valores discordando (ex: resposta da
|
|
139
|
+
API vs query no banco).
|
|
140
|
+
* `HIGH CONFIDENCE` — código mostra invalidação ausente ou ordem errada, sem
|
|
141
|
+
reprodução manual.
|
|
142
|
+
* `POSSIBLE` — caminho plausível de desync, não confirmado.
|
|
143
|
+
* `SPECULATIVE` — "pode ficar stale" sem rastrear o mecanismo.
|
|
144
|
+
* Desyncs que permitem **agir sobre estado errado** (gastar saldo stale) são mais
|
|
145
|
+
graves que desyncs puramente visuais.
|
|
146
|
+
|
|
147
|
+
## False Positives
|
|
148
|
+
|
|
149
|
+
* **Stale visual aceitável** — alguns caches são *desenhados* para ser stale (ex:
|
|
150
|
+
contagem de likes aproximada, eventual consistency por produto). Se o sistema
|
|
151
|
+
tolera stale por design, não é bug. Marcar `POSSIBLE` e levantar como decisão de
|
|
152
|
+
produto se duvidar.
|
|
153
|
+
* **Invalidação existe mas é assíncrona por design** — invalidação eventual dentro de
|
|
154
|
+
uma janela documentada é aceitável; bug é só se a janela é indefinida ou não
|
|
155
|
+
garantida.
|
|
156
|
+
* **URL é puramente de UI** — se a URL só controla view state (tab aberta) sem claims
|
|
157
|
+
sobre dados, "URL ≠ servidor" não aplica.
|
|
158
|
+
* **Optimistic revert existe** — se há rollback no `onError`, não reportar. Confirmar
|
|
159
|
+
a ausência antes.
|
|
160
|
+
* **Read replica com read-your-writes guarantee** — se a leitura pós-escrita vai ao
|
|
161
|
+
primário (ou replica com lag < janela crítica), lag não é problema.
|
|
162
|
+
|
|
163
|
+
## Output Format
|
|
164
|
+
|
|
165
|
+
Para cada par de camadas que discorda com consequência, um finding via
|
|
166
|
+
`templates/audit-report.md`. Em **Affected component**, nomeie as camadas e o fato. Em
|
|
167
|
+
**Reproduction**, mostre a sequência (write → read) que produz a divergência e os dois
|
|
168
|
+
valores observados. Em **Root cause**, diga qual invalidação/ordem/lag/revert falta.
|
|
169
|
+
Em **Recommendation**, indique a estratégia (write-through, invalidação síncrona,
|
|
170
|
+
read-your-writes para o primário, rollback de optimistic, validação de URL no load).
|
|
171
|
+
|
|
172
|
+
Apresente um mapa de camadas por fato (fato | fonte de verdade | cópias | caminho de
|
|
173
|
+
invalidação | ✓/✗), marcando os desyncs. Desyncs que permitem agir sobre estado errado
|
|
174
|
+
primeiro; desyncs visuais depois.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: user-flow-audit
|
|
3
|
+
description: Maps a user flow as entry → preconditions → action → state change → feedback → next state and detects dead ends, impossible states, skippable steps, refresh and back-button problems, and duplicate operations.
|
|
4
|
+
category: audit
|
|
5
|
+
triggers:
|
|
6
|
+
- "audit a user flow"
|
|
7
|
+
- "map the states of a feature"
|
|
8
|
+
- "find dead ends in a flow"
|
|
9
|
+
- "review onboarding or checkout flow"
|
|
10
|
+
- "what happens on refresh or back button"
|
|
11
|
+
priority: high
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# User Flow Audit
|
|
15
|
+
|
|
16
|
+
## Objective
|
|
17
|
+
|
|
18
|
+
Ensinar o agente a modelar um fluxo de usuário como uma **máquina de estados** e a
|
|
19
|
+
procurar estados dos quais o usuário não consegue sair, estados impossíveis, passos que
|
|
20
|
+
podem ser pulados, e desyncs causados por refresh/back-button.
|
|
21
|
+
|
|
22
|
+
A skill trata o fluxo como uma sequência canônica:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
entry → preconditions → action → state change → feedback → next state
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## When to Use
|
|
29
|
+
|
|
30
|
+
* Ao auditar qualquer fluxo multi-passo (onboarding, checkout, criação de personagem,
|
|
31
|
+
publicação, convites, setup).
|
|
32
|
+
* Quando o pedido menciona "flow", "steps", "wizard", "onboarding", "checkout".
|
|
33
|
+
* Quando há risco de o usuário ficar preso em um estado intermediário.
|
|
34
|
+
* **Composição:** frequentemente junto com `state-consistency-audit` (fluxo vs estado
|
|
35
|
+
do servidor), `error-flow-audit` (o que acontece quando um passo do fluxo falha),
|
|
36
|
+
`business-logic-audit` (pré-condições do fluxo = regras de negócio), e
|
|
37
|
+
`edge-case-hunter` (entrances anômalas no fluxo).
|
|
38
|
+
|
|
39
|
+
## Mental Model
|
|
40
|
+
|
|
41
|
+
Um fluxo não é uma lista de telas. É uma **máquina de estados**: cada nó é um estado
|
|
42
|
+
persistido (no servidor, no cliente, ou na URL), e cada transição é uma ação que move
|
|
43
|
+
de um estado a outro. Bugs vivem nas transições e nos estados, não nas telas.
|
|
44
|
+
|
|
45
|
+
As duas falhas mais comuns:
|
|
46
|
+
|
|
47
|
+
1. **Estados sem saída** — o usuário chega a um estado do qual nenhuma ação legítima o
|
|
48
|
+
tira. Dead end.
|
|
49
|
+
2. **Transições implícitas** — o fluxo assume que o estado anterior foi atingido, mas
|
|
50
|
+
nada o impede de pular direto a um estado posterior. Passo pulável.
|
|
51
|
+
|
|
52
|
+
O modelo força a pergunta: *de cada estado, para onde o usuário pode ir — e o que o
|
|
53
|
+
impede de ir para onde não deveria?*
|
|
54
|
+
|
|
55
|
+
## Investigation Procedure
|
|
56
|
+
|
|
57
|
+
1. **Desenhar o fluxo nominal** como `entry → preconditions → action → state change →
|
|
58
|
+
feedback → next state`. Um nó por estado.
|
|
59
|
+
2. **Rotular onde cada estado vive** — servidor, cliente, URL, ou cache. (Isto
|
|
60
|
+
conecta com `state-consistency-audit`.)
|
|
61
|
+
3. **Para cada transição, listar a pré-condição** que deve ser verdadeira para ela
|
|
62
|
+
ocorrer.
|
|
63
|
+
4. **Testar pulabilidade:** a pré-condição é *verificada no servidor* ou *assumida*? Se
|
|
64
|
+
assumida, o passo é pulável via chamada direta ao endpoint do passo seguinte.
|
|
65
|
+
5. **Testar estados sem saída:** para cada estado, existe uma ação legítima que leva a
|
|
66
|
+
um próximo estado útil? Se não, é dead end.
|
|
67
|
+
6. **Testar refresh:** em cada estado, o que acontece se o usuário recarregar? O estado
|
|
68
|
+
é reconstruído a partir do servidor, ou perdido/resetado?
|
|
69
|
+
7. **Testar back-button:** o que acontece ao voltar? O usuário reexecuta uma ação
|
|
70
|
+
não-idempotente? Volta a um estado que não deveria mais ser acessível?
|
|
71
|
+
8. **Testar duplicação:** o usuário pode executar a mesma ação duas vezes (duplo submit,
|
|
72
|
+
double click) e causar dois efeitos?
|
|
73
|
+
9. **Listar estados impossíveis** — combinações que a máquina deveria proibir mas que
|
|
74
|
+
podem ser alcançadas por pulo, refresh, ou back.
|
|
75
|
+
10. **Reportar** findings via `templates/audit-report.md`.
|
|
76
|
+
|
|
77
|
+
## Questions to Ask
|
|
78
|
+
|
|
79
|
+
* Quais são todos os estados do fluxo? Onde cada um é persistido?
|
|
80
|
+
* Para cada transição, qual a pré-condição? Ela é checada no servidor?
|
|
81
|
+
* Existe um estado do qual nenhuma ação leva a lugar útil? (dead end)
|
|
82
|
+
* Posso pular direto para um estado avançado sem passar pelos anteriores?
|
|
83
|
+
* O que o refresh faz em cada estado? O estado é recuperado do servidor ou perdido?
|
|
84
|
+
* O back-button reexecuta uma ação? Volta a um estado obsoleto?
|
|
85
|
+
* Um duplo-submit cria dois recursos / duas recompensas?
|
|
86
|
+
* Existem combinações de estado que deveriam ser impossíveis mas são alcançáveis?
|
|
87
|
+
* O feedback dado ao usuário reflete o estado real do servidor?
|
|
88
|
+
|
|
89
|
+
## Attack Patterns
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
skip preconditions
|
|
93
|
+
POST /step-final (pulando /step-1 e /step-2)
|
|
94
|
+
→ efeito concedido sem pré-condições?
|
|
95
|
+
|
|
96
|
+
dead end
|
|
97
|
+
state: "payment_failed"
|
|
98
|
+
→ existe botão "retry"? "cancel"? ou o usuário fica preso?
|
|
99
|
+
|
|
100
|
+
refresh mid-flow
|
|
101
|
+
state: "form partially submitted"
|
|
102
|
+
refresh → estado reconstruído? ou volta ao início perdendo dados?
|
|
103
|
+
|
|
104
|
+
back-button after submit
|
|
105
|
+
submit → state: "created"
|
|
106
|
+
back → volta ao form → submit novamente
|
|
107
|
+
→ criação duplicada?
|
|
108
|
+
|
|
109
|
+
duplicate operation
|
|
110
|
+
double click em "Submit"
|
|
111
|
+
→ dois requests, dois efeitos?
|
|
112
|
+
|
|
113
|
+
impossible state reachable
|
|
114
|
+
resource marcado "deleted" mas ainda listado e editável
|
|
115
|
+
→ combinação proibida alcançada por caminho indireto
|
|
116
|
+
|
|
117
|
+
stale feedback
|
|
118
|
+
UI mostra "success" mas servidor reverteu por erro interno
|
|
119
|
+
→ feedback ≠ estado real
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Evidence Requirements
|
|
123
|
+
|
|
124
|
+
* **Nomear o estado e a transição** problemáticos (ex: "do estado `submitted` via
|
|
125
|
+
back-button de volta a `form`").
|
|
126
|
+
* **Mostrar onde o estado vive** (server/client/URL/cache) e onde a pré-condição é (ou
|
|
127
|
+
não é) verificada.
|
|
128
|
+
* **Reproduzir ou apontar o mecanismo** — sequência de passos/requests, ou o código que
|
|
129
|
+
assume a pré-condição sem checá-la.
|
|
130
|
+
* **Escalar confiança:**
|
|
131
|
+
* `CONFIRMED` — reproduziu o dead end / o pulo / o desync.
|
|
132
|
+
* `HIGH CONFIDENCE` — mecanismo claro no código (ex: handler não checa etapa
|
|
133
|
+
anterior), sem reprodução manual.
|
|
134
|
+
* `POSSIBLE` — fluxo parece permitir, caminho plausível, não confirmado.
|
|
135
|
+
* `SPECULATIVE` — "acho que refresh pode quebrar" sem rastrear o mecanismo.
|
|
136
|
+
|
|
137
|
+
## False Positives
|
|
138
|
+
|
|
139
|
+
* **Pré-condição verificada no servidor** — se o handler do passo final valida que os
|
|
140
|
+
anteriores ocorreram, o "pulo" não produz efeito. Confirmar antes de reportar.
|
|
141
|
+
* **Estado é puramente de UI** — alguns estados "intermediários" são só feedback visual
|
|
142
|
+
sem estado persistido; "perdê-los" no refresh é aceitável se o servidor é a verdade.
|
|
143
|
+
* **Back-button em fluxo idempotente** — se reexecutar é seguro (idempotência real),
|
|
144
|
+
não é bug (relacionado a `idempotency-audit`).
|
|
145
|
+
* **Dead end é intencional** — alguns estados terminais são deliberados (ex: "conta
|
|
146
|
+
banida"). Reportar como tal, não como defeito, ou marcar como `POSSIBLE` para decisão
|
|
147
|
+
de produto.
|
|
148
|
+
* **Duplicação protegida por disable de botão + server idempotency** — defesa em
|
|
149
|
+
profundidade; se ambas existem, não reportar.
|
|
150
|
+
|
|
151
|
+
## Output Format
|
|
152
|
+
|
|
153
|
+
Para cada estado/transição problemática, um finding via
|
|
154
|
+
`templates/audit-report.md`. Em **Reproduction**, dê a sequência exata de estados
|
|
155
|
+
visitados (incluindo refresh/back) que leva ao defeito. Em **Affected flow**, nomeie o
|
|
156
|
+
fluxo. Em **Recommendation**, indique *onde* corrigir (validação server-side no handler
|
|
157
|
+
do passo final; bloqueio de estado obsoleto; reconstrução de estado no refresh).
|
|
158
|
+
|
|
159
|
+
Anexe um diagrama da máquina de estados com os estados/transições problemáticos
|
|
160
|
+
marcados. Estados sem saída e transições puláveis são os mais graves — liste-os
|
|
161
|
+
primeiro.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility-review
|
|
3
|
+
description: Evaluates keyboard navigation, screen readers, focus management, semantic HTML, contrast, touch targets, reduced motion, forms, and error handling against WCAG standards.
|
|
4
|
+
category: frontend
|
|
5
|
+
triggers:
|
|
6
|
+
- "audit accessibility"
|
|
7
|
+
- "check keyboard navigation"
|
|
8
|
+
- "review screen reader support"
|
|
9
|
+
- "semantic html and aria"
|
|
10
|
+
- "wcag compliance"
|
|
11
|
+
- "touch targets and focus"
|
|
12
|
+
- "reduced motion and forms"
|
|
13
|
+
priority: high
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Accessibility Review
|
|
17
|
+
|
|
18
|
+
## Objective
|
|
19
|
+
|
|
20
|
+
Ensinar o agente a avaliar uma interface contra padrões de acessibilidade (WCAG):
|
|
21
|
+
keyboard, screen readers, focus, semantic HTML, contrast, touch targets, reduced
|
|
22
|
+
motion, forms, e erros. Como define o `plan.md` §10: acessibilidade não é extra — é
|
|
23
|
+
parte da definição de qualidade.
|
|
24
|
+
|
|
25
|
+
## When to Use
|
|
26
|
+
|
|
27
|
+
* Em qualquer tela que será usada por pessoas reais.
|
|
28
|
+
* Antes de lançar features que envolvem formulários, navegação, modal, interação
|
|
29
|
+
complexa, ou conteúdo dinâmico.
|
|
30
|
+
* Quando o pedido menciona "accessibility", "a11y", "WCAG", "keyboard", "screen
|
|
31
|
+
reader", "aria", "focus", "contrast", "touch target", "reduced motion".
|
|
32
|
+
* **Composição:** roda com todas as frontend skills. Pareia especialmente com
|
|
33
|
+
`interaction-design` (focus, keyboard, feedback), `animation-review` (reduced
|
|
34
|
+
motion), `ux-review` (clareza, erro, feedback), e `visual-quality-review` (contrast).
|
|
35
|
+
|
|
36
|
+
## Mental Model
|
|
37
|
+
|
|
38
|
+
Acessibilidade não é sobre "adicionar ARIA". É sobre **garantir que o sistema funciona
|
|
39
|
+
independente de como o usuário acessa**. A regra prática: se uma funcionalidade não
|
|
40
|
+
funciona só com teclado, ou se o conteúdo não é legível por um screen reader, a
|
|
41
|
+
funcionalidade está quebrada para uma parcela dos usuários.
|
|
42
|
+
|
|
43
|
+
Eixos (do `plan.md` §10):
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
keyboard — cada ação é alcançável com Tab/Enter/Esc (sem mouse trap)
|
|
47
|
+
screen readers — conteúdo é legível com SR (alt text, labels, aria-live)
|
|
48
|
+
focus — focus ring visível, ordem lógica, não removido, gerenciado em modais
|
|
49
|
+
semantic HTML — elementos > divs genéricas (button, heading, nav, main, form)
|
|
50
|
+
contrast — texto ≥ 4.5:1 (body) / 3:1 (large) / 3:1 (UI components)
|
|
51
|
+
touch targets — ≥ 44x44px (48x48 recomendado)
|
|
52
|
+
reduced motion — animações respeitam prefers-reduced-motion
|
|
53
|
+
forms — labels, errors, hints, e focus sequence
|
|
54
|
+
errors — comunicados por texto + aria-live, não só cor
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Investigation Procedure
|
|
58
|
+
|
|
59
|
+
1. **Testar keyboard** — Tab por toda a tela. Todos os elementos interativos são
|
|
60
|
+
alcançáveis? A ordem de tab é lógica? Há `tabindex` quebrado? (positivo que
|
|
61
|
+
não é 0, negativo que esconde)
|
|
62
|
+
2. **Testar focus** — O focus ring é visível? Foi removido com `outline: none`? Ao
|
|
63
|
+
abrir modal, focus vai para dentro? Ao fechar, volta ao trigger? O focus não fica
|
|
64
|
+
preso em um elemento (focus trap quebrado)?
|
|
65
|
+
3. **Testar screen reader** — Há `alt` text em imagens? Há `aria-label` em ícones
|
|
66
|
+
semânticos? `aria-live` para conteúdo dinâmico? O conteúdo é legível sem contexto
|
|
67
|
+
visual? (testar com um leitor real ou simulando: fechar os olhos e ouvir)
|
|
68
|
+
4. **Testar semantic HTML** — `<button>` vs `<div onclick>`? `<nav>` vs `<div>`?
|
|
69
|
+
`<h1-h6>` para hierarquia? `<form>` com `<label>`?
|
|
70
|
+
5. **Testar contrast** — texto body ≥ 4.5:1 (WCAG AA). Texto large ≥ 3:1. UI
|
|
71
|
+
components (bordas, ícones) ≥ 3:1. Modo escuro?
|
|
72
|
+
6. **Testar touch targets** — botões, links, inputs ≥ 44x44px. Elementos próximos têm
|
|
73
|
+
espaço entre eles? (touch não preciona o adjacente)
|
|
74
|
+
7. **Testar reduced motion** — animações desligam com `prefers-reduced-motion: reduce`?
|
|
75
|
+
(ver `animation-review` para detalhe)
|
|
76
|
+
8. **Testar forms** — cada input tem `<label>` (não só placeholder)? Erros são
|
|
77
|
+
comunicados por texto (não só cor)? Hints/tooltips são acessíveis (não só hover)?
|
|
78
|
+
Focus sequence entre campos é lógica?
|
|
79
|
+
9. **Sintetizar** — referenciar `references/frontend.yaml` (Impeccable, WCAG docs)
|
|
80
|
+
quando apropriado.
|
|
81
|
+
|
|
82
|
+
## Questions to Ask
|
|
83
|
+
|
|
84
|
+
* Toda ação é alcançável com Tab/Enter/Esc? (sem mouse trap)
|
|
85
|
+
* O focus ring é visível? Foi removido com `outline: none`? (se sim, todo o resto
|
|
86
|
+
falha para teclado)
|
|
87
|
+
* Ao abrir modal, focus vai para dentro? Ao fechar, volta? (focus trap correto?)
|
|
88
|
+
* Imagens têm `alt` text descritivo? (não só "image", vazio, ou o filename)
|
|
89
|
+
* Ícones semânticos têm `aria-label`? (não só decorative)
|
|
90
|
+
* Conteúdo dinâmico (loading, erro, toast) tem `aria-live` / `role="alert"`?
|
|
91
|
+
* A estrutura de headings é hierárquica? (h1 → h2 → h3, não pula)
|
|
92
|
+
* `<button>` é usado para ações, não `<div onclick>`?
|
|
93
|
+
* Contraste de texto ≥ 4.5:1? (WCAG AA)
|
|
94
|
+
* Touch targets ≥ 44x44px? (especialmente mobile)
|
|
95
|
+
* Cada input tem `<label>` visível? (não só placeholder)
|
|
96
|
+
* Erros são comunicados por texto + aria-live, não só por cor?
|
|
97
|
+
|
|
98
|
+
## Attack Patterns
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
keyboard trap
|
|
102
|
+
modal aberto, Tab não sai do modal (correto). Mas não volta ao fechar (erro).
|
|
103
|
+
dropdown com itens não acessíveis por teclado (só mouse)
|
|
104
|
+
|
|
105
|
+
focus removed
|
|
106
|
+
`*:focus { outline: none !important }` — o maior crime de acessibilidade
|
|
107
|
+
→ usuário de teclado não vê onde está
|
|
108
|
+
|
|
109
|
+
no alt text
|
|
110
|
+
<img src="chart.png"> sem alt → screen reader lê "chart.png"
|
|
111
|
+
ícone de like sem aria-label → "button" sem contexto
|
|
112
|
+
|
|
113
|
+
heading hierarchy broken
|
|
114
|
+
h1 → h3 (pula h2) → conteúdo perde estrutura
|
|
115
|
+
tudo é h1 (sem hierarquia)
|
|
116
|
+
|
|
117
|
+
div as button
|
|
118
|
+
<div onclick="submit()"> vs <button type="submit">
|
|
119
|
+
→ não acessível por teclado, não tem role, não tem estado
|
|
120
|
+
|
|
121
|
+
contrast insufficient
|
|
122
|
+
gray-400 (#9CA3AF) em white (#FFFFFF) → 2.9:1, falha WCAG AA
|
|
123
|
+
|
|
124
|
+
touch target too small
|
|
125
|
+
link de 20×20px no mobile → impossível acertar com o dedo
|
|
126
|
+
|
|
127
|
+
label absent
|
|
128
|
+
placeholder="Username" como único label → perde contexto quando preenchido
|
|
129
|
+
→ <label> necessário
|
|
130
|
+
|
|
131
|
+
error by color only
|
|
132
|
+
input com borda vermelha sem texto de erro → daltônico não vê
|
|
133
|
+
→ precisa de texto + aria-live
|
|
134
|
+
|
|
135
|
+
reduced motion ignored
|
|
136
|
+
parallax e flutuação constantes sem `prefers-reduced-motion`
|
|
137
|
+
→ desconforto vestibular
|
|
138
|
+
|
|
139
|
+
skip navigation absent
|
|
140
|
+
<main> sem "Skip to content" → usuário de teclado tab pelos 20 links do nav
|
|
141
|
+
toda vez que carrega
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Evidence Requirements
|
|
145
|
+
|
|
146
|
+
* **Nomear o eixo** (keyboard/focus/screen reader/semantic/contrast/touch/reduced
|
|
147
|
+
motion/forms/errors).
|
|
148
|
+
* **Mostrar o elemento exato** e a violação (ex: `*:focus { outline: none !important }`,
|
|
149
|
+
`alt=""` em imagem informativa, `<div onclick>` para ação, contraste 2.9:1).
|
|
150
|
+
* **Referenciar o critério WCAG** quando aplicável (ex: "WCAG 1.4.3 Contrast Minimum",
|
|
151
|
+
"2.1.1 Keyboard", "2.4.7 Focus Visible").
|
|
152
|
+
* **Escalar confiança (Accessibility):**
|
|
153
|
+
* `CONFIRMED` — violação mensurável (contraste < 4.5:1, focus removido, keyboard
|
|
154
|
+
trap, no alt text, label ausente, touch target < 44px).
|
|
155
|
+
* `HIGH CONFIDENCE` — padrão claramente violado.
|
|
156
|
+
* `POSSIBLE` — violação marginal (ex: contraste 4.3:1, touch target 40px).
|
|
157
|
+
* `SPECULATIVE` — necessidade não confirmada (ex: "talvez precise de aria-live aqui").
|
|
158
|
+
|
|
159
|
+
## False Positives
|
|
160
|
+
|
|
161
|
+
* **Decorative image** — `alt=""` é *correto* para imagens decorativas. Não reportar
|
|
162
|
+
como "alt ausente". (Reportar quando é informativa sem alt.)
|
|
163
|
+
* **Focus visible por outro sinal** — se o `outline: none` tem substituto (border,
|
|
164
|
+
background, box-shadow) em todos os estados, é aceitável. Confirmar antes de
|
|
165
|
+
reportar.
|
|
166
|
+
* **Touch target tem espaço extra** — se o target visual é 30px mas o padding faz
|
|
167
|
+
o hit area ≥ 44px, é ok. Avaliar pelo hit area real, não só pelo visual.
|
|
168
|
+
* **Label oculto mas acessível** — `aria-label`/`<label class="sr-only">` é válido
|
|
169
|
+
se o screen reader lê. Não exigir label visível se o SR tem contexto suficiente.
|
|
170
|
+
* **Skip navigation em app SPA** — em apps de página única, skip to content pode ser
|
|
171
|
+
substituído por rota/foco gerenciado. Avaliar o contexto.
|
|
172
|
+
* **Reduced motion com fallback** — se o app respeita reduced motion, as animações
|
|
173
|
+
que ainda rodam precisam ser avaliadas individualmente, não como "não respeita".
|
|
174
|
+
|
|
175
|
+
## Output Format
|
|
176
|
+
|
|
177
|
+
Para cada violação, um finding via `templates/audit-report.md`. Em **Affected
|
|
178
|
+
component**, nomeie o elemento. Em **Reproduction**, descreva o comportamento (ex:
|
|
179
|
+
"Tab 3×: focus vai do header para o footer, pulando todo o conteúdo principal"). Em
|
|
180
|
+
**Root cause**, aponte o eixo e o critério WCAG. Em **Recommendation**, dê a correção
|
|
181
|
+
(adicionar focus ring, `alt` text, `<label>`, contraste, touch target, ARIA, skip
|
|
182
|
+
navigation, reduzir motion).
|
|
183
|
+
|
|
184
|
+
Apresente por eixo, ordem de severidade: keyboard traps e focus removido (= bloqueante
|
|
185
|
+
para usuário de teclado) primeiro; contrast e labels depois; semantic HTML e touch
|
|
186
|
+
targets em seguida; forms e reduced motion por último.
|