Commit graph

300 commits

Author SHA1 Message Date
Francisco Solla
b926e71d91 feat(C155): fallback de listagem desce ao mês quando o ano inteiro falha 2026-09-14 01:54:55 -03:00
Francisco Solla
563a407572 feat(C155): fallback de listagem por ano quando a API recusa página funda 2026-09-14 01:54:55 -03:00
Francisco Solla
da6a392260 docs(C155): triage de débitos pós-simplify e HELP do --date 2026-09-14 01:54:55 -03:00
Francisco Solla
4f5f32dfef refactor(C155): simplify pós-review — backoff no camaraFetch, runLegislature e ajustes de guard/relatório 2026-09-14 01:54:55 -03:00
Francisco Solla
aa540ba9e1 feat(C155): backfill 54ª–57ª com cobertura por legislatura, guard de escrita e verificação de VOD 2026-09-14 01:54:55 -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
7ca708a3cb fix(C153): colisão de sourceKey, match de município e reclassificação limpa
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.
2026-09-13 02:08:55 -03:00
Francisco Solla
aca3ea6188 feat(C153): catálogo de falas do Solla — schema, access, import idempotente e busca
Collections speech/speechSegment no grupo Comunicação, papel communicator
(leitura restrita) e índice trigram normalizado para a busca por segmento.
Import idempotente da Câmara (discurso→evento→trecho→VOD→ASR) com facetas
gazetteer+LLM, proveniência e preservação de curadoria manual; parser de
quem presidia; reusa a lógica pura do piloto C152 via scripts/lib.

Related #955
2026-09-13 02:08:55 -03:00
Francisco Solla
b03b7bd969 feat(OPS103): staging no homeserver com verify único e aprovações separadas (Closes #968) 2026-09-12 22:48:22 -03:00
Francisco Solla
907b9ba96b refactor(OPS102): fixes do /simplify — comentários vivos alinhados, pin do exit 0 com mais dentes, runbook/changelog precisos 2026-09-12 21:32:28 -03:00
Francisco Solla
c9bd627de4 fix(OPS102): deploy manual roda até o fim mesmo com main à frente (remove guards de stale run) (Closes #962) 2026-09-12 21:32:28 -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
fa6933c1cd feat(C152): piloto da fonte de vídeos e transcrição minutada da Câmara
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/.
2026-09-12 20:43:36 -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
52a2ef6dd1 fix(deploy): gera o importmap com a condition react-server e falha em stale run
OPS99 passou a rodar 'pnpm exec payload generate:importmap' no builder do
Dockerfile sem --conditions=react-server; o server-only resolve para o
módulo que lança e TODO build Docker falha — desde a OPS99 (27/08 19:05
UTC) nenhum deploy real aconteceu e a prod ficou no commit 4d6533c04.
Agora o builder chama 'pnpm generate:importmap' (o script dono das flags,
mesmo usado no predev).

O guard de stale run (main avançou durante o verify) fazia 'exit 0' e o
job reportava sucesso sem deployar (run 34675436246); agora falha com
fatal, sem falso verde.

Testes: unit guarda a linha do Dockerfile (script canônico) e os dois
pontos do guard de stale; build standalone de main validado localmente.
2026-09-12 11:50:10 -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
84de2c1aeb refactor(OPS101): fixes do /simplify — guard de pin robusto, docblock e triage de débitos 2026-09-12 02:17:37 -03:00
Francisco Solla
e4550384b1 fix(worktree): OPS101 — preset canônico deepseek-flash e commands sem pin de modelo (Closes #944) 2026-09-12 02:17:37 -03:00
Francisco Solla
f3cbb4315d ops100: worktree model flags --glm (opencode-go/glm-5.3-flash) e --free (openrouter/openrouter/free) 2026-08-27 19:29:42 -03:00
Francisco Solla
a5dc06ad80 chore: remove unused importMapContract.mjs (OPS99) 2026-08-27 16:05:36 -03:00
Francisco Solla
493f4ba997 feat(OPS99): generate importMap at build time with dummy S3_* envs
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
2026-08-27 16:05:36 -03:00
Francisco Solla
81513d5b14 fix(deploy): reverte package.json para desbloquear o deploy — cache deps do BuildKit
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.
2026-08-27 09:04:47 -03:00
Francisco Solla
fea6c64514 fix(admin): restaura S3ClientUploadHandler no importMap — admin em branco em prod (4ª recorrência da classe OPS69)
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.
2026-08-26 20:27:22 -03:00
Francisco Solla
8dedf1028c feat(worktree): subcomando fix + skill /bug-fix (diagnóstico → fix → verificação → prevenção → post-mortem) 2026-08-26 16:16:18 -03:00
Francisco Solla
ee4ba83535 fix(ops96-audit): parseVitestStdout tolera sufixo do pnpm em suite vermelha — retrato impresso mesmo com falha (contrato do metrics) 2026-08-25 17:09:43 -03:00
Francisco Solla
e562af76d8 feat(audit-p6-m909): deterministic disarm guard for /testing-audit (miss #909) 2026-08-25 11:29:03 -03:00
Francisco Solla
b7ed186237 feat(ops98): veto audit/* no safety-net de auto-merge (PR único do audit fica sem automerge) 2026-08-25 08:52:07 -03:00
Francisco Solla
fca5a1c105 refactor(OPS97): fixes do /simplify — fail-closed em PR inexistente, errata do plano, token neutro na skill 2026-08-25 02:43:31 -03:00
Francisco Solla
eec6fd3d49 fix(OPS97): desarme do auto-merge via GraphQL disablePullRequestAutoMerge com laco verificado (licao do dogfood #905) 2026-08-25 02:43:31 -03:00
Francisco Solla
6bb3cfa540 feat(OPS97): disableAutoMerge no github-api + auto_merge normalizado (tests unit) 2026-08-25 02:43:31 -03:00
Francisco Solla
a734bd7372 refactor(ops96): simplify — dedup ms/DEFAULT_TOP no core, exit fail-closed sem relatório, precisão no desarme e pins de fallback 2026-08-25 02:42:40 -03:00
Francisco Solla
c48851c427 test(ops96-audit): reforçar assertion de api.int.spec.ts + docblock sem família e2e removida 2026-08-25 02:42:40 -03:00
Francisco Solla
c2f0c44d15 test(ops96-audit): mover 4 specs puros de int para unit (relationship, schemaPrimitives, assertTestDatabase, petition-page-layout) 2026-08-25 02:42:40 -03:00
Francisco Solla
54fd059d9f feat(ops96): protocolo completo na SKILL.md + ignores do artefato coverage 2026-08-25 02:42:40 -03:00
Francisco Solla
c13174b8aa feat(ops96): motor de métricas baratas + @vitest/coverage-v8 atrás de flag 2026-08-25 02:42:40 -03:00
Francisco Solla
d679708c06 fix(worktree): OPS95 — corrige mapa de modelos e remove --variant do launch do TUI (Closes #876) 2026-08-24 23:04:39 -03:00
Francisco Solla
facce55eda fix(OPS94): fila de claim não morre mais em issue ready sem frontmatter id (skip + aviso; #659 ganha id B195-F1)
- buildClaimQueue exclui Issues ready sem frontmatter id (não viram worktree
  por construção) e withoutIdReadyIssues() expõe as puladas
- agent:claim / worktree next / pnpm issue next avisam com os números em vez
  de morrer — uma Issue quebrada não trava mais a fila
- pin novo em agentPoolEligibility.unit.spec.ts

Closes #864
2026-08-24 17:23:59 -03:00
Francisco Solla
8951f198ca feat(worktree): flags de modelo --cheap/--pro/--zen/--go/--alibaba com --variant max (OPS93)
Mapa fixo commitado em scripts/lib/worktree.mjs (cheap=cheapestinference/deepseek-v4-flash, pro=opencode-go/qwen3.7-max, zen=opencode-go/ox-alpha-free, go=opencode-go/mimo-v2.5, alibaba=alibaba-token-plan/qwen3.7-max) com WORKTREE_VARIANT=max sempre; resolveWorktreeModel fail-high em múltiplas flags; opencodeLaunchDirective passa a emitir --model <map|preset> --variant max --auto; scripts/worktree.mjs valida at-most-one antes do claim e repassa modelo nos 3 propósitos (next/plan/new) com help/docblock atualizados; .agents/shell/worktree.sh, .opencode/commands/worktree.md, .agents/skills/worktree-next-issue/SKILL.md e local-database skill com contrato remapeado (--go deixa de ser no-op); tests/unit/worktree.unit.spec.ts pina as 5 flags, variant e guardrail.

Closes #859
2026-08-24 17:08:39 -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
4ee25e31d8 fix(ci): curated result surfaces unmapped risk files before high-risk early-return
Hoist collectUnmappedSrc helper in selectE2eSpecs so unmapped risk-area
files are computed before the high-risk early-return. When high-risk wins
and risk files lack a manifest entry, the curated result now includes
them in unmapped and appends '; curated + risk files without mapping: ...'
to reason — mode stays curated.

Pin: testAffected gains combined case (migration + unmapped risk file →
curated with unmapped in reason). Closes #846.
2026-08-24 16:52:52 -03:00
Francisco Solla
b2a46bb026 review(OPS88): mirror gate-ci alinha reset/needsDb ao modo curated (OPS86) 2026-08-24 16:12:44 -03:00
Francisco Solla
6eb40362d9 review(OPS88): simplify — build surface do reset, contrato high-risk, erros rotulados, docs canônicos 2026-08-24 16:12:44 -03:00
Francisco Solla
7d7fee1619 feat(OPS88): verify do CI/deploy com um Postgres e reset determinístico, um build por run (Closes #834) 2026-08-24 16:12:44 -03:00
Francisco Solla
4d64ad580b chore(worktree): preset model do launcher via OPENCODE_WORKTREE_MODEL — fallback comum deepseek-v4-flash (alinhamento infra-solla/iara-pwa) 2026-08-24 15:43:35 -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
6f6e9fb4b4 ops(OPS91): sweep pós-cutover — textos vivos que apontavam o tracker ao Forgejo passam para o GitHub (Closes #841) 2026-08-24 15:08:14 -03:00
Francisco Solla
2aa445dd1d impl(OPS84): verificar valores (não só entidades) da migração — 13 tabelas, 0 diffs (Closes #828) 2026-08-24 15:03:31 -03:00
Francisco Solla
ef01b15467 refactor(OPS85): seed + pós-condição do gerador na lib com pin unitário; rest[0] no guard 2026-08-24 14:44:25 -03:00
Francisco Solla
5440134196 fix(OPS85): guard do changelog usa --no-renames — a migração agrega-deletado+HISTORY-criado não é rename 2026-08-24 14:44:25 -03:00
Francisco Solla
60a71eea4b feat(OPS85): matar o agregado commitado do changelog — registro por-arquivo + HISTORY congelado + leitura sob demanda (Closes #830) 2026-08-24 14:44:25 -03:00
Francisco Solla
ddd58a01d1 fix(deploy): compose referencia a tag qualificada do registry; F2 preserva por image ID (INF13)
O compose do stack referenciava a tag local bare teqo-1313:<sha>, que é uma
segunda ref que nada recria se perdida (achado INF8 24/08: só restava a ref
do registry e o 'compose up -d' falhou com No such image). Agora:

- swap escreve sempre localhost:5000/teqo-1313(-migrator):<sha> (sed
  idempotente aceita ambas as formas); os docker tag bare somem (sem
  consumidor);
- rollback do runbook simplifica (docker pull basta, sem dance de tag);
- limpeza pós-deploy (INF3/F2) preserva TODAS as tags da imagem em uso pelo
  IMAGE ID do container, imune a drift de label do compose.

Runbook docs/ops/teqo-1313-deploy.md atualizado (fluxo, rollback, tabela de
falhas com o achado). Issue de referência (tracker infra-solla):
git.solla.dev/fsolla/infra-solla#24
2026-08-24 14:17:43 -03:00
Francisco Solla
ae79eb507c chore(OPS89): variantes Vercel AI Gateway para a config global do opencode (Closes #836)
- opencode.json: removido provider.vercel inteiro — repo fica $schema + mcp.penpot
- docblock de OPENCODE_PRESET_MODEL e prosa de worktree-next-issue/SKILL.md apontam para ~/.config/opencode/opencode.jsonc
- Forgejo → GitHub na linha reescrita da skill (rastro pós-cutover)
- impl plan + changelog da entrega; débito OPS91 (#841) registrado com plano
2026-08-24 14:05:59 -03:00
Francisco Solla
a2db543e0c ops(OPS82): preset do launch opencode → DeepSeek V4 Flash 0731 (mais barato) + variantes low/high/max + remoção duplicata cheapest-inference (Closes #817) 2026-08-23 23:20:45 -03:00
Francisco Solla
bddbd5c085 ops(OPS79): última migração de dados em produção — reconciliação da vertical campanha (Neon→teqo_1313) + migração do residual público (Closes #797)
- scripts/reconcile-campaign-vertical.mjs: verificação read-only repetível (contagens + junta de IDs das 13 coleções da vertical + conteúdo semântico das rels); absorve os 3 links de edição de portfolio pós-OPS51; nunca imprime PII
- scripts/migrate-signature-orphan.mjs: migração one-off com guard OPS79_MIGRATE_CONFIRM=1, dry-run/apply, transação única, PII mascarada
- Execução 100% no homeserver: aplicado contact 2225 + signature 1486 preservada + subscription 1492; sequences ajustadas; pós-verificação signature 1485=1485 e reconcile 19/19
- Runbook (docs/ops/teqo-1313-deploy.md §OPS79): execução + rollback (DELETE 3 linhas + setval)
- docs: plano de intenção restaurado (deletado acidentalmente em C140) + impl plan + changelog
2026-08-23 22:45:43 -03:00
Francisco Solla
5b4e5ecf22 ops(OOPS80): cutoff fase 1 — desativar acesso pela URL antiga pt.jorgesolla.com.br (congelar a fonte)
- scripts/check-push-chain.mjs: default --site, usage, erro → canônico; prosa Neon/vercel → homeserver
- tests: fixtures unit (WebAuthn, JsonMutation, aiMarkdownLinks) + int (campaignMultiPhones) → canônico, semântica preservada
- campaignMultiPhones: adiciona ensureLeasedConsent para invite consent fixture (fix pre-existente)
- personas (.opencode/agent/designer, solla-comunicacao, skill): canônico + operação homeserver
- docs: intenção + impl plan + changelog entry; CHANGELOG-AGENTS append-only preservado
- Sem migration, sem schema, sem access/Consent, sem UI
- Closes #796
2026-08-23 20:14:59 -03:00
Francisco Solla
9467e0f292 fix(campaign): C140 — manual dispatch in 18 forms prevents React 19 form reset on error
Fix manual dispatch em 18 formulários de /campanha que usavam <form action={submitAction}>: campos não controlados voltavam ao defaultValue após qualquer action assentada (sucesso ou erro) do React 19.

- WizardStepFormChrome alterado uma vez (cobre 3 wizard steps)
- 15 forms recebem dispatch manual arquivo a arquivo
- AdvisorPasswordResetButton (2 micro-forms sem campo digitável) ficou como está
- Unit spec novo campaignWizardStepFormChrome.unit.spec.tsx
- CHANGELOG-AGENTS.md cleaned (98 duplicates removed)

Also fixes:
- campaignPageActor writable gate only blocks advisors (was blocking coordinators too)
- campaignLeaderships e2e stability (waitForLoadState before attribute assertion)
2026-08-23 17:54:19 -03:00
Francisco Solla
3eec76ef71 style: prettier no impl plan e no help do worktree 2026-08-23 16:08:12 -03:00
Francisco Solla
67c213585c ops(OPS78): launch do worktree opencode roteia pelo Vercel AI Gateway com variantes low/high/max (Closes #784) 2026-08-23 16:08:12 -03:00
Francisco Solla
038758eb81 docs(OPS47): alinha plano do agent-pool à política main-based pós-OPS71
- agent-pool-orchestrator.md: PR --base stage + issue-done-on-stage-merge.yml
  + promote stage→main viram --base main + issue-done-on-main-merge.yml
  + sem promote; migration-lock → countOpenSchemaPrs; model-selection →
  agent-pool-models.mjs; agent-github.mjs → agent-forgejo.mjs; workflow
  marcado removido OPS65 (caminho real .forgejo/workflows/agent-pool.yml)
- agent-pool/SKILL.md: L79 sem 'Antes do promote'; L105 agent-pool-github→forgejo
- comentário/label: agent-pool-state.mjs 'merge in stage'→main;
  agentPoolState.unit.spec.ts label 'on stage merge'→main
- changelog 2026-08-23-ops47.md (Closes #735)
2026-08-23 15:23:52 -03:00
Francisco Solla
4486c05b49 fix(api): removeLabels idempotente — 404 em label ausente não derruba o flip (dispatch re-run 404'd no in-progress já removido) 2026-08-23 15:20:21 -03:00
Francisco Solla
0822cca82c chore: reduce token overhead in agent workflow
- Drop model-selection skill (dead, pool is dormant)
- Drop PNG rendering from UI planning (HTML-only, no pnpm ui-draft:render)
- Trim code-simplification: 331→~150 lines, Python/React examples moved to reference/
- Compress engineering-brief.md: remove redundant 'Pinhos do repo' (alwaysApply rules)
- Deduplicate capture-review-debts: reference decision-quality.md instead of re-explaining
- Create concise-output skill (35 lines) to enforce terse agent output
- Trim all 99 skill descriptions to ≤1 sentence
- Split AGENTS.md into layers: core (73 lines, always) + campaign/public/infra (on-demand)
- Decompose plan-issue: exploration sub-agent + parallel plan-writers + register sub-agent
- Decompose work-issue: exploration sub-agent + plan-writer + 2 parallel reviewers + debt sub-agent
- Update agent-work-issue to match sub-agent pattern
2026-08-21 20:38:56 -03:00
Francisco Solla
c90f70aab4 ops(OPS76): tracker de Issues migra do Forgejo para o GitHub — GitHub como host único
Decisão (2026-08-21): o GitHub passa a ser a fonte única — main, branches,
PRs, CI via Actions E tracker de Issues. O Forgejo deixa de ser canal ativo
(git.e issue tracker congelados pós-convergência).

Data: 17 issues migradas do Forgejo para o GitHub (frontmatter id/depends/
serializes/priority/model preservados, labels mapeados, comment de origem
em cada uma; tabela Forgejo→GitHub registrada).

Tooling: github-api.mjs estendido com a camada de issues/labels/comments/
close/workflow-dispatch/contents (mesmo shape normalizado do forgejo-api);
18 scripts trocam o transporte forgejo-api → github-api; forgejo-api.mjs e
seu teste (961 linhas) removidos; teste githubApi.unit.spec.ts novo (7 casos);
issue-transition-on-merge.mjs renomeado e reapontado.

Flips pós-merge: issue-done e plan-issue-ready agora usam o GITHUB_TOKEN com
issues:write no repo (sem FORGEJO_API_TOKEN) e fecham/flipam Issues no GitHub.

Docs: AGENTS.md, AGENT-OPS.md e skills do fluxo vivo (work-issue, plan-issue,
project-status, capture-review-debts, worktree-next-issue, agent-work-issue)
apontam o tracker para github.com/fsolla/teqo/issues.

Gates: tsc/lint/format/knip/cycles/unit 2416 verdes.
2026-08-21 08:19:06 -03:00
Francisco Solla
c723d6accf
merge(OPS75): convergência Forgejo→GitHub — GitHub como fonte única
* deploy: limpeza pós-deploy do build cache e tags locais antigas (INF3/F2)

O deploy compila no homeserver e acumulava build cache + tags locais
antigas no disco raiz (incidente 19/08: 45G de cache). Após o smoke:
docker builder prune -f + remoção das tags locais teqo-1313 exceto o
SHA em uso (best-effort, registry preserva o rollback).

* 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.

* plans(C141,C143,C142): permissões granulares por assessor + responsáveis de demanda + UI por perfil

* plans(C141,C143,C142): permissões granulares por assessor + responsáveis de demanda + UI por perfil

* feat(S8): seção 'Nossa história' na home — vídeo da trajetória embutido do YouTube (Closes #91)

* docs(S8): resolução do conflito com S9 (história → newsletter) + nota do guard e2e dev-only

* feat(S10): pixel do Meta na página pública de campanha (campo admin em SiteSettings + PageView na home + Lead no sucesso da captura)

Campo tracking.facebookPixelId no global SiteSettings (mesma validação/normalização do precedente Petition, regra centralizada em facebookPixel.ts); home (grupo (home)) injeta o base code + PageView quando o ID está configurado (fail-closed) e o CampaignNewsletterForm dispara exatamente um Lead no sucesso do S9. Migration 20260819_213947_add_site_settings_facebook_pixel_id. Closes #94

* refactor(S10): simplify pass — Lead wiring unit-tested e raças de estado compartilhado do e2e resolvidas

e2e: stub hermético de connect.facebook.net no guard compartilhado (specs paralelos renderizam a home com o pixel); advisory lock no Postgres serializando o login do usuário de teste (Payload perde sessão em login concorrente read-modify-write — classe documentada no webauthn); seedTestUser vira create-if-missing (sem janela de usuário ausente nem reset de senha); Lead do form protegido do catch de erro (tracking nunca falha uma captura commitada); descrição do campo compartilhada; unidade nova campaignNewsletterLead cobre exatamente um Lead no sucesso.

* feat(C141): perfis de permissão por assessor — Visão × Edição configuráveis, enforcement fail-closed (Closes #105)

* chore(C141): regenera changelog agregado após rebase com S10

* plans(C144): options write-scoped para formulários de escrita pós-C141

---------

Co-authored-by: Francisco Solla <admin@solla.dev>
2026-08-21 01:41:18 -03:00
Francisco Solla
0c7a3b9023 style(OPS72): alinha continuação do comentário do gate-ci.mjs (prettier) 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
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
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
4bf6086caa fix(OPS71): parse de --body-file no github-pr.mjs 2026-08-19 19:17:19 -03: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
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
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
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
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
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
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
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
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
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
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
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
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
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
7a8f4d9830 ops(OPS55): prettier em scripts/worktree.mjs (formato pós-commit) 2026-08-18 01:53:27 +00:00
Francisco Solla
ff14cef653 ops(OPS55): worktree respeita WORKTREES_ROOT do .forgejo/worktree.env
- worktree.mjs: WORKTREES_ROOT = env ?? fallback (~/.cursor/worktrees/teqo)
- .forgejo/worktree.env novo: FORGEJO_REPOSITORY=fsolla/teqo + root atual
  (migração para ~/.worktrees/teqo fica para quando as sessões opencode
  ativas nos worktrees antigos fecharem — OPS52/OPS53/PUB5)

Roteador global (~/.config/shell/worktree.sh) exporta as chaves do arquivo
por projeto; iara-pwa já usa o mesmo mecanismo.
2026-08-18 01:53:27 +00:00
Francisco Solla
89e89c5118 fix(OPS52): S3_WORKTREE_REQUIRED_KEYS sem export (knip) 2026-08-17 22:36:34 -03: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
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
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
3cd0b4e0f5 style(worktree): prettier na ordem dos imports de node:fs 2026-08-17 19:32:40 -03:00