OPS108 — Modo autônomo por flag --auto nas skills plan-issue e work-issue

This commit is contained in:
Francisco Solla 2026-09-15 07:22:00 -03:00
parent a164961344
commit 46f659147d
7 changed files with 192 additions and 1 deletions

View file

@ -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:

View file

@ -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 <N> --auto` (a flag vive dentro do prompt, junto do `--issue <N>` 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`:

View file

@ -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

View file

@ -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 <N> [--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

View file

@ -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.

View file

@ -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 <N>` 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 <N> [--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)

View file

@ -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`,
)
})
})