6.3 KiB
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(viapnpm pushe.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+ escapechangelog-rewrite:) não muda. - O que NÃO negociar: sem bypass novo local (falha local honesta +
git push --no-verifydocumentado para o caso raro); semplans-only-closesno 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 (defaultorigin/mainquandoGITHUB_BASE_REFausente) e o gate local é exatamente o espelho serial que omite o jobdocs-guards.
Abordagem recomendada
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 blocosrun()na fase 1 (apósformat:check, antes do bloco condicional de typecheck — incondicionais, como o jobdocs-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 passedmenciona os docs guards. Nada mais muda no arquivo.package.json/.husky/pre-push/scripts/git-push.mjs: intocados —gate:push=gate:cicontinua sendo a cadeia única..forgejo/workflows/ci-pr.yml/ci.yml: intocados —docs-guardscontinua dono do escapechangelog-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
- 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). - 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 escapechangelog-rewrite:segue só no CI/PR body). Verificação: grep final por descrições do gate que omitam os docs guards. - Changelog + gates —
docs/changelog/2026-08-18-ops63.md+pnpm changelog:build;pnpm gate:cilocal (cascata completa emteqo_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-closesno 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-markerslê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 usagit 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/maindesatualizado no worktree: merge-base velho pode acusar diff inexistente ou deixar passar diff novo. Mesmo comportamento dos guards existentes (ci-scopeusaorigin/main); ofetchdo 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 dopnpm pushde 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:civerdes; caminho de falha do append-only verificado manualmente com commit descartável