teqo-backup/docs/plans/worktree-new-worktree-generico-impl.md

81 lines
9.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: 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 de `origin/main` com branch `work/<bag>` (colisão → sufixo `-2`/`-3`) ou `work/<n>` sequencial livre, provisionado de forma idêntica ao `plan` (slot/porta/bancos próprios, migrations aplicadas, `--no-migrate` respeitado, comentário `gerado por pnpm worktree new`), cada invocação cria UM DIFERENTE, herda o default-go da OPS24 (`--stay` suprime, `--go` no-op), `kill` funciona de dentro dele, help/uso + AGENTS.md atualizados.
- **O que NÃO negociar:** prefixo minúsculo `work/…` que nunca colide com `<Code>-<slug>` do `next` nem com `plans/plan-issue-…` do `plan`; provisionamento idêntico ao `plan`; `new` **não** é um segundo `plan` (sem função de planejamento embutida); OPS26 (launch do opencode) é dependência **opcional e não está em main** — o `new` nasce só com o default-go, sem duplicar o launch.
- **O que reavaliar:**
- A hipótese "generalizar `planBranchName` se 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 exato `generated by pnpm worktree next` e `readLiveSlots`/`worktreeDatabaseNamesOf`/`envFileIsGenerated` dependem dele para detectar envs geradas (inclusive de worktrees `next` vivos). O `new` **não** parametriza o marker — o propósito real aparece na linha `generatedBy` do header (`gerado por pnpm worktree new`), que o `provision` já deriva de `purpose`.
## Abordagem recomendada
```mermaid
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 interno `namespaceBranchName`; exports `WORK_BRANCH_PREFIX = 'work'` e `workBranchName`; `planBranchName` refatorado para wrapper (contrato idêntico, testes intactos).
- **`scripts/worktree.mjs`**: runner `cmdNamespaceBranch` extraído de `cmdPlan` (taken-set, branch, add, provision, summary, cd); `cmdNew(stay, skipMigrate, bag)` com `purpose: 'new'`; dispatch com `new`; bloco de uso (`pnpm worktree` sem args) e docblock do header atualizados (`new [bag] [--stay] [--no-migrate]`).
- **`tests/unit/worktree.unit.spec.ts`**: describe novo `workBranchName` — 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 com `next` nem `plan` (regex de lowercase-led), invocações paralelas → branches diferentes.
- **`.agents/shell/worktree.sh`**: header de uso com `new [bag] [--stay]` (a função já é genérica — repassa args e aplica o `cd`).
- **`.opencode/commands/worktree.md`**: frontmatter `description` + corpo com `new [bag] [--stay]` (mesmo contrato de `cd` do `plan`).
- **`AGENTS.md`** (seção "Per-worktree environments"): subcomando `new [bag]` + namespace `work/…` + `purpose: 'new'`.
- **`.agents/skills/worktree-next-issue/SKILL.md`**: linha dos subcomandos com `new [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
1. **Lib pura + testes** — `namespaceBranchName`/`workBranchName`/`WORK_BRANCH_PREFIX` + `workBranchName.unit.spec.ts`; `pnpm test:unit` no arquivo.
2. **Runner + comando** — `cmdNamespaceBranch` (refactor de `cmdPlan`) + `cmdNew` + dispatch + uso + docblock; superfícies (shell/opencode) + docs (AGENTS.md, skill, CHANGELOG).
3. **Verificação manual do caminho feliz** — um `worktree new teste-gate` real (cria worktree + bancos + migrations no container local), conferir branch `work/teste-gate`, env com `gerado por pnpm worktree new` e porta própria, e derrubar com `pnpm worktree kill --force` de dentro (valida também o `kill` num branch `work/…`). `pnpm worktree` sem args mostra o `new` no uso.
4. **Gates** — `pnpm gate:fast` na iteração; `pnpm push` na 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 — o `new` nasce com default-go; a superfície é a mesma que a OPS26 estenderá (nada a construir).
- Não tocar `scripts/lib/worktree-env.mjs` nem `GENERATED_ENV_MARKER` (pinados por teste).
- Não criar terceira função embutida (`worktree new --issue` etc.) — para isso existem `next` e `plan`.
- 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 `cmdPlan` introduz regressão no fluxo existente** — mitigação: extração com diff minimal (movimentação pura de corpo + parâmetros `purpose`/`label`/branch fn), mensagens de saída idênticas; conferir por diff e pelo caminho feliz do `new` (compartilha o runner).
- **`work/…` colide com branch manual do usuário** — taken-set local+origin já cobre (sufixo `-2`/`-3`); comportamento idêntico ao `plan`, documentado.
- **Slot do `new` colide com worktrees vivos** — herdado: `readLiveSlots` + bump no `worktreeEnvironment` (já testado).
- **Texto do marker `next` em envs de `new` confundir leitura** — a linha `generatedBy` carrega o propósito real; registrado neste impl plan.
- **`worktree new` sem Docker degrada para env compartilhado** — herdado do `provision` (mesmo aviso do `next`/`plan`).
## Aceite de engenharia
- [ ] Aceite de produto da intenção ainda coberto (`new [bag]` de `origin/main`; branch `work/<bag>`/`work/<n>` sem colisão; provision igual ao `plan` com `purpose: 'new'` e `--no-migrate` respeitado; default-go/`--stay`; `kill` funciona; uso + AGENTS.md atualizados)
- [ ] Invariantes AGENTS/engineering-standards (marker e `worktree-env.mjs` intocados; nenhum DB de prod; libs puras unit-testadas)
- [ ] Testes de domínio previstos: `workBranchName` (unit) + caminho feliz manual descartável