diff --git a/.agents/skills/plan-issue/SKILL.md b/.agents/skills/plan-issue/SKILL.md index 42252beb..f52df98a 100644 --- a/.agents/skills/plan-issue/SKILL.md +++ b/.agents/skills/plan-issue/SKILL.md @@ -122,6 +122,17 @@ Antes de criar Issues: **Pare e espere.** Itere até confirmação explícita do lote. +## Modo autônomo (`--auto`) + +Opt-out da pausa por invocação. Detecta-se em `$ARGUMENTS`: `/plan-issue --auto ...` (a flag vive dentro do prompt, junto dos demais argumentos; sem parser dedicado). + +- **Sem a flag (ou flag desconhecida):** o Passo 4 GATE vale idêntico — pare e espere confirmação explícita. Nada no tracker antes do gate. +- **Com a flag:** o Passo 4 vira apresentação-sem-pausa (o overview continua obrigatório, mas no resumo final, não como pedido de confirmação); o Passo 5 (registro) segue direto: `pnpm agent:register` por plano aprovado pelo próprio agente, PR `Related #N`, `pnpm agent:ready`. +- **Pode auto-aprovar:** conteúdo e fatiamento dos planos, prioridade, dependências, abordagem de intenção, cortes de escopo já previstos na skill e os literais de dados que o próprio plano recomenda (registrados como assumidos). +- **Continua parando (vale mesmo com a flag):** Consent/LGPD fail-closed, migração de schema, contrato de URL público, shapes públicos, produção/aprovação humana; divergência material de produto → o item **não é registrado** e fica fora do lote até decisão humana, reportado no resumo final. +- **Não confundir:** `--auto` aqui é flag da skill dentro do prompt; o `--auto` do CLI opencode (auto-aprovar permissões de ferramenta) é outro conceito. +- **Convivência:** `agent-work-issue` continua sendo o contrato do pool dormente; este modo é opt-in da sessão humana. Consolidação futura é item à parte. + ## Passo 5 — Registro (sub-agente registrador) Após confirmação, dispatche o sub-agente registrador com: diff --git a/.agents/skills/work-issue/SKILL.md b/.agents/skills/work-issue/SKILL.md index 3cb4fe1a..01643a8b 100644 --- a/.agents/skills/work-issue/SKILL.md +++ b/.agents/skills/work-issue/SKILL.md @@ -8,7 +8,7 @@ disable-model-invocation: true Executa uma Issue já claimada de ponta a ponta com supervisão humana. A sessão nasce no contrato: Issue claimada, worktree correto, branch correta. -**Proibido:** DB de prod; merge sem CI green; editar outras Issues `in-progress`; pular a pausa do impl plan; Draft / sem auto-merge. +**Proibido:** DB de prod; merge sem CI green; editar outras Issues `in-progress`; pular a pausa do impl plan (exceto com `--auto`, ver §Modo autônomo); Draft / sem auto-merge. ## Decomposição em sub-agentes @@ -133,6 +133,17 @@ Apresente no chat: Divergência material de produto → pare, pergunte ao humano. +## Modo autônomo (`--auto`) + +Opt-out da pausa por invocação. Detecta-se em `$ARGUMENTS`: `/work-issue --issue --auto` (a flag vive dentro do prompt, junto do `--issue ` do contrato OPS33; sem parser dedicado). + +- **Sem a flag (ou flag desconhecida):** o Passo 3c GATE vale idêntico — apresente o plano, pare e aguarde confirmação explícita. Proibido pular a pausa. +- **Com a flag:** o impl plan do Passo 3b nasce marcado `aprovado` pelo próprio agente; o Passo 3c vira apresentação-no-chat-sem-espera (abordagem + rejeitadas + fases + riscos continuam obrigatórios no chat e no arquivo) e a execução (Passo 4) segue direto até o PR. +- **Pode auto-aprovar:** abordagem técnica, cortes de escopo já previstos na skill e os literais de dados que o próprio plano recomenda (registrados como assumidos). +- **Continua parando (vale mesmo com a flag):** Consent/LGPD fail-closed, migração de schema, contrato de URL público, shapes públicos, produção/aprovação humana, merge sem CI green, DB de prod; divergência material de produto → pare, comente na Issue e flipe para `blocked` (precedente do fluxo autônomo). O claim continua contrato do ambiente: este modo nunca claima Issue. +- **Não confundir:** `--auto` aqui é flag da skill dentro do prompt; o `--auto` do CLI opencode (auto-aprovar permissões de ferramenta) é outro conceito. +- **Convivência:** `agent-work-issue` continua sendo o contrato do pool dormente; este modo é opt-in da sessão humana. Consolidação futura é item à parte. + ## Passo 4 — Executar `SwitchMode` → `agent`. Siga `execution-pipeline.md`: diff --git a/.opencode/commands/plan-issue.md b/.opencode/commands/plan-issue.md index a4bfa1ea..36e2c733 100644 --- a/.opencode/commands/plan-issue.md +++ b/.opencode/commands/plan-issue.md @@ -4,4 +4,6 @@ description: Transforma ideias humanas em Issues GitHub + planos de intenção e Carregue a skill `plan-issue` (ferramenta de skills, nome exato) e siga o fluxo dela de ponta a ponta. A fonte canônica é `.agents/skills/plan-issue/SKILL.md` — não a transcreva nem recrie o fluxo. +Uso: /plan-issue [--auto] [...ideias]. Com `--auto`, a skill dispensa a pausa do GATE e segue até o registro; sem ela, o fluxo supervisionado atual vale. + $ARGUMENTS diff --git a/.opencode/commands/work-issue.md b/.opencode/commands/work-issue.md index 0c619712..3b4ecc6e 100644 --- a/.opencode/commands/work-issue.md +++ b/.opencode/commands/work-issue.md @@ -4,4 +4,6 @@ description: Executa uma Issue já claimada de ponta a ponta com supervisão hum Carregue a skill `work-issue` (ferramenta de skills, nome exato) e siga o fluxo dela de ponta a ponta. A fonte canônica é `.agents/skills/work-issue/SKILL.md` — não a transcreva nem recrie o fluxo. +Uso: /work-issue --issue [--auto]. Com `--auto`, a skill marca o impl plan como aprovado e executa até o PR sem pausa; sem ela, a pausa do GATE vale. + $ARGUMENTS diff --git a/docs/changelog/2026-09-15-ops108.md b/docs/changelog/2026-09-15-ops108.md new file mode 100644 index 00000000..4f0d2bb9 --- /dev/null +++ b/docs/changelog/2026-09-15-ops108.md @@ -0,0 +1 @@ +**OPS108 — Modo autônomo por flag nas skills plan-issue e work-issue (2026-09-15):** as duas skills ganham opt-out da pausa por invocação via flag única `--auto` (dentro do prompt, ex. `/work-issue --issue 1018 --auto`); sem a flag, os GATEs atuais valem idênticos. Com a flag, `plan-issue` registra sem confirmação (divergência material → item fora do lote) e `work-issue` marca o impl plan como aprovado e executa até o PR (divergência → `blocked`, nunca claima). A flag nunca atravessa os invariantes duros (Consent/LGPD fail-closed, migração de schema, URL público, shapes públicos, produção, CI green). Os dois comandos viram a flag numa linha de uso sem ganhar lógica (guard OPS25 intacto); o texto desambigua do `--auto` do CLI opencode. Testes: novo `skillsAutoFlag.unit.spec.ts` (8 testes pinando flag, fail-safe, lista dura e contrato thin-wrapper); suite unit 3075 verde, lint/typecheck/format limpos. diff --git a/docs/plans/ops108-modo-autonomo-por-flag-impl.md b/docs/plans/ops108-modo-autonomo-por-flag-impl.md new file mode 100644 index 00000000..89b82d39 --- /dev/null +++ b/docs/plans/ops108-modo-autonomo-por-flag-impl.md @@ -0,0 +1,92 @@ +# Impl: OPS108 — Modo autônomo por flag nas skills plan-issue e work-issue + +Status: aprovado +Atualizado em: 2026-09-15 +Issue: #1018 +Intenção: docs/plans/ops108-modo-autonomo-por-flag.md +Appetite restante: herdado (~0,5–1 dia eng; só texto de skills/comandos + teste unit) + +## Leitura da intenção + +- **Outcome:** com `--auto`, `plan-issue` vai do overview ao registro e `work-issue` vai do impl plan ao PR **sem** pausa de confirmação; sem a flag, os GATEs atuais permanecem idênticos. +- **O que NÃO negociar:** a pausa continua default (opt-in por invocação); a flag nunca atravessa os invariantes duros (Consent/LGPD fail-closed, migração de schema, contrato de URL público, shapes públicos, aprovação humana de produção); divergência material de produto continua parando (work-issue → `blocked`; plan-issue → item fora do lote); claim continua contrato do ambiente (`work-issue --auto` nunca claima); sem CI green não mergea; comandos continuam wrappers finos (guard OPS25). +- **O que reavaliar:** a hipótese de direção ("a flag viaja no texto já repassado hoje") está correta — `$ARGUMENTS` já carrega `--issue ` por convenção de prompt, sem parser; `--auto` segue o mesmo caminho. Confirmado: `scripts/lib/worktree.mjs:207-232` monta `--prompt "/work-issue --issue N"` (a flag da skill vive **dentro** da string do prompt, não no argv do CLI). + +## Abordagem recomendada + +```mermaid +flowchart LR + A["/plan-issue --auto ou /work-issue --issue N --auto"] --> B["skill detecta --auto em $ARGUMENTS"] + B -->|"sem flag"| C["GATE atual idêntico (pausa)"] + B -->|"com flag"| D["auto-aprova recomendação própria, segue até registro/PR"] + D --> E["parada dura? → para e reporta (blocked/fora do lote)"] +``` + +**Opções consideradas:** A | B | C (por decisão abaixo) +**Recomendação:** semântica espelhada mínima em cada `SKILL.md` + uma linha de descobribilidade em cada comando + spec unit que pina o novo texto. +**Rejeitadas:** ver cada decisão. + +### Decisões de engenharia + +**D1 — Onde vive a semântica da flag.** +Opções: A) bloco compartilhado novo referenciado pelas duas skills · B) seção dedicada espelhada em cada `SKILL.md` · C) só nos comandos. +Recomendação: B — cada skill é dona do próprio GATE (`plan-issue` Passo 4, `work-issue` Passo 3c), e skills são fonte canônica autocontida (sub-agentes recebem contexto mínimo); ~15 linhas espelhadas, cada uma redigida na voz do próprio GATE. +Alternativas rejeitadas: A porque um include cross-skill cria indireção e acoplamento para um texto curto que precisa ser lido inline no momento do gate; C porque comandos são proibidos de transcrever fluxo (guard OPS25 — `opencodeCommands.unit.spec.ts`). + +**D2 — Como os comandos expõem `--auto` sem ganhar lógica.** +Opções: A) só no `description` do frontmatter · B) uma linha de uso no corpo, mantendo `$ARGUMENTS` + ponteiro canônico · C) nada nos comandos. +Recomendação: B — ex. `Uso: /work-issue --issue [--auto]` + frase de uma linha ("`--auto` dispensa a pausa do GATE; sem ela, o fluxo supervisionado atual vale"). O corpo continua sem transcrever o fluxo. +Alternativas rejeitadas: A porque `description` é curta (TUI) e não comporta semântica nem desambiguação; C porque a flag ficaria indescobrível no prompt `/`. + +**D3 — Desambiguação `--auto` da skill vs `--auto` do CLI opencode.** +Opções: A) nota explícita de uma frase em cada skill (e eco curto nos comandos) · B) renomear a flag (ex. `--autonomous`) · C) ignorar a colisão. +Recomendação: A — a intenção já decidiu `--auto` (gate 2026-09-15); o texto diz: "`--auto` aqui é flag da skill dentro do prompt (ex. `/work-issue --issue 1018 --auto`); não confundir com `--auto` do CLI opencode (auto-aprovar permissões de ferramenta, ex. `launch opencode … --auto --prompt …` em `scripts/lib/worktree.mjs:218`)". +Alternativas rejeitadas: B porque reabre decisão de produto fechada e quebra a simetria `--issue`/`--auto` no mesmo canal (`$ARGUMENTS`); C porque a colisão é real e já pinada em teste (`worktree.unit.spec.ts:164-277`) — silêncio geraria invocações erradas. + +**D4 — Questões em aberto da intenção (assumidos).** +Opções (divergência no `plan-issue` autônomo): A) não registra, reporta no resumo · B) registra `blocked`. +Recomendação: A — preserva "nada no tracker antes do gate"; o item volta barato quando o humano decidir. +Opções (`work-issue --auto` vs `agent-work-issue`): A) separados neste item · B) consolidar. +Recomendação: A — contratos diferentes (sessão humana opt-in vs pool dormente); convivência documentada numa frase em cada skill; consolidação é item futuro. + +### Componentes / mudanças + +- **`plan-issue/SKILL.md`** (`.agents/skills/plan-issue/SKILL.md`): nova seção "Modo autônomo (`--auto`)": detecção em `$ARGUMENTS`; Passo 4 GATE vira apresentação-sem-pausa (overview continua obrigatório no resumo); registro segue Passo 5 sem confirmação; lista fechada do que pode auto-aprovar vs parada dura (cópia das listas da intenção); divergência material → item fora do lote + resumo final; fail-safe ("flag ausente/desconhecida → GATE idêntico"); nota de desambiguação CLI; frase de convivência com `agent-work-issue`. +- **`work-issue/SKILL.md`** (`.agents/skills/work-issue/SKILL.md`): nova seção "Modo autônomo (`--auto`)": parsing em Passo 1 (`--auto` em `$ARGUMENTS`, ex. `/work-issue --issue 1018 --auto`); Passo 3b marca impl plan `aprovado` sem pausa; Passo 3c vira apresentação-no-chat-sem-espera; "Proibido" ganha exceção explícita ("pular a pausa do impl plan" continua proibido, exceto com `--auto` — todo o resto do Proibido intocado, inclusive nunca claimar); mesmas listas fechadas; divergência → `blocked` (precedente `agent-work-issue`); fail-safe; desambiguação CLI. +- **Comandos** (`.opencode/commands/plan-issue.md`, `work-issue.md`): +1 linha de uso com `[--auto]` + 1 frase (dispensa a pausa; aponta a skill como canônica). `$ARGUMENTS`, `` `nome` ``, ponteiro canônico e frontmatter `description` sem `model:` preservados — guard existente continua verde. +- **Migration:** sem migration (só markdown). +- **Access / Consent:** N/A (sem superfície de dados); as listas de parada dura citam Consent fail-closed sem tocá-lo. +- **UI:** Impeccable A — sem UI, sem rascunho. +- **Testes:** novo `tests/unit/skillsAutoFlag.unit.spec.ts` pinando o contrato textual: cada `SKILL.md` contém `--auto` + menção de parada dura (Consent/LGPD, migração, URL público, produção) + frase fail-safe ("sem a flag"); cada comando contém `--auto` + `$ARGUMENTS` + ponteiro canônico. Guard `opencodeCommands.unit.spec.ts` existente deve continuar verde sem edição (se exigir ajuste, o ajuste é no comando, não no guard). + +### Dados → forma (se aplicável) + +N/A — sem superfície de dados (intenção: "Dados: N/A"). + +## Fases verificáveis + +1. **Tracer / texto+teste** (quota: quase todo o appetite) — editar 2 `SKILL.md` + 2 comandos + criar `skillsAutoFlag.unit.spec.ts`; rodar `pnpm vitest run tests/unit/skillsAutoFlag.unit.spec.ts tests/unit/opencodeCommands.unit.spec.ts`; depois `pnpm gate:fast`. +2. **UI** — N/A (Impeccable A). +3. **Gates** — `pnpm gate:fast` na iteração; entrega com `pnpm push` (não `git push` nu). E2E: nenhum (sem superfície de produto; skill-docs não entram no `E2E_AFFECTED_MANIFEST`). Int: nenhum (sem DB). + +## Rabbit holes / Não escopo (engenharia) + +- Renomear a flag ou ler `--auto` do argv do CLI (a flag mora no prompt `$ARGUMENTS`, não no argv — `worktree.mjs:218` é outro `--auto`). +- Lógica nos comandos (quebra OPS25) ou skill gêmea "autônoma" (twin = drift; intenção corta). +- Puxar claim/pool/launch de worktree para a skill (contrato do ambiente; `worktree.mjs:207-232` intocado). +- Tocar `agent-work-issue`, `AGENT-OPS.md`, `engineering-audit` ou `E2E_AFFECTED_MANIFEST` (fora do escopo fechado: 2 skills + 2 comandos + 1 spec). +- Quoting: exemplos com aspas no prompt passam por xargs no launch — manter exemplos simples, sem aspas aninhadas. + +## Riscos e mitigação + +- **Colisão mental `--auto` skill vs CLI** → mitigado pela nota de desambiguação (D3) + exemplo canônico `/work-issue --issue 1018 --auto` nos dois lugares. +- **Bypass acidental de invariante** → mitigado pelas listas fechadas duplicadas (auto-aprovável vs parada dura) + fail-safe + spec que pina a menção de parada dura. +- **Drift com `agent-work-issue`** → mitigado pela frase de convivência (D4-A); sem edição no contrato do pool. +- **Sub-agente escritor indisponível** (falhou 2x nesta sessão com erro transitório do provider) → mitigado: plano escrito inline pelo agente principal a partir dos findings do explorador + leitura direta dos arquivos; registrar no resumo. + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção ainda coberto (flag opt-out só da pausa; listas fechadas; fail-safe; claim/CI/produção intocados) +- [ ] Invariantes AGENTS/engineering-standards (edit the owner, don't twin; comandos finos; copy pt-BR / identificadores em inglês) +- [ ] Testes de domínio previstos (unit textual do contrato; sem access/write paths — sem unit/int de domínio) +- [ ] Self-score decision-quality: 5/5 (caras com rejeitadas: D1–D4; cabe no appetite; rabbit holes nomeados; depth check: zero módulo novo, reuso dos donos; outcome da intenção intacto) diff --git a/tests/unit/skillsAutoFlag.unit.spec.ts b/tests/unit/skillsAutoFlag.unit.spec.ts new file mode 100644 index 00000000..2498bd1d --- /dev/null +++ b/tests/unit/skillsAutoFlag.unit.spec.ts @@ -0,0 +1,72 @@ +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { describe, expect, it } from 'vitest' + +// OPS108: pins the `--auto` autonomous-mode contract of the plan-issue and +// work-issue skills (and their thin command wrappers). The flag lives inside +// the prompt ($ARGUMENTS), not in the opencode CLI argv — the specs below pin +// the fail-safe wording (no flag = current supervised flow) and the hard-stop +// list that the flag can never cross. + +const repoRoot = process.cwd() + +const skills = ['plan-issue', 'work-issue'] as const + +function readSkill(name: (typeof skills)[number]): string { + return readFileSync(resolve(repoRoot, `.agents/skills/${name}/SKILL.md`), 'utf8') +} + +describe.each(skills)('skill /%s documents the --auto autonomous mode', (name) => { + it('declares the flag, the fail-safe and the CLI disambiguation', () => { + const content = readSkill(name) + + expect(content, 'skill must declare the --auto flag').toContain('--auto') + expect(content, 'skill must name the autonomous-mode section').toContain('Modo autônomo') + expect(content, 'skill must state the no-flag fail-safe').toContain('Sem a flag') + expect(content, 'skill must disambiguate from the opencode CLI flag').toContain('Não confundir') + expect(content, 'disambiguation must name the opencode CLI').toContain('CLI opencode') + }) + + it('pins the hard-stop list the flag can never cross', () => { + const content = readSkill(name) + + expect(content, 'hard-stop must cite Consent/LGPD').toContain('Consent/LGPD') + expect(content, 'hard-stop must cite schema migration').toContain('migração de schema') + expect(content, 'hard-stop must cite the public URL contract').toContain('URL público') + expect(content, 'hard-stop must cite production').toContain('produção') + }) +}) + +describe('work-issue --auto keeps the environment contract', () => { + it('self-approves the impl plan but never claims and flips divergence to blocked', () => { + const content = readSkill('work-issue') + + expect(content, 'autonomous impl plan must be self-approved').toContain('aprovado') + expect(content, 'autonomous mode must never claim an issue').toContain('nunca claima') + expect(content, 'material divergence must flip to blocked').toContain('blocked') + }) +}) + +describe('plan-issue --auto keeps the tracker gate', () => { + it('skips only the pause and leaves diverged items unregistered', () => { + const content = readSkill('plan-issue') + + expect(content, 'diverged items must stay unregistered').toContain('não é registrado') + expect(content, 'nothing hits the tracker before the gate').toContain( + 'Nada no tracker antes do gate', + ) + }) +}) + +describe.each(skills)('command /%s advertises --auto without gaining logic', (name) => { + it('mentions --auto while keeping the thin-wrapper contract', () => { + const content = readFileSync(resolve(repoRoot, `.opencode/commands/${name}.md`), 'utf8') + + expect(content, 'command must advertise the --auto flag').toContain('--auto') + expect(content, 'command must pass arguments through').toContain('$ARGUMENTS') + expect(content, 'command must reference the skill by exact name').toContain(`\`${name}\``) + expect(content, 'command must point at the canonical skill file').toContain( + `.agents/skills/${name}/SKILL.md`, + ) + }) +})