16 KiB
Impl: Launch do worktree honra as flags do humano: o modelo pedido e o --auto chegam onde deveriam
Status: em execução Atualizado em: 2026-09-16 Issue: #1088 Intenção: docs/plans/worktree-launch-honra-flags.md Appetite restante: herdado ~1 dia (parser + transporte + modelo na sessão; sem UI)
Leitura da intenção
- Outcome: digitar
--zen/--go/…abre a SESSÃO (a que o TUI anexa) no modelo pedido em todos os purposes — inclusive driverless (new,plansem bag);--autochega à mensagem inicial nas formas/work-issue --issue <N> --auto,/plan-issue --auto <bag>,/bug-fix --auto <bag>; flag desconhecida ou não-honrável falha alto com sugestão, nunca descarte silencioso. - O que NÃO negociar: preset
deepseek/deepseek-flashintocado; OPS119 (plan/newsem bag driverless); ciclo OPS110 (serve/start/attach/stop,TEQO_WORKTREE_TERMINAL) e contratopurposeInvocationpreservados; sem mudança de provider/auth;--autodo humano ≠--autode permissões doopencode run. - O que reavaliar: (i) a rota
/api/session/:id/modelnão é necessária no caminho principal —resolveStartDecisionsó devolvereusecom driver vivo/sessão busy (lib/agent-session.mjs:393-417), então não há reuso ocioso para trocar de modelo; (ii) o item "modelo na sessão" exige tocarcreateSession(HTTP) — manter a decisão testável extraindo a construção do body para helper puro.
Extensão de escopo (aprovada pelo humano em 2026-09-16)
A revisão de simplify provou que a skill bug-fix não tinha modo --auto (só work-issue/plan-issue têm): transpor /bug-fix --auto <bag> seria honra de mentira e sujaria a descrição do bug com --auto. O humano decidiu (opção "Implementar --auto no bug-fix") estender o item: a skill bug-fix ganha a seção §Modo autônomo (--auto), espelhando work-issue/plan-issue (auto-aprova diagnóstico/fix/prevenção/post-mortem/PR; continua parando em DB/prod, Consent/LGPD, migração, URL/shapes públicos, aprovação humana da produção e divergência material — --auto nunca aprova o environment production), e .opencode/commands/bug-fix.md documenta o uso. Isso sobrepõe o "Fora de escopo" da intenção por decisão explícita do dono do produto.
Abordagem recomendada
flowchart LR
H["worktree plan --zen --auto bag"] --> P[parseArgs]
P --> V["validateWorktreeFlags<br/>allowlist + nearestAcceptedFlag"]
V -->|desconhecida| D1["die + sugestão"]
V --> S["assertSkillAutoSupported(purpose,bag)"]
S -->|não honrável| D2["die + forma válida"]
S --> M["resolveWorktreeModel (at-most-one)"]
M --> L["opencodeLaunchDirective<br/>--model + --skill-auto"]
L --> SH["shell xargs"]
SH --> ST["agent-session start"]
ST --> CS["createSession POST /session {model}"]
ST --> PI["purposeInvocation(auto) → /work-issue --issue N --auto"]
CS --> TUI["opencode attach herda o model"]
PI --> DR["driverArgs run --auto -- …"]
Opções consideradas: A | B | C (por decisão abaixo)
Recomendação: modelo na sessão via POST /session (B), --auto anexado em purposeInvocation (A), validação fail-high espelhando OPS110-F1 — ataca a raiz (descarte silencioso / ausência de model na sessão) sem processo fantasma nem dono duplicado.
Rejeitadas: driver mínimo só para fixar modelo (A da intenção — reintroduz submissão espúria no driverless, contra OPS119); deixar driverless quebrado (C); --auto na diretiva de launch (dois donos do comando).
Componentes / mudanças
WORKTREE_FLAG_ALLOWLIST+validateWorktreeFlags(scripts/lib/worktree.mjs, novo): allowlist por subcomando; reusanearestAcceptedFlagimportado descripts/lib/agent-session.mjs(direçãolib→lib;agent-session.mjssó importaslug/builtins — sem ciclo;check:cyclescobre sósrc). Chamado no dispatch (scripts/worktree.mjs:1031) apóssubcommand, antes decmdNext/git. Allowlist:next:[issue,stay,no-migrate,+modelFlags,+auto],plan/new:[stay,no-migrate,+modelFlags,+auto],fix:[stay,no-migrate,headless,directive,+modelFlags,+auto],kill:[force,stay](stayfica p/ preservar a mensagem específica dekill, :1054).assertSkillAutoSupported({purpose, bag})(scripts/lib/worktree.mjs, novo, puro): matriz determinística —nextsempre;fixsempre;plansó com bag;newnunca;--stay+--autonunca (o launch é suprimido). Chamado no dispatch (mesmo ponto), antes de qualquer git. Sem migration, sem access, sem Consent.opencodeLaunchDirective(scripts/lib/worktree.mjs:202-232): novo parâmetroskillAuto→ emite--skill-auto(nome interno, decisão C) só quandotrue.--model=continua sempre (driverless inclusive).printLaunchDirective(scripts/worktree.mjs:225-239) + call sites :643 (next) e :747 (cmdNamespaceBranch): repassarskillAuto: Boolean(flags.auto).parseModelRef(model)+buildCreateSessionBody({model})(scripts/lib/agent-session.mjs, novos, puros): split no PRIMEIRO/→{providerID,id}; throw se não houver/; semvariant(OPS95); body{}sem model. Colocados aqui (não emlib/worktree.mjs) porquelib/worktree.mjspassará a importar delib/agent-session.mjs(decisão A) — evita ciclo.createSession(url, dir, model)(scripts/agent-session.mjs:269-276): usabuildCreateSessionBody;POST /session?directory=…com{ model: {providerID,id} }(verificado ao vivo noserve1.18.31: a sessão passa a reportar o model).cmdStart(scripts/agent-session.mjs:465-559): passamodelaocreateSession(:515);purposeInvocation({..., auto: Boolean(flags['skill-auto'])})(:472); decisão D no ramoreuse(:511).driverArgs(:291-317) intocado — segue emitindo o--autode permissões; o--autodo humano viaja como texto da invocation.purposeInvocation(scripts/lib/agent-session.mjs:187-203): novoauto=false; formas exatas--issue <N>[ --auto],[--auto ]<bag>,[--auto ][<bag>].SESSION_FLAG_ALLOWLIST.start(scripts/lib/agent-session.mjs:214): incluirskill-auto.assertSessionState(scripts/lib/agent-session.mjs:113-125): não exigemodel(decisão E); só doc.- Docs/comando:
.agents/shell/worktree.sh(comentário; shell não muda) e.opencode/commands/worktree.md(documentar--autoe o fail-high). - Migration: sem migration. Access/Consent/UI: N/A (Impeccable A — sem UI).
Dados → forma (se aplicável)
N/A — CLI; o efeito é observável no model do estado/servidor (~/.local/state/teqo/agent-sessions/<slug>.json e GET /session/<id>) e no log do driver.
Decisões de engenharia (caro de reverter)
| # | Decisão | Opções / Rejeitadas |
|---|---|---|
| A | validateWorktreeFlags em lib/worktree.mjs, reusando nearestAcceptedFlag |
Rec: reusar (um dono da distância OSA). Rej: duplicar OSA (drift); extrair p/ lib/cli.mjs (churn, 1 call site). |
| B | --auto não-honrável falha alto via assertSkillAutoSupported({purpose,bag}) puro chamado no dispatch antes do git |
Rec: helper puro (testável, falha antes de criar worktree). Rej: embutir em purposeInvocation (tarde, já provisionou); validar na skill (fora do launcher). |
| C | Nome do transporte interno no start |
Rec: --skill-auto — inequívoco, separa do --auto de permissões que driverArgs já emite. Rej: reusar --auto no start (dois significados no mesmo pipeline). |
| D | Sessão reusada com state.model ≠ pedido |
Rec: die claro ("run já em andamento no modelo X; encerre com pnpm agent:session stop e reabra para trocar") — reuse só ocorre com driver vivo/sessão busy, então não há janela para trocar; nunca descarte silencioso. Rej: trocar via /api/session/:id/model (complexidade extra; o ramo "ocioso" é inalcançável por resolveStartDecision); ignorar (quebra "honra as flags"). Gatilho de revisão: se reabrir sessão viva virar caso de uso, implementar a troca pela rota. |
| E | assertSessionState exige model? |
Rec: não exigir; só documentar model. Rej: exigir (estados antigos; model nunca foi obrigatório). |
| F | Parse do modelo | Rec: parseModelRef puro em lib/agent-session.mjs, throw sem /, sem variant (OPS95). Rej: em lib/worktree.mjs (ciclo com A); aceitar sem / (servidor rejeita remoto). |
Fases verificáveis
- Tracer — modelo na sessão:
parseModelRef/buildCreateSessionBody+createSession(model)+cmdStartpassandomodel. Prova: unit puro do body e criação real contra oservelocal.<0,3 dia> - Transporte
--auto:purposeInvocation(auto)nas 3 formas +SESSION_FLAG_ALLOWLIST.start+opencodeLaunchDirective({skillAuto})+ repasse nos call sites. Unit das formas exatas.<0,3 dia> - Fail-high no parser:
WORKTREE_FLAG_ALLOWLIST/validateWorktreeFlags/assertSkillAutoSupported+ dispatch + decisão D. Unit de flag desconhecida com sugestão, das células falsas da matriz e do reuso com modelo divergente.<0,3 dia> - Skill
bug-fix§Modo autônomo (extensão aprovada): seção na skill +Uso:no comando, espelhandowork-issue/plan-issue.<0,1 dia> - Docs + gates: atualizar comentários/
.opencode/commands/worktree.md;pnpm gate:fast; push viapnpm push.<0,1 dia>
Adiado com gatilho (triage de simplify, 2026-09-16)
Nenhum achado atingiu o piso de registro (score ≥3); os dois abaixo ficam adiados com gatilho:
assertKnownFlags/nearestAcceptedFlagcomo helpers neutros de CLI hoje moram emscripts/lib/agent-session.mjse são importados porscripts/lib/worktree.mjs(acopla launcher→domínio de sessão; sem ciclo, sem risco de runtime). Gatilho: um 3º consumidor de validação de flag, ouscripts/lib/cli.mjsganhar o dono do parser — então mover os três paracli.mjs.- Branch de reuso divergente (
scripts/agent-session.mjs, decisão D) sem teste unit — o CLI de sessão não tem harness impuro; a decisão pura está coberta porresolveStartDecision. Gatilho: o CLI ganhar harness impuro, ou a próxima mudança nesse ramo.
Rabbit holes / Não escopo (engenharia)
- Seletor de modelo no TUI / mexer em
WORKTREE_MODEL_MAPou preset. --autode permissões doopencode run; semântica das skills;--variant.- Caminho
--headless(OPS106): usaopencodeHeadlessArgs, não a invocation de skill —--autodo humano não se aplica; documentar. --modelcombinado com--stay: mantém comportamento atual (gatilho: aplicar a mesma regra depois se incomodar).- Validar flags do comando
/worktreecomo camada separada (a validação é no script, único dono; shell e comando não ganham allowlist própria).
Riscos e mitigação
- TUI não herda o model da sessão criada → validar cedo no Tracer (fase 1); fallback já desenhado e documentado (
POST /api/session/:id/model), sem mudar contrato público. - Quebra dos specs que pinam a diretiva (tests/unit/worktree.unit.spec.ts:153-320) →
--skill-autoé aditivo; strings semautopermanecem idênticas. --autoduplicado confundir replay de argv → nome interno distinto (C) e comentário nodriverArgs.- Reuse + modelo divergente (D) →
dieclaro; se estourar o appetite, manter odie(mais simples que funciona).
Aceite de engenharia
- Aceite de produto da intenção ainda coberto (modelo em todos os purposes;
--autonas 3 formas; fail-high) - Invariantes AGENTS/engineering-standards (preset/OPS110/OPS119 intactos; sem provider/auth)
- Testes de domínio previstos (unit puro; int N/A justificado)
Self-score (decision-quality)
5/5 — (1) decisões caras A–F com rejeitadas; (2) cabe no appetite ~1 dia com fases ≤0,3 dia; (3) rabbit holes nomeados (seletor, headless, --stay); (4) depth check: reusa nearestAcceptedFlag e o padrão validateSessionFlags, sem módulo pass-through; (5) outcome de produto preservado — engenharia não reescreveu o aceite.