Commit graph

1025 commits

Author SHA1 Message Date
Francisco Solla
2b8213eff6 test(B193): adapta o e2e C109 do main ao contrato do card novo — rodapé expandível após registrar
O teste C109 (merge do main via rebase) esperava o CTA 'Registrar atualização'
sempre visível no card; o card B193 troca o rodapé para a linha expandível
'Última atualização' após o primeiro registro (CTA dentro da expansão). A
cobertura da C109 (abrir/registrar/fechar/reabrir + sheet de nível) é mantida.
2026-08-10 11:23:49 -03:00
Francisco Solla
5d38bed12f test(B193): e2e robustos — cards ancorados pelo link exato do nome e C106 com timestamps determinísticos
- '?q=' casa prefixos de nome ('Coribe' também retorna 'São Félix do Coribe'):
  os cards B193 são ancorados por has:{ link do nome exato }.
- C106: os 8 updates seedados podiam compartilhar o mesmo now() de transação,
  tornando o sort -createdAt instável (flake '#8 no topo'); createdAt
  explícito e crescente.
2026-08-10 11:23:49 -03:00
Francisco Solla
b210939226 refactor(B193): simplify fixes pós-revisão — mapper único de updates, API de trigger uniforme, grupos de relação extraídos, hardening do sheet host
- Extrai toMunicipalityUpdateViewModel/loadUpdateAuthorNames compartilhados
  (feed + batch da lista); remove avatarClassName morto.
- MunicipalityRelationTriggerProps compartilhado nos 3 wrappers; triggerClassName
  no ExpectedVotesControl; ChipLabel/RelationGroup/CityDash no card.
- Sheet host: cleanup de unmount no overlay (sem drawer órfão), registerFooter
  valida open, branch nulo do handleOpenChange limpa footer; chrome freeze
  documentado.
- A11y/UX: ícone de alerta no rodapé frio (WCAG 1.4.1), iniciais de avatar
  aria-hidden, chip sem_base sem target morto, label da cidade com Tendência,
  aria-label da barra anuncia os 3 cenários, card layout 'colocação · votos'.
- E2E: settleStream no B184 (flake da cópia transitória do streaming), cards
  ancorados por hasText (q= casa prefixos), cor da borda via classe do token.
2026-08-10 11:23:49 -03:00
Francisco Solla
3d8dbae61f feat(B193): card de município mobile denso com edit-where-you-see e rodapé de atualização expandível
- Card denso (wireframe): posição de voto à direita, barra de cenários com
  marcador do ativo, chips classe/tendência/nível (label à esquerda), 3 grupos
  de avatares com wrap (um por entidade), rodapé Última atualização + chevron
  que expande revelando o último card de atualização + CTA registrar; sem
  atualização → CTA direto; prioridade = borda lateral direita 6px primary.
- Controles ganham prop trigger/triggerClassName (precedente RelationChipCell);
  MunicipalityRelationAvatarStack ganha modo wrap.
- C109 (sheet quebra ao abrir) corrigida: sessões do sheet host keyed por id
  (imune à identidade dos callbacks recriados por render) e footer customizado
  renderizado pelo host dentro da árvore do Drawer (portal perdia o Dialog
  context — reproduzido digitando no sheet).
- Dado: MunicipalityListViewModel.lastUpdate (batch por página, scoped).
- E2E novos (barra/votos, chip nível, borda prioridade, expansão/CTA vazio).
Sem migration/access/Consent.
2026-08-10 11:23:49 -03:00
Francisco Solla
da20ed6190 refactor(OPS26): simplify fixes do /simplify — marcador TEQO_ prefixado, helper printLaunchDirective, IFS pinado no launch, docs reconciliadas 2026-08-10 10:58:55 -03:00
Francisco Solla
344fe843df feat(OPS26): worktree next/plan no terminal abrem o opencode com presets (modelo deepseek-v4-flash, auto-approve, skill inicial) 2026-08-10 10:51:19 -03:00
Francisco Solla
7a4279b281 feat(OPS25): comandos opencode /work-issue e /plan-issue como atalhos diretos às skills
Dois comandos novos (.opencode/commands/) que instruem o agente a
carregar a skill de mesmo nome pela ferramenta de skills e seguir o
fluxo dela ponta a ponta, com $ARGUMENTS repassado. Frontmatter com
description (autocomplete do TUI) e model deepseek/deepseek-v4-flash
(alinhado ao launch da OPS26). Guard unit pina o acoplamento
comando↔skill para renomes não quebrarem silenciosamente.
2026-08-10 10:25:26 -03:00
Francisco Solla
972c6ac8ba
Merge pull request #577 from fsolla/D7-sticky-dos-headers-da-tabela-desktop-quebrado-b41-morto-t
fix(D7): sticky dos headers das tabelas desktop volta a engajar (overflow-x-clip)
2026-08-10 09:52:54 -03:00
Francisco Solla
bc661821c8
Merge pull request #574 from fsolla/B192-sollinha-follow-ups-sugeridos-apos-cada-resposta-instru
B192 — Sollinha: follow-ups sugeridos após cada resposta (instruídos no prompt, extraídos pela UI)
2026-08-10 09:37:45 -03:00
Francisco Solla
50aa11eb95
Merge pull request #575 from fsolla/B186-sollinha-prioridades-do-momento-gestao
B186 — Sollinha: prioridades do momento (getMunicipalityPriorities)
2026-08-10 09:20:18 -03:00
Francisco Solla
2fa38a08f5 style(B192): format (prettier) 2026-08-10 09:13:38 -03:00
Francisco Solla
382c4c46ec feat(B192): follow-ups sugeridos após cada resposta do Sollinha (bloco no prompt + chips extraídos) 2026-08-10 09:13:38 -03:00
Francisco Solla
00a4b7f0c7 fix(D7): sticky dos headers das tabelas desktop volta a engajar (overflow-x-clip)
O wrapper do ui/Table (`overflow-x-auto`, depois `overflow-x-hidden` via
container queries) é um scroll container nos dois eixos — o overflow-y computa
para `auto` — e o th `position: sticky; top: 0` declarado pelo B41 resolvia
contra ele, que nunca rola (o scroll vertical real é o CampaignContentScroll da
shell). A feature estava silenciosamente morta desde o B41.

`overflow-x: clip` (permitido ao lado de `overflow-y: visible`, sem promover
visible→auto) NÃO cria scroll container: o th volta a resolver contra o
scrollport da shell e engaja. Troca nas duas listas com sticky declarado
(MunicipalityList, TerritoryList), preservando o fallback `overflow-x-auto`
para browsers sem container queries. As demais listas (lideranças, demandas,
dobradinhas, apoiadores) estouram em 1440px e dependem do scroller — não
tocadas.

Validado nos 3 engines (Chromium/Firefox/WebKit): th 174 → 69 (topo do content
box do scrollport, padding da shell acima — mesmo ponto onde o omnibox mobile
do B184 cola). E2E novo pina o engajamento com asserção bilateral + guarda de
computed style; unit pinado no novo contrato; JSDoc do containerClassName
atualizado. Closes #543.
2026-08-10 09:11:38 -03:00
Francisco Solla
e9d7f0da39
Merge pull request #567 from fsolla/C109-quick-edit-sheet-mobile-da-lista-de-municipios-quebra-a
fix(campanha): quick-edit sheet mobile com footer custom quebrava ao abrir (C109)
2026-08-10 08:49:39 -03:00
Francisco Solla
b354ad55d6 style(B186): simplify fixes — NaN tiebreak, janela simétrica, testes sem bomb-relógio
- byGravity estagnação: Infinity - Infinity (NaN) matava o tiebreak para dois
  municípios nunca-sinal (topo da lista) — comparação explícita + teste
- fronteira da janela: módulo puro inclusivo (>= cutoff) e query da tool em
  greater_than_equal (antes greater_than, assimétrico) + testes 30/31 dias
- ultimoSinalAtrasDias reporta a idade da atualização decisiva no balde sinal
  (E9 podia devolver null ao lado de evidência 'há 2 dias' em linha stale)
- fixtures da tool com datas relativas a Date.now() (absolutas apodreceriam
  em ~30 dias); assert de user: ctx.user no caminho do assessor
- options type em inglês (windowDays/reason/sortBy); MunicipalityPriorityReason
  não exportado (knip); agora passado da tool (um relógio)
- expectedVotes select estreito { central: true }; sort '-createdAt' removido
  (módulo re-deriva o decisivo); NARROW_SCOPE_HINT compartilhado em aiToolScope
- baseResponse unifica o shape (short-circuit + retorno); docblock órfão da
  extração removido; prompt: coordenação/candidatura
- impl plan: registro de execução + shape entregue
2026-08-10 08:37:49 -03:00
Francisco Solla
11886a653d feat(B186): Sollinha — prioridades do momento (getMunicipalityPriorities)
Tool nova do chat que responde "quais devem ser minhas prioridades neste
momento?" com top N municípios do escopo, cada um com uma linha de
evidência e o critério declarado. Ranking derivado na hora em módulo puro
(municipalityPriorities.ts): 3 baldes por gravidade (sinal desfavorável
recente > estagnação > potencial alto com nível baixo), exclusão de
atualização recente favorável, frescor E9 (max lastUpdateAt/lastPledgeAt),
potencial = expectedVotes.central com fallback nos válidos 2022 (artefato).

Resolver de escopo da B185 extraído para aiToolScope.ts (2º consumidor;
getPendingLeaderships refatorado sem mudança de comportamento). Gate staff
fail-closed, leituras overrideAccess: false + user. Seção nova no system
prompt. 38 unit tests novos; 1772 unit + 680 int verdes; build OK.
2026-08-10 08:37:49 -03:00
Francisco Solla
2b3c48e063 refactor(B189): simplify — nomes alinhados à B186, loader de nomes compartilhado, guard de escopo vazio
- aiToolScope.ts: AIToolScope/AIToolScopeKind/AIToolScopeMunicipality
  (mesmos nomes da extração da B186 em voo — conflito de merge vira trivial)
- loadNamesByIds da B185 e loadAdvisorNames da B189 fundidos em
  aiToolQueries.ts (loadAIToolNamesByIds) — don't twin
- guard fail-closed de escopo vazio (assessor fora do portfólio) + teste
- sortStale sem ?? 0 (partição garante ISO) — type honesty
- prompt: exemplo de Salvador com limiar estrito; index ordenado
2026-08-10 08:12:00 -03:00
Francisco Solla
6027aac941 feat(B189): Sollinha — municípios sem atualização recente (tool getMunicipalitiesWithoutUpdate)
Tool nova da família B185/B186: cobertura do acompanhamento por recência
de municipality.lastUpdateAt (derivado dos hooks de municipalityUpdate),
limiar default 30 ajustável, lista exaustiva com total, nunca atualizados
no topo, assessores por item. Resolver de escopo extraído para
aiToolScope.ts (2o call site; B186 sera o 3o). Gate staff fail-closed;
unit tests 14 novos + regressao B185 verde.
2026-08-10 08:12:00 -03:00
Francisco Solla
5ad2f7c722 fix(campanha): quick-edit sheet mobile com footer custom quebrava ao abrir (C109)
O footer custom ('Registrar atualização' B42, 'Registrar movimento' E14) era
portalado da célula para o DrawerFooter do shared-sheet: createPortal preserva
o contexto React da origem (sem Drawer.Root), então o DrawerCloseButton
(base-ui DialogClose) crashava em useDialogRootContext() e o sheet nunca
renderizava.

O footer agora viaja no chrome do CampaignListSheetHost (mesmo mecanismo de
title/description) e é renderizado inline no DrawerFooter — contexto correto
por construção. O elemento continua criado na célula (estado do form e
associação form attribute preservados). footerPortalRef/attachFooterPortal/
hasCustomFooter removidos; fallback sem provider e popover intactos.

Cobertura: unit do shared path (abre/fecha/reabre + submit via form attribute)
e e2e mobile no describe B42 (abre, registra, fecha — incluindo o sheet do
nível E14).
2026-08-10 08:11:05 -03:00
Francisco Solla
733b019f2b
Merge pull request #560 from fsolla/B190-sollinha-municipios-sem-dobradinha-dobradinhas-orfas-co
B190 — Sollinha: municípios sem dobradinha + dobradinhas órfãs (cobertura de parcerias)
2026-08-10 07:35:01 -03:00
Francisco Solla
7bdd7f91b1
Merge pull request #559 from fsolla/B187-sollinha-links-com-aparencia-de-link-nas-respostas
B187 — Sollinha: links com aparência de link nas respostas
2026-08-10 07:34:36 -03:00
Francisco Solla
4373599192 feat(B190): Sollinha — cobertura de parcerias (municípios sem dobradinha + dobradinhas órfãs)
Nova tool getPartnershipCoverage com dois modos: municípios do escopo sem
nenhuma dobradinha vinculada (Salvador agrupada por cidade, drill-down por
ZE) e dobradinhas órfãs (sem município vinculado no registro inteiro, via
bypass agregado-only do helper B34). Gate fail-closed para leader; região
tolerante a acento; critério declarado na resposta. buildCampaignLinks ganha
o filtro stateDeputies sem_dobradinha na lista de municípios (URL B176).

Closes #527
2026-08-10 07:24:15 -03:00
Francisco Solla
1016668265 feat(B187): links com aparência de link nas respostas do Sollinha
Estilo de links no markdown do chat (primária + sublinhado + hover/foco),
externos abrem em aba nova; compõe com o next/link interno do B188.
Sem migration; e2e + unit cobrem a superfície.
2026-08-10 07:20:44 -03:00
Francisco Solla
1937688fc5
Merge pull request #555 from fsolla/C106-atualizacoes-mobile-feed-sem-moldura-omnibox-sticky-sem
C106 — Atualizações mobile: feed sem moldura (omnibox sticky sem label, criar no header, cards edge-to-edge)
2026-08-10 04:31:17 -03:00
Francisco Solla
74ccce432d
Merge pull request #554 from fsolla/B191-sollinha-acoes-rapidas-de-abertura-no-chat-chips-de-per
feat(B191): chips de pergunta de abertura no chat Sollinha
2026-08-10 04:15:38 -03:00
Francisco Solla
40f38eeb8c feat(C106): cards do feed edge-to-edge com linha + empty state (padrão B184/C107 compartilhado)
- Adota o mecanismo canônico que o irmão B184/C107 estabeleceu em main
  (campaignListOmniboxFormClassName + campaignMobileHeaderIconClassName):
  a hipótese original de CSS de página virava twin — rebase resolveu
  mantendo a versão do irmão em CampaignUpdatesFilters.
- C106 entrega o que o irmão não cobriu: cards edge-to-edge (-mx-4,
  border-b last:border-b-0, padrão exato de MunicipalityListMobileCards),
  neutralização do leak do @layer base ul (my-0 list-none [&>li]:mt-0) e
  copy única do empty state.
- E2E campaignUpdatesMobile: label oculta, form sticky, icon abre o bottom
  sheet, cards edge-to-edge, empty state, regressão desktop.
- Impl plan + changelog atualizados com a decisão revisada.
2026-08-10 04:12:33 -03:00
Francisco Solla
7eaa65ce03 feat(C106): atualizações mobile sem moldura — omnibox sticky, criar no header, cards edge-to-edge
- CSS de página (padrão C101): .campaign-updates-filter-strip sticky sob a
  top bar com linha separadora, :has() zera o padding-top do scroll container
- Chassis B127 ganha o hook estável data-slot=campaign-omnibox-field (sem
  mudança de comportamento)
- 'Nova atualização' vira icon no header via SetCampaignHeaderAction (slot
  C94/C95), gate de resolução 100% CSS (md:hidden); mesmo modal C89
- Cards edge-to-edge (-mx-4) sem borda, divisor de linha; corrigido o leak do
  @layer base editorial (ul my-6 ml-6 [&>li]:mt-2) que afetava a única lista
  campaign em <ul>
- Empty state com copy única descrevendo a ação
- E2E campaignUpdatesMobile (mobile + regressão desktop); impl plan + changelog
2026-08-10 04:12:23 -03:00
Francisco Solla
04951f6772 feat(B191): chips de pergunta de abertura no chat Sollinha (curados por papel e viewport)
- src/lib/sollinhaOpeningQuestions.ts: catálogo puro de chips de abertura
  (staff: 4 perguntas ancoradas nas tools atuais; leader: conjunto seguro
  sem tool eleitoral — fail-closed B180)
- ChatChipGroup: primitivo presentacional reutilizável (slot compartilhado
  com o B192)
- role do usuário flui do layout server pelo shell até o context do chat
- slot acima do input renderiza só com conversa vazia (messages 0 + ready);
  tocar envia via sendMessage e os chips somem no primeiro envio
- unit tests do catálogo + e2e (staff desktop 4 / mobile 3 / leader seguro)
2026-08-10 03:49:08 -03:00
fsolla
437b5f0c2f test(C103): mobile agenda e2e clica o slot diretamente (C104 all-day lane mudou o grid) 2026-08-10 03:34:33 -03:00
Francisco Solla
acd522f035 feat(C103): agenda mobile — formulário de criação usável (sheet do topo, sem labels, salvar fixo, seletor em bottom sheet) 2026-08-10 03:34:33 -03:00
Francisco Solla
3ca200e7b0
Merge pull request #549 from fsolla/B184-municipios-mobile-lista-sem-moldura-omnibox-sticky-x-de
B184 — Municípios mobile: lista sem moldura (omnibox sticky, X de limpar no input, salvar filtro no header, cards edge-to-edge)
2026-08-10 03:30:25 -03:00
Francisco Solla
d60fe3867a
Merge pull request #552 from fsolla/C107-atualizacoes-mobile-sheet-de-criacao-usavel-altura-auto
C107 — Atualizações mobile: sheet de criação usável (altura auto até o topo, form sem labels com divisórias, placeholders, polaridade em toggle, checks à direita)
2026-08-10 03:29:43 -03:00
Francisco Solla
324e1c3c51 feat(B184): omnibox mobile sem moldura como padrão das listas (sticky no form, X de limpar no input, salvar filtro no header, cards edge-to-edge)
- Chassis CampaignListOmnibox (B127): abaixo de md a label some (input carrega
  aria-label), o campo perde borda/sombra (md: restaura), o form do filtro vira
  barra sticky com linha separadora (sticky no form porque não escapa do
  containing block — a coluna do input dentro do wrapper curto não grudava),
  o texto Limpar vira X circular dentro do input (chips OU busca; slot sempre
  renderizado com invisible; hit target 44px; live region de limpeza), botão
  texto Limpar desktop-only. Padrão para as 11 listas via
  campaignListOmniboxFormClassName.
- Municípios: Salvar filtro vira icon no header mobile (C94/C95 slot,
  presentation='icon', nome de rename = contrato 2.5.3); cards edge-to-edge
  com separador de linha (border-b last:border-b-0, -mx-4, gate md:).
- Atualizações: Nova atualização vai para o header mobile (único trailing
  mobile-visible que grudaria na barra).
- campaignMobileHeaderIconClassName compartilhada (3º consumidor).
- E2E mobile novo (390x844) com pins: foco/aria-expanded após limpar, salvar
  -> renomear no header, cards, desktop invertido.
- D7 registrado no caminho: sticky dos headers da tabela desktop morto.
2026-08-10 03:11:43 -03:00
Francisco Solla
7cf820f51b fix(B185): revisão /simplify — escopo efetivo de assessor, chips sem vazamento, DRY e convenções
- Município/território real fora do portfólio devolve lista vazia escopada (antes
  erro 'não reconhecido') + escopoRestrito: true declarado na resposta
- Chips de município fora do acesso omitidos (nunca id pelado)
- uniqueRelationshipIds canônico no lugar do uniqueIDs local
- Chaves do input zod em inglês (scope/filter/mode); Salvador sem round-trip
- Pledge query restrita a lideranças engajado + dica quando truncado + prompt
2026-08-10 03:07:13 -03:00
Francisco Solla
5f1a20de54 test(C108): asserções assíncronas no spec do bell — itens renderizam após a action resolver (robustez sob carga) 2026-08-10 02:56:21 -03:00
Francisco Solla
c3f10cce23 feat(C108): sino de notificações — abrir marca tudo como lido, painel limpo, modal central no desktop
openCampaignNotifications (lista + marca em 1 round trip) substitui
listCampaignNotifications + markAllCampaignNotificationsRead; markAllNotificationsRead
extraída (owner-scoped, idempotente, cap 200) com int test; bell vira Dialog
desktop / Drawer mobile (par C94), sem título/rodapé/botões, skeleton de
loading, safe-area, focus-visible, role=alert; readAt sai do contrato do item;
P4-D ledger atualizado (batch sem transação agora no hot path, self-healing).
2026-08-10 02:56:20 -03:00
Francisco Solla
07464b8810
Merge pull request #548 from fsolla/B188-sollinha-contexto-da-conversa-persiste-na-sessao-da-jan
B188 — Sollinha: contexto da conversa persiste na sessão da janela/tab
2026-08-10 02:53:54 -03:00
Francisco Solla
03a2f57d03 feat(B185): tool do Sollinha para lideranças pendentes de abordagem + assessores responsáveis
getPendingLeaderships: escopo por território/cidade(Salvador=19ZE)/município com
folding de acento, critério de pendência declarado (status A abordar/Em disputa
ou Engajado sem votePledge no escopo; negativo excluído na query), filtro
sem_assessor (C3), modo municipios_sem_lideranca (C4), nomes de assessores via
query própria, gate staff fail-closed (leader deny antes de qualquer query).
Sem migration/Consent/UI; unit tests 14 novos; allowlist P3-D justificada.
2026-08-10 02:27:04 -03:00
Francisco Solla
c8e009b47a
Merge pull request #537 from fsolla/C104-agenda-compromisso-todo-o-dia-na-criacao-rapida-so-data
C104 — Compromisso 'Todo o dia' na criação rápida da agenda (só datas, multi-dia)
2026-08-10 01:37:56 -03:00
Francisco Solla
5380154dca fix(B188): simplify — settle não reabre chat fechado no reload desktop; prune com cabeça de pergunta; node fora do Link; mock SSE v7 no B167
- P1: B167 settle forçava open=true no load desktop mesmo com sessão restaurada
  open:false (storage auto-curava para true); restore agora vence quando existe
- prune: cabeça assistant quebra convertToModelMessages; drop até ancorar em user
- MarkdownLink: node do react-markdown não vaza para <Link>/<a>
- isUIMessage: parts com shape inválido falham-closed no restore
- B167 mock: chunk legado {"type":"text"} falha schema v7 (status preso em erro);
  agora usa start/text-start/text-delta/text-end/finish
2026-08-10 01:34:53 -03:00
Francisco Solla
9c1d4839b2
Merge pull request #539 from fsolla/agent/C101-ux-agenda-mobile-affordance-do-hoje-no-titulo-navegacao
C101-ux — Agenda mobile: navegação por teclado + affordance do tap no título
2026-08-10 01:17:05 -03:00
Francisco Solla
ca1dbb80f7 feat(B188): contexto da conversa do Sollinha persiste na sessão da janela/tab
- sessionStorage por aba: mensagens + estado aberto, restaurados no reload;
  escrita só em estado settle (nunca mid-stream); guardrail 50 msgs / 256 KB
- links internos (/campanha…) do markdown navegam via next/link (sem reload)
- e2e novo + ajuste do B167 (visita mobile nova agora loga em viewport mobile)
2026-08-10 01:13:25 -03:00
Francisco Solla
8b5beb511c fix(C104): rótulo do cabeçalho de detalhe all-day + e2e do slot com allDaySlot ativo 2026-08-10 01:13:12 -03:00
Francisco Solla
ea700e3113 feat(C104): compromisso 'Todo o dia' na criação rápida da agenda (só datas, multi-dia) 2026-08-10 01:13:12 -03:00
Francisco Solla
4e920ee064 feat(C107): sheet de criação de atualização usável no mobile — altura auto até o topo, form sem labels com divisórias full-bleed, placeholders, polaridade em toggle, checks à direita
- MunicipalityUpdateFields ganha layout 'labeled'|'list'; polaridade vira ToggleGroup Ruim|Neutra|Boa nas duas resoluções (mesmo dado, hidden input)
- layout 'list': labels sr-only, textarea sem borda, divisórias divide-y full-bleed, Urgente/Sinalizar adversário como linha tocável com check à direita
- StrictCombobox: prop placeholder (backward-compatible)
- Sheet do feed: max-h-dvh (teto no topo, rola por dentro), título sr-only, form lista com placeholders descritivos; desktop preservado
- Quick-edit B42 herda o visual no sheet mobile via layout
- unit/e2e atualizados para o toggle (radio)
2026-08-10 01:11:24 -03:00
Francisco Solla
8c79eddf96
Merge pull request #536 from fsolla/C105-agenda-tags-para-agrupar-compromissos-filtro-por-tag-na
feat(C105): tags na agenda — sheet, rótulo no evento e prefill do Mais detalhes
2026-08-10 00:58:56 -03:00
Francisco Solla
f3c41d3382 feat(C101-ux): agenda mobile — navegação por teclado (região focável) + affordance do tap no título
- useAgendaKeyboardNavigation: ArrowLeft/Right → prev/next no mobile
  (FullCalendar v7 não tem setas no grid — verificado no bundle); skips
  para modificadores/dialog/menu/inputs; restore de foco na região
- Região de teclado mobile-only: tabIndex/role/aria-label gateados por
  isMobile (desktop mantém a toolbar e sua árvore a11y intacta)
- onTitleClick ganha icon?: LucideIcon (glyph da página, aria-hidden);
  agenda passa RotateCcw como affordance do 'voltar para hoje'
- E2E: teste de teclado (dia e mês), glyph no título, hardening do swipe
  CDP (pacing 25ms — burst era classificado como flick pelo Chromium e
  disparava back-nav ~35% das runs)
- Unit: 14 testes do hook/pura + 4 do renderer do glyph; impl plan
  docs/plans/c101-ux-pos-critique-impl.md (aprovado pelo humano)

Closes #512
2026-08-10 00:53:19 -03:00
Francisco Solla
cd41b44de4 feat(C105): tags no sheet da agenda + rótulo no evento + prefill no "Mais detalhes"
O filtro por tag na omnibox já existia (C94); o C105 completa a superfície da
agenda: campo de tags no overlay de criação inline (chips, sugestões das tags
já usadas), rótulo leve #tag no evento do calendário (oculto no mês/mobile) e
tags via prefill do "Mais detalhes" (params repetidos, parse fail-closed).

- ActivityTagInput extraído do ActivityForm (compartilhado, aria-label novo)
- "Mais detalhes" espelha estado vivo (horários editados + tags)
- popover do sheet com max-h --radix-popper-available-height (shift-before-flip
  do Radix clipava os campos do topo em viewports baixos)
- submit lê tags do estado espelho (fonte única), 4 KB cap p/ responsáveisJson
- limites mantidos 20/80 (C14), sem migration/access/Consent

Impl: docs/plans/agenda-tags-eventos-impl.md (aprovado no gate humano)
2026-08-10 00:21:20 -03:00
Francisco Solla
c2ddbf6006 refactor(D6): simplify — find endpoint-only no subscribe, sslmode verify-full, guards no check-push-chain 2026-08-10 00:01:46 -03:00
Francisco Solla
f4873b8b3f fix(D6): push no device ponta a ponta — SW dinâmico, subscribe idempotente, check-push-chain
- /campanha/sw.js force-dynamic: buildId VERCEL_DEPLOYMENT_ID em runtime
  (deploys CLI --prebuilt não têm envs de build; prod servia campanha-dev)
- subscribeCampaignPush find-before-create (endpoint UNIQUE — 2º toque
  estourava com erro falso)
- scripts/check-push-chain.mjs + pnpm db:check:push-chain (read-only,
  veredito por elo: SW/envs/consent/inscrições/eventos)
- VAPID envs adicionadas no Vercel production (ops)
- impl plan + CHANGELOG-AGENTS
2026-08-09 23:41:00 -03:00
Francisco Solla
a7dce2c716
Merge pull request #516 from fsolla/C101-agenda-mobile-calendario-com-cara-de-app-nativo-context
C101 — Agenda mobile: calendário com cara de app nativo (contexto no header, navegação por arrasto, filtro sticky, dia na hora atual)
2026-08-09 22:43:12 -03:00
Francisco Solla
de205c61e4 style(C101): format 2026-08-09 22:23:18 -03:00
Francisco Solla
1d326a72ab fix(C101): e2e mobile mira o primeiro .activity-agenda (FC mantém grids stale em transições) 2026-08-09 22:22:12 -03:00
Francisco Solla
51fb715ec7 feat(C101): agenda mobile com cara de app nativo — contexto no header, swipe, filtro sticky, dia na hora atual
- toolbar do FullCalendar some no mobile; título do header mostra o período
  ("9 Agosto", "3–9 Agosto", "Agosto", "Agenda") e tap volta ao hoje
- navegação por arrasto horizontal (useAgendaSwipeNavigation): limiar 48px,
  claim 12px com preventDefault, touchcancel sintético contra o long-press
  do FC, dateClick do dia suprimido (C91/C15 intactos)
- filtro vira strip edge-to-edge sem rótulo, sticky sob a barra do app
  (containing block via :has no padding do scroll container)
- visão de dia com scroll interno (height fixa), 08:00 na abertura,
  cabeçalho do dia fixo, scroll preservado ao trocar de dia
- desktop inalterado; impl plan + plano pós-critique (C101-ux)
2026-08-09 22:09:47 -03:00
Francisco Solla
457aad5a21
Merge pull request #510 from fsolla/C102-mobile-sem-botao-sheet-de-sidebar-navegacao-pela-barra-
C102 — Mobile: sem botão/sheet de sidebar — navegação pela barra inferior + Mais
2026-08-09 19:56:07 -03:00
Francisco Solla
f0dff330bd feat(C102): sem hamburger/sheet de sidebar no mobile staff — navegação pela barra + Mais
No mobile (<768px) staff perde o botão de abrir sidebar na top bar
(CampaignMobileTopBar role-gated via useCampaignPageChromeRole +
isStaffCampaignRole; leader mantém) e o sheet de navegação deixa de
montar (CampaignSidebar early-return quando isMobile && staff).
Filtros salvos de Municípios (B18) migram para o drawer Mais
(MunicipalityNavSavedFilters variant 'overflow', mesma store,
apagar/desfazer/sucessão de foco; drawer ganha overflow-y-auto).

Correção de bug pré-existente (D4): o Toaster vivia dentro do wrapper
[contain:layout_paint] do painel da AI, que vira containing block para
position:fixed e stacking context próprio — toasts com ação (ex.
Desfazer) ficavam inalcançáveis sob os portais z-50 de drawer/sheet.
Movido para o root layout (campaign).

Testes: unit top bar (staff sem trigger / leader com trigger) + e2e
(staff sem trigger/dialog, leader com sheet, B18 no drawer completo).
2026-08-09 19:13:43 -03:00
Francisco Solla
27daa1339e feat(C99): toda conta campaignUser aponta para uma ficha Contact; lideranças ganham assessores responsáveis 2026-08-09 19:12:35 -03:00
Francisco Solla
7af76b9f68
Merge pull request #509 from fsolla/B183-pwa-ios-foco-em-inputs-aplica-auto-zoom-que-nao-volta-f
fix(campanha): impede auto-zoom do iOS em inputs do PWA (B183)
2026-08-09 19:10:58 -03:00
Francisco Solla
e835108324 fix(campanha): impede auto-zoom do iOS em inputs do PWA (B183)
Regra CSS única em styles.css — @media (pointer: coarse) and (hover: none)
+ [data-theme='campaign'] :is(input, select, textarea, [contenteditable]) a
16px (limiar do Safari iOS). Cobre omnibox B127, Command search, native-select
e textareas em qualquer orientação do iPhone; desktop intocado.

E2E campaignIosInputZoom: emulação iPhone 13 pina 16px na omnibox de
/campanha/municipios; control desktop pina 14px.

Closes #501
2026-08-09 18:54:33 -03:00
Francisco Solla
dff940681d fix(B182): PWA iOS — heal do zoom preso do visual viewport no standalone
Motor client-side (standalone-iOS only, no-op em aba/desktop):
heal imediato no install + launch/bfcache/resume/pós-teclado/resize,
scrollTo(0,0) + re-measure nudge, retry ≤3×250ms por episódio,
pinça preservada (janela renovada por gesture*/touch ≥2), debug
global para verificação em aparelho, fallback maximum-scale off.

40 testes unit (jsdom) pinam detecção, guards, retry e teardown.
2026-08-09 18:50:00 -03:00
Francisco Solla
3fea9e5920 style(C98): format (prettier) 2026-08-09 16:54:24 -03:00
Francisco Solla
a75228fd34 test(C98): detect CI=true (GitHub Actions) for the prod-mode e2e skip
GitHub Actions injects CI=true (not CI=1), so the full feed flow ran on
the prod build in CI and the create failed with
'NEXT_PUBLIC_SITE_URL precisa usar HTTPS em produção.' — the canonical
fail-closed rejecting the localhost test env. Accept '1' and 'true'.
2026-08-09 16:53:46 -03:00
Francisco Solla
d03da6d49a test(C98): skip full feed flow when e2e runs the prod build
CI and `pnpm push` boot the production build with
NEXT_PUBLIC_SITE_URL=http://localhost:3000, which getCampaignInviteBaseURL
rejects at runtime (fail-closed: feed URLs must be canonical public DNS),
so the generate step of the e2e can only run against the dev server. The
prod-mode CI still covers the unlocked dialog (C93 component unit) and the
filterless feed content (C93 int tests).
2026-08-09 16:20:59 -03:00
Francisco Solla
65ed945b27 feat(C98): verify calendar import link generation end-to-end (env fix + e2e)
Production feed generation failed with a generic message because
NEXT_PUBLIC_SITE_URL was missing on Vercel production: buildFeedUrl calls
getCampaignInviteBaseURL(), which throws in NODE_ENV=production without the
env (intentional fail-closed), so the action catch returned 'Não foi
possível criar o feed de calendário.' and nothing was created. The local
real-flow verification (login -> agenda without filters -> header icon ->
name -> generate -> link /campanha/agenda/ical/<secret> -> iCal 200 with
the activity) proved C92/C93 work; the divergence was the environment.
Configured NEXT_PUBLIC_SITE_URL=https://pt.jorgesolla.com.br in Production
(+ checked Preview) and redeployed.

New e2e campaignAgendaFeed pins the real flow: desktop (header -> dialog
-> generate -> link responds iCal with the fixture activity) and mobile
(FAB -> sheet -> generate). The e2e fixture now owns calendarFeed rows
(createdBy NOT NULL made the campaignUser cleanup transaction abort).

AGENTS.md deploy checklist notes NEXT_PUBLIC_SITE_URL as mandatory in
Production.

Closes #483
2026-08-09 16:20:59 -03:00
Francisco Solla
fdedfa9ad4 feat(C97): seletor shadcn de dia+horário 24h no popover de criação rápida da agenda
Troca o datetime-local nativo (AM/PM no idioma do browser, horário pouco
confiável) por ActivityDateTimeField: trigger com rótulo civil
dd/mm/aaaa às hh:mm + popover com Calendar (react-day-picker 9.7,
timeZone America/Bahia, locale pt-BR) e selects Hora 00-23 / Minuto
em passos de 15. Contrato de valor e submit inalterados; prefill do
slot mantido. Novo ui/Calendar.tsx (variante radix-nova), helpers
puros em campaignTime.ts. Sem migration. Closes #482 (PR).
2026-08-09 15:13:15 -03:00
Francisco Solla
ff53e15e7e feat(B180): gate fail-closed de leader nas tools eleitorais legadas do Sollinha (getTopDeputies/getMunicipalityVotes)
ElectionDataGate compartilhado (3 call sites) + refactor da B177 para o helper.
2026-08-09 14:54:31 -03:00
Francisco Solla
07b4509e42 feat(campaign): Tendência migra para Ações ao colapsar a coluna (B172)
Em /campanha/municipios (staff), abaixo de 60rem do container a coluna
Tendência some inteira (hidden @min-60:table-cell no th+td, invertendo a
decisão B158 'container query nunca duplica' só para Tendência) e o mesmo
editor monta como ícone compacto na coluna Ações, ao lado do de atualização;
acima de 60rem volta o badge completo e Ações é gated fechado quando nada
está oculto manualmente. Ações torna-se sempre montada para staff e pinada
por ocultação manual no B17. Uma instância visível do editor por linha por
faixa (dual-render CSS-gated, sem JS/resize). Sem migration. Closes #446.
2026-08-09 13:36:04 -03:00
Francisco Solla
4fbb92b886 fix(e2e): C95 — grid asserts com first() (FC v7 mantém grids antigos no DOM) 2026-08-09 12:42:21 -03:00
Francisco Solla
dff3a9e8b1 feat(C95): seletor de modo de visualização no header do app (agenda)
- view=week|day|month|list no contrato URL da agenda (canônico, fail-closed)
- seletor compacto 'Semana ▾' no header desktop e mobile (slot C94/C95)
- FullCalendar headerToolbar sem os 4 botões de modo; escolha vence resize
- effect normaliza a vista também quando a URL perde o param (sem vista órfã)
- useNarrowMeasured(breakpoint) em hooks/use-mobile (settle de hidratação)
- e2e reescrito no seletor + persistência + escolha-vence-resize + estabilização
  dos waits de slot (grid lazy do FullCalendar substitui gridcells no load)
2026-08-09 12:34:04 -03:00
Francisco Solla
6c34da4a47
Merge pull request #488 from fsolla/B178-salvador-cidade-agregada-no-campanha-pagina-virtual-lin
fix(B178): review do simplify — ordenação de nulls nativos, mapa sem slug da cidade, a11y e testes de borda
2026-08-09 10:53:46 -03:00
Francisco Solla
d8ee2dbafa fix(B178): review do simplify — nulls nativos em DESC, mapa sem slug da cidade, sr-only morto, sinal mobile, paginação/edges testados 2026-08-09 10:40:18 -03:00
Francisco Solla
6cf29b3810
Merge pull request #486 from fsolla/B179-sollinha-persona-no-genero-masculino-prompt-referencias
B179 — Sollinha: persona no gênero masculino (prompt + referências .md)
2026-08-09 10:22:35 -03:00
Francisco Solla
f1a8332835
Merge pull request #487 from fsolla/B178-salvador-cidade-agregada-no-campanha-pagina-virtual-lin
feat(B178): Salvador como cidade no /campanha — página virtual, linha comum na lista e busca, sem dupla contagem
2026-08-09 10:21:16 -03:00
Francisco Solla
6760b99942 feat(B179): Sollinha persona no gênero masculino (prompt + placeholder + docs) 2026-08-09 10:07:21 -03:00
Francisco Solla
de66fe3e3f feat(B178): Salvador como cidade no /campanha — página virtual, linha comum na lista e busca, sem dupla contagem 2026-08-09 10:07:05 -03:00
Francisco Solla
d5d1d0c30e chore(B181): remove o aviso de janela vazia da agenda
Sem bloco 'Nenhum compromisso nesta janela e neste filtro' nem botão
'Criar atividade' acima do calendário: a criação segue pelo slot click
(C91), botão dos filtros e FAB. buildActivityCreateHref permanece com
2 consumidores. Teste unitário de regressão + impl plan + changelog.
2026-08-09 09:34:25 -03:00
Francisco Solla
b98366ea66 test(C93): fix beforeEach import in dialog unit spec 2026-08-09 09:04:51 -03:00
Francisco Solla
2ba7a88531 feat(C93): allow generating calendar import link without filters
The 'Link de import' button was disabled whenever no agenda filter was
active, forcing a filter to subscribe to the full campaign agenda. The
server already supported the empty-filter case (filterMunicipality empty
= entire creator scope via buildFeedWhere/resolveFeedCreatorAccess), so
this unlocks the button and drops the now-dead state/hasFilters props.
The dialog copy is neutralized ('recorte' -> 'agenda') to cover both the
filtered and filterless shapes.

Int tests pin the content acceptance: coordinator filterless feed covers
all municipalities; advisor filterless feed is scoped to the administered
portfolio (nothing out-of-scope leaks); an advisor with no assigned
municipality serves an empty feed (fail-closed). A new component test
asserts the button is enabled with zero filters and the create flow works.

Closes #437
2026-08-09 09:02:06 -03:00
Francisco Solla
cd4698571a fix(e2e): first() em asserts com duplicação transitória de RSC pending (flake pré-existente, base reproduz) 2026-08-09 04:37:27 -03:00
Francisco Solla
6c631bdeec fix(e2e): agenda — waits de visibilidade para medir o slot do calendário (estabiliza inline/mais detalhes) 2026-08-09 04:36:02 -03:00
Francisco Solla
ad39602d04 feat(C94): toolbar omnibox + e2e agenda hardening + changelog 2026-08-09 04:36:02 -03:00
Francisco Solla
e0530a3a90 feat(C94): agenda omnibox + feed no header/FAB 2026-08-09 04:35:39 -03:00
Francisco Solla
663c57919f fix(B176): ordena o scope esperado do int spec (alocação não é ascendente) 2026-08-09 03:19:54 -03:00
Francisco Solla
97dd13443e test(campaign): endurece locators de texto contra duplicata transitória de hidratação
Mesmo padrão já pinado em 'Otimista: 5.000': o slot server-composed
renderiza duas cópias por um instante durante a hidratação — .nth(0)
nas asserções de perfil (webauthn) e dossiê (municípios). Não relacionado
ao B176; observado deterministicamente no servidor de produção local.
2026-08-09 03:19:54 -03:00
Francisco Solla
14bbee6e00 feat(B176): filtrar municípios por Dobradinha/Liderança/Partido + coluna Dobradinhas ao assessor
Omnibox de /campanha/municipios ganha as dimensões Dobradinha (nome do
contato), Liderança (nome do contato) e Partido (sigla), com recortes
"Sem dobradinha"/"Sem liderança"/"Sem partido" no precedente de
"Sem nível". Params aditivos stateDeputy/leadership/party na URL
congelada; buildMunicipalityListWhere ganha catálogo request-scoped
fail-closed (municipalityRelationSets) que vira as leituras reversa e
cruzada em where — sem migration, paginação nativa mantida; facets
estendidas e o mapa compartilha o catálogo. Coluna Dobradinhas liberada
ao staff (servidor já scoped B37/B157; só o gate de UI mudou).
Extrato do par sentinela+exists:false em campaignListUrl (5 call sites).
2026-08-09 03:19:54 -03:00
Francisco Solla
5a7f3bbb8a
Merge pull request #475 from fsolla/B177-sollinha-responder-em-quais-cidades-x-foi-o-deputado-ma
feat(B177): Sollinha responde em quais cidades X foi o deputado mais votado (rank competitivo reverso, flexível)
2026-08-09 02:46:17 -03:00
Francisco Solla
234c69ceef
Merge pull request #468 from fsolla/C96-feed-ical-interseptar-escopo-do-criador-no-read-defesa-e
fix(C96): intersect creator scope in calendar feed read (+ tests)
2026-08-09 02:35:56 -03:00
Francisco Solla
cef946ad7d feat(B177): Sollinha responde em quais cidades X foi o deputado mais votado (rank competitivo reverso, flexível) 2026-08-09 02:35:53 -03:00
Francisco Solla
7785c1088c fix(worktree): provisiona PLAYWRIGHT_BASE_URL no .env.test.local — e2e usa porta do slot (OPS20)
O .env.test.local (que o playwright.config.ts lê) passa a carregar
PLAYWRIGHT_BASE_URL na porta do slot (3100+slot); antes só o .env.local
(que o config não lê) o tinha, então dois test:e2e em worktrees paralelos
subiam dev servers na porta 3000. Causa-raiz tratada: os dois blocos env
inline do provision viraram um builder puro único (worktreeEnvFileContents
em scripts/lib/worktree-env.mjs) que deriva NEXT_PUBLIC_SITE_URL +
PLAYWRIGHT_BASE_URL do mesmo devPort para ambos os arquivos — paridade
estrutural, drift impossível + unit tests de paridade. playwright.config.ts
intocado (a variável certa já existia).
2026-08-09 02:19:26 -03:00
Francisco Solla
177803a54a
Merge pull request #470 from fsolla/B173-sollinha-consulta-por-voz-no-chat
B173 — Sollinha: consulta por voz no chat
2026-08-09 02:16:53 -03:00
Francisco Solla
9f997aa232
Merge pull request #471 from fsolla/B175-repintar-a-lista-de-territorios-no-padrao-da-lista-de-m
B175 — Repintar a lista de Territórios no padrão da lista de Municípios
2026-08-09 01:58:06 -03:00
Francisco Solla
4668c018bf feat(B173): consulta por voz no chat da Sollinha (STT → rascunho)
Adiciona entrada por voz ao chat: botão de mic no InputGroup grava no
browser (getUserMedia + MediaRecorder), envia o clip multipart para a nova
rota POST /campanha/api/ai-transcribe (auth cookie + rate limit no mesmo
balde + same-origin) e injeta a transcrição como rascunho editável — o
envio segue o fluxo normal do chat. O áudio nunca é persistido.

STT via Deep Infra whisper-large-v3 (DEEPINFRA_API_KEY, mesma do B174);
a costura de rede vive em utilities/ai/deepInfraTranscribe.ts; a máquina
de captura em useMicTranscript. Falha de permissão/provedor degrada a
mensagem em pt-BR sem quebrar o chat por texto.

Closes #448
2026-08-09 01:47:03 -03:00
Francisco Solla
fe81358fe3 B170 — spec do contrato do colapso de chips + shift único do load SSR deferido documentado
F1: RelationChipCell ganha spec unitário (relationChipCellCollapse) que pina o
colapso medido pelo layout real (16 chips → slice de 3 linhas = 12), expande
pelo 'Ver mais…' e recolhe pelo 'Ver menos', com getBoundingClientRect stubado
por índice — a lacuna que o B169 deixou (specs com measureOverflow:false ou
1 chip) não tinha regressão do comportamento de colapso após a mudança de timing.

F2: medição PerformanceObserver no worktree confirma o resíduo pré-hidratação
do B169 sem regressão material: load SSR→hidratação com shifts únicos
[0.139, 0.043, 0.257] (fontes do shell, nenhuma célula de chips) e ordenação
client-side com zero shifts novos; eliminá-lo exigiria estimativa de largura
(rejeitada) ou mudança de layout/regra (anti-goal) → defer documentado.

Sem migration, dados, access ou mudança visual.
2026-08-09 01:36:59 -03:00
Francisco Solla
5b342146d5 fix(C96): intersect creator scope in calendar feed read
buildFeedWhere trusted filterMunicipality without intersecting the
creator's current municipality scope when accessibleMunicipalityIds is
non-null (advisor). An advisor removed from a municipality after creating
a pinned feed kept serving its activities. Now the read re-derives the
creator's scope on every request (fail-closed, aligned with C16) and
intersects it into the where clause via the canonical
advisorMunicipalityScopeWhere fragment.

Int tests cover: advisor in-scope lists, advisor removed stops serving,
coordinator (unrestricted) unchanged. No migration (config/consulta only).
2026-08-09 00:55:11 -03:00
Francisco Solla
e65534ca9b test(e2e): atualizar marcador do território no picker de colunas (coluna Municípios saiu na B175) 2026-08-09 00:38:27 -03:00
Francisco Solla
e3f6d3f5eb
Merge pull request #457 from fsolla/C89-pagina-atualizacoes-feed-de-cards-filtro-criar-modal-bot
C89 — Página Atualizações: feed de cards + filtro + criar (modal / bottom sheet)
2026-08-09 00:05:05 -03:00
Francisco Solla
e75bb1c2f3 feat(campaignTerritories): repintar a lista de Territórios no padrão da lista de Municípios (B175)
- Colunas responsivas ao painel via @container/territory-list (P0→P3), sem scroll horizontal
- Contagem de municípios no nome ("Irecê (20)"); % e votos absorvidos na célula 2022 (hover = válidos)
- Célula 2026 no detalhe de Municípios (valor central + %, faixa de cenários no hover, leitura)
- Colunas de rede só leitura: Assessor, Liderança, Dobradinha (para todo o staff)
- Cobertura oculta por padrão no seletor; labels curtos (2022/2026/Captura/Cobertura)
- loadTerritoryOverview devolve { rows, references }; rollup puro com soma por cenário, hasEstimate e uniões de rede
2026-08-08 23:58:54 -03:00
Francisco Solla
c9134becd1
Merge pull request #456 from fsolla/C92-link-de-import-corrigir-criacao-do-feed
fix(C92): corrige criação do link de import da agenda (feed)
2026-08-08 23:39:43 -03:00
Francisco Solla
29ed733190 fix(C92): calendar feed creation stripped by admin-only system fields
createCalendarFeedLinkRecord failed with `overrideAccess:false` +
`user: campaignUser` because Payload stripped the required
`secretSlug`/`createdBy` fields (create access was admin-only) -> 400
behind the generic message; revokeCalendarFeedRecord silently no-opped
(revokedAt update access also admin-only).

- canCreateCalendarFeed now enforces advisor filterMunicipality scope
  on data (canCreateSupporter precedent) + canSetCalendarFeedSecret /
  canSetCalendarFeedRevocation permit staff to mint the secret on create
  and mark a feed revoked; secret read stays `() => false` and update
  admin-only.
- CalendarFeed stamps createdBy via stampCampaignCreatedBy; secret
  create + revokedAt update use the new staff field accesses.
- Action refactored to `*Record` (payload, actor) cores + thin wrappers;
  assertFilterMunicipalityInScope (tour precedent); console.error keeps
  the real error on the server while the friendly message stays.
- 10 int tests for create/scope/revoke/leader/secret; fixture tracks
  calendarFeed cleanup.

Closes #436
2026-08-08 23:13:30 -03:00
Francisco Solla
5a51d60c9e style(C89): formata (prettier) 2026-08-08 23:11:05 -03:00
Francisco Solla
7343f15053 feat(C89): página Atualizações — feed de cards + filtro omnibox + criar (modal/bottom sheet)
Substitui o placeholder B164 por um feed operacional da carteira (staff):
- Cards com avatar do autor, polaridade/urgente/adversário, município e data
- Filtro omnibox (município via dotted-path, polaridade, urgente, autor, busca)
  no padrão das listas (URL canônica + adapter puro)
- Criação in-place: modal desktop / bottom sheet mobile, reusando o registro
  unificado (C87) com prefill do município do filtro ativo
- Leader lockdown intacto (gate staff + canReadMunicipalityUpdate fail-closed)
- parseMunicipalityUpdateFormData único para os 3 forms de atualização
- Loader cruzando a carteira sem cláusula de município (access por ator)
- Int/unit specs (19 unit + 5 int) + convenções

Fix real: o modal de criação não pode ser filho React do <form> de filtros —
React 19 não liga progressive enhancement em action-form aninhado.
2026-08-08 23:07:23 -03:00
Francisco Solla
0a073c8e61
Merge pull request #454 from fsolla/B171-barra-inferior-mobile-folga-no-topo-rotulos-sem-sobrepo
fix(campaign): barra inferior mobile — folga no topo e rótulos sem sobreposição
2026-08-08 19:15:59 -03:00
Francisco Solla
f3d2f46a58 test(campaign): reforça e2e da barra inferior mobile após /simplify
- pina paddingTop >= 10px (pt-2.5) e cobre viewport 320px — o aceite da
  intenção promete rótulos legíveis em qualquer largura mobile.
2026-08-08 18:45:42 -03:00
Francisco Solla
2ba0cebf6f
Merge pull request #452 from fsolla/C91-criar-evento-inline-no-calendario-da-agenda-estilo-googl
C91: criar evento inline no calendário da agenda (estilo Google Calendar)
2026-08-08 18:45:27 -03:00
Francisco Solla
eb94a81ebc fix(campaign): barra inferior mobile — folga no topo e rótulos sem sobreposição
B171 #443
- CampaignBottomNav: pt-2.5 no nav (respiro da borda superior) e rótulos
  text-[10px] nowrap com gap-1 — sem encolher a área de toque.
- e2e: assert paddingTop >= 8px, rótulos < 12px e sem sobreposição horizontal.
2026-08-08 18:37:12 -03:00
Francisco Solla
c1bedcf838 test(campaignMunicipalities): torna o assert Otimista: 5.000 determinístico
O getByText sem índice também casa com o span sr-only do hover-preview quando
uma linha está hovered — strict-mode violation flaky no CI. .nth(0) fixa no
resumo da célula.
2026-08-08 18:29:42 -03:00
Francisco Solla
77673147ca feat(C91): cria evento inline no calendário da agenda (popover/drawer no slot clicado) 2026-08-08 18:27:25 -03:00
Francisco Solla
cfb27296f6 test(campaignActivity): fecha o chat antes do passo mobile da agenda (B167)
B167 migra um chat aberto no painel desktop para o drawer full-screen ao
encolher a viewport — o drawer cobriria o calendário no passo mobile deste
spec. Fecha o chat antes do resize para garantir a superfície de teste.
2026-08-08 18:11:09 -03:00
Francisco Solla
0e38d19d42 feat(B167): migra o chat Sollinha entre painel e drawer ao redimensionar
- Converse (useChat) lifts to the provider: survives desktop panel <-> mobile
  drawer swaps, internal /campanha navigation and resize (new tab still resets).
- Chat Panel/Separator always mounted (RRP panel-count change forces a full
  navigation); footprint hidden via elementRef + .b167-ai-chat-hidden
  (display:none !important), avoiding RRP's imperative collapse/expand races.
- Single 'open' flag drives both surfaces (open && viewport); reconciled with
  the panel at desktop settle and on user drag-collapse (gated by isUserInteraction).
- Mobile Drawer extracted to CampaignAIDrawer; FAB/setOpen unified; focus moves
  into the chat on the mobile->desktop swap.
- e2e campaignAiChatResize: desktop->mobile, mobile->desktop, closed stays
  closed, fresh mobile visit closed, navigation survival, new tab resets.
- Impl plan: docs/plans/migrar-chat-painel-drawer-resize-impl.md

Closes #416
2026-08-08 17:52:02 -03:00
Francisco Solla
63642483a4 feat(C90): remodela responsáveis da Atividade em campo único polimórfico
Um campo responsible (staff campaignUser · leadership · stateDeputy) multi-valor
substitui responsible/Contact + advisors + leadership; assessor mantém acesso via
notação-objeto equals no access; nova migração esquema+dados (advisors/leadership
→ responsible); busca q da lista título-only; notificação notifica staff do campo.
Closes #426
2026-08-08 17:10:48 -03:00
Francisco Solla
8b2bc51aa5
Merge pull request #433 from fsolla/plans/plan-issue
worktree: plan cria um worktree diferente por invocação (paralelismo /plan-issue)
2026-08-08 16:52:26 -03:00
Francisco Solla
6c8c466b40 fix(worktree): plan cria um worktree diferente por invocação (paralelismo /plan-issue)
Cada 'pnpm worktree plan' agora deriva um branch próprio: plans/plan-issue-<bag>
quando um bag é passado (sufixo -2/-3 em colisão) ou o próximo sequencial
plans/plan-issue-<n> quando não. O prefixo minúsculo plans/… nunca colide com o
branch <Code>-<slug> de 'next'. planBranchName (scripts/lib/worktree.mjs)
unit-testado; docs e shell/command atualizados.
2026-08-08 16:40:05 -03:00
Francisco Solla
1adf5f3d73
Merge pull request #432 from fsolla/B166-largura-padrao-do-chat-sollinha-com-teto-no-desktop
feat(B166): largura padrão do chat Sollinha com teto de 360 px e resize livre persistido
2026-08-08 16:24:30 -03:00
Francisco Solla
441c8d1156 refactor(B166): endurece e2e (poll determinístico) e clamp exato após revisão 2026-08-08 16:04:54 -03:00
Francisco Solla
c399b918c3 feat(worktree): comando plan — worktree dedicado ao /plan-issue
Novo subcomando `pnpm worktree plan [--go] [--no-migrate]` cria/reutiliza o
worktree de planejamento no branch fixo `plans/plan-issue`
(`PLAN_WORKTREE_BRANCH`), sem vínculo com a fila de claim: um `next` posterior
da próxima Issue nunca colide em branch nem em slot (slot derivado por hash do
branch, bumpado entre worktrees vivos). Mesmo provisionamento isolado do
`next` — porta 3100+slot, bancos teqo_wt<slot>/teqo_wt<slot>_test, migrations.

- scripts/worktree.mjs: cmdPlan + provision() com purpose/issue opcionais
- scripts/lib/worktree.mjs: constante PLAN_WORKTREE_BRANCH
- tests/unit/worktree.unit.spec.ts: derivação de slot do plano
- docs: .opencode/commands/worktree.md, shell, skills, AGENTS.md, changelog
2026-08-08 16:00:27 -03:00
Francisco Solla
2ca1afa6a7 feat(B166): largura padrão do chat Sollinha com teto de 360 px e resize livre persistido 2026-08-08 15:32:07 -03:00
Francisco Solla
ec0b109b5a
Merge pull request #430 from fsolla/B168-desfazer-encadeamento-automatico-das-acoes-rapidas-do-w
B168 — Desfazer o encadeamento automático das ações rápidas do wizard
2026-08-08 14:30:27 -03:00
fsolla
7faf3ed27f feat(campanha): B168 — desfazer encadeamento automático das ações rápidas do wizard
Ações de ajuste (votos, tendência, atualização, liderança) agora retornam à
origem ao salvar (toast + from allowlist B110 / Início), sem avançar sozinhas
para outro wizard. Removidos fila de encadeamento (wizardActionChain.ts),
param entry, skip/Pular (chrome mobile sempre X) e prefill de nota sem
produtor. Liderança: botão pós-save 'Continuar' → 'Concluir' (origem).
Sem migration.
2026-08-08 14:10:47 -03:00
Francisco Solla
bd06018bde B164: barra de navegação inferior no mobile para staff
Mobile bottom nav (staff only): 5 items (Início, Municípios, Atualizações,
Agenda, Mais) with Drawer overflow for secondary destinations. Leader and
desktop remain sidebar-only. FAB + scroll padding adjusted for safe-area.

Resolves #400
2026-08-08 12:57:05 -03:00
Francisco Solla
9447686edf
Merge pull request #421 from fsolla/worktree-envs-isoladas
feat(scripts): worktree next provisiona ambientes isolados (porta + bancos + migrations)
2026-08-08 11:51:46 -03:00
Francisco Solla
25773d0296
feat(C16): add calendar feed sync by filter with Google Calendar import link (#419)
- Add calendarFeed collection with secret slug credential
- Add iCal feed endpoint at /campanha/agenda/ical/[secret] (unauthenticated)
- Add server actions for creating and revoking calendar feeds
- Add CalendarFeedButton UI component in agenda filters bar
- Add unit tests for iCal generation
- Fail-closed access: feed re-evaluates creator's municipality scope on each read
- Leader lockdown preserved; no PII in feed output
2026-08-08 14:47:17 +00:00
Francisco Solla
8125334401 feat(worktree): ambientes isolados por worktree — porta própria, bancos próprios, migrations no provision
pnpm worktree next [--go] [--no-migrate] agora provisiona um ambiente
determinístico por worktree: slot = parte numérica do código da Issue
(C15→15; hash sem dígitos; bump em colisão entre worktrees vivos), dev
server em 3100+slot, bancos teqo_wt<slot> / teqo_wt<slot>_test no
container compartilhado (-p teqo) com a cadeia de migrations aplicada;
escreve .env.local + .env.test.local (gitignored, marcador gerado; envs
manuais nunca sobrescritos) e copia PAYLOAD_SECRET/Blob/VAPID do main.
kill remove o worktree e dropa os bancos gerados (WITH FORCE, best-effort).

Suites leem .env.test.local depois de .env.test (vitest.setup /
playwright / db-doctor / gate-ci espelham o layering) e
assertTestDatabase aceita teqo_test ou teqo_<worktree>_test em localhost
só (fail-closed preservado). db:start/db:stop fixam -p teqo (um
container de qualquer diretório); max_connections 200→400. Sem Docker
(Cursor Cloud): degrada ao compartilhado com aviso.

Derivação pura em scripts/lib/worktree-env.mjs (unit-tested); fluxo
validado end-to-end: provision → migrate → 598/598 int no banco do
worktree → kill sem resíduo.
2026-08-08 11:33:07 -03:00
Francisco Solla
c5001b5005
Merge pull request #410 from fsolla/C87-atualizacao-unificada-texto-polaridade-urgente
feat(campaign): C87 — atualização unificada (texto + polaridade + urgente)
2026-08-07 07:20:28 -03:00
Francisco Solla
11a9f45d69 test(e2e): fix C87 unified-update specs (polarity + adversary signal) 2026-08-07 06:56:41 -03:00
Francisco Solla
212a1f2423 feat(campaign): C87 — atualização unificada (texto + polaridade + urgente)
Substitui o modelo de MunicipalityUpdate (kind/semanal/urgente/nota/sinal + signalType)
por um modelo unificado: body obrigatório + polarity (boa/neutra/ruim) + urgent + adversarySignal.

- Schema: polarity enum obrigatório, urgent boolean default false, adversarySignal boolean
- Wizard: novo WizardUpdateBodyStep com campo de texto + polaridade + urgente
- Lista: controle unificado de atualização com polaridade inline
- Feed: exibe polaridade como badge colorido + indicador de urgência
- V2 status strip: atualização rápida de polaridade
- E11: queries atualizadas para polarity + adversarySignal
- Migração: 20260807_030000_unify_municipality_update (backfill polarity=neutra, urgent de kind, adversarySignal de kind+signalType)
- Testes: 18/18 integração C87, 593/593 total, 1399/1399 unitários
- Fix: testes de state deputy usavam nomes fixos que colidiam entre execuções
2026-08-07 05:04:41 -03:00
Francisco Solla
55178247c8
Merge pull request #409 from fsolla/C15-fullcalendar-em-campanha-agenda
feat(campanha): C15 — FullCalendar em /campanha/agenda
2026-08-07 04:56:52 -03:00
Francisco Solla
59983b33f7 feat(campanha): C15 — FullCalendar em /campanha/agenda
Issue #390 — entrada operacional staff com FullCalendar v7 route-local
(semana/mês/lista), filtros compartilháveis por município/deputado/tag,
criação pelo slot, detalhe por URL e remarcação drag/resize sob RBAC,
transação e lock por atividade.

- /campanha/atividades e giros permanecem como lista/fluxos legados
- Sem migration/collection nova; reutiliza activity de C14
- Fuso Bahia, range exclusivo, erro/revert, loading/empty testados
- Leader negado, advisor limitado pelo access atual
- E2E cobre filtros, drag, mobile 390x844 e slot prefill
- Int specs cobrem RBAC, concorrência e defense-in-depth

Impl: docs/plans/fullcalendar-agenda-campanha-impl.md
2026-08-07 04:37:36 -03:00
Francisco Solla
b61f1ec36a style(tests): prettier-format worktree unit spec 2026-08-07 01:27:43 -03:00
Francisco Solla
1c0648d59a feat(scripts): deterministic worktree + issue CLI (next --go, kill, all) 2026-08-07 01:27:22 -03:00
Francisco Ignácio Fontoura Solla
c1fb8c03c4 test(e2e): document municipality locator boundary 2026-08-06 19:12:32 -03:00
Francisco Ignácio Fontoura Solla
9c99469c36 test(e2e): stabilize municipality campaign journeys 2026-08-06 19:00:13 -03:00
Francisco Ignácio Fontoura Solla
48623b0d06 fix(campanha): preserve StateDeputy migration identity 2026-08-06 18:38:47 -03:00
Francisco Ignácio Fontoura Solla
033c270a89 feat(campanha): B163 — editar contatos das dobradinhas 2026-08-06 16:52:55 -03:00
Francisco Ignácio Fontoura Solla
30de02fd89 fix(e2e): use correct submit label 'Criar atividade' from nova page 2026-08-06 06:09:11 -03:00
Francisco Ignácio Fontoura Solla
86ceeaaf00 fix(e2e): simplify activity E2E (remove tag input interaction) 2026-08-06 05:54:41 -03:00
Francisco Ignácio Fontoura Solla
39c5e931ca fix(e2e): update activity E2E for C14 tags form (no kind/origin) 2026-08-06 05:36:38 -03:00
Francisco Ignácio Fontoura Solla
adc29c0379 feat(campaign): C14 remodel activity for agenda paradigm (#389)
- Replace kind enum with free-form tags (text[] hasMany)
- Remove origin and deadline fields
- Status: remove rascunho/planejado, keep confirmado/realizado/cancelado
- startAt required for user-facing creates (optional for tour drafts)
- Add deputyPresent time gate (only coordinator/candidate can reschedule)
- UI: calendar-like form with tag input, no origin/deadline, no draft status
- E13 giros: tourStopRoleActivityKind → tourStopRoleActivityTags
- Migration: kind→tags with label mapping, status remap, enum recreation
- Activity texts table creation (was missing from migration history)

Closes #389
2026-08-06 05:20:05 -03:00
Cursor Agent
5f97b6a21c
feat(ai): add buildCampaignLinks tool for Sollinha navigation (B162)
Introduce a closed-catalog URL builder that delegates to existing list/detail
href helpers, with role-based guards for leader lockdown and unrestricted-only
assessor routes. Register the tool in the AI chat and teach the system prompt
when to offer markdown links after resolving entity ids/slugs.

Closes #383
2026-08-06 00:22:11 +00:00
Francisco Solla
8abf18ecf9 test: stabilize responsive municipality journeys 2026-08-05 15:00:33 -03:00
Francisco Solla
2f8d16881d test: align municipality e2e journeys with responsive columns 2026-08-05 14:24:47 -03:00
Francisco Solla
cc60b82245 fix: force container width past 48rem in iPad test to reveal the table 2026-08-05 12:32:05 -03:00
Francisco Solla
a36b0b2625 simplify: safe fallback for browsers without container queries, trim test overhead 2026-08-05 12:28:17 -03:00
Francisco Solla
150bea2621 feat: make municipality columns follow content width 2026-08-05 11:35:20 -03:00
Francisco Solla
423c61d40b feat: distinguish municipality column defaults from explicit visibility 2026-08-05 10:49:58 -03:00
Francisco Ignácio Fontoura Solla
9fa701633e feat(campaign): unify municipality relation editors 2026-08-05 02:01:40 -03:00
Francisco Ignácio Fontoura Solla
896d957497 chore: migrate .cursor/ references to .agents/ folder
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.
2026-08-05 00:09:09 -03:00
Francisco Solla
4b79f00125
Merge pull request #373 from fsolla/feature/ai-chat-sollinha
feat: AI chat 'Sollinha' — campaign data assistant (v1 read-only)
2026-08-04 06:26:48 -03:00
Francisco Ignácio Fontoura Solla
f9cd1df7b2 feat: AI chat 'Sollinha' — campaign data assistant (v1 read-only)
- Streaming AI endpoint at /campanha/api/ai-chat with DeepSeek V4 Flash
- 8 tools: calculate, getMunicipalityVotes, getTopDeputies, getDobradinhas,
  getMunicipalityOverview, getLeaderships, getOrganizations, searchEntities
- In-memory rate limiter (50 msgs / 15 min per user)
- Desktop: resizable right sidebar via react-resizable-panels
- Mobile: full-screen Drawer opened by FAB (2/3 size of quick-actions FAB)
- Markdown rendering with react-markdown + remark-gfm (tables, bold, lists)
- Session-bound context (clean slate each open)
- Auth via existing campaign-token cookie, tools respect user RBAC
- Architecture doc: docs/plans/ai-chat-sollinha.md
- E2E tests: campaignAiChat.e2e.spec.ts (mocked endpoint)
2026-08-04 06:17:31 -03:00
Francisco Ignácio Fontoura Solla
6b8864debb B156 — assessores por dobradinha (campo, coluna e seção; RelationOptionCell extraído) 2026-08-04 04:57:15 -03:00
Francisco Ignácio Fontoura Solla
9780bb23df style: format campaignComponents unit spec 2026-08-04 02:53:44 -03:00
Francisco Ignácio Fontoura Solla
086a9c8ee0 B157 — coluna de dobradinhas na lista de municípios (+ criação inline) 2026-08-04 02:53:43 -03:00
Francisco Ignácio Fontoura Solla
596085c977 feat(campanha): B155 — coluna de lideranças na lista de municípios
Espelho da coluna de Assessores: chips com nome do contato, popover/sheet
com Command + delta otimista e criação inline (Nome + Celular). Sem
migration — leitura reversa de leadership.municipalities no bundle e
escrita reusando as actions B34/wizard; rota POST
/campanha/municipios/leaderships via campaignJsonMutationRoute.
2026-08-04 01:49:55 -03:00
Francisco Ignácio Fontoura Solla
b74268714c C86+ — simplify: JSDoc do contrato home, testes de borda e nota no impl plan
Fixes do /simplify (3 revisores read-only): JSDoc de normalizePathname/isListPath
documentando o invariante (homes nunca são '/' nem terminam em slash), teste de
multi-slash e inputs degenerados, comentário de isCampaignHomePath encurtado.
Sem mudança de comportamento.
2026-08-04 01:17:53 -03:00
Francisco Ignácio Fontoura Solla
4a90fb19ec C86+ — pathname de lista das ações rápidas em fonte única (isListPath)
Extrai isListPath + normalizePathname exportados em campaignQuickActionPaths.ts
e migra os 9 wrappers (7 públicos + 2 privados) + a 3a cópia de normalize em
campaignPageChrome.ts. Refactor estrutural sem mudança de comportamento;
derivação de regex de detalhe numérico adiada (gatilho: 3o vertical [id]).
Inclui docs/plans/escala-dry-pos-acoes-rapidas-impl.md e o fix de format
pré-existente de docs/plans/liderancas-coluna-municipios.md (bloqueava format:check).
2026-08-04 01:14:35 -03:00
Francisco Ignácio Fontoura Solla
5a52bb0efa fix(int): batch B19 usa municípios alocados — remove raça com cap de assessores
O teste 'assigns and removes multiple municipalities in one batch' escolhia o
segundo município com id not_equals cru; sob ~48 workers paralelos podia cair
num município que um spec paralelo (cap 10 do B27/B154) acabara de encher,
fazendo o batch estourar o teto. getMunicipality() aloca município
globalmente único e purga resíduo.
2026-08-03 23:13:01 -03:00
Francisco Ignácio Fontoura Solla
26b2205c3a B154 — criar assessor inline no popover da lista de municípios
Closes #352

- POST /campanha/municipios/advisors aceita body de criação (name) na union;
  createMunicipalityAdvisorRecord cria campaignUser advisor (stub
  <slug>@criado.invalid, senha aleatória) e atribui ao município na mesma
  transação (cap 10 com rollback, lock municipality-advisors:{id})
- MunicipalityAdvisorCreateProvider compartilha o assessor criado entre linhas;
  opção "+ Criar assessor" e chip otimista com id temporário no control
- advisorNameSchema unificado em primitives.ts; predicado de placeholder cobre
  @criado.invalid (reset de senha e célula de e-mail tratam stubs inline)
- Destrava o spec e2e de municipios (falhas pré-existentes em main): key
  warning do fragment trailing de MunicipalityFilters; settle-wait da animação
  do Drawer no teste B136
2026-08-03 22:46:06 -03:00
Francisco Ignácio Fontoura Solla
cf09cb548d format: Prettier 2026-08-03 21:06:52 -03:00
Francisco Ignácio Fontoura Solla
d200a1b012 B86 — ações rápidas — apoiadores (staff): catálogo no FAB/overlay 2026-08-03 21:03:13 -03:00
Cursor Agent
607eecd12b
B153 — simplify: phone assert helper, displayValue interno
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 14:09:14 +00:00
Cursor Agent
bb4c1ab199
B153 — editar nome, e-mail e celular da liderança (lista + detalhe)
- leadershipContactUpdateSchema + updateLeadershipContactRecord
- LeadershipContactFieldControl (lápis → input → blur-save)
- Lista e bloco Contato no detalhe
- Int tests para update, conflito de telefone e escopo

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 14:02:59 +00:00
Cursor Agent
fb75f7235b
fix(P5-P1): schedule campaign notification push after txn commit
queueMicrotask under req.transactionID raced withPayloadTransaction
commit. Register after-commit callbacks, soft-fail missing notification
rows on send, and pin the schedule with a convention unit spec.
2026-08-03 04:40:38 +00:00
Cursor Agent
30a5a8a5e3
B151 — FAB secundário no detalhe município v2 (#334)
- Catálogo v2 de ações rápidas: dossiê, eleições, nova liderança, giro
- Integra no chassis B126 via registry + context sync na rota v2
- Omitido registrar sinal (já na faixa de status B147)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 01:03:10 +00:00
Francisco Solla
2414001606
Merge pull request #343 from fsolla/cursor/pool-i333-5914f625-bd1e
B150 — Município v2 bloco Agora (encaminhamento + sugestão + visita)
2026-08-02 21:53:38 -03:00
Francisco Solla
d134dd0462
Merge pull request #346 from fsolla/cursor/pool-i315-94cacb95-bf11
B145 — Detalhe de entidade — título no header (sem hero espaçoso)
2026-08-02 21:52:20 -03:00
Francisco Solla
7753470a3b
Merge pull request #345 from fsolla/cursor/pool-i296-f91a8b70-0be4
OPS18 — Promote plan-issue dual (agente + Action no merge)
2026-08-02 21:45:56 -03:00
Cursor Agent
ffe7eca629
test(e2e): assert assessoria on dossiê tab after B145 hero removal 2026-08-03 00:29:48 +00:00
Cursor Agent
c62fe67809
test(e2e): assert entity identity in shell chrome (B145) 2026-08-03 00:29:48 +00:00
Cursor Agent
5696aef9ed
B145 — move entity identity to shell header on detail pages
- Detail routes return null from campaignPageChrome catalog (override per page)
- SetCampaignPageChrome + generateMetadata on 8 entity detail routes
- Remove body hero (h1, Voltar links, duplicate identity chips)
- Keep operational badges/chips in page body
- Update zone map e2e to assert shell chrome title slot

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:29:48 +00:00
Cursor Agent
debfd37919
style: prettier B150 files 2026-08-03 00:28:54 +00:00
Cursor Agent
6955cccc6b
B150 — Município v2 bloco Agora (encaminhamento + sugestão + visita)
- Loader e seção Agora na rota /campanha/municipio/[slug]/v2
- Encaminhamento editável com auto-save via POST /municipios/next-steps
- Até 2 sugestões E11 com resolve/dispensa existente; silêncio explícito
- Visita E13 condensada em uma linha + link ao compositor de giros
- Revalidação v2 ao resolver sugestão; helper compartilhado de empty state E11

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:28:47 +00:00
Francisco Solla
deefe566b0
Merge pull request #341 from fsolla/cursor/pool-i332-0d1ca390-b998
B149 — Município v2 — rede (lista edit-in-place)
2026-08-02 21:24:44 -03:00
Cursor Agent
7665b1062c
fix(ops): OPS18 simplify — Related regex, already-ready, job gate
Aperta parseRelatedIssueNumbers (anti hyphen), canPromotePlanIssue
already-ready, workflow só com Related # no body + PR_BODY, soft-fail
por Issue no Action script.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:22:55 +00:00
Francisco Solla
90bbfeb2da
Merge pull request #339 from fsolla/cursor/pool-i302-2f4dd995-03ba
B136 — Início / FAB — grade 3 colunas × 2 linhas (corrigir orientação)
2026-08-02 21:20:55 -03:00
Cursor Agent
b7f4e16c0f
feat(ops): OPS18 — Action promote Related #N no merge
Safety net dual com agent:ready: no merge em main, workflow lê Related #N
e promove Issues ainda aguardando plano (canPromotePlanIssue), soft-skip
idempotente. Helper parseRelatedIssueNumbers + pins unitários.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:20:48 +00:00
Francisco Solla
1266b47b0e
Merge pull request #336 from fsolla/cursor/pool-i292-298f3db9-b2f7
OPS17 — Ciclo de vida plan-issue — colaboração fechada antes de claim
2026-08-02 21:17:03 -03:00
Francisco Solla
43dcd8da9b
Merge pull request #325 from fsolla/cursor/pool-i306-4a43a35f-79fc
B138 — Atividades: presets e busca na omnibox; remover abas
2026-08-02 21:14:02 -03:00
Cursor Agent
c95d88562e
format + e2e prewarm for pledge autosave routes 2026-08-03 00:11:22 +00:00
Cursor Agent
7d89356710
B149 — Município v2 rede: lista edit-in-place com top-N lideranças
- Seção Rede na rota v2 com tabela/lista densa e CTAs Ver todas / Nova
- Células autosave (B9) para declarado e estimado via JSON routes
- Loader + view puro com ordenação por estimado/declarado
- Revalidate v2 em writes de pledge; unit tests de merge/sort

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:10:30 +00:00
Cursor Agent
d2cc1d2693
feat(ops): OPS17 — plan-issue blocked até plano em main
Register com --plan nasce blocked (não claimável); pnpm agent:ready
promove após o plano em main. Gate da skill plan-issue endurecido.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:06:14 +00:00
Cursor Agent
0d124b47cb
fix: restore searchOnlyListOmnibox exports from main (CI merge) 2026-08-03 00:01:48 +00:00
Cursor Agent
a2e952b3a2
fix(B136): resolve listOmniboxB128 merge conflict markers for CI 2026-08-02 23:53:33 +00:00
Cursor Agent
a2553e1f03
merge main into B147; keep optional sinal body for v2 status
Align with B134 engagement write path; preserve B147 sinal empty-motivo
(collection + zod) required by the status-strip confirm rite.
2026-08-02 23:48:29 +00:00
Francisco Solla
e3a54be217
Merge pull request #337 from fsolla/cursor/pool-i313-f30f822f-8e1d
B144 — Sidebar — entrada para Organizações
2026-08-02 20:38:36 -03:00
Cursor Agent
764343da8e
test(B128): cover search-only omnibox exports for knip graph 2026-08-02 23:36:37 +00:00
Cursor Agent
18c1317884
chore(B136): keep groupActionsByRow internal; drop unused import 2026-08-02 23:36:28 +00:00
Cursor Agent
4cbb65963b
fix(B136): pass raw boxes to grid geometry assert 2026-08-02 23:36:28 +00:00
Cursor Agent
3ac5d303e8
test(B136): assert 3×2 grid by column clusters (tolerant row heights) 2026-08-02 23:36:28 +00:00
Cursor Agent
993b9af69b
style: format B136 files with Prettier 2026-08-02 23:36:28 +00:00
Cursor Agent
2f2c8563a5
B136 — corrigir grade 3×2 no Início e FAB overlay
- layout=grid no botão com w-full/min-w-0 sem box-content no mobile
- li min-w-0, gap-2, data-layout=grid-3 no strip
- e2e com geometria (boundingBox) em viewport 390
- impl plan docs/plans/inicio-acoes-grade-3cols-2rows-impl.md

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:36:28 +00:00
Cursor Agent
ce44ec1949
chore: drop unused search-only omnibox exports; finish B147 simplify notes
Knip: remove dead suggestions/remove/clear helpers after organizações moved
to a typed omnibox. Land deferred-helper note + empty-sinal int coverage.
2026-08-02 23:35:22 +00:00
Cursor Agent
6b8aa8b6cf
fix(campanha): B147 simplify — sinal empty body + cold select
Allow empty motivo on sinal through collection beforeValidate; distinguish
warm-typeless vs cold in the v2 signal select; reuse list signal form action.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:33:46 +00:00
Cursor Agent
c66ffd598c
fix(campanha): map municipio v2 route in e2e affected manifest
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:30:51 +00:00
Cursor Agent
0aa47f8673
feat(campanha): B147 município v2 shell + faixa de status
Rota paralela /campanha/municipio/<slug>/v2 com selects de nível, tendência e
sinal (modal de motivo opcional), classe territorial e agregado. Soft-dep B134:
motivo opcional no movimento de nível e remoção do campo “voltar atrás” do
write path/lista.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:29:05 +00:00
Cursor Agent
4ea2085239
B138 — simplify: min-length e normalização da busca
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:26:48 +00:00
Cursor Agent
f0a7758eb3
B138 — atividades: presets e busca na omnibox; remove abas
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:26:48 +00:00
Cursor Agent
7fe42a5fa5
test(omnibox): cover search-only helpers for knip graph (#313)
Knip could not see exports only referenced from client filter components;
extend B128 unit spec so CI knip stays green.
2026-08-02 23:25:50 +00:00
Francisco Solla
6539d0cc55
Merge pull request #329 from fsolla/cursor/pool-i288-97b0de1d-f857
B134 — Motivo opcional (tendência + nível); remover voltar atrás do DB
2026-08-02 20:25:23 -03:00
Francisco Solla
466a34525d
Merge pull request #328 from fsolla/cursor/pool-i317-1bee052c-c52f
B146 — FAB overlay idle on open; close/handle chrome
2026-08-02 20:23:54 -03:00
Francisco Solla
8a60b6f0f7
Merge pull request #326 from fsolla/cursor/pool-i310-73006baf-d8e9
B142 — Assessores — filtrar por município da carteira na omnibox
2026-08-02 20:22:16 -03:00
Francisco Solla
56b96198cb
Merge pull request #324 from fsolla/cursor/pool-i309-84ab81f4-8444
B141 — Apoiadores — filtro por fonte na omnibox
2026-08-02 20:20:40 -03:00
Francisco Solla
66c84a9f6b
Merge pull request #321 from fsolla/cursor/pool-i289-de3861a0-5cd2
B135 — Wizard Voltar = passo imediatamente anterior na cadeia
2026-08-02 20:19:52 -03:00
Francisco Solla
c90c7d3722
Merge pull request #323 from fsolla/cursor/pool-i311-f0d9725a-dfcb
B143 — Lideranças — filtrar por organização e dobradinha na omnibox
2026-08-02 20:19:41 -03:00
Francisco Solla
4448d6c80b
Merge pull request #322 from fsolla/cursor/pool-i308-39f49212-ad42
B140 — Demandas omnibox: tipo e busca
2026-08-02 20:19:17 -03:00
Cursor Agent
ea6641071e
test(nav): reuse ORGANIZATIONS_LIST_PATH in sidebar spec
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:15:18 +00:00
Cursor Agent
0dc3d22646
feat(nav): add Organizações to staff sidebar (B144, #313)
Insert Organizações after Lideranças in staffNav so staff can discover
the existing /campanha/organizacoes list from the structural nav.
Leaders remain on leaderNav; isCampaignNavActive already covers list,
detail, and /nova paths.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:13:34 +00:00
Cursor Agent
974907f023
Format migration unit spec 2026-08-02 23:10:20 +00:00
Cursor Agent
b931092320
B142 — format + impl plan deferred debts note 2026-08-02 23:09:01 +00:00
Cursor Agent
f40957f101
B134 — allow empty rationale on movimento allocation decisions
- Relax AllocationDecision.rationale required flag; validate in hook
- Fix empty-motivo int test to use first movement from null

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:08:52 +00:00
Cursor Agent
fced1f3e1d
B146 — focus dialog title on open (B42 pattern); consolidate mobile test stub
- Move initial focus to sr-only title instead of void/body (a11y).
- Fold matchMedia mock into stubMobileViewport helper.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:07:29 +00:00
Cursor Agent
7f17a7db79
fix(campanha): B140 wire onCommitQuery + simplify import hygiene
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:06:48 +00:00
Cursor Agent
4d4764893e
B142 — filtrar assessores por município da carteira na omnibox
Adiciona dimensão municipality na URL e omnibox da lista de assessores,
espelhando o precedente de lideranças: chips removíveis, busca tipada no
catálogo e filtro server OR via municipality.advisors.

Closes #310

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:05:33 +00:00
Cursor Agent
989e3abab8
Format B135 wizard back navigation files 2026-08-02 23:05:09 +00:00
Cursor Agent
c9fdc59ed2
B143 — omnibox de lideranças filtra por organização e dobradinha
- URL state: organization / stateDeputy (OR inclusivo por dimensão)
- Facets cross-filtered em leadershipData
- Adapter omnibox + wiring na página
- Testes unit/int e plano de implementação

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:04:45 +00:00
Cursor Agent
56e60ce589
B146 — stub innerWidth in mobile overlay unit tests
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:04:32 +00:00
Cursor Agent
28525aab4b
B135 — Voltar no wizard usa passo lógico anterior na cadeia (#289)
- wizardPreviousHref em wizardActionChain para todos os elos B98
- Avanço encadeado com router.push; Voltar no chrome com replace
- Testes unitários para href anterior e navegação pending

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:04:26 +00:00
Cursor Agent
e731e407ab
style: prettier format B139 files 2026-08-02 23:04:25 +00:00
Cursor Agent
59206574e6
Fix int test marker helper call (fixtures.value)
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:04:25 +00:00
Cursor Agent
e8f21a9885
B140 — Demandas omnibox: tipo e busca na lista
- Add q param to demand list URL contract and buildDemandListWhere
- Extend demandOmnibox with kind + search chips/suggestions/actions
- Search title and leadership contact name (visible list columns)
- clearDemandOmnibox drops bar filters but keeps activity deep-link
- Unit + int tests; impl plan docs/plans/demandas-omnibox-tipo-busca-impl.md

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:03:24 +00:00
Cursor Agent
b509c7d7b3
B141 — filtro por fonte na omnibox de apoiadores (#309)
Adiciona dimensão Fonte ao estado de lista, where-builder, aggregate SQL
e adapter omnibox (B128), com guard isSupporterSource e testes.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:03:22 +00:00
Cursor Agent
23e71d2de3
B146 — FAB overlay idle on open; close/handle chrome fixes
- Block Dialog onOpenAutoFocus and Drawer initialFocus so search does not
  steal focus on open (actions stay visible per B132 idle state).
- Reserve md:pr-10 on search chrome so desktop close button does not overlap.
- Enable showSwipeHandle on mobile drawer (no X button).
- Unit tests for idle state, swipe handle, and close spacing.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:03:04 +00:00
Cursor Agent
5335f69b43
B134 — motivo opcional (tendência + nível); remove reversalSignals
- Make engagement level note optional in schema and UI
- Remove reversalSignals field from popover, action snapshot and DB
- Add data migration to strip reversalSignals from movimento snapshots
- Wizard trend note step no longer requires text; labels show (opcional)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:02:53 +00:00
Cursor Agent
68ab54756e
B139 — tipo na omnibox de organizações
Adapter organizationOmnibox (q + kind exclusivo), OrganizationFilters
migrado do searchOnly degenerado, testes unit B128.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:02:46 +00:00
Francisco Solla
345db9474f
Merge pull request #287 from fsolla/cursor/pool-i283-a9b2f259-fcc3
B133 — Listas /campanha — subtítulos curtos (sem prosa; filtro salvo em Municípios)
2026-08-02 18:33:46 -03:00
Francisco Solla
285caba300
Merge pull request #286 from fsolla/cursor/pool-i280-093296a2-744e
B132 — Ações rápidas — grade 3×2 (Início + FAB) e polimento do overlay
2026-08-02 18:23:29 -03:00
Cursor Agent
f89fc4b81f
feat(campanha): B133 — listas sem subtítulo de prosa + filtro salvo no header
- Remove prose subtitles from list routes, Quadro and create/edit chrome
- Keep subtitles on Organizações, Conceitos, Perfil, Contatos (out of scope)
- MunicipalityListPageChrome: saved filter name as header subtitle when URL matches
- Edit pages: title-only chrome override
- Impl plan: docs/plans/listas-sem-subtitulo-prosa-impl.md

Closes #283

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 21:16:46 +00:00
Francisco Solla
1771c5c2c4
Merge pull request #282 from fsolla/cursor/pool-i265-cbfa8dec-92a1
B128 — Adotar barra omnibox nas demais listas /campanha
2026-08-02 18:12:49 -03:00
Cursor Agent
8d30667c2a
B132 — grade 3×2 no Início/FAB overlay e polimento do drawer
- CampaignHomeActionStrip: grid-cols-3 no mobile; variante grid para overlay
- CampaignQuickActionsOverlay: grade no lugar da strip; altura por conteúdo;
  pt-4 na busca focada; limpa query ao fechar
- CampaignQuickActionsHost: clear() em fechamento e mudança de rota
- Testes unit atualizados; impl plan em docs/plans/

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 21:08:09 +00:00
Cursor Agent
f59d4a6736
chore: format + e2e manifest mappings for B128 omnibox modules 2026-08-02 20:56:12 +00:00
Cursor Agent
0903576f9e
B128 — Adotar barra omnibox nas demais listas /campanha
Estende o chassis B127 (CampaignListOmnibox + adapters puros por domínio)
para territórios, lideranças, dobradinhas, apoiadores, atividades, demandas,
organizações e assessores. Remove pilhas antigas (search debounced, collapsible,
mobile selects, CampaignFilterChips). URL contracts extraídos para módulos
client-safe onde necessário (demand/organization/advisor). Testes unitários
nos adapters + hook navigate-only.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 20:55:12 +00:00
Francisco Ignácio Fontoura Solla
0baeda22a0 Fix e2e failures from FAB chrome waits and B27 advisor strict-mode.
B126 mobile specs looked for a page heading that shell chrome replaced;
wait on campaignPageChrome with exact title text, and wrap the desktop
title so subtitle no longer collides. Pin B27 to the município-specific
assessores trigger and map home-search libs to campaignMunicipalities.
2026-08-02 17:49:08 -03:00
Cursor Agent
02c4843c5c
fix(e2e): restore chrome title slot locator; drop unused scenario hook
Ensure campaignPageChrome targets campaign-page-chrome-title after rebase,
and remove useMunicipalityEstimateScenario orphaned by B129 overview removal.
2026-08-02 20:32:06 +00:00
Cursor Agent
a68e1d8efb
fix(e2e): target chrome title slot so subtitle does not collide
Desktop chrome put title+subtitle in one text node; exact getByText
failed. Mobile subtitle matched Municípios case-insensitively. Mark the
title with data-slot and assert against that node.
2026-08-02 20:31:18 +00:00
Cursor Agent
8e9864362b
fix(e2e): exact match in campaignPageChrome for mobile subtitles
Mobile top bar puts title + subtitle in the same chrome slot; substring
getByText('Municípios') also hit 'Os 435 municípios…'.
2026-08-02 20:31:07 +00:00
Cursor Agent
62673367c9
test(e2e): harden município list selectors after B127 omnibox
Use campaignPageChrome for FAB specs and .first() on Editar assessores
when mobile+desktop cells both mount.
2026-08-02 20:30:55 +00:00
Cursor Agent
d64b700872
test(e2e): assert municipality omnibox instead of Ordenado por label 2026-08-02 20:30:55 +00:00
Cursor Agent
3c993463bd
style: prettier municipalityOmnibox unit spec 2026-08-02 20:30:55 +00:00
Cursor Agent
3b2be37972
fix(campanha): B127 omnibox navigate + keyboard a11y from simplify
Use hook.navigate so draft search cannot clobber chips; replace cmdk with
ARIA combobox keyboard contract; memoize suggestion seeds per facet snapshot.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 20:30:55 +00:00
Cursor Agent
21e20a6719
feat(campanha): B127 omnibox filter bar chassis + Municípios pilot
Replace the stacked município filter controls with a shared omnibox
(chips + suggestions) backed by the frozen list URL contract; scenario
and non-default sort appear as presentation chips.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 20:30:55 +00:00
Cursor Agent
16c2358479
B129 — Restore anchored trend-control E2E regex after rebase 2026-08-02 20:07:35 +00:00
Cursor Agent
a92701db68
B129 — Format campaignMunicipalities E2E spec 2026-08-02 20:04:07 +00:00
Cursor Agent
4cb21576c2
B129 — Anchor trend-control E2E regex per codebase convention 2026-08-02 20:04:07 +00:00
Cursor Agent
5ded385ed9
B129 — Stabilize campaignPageChrome E2E helper for title + subtitle layout 2026-08-02 20:04:07 +00:00
Cursor Agent
5bb8bcc62b
B129 — Align municipality E2E with page chrome and scoped trend control 2026-08-02 20:04:07 +00:00
Cursor Agent
76597292c9
B129 — Simplify cleanup after overview removal
Fix duplicated JSDoc, trim stale int test, defer perf follow-ups in impl plan.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 20:03:58 +00:00
Cursor Agent
b4f2e2fa0a
B129 — Remove municipality list overview panel
Remove MunicipalityListOverview from /campanha/municipios and drop the
overview field/computation from loadMunicipalityListPageBundle. Filters,
scenario selector, and per-row goal coverage for sorting remain unchanged.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 20:03:58 +00:00
Francisco Solla
f176412e2b
Merge pull request #277 from fsolla/cursor/pool-i271-cbcf1835-00b9
B130 — Sidebar /campanha sem logo nem chip de escopo no topo
2026-08-02 17:03:28 -03:00
Cursor Agent
1cab8e9539
Harden B126 FAB e2e waits for layout chrome instead of page h1 2026-08-02 19:50:40 +00:00
Francisco Solla
7839a88a5a
Merge pull request #270 from fsolla/cursor/pool-i250-d89b6314-599f
B123 — Campanha — orientação na shell + aba (sem títulos grandes de seção)
2026-08-02 16:16:58 -03:00
Francisco Solla
5e7219f849
Merge pull request #269 from fsolla/cursor/pool-i260-ac255f26-42ec
B126 — FAB de ações rápidas (substituir bottom drawer persistente)
2026-08-02 16:15:01 -03:00
Cursor Agent
5fe0510f84
test(e2e): atualiza specs do drawer persistente para FAB overlay (B126) 2026-08-02 19:01:06 +00:00
Cursor Agent
6d793026d7
test(e2e): match page chrome title without exact text on desktop 2026-08-02 19:01:03 +00:00
Cursor Agent
3d13972e48
test(e2e): assert visible campaign page chrome on desktop viewport 2026-08-02 18:55:16 +00:00
Francisco Solla
62fb4c66e8
Merge pull request #263 from fsolla/cursor/pool-i259-b024c8a7-cbc1
OPS16 — CI: cooldown de deploy de produção 30 → 15 min
2026-08-02 15:46:51 -03:00
Cursor Agent
6ad0ed6cc7
style: format B126 quick-actions files 2026-08-02 18:40:07 +00:00
Cursor Agent
dbce18002c
B126 — FAB de ações rápidas substitui drawer persistente
Remove o bottom drawer não-modal (snap/peek/scroll-coupling) e introduz
um FAB em todas as larguras fora do Início e wizards. Abrir o FAB mostra
overlay modal: Dialog no desktop (busca em cima) e Drawer no mobile
(ações em cima), reutilizando strip e busca globais do Início.

Closes #260

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:39:27 +00:00
Cursor Agent
cad3556bce
OPS16: reduce production deploy cooldown from 30 to 15 minutes
Change DEPLOY_COOLDOWN_MS constant, update ci.yml deferral log, and
adjust unit test pins. Closes #259.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:38:36 +00:00
Francisco Solla
33d775396e
Merge pull request #258 from fsolla/cursor/pool-i253-48e749f5-b609
B124 — Sidebar Municípios: filtros salvos colados (sem disclosure)
2026-08-02 15:38:01 -03:00
Cursor Agent
9f19fb2f88
chore: format 2026-08-02 18:24:31 +00:00
Cursor Agent
57b1790e12
B124: corrige foco ao apagar filtro e remove useId morto
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:23:47 +00:00
Cursor Agent
1bcb49805c
format: HomeSearchMunicipalityGroup + homeSearchUi spec 2026-08-02 18:22:36 +00:00
Cursor Agent
7e6bc9d984
B125 — nearest município first in search suggest + wizard
- Restore B117 home search: scopeMunicipalities, merge prefix, session prompt
- Shared useNearestMunicipalitySlug (B14 key) for busca geral + wizard
- Wizard merge uses geoSlug only — no Perto de você / km copy on rows
- Impl plan: docs/plans/municipio-geo-topo-sugestoes-busca-impl.md

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:21:52 +00:00
Cursor Agent
9d86510b57
B124: filtros salvos sempre visíveis sob Municípios (sem disclosure)
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:20:27 +00:00
Cursor Agent
d4425251c6
style: prettier em arquivos B123 2026-08-02 18:11:54 +00:00
Cursor Agent
24e35d5a14
test: atualizar specs do shell para CampaignPageChromeProvider
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:11:11 +00:00
Cursor Agent
ccfbf592a8
B123 — orientação na shell + aba sem títulos de seção no corpo
Move page title/subtitle to CampaignMobileTopBar, CampaignDesktopHeader,
and tab metadata (Solla - Campanha - %s). Strip section headers and scope
chips from list/create routes; detail pages keep entity h1.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 18:08:47 +00:00
Cursor Agent
b50e799796
B122 — format 2026-08-02 17:58:24 +00:00
Cursor Agent
74c9c3a6e5
B122 — simplify: private button classes, drop bleed shell prop
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 17:57:40 +00:00
Cursor Agent
8a64e330e6
B122 — fix unit assertion for responsive overflow classes
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 17:55:13 +00:00
Cursor Agent
5b6af56b22
B122 — Início mobile: ações em grade 2×3 sem scroll
- CampaignHomeActionStrip: variant responsive (grid lt-md, strip md+)
- CampaignHomeActionButton: layout strip/grid/responsive class contracts
- CampaignHomeLayout: remove mobile bleed for grid gutter
- CampaignQuickActionsDrawer: explicit variant=strip
- Plans: inicio-acoes-grid-2x3-mobile + impl
- Tests: unit grid/strip + e2e mobile visibility

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 17:53:39 +00:00
Francisco Ignácio Fontoura Solla
9e4c537f8f Format campaignAccessRequestMemo int spec with Prettier. 2026-08-02 14:06:20 -03:00
Francisco Ignácio Fontoura Solla
3001b9da00 Pin access memo int test to the actor's campaign_user read.
Depth-1 populate of leftover leadership.user rows was counted as a second
fresh-actor select and falsely failed the per-request memo guard.
2026-08-02 14:05:53 -03:00
Francisco Ignácio Fontoura Solla
8bca9d8e82 Separate intention plans from engineering execution in agent skills.
plan-issue now shapes product intent only; agent-work-issue (pool) and
work-issue (human gate) deliberate the implementation plan before coding.
2026-08-02 14:02:18 -03:00
Francisco Solla
906008ef5d
Merge pull request #246 from fsolla/cursor/pool-i240-27201b52-9bc6
OPS13 — Remover pins de className / tokens Tailwind literais
2026-08-02 13:58:59 -03:00
Francisco Solla
1ecfd200be
Merge pull request #245 from fsolla/cursor/pool-i241-a6a48218-9e90
OPS14 — Form-action guard — política na escada, não no filename
2026-08-02 13:57:26 -03:00
Cursor Agent
62b6001ed8
OPS14 — form-action guard sweeps ladder, not filename
Replace the W4d filename-based convention spec with a sweep of every
src/app/(campaign) file that calls mapCampaignFormActionError without
runCampaignFormAction/Redirect. Migrate profile, leaderSupporter, and
convite-login to the shared wrappers; allowlist auth/password and the
existing bespoke formActions exceptions with documented reasons.

Closes #241

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 16:47:42 +00:00
Cursor Agent
0c605f4375
OPS13 — remove className/Tailwind literal pins from unit specs
Replace layout/spacing utility string assertions with stable contract
pins (data-slot, data-retracted, exported constant identity, ARIA).
Delete cosmetic-only padding/bleed/grid pins per plan remover-pins-classname.

Closes #240

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 16:47:27 +00:00
Cursor Agent
76ec34c436
OPS15: remove B102 TooltipProvider JSX nesting guard (#242)
Delete the source-shape regex pin from codebaseConventions.unit.spec.ts.
Behavioral coverage remains in campaignQuickActionsDrawer.unit.spec.tsx
(focus→suggest with priority:alta hits).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 16:45:58 +00:00
Francisco Solla
8741eb5151
Merge pull request #154 from fsolla/cursor/pool-i149-d8165a59-5947
B110 — Wizard return path via allowlisted from query
2026-08-01 18:04:14 -03:00
Francisco Solla
ab910b499b
Merge pull request #153 from fsolla/cursor/pool-i148-3c37da6e-2281
B109 — Bottom drawer: dock polish, fullscreen search, exclude current entity
2026-08-01 18:03:27 -03:00
Cursor Agent
2e3740ef3f
Format B110 wizard return path changes 2026-08-01 20:40:04 +00:00
Cursor Agent
d5697bcfcb
B110 — wizard return path via allowlisted from query
Propagate return path from quick actions through the B98 wizard chain so
dismiss, skip, and chain end navigate back to the originating page instead
of always redirecting to Início.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 20:39:24 +00:00
Cursor Agent
fcd0206da6
chore: prettier format (B109) 2026-08-01 20:38:53 +00:00
Cursor Agent
490d5deeb7
B109 — bottom drawer: fullscreen search, wider labels, exclude current entity
- Add QUICK_ACTIONS_SNAP_FULL; drawer expands on search focus and hides action strip
- Tighten dock bottom gap; restore dock/collapsed on blur per scroll position (B100)
- Widen CampaignHomeActionButton to w-[5.5rem] for two-line labels
- Client-side filter excludes current page entity from drawer search hits
- Unit + e2e coverage for full snap, exclusion, and label readability
2026-08-01 20:38:10 +00:00
Cursor Agent
7a0f9ffed5
B111 — strip edge-to-edge bleed + silent flat delta on Início
- Allow horizontal bleed on home-actions retraction shell (overflow-y-hidden
  instead of overflow-hidden) so -mx-4 is not clipped by the chrome wrapper.
- Hide HomeSummaryDelta chip and period caption when delta is 0 or null;
  add shouldShowHomeSummaryDelta helper and unit pins.

Closes #150

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 20:36:19 +00:00
Francisco Solla
7317eb335a
Merge pull request #147 from fsolla/cursor/pool-i143-27c538e8-dc86
B108 — Wizard autofocus do 1º input + destaque do último cenário focado
2026-08-01 16:17:22 -03:00
Cursor Agent
19fb7ca76d
Format campaignWizardShell unit spec 2026-08-01 19:03:35 +00:00
Cursor Agent
ef4cef8c4c
Fix vote estimate compact highlight test assertion
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 19:01:12 +00:00
Francisco Solla
5cdcda98b9
Merge pull request #146 from fsolla/cursor/pool-i141-1ce99b5a-0b74
B106 — Início mobile — Voltar no header + Android back + skeleton das sugestões
2026-08-01 15:59:53 -03:00
Cursor Agent
a52a622ba5
B108 — wizard autofocus first input + active scenario highlight
- CampaignWizardShell: contentFocus prop skips title focus on form steps
- Wizard steps with inputs: contentFocus=none + autoFocus on first field
- VoteEstimateScenarioInputs compact: highlight follows activeScenario
- Municipality list compact overlay tracks focused scenario for highlight
- Unit tests for shell focus mode, compact classes, and votes step

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:59:42 +00:00
Francisco Solla
97ae71b0fe
Merge pull request #145 from fsolla/cursor/pool-i142-0f38ef32-58e4
B107 — Wizard município — sem título + busca sticky + skeleton
2026-08-01 15:57:38 -03:00
Cursor Agent
593b2273c2
style: prettier e2e wizard specs 2026-08-01 18:45:01 +00:00
Cursor Agent
ca18ce0f17
test(e2e): wizard município sem h1 visível (B107) 2026-08-01 18:44:39 +00:00
Cursor Agent
c524b1f19a
B105: format home search unit + mark plan implementado 2026-08-01 18:40:51 +00:00
Cursor Agent
98a69ab153
B105: bottom drawer handle top, scroll-up dock, discreet search peek
Revert handle to the top of the sheet; recalibrate collapsed/dock snaps
for handle+search and 2-line action labels; scroll ↓ collapses and ↑
re-docks; collapsed keeps a placeholder-less search peek that expands
on focus.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:40:51 +00:00
Cursor Agent
f80f672732
B106 — format 2026-08-01 18:38:50 +00:00
Cursor Agent
13a9ccf064
style: prettier CampaignWizardShell and tests 2026-08-01 18:36:55 +00:00
Cursor Agent
fc465639f2
B107 — wizard município sem título, busca sticky e skeleton
Remove the municipality step h1 via optional stepTitle on CampaignWizardShell.
Keep search input sticky under the wizard header, show hit-row skeletons
while suggest/search loads, and tighten mobile top padding when titleless.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:36:30 +00:00
Cursor Agent
84365ddf54
B106 — Voltar no header mobile, Android back e skeleton das sugestões
- CampaignHomeSearchChromeContext + publisher no Início
- CampaignMobileTopBar troca SidebarTrigger por Voltar quando uiFocused
- useHomeSearchFocusHistory com pushState/popstate para back do SO
- HomeSearchSuggestSkeleton enquanto isFetching sem hits
- Unit tests para history helper, top bar e skeleton

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:36:28 +00:00
Francisco Solla
de9377de85
Merge pull request #138 from fsolla/cursor/pool-i129-7bc105bb-7d33
B102 — Crash — busca no bottom drawer (focus / digitação)
2026-08-01 15:33:04 -03:00
Francisco Solla
1c2f767d96
Merge pull request #140 from fsolla/cursor/pool-i130-c5143bec-1fbc
B103 — Remover título Sugestões no empty state da busca
2026-08-01 15:24:21 -03:00
Cursor Agent
0174ae25db
B102 — pin layout TooltipProvider wrap + mark plan done
Conventions guard prevents regressing provider under CampaignAppScrollChrome.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:18:19 +00:00
Cursor Agent
653f04062b
B102 — fix drawer search crash on focus/type (TooltipProvider scope)
Lift TooltipProvider in the campaign app layout so it wraps
CampaignAppScrollChrome (scroll + quick-actions drawer). Focus→suggest
with priority-alta hits mounted CampaignHoverTooltip outside the old
provider and crashed the page. Unit pins cover focus→suggest and type→search.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:18:19 +00:00
Cursor Agent
259488fd33
B103 — omit Sugestões heading in home search suggest mode
Hide the visible h2 when resultKind is suggest; keep aria-label on the
section for screen readers. Search mode still shows Municípios heading.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 18:11:54 +00:00
Francisco Solla
313a041d5b
Merge pull request #137 from fsolla/cursor/pool-i131-803d9700-95db
B104 — Wizard: label genérica "Pular" nas ações encadeadas
2026-08-01 15:10:27 -03:00
Cursor Agent
2fa6e7298a
B104 — Wizard: label genérica "Pular" nas ações encadeadas
Substitui as quatro labels longas de skip (votos, tendência, sinal,
liderança) por uma constante compartilhada WIZARD_CHAIN_SKIP_LABEL.
Resolvers e condições de exibição permanecem intactos (B96/B98).

Closes #131

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 17:57:12 +00:00
Cursor Agent
1053c571eb
B101 — strip de ações: bleed sem gutter + hit area via padding
- CampaignHomeActionStrip: gap-0 no ul, px-4 interno (md:px-0)
- CampaignHomeActionButton: px-2 + box-content para hit area sem encolher coluna
- CampaignQuickActionsDrawer: -mx-4 bleed na strip (espelha Início)
- Pins unitários para classes de layout B101

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 17:55:54 +00:00
Cursor Agent
fded303fe6
fix(ci): stop trusting GET /v13 alias for production domain ownership
CI short-circuited "already aliased" when v13 listed pt.jorgesolla.com.br
on a fresh --prod deploy while the domain still pointed at an older
Current. Verify only via GET /v2/.../aliases and /v4/aliases?domain=.
Also drop invalid --yes on vercel alias set (CLI 55).
2026-08-01 16:12:30 +00:00
Cursor Agent
fdd318692c
fix(ci): clear domain gitBranch so CLI prod deploys can alias
Git-branch-bound domains only move via Git Integration. With Git
auto-deploy OFF, clear the binding, then promote + alias + verify.
2026-08-01 15:59:41 +00:00
Cursor Agent
bcca5da92c
fix(ci): verify domain alias via aliases API + vercel alias set
Promote + POST aliases still left pt.jorgesolla.com.br off the staged
deployment's GET /v13 alias list. Verify with GET /v2/.../aliases and
GET /v4/aliases?domain=, log steps live, and also run `vercel alias set`
(recommended when Git Integration is off).
2026-08-01 15:48:59 +00:00
Cursor Agent
c59b3fe31d
fix(ci): explicitly alias pt.jorgesolla.com.br after Vercel promote
Promote alone left the staged deployment on *.vercel.app only. Assign the
custom hostname via POST /v2/deployments/{id}/aliases after verifying it is
attached to the project domains list.
2026-08-01 15:37:16 +00:00
Cursor Agent
baa8f14f82
fix(ci): promote Vercel deploy to pt.jorgesolla.com.br
Disabling Git auto-deploy is correct for Actions-gated production. The
separate project toggle "Auto-assign Custom Production Domains" must stay
ON — when OFF, `vercel deploy --prod` only aliases *.vercel.app and leaves
the custom domain on an older Current (exactly what we saw after #122).

After each deploy, ensure-production-alias re-enables auto-assign if needed,
promotes the deployment, and verifies pt.jorgesolla.com.br is attached.
Also adds workflow_dispatch "Vercel promote production" for promote-without-rebuild.
2026-08-01 15:26:31 +00:00
Francisco Solla
f20fca0506
Merge pull request #120 from fsolla/cursor/pool-i106-d9499b1d-9029
B98 — Encadear ajustes após a ação principal do wizard
2026-08-01 09:06:11 -03:00
Cursor Agent
ca623e7aee
B98 — e2e espera a cadeia após save; knip limpa exports
Atualiza campaignHomeActions para navegar ao próximo elo
(votos→tendência, sinal→tendência, liderança→sinal) e remove
exports de skip de votos não usados.
2026-08-01 11:52:50 +00:00
Francisco Solla
a7394d3b9e
Merge pull request #119 from fsolla/cursor/pool-i114-36b6c1a5-2d0b
B100 — Bottom drawer mobile — dock visível + recolhe no scroll
2026-08-01 08:49:14 -03:00
Cursor Agent
d8f54a5d73
B98 — format Prettier nos arquivos do encadeamento 2026-08-01 11:42:08 +00:00
Cursor Agent
346949d035
B98 — Encadear ajustes do wizard após a ação principal
Após salvar (ou Pular) votos/tendência/sinal/liderança, navega ao
próximo elo da matriz v1 no mesmo município via entryAction, em vez
de voltar ao Início. Helper puro wizardActionChain + skips alinhados.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 11:41:26 +00:00
Francisco Solla
b809bf71dd
Merge pull request #118 from fsolla/cursor/pool-i116-2df83301-bbe5
OPS12 — CI bloqueia Closes/Fixes em PR só docs/plans
2026-08-01 08:39:47 -03:00
Cursor Agent
a056616d4a
Fix drawer snap geometry: handle at bottom, scroll on content ref
- Reorder drawer so handle sits at the screen bottom (snap shows bottom band)
- Hide dock content when collapsed instead of clipping through search
- Attach scroll-collapse listener directly on campaign-content-scroll ref
2026-08-01 11:35:09 +00:00
Cursor Agent
1ad8863994
OPS12 — CI bloqueia Closes/Fixes em PR só docs/plans
- Lógica pura em plansOnlyClosesGuard.mjs + CLI check-plans-only-pr-closes
- Job plans-only-closes em ci-pr.yml (rollup checks)
- Unit tests com fixture do incidente #115
- plan-issue, agent-pr-workflow, PR template e GUARDRAILS atualizados

Closes #116

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 11:26:19 +00:00
Cursor Agent
e09f01e683
Format + e2e manifest for Drawer/shell B100 2026-08-01 11:20:08 +00:00
Cursor Agent
1bb3fc7303
B100 — bottom drawer dock on load + collapse on scroll
- Replace expanded 55dvh snap with fixed DOCK height (12rem) + COLLAPSED handle
- Fix Base UI snap geometry via padding-bottom on Drawer popup
- Lift snap state to CampaignQuickActionsSnapProvider with pathname reset
- Collapse drawer when campaign-content-scroll passes threshold; reopen only via handle/pull
- Block collapse while global search is focused or active
- Dynamic content-scroll padding for dock vs collapsed peek
- Unit tests for dock default, scroll collapse, handle toggle; e2e on municipality detail

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 11:19:13 +00:00
Cursor Agent
5181116d84
B96 — Header wizard: X na tendência standalone, Pular só encadeada
resolveWizardTrendSkip(entryAction) espelha resolveWizardSignalSkip:
skip só quando entryAction presente e diferente de change-trend.
WizardTrendChoiceStep e WizardTrendNoteStep condicionam skip/trailingAction.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 11:18:13 +00:00
Francisco Solla
270c58424b
Merge pull request #113 from fsolla/cursor/pool-i107-0e9a1b5f-b248
B99 — Strip de ações do Início — edge-to-edge no mobile + gap menor
2026-08-01 08:14:36 -03:00
Francisco Solla
18bcee4454
Merge pull request #112 from fsolla/cursor/pool-i103-cc23cd9a-d9c9
B95 — Polimento busca município no wizard (bullets, gaps, título mobile)
2026-08-01 08:10:02 -03:00
Francisco Solla
36b39ffdd9
Merge pull request #110 from fsolla/cursor/pool-i105-88d1d9e1-68c5
B97 — Salvar tendência fecha o wizard (não volta ao começo)
2026-08-01 08:07:41 -03:00
Cursor Agent
cbddd8c796
OPS11 — cooldown de 30 min entre deploys de produção (Vercel)
- Script vercel-deploy-cooldown consulta último deploy READY via API Vercel
- Job deploy em ci.yml adia upload quando idade < 30 min (sucesso, não falha)
- Job requeue espera o residual e re-dispara ci.yml
- vercel deploy passa --archive=tgz para reduzir pressão de upload
- Testes unitários para computeCooldownWait e fetch da API

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 10:54:55 +00:00
Cursor Agent
5ce82e2229
B97 — Salvar tendência fecha wizard (redirect home quando status já aplicado)
Após salvar a nota, o RSC refresh via server action via
redirecionava à etapa de escolha. Agora  manda
para Início nesse caso; o client usa  no sucesso.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 10:54:51 +00:00
Cursor Agent
afa05fd5fc
B99 — strip de ações edge-to-edge no mobile + gap menor
- Bleed do slot home-actions com -mx-4 (compensa p-4 do scroll) só no mobile
- Gap horizontal da strip: gap-3 → gap-2
- Unit pin das classes de bleed em campaignHomeLayout

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 10:54:39 +00:00
Cursor Agent
7b3d3096cd
B95 — unit test for mobile wizard shell padding
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 10:53:33 +00:00
Cursor Agent
2fc73aa7a4
style: prettier B94 files 2026-08-01 07:44:00 +00:00
Cursor Agent
20c69bd0de
B94 — wizard empty state: município mais próximo (geo)
No idle do passo de busca de município, resolve client-side o município
mais próximo no escopo do ator quando a permissão de geo já está granted
(reuso B14: campaignGeolocation + municipalityProximity + malha lazy).

- Prop RSC accessibleMunicipalities na rota acoes/[slug]
- Hook useWizardNearestMunicipality (fail-soft, só inScope)
- Merge geo no topo da lista B92 via wizardMunicipalitySuggestMerge
- Unit tests do merge/format

Closes #94

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 07:43:42 +00:00
Cursor Agent
8a720b0285
style: prettier 2026-08-01 07:24:10 +00:00
Cursor Agent
0114c348d5
test: fix wizard votes step props and continuity scope filter spec
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 07:23:30 +00:00
Cursor Agent
dbf6a7fdce
B93 — wizard empty state continuity (visitados + última ação)
Merge client-side recent visits and last-acted municipality storage into
wizard municipality suggest idle list ahead of B92 server rankings.

- Add campaignLastActedMunicipality + wizardMunicipalitySuggestMerge libs
- Extend wizard-municipality-suggest with scopeMunicipalities for hit lookup
- Record last-acted on wizard write success (votes, signal, trend, leadership)
- Clear last-acted on logout alongside recent visits

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 07:21:56 +00:00
Francisco Solla
7f7c707d4d
Merge pull request #98 from fsolla/cursor/pool-i92-174dd43e-5e6f
B92 — Empty state do wizard: chassis + ranking de esquecidos
2026-08-01 04:17:53 -03:00
Francisco Solla
269fb79f37
Merge pull request #97 from fsolla/cursor/pool-i42-980ec2b6-2133
C11 — Escala planos (picker pós-C12 + paginação das demandas vinculadas)
2026-08-01 04:16:27 -03:00
Cursor Agent
2dcd3ab3f8
test(e2e): adapt demand form activity picker assertion (C11) 2026-08-01 07:02:06 +00:00
Cursor Agent
1ed65d0d81
B92 — wizard idle suggest: rank + loader + UI
Add wizard-municipality-suggest mode to home-search with cold-first
ranking (signal → E14 → trend → E10 class → 2022 votes). Wizard
municipality step now fetches suggestions when query is idle.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 07:01:19 +00:00
Cursor Agent
196e15b827
fix(agent-pool): pin fast=false on Cloud Agent spawns
Cursor's Create-Agent API defaults omitted `fast` to true, so pool
workers requesting composer-2.5 were billed as composer-2.5-fast.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:56:57 +00:00
Francisco Solla
bcfd3fe598
Merge pull request #90 from fsolla/cursor/pool-i12-d50dd384-d1b2
B57 — Delta 7 dias do total estimado no Início
2026-08-01 03:49:37 -03:00
Francisco Solla
d9a2414656
Merge pull request #84 from fsolla/cursor/pool-i26-856c30e7-8e05
B90 — Ações rápidas — Conceitos + Perfil (só busca staff)
2026-08-01 03:43:41 -03:00
Cursor Agent
2ac6f4c8fa
style: prettier (C11 touched files) 2026-08-01 06:39:34 +00:00
Cursor Agent
f394c76432
test(C11): fix unit spec imports and payload stub typing
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:38:55 +00:00
Cursor Agent
8372cec91d
C11: picker remoto de atividades na demanda + paginação de vínculos
- Substitui loadActivityOptions (pagination: false) por AsyncSearchCombobox
  com busca server-backed filtrada por município (searchActivityRelationOptions).
- Pagina card Demandas vinculadas no detalhe da atividade (10/página) com
  soma de custo em query separada e link Ver todas na lista filtrada.
- Adiciona filtro ?activity= na lista /campanha/demandas.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:37:53 +00:00
Cursor Agent
af6c180211
B57 — delta 7 dias do total estimado no Início
- Collection campaignVoteSummarySnapshot (snapshot diário statewide)
- Gravação idempotente on-read no loadCampaignHomeSummary + retenção 30d
- UI: seta + magnitude ao lado do total B56 (aria-label completo)
- Reusa rollup statewide para CG/candidato; assessor vê Δ global (opção A)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:33:39 +00:00
Cursor Agent
a0a693fb27
style: Prettier tests after rebase 2026-08-01 06:29:16 +00:00
Cursor Agent
63a4b43951
style: Prettier (B90) 2026-08-01 06:28:15 +00:00
Cursor Agent
7844afcc6b
B90 — Ações rápidas Conceitos + Perfil (só busca staff)
Registry B79 para /campanha/conceitos e /campanha/perfil: catálogo vazio
para staff, busca global no drawer expandido via CampaignStaffGlobalSearch.
Leader em /perfil continua sem drawer (lockdown B89).

Closes #26

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:28:15 +00:00
Cursor Agent
ca80da178c
style: Prettier on B82 files 2026-08-01 06:24:54 +00:00
Cursor Agent
d5da7b0adf
B82 — Ações rápidas em lideranças (lista + detalhe)
Registra catálogo contextual no drawer B79 para /campanha/liderancas:
lista com update-leadership; detalhe com cinco wizards e prefill de
município quando N=1. Adiciona query leadershipId no wizard A4 e
CampaignQuickActionContextSync na ficha.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:24:54 +00:00
Francisco Solla
7261f3f588
Merge pull request #87 from fsolla/cursor/pool-i35-456a9692-831a
RS+ — Auth read leve + shells de senha
2026-08-01 03:23:51 -03:00
Francisco Solla
14c8f79ff9
Merge pull request #77 from fsolla/cursor/pool-i19-c4629a49-6cdb
B83 — Ações rápidas — Dobradinhas
2026-08-01 03:22:15 -03:00
Cursor Agent
21e5027b8b
B83 — catálogo de ações rápidas para /dobradinhas
Registra no drawer B79: lista (Nova dobradinha + 6 ações Início sem
prefill), detalhe e /nova (só Início). Integra com catálogos B80–B89.

Closes #19
2026-08-01 06:10:07 +00:00
Cursor Agent
104d6d4589
fix(RS+): depth:0 sem select parcial; form schema de convite separado
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:08:53 +00:00
Cursor Agent
7484aab28a
RS+ Fase 1+3: auth read leve, password fields e login state
- authenticateCampaignToken usa depth:0 + select mínimo (sem join media)
- getCampaignUserWithAvatar para layout/perfil; hasCampaignSession em rotas públicas
- CampaignPasswordFields reutilizado em reset, perfil e convite
- LoginForm migra para CampaignFormActionState + mapCampaignFormActionError
- invite login schema unifica refineMatchingPasswords

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:07:28 +00:00
Cursor Agent
6f61296064
fix: close first it block in onda0Provision int spec
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:04:05 +00:00
Cursor Agent
cbe171c597
O0+ (#34): consent key DRY, SQL int tests, Footer condicional, B68 suggest RSC
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 06:04:05 +00:00
Francisco Solla
5c6ed8cf31
Merge pull request #82 from fsolla/cursor/pool-i23-0fce1b59-4cb2
B87 — Ações rápidas — Assessores (unrestricted)
2026-08-01 03:03:49 -03:00
Cursor Agent
86f948540b
fix: rebase on main (B85 demands + B87 assessores) 2026-08-01 05:49:25 +00:00
Cursor Agent
be317191b6
B87 — Ações rápidas assessores (unrestricted)
Registry B79 para /campanha/assessores (lista + detalhe): catálogo
new-advisor com deep link ?criar=1; mount gate só unrestricted;
/novo redireciona para o deep link; busca global permanece na lista
(B91 no drawer).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:48:08 +00:00
Cursor Agent
c73034aa77
B91 — Mount drawer search only when expanded; unify body export
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:46:42 +00:00
Cursor Agent
5ec167f8dc
B91 — Fix drawer search unit test assertions
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:44:39 +00:00
Cursor Agent
d639988b83
B91 — Global search in mobile quick-actions drawer
Extract CampaignGlobalSearchMount (provider + body) from home chrome
so the same home-search contract mounts in CampaignQuickActionsDrawer
when expanded. Refactor CampaignHomeStaffChrome to use the provider.

Closes #55

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:42:59 +00:00
Francisco Solla
90d130af33
Merge pull request #80 from fsolla/cursor/pool-i21-97442a76-e99b
B85 — Ações rápidas — Demandas
2026-08-01 02:41:05 -03:00
Cursor Agent
dfa080f74d
B89 — Ações rápidas em Contatos (liderança)
Wire the B79 drawer on /campanha/contatos for leader role with the B45
catalog (register-supporter, my-contacts). Actions use in-page hash anchors
instead of staff home-search; no drawer on Início (existing B79 gate).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:27:45 +00:00
Cursor Agent
aa0cc4e15a
test: resolve searchStaffMunicipalityHits conflict after rebase 2026-08-01 05:27:41 +00:00
Cursor Agent
2baad93f71
test: retry municipality pair when word-start queries collide
Allocator stays slug-free (miss #73); skips Conde/Condeúba-style prefixes.
2026-08-01 05:26:12 +00:00
Cursor Agent
7812dee1f3
style: Prettier (B85) 2026-08-01 05:26:12 +00:00
Cursor Agent
c05b04236e
B85 — Ações rápidas no drawer de Demandas (lista + detalhe)
Registra catálogo contextual no chassis B79:
- Lista: um launcher register-demand → /demandas/nova (wizard A5 ainda stub)
- Detalhe: A1–A5 com prefill de município via context sync
- Expõe municipalityId no view model para prefill do form de criação

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:26:12 +00:00
Cursor Agent
a7ecdbc844
chore: format B88 quick-action files 2026-08-01 05:12:11 +00:00
Cursor Agent
9b76f19b85
B88 — Ações rápidas em Organizações (lista + detalhe)
Registra /campanha/organizacoes no registry B79 com launcher
new-organization na lista; detalhe vazio até existir filtro de
lideranças por org ou município óbvio único.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:11:31 +00:00
Cursor Agent
bdbab71112
fix(test): retry municipality pair when word-start queries collide 2026-08-01 04:52:17 +00:00
Cursor Agent
2cd1e7cfd7
fix(test): retry municipality allocation until word-start queries are distinct 2026-08-01 04:49:36 +00:00
Cursor Agent
b7ad9ad326
merge: sync with main (B80 municipalities quick actions) 2026-08-01 04:44:03 +00:00
Cursor Agent
39b9065ad2
style: Prettier after rebase merge 2026-08-01 04:35:38 +00:00
Cursor Agent
66e84ab85c
fix: resolve rebase conflict markers in quick-action unit spec 2026-08-01 04:35:38 +00:00
Cursor Agent
eea2d9afa1
B84 — Ações rápidas na vertical Atividades (lista + detalhe)
Registra catálogo B84 no registry B79: Nova atividade e Planejar giro na
lista; wizards A1–A5 com ?municipio= no detalhe quando há município, mais
atalhos Editar/Tarefas/Atualizações. Context bridge no detalhe; drawer
omitido em /atividades/giros (compositor E13 já tem CTAs).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:35:38 +00:00
Cursor Agent
3463389942
style: prettier registry 2026-08-01 04:22:25 +00:00
Cursor Agent
6f4402dab6
style: prettier 2026-08-01 04:21:25 +00:00
Cursor Agent
6030d42e2f
B80 — Ações rápidas em Municípios (lista + detalhe A1–A5)
Registra catálogo staff no drawer B79 para /campanha/municipios e
/campanha/municipios/[slug]: lista espelha Início (sem prefill); detalhe
prefill ?municipio= nos wizards A1–A5 e omite Ver esquecidos.

Closes #16

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:21:15 +00:00
Cursor Agent
09e76ec8cb
B81 — format 2026-08-01 04:10:16 +00:00
Cursor Agent
7f2ac29368
B81 — simplify: path constant, role cache, href parity test
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:09:35 +00:00
Cursor Agent
8c9eeaf273
B81 — register staff Início quick actions on /campanha/territorios
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:06:22 +00:00
Cursor Agent
18fe4f8e9b
fix(agent-pool): ban -fast model slugs
Never map or request fast=true. Slugs ending in -fast are stripped to
the base (with a warn); model-selection and the pool plan document the ban.
2026-08-01 03:34:35 +00:00
Cursor Agent
629364f3e4
fix(agent-pool): map Grok effort + Kimi reasoning slugs to Cloud API
cursor-grok-4.5-{low,medium,high}[-fast] and kimi-k3-low were falling
back to composer-2.5 against live /v1/models (grok-4.5 + effort,
kimi-k3 + reasoning). Teach resolvePoolModel the mapping, document
when to pick each Grok effort in model-selection, and align plan/work
skills.
2026-08-01 03:32:41 +00:00
Francisco Ignácio Fontoura Solla
6c90d76f10 test: pin exported skip predicates so knip stays clean 2026-07-31 21:09:20 -03:00
Francisco Ignácio Fontoura Solla
f021992de0 ci: main-only cutover with gated Vercel deploy
Remove stage/promote/Neon smoke. PR CI targets main; ci.yml full-suite
verifier deploys via vercel CLI and requeues when HEAD moves. Pool uses
POOL_GITHUB_TOKEN and POOL_BASE_REF=main. Docs/skills/rules retargeted.
2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
42d1a7be93 ci(pr): prettier + ignore local tool artifacts in format check 2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
9d810d1564 ci(pr): cascade phase1→phase2 with blast-radius skips
Add scope classifiers for code/build jobs, always-on test-location
guardrails, and gate:ci parity. Default affected base is origin/main.
2026-07-31 20:57:18 -03:00
Cursor Agent
7a876ec334 refactor(notifications): bell loaders read through owner access, no bypass
countUnreadNotifications/loadNotificationList took a caller-supplied
recipientID with module-level admin bypass — one misuse away from reading
someone else's inbox. They now take the actor (CampaignUser) and read
with overrideAccess: false under canReadOwnNotifications, keeping the
explicit recipient filter as a second fence. Pin: owner isolation at the
loader level in campaignNotifications.int.spec.ts.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 20:57:18 -03:00
Cursor Agent
6705db80eb security: lock public globals update to payloadAdminOnly (Pass 4 P0)
The four public globals (site-settings, home, metadata, privacy-policy)
spelled update access as "any authenticated user" — and campaign JWTs
authenticate against /api/*, so any campaign session (leader included)
could PATCH the public site. payloadAdminOnly matches the collection
lockdown precedent; editors lose config-global writes deliberately
(content editing stays on canManagePublishedContent collections).

Pins: identity + deny (coordinator/editor) + allow (admin fn) + anonymous
read in collectionAccessLockdown.int.spec.ts. Recurrence guard:
globalAccessConventions.unit.spec.ts bans the Boolean(user) shape in
src/globals.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 20:57:18 -03:00
Cursor Agent
84fed02ee5 test(miss-54): deterministic guard for unsettled goto→goto + nav convention
The SavedFilters incident shape — a page.goto fired while the previous
heavy RSC navigation was still in flight (net::ERR_ABORTED) — gets a
build-level guard: two gotos with no settle between them (await expect /
waitForURL / click / reload / login / catch) fail the suite. Measured
baseline on the current tree: 58 gotos across 17 specs, 0 unsettled
pairs — the six SOFT pairs from the miss audit all settle on awaited
assertions, so the guard starts clean without churning them.

TESTING.md records the convention the miss asked for: goto for cold
loads and URL contracts; in-shell navigation for continuity scenarios;
interactive-state waits before dynamic clicks; a dev-mode caveat for the
campaignLeaderships chip spec (deterministic dev failure at the
suggestion/commit step, prod green — ledgered).
2026-07-31 20:57:18 -03:00
Cursor Agent
5c59793e4c test(miss-53): gate WebAuthn ceremonies on authenticator readiness
The app's biometrics probe is one-shot per island mount: when
isUserVerifyingPlatformAuthenticatorAvailable() resolves before the CDP
virtual authenticator answers, the enrollment form / biometric button
never mount and no wait flips them (prod-build CI flake). New
expectCampaignBiometricsReady(page) polls the platform answer BEFORE the
surface whose island probes it; adopted at both ceremony entries in
campaignWebAuthn (verified green locally). Conventions pin:
e2eNavigationConventions fails the build when a virtual-authenticator
spec skips the readiness gate.
2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
b1a34a8c3a fix(tests): allocate homeSearch municipalities; scope B42 trend drawer 2026-07-31 20:57:17 -03:00
Cursor Agent
e51a1dc9b2 style: prettier campaignHomeActions after URL anchor edits 2026-07-31 20:57:17 -03:00
Cursor Agent
03314e620a test(miss-73): deterministic allocator guardrails in conventions spec
Recurrence prevention for the deadlock class, three rules:
1. a spec file calling getMunicipality(<arg>) while mutating
   municipalities (assign/touch/create*/payload.update municipality)
   fails the build — the class miss #73 named;
2. hardcoded municipio=<slug> deep links banned in tests/e2e —
   claimMunicipality().slug is the only source;
3. e2e name: /.../ regex literals containing a catalog municipality name
   must be ^-anchored — the literal-regex dodge of the P3-C template
   guard, keyed on the catalog so copy regexes stay untouched.
2026-07-31 20:57:17 -03:00
Cursor Agent
6b894a04c5 test(miss-73): allocate municipalities, never pin seeded slugs (deadlock class)
Int specs pinning seeded rows (cairu/feira-de-santana/catalog[0..2] or
class-derived fixed slugs) and mutating them deadlocked municipality_rels
under the parallel suite (40P01); e2e specs deep-linking ?municipio=cairu
raced the same row from two workers.

- homeSearchStateDeputies: 2 fixed slugs -> 2 allocations.
- municipalityPageData classe test: rewritten class-AGNOSTIC — allocates
  rows until two distinct territorial classes (same pinned behavior:
  filter narrows, totals honest, classe sort = descending weight), no
  fixed reduto/marginal slugs. nivel test + facet test: plain allocations.
- campaignHomeActions/campaignWizardChrome: 11 hardcoded cairu deep links
  -> claimMunicipality(); hit clicks disambiguate by href (catalog names
  are not prefix-unique); caption/URL assertions use ^-anchored full-URL
  regexes (P3-C template guard). /Cairu/i literals gone.

Verified: int 17/17, e2e 18/18 (dev mode), unit 1106/1106.
2026-07-31 20:57:17 -03:00
Francisco Solla
7a66fe4f25
Merge pull request #5 from fsolla/cursor/p4-shared-sheet-host-78a7
Miss #52 guardrail: shared sheet host on the chip-relation lists + fail-loud guard
2026-07-31 13:21:48 -03:00
Cursor Agent
c83cf28497 test(miss-52): sheet cells without the shared host fail loud
- CampaignCellEditOverlay: dev-mode console.error when a variant=sheet
  cell mounts without CampaignListSheetProvider (prod keeps the
  lazy-mount fallback for resilience; unit tests exercise it, so the
  invariant is development-only).
- sharedSheetHostConventions.unit.spec.ts: any tsx rendering
  <MunicipalityPortfolioCell / <LeadershipStateDeputyRelationCell must
  reference CampaignListSheetProvider — the next chip table cannot
  reintroduce per-cell Drawers by omission.
2026-07-31 13:17:19 -03:00
Cursor Agent
9ae062c77c refactor(lib): close the lib→utilities type-import hole + Local API omission guard
- suggestionCatalog type-imports MunicipalityTerritorialClass from
  lib/territorialClassAnchors (the single source) instead of the
  utilities re-export — direction inversion introduced in Pass 3.
- ESLint src/lib zone: @/utilities|components|app restriction now rejects
  type imports too (allowTypeImports: false); shared types live in lib
  contract modules by policy.
- engagementLevel imports DAY_MS from lib/text (fold missed in P3-K).
- searchHomeAdvisors: delete stale 'Intentional admin bypass' comment —
  the call is overrideAccess: false under the staff gate.
- New guard localApiOverrideAccessConventions.unit.spec.ts: payload calls
  with user must declare overrideAccess (baseline measured: 59 calls, 0
  missing), and runtime dynamic import/require of payload/next server
  modules is banned in utilities (dodges the server-only sweep).
2026-07-31 13:17:10 -03:00
Cursor Agent
0991c04cd7 fix(seed-posts): import dieWithLabel before calling it (Pass 4 P1)
The P3-H CLI consolidation left seed-posts calling dieWithLabel with no
import — a ReferenceError on the empty-fetch abort path, invisible to
lint/typecheck/knip. Recurrence guard in the same delivery:
scriptCliConventions.unit.spec.ts fails the build when a script calls
dieWithLabel/dieAgent without importing (or defining) it, and bans
re-spelled 'const DAY_MS =' outside src/lib/text.ts (P3-K's fold leaked
one private copy in engagementLevel.ts).
2026-07-31 13:17:10 -03:00
Francisco Solla
8dd60cf962 A6 — Dobradinha 2026 automática quando o TSE publicar candidaturas (pós-15/08) (#82)
* docs(A6): freshness audit da #27 no plano de dobradinhas 2026

electionInsights.ts foi deletado no Pass 2 W4a (modulo puro novo),
electionAlliances.ts nao nasce (campoParties + electionPartySpectrum ja
carregam a taxonomia), questoes em aberto resolvidas (tiers, score,
proporcionais, lockdown de lideranca), card na aba Eleicoes, overview da
lista fora de escopo, cache election-tse com bust na conciliacao Fase 5.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(A6): insight de dobradinhas 2026 na aba Eleicoes do municipio

Lista ranqueada de candidatos proporcionais que concorrem de novo em 2026
(runningAgain2026=sim) por geografia do municipio: score 0,6 alinhamento
(campo 2026 + espectro Bolognesi) + 0,4 forca local normalizada pelo maximo
do conjunto. Estado pendente ate a conciliacao pos-15/08; Solla excluido
(referencia, nao parceiro); leitura derivada sem escrita; loader cacheado
sob a tag election-tse como os irmaos; link para /campanha/dobradinhas
como caminho de registro operacional.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* test(A6): cobertura unit/int do insight + lease das colecoes eleitorais

Unit: tiers (campo/espectro/falha-para-o-meio), score, ordenacao, empates,
limite, zero votos. Int: pending2026 sem conciliacao (mesmo com votos
locais), so adversarios, misto com exclusao de Solla/nao e geografia
alheia. Lease advisory election-collections serializa os dois specs que
tratam as colecoes eleitorais como test-owned (o import wipe-all apagaria
as fixtures do novo spec no meio do assert, 48-way paralelo).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* refactor(A6): fixes do /simplify (3 reviewers paralelos)

P1: campo coalition morto removido do input/select/mapping (sem consumidor).
P1: hooks do lease com timeout explicito de 120s nos dois specs eleitorais
(o wait advisory passa dos 10s default do Vitest sob contencao — flake em
ambas as direcoes). P2: sonda de conciliacao via payload.count (idioma da
casa), desempate final por candidateNumber (ordem deterministica), geography
como prop na secao (sem lookup duplicado), casing Dep. Federal/Estadual
alinhado, marcador 1313 via BASELINE_TICKET_2022, entrada no CHANGELOG.

Declinados com justificativa: zones como tupla nao-vazia (churn em tipo
compartilhado por assert provada no catalogo) e early-start da promise
antes do Promise.all (loader-dentro-do-Suspense e o idioma da casa; ganho
so em cache frio).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 00:14:27 -03:00
Cursor Agent
83f0b1cf48 fix: remove leftover merge conflict markers in int test 2026-07-31 02:34:15 +00:00
Cursor Agent
c7aac4da1b test: pin home search municipality scope fixtures to distinct slugs 2026-07-31 02:34:15 +00:00
Cursor Agent
5d45909780 fix: drop unused quick-action setter hook until B80 2026-07-31 02:34:14 +00:00
Cursor Agent
224cbf84f5 test: cover quick action context setter hook for knip 2026-07-31 02:34:14 +00:00
Cursor Agent
a2eabaccba B79 — chassis do bottom drawer de ações rápidas + busca global (mobile)
Monta CampaignQuickActionsDrawer não-modal no layout (app) para staff fora
do Início e de /acoes, com snap collapsed/expanded, padding de peek no scroll,
registry vazio para catálogos B80+, e busca global reutilizada via
CampaignStaffGlobalSearch.

Closes #15

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 02:34:08 +00:00
Francisco Solla
b2b11c3dfe Merge pull request #81 from fsolla/cursor/d2-push-sino-in-app-af30
D2 — Push + sino in-app
2026-07-30 23:25:11 -03:00
Cursor Agent
b9a81f7ad0 D2 — skip notification hooks in Vitest; fixture cleanup for push rows 2026-07-31 02:09:52 +00:00
Cursor Agent
de67acf41b D2 — Push + sino in-app para /campanha
- Collections Notification e PushSubscription com migration
- Sino no header (mobile + desktop) com drawer de avisos lidos/não lidos
- Hooks em municipalityUpdate, supporter, activity e convite aceito
- Push Web via web-push + handlers no service worker /campanha/sw.js
- Opt-in LGPD em /campanha/perfil (chave campanha-notificacoes-push)
- Testes unit/int de contrato e access

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 01:51:41 +00:00
Francisco Ignácio Fontoura Solla
773e4426eb fix(test): queries distintas em specs de portfolio de município
Nova Soure vs Nova Canaã compartilham o primeiro token; distinctWordStartQuery
escolhe palavra que casa só com o alvo e evita falso positivo no assessor.
2026-07-30 22:29:20 -03:00
Francisco Ignácio Fontoura Solla
0fece8cef5 fix(test): isola municípios nos homeSearch specs para evitar deadlock
Specs de portfolio deixam de mutar cairu/feira compartilhados e passam a
usar getMunicipality() alocado; testDatabaseLease ignora updatedAt volátil.
2026-07-30 22:07:40 -03:00
Francisco Ignácio Fontoura Solla
b533139eb2 feat(ops): promote último commit ci-stage green sem exigir head verde
agent:promote passa a escolher o SHA mais novo à frente de main com
ci-stage success e usa promote/last-green quando o head de stage está
vermelho, destravando o bootstrap manual para main.
2026-07-30 21:56:50 -03:00
Cursor Agent
90a97d3c76 style: prettier 2026-07-31 00:27:50 +00:00
Cursor Agent
6c8d7d418c B78 — Indicar carregamento na navegação do wizard
Reusa CampaignListPendingBoundary no layout autenticado para compartilhar
useTransition entre o header mobile e o conteúdo do passo. Voltar, X, Pular,
seleção de município e tiles de passo passam por CampaignWizardNavLink;
o corpo dima com aria-busy e o header mostra skeleton nos slots de título.

CampaignTransitionAnchor agora retorna a promise do router para manter o
pending honesto até a navegação terminar.

Closes #14
2026-07-31 00:24:39 +00:00
Francisco Solla
e04db48742 feat(ops): auto-promote stage→main e merge em stage sem exigir head (#68)
* feat(ops): auto-promote stage→main e merge em stage sem exigir head

Substitui o promote manual por workflow disparado no ci-stage green,
relaxa strict na proteção de stage (script configure:branch-protection)
e documenta o novo paradigma em AGENT-OPS e work-issue.

* style: format AGENT-OPS and configure-branch-protection for ci-pr

* feat(ops): pnpm push com ensure-repo-deps determinístico

Bootstrap install + Husky antes de todo push (pnpm push e pre-push).
Documenta fluxo canônico em work-issue, agent-pr-workflow e AGENT-OPS.

* fix(ops): pnpm push roda gate no script, sem depender do Husky

Cloud environment.json chama prepare explicitamente após install.

* test(int): isolate advisor portfolio search from shared cairu slug

Parallel int specs were stomping municipality.advisors on the fixed
cairu row; allocate per-test municipalities instead.
2026-07-30 21:12:52 -03:00
Francisco Solla
5ee9e840a2 Merge pull request #67 from fsolla/cursor/wizard-mudar-tendencia-a971
B64 — Wizard mudar tendência (grid colorido + justificativa + Pular)
2026-07-30 20:10:37 -03:00
Francisco Solla
680ff56754 feat(agent-pool): orquestrador de pool de workers Cursor Cloud (fases A–D) (#65)
* feat(agent-pool): fundacao read-only do orquestrador (fase A)

- docs/plans/agent-pool-orchestrator.md: plano completo (14 secoes)
- buildClaimQueue extraido de agent-claim para agent-github (comportamento identico, pinado)
- agent-pool-eligibility: isAutonomousClaimable puro e fail-closed + buildPoolQueue
- agent-pool-state: parsePoolConfig, marcadores pool-worker, classifyPoolClaim, spawn plan, auto-stop
- agent-pool-github: leitura de repo variables (degrada nao-estrito), claims/PRs/migration-lock
- pnpm agent:pool -- status|tick --dry-run (inspecao local; live chega na fase B)
- 34 specs unit novos (992/992 verde); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(agent-pool): spawn remoto de workers via Cursor API (fase B)

- agent-pool-cursor: client fino da Cloud Agents API v1 (fetch injetavel; CursorApiError com status/body)
- agent-pool-models: resolvePoolModel contra /v1/models (id/alias, sufixo -fast -> param, fallback composer-2.5 com warn)
- agent-pool-prompt: contrato do worker (claim pre-feito, pula passo 1; PR base stage + Closes #N; promote proibido)
- claim coordenado no tick: lock otimista + flip + marcador; agentId idempotente bc-<uuid>; 409 recupera; rollback em falha de spawn
- agent:pool workflow --action start|stop|pause|resume|tick|status (variables POOL_*); doctor; wrappers gh workflow run
- .github/workflows/agent-pool.yml (dispatch-only; schedule/triggers na fase C)
- 19 specs novos (1011/1011); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(agent-pool): ciclo de vida do supervisor (fase C)

- derivePoolClaims com status real dos runs (getCursorRun), heal de spawn via agentId deterministico, cancel de duplicatas
- reconcilePoolClaims: falha terminal -> comentario + blocked + archive; merge concluido -> slot livre + archive
- auto-stop real: fila drenada + zero ativos -> POOL_ENABLED=false (exclusoes migration-busy nao desligam)
- job summary markdown (GITHUB_STEP_SUMMARY) — superficie remota de status
- workflow: schedule */10min + pull_request closed em stage (ativam na default branch apos promote)
- 2 specs novos (1013/1013); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* docs(agent-pool): runbook operacional e adendo do spike (fase D)

- skill agent-pool: runbook humano (pre-requisitos, start/stop/pause remoto, ciclo de vida, triage, smoke de aceite)
- AGENT-OPS: linha na tabela de comandos + secao Agent pool (canal, secrets, ativacao pos-promote)
- CHANGELOG-AGENTS: entrada curta da entrega
- engineering-audit: pause/resume do pool no protocolo do modo solitario
- plano secao 14.1: evidencias do spike 2026-07-30 (environment repo-file confirmado, id bc-<uuid>, degradacao 403, pendencias do primeiro run com CURSOR_API_KEY)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* fix(test): teardown flake no homeSearchUi.unit.spec (dono do CI)

O scheduler do react (prefetch do next/link) podia disparar apos o teardown
do jsdom e derrubar o job unit com 'window is not defined' (run 30587862940).
afterEach(cleanup) — padrao dos 14 specs irmaos.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-30 20:08:34 -03:00
Cursor Agent
fcbc463990 fix: format + wire WIZARD_TREND_SAVED_MESSAGE for knip 2026-07-30 23:03:25 +00:00
Cursor Agent
f0f3d4534d B64 — Wizard mudar tendência (grid + justificativa + skip) 2026-07-30 23:03:25 +00:00
Cursor Agent
8ff9e20a88 fix(B55): defer share strategy to click + stabilize unit tests 2026-07-30 22:47:03 +00:00
Cursor Agent
b96bf190e8 B55 — Busca global: ações de linha (WA e compartilhar)
Adiciona trailing actions nas linhas de resultado da busca global:
- WhatsApp em lideranças e assessores (reusa whatsAppHrefForPhone)
- Compartilhar em demandas e atividades (Web Share nativo com fallback wa.me)
- trailingAction fora do Link da linha para não roubar navegação

Closes #9
2026-07-30 22:47:03 +00:00
Cursor Agent
10377d90fb feat(campanha): remove mobile bottom nav (B73)
Remove CampaignBottomNav and getCampaignBottomNav — mobile navigation
is now sidebar-only (Sheet via SidebarTrigger). Drop the pb-24 padding
that reserved space for the fixed bar; scroll uses symmetric p-4 on mobile.

Impeccable: N/A (shell chrome removal, class B per plan).

Plan: docs/plans/remover-bottom-nav-mobile.md
2026-07-30 22:13:07 +00:00
Francisco Solla
142b0963e0 B54 — Busca global: layout responsivo dos resultados (#60)
* feat(campanha): responsive grid and viewport cap for home search results (B54)

Adds HomeSearchResultsLayout with 1/2/3-column breakpoints and allocateHitBudget so Municípios keep priority while total hits stay within per-viewport limits.

Closes #8

* fix(campanha): un-export knip-unused home search budget helpers
2026-07-30 18:45:44 -03:00
Francisco Ignácio Fontoura Solla
30170509f7 fix(e2e): WebAuthn on prod build, saved-filters navigation, Vercel scripts
CI e2e serves a production build on localhost; Next inlines NODE_ENV so
WebAuthn RP resolution now treats localhost requests as test mode. Saved
filters avoids a racing bare goto. Analytics/SpeedInsights render only on Vercel.
2026-07-30 17:37:20 -03:00
Francisco Ignácio Fontoura Solla
6a20e98047 fix(campanha): share one Drawer across municipalities mobile cards
Root cause of the prod-build React #130 on /campanha/municipios: the
md:hidden mobile-cards tree still hydrated ~25 rows x ~5 sheet controls
(~125 eager Drawer/DrawerCloseButton instances) even on wide viewports.
Filtered ?q=... routes failed more often only because they exercise the
list with filters active — the search param itself was not the bug.

CampaignListSheetProvider hosts a single bottom Drawer for the whole
mobile section; CampaignCellEditOverlay sheet cells portal their
body/footer into it while keeping per-cell open state and autosave
scaffolds. Footers with DrawerCloseButton are only created once a sheet
actually opens. Desktop popover path is unchanged; per-cell Drawer
remains the fallback for surfaces without the provider.

Also: DrawerCloseButton (function render prop, avoids base-ui element
render in prod bundles) replaces the DrawerClose render={<Button/>}
pattern across call sites; stale e2e selectors refreshed; e2e affected
script + playwright github/list reporters for CI; use-mobile reverted
(no consumers of the gate remain).

Guardrail filed as issue #52.
2026-07-30 17:37:20 -03:00
Francisco Ignácio Fontoura Solla
2e0cc2c03e chore(ci): parallel PR jobs, affected tests, push gate, owner-of-red-CI policy
- ci-pr.yml: split the serial checks job into parallel lint/format/
  typecheck/knip/cycles + unit/int (vitest --changed vs PR base via
  scripts/test-affected.mjs, full fallback on high-risk paths) + e2e
  (manifest path->spec via scripts/e2e-affected.mjs, blocking) + build;
  migration-lock gains checkout + pull-requests:read (fixes the failures
  that blocked PR #50)
- ci.yml (main): full safety net — seed:minimal + int full + build + e2e
- package.json: gate:fast / gate:push; .husky/pre-push runs gate:push
- docs: "Dono do PR, dono do CI" policy in AGENT-OPS + work-issue +
  engineering-standards; changelog entry; plans for OPS3-OPS7
2026-07-30 17:37:20 -03:00
Francisco Ignácio Fontoura Solla
5b4ab9c020 fix(ci): unblock PR checks — checkout in migration-lock, seed-proof leadership spec
migration-lock ran `gh pr list --json files` without actions/checkout, so
every PR failed the job in 3s with "not a git repository". The leadership
portfolio spec asserted the exact global hit set with fixed names, which the
minimal seed (Seed Liderança Um/Dois) collides with via word-start 'Lider';
switch to the unique-token pattern already used by the demands spec.
2026-07-30 13:25:49 -03:00
Francisco Ignácio Fontoura Solla
9eafe6081c feat(campanha): add Demandas group to home global search (B53)
Staff can find demands by title from the Início omnibox with municipality and status in the secondary line, scoped by campaignDemand access.
2026-07-30 12:39:35 -03:00
Francisco Ignácio Fontoura Solla
40e7ecea33 test: fix pre-existing unit drift — wrap Tooltip-needing renders in TooltipProvider (944/944 green) 2026-07-30 05:35:25 -03:00
Francisco Ignácio Fontoura Solla
d0249e5ee5 fix: drop unused ts-expect-error in manifest pin 2026-07-30 04:46:51 -03:00
Francisco Ignácio Fontoura Solla
777fde2eed ci: PR gate with minimal DB + build + migration-lock; stage gate without build 2026-07-30 04:44:37 -03:00
Francisco Ignácio Fontoura Solla
6ac26404d2 feat: db:seed:minimal — minimal synthetic campaign DB for agents/CI 2026-07-30 04:43:01 -03:00
Francisco Ignácio Fontoura Solla
3f16998716 chore: format B63 wizard signal files. 2026-07-30 04:03:11 -03:00
Francisco Ignácio Fontoura Solla
b4ce8e45d0 docs: align B63 entry query param with B70 and record simplify deferrals. 2026-07-30 04:02:30 -03:00
Francisco Ignácio Fontoura Solla
8c7633da4a B63+: wire wizard signal steps to B75 chrome and DRY skip. 2026-07-30 04:01:49 -03:00
Francisco Ignácio Fontoura Solla
09fa21d4b3 B63: wizard registrar sinal (grid de tipos + passo de texto).
Two-step UX-1 flow on the B59 chassis with URL contract ?signalType= and optional ?entryAction= for embedded skip; reuses createMunicipalityListSignalFormAction.
2026-07-30 04:01:48 -03:00
Francisco Ignácio Fontoura Solla
dbd9d0fa3e feat(campanha): ship B70 leadership update wizard (grid + short form).
Replaces the post-municipality stub with a full wizard: tile grid, contact+leadership writes, entryAction skip, and Continuar after save.
2026-07-30 03:51:25 -03:00
Francisco Ignácio Fontoura Solla
625f25c984 B77+: simplify vote wizard helpers after single-screen delivery.
Unify violation highlights with getWizardVoteViolation, share the expected-votes save error constant, and drop dead parseWizardVoteDraft plus unused municipalitySlug prop.
2026-07-30 03:40:20 -03:00
Francisco Ignácio Fontoura Solla
f2bdb1ce3c B77: replace vote wizard ?cenario= ritual with single-screen editor.
Three scenarios on one step with confirm-only validation, legacy URL redirect, and updated tests/docs.
2026-07-30 03:31:57 -03:00
Francisco Ignácio Fontoura Solla
037785b280 B75: wizard mobile header chrome on Mandate Red bar.
Publish flow navigation from CampaignWizardShell via layout-level chrome so wizards no longer stack a second sticky header under the app bar.
2026-07-30 03:26:54 -03:00
Francisco Ignácio Fontoura Solla
37a7062428 B69: simplify the leadership model to exclusive + drop sector/consentNote.
Organization stays by product decision (political branching, e.g. SindMed);
the leadership gains `exclusive` (default true) with list badge and form
checkbox, and sector/sectorNotes/consentNote leave the collection, zod,
forms, list filters/sort, invite autofill and redemption.
2026-07-30 02:32:56 -03:00
Francisco Ignácio Fontoura Solla
af829e1099 fix(campanha): restore vote wizard violation banner on scenario jump (B76)
Keep violationEditedScenario when navigating to the inconsistent scenario
so média shows warning immediately after pessimista breaks order; clear
on draft/shortcut edits. Adds regression unit tests and roadmap B76.
2026-07-30 02:16:12 -03:00
Francisco Ignácio Fontoura Solla
f41b0fbada fix(campanha): restore wizard links on desktop home action strip
Defer pointer capture until drag exceeds the pan threshold so Link
clicks still receive pointerup and navigate to /campanha/acoes/*.
2026-07-30 01:41:21 -03:00
Francisco Ignácio Fontoura Solla
03bcf2e719 B71: polish home search results (bleed hover, list-none, sentence-case headings).
Centralize layout tokens in homeSearchUi.ts so hit rows fill the scroll padding on hover/focus and group lists drop public-site bullet leakage.
2026-07-30 01:27:05 -03:00
Francisco Ignácio Fontoura Solla
31bd488c1e B66: retract home chrome on search focus with animated transition.
Emends B47 so focusing the input clears strip/summary before typing; hardens hit rows against blur-before-click.
2026-07-30 01:21:37 -03:00
Francisco Ignácio Fontoura Solla
34e5c6ed42 B61: wizard vote step with scenario passes and batch save.
Staff can adjust municipality expected votes through média→pessimista→otimista steps with shortcuts, coherence warnings, and a single POST on completion.
2026-07-30 01:16:19 -03:00
Francisco Ignácio Fontoura Solla
a8cb771303 B62+: flatten adversary signal to boolean and harden leader redirect e2e.
The simplify pass dropped the vestigial object wrapper on E11 input and aligned the leader home test with the retrying toHaveURL idiom used elsewhere.
2026-07-29 23:33:46 -03:00
Francisco Ignácio Fontoura Solla
120ed8d9b5 B62: simplify the signal model to type + text (drop fonte/triangulado).
Drops signalSource/triangulated from municipalityUpdate (migration
simplify_municipality_signal_fields), rewrites signal-type labels for the
field team, and treats any adversary signal as confirmed in E11 (P1
triage 1). Also fixes the stale post-B43 leader-home e2e and lowers a
drifted bypass pin.
2026-07-29 23:24:00 -03:00
Francisco Ignácio Fontoura Solla
342e528c87 feat(E10+): Escala e DRY pós-E10
- F1: loadMunicipalityScopeFromDocs evita segunda query em municipalityPageData
- F2: computeAllMunicipalityPotentials memoizado por processo
- F3: TooltipProvider movido para o layout de (app)
- F4: DRY nos filtros multi do mobile em MunicipalityFilters
2026-07-29 23:18:25 -03:00