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.
Sucessor/refino do B158: onde a coluna Tendência colapsaria para ícone no
próprio lugar, esconder a coluna inteira e mostrar o editor como ícone em
Ações, ao lado do ícone de atualização. Uma instância por linha (invariante
B158). Related #446.
O ciclo medir→recolher de RelationChipCell rodava em useEffect (pós-pintura),
fazendo cada remount (navegação RSC, paginação, ordenação, filtro) pintar o
clamp max-h-18 e só então colapsar para o slice de 3 linhas — uma onda de
LayoutShift por célula (tabela 1122→682px). Mudar os 3 effects de
medição/invalidação para useLayoutEffect faz a primeira pintura já chegar na
altura final (medido: tabela estável, zero LayoutShift nas navegações).
Documenta o resíduo do load SSR (paint pré-hidratação) e registra follow-up
B170 (#441) para o spec do contrato de colapso + avaliação do shift único.
- 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
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.
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
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.
The AI FAB (Sollinha) sits at bottom-20 (80px) with z-40 in a different
stacking context. Moving the Quick Actions FAB from bottom-4 to
bottom-[calc(4rem)] for the bottom nav caused overlap at the shared
z-40, so the AI button intercepted clicks. Raised to 7rem (112px) to
clear both the bottom nav (4rem) and the AI FAB button (ending at 7rem).
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
A célula de nome lia 'nome + território' como bloco: a altura mínima da
linha passa a viver no wrapper da célula (preservando o alinhamento dos
controles), o link do nome perde o espaço vazio forçado abaixo do texto
e o TerritoryLink ganha a variante compact na tabela desktop (o card
mobile mantém os 44px de alvo de toque).
Verificado geometricamente no browser (1 e 2 linhas): vão texto→texto
de 2–4px, sem sobreposição, links intactos, linha com nome curto sem
desalinhar as demais colunas.
Closes#412
- 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
Plano curto do débito identificado durante os testes de envs de
worktree: purgeMunicipalityResidue não limpa municipality.advisors;
run interrompido deixa resíduo que flaka os specs de cap (10
assessores) na suite completa.
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.
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
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
- 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
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
Base responsive behavior on the content panel using container queries, preserve territory filtering in the omnibox, and resolve column visibility and single-mount action contracts before implementation.
The plan previously suggested the UI wouldn't change — only the code would be
refactored. Updated to make explicit that:
- Dobradinhas editor changes from RelationChipCell to the same
CampaignCellEditOverlay + Command + chips as Assessores/Liderancas.
- Liderancas display gains an avatar stack matching Assessores/Dobradinhas.
- Inline create is always name-only across all three columns (leadership
phone is added later on the detail page).
- Assessores is the reference — its UI doesn't change.
Related #374
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.
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.
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).
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.
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
- 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>
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.
Ledger, quality-plan section, entrega-engenharia-p5, GUARDRAILS harvest
(empty miss queue), TESTING e2e count 17/51, and CHANGELOG entry for the
2026-08-03 autonomous audit (delta since Pass 4).
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
- loadMunicipalityV2ContaData + MunicipalityV2LocalAccountSection
- Inline edit-in-place com cenário ativo local para cobertura
- Extract MunicipalityTerritorialClassRow do card E8
- Integra com B149 rede e B150 agora na mesma página v2
- Impl plan docs/plans/municipio-v2-conta-local-impl.md
- 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>
- 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>
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>
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>
- 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>
Move CampaignColumnPicker to omnibox trailing via CampaignColumnPickerTrailing
on all seven B17 list surfaces (+ organizações). Remove the picker row from
CampaignTable and drop visible sort-summary spans on territórios, lideranças
and dobradinhas (sr-only table captions keep sort context).
Closes#304
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
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>
- 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>
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>
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>
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>
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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
Registra a intenção de produto (#288): soltar Motivo no wizard de tendência e no nível de envolvimento, e apagar do modelo/DB o campo de sinais de reversão.
- 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>
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>
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>
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>
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>
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>