From 6f6e9fb4b4896ac30284e9c3f52638a5eeed33b9 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 24 Aug 2026 14:47:40 -0300 Subject: [PATCH] =?UTF-8?q?ops(OPS91):=20sweep=20p=C3=B3s-cutover=20?= =?UTF-8?q?=E2=80=94=20textos=20vivos=20que=20apontavam=20o=20tracker=20ao?= =?UTF-8?q?=20Forgejo=20passam=20para=20o=20GitHub=20(Closes=20#841)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/rules/agent-pr-workflow.mdc | 2 +- .agents/skills/engineering-audit/SKILL.md | 2 +- .agents/skills/plan-issue/SKILL.md | 2 +- .agents/skills/project-status/SKILL.md | 2 +- .../skills/work-issue/execution-pipeline.md | 6 +- AGENTS-infra.md | 2 +- README.md | 8 +- docs/changelog/2026-08-24-ops91.md | 1 + ...p-pos-cutover-forgejo-textos-vivos-impl.md | 113 ++++++++++++++++++ docs/roadmap.md | 2 +- scripts/agent-status.mjs | 2 +- scripts/github-pr.mjs | 2 +- scripts/issue-transition-on-merge.mjs | 14 +-- scripts/lib/agent-forgejo.mjs | 4 +- scripts/lib/cli.mjs | 22 ++-- scripts/lib/github-api.mjs | 8 +- 16 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 docs/changelog/2026-08-24-ops91.md create mode 100644 docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md diff --git a/.agents/rules/agent-pr-workflow.mdc b/.agents/rules/agent-pr-workflow.mdc index 702ec9fb..7f7cfed1 100644 --- a/.agents/rules/agent-pr-workflow.mdc +++ b/.agents/rules/agent-pr-workflow.mdc @@ -5,7 +5,7 @@ alwaysApply: true # Agent PR workflow (Teqo) -**This repo overrides any user-level or Cloud-tool default that says "create PRs as draft".** Agent deliveries merge to `main` via required checks + GitHub's native auto-merge (OPS71). The ISSUE TRACKER stays on the Forgejo — GitHub is only the Actions host. +**This repo overrides any user-level or Cloud-tool default that says "create PRs as draft".** Agent deliveries merge to `main` via required checks + GitHub's native auto-merge (OPS71). The ISSUE TRACKER lives on GitHub too (OPS76, 2026-08-21) — GitHub is the single host for code, CI, PRs and Issues. ## Create (Ready, base main) diff --git a/.agents/skills/engineering-audit/SKILL.md b/.agents/skills/engineering-audit/SKILL.md index ec59bd10..bd5df963 100644 --- a/.agents/skills/engineering-audit/SKILL.md +++ b/.agents/skills/engineering-audit/SKILL.md @@ -66,7 +66,7 @@ Antes da varredura, colha os défices comportamentais registrados pelo fluxo: pnpm issue all # kind `agent-miss` nos contadores; ou api.listIssues({ state: 'open', labels: 'kind:agent-miss' }) ``` -**Investigue cada miss colhida** (leitura da Issue via API do Forgejo/MCP — disponível no Cloud): o corpo registra causa raiz, fix aplicado e o guardrail proposto por `agent:file-miss`. Verifique no código se o fix segurou e se a classe do defeito ainda é reproduzível. Miss cuja classe já tem guarda viva não gera guardrail novo — gera item de hardening da guarda existente (o passo 2 já trata guarda dodgeable como finding). +**Investigue cada miss colhida** (leitura da Issue via API do GitHub/MCP — disponível no Cloud): o corpo registra causa raiz, fix aplicado e o guardrail proposto por `agent:file-miss`. Verifique no código se o fix segurou e se a classe do defeito ainda é reproduzível. Miss cuja classe já tem guarda viva não gera guardrail novo — gera item de hardening da guarda existente (o passo 2 já trata guarda dodgeable como finding). Cada Issue colhida alimenta o **Passo 4b (recurrence prevention)**: toda miss vira candidata a guardrail determinístico (tipo / ESLint / convention spec / CI / pin comportamental). O ledger desses guardrails vive em **`docs/GUARDRAILS.md`** (criar o arquivo na primeira execução — é o escopo da Issue OPS2): uma linha por guardrail — miss de origem (link da Issue), classe 1–6, mecanismo, status. Quando o guardrail mergeia, **feche as Issues colhidas** (`done`) com comentário apontando o guardrail. Em Cursor Cloud o fechamento é determinístico e não exige escrita manual: o PR do guardrail carrega `Closes #N` (keyword repetida por número — o regex do `issue-done-on-main-merge.yml` não resolve `Closes #A, #B`) e o merge em `main` flippa a miss para `done`; guardrails de várias misses podem ir no mesmo PR desde que cada uma tenha seu `closes`. Miss que não admite guardrail determinístico vira convenção explícita marcada "judgment-only" — não fingir que doc é guarda. diff --git a/.agents/skills/plan-issue/SKILL.md b/.agents/skills/plan-issue/SKILL.md index 90aacba1..6d8cbf9a 100644 --- a/.agents/skills/plan-issue/SKILL.md +++ b/.agents/skills/plan-issue/SKILL.md @@ -20,7 +20,7 @@ parse (main agent) **Regras duras:** -1. **Nada no Forgejo antes do gate.** Antes da confirmação: proibido `pnpm agent:register`, criar Issue/PR. +1. **Nada no tracker antes do gate.** Antes da confirmação: proibido `pnpm agent:register`, criar Issue/PR. 2. **Register com `--plan` nasce `blocked`.** Promote só depois do plano em `main`. 3. **Planos de Issues `in-progress`/`done`/`in-prod` são imutáveis.** 4. **Rascunho UI (obrigatório se muda UI):** HTML+Tailwind commitado no repo. Classe A/sem UI → sem rascunho. diff --git a/.agents/skills/project-status/SKILL.md b/.agents/skills/project-status/SKILL.md index 946656b4..1bd49209 100644 --- a/.agents/skills/project-status/SKILL.md +++ b/.agents/skills/project-status/SKILL.md @@ -22,6 +22,6 @@ pnpm agent:status - **Bloqueios** — cada `blocked` com o motivo (dep aberta, jurídico, `requirements-changed`). - **Sugestões de consolidação** — as heurísticas do script (merge por domínio+prio; break-down por gargalo de depends) como **texto para o usuário decidir**. Nunca aplique consolidação aqui: aplicação é manual ou via `plan-issue`/`pnpm agent:register`. Nunca proponha nada sobre Issues `in-progress`. -3. Se o usuário pedir interpretação além da saída (ex.: "o que destrava mais coisa?"), responda a partir do grafo — sem inventar dados que o script não imprimiu; se faltar dado, leia a Issue via API do Forgejo (MCP/`pnpm issue`) somente-leitura. +3. Se o usuário pedir interpretação além da saída (ex.: "o que destrava mais coisa?"), responda a partir do grafo — sem inventar dados que o script não imprimiu; se faltar dado, leia a Issue via API do GitHub (MCP/`pnpm issue`) somente-leitura. **NÃO faz:** editar `docs/roadmap.md` (legado congelado), claim, registro, qualquer escrita de Issue. diff --git a/.agents/skills/work-issue/execution-pipeline.md b/.agents/skills/work-issue/execution-pipeline.md index 4fd3675f..4288d572 100644 --- a/.agents/skills/work-issue/execution-pipeline.md +++ b/.agents/skills/work-issue/execution-pipeline.md @@ -69,14 +69,14 @@ antes do deploy**. para conferir a leitura completa localmente (seed do HISTORY). O diff commitado é só a entrada nova. 3. **`pnpm push -u origin HEAD`** — origin é o **GitHub** (OPS71; o tracker de - Issues continua no Forgejo por API). + Issues também vive no GitHub — OPS76). 4. PR no **GitHub** via `GITHUB_TOKEN= node scripts/github-pr.mjs --head --title " — " --body-file ` — **Ready** (nunca draft; o script não tem flag draft), base `main`, body com `Closes #` (ou `Related #N` em plans-only). Em Cursor Cloud: `ManagePullRequest` com `draft: false`. 5. O safety net `agent-pr-ready-automerge.yml` arma o **auto-merge nativo do GitHub** (`enablePullRequestAutoMerge`, rebase) — o servidor mergea quando o required check `CI (PR) / checks` fica verde; nada a armar. 6. No merge, o workflow `issue-done-on-main-merge.yml` flipa `done`/`in-prod` - **no Forgejo** (lê o body do PR via API do GitHub, escreve no tracker por - `FORGEJO_API_TOKEN`). Comente na Issue o desfecho em uma linha. + **no GitHub** (lê o body do PR via API do GitHub, escreve no tracker por + `GITHUB_TOKEN`). Comente na Issue o desfecho em uma linha. ## Deltas por ator diff --git a/AGENTS-infra.md b/AGENTS-infra.md index 032f8f3b..c5699105 100644 --- a/AGENTS-infra.md +++ b/AGENTS-infra.md @@ -2,7 +2,7 @@ ## Deploy & CI -Production is self-hosted on the homeserver (`jorgesolla1313.com.br`, behind the Cloudflare tunnel; container/database `teqo-1313`/`teqo_1313`). **There is no Vercel deploy anymore** — the Vercel deploy job, `vercel-promote.yml`, the Vercel scripts/`vercel.json` and the `VERCEL_*` repo secrets were removed in OPS50 (2026-08-17). **CI/PR/deploy live on GitHub Actions (OPS71, 2026-08-19); the ISSUE TRACKER stays on the Forgejo** (`git.solla.dev/fsolla/teqo` — labels, claims, `pnpm issue`/`agent:*` and the post-merge flips all keep talking to the Forgejo API; GitHub is only the Actions host). The PR CI (`ci-pr.yml`, job único `checks` — OPS62) runs the same cascade as always (guards → lint → format → typecheck → knip → cycles → unit → int → build → e2e selected/afetado, skips via `ci-scope.mjs`); its check-run `CI (PR) / checks` is the required check of `main`. **There is NO main verifier (`ci.yml`) anymore**: publishing is a **manual `workflow_dispatch`** (`.github/workflows/deploy.yml`) with two jobs — `verify` (hosted `ubuntu-latest`, suíte **full** incl. e2e full) → `deploy` (`needs: [verify]`, `runs-on: [self-hosted, homeserver]`): the GitHub self-hosted runner ON the homeserver executes `scripts/deploy-homeserver.sh` **locally** (no SSH; outbound-only runner, works behind the tunnel; the hosted job never touches the homeserver). The remote Docker deployment is gone; the production container runs the monolith directly. The Docker Compose file is used ONLY for local dev databases. The `run-deploy.yml` (OPS38/OPS46) was deleted in OPS71. There is no `run-deploy.yml` or equivalent in GitHub Actions — deploy is `deploy.yml` only. +Production is self-hosted on the homeserver (`jorgesolla1313.com.br`, behind the Cloudflare tunnel; container/database `teqo-1313`/`teqo_1313`). **There is no Vercel deploy anymore** — the Vercel deploy job, `vercel-promote.yml`, the Vercel scripts/`vercel.json` and the `VERCEL_*` repo secrets were removed in OPS50 (2026-08-17). **CI/PR/deploy AND the ISSUE TRACKER live on GitHub (OPS71 + OPS76, 2026-08-21)** (`github.com/fsolla/teqo/issues` — labels, claims, `pnpm issue`/`agent:*` and the post-merge flips talk to the GitHub API; GitHub is the single host). The PR CI (`ci-pr.yml`, job único `checks` — OPS62) runs the same cascade as always (guards → lint → format → typecheck → knip → cycles → unit → int → build → e2e selected/afetado, skips via `ci-scope.mjs`); its check-run `CI (PR) / checks` is the required check of `main`. **There is NO main verifier (`ci.yml`) anymore**: publishing is a **manual `workflow_dispatch`** (`.github/workflows/deploy.yml`) with two jobs — `verify` (hosted `ubuntu-latest`, suíte **full** incl. e2e full) → `deploy` (`needs: [verify]`, `runs-on: [self-hosted, homeserver]`): the GitHub self-hosted runner ON the homeserver executes `scripts/deploy-homeserver.sh` **locally** (no SSH; outbound-only runner, works behind the tunnel; the hosted job never touches the homeserver). The remote Docker deployment is gone; the production container runs the monolith directly. The Docker Compose file is used ONLY for local dev databases. The `run-deploy.yml` (OPS38/OPS46) was deleted in OPS71. There is no `run-deploy.yml` or equivalent in GitHub Actions — deploy is `deploy.yml` only. **Guarding `pnpm push` (OPS24+):** every PR push goes through `scripts/git-push.mjs`, which runs `ensure-repo-deps` (install + Husky in worktrees — OPS51), then `gate:push` (the pre-push hook), which chains `gate:ci` — a serialized mirror of the CI `checks` job (**without e2e**: OPS72 moved e2e to an explicit skill step) — plus the `docs-guards` (changelog entries additions-only + frozen HISTORY snapshot + uncommitted regenerable aggregate — OPS63/OPS85; conflict markers; the rewrite escape stays in CI only). The hook blocks `git push` on failure; escape: `git push --no-verify` (WIP only). There is no YAML `push` gate — the hook is the gate. **CI red on your PR is yours**, even pre-existing or infra — "out of feature scope" is not a stop condition (policy "Dono do PR, dono do CI" in `docs/AGENT-OPS.md`). diff --git a/README.md b/README.md index 3dad0584..07121fc4 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ > **Cheatsheet de operação agentic (humanos)** — o fluxo em 5 linhas: > -> 1. O agente roda `pnpm agent:claim` (ou `pnpm worktree next --issue N`) e pega a próxima Issue `ready` por prioridade (`prio:P0..P3`). O **tracker de Issues vive no Forgejo** (`git.solla.dev/fsolla/teqo`); o código/PR/CI vive no **GitHub**. +> 1. O agente roda `pnpm agent:claim` (ou `pnpm worktree next --issue N`) e pega a próxima Issue `ready` por prioridade (`prio:P0..P3`). O **tracker de Issues, o código, os PRs e o CI vivem no GitHub** (`github.com/fsolla/teqo`). > 2. Ele implementa, roda o fast gate (`lint + typecheck + unit`) e `pnpm push -u origin HEAD` — abre a PR para `main` (Ready, `Closes #N`). > 3. CI verde (`CI (PR) / checks`) → o safety net `agent-pr-ready-automerge.yml` arma o **auto-merge nativo** (rebase) — o servidor só mergea com o required check verde. > 4. Publicar é **manual**: dispatch de `deploy.yml` roda a suíte full (`verify`) e o job `deploy` publica no homeserver (`jorgesolla1313.com.br`, runner self-hosted) — migrações aplicadas antes do rollout. Nada é automático pós-merge. -> 5. Secrets humanos (uma vez): `FORGEJO_API_TOKEN` no GitHub (flips pós-merge no Forgejo); envs de prod em `~/stack/teqo-1313.env` no homeserver. Branch protection de `main` já aplicada (reaplicar: `pnpm configure:branch-protection`). +> 5. Secrets humanos (uma vez): `GITHUB_TOKEN` (PAT com escopo `repo` + `issues: write`) para os scripts de agente/PR (`pnpm issue`/`agent:*`, `github-pr.mjs`); envs de prod em `~/stack/teqo-1313.env` no homeserver. Branch protection de `main` já aplicada (reaplicar: `pnpm configure:branch-protection`). > > Comandos: `pnpm agent:claim | agent:register | agent:prioritize | agent:file-miss | worktree next` e `pnpm db:seed:minimal`. > Labels: estado `ready|in-progress|blocked|done|in-prod`, `prio:P0..P3`, `kind:*`, `needs:migration|consent`, `requirements-changed`. @@ -113,7 +113,7 @@ Then run: - `pnpm test:e2e` — Playwright (requires the test DB schema and a free port) - `pnpm test:all` — unit + integration + E2E -Quality gates (also enforced by CI on the Forgejo Actions): `pnpm lint` (zero warnings — `--max-warnings=0`), `pnpm typecheck`, and `pnpm exec knip` (dead files/dependencies fail; delete what your change orphaned). Standards: `.agents/rules/engineering-standards.mdc`. +Quality gates (also enforced by CI on GitHub Actions — `ci-pr.yml`): `pnpm lint` (zero warnings — `--max-warnings=0`), `pnpm typecheck`, and `pnpm exec knip` (dead files/dependencies fail; delete what your change orphaned). Standards: `.agents/rules/engineering-standards.mdc`. ## Tech Stack @@ -130,4 +130,4 @@ Internal campaign tool for municípios (435 pré-definidos), local leaderships, ## Roadmap -Backlog and future plans live as tracked [Forgejo Issues](https://git.solla.dev/fsolla/teqo/issues) (spec + deps + prio + model per issue; flow skills: `plan-issue` → `work-issue` → `project-status`; `docs/roadmap.md` is a frozen legacy stub). +Backlog and future plans live as tracked [GitHub Issues](https://github.com/fsolla/teqo/issues) (spec + deps + prio + model per issue; flow skills: `plan-issue` → `work-issue` → `project-status`; `docs/roadmap.md` is a frozen legacy stub). diff --git a/docs/changelog/2026-08-24-ops91.md b/docs/changelog/2026-08-24-ops91.md new file mode 100644 index 00000000..be7241c5 --- /dev/null +++ b/docs/changelog/2026-08-24-ops91.md @@ -0,0 +1 @@ +**OPS91 — sweep pós-cutover OPS76: textos vivos que ainda apontavam o tracker de Issues ao Forgejo (2026-08-24):** docs-only, zero mudança de comportamento. O cutover OPS71+OPS76 (2026-08) tornou o GitHub o host único (CI + tracker de Issues em `github.com/fsolla/teqo/issues`, labels/claims/flips via API do GitHub com `GITHUB_TOKEN`); o Forgejo ficou congelado. A varredura da sessão OPS89 (#836) virou esta Issue (#841): **23 alvos de prosa/comentário corrigidos in place em 14 arquivos (30+/30−)**, cada arquivo dono da própria frase, com `AGENTS.md:15` como fraseado canônico. **Classe (a) — tracker/API apontando o Forgejo → GitHub (12):** `.agents/rules/agent-pr-workflow.mdc:8` ("ISSUE TRACKER stays on the Forgejo" → "lives on GitHub too (OPS76) — single host"); `.agents/skills/work-issue/execution-pipeline.md:69,75-76` (flip pós-merge "no Forgejo" via `FORGEJO_API_TOKEN` → "no GitHub" via `GITHUB_TOKEN`); `.agents/skills/project-status/SKILL.md:25` e `engineering-audit/SKILL.md:69` (leitura da Issue "via API do Forgejo" → GitHub); `.agents/skills/plan-issue/SKILL.md:23` ("Nada no Forgejo antes do gate" → "Nada no tracker"); `docs/roadmap.md:5` (ponteiro da fonte canônica → Issues do GitHub; stub segue congelado — precedente OPS68); `AGENTS-infra.md:5` (espelho do AGENTS.md com a realidade invertida — o mais grave, camada AGENTS); `README.md:9,13,133` (tracker no Forgejo / `FORGEJO_API_TOKEN` como instrução ativa / link do tracker); `scripts/github-pr.mjs:4` (comentário de cabeçalho). **Classe (b) — resíduo de host da era Forgejo, eixo diferente (11, decisão do gate):** `README.md:116` ("CI on the Forgejo Actions" → GitHub Actions `ci-pr.yml`); cabeçalho de `scripts/lib/agent-forgejo.mjs:3,7` (citava `forgejo-api.mjs` removido → `github-api.mjs` real; rename de pureza segue vetado na intenção); `scripts/issue-transition-on-merge.mjs` (usage + 6 prefixos de log `forgejo-issue-transition` → nome real do arquivo, era instrução de arquivo inexistente); `scripts/agent-status.mjs:26` (die "Forgejo issue list failed" → GitHub); `scripts/lib/cli.mjs:34,50` (comentários da allowlist LOCAL_HOSTS emoldurados como era-Forgejo — CI GitHub hosted usa localhost publicado OPS71); `scripts/lib/github-api.mjs:9,183,211` (refs a lib removida). /simplify (2 revisores): P1 aplicado — emoldurar os comentários de `cli.mjs` como histórico (o job-network/bridge gateway era Forgejo; o GitHub hosted publica portas), não como mecanismo vivo; P3 aplicado — `README.md:13` cortou "flips pós-merge" do PAT humano (flips usam o token built-in do Actions com `issues: write`). Débitos: nada registrado (triage: 2 já_resolvidos, 4 descartados score ≤2, 1 defer com gatilho — resíduo Forgejo no pool dormente será limpo se o pool for reativado). Fora de escopo respeitado: histórico correto (`agent-pr-workflow.mdc:60,85`, runbook `docs/ops/teqo-1313-deploy.md`, `AGENT-OPS.md:95` legado, pool dormente, `docs/CUTOVER-MAIN-ONLY.md`) e docs congelados (`docs/plans/*`, `docs/changelog/*`, `docs/CHANGELOG-AGENTS.md`) intactos. Verificação: grep de saneamento com zero refs vivas a tracker/API do Forgejo fora das classes históricas; `pnpm gate:fast` verde (256 arquivos/2443 testes), lint/format verdes; impl plan `docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md` (self-score 5/5). diff --git a/docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md b/docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md new file mode 100644 index 00000000..067ca6fb --- /dev/null +++ b/docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md @@ -0,0 +1,113 @@ +# Impl: Sweep pós-cutover OPS76: textos vivos ainda apontam o tracker ao Forgejo + +Status: rascunho +Atualizado em: 2026-08-24 +Issue: #841 +Intenção: docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos.md +Appetite restante: herdado (~0,5 dia — só edição de prosa; sem estouro possível) + +## Leitura da intenção + +- **Outcome:** todo texto vivo do repo que instrui leitura/escrita de Issues no tracker do Forgejo (`git.solla.dev/fsolla/teqo/issues`, API, `FORGEJO_API_TOKEN`) passa a apontar o GitHub (`github.com/fsolla/teqo/issues`, API do GitHub, `GITHUB_TOKEN`) como tracker único; menções históricas corretas permanecem intactas. +- **O que NÃO negociar:** zero mudança de comportamento em scripts (só prosa/comentários); não reescrever história (runbook de rollback, marcações "legado/removido/congelado", `docs/plans/**`, `docs/changelog/**`, `CHANGELOG-AGENTS.md`); não renomear `scripts/lib/agent-forgejo.mjs`. +- **O que reavaliar:** o aceite "grep de saneamento: nenhuma ref viva a tracker/API do Forgejo fora das classes FORA" exige decidir o destino dos 11 itens **BORDA** (eixo diferente do tracker — CI, comentários de lib, prefixos de log) — sem essa decisão o grep de saída fica contaminado. Decidido abaixo (mesma classe de resíduo pós-cutover; incluir no mesmo diff, marcado como decisão do gate). + +## Abordagem recomendada + +```mermaid +flowchart LR + A[Achados explorador: 12 vivo-errado + 11 borda] --> B[Fase única: edição de prosa in place] + B --> C[Classe a: tracker/API Forgejo → GitHub
12 alvos] + B --> D[Classe b: eixo CI/scripts → GitHub
11 alvos, decisão do gate] + C --> E[Verificação: grep saneamento + pnpm gate:fast] + D --> E + E --> F[Changelog OPS91 + push] +``` + +**Opções consideradas:** + +- **A) In place, fase única, BORDA dentro** — cada arquivo dono da própria frase; os 11 itens BORDA entram no mesmo diff. +- **B) In place, BORDA fora** — só os 12 vivo-errado; BORDA vira Issue nova. +- **C) Documento novo (lista de exceções)** — não editar, documentar o resíduo. + +**Recomendação: A** — porque é a mesma classe de resíduo pós-cutover (leitor novo tropeça igual em "tracker no Forgejo" e em "Forgejo Actions" na linha do CI); a edição é prosa/comentário sem comportamento, o diff inteiro é atomicamente revertível, e o grep de saneamento só fecha sem exceções. O appetite (~0,5 dia) comporta os 23 alvos com folga. O BORDA fica **marcado no PR como decisão do gate** (o humano pode vetar os 11 e reduzir o diff para a classe a sem custo — arquivos independentes). +**Rejeitadas:** B porque espalha a correção em duas entregas com zero isolamento de risco (nenhum alvo é código) e deixa o grep de saída do PR A com exceções; C porque deixa instrução viva errada no repo (o aceite da intenção exige corrigir, não documentar). + +**Decisões de engenharia:** + +- **Fraseado canônico:** espelhar `AGENTS.md:15` como fonte de verdade de fraseado ("CI/PR/deploy AND the ISSUE TRACKER live on GitHub (OPS71 + OPS76, 2026-08-21) (`github.com/fsolla/teqo/issues` … GitHub is the single host)") — evita que as camadas AGENTS (`.mdc`, `AGENTS-infra.md`) divirjam entre si. Alternativa (tradução livre por arquivo) rejeitada: cada reescrita divergente seria o próximo drift. +- **`docs/roadmap.md` (stub congelado):** editar só a linha-ponteiro da fonte canônica — precedente exato do OPS68 ("só o ponteiro da fonte canônica — o stub continua congelado"); o congelamento cobre conteúdo, não ponteiro vivo para o tracker morto. Alternativa "não tocar" rejeitada: o stub diz explicitamente "Status rápido: `pnpm agent:status`" e o ponteiro é o texto vivo que o leitor usa. + +### Componentes / mudanças + +- **Migration:** sem migration. +- **Access / Consent:** N/A — nenhuma mudança de schema ou escrita em dados. +- **UI:** N/A — Impeccable A, zero superfície. +- **Testes:** nenhum novo; verificação por grep + `pnpm gate:fast` (nenhum string editado é pinado em spec — verificado: `grep` em `tests/` por `forgejo-issue-transition|Forgejo issue list failed|forgejo-api|Forgejo Actions` = 0 matches). + +### Lista arquivo:linha → texto alvo + +**Classe (a) — VIVO-ERRADO (core da Issue):** + +| # | Arquivo:linha | Atual | Alvo | +| --- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| a1 | `.agents/rules/agent-pr-workflow.mdc:8` | "…native auto-merge (OPS71). The ISSUE TRACKER stays on the Forgejo — GitHub is only the Actions host." | "…native auto-merge (OPS71). The ISSUE TRACKER lives on the GitHub too (OPS76, 2026-08-21) — GitHub is the single host for code, CI, PRs and Issues." | +| a2 | `.agents/skills/work-issue/execution-pipeline.md:69` | "(OPS71; o tracker de Issues continua no Forgejo por API)." | "(OPS71; o tracker de Issues também vive no GitHub, OPS76)." | +| a3 | `.agents/skills/work-issue/execution-pipeline.md:74–76` | "flipa `done`/`in-prod` **no Forgejo** (lê o body do PR via API do GitHub, escreve no tracker por `FORGEJO_API_TOKEN`)" | "flipa `done`/`in-prod` **no GitHub** (lê o body do PR via API do GitHub, escreve no tracker por `GITHUB_TOKEN`)" | +| a4 | `.agents/skills/project-status/SKILL.md:25` | "leia a Issue via API do Forgejo (MCP/`pnpm issue`)" | "leia a Issue via API do GitHub (MCP/`pnpm issue`)" | +| a5 | `.agents/skills/engineering-audit/SKILL.md:69` | "(leitura da Issue via API do Forgejo/MCP — disponível no Cloud)" | "(leitura da Issue via API do GitHub/MCP — disponível no Cloud)" | +| a6 | `.agents/skills/plan-issue/SKILL.md:23` | "1. **Nada no Forgejo antes do gate.**" | "1. **Nada no tracker antes do gate.**" | +| a7 | `docs/roadmap.md:5` | "[Issues do Forgejo](https://git.solla.dev/fsolla/teqo/issues)" | "[Issues do GitHub](https://github.com/fsolla/teqo/issues)" (só o ponteiro; stub segue congelado) | +| a8 | `AGENTS-infra.md:5` | "**CI/PR/deploy live on GitHub Actions (OPS71, 2026-08-19); the ISSUE TRACKER stays on the Forgejo** (`git.solla.dev/fsolla/teqo` — labels, claims, `pnpm issue`/`agent:*` and the post-merge flips all keep talking to the Forgejo API; GitHub is only the Actions host)." | "**CI/PR/deploy AND the ISSUE TRACKER live on GitHub (OPS71 + OPS76, 2026-08-21)** (`github.com/fsolla/teqo/issues` — labels, claims, `pnpm issue`/`agent:*` and the post-merge flips talk to the GitHub API; GitHub is the single host)." (espelho verbatim de `AGENTS.md:15`) | +| a9 | `README.md:9` | "O **tracker de Issues vive no Forgejo** (`git.solla.dev/fsolla/teqo`); o código/PR/CI vive no **GitHub**." | "O **tracker de Issues, o código, os PRs e o CI vivem no GitHub** (`github.com/fsolla/teqo`)." | +| a10 | `README.md:13` | "Secrets humanos (uma vez): `FORGEJO_API_TOKEN` no GitHub (flips pós-merge no Forgejo); envs de prod em `~/stack/teqo-1313.env` no homeserver." | "Secrets humanos (uma vez): `GITHUB_TOKEN` (PAT com escopo `repo` + `issues: write`) para os scripts de agente/PR e flips pós-merge; envs de prod em `~/stack/teqo-1313.env` no homeserver." | +| a11 | `README.md:133` | "tracked [Forgejo Issues](https://git.solla.dev/fsolla/teqo/issues)" | "tracked [GitHub Issues](https://github.com/fsolla/teqo/issues)" | +| a12 | `scripts/github-pr.mjs:4` | "delivery flow (OPS71: PRs live on GitHub; the tracker stays on Forgejo)." | "delivery flow (OPS71: PRs live on GitHub; the tracker lives on GitHub too — OPS76)." | + +**Classe (b) — BORDA (decisão do gate: incluir no mesmo diff):** + +| # | Arquivo:linha | Atual | Alvo | +| --- | --------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| b1 | `README.md:116` | "Quality gates (also enforced by CI on the Forgejo Actions)" | "Quality gates (also enforced by CI on GitHub Actions — `ci-pr.yml`)" | +| b2 | `scripts/lib/agent-forgejo.mjs:3` | "through the Forgejo REST API (scripts/lib/forgejo-api.mjs)" | "through the GitHub REST API (scripts/lib/github-api.mjs)" | +| b3 | `scripts/lib/agent-forgejo.mjs:7` | "Issue contract (spec + status + deps in Forgejo Issues, per the parallel" | "Issue contract (spec + status + deps in GitHub Issues, per the parallel" | +| b4 | `scripts/issue-transition-on-merge.mjs:40` | "Usage: node scripts/forgejo-issue-transition.mjs --pr " | "Usage: node scripts/issue-transition-on-merge.mjs --pr " (nome morto da era pré-rename — bug de doc no usage) | +| b5 | `scripts/issue-transition-on-merge.mjs:51,55,64,80,84,90` | prefixo de log `[forgejo-issue-transition]` (6 ocorrências) | prefixo `[issue-transition]` (6 ocorrências) | +| b6 | `scripts/agent-status.mjs:26` | `die("Forgejo issue list failed: ${error.message}")` | `die("GitHub issue list failed: ${error.message}")` | +| b7 | `scripts/lib/cli.mjs:34` | "names of the self-hosted Forgejo runner (OPS62 X1:" | "names of the self-hosted GitHub Actions runner (OPS62 X1:" | +| b8 | `scripts/lib/cli.mjs:50` | "self-hosted Forgejo runner publishes each job's `services:` Postgres" | "self-hosted GitHub Actions runner publishes each job's `services:` Postgres" | +| b9 | `scripts/lib/github-api.mjs:9` | "Retry (same policy as forgejo-api OPS67)" | "Retry (OPS67 policy)" (ref a lib removida; sem comportamento) | +| b10 | `scripts/lib/github-api.mjs:183` | "normalized to the agent-script contract (same as forgejo-api)." | "normalized to the agent-script contract." | +| b11 | `scripts/lib/github-api.mjs:211` | "filter them out (the Forgejo `type: issues` query had no GitHub equivalent)." | "filter them out (the GitHub issues endpoint mixes PRs; there is no `type` filter)." | + +## Fases verificáveis + +**Fase única — edição de prosa (23 alvos):** aplicar a lista acima in place (edição linha-a-linha; nenhum arquivo reescrito inteiro). Não há ordem de dependência entre alvos; commits agrupam por arquivo na branch `OPS91-`. + +**Verificação (mesma fase):** + +1. Grep de saneamento (com as exclusões FORA): zero matches vivos em superfícies não-congeladas de `git.solla.dev/fsolla/teqo`, `tracker stays on the Forgejo`, `no Forgejo`, `FORGEJO_API_TOKEN` (como instrução ativa — `AGENT-OPS.md:95` e runbook seguem como refs históricas/legado), `Forgejo Actions`, `forgejo-api` (comentários de lib), `[forgejo-issue-transition]`, `Forgejo issue list failed`. +2. `pnpm gate:fast` (lint/format cobrem os `.md`/`.mdc`/`.mjs` editados; nenhum string é pinado em spec — verificado). +3. `pnpm changelog:check` após registrar a entrada `docs/changelog/2026-08-24-ops91.md` + `pnpm changelog:build` (insert-only — o diff será só a entrada nova + o agregado). + +## Rabbit holes / Não escopo (engenharia) + +- Rename de pureza de `scripts/lib/agent-forgejo.mjs` (e `agent-pool-forgejo.mjs`) — fora de escopo declarado na intenção (score 1, churn alto). +- Reabrir histórico: `docs/plans/*`, `docs/changelog/*`, `docs/CHANGELOG-AGENTS.md`, `docs/ops/teqo-1313-deploy.md` (runbook de rollback cita o Forgejo de propósito), marcações "legado/removido/congelado" já corretas (`AGENT-OPS.md:95`, `agent-pr-workflow.mdc:60,85`), skill `agent-pool` dormente. +- Reescrever os scripts que já falam GitHub (`issue-transition-on-merge.mjs` além dos 7 alvos de prosa) — zero mudança de comportamento. + +## Riscos e mitigação + +- **Lint/format em `.mdc`/`.md`:** gates cobrem; se Prettier divergir no alvo (linha longa no `README.md:9` reescrita), `pnpm format` antes do push. +- **docs-guards de changelog:** entrada OPS91 obrigatória antes do push (guard append-only); sem ela o push falha — passo explícito na verificação. +- **Veto do gate no BORDA:** se o humano vetar, descartar os 11 alvos b\* — os arquivos são independentes, o diff da classe a fecha sozinho; registrar no comentário da PR. +- **Deriva de fraseado entre camadas:** mitigada espelhando `AGENTS.md:15` nos alvos em inglês de camada AGENTS (a8, a1) e mantendo link/URL iguais nos alvos em pt (a7, a11). + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção coberto: 23 alvos corrigidos; grep de saneamento sem ref viva a tracker/API do Forgejo fora das classes FORA +- [ ] Invariantes AGENTS/engineering-standards: edição in place, dono por arquivo, zero comportamento alterado, zero twin +- [ ] Testes de domínio previstos (unit/int): N/A — prosa; verificação por grep + `pnpm gate:fast` verde +- [ ] Changelog `docs/changelog/2026-08-24-ops91.md` + `pnpm changelog:build` + `changelog:check` + +**Self-score decision-quality: 5/5** — (1) decisões caras (BORDA no diff, roadmap stub, fraseado canônico) têm opções e rejeitadas registradas; (2) cabe no appetite (prosa, 23 edições pontuais, fase única); (3) rabbit holes nomeados (rename de pureza, histórico, scripts já corretos); (4) depth check: zero arquivo/helper novo, edita o dono existente; (5) aceite da intenção permanece satisfeito — reescrita nunca reescreveu o outcome, só o corrigiu. diff --git a/docs/roadmap.md b/docs/roadmap.md index ef8242b4..13e83946 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -2,6 +2,6 @@ **Congelado em 2026-07-30.** Este arquivo não é mais editado. -- **Fonte canônica de spec/status/deps/prio/modelo:** [Issues do Forgejo](https://git.solla.dev/fsolla/teqo/issues) (frontmatter `id/depends/serializes/priority/model`; estado em labels `ready | in-progress | blocked | done | in-prod`). Fluxo: skills `plan-issue` → `work-issue` → `project-status` (ver `docs/AGENT-OPS.md`). Status rápido: `pnpm agent:status`. +- **Fonte canônica de spec/status/deps/prio/modelo:** [Issues do GitHub](https://github.com/fsolla/teqo/issues) (frontmatter `id/depends/serializes/priority/model`; estado em labels `ready | in-progress | blocked | done | in-prod`). Fluxo: skills `plan-issue` → `work-issue` → `project-status` (ver `docs/AGENT-OPS.md`). Status rápido: `pnpm agent:status`. - **Histórico e entregas:** [`docs/changelog/`](changelog/) (registro por-arquivo) + [`docs/plans/`](plans/) — leitura agregada sob demanda: `pnpm changelog:read` (ou `pnpm changelog:build`, gitignored). - **Conteúdo integral deste roadmap:** preservado no git history (última versão viva no commit imediatamente anterior a este stub). diff --git a/scripts/agent-status.mjs b/scripts/agent-status.mjs index 72a73eae..8689adf3 100644 --- a/scripts/agent-status.mjs +++ b/scripts/agent-status.mjs @@ -23,7 +23,7 @@ let issues try { issues = await api.listIssues({ state: 'all', limit: 200 }) } catch (error) { - die(`Forgejo issue list failed: ${error.message}`) + die(`GitHub issue list failed: ${error.message}`) } const rows = issues diff --git a/scripts/github-pr.mjs b/scripts/github-pr.mjs index fef6d39f..b75482b1 100644 --- a/scripts/github-pr.mjs +++ b/scripts/github-pr.mjs @@ -1,7 +1,7 @@ /** * Plain-Node CLI (no pnpm) — create a GitHub PR (Ready, never draft) for * agent deliveries. Replaces the Forgejo MCP `create_pull_request` in the - * delivery flow (OPS71: PRs live on GitHub; the tracker stays on Forgejo). + * delivery flow (OPS71: PRs live on GitHub; the tracker lives on GitHub too — OPS76). * * GITHUB_TOKEN= node scripts/github-pr.mjs \ * --head --title "" --body-file diff --git a/scripts/issue-transition-on-merge.mjs b/scripts/issue-transition-on-merge.mjs index b01d45ab..55ee0bdf 100644 --- a/scripts/issue-transition-on-merge.mjs +++ b/scripts/issue-transition-on-merge.mjs @@ -37,7 +37,7 @@ const parseArgs = (argv) => { const flags = parseArgs(process.argv) const prNumber = Number(flags.pr) if (!Number.isInteger(prNumber) || prNumber <= 0) { - console.error('Usage: node scripts/forgejo-issue-transition.mjs --pr ') + console.error('Usage: node scripts/issue-transition-on-merge.mjs --pr ') process.exit(1) } @@ -48,11 +48,11 @@ const pr = ? { merged: true, body: bodyFromEnv } : await api.getPullRequest(prNumber) if (!pr) { - console.error(`[forgejo-issue-transition] PR #${prNumber} não encontrada`) + console.error(`[issue-transition] PR #${prNumber} não encontrada`) process.exit(1) } if (!pr.merged) { - console.log(`[forgejo-issue-transition] PR #${prNumber} não mergeada — skip`) + console.log(`[issue-transition] PR #${prNumber} não mergeada — skip`) process.exit(0) } @@ -61,7 +61,7 @@ const numbers = [ ] if (numbers.length === 0) { - console.log(`[forgejo-issue-transition] PR #${prNumber}: nenhum Closes/Fixes #N — nada a fazer`) + console.log(`[issue-transition] PR #${prNumber}: nenhum Closes/Fixes #N — nada a fazer`) process.exit(0) } @@ -77,17 +77,17 @@ for (const number of numbers) { // (`Closes #N`) só acontece para issues AINDA abertas citadas no body — // o flip de labels (done/in-prod/remover in-progress) é explícito aqui. await api.closeIssue(number) - console.log(`[forgejo-issue-transition] #${number}: done + in-prod + fechada`) + console.log(`[issue-transition] #${number}: done + in-prod + fechada`) } catch (error) { flipFailed = true console.error( - `[forgejo-issue-transition] #${number}: FLIP FALHOU — ${error instanceof Error ? error.message : error}`, + `[issue-transition] #${number}: FLIP FALHOU — ${error instanceof Error ? error.message : error}`, ) } } if (flipFailed) { console.error( - `[forgejo-issue-transition] PR #${prNumber}: ${numbers.length} Issue(s) citada(s), pelo menos 1 flip falhou — os labels de status podem estar mentindo.`, + `[issue-transition] PR #${prNumber}: ${numbers.length} Issue(s) citada(s), pelo menos 1 flip falhou — os labels de status podem estar mentindo.`, ) process.exit(1) } diff --git a/scripts/lib/agent-forgejo.mjs b/scripts/lib/agent-forgejo.mjs index b9a2e340..23941a69 100644 --- a/scripts/lib/agent-forgejo.mjs +++ b/scripts/lib/agent-forgejo.mjs @@ -1,10 +1,10 @@ /** * Shared helpers for the agent ops scripts (`pnpm agent:*`). Everything goes - * through the Forgejo REST API (scripts/lib/forgejo-api.mjs) — agents never + * through the GitHub REST API (scripts/lib/github-api.mjs) — agents never * get a token of their own beyond the env, and these scripts never touch * stage/prod database URLs. * - * Issue contract (spec + status + deps in Forgejo Issues, per the parallel + * Issue contract (spec + status + deps in GitHub Issues, per the parallel * paradigm plan): each trackable issue carries a YAML-ish frontmatter block * at the top of the body: * diff --git a/scripts/lib/cli.mjs b/scripts/lib/cli.mjs index 6621ead4..ee70a470 100644 --- a/scripts/lib/cli.mjs +++ b/scripts/lib/cli.mjs @@ -31,9 +31,10 @@ export const loadCliEnv = () => { /** * Local database hosts — docker-compose service name, the job-network service - * names of the self-hosted Forgejo runner (OPS62 X1: services are reached by - * name, no host publish), and the bridge gateway (OPS50: legacy per-port - * publish kept in the allowlist). THE one list. + * names of the Forgejo-era runner (OPS62 X1: services by name, no host publish — + * the GitHub hosted CI reaches services on published localhost ports, OPS71), + * and the bridge gateway (OPS50: legacy per-port publish kept in the + * allowlist). THE one list. */ export const LOCAL_HOSTS = new Set([ 'localhost', @@ -46,12 +47,15 @@ export const LOCAL_HOSTS = new Set([ ]) /** - * Gateway of the default route, read from /proc/net/route (Linux). The - * self-hosted Forgejo runner publishes each job's `services:` Postgres on the - * bridge gateway IP (192.168.x.1 — differs per job), so a job reaches it via - * that IP instead of localhost. The same octet order the ci-pr.yml awk - * produces (the /proc fields are little-endian). Returns null when there is - * no default route or the file is unreadable (macOS/Windows dev boxes). + * Gateway of the default route, read from /proc/net/route (Linux). In the + * Forgejo-era CI the self-hosted runner published each job's `services:` + * Postgres on the bridge gateway IP (192.168.x.1 — differs per job), so a job + * reached it via that IP instead of localhost; the GitHub hosted CI (OPS71) + * reaches services on published localhost ports instead, and the bridge + * gateway stays in the allowlist for Docker-based runners. The same octet + * order the ci-pr.yml awk produces (the /proc fields are little-endian). + * Returns null when there is no default route or the file is unreadable + * (macOS/Windows dev boxes). */ export const defaultGatewayHost = () => { try { diff --git a/scripts/lib/github-api.mjs b/scripts/lib/github-api.mjs index 2ae14a50..3955f753 100644 --- a/scripts/lib/github-api.mjs +++ b/scripts/lib/github-api.mjs @@ -6,7 +6,7 @@ * Auth: `GITHUB_TOKEN` (PAT locally; the built-in Actions token in * workflows). Base URL: `GITHUB_API_URL`, else `https://api.github.com`. * - * Retry (same policy as forgejo-api OPS67): transient failures retry with + * Retry (OPS67 policy): transient failures retry with * exponential backoff — a fetch that rejects (network-level, DNS/TCP/reset) * retries on ANY method; a 5xx retries on GET only, so read polls survive * while write endpoints keep failing closed instead of risking duplicated @@ -180,7 +180,7 @@ export const createApi = ({ base: { ref: pr.base?.ref ?? '' }, }) - /** Issue shape normalized to the agent-script contract (same as forgejo-api). */ + /** Issue shape normalized to the agent-script contract. */ const normalizeIssue = (issue) => ({ number: issue.number, title: issue.title, @@ -208,8 +208,8 @@ export const createApi = ({ const issues = await request(`/repos/${owner}/${name}/issues`, { query: { state, labels, per_page: limit, page }, }) - // GitHub returns PRs in the issues list; filter them out (the Forgejo - // `type: issues` query had no GitHub equivalent). + // GitHub returns PRs in the issues list; filter them out (the GitHub + // issues endpoint mixes PRs; there is no `type` filter). return (Array.isArray(issues) ? issues : []) .filter((issue) => !issue.pull_request) .map(normalizeIssue)