Collection archivePhoto ganha o grupo catalog (legenda/descrição/cena/texto
visível/temas/pessoas/município/origem/catalogedAt), curatedFields (o que a
assessoria edita nunca é sobrescrito), takenOn e searchText com índice GIN trgm
(migration aditiva add_archive_photo_catalog); hook beforeValidate deriva o
índice e marca curadoria só em edição humana; pipeline transacional baixa os
bytes privados, prepara JPEG <=1024px com sharp e mescla o VLM local com o
gazetteer/catálogo curado (pessoas só do TEXTO, nunca de rosto); CLI
pnpm archive:catalog (plan/--apply/--verify, recibo honesto, idempotente por
catalogedAt) com engine OpenAI-compatible na rede privada atrás de guarda de
host (ARCHIVE_VISION_ALLOW_REMOTE é o escape); client HTTP com retry/timeout e
parser zod fail-closed; resolver de município extraído para owner compartilhado
com o catálogo de peças delegando; unit/int novos e runbook staging-primeiro.
Troca o motor do modo tema (Câmara + internet) de expansão lexical para
embeddings (Deep Infra, BAAI/bge-m3), com índice próprio (speechEmbedding),
ranking por proximidade em memória, evidência real do trecho mais próximo,
vazio honesto por limiar e degradação para a busca literal. Inclui a
collection oculta + migration, o CLI pnpm acervo:index (--dry-run/--limit/
--source/--force/--probe), o port do design aprovado e a suíte de testes.
Pós-rebase: o snapshot da migration seguinte (20260926_062039_add_archive_photo,
do C231 já na main) foi regenerado para incluir speech_embedding — sem ele o
próximo migrate:create recriaria a tabela.
Closes#1360
Skills vendoradas (UPSTREAM.md): suíte Taste (design-taste-frontend,
gpt-taste, redesign-existing-projects, briefs de estilo, stitch-design-taste,
full-output-enforcement), web-design-guidelines e awesome-design-md.
Playwright CLI global + skill. Guardrail de marca do kit 1313 retirado:
DESIGN.md §7 (Evolution) passa a ser o dono vivo das regras visuais.
Doutrina ui-design-html.md §Skills de design, designer/-degraded e
designer-campanha-solla alinhados; changelog DESIGN1.
Falha pré-existente reproduzida em origin/main: a página de demanda monta o
form de responsáveis duas vezes no DOM (visível + cópia oculta) e
campaignDemandVisibility batia nos dois com locator estrito — classe
"locator não-escopado" do plano OPS83/#824. O CI curado do PR roda a spec
(diff high-risk) e ficaria vermelho; os locators agora miram o elemento
visível.
Registro no impl plan do C231 e no changelog.
Collection privada archivePhoto (flickrId unique+index, metadados do Flickr +
EXIF, leitura pela comunicação, mutação admin, entrada no mapa S3) com migration
aditiva add_archive_photo; ingestão idempotente (transação + upload) e CLI
pnpm flickr:import (plano dry-run por padrão, --apply com intenção/TEQO_ENV/S3,
--verify inventário read-only, recibos JSON em data/flickr/reports). Guard de
alvo extraído de reel-ingest para scripts/lib/cli.mjs; downloadUrlToFile ganha
headers (User-Agent do Flickr).
A detecção automática do Tailwind v4 lia os designs HTML de docs/plans/
e a classe bg-[url('../../public/…')] do design do álbum (4bf0c0c0)
virava módulo relativo no CSS de produção, quebrando next build na main
e o verify do deploy. @source not mantém o CSS publicado só com os
componentes reais (a variante root-absolute já existe em src).
O conjunto curado roda specs concorrentes que publicam peças; a home pede o
still e /frame responde 404 até a cura (C226). As specs da home passam a usar
a mesma declaração que frontend.e2e.spec.ts, fechando o flake do CI.
Lê a janela recente (12 mídias) de @depjorgesolla pela Graph API oficial e
cria Rascunho só para as novidades pelo pipeline C211/C220 (ou peça-link com
motivo honesto), com progresso real e recibo novas/já estavam/falharam.
Dedupe pela identidade do post (shortcode em qualquer grafia p/reel/tv), sem
migration, compartilhado com o Adicionar por link; fail-closed de credencial
nas duas pontas; nada é publicado.
Closes#1362
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
Hero com pattern oficial, placa do 1313 e faixa amarela; 1313 visivel no mobile. Filtros com ChevronDown do lucide centralizado (nao mais o glifo baixo) e accordion exclusivo via <details name>. Artefato: docs/plans/central-conteudos-hero-polimento-ui-design.html
Causa-raiz do sintoma residual: o container e IPv4-only e a rota IPv4 ate o
CDN do Instagram e instavel nesta rede (IPv6 e o caminho confiavel); o resolver
baixava com uma tentativa e virava 'indisponivel'. O dono do download ganha
teto de conexao por tentativa e ate 6 tentativas; resposta nao-2xx nao repete.
Rede IPv6 ja corrigida no stack (fora do repo) e documentada no runbook.
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).
O resolver pedia a janela de 500 (ate 10 paginas de cursor) antes de casar o
shortcode; erro em pagina >=2 virava 'indisponivel' mesmo com o post na pagina 1.
O dono da paginacao ganha shouldStopAt (board/sync intocados) e o resolver para
na pagina que contem o post — a colagem tipica volta a custar uma chamada.