Skip to content

Commit eb73f86

Browse files
melgarafaelclaude
andcommitted
test(e2e): 15 → 28 das 32 specs no CI, provadas rodando (#63)
Os números da issue e dos 5 docs tinham envelhecido DE NOVO: o épico IA 360 trouxe 12 specs e nenhuma entrou no gate. Eram 32 specs com 15 rodando, não "10 das 20". Não adicionei nada no escuro. Montei o ambiente do CI localmente (Supabase local + baseline.sql aplicado + next build + next start) e rodei as candidatas: **41 testes verdes em 13 specs**. Só entra o que passou. DOIS DEFEITOS DE VERDADE, achados por rodar: 1. `followup-builder`, `followup-queue` e `followup-journey` liam `.e2e-creds.json` no CARREGAMENTO DO MÓDULO e só depois o `beforeAll` rodava o seed, que escreve no arquivo — o objeto em memória nunca via o bloco novo. O diagnóstico anterior ("o seed não grava followup_agent_fixtures") descrevia o sintoma; o seed sempre gravou certo. Varri as 32 specs atrás das irmãs: 3 tinham o defeito, 3 já reliam (`queue-assign` é o controle positivo — ela passa no CI justamente por reler). Corrigidas as 3. 2. `escalacao-ciclo` e `central-de-avisos-capacidades` não semeiam sozinhas — o cabeçalho delas manda rodar o seed à mão, e o workflow não rodava. Agora roda. `--env-file=.env.local` só no de capacidades, e a assimetria tem motivo: ele importa `lib/env.ts`, que lê `process.env`; o de escalação parseia o `.env.local` sozinho. Sem a flag ele morre na validação Zod. `capacidades-do-agente` fica de fora porque REPROVA, e o vermelho está certo: ligar o pacote "Atender" enche o teto de 20 capacidades e a UI DESABILITA o checkbox da capacidade crítica que o próprio desenho manda o humano marcar à mão ("o pacote não liga por você"). O seed liga 3, o pacote traz >=17 automáticas — determinístico, não dado sujo. É o catálogo ter crescido depois que a spec foi escrita, e ninguém soube porque ela nunca rodou no gate: a tese desta issue, demonstrada. Fica declarado no summary do job, não escondido. NÃO promovi o `e2e` a check obrigatório, embora a issue peça: eu ACABEI de mudar o conjunto de specs, então as execuções verdes anteriores eram de outro conjunto — usá-las como prova de estabilidade deste seria medir uma coisa e concluir sobre outra, que é o erro que o dono do repo já pegou em si mesmo neste mesmo item. Ressalva medida: o ensaio local rodou num banco com dados acumulados de sessões anteriores (schema idêntico — baseline re-aplicado —, dados não). Um banco fresco do CI pode expor dependência de estado que aqui passou despercebida. Como o job não é obrigatório, o pior caso é vermelho visível, não merge bloqueado. gov:verify verde: 256 arquivos, 2372 testes. Refs #63 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HstH7nNmrTvCtZsasppeHj
1 parent f261cef commit eb73f86

8 files changed

Lines changed: 77 additions & 30 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ jobs:
103103
EOF
104104
pnpm exec tsx scripts/seed-e2e-credentials.ts
105105
106+
# A maioria das specs roda o próprio seed num `beforeAll`. Estas DUAS não —
107+
# o cabeçalho delas manda rodar à mão — e é só por isso que ficavam de fora.
108+
#
109+
# `--env-file` só na segunda, e a diferença não é capricho: o seed de
110+
# capacidades importa `lib/env.ts` (via lib/ai/embed.ts), que lê
111+
# `process.env`; o de escalação parseia o `.env.local` por conta própria.
112+
# Sem a flag o de capacidades morre na validação Zod das 3 vars do Supabase.
113+
- name: Semear as fixtures que as specs não semeiam sozinhas
114+
run: |
115+
pnpm exec tsx scripts/seed-e2e-escalacao.ts
116+
pnpm exec tsx --env-file=.env.local scripts/seed-e2e-capacidades-ausentes.ts
117+
106118
# O que ficou DE FORA (e por quê) é declarado no passo de summary —
107119
# cobertura parcial silenciosa se lê como cobertura total.
108120
#
@@ -124,7 +136,13 @@ jobs:
124136
rbac-roles.spec.ts inbox-scope.spec.ts reset-password-mfa.spec.ts \
125137
degradacao-silenciosa.spec.ts vps-webhook-outbound-ssrf.spec.ts \
126138
kanban-owner-filter.spec.ts queue-assign.spec.ts \
127-
risk-radar.spec.ts invite-lifecycle.spec.ts system-update.spec.ts
139+
risk-radar.spec.ts invite-lifecycle.spec.ts system-update.spec.ts \
140+
agente-novo-e-uso.spec.ts agente-organiza-operacao.spec.ts \
141+
central-de-avisos-capacidades.spec.ts escalacao-ciclo.spec.ts \
142+
navegacao.spec.ts olhar-telas-do-epico.spec.ts pipelines-gestao.spec.ts \
143+
qa-agente-usa-as-maos.spec.ts qa-selo-no-funil-usado.spec.ts \
144+
qa-telas-descobertas-w4.spec.ts retorno-anti-morte.spec.ts \
145+
followup-builder.spec.ts followup-queue.spec.ts
128146
env:
129147
INTERNAL_SECRET: ci-placeholder-nao-e-segredo
130148
CPF_ENCRYPTION_KEY: ci-placeholder-nao-e-segredo
@@ -143,17 +161,25 @@ jobs:
143161
{
144162
echo "## E2E — cobertura deste job"
145163
echo ""
146-
echo "**Rodou (15 de 20 specs):** smoke, auth, error-pages, password-recovery,"
164+
echo "**Rodou (28 de 32 specs):** smoke, auth, error-pages, password-recovery,"
147165
echo "signup-journey, rbac-roles, inbox-scope, reset-password-mfa,"
148166
echo "degradacao-silenciosa, vps-webhook-outbound-ssrf, kanban-owner-filter,"
149-
echo "queue-assign, risk-radar, invite-lifecycle, system-update."
167+
echo "queue-assign, risk-radar, invite-lifecycle, system-update,"
168+
echo "agente-novo-e-uso, agente-organiza-operacao, central-de-avisos-capacidades,"
169+
echo "escalacao-ciclo, navegacao, olhar-telas-do-epico, pipelines-gestao,"
170+
echo "qa-agente-usa-as-maos, qa-selo-no-funil-usado, qa-telas-descobertas-w4,"
171+
echo "retorno-anti-morte, followup-builder, followup-queue."
150172
echo ""
151-
echo "**Não rodou (5):**"
173+
echo "**Não rodou (4):**"
152174
echo "- precisa de WAHA: followup-journey, webhooks"
153175
echo "- precisa de WAHA + Redis + Resend + Nuvemshop: vps-fresh-onboarding (P0)"
154-
echo "- seed próprio falha neste ambiente (medido): followup-builder"
155-
echo " (seed-e2e-followup-agent.ts não grava followup_agent_fixtures) e"
156-
echo " followup-queue (followup_promise ausente em .e2e-creds.json)."
176+
echo "- **capacidades-do-agente: fora porque REPROVA, e o vermelho está certo.**"
177+
echo " Ligar o pacote 'Atender' enche o teto de 20 capacidades, e aí a UI"
178+
echo " DESABILITA o checkbox da capacidade crítica que o próprio desenho"
179+
echo " manda o humano marcar à mão ('o pacote não liga por você'). O seed"
180+
echo " liga 3; o pacote traz >=17 automáticas. Não é dado sujo: é o catálogo"
181+
echo " ter crescido depois que a spec foi escrita — e ninguém soube porque"
182+
echo " ela nunca rodou no gate, que é a tese desta issue."
157183
echo ""
158184
echo "Expandir é trabalho de seguimento — issue #63."
159185
} >> "$GITHUB_STEP_SUMMARY"

AGENTS.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,13 @@ mudança toca schema, RLS ou UI, `gov:verify` verde **não** é prova — rode `
6666
efêmero pg17). `.github/workflows/perf.yml`: `build-and-size` = `pnpm build`.
6767
**Os três são checks obrigatórios** na branch protection da `main`.
6868

69-
`.github/workflows/e2e.yml` roda **10 das 20 specs** Playwright (`smoke`, `auth`,
70-
`error-pages`) contra um Supabase local de verdade com o `baseline.sql` aplicado — o mesmo
71-
banco que o self-hoster tem. **Não é obrigatório ainda** (falta dado de estabilidade) e as
72-
outras 16 continuam sem gate: se você mexeu em UI ou fluxo de usuário fora desse
73-
subconjunto, a prova é sua.
69+
`.github/workflows/e2e.yml` roda **28 das 32 specs** Playwright contra um Supabase local de
70+
verdade com o `baseline.sql` aplicado — o mesmo banco que o self-hoster tem. **Não é
71+
obrigatório ainda** (o conjunto de specs acabou de mudar, então execuções verdes anteriores
72+
eram de outro conjunto e não provam a estabilidade deste). As 4 de fora: `followup-journey` e
73+
`webhooks` (precisam de WAHA), `vps-fresh-onboarding` (WAHA + Redis + Resend + Nuvemshop; é a
74+
P0 da doutrina de QA) e `capacidades-do-agente`, que está fora porque REPROVA de verdade — ver
75+
o summary do job. Se você mexeu em UI fora desse subconjunto, a prova é sua.
7476

7577
## Padrões de código (observados no repo, não inventados)
7678

@@ -115,18 +117,19 @@ subconjunto, a prova é sua.
115117
## Testes existentes (CONFIRMADO)
116118

117119
- **221** arquivos `*.test.ts(x)` unitários (rodam em `test:unit` e no CI)
118-
- **56** arquivos de invariante de banco em `tests/invariants/` — RLS/isolamento cross-tenant,
120+
- **67** arquivos de invariante de banco em `tests/invariants/` — RLS/isolamento cross-tenant,
119121
RBAC, governança (G1–G6). Excluídos do `test:unit` de propósito; rodam via `pnpm test:db`
120122
**e no job `invariants` do CI**.
121-
- **19** specs Playwright em `tests/e2e/`. **3 rodam no CI** (`smoke`, `auth`,
122-
`error-pages`, via `e2e.yml`, não-obrigatório). As outras 16 — incluindo
123-
`vps-fresh-onboarding` e `vps-webhook-outbound-ssrf` — ainda não: dependem de fixture
124-
semeada ou de serviço externo. Ver issue #63.
123+
- **32** specs Playwright em `tests/e2e/`. **28 rodam no CI** (via `e2e.yml`,
124+
não-obrigatório). As 4 de fora dependem de serviço externo (WAHA/Redis/Resend/Nuvemshop) —
125+
incluindo `vps-fresh-onboarding` — ou reprovam legitimamente (`capacidades-do-agente`).
126+
Ver issue #63.
125127

126128
## Limitações conhecidas (estado em 2026-07-29, contra `origin/main` @ 789dfa6)
127129

128-
- **10 das 20 specs E2E seguem fora do CI.** Se você mexeu em UI ou fluxo de usuário fora
129-
de `smoke`/`auth`/`error-pages`, a prova é sua — nenhum gate automático cobre.
130+
- **4 das 32 specs E2E seguem fora do CI**, e o `e2e` ainda não é check obrigatório: um PR
131+
que o quebre entra na `main` assim mesmo. Se você mexeu em UI coberta só por essas 4, a
132+
prova é sua.
130133
- Rate limit HTTP existe em **2** pontos do código (webhook de captação e dispatcher de IA);
131134
login, signup, aceite de convite, crons e MCP estão sem. Não há lockout por conta no login.
132135
- Fallback do rate limit é **em memória** — sem Upstash configurado o limite é por processo.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ Checks **obrigatórios** na branch protection da `main` (verificado na configura
188188

189189
Check **não-obrigatório** (roda, mas não segura merge):
190190

191-
- **`e2e`** (`e2e.yml`) — sobe Supabase local, aplica o `baseline.sql` e roda **10 das 20 specs** Playwright (`smoke`, `auth`, `error-pages`, `password-recovery`, `signup-journey`, `rbac-roles`, `inbox-scope`, `reset-password-mfa`, `degradacao-silenciosa`, `vps-webhook-outbound-ssrf`). As outras 10 dependem de serviço externo (WAHA, Redis, Resend, Nuvemshop) e seguem sem gate (issue #63) — inclusive a `vps-fresh-onboarding`, que é P0.
191+
- **`e2e`** (`e2e.yml`) — sobe Supabase local, aplica o `baseline.sql` e roda **28 das 32 specs** Playwright. As 4 de fora: `followup-journey` e `webhooks` (precisam de WAHA), `vps-fresh-onboarding` (WAHA + Redis + Resend + Nuvemshop — é a P0 da doutrina de QA Visual) e `capacidades-do-agente`, que está fora porque **reprova de verdade**: ligar o pacote "Atender" enche o teto de 20 capacidades e a UI desabilita o checkbox da capacidade crítica que o próprio desenho manda marcar à mão. O `e2e` **ainda não é obrigatório** — o conjunto de specs mudou em 2026-08-05, então as execuções verdes anteriores eram de outro conjunto e não servem de prova de estabilidade deste (issue #63).
192192

193193
Ao mexer em schema, RLS, RBAC, atribuição, escopo, roteamento, follow-up, webhooks ou automações: rode `pnpm test:db` **localmente** antes de abrir PR. É o único caminho que exercita o `baseline.sql` que o self-hoster realmente aplica.
194194

docs/current-state.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,19 @@ Estes são achados de código/config verificados nesta auditoria, não relatos.
120120

121121
### 4.1 Os E2E quase não rodam no CI 🟠 — parcialmente resolvido em 2026-07-30
122122

123-
> **Atualização (2026-08-03):** `e2e.yml` roda **10 das 20 specs** (`smoke`, `auth`,
124-
> `error-pages`) contra Supabase local com o `baseline.sql` aplicado. Não-obrigatório ainda.
125-
> A primeira execução real já pagou o job: achou a página `/500`, que `public-paths.ts`
126-
> declarava pública e **nunca havia sido criada**. As 16 restantes seguem sem gate — o texto
127-
> abaixo continua valendo para elas.
123+
> **Atualização (2026-08-05, issue #63):** `e2e.yml` roda **28 das 32 specs** contra Supabase
124+
> local com o `baseline.sql` aplicado. Não-obrigatório ainda. A primeira execução real já
125+
> pagou o job: achou a página `/500`, que `public-paths.ts` declarava pública e **nunca havia
126+
> sido criada**. A rodada de 2026-08-05 pagou de novo: as 12 specs do épico IA 360 nunca
127+
> tinham entrado no gate, e ao rodá-las apareceu um defeito de produto real
128+
> (`capacidades-do-agente` — o teto de 20 capacidades desabilita a crítica que o desenho manda
129+
> marcar à mão). As 4 restantes seguem sem gate — o texto abaixo continua valendo para elas.
128130
129131
O gate de isolamento RLS **roda**`ci.yml` tem o job `invariants` chamando `pnpm test:db`,
130132
que sobe `pgvector/pgvector:pg17`, aplica `baseline.sql` em modo install e update, e roda os
131133
56 arquivos de `tests/invariants/`. Esse buraco está fechado.
132134

133-
O que continua fora: **10 das 20 specs Playwright**. A `vps-webhook-outbound-ssrf.spec.ts`,
135+
O que continua fora: **4 das 32 specs Playwright**. A `vps-webhook-outbound-ssrf.spec.ts`,
134136
única prova automatizada do guard de SSRF, **passou a rodar** no `e2e.yml`. Mas a
135137
`vps-fresh-onboarding.spec.ts` — a jornada que a doutrina de QA Visual classifica como o
136138
caminho mais crítico do produto — continua fora, porque exige WAHA + Redis + Resend +

docs/harness-audit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ verificados por leitura de arquivo, config e workflow.
2828
| H2 — Reproduzível || Quickstart no README, `docs/SETUP.md`, `.nvmrc` (22), `packageManager` fixo, `pnpm-lock.yaml`, `docker-compose.yml`, `install.sh` do kit self-host, `baseline.sql` |
2929
| H3 — Verificável || `lint` + `typecheck` + `test:unit` + `build`; CI roda os 3 primeiros em PR |
3030
| H4 — Preparado para agentes || `CLAUDE.md` doutrinal forte; `AGENTS.md` **criado nesta auditoria**; documentação técnica extensa; **e o CI roda o gate de isolamento RLS** (job `invariants``pnpm test:db`) |
31-
| H5 — Automação avançada | ⚠️ **parcial** | CI confiável e ambiente isolado ✅ (Postgres efêmero pg17, worktrees, gov-loop com maker≠checker e hash-check). Faltam: **10 das 20 specs E2E fora do CI** (10 rodam via `e2e.yml`, ainda não-obrigatório), `format:check` fora do CI, e o comando único local (`gov:verify`) não cobre `test:db`/`test:e2e` |
31+
| H5 — Automação avançada | ⚠️ **parcial** | CI confiável e ambiente isolado ✅ (Postgres efêmero pg17, worktrees, gov-loop com maker≠checker e hash-check). Faltam: **4 das 32 specs E2E fora do CI** (28 rodam via `e2e.yml`, ainda não-obrigatório — e enquanto for opcional um PR que o quebre entra na `main`), `format:check` fora do CI, e o comando único local (`gov:verify`) não cobre `test:db`/`test:e2e` |
3232

3333
**Por que H4 e não H5:** a instrução da auditoria é explícita — não atribuir nível só
3434
porque os arquivos existem, avaliar se o processo está implementado. Aqui está: o gate de

tests/e2e/followup-builder.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function loadCreds(): Creds {
4343
return JSON.parse(fs.readFileSync(CREDS_PATH, "utf8")) as Creds;
4444
}
4545

46-
const creds = loadCreds();
46+
let creds = loadCreds();
4747

4848
async function login(page: Page, email: string): Promise<void> {
4949
await page.goto("/login");
@@ -598,6 +598,12 @@ test.describe("followup flow builder — editor de condição de aresta / ai_cla
598598
test.describe("followup flow selector no editor do agente (Task 7.2)", () => {
599599
test.beforeAll(() => {
600600
execFileSync("npx", ["tsx", "scripts/seed-e2e-followup-agent.ts"], { stdio: "inherit" });
601+
// O seed ESCREVE em .e2e-creds.json, e `creds` foi lido no carregamento do
602+
// módulo — sem reler, o objeto em memória nunca vê o bloco que o seed
603+
// acabou de gravar. Foi por isto que esta spec ficou fora do CI: a mensagem
604+
// "o seed não grava X" descrevia o sintoma, e o seed gravava certo desde
605+
// sempre. Mesmo idioma de queue-assign.spec.ts, que passa por isso.
606+
creds = JSON.parse(fs.readFileSync(CREDS_PATH, "utf8")) as Creds;
601607
});
602608

603609
test("admin vincula um fluxo publicado ao agente, salva, e a persistência é provada via API", async ({

tests/e2e/followup-journey.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function loadInternalSecret(): string {
7373
return secret;
7474
}
7575

76-
const creds = loadCreds();
76+
let creds = loadCreds();
7777
const secret = loadInternalSecret();
7878

7979
/** Roda 1 subcomando do helper de SQL cru e devolve o JSON impresso na última linha. */
@@ -188,6 +188,10 @@ test.describe("followup — jornada completa (Task 8.3)", () => {
188188

189189
test.beforeAll(() => {
190190
execFileSync("npx", ["tsx", "scripts/seed-e2e-followup-agent.ts"], { stdio: "inherit" });
191+
// O seed ESCREVE em .e2e-creds.json, e `creds` foi lido no carregamento do
192+
// módulo — sem reler, o objeto em memória nunca vê o bloco que o seed
193+
// acabou de gravar. Mesmo idioma de queue-assign.spec.ts, que passa por isso.
194+
creds = JSON.parse(fs.readFileSync(CREDS_PATH, "utf8")) as Creds;
191195
});
192196

193197
test("silêncio → enroll → trigger→wait→action→classify → resposta → outcome → fila", async ({ page }) => {

tests/e2e/followup-queue.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function loadCreds(): Creds {
5555
return JSON.parse(fs.readFileSync(CREDS_PATH, "utf8")) as Creds;
5656
}
5757

58-
const creds = loadCreds();
58+
let creds = loadCreds();
5959

6060
async function login(page: Page, email: string): Promise<void> {
6161
await page.goto("/login");
@@ -142,6 +142,12 @@ async function cleanupLiveEnrollment(page: Page, live: LiveEnrollment): Promise<
142142
test.describe("followup queue — fila unificada (Task 7.1)", () => {
143143
test.beforeAll(() => {
144144
execFileSync("npx", ["tsx", "scripts/seed-e2e-followup-promise.ts"], { stdio: "inherit" });
145+
// O seed ESCREVE em .e2e-creds.json, e `creds` foi lido no carregamento do
146+
// módulo — sem reler, o objeto em memória nunca vê o bloco que o seed
147+
// acabou de gravar. Foi por isto que esta spec ficou fora do CI: a mensagem
148+
// "o seed não grava X" descrevia o sintoma, e o seed gravava certo desde
149+
// sempre. Mesmo idioma de queue-assign.spec.ts, que passa por isso.
150+
creds = JSON.parse(fs.readFileSync(CREDS_PATH, "utf8")) as Creds;
145151
});
146152

147153
test("manager vê enrollment na fila, filtra por status/fluxo, cancela, e a promessa seedada aparece", async ({

0 commit comments

Comments
 (0)