From 91ae6b5408f9963973efb605d9f729fd9fadc519 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 10 Aug 2026 21:28:52 -0300 Subject: [PATCH] =?UTF-8?q?test(OPS35+):=20restaurar=20pin=20de=20browser?= =?UTF-8?q?=20da=20visibilidade=20CSS=20das=20colunas=20de=20rede=20em=20T?= =?UTF-8?q?errit=C3=B3rios=20(Closes=20#616)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CHANGELOG-AGENTS.md | 2 + ...n-visibilidade-colunas-territorios-impl.md | 61 +++++++++++++++++++ scripts/lib/e2e-affected-manifest.mjs | 5 +- .../campaignTerritoriesColumns.e2e.spec.ts | 53 ++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 docs/plans/restaurar-pin-visibilidade-colunas-territorios-impl.md create mode 100644 tests/e2e/campaignTerritoriesColumns.e2e.spec.ts diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index b33b3d8e..5e487aaf 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-10):** **OPS35+** (Issue #616) — restaurado o pin de browser que o OPS35 documentou como perdido: a **visibilidade CSS real** das colunas de rede (Assessor / Liderança / Dobradinha) e dos sortables de `/campanha/territorios` a 2200px. Spec novo mínimo `tests/e2e/campaignTerritoriesColumns.e2e.spec.ts` (projeto `campaign`, corpo exato do teste 3 do spec deletado — `getByRole('columnheader')` visível por rung container-query `@min-[Nrem]/territory-list`, Cobertura `count 0`); divisão de trabalho com o HTTP preservada (presença + classes de rung continuam no `campaignTerritoriesHttp`). Manifesto `e2e-affected`: entrada territórios agora mapeia os 2 specs **e** o arquivo dono das rungs `TerritoryListColumns.tsx` (uma mudança de rung acorda o pin; o seletor faz união, specs de municípios continuam acordando). Prova de não-vazio por mutação (rung `advisor` → `@min-[200rem]` fez o spec falhar em "Assessor"; revertido). Sem migration/access/Consent/UI/CSS. Intenção [`restaurar-pin-visibilidade-colunas-territorios.md`](plans/restaurar-pin-visibilidade-colunas-territorios.md); impl [`restaurar-pin-visibilidade-colunas-territorios-impl.md`](plans/restaurar-pin-visibilidade-colunas-territorios-impl.md). + **Recently resolved (2026-08-10):** **B195** (Issue #591) — wizard "Registrar pedido" completo (A5, adiado no B85): o passo final saiu do stub e virou formulário funcional no shell do wizard — município no header do app (chrome `municipalityLabel`), **sem título grande de passo** (`stepTitle={null}` + `contentFocus="none"`), Tipo → Atividade (opcional) → **campo único** "O que você precisa?"; salvar ("Abrir demanda") → toast + retorno à origem (`returnPath`). **Mudança de modelo da demanda:** o cadastro (wizard e `/demandas/nova`, sem divergência) tem UM campo de texto (`description` required, min 2, max 4000 — constante `CAMPAIGN_DEMAND_BODY_MAX_LENGTH`); o **título virou derivado por IA** (`src/utilities/ai/campaignDemandTitle.ts` — `generateText` DeepSeek `deepseek-v4-flash` como o Sollinha, timeout 4s, sem key → `null`, prompt com rótulo do tipo) com **fallback = texto truncado** (`src/lib/demandTitle.ts`, puro: colapsa whitespace, ≤160) no create e **keep-previous na edição** (novo `updateCampaignDemandRecord` + editor inline no detalhe — recalc só quando a descrição muda; slug preservado pela invariante canônica); `campaignDemandCreateSchema` perdeu `title` (só os drafts de demanda da atividade mantêm título digitado — vertical fora de escopo, schema desacoplado). **Unicidade de slug defensiva só no create das forms** (`ensureUniqueDemandSlug`, opt-in `context.campaignDemandUniqueSlug` fail-closed em transação ativa — precedente `skipContactPhoneInvariant`): probe único da janela `-2…-20` + advisory lock `campaign-demand-slug:` contra race; activity drafts/admin mantêm a constraint estrita (contrato de rollback C90 pinado em teste). Page: branch `register-demand` no router + **staff gate ANTES do load do escopo** (`gate: 'staff'` → líder vai a `/campanha/contatos`, B43 — o 403 do scope load pré-existente virava erro não determinístico; stub `WizardMunicipalitySelectedStub` removido, placeholder "em breve" apagado). **Bug latente corrigido no hook compartilhado** `useCampaignFormSuccessToast` (dependência `[state.message, state.status]` → `[state]`: segundo save com a mesma mensagem — caso do editor — não disparava toast/refresh). Copy compartilhada em `schemas/campaignDemand.ts`; parser único `parseCampaignDemandCreateFormData` (`utilities/demand/`). **Sem migration** (título continua required, preenchido pelo record). Testes: unit do fallback/limites exatos; int (create deriva, colisão `-2`/`-3`, min 2, update keep-previous/skip, leader edit bloqueado, pin "sem `DEEPSEEK_API_KEY`" no `beforeAll`); e2e novo `campaignRegisterDemand` (fluxo completo com título na lista, back-nav à busca, lockdown de líder). Intenção [`registrar-pedido-wizard.md`](plans/registrar-pedido-wizard.md); impl [`registrar-pedido-wizard-impl.md`](plans/registrar-pedido-wizard-impl.md). **Recently resolved (2026-08-10):** **OPS33** (Issue #595) — `worktree next` **claima deterministicamente** antes de criar o worktree: mesma fila/ordem e lock otimista do `pnpm agent:claim`, com o claim extraído para `scripts/lib/agent-github.mjs` (`claimIssue` — lock + flip + comentário; `claimBriefLines` — brief compartilhado; `claimQueueEntry` — shape para o reopen; `claimTargetVerdict` — decisão pura testada) e o `agent-claim.mjs` virou CLI fino com a **mesma saída**; claim falhou (fila vazia/race/`--issue` inválido) → motivo e saída **sem worktree órfão** (branch validada antes do claim; hint de falha pós-claim). `--issue ` claima a Issue direcionada (`ready`) ou **reabre** uma já claimada (`in-progress` — sem re-claim, idempotente; recupera também falha pós-claim); a saída imprime o brief e avisa "já claimada — NÃO rodar `pnpm agent:claim`". O **launch** do terminal agora envia `/work-issue --issue ` (prompt citado na diretiva) e a função `worktree()` de `.agents/shell/worktree.sh` passou a **tokenizar a diretiva via xargs** (quote-aware, nunca eval) — o valor do `--prompt` carrega espaço. `--stay` suprime cd+launch mas o claim acontece; o comando `/worktree` roda o mesmo script (claim incluso, sem launch). Guard `-B` contra reset de commits de sessão no reopen. Pool intacto (`claimIssueForPool` coordenado). Docs: AGENTS.md ("Per-worktree environments"), header/uso do script, `.opencode/commands/worktree.md`, skill `worktree-next-issue` (sai "read-only"), AGENT-OPS. Sem migration/dados/access/UI. Intenção [`worktree-next-claima-issue-deterministicamente.md`](plans/worktree-next-claima-issue-deterministicamente.md); impl [`worktree-next-claima-issue-deterministicamente-impl.md`](plans/worktree-next-claima-issue-deterministicamente-impl.md). diff --git a/docs/plans/restaurar-pin-visibilidade-colunas-territorios-impl.md b/docs/plans/restaurar-pin-visibilidade-colunas-territorios-impl.md new file mode 100644 index 00000000..bace0dce --- /dev/null +++ b/docs/plans/restaurar-pin-visibilidade-colunas-territorios-impl.md @@ -0,0 +1,61 @@ +# Impl: Restaurar pin de visibilidade CSS das colunas de rede em Territórios + +Status: aprovado +Atualizado em: 2026-08-10 +Issue: #616 +Intenção: docs/plans/restaurar-pin-visibilidade-colunas-territorios.md +Appetite restante: herdado (~0,25 dia eng) + +## Leitura da intenção + +- **Outcome:** um spec de browser mínimo pina que, a 2200px, os columnheaders de rede (Assessor / Liderança / Dobradinha) e o restante do contrato (sortables) de `/campanha/territorios` estão **visíveis** — visibilidade CSS real via container queries (`@min-[Nrem]/territory-list:table-cell`) — e Cobertura permanece oculta; verde no job e2e existente. +- **O que NÃO negociar:** nada de re-migrar a família; não duplicar asserções do `campaignTerritoriesHttp` (presença no HTML + classes de rung continuam lá); zero mudança de CSS/UI; spec mínimo (só este comportamento). +- **O que reavaliar:** a letra do manifesto `e2e-affected` da intenção ("rota `territorios` → specs `['campaignTerritoriesHttp', 'campaignTerritoriesColumns']`") — o prefixo de rota não acorda o spec quando o **arquivo das rungs** muda (ver D2; as rungs vivem em `src/components/campaign/municipality/TerritoryListColumns.tsx`, hoje mapeado para specs de municípios). + +## Abordagem recomendada + +```mermaid +flowchart LR + Spec[campaignTerritoriesColumns.e2e.spec.ts
1 teste — corpo do antigo teste 3] --> Login[campaign.login
viewport 2200x1000 antes do login] + Login --> Goto[goto /campanha/territorios] + Goto --> Sort[sortables visíveis
Ordenar por 2022/Captura/2026/Classe/Assessoria] + Sort --> Net[rede visível
Assessor/Liderança/Dobradinha exact] + Net --> Cobertura[Cobertura count 0] + Manifest[e2e-affected: territórios
+campaignTerritoriesColumns
+prefixo TerritoryListColumns] --> Spec +``` + +**Opções consideradas:** D1 (corpo do spec) A | B · D2 (manifesto) A | B +**Recomendação:** corpo **exato** do antigo teste 3 (D1-A) + manifesto com o spec **e** o prefixo do arquivo das rungs (D2-B) — porque o corpo restaurado já foi aceito por produto (B175) e o prefixo extra é o que faz o pin acordar exatamente quando o CSS que ele protege muda. +**Rejeitadas:** D1-B (versão enxuta só com as 3 colunas de rede) porque perde o pin de "Cobertura oculta" e dos sortables sem ganho de custo; D2-A (só a letra da intenção) porque deixa o spec mudo exatamente para a regressão que o motivou. + +### Componentes / mudanças + +- **`tests/e2e/campaignTerritoriesColumns.e2e.spec.ts`** (novo, projeto `campaign` — casa `/campaign.*\.e2e\.spec\.ts/`): um teste que restaura o corpo do teste 3 do spec deletado (`campaignTerritories.e2e.spec.ts` em `1f31d1b6^`), com o mesmo comentário B175 e a mesma ordem: `page.setViewportSize({ width: 2200, height: 1000 })` → `campaign.login(page, email, password)` → `page.goto('/campanha/territorios')` → loop de sortables (`getByRole('columnheader', { name: new RegExp(\`^Ordenar por ${header}\`) })`visível para`['2022', 'Captura', '2026', 'Classe', 'Assessoria']`) → loop da rede (`columnheader`exact`['Assessor', 'Liderança', 'Dobradinha']`visível) →`columnheader` `'Cobertura'`com`toHaveCount(0)`. Cabeçalho do arquivo documenta a divisão de trabalho com o spec HTTP (presença + rungs no HTML são do HTTP; visibilidade em viewport é browser) e por que ele existe (OPS35+). +- **`scripts/lib/e2e-affected-manifest.mjs`** (entrada territórios): `specs: ['campaignTerritoriesHttp']` → `['campaignTerritoriesHttp', 'campaignTerritoriesColumns']` e `prefixes` ganha `src/components/campaign/municipality/TerritoryListColumns` (arquivo das rungs; startsWith prefixo-de-arquivo é válido no contrato do manifesto — mesmos checks unit de `e2eAffectedManifest.unit.spec.ts` seguem passando). +- **Migration:** sem migration. +- **Access / Consent:** sem mudança. +- **UI:** Impeccable A — N/A (infra de testes; sem UI nova). + +## Fases verificáveis + +1. **Spec + manifesto** — criar o spec novo e a entrada do manifesto. Prova de não-vazio (muta): com `@min-[60rem]/territory-list` de `advisor` temporariamente alterado para `@min-[200rem]`, o spec deve falhar em `Assessor`; reverter. Depois rodar os pins unit afetados: `pnpm test:unit tests/unit/e2eAffectedManifest.unit.spec.ts tests/unit/ciSkipInvariants.unit.spec.ts tests/unit/testAffected.unit.spec.ts`. +2. **E2e local** — `pnpm test:e2e tests/e2e/campaignTerritoriesColumns.e2e.spec.ts` (dev, worktree: `teqo_wt35_test`, porta 3135) verde; conferir que o job CI continua verde no PR (o spec novo entra no `campaign` project). +3. **Gates** — `pnpm gate:fast` (lint + tsc + unit), `pnpm format:check`, `pnpm exec knip`, `pnpm check:cycles`; entrega via `pnpm push`. + +## Rabbit holes / Não escopo (engenharia) + +- **Re-migrar famílias ou tocar o `campaignTerritoriesHttp`** — fora de escopo; a equivalência HTTP continua como está. +- **Pinar rungs das outras listas** (municípios, lideranças…) — rabbit hole de produto já catalogado na intenção; o pin de municípios já existe (`campaignMunicipalityResponsiveColumns`). +- **Cobrir mudanças de `tailwind.config`/`globals.css` no manifesto** — gap do OPS5 conhecido, não desta issue; uma regressão de container-query Tailwind que quebre a rung também quebra o pin via `TerritoryListColumns.tsx` quando o arquivo mudar. + +## Riscos e mitigação + +- **Flake de viewport/container:** o corpo é exatamente o que passou em CI por semanas antes do OPS35 (2200px com sidebar expandida deixa o container acima de 72rem). Sem estado novo, sem interação — risco baixo. +- **Custo do job e2e:** +1 teste browser (~5–10 s dev; <1 s prod) — o trade do OPS35 foi explícito: viewports continuam em browser. +- **Prefixo novo no manifesto muda o alcance do `selectE2eSpecs`:** mudanças em `TerritoryListColumns.tsx` passam a rodar os 2 specs de territórios **além dos** specs de municípios (o seletor faz união de entradas) — é o comportamento desejado (a regressão de CSS de territórios acorda o pin de territórios). + +## Aceite de engenharia + +- [x] Aceite de produto da intenção ainda coberto (corpo do teste 3 restaurado; nada de CSS/HTTP tocado) +- [x] Invariantes AGENTS/engineering-standards (spec novo mapeado no manifesto; unit pins verdes; sem twin de fixtures — `campaign.login` reusado) +- [x] Testes de domínio previstos: spec e2e novo + prova de não-vazio (mutação revertida) + pins unit do manifesto diff --git a/scripts/lib/e2e-affected-manifest.mjs b/scripts/lib/e2e-affected-manifest.mjs index 20653137..19947ddf 100644 --- a/scripts/lib/e2e-affected-manifest.mjs +++ b/scripts/lib/e2e-affected-manifest.mjs @@ -67,8 +67,11 @@ export const E2E_AFFECTED_MANIFEST = [ `${CAMPAIGN_APP}/territorios`, 'src/components/campaign/tour', 'src/utilities/territory', + // OPS35+ — the network-column rungs live in this file; a rung regression + // must wake the CSS-visibility browser pin, not only the municipios specs. + 'src/components/campaign/municipality/TerritoryListColumns', ], - specs: ['campaignTerritoriesHttp'], + specs: ['campaignTerritoriesHttp', 'campaignTerritoriesColumns'], }, { prefixes: [`${CAMPAIGN_APP}/conceitos`], diff --git a/tests/e2e/campaignTerritoriesColumns.e2e.spec.ts b/tests/e2e/campaignTerritoriesColumns.e2e.spec.ts new file mode 100644 index 00000000..8c58dae2 --- /dev/null +++ b/tests/e2e/campaignTerritoriesColumns.e2e.spec.ts @@ -0,0 +1,53 @@ +import { expect, test } from './fixtures/campaignE2EFixtures.js' + +/** + * OPS35+ — restores the browser pin the HTTP migration dropped. OPS35 migrated + * `campaignTerritories` to the browserless HTTP mode (`campaignTerritoriesHttp`) + * because its assertions were server-rendered content; its one browser-only + * concern — CSS rung visibility at a viewport — was documented as lost at the + * time. That loss is a real hole: the network columns hide behind container + * queries (`@min-[Nrem]/territory-list:table-cell`, TerritoryListColumns.tsx), + * and the HTTP spec only pins presence + rung classes in the HTML, never the + * visual effect. This spec is that pin back: at a wide viewport the read-only + * network columns and every rung-gated sortable are actually visible, and + * Cobertura is not rendered at this width. It carries the exact body of the + * deleted spec's test 3 (accepted product behavior since B175) — nothing else, + * no CSS, no HTTP duplication. + */ + +test.describe('Territórios de Identidade — colunas de rede (browser)', () => { + test('a wide panel surfaces the read-only network columns; Cobertura stays hidden by default', async ({ + campaign, + page, + }) => { + const { fixtures } = campaign + const coordinator = await fixtures.createCampaignUser('coordinator', { + name: fixtures.value('Coordenadora Rede Larga'), + }) + const password = coordinator.password + const email = coordinator.email! + + await page.setViewportSize({ width: 2200, height: 1000 }) + await campaign.login(page, email, password) + await page.goto('/campanha/territorios') + + // B175 — the responsive rungs are all visible at a wide panel (P0 + Captura + // + network + Classe + Assessoria). Cobertura is P3 AND hidden in the picker. + // Sortable headers are announced "Ordenar por