Replace all project-local .cursor/ path references with .agents/ across rules, skills, environment config, hooks, and ignore files. Keep Cursor editor paths (~/.cursor/, window.cursor, CURSOR_API_KEY) and vendored Impeccable editor-hook paths unchanged.
10 KiB
Escala e DRY pós-B2 (geometrias + scripts CLI)
Status: Fases 1–3 entregues (F1 com B3 em 2026-07-19; F2 em P3-H/scripts/lib/cli.mjs; F3 em #28)
Atualizado em: 2026-07-31 (F3: bahiaGeometryModuleFactory.ts + wrappers mun/TI)
Item do roadmap: docs/roadmap.md (Trilha B, item B5)
Responsável: —
Contexto
O B2 (mapa-bahia-geometrias.md Fase 1) entregou a fundação estática do mapa: *.topo.json de municípios e Territórios de Identidade, bahiaMunicipalityCodes, helpers em bahiaGeometries.ts, script pnpm build:geometries. A passagem /simplify de 2026-07-18 limpou dead code e churns baratos no script/codegen, mas deixou de fora dois follow-ups que os revisores (performance / reuse) marcaram como importantes e maiores que cleanup pontual:
- Carregamento eager de geometrias no cliente.
src/lib/bahiaGeometries.tsimporta os dois TopoJSON, rodatopojson-clientfeature()em 417+27 polígonos e monta índices no load do módulo — custo de bundle/memória antes de qualquer superfície Leaflet (B3). - Cache de download duplicado nos CLIs.
ensureCachedJsonemscripts/build-bahia-geometries.mjsé estruturalmente o mesmo queensureCachedZipemscripts/seed-tse-results.mjs(mkdir → hit →downloadToBuffer→ SHA-256 → write), só muda extensão/parse. Pós-A7 F2:scripts/build-bahia-tse-city-codes.mjs(pnpm build:tse-city-codes) clona o mesmo padrãoensureCachedZip— terceiro call site. Pós-A8 (capture-review-debts2026-07-20):scripts/build-bahia-demographics.mjs(pnpm build:demographics) clonaensureCachedJson— quarto call site; reforça prioridade da Fase 2 sem novo ID de roadmap. Pós-B8 F2 (capture-review-debts2026-07-26):scripts/build-municipality-zone-geometries.mjsclona o padrão comoensureCachedBinary(devolve o buffer cru, porque a entrada é um SHP e não um JSON) — quinto call site, e o primeiro que valida a decisão decacheDirpor chamada com um segundo consumidor real da mesma pasta (GEOMETRIES_CACHE_DIR, defaultdata/geometries/). O bloco todo (die/log/sha256/cacheDir/writeJson) veio junto, então a Fase 2 tem hoje 5 cópias a colapsar, não 4. - Factory duplicada mun/TI pós-B3 F1.
bahiaMunicipalityGeometries.tsebahiaTerritoryGeometries.tsrepetem o mesmo pipelinetopojson.feature→ array →Mappor chave → export do módulo indexado — só mudam topology object, propriedade de chave (codareavscode) e getter.
Este plano é o registro canônico desses follow-ups. Sem ele, o B3 pode embarcar o mapa com payload desnecessário no path default, e o próximo script de dado aberto volta a clonar o cache.
Explicitamente fora (revisores pediram skip): helpers genéricos de localeCompare('pt-BR') e de checksum/evidence nos testes int — a duplicação com bahiaTseZones.int.spec.ts é intencional e legível por domínio.
Objetivos
- Superfícies de mapa (B3) carregam geometria só sob demanda: município e território podem ser importados em separado; nenhum path de
/campanhaque não mostre mapa paga o decode TopoJSON→GeoJSON. - Scripts CLI de dado aberto (
build:geometries,db:seed:tse, futuros) compartilham um helper de cache em disco + download, sem mudar proveniência/SHA nem guards de banco. - Guardrails: sem migration, sem collection, sem Consent, sem server action de produto; B2 permanece a fonte dos artefatos estáticos versionados.
Decisões travadas
- Um item B5, três fases ordenadas. Mesmo racional do C6/C7/A7: um ID de roadmap, PRs por fase. Ordem: lazy load (impacta B3) → helper de cache CLI (barato, sem UI) → factory geometrias mun/TI (DRY pós-split B3).
- Dependência dura de B2. Este item só faz sentido com os
*.topo.jsonebahiaGeometriesjá commitados; não reabre o escopo de geração/simplificação das malhas. - 2026-07-19 (Fase 1 entregue com B3): lazy load split mun/TI via
loadMunicipalityGeometryModule/loadTerritoryGeometryModule; map islandsdynamic(..., { ssr: false }). - Cortável se o mapa permanecer fora do caminho crítico. B3 já é corte seguro no roadmap; B5 F2 (cache CLI) é ainda mais cortável — a duplicação atual é ~20 linhas. F1 só é “não cortável” no sentido de qualidade do B3 (bundle), não de calendário eleitoral.
- Não extrair helpers de sort pt-BR nem de evidence SHA nos testes. Decisão dos revisores do simplify (2026-07-18): indirection sem ganho.
- i18n e naming (AGENTS.md): identificadores em inglês (
loadMunicipalityFeatures,loadTerritoryFeatures,ensureCachedDownload), strings visíveis em pt-BR só se algum log de CLI for user-facing (hoje são logs de build).
Questões em aberto
- API pública pós-lazy: manter
getMunicipalityFeaturesync após warm, ou só async loaders? Recomendação: loaders async (loadMunicipalityGeometryModule()/loadTerritoryGeometryModule()) que resolvem o módulo já indexado; os getters sync atuais viram métodos do módulo carregado (ou ficam em submodule lazy). Evitar Promise em cada hover do mapa. Onde vive o helper de cache CLI?RESOLVIDO POR PRECEDENTE (capture-review-debtspós-B8+, 2026-07-27):scripts/lib/. O B8+ precisou compartilhar a política de escala de chão entre os dois scripts de geometria e criouscripts/lib/topology.mjs— o primeiro módulo da pasta. A F2 pousa nele (scripts/lib/cached-download.mjs) em vez de abrir um segundo endereço: dois lares para código de CLI é exatamente a duplicação que esta fase existe para remover. A recomendação anterior (src/lib/cliCachedDownload.ts) fica rejeitada —src/lib/é client-safe por contrato (.agents/rules/codebase-map.mdc) e um helper que fazmkdir/fetch/writeFilenão pertence a uma pasta que o bundle do browser pode alcançar, mesmo que hoje ninguém o importe de lá.- Unificar env
TSE_CACHE_DIR/GEOMETRIES_CACHE_DIR? Recomendação: não — um parâmetrocacheDirpor chamada; cada script mantém sua env e pasta sobdata/.
Abordagem proposta
flowchart TD
B2["B2 geometrias ✓"] --> F1
F1["Fase 1 — Lazy geometrias<br/>split mun/TI + dynamic import"]
F1 --> F2["Fase 2 — ensureCachedDownload<br/>CLI compartilhado"]
F1 --> F3["Fase 3 — buildGeometryModuleFromTopology<br/>(mun/TI DRY)"]
F1 -.preferencialmente.-> B3["B3 Leaflet"]
B2 --> B3
Fase 1 — Lazy load de geometrias (antes ou com B3)
- Partir
src/lib/bahiaGeometries.ts: deixar de avaliarfeature()+ Maps no top-level do entry que o app importa por padrão. - Submódulos ou dynamic
import()separados para municípios vs territórios (dashboard só TI; overview pode precisar dos dois sob toggle). BahiaMap/ superfícies B3:dynamic(() => import(...), { ssr: false })já previsto no plano do mapa; o import interno deve apontar para o loader lazy, não para um barrel que decoda tudo.- Testes int: importar o submodule (ou await loader) — ambiente node, sem regressão de cobertura 417/27.
- Critério de verificação: nenhum import estático de página
/campanha(exceto a ilha do mapa) puxa os*.topo.jsonno grafo de módulo (inspecionar bundle /pnpm build+ analysis leve).
Fase 2 — Helper de cache CLI
- Extrair
ensureCachedDownload({ cacheDir, key, url, ext?, logPrefix })a partir deensureCachedJson/ensureCachedZip. - Retorno:
{ url, buffer, hash }(parse JSON/ZIP fica no caller —build-bahia-geometriesjá fazJSON.parse; seed TSE usareadZipEntry). - Continuar usando
downloadToBuffer(src/lib/electionResultsZip.ts). - Migrar
scripts/build-bahia-geometries.mjs,scripts/seed-tse-results.mjs,scripts/build-bahia-tse-city-codes.mjsescripts/build-bahia-demographics.mjs; sem mudança de comportamento nem de pastasdata/geometries//data/tse//data/demographics/.
Fase 3 — Factory compartilhada mun/TI
- Extrair
buildGeometryModuleFromTopology<TProps, TKey>({ topology, objectName, keyProperty, getFeature })emsrc/lib/bahiaGeometryModuleFactory.ts(ou ao lado debahiaGeometriesTypes.ts). bahiaMunicipalityGeometries.ts/bahiaTerritoryGeometries.tsficam como thin wrappers: import JSON + chamada à factory comcodarea/code.- Testes int existentes (
bahiaGeometries.int.spec.ts) permanecem verdes — sem mudança de API pública dos loaders async. - Não fundir os dois JSON num único bundle (mantém split lazy B5 F1).
Migration: nenhuma nas três fases. Sem collection, sem Consent, sem server action de produto.
Dependências
- Dura: B2 Mapa Fase 1 (geometrias) — já entregue.
- Suave: B3 Leaflet — consome a Fase 1; se B3 implementar o lazy load, fecha F1 neste plano.
- Reusa:
downloadToBuffer(src/lib/electionResultsZip.ts), artefatos emsrc/lib/geometries/, padrões de script descripts/seed-tse-results.mjs.
Não escopo
- Implementação Leaflet / componentes de mapa → B3 (mapa-bahia-geometrias.md Fase 2).
- Camada de zonas TSE no mapa → B4.
- Helper genérico de
localeCompare('pt-BR')ou de evidence SHA nos testes int — rejeitado no simplify. BahiaMapsetStyleincremental → B6 (escala-dry-pos-b3.md).- Query duplicada lista+coroplético 2022 → A7 F4 (escala-dry-pos-a4.md).
- Mudança de qualidade/simplificação das malhas IBGE ou re-download obrigatório — só se a F2 tocar proveniência (não deve).
Referências
docs/roadmap.md(Trilha B, item B5; B2/B3)docs/plans/mapa-bahia-geometrias.md— plano pai; B3 deve preferir a F1 deste itemdocs/plans/escala-dry-pos-c2.md/escala-dry-pos-c3.md— precedente de registro pós-/simplifysrc/lib/bahiaGeometries.ts— lazy loaders asyncsrc/lib/bahiaMunicipalityGeometries.ts/bahiaTerritoryGeometries.ts— near-duplicate pós-F1src/lib/bahiaGeometriesTypes.ts— tipos*GeometryModulescripts/build-bahia-geometries.mjs—ensureCachedJsonscripts/build-bahia-demographics.mjs—ensureCachedJson(4º call site; A8)scripts/seed-tse-results.mjs—ensureCachedZipsrc/lib/electionResultsZip.ts—downloadToBuffer- AGENTS.md — naming inglês; Bahia implícita; dado estático versionado