Home abre no agradecimento pelos 152.049 votos com board 1+3, trajetória, bandeiras do mandato, galeria de fotos e rodapé institucional; chrome novo (header home/interno, lockup de nome, menu mobile acessível) e /artigos//fotos com título compacto.
As superfícies de campanha saem do ar (308 de /conteudos, /jingles e /cards), junto com newsletter, estúdio público de cards e UI da Central/jingles; download de foto e .ics perdem o 1313. Sem migration, sem Consent novo e sem apagar dados do CMS.
- /fotos/encontre + POST /api/fotos/selfie: só o vetor 128-d, sem imagem, sem score e
sem nomes de terceiros; fail-closed pela flag photoAlbum.selfieSearchEnabled (default
false) e pelos Consents por chave estável (busca-selfie-fotos/busca-selfie-indice, não
seedados — a superfície nasce fechada até o aval jurídico/DPIA).
- Índice A/C do gate (PR #1370): só quem aderiu é indexado; descriptors do acervo são
transitórios. Collection faceSubject (admin-only, sem vínculo com Contact), vínculos
matchedPhotos e marker faces.checkedKey no archivePhoto (modelo + enrolledAt).
- Engine @vladmandic/face-api nos dois lados: browser lazy com modelos same-origin
(copy-face-vision-assets no prebuild/predev/Docker) e CLI node-wasm sem tfjs-node.
CLIs ops-only faces:enroll / faces:index --apply|--verify com guardas C231/C232.
- Opt-out self-service por reconhecimento (leave-index) + canal de remoção do C233;
rate limit próprio (namespace selfie:<hash>) e bounded body extraído do beacon.
- UI portada classe-a-classe do design estendido (paridade certificada, tier primário);
e2e frontendFotosSelfie (manifest+curado+projeto, engine stubada) e canário do engine
real; runbook §C234, contrato em AGENTS-public.md e changelog.
- Migration aditiva add_face_subject (SQL revisado); hard-stops aprovados na sessão.
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.
/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
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.
- ci-pr.yml: split the serial checks job into parallel lint/format/
typecheck/knip/cycles + unit/int (vitest --changed vs PR base via
scripts/test-affected.mjs, full fallback on high-risk paths) + e2e
(manifest path->spec via scripts/e2e-affected.mjs, blocking) + build;
migration-lock gains checkout + pull-requests:read (fixes the failures
that blocked PR #50)
- ci.yml (main): full safety net — seed:minimal + int full + build + e2e
- package.json: gate:fast / gate:push; .husky/pre-push runs gate:push
- docs: "Dono do PR, dono do CI" policy in AGENT-OPS + work-issue +
engineering-standards; changelog entry; plans for OPS3-OPS7