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.
next/plan imprimem 'cd <dir>' na última linha por padrão; --stay suprime;
--go segue aceito como no-op. kill imprime 'cd <main>' como última linha
(depois do drop dos bancos) e recusa --stay. Superfícies: worktree.sh
aplica o cd por padrão; comando /worktree do opencode idem. Docs
(AGENTS.md, skills, CHANGELOG) atualizadas.
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.
O job roda script plain Node (gh CLI) sem pnpm/action-setup; setup-node@v5
com package-manager-cache: true (default) resolve o packageManager do
package.json e procura o executável pnpm -> 'Unable to locate executable
file: pnpm'. Mesmo precedente do cooldown do ci.yml, vercel-promote e
archive-cursor-agent. Varredura dos 21 usos de setup-node: único job com o
bug latente.
Closes#566
- 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
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.
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.
- D6 (Issue #521) voltou verbatim de a4d7b883 (merge do D6; idêntica a 8c79eddf): o rebase do C104 (ea700e31) tinha cortado o final 'Pendência humana: …' e o prettier (d52d0016) escapou '**Pe' -> '\*\*Pe' literal no markdown
- Impl plan: corrige atribuição da re-adição da B183 (7eaa65ce/PR #555, não fab834fc) e classificação do B189 (não mergeada; entrada no tip da branch)
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).
- C102 (Issue #498) restaurada verbatim de 8c79eddf na posição canônica (entre C104 e B179)
- C108 (Issue #522) restaurada verbatim de c3f10cce no topo da seção
- B183 (Issue #501) já havia sido re-adicionada pela branch da C106 (7eaa65ce, PR #555) — verificada byte-idêntica, sem ação
- Removidos: D6 duplicado (no lugar da C102), C106 v1 (rascunho pré-rebase) e 2 linhas de git log vazadas
- Impl: docs/plans/restaurar-entradas-changelog-b183-c102-impl.md
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
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.
- 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.
- 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
- 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)
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).