- 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
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)
- 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.
- 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
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).
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.
- 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
- 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)
- 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)
- 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
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)