O primeiro deploy staging desde a OPS103 falhou nas migrations (run 34765554681,
job deploy-staging): a imagem do migrator roda `pnpm migrate` no start do
container via corepack e o cache do corepack do build da stage `deps` não é
copiado para a imagem, então o runtime tenta baixar pnpm do registry.npmjs.org
e falhou com ETIMEDOUT/ENETUNREACH (o mesmo download funcionou no build minutos
antes e no migrator de produção). Produção ficou skipped e intocada.
`RUN corepack install` na stage `AS migrator` baixa o pnpm (versão do
packageManager) em build time e assa o cache na layer — o runtime fica offline.
Prova: `docker run --network none <imagem> pnpm --version` = 10.11.0 com o bake
e o erro exato do staging sem ele. Pin novo em deployScript.unit.spec.ts;
post-mortem e changelog.
O verify do deploy manual (run 34763253924, main 7ca708a3) falhou em
tests/int/googleCalendarSync.int.spec.ts ("a cancelled event...") nas duas
tentativas do retry: o spec isolava o espelho e a limpeza com
`{ title: { like: 'C114%' } }`, mas no Payload 3.82 `like`/`contains` compilam
para ILIKE '%…%' (contains, sem ancoragem) — um UUID de fixture de outro spec
embutindo o hex `c114` entrava no escopo e podia ser deletado pela limpeza.
Escopa passes e cleanup pelos ids criados (ownedActivityIds), com
`{ track: false }` para linhas deliberadamente alheias; teste de regressão do
cenário exato (falha sem o fix); pin de convenção em codebaseConventions
banindo marcador de título hex em tests/int; post-mortem e changelog.
Chave sufixada por hash de conteúdo para falas distintas que compartilham
data/tipo/fase (caso real 2026-06-17T17:16); keywords oficiais deixam de
alimentar o gazetteer de lugares; nomes ambíguos (Saúde, Wagner, Santana...)
exigem contexto de lugar; teste de nav do communicator; plano com prova viva
e changelog.
A home abre o mesmo compositor do /cards ali mesmo (dialog no desktop,
drawer no mobile) e perde o CTA: os três modelos viram botões e nada
navega. A seção sobe para imediatamente antes da captura de novidades.
O nome no card e no tile alinha à esquerda com o SOU (x=213) via leftX
+ drawCardName extraído; o tile passa a desenhar SEU NOME com o próprio
pipeline real (canvas transparente sobre a base). Modal sem o vão
interno (p-0 sm:p-0) e ajuda legível no tema campaign-site
(--muted-foreground). Remove a variante link e o cardModelHref sem call
site. Unit atualizado (+ drawCardName) e e2e com alinhamento por pixel;
sem migration, sem PII em rede.
Spike de viabilidade da fonte (sem DB/Payload): script + lib pura + unit tests; 3 discursos (55ª/56ª/57ª) processados ponta a ponta com segmentos ASR; relatório em docs/research/.
Adiciona o checkbox publicEvent ("Evento público", default false, indexado)
à collection activity para registrar a intenção de expor o compromisso na
futura agenda pública do site — sem consumo agora e sem mudar agenda, lista,
detalhe ou o espelho do Google.
- Activity.ts: campo checkbox + snapshot do guard de liderança
- zod/formData/view model do form + ActivityOverlay (ao lado de "Todo o dia")
- migration add_activity_public_event + types regenerados
- unit (overlay), int (parse/persistência/reabertura) e e2e (roundtrip)
O RUN do builder encadeia gerador de importMap e next build com '&&', mas
prefixos de shell (VAR=x cmd1 && cmd2) valem só para o primeiro comando.
Com o OPS99, NEXT_OUTPUT_STANDALONE=1 e NODE_OPTIONS ficaram presos no
gerador: o next build rodou sem output standalone e o stage runner falhou
no COPY de /app/.next/standalone (run 34701266297, pós-#953 — o #953 já
corrigiu o importmap sem react-server e o falso verde do stale run).
Cada comando passa a declarar seu próprio env, e o guard de invariantes do
Dockerfile em tests/unit/deployScript.unit.spec.ts ganha o pin do escopo
(flags no lado do next build). Inclui post-mortem e changelog.
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.
Post-mortem do bug-fix com as duas causas-raiz (host fbcdn fora do
allowlist + search:'' do pattern new URL), o pré-requisito do teste de
calendário, a verificação local e as prevenções baratas implementadas e
deferidas.
Fecha o registro do post-mortem: PRs #926/#927/#929, deploy manual verde
(verify 19m + deploy 16m), /admin/login renderizando em produção com 0
erros de console na verificação read-only via browser — confirmação final
do humano pendente. Changelog do desbloqueio estrutural do deploy
(override sharp → binários via npm registry).
Generate the Payload importMap at build time (Dockerfile) and dev time
(predev hook) with dummy S3_* envs, eliminating the class of bugs where
the admin goes blank because the S3ClientUploadHandler entry is missing
(OPS69/OPS72/OPS73).
Changes:
- Dockerfile: add pnpm generate:importmap with dummy S3_* envs before next build
- scripts/predev-importmap.mjs: predev hook that generates importMap if missing or stale
- Delete scripts/check-importmap-s3.mjs (guard no longer needed)
- Delete tests/unit/importMapS3UploadHandler.unit.spec.ts (unit pin no longer needed)
- Remove check-importmap-s3.mjs step from CI workflows
- Add importMap.js to .gitignore (build artifact, not committed)
- Update AGENTS.md with new contract
Closes#928
O deploy de fea6c645 falhou 2x: a mudança do script generate:importmap
em package.json invalidou a cache da camada deps do Dockerfile no
homeserver, e o pnpm install re-rodou do zero — o postinstall do
sharp@0.32.6 (transitivo) estourou o timeout baixando o libvips de
objects.githubusercontent.com ('sharp: Installation error: Request
timed out'; o git fetch de github.com funciona — o release vai por
outro hostname da CDN). Produção intocada (falha pré compose swap).
Reverte package.json ao byte do estado cacheado (a9f0c61e) — o install
nem roda. Remove o wrapper generate-importmap.mjs (desligado vira dead
code); proteção do importMap fica com o guard CI+deploy + pin unit +
contrato. Hardening do deps documentado no post-mortem (retry do
install / libvips via apk / proxy), changelog do follow-up incluído.
O importMap commitado perdeu a entrada do handler S3 no 1c865bd8
(regenerado sem as envs S3_*): com S3_* em prod, getFromImportMap
retorna undefined e a raiz do Payload não monta — tela branca, zero
erros de console, invisível no loop local (storage local em dev/test).
Fix da causa: regeneração com S3_* dummy (+2 linhas). Prevenção no
mesmo PR — o guard planejado no OPS69 (depends #77/#87) que nunca
existiu: pin unit, guard CI+deploy que regenera com envs dummy e falha
em divergência, wrapper do generate:importmap que injeta as envs, e
contrato compartilhado da chave. Post-mortem + changelog incluídos.
O dispatch manual do C139 montava new FormData(form) sem o submitter;
os botões de transição (name=status value=aprovada|rejeitada|…) nunca
viajavam e transitionDemandFormAction rejeitava toda decisão por status
vazio. submitterFrom (cast honesto nativeEvent as SubmitEvent) + segundo
arg do construtor nos handlers de transição e custo; C139 intacto.
Teste unit novo pina o glue FormData→status (SubmitEvent real, não
fireEvent.submit). E2e 'advisor opens a demand and decides it' verde
local (isolado e em runs de arquivo inteiro).
- Consent.beforeDelete bloqueia delete com referências (APIError 409 composto
por coleção), fim do 23502 cru no admin (contagens com overrideAccess).
- submitWhatsapp/submitPetitionSignature delegam o ciclo de transação ao dono
withPayloadTransaction (rollback no commit-falho, AggregateError, after-commit),
preservando os contratos { ok: true } / { ok: true, signatureNumber }.
- contactStateSchema exportado + contactCitySchema/contactCityFieldSchema
consolidados; as 4 fichas usam os exports únicos (fim das cópias inline do
predicate value in CitiesByState). withMutableConsentFixture purga consent
via raw SQL para bypassar o hook no harness de teste.
Co-Authored-By: opencode <noreply@opencode.ai>
O validate de itemId em social-feed-settings lia data.platform (doc inteiro,
sempre undefined) e nunca validava ID de vídeo em produção. Agora lê
siblingData.platform via helper validateExcludedItemId. Stub YouTube ganha
ids de 11 chars e o spec acompanha, destravando o e2e do describe
'Campaign home content section'. Sem migration.
- recordLastSeenSnapshot: update condicional (id + updatedAt) em vez de
last-writer-wins; miss descarta a visão contaminada (never-throw)
- token CAS capturado no topo de runSyncPass: mesmo doc alimenta as
decisões D2-D4 e o guard da escrita (fecha a janela basis-vs-token)
- teste int de corrida determinístico (promise gates): passada obsoleta
não ressuscita remoção permanente (RED confirmado antes do guard)
- convenção de hermeticidade da janela documentada no header da spec
Make actor_id nullable to match its ON DELETE set null FK; set SupporterImportBatch.actor required:false so config agrees with schema. Keep the deleteCampaignUserImportBatches hook + hand-deletes as explicit cleanup (no schema cascade, per webauthn precedent). Adds int coverage for account-delete with an orphan batch.
Os afterChange/afterDelete de activity e googleCalendarSync rodavam a reconciliacao completa dentro da transacao do save, segurando row lock + pool por todo o I/O do Google (15s por hop, N RTTs -> 45-75s). Corrige com deadline proprio do hook (GOOGLE_CALENDAR_SYNC_HOOK_TIMEOUT_MS=5s) via AbortSignal.timeout combinado com per-hop 15s no cliente (AbortSignal.any) — manual/webhook mantem 15s sem transacao como fallback confiavel. Fail-closed preservado (abort vira paused). Pin unit hook < per-hop + int hook <=7s com fetch lento.
Consulta o index do claim crashado e dirige o cursor compartilhado até
ele (bound catalogSize) em vez do loop probabilístico catalogSize*3 —
elimina o flake sob carga paralela. Branch null quando outra spec já
roubou antes do SELECT e fallback claimCount==0 para race durante o
drive, invariante claimCount(crashed)==0 preservado.
Extract sollinhaChatStreamBody + mockSollinhaChat into campaignE2EFixtures.ts
(canonical v7 wire format), reuse across all 6 chat e2e specs and migrate the
two legacy specs (transcribe, openingChips) from the format that fails the v7
client schema. Closes#556