9.3 KiB
Impl: Worktree new: worktree genérico sem função pré-definida
Status: aprovado Atualizado em: 2026-08-10 Issue: #572 Intenção: docs/plans/worktree-new-worktree-generico.md Appetite restante: ~0,5–1 dia eng (herdado — scripts/docs/testes, sem runtime nem DB de dados)
Leitura da intenção
- Outcome:
worktree new [bag]cria um worktree neutro a partir deorigin/maincom branchwork/<bag>(colisão → sufixo-2/-3) ouwork/<n>sequencial livre, provisionado de forma idêntica aoplan(slot/porta/bancos próprios, migrations aplicadas,--no-migraterespeitado, comentáriogerado por pnpm worktree new), cada invocação cria UM DIFERENTE, herda o default-go da OPS24 (--staysuprime,--gono-op),killfunciona de dentro dele, help/uso + AGENTS.md atualizados. - O que NÃO negociar: prefixo minúsculo
work/…que nunca colide com<Code>-<slug>donextnem complans/plan-issue-…doplan; provisionamento idêntico aoplan;newnão é um segundoplan(sem função de planejamento embutida); OPS26 (launch do opencode) é dependência opcional e não está em main — onewnasce só com o default-go, sem duplicar o launch. - O que reavaliar:
- A hipótese "generalizar
planBranchNamese valer a pena" — vale: a matemática de sufixo de colisão (-2/-3), sequencial livre e truncamento é idêntica e sutil entre os dois namespaces; um helper único impede drift (mesma família de guarda que o repo já aplica nos prefixos next/plan). GENERATED_ENV_MARKERé congelado: o unit test pina o texto exatogenerated by pnpm worktree nextereadLiveSlots/worktreeDatabaseNamesOf/envFileIsGenerateddependem dele para detectar envs geradas (inclusive de worktreesnextvivos). Onewnão parametriza o marker — o propósito real aparece na linhageneratedBydo header (gerado por pnpm worktree new), que oprovisionjá deriva depurpose.
- A hipótese "generalizar
Abordagem recomendada
flowchart LR
A[pnpm worktree new [bag]] --> B[namespaceBranchName: work/<bag> ou work/<n>]
A2[pnpm worktree plan [bag]] --> B2[planBranchName: plano existente]
B --> C[runner compartilhado cmdNamespaceBranch: fetch → taken-set → add → provision → cd]
B2 --> C
C --> D[worktree.mjs: purpose 'new'/'plan' + linhas cd por padrão]
D --> E[superfícies: worktree.sh + /worktree opencode aplicam cd]
Decisão 1 — nomeação de branch (scripts/lib/worktree.mjs).
Opções: A) extrair helper interno namespaceBranchName({ prefix, bag, taken, maxLength, fallback }); planBranchName vira wrapper fino (contrato + testes intactos) e novo workBranchName com WORK_BRANCH_PREFIX = 'work'; B) parametrizar planBranchName com prefixo opcional; C) duplicar a lógica em workBranchName.
Recomendação: A — a matemática de colisão/sequencial/truncamento é idêntica e sutil; um único lugar evita drift (os dois namespaces vivem no mesmo arquivo há uma semana de evolução ativa). Rejeitadas: B — planBranchName produzindo work/… mente no nome e obriga default paramétrico; C — duplica ~25 linhas de truncamento delicado (budget 60, keep mínimo 1).
Detalhes do contrato workBranchName: sem bag → próximo work/<n> sequencial livre (começa em work/1); com bag → work/<slug>; colisão → work/<slug>-2, -3, …; slug vazio → fallback 'work' (o plan preserva o fallback 'plano'); bag numérico (work new 3) compartilha o namespace sequencial (precedente uniforme do plan); budget total 60 (espelha branchNameForIssue/PLAN_BRANCH_MAX_LENGTH); o prefixo work/ é lowercase-led → estruturalmente disjunto do <Code>-<slug> uppercase-led e do plans/plan-issue-….
Decisão 2 — cmdNew (scripts/worktree.mjs).
Opções: A) extrair de cmdPlan o runner compartilhado cmdNamespaceBranch({ purpose, branchName, label }) (fetch → taken-set → check-ref-format → worktree add → provision → summary do ambiente → cd), com cmdPlan refatorado para o runner e cmdNew como segundo call site; B) clonar cmdPlan em cmdNew (~60 linhas); C) mode flag dentro de cmdPlan.
Recomendação: A — o CLI está em evolução ativa (OPS24/OPS26/OPS27 na mesma semana); duplicar ~60 linhas de git/provisionamento drifta. Rejeitadas: B — duplicação cara de manter; C — embaralha propósito em flag e cresce o teste mental do dispatch.
Decisão 3 — marker de env.
Opções: A) GENERATED_ENV_MARKER inalterado (texto histórico …next) para todos os purposes; B) parametrizar o marker por purpose.
Recomendação: A — o marker é contrato congelado e pinado: readLiveSlots, worktreeDatabaseNamesOf, envFileIsGenerated e o unit test dependem do texto exato; mudar quebraria a detecção de envs de worktrees next vivos e exigiria "migração" de marker. O comentário real por-worktree já diz o propósito (generatedBy). Rejeitada: B.
Componentes / mudanças
scripts/lib/worktree.mjs: helper internonamespaceBranchName; exportsWORK_BRANCH_PREFIX = 'work'eworkBranchName;planBranchNamerefatorado para wrapper (contrato idêntico, testes intactos).scripts/worktree.mjs: runnercmdNamespaceBranchextraído decmdPlan(taken-set, branch, add, provision, summary, cd);cmdNew(stay, skipMigrate, bag)compurpose: 'new'; dispatch comnew; bloco de uso (pnpm worktreesem args) e docblock do header atualizados (new [bag] [--stay] [--no-migrate]).tests/unit/worktree.unit.spec.ts: describe novoworkBranchName— sequencial livre (skip de taken), bag →work/<slug>, colisão-2/-3, slug vazio → fallback, bag numérico compartilha o namespace, truncamento ≤60, nunca colide comnextnemplan(regex de lowercase-led), invocações paralelas → branches diferentes..agents/shell/worktree.sh: header de uso comnew [bag] [--stay](a função já é genérica — repassa args e aplica ocd)..opencode/commands/worktree.md: frontmatterdescription+ corpo comnew [bag] [--stay](mesmo contrato decddoplan).AGENTS.md(seção "Per-worktree environments"): subcomandonew [bag]+ namespacework/…+purpose: 'new'..agents/skills/worktree-next-issue/SKILL.md: linha dos subcomandos comnew [bag] [--stay] [--no-migrate].docs/CHANGELOG-AGENTS.md: entrada curta da entrega.- Migration: sem migration (nenhum schema). Access / Consent: N/A — CLI de dev fora do runtime. UI: Impeccable A — N/A (sem superfície de produto).
Fases verificáveis
- Lib pura + testes —
namespaceBranchName/workBranchName/WORK_BRANCH_PREFIX+workBranchName.unit.spec.ts;pnpm test:unitno arquivo. - Runner + comando —
cmdNamespaceBranch(refactor decmdPlan) +cmdNew+ dispatch + uso + docblock; superfícies (shell/opencode) + docs (AGENTS.md, skill, CHANGELOG). - Verificação manual do caminho feliz — um
worktree new teste-gatereal (cria worktree + bancos + migrations no container local), conferir branchwork/teste-gate, env comgerado por pnpm worktree newe porta própria, e derrubar compnpm worktree kill --forcede dentro (valida também okillnum branchwork/…).pnpm worktreesem args mostra onewno uso. - Gates —
pnpm gate:fastna iteração;pnpm pushna entrega (format/knip/cycles/test/build).
Rabbit holes / Não escopo (engenharia)
- Launch do opencode no
new: OPS26 é o dono e não está em main — onewnasce com default-go; a superfície é a mesma que a OPS26 estenderá (nada a construir). - Não tocar
scripts/lib/worktree-env.mjsnemGENERATED_ENV_MARKER(pinados por teste). - Não criar terceira função embutida (
worktree new --issueetc.) — para isso existemnexteplan. - Não renomear/normalizar branches
work/…manuais existentes nem "migrar" markers de envs vivas. - Sem e2e: CLI fora do runtime; cobertura = unit da lib pura + verificação manual descartável.
Riscos e mitigação
- Refatorar
cmdPlanintroduz regressão no fluxo existente — mitigação: extração com diff minimal (movimentação pura de corpo + parâmetrospurpose/label/branch fn), mensagens de saída idênticas; conferir por diff e pelo caminho feliz donew(compartilha o runner). work/…colide com branch manual do usuário — taken-set local+origin já cobre (sufixo-2/-3); comportamento idêntico aoplan, documentado.- Slot do
newcolide com worktrees vivos — herdado:readLiveSlots+ bump noworktreeEnvironment(já testado). - Texto do marker
nextem envs denewconfundir leitura — a linhageneratedBycarrega o propósito real; registrado neste impl plan. worktree newsem Docker degrada para env compartilhado — herdado doprovision(mesmo aviso donext/plan).
Aceite de engenharia
- Aceite de produto da intenção ainda coberto (
new [bag]deorigin/main; branchwork/<bag>/work/<n>sem colisão; provision igual aoplancompurpose: 'new'e--no-migraterespeitado; default-go/--stay;killfunciona; uso + AGENTS.md atualizados) - Invariantes AGENTS/engineering-standards (marker e
worktree-env.mjsintocados; nenhum DB de prod; libs puras unit-testadas) - Testes de domínio previstos:
workBranchName(unit) + caminho feliz manual descartável