teqo-backup/docs/plans/plan-issue-promote-dual-impl.md
Cursor Agent 7665b1062c
fix(ops): OPS18 simplify — Related regex, already-ready, job gate
Aperta parseRelatedIssueNumbers (anti hyphen), canPromotePlanIssue
already-ready, workflow só com Related # no body + PR_BODY, soft-fail
por Issue no Action script.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-03 00:22:55 +00:00

89 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Impl: Promote plan-issue — agente + Action no merge (dual)
Status: aprovado
Atualizado em: 2026-08-03
Issue: #296
Intenção: docs/plans/plan-issue-promote-dual.md
Appetite restante: ~0,5d (workflow + helper puro + script + pins; sem UI)
## Leitura da intenção
- **Outcome:** depois do plano em `main`, a Issue `blocked` aguardando plano vira `ready` mesmo se o agente do plan-issue não rodou `pnpm agent:ready`.
- **O que NÃO negociar:** promote só no **merge** (nunca no CI do PR aberto); não promover `blocked` de produto/deps sem heurística conservadora (`blocked` + link `docs/plans/` + sem gates humanos / estados ativos-terminais).
- **O que reavaliar:** “estender `issue-done-on-main-merge.yml` vs job irmão”; “bash inline vs node reusando `canPromotePlanIssue`”; restrição extra a diff `docs/plans/**`.
## Abordagem recomendada
```mermaid
flowchart LR
Merge["PR merge → main"] --> Parse["parseRelatedIssueNumbers(body)"]
Parse --> Fetch["gh issue view #N"]
Fetch --> Gate["canPromotePlanIssue"]
Gate -->|ok| Flip["blocked→ready + comment"]
Gate -->|no-op| Skip["log skip"]
Agent["pnpm agent:ready"] --> Gate
```
**Opções consideradas:** A) segundo job no mesmo workflow de `Closes`→`done` · B) workflow irmão dedicado + script node que reusa `canPromotePlanIssue` · C) bash inline reimplementando a heurística
**Recomendação:** B — porque o predicado de promote já vive em `scripts/lib/agent-plan-lifecycle.mjs` (OPS17); duplicar em bash é débito; misturar com o job de `Closes` acopla dois contratos e força checkout/`node` no job que hoje é só `gh`.
**Rejeitadas:** A — checkout + install no workflow de done só para um job; C — diverge do pin unitário e do `agent:ready`.
### Decisões de engenharia
1. **Onde vive o parse de `Related #N`?**
Opções: A) `plansOnlyClosesGuard.mjs` · B) `agent-plan-lifecycle.mjs` · C) só no script da Action.
**Recomendação:** B — é o lifecycle de promote; o guard de closes só rejeita keywords de fechamento.
**Rejeitadas:** A (domínio errado); C (sem pin unitário).
2. **Restringir a PRs que tocam `docs/plans/`?**
Opções: A) sim · B) não — só `Related` + `canPromotePlanIssue`.
**Recomendação:** B — `Related #N` já é o contrato dos PRs de planos (`plans-only-closes`); o predicado conservador evita flip de deps de produto. Restrição de path é rabbit hole (API de files, false negatives em PRs mistos).
**Rejeitada:** A até haver falso positivo real.
3. **Script da Action vs estender `agent:ready`?**
Opções: A) `agent:ready -- --from-pr N` soft · B) script dedicado `agent-promote-related-on-merge.mjs` soft-skip.
**Recomendação:** B — `agent:ready` continua fail-closed para o humano/agente; a Action precisa soft-skip em Issues Related que não estão “aguardando plano”.
**Rejeitada:** A — mistura UX CLI com safety net.
4. **`pnpm install` no workflow?**
Opções: A) install frozen · B) só `actions/setup-node` + `node scripts/…` (deps só stdlib).
**Recomendação:** B — a cadeia `agent-plan-lifecycle` → `agent-github` / `agent-pool-eligibility` não importa packages npm.
**Rejeitada:** A — custo sem ganho.
### Componentes / mudanças
- **`parseRelatedIssueNumbers(body)`** (`scripts/lib/agent-plan-lifecycle.mjs`): puro; case-insensitive; dedupe; ignora `Closes`/`Fixes`.
- **`scripts/agent-promote-related-on-merge.mjs`:** lê body do PR via `gh`; para cada `#N`, `canPromotePlanIssue`; flip + comentário “Action OPS18”; soft-skip (incl. already-ready).
- **`.github/workflows/plan-issue-ready-on-main-merge.yml`:** `pull_request` closed + merged → `main`; checkout + node 24; sem pnpm.
- **Docs:** `docs/AGENT-OPS.md` (tabela CI + nota OPS17/18); changelog curto; skill `plan-issue` já cita OPS18 — só amarrar o nome do workflow se faltar.
- **Migration / Access / Consent / UI:** N/A.
## Fases verificáveis
1. **Tracer** — helper + pins unit + script + workflow.
2. **Docs** — AGENT-OPS + changelog; skill se necessário.
3. **Gates** — `pnpm gate:fast`; entrega com `pnpm push`.
## Rabbit holes / Não escopo (engenharia)
- Promover no `ci-pr` / checks do PR aberto.
- Label nova `needs:plan`.
- Matar o promote do agente (`agent:ready`).
- Hoist `HUMAN_GATE_LABELS` / `issueHasPlanLink` / lista ativa-ou-terminal para módulo neutro — defer até 3º consumidor fora do lifecycle.
- Extrair executor compartilhado promote-one-issue (`agent:ready` × Action) — defer até 3º call site ou política de falha convergente.
- Unificar workflows de merge (`issue-done` + plan-ready) num único runner — defer; acoplamento rejeitado na decisão B.
- Comentário duplicado se agente e Action promovem na mesma janela — benigno; labels idempotentes.
## Riscos e mitigação
- **Flip frouxo de `Related` de dep de produto:** mitiga `canPromotePlanIssue` (exige link `docs/plans/` + `blocked` + sem needs:\*/estados).
- **Segunda execução (agente + Action):** idempotente — already-ready = skip.
- **Workflow só na default branch:** igual aos outros merge triggers; merge em `main` é o evento.
## Aceite de engenharia
- [x] Aceite de produto da intenção ainda coberto
- [x] Invariantes AGENTS/engineering-standards (sem schema/access)
- [x] Testes de domínio: unit em `parseRelatedIssueNumbers` + predicado existente
Self-score decision-quality: 5/5