teqo-backup/docs/CHANGELOG-AGENTS.md

14 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog de contexto para agentes (fatias do AGENTS.md)
Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agentes paralelos): "Known Gaps" e todos os blocos "Recently resolved". Histórico — leia só quando precisar do contexto de uma entrega passada; o sempre-presente fica no `AGENTS.md`. Itens novos de "Recently resolved" passam a ser registrados aqui (uma entrada curta), não no AGENTS.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:<base>` 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 <N>` 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 <N>` (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).
**Recently resolved (2026-08-10):** **OPS28** (Issue #584) — `worktree next`/`plan` passam a aplicar o **seed mínimo** além das migrations em cada banco provisionado (`migrate` + `db:seed:minimal` por banco, dev e test) — paridade com a CI, que roda o mesmo par antes do e2e: o e2e de worktree deixa de divergir do de CI (specs dependentes dos pins `priority: 'alta'` de `salvador-ze-1`/`camacari` — FAB suggest e `campaignSavedFilters` — falhavam só localmente com banco migrado cru 435×normal, o padrão que o ledger TECH-DEBT row 19 ficha como "masks real failures"). `--no-migrate` pula migrate **e** seed (o seed depende do catálogo migrado); re-provisionamento continua idempotente (upserts). Fallback sem Docker (Cursor Cloud) também roda migrate+seed nos `teqo`/`teqo_test` compartilhados (decisão de produto do gate: paridade total). **Guard de regressão** `tests/e2e/seedParity.setup.e2e.spec.ts` no projeto `setup` do Playwright: falha cedo com mensagem clara ("rode `pnpm migrate && pnpm db:seed:minimal`") se o banco de teste não estiver seedado — dependentes do setup pulam, nada de spec vermelho confuso; a evidência checada são os **users do seed** (`MINIMAL_CAMPAIGN_USERS`), deliberadamente não os pins `priority: 'alta'` — o cleanup dos fixtures e2e reseta `priority` dos municípios que toca (round-robin por sequência persistente), então pins morrem legitimamente após runs repetidas no mesmo banco de worktree e só o `pnpm worktree next` re-aplica (guard nunca checaria pins); `scripts/worktree.mjs` entrou em `HIGH_RISK_EXACT` (mudança no provisionador roda a suíte cheia na CI). Sem migration/access/Consent/UI. Intenção [`worktree-seed-minimal-paridade-e2e-ci.md`](plans/worktree-seed-minimal-paridade-e2e-ci.md); impl [`worktree-seed-minimal-paridade-e2e-ci-impl.md`](plans/worktree-seed-minimal-paridade-e2e-ci-impl.md).
**Recently resolved (2026-08-10):** **OPS34** (Issue #599) — o job `e2e` do CI (o mais longo da cascata, 435 s medidos no run 31412425553: ~67 s fixos + build 172 s + testes 196 s) roda agora **em 2 shards paralelos** (`--shard=N/2`, matrix `fail-fast: false`, build por shard — opção A da intenção validada pela medição: por shard ≈ 5–5,5 min; artefato de build compartilhado rejeitado porque os shards só começam após o build → wall maior). **Cadeia de projetos do Playwright (`setup → campaign → frontend → admin`) + prewarm do `setup` viraram dev-only:** `playwright.config.ts` ganhou `isProdMode = CI || E2E_PROD=1` (mesma condição do `webServer.command`, agora reusada) — em prod o projeto `setup` sai do array (arquivo não casado = não coletado) e as `dependencies` zeram (famílias em paralelo contra o build de produção, onde não há compile a preaquecer); dev intocado (provado: `--list` mostra `[setup]` só sem CI; smoke e2e dev rodou setup→campaignAuth). `ci-pr.yml`: o job `scope` emite `e2e_matrix`/`e2e_shards` via novo `e2eShardConfig(mode)` em `scripts/lib/test-affected-core.mjs` (fonte única, unit-pinada: full → `[1,2]`/2, selected/none → `[1]`/1 — pagar 2 builds por 3 specs é desperdício); `ci.yml` hardcodeia `[1,2]`/2 com comentário apontando o single-source. `PLAYWRIGHT_WORKERS: 2` explícito no env dos jobs e2e (aceite: explícito, default local intocado). Armadilha registrada nos workflows: flags do playwright vão **sem** o separador `--` — pnpm repassa o `--` literal ao playwright e a flag vira file pattern ("No tests found"). Specs intactas (o `setup` não foi removido — virou dev-only, já documentado como prewarm dev-only); sem migration/access/Consent/UI. **Medição pós-merge (main, run 31418206512):** job e2e 435 s → **326 s (shard 1) / 369 s (shard 2)** — shard 2 = fixo 68 s + build 176 s + testes 124 s (a fase de testes caiu de 196 s seriais para 124 s — metade da suite por runner + cadeia removida; o shard 2 ainda pagou retry da flake conhecida de `campaignMunicipalities`; shard 1: testes ~82 s); gate completo do pipeline (checks) ~7,5 → ~6,4 min. Build passou a dominar o shard — gatilho da intenção (opção B, artefato compartilhado) reavaliável quando os testes encolherem mais. Intenção [`ci-e2e-paralelizar-job.md`](plans/ci-e2e-paralelizar-job.md); impl [`ci-e2e-paralelizar-job-impl.md`](plans/ci-e2e-paralelizar-job-impl.md).
**Recently resolved (2026-08-10):** **OPS35** (Issue #600) — modo de spec e2e **full-stack sem browser** (paradigma HTTP): mesma suíte (`pnpm test:e2e`), mesmo job/CI, mesmo servidor + DB reais, sem lançar navegador. Base nova `tests/e2e/fixtures/campaignHttpTest.ts` estende o `test` **cru** de `@playwright/test` (nunca `e2eTest.ts` — o guard `e2eFailureGuard` é `auto` e puxaria `page`/`context`), reusa o fixture `campaign` do browser (exportado como `campaignFixture` — ownership/cleanup in-process, zero caminho de seed paralelo) e adiciona o fixture `campaignRequest(user, senha)`: login pelo **REST real do Payload** (`POST /api/campaignUser/login`, `loginWithUsername` aceita email/username) → `request.newContext` com o cookie canônico `campaign-token` (path `/campanha`, flags espelhando `setCampaignAuthCookie`; `secure` pelo scheme do baseURL, não pelo NODE_ENV do runner). **Nomenclatura ajustada contra a letra da intenção** (descoberta de execução): `*.http.e2e.spec.ts` não casa o `testMatch` `/campaign.*\.e2e\.spec\.ts/` do projeto → `campaign<Family>Http.e2e.spec.ts` no mesmo projeto (dependência `setup` de prewarm já coberta). **Primeira família migrada: `campaignTerritories`** (4 specs `campaignTerritoriesHttp.e2e.spec.ts`, browser removido; manifesto `e2e-affected` atualizado — unit-pinned). Equivalências: clicks viravam GETs de URL (`?sort=votes2022` → caption "Ordenado por 2022 (maior primeiro)"; `?region=Irecê` → "1 território encontrado"); DOM virou HTML renderizado — com duas realidades de render registradas no header do spec: separadores RSC `<!-- -->` (asserções rodam sobre `rendered()`, comentários são transporte, não conteúdo) e o label "Cobertura" presente no flight payload (menu do picker é client component serializado mesmo fechado) — o pin "oculta por default" é a ausência do controle sortable, que `resolveVisibleColumns` não renderiza. RBAC leader: o redirect `noLeader` é `redirect()` **pós-stream** → o documento responde 200 com `<meta http-equiv="refresh" content="1;url=/campanha/contatos">` (Next `next-route-redirect`), não 3xx — o meta é o contrato assertado (dev 307/prod 308 quando o redirect vence o stream; nunca pinar status numérico). **Medição (dev server, worktree 3135, load ~68, prewarm incluído nas duas versões):** família browser 56,0 s (22,9/17,5/7,9/7,7) vs HTTP 33,7 s (10,8/8,4/2,4/12,1) — **−40%**; o ganho é estrutural (sem login de browser, sem hidratação, sem waitForURL), a carga da máquina domina a variância. No prod build local (`E2E_PROD=1`, o modo do CI) a família HTTP fecha **4/4 em 3,1 s** (1,4/0,9/0,5/0,3). Guard de falhas client permanece exclusivo do browser (aceite). Sem migration/access/Consent/UI/schema. Intenção [`e2e-full-stack-sem-browser.md`](plans/e2e-full-stack-sem-browser.md); impl [`e2e-full-stack-sem-browser-impl.md`](plans/e2e-full-stack-sem-browser-impl.md).