Commit graph

62 commits

Author SHA1 Message Date
Francisco Solla
dbe77f08cc feat(C226): frame estático nos vídeos da Central antes do play
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
Peça de vídeo publicada e com arquivo passa a mostrar um frame estático
leve no card público antes do play, sem nenhum request do arquivo antes
do gesto (`preload="none"` do S27 intacto).

O frame é um derivado privado em `contentMedia` (nome determinístico),
gerado por um dono só — `ensureContentPieceFrame` — chamado pelo job
C211 (que já tem o arquivo em disco: a extração sai de graça) e pela
rota `GET /conteudos/<slug>/frame` em miss, que cura o acervo anterior
sem backfill, fila, collection nova ou migration.

A porta reusa o gate de slug publicado e o proxy de mídia privada do
`/midia`: o kill switch fecha as duas de uma vez. Sem frame possível, o
slot mostra o estado neutro honesto com play e o marcador — nunca
imagem quebrada, nunca retry.

A projeção declara o frame por elegibilidade (vídeo com arquivo), não
pela existência do derivado, então a cura é invisível para o cache da
listagem e não há tag para bustar. O frame também aparece na página da
peça e no card da home (variante compacta de 124px, cena 05).

A passagem de design (tier primário) achou dois bugs que só o browser
mostra: o glyph de imagem quebrada numa requisição que falha, e — ao
esconder a imagem até decodificar — o frame sumindo para sempre no
caminho mais rápido (uma imagem que carrega antes da hidratação não
dispara `load` nem `error` de novo; o `ref` decide os dois desfechos).

Também: o guard de falha de e2e aceita `RegExp` e confere os paths
declarados antes do caso do favicon (todo 404 do browser traz a mesma
mensagem), o manifest de e2e mapeia os módulos novos, e o job C211
passa o arquivo que já tem em disco em vez de rebaixar o vídeo.

Design tier: central-conteudos-preview-frame-video
Closes #1352
2026-09-25 14:56:06 -03:00
Francisco Solla
63bd370b6c feat(C222): apagar uma peça na Central de Conteúdos (Closes #1330)
Hard delete por peça (sem lixeira/lote/telemetria) em qualquer estado, com confirmação que nomeia o link público só quando publicado; access próprio da vertical + admin (advisor/leader fail-closed), cascata da mídia pós-commit e bust da tag contentPieces no afterDelete; máquina do diálogo extraída no 3º call site (C183/C199/C222).
2026-09-24 23:49:28 -03:00
Francisco Solla
b119d203aa feat(S37): liderancas que aparecem nas pecas na Central de Conteudos (Closes #1303)
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
2026-09-24 17:40:57 -03:00
Francisco Solla
425361d278 feat(C216): terceira fonte do acervo — falas na internet (Closes #1292) 2026-09-24 17:01:42 -03:00
Francisco Solla
bf2c7a25c4 feat(S38): cards personalizáveis como itens do catálogo (Closes #1304) 2026-09-24 16:57:03 -03:00
Francisco Solla
59f70dccf3 fix(C221): "Limpar" do omnibox não quebra linha no mobile (Closes #1313) 2026-09-24 16:35:13 -03:00
Francisco Solla
11de48f726 feat(S39): seção da Central de Conteúdos na home (Closes #1305) 2026-09-24 14:53:36 -03:00
Francisco Solla
38e6742673 feat(C219): paridade de busca e filtros nas gravações do acervo (Closes #1295)
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
Classificação automática com proveniência (job + CLI recordings:classify), facetas ano/tema/alcance/município/duração na URL canônica, busca por tema no corpus próprio, ordenação e a barra de filtros portada do design aprovado sobre os shells compartilhados. Deep-links existentes byte-idênticos; Câmara intocada.
2026-09-24 04:00:06 -03:00
Francisco Solla
8318fb5fce feat(C215): ingestão de falas da internet no acervo — origem web, espelho privado e catálogo (Closes #1291) 2026-09-24 03:26:10 -03:00
Francisco Solla
e8a190005a feat(S32): contadores anônimos de download dos cards na home staff (Closes #1273) 2026-09-23 18:12:42 -03:00
Francisco Solla
8d81883509 feat(S28): busca por tema na Central de Conteúdos pública 2026-09-23 15:35:21 -03:00
Francisco Solla
f47c2645ac feat(C213): contadores anônimos de circulação por peça na Central de Conteúdos (Closes #1258)
Collection contentEvent (hidden, sem hooks, sem PII) + beacon público
POST /api/content-events fail-soft, download contado no servidor na rota
de mídia, agregação em uma query group-by e a coluna "Circulação" na lista
interna + painel na ficha (port classe-a-classe do artefato estendido,
crítica final do designer certificada).
2026-09-23 15:29:43 -03:00
Francisco Solla
75e5825b05 refactor(S27-FOLLOWUP-DRY): dono único do OG image de fallback do frontend público (Closes #1276)
O fallback de imagem OG (própria → global `metadata` → null, sempre absoluto
no deployment origin) deixa de estar copiado em 6–7 páginas públicas e ganha
o dono server-only `src/utilities/ogImageReads.ts` (`resolveOgImage(configured)
→ { url, media }`), com `resolveShareLinkOgImageUrl` delegando (sem twin) e os
8 consumidores migrados — 2 do S27, jingles, corte, abaixo-assinado ×2, artigo
e layout. `seo.ts` segue puro; sem migration, sem Consent/access, Impeccable A.

- unit novo do resolver (8 casos) + pin do módulo em codebaseConventions; int
  do shareLink verde sem tocar asserção; e2e frontendConteudos/frontendJingles/
  campaignSpeechCut/frontendShareLink/frontend verdes, com asserts novos de
  og:image (helper `metaContent` extraído do spec do share-link)
- dono novo entra no E2E_AFFECTED_MANIFEST nas 4 entradas de OG públicas
- deltas intencionais registrados: deployment origin no lugar do canônico em
  corte/abaixo-assinado/artigo e OG absoluto no layout
2026-09-23 14:30:40 -03:00
Francisco Solla
2a0dca6243 fix(S29): achados da review — snapshot pós-rebase, manifest e2e e a11y do anúncio
- snapshot drizzle da migration regenerado pós-rebase (inclui as tabelas do
  S27; sem ele o próximo migrate:create recriaria content_piece)
- manifest e2e: CampaignFooter/jingleReads/campaignTime acordam o
  frontendShareLink; dynamicParams inerte removido da rota .ics
- anúncio: dl válido (Local sem dd), aria-live só no status/chip, sem
  hasImage/cast; hook direct deixa o erro de URL falar antes (+ teste int)
- débitos triados no impl plan (folding .ics -> #1281; deferidos com gatilho)

Related #1268
2026-09-23 12:37:00 -03:00
Francisco Solla
b8051bb7e0 feat(S29): link com destino trocável e página de anúncio da atividade
- shareLink ganha modo (direct|announcement), pool de destinos com "Destino no
  ar" por linha e dados do evento; destination escalar sai via migration com
  backfill lossless (legado vira direct com destino no ar)
- página de anúncio pública portada classe-a-classe, header campaign-site
  extraído (CampaignPageHeader) e ativação/troca sem reload por poll da rota
  /api/share-link/<slug>/live (fail-closed, no-store)
- agenda Google (action=TEMPLATE) + .ics por rota sob o slug; compartilhar o
  link curto (kind event); S19 intacto e 404 uniforme preservado

Related #1268
2026-09-23 11:48:12 -03:00
Francisco Solla
dda55adc27 feat(S27): Central de Conteúdos pública — Peça voto pra Solla 1313
/conteudos + /conteudos/<slug>: catálogo filtrável (Tipo · Cidade · Região ·
Tema · Instituição + termo na URL canônica), play sob demanda (Vídeo/Áudio só
carregam no toque; Foto com preview leve), Baixar e Compartilhar do próprio
card com a mensagem de voto por tipo (wa.me sem destinatário, editável; "A
mídia" usa a folha nativa e cai em download onde o aparelho não suportar) e o
card personalizado como peça sintética do catálogo → /cards.

- leitura pública nova (contentPieceReads) cacheada na tag contentPieces com o
  gate where status=publicado + contentPieceIsPublic; a rota GET
  /conteudos/<slug>/midia reusa buildPrivateMediaResponse (range, disposition,
  private no-store, 404 uniforme) com download jorge-solla-1313-<slug>.<ext> e
  a allowlist inline do dono estendida (webp/gif/avif + áudios das peças)
- contrato público puro do catálogo (filtros/VM/download) + mensagens de voto
  (contentPieceShare); OG com fallback do global; conteudos entra nos slugs
  reservados; kill switch por peça vale na hora (lista, página, rodapé/selo);
  descoberta condicional no CampaignFooter e no header local
- UI portada classe-a-classe do design aprovado (cenas 01–08, com as cenas
  06–08 estendidas no trigger a); sem migration, sem Consent e sem access novo;
  contentMedia segue privada (sem segunda cópia de mídia)
- testes: 2 unit specs novos, 18 casos int (leitura pública + rota de mídia) e
  e2e frontendConteudos (5 casos); manifest e curado atualizados
- follow-up de engenharia: #1276 (docs/plans/escala-dry-pos-s27.md)

Closes #1255
2026-09-23 11:13:57 -03:00
Francisco Solla
6480768926 feat(S30): card Time do estadual — 5º modelo com as 53 dobradinhas (Closes #1271) 2026-09-23 10:06:17 -03:00
Francisco Solla
cb61cf1152 feat(C211): Central de Conteúdos interna — envio em lote, link, catalogação e publicação
Envio em lote (1 request raw-body por arquivo, fan-out ≤2, até 50 arquivos) e
adicionar por link (Instagram/YouTube; extração só pela Graph API da conta
própria, senão peça-link) em /campanha/comunicacao/conteudos. Cada peça tem
estado próprio (Processando/Pronto/Falhou + Reprocessar), transcrição/texto e
catalogação automática revisável; publicar/despublicar preserva arquivo, slug
e publishedAt.

- collections contentPiece + contentMedia (upload privado no S3) e migration
  20260923_032714_add_content_piece (slug único gerado no 1º publish,
  sourceUrl única, searchText com índice trigram)
- pipeline reusa C199 (job after() + reaper + retry condicional, transcrição
  DeepInfra, ffmpeg/chunk), o classificador de facetas do acervo e o
  institutionCatalog; curatedFields impede o job de sobrescrever curadoria
- gate único canReadCommunicationCatalog (advisor/leader negados); delete
  só pelo admin; sem Consent novo
- UI portada do design aprovado (lista desktop + cards mobile, diálogo de
  lote com progresso por arquivo, diálogo de link, ficha editável, kill
  switch); nav/chrome/column-picker/manifest atualizados
- instagramFeed expõe mediaUrl; revalidateTag ganha wrapper seguro fora de
  request (fixtures/scripts)
- testes: 2 unit specs, 1 int spec (16 casos) e 7 e2e na vertical
- follow-up de engenharia: #1267 (docs/plans/escala-dry-pos-c211.md)

Closes #1254
2026-09-23 02:25:15 -03:00
Francisco Solla
bb550cebff feat(S25): player próprio da Rádio 1313 na home — exclusividade, share e fail-soft (Closes #1239) 2026-09-21 13:39:42 -03:00
Francisco Solla
c7eeb5dbd8 feat(S21): jingles de Jorge Solla no site público — ouvir e baixar (Closes #1222)
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
2026-09-20 01:48:20 -03:00
Francisco Solla
468f33e5b2 feat(S19): links de compartilhamento com miniatura OG (Closes #1207) 2026-09-19 14:02:17 -03:00
Francisco Solla
d0f606a55d feat(C199): gravações enviadas no acervo de comunicação (Closes #1166) 2026-09-18 23:42:30 -03:00
Francisco Solla
448f3845a2 feat(C194): biblioteca de reels na vertical Comunicação (#1153)
Lista publicados-only com capas 9:16, detalhe com player 9:16 e painel de
downloads (vídeo sem áudio primário; com áudio, mp3 e .srt quando existem;
transcrição em texto) e kill switch reversível: despublicar tira da lista e
bloqueia a mídia (C193 decision B), republicar reativa e re-carimba publishedAt.

Fiação: gate communicationCatalog (rename do speechCatalog), 3º sub-item da
vertical no nav, rule da lista no chrome, paths e loader próprios; sem
migration, sem Consent, sem tocar Media.read ou a rota de mídia do C193.

Design tier: DEGRADED — crítica final do designer indisponível no tier
primário (limite de uso); requer sign-off humano antes de push/PR.
2026-09-18 21:54:37 -03:00
Francisco Solla
ddfd8b31f3 feat(C191-runtime): cobertura do entry/PNG real e parse pt-BR explícito (Closes #1151) 2026-09-18 19:08:22 -03:00
Francisco Solla
c0b83611ee chore(C168): mapeia superfícies da biblioteca de cortes no manifesto e2e 2026-09-15 12:56:51 -03:00
Francisco Solla
897154b58b feat(campaign): C165 — importa eventos do Google como atividades sem município (Closes #1010) 2026-09-15 11:54:21 -03:00
Francisco Solla
352801853f feat(C167): corta o trecho [início,fim] do acervo e publica o arquivo numa página compartilhável
Seleção do C166 ganha "Cortar vídeo": título/descrição por IA (editáveis, com fallback determinístico), job assíncrono com ffmpeg por re-encode exato e polling dos quatro passos, e página pública unlisted /corte/<id> (noindex, OG, crédito CC BY) servindo o mesmo MP4. Collection speechCut com access canReadSpeechCut (advisor/leader negados) e kill switch por status; ffmpeg entra só no runner da imagem com FFMPEG_PATH para teste.
2026-09-15 10:24:36 -03:00
Francisco Solla
dd8daa2420 feat(C159): Sollinha na assessoria de comunicação — chat escopado ao acervo (Closes #983) 2026-09-14 05:45:28 -03:00
Francisco Solla
3e062749fa feat(C154): vertical de comunicação — papel communicator e acervo pesquisável
Papel communicator com nav própria e gate speechCatalog; acervo em
/campanha/comunicacao/acervo com busca normalizada (destaque no trecho),
filtros de ano/tema/alcance/fase/município/duração em URL canônica e
paginação por discurso via speech.searchText (GIN trgm + backfill).
Detalhe com player nativo no trecho, transcrição clicável, baixar/abrir
fonte e fallback sem VOD. Resolve o débito do C153 (Sollinha fora da
vertical) e corrige o parser de keywords oficiais no dono.

Closes #956
2026-09-13 13:46:58 -03:00
Francisco Solla
cc6d2abd04 feat(campaign): C150 — calendário principal escolhido pelo candidato/coordenador + inscrição de staff no Google (Closes #935) 2026-09-12 21:01:12 -03:00
Francisco Solla
d9b778b269 feat(campaign): C149 — botão Conectar com o Google (OAuth) substitui a configuração manual da service account (Closes #934) 2026-09-12 19:49:24 -03:00
Francisco Solla
681f3bb566 feat(S13): cards personalizados da campanha no site público
Home ganha a seção Faça parte com os três modelos e a rota /cards
concentra catálogo + compositor client-side (dialog desktop, bottom
drawer mobile): nome no slot medido do mestre com Brexter 700 e foto
full-bleed sob o overlay oficial (pan/zoom clampados), download PNG
sem conta e sem enviar nome/foto. Núcleo puro em src/lib/card* com
unit tests; e2e cobre home→deep-link, nome→PNG real, erro fail-closed,
foto e drawer mobile. Sem migration/PII.
2026-09-12 03:25:43 -03:00
Francisco Solla
087635d08b fix(agenda): modais usáveis no desktop — criação, sync e feed (C148) 2026-09-12 02:23:23 -03:00
Francisco Solla
a79d2d3790 fix: prettier format 2026-08-24 16:57:08 -03:00
Francisco Solla
1b846463b0 test(e2e): OPS87 — migrar asserções de servidor para HTTP sem browser (Concepts + PermissionProfile + People) + medir −65% (Closes #833) 2026-08-24 16:57:08 -03:00
Francisco Solla
9c5cd7830b simplify(OPS86): gate fail-fast no unmapped-risk, smoke fallback no manifesto, pins de sync do wrapper, docs AGENT-OPS (Related #846) 2026-08-24 15:19:52 -03:00
Francisco Solla
9af4b92851 ops(OPS86): blast radius dos testes — curated e2e em high-risk, fail-closed para risco sem mapping, unit/int full na seleção vazia (Closes #832) 2026-08-24 15:19:52 -03:00
Francisco Solla
0b4d7913fe test(C139): suite e2e campaignContacts e remapeamento do manifest
Cobre a página /campanha/contatos: recorte por omnibox + WhatsApp na
linha, create row desktop (erro de validação mantém o valor digitado,
conflito de nome com mensagem segura, create válido com POST), gate de
staff (líder cai em meus-contatos) e os sheets mobile (create via FAB
com telefone, edit com conflito + ficha atômica). Nomes criados via UI
são letras-only: o runID hex vira [a-p] (bijection, 128 bits).

O manifest ganha a família própria campaignContacts (antes caía no
campaignHomeActions).
2026-08-13 15:16:34 -03:00
Francisco Solla
621364f41f feat(C139): página /campanha/contatos — lista desktop com células editáveis, criação e filtros 2026-08-13 14:29:11 -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
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
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
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
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
6fbb71c7e7 chore(OPS22): mapeia o domínio do Sollinha no e2e-affected manifest (chat specs acordam com mudanças no chat) 2026-08-10 13:12:12 -03:00
Francisco Solla
bd06018bde B164: barra de navegação inferior no mobile para staff
Mobile bottom nav (staff only): 5 items (Início, Municípios, Atualizações,
Agenda, Mais) with Drawer overflow for secondary destinations. Leader and
desktop remain sidebar-only. FAB + scroll padding adjusted for safe-area.

Resolves #400
2026-08-08 12:57:05 -03:00
Francisco Solla
59983b33f7 feat(campanha): C15 — FullCalendar em /campanha/agenda
Issue #390 — entrada operacional staff com FullCalendar v7 route-local
(semana/mês/lista), filtros compartilháveis por município/deputado/tag,
criação pelo slot, detalhe por URL e remarcação drag/resize sob RBAC,
transação e lock por atividade.

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

Impl: docs/plans/fullcalendar-agenda-campanha-impl.md
2026-08-07 04:37:36 -03:00
Cursor Agent
30a5a8a5e3
B151 — FAB secundário no detalhe município v2 (#334)
- Catálogo v2 de ações rápidas: dossiê, eleições, nova liderança, giro
- Integra no chassis B126 via registry + context sync na rota v2
- Omitido registrar sinal (já na faixa de status B147)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 01:03:10 +00:00
Cursor Agent
6b8aa8b6cf
fix(campanha): B147 simplify — sinal empty body + cold select
Allow empty motivo on sinal through collection beforeValidate; distinguish
warm-typeless vs cold in the v2 signal select; reuse list signal form action.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:33:46 +00:00
Cursor Agent
c66ffd598c
fix(campanha): map municipio v2 route in e2e affected manifest
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:30:51 +00:00