diff --git a/docs/plans/ajuste-votos-wizard.md b/docs/plans/ajuste-votos-wizard.md new file mode 100644 index 00000000..1090a541 --- /dev/null +++ b/docs/plans/ajuste-votos-wizard.md @@ -0,0 +1,109 @@ +# Etapa de ajuste de votos no wizard (cenários + atalhos) + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B61 — UX-1 wizards) +Impeccable: C — UI nova de passo numérico com coerência entre cenários +Appetite: ~1–1,25 dia eng; 3 sub-passos de cenário + atalhos + navegação de violação; grava via action existente no wiring A1 (neste item: UI + validação client espelhando servidor) +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (Feel the action; Clarity under pressure) / `DESIGN.md` · `VoteEstimateScenarioInputs` / `MunicipalityListExpectedVotesControl` · tema `campaign`. + +Na implementação: shape → craft → critique → polish. + +Brief compacto: + +- **Persona:** CG no celular ajustando Cairu −100 / Salinas 300→150; quer atalho, não teclado minucioso. +- **Job principal:** editar **um cenário por vez** (média → pessimista → otimista), com atalhos e confirmação explícita para o próximo. +- **Estratégia de cor:** Restrained; warning de coerência usa tom de alerta do tema (não Signal Red decorativo). +- **Edit where you see:** não neste passo — é fluxo com **submit explícito** por cenário (“Ajustar estimativa pessimista →”), exceção já prevista em `campanha-edit-where-you-see` para confirmação. +- **Anti-goals:** três inputs na mesma tela com um “Salvar”; input enorme estilo hero-metric SaaS; auto-save silencioso sem CTA. + +## Dados → decisão → apresentação + +- **Vou apresentar dados?** Sim — valor atual de `expectedVotes[cenário]` e o valor em edição. +- **Decisões:** staff escolhe o novo número da projeção da mesa por cenário; decide se corrige coerência ou volta ao cenário que quebrou. +- **Forma:** **número + contexto** (rótulo do cenário + valor atual). **Rejeitado:** chart; três gauges; % estadual. +- **Profile:** 1 inteiro por passo; ordem `pessimistic ≤ central ≤ optimistic` (`getVoteEstimateOrderViolation`). +- **Anti-goals de dado:** não misturar `declaredVotes` de liderança; liderança nunca vê esta etapa. + +## Contexto + +Ação A1 do Início ([fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)): após escolher município (**B60**), ajustar estimativas. Pedido (2026-07-29): + +- Input **já preenchido** com o valor atual; editável. +- Atalhos: **2×**, **+50**, **−50**, **+100**, **−100** (aplicam sobre o valor corrente do input). +- Input **grande, não enorme**. +- Ordem fixa: **média → pessimista → otimista** (`central` → `pessimistic` → `optimistic`). +- Se um cenário **quebrar a lógica** com um já definido, navegar **para o cenário que ficou inconsistente**, mostrar **warning** (por quê) e atalho para **voltar ao cenário que a pessoa acabou de editar**. +- CTA por passo: ex. **“Ajustar estimativa pessimista →”** (não auto-avanço mudo). + +Invariante de servidor já existe: `getVoteEstimateOrderViolation` + hook em `Municipality` / `VOTE_ESTIMATE_ORDER_ERROR_MESSAGE` (`src/lib/voteEstimate.ts`). + +## Objetivos + +- Etapa(s) `WizardExpectedVotesStep` (ou uma rota com `?scenario=`) no **B59**. +- Estado local dos três números; ao confirmar um cenário, validar contra os já confirmados; se violar, `setScenario(violation)` + banner + link “Voltar para [cenário editado]”. +- Atalhos mutam o draft (floor 0, ceiling `MAX_VOTE_COUNT`); `2×` = dobrar o valor atual do input (se vazio/0, definir comportamento: ficar 0 ou no-op — **recomendação:** no-op se <1). +- CTA confirma o cenário e avança (ou conclui o bloco de votos e devolve ao orquestrador A1). +- Reusar labels `voteEstimateScenarioLabels` (Média / Pessimista / Otimista). +- Escrita persistente: **reusar** action/endpoint de `expectedVotes` já usados pela lista (`MunicipalityListExpectedVotesControl` / POST expected-votes) — neste item pode entregar UI + schema de passo; o commit final do A1 pode batchar os três no wiring. **Mínimo:** não inventar segunda semântica de ordem. +- Staff-only; sem migration / Consent. +- Unit: coerência (casos que forçam jump + atalho de retorno); atalhos aritméticos. + +## Decisões travadas + +- **Ordem de edição: média → pessimista → otimista** (pedido). **Rejeitado:** três campos numa tela; ordem pessimista-primeiro (é a ordem do invariante de armazenamento, não do ritual de mesa). +- **Confirm explícito por cenário** (CTA nomeado). **Rejeitado:** auto-save estilo B27 (pedido pede botão; e coerência cross-cenário pede commit consciente). +- **Violação → navegar ao cenário quebrado + warning + atalho de retorno.** **Rejeitado:** só toast bloqueando; forçar edição inline sem mudar de passo. +- **Atalhos sobre o valor do input**, não sobre o valor persistido antigo após dirty. **Rejeitado:** resetar ao “atual” a cada atalho. +- **i18n:** `central`/`pessimistic`/`optimistic` nos ids; copy “Ajustar estimativa … →”. + +## Questões em aberto + +- **Gravar no servidor a cada CTA de cenário ou só no commit final do A1?** **Opções:** A por cenário | B batch no fim. **Recomendação:** B no wiring A1 (um write); este item valida localmente e devolve os três valores — evita half-write se o CG abandonar no pessimista. _(assumido — validar na implementação do A1)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Mun["município B60"] --> C["passo Média"] + C --> P["passo Pessimista"] + P --> O["passo Otimista"] + C & P & O --> V{"ordem ok?"} + V -->|não| Jump["vai ao cenário violado + warning"] + V -->|sim| Next["próximo bloco / quer também?"] +``` + +Componentes: + +- **`WizardExpectedVotesStep`**: input grande (`text-2xl`/`text-3xl` — medir no craft; não `text-6xl`), fila de atalhos (`Button` outline), CTA, banner de coerência. +- **Helper puro** `applyVoteShortcut(value, 'double' | '+50' | …)` + reuso `getVoteEstimateOrderViolation`. +- **Migration:** Sem migration (campos já existem). + +## Dependências + +- Dura: **B59**. Soft: **B60** (município já escolhido). Reuso: A10 ✓ / `voteEstimate.ts` / controle da lista. + +## Não escopo + +- Wiring completo A1 (sinal/tendência/resumo) → fatia posterior UX-1. +- Edição de `votePledge.estimatedVotes` → fora (esta etapa = `municipality.expectedVotes` da mesa). +- Leader → lockdown. + +## Rabbit holes + +- **Mini-spreadsheet dos 3 cenários com sync bidirecional.** **Mitigação:** um cenário visível por vez. +- **Recalcular meta E8 / cobertura ao vivo no passo.** **Mitigação:** número cru; cobertura fica no resumo A1 ou no Quadro. + +## Adiado com gatilho + +- **Preview de cobertura E8 no rodapé do passo.** Revisitar se o CG pedir “isso fecha a meta?” durante o ajuste (sessão). + +## Referências + +- [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) · [cenarios-estimativa-votos.md](cenarios-estimativa-votos.md) · `src/lib/voteEstimate.ts` · `MunicipalityListExpectedVotesControl.tsx` · `VoteEstimateScenarioInputs.tsx` · collection `Municipality` hook de ordem +- AGENTS.md — assimetria leader; `overrideAccess: false` +- `PRODUCT.md` / `DESIGN.md` · `.cursor/rules/campanha-edit-where-you-see.mdc` (exceção submit explícito) diff --git a/docs/plans/busca-municipio-wizard.md b/docs/plans/busca-municipio-wizard.md new file mode 100644 index 00000000..0dfcfd43 --- /dev/null +++ b/docs/plans/busca-municipio-wizard.md @@ -0,0 +1,96 @@ +# Etapa de busca de município nos wizards + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B60 — UX-1 wizards) +Impeccable: B — encaixe da UX da busca do Início (B47/B48) numa etapa de wizard, só municípios +Appetite: ~1 dia eng; input + lista de hits + auto-avanço; soft reuso do loader B48 +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` / `DESIGN.md` · B47 `CampaignHomeSearch` / B48 linhas de município · tema `campaign`. + +Na implementação: craft compacto → critique → polish. + +Brief compacto: + +- **Persona:** CG digita “Cairu” no meio do ritual A1/A2 e quer o município em 1 toque. +- **Job principal:** achar o município no escopo do ator e **avançar sozinho** ao selecionar. +- **Estratégia de cor:** Restrained. +- **Edit where you see:** não — seleção é navegação de fluxo. +- **Anti-goals:** grupos (TI/lideranças/…); botão “Continuar” após selecionar; card por linha. + +## Dados → decisão → apresentação + +- **Vou apresentar dados?** Sim — hits de município (nome, TI, opcional “2022” se B48 já tiver o readout). +- **Decisões:** escolher **qual** município entra no fluxo. +- **Forma:** lista ranqueada por nome (word-start), igual espírito B48. **Rejeitado:** mapa; grupos multi-entidade. +- **Profile:** scoped ao access; tipicamente <20 hits. +- **Anti-goals de dado:** sem inventar métrica; sem TIs neste passo (wizard precisa de unidade operacional `municipality`). + +## Contexto + +No Início, **B47** ✓ entrega input + modo focado; **B48** lista Municípios (+ TIs no mesmo grupo). Nos wizards (pedido 2026-07-29): **mesma experiência de busca**, com duas diferenças — (1) **sem grupos**: só municípios; (2) **selecionar = avançar** ao próximo passo, sem confirmar. + +Serve A1 (votos), A2 (sinal), A3/A4/A5 e qualquer fluxo que precise de local cedo ([fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) § contrato #2). + +## Objetivos + +- Etapa reutilizável (ex. `WizardMunicipalitySearchStep`) montada no **B59**: input debounce (mesmo espírito 250 ms de B47), lista de hits **apenas** `municipality`, escopo `overrideAccess: false`. +- Clique/Enter no hit → callback/`router.push` para o próximo passo **com município escolhido** (slug ou id na URL/query do fluxo). +- Sem botão “Continuar” / “Confirmar município”. +- Recentres / prioritários opcionais acima da busca (soft; cortável) — 3 recentes se `recentVisits` já existir no Início. +- Match: `matchesAtWordStart` / `normalizeSearchPhrase` (`lib/wordStartFilter.ts`). +- Sem migration / Consent. +- Se **B48** já tiver `searchHomeMunicipalities`, **filtrar/omitir TIs** e reusar o loader; senão, loader mínimo só de municípios (depth: não duplicar fuzzy). + +## Decisões travadas + +- **Só municípios** — TIs fora deste passo (wizard grava em `municipality`). **Rejeitado:** copiar B48 com TIs (não há `municipality` para um TI). +- **Select = auto-avanço.** **Rejeitado:** seleção + “Continuar” (pedido explícito; atrito no ritual). +- **Mesma linguagem visual da busca do Início** (linha sem card, tipografia). **Rejeitado:** `Command`/`Combobox` de lista (B27) como UI primária — é outro modelo mental. +- **i18n:** `WizardMunicipalitySearch`, copy “Em qual município?”. + +## Questões em aberto + +- **Mostrar votos 2022 à direita no wizard?** **Opções:** A sim (paridade B48) | B só nome+TI. **Recomendação:** B no v1 se B48 ainda não landou; A quando o readout existir e não atrasar o appetite. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Shell["B59 Shell"] --> Input["input debounce"] + Input --> Loader["searchMunicipalitiesScoped"] + Loader --> Hits["lista só município"] + Hits -->|select| Next["próximo passo + slug"] +``` + +Componentes: + +- **`WizardMunicipalitySearchStep`** (client island): input + results; registra seleção. +- **Loader** — reuso B48 ou `utilities/municipality/` com `overrideAccess: false`. +- Reuso: `useHomeSearchQuery` pattern / debounce B47; **não** importar o chrome focado do Início inteiro. +- **Migration:** Sem migration. + +## Dependências + +- Dura: **B59**. Soft: **B48** (UX/loader), **B47** ✓ (debounce/modo focado como referência). + +## Não escopo + +- Grupos lideranças/assessores/… → B49–B53 (só Início). Ajuste de votos → **B61**. Chassis → **B59**. + +## Rabbit holes + +- **Um único componente de busca para Início e wizard.** **Mitigação:** compartilhar loader + row renderer; chrome (grupos vs só município, onSelect vs Link) fica nos wrappers — extrair shared só com 2 call sites reais medidos. + +## Adiado com gatilho + +- **Chips de recentes/prioritários acima do input.** Revisitar se o CG pedir atalho sem digitar (sessão). + +## Referências + +- [busca-global-inicio-input.md](busca-global-inicio-input.md) · [busca-global-resultados-municipios.md](busca-global-resultados-municipios.md) · [chassis-wizard-campanha.md](chassis-wizard-campanha.md) · `lib/wordStartFilter.ts` · `recentVisits` utilities +- AGENTS.md — access advisor; `overrideAccess: false` +- `PRODUCT.md` / `DESIGN.md` diff --git a/docs/plans/chassis-wizard-campanha.md b/docs/plans/chassis-wizard-campanha.md new file mode 100644 index 00000000..e577451b --- /dev/null +++ b/docs/plans/chassis-wizard-campanha.md @@ -0,0 +1,95 @@ +# Chassis do wizard de campanha (shell + voltar) + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B59 — UX-1 wizards) +Impeccable: C — UI nova (shell de fluxo multi-passo sob `/campanha`) +Appetite: ~0,75 dia eng; layout de passo + header mobile + slot de conteúdo; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (Clarity under pressure; Feel the action; anti spreadsheet) / `DESIGN.md` · tema `data-theme='campaign'` · shells `CampaignPageShell` onde couber. + +Na implementação (`implement-roadmap-item`): shape → craft → critique → polish. + +Brief compacto: + +- **Persona / contexto:** CG/assessor no celular entre Zaps; um passo por vez, sem chrome de lista. +- **Job principal:** enquadrar qualquer etapa de wizard (busca, votos, sinal…) com volta óbvia no mobile e conteúdo focável. +- **Estratégia de cor:** Restrained. +- **Edit where you see:** não — fluxo linear; escrita nas etapas filhas. +- **Anti-goals:** sidebar/nav do app competindo com o passo; seta “voltar” no desktop (lá é browser); segundo design system de wizard. + +## Dados → decisão → apresentação + +Dados: N/A — chrome de navegação; conteúdo numérico/textual nas etapas B60–B63. + +## Contexto + +Chassis UX-1 do Início (**B43–B47** ✓) deixa ações inertes até existir **wizard**. O rascunho [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) pede “uma coluna / um passo visível” e interrupção recuperável. Pedido de produto (2026-07-29): **em mobile**, seta de retorno no topo esquerdo de toda etapa; **em desktop/tablet**, expectativa = botão voltar do browser (sem duplicar chrome). + +Este item é o **frame** compartilhado — não a busca, nem o ajuste de votos, nem o sinal. + +## Objetivos + +- Componente shell (nome final no PR, ex. `CampaignWizardShell`) com: título/pergunta do passo, slot `children`, slot opcional de ação no topo direito (ex. “Pular…” do B63), **back control só em viewport mobile** (`md:hidden` ou equivalente) que navega ao passo anterior / Início conforme o fluxo. +- Desktop: sem seta; documentar que `history.back` / rota anterior cobre o gesto. +- Layout: uma coluna; conteúdo alinhado ao bottom em mobile quando a etapa pedir (B63 tipifica), top em `md+` — o shell expõe um prop de alinhamento (`contentAlign: 'start' | 'end'`) sem hardcodar o grid de sinais. +- Rotas sob `/campanha/…` (recomendação UX-1 travada: rota, não Sheet — sobrevive a refresh/Zap). Este item define o layout e o header; as rotas concretas nascem com B60/B61/B63 ou com o wiring A1. +- Sem migration / Consent / action de escrita. +- a11y: seta com nome acessível contendo o verbo visível (“Voltar”); foco gerenciado ao trocar de passo (mínimo: foco no heading do passo). + +## Decisões travadas + +- **Voltar mobile = chrome do wizard; desktop = browser.** **Rejeitado:** seta também em `md+` (ruído; duplica gesto nativo); só `Link` “Cancelar” no rodapé (longe do polegar no topo). +- **Wizard = rota**, não Drawer sobre o Início. **Rejeitado:** Sheet full-screen (refresh/Zap perde contexto; URL não compartilha passo). Fonte: gate UX-1 2026-07-29 + rascunho. +- **Shell não grava estado de domínio** — só navegação/chrome. Estado de município/votos/sinal fica nas etapas ou num store mínimo do fluxo (Adiado com gatilho se 3º wizard pedir). +- **i18n:** ids em inglês (`WizardShell`, `onBack`, `trailingAction`); copy pt-BR. + +## Questões em aberto + +- **Path canônico (`/campanha/acoes/…` vs `/campanha/wizards/…`)?** **Opções:** A `acoes` | B `wizards`. **Recomendação:** A — linguagem de mesa (“ação”), alinhada ao catálogo B45. _(assumido — validar no 1º wizard que montar rota)_ + +## Abordagem proposta + +```mermaid +flowchart TB + Home["/campanha Início"] --> Route["rota de passo"] + Route --> Shell["CampaignWizardShell"] + Shell --> Back["Voltar md:hidden"] + Shell --> Trail["trailingAction?"] + Shell --> Body["children = etapa"] +``` + +Componentes: + +- **`CampaignWizardShell`** em `src/components/campaign/shared/` (ou `shell/`): header + main; reusa tokens/`Button` ghost para a seta (`ArrowLeft`). +- **Hook/helper de passo** (opcional neste item): `useWizardBack({ previousHref })` — se <2 call sites, inline no shell. +- **Migration:** Sem migration, sem collection, sem server action. + +## Dependências + +- Dura: **B45** ✓ (ações existem; wizards ainda inertes). Soft: B46 ✓ (thumb zone — alinhamento de conteúdo). +- Desbloqueia **B60**, **B61**, **B63**. + +## Não escopo + +- Busca de município → **B60**. Ajuste de votos → **B61**. Modelo/UI de sinal → **B62**/ **B63**. +- Resumo final + CTA “Registrar atualização” do A1 → wiring do wizard A1 (após estas etapas). +- Rascunho persistente cross-session → Adiado. + +## Rabbit holes + +- **State machine genérica de wizard (XState / multi-route orchestrator).** **Mitigação:** shell burro + hrefs/query de passo por fluxo; extrair só no 3º wizard. +- **App chrome (`CampaignSidebar`) dentro do wizard.** **Mitigação:** layout de `(app)` pode permanecer; visualmente o conteúdo é full-bleed de passo — não inventar segundo layout group sem medição. + +## Adiado com gatilho + +- **Persistência de rascunho (município + ação) ao reabrir.** Revisitar quando a 1ª sessão real for interrompida por Zap e o CG reclamar (UX-1 § contrato #8). + +## Referências + +- [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) · [catalogo-acoes-inicio-por-persona.md](catalogo-acoes-inicio-por-persona.md) · `CampaignPageShell` · `PRODUCT.md` Clarity under pressure +- AGENTS.md — naming; campanha auth; sem Consent novo +- `PRODUCT.md` / `DESIGN.md` — Field Desk / tokens campaign diff --git a/docs/plans/fluxos-acao-primeiro-inicio.md b/docs/plans/fluxos-acao-primeiro-inicio.md index bc2fc86b..6e76415f 100644 --- a/docs/plans/fluxos-acao-primeiro-inicio.md +++ b/docs/plans/fluxos-acao-primeiro-inicio.md @@ -80,16 +80,16 @@ Copy: zero “broker”, “campo sem captura”, “LQ” nos passos. Se precis **Abre:** botão Início · ou sugestão E11 “atualizar projeção” · ou deep-link. -| Passo | Pergunta | UI | Escrita (já existe / quase) | -| ----- | ----------------------------------- | ---------------------------------------------- | ------------------------------------------------ | -| 1 | Em qual município? | Busca + 3 recentes / prioritários | — | -| 2 | Qual a nova estimativa **média**? | Número; mostra valor atual | `expectedVotes.central` | -| 3 | E o **pessimista**? | Número; default = atual ou coerência com média | `expectedVotes.pessimistic` | -| 4 | E o **otimista**? | Número | `expectedVotes.optimistic` | -| 5 | Quer registrar **o que aconteceu**? | Sim → mini A2 embutido / Não | `municipalityUpdate` sinal | -| 6 | Quer mudar a **tendência**? | Sim → A3 embutido / Não | `politicalTrend` + nota | -| 7 | Quer ajustar **liderança** agora? | Sim → A4 embutido (curto) / Não | leadership / pledge | -| 8 | Resumo → **Registrar atualização** | CTA único | transação ou sequência das actions já existentes | +| Passo | Pergunta | UI | Escrita (já existe / quase) | +| ----- | ----------------------------------- | ------------------------------------------------------------------------------- | --------------------------- | +| 1 | Em qual município? | Busca só municípios (UX Início, sem grupos); select → avança (**B60**) | — | +| 2 | Qual a nova estimativa **média**? | Input grande pré-preenchido + atalhos 2× / ±50 / ±100; CTA “Ajustar…” (**B61**) | `expectedVotes.central` | +| 3 | E o **pessimista**? | Idem; se quebrar ordem vs média → jump + warning + atalho de volta (**B61**) | `expectedVotes.pessimistic` | +| 4 | E o **otimista**? | Idem + coerência vs anteriores (**B61**) | `expectedVotes.optimistic` | +| 5 | Quer registrar **o que aconteceu**? | Sim → mini A2 (**B63**) / Não | `municipalityUpdate` sinal | +| 6 | Quer mudar a **tendência**? | Sim → A3 embutido / Não | `politicalTrend` + nota | +| 7 | Quer ajustar **liderança** agora? | Sim → A4 embutido (curto) / Não | leadership / pledge | +| 8 | Resumo → **Registrar atualização** | CTA único | actions existentes | **Corte 03/08 se apertar:** passos 1–4 + 8; 5–7 como “Quer também?” com um campo cada, sem sub-wizard profundo. @@ -99,17 +99,17 @@ Copy: zero “broker”, “campo sem captura”, “LQ” nos passos. Se precis ## Fluxo A2 — Registrar o que aconteceu (sinal) -| Passo | Pergunta | UI | -| ----- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| 1 | Em qual município? | Busca (pular se veio de A1) | -| 2 | O que aconteceu? (título curto) | Texto — “Vereador foi apoiar outro candidato” | -| 3 | Que tipo? | Lista curta em linguagem de mesa: Invasão · Perda de apoio · Novo apoio · Dificuldade · Outro (mapear para `kind`/enum existente) | -| 4 | Quem contou? (fonte) | Texto livre | -| 5 | Confirmou com outra pessoa? | Sim / Não (triangulação) | -| 6 | Urgente? | Sim / Não | -| 7 | Resumo → **Registrar** | | +**Modelo (B62, 2026-07-29):** só `signalType` + texto (`body`). **Sem** fonte nem triangulado. Rótulos de mesa (enum values estáveis): Invasão · Rede esfriou · Adversário apareceu · Alguém pediu algo · Outro. -Opcional ao fim: “Isso muda a projeção de votos?” → entra em A1 passo 2. +| Passo | Pergunta | UI | +| ----- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | Em qual município? | Busca só municípios, select → avança (**B60**; pular se veio de A1) | +| 2 | Que tipo? | Grid de tiles grandes + Drawer de info por tipo (**B63**); “Pular registro de sinal” no topo direito **só se** a origem ≠ ação `register-signal` | +| 3 | Detalhe? | Texto livre; Skip (mesma regra); **Salvar** inferior direito (**B63**) | + +Chrome de toda etapa: seta **Voltar** no topo esquerdo **só mobile**; desktop = browser (**B59**). + +Opcional ao fim: “Isso muda a projeção de votos?” → entra em A1 / **B61**. --- @@ -214,16 +214,17 @@ Não competir com os botões. 1. **B44** Botão circular + strip horizontal — [botao-acao-inicio-strip.md](botao-acao-inicio-strip.md) 2. **B45** Catálogo por persona + botões (atalhos reais; wizards ainda inertes) — [catalogo-acoes-inicio-por-persona.md](catalogo-acoes-inicio-por-persona.md) -### Wizards (ainda planejamento — IDs depois de B45) +### Wizards (etapas compartilhadas registradas 2026-07-29) -3. **Chassis do wizard** (shell de passos + busca de município + CTA final + resumo) -4. **A1** (votos) com “quer também?” sinal + tendência -5. **A6** — atalho já parcialmente em B45 (`uncovered-municipalities`); tabela above-the-fold na lista se ainda faltar -6. **A2** standalone (se não ficou bom só embutido) -7. **A4** curto (status / observação / quem coordena) -8. **A5** + edição de demanda -9. Ligar E11 → wizards -10. Copy pass + A3 standalone se ainda fizer falta +3. **B59** Chassis (shell + voltar mobile) — [chassis-wizard-campanha.md](chassis-wizard-campanha.md) +4. **B60** Busca município (só municípios; select → avança) — [busca-municipio-wizard.md](busca-municipio-wizard.md) +5. **B61** Ajuste votos (média→pessimista→otimista + atalhos + coerência) — [ajuste-votos-wizard.md](ajuste-votos-wizard.md) +6. **B62** Simplificar modelo de sinal (tipo+texto; labels mesa) — [simplificar-modelo-sinal.md](simplificar-modelo-sinal.md) +7. **B63** Wizard sinal (grid tipo + texto + pular) — [wizard-registro-sinal.md](wizard-registro-sinal.md) +8. **A1** wiring (orquestrar B60+B61 + “quer também?” sinal/tendência + resumo/CTA) +9. **A6** — atalho já em B45 (`uncovered-municipalities`) +10. **A2** standalone = B60+B63 (se não ficou bom só embutido em A1) +11. **A4** curto · **A5** + edição de demanda · E11 → wizards · A3 standalone --- diff --git a/docs/plans/simplificar-modelo-sinal.md b/docs/plans/simplificar-modelo-sinal.md new file mode 100644 index 00000000..c403530f --- /dev/null +++ b/docs/plans/simplificar-modelo-sinal.md @@ -0,0 +1,90 @@ +# Simplificar modelo de sinal (tipo + texto; sem fonte/triangulado) + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B62 — UX-1 / C12 sinais) +Impeccable: B — schema + forms existentes (`MunicipalitySignalFields`, B26, admin); UI nova de escolha = **B63** +Appetite: ~0,75–1 dia eng; migration drop + zod/collection/UI lista/detail; rename de labels; ajuste E11 +Responsável: — + +## Dados → decisão → apresentação + +Dados: N/A neste item como superfície nova — remove campos; labels novas alimentam **B63** e B26. + +## Contexto + +C12 ✓ tipou `municipalityUpdate` `kind: 'sinal'` com `signalType`, `signalSource` (obrigatório) e `triangulated`. B26 ✓ e `MunicipalitySignalFields` expõem os três. Pedido de produto (2026-07-29, gate confirmado): **sinais = só tipo + texto**; dropar Fonte e Triangulado; **renomear rótulos** para militantes PT ~60 anos (sem “broker”). + +E11 ✓ lê `triangulated` em sinais de adversário (`municipalityTriggers.ts` / `suggestionCatalog`) — ao dropar, o motor trata presença do sinal como o ramo “fonte única” (ou só “há sinal”), sem segunda dimensão. + +## Objetivos + +- Migration: remover colunas `signal_source` e `triangulated` de `municipality_update` (e enums/admin conforme snapshot Payload); `pnpm migrate:create simplify_municipality_signal_fields`. +- Zod `municipalityUpdateCreateSchema`: para `kind: 'sinal'`, exigir `signalType` + `body`; **não** exigir `signalSource`; remover `triangulated`. +- Collection `MunicipalityUpdate`: dropar fields; ajustar `beforeValidate` que hoje exige fonte. +- Labels/descriptions (enum **values** preservados — sem rewrite de rows): + + | value | label | description curta | + | ------------------- | ------------------- | ----------------------------------------- | + | `invasao` | Invasão | Adversário ocupando nosso espaço | + | `esfriamento` | Rede esfriou | Aliados pararam de responder / cairam | + | `visita_adversario` | Adversário apareceu | Visita ou agenda dele no município | + | `proposta_broker` | Alguém pediu algo | Liderança/intermediário pediu ou ofereceu | + | `outro` | Outro | Fato importante que não encaixa acima | + +- Atualizar `MunicipalitySignalFields`, B26 form, detail/dossier readouts, int tests, seeds se houver. +- E11: remover branch `triangulated` do input de sugestão; copy do catálogo que cita “triangulado” / “fonte única” no eixo de adversário → presença do sinal basta (recalibrar texto mínimo; **não** reabrir E15). +- Sem Consent novo. Sem UI de grid do wizard (isso é **B63**). + +## Decisões travadas + +- **Modelo = `signalType` + `body` apenas.** **Rejeitado:** manter fonte/triangulado opcionais “por se acaso” (pedido: simplificar; campos mortos confundem mesa). +- **Enum values estáveis; só labels/descriptions mudam.** **Rejeitado:** renomear `proposta_broker` → `proposta_intermediario` neste slice (migration de dados + grep em E11/tests sem ganho de UX). Gatilho: se copy interna/código ainda vazar “broker” em string de usuário após labels — aí rename. +- **E11 sem dimensão triangulado.** **Rejeitado:** inventar proxy (“texto longo = triangulado”); manter coluna só para o motor. +- **i18n:** ids `invasao`…; copy pt-BR nova. + +## Questões em aberto + +- **Textos longos do drawer de info (B63) — quem redige?** **Opções:** A eng com copy provisória no plano B63 | B assessoria. **Recomendação:** A no ship (o quê / consequências / quando usar em 3–5 frases); B revisa no R6. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Mig["migration DROP fonte/triangulated"] --> Zod["zod + collection"] + Zod --> UI["SignalFields + B26"] + Zod --> E11["triggers/catalog"] +``` + +Componentes: + +- Migration + `MunicipalityUpdate.ts` + `municipalityUpdate.ts` schema. +- `MunicipalitySignalFields.tsx` — select de tipo + textarea; sem checkbox/fonte. +- `municipalityTriggers.ts` / `suggestionCatalog.ts` — dropar `triangulated` do tipo de input. +- View models de update/dossier — parar de exibir fonte/triangulado. +- **Migration:** sim (drop columns; sem backfill necessário além de drop). + +## Dependências + +- Dura: **C12** ✓. Soft: B26 ✓ (superfície a atualizar). Desbloqueia **B63**. + +## Não escopo + +- Grid de botões + drawer de info + “Pular” → **B63**. +- Novos tipos de sinal (Perda de apoio / Novo apoio do rascunho UX-1) — **não** expandir taxonomia neste item; só renomear os 5 existentes. Gatilho: sessão pedir tipo que não cabe em `outro`. + +## Rabbit holes + +- **Reescrever taxonomia inteira + migração de valores históricos.** **Mitigação:** labels only. +- **Unificar `kind` urgente/nota com sinal.** **Mitigação:** fora — C12 já separou. + +## Adiado com gatilho + +- **Rename enum `proposta_broker`.** Revisitar se `broker` ainda aparecer em UI/admin após labels (grep pt-BR + admin). +- **Novos signalType values.** Revisitar com evidência de mesa (tipos que caem sempre em `outro`). + +## Referências + +- [registro-fundacao.md](registro-fundacao.md) · [registrar-sinal-lista-municipios.md](registrar-sinal-lista-municipios.md) · [motor-de-sugestoes.md](motor-de-sugestoes.md) · `src/lib/schemas/municipalityUpdate.ts` · `src/collections/MunicipalityUpdate.ts` · `MunicipalitySignalFields.tsx` · `municipalityTriggers.ts` +- AGENTS.md — migrations; `overrideAccess: false` +- Impeccable: B — craft compacto nos forms existentes diff --git a/docs/plans/wizard-registro-sinal.md b/docs/plans/wizard-registro-sinal.md new file mode 100644 index 00000000..e654148e --- /dev/null +++ b/docs/plans/wizard-registro-sinal.md @@ -0,0 +1,113 @@ +# Wizard registrar sinal — escolher tipo + detalhar texto + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B63 — UX-1 wizards) +Impeccable: C — UI nova (grid de tipos + drawer de info + passo de texto) +Appetite: ~1–1,25 dia eng; 2 etapas no B59; write via action `createMunicipalityUpdate` existente +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` / `DESIGN.md` · `Drawer` shadcn · tema `campaign` · labels de **B62**. + +Na implementação: shape → craft → critique → polish. + +Brief compacto: + +- **Persona:** CG/assessor no polegar; acabou de ouvir no Zap e precisa classificar o fato sem jargão. +- **Job principal:** escolher o tipo de sinal em 1 toque (com info sob demanda) e, se quiser, detalhar em texto; ou pular quando o sinal não era o motivo do wizard. +- **Estratégia de cor:** Restrained — fundo transparente, borda cinza clara (`border-border`), título/ícone `foreground`; sem fill primary nos tiles. +- **Edit where you see:** não — fluxo com Salvar explícito no passo de texto. +- **Anti-goals:** select nativo escondido; “broker”; obrigar sinal quando o fluxo veio de votos; cards com sombra/elevação. + +## Dados → decisão → apresentação + +Dados: N/A como métrica — tipo categórico + texto livre. Decisão = classificar o fato e se registrar agora ou pular. + +## Contexto + +Após **B62**, o modelo é `signalType` + `body`. Pedido (2026-07-29, gate confirmado): + +**Etapa 1 — tipo** + +- Cada tipo = botão **quadrado grande**, cantos arredondados; ícone canto **superior esquerdo**; título + descrição curta alinhados ao **inferior esquerdo**; fundo transparente + borda cinza clara; título/ícone pretos (`foreground`). +- Ícone de **informação** no tile → bottom **Drawer** com: o que é, consequências, quando usar (copy longa; provisória ok). +- Grid; alinhamento do bloco: **bottom no mobile**, **top em tablet/desktop** (prop `contentAlign` do **B59**). +- Topo direito: **“Pular registro de sinal”** — **exceto** quando a ação que abriu o wizard foi `register-signal` (B45). + +**Etapa 2 — texto** + +- Texto livre para detalhar. +- “Pular registro de sinal” ainda no topo direito (mesma regra de origem). +- Inferior direito: **“Salvar”** → cria `municipalityUpdate` `kind: 'sinal'`. + +Município: já escolhido (**B60**) ou pré-preenchido se veio embutido de A1. + +## Objetivos + +- Duas telas/passos no shell B59: `WizardSignalTypeStep` + `WizardSignalBodyStep`. +- Catálogo de tiles: mapear os 5 `municipalitySignalTypes` → ícone Lucide + label/description curta (B62) + `infoContent` (markdown/plain). +- Info: botão `Info` com `stopPropagation` para não selecionar o tipo ao abrir o Drawer. +- Select de tipo = avança para o passo de texto (sem CTA intermediário). +- Skip: `Link`/button → próximo bloco do fluxo pai ou Início; **hidden** se `entryAction === 'register-signal'`. +- Salvar: `createMunicipalityUpdate` / formAction existente com `overrideAccess: false`; pending no botão (Feel the action). +- Atualizar B26 list control **não** é obrigatório a adotar o grid (pode permanecer select pós-B62); opcional Adiado. +- Sem migration (B62 já migrou). Sem Consent. +- Unit/e2e smoke: skip oculto no entry signal; info não seleciona tipo; salvar exige tipo+body. + +## Decisões travadas + +- **Duas etapas (tipo → texto), um item de roadmap.** **Rejeitado:** IDs separados (texto é fino demais sozinho). +- **Skip visível só se a origem ≠ `register-signal`.** **Rejeitado:** sempre mostrar (permitiria “registrar sinal” sem sinal); nunca mostrar (bloqueia “quer também?” após votos). +- **Tiles transparentes + borda**, não filled primary. **Rejeitado:** cards elevados / Signal Red no idle. +- **Info = Drawer bottom** (pedido). **Rejeitado:** Tooltip only (touch); Dialog centrado. +- **i18n:** `signalType`, `entryAction`, `skipSignal`; copy “Pular registro de sinal” / “Salvar”. + +## Questões em aberto + +- **Após Salvar no fluxo standalone A2: ir ao Início ou oferecer “ajustar votos”?** **Opções:** A Início + toast | B “Quer atualizar votos?”. **Recomendação:** A no v1; B no wiring A1/A2 contínuo. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart TB + Entry["origem: register-signal | embutido"] --> Type["grid tipos"] + Type -->|info| Drawer["Drawer detalhes"] + Type -->|select| Body["textarea"] + Type & Body -->|skip se permitido| Skip["próximo / Início"] + Body -->|Salvar| Action["createMunicipalityUpdate"] +``` + +Componentes: + +- **`WizardSignalTypeStep`** + **`WizardSignalBodyStep`** em `components/campaign/municipality/` (ou `shared/` se 2º call site). +- **`municipalitySignalTypeMeta`** em `lib/` (ícone id, short description, long info) — client-safe; labels de B62. +- Reuso: `Drawer`, `Button`, B59 trailing slot + `contentAlign="end"` no mobile da etapa de tipo. +- **Migration:** Sem migration (depende de B62). + +## Dependências + +- Dura: **B59**, **B62**. Soft: **B60** (município no standalone). Soft: B45 ✓ (`register-signal` id). + +## Não escopo + +- Dropar fonte/triangulado / labels → **B62** (pré-requisito). +- Wiring “quer também?” dentro de A1 → fatia A1. +- Trocar B26 para o grid de tiles → Adiado. + +## Rabbit holes + +- **CMS dos textos de info.** **Mitigação:** constantes em `lib/` até R6/assessoria. +- **Mais de 5 tipos / multi-select.** **Mitigação:** um tipo por sinal (C12). + +## Adiado com gatilho + +- **Grid de tipos no Popover B26.** Revisitar se o select pós-B62 ainda gerar dúvida de classificação em campo. +- **Copy longa revisada pela assessoria.** Revisitar no R6. + +## Referências + +- [simplificar-modelo-sinal.md](simplificar-modelo-sinal.md) · [chassis-wizard-campanha.md](chassis-wizard-campanha.md) · [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) · [registrar-sinal-lista-municipios.md](registrar-sinal-lista-municipios.md) · `actions/municipalityUpdate` · `campaignHomeActions.ts` (`register-signal`) +- AGENTS.md — Local API `overrideAccess: false` +- `PRODUCT.md` / `DESIGN.md` · Drawer B42/B34 diff --git a/docs/roadmap.md b/docs/roadmap.md index 10de82ea..e90c15da 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-29 — B47 ✓ (busca global input + modo focado no Início); fill-in Cenário junto aos filtros (municípios) ✓; B46 ✓; B58 registrado; B48–B55 resultados busca. +Atualizado em: 2026-07-29 — B59–B63 registrados (chassis wizard + busca município + ajuste votos + simplificar sinal + wizard sinal); B47 ✓; B58; B48–B55. Registro canônico dos **próximos** planos e débitos. Histórico de entregas: resumo abaixo + planos em [`docs/plans/`](plans/) + notebook [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc). ## Âncoras do calendário eleitoral 2026 (Res. TSE 23.760/2026) @@ -83,11 +83,16 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re ### Demais itens abertos -- **UX-1 fluxos ação-primeiro (prioridade pós-sessão 29/07)** — evidência: [snapshot](plans/sessao-observada-coordenador-2026-07-29-snapshot.md) · O9–O11 em CUSTOMER.md · **rascunho:** [fluxos-acao-primeiro-inicio.md](plans/fluxos-acao-primeiro-inicio.md). Soft deadline: **03/08**. **Chassis:** ~~**B43**~~ ✓ → ~~**B44**~~ ✓ → ~~**B45**~~ ✓ → ~~**B46**~~ ✓ → ~~**B47**~~ ✓ → **B58** (polimento strip) → **B48–B55** (resultados busca) · **B56** (resumo total+cobertura no topo do Início); **B57** delta 7d = futuro/baixa prioridade; wizards com escrita = fatias seguintes. +- **UX-1 fluxos ação-primeiro (prioridade pós-sessão 29/07)** — evidência: [snapshot](plans/sessao-observada-coordenador-2026-07-29-snapshot.md) · O9–O11 em CUSTOMER.md · **rascunho:** [fluxos-acao-primeiro-inicio.md](plans/fluxos-acao-primeiro-inicio.md). Soft deadline: **03/08**. **Chassis Início:** ~~**B43**~~ ✓ → ~~**B44**~~ ✓ → ~~**B45**~~ ✓ → ~~**B46**~~ ✓ → ~~**B47**~~ ✓ → **B58** (polimento strip) → **B48–B55** (resultados busca) · **B56** (resumo) · **B57** delta 7d = futuro. **Wizards (etapas):** **B59** chassis → **B60** busca município · **B61** ajuste votos · **B62** simplificar sinal → **B63** wizard sinal; wiring A1/A2 depois. - ~~**B43** Início em branco + Quadro~~ — **entregue 2026-07-29** (`/campanha` blank; staff dashboard em `/campanha/quadro`; liderança em `/campanha/contatos`; nav Início+Quadro / Início+Meus contatos; `requireCampaignPageActor` manda leader bloqueado → `/campanha/contatos`; sem migration) · [plano](plans/inicio-em-branco-quadro.md) - ~~**B44** Botão de ação do Início + lista horizontal~~ — **entregue 2026-07-29** (`CampaignHomeActionButton` + `CampaignHomeActionStrip`; Tooltip fine / long-press Drawer coarse; montado no Início por B45 ✓) · ~1d · Janela 1 · Impeccable C · [plano](plans/botao-acao-inicio-strip.md) - ~~**B45** Catálogo de ações por persona~~ — **entregue 2026-07-29** (`src/lib/campaignHomeActions.ts` + `CampaignHomeActions`; staff 6 ações com `uncovered-municipalities` → `?coverage=sem_assessor&sort=votos`; leader 2 com `my-contacts` → `/campanha/contatos`; demais inertes; ilha client por limite RSC↔ícones Lucide) · [plano](plans/catalogo-acoes-inicio-por-persona.md) - **B58** Polimento visual da strip de ações do Início — remover heading "O que você quer fazer?"; rótulos curtos (Ajustar votos, Registrar sinal, …; atalho → **"Ver esquecidos"** _(proposto — validar com produto)_); scale discreto no círculo (hover/long-press) + opacity no active; scrollbar horizontal oculta (pan preservado) · Depende de B45 ✓ · ~0,5d · Janela 1 · Paralelizável com ~~B46~~ ✓ · Impeccable B · [plano](plans/polimento-strip-acoes-inicio.md) +- **B59** Chassis do wizard (shell de passo + seta Voltar só mobile; desktop = browser) · Depende de B45 ✓ · ~0,75d · Janela 1 · Impeccable C · [plano](plans/chassis-wizard-campanha.md) +- **B60** Etapa busca município nos wizards (UX da busca do Início, só municípios; select → avança) · Depende de B59 · soft B48 · ~1d · Janela 1 · Impeccable B · [plano](plans/busca-municipio-wizard.md) +- **B61** Etapa ajuste de votos (média→pessimista→otimista; atalhos 2×/±50/±100; coerência com jump+warning) · Depende de B59 · soft B60 · ~1–1,25d · Janela 1 · Impeccable C · [plano](plans/ajuste-votos-wizard.md) +- **B62** Simplificar modelo de sinal (dropar Fonte/Triangulado; labels mesa sem “broker”; migration + E11) · Depende de C12 ✓ · ~0,75–1d · Janela 1 · Impeccable B · [plano](plans/simplificar-modelo-sinal.md) +- **B63** Wizard registrar sinal (grid de tipos + Drawer info + texto + “Pular” se origem ≠ register-signal) · Depende de B59 + B62 · soft B60 · ~1–1,25d · Janela 1 · Impeccable C · [plano](plans/wizard-registro-sinal.md) - ~~**B46** Posição dos botões de ação (thumb zone)~~ — **entregue 2026-07-29** (`CampaignHomeLayout`: coluna `min-h-full` + spacer mobile; strip em `data-slot="home-actions"` na metade inferior do viewport; `md+` no topo; slot `searchSlot` + `data-slot="home-search"` para B47; sem migration) · [plano](plans/posicao-botoes-acao-inicio-thumb-zone.md) - ~~**B47** Busca global — input (debounce, sem botão; modo focado: sobe e esconde o resto)~~ — **entregue 2026-07-29** (`CampaignHomeStaffChrome` + `CampaignHomeSearch` + `useHomeSearchQuery`; debounce 250 ms; query ativa ≥2 chars; `HomeSearchContext` para B48+; staff-only via `isStaffCampaignRole`; Escape limpa; região `aria-live` sem hits ainda; sem migration) · [plano](plans/busca-global-inicio-input.md) - **B48** Busca — resultados Municípios (+ TIs no mesmo grupo; flag de prioridade alinhada; “2022” / agregado à direita; sem card) · Depende de B47 · ~1–1,5d · Janela 1 · Impeccable B · [plano](plans/busca-global-resultados-municipios.md) @@ -216,6 +221,11 @@ flowchart TD B55n["B55 Busca ações
WA / share"] B56n["B56 Resumo campanha
total + cobertura"] B57n["B57 Delta 7d estimativa
(futuro / baixa prio)"] + B59n["B59 Chassis wizard
shell + voltar mobile"] + B60n["B60 Busca município
nos wizards"] + B61n["B61 Ajuste votos
cenários + atalhos"] + B62n["B62 Simplificar
modelo de sinal"] + B63n["B63 Wizard sinal
tipo + texto"] B15n["B15 ✓ Ordenar lista
por coluna"] B16n["B16 ✓ Filtros no header
da lista"] B17n["B17 ✓ Seletor de colunas
das 7 listas"] @@ -300,6 +310,16 @@ flowchart TD B46n -.thumb-zone layout.-> B56n B47n -.some no modo focado.-> B56n B56n --> B57n + B45n --> B59n + B59n --> B60n + B59n --> B61n + B59n --> B63n + B48n -.UX/loader.-> B60n + B60n -.município.-> B61n + B60n -.município standalone.-> B63n + C12i --> B62n + B62n --> B63n + B26n -.forms a alinhar.-> B62n B16n -.densidade do th.-> B22n E18n -.oneLiner dos conceitos.-> B22n B22n -.colunas se explicam.-> B17n @@ -391,11 +411,11 @@ flowchart TD B8F2 -.rank por ZE = débito aberto.-> B14n ``` -Paralelizáveis agora: **B58** (polimento strip de ações); **B47** (próximo do chassis UX-1 — desbloqueia B48–B55); **B56** (resumo Início — soft B47, não bloqueia busca); após **B47**, **B49–B53** em paralelo entre si (soft ordem visual sob B48). **B57** não é paralelizável agora (depende de B56 e está de propósito no futuro). Demais já listados: ~~**B43**~~ ✓ (chassis UX-1 — entregue 2026-07-29), ~~**B42**~~ ✓ (polimento mobile da mesma lista — entregue 2026-07-27), **B21** (página dos TIs; sem dep dura — E17 ✓/B15 ✓/B16 ✓ já entregues; E12 pousa nela depois; **B25** é o único item que espera B21 — o link do território na lista só existe depois do destino), ~~**B17**~~ ✓ (seletor de colunas; soft B15 ✓/B16 ✓ — pousa no header de coluna atual), ~~**B18**~~ ✓ (filtros salvos + submenu Municípios; soft B16 ✓/B17 — botão na barra), **B22** (explicação por coluna no header; sem dep dura — B15 ✓/B16 ✓/E18 ✓), **B23** (tooltip nas células; a mecânica genérica e a promoção do primitivo já saíram no E10 ✓ — restam os consumidores Assessores/Tendência e a convivência com o Popover), ~~**B24**~~ ✓ (auto-save + justificativa no popover de Tendência; entregue 2026-07-26), **B26** (registrar sinal na célula "Último sinal"; sem dep dura — E9 ✓/C12 ✓ já entregues; soft B24/B23 só pela coerência de contrato na mesma linha), **B27** (combobox + chips + auto-save no popover de Assessores; sem dep dura — B19 ✓ já deu o delta+lock e o padrão de chips; soft B24 pelo endpoint e B23 pelo gatilho), **B28** (e-mail + celular na lista de lideranças; sem dep dura — B19 ✓ é só o padrão soft de copy/WhatsApp; `phone` já está no view model), **B29** (sort + filtro no header da lista de lideranças; sem dep dura — B15 ✓/B16 ✓ deram o contrato e o popover; soft B21 e B28 só pela ordem de chegada: quem entrar depois herda o head compartilhado / encaixa as colunas do outro), fill-ins (renomear coluna Cobertura → quick win de densidade no header; ~~remover coluna Tipo~~ ✓; ~~remover lista de zonas do Início~~ ✓; ~~ícone de prioridade na lista~~ ✓; ~~Cenário junto aos filtros~~ ✓ — encaixa na barra slim de B16 ✓; ~~selecionados no topo nos filtros do header~~ ✓ — helper puro que B21 reusa —; O0+, RS+). **B30** (convite WhatsApp na lista de lideranças; sem dep dura — reusa integralmente a action/collection existentes; soft B28 só pelo ícone na mesma linha, qualquer um pode ir primeiro — **entregue 2026-07-26**), **B31** (coluna de dobradinhas na lista de lideranças; sem dep dura — o dado já está no view model e o catálogo já tem loader; soft B27 pela peça de célula, que só vira extração compartilhada no 3º call site, e B28/B29/B30 pela ordem de chegada na mesma tabela), ~~**B32**~~ ✓ (editar Status de apoio na célula da lista de lideranças; entregue 2026-07-26), **B33** (sort + filtro no header da lista de dobradinhas; sem dep dura — B15 ✓/B16 ✓ deram o contrato; soft B29/B21 só pela ordem de chegada na mesma corrida de extração do chrome compartilhado, quem entrar primeiro paga a peça e o outro só consome), **B34** (chips editáveis de municípios na lista de lideranças; sem dep dura — B19 ✓ já deu o delta+lock, o padrão de chips e o loader do catálogo a promover; soft B27/B31 pela mesma peça de célula editável — quem chegar depois herda o combobox/Popover em vez de reinventar —, e B28/B30/B32 só pela mesma tabela; _decisão a validar com produto_: Popover por célula em vez do toggle "Editar" pedido literalmente, mesmo precedente do B31/B32), **B37** (chips editáveis de municípios na lista de dobradinhas; sem dep dura — B19 ✓/B34 já dão a lib a promover e o desenho do Popover+combobox; soft B34 só pela ordem de chegada em quem faz a promoção da lib, B31 pela mesma família de pedido em outra tabela, B33 pela mesma tabela; _mesma decisão a validar com produto_ do B31/B32/B34: Popover por célula, não toggle). **B36** (coluna de lideranças na lista de dobradinhas; sem dep dura — reusa integralmente a mutação/schema/lock que o B31 desenha para a mesma relação, do lado oposto; soft B31 pela corrida de extração da célula/action compartilhadas, B19 ✓ pelo padrão de origem, B33 pela mesma tabela), **C13** (renomear Plano de Ação → Atividade; sem dep dura — não toca nenhuma das listas em curso (B26–B37 são municípios/lideranças/dobradinhas), então roda em paralelo a elas; a única ordem que importa é **antes de E13**, que constrói dentro da vertical renomeada, e antes do congelamento ~20/09 por carregar migration; PR mecânico e isolado, não misturar com feature). **D3** só após smoke + folga e só se o fluxo sede-digita (C12 ✓) não absorver os deltas do ZAP (não compete com E9). +Paralelizáveis agora: **B58** (polimento strip); **B59** (chassis wizard — desbloqueia B60/B61/B63); **B62** (simplificar sinal — paralelo a B59; desbloqueia B63); **B56** (resumo Início); **B48–B53** após B47 ✓ (soft ordem visual sob B48; depois B54/B55). Após **B59**: **B60** ∥ **B61**. **B63** espera B59+B62. **B57** futuro (depende B56). Fill-ins / débitos `*+` e itens já ✓ permanecem fora do caminho crítico UX-1. **D3** só após smoke + folga se UX-1 não absorver o Zap. ### Sequência por janela (só pendentes) -**Janela 1 — agora → 05/08 (convenções):** **prioridade pós-29/07 — ~~B43~~ ✓ → ~~B44~~ ✓ → ~~B45~~ ✓ → ~~B46~~ ✓ → B58 → B47 → B48** (e em paralelo B49–B53; depois B54/B55) · **B56** resumo total+cobertura no Início (paralelo ao chassis de busca) — chassis UX-1 + busca global + briefing numérico; soft 03/08 · [fluxos](plans/fluxos-acao-primeiro-inicio.md); smoke pós-deploy em produção + onboarding do time (Onda 0 §2/§4); **E4R ✓** em código (aplicar `pnpm db:seed:projecao` em produção após smoke); **E8 ✓** conta da cadeira entregue; **E17 ✓** tabela TI no Início; **B15 ✓** (sort por coluna) e **A11 ✓** (posição em votos + `sort=votos`; o default do staff passou a `deficit` no E9 ✓) entregues 2026-07-24; **E18 ✓** documentação de conceitos (`/campanha/conceitos`, v1 só E8) entregue 2026-07-24; **B19 ✓** gerenciar assessores (caminho do onboarding sem `/admin` — criar/ativar contas e carteiras) entregue 2026-07-24; **B16 ✓** filtros no header entregue 2026-07-25; **B20 ✓** Municípios prioritários no Início entregue 2026-07-25; ~~**B38**~~ ✓ sidebar recolhido em tablet (entregue 2026-07-26 — `offcanvas` + trigger `md+` + cookie/`matchMedia`); **B39** (lembrar de mim / sessão longa — o default de 2 h está derrubando sessão no onboarding; ~0,5 dia, entra cedo) e, logo atrás se folga, ~~**B40**~~ ✓ (biometria WebAuthn — entregue 2026-07-28); ~~**B41**~~ ✓ (scroll horizontal + sticky na lista de municípios — entregue 2026-07-27) e ~~**B42**~~ ✓ (polimento mobile da mesma lista — Drawer em Tendência/Assessores/Votos estimados + card compacto e clicável; entregue 2026-07-27); **B21** (página dos Territórios de Identidade — a leitura regional sai do painel do Início e ganha tela própria com sort/filtro; útil já no onboarding para dividir carteiras) se folga — e, logo atrás dela, **B25** (link do território na célula da lista: quarto de dia, é o que faz a página nova ser encontrada de dentro do trabalho); **B22** (explicação por coluna no header — a tabela deixa de exigir o autor do E8/E9 ao lado para ser lida; quick win de onboarding, meio dia); **B23** (tooltip nas células — nomes dos assessores no hover da coluna, hoje só iniciais, e a justificativa da tendência, hoje invisível na lista; capacidade genérica da célula, meio dia, casa com B22 pelo mesmo primitivo); ~~**B24**~~ ✓ (auto-save + justificativa no popover de Tendência — entregue 2026-07-26; a mesa corrige em massa as notas semeadas pelo E4R ✓ sem abrir 435 telas de edição); **B26** (registrar sinal na própria célula "Último sinal" — fecha o laço do E9 ✓: hoje a fila diz onde está frio e obriga a sair dela para registrar qualquer coisa; ~0,75 dia, e é o item que mais aumenta a chance do gate de adoção abaixo, porque o registro passa a caber no gesto de varrer a lista, inclusive no celular); **B27** (combobox + chips + auto-save nos Assessores — é literalmente a tela em que o CG divide as carteiras no onboarding: hoje procura o nome numa coluna de checkboxes e confirma num botão; ~0,75 dia, casa com B24 pelo mesmo endpoint e com B23 pelo mesmo gatilho); **B28** (e-mail + celular na lista de lideranças — copy + WhatsApp como em assessores, sem abrir a ficha; ~0,5–0,75 dia, quick win de campo no onboarding da rede); **B30** (convidar para completar cadastro por WhatsApp na mesma lista — reusa o convite que já existe no detalhe, sem abrir a ficha; ~0,25–0,5 dia, o quick win mais barato desta leva porque não toca action/collection nenhuma; casa com B28 pela mesma linha, ícone distinto); **B31** (coluna de dobradinhas na mesma lista — é o preenchimento em lote que o onboarding exige: o E4R ✓ deixou dobradinha só no município e as fichas vazias, e completar uma a uma pelo detalhe é o gesto que trava a mesa; ~1 dia, casa com B27 pela mesma peça de célula — quem for primeiro define o padrão do chip+combobox in-list); ~~**B32**~~ ✓ (editar Status de apoio na célula da mesma lista — entregue 2026-07-26; fecha o adiado do próprio B29); **B34** (chips editáveis de municípios na mesma lista — a coluna "Municípios" ganha o mesmo tratamento da coluna homônima em Assessores (B19 ✓): chips com link + Popover de edição com combobox, em vez do texto morto atual; ~1–1,25 dia, casa com B27/B31 pela mesma peça de célula editável — quem entrar antes define o padrão do combobox+chip in-list que os outros herdam); **B37** (o mesmo tratamento de chips editáveis na coluna "Municípios" de `/campanha/dobradinhas` — hoje um número morto, `municipalityCount`; ~0,75–1 dia, herda a lib promovida pelo B34 se ele já tiver landado, senão promove ela mesma); **B36** (a coluna "Lideranças" da mesma lista ganha o mesmo tratamento — chips nomeados + Popover + busca + auto-save por delta, reusando sem alteração a mutação que o B31 desenha para o lado oposto da mesma relação; ~0,5–1,5 dia dependendo de quem entre B31/B36 chega primeiro na corrida de extração da célula compartilhada); **B33** (sort + filtro no header de `/campanha/dobradinhas` — pedido explícito de reuso: Nome/Partido no header e filtro por Partido, sem duplicar o chrome de `/campanha/municipios`; ~0,5–1,5 dia dependendo de quem chega primeiro na corrida de extração do chrome compartilhado com B29/B21; entra perto de B29 por reusar a mesma peça); **B29** (sort + filtro no header da mesma lista — é a tela onde o assessor recebe a carteira e precisa recortar "quem ainda está a abordar aqui" / "quem ainda não tem acesso ao app" antes de convidar; ~1,5–2 dias porque paga também a extração do head compartilhado, então entra depois dos quick wins — mas antes se B21 for adiado, para o head nascer compartilhado); ~~**B17**~~ ✓ (seletor de colunas) / ~~**B18**~~ ✓ (filtros salvos + atalho sidebar) entregues 2026-07-28; fill-in **renomear coluna Cobertura** (header curto na fila — quarto de dia; glossário/KPI intactos); fill-in **remover coluna Tipo** (densidade morta na fila — quarto/meio dia; Salvador via slugs cobre ZE); ~~fill-in **remover lista de zonas do Início**~~ ✓ (entregue 2026-07-28); fill-in **selecionados no topo nos filtros do header** (desmarcar sem caçar checkbox; helper puro que B21 herda — quarto de dia); **C13 ✓** (renomeado "Plano de Ação" → "Atividade" em toda a vertical, incluindo a migration data-preserving — entregue 2026-07-25); gate de adoção: sinal Little Hire — ≥1 update espontâneo até 30/07, cobrança da tabela dispensada (planilhas já em `docs/sheets/`) — acompanhamento em [IMPROVE-APP-PLAN.md](IMPROVE-APP-PLAN.md); **R6** critique/polish; **E9 ✓** entregue no mesmo dia; **B14 ✓** (atalho geo entregue 2026-07-26); Onda 0 jurídica em paralelo (externa). +**Janela 1 — agora → 05/08 (convenções):** **prioridade pós-29/07 — ~~B43–B47~~ ✓ → B58 → B48** (∥ B49–B53; depois B54/B55) · **B56** · **wizards B59 → B60∥B61 · B62 → B63** (soft 03/08) · [fluxos](plans/fluxos-acao-primeiro-inicio.md); smoke + onboarding Onda 0; **E4R ✓** aplicar seed em produção após smoke; demais entregues/pendentes da janela (E8/E9/E17/E18/B15–B42/B19/…) inalterados no detalhe abaixo — foco operacional = busca B48 + etapas de wizard B59–B63. **E8 ✓** conta da cadeira entregue; **E17 ✓** tabela TI no Início; **B15 ✓** (sort por coluna) e **A11 ✓** (posição em votos + `sort=votos`; o default do staff passou a `deficit` no E9 ✓) entregues 2026-07-24; **E18 ✓** documentação de conceitos (`/campanha/conceitos`, v1 só E8) entregue 2026-07-24; **B19 ✓** gerenciar assessores (caminho do onboarding sem `/admin` — criar/ativar contas e carteiras) entregue 2026-07-24; **B16 ✓** filtros no header entregue 2026-07-25; **B20 ✓** Municípios prioritários no Início entregue 2026-07-25; ~~**B38**~~ ✓ sidebar recolhido em tablet (entregue 2026-07-26 — `offcanvas` + trigger `md+` + cookie/`matchMedia`); **B39** (lembrar de mim / sessão longa — o default de 2 h está derrubando sessão no onboarding; ~0,5 dia, entra cedo) e, logo atrás se folga, ~~**B40**~~ ✓ (biometria WebAuthn — entregue 2026-07-28); ~~**B41**~~ ✓ (scroll horizontal + sticky na lista de municípios — entregue 2026-07-27) e ~~**B42**~~ ✓ (polimento mobile da mesma lista — Drawer em Tendência/Assessores/Votos estimados + card compacto e clicável; entregue 2026-07-27); **B21** (página dos Territórios de Identidade — a leitura regional sai do painel do Início e ganha tela própria com sort/filtro; útil já no onboarding para dividir carteiras) se folga — e, logo atrás dela, **B25** (link do território na célula da lista: quarto de dia, é o que faz a página nova ser encontrada de dentro do trabalho); **B22** (explicação por coluna no header — a tabela deixa de exigir o autor do E8/E9 ao lado para ser lida; quick win de onboarding, meio dia); **B23** (tooltip nas células — nomes dos assessores no hover da coluna, hoje só iniciais, e a justificativa da tendência, hoje invisível na lista; capacidade genérica da célula, meio dia, casa com B22 pelo mesmo primitivo); ~~**B24**~~ ✓ (auto-save + justificativa no popover de Tendência — entregue 2026-07-26; a mesa corrige em massa as notas semeadas pelo E4R ✓ sem abrir 435 telas de edição); **B26** (registrar sinal na própria célula "Último sinal" — fecha o laço do E9 ✓: hoje a fila diz onde está frio e obriga a sair dela para registrar qualquer coisa; ~0,75 dia, e é o item que mais aumenta a chance do gate de adoção abaixo, porque o registro passa a caber no gesto de varrer a lista, inclusive no celular); **B27** (combobox + chips + auto-save nos Assessores — é literalmente a tela em que o CG divide as carteiras no onboarding: hoje procura o nome numa coluna de checkboxes e confirma num botão; ~0,75 dia, casa com B24 pelo mesmo endpoint e com B23 pelo mesmo gatilho); **B28** (e-mail + celular na lista de lideranças — copy + WhatsApp como em assessores, sem abrir a ficha; ~0,5–0,75 dia, quick win de campo no onboarding da rede); **B30** (convidar para completar cadastro por WhatsApp na mesma lista — reusa o convite que já existe no detalhe, sem abrir a ficha; ~0,25–0,5 dia, o quick win mais barato desta leva porque não toca action/collection nenhuma; casa com B28 pela mesma linha, ícone distinto); **B31** (coluna de dobradinhas na mesma lista — é o preenchimento em lote que o onboarding exige: o E4R ✓ deixou dobradinha só no município e as fichas vazias, e completar uma a uma pelo detalhe é o gesto que trava a mesa; ~1 dia, casa com B27 pela mesma peça de célula — quem for primeiro define o padrão do chip+combobox in-list); ~~**B32**~~ ✓ (editar Status de apoio na célula da mesma lista — entregue 2026-07-26; fecha o adiado do próprio B29); **B34** (chips editáveis de municípios na mesma lista — a coluna "Municípios" ganha o mesmo tratamento da coluna homônima em Assessores (B19 ✓): chips com link + Popover de edição com combobox, em vez do texto morto atual; ~1–1,25 dia, casa com B27/B31 pela mesma peça de célula editável — quem entrar antes define o padrão do combobox+chip in-list que os outros herdam); **B37** (o mesmo tratamento de chips editáveis na coluna "Municípios" de `/campanha/dobradinhas` — hoje um número morto, `municipalityCount`; ~0,75–1 dia, herda a lib promovida pelo B34 se ele já tiver landado, senão promove ela mesma); **B36** (a coluna "Lideranças" da mesma lista ganha o mesmo tratamento — chips nomeados + Popover + busca + auto-save por delta, reusando sem alteração a mutação que o B31 desenha para o lado oposto da mesma relação; ~0,5–1,5 dia dependendo de quem entre B31/B36 chega primeiro na corrida de extração da célula compartilhada); **B33** (sort + filtro no header de `/campanha/dobradinhas` — pedido explícito de reuso: Nome/Partido no header e filtro por Partido, sem duplicar o chrome de `/campanha/municipios`; ~0,5–1,5 dia dependendo de quem chega primeiro na corrida de extração do chrome compartilhado com B29/B21; entra perto de B29 por reusar a mesma peça); **B29** (sort + filtro no header da mesma lista — é a tela onde o assessor recebe a carteira e precisa recortar "quem ainda está a abordar aqui" / "quem ainda não tem acesso ao app" antes de convidar; ~1,5–2 dias porque paga também a extração do head compartilhado, então entra depois dos quick wins — mas antes se B21 for adiado, para o head nascer compartilhado); ~~**B17**~~ ✓ (seletor de colunas) / ~~**B18**~~ ✓ (filtros salvos + atalho sidebar) entregues 2026-07-28; fill-in **renomear coluna Cobertura** (header curto na fila — quarto de dia; glossário/KPI intactos); fill-in **remover coluna Tipo** (densidade morta na fila — quarto/meio dia; Salvador via slugs cobre ZE); ~~fill-in **remover lista de zonas do Início**~~ ✓ (entregue 2026-07-28); fill-in **selecionados no topo nos filtros do header** (desmarcar sem caçar checkbox; helper puro que B21 herda — quarto de dia); **C13 ✓** (renomeado "Plano de Ação" → "Atividade" em toda a vertical, incluindo a migration data-preserving — entregue 2026-07-25); gate de adoção: sinal Little Hire — ≥1 update espontâneo até 30/07, cobrança da tabela dispensada (planilhas já em `docs/sheets/`) — acompanhamento em [IMPROVE-APP-PLAN.md](IMPROVE-APP-PLAN.md); **R6** critique/polish; **E9 ✓** entregue no mesmo dia; **B14 ✓** (atalho geo entregue 2026-07-26); Onda 0 jurídica em paralelo (externa). **Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; ~~**E9** fila de alocação~~ (entregue 2026-07-24, antecipada da janela 2); ~~**E16** dossiê do município~~ (entregue 2026-07-25, antecipado da janela 2); D2 se sobrar folga. @@ -405,9 +425,9 @@ Paralelizáveis agora: **B58** (polimento strip de ações); **B47** (próximo d ### Cortes seguros / não cortáveis -**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**B43–B46** chassis UX-1~~ ✓ (entregue 2026-07-29); **B47–B48** busca mínima (input → Municípios — cortar = Início sem achar município até 03/08); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); ~~**E8** conta da cadeira~~ (entregue 2026-07-24); ~~**C12** registro-fundação~~ (entregue 2026-07-24); C2 dados reais; assimetria declarado×estimado (relação de campo); ~~**E9** fila de alocação~~ (entregue 2026-07-24 — era o mínimo de "inteligência, não planilha"); ~~**B19** gerenciar assessores~~ (entregue 2026-07-24 — CG/candidato não entram em `/admin`); **B39** lembrar de mim / sessão longa (default de 2 h derruba sessão no onboarding/campo — atrito diário de adoção). +**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**B43–B47** chassis UX-1 + busca input~~ ✓; **B48** busca Municípios (cortar = Início sem achar município até 03/08); **B59–B61** etapas mínimas do wizard de votos (chassis + busca município + ajuste — cortar = ações do Início continuam inertes no ritual A1); **B62** simplificar sinal (schema — cortar = wizard B63 e B26 seguem exigindo Fonte/Triangulado); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); ~~**E8** conta da cadeira~~ (entregue 2026-07-24); ~~**C12** registro-fundação~~ (entregue 2026-07-24); C2 dados reais; assimetria declarado×estimado (relação de campo); ~~**E9** fila de alocação~~ (entregue 2026-07-24 — era o mínimo de "inteligência, não planilha"); ~~**B19** gerenciar assessores~~ (entregue 2026-07-24 — CG/candidato não entram em `/admin`); **B39** lembrar de mim / sessão longa (default de 2 h derruba sessão no onboarding/campo — atrito diário de adoção). -**Cortes seguros** (se o prazo apertar, nesta ordem): **B58** polimento da strip (rótulos longos e heading continuam funcionais — cortar = polish visual antes do 03/08); **B57** delta 7d (Início continua com total+cobertura do B56; Δ era futuro de propósito); **B56** resumo no Início (Quadro já tem os mesmos agregados — cortar = Início sem briefing numérico); **B55** ações WA/share na busca (linha continua clicável); **B54** grid/cap (empilhar sempre); **B51**/**B53**/**B52**/**B50**/**B49** grupos secundários da busca (nessa ordem — Municípios B48 fica); **D5** inbox→rascunhos (manter registro manual + `wa.me`); **D4** envio bridge (manter `wa.me`); **D3** fundação do canal (atalho `wa.me` continua); **E12** camada TI (rollup manual por lista; **E17** já dá a leitura regional básica no Início); **E13** planejador de giros (rebaixado na fila de corte em 2026-07-24: "perna"/agenda é a restrição dominante nomeada em campo — cortar só depois de E12; agenda segue manual com J-A/J-B como guia); **E15** backtest (pós-eleição por definição — cortar = perder o aprendizado 2030); **E11** motor v1 (manter a fila E9 ✓ sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); ~~**E14** níveis~~ (entregue 2026-07-27 — `priority` continua, intocado); ~~**B8 F2** polígonos~~ (entregue 2026-07-26 — o mapa não é mais agregado em Salvador); **D2** push (adiar); **A6**; ~~**B14** município mais próximo~~ (entregue 2026-07-26); ~~**B38** sidebar recolhido em tablet~~ (entregue 2026-07-26); ~~**B40** login biométrico~~ (entregue 2026-07-28 — senha + B39 ✓ continuam, e cortar teria custado só o atalho de digital/Face ID; era o mais caro e o primeiro a cair desta leva B39–B42); **B42** polimento mobile da lista de municípios (Popover e card longos continuam usáveis — cortar custa conforto de teclado e densidade no celular); ~~**B41** scroll horizontal + sticky~~ (entregue 2026-07-27) (a tabela continua legível com zoom/pan da página — cortar custa o overflow em tablet; cai **depois** de B42 porque o bug de layout em mesa é mais caro que o polish mobile); ~~**B18** filtros salvos~~ ✓ entregue (lista + filtros manuais continuam; Visitados cobrem “voltar”); **B21** página dos Territórios de Identidade (o painel do E17 ✓ no Início continua entregando a leitura regional) — **B25** cai junto por definição (sem destino, sem link); ~~**B17** seletor de colunas~~ ✓ entregue (tabela completa continua; scroll horizontal); ~~**B22** explicação no header~~ (entregue 2026-07-26); ~~**B23** tooltip nas células~~ (entregue 2026-07-26); ~~**B24** auto-save + justificativa na Tendência~~ (entregue 2026-07-26); **B27** combobox + chips nos Assessores (a lista de checkboxes com "Salvar" continua atribuindo — cortar custa a mesma coerência do B24 mais o tempo do CG procurando nomes numa coluna durante o onboarding; corte junto com B24, já que dividem o endpoint); **B28** e-mail + celular na lista de lideranças (a ficha `/liderancas/[id]` continua mostrando o contato — cortar custa o gesto de copiar/abrir ZAP na varredura; quick win barato de cortar e de fazer); ~~**B30** convite de completar cadastro por WhatsApp na lista~~ (entregue 2026-07-26); **B31** coluna de dobradinhas nas lideranças (o `RelationMultiSelect` da ficha interna continua vinculando — cortar custa o preenchimento em lote do onboarding, uma ficha aberta por liderança, e mantém o vínculo invisível na varredura; corte junto com B27, já que dividem a peça de célula, e antes de B29 só se a mesa já tiver preenchido as dobradinhas à mão); ~~**B32** editar Status de apoio na célula~~ (entregue 2026-07-26); **B34** chips editáveis de municípios na lista de lideranças (o texto simples `row.municipalityNames.join(', ')` continua legível e a ficha `/liderancas/[id]` continua vinculando município via formulário — cortar custa o link direto ao município, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma peça de célula editável, e antes de B29 pelo mesmo motivo de coerência); **B37** chips editáveis de municípios na lista de dobradinhas (o número `municipalityCount` continua legível e a ficha de município continua vinculando dobradinha pelo `MunicipalityStrategyForm` — cortar custa o link direto, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha do município; corte junto com B34, já que dividem a mesma lib e a mesma decisão de container); ~~**B36** coluna de lideranças na lista de dobradinhas~~ (entregue 2026-07-26; a contagem `leadershipCount` era o fallback e a ficha `/liderancas/[id]` continua vinculando dobradinha via formulário — cortar custa o link direto à liderança, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma mutação/schema/lock); ~~**B33** sort + filtro no header das dobradinhas~~ (entregue 2026-07-26); **B29** sort + filtro no header das lideranças (a busca por nome continua funcionando e o escopo do assessor já reduz a lista — cortar custa o recorte por status/setor/município no onboarding da rede e mantém o header rico como cópia única de municípios; por ser o item mais caro desta faixa (~1,5–2 dias com a extração do head), cai **antes** do B26, que é o que sustenta o gate de adoção); **B26** registrar sinal na célula (o formulário completo em `/[slug]?tab=updates` continua sendo o caminho — cortar custa o laço da fila do E9 ✓ e o registro em campo pelo celular, que é justamente onde o gate de adoção se decide; por isso cai **depois** de B22/B23/B24); ~~**C13** renomear Plano de Ação → Atividade~~ (entregue 2026-07-25); **B20** prioritários no Início (lista `?priority=alta` continua; só perde o atalho — mas a seção atual mente, então cortar = conviver com o bug); fill-in **renomear coluna Cobertura** (a lista continua legível com o rótulo longo — corte = header mais largo); fill-in **remover coluna Tipo** (a lista continua completa; só mantém densidade morta — corte = conviver com a coluna); ~~fill-in **remover lista de zonas do Início**~~ ✓ (entregue 2026-07-28 — o mapa B8 F2 ✓ continua abrindo cada ZE); ~~**B16** filtros no header~~ (entregue 2026-07-25); ~~**B15** sort por coluna~~ (entregue 2026-07-24); ~~**A11**/**E17**~~ (entregues 2026-07-24); ~~**E18** documentação de conceitos~~ (entregue 2026-07-24); ~~**E16** dossiê~~ (entregue 2026-07-25); demais débitos/fill-ins. +**Cortes seguros** (se o prazo apertar, nesta ordem): **B58** polimento da strip (rótulos longos e heading continuam funcionais — cortar = polish visual antes do 03/08); **B63** wizard sinal standalone (A1 ainda pode embutir select pós-B62; cortar = sem grid/Drawer de tipos até folga); **B57** delta 7d (Início continua com total+cobertura do B56; Δ era futuro de propósito); **B56** resumo no Início (Quadro já tem os mesmos agregados — cortar = Início sem briefing numérico); **B55** ações WA/share na busca (linha continua clicável); **B54** grid/cap (empilhar sempre); **B51**/**B53**/**B52**/**B50**/**B49** grupos secundários da busca (nessa ordem — Municípios B48 fica); **D5** inbox→rascunhos (manter registro manual + `wa.me`); **D4** envio bridge (manter `wa.me`); **D3** fundação do canal (atalho `wa.me` continua); **E12** camada TI (rollup manual por lista; **E17** já dá a leitura regional básica no Início); **E13** planejador de giros (rebaixado na fila de corte em 2026-07-24: "perna"/agenda é a restrição dominante nomeada em campo — cortar só depois de E12; agenda segue manual com J-A/J-B como guia); **E15** backtest (pós-eleição por definição — cortar = perder o aprendizado 2030); **E11** motor v1 (manter a fila E9 ✓ sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); ~~**E14** níveis~~ (entregue 2026-07-27 — `priority` continua, intocado); ~~**B8 F2** polígonos~~ (entregue 2026-07-26 — o mapa não é mais agregado em Salvador); **D2** push (adiar); **A6**; ~~**B14** município mais próximo~~ (entregue 2026-07-26); ~~**B38** sidebar recolhido em tablet~~ (entregue 2026-07-26); ~~**B40** login biométrico~~ (entregue 2026-07-28 — senha + B39 ✓ continuam, e cortar teria custado só o atalho de digital/Face ID; era o mais caro e o primeiro a cair desta leva B39–B42); **B42** polimento mobile da lista de municípios (Popover e card longos continuam usáveis — cortar custa conforto de teclado e densidade no celular); ~~**B41** scroll horizontal + sticky~~ (entregue 2026-07-27) (a tabela continua legível com zoom/pan da página — cortar custa o overflow em tablet; cai **depois** de B42 porque o bug de layout em mesa é mais caro que o polish mobile); ~~**B18** filtros salvos~~ ✓ entregue (lista + filtros manuais continuam; Visitados cobrem “voltar”); **B21** página dos Territórios de Identidade (o painel do E17 ✓ no Início continua entregando a leitura regional) — **B25** cai junto por definição (sem destino, sem link); ~~**B17** seletor de colunas~~ ✓ entregue (tabela completa continua; scroll horizontal); ~~**B22** explicação no header~~ (entregue 2026-07-26); ~~**B23** tooltip nas células~~ (entregue 2026-07-26); ~~**B24** auto-save + justificativa na Tendência~~ (entregue 2026-07-26); **B27** combobox + chips nos Assessores (a lista de checkboxes com "Salvar" continua atribuindo — cortar custa a mesma coerência do B24 mais o tempo do CG procurando nomes numa coluna durante o onboarding; corte junto com B24, já que dividem o endpoint); **B28** e-mail + celular na lista de lideranças (a ficha `/liderancas/[id]` continua mostrando o contato — cortar custa o gesto de copiar/abrir ZAP na varredura; quick win barato de cortar e de fazer); ~~**B30** convite de completar cadastro por WhatsApp na lista~~ (entregue 2026-07-26); **B31** coluna de dobradinhas nas lideranças (o `RelationMultiSelect` da ficha interna continua vinculando — cortar custa o preenchimento em lote do onboarding, uma ficha aberta por liderança, e mantém o vínculo invisível na varredura; corte junto com B27, já que dividem a peça de célula, e antes de B29 só se a mesa já tiver preenchido as dobradinhas à mão); ~~**B32** editar Status de apoio na célula~~ (entregue 2026-07-26); **B34** chips editáveis de municípios na lista de lideranças (o texto simples `row.municipalityNames.join(', ')` continua legível e a ficha `/liderancas/[id]` continua vinculando município via formulário — cortar custa o link direto ao município, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma peça de célula editável, e antes de B29 pelo mesmo motivo de coerência); **B37** chips editáveis de municípios na lista de dobradinhas (o número `municipalityCount` continua legível e a ficha de município continua vinculando dobradinha pelo `MunicipalityStrategyForm` — cortar custa o link direto, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha do município; corte junto com B34, já que dividem a mesma lib e a mesma decisão de container); ~~**B36** coluna de lideranças na lista de dobradinhas~~ (entregue 2026-07-26; a contagem `leadershipCount` era o fallback e a ficha `/liderancas/[id]` continua vinculando dobradinha via formulário — cortar custa o link direto à liderança, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma mutação/schema/lock); ~~**B33** sort + filtro no header das dobradinhas~~ (entregue 2026-07-26); **B29** sort + filtro no header das lideranças (a busca por nome continua funcionando e o escopo do assessor já reduz a lista — cortar custa o recorte por status/setor/município no onboarding da rede e mantém o header rico como cópia única de municípios; por ser o item mais caro desta faixa (~1,5–2 dias com a extração do head), cai **antes** do B26, que é o que sustenta o gate de adoção); **B26** registrar sinal na célula (o formulário completo em `/[slug]?tab=updates` continua sendo o caminho — cortar custa o laço da fila do E9 ✓ e o registro em campo pelo celular, que é justamente onde o gate de adoção se decide; por isso cai **depois** de B22/B23/B24); ~~**C13** renomear Plano de Ação → Atividade~~ (entregue 2026-07-25); **B20** prioritários no Início (lista `?priority=alta` continua; só perde o atalho — mas a seção atual mente, então cortar = conviver com o bug); fill-in **renomear coluna Cobertura** (a lista continua legível com o rótulo longo — corte = header mais largo); fill-in **remover coluna Tipo** (a lista continua completa; só mantém densidade morta — corte = conviver com a coluna); ~~fill-in **remover lista de zonas do Início**~~ ✓ (entregue 2026-07-28 — o mapa B8 F2 ✓ continua abrindo cada ZE); ~~**B16** filtros no header~~ (entregue 2026-07-25); ~~**B15** sort por coluna~~ (entregue 2026-07-24); ~~**A11**/**E17**~~ (entregues 2026-07-24); ~~**E18** documentação de conceitos~~ (entregue 2026-07-24); ~~**E16** dossiê~~ (entregue 2026-07-25); demais débitos/fill-ins. ## Bloqueadores atuais