diff --git a/.agents/rules/agent-pr-workflow.mdc b/.agents/rules/agent-pr-workflow.mdc index 7785a35d..36e3c711 100644 --- a/.agents/rules/agent-pr-workflow.mdc +++ b/.agents/rules/agent-pr-workflow.mdc @@ -37,7 +37,7 @@ When the environment says to use `ManagePullRequest` instead of `gh pr create`: **Use `pnpm push`, never bare `git push`.** Não depende do Husky estar wired: 1. `ensure-repo-deps` — `pnpm install` se `node_modules` faltar/stale + `pnpm run prepare` (Husky) se hooks não estiverem apontando para `.husky` -2. `pnpm gate:push` — espelho do `ci-pr.yml` sem e2e (e2e roda uma vez, no CI, após o push; local opcional: `pnpm test:e2e:affected`) +2. `pnpm gate:push` — espelho do `ci-pr.yml` sem e2e (e2e roda uma vez, no CI, após o push; local opcional: `pnpm test:e2e:affected`), com os `docs-guards` locais (OPS63: changelog append-only, agregado sync e marcadores de conflito — o escape `changelog-rewrite:` fica só no CI, body do PR) 3. `git push --no-verify` — só porque o gate já rodou no passo 2 (não é escape hatch) ```bash diff --git a/.agents/rules/engineering-standards.mdc b/.agents/rules/engineering-standards.mdc index 7b8ae662..efb353be 100644 --- a/.agents/rules/engineering-standards.mdc +++ b/.agents/rules/engineering-standards.mdc @@ -11,7 +11,7 @@ Established 2026-07-23 by the engineering-standards hardening (tracker: `docs/IM 1. **Dead code dies immediately.** After your edits, check what your change orphaned and delete it in the same delivery: run `pnpm exec knip` (files/dependencies are CI-blocking errors; unused exports are warnings being driven to zero). Never leave commented-out code, unused exports, superseded components, or one-shot scripts behind — git history is the archive. 2. **Zero warnings.** `pnpm lint` runs with `--max-warnings=0`. Deliberate placeholders use a `_` prefix. Never silence a rule without an inline justification comment. -3. **Gate em duas velocidades (desde 2026-07-30, paradigma de agentes paralelos — `docs/AGENT-OPS.md`).** **Fast gate local do agente** (iteração durante a sessão): `pnpm gate:fast` (lint + typecheck + test:unit). **Push canônico:** `pnpm push` — `ensure-repo-deps` (install + Husky em worktrees) + `git push`; o hook pre-push roda `gate:push` → `gate:ci` (espelho serial do `ci-pr.yml` **sem e2e** — OPS59: o e2e roda **uma vez, no CI**, em PR e na main; local opcional para feedback antecipado: `pnpm test:e2e:affected`). Não use `git push` nu. Fonte única: scripts `gate:*` e `scripts/git-push.mjs` em `package.json`; escape para WIP: `git push --no-verify`. Migrations NÃO são serializadas entre PRs: a cadeia é validada por `pnpm migrate` + `test:int` (incl. `campaignMigrationReconciliation`) em todo PR e na main; rebase antes de `migrate:create` continua obrigatório (o antigo `migration-lock` foi removido em 2026-08-12 — deadlock de CI). Entrega = merge em `main`; deploy gated pelo verificador `ci.yml`. **CI vermelho no teu PR é teu, mesmo pré-existente ou de infra** — "fora do escopo da feature" não é critério de parada (política "Dono do PR, dono do CI" em `docs/AGENT-OPS.md`). +3. **Gate em duas velocidades (desde 2026-07-30, paradigma de agentes paralelos — `docs/AGENT-OPS.md`).** **Fast gate local do agente** (iteração durante a sessão): `pnpm gate:fast` (lint + typecheck + test:unit). **Push canônico:** `pnpm push` — `ensure-repo-deps` (install + Husky em worktrees) + `git push`; o hook pre-push roda `gate:push` → `gate:ci` (espelho serial do `ci-pr.yml` **sem e2e** — OPS59: o e2e roda **uma vez, no CI**, em PR e na main; local opcional para feedback antecipado: `pnpm test:e2e:affected`; **com os `docs-guards` locais** — OPS63: changelog append-only, agregado sync e marcadores de conflito, mesmos scripts do CI; o escape `changelog-rewrite:` fica só no CI, body do PR). Não use `git push` nu. Fonte única: scripts `gate:*` e `scripts/git-push.mjs` em `package.json`; escape para WIP: `git push --no-verify`. Migrations NÃO são serializadas entre PRs: a cadeia é validada por `pnpm migrate` + `test:int` (incl. `campaignMigrationReconciliation`) em todo PR e na main; rebase antes de `migrate:create` continua obrigatório (o antigo `migration-lock` foi removido em 2026-08-12 — deadlock de CI). Entrega = merge em `main`; deploy gated pelo verificador `ci.yml`. **CI vermelho no teu PR é teu, mesmo pré-existente ou de infra** — "fora do escopo da feature" não é critério de parada (política "Dono do PR, dono do CI" em `docs/AGENT-OPS.md`). 4. **Run gate commands bare, never piped** (`pnpm test | tail` swallows the exit code). Never run `knip --fix` blind — verify each removal with `git grep -w ` first (knip still cannot load `payload.config.ts`, so its graph is incomplete; ledgered as P3). ## Type honesty diff --git a/docs/AGENT-OPS.md b/docs/AGENT-OPS.md index 7fd2e1ca..c301b06e 100644 --- a/docs/AGENT-OPS.md +++ b/docs/AGENT-OPS.md @@ -57,7 +57,7 @@ Labels: `ready|in-progress|blocked|done|in-prod`, `prio:*`, `kind:*`, `needs:*`. 1. Schema → atualizar `db:seed:minimal` no mesmo PR (`needs:migration`). 2. Migrations NÃO são serializadas entre PRs: o CI (migrate + int, incl. `campaignMigrationReconciliation`) valida a cadeia em todo PR e na main; rebase antes de `migrate:create` continua obrigatório. 3. Ready + auto-merge: `gh pr create --base main` → `gh pr merge --auto --rebase`; `gh pr checks --watch --required` (Vercel Git não é gate). **Merge é por rebase** (nunca merge commit): preserva a linha histórica limpa do `main` e é o modo que o CI guarda contra conflitos latentes. -4. **Changelog da entrega (OPS44):** toda entrega escreve `docs/changelog/-.md` (ex. `2026-08-13-ops44.md`) e roda `pnpm changelog:build` antes do push — o agregado `docs/CHANGELOG-AGENTS.md` é **insert-only** (nunca remove/reescreve entradas; entradas históricas não são migradas). Guard de CI `docs-guards`: (a) agregado é append-only (multiset sobre blobs — perda de linha falha); (b) `docs/changelog/` é additions-only (M/D falham); (c) agregado está up to date com `docs/changelog/` (`changelog:check`); (d) nenhum marcador de conflito em diffs de markdown (docs/, AGENTS.md, .agents/ — cobre PRs docs-only, onde a suíte unit não roda). Restauração legítima (header muda, restauração estilo D8) escapa escrevendo **`changelog-rewrite: ` como linha própria** no body do PR — definição canônica aqui; o texto do checkbox do PR template nunca ativa (linha-ancorado). A própria troca `--merge` → `--rebase` deste doc usou o escape — header do agregado mudou. +4. **Changelog da entrega (OPS44):** toda entrega escreve `docs/changelog/-.md` (ex. `2026-08-13-ops44.md`) e roda `pnpm changelog:build` antes do push — o agregado `docs/CHANGELOG-AGENTS.md` é **insert-only** (nunca remove/reescreve entradas; entradas históricas não são migradas). Guard de CI `docs-guards`: (a) agregado é append-only (multiset sobre blobs — perda de linha falha); (b) `docs/changelog/` é additions-only (M/D falham); (c) agregado está up to date com `docs/changelog/` (`changelog:check`); (d) nenhum marcador de conflito em diffs de markdown (docs/, AGENTS.md, .agents/ — cobre PRs docs-only, onde a suíte unit não roda). **Desde OPS63 os mesmos três checks rodam também no pre-push local** (`gate:push` → `gate:ci`, fase 1 — mesmos scripts, default `origin/main`). Restauração legítima (header muda, restauração estilo D8) escapa escrevendo **`changelog-rewrite: ` como linha própria** no body do PR — definição canônica aqui; o texto do checkbox do PR template nunca ativa (linha-ancorado). **O escape é CI-only (body do PR):** no pre-push local a restauração legítima falha com a mesma mensagem do CI — bypass documentado é `git push --no-verify` direto (o CI, com o escape no body, continua sendo a via da restauração). A própria troca `--merge` → `--rebase` deste doc usou o escape — header do agregado mudou. ## Registries compartilhados (serialização por arquivo, não por PR) diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 0613e7c3..3fa0b536 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -40,6 +40,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente **Recently resolved (2026-08-18):** **OPS60** — o sync do `pnpm db:seed:posts` ganhou guard de intenção explícita para escrita no bucket (achado do /simplify da #37): com as envs `S3_*` ativas, o modo sync exige `SEED_MEDIA_CONFIRM=1` (mesmo padrão `isTruthyEnv` do `MEDIA_RECOVER_CONFIRM` da OPS52-media-guard) e recusa com exit 1 antes de qualquer escrita sem a flag — o risco fechado: um worktree com cópia de credenciais `S3_*` de prod (all-or-nothing do `worktree-env.mjs`) rodando sync contra DB local sobrescrevia covers no bucket de **prod** (o seed é idempotente por slug e sobrescreve o key determinístico). Storage local (sem `S3_*` → disk) e `--dry-run` seguem sem flag (não escrevem); echo do alvo ganhou `Bucket`/`Endpoint` quando S3 ativo (paridade com o `media:recover`). Implementação no dono: `isTruthyEnv` (já existente da #37) + `resolveS3StorageEnv` — sem twin. Runbook do OPS58 atualizado com o comando real; nota no AGENTS.md. +**Recently resolved (2026-08-18):** **OPS63** — os `docs-guards` (OPS44) rodam também no pre-push local: o espelho `scripts/gate-ci.mjs` ganhou os três checks na fase 1 (changelog append-only, agregado sync `--check`, marcadores de conflito em markdown) como chamadas diretas dos mesmos scripts do CI — labels espelhando os steps do job `docs-guards`; header do arquivo e linha final atualizados. `package.json`, `.husky/pre-push`, `git-push.mjs` e workflows `.forgejo/*` intocados: `gate:push` = `gate:ci` continua a cadeia única, o job `docs-guards` segue dono do escape `changelog-rewrite:` (body do PR — CI-only; restauração legítima D8 falha local com a mesma mensagem do CI e usa o bypass documentado `git push --no-verify`, com o CI como via do escape). Docs sincronizadas: `.agents/rules/engineering-standards.mdc` e `agent-pr-workflow.mdc` (gate descrito como "espelho do ci-pr.yml sem e2e, com docs-guards locais"), `docs/AGENT-OPS.md` (contrato item 4 — OPS44: checks no pre-push + nota do escape CI-only). Verificado por grep: `AGENTS.md` e skills não descreviam o gate sem os checks de docs — sem mudança. Anti-goals respeitados: sem bypass novo (falha local honesta), sem `plans-only-closes` local (contrato de PR body). Sem migration/access/Consent/UI/schema. Verificação: três checks green na branch; caminho de falha validado (commit descartável removendo linha do agregado → append-only falha com mensagem acionável); `pnpm gate:ci` local full; CI roda full nesta PR por HIGH_RISK (`gate-ci.mjs`). + **Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte. **Recently resolved (2026-08-17):** **OPS53** — o deploy do site 1313 virou etapa do CI: mergir em `main` com a suite verde == site atualizado em `jorgesolla1313.com.br`, sem passo manual. O job `deploy` em `.forgejo/workflows/ci.yml` roda no host do runner (workstation, `runs-on: host`) e streama `scripts/deploy-homeserver.sh` para o homeserver via SSH (`ssh homeserver "bash -s -- "`); o script remoto (bash puro, homeserver sem dependências novas) faz: guard de HEAD (`git ls-remote` de `main` == SHA do job, senão skip "stale"), `flock` de serialização, workspace `~/teqo-deploy` (clone do Forgejo local), `docker login localhost:5000` (REGISTRY_USER/PASSWORD de `~/stack/.env`), build dos stages `runner`+`migrator` no homeserver (`--network stack_default` para a geração estática alcançar o `postgres:5432` do compose; secrets de `~/stack/teqo-1313.env` via BuildKit, sem camadas/logs), push no registry local, swap dos tags de imagem no compose (com backup `docker-compose.yml.pre-`), **migrate pelo serviço de maintenance `teqo-1313-migrate` antes do rollout**, `docker compose up -d`, espera do healthcheck e smoke (`/`, `/campanha/login`, `/admin`, barreira 307, WebAuthn `login-options` com body JSON, `POST /api/revalidate` com o secret real — rehearsal validado contra o site vivo). Falha pós-swap → rollback automático best-effort (restore do backup + `up -d` da imagem anterior) + job vermelho. **Divergência deliberada da intenção (fato novo):** a intenção assumia o runner no homeserver; verificado (`infra-solla/STATE.md` + ao vivo) que o runner é a `workstation-runner` na workstation e o stack de produção vive no homeserver — a abordagem A foi corrigida para "job `runs-on: host` na workstation + todo o deploy no homeserver via SSH", o que manteve **zero secrets novos no Forgejo** e zero mudança de infra no compose. Nenhum secret no repo; envs seguem chmod-600 no homeserver. Runbook (rollback manual, falhas conhecidas, decisão de segurança do host label): `docs/ops/teqo-1313-deploy.md` (novo). Guarda estrutural nova: `tests/unit/deployScript.unit.spec.ts` (bash -n + invariantes do pipeline: HEAD guard, BuildKit secrets, ordem migrate→rollout, smoke). AGENTS.md/AGENT-OPS.md atualizados. Primeiro deploy real = o merge deste próprio item (tracer bullet); infra-solla `STATE.md` anotado à parte. diff --git a/docs/changelog/2026-08-18-ops63.md b/docs/changelog/2026-08-18-ops63.md new file mode 100644 index 00000000..51628c8a --- /dev/null +++ b/docs/changelog/2026-08-18-ops63.md @@ -0,0 +1 @@ +**Recently resolved (2026-08-18):** **OPS63** — os `docs-guards` (OPS44) rodam também no pre-push local: o espelho `scripts/gate-ci.mjs` ganhou os três checks na fase 1 (changelog append-only, agregado sync `--check`, marcadores de conflito em markdown) como chamadas diretas dos mesmos scripts do CI — labels espelhando os steps do job `docs-guards`; header do arquivo e linha final atualizados. `package.json`, `.husky/pre-push`, `git-push.mjs` e workflows `.forgejo/*` intocados: `gate:push` = `gate:ci` continua a cadeia única, o job `docs-guards` segue dono do escape `changelog-rewrite:` (body do PR — CI-only; restauração legítima D8 falha local com a mesma mensagem do CI e usa o bypass documentado `git push --no-verify`, com o CI como via do escape). Docs sincronizadas: `.agents/rules/engineering-standards.mdc` e `agent-pr-workflow.mdc` (gate descrito como "espelho do ci-pr.yml sem e2e, com docs-guards locais"), `docs/AGENT-OPS.md` (contrato item 4 — OPS44: checks no pre-push + nota do escape CI-only). Verificado por grep: `AGENTS.md` e skills não descreviam o gate sem os checks de docs — sem mudança. Anti-goals respeitados: sem bypass novo (falha local honesta), sem `plans-only-closes` local (contrato de PR body). Sem migration/access/Consent/UI/schema. Verificação: três checks green na branch; caminho de falha validado (commit descartável removendo linha do agregado → append-only falha com mensagem acionável); `pnpm gate:ci` local full; CI roda full nesta PR por HIGH_RISK (`gate-ci.mjs`). diff --git a/docs/plans/ops63-doc-guard-no-pre-push-impl.md b/docs/plans/ops63-doc-guard-no-pre-push-impl.md new file mode 100644 index 00000000..cf96b849 --- /dev/null +++ b/docs/plans/ops63-doc-guard-no-pre-push-impl.md @@ -0,0 +1,65 @@ +# Impl: OPS63 — Doc-guard também no pre-push (changelog append-only, agregado e marcadores de conflito) + +Status: aprovado +Atualizado em: 2026-08-18 +Issue: #60 +Intenção: docs/plans/ops63-doc-guard-no-pre-push.md +Appetite restante: herdado (~0,5 dia eng; uma mudança aditiva pequena) + +## Leitura da intenção + +- **Outcome:** `pnpm gate:push` (via `pnpm push` e `.husky/pre-push`) roda os três checks de docs — changelog append-only, agregado up to date (`--check`) e zero marcadores de conflito em markdown — junto da cascata atual; falha bloqueia o push com a mesma mensagem do CI; o CI (`docs-guards` + escape `changelog-rewrite:`) não muda. +- **O que NÃO negociar:** sem bypass novo local (falha local honesta + `git push --no-verify` documentado para o caso raro); sem `plans-only-closes` no pre-push (contrato de body de PR); reuso dos guards existentes, sem duplicar lógica; workflows `.forgejo/*` intocados. +- **O que reavaliar:** nada — a hipótese de direção (adicionar etapa de docs no `scripts/gate-ci.mjs`) confere após leitura do código: os três scripts existem, são autossuficientes (default `origin/main` quando `GITHUB_BASE_REF` ausente) e o gate local é exatamente o espelho serial que omite o job `docs-guards`. + +## Abordagem recomendada + +```mermaid +flowchart LR + P[push / pre-push] --> G[gate:push = gate:ci] + G --> D[docs-guards: 3 checks] + D --> A1[node check-changelog-append-only.mjs] + D --> A2[node build-changelog.mjs --check] + D --> A3[node check-docs-conflict-markers.mjs] + A1 & A2 & A3 --> R[demais checks da fase 1] + R --> OK[push] +``` + +**Opções consideradas:** A | B | C +**Recomendação:** A — chamadas diretas dos três scripts existentes na fase 1 (cheap) do `scripts/gate-ci.mjs`, com labels espelhando os steps do job `docs-guards` do `ci-pr.yml`. Porque: zero duplicação (os scripts são o CI), o espelho local continua sendo o único ponto de cascata, custo ~1s por check e os scripts já fazem no-op quando não há diff. É o mesmo padrão dos `check-test-locations`/`lint`/`format:check` (incondicionais). +**Rejeitadas:** B — wrapper novo `scripts/gate-docs.mjs` (indireção sem volatilidade: 3 chamadas virariam um arquivo; os scripts individuais continuariam sendo a fonte). C — script pnpm `gate:docs` no `package.json` (package.json ganha um agregado que só o gate usa; chamar `node scripts/...` direto espelha os steps do CI e não mexe em nada além do espelho). + +### Componentes / mudanças + +- **`scripts/gate-ci.mjs`**: 3 blocos `run()` na fase 1 (após `format:check`, antes do bloco condicional de typecheck — incondicionais, como o job `docs-guards`): `node scripts/check-changelog-append-only.mjs`, `node scripts/build-changelog.mjs --check`, `node scripts/check-docs-conflict-markers.mjs`. Header do arquivo ganha menção ao OPS63. Linha final `✓ all checks passed` menciona os docs guards. Nada mais muda no arquivo. +- **`package.json` / `.husky/pre-push` / `scripts/git-push.mjs`**: intocados — `gate:push` = `gate:ci` continua sendo a cadeia única. +- **`.forgejo/workflows/ci-pr.yml` / `ci.yml`**: intocados — `docs-guards` continua dono do escape `changelog-rewrite:` (PR body); sem mudança. +- **Migration:** sem migration. +- **Access / Consent:** N/A. +- **UI:** Impeccable A — N/A (sem superfície UI). + +## Fases verificáveis + +1. **Espelho** — editar `scripts/gate-ci.mjs` (fase 1 + header + linha final). Verificação: os três checks green nesta branch (sem diff de changelog ainda → "no changelog diff"/"no markdown changes"/"aggregate up to date"); caminho de falha validado com um commit descartável que remove linha do agregado (append-only deve falhar). +2. **Docs do fluxo** — `.agents/rules/engineering-standards.mdc` (linha do gate: "espelho do ci-pr.yml sem e2e" → "sem e2e, com docs-guards OPS63"), `.agents/rules/agent-pr-workflow.mdc` (item 2 do fluxo do agente), `docs/AGENT-OPS.md` (contrato item 4 — OPS44: os três checks rodam também no pre-push local; o escape `changelog-rewrite:` segue só no CI/PR body). Verificação: grep final por descrições do gate que omitam os docs guards. +3. **Changelog + gates** — `docs/changelog/2026-08-18-ops63.md` + `pnpm changelog:build`; `pnpm gate:ci` local (cascata completa em `teqo_test`; os docs guards rodam na fase 1 — esta PR é a primeira que os exercita de verdade, pois o agregado muda); CI roda full no PR (`scripts/gate-ci.mjs` é HIGH_RISK). + +## Rabbit holes / Não escopo (engenharia) + +- **`plans-only-closes` no pre-push** — depende do body do PR; CI-only por design da intenção. +- **Escape local para o append-only** — a restauração legítima (D8) falha localmente sem o PR body; decisão da intenção já fixada: mensagem acionável do guard + bypass documentado `git push --no-verify`. +- **Flag/env de "pular docs"** — anti-goal explícito da intenção. +- **Varredura da working tree vs HEAD** — `check-docs-conflict-markers` lê `HEAD:` (commitado), como o CI lê o diff do PR; rascunho não commitado fica fora — consistente, sem mudança. +- **Novo script pnpm / wrapper** — rejeitado (opções B/C acima). + +## Riscos e mitigação + +- **Falha local de append-only em PR que usa o escape `changelog-rewrite:`** (ex. restauração D8): o guard falha com a mensagem do CI sem o escape; o agente usa `git push --no-verify` (escape já documentado em AGENT-OPS). O CI continua aceitando o escape via PR body — nenhuma entrega legítima fica presa. Mitigação: nota no impl do changelog/AGENT-OPS. +- **`origin/main` desatualizado no worktree**: merge-base velho pode acusar diff inexistente ou deixar passar diff novo. Mesmo comportamento dos guards existentes (`ci-scope` usa `origin/main`); o `fetch` do rebase obrigatório no fluxo reduz a janela. Sem ação nova. +- **Acoplamento com OPS59** (mesmo arquivo): já mergeado em `main`; a branch deste worktree nasceu dele — mudança aditiva sem conflito; rebase obrigatório antes do `pnpm push` de qualquer forma. + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção ainda coberto (três checks no pre-push; CI intocado) +- [ ] Invariantes AGENTS/engineering-standards (gate bare; `format:check`; knip/cycles sem arquivos novos) +- [ ] Testes de domínio previstos: nenhum teste novo (nenhum módulo compartilhado muda); suíte unit + `pnpm gate:ci` verdes; caminho de falha do append-only verificado manualmente com commit descartável diff --git a/docs/plans/ops63-doc-guard-no-pre-push.md b/docs/plans/ops63-doc-guard-no-pre-push.md index 5dfb142d..bbaaf3cf 100644 --- a/docs/plans/ops63-doc-guard-no-pre-push.md +++ b/docs/plans/ops63-doc-guard-no-pre-push.md @@ -8,15 +8,16 @@ model: composer-2.5 # OPS63 — Doc-guard também no pre-push (changelog append-only, agregado e marcadores de conflito) -Status: rascunho +Status: entregue Atualizado em: 2026-08-18 Issue: #60 Priority: P2 Model: composer-2.5 Impeccable: A — N/A (sem superfície UI) Rascunho UI: N/A — sem UI -Appetite: ~0,5 dia eng; um outcome verificável (push local roda os checks de docs) +Appetite: ~0,5 dia eng; um outcome verificável (push local roda os checks de docs) — entregue em 1 sessão Responsável: — +Impl: [docs/plans/ops63-doc-guard-no-pre-push-impl.md](ops63-doc-guard-no-pre-push-impl.md) ## Intenção diff --git a/scripts/gate-ci.mjs b/scripts/gate-ci.mjs index 726a1add..8c0268b3 100644 --- a/scripts/gate-ci.mjs +++ b/scripts/gate-ci.mjs @@ -4,6 +4,10 @@ * then phase-2 expensive (int/build). e2e is NOT part of the local mirror * (OPS59): it runs once, in CI (PR job `e2e` → `checks`; main `ci.yml` e2e → * `checks` → `deploy`). Local optional feedback: `pnpm test:e2e:affected`. + * Docs guards (OPS63): the `docs-guards` checks (changelog append-only, + * aggregate sync, conflict markers) also run here — cheap git diffs, same + * scripts the CI job runs (the `changelog-rewrite:` escape stays CI-only, + * PR body). * Skips align with `scripts/ci-scope.mjs` (`origin/main` by default). * * Preflight checks only `teqo_test` (ci-pr never touches the dev DB). Escape @@ -81,6 +85,11 @@ const main = async () => { run('lint', 'pnpm', ['lint']) run('format:check', 'pnpm', ['format:check']) + // --- Docs guards (OPS63): same checks as the CI `docs-guards` job --- + run('docs-guards (changelog append-only)', 'node', ['scripts/check-changelog-append-only.mjs']) + run('docs-guards (aggregate sync)', 'node', ['scripts/build-changelog.mjs', '--check']) + run('docs-guards (conflict markers)', 'node', ['scripts/check-docs-conflict-markers.mjs']) + if (scope.code.mode === 'none') { console.log('\n[gate:ci] ⊘ typecheck/knip/cycles skipped (no code surface)') } else { @@ -149,7 +158,9 @@ const main = async () => { console.log(`\n[gate:ci] ▶ e2e: runs in CI (job e2e, ${specs}) — not in this gate (OPS59)`) } - console.log('\n[gate:ci] ✓ all checks passed (ci-pr mirror without e2e — e2e verified in CI)') + console.log( + '\n[gate:ci] ✓ all checks passed (ci-pr mirror: docs guards local — OPS63; e2e verified in CI)', + ) } await main()