diff --git a/docs/plans/ops71-ci-github-actions-tracker-forgejo.md b/docs/plans/ops71-ci-github-actions-tracker-forgejo.md new file mode 100644 index 00000000..112f1b8a --- /dev/null +++ b/docs/plans/ops71-ci-github-actions-tracker-forgejo.md @@ -0,0 +1,241 @@ +--- +id: OPS71 +depends: [] +serializes: + [ + '.github/workflows/', + '.forgejo/workflows/', + 'docs/AGENT-OPS.md', + '.agents/skills/work-issue/', + '.agents/rules/agent-pr-workflow.mdc', + ] +priority: P1 +model: cursor-grok-4.5-high +--- + +# CI/PR do Forgejo Actions de volta para o GitHub Actions — tracker de Issues permanece no Forgejo + +Status: rascunho +Atualizado em: 2026-08-19 +Issue: #97 +Priority: P1 +Model: cursor-grok-4.5-high +Impeccable: A — N/A (sem UI) +Rascunho UI: N/A — sem UI +Appetite: ~2–3 dias eng; um outcome verificável (CI verde no GitHub, tracker vivo no Forgejo, deploy preservado) +Responsável: — + +## Intenção + +O Forgejo Runner roda na workstation (Ryzen 7 7800X3D) e **está pedindo muito +da máquina**: a suíte inteira (build + int + e2e) roda localmente a cada PR e +na janela de main, disputando CPU com o dev server, o Docker local, o browser +e os worktrees paralelos. O OPS65 já tentou mitigar (janela de 30 min + matar +o pool), mas o custo por PR continua pesado — e ele cresce com o número de PRs +abertos, que é exatamente o que queremos aumentar. + +A decisão: **migrar o CI de volta para o GitHub Actions** (runners hosted, +custo zero na workstation, um runner por PR — PRs avançam em paralelo de +verdade). **O tracker de Issues permanece no Forgejo local** (`git.solla.dev`, +já público via Cloudflare — confirmado HTTP 200 hoje): o CI no GitHub fala com +o tracker por API (`FORGEJO_API_TOKEN` + `FORGEJO_API_URL`), como os scripts +locais já fazem. Não é voltar ao mundo pré-OPS50 — é um novo meio-termo +deliberado: **código/PR/CI no GitHub, Issues/labels no Forgejo**. + +Mantemos a **estrutura de verificações** construída nos OPS61/OPS62/OPS64 +(job único `checks` sequencial, guards no início, skips de escopo via +`ci-scope.mjs`, rollup `CI (PR) / checks` como gate de merge) — o que muda é o +host onde ela roda. E a política de e2e muda junto (ver OPS72: e2e local +apenas para os testes afetados, não full no CI de PR). **O deploy deixa de ser +automático** (OPS53/OPS65 viram histórico): vira uma **action manual +(`workflow_dispatch`)** que roda **todas as verificações full** antes de +publicar — ver "Questões em aberto" (questão 1) e o fluxo abaixo. + +## Persona e fluxo + +- **Persona / contexto:** o dono da máquina (dev/coordenador do repo) rodando + dev local, worktrees e múltiplos PRs; os agentes (humanos e pool) que abrem + PRs e acompanham o CI até o merge; o coordenador que dispara o deploy quando + decide publicar. +- **Job principal:** cada PR custa ~0 da workstation; o CI roda no GitHub em + paralelo para todos os PRs; o merge continua automático e seguro; publicar + em produção é uma ação manual com verificação full. +- **Fluxo desejado:** + 1. Agente claima Issue no Forgejo (igual hoje — tracker intocado). + 2. Worktree/branch local; `pnpm push` para o **GitHub**; PR aberto no + **GitHub** (Ready, base `main`, `Closes #N`). + 3. GitHub Actions roda o job único `checks` (mesma cascata de hoje, com e2e + apenas nas áreas afetadas — OPS72) e posta o status no PR. + 4. Merge automático quando o rollup `CI (PR) / checks` fica verde + (auto-merge nativo ou safety net equivalente). + 5. Pós-merge: Issue flipada para `done`/`in-prod` no **Forgejo** (workflow + GitHub Actions → API do Forgejo); `Related #N` promove `blocked` → + `ready` no Forgejo. + 6. Deploy **manual** (workflow_dispatch): rodar verificações full → o job + `deploy` executa `scripts/deploy-homeserver.sh` no homeserver (runner + self-hosted lá, ou via SSH da workstation como fallback — questão 1). +- **Anti-goals de produto:** NÃO migrar o tracker (Issues/labels/claims ficam + no Forgejo); NÃO re-introduzir jobs paralelos com matrix no CI (a estrutura + de job único do OPS62 vale no GitHub também); NÃO tocar em schema/migrations + de produto; NÃO re-publicar Vercel ou qualquer segundo deploy; deploy NÃO + volta a ser automático a cada merge. + +## Objetivo e aceite + +- O CI de PR (`ci-pr.yml`) roda no GitHub Actions com a **mesma cascata de + verificações** de hoje (guards → lint → format → typecheck → knip → cycles → + unit → int → build → e2e **selected/afetado**, com skips de escopo), como + **um job único `checks`**. +- O **deploy** vira uma action **manual** (`workflow_dispatch` no GitHub + Actions) com **dois jobs no mesmo workflow**: `verify` (hosted + `ubuntu-latest`, suíte **full** incl. e2e full) → `deploy` + (`needs: [verify]`, `runs-on: [self-hosted, homeserver]` — runner do GitHub + no homeserver executa `scripts/deploy-homeserver.sh` localmente, sem SSH). + O hosted nunca toca o homeserver; o self-hosted não conta minutos hosted. + Sem verificador de main (`ci.yml`) — só o pre-deploy roda full + (decisão do usuário, gate 2026-08-19). +- O rollup `CI (PR) / checks` continua sendo o required check de `main` e o + gate do merge automático (agora no GitHub). +- PRs são abertas no GitHub; agentes acompanham o CI até o merge em `main` + (skills atualizadas — work-issue/agent-work-issue/execution-pipeline, + AGENT-OPS, agent-pr-workflow). +- As Issues **não migram**: labels, claims, `pnpm issue`/`agent:*` continuam + falando com o Forgejo; flips pós-merge funcionam via API (verificado: o + Forgejo é alcançável publicamente). +- A workstation não roda mais a suíte de CI; o Forgejo runner é desligado. +- Deploy manual com verificação full preservado (ver questão 1). +- Um repo GitHub sincronizado (histórico atual de `main` presente — hoje o + repo GitHub está parado em 2026-08-13). + +## Dados (intenção) + +- **Vou apresentar dados?** Não. +- **Decisões desbloqueadas:** nenhuma KPI nova. (O "custo da workstation" é + observável pela própria máquina; não precisa de métrica nova.) + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** + - `.github/workflows/` (novo): `ci-pr.yml` (job único `checks`, e2e + selected — OPS72), `deploy.yml` (**`workflow_dispatch` manual, dois jobs: + `verify` hosted full → `deploy` `runs-on: [self-hosted, homeserver]`**), + `agent-pr-ready-automerge.yml` (safety net), `issue-done-on-main-merge.yml`, + `plan-issue-ready-on-main-merge.yml`, `archive-cursor-agent.yml`. Sem + `ci.yml` (verificador de main eliminado — decisão do gate). + - `scripts/`: adaptar `forgejo-pr-automerge.mjs` (ou substituir por + auto-merge nativo do GitHub / API `merge_when_checks_succeed`), + `forgejo-issue-transition.mjs` e `agent-promote-related-on-merge.mjs` + (leem o PR — agora do GitHub — e flipam no Forgejo), + `forgejo-dispatch.mjs` → `gh workflow run` (ou API GitHub), novos helpers + GitHub (`scripts/lib/`); `configure-branch-protection.mjs` → proteção no + GitHub. + - `scripts/deploy-homeserver.sh` — revisar o modo de invocação (runner no + homeserver: sem `ssh`, execução local; via workstation: SSH mantido) e o + `TEQO_REPO_URL` (hoje `localhost:3000` do Forgejo no homeserver — com o + código no GitHub, apontar para o GitHub ou manter o espelho local). + - `.agents/skills/**`, `.agents/rules/agent-pr-workflow.mdc`, + `docs/AGENT-OPS.md`, `AGENTS.md` — o paradigma de PR passa a apontar para + o GitHub (PR/CI), tracker Forgejo. +- **Precedente a olhar:** `docs/plans/ops50-ci-github-para-forgejo.md` (o + espelho inverso — o que foi migrado para o Forgejo e como), `ops62`, + `ops65`, `ops61` (branch protection/contratos de merge), `docs/ops/teqo-1313-deploy.md`, + e o projeto **`infra-solla`** (ver "Nota para o executor"). +- **Risco de acoplamento:** o deploy (OPS53) hoje depende do runner local da + workstation (SSH para o homeserver) e o build de produção **não pode sair do + homeserver** (migrate no banco de prod antes do build — OPS66, estático na + rede do compose, registry local `localhost:5000`). O GitHub hosted não + alcança nada disso — por isso o job `deploy` roda no runner self-hosted do + homeserver (decisão do gate). O `ci-classify-production.mjs` (gate OPS65) + morre com o deploy manual (sem janela automática) — pode ser removido ou + reutilizado no dispatch como check informativo de "produção mudou desde o + último deploy". + +## Nota para o executor — projeto `infra-solla` + +Para detalhes do homeserver (stack, registry, tunel, runner, rede, `ssh +homeserver` via tailnet), **leia o projeto `infra-solla`** (fora deste repo): +`STATE.md` e `plano-infra-final.md` §"Arquitetura de deploy". É a fonte usada +pelo OPS53 para corrigir a premissa de onde rodava o runner, e é onde o +passo manual "instalar o runner self-hosted do GitHub no homeserver" deve ser +registrado/anotado. Não invente topologia: confirme lá (homeserver 8c/16GB, +laptop; `~/stack/docker-compose.yml`; registry `localhost:5000` com htpasswd; +cloudflared; `ssh homeserver` pela tailnet) antes de decidir como o runner +self-hosted se instala e como o `deploy.yml` o alcança. + +## Dependências + +- OPS72 (e2e local afetado + CI de PR sem e2e full) — **serializa com este + item** (ambos tocam `ci-pr.yml` e a skill work-issue); OPS72 declara + `depends: [OPS71]` para rodar depois e evitar rebase cruzado. Decisão de + gate: CI de PR mantém e2e **selected** (afetado) — confirmado. +- #85 (OPS70, gate skipped no schedule do Forgejo) — **absorvida**: com o + deploy manual, o gate de schedule deixa de existir; fechar como superada + quando este item mergear. +- #87 (OPS70, guard de importMap) — independente, permanece na fila; o + executor dela deve mirar os workflows novos do GitHub. +- #72 (S3-FOLLOWUP, e2e local `--no-deps` colide no seedTestUser) — não é + duplicada; o OPS72 (e2e local afetado) cita a limitação existente. + +## Fora de escopo + +- Migrar Issues/labels do Forgejo (anti-goal explícito). +- Mudar a estrutura de verificações (job único, guards, skips) — preservada. +- Alterar o `e2e-affected`/`ci-scope` em si — mecânica já existe; só muda + onde/quando roda (OPS72). +- Repositório do Forgejo: continua como tracker; se virar mirror read-only de + main ou parar de receber push — decisão do impl, mas nunca fecha o tracker. +- Upgrade do Forgejo no homeserver (decisão de infra separada). + +## Rabbit holes de produto + +- **Re-engenharia do deploy.** O job `deploy` dependia do runner local; sem + decisão, o "preservar deploy" vira um projeto de infra. **Corte neste item:** + uma das opções da questão 1, fechada no gate, sem inventar terceiro caminho. +- **Reescrever o contrato de merge.** Auto-merge do GitHub é nativo, mas o + wait-por-checks/rollup do OPS64 foi calibrado para o Forgejo; portar + errado = merge com CI vermelho. **Corte:** usar o mecanismo nativo do GitHub + (o rollup `CI (PR) / checks` vira o required check lá) e pin unit-testado no + script, sem reimplementar semáforos novos. +- **"Já que estamos no GitHub, migra as Issues também".** O usuário foi + explícito: tracker fica no Forgejo. **Corte:** qualquer proposta de migrar + o tracker é rejeitada neste item. + +## Questões em aberto (produto) + +- **Como fica o deploy?** **Decisão do usuário (gate 2026-08-19):** deploy + **manual** via `workflow_dispatch` no GitHub Actions — não é mais automático + a cada merge. O job de deploy roda **todas as verificações full** antes de + publicar. **Formato (confirmado):** **dois jobs no mesmo `deploy.yml`** — + `verify` no hosted `ubuntu-latest` (suíte full) e `deploy` com + `needs: [verify]` rodando em **runner self-hosted do GitHub no homeserver** + (conecta outbound ao GitHub — funciona atrás do Cloudflare tunnel; o job + executa `scripts/deploy-homeserver.sh` localmente, sem SSH; não conta + minutos hosted). O build de produção **não pode rodar fora do homeserver** + (migrate pré-build no banco de prod — OPS66, estático na rede do compose, + registry local `localhost:5000`) — por isso o hosted só verifica e o + self-hosted só build/deploy. Fallbacks registrados (não escolhidos): runner + na workstation via SSH (B) ou runbook manual no homeserver (C). +- **E o ci.yml (verificador de main)?** **Decidido (gate 2026-08-19):** + **eliminar** — fica só o pre-deploy (`verify` do dispatch manual) como + verificação full. Motivo: custo mínimo de Actions; o deploy é deliberado e + roda full na hora; se um dia o merge em main quiser rede de regressão, o + `ci.yml` volta com 1 linha de trigger (sem deploy). +- **Fatiamento do lote?** **Opções:** A) OPS71 (este, CI+PR+merge+deploy) e + OPS72 (e2e local afetado) separados com `depends`; B) um único item com tudo. + **Recomendação:** A — cada um tem outcome verificável sozinho e o OPS72 + (skill work-issue + ci-pr sem e2e full) é pequeno. _(assumido — validar)_ +- **Repo GitHub defasado.** Hoje parado em 2026-08-13 (pré-OPS50). **Opções:** + A) re-sincronizar via push do estado local (histórico é ancestral comum — + fast-forward limpo); B) import novo (histórico duplicado/PRs antigas órfãs). + **Recomendação:** A. _(assumido — validar)_ + +## Referências + +- GitHub Issue: #97 (OPS71) +- Rascunho UI (gate): N/A +- `.forgejo/workflows/` (a remover após cutover), `docs/plans/ops50-ci-github-para-forgejo.md`, + `docs/plans/ops62-ci-fail-fast-forgejo.md`, `docs/plans/ops65-ci-main-janela-30min-e-matar-pool.md`, + `docs/plans/ops61-forgejo-contratos-merge-labels-e-branch-protection.md`, + `scripts/forgejo-pr-automerge.mjs`, `scripts/forgejo-issue-transition.mjs`, + `scripts/forgejo-dispatch.mjs`, `scripts/configure-branch-protection.mjs`, + `scripts/ci-classify-production.mjs`, `docs/ops/teqo-1313-deploy.md` diff --git a/docs/plans/ops72-e2e-local-afetado-ci-sem-full.md b/docs/plans/ops72-e2e-local-afetado-ci-sem-full.md new file mode 100644 index 00000000..246594eb --- /dev/null +++ b/docs/plans/ops72-e2e-local-afetado-ci-sem-full.md @@ -0,0 +1,146 @@ +--- +id: OPS72 +depends: [OPS71] +serializes: ['ci-pr.yml', '.agents/skills/work-issue/'] +priority: P1 +model: composer-2.5 +--- + +# E2E local apenas para os testes afetados — CI de PR sem e2e full + +Status: rascunho +Atualizado em: 2026-08-19 +Issue: #98 +Priority: P1 +Model: composer-2.5 +Impeccable: A — N/A (sem UI) +Rascunho UI: N/A — sem UI +Appetite: ~0,5–1 dia eng; um outcome verificável (skill work-issue exige e2e afetado local; CI de PR não roda e2e full) +Responsável: — + +## Intenção + +O e2e é a etapa mais cara do CI de PR (build `.next-e2e` + playwright + +suíte inteira). Com o CI voltando para o GitHub Actions (OPS71), o custo por +PR deixa de ser a CPU da workstation — mas o tempo de veredito por PR continua +importando: e2e full é a maior fatia do job único `checks` (OPS62), e a +política de hoje (OPS59: "e2e roda UMA vez, no CI; local opcional") significa +que cada PR paga a suíte e2e inteira, inclusive specs em superfícies que o PR +não tocou. + +Queremos: **e2e no CI de PR apenas para os testes afetados** (novos testes +criados + specs da mesma superfície trabalhada — a mecânica `e2e selected` já +existe via `ci-scope.mjs`/manifest do OPS59), e o **restante da cobertura e2e +roda localmente** — também apenas nos afetados — como passo do fluxo do agente +antes do PR (atualizar a skill `work-issue` para exigir isso). O e2e **full** +fica para o **deploy manual** (workflow_dispatch do OPS71) e para o verificador +de main — como rede de segurança antes de publicar — sem nunca rodar full por +PR. + +**Decisão do gate (2026-08-19):** o CI de PR **continua rodando o e2e das +áreas afetadas** (modo `selected`) — confirmado pelo usuário; o e2e local +afetado é o passo da skill; o full fica no deploy/verificador de main. + +## Persona e fluxo + +- **Persona / contexto:** o agente (humano no worktree ou worker do pool) que + implementa uma Issue e abre o PR; o dono da máquina que não quer o CI + ocupando o runner por 40+ min por PR. +- **Job principal:** saber que os testes novos/afetados do seu PR passam, + com custo mínimo de CI e feedback local rápido. +- **Fluxo desejado:** + 1. Agente implementa; roda os gates locais de sempre (`pnpm gate:push`). + 2. **Novo passo obrigatório:** roda `pnpm test:e2e:affected` (ou o modo + selecionado equivalente) — cobre os novos testes criados + specs da + mesma superfície trabalhada. + 3. CI de PR roda a cascata **sem e2e full**: apenas o conjunto afetado + (modo `selected` — já suportado pelo classifier; decisão do gate). + 4. Deploy manual (OPS71) e verificador de main rodam e2e **full** antes de + publicar — a rede de segurança final inegociável. +- **Anti-goals de produto:** NÃO remover o e2e do deploy/verificador de main; + NÃO deixar o agente pular o e2e local sem justificativa; NÃO reintroduzir + "e2e é só no CI" (OPS59 era isso e custa caro por PR). + +## Objetivo e aceite + +- Skill `work-issue` (e `execution-pipeline.md`, `agent-work-issue` onde + aplicável) passam a exigir, antes do `pnpm push`/PR: **e2e local afetado** + (`pnpm test:e2e:affected`), com o critério "novos testes + mesma superfície" + documentado. +- CI de PR: e2e roda em modo `selected` (manifest afetado) — nunca `full`. O + classificador de escopo (`ci-scope.mjs`) é a fonte da decisão. +- Deploy manual e verificador de main: e2e **full** preservado (rede de + segurança pré-deploy/pré-publicação). +- A skill `work-issue` documenta a limitação da #72 (e2e local com `--no-deps` + - projetos paralelos colide no `seedTestUser` — usar `--workers=1` ou a + cadeia padrão de projetos). +- Gate local (`pnpm gate:push`) intocado — e2e continua fora do pre-push; o + e2e local é passo **explícito do fluxo da skill**, não do hook. + +## Dados (intenção) + +- **Vou apresentar dados?** Não. +- **Decisões desbloqueadas:** o agente decide o conjunto afetado com base no + diff da superfície — critério documentado na skill, não métrica nova. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** + - `.agents/skills/work-issue/SKILL.md` e `execution-pipeline.md` — novo + passo "e2e local afetado" antes do push; delta para `agent-work-issue` + quando fizer sentido. + - `.forgejo/workflows/ci-pr.yml` → (OPS71) `.github/workflows/ci-pr.yml` — + etapa e2e apenas `selected`; remover `full` do modo PR. + - `.github/workflows/ci.yml` (verificador de main) e `deploy.yml` (dispatch + manual) — e2e `full` preservado neles. + - `docs/AGENT-OPS.md`, `AGENTS.md` (política "e2e = CI" do OPS59 vira + "e2e afetado = local + CI selected; full = deploy/verificador de main"). +- **Precedente a olhar:** `docs/plans/ops59-gate-local-sem-e2e.md` (política + atual), `ci-scope.mjs` (modos `full/selected/none`), `e2e-affected-manifest.mjs`, + `docs/plans/ops62-ci-fail-fast-forgejo.md`. +- **Risco de acoplamento:** toca a mesma superfície do OPS71 (workflows + a + skill work-issue é citada pelo fluxo de PR) — por isso `depends: [OPS71]` e + serialização explícita (OPS71 serializa `ci-pr.yml` e `work-issue`). + +## Dependências + +- OPS71 (CI no GitHub) — este item edita o `ci-pr.yml` novo e a skill que o + OPS71 já aponta para o GitHub; `depends: [OPS71]`. +- #72 (S3-FOLLOWUP) — citada como limitação documentada; não é duplicada nem + absorvida (fica na fila). + +## Fora de escopo + +- Mudar a mecânica do classifier/manifest (`ci-scope.mjs`, + `e2e-affected-manifest.mjs`) — funciona; só muda onde/em que modo roda. +- Implementar o fix da #72. +- Remover o e2e do deploy manual / verificador de main. + +## Rabbit holes de produto + +- **"Se é afetado, nem precisa do CI e2e".** O CI selected é a rede de + segurança quando o agente erra o critério (esqueceu um spec da mesma + superfície) — e é o que o usuário pediu para manter. **Corte:** CI selected + permanece; local é complementar, não substituto. +- **"Full só no deploy cria gap de regressão".** Aceito e deliberado: o full + gateia a publicação (deploy manual) e o verificador de main; PRs afetados + cobrem a superfície do diff. **Corte:** não reintroduzir full por PR. + +## Questões em aberto (produto) + +- **CI de PR: e2e selected ou nenhum e2e?** **Decidido no gate (2026-08-19):** + e2e **selected** (manifest afetado) no CI de PR — o usuário confirmou + ("O CI PR deve continuar fazendo o e2e das áreas afetadas"). +- **O e2e local afetado vale também para o pool (`agent-work-issue`)?** + **Opções:** A) sim, mesmo passo obrigatório; B) pool roda só o CI selected + (workers Cloud sem worktree local com browsers instalados). + **Recomendação:** A quando o worktree local tiver browser; senão B com + justificativa registrada. _(assumido — validar)_ + +## Referências + +- GitHub Issue: #98 (OPS72) +- Rascunho UI (gate): N/A +- `.agents/skills/work-issue/SKILL.md`, `.agents/skills/work-issue/execution-pipeline.md`, + `scripts/ci-scope.mjs`, `scripts/lib/e2e-affected-manifest.mjs`, + `docs/plans/ops59-gate-local-sem-e2e.md`, `docs/plans/ops62-ci-fail-fast-forgejo.md`