Commit graph

303 commits

Author SHA1 Message Date
Francisco Ignácio Fontoura Solla
fb2af739fc Merge branch 'cursor/6b8a5e62' 2026-07-27 08:30:58 -03:00
Francisco Ignácio Fontoura Solla
753eae388f Teach rebase-on-main to auto-handle dirty trees via commit or stash. 2026-07-27 08:30:49 -03:00
Francisco Ignácio Fontoura Solla
423aa42185 Merge branch 'cursor/7046b47e' 2026-07-27 08:21:48 -03:00
Francisco Ignácio Fontoura Solla
7eb3ca20a6 Format the metadata resilience plan for Prettier. 2026-07-27 08:21:25 -03:00
Francisco Ignácio Fontoura Solla
fa75c355a0 Make public SEO resilient when the metadata global is empty.
resolveSiteMetadata fails open on missing URL/text so next build no longer crashes on a fresh or poisoned metadata cache.
2026-07-27 08:20:33 -03:00
Francisco Ignácio Fontoura Solla
4bbd526dfd Merge branch 'cursor/58bad531' 2026-07-27 02:04:47 -03:00
Francisco Ignácio Fontoura Solla
9afa474879 Register the B8+ review debts from capture-review-debts.
Dos 20 achados do /simplify, 12 entraram na entrega e 3 ja estavam
cobertos (o modo `territory` pertence ao E12 por rabbit hole do plano, a
shoelace duplicada e descarte declarado, e o B5 F2 ja contava os 5 call
sites dos helpers de CLI). Sobraram dois fatos sem registro.

O revisor de perf mediu que `quantize` aplica um unico numero de digitos
aos dois eixos: a grade de ~103 m e exata em longitude e a latitude segue
o aspect ratio do bbox (102,8x109,2 m no estado, 102,9x80,1 m em
Salvador). Corrigir exige `{scale, translate}` explicito e reescrever os
tres artefatos, perdendo a prova de byte-identidade da malha estadual por
um erro de ~9% num eixo num mapa que para em maxZoom 10 — entao vira
gatilho no plano-pai, nao item de roadmap.

E o B8+ respondeu por precedente uma pergunta em aberto do B5 F2: com
`scripts/lib/topology.mjs`, o helper de cache da F2 pousa em
`scripts/lib/` em vez de abrir um segundo endereco. A recomendacao antiga
(`src/lib/cliCachedDownload.ts`) fica rejeitada: `src/lib/` e client-safe
por contrato e nao e lugar de codigo que faz mkdir/fetch/writeFile.

Sem novos IDs de roadmap.
2026-07-27 02:04:09 -03:00
Francisco Ignácio Fontoura Solla
2c30944593 B8+: escala das malhas em unidades reais, contrato do map key e degradacao
F1 — a tolerancia de simplificacao e os digitos de quantizacao viraram
politica em unidades de solo em scripts/lib/topology.mjs, consumida pelos
dois scripts de geometria. Os quantis 0.35 copiados cortavam as 19 ZE de
Salvador a ~0.6 m2 e os 417 municipios a ~1.78 km2; com tolerancia
absoluta e a grade de ~103 m do estado, a malha das zonas caiu de 480 para
30.2 pontos/feature. bahia-municipalities segue byte-identico e o tripwire
de bytes deu lugar a pontos/feature.

F2 — featureMapKey passou a ser asserido sobre features reais das tres
malhas comprometidas (417 codarea, 27 code, 19 slug), e o comentario de
MAP_KEY_PROPERTIES agora diz o que o codigo faz: as propriedades sao
mutuamente exclusivas, nao ordenadas por especificidade.

F3 — a malha das zonas e um detalhe sobre o mapa, nao o mapa: se o chunk
falha, Salvador cai no poligono municipal que ja esta desenhado embaixo e
o resto da Bahia continua funcionando, com o motivo logado. A ressalva de
aproximacao saiu do gate de comparacao e passou a depender de
bundle.hasZoneMunicipalities, derivado no servidor de municipality.kind.

F4 — orfaos deletados: os dois acessores sem chamador, isZoneMapKey (o
servidor ja sabe a regra), o wrapper simplifyTopology e o helper
polygonCount com um branch inalcancavel. MunicipalityMapNavigation
colapsou em string | undefined e MunicipalitiesByMapKey em
Record<string, string>.
2026-07-27 01:59:36 -03:00
Francisco Ignácio Fontoura Solla
dd8c4d1d27 Fix Prettier formatting in roadmap after users.roles docs update. 2026-07-27 01:54:58 -03:00
Francisco Ignácio Fontoura Solla
bb5ac362ed Add users.roles so Payload /admin can separate editors from admins.
Editors publish post/tag/media without PII or campaign access; existing users backfill as admin.
2026-07-27 01:54:29 -03:00
Francisco Ignácio Fontoura Solla
a5b6341ccb docs: plan fill-in to drop zone list from campaign home map
After B8 F2 polygons, the textual zone breakdown under the dashboard map is redundant; register the removal in the roadmap and align B8+/B8 plan notes.
2026-07-27 00:00:29 -03:00
Francisco Ignácio Fontoura Solla
75cdea38b4 Register the B8 F2 review debts as B8+.
The three /simplify reviewers reported after the merge, so their findings
became a fill-in instead of a fix. The lot is anchored on a measurement that
contradicts "small mesh, small artifact": the 19 Salvador zones carry 9,120 arc
points against 9,139 for ALL 417 Bahia municipalities, because both simplify
constants are relative to the file's own bbox and were copied verbatim from the
sibling script -- the same "1e4 grid" is ~103 m statewide and ~3.9 m in a city
26x narrower, on a map that stops at maxZoom 10.

That measurement inverts one reviewer's recommendation, which is why it is
written down rather than left to be re-derived: extracting the shared constants
as they stand would cement the defect and give it the authority of a policy, so
what gets shared between the two build scripts is the ground scale.

The other three: nothing runs featureMapKey over a real feature outside the
e2e, so the three spellings of the map key can drift and paint grey with no
error anywhere; Promise.all over both meshes plus the memoized rejection turned
a 19-polygon chunk into a single point of failure for the whole map, where the
municipal mesh alone used to suffice; and the "approximate, not the official TSE
boundary" caveat hangs off the zone LIST's render condition, so it vanishes in
comparison mode while the approximate mesh is still painted.

Also records what this delivery orphaned (getTerritoryFeature lost its last
production caller when fitMapToHighlights stopped consulting geometry modules,
and knip cannot see it because specs are entry points), and bumps B5 F2's
call-site counter to five.
2026-07-26 23:35:49 -03:00
Francisco Ignácio Fontoura Solla
1d3ec3c772 Merge branch 'cursor/a2a2fd95' 2026-07-26 23:26:15 -03:00
Francisco Ignácio Fontoura Solla
7b70322118 Document B8 F2 and apply the simplify pass findings.
Records the Salvador zone delivery where the next agent will look for it —
roadmap, plan as-built, notebook and the geometry/map sections of AGENTS.md —
including the two measurements that are easy to re-derive wrongly (the bay
water in Salvador's municipal polygon, and ZE 16 wrapping ZE 14, which is why
neither an area ratio nor a centroid check is a valid overlap test).

Simplify fixes: the build script re-indexes simplified geometries by a unique
id instead of neighbourhood name, so a repeated name in a future mesh cannot
silently collapse two polygons inside a committed artifact; a stale comment
claiming zone polygons do not exist; and two int assertions that the Fase 0
catalog hydration turned tautological.

Ledgers the Aikido outage: the scanner returns Opengrep exit 2 for any input,
so the mandatory per-change scan was skipped, not passed.
2026-07-26 23:25:42 -03:00
Francisco Ignácio Fontoura Solla
84e796ab49 B8 F2: polígonos das ZE de Salvador e re-keying do coroplético por map key
Salvador era um polígono só carregando os votos da cidade inteira, então o
coroplético não conseguia responder "qual pedaço de Salvador está fraco / é
meu". As 19 ZE ganham polígono próprio, dissolvido da malha de bairros do IBGE
(Censo 2022) pela lista de circunscrição da RA 02/2017 do TRE-BA, e o mapa passa
a pintar por map key (slug para as zonas, ibgeCode para o resto) em vez de
codarea. O polígono municipal de Salvador continua desenhado por baixo, sem dado
e sem interação, para não abrir buraco no contorno do estado.

Aproximação declarada na copy e no cabeçalho do script: o TRE publica a
circunscrição como lista de bairros, então o bairro é o átomo geométrico —
Periperi, que a resolução corta na Rua das Pedrinhas, vai inteiro para a ZE 17.
competitiveRank segue por cidade (o artefato TSE só tem federalRankByIbgeCode).

Sem migration, sem collection, sem action, sem Consent.
2026-07-26 23:08:09 -03:00
Francisco Ignácio Fontoura Solla
2ac3a3e512 Fix Prettier on B40/B41 plan docs. 2026-07-26 22:17:04 -03:00
Francisco Ignácio Fontoura Solla
6fc3bd04b2 Register B39–B42: session persistence, biometrics, and municipality list UX. 2026-07-26 22:15:39 -03:00
Francisco Ignácio Fontoura Solla
f4e7b4f1e0 Merge branch 'cursor/3907847e' 2026-07-26 21:27:45 -03:00
Francisco Ignácio Fontoura Solla
a911c7191f chore: format after E12 (pre-push gate). 2026-07-26 21:27:23 -03:00
Francisco Ignácio Fontoura Solla
f06e573f15 E12: camada TI com rollups MAUP e benchmark intra-TI.
Estende territórios com cobertura/captura/classe agregadas e compara captura do município aos pares do mesmo TI no card Conta da cadeira.
2026-07-26 21:26:47 -03:00
Francisco Ignácio Fontoura Solla
ba90a3b2dc Merge branch 'cursor/6b2ac7b2' 2026-07-26 21:16:40 -03:00
Francisco Ignácio Fontoura Solla
06ea1ff295 B36: chips de lideranças na lista de dobradinhas
Espelha a aresta leadership.stateDeputies do B31 do lado do deputado, com célula compartilhada bidirecional, loadLeadershipOptions e casca própria de formActions.
2026-07-26 21:15:44 -03:00
Francisco Ignácio Fontoura Solla
162d4c10fe perf(campanha): roda as funções em gru1 e encurta o waterfall de queries
As funções rodavam no default iad1 (Virgínia) enquanto o Neon fica em
sa-east-1 (São Paulo): cada query do Payload atravessava o equador duas
vezes (~120 ms por ida-e-volta) e uma página do /campanha faz uma dúzia.
Medido em produção: 410-470 ms de TTFB para um 307 que não toca o banco.

- vercel.json fixa regions: [gru1], mesma região AWS do banco (Hobby
  permite uma região, mas permite escolher qual).
- loadStatewideSuggestedGoals passa a ser exportada e é disparada dentro
  do Promise.all que já lê o escopo, na lista e no dashboard: o read do
  global campaignGoals saía sozinho no fim da requisição.
- loadMunicipalityScope lê os pledges em paralelo quando o ator é
  irrestrito e não há filtro (aí o IN de 435 ids não exclui nada);
  qualquer escopo mais estreito continua esperando os ids.

Débitos medidos no caminho e registrados em TECH-DEBT: o fan-out de
leituras do ator em getFreshCampaignUser (~5 por render) e a leitura
dupla em authenticateCampaignToken.
2026-07-26 21:00:06 -03:00
Francisco Ignácio Fontoura Solla
8b8a242073 Merge branch 'cursor/c2dfe0d1' 2026-07-26 20:47:36 -03:00
Francisco Ignácio Fontoura Solla
d6455df5e7 B30: convite WhatsApp na lista de lideranças
Staff gera convite de autopreenchimento na coluna de ações da lista, reutilizando createCampaignInvite e alinhando validação de celular ao link de contato do B28.
2026-07-26 20:47:11 -03:00
Francisco Ignácio Fontoura Solla
013f167d47 Fix /campanha/liderancas 500: RSC-safe phone display
CampaignCopyableCell is a Client Component; passing formatDisplay as a
function from the leaderships RSC page broke serialization in production.
Use a preformatted displayValue string at the call sites instead.
2026-07-26 20:16:01 -03:00
Francisco Ignácio Fontoura Solla
3d2cd4e163 Merge branch 'cursor/b37ff931' 2026-07-26 19:54:39 -03:00
Francisco Ignácio Fontoura Solla
d869e6a653 docs(B31): marca adiado do Context como resolvido pela 2ª rodada de /simplify 2026-07-26 19:53:57 -03:00
Francisco Ignácio Fontoura Solla
ef3d5b6c22 B31: coluna de dobradinhas na lista de lideranças
Coluna "Dobradinhas" em /campanha/liderancas com chips clicáveis
para /campanha/dobradinhas/<slug> e edição via Popover+Command
(padrão B27, escrita via server action delta + lock, como B19).

- LeadershipRowViewModel.stateDeputies (StateDeputySummary[]) via
  loadStateDeputySummaries, substituindo stateDeputyNames.
- setLeadershipStateDeputyMembership(Record): delta com
  withPayloadTransaction + acquireTextAdvisoryLocks + revalidatePath.
- nextStateDeputyIdsAfterMembership (lib/leadershipStateDeputyMembership.ts),
  espelha nextAdvisorIdsAfterMembership.
- loadStateDeputyOptions estendido com plainName/party/slug.
- Catálogo entregue uma vez por tabela via leadershipColumns(options)
  (columns factory, mesmo precedente de municipalityListColumns).

/simplify (1ª rodada): corrige revert otimista por delta (não pelo
snapshot inteiro), remove fallback silencioso na adição, extrai
sameIdSet compartilhado com MunicipalityListAdvisorsControl (B27), e
evita leitura+revalidação supérfluas no caminho no-op da action.

/simplify (2ª rodada, pós-rebase): troca o StateDeputyOptionsProvider
(Context) inicial pela columns factory acima — a 1ª rodada tinha
validado o Context por falta da evidência de que MunicipalityList.tsx
já resolve o mesmo problema sem Context; corrige revalidatePath para
o id concreto em vez do template dinâmico '/campanha/liderancas/[id]'
(que invalidava toda ficha a cada toggle); remove non-null assertion
movendo a guarda para dentro do setCurrent updater.

Gate verde: tsc, lint --max-warnings=0, format:check, unit 530/530,
int 406/406 (incl. os 6 de campaignLeadershipStateDeputyMembership),
build. Aikido: 0 achados nos arquivos novos/editados.
2026-07-26 19:50:15 -03:00
Francisco Ignácio Fontoura Solla
2ee9a4fe23 Merge branch 'cursor/4c694ada' 2026-07-26 19:00:16 -03:00
Francisco Ignácio Fontoura Solla
a60200cf6c B25: link do território na lista de municípios
Fecha a navegação inversa para /campanha/territorios com âncoras estáveis compartilhadas entre lista e destino.
2026-07-26 18:59:49 -03:00
Francisco Ignácio Fontoura Solla
e5dd0c3b3f Merge branch 'cursor/716686b9' 2026-07-26 18:47:45 -03:00
Francisco Ignácio Fontoura Solla
d38997686d fix(mapa): limita pan/zoom do BahiaMap ao estado
Evita que arrastar ou dar zoom-out no mapa do Início tire o usuário da Bahia por engano em campo; maxBounds + minZoom runtime alinhados ao fitBounds.
2026-07-26 18:47:06 -03:00
Francisco Ignácio Fontoura Solla
0942686433 Merge branch 'cursor/617e8b7f' 2026-07-26 18:40:58 -03:00
Francisco Ignácio Fontoura Solla
2086ed50e9 B38: sidebar recolhido em tablet no shell /campanha
Rail offcanvas com cookie, default colapsado em viewport tablet e trigger visível a partir de md; fecha o item no roadmap.
2026-07-26 18:40:31 -03:00
Francisco Ignácio Fontoura Solla
f96a2db2cb Add roadmap-items skill for batch roadmap registration.
Orchestrates roadmap-item per idea with consolidated roadmap edits (IDs, mermaid, Ordem).
2026-07-26 18:29:53 -03:00
Francisco Ignácio Fontoura Solla
97c0953a03 format: prettier no plano do B38 (sidebar recolhido em tablet) 2026-07-26 18:09:24 -03:00
Francisco Ignácio Fontoura Solla
93d10d15a7 Merge branch 'cursor/0c5cae5d'
Conflito unico em docs/roadmap.md, nas cinco superficies em que B14 aparece
(linha "Atualizado em", bullet do item, no do grafo, Paralelizaveis/Janela 1 e
Cortes seguros): main tinha B14 pendente mais os registros de hoje (B38 e o
fill-in de limitar o mapa a Bahia), a feature tinha B14 entregue sem eles.
Resolvido preservando os dois lados em todas elas.
2026-07-26 18:08:36 -03:00
Francisco Ignácio Fontoura Solla
a305188e69 docs(B38): registra sidebar recolhido em tablet + fill-in de limitar o mapa a Bahia
Registro de roadmap-item de dois pedidos de produto de 2026-07-26, commitado
como estava na arvore de trabalho do checkout principal:

- B38 sidebar recolhido em tablet (`collapsible="offcanvas"`, trigger em `md+`,
  default por faixa de viewport com o cookie `sidebar_state` vencendo apos o
  toggle) + entrada no grafo de dependencias.
- Fill-in: limitar pan/zoom do mapa da Bahia a `BAHIA_BOUNDS` (`maxBounds` com
  folga de 10%, `maxBoundsViscosity: 1` e `minZoom` calculado no
  `ResizeObserver` que ja existe), so em `BahiaMap.tsx`.
- Nota de dependencia soft do B18 para o B38: com o rail recolhido, o submenu
  de filtros salvos precisa tratar collapsed como "abrir rail".
2026-07-26 18:05:51 -03:00
Francisco Ignácio Fontoura Solla
5ace1a04b2 simplify(B14): corrige o mecanismo da fronteira, estado unsupported e duplicatas
Terceira rodada de /simplify sobre o B14, com dois enunciados errados corrigidos:

- A propriedade da fronteira compartilhada NAO vem da ordem do `.find()`, e sim
  da convencao meio-aberta do ray casting (`point.lng < crossingLng`): a aresta
  pertence ao poligono cujo interior fica a leste (ao norte, se horizontal). Tres
  comentarios diziam o contrario, e o do unit test dizia "oeste" enquanto
  afirmava `east`.
- O plano dizia que o `Promise.all` esconde o decode da malha atras do fix de
  GPS. Nao esconde: o decode roda no topo do modulo, quando o chunk executa. O
  paralelo continua certo por outro motivo (decode ~2 ms; sequencial jogaria os
  42 KB no caminho critico quando o fix vem do cache).

Simplificacoes: `unsupported` virou estado proprio, entao `failureCopy` deixa de
ser anulavel e caem duas leituras defensivas inalcancaveis; "Voce esta em " era
escrito duas vezes (frase sr-only + JSX), o que anulava justamente a invariante
que o comentario alegava; `zoneCityHrefs` virou `Partial` porque a busca falha
para os 416 municipios sem zona e o guard existia sem o tipo exigir;
`MunicipalityForIbgeIndex` era duplicata campo-a-campo de `AccessibleMunicipality`;
`kmFormatter` duplicava `oneDecimalFormatter`; `RecentlyVisitedCard` carregava
tres props que nenhum caller passa (dez condicionais mortas); e o bbox de feature
estava escrito em dois specs (agora `tests/helpers/featureBounds.ts`, compartilhado
por vitest e Playwright).

Copy: fora da Bahia sem nada por perto dizia a mesma coisa em duas frases e nunca
dizia que voce estava fora do estado. Agora o headline afirma o fato e o detalhe
reusa "Nenhum municipio da sua carteira por perto.".

Tambem limpa um achado de knip herdado do main (`MunicipalityPoliticalTrendViewModel`
exportado sem uso) e registra no plano o caminho afiado do debito de payload RSC
mais dois adiados com gatilho.
2026-07-26 18:03:44 -03:00
Francisco Ignácio Fontoura Solla
e482005ce0 B14: municipio mais proximo (atalho geo no Inicio staff)
Card "Onde estou" no dashboard staff: point-in-polygon contra a malha
municipal que o painel do mapa ja carrega, sem artefato de centroides,
sem migration, sem collection, sem server action e sem Consent -- a
posicao e lida por navigator.geolocation e casada no browser, nunca sai
do aparelho.

- src/lib/municipalityProximity.ts: haversine, ray casting, centroide
  ponderado por area e uniao discriminada
  (inScope | zoneCity | outOfScope | outsideBahia)
- src/utilities/campaignGeolocation.ts: flag de sessao, leitura guardada
  de permissao e requestCurrentPosition com falha tipada
- Salvador nao ganha ZE chutada: link para a lista filtrada, com href
  serializado no servidor (importar o serializador canonico no cliente
  custava 21 kB de First Load JS)
- fallback fora da carteira ordenado por centroide, teto de 150 km
- fix grosseiro (>10 km) mostra o resultado com ressalva de precisao

/campanha First Load JS: 265 -> 268 kB.
2026-07-26 17:33:33 -03:00
Francisco Ignácio Fontoura Solla
f63cd2cd50 Merge branch 'cursor/2f22c6f6' 2026-07-26 16:28:12 -03:00
Francisco Ignácio Fontoura Solla
ca1a2081fe docs(B33): registra débito de engenharia do /simplify (B33+)
Fecha a entrega do B33 registrando o achado do /simplify que sobrou maior
que cleanup: StateDeputyFilters é o 3º call site do mesmo scaffold de
navegação/debounce de TerritoryFilters/MunicipalityFilters. Novo fill-in
B33+ (plano escala-dry-pos-b33.md) extrai useCampaignListFilterNavigation.
Agregado SQL do facet de partido formalizado como Adiado com gatilho no
plano-pai; referência cruzada obsoleta em escala-dry-pos-e10.md corrigida
(party é texto livre, não enum fechado — o gatilho ali não disparou).
2026-07-26 16:27:37 -03:00
Francisco Ignácio Fontoura Solla
8c55daffb2 B33: ordenação e filtro no header da lista de dobradinhas
- stateDeputyListUrl.ts / stateDeputyListFilters.ts: contrato de URL/filtro
  próprio (sort/dir, filtro de Partido multi + sentinela "Sem partido"),
  espelhando municipalityListUrl.ts (paginação/resolveListUrl) e
  territoryListUrl.ts (sort keys/labels)
- stateDeputyData.ts: where/sort resolvidos no banco + facet de partido
  (loadStateDeputyPartyFacet), select estreito na query principal
- StateDeputySortableHead / StateDeputyHeaderFilter / StateDeputyFilters:
  wrappers finos sobre o chrome compartilhado (shared/CampaignSortableHead,
  CampaignHeaderFilterPopover), paridade mobile incluída
- dobradinhas/page.tsx: redirect canônico em duas fases, CampaignTable
  sempre montada com caption + empty state, resumo de ordenação aria-live
- Correção pós-entrega (design review): "Sem partido" no mobile + caption
  da tabela + copy plural do empty state
- /simplify (1ª rodada): buildStateDeputyPartyOptions compartilhado, remove
  discriminante filterParam redundante, dedupe/if mortos em
  stateDeputyListUrl.ts
- /simplify (2ª rodada, pós-rebase): remove cast/type morto em
  stateDeputyData.ts, stateDeputyColumns usa StateDeputyFilterOption em vez
  de duplicar a forma, inlinea parsePartiesParam; extrai truncatedNamesLabel
  compartilhado em campaignListUrl.ts (3º call site — municipalityListFilters
  e territoryListFilters tinham a mesma função com nomes distintos)

Gate verde (tsc, lint, format, check:cycles, 396 testes, build); Aikido
0 achados nos 7 arquivos tocados nesta rodada. Docs:
docs/plans/ordenacao-filtros-lista-dobradinhas.md, docs/roadmap.md,
.cursor/rules/projects/nucleos-eleitorais.mdc.
2026-07-26 16:21:40 -03:00
Francisco Ignácio Fontoura Solla
a04f4dc191 Merge branch 'cursor/eb00cad1' 2026-07-26 15:33:34 -03:00
Francisco Ignácio Fontoura Solla
7cafc2297b docs(B28): registra débitos do /simplify no plano-pai (adiado com gatilho)
- botão WhatsApp por linha duplicado (2 call sites) — gatilho: 3º site
- copyText vs copyMessage em SupporterShareKit — gatilho: 3º site de copy-to-clipboard
2026-07-26 15:32:57 -03:00
Francisco Ignácio Fontoura Solla
9150092e17 B28: e-mail + celular na lista de lideranças
- colunas E-mail/Celular (copy-on-click, toast, "—" vazio) + coluna de
  Ações com WhatsApp (wa.me), no padrão de /campanha/assessores (B19)
- shared/CampaignCopyableCell.tsx nasce compartilhado já no 2º call site;
  AdvisorsTable.tsx migrado no mesmo PR para consumi-lo
- whatsAppHrefForPhone promovido a lib/phone.ts
- leadershipData.ts: contactSummary (+ email), loadLeadershipDetail
  deduplicado (reusa row.email)
- simplify: remove try/catch morto em whatsAppHrefForPhone, remove casts
  redundantes em contactSummary, displayEmail retorna string | null direto
- docs: nucleos-eleitorais.mdc, roadmap.md, plano B28 marcados entregues
2026-07-26 15:30:39 -03:00
Francisco Ignácio Fontoura Solla
5ab25228e3 Merge branch 'cursor/32508717' 2026-07-26 14:06:25 -03:00
Francisco Ignácio Fontoura Solla
b3283ee061 B24: auto-save e justificativa no popover de Tendência.
Remove o botão Salvar da lista de municípios; grava via POST political-trend sem revalidar a lista, com flush coalescido e feedback na célula.
2026-07-26 14:05:36 -03:00
Francisco Ignácio Fontoura Solla
e0d0936fab Merge branch 'cursor/0578e7e1' 2026-07-26 05:13:09 -03:00