mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
85 lines
9.5 KiB
Markdown
85 lines
9.5 KiB
Markdown
# Impl: OPS85 — Estratégia para eliminar conflitos de merge no changelog e reavaliar migração da documentação para fora do repo
|
||
|
||
Status: implementado
|
||
Atualizado em: 2026-08-24
|
||
Issue: #830
|
||
Intenção: docs/plans/ops85-changelog-e-docs-externos.md
|
||
|
||
## Leitura da intenção
|
||
|
||
- **Outcome:** zero conflito em arquivo de changelog; o guard de sync deixa de ser requisito de CI; a leitura "Recently resolved" permanece acessível com o **mesmo conteúdo**, por comando; a decisão de migrar docs para fora sai documentada com evidência (arquivamento explícito).
|
||
- **Anti-goals:** leitura preservada; sem cerimônia de merge por PR; ciclo claim→plano→PR intacto; sem segunda fonte de verdade; núcleo do `AGENTS.md` tocado só no apontamento mínimo.
|
||
|
||
## Descoberta do explorador (mudou o desenho da opção A)
|
||
|
||
`docs/changelog/` tem 65 arquivos (todos pós-OPS44, ≥2026-08-13), mas o agregado tinha **171 blocos "Recently resolved"** — ou seja, **106 entradas (2026-07-23 → 2026-08-13) existiam apenas no agregado**. Matar o agregado commitado sem preservá-las violaria o anti-goal "não perder a leitura estabelecida". Solução: **snapshot congelado** `docs/CHANGELOG-AGENTS-HISTORY.md` (D2), commitado 1× nesta entrega.
|
||
|
||
## Abordagem recomendada (Opção A — aprovada no gate)
|
||
|
||
O agregado deixa de ser commitado; `docs/changelog/` é o único registro versionado; a leitura é gerada sob demanda a partir do snapshot congelado + entradas; o guard de sync morre no CI/gate; o append-only vira additions-only por-arquivo + HISTORY congelado + agregado não-commitado.
|
||
|
||
**Rejeitadas:** **B** (merge driver git: desloca o custo para cada rebase, depende de config em toda máquina do pool; o incidente B183/C102 foi um merge "silencioso" que o driver não pega); **C** (status quo com serializes/rebase: é a evidência do humano provando que não funciona).
|
||
|
||
### Decisões de engenharia
|
||
|
||
- **D1 — destino do arquivo:** agregado **gitignored** no mesmo path (`git rm --cached`, working tree preservado). Remoção total do histórico (rewrite de 63 commits) rejeitada; congelar no lugar rejeitado (um agente poderia re-commitar e devolver a classe de conflito).
|
||
- **D2 — seed histórico:** `docs/CHANGELOG-AGENTS-HISTORY.md` = cópia integral do agregado atual + marcador de congelamento; o gerador usa `.blocks` como seed quando o arquivo local não existe. Fora de `docs/changelog/` (o `listChangelogEntries` é fail-closed para nomes fora do padrão).
|
||
- **D3 — comandos:** `changelog:build` (grava gitignored) + `changelog:read` (stdout, novo) + `changelog:check` (sanidade local opcional, fora de CI/gate). Uma flag no script existente, zero módulo novo.
|
||
- **D4 — mecanismo substituto do append-only:** (1) entradas por-arquivo additions-only (já existia); (2) HISTORY congelado zero-diff (só criação); (3) agregado proibido no diff — remoção única (status D) permitida só junto com a criação do HISTORY (a migração OPS85); (4) pós-condição do gerador: toda entrada parseada aparece no output. O multiset (`missingLines`) morre — a garantia de não-perda vira additions-only + pós-condição. Escape `changelog-rewrite:` permanece, CI-only, agora cobrindo correção do HISTORY.
|
||
- **D5 — docs externos:** **arquivados com evidência** — o conflito era do agregado (arquivo comum derivado), não das entradas por-arquivo; migrar 12+ skills criaria segunda fonte de verdade que os guards não enxergam. Alvo registrado se um dia for: Wiki.js. Nada instalado.
|
||
|
||
### Correção pós-revisão (self-hosting do guard)
|
||
|
||
O guard com `--name-status` detectou a migração como **rename** (`R099 docs/CHANGELOG-AGENTS-HISTORY.md`), o que falharia a regra "HISTORY congelado". Fix: `git diff --no-renames` no guard — a semântica é por path (deleção do agregado + criação do HISTORY são paths distintos), rename é ruído; o shape `A HISTORY + D agregado + A entrada` passa limpo, e diffs futuros ficam inequívocos.
|
||
|
||
## Componentes / mudanças
|
||
|
||
- `scripts/lib/changelog.mjs`: + `CHANGELOG_HISTORY`, + `CHANGELOG_HEADER` (cabeçalho estático novo usado no seed); `assertChangelogAppendOnly` reescrito (`{ changelogDiff, historyDiff, aggregateDiff }`); `missingLines` removido.
|
||
- `scripts/build-changelog.mjs`: leitura do agregado opcional (ausente → seed do HISTORY); `--stdout`; `--check` local (arquivo ausente → ok); pós-condição de não-perda.
|
||
- `scripts/check-changelog-append-only.mjs`: scope = agregado + HISTORY + dir; `--no-renames`; regras D4; escape intacto.
|
||
- `scripts/gate-ci.mjs`: remoção do run "aggregate sync" (fase 1 docs-guards).
|
||
- `.github/workflows/ci-pr.yml`: remoção do step "Aggregate is up to date".
|
||
- `package.json`: + `changelog:read`.
|
||
- `.gitignore`: + `/docs/CHANGELOG-AGENTS.md`.
|
||
- `docs/CHANGELOG-AGENTS-HISTORY.md` (novo, commitado): snapshot integral do agregado (212 blocos: 171 "Recently resolved" + Known Gaps + duplicatas históricas) + marcador de congelamento.
|
||
- Git index: `git rm --cached docs/CHANGELOG-AGENTS.md`.
|
||
- Contratos: `AGENTS.md`, `docs/AGENT-OPS.md` (item 4 + "Registries compartilhados" + seção "Decisões arquivadas"), `.agents/skills/work-issue/execution-pipeline.md`, `.github/pull_request_template.md`, `docs/roadmap.md`, `scripts/lib/agent-pool-prompt.mjs`, `.agents/rules/engineering-standards.mdc`, `.agents/rules/agent-pr-workflow.mdc`, `AGENTS-infra.md`.
|
||
- Testes: `tests/unit/changelog.unit.spec.ts` (assinatura nova + migração OPS85 + HISTORY congelado + agregado proibido; `missingLines` removido), `tests/unit/agentPoolPrompt.unit.spec.ts` (pin sem `changelog:build`).
|
||
|
||
## Fases executadas
|
||
|
||
1. Morte do agregado commitado + gerador sob demanda (lib + build + check + package.json + .gitignore).
|
||
2. HISTORY + `git rm --cached`; verificação em clone-fresco (213 blocos, órfãs pré-OPS44 preservadas).
|
||
3. Guard de sync fora de CI/gate (gate-ci.mjs, ci-pr.yml, AGENTS-infra.md).
|
||
4. Contratos (lista acima).
|
||
5. Testes + gates (`pnpm gate:fast` verde: 2440 testes; `--no-renames` fix).
|
||
6. Entrada `docs/changelog/2026-08-24-ops85.md` sem `changelog:build` antes do push — tracer bullet do fluxo novo.
|
||
|
||
## Riscos e mitigação
|
||
|
||
- **Perda das 106 entradas pré-OPS44:** seed HISTORY commitado + guard zero-diff + pós-condição do gerador + verificação em clone-fresco (fase 2). O HISTORY nunca é escrito pelo fluxo — a classe de conflito não volta por ele.
|
||
- **Alguém re-commita o agregado:** guard proíbe qualquer diff no path regenerável (exceto a migração única); PR vermelho com mensagem clara.
|
||
- **Clone-fresco sem arquivo em disco:** `changelog:read` (stdout) é o caminho canônico; `changelog:build` regenera.
|
||
- **Duplicação seed-vs-arquivos (65 blocos nos dois):** idempotência por texto exato do `buildChangelog` (verificado: 213 blocos, sem duplicatas).
|
||
- **Drift de contrato skills/tests/pool:** mesma entrega (fase 4+5); pin `agentPoolPrompt` quebra no CI se esquecido.
|
||
- **Escape mal usado:** semântica CI-only preservada; nada de bypass local novo.
|
||
|
||
## Aceite de engenharia
|
||
|
||
- Zero conflito de changelog (arquivo comum mutável sumiu do git); leitura "Recently resolved" com o mesmo conteúdo (seed + entradas); guard de sync fora de CI; decisão docs externos documentada (arquivada com evidência, alvo Wiki.js registrado).
|
||
- Nada commitado além da entrada por-arquivo; HISTORY é o único arquivo de changelog commitado e é congelado; escape CI-only preservado; `git rm --cached` sem rewrite de histórico.
|
||
- Testes: unit changelog (assinatura nova + migração), pin agentPoolPrompt; verificação manual em clone-fresco; `pnpm gate:fast` verde; guard append-only passa no próprio diff da migração.
|
||
|
||
## Triage pós-simplify (capture-review-debts, 2026-08-24)
|
||
|
||
**Já resolvido no simplify/critique (não reabrir):** 106 entradas pré-OPS44 só no agregado → HISTORY congelado commitado + verificação em clone-fresco (fase 2); classe de conflito do changelog → agregado gitignored; docs externos → arquivado com evidência (AGENT-OPS "Decisões arquivadas", alvo Wiki.js registrado).
|
||
|
||
**Explicitamente fora / Adiado com gatilho:**
|
||
|
||
- **Adiado com gatilho — pós-condição substring do gerador (`missingAggregateEntries`):** entrada que seja substring de bloco existente passa sem bloco próprio, e entrada com `\n\n` interno escapa da idempotência por bloco (pré-OPS44). Garantia honesta para "perda silenciosa" (bloco que some falha); reforço para igualdade de bloco só quando 1ª entrada real com `\n\n` interno for observada no agregado regenerado (duplicata visível, cosmético).
|
||
- **Adiado com gatilho — `--check` no-op com agregado local ausente:** documentado no docblock; viraria falso-verde só se `--check` voltar a rodar em CI/gate (OPS85 tirou do gate) — gatilho: re-entrada do check em CI.
|
||
- **Descartados:** guard "A do HISTORY sem D do agregado" (heuristic aceita; janela de migração fechada, HISTORY já em main); header antigo pré-OPS85 no build local (artefato regenerável, leitura canônica `changelog:read`, convergência via seed); CLI do gerador sem spec (convenção: camada CLI de scripts sem infra de teste; lógica pinada na lib); rótulo `docs-guards` sem step homônimo no ci-pr.yml (rótulo conceitual vivo no `gate-ci.mjs` — alinhar nomes = churn sem valor); `--stdout --check` com `--check` vencendo (precedência documentada, operação mais forte); knip sem carregar `payload.config.ts` (P3 ledgered pré-existente, TECH-DEBT.md).
|
||
|
||
## Self-score decision-quality
|
||
|
||
**5/5** — decisões caras com rejeitadas explícitas (A/B/C + D1-D5); cabe no appetite; rabbit holes nomeados; depth check (zero módulo novo, seed é dado não código); intenção satisfeita — a extensão (seed HISTORY) existe para cumprir o anti-goal de conteúdo íntegro.
|