Commit graph

1380 commits

Author SHA1 Message Date
Francisco Solla
2d56f4f145 docs(OPS73-FOLLOWUP): plano de débitos pós-OPS73 — re-home S10 + origin homeserver + race seedUser 2026-08-20 02:12:08 -03:00
Francisco Solla
beb5ac4b9a docs(OPS73): changelog + evidência do deploy do fix do admin em prod (Closes #114) 2026-08-20 02:12:08 -03:00
Francisco Solla
3ce5a69555 feat(S11): feed do Instagram configurado não aparece na home — painel de status da sincronização no admin
O fail-closed silencioso da S3 deixava a assessoria cega: token errado não deixava
rastro além do picker vazio. Fim da caixa-preta:

- `instagramSyncStatus` (jsonb hidden) na global `social-feed-settings`, escrito pelo
  render, pelo afterChange (sync awaited quando a credencial IG muda) e pela rota
  `POST /api/social-feed/sync` (botão, auth admin + same-origin).
- `InstagramApiError` + `describeInstagramError`: o fetch parseia o body de erro da
  Graph API e o mapeia para copy pt-BR com a correção (OAuthException/expired →
  "token inválido/expirado ou emitido via Facebook Login — gere pelo Instagram Login";
  "Invalid user id" → confira o ID; 5xx → indisponível; TypeError → rede; AbortError →
  demorou demais). O ID só casa com "user" E "id" juntos (token rejections real falam
  "the user must be logged in" — não podem virar a branch de ID).
- `InstagramSyncStatusPanel` (campo ui irmão do picker): 5 estados fiéis ao rascunho;
  gate respeita o master `enabled`; `outcome` transiente limpo ao editar credencial;
  aria-live nos boxes interativos. Home inalterada (fail-closed intacto).
- Bug real: `afterChange` roda dentro da transação do save (row lock até os hooks
  retornarem); persistir o status via pool numa 2ª conexão deadlockava no lock da
  própria linha (~80-230s/save). Fix: persists do sync-hook na MESMA transação via
  `getPostgresTransactionDatabase` (C6); render/rota sem transação seguem no pool.
  Diagrama: UPDATE transactionid Lock vs idle in transaction; fix verificado 45ms vs 232s.
- Stub IG ganha estado `invalid-token`; runbook docs/ops/instagram-feed-token-runbook.md;
  migration add_instagram_sync_status (jsonb nullable).
- Débitos registrados: #124 (janela de lock) e #125 (polish P3).
- Gates: unit 2463 + int 791; lint/format/tsc/cycles; build; e2e admin 5/5 + frontend serial 19/19.
2026-08-20 00:54:58 -03:00
Francisco Solla
dba75ff886 docs(C143): regera agregado de changelog após rebase (OPS44) 2026-08-19 23:55:07 -03:00
Francisco Solla
2cc8f2c14d feat(C143): demandas visíveis apenas para responsáveis explícitos + candidato/coordenador
- campaignDemand.responsibles (hasMany → campaignUser, staff elegível) com
  hook beforeValidate que descarta ids não-staff (fail-closed)
- canRead/UpdateCampaignDemand por membership: assessor só vê demandas onde
  é responsável; unrestricted/admin sempre; leader nunca (fim do escopo
  municipal de demandas)
- criador entra automaticamente como responsável no hook de criação (form,
  wizard, rascunhos C90); migration com backfill created_by_id → responsável
- UI: picker multi-select na criação (nova + wizard) e card de gestão no
  detalhe com save full-replace + advisory lock; sugestões = assessores do
  município; rótulo 'Visível para: Responsáveis · Candidato · Coordenador'
- int tests reescritos para a regra nova + 5 casos novos; e2e novo de
  visibilidade (lista/URL/gestão); fixture e2e com createCampaignDemand
- impl plan + changelog; importMap regenerado com S3_* dummy (OPS69)
2026-08-19 23:55:07 -03:00
Francisco Solla
624126daca ops(OPS73): importMap regenerado com envs S3 (handler S3ClientUploadHandler) + impl plan do deploy do fix 2026-08-20 02:19:58 +00:00
Francisco Solla
eebe81cd1e feat(S8): seção 'Nossa história' na home — vídeo da trajetória embutido do YouTube (Closes #91) 2026-08-20 02:19:58 +00:00
Francisco Solla
398a91e54a feat(S9): captura de novidades da campanha na home pública (form + CTA do hero)
Seção 'Receba as novidades da campanha' acima do rodapé: nome + WhatsApp
obrigatórios, e-mail/estado/cidade/comentário opcionais, toggle pré-selecionado
'Quero fazer parte do time' gravado como subscription.campaignLevel
(time|esporadico). CTA secundário do hero vira o atalho (#novidades, scroll
suave). Contact + Subscription em withPayloadTransaction com consentimento
novo fail-closed por chave estável 'campanha-novidades' — nada é gravado sem o
texto aprovado no admin. Migration 20260819_192605 (state de contact nullable
+ coluna campaign_level). Impl plan, changelog, int + e2e.
2026-08-20 02:19:58 +00:00
Francisco Solla
cba515bf8b style(OPS72): alinha tabela CI/AUTOMERGE do AGENT-OPS.md (prettier) 2026-08-20 02:12:39 +00:00
Francisco Solla
780392d343 fix(OPS72): referência do guard do importMap (OPS70/#87, não #77) 2026-08-20 02:12:39 +00:00
Francisco Solla
ae1f21d1c4 fix(OPS72): saneia o changelog (tokens markdown que o prettier reescrevia como ênfase) 2026-08-20 02:12:39 +00:00
Francisco Solla
44f496e679 style(OPS72): prettier nos arquivos de changelog (wrap da nota do importMap) 2026-08-20 02:12:39 +00:00
Francisco Solla
82e9ca6b70 fix(OPS72): restaura S3ClientUploadHandler no importMap (regeneração sem envs S3_* do E2E_PROD=1 — classe OPS69)
O pnpm build do run e2e local em prod mode regenerou o importMap sem as envs S3_*,
dropping o handler do storage S3 (o P0 OPS69). Regenerado com envs dummy — diff de
+2 linhas idêntico ao estado fixado.
2026-08-20 02:12:39 +00:00
Francisco Solla
b3261c0dc3 refactor(OPS72): fixes do /simplify — gates e2e do ci-pr apertados para selected; docs alinhadas
- ci-pr.yml: infra e2e (cache/install/build-for-e2e + DB prep) só quando selected — diff high-risk não queima minutos à toa; comentários corrigidos
- engineering-standards.mdc + TESTING.md: política OPS72 (era OPS59/ci.yml stale)
- execution-pipeline/agent-work-issue: mecanismo real da #72 (CI roda famílias em paralelo, não sequencial) + ressalva do diff high-risk no pool
- changelog/impl plan: verificação verdadeira (suíte full local + triagem dos flakes)
2026-08-20 02:12:39 +00:00
Francisco Solla
fb7fc83c56 feat(OPS72): e2e local afetado discricionário na skill; CI de PR só blast radius (nunca full)
- ci-pr.yml: step e2e full removido; selected = único modo no PR (diff high-risk não roda e2e — espelho local + deploy verify cobrem)
- skills work-issue/agent-work-issue + execution-pipeline: passo e2e local afetado (discricionário) + limitação #72 documentada
- gate-ci.mjs: mensagens informativas (texto-only)
- docs AGENTS/AGENT-OPS/agent-pr-workflow.mdc + changelog OPS44
2026-08-20 02:12:39 +00:00
Francisco Solla
e504491dd8 fix(OPS71-FLIP): flip pós-merge silencioso no GitHub — PAT no arm do auto-merge, dispatch de recuperação, close da Issue, retry 410
Root cause (Issue #119): auto-merge armado com GITHUB_TOKEN mergea como
github-actions[bot] e o anti-recursion do GitHub nunca cria runs para o
evento closed — os flips pós-merge (issue-done / plan-issue-ready) não
rodavam em silêncio (PR #746). Arm agora usa AUTOMERGE_PAT (fail-closed);
workflow_dispatch de recuperação nos dois flips; forgejo-issue-transition
fecha a Issue (PATCH state=closed); forgejo-api retenta 410 (proxy respondeu
antes da origem) e loga corpo nginx de forma compacta.
2026-08-19 22:45:29 -03:00
Francisco Solla
6cf5ea5885 docs(OPS71-INFRA): runner do GitHub instalado no homeserver — runbook + changelog (Issue #113) 2026-08-20 00:58:40 +00:00
Francisco Solla
5a1b285d9a plan(OPS71-INFRA): impl plan — runner self-hosted do GitHub no homeserver (Issue #113) 2026-08-20 00:58:40 +00:00
Francisco Solla
02c1e4f36b feat(OPS71): Fase 2 — remover arquivos do fluxo Forgejo (workflows/scripts/specs) pós-validação do GitHub Actions
14 arquivos removidos: .forgejo/workflows/* (6), forgejo-pr-automerge.mjs,
forgejo-dispatch.mjs, ci-classify-production.mjs, lib/dockerignore.mjs,
lib/branch-protection.mjs + 3 specs órfãs. Consumidores ajustados:
test-affected-core (HIGH_RISK_EXACT), ciSkipInvariants (re-ancorado no
deploy.yml dispatch-only — deploy nunca automático), comentários com
referência morta (gate-ci, github-pr-automerge, agent-pr-ready-automerge).
Docs: AGENTS.md, runbook (rollback via git history), README (era GitHub).
Tracker intocado (forgejo-api/agent-*/issue-transition ficam).
2026-08-20 00:20:10 +00:00
Francisco Solla
3ea16243ca style(S11): prettier no rascunho UI do plano 2026-08-19 23:52:59 +00:00
Francisco Solla
851ac3941b plan(S11): feed do Instagram configurado não aparece na home pública — intenção + rascunho UI do painel de status 2026-08-19 23:52:59 +00:00
Francisco Solla
21a8e39fe2 plan(OPS73): deploy do fix OPS69 para produção — admin segue em branco (Related #114) 2026-08-19 23:48:48 +00:00
Francisco Solla
0901d60d72 fix(OPS71): literal do required check = nome do check-run (checks) + docs (pin PR #742) 2026-08-19 23:07:34 +00:00
Francisco Solla
24a867dfc3 feat(OPS71): CI/PR/deploy no GitHub Actions — tracker de Issues permanece no Forgejo (Closes #97) 2026-08-19 19:17:19 -03:00
Francisco Solla
693d2e481b chore(S7): entrada no changelog agregado (OPS44) 2026-08-19 19:39:09 +00:00
Francisco Solla
4a3c3b709c feat(S7): cards 'Acompanhe de perto' sem raio, borda, sombra ou chip; capa de borda a borda (Closes #90) 2026-08-19 19:39:09 +00:00
Francisco Solla
3b33a59a9d plans(OPS71/OPS72): CI volta ao GitHub Actions com tracker no Forgejo + e2e local afetado 2026-08-19 19:27:21 +00:00
Francisco Solla
d8d85de7be docs(S9,S10): planos de intenção — captura de novidades na home + pixel Meta na página de campanha 2026-08-19 18:58:23 +00:00
Francisco Solla
484ed4a424 style(S8): prettier no rascunho UI 2026-08-19 18:57:01 +00:00
Francisco Solla
bf193edf0d docs(S8): plano de intenção — seção de vídeo da história na home de campanha 2026-08-19 18:57:01 +00:00
Francisco Solla
2f47d8f321 style: prettier no rascunho UI do S7 2026-08-19 15:51:55 -03:00
Francisco Solla
3c1c922fe2 plan(S7): cards 'Acompanhe de perto' sem raio, imagem de borda a borda, chips removidos (Related #90) 2026-08-19 15:50:53 -03:00
fbfbfb1329 Merge pull request 'S4 — Compartilhar conteúdos da home no WhatsApp' (#86) from S4-compartilhar-conteudos-da-home-no-whatsapp into main 2026-08-19 16:26:43 +00:00
Francisco Solla
39fb3b47f0 fix(OPS62-followup): âncora de relógio no fixture do municipalitiesWithoutUpdateTool — elimina o flake de ordenação por raiz
Causa raiz (reproduzida, refuta a hipótese da Issue): daysAgo() chamava
Date.now() por chamada; as duas daysAgo(40) de Beta/Alpha podiam cair em ms
diferentes sob carga, deixando Alpha estritamente mais novo — sortStale
ordenava por timestamp e o tie-break por nome nunca disparava (0.08% das
construções sem pausa; 100% com pausa de 1 ms; fixture ancorado: 0 em 2000).
Fix test-only: âncora fixtureNowMs no load do módulo (padrão prioritiesTool)
+ hoist do timestamp empatado (igualdade estrutural) + comentário.
Nada muda em produção — o sort real é determinístico sobre timestamps fixos.
Verificação: spec 30/30, unit 2405, int 791, tsc/lint/format/cycles verdes.
2026-08-19 13:07:30 -03:00
Francisco Solla
067d20357d chore: regenera agregado do changelog pós-rebase (OPS66/67/68 + S4) 2026-08-19 12:52:18 -03:00
Francisco Solla
f35ac335aa style: prettier nos docs do S4 (changelog + plans) 2026-08-19 12:50:26 -03:00
Francisco Solla
8bc8900e9d feat(S4): compartilhar conteúdos da home no WhatsApp 2026-08-19 12:50:19 -03:00
Francisco Solla
7162c65e37 ops(OPS69): regenera importMap com o S3ClientUploadHandler (admin abria em branco em prod)
O importMap commitado ficou sem a entrada @payloadcms/storage-s3/client#S3ClientUploadHandler
desde o OPS52 (dabc00f4 removeu o handler do blob sem adicionar o do S3, porque o
generate:importmap rodou sem as envs S3_*). Com o plugin s3Storage condicional, sem as
envs o importMap sai consistente (dev/test) mas em produção (envs ativas) o admin falhava
ao montar a view — getFromImportMap: PayloadComponent not found in importMap — login nunca
renderizava. Fix: regenerar com as 4 S3_* dummy inline (loadEnvConfig do @next/env não
sobrescreve envs do processo; iterateConfig não toca DB) — diff de exatamente +2 linhas.
Smoke headless com S3 ativo: /admin/login renderiza, zero erros de console. Gates verdes
(tsc/lint/format/knip/cycles/unit+int 791/build). Sem migration/access/UI. Docs: nota no
AGENTS.md (generate:importmap precisa das S3_* enquanto o plugin for condicional) +
changelog. Guard de CI registrado como OPS70 (#87, depends #77).
2026-08-19 12:45:22 -03:00
Francisco Solla
ccfa60c1f2 fix(OPS64): pin do waitForChecks contra rollup mentiroso + docs — OPS62 eliminou o secundário estruturalmente
O sintoma primário da Issue (deadlock do waitForChecks no próprio pending)
já tinha sido entregue pelo OPS61. O secundário — o rollup `checks` reportava
SUCCESS com um shard de e2e vermelho porque o Forgejo 9.0.3 reporta o
resultado do ÚLTIMO shard como o parent de matrix no `needs` (runs 604/607/610;
o `needs.<job>.results` do GitHub não existe neste runner) — foi eliminado
estruturalmente pelo OPS62 (mergeado durante a execução): ci-pr.yml e ci.yml
viraram jobs únicos sequenciais, sem matrix/shard, e o status do job `checks`
já é o veredito honesto da cascata.

O que este item entrega: (a) teste-pino no waitForChecks (cenário run 607:
rollup verde + shard vermelho → o CLI LANÇA "Checks falharam" — nunca confia
no rollup sozinho; invariante de defesa válido e que pegaria qualquer matrix
que volte); (b) docs honestas (agent-pr-workflow.mdc, AGENT-OPS.md) com a
narrativa do parent de matrix e da eliminação estrutural pelo OPS62. O script
ci-rollup-check.mjs e os edits de workflow planejados foram descartados no
gate (redundantes pós-OPS62). Unit 2392 verdes; gates locais ok.
2026-08-19 12:11:41 -03:00
Francisco Solla
80062078b6 docs(OPS68): agregado do changelog com a entrada OPS68 (ficou fora do rebase)
O --ours no conflito do agregado (OPS66) deixou a entrada OPS68 fora do
commit; changelog:build regenera e o docs-guard do CI exige o agregado
commitado.
2026-08-19 13:33:05 +00:00
Francisco Solla
3ca2cbd438 docs(OPS68): reescreve docs canônicos da era GitHub/Vercel para Forgejo/homeserver
Parte 2 do OPS50 (fechamento): AGENT-OPS, README, roadmap (ponteiro),
AGENTS.md (refs Neon/job gate), rule agent-pr-workflow e 8 skills de
operação sem comandos gh (API/MCP + safety net) nem secrets Vercel —
fonte canônica única: git.solla.dev/fsolla/teqo.
2026-08-19 13:33:05 +00:00
Francisco Solla
cc6149191b ops(OPS67): retry com backoff nos fetchs do forgejo-api
Um fetch failed transitorio (rede/DNS/reset) matava o run do safety net
(agent-pr-ready-automerge) no meio da espera de checks. Retry no request()
do cliente: fetch reject retenta em qualquer metodo; body-drop em GET
retenta; 5xx (502/503/504) retenta so em GET (body cancelado antes do
retry); 4xx e body-drop em escrita falham fail-closed. 3 retries, base
300ms x2, jitter +-20%, console.warn pt-BR por retry; opcoes flat no
createApi (retries/backoffMs/jitter/sleepImpl). 12 specs unit novas.
2026-08-19 13:28:37 +00:00
Francisco Solla
8d33e89102 style: prettier — runbook/impl plan/unit test (OPS66) 2026-08-19 10:07:14 -03:00
Francisco Solla
228f2217c7 ops(OPS66): deploy — migrate antes do build do runner (deadlock de migrations em caminho estático) 2026-08-19 10:06:00 -03:00
Francisco Solla
9504c7ccf6 ops(OPS65): CI do main em janela de 30 min (gate .dockerignore fail-open) + matar agent-pool
- ci.yml: on push removido → schedule */30 + workflow_dispatch (fundido com a
  estrutura de job único sequencial do OPS62 X1)
- job gate (host, main-only, sem pnpm): revision do container rodando no
  homeserver (label org.opencontainers.image.revision — o compose pode mentir
  após rollback falho) + diff + classificador .dockerignore
- fail-open: revision desconhecida → suite roda (verde); incapaz de classificar
  → run vermelho (nunca blackout silencioso); deploy duplo-gated
  (needs: [checks, gate] + if explícito)
- deploy-homeserver.sh: guard 'already deployed' idempotente (mesma revision)
- agent-pool.yml removido; pool dormente (144 pnpm install/dia no tick */10)
- specs: dockerignoreMatch, ciClassifyProduction (incl. git-diff com repo
  scratch e exit codes), pins ciSkipInvariants/deployScript; HIGH_RISK_EXACT
- docs: runbook (janela/gate/verificação ao vivo/falhas), AGENTS.md,
  AGENT-OPS.md (tabela X1 fundida), skills pool dormente, changelog OPS65
- impl plan aprovado; intenção marcada entregue
2026-08-18 21:08:12 -03:00
Francisco Solla
dd61cb3985 style: prettier no changelog agregado (pós-rebase S6+S3) 2026-08-18 20:30:10 -03:00
Francisco Solla
a638a8acae feat(S3): seção de conteúdos da home de campanha inclui os posts do Instagram
Board "Acompanhe de perto" (S1+S2) ganha o feed automático do perfil oficial:
loader irmão src/utilities/socialFeed/instagramFeed.ts (Graph API /media com
children p/ carrossel + username, unstable_cache tag social-feed + revalidate
300, snapshot cru pré-exclusão persistido via SQL direto, refresh de token
automático com retry 1x — refresh endpoint só renova tokens de Instagram
Login, page tokens caem no fail-closed), global SocialFeedSettings estendida
(instagramEnabled/AccessToken/UserId/MaxItems + snapshot hidden; excludedItems
já cobria a plataforma desde a S2). Requisito nº 1 — picker de exclusão no
admin (primeiro componente custom do repo, campo ui + importmap): lista os
posts do snapshot com thumbnail e toggle "Não exibir" que escreve em
excludedItems. Card com coverAspect square (1:1 do rascunho), badge
Instagram, título = legenda com fallback para post sem legenda, clique no
permalink (noopener), link "Seguir no Instagram →" derivado do username.
Os feeds externos mudam para o subfolder socialFeed/ (pin da S2 previa).
Migration 20260818_192948_add_instagram_social_feed_fields (não destrutiva).
Stub IG no webServer (porta dev+2000, helper socialStub.ts) + 3 e2e novos no
describe serial + kill switch estendido + smoke do picker no admin.
Closes #20
2026-08-18 20:29:50 -03:00
Francisco Solla
a6c8ece2f5 style: prettier no AGENT-OPS pós-resolução de conflito do rebase 2026-08-18 23:26:13 +00:00
Francisco Solla
ee31aeee4f fix(OPS62): services por nome na rede do job — Forgejo 9 não expande expressões em ports
Achado ao vivo (run 730): a expressão ${{ 5433 + (github.run_id % 100) }} no
ports do service não é avaliada pelo Forgejo 9 — o literal ia ao runner e o
job falhava em 1s. E portas fixas colidiriam entre runs concorrentes (o job
único segura os services pelo run inteiro). Correção: services sem publish
host, alcançados por nome na rede do job (postgres-int/postgres-build:5432)
— solução canônica do Actions, mata a colisão de raiz. Guards fail-closed
(LOCAL_HOSTS + assertTestDatabase) admitem os nomes dos services (hosts
internos do runner; hosts remotos continuam rejeitados); pins novos em
localHosts.unit.spec.ts e ciSkipInvariants (sem ports:, URLs por nome).
2026-08-18 23:26:13 +00:00
Francisco Solla
12a4fe1005 chore(OPS62): CI fail-fast estrutural — job único checks sequencial (X1)
ci-pr.yml e ci.yml deixam de ter jobs irmãos: 1 job 'checks' com steps em
série (guards → lint → format → typecheck → knip → cycles → unit → int →
build → e2e). Step vermelho interrompe o job, o run falha e o slot é
liberado — sem API de cancel (Forgejo 9.0.3 não tem), sem watchdog, sem
upgrade do Forgejo. Skips do ci-scope.mjs viram step id 'scope'; e2e em 1
processo com PLAYWRIGHT_WORKERS=4 (substitui 2 shards × 2 workers); 2
services Postgres por run com portas host derivadas do run_id (runs
concorrentes não colidem); contexto 'CI (PR) / checks' preservado (job id
checks — contrato do automerger OPS61); deploy OPS53 gated em needs checks.
Plumbing morto removido (e2eShardConfig/e2e_shards). Docs: AGENT-OPS,
TESTING.md, changelog. Impl: docs/plans/ops62-ci-fail-fast-forgejo-impl.md
(a intenção watchdog+upgrade fica como histórico do gate).
2026-08-18 23:26:13 +00:00
Francisco Solla
a1a6160ed8 plan(OPS69): admin em branco em produção — importMap sem o handler do storage S3 (Related #77) 2026-08-18 22:59:04 +00:00
Francisco Solla
8c915e59fc fix(S6-FOLLOWUP): test:e2e:affected rodava a suíte completa — --no-deps desliga o closure de deps dos projects em runs filtrados
Causa raiz (medida com o binário 1.58.2, config real): commander parseia
posicionais e -g/--list funcionam; o que arrastava a suíte era o closure de
dependências do dev mode (setup<-campaign<-frontend<-admin): filtrar um spec
do frontend puxava campaign(160)+setup(2). run-e2e-affected.mjs agora gera
test:e2e <passthrough verbatim> --no-deps -- <paths> via helpers puros em
scripts/lib/playwright-e2e-args.mjs (unit-pinned); passthrough corrigido
(pnpm consome o -- e o path era descartado); run-e2e-affected.mjs entrou em
HIGH_RISK_EXACT. Docs: receitas de filtro no playwright.config.ts, AGENTS.md,
changelog.
2026-08-18 19:09:12 -03:00
Francisco Solla
e6bd1d2688 docs(OPS63): status entregue no plano de intenção 2026-08-18 21:35:24 +00:00
Francisco Solla
5af626ebbf ops(OPS63): docs-guards no pre-push — changelog append-only, agregado e marcadores de conflito no gate:ci 2026-08-18 21:35:24 +00:00
Francisco Solla
4f7de670f5 fix(OPS61): merge do safety net com FORGEJO_API_TOKEN — 409 PushRejected do hook pre-receive
O merge POST faz um push para main; o hook pre-receive do Forgejo rejeita o
push quando o usuário do token não tem write no repo — o GITHUB_TOKEN nativo
age como o usuário sistema do Actions. Por isso nenhum auto-merge funcionou
(o CLI morria no 409; runs 'success' eram leituras de PR já mergeada
manualmente). O workflow do safety net agora injeta o mesmo PAT dos pós-merge.
2026-08-18 18:07:55 -03:00
Francisco Solla
e34a964bff chore(OPS61): changelog agregado após rebase (OPS60 + OPS61) 2026-08-18 17:46:16 -03:00
Francisco Solla
8b98930670 fix(OPS61): gate do waitForChecks sem o pending do próprio job — achado ao vivo PR #67
O Forgejo posta o status do próprio job do safety net como pending durante o
run inteiro: o gate antigo (pending.length === 0) era insatisfazível e o CLI
nunca auto-mergeou nada — todo merge Forgejo até aqui foi manual (merged_by
do dono), inclusive a PR #52 com CI vermelho (narrativa da 'raça' corrigida).
O gate é: rollup CI (PR) / checks presente+verde (cascata inteira assentou),
ignorando o contexto do próprio workflow; mergeable false só após assentar.
2026-08-18 17:45:54 -03:00
Francisco Solla
ac11afecf3 ops(OPS61): flip de issues pós-merge com PAT + gate do rollup checks + branch protection real no Forgejo
- issue-done/plan-issue-ready: env FORGEJO_API_TOKEN (GITHUB_TOKEN 403 em
  Issues) + fork guard (checkout de fork roda script do fork com o PAT — RCE)
- forgejo-issue-transition: falha de flip vira exit 1 (job vermelho, nunca
  skip silencioso)
- waitForChecks: exige o rollup CI (PR) / checks presente+verde (raça PR #52:
  snapshot com jobs não-agendados parecia verde); mergeable=false só conta
  como conflito após a cascata assentar; timeouts 30→45min (CLI) e 40→55min
  (workflow)
- configure-branch-protection: contexto real CI (PR) / checks* (Forgejo casa
  por glob contra 'CI (PR) / checks (pull_request)'; literal 'checks' nunca
  casaria), idempotente GET→POST/PATCH, verificação pós-aplicação; decisão
  pura em scripts/lib/branch-protection.mjs
- docs honestas (AGENT-OPS, agent-pr-workflow.mdc, CUTOVER-MAIN-ONLY, README)
- testes: raça do rollup/rollup failure/mergeable pré-settle +
  branchProtection create/update/noop + glob real
2026-08-18 17:45:54 -03:00
Francisco Solla
bf4063b236 plan(OPS66): deploy — build de geração estática lê schema novo antes do passo migrate 2026-08-18 20:29:28 +00:00
Francisco Solla
2d402c81a2 chore(OPS60): guard SEED_MEDIA_CONFIRM para escrita no bucket via db:seed:posts 2026-08-18 20:11:54 +00:00
Francisco Solla
085a69e6e1 docs(S6-FOLLOWUP): plano com evidência empírica do bug de filtros do Playwright (-- e -g) 2026-08-18 19:32:50 +00:00
Francisco Solla
cacb6165ad feat(S6): home de campanha sem carrossel no desktop — grade estática em ≥1024px, mobile preservado 2026-08-18 19:32:50 +00:00
Francisco Solla
8cc9e078ae plan(OPS65): CI do main em janela de 30 min + matar o agent-pool 2026-08-18 19:10:18 +00:00
Francisco Solla
fe5b11cf24 feat(S2): seção de conteúdos da home de campanha inclui os vídeos do YouTube
Board "Acompanhe de perto" (S1) ganha o feed automático do canal oficial:
global SocialFeedSettings (admin-only, kill switch + toggle + maxItems +
exclusões por item), loader src/utilities/youtubeFeed.ts (Data API v3
search→statistics, unstable_cache tag social-feed + revalidate 300,
fail-closed com snapshot persistido via SQL direto — revalidateTag dentro
de unstable_cache lança no Next 15.4), card generalizado
(CampaignArticleCard → CampaignContentCard, external com noopener) e merge
por recência com o grande = mais recente elegível. Stub HTTP do YouTube no
webServer do Playwright (porta dev+1000) + 4 e2e novos no describe serial.
Migration 20260818_111748_add_social_feed_settings (não destrutiva).
Closes #18
2026-08-18 15:23:50 -03:00
Francisco Solla
50749270d6 style: prettier nos planos OPS62/OPS63 2026-08-18 18:17:08 +00:00
Francisco Solla
d5f48ea9db plan(OPS62): CI fail-fast no Forgejo — upgrade do servidor + watchdog cancela o run em qualquer job vermelho
plan(OPS63): doc-guard no pre-push — changelog append-only, agregado e marcadores de conflito no gate local
2026-08-18 18:17:08 +00:00
Francisco Solla
7bd9c9504f ops(OPS59): pre-push sem e2e — e2e roda uma vez, no CI (gate local fica com os outros testes) 2026-08-18 17:56:41 +00:00
Francisco Solla
a60b3880ca feat(S5): banda visual própria para a seção 'Acompanhe de perto' na home 2026-08-18 13:35:53 -03:00
Francisco Solla
228d5da9c8 ops(OPS52-media-guard): reconcile do media:recover exige MEDIA_RECOVER_CONFIRM=1 (guard de intenção explícita) 2026-08-18 12:45:32 -03:00
Francisco Solla
1dfad8ac99 docs(OPS61): evidência da raça do waitForChecks (merge com CI vermelho na PR #52) absorvida no plano 2026-08-18 15:38:44 +00:00
Francisco Solla
7b8e37e162 fix(ops OPS57): simplify — veto de draft vale em conversão mid-run; CLI valida base main; docs sem mecanismo fantasma 2026-08-18 15:24:21 +00:00
Francisco Solla
393b8c9991 ops(OPS58): seed de posts com dry-run e taxonomy atualizada — sync de prod com runbook
- scripts/seed-posts.mjs: modo --dry-run (plan-only, echo do alvo, zero
  escrita) e 4 slugs novos no CLASSIFICATION (3 com tag eleitoral — lock de
  visibilidade eleitoral do repo)
- docs: impl plan com runbook do homeserver, changelog, nota AGENTS.md
2026-08-18 12:15:05 -03:00
Francisco Solla
87a81c29fd chore(ops OPS57): alargar o gatilho do forgejo-pr-automerge para todas as PRs same-repo (draft não-cursor/* = veto) 2026-08-18 11:50:48 -03:00
Francisco Solla
094dbe6b5d plan(OPS59): pre-push sem e2e — e2e roda uma vez no CI 2026-08-18 11:45:03 -03:00
Francisco Solla
71cf477f6e plan(OPS58): atualizar artigos de produção com novos posts do jorgesolla.com.br 2026-08-18 14:32:00 +00:00
Francisco Solla
b78a390371 fix(ops OPS56): forgejo-pr-automerge — veredito de merge por re-read do PR (falso positivo 'já mergeado' com PR aberto) 2026-08-18 14:28:54 +00:00
Francisco Solla
f12bd6474b docs(OPS52-media): runbook atualizado com o fix de infra real (E3 = firewall do host; 38/40 servindo 200) 2026-08-18 10:45:27 -03:00
Francisco Solla
64ea0dbab9 ops(OPS54): allowlist onlyBuiltDependencies migra para pnpm-workspace.yaml — fim do warning do pnpm 10 sobre o campo pnpm ignorado 2026-08-18 09:06:37 -03:00
Francisco Solla
45e29a7785 feat(OPS52-media): recupera os objetos de media de produção do WordPress para o bucket (pnpm media:recover)
- scripts/recover-media.mjs: CLI de reconciliação (reconcile/--dry-run/--verify)
  — lê rows de media + posts.coverImage, resolve a capa com a mesma lógica do
  seed (featured media, senão 1ª imagem inline), PutObject com key = filename;
  DB nunca escrito; fail-closed (S3_* completas, ALLOW_REMOTE_DB, echo do alvo,
  timeouts curtos, covers só do origin WP e image/*)
- scripts/lib/wpArticles.mjs: fetch ao vivo do WordPress extraído do seed
  (sem twin), helpers puros de resolução + row→cover mapping (unit-testados)
- seed-posts.mjs: importa o fetch da lib (comportamento idêntico) e usa
  resolveCoverSource em processContent
- runbook de execução no homeserver (impl plan), nota no AGENTS.md, changelog
Closes #10
2026-08-18 08:20:45 -03:00
Francisco Solla
fbe3da1acf style(plans S6): prettier no rascunho UI 2026-08-18 10:59:57 +00:00
Francisco Solla
c3dde67e91 plan(S6): home de campanha sem carrossel no desktop — grade 3x2 nas bandeiras 2026-08-18 10:59:57 +00:00
Francisco Solla
a4ae631157 plans(S5): separação visual da seção Acompanhe de perto (Related #33) 2026-08-18 10:56:23 +00:00
Francisco Solla
ffab9e97fb plan(OPS52-media): recuperar arquivos de media de produção (capas dos artigos) 2026-08-18 07:26:11 -03:00
Francisco Solla
beea467816 fix(S1): revert revalidate=300 — trava o bust por tag no Next 15.4 prod
Medido em probe: com export const revalidate, revalidateTag('posts') para
de regenerar a home (a rota /artigos, sem o export, convergia; a home
ficava stale para sempre). Remove o export e resolve o congelamento da
label relativa por threshold: relativa < 48h, data absoluta além (nunca
mente congelada). Spec: polling positivo do HTML da home pós-revalidate
(ISR serve stale uma vez sob carga) e about:blank antes do cleanup
(prefetch prod de links apagados = 404 no guard).
2026-08-18 05:07:26 +00:00
Francisco Solla
41dbb564c6 feat(S1): seção de conteúdos na home de campanha (artigos)
Bento 1+4 no desktop e carrossel 1/tela no mobile (~390px) lendo o post
collection com isPostVisible fail-closed; seção some inteira sem posts
visíveis. Zero migration/Consent; home vira ISR com tag posts +
revalidate=300 (label relativa não congela).
2026-08-18 05:07:26 +00:00
54058da059 OPS52 — Migrar a media de produção do Vercel Blob para o Garage S3
Merge pull request #27 from fsolla/OPS52-migrar-a-media-de-producao-do-vercel-blob-para-o-garag

OPS52 — Migrar a media de produção do Vercel Blob para o Garage S3 (Closes #3)
2026-08-18 02:45:52 +00:00
Francisco Solla
a34cd9b68a fix(ops OPS53): deploy — compose run consome o stdin do bash -s (migrate com </dev/null); build via --network host + proxy socat (BuildKit recusa rede bridge custom) 2026-08-18 01:59:17 +00:00
Francisco Solla
dabc00f46c ops(OPS52): migra a media de produção do Vercel Blob para o Garage S3
- payload.config.ts: vercelBlobStorage -> s3Storage (@payloadcms/storage-s3)
  com endpoint Garage (forcePathStyle, region garage); bucket privado — o
  staticHandler do plugin streama os objetos em /api/media/file/<filename>,
  mantendo o contrato de URL relativa das 40 rows pré-existentes
- src/utilities/mediaStorage.ts: resolveS3StorageEnv fail-closed (S3_* parcial
  -> throw no boot com a lista exata; nenhuma -> storage local dev/test)
- seed-posts: remove pre-delete do @vercel/blob (S3 sobrescreve nativamente)
- worktree-env.mjs: s3EnvCopiedLines all-or-nothing (unit-testada)
- envs S3_BUCKET/S3_ENDPOINT/S3_REGION/S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY
  no .env.example/.env.test; cloud-setup.sh sem o sed morto do blob
- deps: -storage-vercel-blob -@vercel/blob +storage-s3; importMap regenerado
- docs: impl plan (revisão A->B: Garage v2 sem acesso anônimo na API S3),
  AGENTS.md, changelog 2026-08-18-ops52
2026-08-17 22:36:34 -03:00
Francisco Solla
0aec0d5040 feat(PUB5): slogan 'Mais saúde mais futuro' no hero e rodapé 2026-08-18 01:31:28 +00:00
Francisco Solla
8854fcb85b style(plans S1-S4): prettier nos planos e no rascunho UI (format pós-commit do lote) 2026-08-17 22:02:14 -03:00
Francisco Solla
02c922eac8 docs(ops OPS53): falha conhecida do runner — PATH dos containers quebra com runner.envs.PATH só de host 2026-08-18 00:50:41 +00:00
Francisco Solla
6a3f61f968 ops(OPS53): etapa de deploy no CI para o homeserver (teqo-1313)
Job deploy em ci.yml (runs-on host na workstation) streama
scripts/deploy-homeserver.sh para o homeserver via SSH: build dos stages
runner+migrator com secrets do teqo-1313.env, push no registry local,
migrate pelo serviço de maintenance antes do rollout, up + smoke. Zero
secrets novos no Forgejo; HEAD guard + flock; rollback automático com
backup do compose; runbook docs/ops/teqo-1313-deploy.md; invariantes
unit do script (bash -n + estrutura).
2026-08-18 00:50:41 +00:00
Francisco Solla
87410ccc63 plan(S1-S4): seção de conteúdos na home de campanha — artigos, YouTube, Instagram + compartilhar WhatsApp 2026-08-18 00:47:52 +00:00
Francisco Solla
346da3896e style(plan): prettier no rascunho UI do PUB5 2026-08-17 21:34:27 -03:00
Francisco Solla
220142cc75 plan(PUB5): slogan da campanha 'Mais saúde mais futuro' no hero e rodapé 2026-08-17 21:33:12 -03:00
Francisco Solla
dfd57344e3 plan(OPS54): silencia o warning do pnpm sobre pnpm.onlyBuiltDependencies ignorado 2026-08-17 21:03:43 -03:00
Francisco Solla
e03ba1f83b ops(OPS51): migra o banco de produção do Neon para o Postgres do homeserver (teqo_1313)
Dump pg_dump -Fc do Neon no homeserver (/srv/hdd/backups/teqo-neon-pre-migracao/),
restore como a role teqo_1313 (o container já lia o banco preparado — sem mudança
de config). Neon intacto como rollback. Divergência da intenção (A/B validada como
B): destino = teqo_1313, não teqo_prod. Débito de media registrado (#10 → OPS52).
2026-08-17 22:41:18 +00:00
Francisco Solla
5afb54b692 chore(OPS53): prettier no plano 2026-08-17 16:06:05 -03:00
Francisco Solla
8cc4900cf0 plan(OPS53): etapa de deploy no CI para o homeserver (container teqo-1313) 2026-08-17 16:01:21 -03:00
Francisco Solla
f151376641 ops(OPS50): remove deploy Vercel do pipeline — produção é self-hosted no homeserver (jorgesolla1313.com.br); ci.yml vira verificador puro (sem deploy/requeue), apaga vercel-promote.yml, scripts/tests/vercel.json e secrets VERCEL_* 2026-08-17 13:17:17 -03:00
Francisco Solla
2f538e396b OPS50: ajusta changelog ao formato prettier 2026-08-16 23:34:28 -03:00
Francisco Solla
f20305759a OPS50: port CI e scripts de agentes do GitHub para o Forgejo
Workflows nativos em .forgejo/workflows/ (ci, ci-pr, agent-pool,
automerge, issue-transition, plan-issue, vercel-promote, archive),
removendo .github/workflows e .github/actions. Runner Forgejo v13 com
services Postgres por porta + DB_HOST via gateway do bridge. Libs de
agente sobre o novo cliente forgejo-api.mjs (zero dependências,
GITHUB_SERVER_URL/FORGEJO_API_URL), helpers de workflow plain-Node.
pnpm issue corrigido (await em função sync) e validado ao vivo. Testes:
forgejoApi.unit.spec.ts novo, pin do matrix do ci.yml atualizado.

Closes #1 (parte 1 — docs/issues/pool seguem na parte 2)
2026-08-16 23:32:20 -03:00
Francisco Solla
8d16b8a0cb plan(OPS50-52): planos de intenção — CI Forgejo, DB Neon→homeserver, Blob→Garage
Related #1 #2 #3
2026-08-16 21:54:25 -03:00
Francisco Solla
665157a0c0 feat(site): hero da campanha responsivo aos boards do Penpot
A primeira dobra da home pública agora acompanha as referências do Penpot
em 393, 768, 1024, 1280, 1366 e 1920 px. No desktop, a escala e a posição
horizontal das fotos dependem somente da largura: Solla, Lula e Wagner
ficam ancorados ao rodapé real do hero mesmo quando o conteúdo aumenta a
seção, e um palco centralizado de 1920 px preserva a proximidade entre
retrato e texto em monitores ultrawide. E2E fixa a geometria das seis
referências, transições de proporção, desktops baixos, ultrawide e os
overrides da WCAG 1.4.12; o caso de regressão também roda no Firefox.
2026-08-16 20:33:00 -03:00
Francisco Solla
98b1f5c089 style(site): formata artefatos da campanha 2026-08-16 00:50:07 -03:00
Francisco Solla
7ddc281f22 feat(site): publica home da campanha Jorge Solla 1313 2026-08-16 00:49:24 -03:00
Francisco Solla
d6df8c6360
Merge pull request #741 from fsolla/C139-contatos-pagina-da-entidade-contact-lista-desktop-cards
C139 — Contatos — página da entidade Contact (lista desktop + cards mobile)
2026-08-13 19:27:17 +00:00
Francisco Solla
d4342a96d7 docs(C139): plano C140 — form reset do React 19 em formulários de campanha
Débito colhido no simplify do C139: ~21 forms com action={submitAction}
apagam o digitado em erro (provado na sessão). Registrado como #740 com
depends em #728.
2026-08-13 15:20:39 -03:00
Francisco Solla
a06f810a40
Merge pull request #739 from fsolla/C134-e2e-agenda-dia-hardcoded-17-de-agosto-de-2026-no-teste-
test(C134): e2e agenda deriva o dia do picker de término de hoje + 1 (sem data hardcoded)
2026-08-13 18:17:27 +00:00
Francisco Solla
fe0a7999a9 test(C134): e2e agenda deriva o dia do picker de término de hoje + 1 (sem data hardcoded)
O teste C104 clicava em '17 de agosto de 2026' fixo no picker de término;
quebrava deterministicamente ~2026-09, quando o dia sairia do grid visível.
Agora o alvo é civilDatePlusDays(formatBahiaCivilDate(new Date()), 1) —
sempre dentro do grid (dia do mês ou day 1 do mês seguinte como outside day).
Absorve também o flake pré-existente do redirect canônico ?tab=overview nos
3 sítios de toHaveURL do detalhe (mesmo regex da linha 219, C123).
2026-08-13 15:01:23 -03:00
Francisco Solla
b420598328
Merge pull request #734 from fsolla/OPS45-e2e-campaigne2efixtures-deixa-lixo-no-banco-de-teste-c
OPS45: e2e fixtures — purge de resíduo no claim + robustez dos int specs
2026-08-13 17:56:11 +00:00
Francisco Solla
9471c8f0aa OPS44: changelog por entrega + guards de docs + merge por rebase 2026-08-13 17:55:22 +00:00
Francisco Solla
bf25100ed5 chore(OPS45): e2e fixtures — purge de resíduo no claim de município + robustez dos int specs (Closes #723)
O cleanup do campaignE2EFixtures estava completo em runs normais (proxy auto-own
cobre contact/stateDeputy); o lixo vinha de runs crashados, cujo cleanup nunca
roda. O claim e2e agora purga resíduo como o int já fazia, e a purga ganhou a
classe dobradinha (stateDeputy vinculados via municipality.stateDeputies + fichas
órfãs com ref-check incluindo stateDeputy — FK ON DELETE RESTRICT).

peopleList.int.spec.ts passa a varrer todas as páginas (lista global de 25/pág)
com dedupe; personDelete/peopleList usam personName() nos nomes fixos, imunes ao
invariante global de nome de dobradinha.
2026-08-13 14:18:54 -03:00
Francisco Solla
1daaaf9f4e feat(C139): domínio de contatos — URL state, loader com vínculos e invariante de nome
Fase 2 do plano aprovado. Três módulos em src/utilities/contacts/:
- contactListUrl: contrato de params (q|gender|state|city|ausencia|vinculo|sort|dir|page),
  canonicalize, hrefs de sort e where de fonte com 'phones.value' like (ILIKE verificado
  no Postgres local).
- contactListData: loader com UM find em contact (user threadado, overrideAccess false),
  vínculos (liderancas/dobradinhas/assessores/equipe) como facets sobre o conjunto
  legível, filtro/sort/paginação em memória, nulos no fim (precedente B15).
- contactNameInvariant: lock contact-name:{normalized} + prefilter ILIKE + veredito
  exato em memória; mensagem de conflito pt-BR.

38 testes unit novos; format + tsc limpos.
2026-08-13 14:05:24 -03:00
Francisco Solla
69044a04bc plan(C139): nome obrigatório e único na criação, com aviso de conflito 2026-08-13 13:10:01 -03:00
Francisco Solla
9583bb16e8 plan(C139): revisão do rascunho UI — coluna CEP, Apagar no mobile, inputs sem moldura 2026-08-13 13:05:03 -03:00
Francisco Solla
dca6ccdc88 plan(C139): remove rascunho do C135 (cancelado no lote 2026-08-13) 2026-08-13 12:57:24 -03:00
Francisco Solla
977dab044e
Merge pull request #729 from fsolla/plans/plan-issue-43
plan(C139): Contatos - pagina da entidade Contact + cancelamento dos planos abertos de Pessoas
2026-08-13 15:56:35 +00:00
Francisco Solla
72d189cfd7 style(plan): prettier no rascunho UI do C139 + re-render PNGs 2026-08-13 12:55:24 -03:00
Francisco Solla
65a5881b2d plan(C139): Contatos — página da entidade Contact; cancela planos abertos de Pessoas (C121/C130+/C132/C135-C138) 2026-08-13 12:53:12 -03:00
Francisco Solla
c2296701e2 style(plans): C135 — formata HTML do rascunho UI (prettier) 2026-08-13 12:05:24 -03:00
Francisco Solla
8ed3da8982 docs(plans): C135 — pessoas mobile feed/cards edge-to-edge com drawer de edição e faixa de status 2026-08-13 12:05:24 -03:00
Francisco Solla
98b03d8e91
Merge pull request #718 from fsolla/C130-pessoas-ajustes-da-tabela-desktop-contato-completo-base
C130 — Pessoas: ajustes da tabela desktop (contato completo, base sob o nome, caret, tooltips, Dobra em, filtro/ordenação por partido)
2026-08-12 16:20:44 -03:00
Francisco Solla
ef41e0c6a3
Merge pull request #726 from fsolla/B200-municipios-mobile-encaixes-dos-cards-densidade-alinhame
B200 — Municípios mobile: encaixes dos cards — densidade, alinhamento de labels e avatares (pós-B196)
2026-08-12 16:01:40 -03:00
Francisco Solla
65659bac2f C130: ajustes da tabela desktop de pessoas (contato completo, base sob o nome, caret, tooltips, Dobra em, filtro/ordenação por partido) 2026-08-12 16:01:25 -03:00
Francisco Solla
98f8ec9b9b
Merge pull request #710 from fsolla/C128-pessoas-qualquer-pessoa-pode-assessorar-liderar-e-ou-do
C128 — Pessoas: qualquer pessoa pode assessorar, liderar e/ou dobrar — ciclo de vida nas tabelas pela lista
2026-08-12 15:45:55 -03:00
Francisco Solla
3ee2d83350 feat(B200): encaixes dos cards mobile de municípios — densidade, alinhamento de labels e pilha de avatares (pós-B196) 2026-08-12 15:40:02 -03:00
Francisco Solla
6f89a8ff59 fix(C128): separadores entre entradas do changelog pós-rebase (C123) 2026-08-12 15:31:01 -03:00
Francisco Solla
c0645067f1 feat(C128): ciclo de vida das capacidades pelas células de /campanha/pessoas (Closes #695)
Qualquer pessoa visível ganha/perde Assessora, Lidera e Aliada em pela
lista: a primeira cidade cria a entidade (conta staff sem credenciais
utilizáveis / liderança / dobradinha), a última remove da tabela com
confirmação destrutiva (manifest: votos+convites da liderança, conta+
autorias+assessorado da assessora; dobradinha sem diálogo). Saída
destrutiva por escopo: assessor encerra Lidera/Aliada com a capacidade
inteira na carteira; Assessora segue coordenação/candidato. Escritas
person-centric por contactId com extração dos deltas B34/B37 e da
cascata de conta (deleteCampaignUserAccount, nextFreeStubCampaignEmail,
matchers de conflito) — sem twin. commitGuard/commitWithNullOwner/
extraFormFields como opt-ins no RelationChipCell. Corrige bug latente
C116 (célula Assessora nunca salvava — contactId faltava no FormData).
Sem migration. Int: 28, unit: 6, e2e: +2.
2026-08-12 15:29:56 -03:00
Francisco Solla
4202462e17 docs(plans): OPS43 — migration-lock bloqueia PRs sem schema por colateral (#721) 2026-08-12 15:03:20 -03:00
Francisco Solla
99a3a5f590 test(OPS42): e2e dev — quiescência do router antes de interagir (login + chat Sollinha) (Closes #691) 2026-08-12 15:03:20 -03:00
Francisco Solla
db8102b47e feat(C123): agenda — overlay modal central como única superfície de criação/edição de atividade (Closes #667) 2026-08-12 14:16:20 -03:00
Francisco Solla
508350fcf1 chore(plan-issue): formata o rascunho do OPS44 (prettier) 2026-08-12 14:12:52 -03:00
Francisco Solla
057eebfbb1 plans(OPS44): redesenhar fluxo de registros/compartilhados para menos conflitos (Related #713) 2026-08-12 14:12:52 -03:00
Francisco Solla
7e05dc925d feat(C131): Salvador como opção agregada no dropdown de municípios das células de capacidade
O dropdown de adição de municípios (pessoas ×3, assessores, lideranças,
dobradinhas) oferece 'Salvador — Todas as zonas' como primeiro hit da busca:
um toque adiciona as 19 ZEs num lote, via máquina existente do RelationChipCell
(delta otimista, cap/floor, undo, colapso 'Salvador (19)' B178, exact-match C116).

- searchMunicipalityPortfolio ganha kind 'city' (NORMALIZED_ZONE_CITIES,
  derivado do catálogo) com gate de escopo por contagem: o agregado só aparece
  quando a cidade inteira está no escopo addableIds do ator — a label 'Todas as
  zonas' nunca mente para assessor com carteira parcial.
- hitDescription ganha o branch 'Todas as zonas'; sem migration/access/schema.
- Testes: unit busca pura (19 ids, soma, escopo parcial, tudo atribuído,
  posição), cell render, e2e em campaignPeople (agregado → chip → 19 zonas no
  DB).
2026-08-12 14:02:34 -03:00
Francisco Solla
a6d5a2695c
Merge pull request #727 from fsolla/ci-remove-migration-lock
chore(ci): remove o migration-lock — deadlock de CI com PRs de schema paralelos
2026-08-12 13:58:35 -03:00
Francisco Solla
4626a2ea06 chore(ci): remove o migration-lock — deadlock de CI com PRs de schema paralelos (checks passa a ser o único required)
O check falhava quando >1 PR aberto tocava src/migrations/ + payload-types.ts,
deadlockando AMBOS os PRs (9 de 13 runs de CI falhos desde 11/08). A validação
real da cadeia de migrations já roda em todo PR e na main: pnpm migrate +
test:int (incl. campaignMigrationReconciliation). Pool mantém a serialização
de spawn de issues needs:migration via countOpenSchemaPrs.
2026-08-12 13:42:05 -03:00
Francisco Solla
1326d54413 style(OPS30): prettier no changelog pós-rebase 2026-08-12 13:34:42 -03:00
Francisco Solla
48088d8162 refactor(OPS30): fixes do /simplify — pessoas no prewarm, login com sub-budgets, retry da demanda via existência, B24 no helper do arquivo
Review 3× read-only (0 P1):
- setup: /campanha/pessoas faltava (campaignPeople); log do 1º hit
  quando o retry salva; comentário do budget ancorado no medido.
- login: sub-budgets 15+5+30 (antes 30+5+30 estourava o test timeout
  e credencial errada morria de timeout genérico); retry bifurca pela
  URL real (dashboard/fora do login/re-submit) — re-submit cego
  contra página que saiu do login falhava com fill na dashboard.
- :713: retry cego criaria duplicata que o UNIQUE do slug refuta —
  o retry consulta por título e navega ao detail quando o 1º POST
  criou mas o redirect se perdeu.
- B24: locator reusa visibleMunicipalityButton (escapeRegExp +
  fronteira de nome, 23/435 com prefixo) e ensureWideMunicipalityList
  re-rodado pós-reload (padrão do irmão dobradinhas).
- :1063: waitForResponse com timeout 5s para o toPass decidir.

Re-prova: campaignMunicipalities 22/22 (arquivo atual, B193+OPS29 do
main) ×3 consecutivos em dev a 2 workers (load 5-17).
2026-08-12 13:34:42 -03:00
Francisco Solla
02beea808b chore(OPS30): hardening e2e em dev — prewarm completo, login resiliente a compile-frio, fechamento do P4-L
- setup.e2e.spec.ts: prewarm GET 13→26 rotas (rota /editar, alvos de
  redirect, wizards, PWA artifacts, rotas do chat Sollinha) + POST
  ganhou next-steps, home-search, ai-chat, ai-transcribe; prewarmGet
  com retry-único (abort do compile aborta a conexão do próprio
  prewarm); budget explícito 420s no lugar do test.slow().
- campaignE2EFixtures.ts: campaign.login com retry-único do submit
  (re-fill+re-click com grace-wait do destino exato) — submit abortado
  pelo full-page reload do compile frio nunca navega.
- campaignMunicipalities.e2e.spec.ts: refactor mínimo por evidência —
  B24 (estágio largo B158 + toggle do trigger pós-reload), :713 (retry
  do server action de criação), :1063 (seed parity via db:seed:minimal
  — achado do OPS28 — + retry do suggest).
- P4-L fechado com evidência: campaignLeaderships ×3 dev + ×1
  E2E_PROD=1 (TECH-DEBT row 59 + TESTING.md caveat).
- Aceite: campaignMunicipalities 17/17 ×3 consecutivos em dev a 2
  workers (load 5-18).

Sem migration/access/Consent/UI.
2026-08-12 13:34:42 -03:00
Francisco Solla
d7acb5aa48 feat(C129): nome de legenda da dobradinha — campo ballotName + display discreto sob o nome (Closes #696) 2026-08-12 12:52:35 -03:00
Francisco Solla
9d330f48e4 merge(C115): rebase em main (C126 activityWhere, C124 tasks, C122 e2e estados, OPS40/41) — spec portado para runSync escopado 2026-08-11 22:48:14 -03:00
Francisco Solla
1fb62039d8 style(C115): format pós-rebase no CHANGELOG 2026-08-11 22:42:36 -03:00
Francisco Solla
6a83dbcc25 docs(C115): débito pós-review — corrida do snapshot + hermeticidade int (#694) 2026-08-11 22:42:05 -03:00
Francisco Solla
2f247d40a6 fix(C115): revisão /simplify — relógio com baseline de espelho, reopen sem re-cancelamento, hardening do webhook
- lastMirroredChangeAt (activity, migration): clock rule compara contra a
  última mudança espelhada, não updatedAt (task toggle não silencia edição
  pendente do Google)
- snapshot exclui ids que o motor deletou (removedEventIds separado do
  remoteEventIds): reopen pós-cancelamento não vira re-cancelamento fantasma
- cancelamento do Google respeita o relógio: cancel mais novo cancela; Teqo
  mais novo (reopen) limpa o trashed e recria
- escrita reversa sem req.user: autoria do registro correta + sem gate 403
  do deputado; hook de sync pula a própria escrita reversa (sem passada aninhada)
- canal: segredo rotaciona por criação; novo canal persistido antes do stop
  do antigo; identity fields read:false; create admin-only; leitura prefere
  linha configurada; pre-filter do webhook antes do bootstrap do Payload
- testes: all-day ida-e-volta, task-toggle, reopen, slug congelado, e2e dos
  caminhos 200, render tests do dialog/feed; asserções herméticas a
  atividades ambiente dos arquivos irmãos (DB compartilhado)
2026-08-11 22:42:05 -03:00
Francisco Solla
7629a505e0 feat(C115): edição pelo Google Calendar volta para o Teqo (motor bidirecional + webhook push)
- runSyncPass bidirecional: regra do relógio decide direção (Google mais novo
  aplica título/horário; Teqo re-asserta), cancelamento via showDeleted +
  snapshot lastSeenEventIds, registro no updates feed
- webhook público /campanha/agenda/google-webhook/[secret] fail-closed
  (secret + X-Goog headers constant-time), canal lazy sem cron (ensure em
  toda passada, renovação com id novo + stop do antigo)
- migration hand-written (campos de canal/snapshot em googleCalendarSync);
  mutationKind dedicado libera título via Google sem quebrar slug canônico
- UI: seção 'Edições pelo Google' no dialog + autor 'Google Calendar' no feed
- testes: unit (reverse mapping, validação webhook), int (motor bidirecional
  com stub, 10 cenários), e2e (webhook 404 fail-closed)
2026-08-11 22:38:53 -03:00
Francisco Solla
c69990a3f0 fix(C126): isola o escopo do espelho Google nos int specs (fim do flake paralelo) 2026-08-11 21:33:07 -03:00
Francisco Solla
c08be6c46a
Merge pull request #707 from fsolla/C125-c125-pessoas-pos-c117-ux-descoberta-de-ordenacao-no-mob
C125: Pessoas pós-C117 UX — descoberta de ordenação no mobile e facet de ausência completo
2026-08-12 00:04:50 +00:00
Francisco Solla
6d41517a05
Merge pull request #705 from fsolla/C122-c114-follow-up-cobertura-de-testes-dos-estados-do-espel
C122 — cobertura de testes dos estados do espelho Google (componente + e2e + action)
2026-08-11 23:49:39 +00:00
Francisco Solla
e3ffb9d8a7
Merge pull request #706 from fsolla/plans/plan-issue-39
docs(plans): B200 — Municípios mobile: encaixes dos cards (densidade, labels, avatares)
2026-08-11 23:42:01 +00:00
Francisco Solla
d004a2b0eb chore(plan-issue): formata o rascunho UI do B200 (prettier) 2026-08-11 20:39:45 -03:00
Francisco Solla
4ad644d8ca
Merge pull request #702 from fsolla/B199-sollinha-fechar-o-drawer-durante-streaming-pode-reabri-
fix(B199): fechar o drawer mid-stream persiste open:false imediato — reload não ressuscita
2026-08-11 23:37:59 +00:00
Francisco Solla
7fabb72df8 feat(C125): facet de ausência completo (qualquer_ausencia) e descoberta de ordenação no mobile
- PEOPLE_ABSENCES ganha o 4º valor qualquer_ausencia ("Qualquer ausência"),
  a união exata das três ausências em UM chip — o 3º clique dos específicos
  deixa de colapsar o facet (3 de 4 membros) e o parse absorve o guarda-chuva
  sobre os específicos, sem tocar no B18 genérico de capacity/status
- peopleListSortPrimaryOptions: seeds do grupo Ordenação viram as 7 direções
  primárias com emptyQueryVisible, então o mobile descobre as 7 chaves ao
  tocar no bar (e "ordenar" não corta aliada/assessorado/base no cap 8)
- CampaignListOmnibox reabre o popover no mousedown: clicar num input já
  focado (logo após commitar busca com Enter) não disparava focus, deixando
  a descoberta morta no mobile até digitar
- tests: contrato de URL (absorção + não-colapso), predicado + AND, novo spec
  de omnibox, e2e do 3º clique e da descoberta mobile (390×844)
2026-08-11 20:34:51 -03:00
Francisco Solla
980f66983a docs(plans): B200 — Municípios mobile: encaixes dos cards (densidade, labels, avatares) 2026-08-11 20:34:13 -03:00
Francisco Solla
d6dfdf70ed fix(OPS41): resolve marcadores de conflito commitados no AGENTS.md + guardrail anti-reincidência (#689)
O commit OPS33 (f0f5b73f) entrou com o arquivo já conflitado na região do
bullet 'Per-worktree environments' (<<<<<<< HEAD indentado + lado HEAD
pré-OPS33 com artefato '- # ' + fechamento corrompido '> > > > > > >'; o
======= foi removido no próprio commit) e o OPS37 herdou o estado.

Resolução perda zero validada por diff: manter o lado superset (OPS28 +
OPS31 + OPS33 — claim determinístico, --issue N, prompt citado) e
descartar o lado HEAD; -3 linhas no total; scan de todos os trackeados
confirmou AGENTS.md como único arquivo afetado.

Guardrail: describe 'no conflict markers in committed files' em
codebaseConventions.unit.spec.ts — varre git ls-files e falha em
<<<<<<< / >>>>>>> (tolerante a indentação) e na forma corrompida
^\s*(> ){7}; validado negativo contra o estado sujo (AGENTS.md:37, 40).
2026-08-11 20:23:50 -03:00
Francisco Solla
5940c4c61e fix(OPS40): pnpm dev honra o PORT do .env.local — next dev -p via scripts/dev.mjs (Closes #682)
next dev resolve a porta antes de @next/env carregar env-files (commander
.env('PORT') no bin/next), então o PORT=3100+slot do provisionador de
worktree era ignorado e todo dev server subia em 3000. pnpm dev/devsafe
passam por scripts/dev.mjs: preflight do guard-dev-db inline, env com a
mesma precedência (real > .env.local > .env, override:false) e porta como
flag CLI -p (resolveDevPort/nextDevArgs em scripts/lib/cli.mjs,
unit-testados). Sem PORT → nenhum -p → default 3000 + allowRetry intactos.
2026-08-11 20:04:07 -03:00
Francisco Solla
1ed585a084
Merge pull request #700 from fsolla/plans/plan-issue-38
docs(plans): C128–C132 — ajustes de Pessoas (capacidades, legenda, tabela, Salvador, criação rápida)
2026-08-11 22:59:53 +00:00
Francisco Solla
1a71e5d021 docs(plans): C128–C132 — ajustes de Pessoas (capacidades, legenda, tabela, Salvador, criação rápida) 2026-08-11 19:54:13 -03:00
Francisco Solla
c7c9556ff3 test(C122): cobertura dos estados do espelho Google — unit do chrome, e2e dos 4 estados + FAB, int da action de desativar 2026-08-11 19:43:19 -03:00
Francisco Solla
54205fb093 fix(B199): fechar o drawer mid-stream persiste open:false imediato — reload não ressuscita (Closes #672) 2026-08-11 19:41:48 -03:00
Francisco Solla
631635ee89 chore(plan-issue): rascunho UI/UX do gate vira HTML+Tailwind commitado no repo (ad-hoc) 2026-08-11 19:14:21 -03:00
Francisco Solla
b604157fc7
Merge pull request #685 from fsolla/OPS39-prewarm-e2e-dev-adicionar-campanha-home-search-post-e-
OPS39 — Prewarm e2e: home-search POST e mudar-tendencia GET no setup spec
2026-08-11 20:14:51 +00:00
Francisco Solla
4043573152 fix(C118): fecha describe do e2e campaignPeople (merge com C117) e formata changelog 2026-08-11 16:32:49 -03:00
Francisco Solla
c0ffc22026 feat(C118): página de detalhe da pessoa — seções montadas pelas capacidades (Closes #657) 2026-08-11 16:32:21 -03:00
Francisco Solla
94c89a57a1 feat(C124): tarefa de atividade só com texto, responsável e concluída (sem prazo) 2026-08-11 16:05:07 -03:00
Francisco Solla
15461d7abf
Merge pull request #679 from fsolla/B197+-assessores-menu-do-picker-de-colunas-derivado-da-mesma
B197+: picker de colunas de assessores derivado da mesma fonte do AdvisorsTable
2026-08-11 18:53:09 +00:00
Francisco Solla
fdca8ff9c4 format: prettier no impl plan OPS39 2026-08-11 15:47:12 -03:00
Francisco Solla
21b4b4d0c6 fix(gate): seleção e2e contendo apenas o spec setup vira none (setup é dev-only; OPS39) 2026-08-11 15:47:12 -03:00
Francisco Solla
2fec6a407e test(OPS39): prewarm POST /campanha/home-search e GET /campanha/acoes/mudar-tendencia no setup e2e (Closes #645) 2026-08-11 15:47:12 -03:00
Francisco Solla
a8414a49f1 Merge origin/main into C117 (C116/C119/OPS40) — sortable heads sobre células editáveis 2026-08-11 15:43:23 -03:00
Francisco Solla
421a680402
Merge pull request #683 from fsolla/plans/plan-issue-40
plans(OPS40): worktree dev — PORT do .env.local não honrado pelo next dev
2026-08-11 18:27:28 +00:00
Francisco Solla
f1405b04a5
Merge pull request #680 from fsolla/C119-status-de-apoio-lembranca-em-liderancas
C119 — Status de apoio 'Lembrança' em lideranças
2026-08-11 18:25:18 +00:00
Francisco Solla
7768b366f9 merge: main into B197+ (CHANGELOG C116) 2026-08-11 15:24:56 -03:00
Francisco Solla
2ef0d38dd9 plans(OPS40): PORT do .env.local não é honrado pelo next dev no worktree 2026-08-11 15:24:33 -03:00
Francisco Solla
9e5637fa7c plans(C125): débitos UX pós-C117 — descoberta de ordenação mobile e facet de ausência completo (depends 656) 2026-08-11 15:23:45 -03:00
Francisco Solla
a83bedd7a8 feat(C116): pessoas — edição onde você vê na lista desktop (células sempre input, chips territoriais internos, nome-link, tooltips nas ações) 2026-08-11 15:09:56 -03:00
Francisco Solla
45e687bf32 merge: main into B197+ (CHANGELOG B198) 2026-08-11 15:05:06 -03:00
Francisco Solla
6e5a293781 merge: main into B197+ (CHANGELOG) 2026-08-11 14:54:36 -03:00
Francisco Solla
5fa4e22c9d feat(C119): status de apoio 'Lembrança' em lideranças — enum, badge e exclusão de pendências 2026-08-11 14:52:49 -03:00
Francisco Solla
6011efcee7 chore(B197+): picker de colunas de assessores derivado da mesma fonte do AdvisorsTable (#642) 2026-08-11 14:40:24 -03:00
Francisco Solla
aa8fe582c0 feat(B198): drawer mobile do Sollinha fecha ao navegar por link interno
O renderer de links do chat decide interno vs externo (B187/B188); a decisão
pura de fechamento mora no owner src/lib/ai/markdownLinks.ts
(shouldCloseDrawerOnLinkClick: interno && mobile && clique simples) e o
markdownComponents estatico virou useMemo no corpo do CampaignAIChat para
alcancar o contexto. Desktop inalterado; links externos/modos de nova aba
mantem o drawer da aba de origem; sessao e restore intactos (close persiste
open:false).

- unit: 12 testes da decisao pura
- e2e (modo CI, E2E_PROD=1): interno fecha + navega sem load + reload nao
  reabre; externo abre popup e mantem o drawer
- debito B199/#672 (depends #660): race de persistencia ao fechar mid-stream
2026-08-11 14:29:29 -03:00
Francisco Solla
9789bf36e6
Merge pull request #673 from fsolla/C114-google-calendar-agenda-da-campanha-espelhada-com-notifi
feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
2026-08-11 17:17:17 +00:00
Francisco Solla
d56f2250c6
Merge pull request #675 from fsolla/D12-flake-e2e-getbylabel-atividade-relacionada-resolve-2-ele
fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (D12 #641)
2026-08-11 17:11:46 +00:00
Francisco Solla
70070db8dc feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
Espelho oficial da agenda em calendário Google compartilhado via API
(service account, jose + fetch): reconciliação por passada com id
determinístico teqo+base32hex (upsert idempotente, sem estado por evento),
hooks afterChange/afterDelete na activity (só campos espelhados, nunca
lança), auto-resync quando calendarId muda, retry na view da agenda,
pill de estado no header + FAB + diálogo (4 estados). Cancelado no Teqo
deleta o evento no Google. Sem participantes no v1 (decisão de produto).
Sem migration nova de tabela (hand-written). Closes #635
2026-08-11 13:53:51 -03:00
Francisco Solla
b42de3e7ec feat(C117): ordenação por coluna (header + omnibox) e filtros de ausência em Pessoas
- sort/dir/ausencia no contrato de URL de /campanha/pessoas (molde territórios/B15)
- sortPeopleRows sobre o conjunto filtrado, nulls no fim, tiebreak nome→contactID
- PeopleSortableHead (CampaignSortableHead) com aria-sort nas 7 colunas visíveis
- omnibox: grupo Ordenação (chave×direção) e facet Ausência (Sem assessor/base/contato)
- clear preserva o sort; tests unit + e2e; sem migration/collection
2026-08-11 13:33:21 -03:00
Francisco Solla
a740f1eb16 fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (flake pré-existente, precedente cd469857)
D12 (#641): strict-mode violation (2 triggers idênticos) só em runs
completos sob carga. Estado estável tem 1 match (verificado em browser);
o 2º match é duplicação transitória RSC-pending, mesma classe de cd469857.
Mesmo alvo em campaignActivity:52 (único outro uso do label em e2e).
2026-08-11 13:21:21 -03:00
Francisco Solla
be4bb49da9 plans(C123,C124): agenda — modal central como única superfície de criar/editar e tarefa sem data/hora 2026-08-11 13:17:05 -03:00
Francisco Solla
c83bd779d2
Merge pull request #665 from fsolla/C112-multiplos-telefones-por-pessoa
C112 — Múltiplos telefones por pessoa
2026-08-11 14:27:47 +00:00
Francisco Solla
b025361959
Merge pull request #650 from fsolla/D10-d10-auto-cura-de-leftovers-de-consent-supporter-abba-do-
D10 — Auto-cura de leftovers de consent/supporter + ABBA do shared-write do lease de consent (pós-D9)
2026-08-11 13:59:54 +00:00
Francisco Solla
442378d7c8
Merge pull request #654 from fsolla/OPS36-e2e-journeys-sessao-compartilhada-arquivos-pesados
OPS36 — E2E: journeys com sessão compartilhada nos arquivos pesados (amortizar overhead por teste)
2026-08-11 13:48:29 +00:00
Francisco Solla
6de8fad8f6 feat(C112): múltiplos telefones por pessoa — Contact.phones array (ordem = prioridade)
- Contact.phone (text único) → phones (array field Payload, subcampo value):
  fonte única; principal = phones[0]; beforeValidate normaliza, filtra vazios,
  rejeita inválido e duplicado DENTRO da ficha (C111 entre fichas preservado)
- migration add_contact_phones_array: backfill phone → phones[0] (gen_random_uuid)
  antes do drop da coluna + índice em value (hand-edit documentado; snapshot .json
  gerado mantém as colunas C99/C100 já existentes)
- primaryPhoneOf / phoneValuesOf / reorderWithPrimaryPhone em src/lib/phone.ts
  (ordem = prioridade; inline edit = seta o principal preservando o resto)
- findOrCreateContactByPhone aceita phones[] (dedupe pelo primário; reuse faz
  append dos faltantes; locks em todos os números digitados)
- sync conta→ficha (CampaignUser): phone da conta faz APPEND quando a ficha não
  tem (nunca reordena nem remove); tombstone LGPD substitui TODOS os telefones
- convite redemption: telefone digitado vira principal, demais preservados
- import CSV: preview sinaliza compartilhado também por número secundário;
  bulk insere contact_phones (JOIN nas leituras, uuid no insert)
- busca por telefone (apoiadores, pickers, combobox) casa qualquer número
- UI: PhonesFieldEditor compartilhado (repetível mascarado, remover, ↑/↓,
  modo inline com Salvar) nas 5 superfícies — liderança (form/ficha/lista),
  apoiador (form/ficha), dobradinha (ficha), assessor (ficha), wizard
- UI pública (petição/WhatsApp) intocada: zod e forms ficam com phone;
  as ações mapeiam phone → phones[0]
- testes: spec novo campaignMultiPhones (aceite completo), specs C111/C99
  atualizados, e2e wizard/apoiador ajustados
2026-08-11 10:47:02 -03:00
Francisco Solla
d23433a39a plans(C119,B198): status Lembrança em lideranças e Sollinha fecha o drawer ao navegar em mobile 2026-08-11 10:44:53 -03:00
Francisco Solla
29ce38bd29 Merge remote-tracking branch 'origin/main' into OPS36-e2e-journeys-sessao-compartilhada-arquivos-pesados
# Conflicts:
#	docs/CHANGELOG-AGENTS.md
2026-08-11 10:31:04 -03:00
Francisco Solla
62335084a1
Merge pull request #658 from fsolla/plans/plan-issue-35
plans(C116,C117,C118): lista de pessoas — edição onde você vê, ordenação/filtros de ausência e detalhe por capacidades
2026-08-11 13:25:14 +00:00
Francisco Solla
3af97e960f plans(C116,C117,C118): lista de pessoas — edição onde você vê, ordenação/filtros de ausência e detalhe por capacidades 2026-08-11 10:23:10 -03:00
Francisco Solla
3df0b16a66 docs(OPS39): plano do prewarm registrado como Issue #645 (depends OPS30) 2026-08-11 09:40:14 -03:00
Francisco Solla
a0fe13c3d1 chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login) e 4 lookups do strip de ações com .first()
  (duplicado transitório RSC)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 09:35:14 -03:00
Francisco Solla
c81a439d36 merge: origin/main (C113/OPS38 changelog, wizard chrome) 2026-08-11 09:01:13 -03:00
Francisco Solla
5577d31c38 fix(C113): feed iCal da agenda vivo — contrato de frescor no lugar do snapshot congelado
Spike no build de produção provou que o Next 15 não congela route handlers GET
(rota dinâmica por default); o freeze era o contrato de resposta
(public, max-age=3600 sem validadores) permitindo caches compartilhados
reterem o snapshot do primeiro fetch. Fix: force-dynamic explícito +
buildICalFeedResponse com public no-cache, ETag forte sha256, Last-Modified,
304 só por If-None-Match (nunca por data — blind spot de exclusão), dica
X-PUBLISHED-TTL no corpo do VCALENDAR (RFC 7986) e 404 no-store para
revogação fail-closed no CDN. Testes unit/int/e2e (dev + prod build).
2026-08-11 08:42:42 -03:00
Francisco Solla
e4c73803cd
Merge pull request #648 from fsolla/B196-municipios-mobile-omnibox-colado-ao-header-e-card-mais-
B196 — Municípios mobile: omnibox colado ao header e card mais denso
2026-08-11 11:24:10 +00:00
Francisco Solla
5817b3a74b merge: origin/main (CHANGELOG header + B197/OPS35+ entries) 2026-08-11 08:06:34 -03:00
Francisco Solla
643338888d
docs(OPS38): corrige header e ordem do topo do changelog — squash do #646 sobre main avançado re-clobberou (#649)
- PR #646 squash-merged sobre main com 4 merges em voo que tocavam o CHANGELOG (push #634, OPS37 #624, OPS35+ #638, B197 #639) — resolução do GitHub reproduziu o defeito da Issue: header perdido + topo fora da ordem canônica
- Reconstrução: header da base boa (0b520fdb) + 8 entradas pós-base na ordem de merge (B197, OPS35+, Push, OPS37, D9, B195, OPS33, OPS35) + base intacta
- Verificação: diff vs 0b520fdb = +8/−0; 128 entradas; md5 das 8 vs fontes OK; format:check + gate:fast OK
- Impl: docs/plans/changelog-agents-truncado-ops35-ops33-impl.md (segunda passada)
2026-08-11 11:04:26 +00:00
Francisco Solla
0a9da04c92
Merge pull request #647 from fsolla/D11-escala-dry-chrome-comum-dos-passos-finais-do-wizard-pos-
D11 — Escala/DRY: chrome comum dos passos finais do wizard (pós-B195)
2026-08-11 10:59:13 +00:00
Francisco Solla
02abd7de89 feat(B196): omnibox colado ao header e card mobile mais denso (Closes #607)
Chassis de lista (vale para todas as listas, gate A):
- scrollport: max-md:has-[.campaign-list-omnibox-form]:pt-0 — o containing
  block do sticky passa a começar no topo; a barra cola no header (sem vão
  de 16px, nada passa por cima no scroll)
- barra densa: campo min-h-8 py-0 + X size-8 → ~40px no total; anel de
  foco escopado a md (mobile sem anel de cor; caret é o indicador)
- trailing cluster desktop-only por padrão (edgeToEdge removido)
- pt-4 md:pt-0 nas 6 listas com linha de ações antes do form (incl. organizacoes)

Card mobile (B193 densificado):
- nome 18px leading-tight com território a ~2px
- chips como blocos label-acima (Classe/Tendência/Nível, gate B) via ChipBlock
- avatares numa linha por grupo com overlap proporcional (flex-1 cells,
  sem cap/wrap/…, overflow-hidden nas bordas) — prop wrap → overlapRow
- MissingAdvisorBadge com wrap para não clipar na célula do grupo
- densidade vertical gap-2.5/py-3; toques min-h-11 preservados

e2e B196 (6 testes) + regressão B184/B183/B193 verde.
2026-08-11 07:55:50 -03:00
Francisco Solla
1f68e543c5
docs(OPS38): restaura CHANGELOG-AGENTS truncado — 120 entradas de volta + re-anexa OPS35/OPS33/B195/D9 (#646)
- Merges #621 (OPS35) e #623 (OPS33) clobberaram docs/CHANGELOG-AGENTS.md de 246 para 12 linhas (resolução de conflito pegou cópia velha)
- Base boa: 0b520fdb (merge OPS28, PR #614) — 246 linhas, 120 entradas, byte-idêntica a 0047d263 e ao pai do merge OPS35
- Re-anexadas verbatim no topo (ordem de merge): D9 (#630), B195 (#622), OPS33 (#623), OPS35 (#621)
- OPS34/OPS28/B193 já estavam na base boa (md5 idêntico) — não duplicadas
- Diff vs base boa: +4 entradas, 0 remoções; grep: 124 entradas, cada código 1x
- Impl: docs/plans/changelog-agents-truncado-ops35-ops33-impl.md
2026-08-11 10:47:27 +00:00
Francisco Solla
07dd68511f chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 07:41:14 -03:00
Francisco Solla
8d007285d5 chore(D10): auto-cura de leftovers de consent/supporter + janela missing-consent exclusiva (12/12 sob 6 workers)
F1: purgeOrphanedConsentRenames sob lease exclusivo (autocommit) nos 3
pontos que tocam a key canônica; renames de teste migrados para chaves
privadas; F1b: purge de contatos órfãos no purgeMunicipalityResidue.
F2 (medição): shared-writer ABBA refutado; janela do missing-consent
exclusiva + Onda0 atômico (down transacional) + provision exclusivo.
Contraprova: 7 arquivos da key x 6 workers = 12/12 verdes (baseline 1/4).
2026-08-11 07:41:04 -03:00
Francisco Solla
0c952e5824 plans(B197+): escala-dry-pos — picker de assessores derivado do AdvisorsTable (débito pós-B197) 2026-08-11 07:39:17 -03:00
Francisco Solla
06bf1cad0c chore(D11): chrome comum dos passos finais do wizard — WizardStepFormChrome + DemandFields 2026-08-11 07:37:44 -03:00
Francisco Solla
170ce59ef4
Merge pull request #640 from fsolla/C111-telefone-nao-unico-duas-pessoas-podem-compartilhar-o-me
C111 — Telefone não único: duas pessoas podem compartilhar o mesmo celular
2026-08-11 03:58:55 +00:00
Francisco Solla
162a518416 feat(C111): telefone não único — duas pessoas podem compartilhar o mesmo celular
Contact.phone deixa de ser único em enforcement (nunca houve constraint DB):
- remove o hook enforceUniqueContactPhone e os asserts assertContactPhone*
  (conflicts/ambiguity) — os 5 fluxos de escrita de pessoa salvam com número
  compartilhado; rename contactPhoneInvariant.ts -> contactPhoneLocks.ts
  (locks passam a significar serialização, não unicidade)
- findOrCreateContactByPhone: 0 fichas cria, 1 reusa (dedupe C6/C99),
  2+ cria ficha nova (a identidade da pessoa em criação é a ficha nova)
- convite autofill/login: ficha ancorada pode compartilhar o phone; o lado
  conta permanece fail-closed (username DB-unique; check de 2+ contas)
- import CSV: preview sinaliza telefone_compartilhado (resolução manual);
  confirm/bulk abortam fail-closed se um phone ganhar 2+ fichas (race)
- fix de achado de execução: supporter_import_batch.actor_id NOT NULL com
  FK ON DELETE set null derrubava o delete de campaignUser com batch
  órfão (23502) — CampaignUser.beforeDelete limpa batches + fixture
  cleanup descobre/deleta batches dos accounts owned
2026-08-10 23:03:05 -03:00
Francisco Solla
419ba39ecc
Merge pull request #639 from fsolla/B197-ocultar-a-coluna-de-e-mail-por-padrao-nas-listas-de-pes
B197 — Ocultar a coluna de e-mail por padrão nas listas de pessoas
2026-08-11 01:39:57 +00:00
Francisco Solla
02a33f1bc0
Merge pull request #638 from fsolla/OPS35+-restaurar-pin-de-visibilidade-css-das-colunas-de-rede
test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios
2026-08-11 01:23:44 +00:00
Francisco Solla
e75148f3c4
Merge pull request #634 from fsolla/push-optin-automatico-fix-prompt-nativo
feat(push): opt-in automático e fix do prompt nativo no /campanha
2026-08-11 01:07:15 +00:00
Francisco Solla
3e9390230b feat(B197): e-mail oculto por padrão nas listas de pessoas — default B17 nas 5 listas + coluna própria em pessoas/apoiadores + picker em assessores 2026-08-10 21:57:52 -03:00
Francisco Solla
e86331fe4d
Merge pull request #624 from fsolla/OPS37-container-postgres-compartilhado-e-recriado-por-outro-
fix(OPS37): db:start idempotente — não recria o container Postgres compartilhado
2026-08-11 00:54:38 +00:00
Francisco Solla
bfe3bf6b19
Merge pull request #637 from fsolla/plans/plan-issue-33
plans(C114,C115): Google Calendar na agenda — espelho com notificação + edição bidirecional
2026-08-11 00:39:32 +00:00
Francisco Solla
21463f52c4 format: prettier nos planos C114/C115 2026-08-10 21:36:35 -03:00
Francisco Solla
80d8b67551 plans(C114,C115): integração Google Calendar da agenda — espelho com notificação e edição bidirecional 2026-08-10 21:30:40 -03:00
Francisco Solla
91ae6b5408 test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios (Closes #616) 2026-08-10 21:28:52 -03:00
Francisco Solla
c4212b3e03 feat(push): opt-in automático e fix do prompt nativo no /campanha
- requestPermission agora é chamado sincronamente no clique: dentro do
  startTransition(async ...) o navegador perdia a user activation e o Chrome
  auto-negava silenciosamente ("Permissão de notificação negada" sem prompt)
- CampaignPushOptInToast no shell (app): staff com permission 'default', sem
  inscrição ativa, consentimento publicado e VAPID configurado; dismiss em
  localStorage; iOS só com app instalado
- cerimônia de ativação/desativação extraída para
  src/utilities/campaignPushClient.ts, compartilhada com o card de perfil
- mensagens distintas para denied (reativar nas configurações do site) vs
  default (prompt não exibido); pin de top-level registrado
- CHANGELOG-AGENTS.md: entrada única
2026-08-10 21:14:56 -03:00
Francisco Solla
c4865135b4
Merge pull request #633 from fsolla/C110+-ux-pos-critique-refinamentos-do-preview-do-arrasto-da-
C110+ — UX pós-critique: preview do arrasto — indicadores ancorados ao frame, gradiente deduplicado, e2e prev
2026-08-11 00:14:04 +00:00
Francisco Solla
6ef008b92f plans(C113): feed iCal da agenda congela na criação do link — plano de intenção 2026-08-10 21:02:11 -03:00
Francisco Solla
48707d0bff
Merge pull request #630 from fsolla/D9-d9-flake-do-consent-restore-do-testdatabaselease-sob-carg
fix(D9): flake do consent restore do testDatabaseLease sob carga — janelas leaseadas, Onda0 com restore de ids, asserções por snapshot
2026-08-10 23:45:52 +00:00
Francisco Solla
7c17d43723 fix(OPS37): db:start idempotente — não recria o container Postgres compartilhado (no-op quando saudável; --force-recreate explícito) 2026-08-10 20:40:36 -03:00
Francisco Solla
9cb8fef98d feat(C110+): preview do arrasto — indicadores ancorados ao frame, gradiente deduplicado, e2e do direction prev
- scene posicionado único (.activity-agenda-swipe-scene) envolve frame + dots/bars:
  percentuais resolvem contra o box do frame, não o content (chevron/label inclusos)
- width: 100% no scene (fix do /simplify: align-items flex-end do prev colapsava a 1px)
- --swipe-frame-columns deduplica o gradiente de 14.285% (2 call sites)
- unit test estrutural do preview (month/day/week/list, chevron por direção)
- e2e novo: direction prev com frame visível (pina o fix do colapso)
2026-08-10 20:38:04 -03:00
Francisco Solla
f44254cea5
Merge pull request #628 from fsolla/plans/plan-issue-30
plans(C111,C112,B197): telefone não único, múltiplos telefones, e-mail oculto por padrão
2026-08-10 23:30:46 +00:00
Francisco Solla
b19c567b41 plans(C111,C112,B197): telefone não único, múltiplos telefones por pessoa, e-mail oculto por padrão nas listas 2026-08-10 20:26:13 -03:00
Francisco Solla
9e2b22ef86 fix(D9): flake do consent restore sob carga — janela leaseada, Onda0 shared com restore de ids, asserções por snapshot
- withMissingInviteConsentFixture serializa a janela de operação sob lease
  (serializeWindow default true; teste de ensures concorrentes migrou para
  key/lease privada; re-verificação/re-delete do gap setup→janela)
- spec Onda0 volta a leases shared (exclusivo causava ABBA com escritores de
  invite) e o down-migration restaura as linhas com os ids originais
  (restoreConsentRows compartilhado; no-op para o resto da suíte)
- asserções de restore referenciam o snapshot do fixture (contrato real);
  rollback asserta id+key; ORDER BY/sort nas leituras limit-1
- contraprova: suíte int completa ×5 + 6 arquivos da key ×13/14 verdes
2026-08-10 20:23:39 -03:00
Francisco Solla
fda91c768d docs(B195): entrada do changelog no topo (após rebase) 2026-08-10 20:16:39 -03:00
Francisco Solla
712d40f83f feat(B195): finalizar wizard Registrar pedido — campo único + título derivado por IA + edição no detalhe 2026-08-10 20:15:05 -03:00
Francisco Solla
f460dac6cf
Merge pull request #623 from fsolla/OPS33-worktree-next-claima-deterministicamente-a-issue-e-abr
OPS33: worktree next claima deterministicamente a Issue (--issue N, reopen) e launch com /work-issue --issue N
2026-08-10 23:08:33 +00:00
Francisco Solla
f0f5b73f3f OPS33: worktree next claima deterministicamente a Issue (--issue N, reopen) e launch com /work-issue --issue N
Claim extraído para scripts/lib/agent-github.mjs (claimIssue/claimBriefLines/
claimQueueEntry/claimTargetVerdict), agent-claim.mjs vira CLI fino com mesma
saída; branch validada antes do claim (sem claim órfão); hint de falha
pós-claim; guard -B contra reset de commits de sessão; diretiva launch com
prompt citado + tokenização xargs no worktree.sh; --issue sem valor/posicional
morrem com motivo (sem misclaim); unit novos (directive com issueNumber,
claimQueueEntry, claimTargetVerdict); docs/textos (AGENTS.md, /worktree, skill
worktree-next-issue, AGENT-OPS, CHANGELOG).
2026-08-10 19:46:54 -03:00
Francisco Solla
1f31d1b628 test(OPS35): modo e2e full-stack sem browser (paradigma HTTP) + migração da família Territórios (Closes #600) 2026-08-10 19:30:29 -03:00
Francisco Solla
b4278f1632
Merge pull request #620 from fsolla/C110-agenda-mobile-feedback-visual-do-arrasto-reveal-do-peri
plans(C110+): UX pós-critique do preview do arrasto da agenda
2026-08-10 22:24:54 +00:00
Francisco Solla
e3e8d3fb4a plans(C110+): débitos pós-critique do preview do arrasto — chevron, dots do mês, gradiente 2026-08-10 19:22:39 -03:00
Francisco Solla
0047d2637c ops28(OPS28): worktrees rodam o seed mínimo no provisionamento — e2e local espelha a CI
- scripts/worktree.mjs: provision() aplica migrate + db:seed:minimal nos
  bancos dev e test (paridade com a CI, que roda o par antes do e2e);
  --no-migrate pula os dois (o seed depende do catálogo migrado); fallback
  sem Docker também migra+seeda o teqo/teqo_test compartilhado (com warning
  de sobrescrita dos pins).
- Guard de regressão tests/e2e/seedParity.setup.e2e.spec.ts no projeto setup:
  falha cedo com mensagem clara se o banco de teste não estiver seedado;
  evidência = users do seed (MINIMAL_CAMPAIGN_USERS), não os pins de priority
  — o cleanup dos fixtures e2e reseta priority por round-robin persistente.
- scripts/lib/test-affected-core.mjs: scripts/worktree.mjs em HIGH_RISK_EXACT.
- Docs: AGENTS.md, skill local-database, CHANGELOG-AGENTS.md, impl plan.

Closes #584
2026-08-10 16:11:27 -03:00
Francisco Solla
91a0aca5f0
Merge pull request #613 from fsolla/OPS34-ci-e2e-paralelizar-o-job-cadeia-serial-so-em-dev-shard
refactor(OPS34): fixes do /simplify — fallback e2e_shards, timeout webServer, pin anti-drift
2026-08-10 15:58:10 -03:00
Francisco Solla
4ae8f96242 refactor(OPS34): fixes do /simplify — fallback e2e_shards, timeout webServer CI-only, pin anti-drift, docs 2026-08-10 15:37:11 -03:00
Francisco Solla
b9c0bf639b
Merge pull request #612 from fsolla/OPS34-ci-e2e-paralelizar-o-job-cadeia-serial-so-em-dev-shard
docs(OPS34): medição pós-merge do job e2e no changelog
2026-08-10 15:26:43 -03:00
Francisco Solla
2850733579 docs(OPS34): medição pós-merge do job e2e (shards 326/369s) no changelog 2026-08-10 15:25:06 -03:00
Francisco Solla
97be0c28d6
Merge pull request #611 from fsolla/OPS34-ci-e2e-paralelizar-o-job-cadeia-serial-so-em-dev-shard
chore(OPS34): e2e CI em 2 shards e cadeia de projetos dev-only
2026-08-10 15:07:51 -03:00
Francisco Solla
4baa617c01
Merge pull request #610 from fsolla/C110-agenda-mobile-feedback-visual-do-arrasto-reveal-do-peri
C110 — feedback visual do arrasto na agenda mobile (reveal do período adjacente + commit/snap-back)
2026-08-10 15:02:53 -03:00
Francisco Solla
cd76cbcb59
Merge pull request #609 from fsolla/OPS31-worktree-plan-abre-o-opencode-com-plan-issue-ja-enviad
OPS31 — Worktree plan abre o opencode com /plan-issue já enviado
2026-08-10 14:48:44 -03:00
Francisco Solla
3b81431d39 refactor(C110): simplify — helpers de dominância/clamp, seed e2e generalizado, CSS sem regras mortas 2026-08-10 14:42:54 -03:00
Francisco Solla
f955c6a7fe chore(OPS34): e2e CI em 2 shards e cadeia de projetos dev-only (Closes #599) 2026-08-10 14:39:39 -03:00
Francisco Solla
9dd8022b6a feat(OPS31): worktree plan abre o opencode com /plan-issue já enviado 2026-08-10 14:36:27 -03:00
Francisco Solla
0830653b8c
Merge pull request #608 from fsolla/plans/plan-issue-29
docs(plan): B196 — municípios mobile: omnibox colado ao header e card denso
2026-08-10 14:36:20 -03:00
Francisco Solla
536051bcd2 docs(plan): B196 — municípios mobile: omnibox colado ao header e card denso (pós-B193/B184) 2026-08-10 14:29:00 -03:00
Francisco Solla
9311c1623c
Merge pull request #541 from fsolla/C100-pessoas-lista-unificada-da-campanha-rota-campanha-pesso
feat(C100): lista unificada de pessoas em /campanha/pessoas
2026-08-10 14:22:57 -03:00
Francisco Solla
549d3f8167
Merge pull request #606 from fsolla/OPS29-busca-global-sem-sugestoes-mostra-regiao-em-branco-est
plans(OPS37): container Postgres compartilhado é recriado por outro worktree — mata dev+e2e em voo
2026-08-10 14:10:01 -03:00
Francisco Solla
cfa7c458b9 plans(OPS37): container Postgres compartilhado é recriado por outro worktree — mata dev+e2e em voo (Closes #605) 2026-08-10 13:58:45 -03:00
Francisco Solla
b23a323f83 feat(C110): feedback visual do arrasto na agenda mobile — reveal do período adjacente + commit/snap-back 2026-08-10 13:51:55 -03:00
Francisco Solla
cc7dff88c4 feat(C100): lista unificada de pessoas em /campanha/pessoas
Merge por Contact (uma linha por pessoa física) de lideranças,
dobradinhas e staff, com capacidades como colunas; omnibox com
capacidade/município/apoio + filtros salvos (2º call site do B18 via
factory createSavedFilterStore); apagar pessoa com manifest de
confirmação e cascata transacional; mobile edge-to-edge (omnibox sem
bordas, cards com divisórias, salvar filtro no header). Sem migration.

Closes #495
2026-08-10 13:45:46 -03:00
Francisco Solla
89e7292529
Merge pull request #580 from fsolla/OPS22-ci-main-e2e-da-agenda-mobile-vermelho-desde-o-merge-do
OPS22 — fix: settle do chat desktop não vaza drawer para a visita mobile da mesma aba (CI main / deploy desbloqueado)
2026-08-10 13:35:07 -03:00
Francisco Solla
0fefbd0490
Merge pull request #604 from fsolla/OPS29-busca-global-sem-sugestoes-mostra-regiao-em-branco-est
fix(OPS29): estado vazio honesto na busca global sem sugestões curadas
2026-08-10 13:31:03 -03:00
Francisco Solla
7d970dfce5 style(OPS22): prettier no CHANGELOG 2026-08-10 13:13:24 -03:00
Francisco Solla
5bc154dd20 fix(OPS22): settle do chat desktop não vaza drawer para a visita mobile da mesma aba — CI main verde (deploy desbloqueado) 2026-08-10 13:12:12 -03:00
Francisco Solla
fb7f85cd59 fix(OPS29): estado vazio honesto na busca global sem sugestões curadas (Closes #585)
- HomeSearchResultsShell renderiza 'Nenhuma sugestão ainda — priorize
  municípios na planilha de projeção' quando o suggest responde success
  sem hits (Início e FAB overlay compartilham o shell; SSR initialSuggest
  e POST cobertos); gateada por !isFetching para não falar sob o skeleton
- B126 e2e determinístico: pin um município priority alta claimado;
  testes OPS29 novos usam advisor sem carteira (scoped, imune a alta
  global de workers paralelos); settle de animação do overlay no B126
- unit: 3 casos novos no shell + overlap skeleton/mensagem
2026-08-10 12:54:16 -03:00
Francisco Solla
7b044ff6ef
Merge pull request #603 from fsolla/OPS32-work-issue-no-paradigma-de-issue-ja-claimada-model-sel
OPS32: work-issue no paradigma de Issue já claimada + model-selection só-Cursor
2026-08-10 12:53:36 -03:00
Francisco Solla
4e5f9e738e OPS32: work-issue no paradigma de Issue já claimada + model-selection só-Cursor
- work-issue reescrita como fluxo próprio do humano: sem claim/verificações
  (contexto via $ARGUMENTS, 1 pergunta de fallback, validação gh issue view,
  nunca claim); plano de intenção do body da Issue (contrato extractPlanPath)
- desacoplada de agent-work-issue; Passos 4-6 extraídos para
  execution-pipeline.md compartilhado com deltas por ator
- model-selection volta ao cardápio único Cursor (remove model-local e o
  mapeamento canônico model:->DeepSeek)
- correlatos: claim brief (metadata consultiva + footer sem stage), pool
  prompt, command description, AGENT-OPS, plan-issue, rebase-on-main,
  CHANGELOG-AGENTS
2026-08-10 12:50:18 -03:00
Francisco Solla
a7221cb711
Merge pull request #602 from fsolla/plans/plan-issue-e2e-velocidade
plans(OPS34-OPS36): acelerar o processo de verificação e2e
2026-08-10 12:32:44 -03:00
Francisco Solla
52ad9d8f85 plans(OPS34-OPS36): acelerar o processo de verificação e2e — paralelizar job CI, paradigma HTTP sem browser, journeys seriais 2026-08-10 12:29:42 -03:00
Francisco Solla
e20069ea1b
Merge pull request #598 from fsolla/D9-test-database-lease-consent-flake
docs(plan): D9 — flake do consent restore do testDatabaseLease sob carga
2026-08-10 12:10:42 -03:00
Francisco Solla
21056eb384
Merge pull request #597 from fsolla/plans/plan-issue-27
plans: OPS31-OPS33 — worktree plan envia /plan-issue, work-issue no paradigma claimado, next claima deterministicamente
2026-08-10 12:08:06 -03:00
Francisco Solla
5e339dc68b style(plan): prettier no plano D9 2026-08-10 12:07:55 -03:00
Francisco Solla
e8cc702ad3 docs(plan): D9 — flake do consent restore do testDatabaseLease sob carga 2026-08-10 12:06:37 -03:00
Francisco Solla
13a10d9069 plans(OPS31-OPS33): claim determinístico no worktree next + work-issue no paradigma claimado + /plan-issue no launch do plan 2026-08-10 12:05:02 -03:00
Francisco Solla
677f80c3f0
Merge pull request #592 from fsolla/plans/plan-issue-registrar-pedido
plan(B195): finalizar wizard Registrar pedido (A5) — campo único + título por IA
2026-08-10 12:03:40 -03:00
Francisco Solla
4dbba7c713 format(B195): prettier no plano 2026-08-10 12:01:39 -03:00
Francisco Solla
a97bf1a010 plan(B195): finalizar wizard Registrar pedido (A5) — campo único + título por IA 2026-08-10 11:59:06 -03:00
Francisco Solla
880235ce0f
Merge pull request #588 from fsolla/OPS27-worktree-new-worktree-generico-sem-funcao-pre-definida
OPS27: worktree new — worktree neutro sem função pré-definida
2026-08-10 11:57:48 -03:00
Francisco Solla
06d34ed23b OPS27: worktree new — worktree neutro sem função pré-definida (work/<bag>) 2026-08-10 11:24:52 -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
dc2f76e1a1
Merge pull request #590 from fsolla/plans/plan-issue-26
plans(C110): feedback visual do arrasto da agenda mobile
2026-08-10 11:19:32 -03:00
Francisco Solla
7f7eaa8e58 plans(C110): feedback visual do arrasto da agenda mobile (reveal do período adjacente + commit/snap-back) 2026-08-10 11:16:01 -03:00
Francisco Solla
4551a4e997
Merge pull request #583 from fsolla/OPS26-worktree-go-abre-o-opencode-com-pre-selecoes-deepseek-
OPS26: worktree next/plan no terminal abrem o opencode com presets (DeepSeek V4 Flash, auto-approve, skill inicial)
2026-08-10 11:11:56 -03:00
Francisco Solla
3550c1ba4f
Merge pull request #587 from fsolla/plans/plan-issue-23
plans(OPS28-30): flaky e2e — seed no worktree, estado vazio do suggest, hardening dev
2026-08-10 11:10:40 -03:00
Francisco Solla
b95682da9f plans(OPS28-30): flaky e2e — paridade de seed no worktree, estado vazio do suggest, hardening dev compile-frio (Related #584 #585 #586) 2026-08-10 11:07:25 -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
152d0a1473 OPS24: worktree CLI default-go (--stay escape) + kill volta ao main
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.
2026-08-10 10:21:08 -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
38e8beffba
Merge pull request #576 from fsolla/plans/plan-issue-22
docs(plan): lote OPS24-OPS27 — worktree CLI + comandos opencode
2026-08-10 09:14:04 -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
265c12f761 style(plan): prettier no plano OPS24 2026-08-10 09:11:58 -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
b70cbae513 docs(plan): lote OPS24-OPS27 — worktree CLI (go default, kill→main, launch opencode, new) + comandos opencode /work-issue e /plan-issue 2026-08-10 09:10:25 -03:00
Francisco Solla
a970f05c8a
Merge pull request #568 from fsolla/OPS23-ops18-action-de-promote-related-n-no-merge-falha-sempr
fix(OPS23): plan-issue-ready — disable package-manager-cache no setup-node
2026-08-10 08:56:57 -03:00
Francisco Solla
eb9787d695 fix(OPS23): plan-issue-ready — disable package-manager-cache no setup-node
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
2026-08-10 08:54:06 -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
9b1da7a09f
Merge pull request #565 from fsolla/B189-sollinha-municipios-sem-atualizacao-recente-cobertura-d
B189 — Sollinha: municípios sem atualização recente (cobertura do acompanhamento)
2026-08-10 08:27:39 -03:00
Francisco Solla
cdba6bfff7 docs(B189): impl plan — registro de execução (entrega + simplify + colisão B186) 2026-08-10 08:17:33 -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
1afe2aeed6 fix(D8): restaura entrada D6 truncada pelo rebase do C104 (revisão do /simplify)
- 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)
2026-08-10 08:11:39 -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
0acf29a638 docs(D8): restaura entradas C102/C108 perdidas do CHANGELOG e limpa lixo do rebase do C106
- 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
2026-08-10 08:08:11 -03:00
Francisco Solla
32b6929ddb
Merge pull request #563 from fsolla/plans/plan-issue-21
docs(OPS22): plano — CI main e2e da agenda mobile vermelho desde o B188 (deploy bloqueado)
2026-08-10 08:05:29 -03:00
Francisco Solla
f54552bc7f docs(OPS22): plano — CI main e2e da agenda mobile vermelho desde o B188 (deploy bloqueado) 2026-08-10 08:00:51 -03:00
Francisco Solla
f073fc48c9 style(OPS21): prettier-format impl plan 2026-08-10 07:52:16 -03:00
Francisco Solla
e54ed44fd7 docs(OPS21): REVALIDATE_SECRET na Vercel production — runbook /api/revalidate restaurado 2026-08-10 07:49:17 -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
c4591eedaa
Merge pull request #558 from fsolla/B185-sollinha-liderancas-pendentes-de-abordagem-assessores-r
docs(D8): plano — restaurar entradas B183/C102 perdidas do CHANGELOG
2026-08-10 07:25:00 -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
3be112a42e docs(D8): plano — restaurar entradas B183/C102 perdidas do CHANGELOG (merge do C104) 2026-08-10 07:23:23 -03:00
Francisco Solla
807b8fde61 style(B187): format (prettier) 2026-08-10 07:21:25 -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
fab834fc95 docs(B184): changelog entry (re-added after second rebase conflict) 2026-08-10 03:12:02 -03:00
Francisco Solla
426d14bbb2 docs(B184): agenda exception (C101 strip) after rebase 2026-08-10 03:11: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
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
1b92c3a305 docs(B193): remove dependência C109 — defect descartado como não-reproduzível em main 2026-08-10 02:07:50 -03:00
Francisco Solla
4fd39cc21c
Merge pull request #544 from fsolla/plans/plan-issue-19
docs(B193): plano de intenção — card de município mobile denso
2026-08-10 01:50:34 -03:00
Francisco Solla
01621e28ab docs(B193): plano de intenção — card de município mobile denso (edit-where-you-see + última atualização expandível) 2026-08-10 01:42:48 -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
d52d001613 style(C104): format (prettier) 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
a4d7b88375
Merge pull request #535 from fsolla/D6-push-no-dispositivo-funcionando-de-ponta-a-ponta-notifica
fix(D6): push no device ponta a ponta — SW dinâmico, subscribe idempotente, check-push-chain
2026-08-10 00:27:47 -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
944cb6bac1 docs(B191,B192): planos de intenção — ações rápidas de abertura no chat Sollinha (chips no estado vazio) e follow-ups na própria resposta (bloco no prompt, extraído pela UI, slot acima do input) 2026-08-10 00:08:45 -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
7576a3489e docs(B185,B186,B189,B190): formatação prettier dos planos 2026-08-09 23:37:04 -03:00
Francisco Solla
1e9693410b docs(B185,B186,B187,B188,B189,B190): planos de intenção — Sollinha gestão (lideranças pendentes, prioridades do momento, cobertura de atualização e de dobradinhas) + links com aparência de link + contexto por sessão da janela 2026-08-09 23:34:57 -03:00
Francisco Solla
7fedf45cd7 docs(C108,D6): planos de intenção — sino de notificações (ler ao abrir + modal desktop) e push no device em produção 2026-08-09 23:10:52 -03:00
Francisco Solla
8896dcb952
Merge pull request #520 from fsolla/plans/plan-issue-16
docs: plano C107 — atualizações mobile sheet de criação usável
2026-08-09 22:56:03 -03:00
Francisco Solla
28c7ef8b2b docs(C107): plano de intenção — 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-09 22:52:31 -03:00