diff --git a/.cursor/rules/projects/nucleos-eleitorais.mdc b/.cursor/rules/projects/nucleos-eleitorais.mdc index df8dde80..2cef8987 100644 --- a/.cursor/rules/projects/nucleos-eleitorais.mdc +++ b/.cursor/rules/projects/nucleos-eleitorais.mdc @@ -7,6 +7,7 @@ alwaysApply: false ## Status +- **B19 registrado (2026-07-24):** gerenciar assessores em `/campanha/assessores` (lista + novo + detalhe) — só Coordenador Geral e Candidato; criar/editar contas `advisor`, carteira de municípios, reenviar senha; alinha access update/phone do `candidate`. Sem migration. Plano: [`gerenciar-assessores.md`](../../../docs/plans/gerenciar-assessores.md). - **C12 Registro-fundação entregue em código (2026-07-24):** trajetória de `votePledge` por versions nativas com baseline; atualização municipal `sinal` tipada (explicações, fonte e triangulação); `actionPlan.origin`; demandas vinculadas ao plano com custo derivado e criação múltipla atômica no formulário; `allocationDecision` ex-ante imutável com snapshot pequeno. Migration consolidada `20260724_180000_add_campaign_foundation_records`; testes de integração e jornada E2E dedicados. - **B18 registrado (2026-07-24):** filtros **salvos com nome** em `/campanha/municipios` (`localStorage`) + acesso rápido de 2º nível sob Municípios no sidebar (hover desktop / expand mobile; sticky só no filtro salvo ativo). Soft B16/B17 (barra). Fatia concreta do “saved views” que FD2 adiava como genérico. Sem migration. Plano: [`filtros-salvos-municipios.md`](../../../docs/plans/filtros-salvos-municipios.md). - **Avaliação: reorder de colunas por DnD (2026-07-24):** pedido para arrastar headers em `/campanha/municipios` → **fora de escopo** (não confundir com **B17** hide/show). Tecnicamente possível (~1–1,5d), mas conflita com sort B15 + filtros B16 no mesmo header, anti spreadsheet do PRODUCT, e não desbloqueia decisão de alocação. Gatilho em [`reordenar-colunas-lista-municipios.md`](../../../docs/plans/reordenar-colunas-lista-municipios.md). diff --git a/docs/plans/gerenciar-assessores.md b/docs/plans/gerenciar-assessores.md new file mode 100644 index 00000000..a7b82843 --- /dev/null +++ b/docs/plans/gerenciar-assessores.md @@ -0,0 +1,149 @@ +# B19 — Gerenciar assessores (`/campanha/assessores`) + +Status: rascunho +Atualizado em: 2026-07-24 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Demais itens abertos, B19; superfície de coordenação) +Impeccable: C — UI nova em `/campanha/assessores` (lista + novo + detalhe); sem design-ref +Appetite: ~1,5–2 dias eng; lista + create + detalhe/edição in-context + gate de nav; sem migration; alinhamento de access `candidate`↔`coordinator` nos helpers de `campaignUser` +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (princípios 2 clareza sob pressão, 3 Edit where you see, 4 Auto-save, 8 Feel the action) / `DESIGN.md` (register `product`; Field Desk) · tema `data-theme='campaign'` · precedente de lista+CRUD [`organizacoes/page.tsx`](<../../src/app/(campaign)/campanha/(app)/organizacoes/page.tsx>) e [`dobradinhas/`](<../../src/app/(campaign)/campanha/(app)/dobradinhas/>). + +Na implementação (`implement-roadmap-item`): shape → craft → critique → polish. + +Brief compacto: + +- **Persona / contexto:** Alex (Coordenador Geral) e o Candidato no desk — onboarding do time (Onda 0 §4) e manutenção da carteira de assessores; sem ir ao Payload `/admin` (JWT de campanha não entra lá). +- **Job principal:** criar e manter contas `advisor` (nome, e-mail, celular) e ver/ajustar quais municípios cada um administra. +- **Estratégia de cor:** Restrained — lista densa Field Desk; sem hero-metric de “N assessores”. +- **Edit where you see:** sim — campos de conta e vínculo a municípios editáveis no detalhe (Popover/multi-select + auto-save); create multi-campo com submit explícito. +- **Anti-goals:** página “Equipe” genérica com todos os papéis; IAM tipo SaaS; ranking/gamificação de assessores; spreadsheet de municípios×assessores; expor a lista a `advisor`/`leader`. + +## Dados → decisão → apresentação + +- **Vou apresentar dados?** Sim, superfície neste item — lista operacional de pessoas + contagem de municípios, não KPI eleitoral. +- **Decisões desbloqueadas:** + - Coordenador / Candidato: “este assessor já tem login válido (e-mail real) ou ainda é placeholder da planilha?” + - Coordenador / Candidato: “quem cobre quantos / quais municípios — falta alguém na carteira X?” + - Coordenador / Candidato: “criar assessor novo agora e depois atribuir municípios?” +- **Forma escolhida:** **tabela/lista** (nome, e-mail, celular, Nº municípios, link detalhe) + detalhe com lista dos municípios — **por quê:** comparar muitas entidades e agir; degrau mais pobre que resolve. **Rejeitado:** dashboard de carga por assessor (vaidade / gaming — research G4); mapa de carteiras (outro job; B13/E12); chart de distribuição. +- **Profile:** categórico + contagem; granularidade = `campaignUser` (role `advisor`); tamanho típico dezenas (seed E4R ~24 assessores); absoluto só como contagem de vínculos. +- **Anti-goals de dado:** sem % estadual; sem ranking de performance; sem KPI de “cadastros feitos pelo assessor”. + +Self-check dados: 5/5. + +## Contexto + +Onde se gerencia assessor hoje: + +- **Conta** (`campaignUser` com `role: 'advisor'`): só via Payload admin (`/admin`) ou seed `pnpm db:seed:projecao` (e-mails `@planilha.invalid`, sem login até trocar). CG/candidato de campanha **não** acessam `/admin`. +- **Carteira:** `municipality.advisors` — já editável na lista/detalhe via `MunicipalityListAdvisorsControl` (B9) + `eligibleCampaignStaffWhere`. +- **Access já existe:** `canManageCampaignUsers` = `isCampaignUnrestricted` (create/delete); `canManageCampaignUserRole` = unrestricted; **mas** `canUpdateCampaignUser` / phone create-update ainda privilegiam só `coordinator` (assimetría pós-role `candidate` — bug de produto para esta superfície). +- Hook `preventAssignedAdvisorDowngrade` impede `advisor`→`leader` enquanto houver municípios; self-service no perfil não altera `role`/`name`/`email`/`phone`. + +Pedido de produto (2026-07-24): página na vertical `/campanha` para gerenciar assessores, **visível só a Coordenador Geral e Candidato**. + +## Objetivos + +- Rotas `/campanha/assessores`, `/campanha/assessores/novo`, `/campanha/assessores/[id]` (id numérico, como lideranças). +- Gate de página + item de nav: só `isCampaignUnrestricted`; `advisor`/`leader` → redirect `/campanha` (e item ausente no sidebar). +- Lista: busca por nome/e-mail; colunas Nome · E-mail · Celular · Municípios (contagem) · link para detalhe; só `role === 'advisor'`. +- Criar assessor: nome + e-mail (obrigatório para login staff) + celular opcional; senha aleatória server-side; se e-mail entregável, disparar `forgotPassword` (fluxo já em `actions/password.ts`) — CG não vê a senha. +- Detalhe: editar nome/e-mail/celular (auto-save); listar municípios administrados com link; atribuir/remover municípios no contexto (atualiza `municipality.advisors`); CTA “Enviar link de redefinição de senha”. +- Sem delete na UI v1 (collection delete permanece para admin Payload). +- Sem migration, sem collection nova, sem `Consent` novo. +- Testes int: gate de rota/access (advisor negado; unrestricted ok) + create com `overrideAccess: false`. + +## Decisões travadas + +- **Visibilidade e escrita = `isCampaignUnrestricted` (coordinator + candidate).** Pedido explícito; alinha create já existente. **Rejeitado:** só coordinator (candidate ficaria cego no onboarding); staff inteiro vê a lista (assessor não gerencia pares — vazaria e-mails/carteiras); Payload `/admin` como UX principal (CG não entra). +- **Alinhar access de update/phone de terceiros a unrestricted.** Hoje `canUpdateCampaignUser` / `canCreateCampaignUserPhone` / `canUpdateCampaignUserPhone` são coordinator-only — candidate criaria conta e não corrigiria e-mail. **Rejeitado:** deixar assimetria (página inútil para candidate); página “só leitura” para candidate. +- **Escopo = só papel `advisor`.** Create força `role: 'advisor'`; lista filtra esse role. Coordinator/candidate continuam fora desta UI. **Rejeitado:** “Equipe” multi-papel (explode IAM); promover assessor a coordinator nesta tela. +- **URL `/campanha/assessores` + detalhe por `[id]`.** Português no segmento (padrão da vertical); id interno (sem slug em `campaignUser`). **Rejeitado:** `/campanha/usuarios` genérico; slug inventado. +- **Sem delete na UI v1.** Evita órfãos e disputa com vínculos. Desativar = remover municípios + (futuro) flag — Adiado. **Rejeitado:** soft-delete/collection `archived` neste appetite; hard-delete com cascade silenciosa. +- **Senha: nunca exibida; onboarding via forgot-password / e-mail.** Reusa Resend + `requestCampaignPasswordReset` (ou equivalente privilegiado unbound ao titular). Placeholder `@planilha.invalid` exige troca de e-mail antes do envio. **Rejeitado:** senha temporária na tela (vazamento em share/screenshot); SMS. +- **Atribuição de municípios no detalhe do assessor (além de B9).** Edit where you see: quem olha a carteira do assessor ajusta ali; B9 continua no sentido município→assessores. **Rejeitado:** só links “vá à lista de municípios” (onboarding sofrível); segunda planilha full-grid. +- **i18n e naming:** identificadores em inglês (`AdvisorList`, `loadAdvisorListPageData`, `createAdvisor`, `updateAdvisorMunicipalities`, `reloadUnrestrictedActor`); strings pt-BR (“Assessores”, “Novo assessor”, “Enviar link de senha”). + +## Questões em aberto + +- **Nav: item no sidebar staff filtrado, ou só link a partir do Início/onboarding?** **Opções:** A) item “Assessores” no `staffNav` só se unrestricted | B) fora do nav (como Organizações hoje) + atalho no dashboard. **Recomendação:** **A** — onboarding precisa descobrir a superfície; bottom nav mobile **não** inclui (já no limite de 5; perfil/organizações também ficam de fora). _(assumido — validar no craft)_ +- **Create: celular obrigatório?** **Opções:** obrigatório | opcional. **Recomendação:** opcional — e-mail é o login staff; celular ajuda contato/WhatsApp (D3) mas seed já criou name-only. +- **Atribuição em lote no create (municípios no mesmo form)?** **Opções:** create só conta → detalhe atribui | create com multi-select. **Recomendação:** create só conta (submit atômico curto); municípios no detalhe com auto-save — evita form monstro e falha parcial. + +## Abordagem proposta + +```mermaid +flowchart LR + Gate["isCampaignUnrestricted
page + nav"] + List["/campanha/assessores
lista + busca"] + New["/campanha/assessores/novo"] + Detail["/campanha/assessores/id"] + Actions["actions/advisor.ts
create / update / municipalities / reset"] + CU["campaignUser
role=advisor"] + Mun["municipality.advisors"] + Mail["forgotPassword / Resend"] + + Gate --> List --> Detail + Gate --> New --> Detail + List --> Actions + New --> Actions + Detail --> Actions + Actions --> CU + Actions --> Mun + Actions --> Mail +``` + +Componentes: + +- **`reloadUnrestrictedActor`** em `src/utilities/campaignActionContext.ts` (espelha `reloadCoordinatorActor` com `isCampaignUnrestricted`) — depth: reusar o padrão; não inventar policy paralela. +- **Access:** ajustar `canUpdateCampaignUser` (não-self), `canCreateCampaignUserPhone`, `canUpdateCampaignUserPhone` para `isCampaignUnrestricted` (+ admin) em `src/utilities/access/campaignUsers.ts`; int tests cobrindo candidate. +- **`src/utilities/advisorData.ts`** (server-only): `parseAdvisorListParams`, `loadAdvisorListPageData` (find `campaignUser` where role=advisor + contagem de municípios via query agregada ou map a partir de `municipality.advisors`), `loadAdvisorDetail` com lista de municípios; `select` mínimo; `overrideAccess: false`. +- **`src/app/(campaign)/campanha/actions/advisor.ts`:** `createAdvisor` / `updateAdvisorProfile` / `setAdvisorMunicipalities` / `sendAdvisorPasswordReset` — assert unrestricted; create com senha aleatória (`crypto.randomBytes`) + `role: 'advisor'`; municipalities em transação se N writes; reset só se e-mail não-placeholder. +- **UI:** `AdvisorList` / `AdvisorForm` / `AdvisorDetail` / `AdvisorMunicipalitiesControl` em `src/components/campaign/` — shells `CampaignPageShell`, `CampaignListPendingBoundary`, `CampaignSearchForm`, `CampaignListPagination`; Feel the action nos controles. +- **`nav.ts`:** item “Assessores” (ícone `UserCog` ou similar) incluído só quando `isCampaignUnrestricted(role)`; bottom nav inalterado. +- **Rotas** sob `src/app/(campaign)/campanha/(app)/assessores/`. +- **Sem migration.** + +## Dependências + +- Nenhuma dura de outro item aberto. Reusa `campaignUser` access/hooks, B9 (`municipality.advisors`), reset de senha (`actions/password.ts` / Resend), shells de lista (organizações/dobradinhas), `eligibleCampaignStaffWhere`. +- **Suave:** onboarding Onda 0 §4 (esta UI é o caminho sem `/admin`); E4R seed já populou assessores placeholder — a página é o jeito de “ativar” e-mails reais. + +## Não escopo + +- Gerenciar `coordinator` / `candidate` / `leader` nesta UI. +- Delete / arquivar assessor. +- Inbox cruzado ou métricas de performance por assessor (D5 / research anti-gaming). +- Convite WhatsApp tipado para assessor (D3–D4; staff login = e-mail). +- Preferências UI syncadas em `campaignUser` (B17/B18). +- Alterar o modelo `municipality.advisors` (hasMany permanece). + +## Rabbit holes + +- **IAM completo (desativar, auditoria, 2FA, sessões).** Explode appetite. **Mitigação:** create/edit/reset + lista; sem delete. +- **Planilha municípios×assessores bidirecional full-grid.** Conflita com anti spreadsheet do PRODUCT. **Mitigação:** multi-select no detalhe + B9 no sentido inverso; sem data-grid. +- **Unificar com Organizações / “cadastros de referência” genérico.** Pass-through raso. **Mitigação:** actions e loaders nomeados `advisor*`; `runStaffEntityMutation` só se couber sem forçar policy staff genérica (aqui é unrestricted). +- **Consent por e-mail de assessor.** Staff interno, não opt-in de apoiador. **Mitigação:** sem chave nova; se jurídico pedir depois, defer. + +## Adiado com gatilho + +- **Delete / desativar assessor na UI.** Revisitar quando: CG pedir remoção recorrente **e** houver regra clara para municípios órfãos (reassign obrigatório). +- **Create com municípios no mesmo submit.** Revisitar quando: onboarding medir ≥2 cliques extras como atrito real pós-R6. +- **Nav mobile dedicado.** Revisitar se bottom nav for redesenhado (hoje teto 5). + +## Referências + +- `docs/roadmap.md` (Demais itens abertos · B19; Onda 0 §4 onboarding) +- `src/collections/CampaignUser.ts` — auth, hooks de downgrade/self-service, fields +- `src/utilities/access/campaignUsers.ts` — `canManageCampaignUsers`, update/phone asymmetry +- `src/utilities/access/shared.ts` — `isCampaignUnrestricted`, `eligibleCampaignStaffWhere` +- `src/components/campaign/MunicipalityListAdvisorsControl.tsx` — precedente de atribuição +- `src/components/campaign/nav.ts` — gate de itens por papel +- `src/app/(campaign)/campanha/(app)/organizacoes/page.tsx` — shell de lista CRUD +- `src/app/(campaign)/campanha/actions/password.ts` — forgotPassword / Resend +- `src/utilities/campaignActionContext.ts` — `reloadCoordinatorActor` (padrão a espelhar) +- AGENTS.md — Campaign auth, naming, `overrideAccess: false`, candidate = unrestricted visibility +- `PRODUCT.md` / `DESIGN.md` — Field Desk; Edit where you see; Feel the action diff --git a/docs/roadmap.md b/docs/roadmap.md index 50102659..ea6914c0 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-24 (C12 ✓; C11 absorve escala plano↔demandas; B18; B17; B16; reorder DnD fora de escopo; A11 ✓; B15 ✓; E4R ✓; E17 ✓; B14; janela 1: smoke, R6, E8) +Atualizado em: 2026-07-24 (B19 gerenciar assessores; C12 ✓; C11 absorve escala plano↔demandas; B18; B17; B16; reorder DnD fora de escopo; A11 ✓; B15 ✓; E4R ✓; E17 ✓; B14; janela 1: smoke, R6, E8) 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) @@ -30,7 +30,7 @@ Textos provisórios de Consent + `/privacidade` auto-provisionados ([onda-0.md]( 1. **Lote jurídico único** _(externo)_ — textos finais (substituem provisórios) + base LGPD art. 11: `lideranca-autopreenchimento`, `apoiador-cadastro`, `apoiador-intencao-voto`, `campanha-notificacoes-push`, **`campanha-whatsapp-canal`** (D3 — armazenamento/processamento do canal interno staff↔lideranças; se o lote já tiver fechado sem esta chave, entra no próximo passe com a assessoria — não abrir rodada jurídica só por ela), Aviso de Privacidade, avaliação RIPD. 2. **Smoke pós-deploy** _(desbloqueado — deploy da remodelagem aplicado em produção em 2026-07-23)_ — `NEXT_PUBLIC_SITE_URL` HTTPS, login `/campanha`, município de teste; checklist no AGENTS.md. 3. **Ativação com dados reais** assim que (1) liberar — lideranças/apoiadores reais e import em massa. -4. **Onboarding do time** — usuários `coordinator`/`advisor`, primeiros municípios assumidos, treino de campo; **seed da planilha de prioridades via E4R** (engenharia pronta — `pnpm db:seed:projecao`; aplicar em produção após smoke — [plano](plans/import-planilha-projecao.md)); rede/lideranças (nomes) só após o lote jurídico. +4. **Onboarding do time** — usuários `coordinator`/`advisor`, primeiros municípios assumidos, treino de campo; **B19** `/campanha/assessores` é o caminho no app (CG/candidato — sem `/admin`); **seed da planilha de prioridades via E4R** (engenharia pronta — `pnpm db:seed:projecao`; aplicar em produção após smoke — [plano](plans/import-planilha-projecao.md)); rede/lideranças (nomes) só após o lote jurídico. **O0+** (escala/DRY pós-Onda 0) não bloqueia jurídico nem smoke fictício — [plano](plans/escala-dry-pos-onda0.md). @@ -88,6 +88,7 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re - **B16** filtros no header das colunas da lista de municípios — relocaciona `region`/`kind`/`trend`/`coverage`/`priority` para o `TableHead` (desktop) ao lado do sort B15; barra slim (busca + Limpar [+ Cenário]); mobile mantém disclosure · ~1–1,5d · Janela 1–2 · soft: B15 ✓ · cortável · [plano](plans/filtros-no-header-lista-municipios.md) - **B17** seletor de colunas (mostrar/ocultar) na lista de municípios — Popover “Colunas” + `localStorage`; desktop only; `name` obrigatória; soft: B15 ✓ (ids) / B16 (barra slim); prepara viewport para **E9** · ~0,5–1d · Janela 1–2 · cortável · [plano](plans/seletor-colunas-lista-municipios.md) - **B18** filtros salvos na lista de municípios — nomear o estado URL atual (`localStorage`); acesso rápido de 2º nível sob Municípios no sidebar (hover desktop / expand mobile; sticky só no filtro salvo ativo) · ~1–1,5d · Janela 1–2 · soft: B16/B17 (barra) · cortável · [plano](plans/filtros-salvos-municipios.md) +- **B19** gerenciar assessores — `/campanha/assessores` (lista + novo + detalhe): criar/editar contas `advisor`, ver/atribuir municípios, reenviar link de senha; **só Coordenador Geral e Candidato** (`isCampaignUnrestricted`); alinha access de update/phone do `candidate` · ~1,5–2d · Janela 1–2 · sem deps duras · não cortável (onboarding Onda 0 §4 — CG sem `/admin`) · [plano](plans/gerenciar-assessores.md) ### Fill-ins abertos @@ -125,6 +126,7 @@ flowchart TD B16n["B16 Filtros no header
da lista"] B17n["B17 Seletor de colunas
da lista"] B18n["B18 Filtros salvos
+ atalho sidebar"] + B19n["B19 Gerenciar assessores"] E4R["E4R ✓ Import planilha
(seed estratégia)"] A11n["A11 ✓ Posição em votos"] E17n["E17 ✓ Tabela TI no Início"] @@ -174,11 +176,11 @@ flowchart TD B8F2 -.ZE Salvador.-> B14n ``` -Paralelizáveis agora: **E8** (A10 ✓, C12 ✓ e remodelagem em produção), **E16** (compõe o existente), **B14** (atalho geo no Início; soft B8 F2 só para ZE Salvador), **B16** (filtros no header; soft B15 ✓ já entregue), **B17** (seletor de colunas; soft B15/B16 — pousa na barra atual ou slim), **B18** (filtros salvos + submenu Municípios; soft B16/B17 — botão na barra), fill-ins (ícone de prioridade na lista, Cenário junto aos filtros — encaixa na barra slim de B16 —, O0+, RS+). **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 E8/E9). +Paralelizáveis agora: **E8** (A10 ✓, C12 ✓ e remodelagem em produção), **E16** (compõe o existente), **B14** (atalho geo no Início; soft B8 F2 só para ZE Salvador), **B16** (filtros no header; soft B15 ✓ já entregue), **B17** (seletor de colunas; soft B15/B16 — pousa na barra atual ou slim), **B18** (filtros salvos + submenu Municípios; soft B16/B17 — botão na barra), **B19** (gerenciar assessores — onboarding sem `/admin`; sem deps duras), fill-ins (ícone de prioridade na lista, Cenário junto aos filtros — encaixa na barra slim de B16 —, O0+, RS+). **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 E8/E9). ### Sequência por janela (só pendentes) -**Janela 1 — agora → 05/08 (convenções):** 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); **E17 ✓** tabela TI no Início; **B15 ✓** (sort por coluna) e **A11 ✓** (posição em votos + default `sort=votos`) entregues 2026-07-24; **B16** (filtros no header) / **B17** (seletor de colunas) / **B18** (filtros salvos + atalho sidebar) se folga de UX da lista no onboarding; 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; **E8** pode começar em paralelo; **B14** (atalho geo) se sobrar folga de campo no onboarding; Onda 0 jurídica em paralelo (externa). +**Janela 1 — agora → 05/08 (convenções):** 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); **E17 ✓** tabela TI no Início; **B15 ✓** (sort por coluna) e **A11 ✓** (posição em votos + default `sort=votos`) entregues 2026-07-24; **B19** gerenciar assessores (caminho do onboarding sem `/admin` — criar/ativar contas e carteiras); **B16** (filtros no header) / **B17** (seletor de colunas) / **B18** (filtros salvos + atalho sidebar) se folga de UX da lista no onboarding; 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; **E8** pode começar em paralelo; **B14** (atalho geo) se sobrar folga de campo no onboarding; Onda 0 jurídica em paralelo (externa). **Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; **C12 ✓** registro-fundação já em código; **E8** conta da cadeira → **E9** fila de alocação; **E16** dossiê do município (pedido O6 — compõe o existente, melhora com E8); D2 se sobrar folga. @@ -188,7 +190,7 @@ Paralelizáveis agora: **E8** (A10 ✓, C12 ✓ e remodelagem em produção), ** ### Cortes seguros / não cortáveis -**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); C2 dados reais; assimetria declarado×estimado (relação de campo); **E8**+**E9**+**C12** (a conta da cadeira, a fila e o registro ex-ante são o mínimo de "inteligência, não planilha" — e C12 é irrecuperável se não registrar durante a campanha). +**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); C2 dados reais; assimetria declarado×estimado (relação de campo); **E8**+**E9**+**C12** (a conta da cadeira, a fila e o registro ex-ante são o mínimo de "inteligência, não planilha" — e C12 é irrecuperável se não registrar durante a campanha); **B19** gerenciar assessores (onboarding Onda 0 §4 — CG/candidato não entram em `/admin`; sem esta UI o time só nasce via seed ou admin Payload). **Cortes seguros** (se o prazo apertar, nesta ordem): **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 fila E9 sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); **E14** níveis (manter `priority` alta/normal); **B8 F2** polígonos (mapa continua agregado no município; manter F1 bairros); **D2** push (adiar); **A6**; **B14** município mais próximo (lista/busca e Recentes continuam); **B18** filtros salvos (lista + filtros manuais continuam; Visitados cobrem “voltar”); **B17** seletor de colunas (tabela completa continua; scroll horizontal); **B16** filtros no header (barra de filtros atual continua a funcionar); ~~**B15** sort por coluna~~ (entregue 2026-07-24); ~~**A11**/**E17**~~ (entregues 2026-07-24); **E16** dossiê (último dos extras — pedido explícito de campo); débitos/fill-ins.