From 631635ee89d5283f2d1c4d16f6bd787384421d9d Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 11 Aug 2026 19:02:35 -0300 Subject: [PATCH] chore(plan-issue): rascunho UI/UX do gate vira HTML+Tailwind commitado no repo (ad-hoc) --- .agents/skills/plan-issue/SKILL.md | 26 +++--- .../skills/plan-issue/intention-template.md | 20 +++-- .agents/skills/plan-issue/shaping.md | 2 +- .agents/skills/plan-issue/skills-map.md | 2 +- .agents/skills/plan-issue/ui-draft-canvas.md | 54 ------------- .agents/skills/plan-issue/ui-draft-html.md | 80 +++++++++++++++++++ docs/CHANGELOG-AGENTS.md | 2 + package.json | 1 + scripts/render-ui-draft.mjs | 52 ++++++++++++ 9 files changed, 164 insertions(+), 75 deletions(-) delete mode 100644 .agents/skills/plan-issue/ui-draft-canvas.md create mode 100644 .agents/skills/plan-issue/ui-draft-html.md create mode 100644 scripts/render-ui-draft.mjs diff --git a/.agents/skills/plan-issue/SKILL.md b/.agents/skills/plan-issue/SKILL.md index 3b1793b4..4fd69199 100644 --- a/.agents/skills/plan-issue/SKILL.md +++ b/.agents/skills/plan-issue/SKILL.md @@ -4,10 +4,10 @@ description: >- Transforma ideias humanas em Issues GitHub + planos de intenção em docs/plans/ (persona, fluxo, objetivo, direção suave no código — sem decisões duras de engenharia). Se o item muda UI, apresenta no gate um - Cursor canvas com rascunho visual UI/UX. Divide o pedido nas menores - tarefas que ainda fazem sentido. Use quando o usuário pedir /plan-issue, - planejar features, fatiar um pedido em Issues, ou registrar trabalho novo - na fila. + rascunho visual HTML+Tailwind (PNG embutido no plano de intenção). Divide + o pedido nas menores tarefas que ainda fazem sentido. Use quando o usuário + pedir /plan-issue, planejar features, fatiar um pedido em Issues, ou + registrar trabalho novo na fila. disable-model-invocation: true --- @@ -35,7 +35,7 @@ rascunho local (Issue: —) 4. **Promote só depois do plano em `main`.** Nunca flipar para `ready` com o PR ainda aberto — isso recria a race de claim. Caminhos: (A) `pnpm agent:ready -- --issue N` no fim do Passo 6 após merge; (B) Action determinística no merge que lê `Related #N` (OPS18). Ambos idempotentes. 5. **Planos de Issues `in-progress` / `done` / `in-prod` são imutáveis.** Não editar `docs/plans/.md` nem o body de intenção dessas Issues. Refino → **plano + Issue novos** (sucessor; `depends` no pai se fizer sentido). Enquanto a Issue ainda é só `blocked`/`ready` (sem claim), editar o mesmo plano ainda é barato. -**Canvas UI (obrigatório se muda UI):** itens com superfície Impeccable **B / C / D** (ou qualquer mudança do que o usuário vê/toca) devem ter um **Cursor canvas** de rascunho UI/UX no gate — ver [ui-draft-canvas.md](ui-draft-canvas.md). Classe A / sem UI → sem canvas. +**Rascunho UI (obrigatório se muda UI):** itens com superfície Impeccable **B / C / D** (ou qualquer mudança do que o usuário vê/toca) devem ter um **rascunho HTML+Tailwind** (renderizado em PNG e commitado em `docs/plans/`) no gate — ver [ui-draft-html.md](ui-draft-html.md). Classe A / sem UI → sem rascunho. ## Divisão com as skills de execução @@ -45,7 +45,7 @@ rascunho local (Issue: —) | **`work-issue`** | Humano supervisiona: Issue já claimada (claim fora da skill) → plano de **implementação** (Plan mode) → **pausa** para confirmação → executa. | | **`agent-work-issue`** | Pool / autonomia: Issue já claimada → plano de implementação → executa sem pausa → `/simplify` → `capture-review-debts` → PR Ready + auto-merge. | -Aqui **não** se implementa código de produto, **não** se escreve plano de implementação, **não** se roda Impeccable craft/critique/polish. O canvas de rascunho UI/UX é artefato do **gate de intenção**, não entrega de app. +Aqui **não** se implementa código de produto, **não** se escreve plano de implementação, **não** se roda Impeccable craft/critique/polish. O rascunho UI/UX (HTML+Tailwind) é artefato do **gate de intenção**, não entrega de app. **Shaping (não tour):** aplique [shaping.md](shaping.md) em silêncio — appetite, fatia mínima útil, rabbit holes de produto, self-score ≥4 antes de gravar. @@ -58,7 +58,7 @@ Aqui **não** se implementa código de produto, **não** se escreve plano de imp - [ ] 2. Reserva de IDs de uma vez por trilha (roadmap legacy + issuesById()) - [ ] 3. Por item (ordem topológica): classificar → fatiar → explorar só o suficiente → intenção completa - [ ] 4. Sugestão de modelo × effort via model-selection (uma linha por item) -- [ ] 5. GATE: overview do lote + canvas UI/UX (se muda UI) + esboços de fluxo → confirmar/iterar (PARAR aqui até o humano confirmar; sem Issue/PR) +- [ ] 5. GATE: overview do lote + rascunho UI/UX (se muda UI) + esboços de fluxo → confirmar/iterar (PARAR aqui até o humano confirmar; sem Issue/PR) - [ ] 6. Registro: `agent:register` (`--plan` → `blocked`) → PR `Related #N` → merge → `pnpm agent:ready` ``` @@ -92,10 +92,10 @@ Ordem topológica (dependente cita ID do dependido). Por item: | Decisão de NÃO fazer | Comentário/doc, não Issue | 2. **Explorar o código o mínimo** — só para apontar **direção** (rotas/pastas/domínios prováveis) e evitar duplicar algo já entregue. Não inventar signatures, collections novas como decisão travada, nem diagramas de componentes. -3. **Superfície UI (A–D)** como dica para quem for executar — não semear brief Impeccable completo. Classe **B/C/D** (ou qualquer mudança de UI): criar **Cursor canvas** de rascunho UI/UX ([ui-draft-canvas.md](ui-draft-canvas.md) + skill `canvas`) **antes do gate**; ASCII no plano fica opcional. Classe **A** / sem UI: sem canvas. +3. **Superfície UI (A–D)** como dica para quem for executar — não semear brief Impeccable completo. Classe **B/C/D** (ou qualquer mudança de UI): criar **rascunho HTML+Tailwind** ([ui-draft-html.md](ui-draft-html.md)) **antes do gate**, renderizar o PNG (`pnpm ui-draft:render`) e embutir no plano; ASCII no plano fica opcional. Classe **A** / sem UI: sem rascunho. 4. **Dados (intenção)** ou `Dados: N/A`. 5. **Posicionamento:** `P0..P3`, `depends`, appetite, janela eleitoral se relevante, `serializes` se tocar recurso compartilhado (ex. migrations) — sem detalhar a migration. -6. **Plano de intenção** em `docs/plans/.md` via [intention-template.md](intention-template.md), com campo **Canvas UI** preenchido (path do `.canvas.tsx` ou `N/A`). Self-score ≥4/5 ([shaping.md](shaping.md)). +6. **Plano de intenção** em `docs/plans/.md` via [intention-template.md](intention-template.md), com campo **Rascunho UI** preenchido (path do `.html` + PNGs embutidos, ou `N/A`). Self-score ≥4/5 ([shaping.md](shaping.md)). ### O que é proibido no plano de intenção @@ -113,7 +113,7 @@ Ordem topológica (dependente cita ID do dependido). Por item: - Appetite e fora de escopo (produto) - **Direção provável no codebase** (pastas/rotas/domínios — hipotética, revisável) - Questões em aberto com **Opções + Recomendação de produto** (não de engenharia) -- **Se muda UI:** canvas de rascunho UI/UX no gate ([ui-draft-canvas.md](ui-draft-canvas.md)) + link no plano +- **Se muda UI:** rascunho UI/UX (HTML+Tailwind → PNG) no gate ([ui-draft-html.md](ui-draft-html.md)) + PNG embutido no plano ## Passo 4 — Modelo × effort @@ -128,11 +128,11 @@ Skill `model-selection`. Registre no cabeçalho (`Model:`) e no gate. Antes de criar Issues **ou** abrir PR de planos: - Overview: ID, título, prio, depends, appetite, modelo, link do plano local -- **Para cada item que muda UI:** link markdown absoluto do canvas `plan--ui-draft.canvas.tsx` (abrir ao lado do chat) — este é o rascunho visual a validar -- Esboço textual de fluxo só se ajudar; não substitui o canvas quando há UI +- **Para cada item que muda UI:** mostre as imagens do rascunho (PNGs embutidos no plano) + link do `.html` fonte — este é o rascunho visual a validar +- Esboço textual de fluxo só se ajudar; não substitui o rascunho quando há UI - Perguntas acumuladas numa rodada, recomendação de produto primeiro -**Pare e espere.** Itere (incluindo o canvas, se houver UI) até confirmação explícita do lote (não basta um “ok” solto durante a edição). Só então Passo 6. +**Pare e espere.** Itere (incluindo o rascunho — HTML → re-render do PNG, se houver UI) até confirmação explícita do lote (não basta um “ok” solto durante a edição). Só então Passo 6. ## Passo 6 — Registro (não claimável até plano em `main`) diff --git a/.agents/skills/plan-issue/intention-template.md b/.agents/skills/plan-issue/intention-template.md index 88f153aa..15143fbc 100644 --- a/.agents/skills/plan-issue/intention-template.md +++ b/.agents/skills/plan-issue/intention-template.md @@ -13,7 +13,7 @@ Issue: # (após `pnpm agent:register`; senão "—") Priority: Model: Impeccable: — -Canvas UI: -ui-draft.canvas.tsx> +Rascunho UI: -ui-draft.html + PNG embutido abaixo> Appetite: Responsável: — @@ -30,13 +30,21 @@ Responsável: — ### Esboço de fluxo (B/C/D) - + \```text [início] → … → [outcome] \``` +### Rascunho UI (B/C/D) + + + +![Rascunho UI — ](-ui-draft-.png) + ## Objetivo e aceite - @@ -79,7 +87,7 @@ Responsável: — ## Referências - GitHub Issue # -- Canvas UI (gate): +- Rascunho UI (gate): - - `AGENTS.md` / `docs/ARCHITECTURE.md` — só se o item toca convenção já travada no repo ```` @@ -87,7 +95,7 @@ Responsável: — Notas: - Alvo: ~60–100 linhas. Mais que isso costuma significar engenharia vazando para a intenção. -- Classe **A:** `Impeccable: A — N/A`; `Canvas UI: N/A`; omita esboço de fluxo. -- Classe **B/C/D:** canvas obrigatório no gate ([ui-draft-canvas.md](ui-draft-canvas.md)); leia a skill `canvas` ao escrever o `.canvas.tsx`. +- Classe **A:** `Impeccable: A — N/A`; `Rascunho UI: N/A`; omita esboço de fluxo. +- Classe **B/C/D:** rascunho HTML+Tailwind obrigatório no gate ([ui-draft-html.md](ui-draft-html.md)); renderize com `pnpm ui-draft:render` e embuta o PNG no plano. - Não rode `/impeccable` aqui. - Self-score shaping ≥4/5 antes de gravar. diff --git a/.agents/skills/plan-issue/shaping.md b/.agents/skills/plan-issue/shaping.md index ca7cadca..8dc35dcc 100644 --- a/.agents/skills/plan-issue/shaping.md +++ b/.agents/skills/plan-issue/shaping.md @@ -53,6 +53,6 @@ Armadilhas se alguém “só completar” o desejo humano: 4. Direção no codebase é hipótese (não contrato técnico)? 5. Zero decisões duras de engenharia no plano? -Se o item muda UI: canvas de rascunho no gate ([ui-draft-canvas.md](ui-draft-canvas.md)) antes de registrar — não conta como “engenharia no plano”; é validação visual da intenção. +Se o item muda UI: rascunho HTML+Tailwind no gate ([ui-draft-html.md](ui-draft-html.md)) antes de registrar — não conta como “engenharia no plano”; é validação visual da intenção. <4 → corrigir antes de gravar / registrar Issue. diff --git a/.agents/skills/plan-issue/skills-map.md b/.agents/skills/plan-issue/skills-map.md index 38e58cb8..5b8dcf7b 100644 --- a/.agents/skills/plan-issue/skills-map.md +++ b/.agents/skills/plan-issue/skills-map.md @@ -39,7 +39,7 @@ No Teqo: **idea-refine / interview** antes se vago; **spec + plan + tasks** = es `frontend-ui-engineering`, `security-and-hardening`, `performance-optimization`, `code-review-and-quality`, `code-simplification`, `observability-and-instrumentation`, `shipping-and-launch`, `ci-cd-and-automation`, `git-workflow-and-versioning`, `deprecation-and-migration`, `debugging-and-error-recovery`, `browser-testing-with-devtools` — entram em `work-issue` / gates do repo, não no registro da Issue. -**Exceção de superfície:** a skill Cursor **`canvas`** é usada no `plan-issue` só para o **rascunho UI/UX do gate** quando o item muda UI ([ui-draft-canvas.md](ui-draft-canvas.md)). Não é implementação de `/campanha` nem substituto de Impeccable. +**Exceção de superfície:** o **rascunho UI/UX do gate** — HTML+Tailwind renderizado em PNG (`pnpm ui-draft:render`), [ui-draft-html.md](ui-draft-html.md) — é usado no `plan-issue` só quando o item muda UI. Não é implementação de `/campanha` nem substituto de Impeccable. ## Precedência quando conflita diff --git a/.agents/skills/plan-issue/ui-draft-canvas.md b/.agents/skills/plan-issue/ui-draft-canvas.md deleted file mode 100644 index 0cd9d427..00000000 --- a/.agents/skills/plan-issue/ui-draft-canvas.md +++ /dev/null @@ -1,54 +0,0 @@ -# Canvas de rascunho UI/UX (plan-issue) - -Quando o item **muda UI** (classe Impeccable **B / C / D**, ou qualquer superfície que o usuário vê/toca), o gate **obrigatoriamente** apresenta um **Cursor canvas** com rascunho visual da intenção — não ASCII sozinho, não wireframe de implementação. - -Leia e siga a skill `canvas` (`~/.cursor/skills-cursor/canvas/SKILL.md`) ao criar/editar o arquivo. - -## Quando criar - -| Situação | Canvas? | -| -------- | ------- | -| `Impeccable: A` / N/A sem UI / chore sem superfície | Não | -| Encaixe em tela existente (B) | Sim — estado atual → estado desejado da área tocada | -| Fluxo novo ou redesign local (C/D) | Sim — jornada + telas/estados chave | -| Só copy/label sem rearranjo | Não (a menos que o humano peça ver o contexto) | - -Um canvas por item com UI (ou um canvas do lote com secções por ID, se o lote for pequeno e as superfícies forem do mesmo fluxo). Preferir **um arquivo por item** (`plan--ui-draft.canvas.tsx`). - -## Onde gravar - -Path canônico do IDE (não no repo Teqo): - -`/Users//.cursor/projects//canvases/plan--ui-draft.canvas.tsx` - -Ex.: `plan-c42-ui-draft.canvas.tsx`. Escreva o arquivo com a tool de write; não peça `mkdir`. No chat do gate, linke o path absoluto em markdown. - -## O que mostrar (produto) - -Rascunho de **intenção de uso**, legível sozinho: - -1. **Persona + job** (uma linha). -2. **Jornada** (passos / estados) — o que a pessoa vê e decide. -3. **Layout esquemático** das superfícies tocadas: zonas (lista, detalhe, formulário, mapa, empty, erro), hierarquia de atenção, CTAs primários/secundários. -4. **Estados críticos** se mudarem o aceite (vazio, loading, sem permissão, fail-closed) — só os que importam ao produto. -5. **Fora de escopo visual** em callout curto (o que este item *não* redesenha). - -Use `Stack` / `Grid` / `Row` / `Card` / `Pill` / `Callout` / `Text` / `H1`–`H3` de `cursor/canvas`. Cores só via `useHostTheme()`. Pode usar caixas/`div` com bordas de token para simular regiões de tela — isso é rascunho de UX, não componente do app. - -## O que é proibido no canvas - -- Assinaturas, nomes de arquivos/componentes obrigatórios, schema, migrations -- Brief Impeccable completo, craft/critique/polish, paleta/brand do produto Teqo -- Mock com dados inventados como se fossem métricas reais (“No data”, KPIs zerados) — omita secções sem conteúdo -- Gradientes, emojis, box-shadow, arco-íris (regras da skill canvas) -- Travar o executor numa única forma técnica - -## Relação com o plano em `docs/plans/` - -- No plano: campo **Canvas UI:** link/path do `.canvas.tsx` (ou `N/A — sem UI`). -- Esboço ASCII no markdown vira **opcional** (backup textual); o **canvas é o artefato do gate** para UI. -- O canvas **não** é commitado no repo Teqo por default — é artefato de sessão do gate. Se o humano pedir arquivar no repo, só com pedido explícito (fora do fluxo padrão). - -## Gate - -Antes de `pnpm agent:register`, mostre o overview do lote **e** o(s) canvas de UI. Itere no canvas até o humano confirmar a intenção visual. Só então registre a Issue. diff --git a/.agents/skills/plan-issue/ui-draft-html.md b/.agents/skills/plan-issue/ui-draft-html.md new file mode 100644 index 00000000..e4f7b46e --- /dev/null +++ b/.agents/skills/plan-issue/ui-draft-html.md @@ -0,0 +1,80 @@ +# Rascunho UI/UX do gate (plan-issue) + +Quando o item **muda UI** (classe Impeccable **B / C / D**, ou qualquer superfície que o usuário vê/toca), o gate **obrigatoriamente** apresenta um **rascunho visual** da intenção — não ASCII sozinho, não wireframe de implementação. O meio é **HTML + Tailwind**, renderizado em PNG e **commitado no repo Teqo** junto do plano. (Substitui o antigo Cursor canvas, que vivia fora do repo em `~/.cursor/projects//canvases/`, só renderizava no IDE de quem planejou e apodrecia.) + +## Para que serve — e para que NÃO serve + +Serve para **colaborar visualmente com o humano** e fechar **layout, espaçamentos e tamanhos**: hierarquia, zonas, CTAs, densidade, medidas reais. É a linguagem do gate para "é isso que o usuário vê/toca". + +**NÃO é** implementação da feature, **NÃO é** protótipo funcional, **NÃO é** entrega de app, **NÃO é** decisão de engenharia. Quem executa (`work-issue` / `agent-work-issue`) constrói a partir do plano; o rascunho é descartável como fonte e imutável como registro (o PNG aprovado). + +## O que o rascunho DEVE ter (fidelidade mínima) + +1. **Layout real** das superfícies tocadas: zonas (lista, detalhe, formulário, mapa, empty, erro), hierarquia de atenção, CTA primário vs secundário. +2. **Espaçamentos e tamanhos reais** — a mesma escala do app (Tailwind v4: `p-4`, `gap-3`, `h-11`, `text-sm`, `rounded-lg`). **É disso que o gate discorda ou aprova.** +3. **Breakpoints reais**: cena mobile (~390px) e cena desktop (~1280px) quando o fluxo tocar os dois. Um rascunho mobile sem viewport mobile não valida nada. +4. **Estados críticos** se mudarem o aceite (vazio, loading, sem permissão, fail-closed) — como **cenas estáticas separadas**, nunca como interação. +5. **Texto real pt-BR**; valores obviamente genéricos ("Nome da liderança", "12 municípios"); barras cinza para conteúdo desconhecido. + +## O teto (proibido — é rascunho, não implementação) + +1. **Zero funcionalidade.** Nenhum JS de comportamento: sem `onClick`, sem state, sem tabs/drawer/accordion de verdade, sem condicionais. Cada estado = uma cena estática. O único JS do arquivo é o build browser do Tailwind. +2. **Zero código de app.** Não importar de `src/`, não escrever o componente final, não nomear componentes/arquivos obrigatórios. O rascunho é **descartável**: o executor constrói do plano, não copia o rascunho. +3. **Zero brand/polish.** Sem gradientes, emojis, box-shadow, animações, microinteractions, logo/imagens reais, paleta do produto. Cinza neutro (zinc/neutral) + **um único acento** para o CTA primário. Fonte do sistema. +4. **Zero mock de dados.** Sem KPIs realistas, nomes reais, números de votos — placeholders óbvios ou barras cinza. +5. **Zero decisão de engenharia.** Sem schema, migration, nomes de arquivos obrigatórios, assinaturas — o rascunho mostra superfície, não solução. + +## Como fazer (artefatos no repo) + +1. **Escreva** `docs/plans/-ui-draft.html` — um arquivo por item, autossuficiente: + +```html + + + + + + + UI draft — <título do item> + + +
+

Persona/job em uma linha + cenas abaixo

+
…
+
…
+
…
+
+ + +``` + + - Tailwind **v4** (`@tailwindcss/browser@4`) — mesma major do app (4.1.x). Requer rede apenas na hora de renderizar, nunca no artefato. + - **Cada cena que vira imagem: `
`** (rótulo kebab, ex. `mobile`, `desktop-vazio`). Sem `data-shot`, o script captura a página inteira. + - O arquivo **é commitado** com o plano: o rascunho aprovado vira registro permanente do aceite (o PR revisa HTML + PNG; ninguém depende do IDE de quem planejou). + +2. **Renderize:** + +```bash +pnpm ui-draft:render docs/plans/-ui-draft.html +``` + + → `docs/plans/-ui-draft-.png` (uma por cena `data-shot`) ou `docs/plans/-ui-draft.png` (full-page). Usa o Chromium do Playwright já instalado no repo; nunca toca no banco. + +3. **Embute no plano e commite:** + +```markdown +![Rascunho UI — mobile](-ui-draft-mobile.png) +``` + +## Relação com o plano em `docs/plans/` + +- Campo **Rascunho UI:** no cabeçalho: `docs/plans/-ui-draft.html` (ou `N/A — sem UI`). +- PNGs embutidos no corpo do plano (secção "Rascunho UI (gate)"). +- Esboço ASCII no markdown vira **opcional** (backup textual); o PNG é o artefato do gate. +- **É commitado com o plano** (muda o default antigo de "artefato de sessão, fora do repo"): o PNG aprovado é o registro do aceite visual e viaja com a Issue. + +## Gate + +Antes de `pnpm agent:register`, mostre o overview do lote **e** as imagens do rascunho (PNGs embutidos no plano — o humano vê no chat e no PR) + link do `.html` fonte. Itere: **editar HTML → re-renderizar (`pnpm ui-draft:render`) → atualizar PNGs** até confirmação explícita. Só então registre a Issue. + +Legado: os antigos `plan--ui-draft.canvas.tsx` (Cursor) continuam válidos como histórico de planos passados — nenhum rascunho novo usa canvas. diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 33aa3b2d..7503e4b2 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-11):** **Plan-issue — rascunho UI/UX do gate vira HTML+Tailwind commitado no repo (ad-hoc, decisão do usuário).** O artefato deixa de ser **Cursor canvas** (`~/.cursor/projects//canvases/plan--ui-draft.canvas.tsx`, fora do repo, só renderizava no IDE de quem planejou e apodrecia) e passa a ser **HTML + Tailwind v4** (`docs/plans/-ui-draft.html` — `@tailwindcss/browser@4` via jsdelivr, mesma major do app 4.1.x; rede só na hora de renderizar) **commitado com o plano** e **renderizado em PNG** por `pnpm ui-draft:render` (`scripts/render-ui-draft.mjs`, Chromium do Playwright via `@playwright/test`, fallback `load` se o CDN não settle — nunca toca banco/app): uma PNG por cena `
` (ex. `mobile`, `desktop-vazio`) → `docs/plans/-ui-draft-.png`, ou full-page sem `data-shot`; PNGs **embutidos no plano** (visíveis no chat do gate E no PR — o aceite vira registro permanente; o HTML é a fonte iterável). **Contrato de fidelidade explícito em `ui-draft-html.md`** (substitui `ui-draft-canvas.md`, deletado; refs atualizadas em SKILL.md/frontmatter, intention-template.md — campo renomeado `Canvas UI:` → `Rascunho UI:` + secção de embed, skills-map.md, shaping.md): o rascunho DEVE ter layout real, espaçamentos/tamanhos na escala do app (`p-4`, `gap-3`, `h-11`, `text-sm`), breakpoints reais (390/1280) e estados críticos como cenas estáticas; é PROIBIDO ser funcional (zero JS de comportamento — cada estado é cena separada), usar código de app (`src/`), brand/polish (gradientes/emojis/sombras/logo/paleta do produto — cinza neutro + um acento), mock de dados realistas ou decisão de engenharia. Gate: iterar HTML → re-render → embutir até confirmação explícita. Verificado: render real do skeleton (classes v4 aplicadas — oklch zinc, rounded-lg 8px, text-lg 18px). Sem migration/access/Consent/UI de produto/schema. Sem issue — ad-hoc no worktree `work/5`. + **Recently resolved (2026-08-11):** **C124** (Issue #668) — tarefa de atividade fica só com **texto, responsável e concluída**: o campo `due` (Prazo, datetime) sai do modelo e da UI. Remoção em cadeia num único commit simétrico: campo do array `tasks` da `activity` (o guardrail de liderança perde a comparação de `due` no mesmo item — continua fail-closed sobre título/responsável/contagem), `taskSchema` zod, `parseTasksFormData`/`ParsedActivityTask` (formData), `ActivityFormTaskViewModel`/`ActivityTaskViewModel`, input `datetime-local` do `ActivityTaskFields` (grid `2fr_1fr`) e linha "Prazo: …" do `ActivityTaskChecklist` (subtítulo condicional + `break-words` no título — P2 dos reviewers). `doneAt` ("concluída em") permanece como derivado invisível; `taskTotal`/`taskDoneCount` intocados. **Migration** `20260811_183016_drop_activity_task_due` (gerada; diff do gerador tinha no-op de `google_calendar_sync` por snapshot stale — limpa à mão para só o `DROP COLUMN "due"` de `activity_tasks`; dados descartados conforme decidido no gate). Grep pós: zero `due` fora das migrations (histórico frozen); nenhum consumidor fora da vertical (notificações, Google sync, quick actions). Gates: unit+int 736/736, e2e atividade 9/9 (1 flake ECONNRESET no run paralelo — rerun limpo), build OK. Intenção [`atividades-tarefas-sem-prazo.md`](plans/atividades-tarefas-sem-prazo.md); impl [`atividades-tarefas-sem-prazo-impl.md`](plans/atividades-tarefas-sem-prazo-impl.md). **Recently resolved (2026-08-11):** **C118** (Issue #657) — página de detalhe da pessoa `/campanha/pessoas/[id]` (staff), montada pelas **capacidades** da ficha: a mesma rota serve liderança, dobradinha, assessora, apoiador — e só mostra o que a pessoa tem. **Loader irmão** `loadPersonDetail` (`src/utilities/people/personDetail.ts`) reusa o merge da lista (`mergePeopleSources` + `scopePeopleRows`) com os mesmos selects por fonte — o **escopo do assessor é literalmente o da lista** (pessoa só aparece se alguma capacidade municipal estiver na carteira; `null` → `notFound()`), staff com bypass justificado (identity-gated, prova pelo merge); **seção Apoiador** (dado novo vs. view model da lista) lê `supporter` por contact com o access da própria collection (`overrideAccess: false`) — camada dupla, nunca alarga (int testado: apoiador de município fora da carteira é cortado). **`[id]` é `contactID`** (a chave de linha da C100 — e o link do Nome na lista, que a C116 nasceu apontando para `/campanha/liderancas/[id]`, agora aponta para cá). UI: `PersonDetailSections` em `components/campaign/people/` — Ficha (partido/telefone/e-mail/base + pills de capacidade), Liderança (status, acesso ao app, municípios, link "Abrir detalhe de liderança" — convivência v1 com `/campanha/liderancas/[id]`, votos continuam lá), Dobradinha, Assessora (por conta, `campaignRoleLabels`), Assessorado, Apoiador (fonte · município · intenção quando consentida · data), Ações reusando os botões da lista (WhatsApp, `LeadershipInviteRowAction` só com liderança, `DeletePersonButton` ganhou `deletedHref` opcional → volta à lista pós-delete). Sem migration/access novo/Consent; sem votos/KPI (intenção corta; assimetria intacta). Testes: int `personDetail` (matriz de escopo: unrestricted completo, assessor na carteira vê, fora → null, corte de supporter, só-staff só para unrestricted, conta leader não acende Assessora, contato solto/leader → null) e e2e novos em `campaignPeople` (coordinator vê seções por capacidade + link de liderança; dobradinha+apoiador sem bloco de liderança; leader → `/campanha/contatos`). Verificado ao vivo em dev (coordenador/assessor/leader, 404 de id inválido, mobile). Intenção [`pessoas-detalhe-por-capacidades.md`](plans/pessoas-detalhe-por-capacidades.md); impl [`pessoas-detalhe-por-capacidades-impl.md`](plans/pessoas-detalhe-por-capacidades-impl.md). diff --git a/package.json b/package.json index 0b02849c..d084e744 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "test:e2e:affected": "node scripts/run-e2e-affected.mjs", "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts", "test:unit": "cross-env NODE_OPTIONS=--no-deprecation DATABASE_URL=postgresql://invalid:invalid@127.0.0.1:1/unit_tests_must_not_connect vitest run --config ./vitest.unit.config.mts", + "ui-draft:render": "node scripts/render-ui-draft.mjs", "typecheck": "tsc --noEmit", "typecheck:tsc6": "tsc6 --noEmit", "prepare": "husky", diff --git a/scripts/render-ui-draft.mjs b/scripts/render-ui-draft.mjs new file mode 100644 index 00000000..39e5f502 --- /dev/null +++ b/scripts/render-ui-draft.mjs @@ -0,0 +1,52 @@ +/** + * Renders a plan-issue UI draft (HTML + Tailwind) to PNG(s) for the gate. + * + * Usage: pnpm ui-draft:render -ui-draft.html> + * + * - Screenshots each `[data-shot]` scene as `-ui-draft-