OPS75 — Convergência dos mains Forgejo + GitHub #128

Merged
fsolla merged 39 commits from ops75-converge-main into main 2026-08-21 01:47:57 +00:00
Owner

Converge as duas linhagens divergentes pós-cutover OPS71 em uma história única no Forgejo main, para habilitar com segurança o push mirror Forgejo→GitHub (F2 do OPS75).

  • Merge commit 7e76c4f1 com pais = Forgejo main (9ae34b68) e GitHub main (3f74b696). Ambos os mains são ancestrais → nenhum commit de nenhum lado se perde, em qualquer direção de force-push do mirror.
  • Conflitos resolvidos: migrations/index.ts (registra as 5: newsletter, S10 pixel, C141, C143 responsibles, S11 sync status — os arquivos .ts/.json dos dois lados foram unificados pelo merge), demands.ts (C143 leitura + eixo C141), newsletter (versão S10 com pixel), importMap.js (restaurado versão GitHub com handler S3 — classe OPS69), CHANGELOG-AGENTS.md (regenerado via changelog:build, 46 entradas).
  • Gates locais verdes: tsc, lint, format, knip, cycles, unit 2457, int 815 (inclui C141/C143/S10/S11), build; migrations aplicadas ao teoq_test.

Relates: OPS75 (#127)

Converge as duas linhagens divergentes pós-cutover OPS71 em uma história única no Forgejo main, para habilitar com segurança o push mirror Forgejo→GitHub (F2 do OPS75). - Merge commit `7e76c4f1` com pais = Forgejo main (`9ae34b68`) e GitHub main (`3f74b696`). Ambos os mains são ancestrais → nenhum commit de nenhum lado se perde, em qualquer direção de force-push do mirror. - Conflitos resolvidos: `migrations/index.ts` (registra as 5: newsletter, S10 pixel, C141, C143 responsibles, S11 sync status — os arquivos .ts/.json dos dois lados foram unificados pelo merge), `demands.ts` (C143 leitura + eixo C141), newsletter (versão S10 com pixel), `importMap.js` (restaurado versão GitHub com handler S3 — classe OPS69), `CHANGELOG-AGENTS.md` (regenerado via `changelog:build`, 46 entradas). - Gates locais verdes: tsc, lint, format, knip, cycles, unit 2457, int 815 (inclui C141/C143/S10/S11), build; migrations aplicadas ao `teoq_test`. Relates: OPS75 (#127)
fsolla added 39 commits 2026-08-21 01:47:43 +00:00
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).
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.
- 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
- ci-pr.yml: infra e2e (cache/install/build-for-e2e + DB prep) só quando selected — diff high-risk não queima minutos à toa; comentários corrigidos
- engineering-standards.mdc + TESTING.md: política OPS72 (era OPS59/ci.yml stale)
- execution-pipeline/agent-work-issue: mecanismo real da #72 (CI roda famílias em paralelo, não sequencial) + ressalva do diff high-risk no pool
- changelog/impl plan: verificação verdadeira (suíte full local + triagem dos flakes)
O pnpm build do run e2e local em prod mode regenerou o importMap sem as envs S3_*,
dropping o handler do storage S3 (o P0 OPS69). Regenerado com envs dummy — diff de
+2 linhas idêntico ao estado fixado.
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.
- campaignDemand.responsibles (hasMany → campaignUser, staff elegível) com
  hook beforeValidate que descarta ids não-staff (fail-closed)
- canRead/UpdateCampaignDemand por membership: assessor só vê demandas onde
  é responsável; unrestricted/admin sempre; leader nunca (fim do escopo
  municipal de demandas)
- criador entra automaticamente como responsável no hook de criação (form,
  wizard, rascunhos C90); migration com backfill created_by_id → responsável
- UI: picker multi-select na criação (nova + wizard) e card de gestão no
  detalhe com save full-replace + advisory lock; sugestões = assessores do
  município; rótulo 'Visível para: Responsáveis · Candidato · Coordenador'
- int tests reescritos para a regra nova + 5 casos novos; e2e novo de
  visibilidade (lista/URL/gestão); fixture e2e com createCampaignDemand
- impl plan + changelog; importMap regenerado com S3_* dummy (OPS69)
O fail-closed silencioso da S3 deixava a assessoria cega: token errado não deixava
rastro além do picker vazio. Fim da caixa-preta:

- `instagramSyncStatus` (jsonb hidden) na global `social-feed-settings`, escrito pelo
  render, pelo afterChange (sync awaited quando a credencial IG muda) e pela rota
  `POST /api/social-feed/sync` (botão, auth admin + same-origin).
- `InstagramApiError` + `describeInstagramError`: o fetch parseia o body de erro da
  Graph API e o mapeia para copy pt-BR com a correção (OAuthException/expired →
  "token inválido/expirado ou emitido via Facebook Login — gere pelo Instagram Login";
  "Invalid user id" → confira o ID; 5xx → indisponível; TypeError → rede; AbortError →
  demorou demais). O ID só casa com "user" E "id" juntos (token rejections real falam
  "the user must be logged in" — não podem virar a branch de ID).
- `InstagramSyncStatusPanel` (campo ui irmão do picker): 5 estados fiéis ao rascunho;
  gate respeita o master `enabled`; `outcome` transiente limpo ao editar credencial;
  aria-live nos boxes interativos. Home inalterada (fail-closed intacto).
- Bug real: `afterChange` roda dentro da transação do save (row lock até os hooks
  retornarem); persistir o status via pool numa 2ª conexão deadlockava no lock da
  própria linha (~80-230s/save). Fix: persists do sync-hook na MESMA transação via
  `getPostgresTransactionDatabase` (C6); render/rota sem transação seguem no pool.
  Diagrama: UPDATE transactionid Lock vs idle in transaction; fix verificado 45ms vs 232s.
- Stub IG ganha estado `invalid-token`; runbook docs/ops/instagram-feed-token-runbook.md;
  migration add_instagram_sync_status (jsonb nullable).
- Débitos registrados: #124 (janela de lock) e #125 (polish P3).
- Gates: unit 2463 + int 791; lint/format/tsc/cycles; build; e2e admin 5/5 + frontend serial 19/19.
O  em instagramFeed.ts fechava um ciclo rejeitado pelo
check:cycles quando o grafo do config alcançava o feed via o hook da global
(config → global → instagramSync → instagramFeed → config; o madge segue
imports dinâmicos também). Move o loader cached (único importador de
@payload-config entre eles) para instagramFeedView.ts; instagramFeed vira puro
(fetch/parse/persist/describe/builders/gate), sem next/cache nem config. Com a
aresta config recurvada eliminada, o hook volta a ser um import estático — e o
config deixa de puxar unstable_cache, então as int suites que mockam next/cache
passam sem mudança. Gates: check:cycles 0, unit 2463, int 791, lint/tsc/build,
e2e frontend serial 19/19 + admin 5/5.
Cheapest Inference Config
(cherry picked from commit 60857ad34d)
merge(OPS75): convergência dos mains Forgejo + GitHub
Some checks failed
CI (PR) / checks (pull_request) Failing after 2s
PR Ready + auto-merge / ready-automerge (pull_request) Failing after 16s
Promote plan-issue Related on main merge / plan-issue-ready (pull_request) Has been skipped
Main merge flips claimed issues to done + in-prod / issue-done (pull_request) Successful in 14s
7e76c4f116
Unifica as duas linhagens divergentes pós-cutover OPS71 numa história única
no Forgejo main. Forgejo-only: S10 (pixel Meta), C141 (perfis de permissão,
rebase sobre S10). GitHub-only: OPS71-OPS74 (infra GitHub Actions), S8/S9
re-home, S11 (feed Instagram + painel sync), C143 (demandas responsáveis).

Resolução de conflitos: migrations/index.ts registra as 5 (newsletter,
S10 pixel, C141, C143 responsibles, S11 sync status); demands.ts mantém
a regra C143 de leitura + eixo de edição C141; newsletter usa a versão S10
(pixel); importMap restaurado para o do GitHub (handler S3, classe OPS69);
CHANGELOG-AGENTS.md regenerado via changelog:build (46 entradas).

Gates: tsc, lint, format, knip, cycles, unit 2457, int 815, build verdes;
migrations aplicadas ao teqo_test.
fsolla merged commit e7b4745552 into main 2026-08-21 01:47:57 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: fsolla/teqo#128
No description provided.