mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
ops(OPS91): sweep pós-cutover — textos vivos que apontavam o tracker ao Forgejo passam para o GitHub (Closes #841)
This commit is contained in:
parent
51ad84583d
commit
96bc86e94e
16 changed files with 155 additions and 37 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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=<PAT> node scripts/github-pr.mjs --head <branch> --title "<id> — <título>" --body-file <arquivo>` — **Ready** (nunca draft; o script não tem flag draft), base `main`, body com `Closes #<N>` (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
|
||||
|
||||
|
|
|
|||
|
|
@ -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`).
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
1
docs/changelog/2026-08-24-ops91.md
Normal file
1
docs/changelog/2026-08-24-ops91.md
Normal file
|
|
@ -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).
|
||||
113
docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md
Normal file
113
docs/plans/ops91-sweep-pos-cutover-forgejo-textos-vivos-impl.md
Normal file
|
|
@ -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<br/>12 alvos]
|
||||
B --> D[Classe b: eixo CI/scripts → GitHub<br/>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 <N>" | "Usage: node scripts/issue-transition-on-merge.mjs --pr <N>" (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-<slug>`.
|
||||
|
||||
**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.
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=<PAT> node scripts/github-pr.mjs \
|
||||
* --head <branch> --title "<título>" --body-file <path>
|
||||
|
|
|
|||
|
|
@ -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 <N>')
|
||||
console.error('Usage: node scripts/issue-transition-on-merge.mjs --pr <N>')
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue