Merge pull request #578 from fsolla/OPS24-worktree-cli-go-vira-o-padrao-escape-stay-kill-volta-a
OPS24 — Worktree CLI: --go vira o padrão (escape --stay) + kill volta ao main
This commit is contained in:
commit
ab35996f14
8 changed files with 139 additions and 48 deletions
|
|
@ -1,23 +1,26 @@
|
|||
# worktree — CLI determinístico de worktrees em torno da fila de claim, com um
|
||||
# `--go` que realmente troca de diretório.
|
||||
# worktree — CLI determinístico de worktrees em torno da fila de claim, com
|
||||
# troca de diretório por padrão (e `kill` que volta ao main).
|
||||
#
|
||||
# A lógica determinística (fila → branch → worktree) vive em scripts/worktree.mjs;
|
||||
# esta função é só a camada de shell que APLICA o `cd <dir>` que o script imprime,
|
||||
# porque um processo filho (node) não consegue mudar o cwd do shell que o chamou.
|
||||
# esta função é só a camada de shell que APLICA o `cd <dir>` que o script imprime
|
||||
# na última linha, porque um processo filho (node) não consegue mudar o cwd do
|
||||
# shell que o chamou.
|
||||
#
|
||||
# Instalação (uma linha no profile):
|
||||
# source <repo>/.agents/shell/worktree.sh
|
||||
#
|
||||
# Uso (terminal interativo):
|
||||
# worktree next [--go] cria/reutiliza o worktree da próxima Issue claimável;
|
||||
# com --go, cd para dentro dele
|
||||
# worktree plan [bag] [--go] cria um worktree de planejamento do /plan-issue
|
||||
# DIFERENTE a cada chamada (sessões paralelas): com bag,
|
||||
# branch plans/plan-issue-<bag> (sufixo -2/-3 se o nome
|
||||
# já existir), sem bag o próximo plans/plan-issue-<n>
|
||||
# sequencial; com --go, cd para dentro dele
|
||||
# worktree kill [--force]
|
||||
# worktree next [--stay] cria/reutiliza o worktree da próxima Issue claimável
|
||||
# e cd para dentro dele por padrão; --stay não troca
|
||||
# worktree plan [bag] [--stay] cria um worktree de planejamento do /plan-issue
|
||||
# DIFERENTE a cada chamada (sessões paralelas): com bag,
|
||||
# branch plans/plan-issue-<bag> (sufixo -2/-3 se o nome
|
||||
# já existir), sem bag o próximo plans/plan-issue-<n>
|
||||
# sequencial; cd para dentro dele por padrão; --stay não
|
||||
# troca
|
||||
# worktree kill [--force] destrói o worktree atual e cd para o main por padrão
|
||||
#
|
||||
# `--go` explícito continua aceito como no-op (era o antigo padrão).
|
||||
# Read-only no GitHub — claim continua sendo `pnpm agent:claim`.
|
||||
|
||||
worktree() {
|
||||
|
|
@ -25,23 +28,22 @@ worktree() {
|
|||
local root
|
||||
root="$(cd "$(dirname "$src")/../.." && pwd)"
|
||||
|
||||
local has_go=0
|
||||
local stay=0
|
||||
local arg
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "--go" ]; then
|
||||
has_go=1
|
||||
if [ "$arg" = "--stay" ]; then
|
||||
stay=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$has_go" -eq 0 ]; then
|
||||
node "$root/scripts/worktree.mjs" "$@"
|
||||
return $?
|
||||
fi
|
||||
|
||||
local out
|
||||
out="$(node "$root/scripts/worktree.mjs" "$@")" || return $?
|
||||
printf '%s\n' "$out"
|
||||
|
||||
if [ "$stay" -eq 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local dir
|
||||
dir="$(printf '%s\n' "$out" | sed -n 's/^cd //p' | tail -n 1)"
|
||||
if [ -n "$dir" ] && [ -d "$dir" ]; then
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ VMs Cloud **não rodam** `docker compose` / `pnpm db:start`. Postgres nativo: `.
|
|||
|
||||
## Per-worktree environments (`pnpm worktree next`)
|
||||
|
||||
Parallel agents must not fight over port 3000, the shared `teqo_test`, or a second Postgres container. `pnpm worktree next [--go]` provisions each worktree with a deterministic isolated env:
|
||||
Parallel agents must not fight over port 3000, the shared `teqo_test`, or a second Postgres container. `pnpm worktree next [--stay]` provisions each worktree with a deterministic isolated env (`next`/`plan` print `cd <dir>` by default — apply it, or pass `--stay` to suppress; `--go` is still accepted as a no-op):
|
||||
|
||||
- `slot` = numeric part of the issue code (`C15` → 15, `B164` → 164; hash fallback without digits; bumped on collision among live worktrees).
|
||||
- Dev server port `3100+slot`; databases `teqo_wt<slot>` (dev) and `teqo_wt<slot>_test` (tests), created on the **one shared container** with the full migration chain applied (one-time, ~30 s each).
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ Prepara o ambiente git para a próxima Issue da fila de claim, **sem** tocá-la
|
|||
A lógica determinística (fila → código+slug → branch → worktree) vive em **`scripts/worktree.mjs`** — fonte única, coberta por `tests/unit/worktree.unit.spec.ts`. Não a reescreva à mão aqui.
|
||||
|
||||
```bash
|
||||
pnpm worktree next [--go] [--no-migrate] # cria worktree de origin/main + provisiona o ambiente
|
||||
pnpm worktree plan [bag] [--go] [--no-migrate] # worktree de PLANEJAMENTO (/plan-issue): um DIFERENTE por invocação
|
||||
pnpm worktree next [--stay] [--no-migrate] # cria worktree de origin/main + provisiona o ambiente
|
||||
pnpm worktree plan [bag] [--stay] [--no-migrate] # worktree de PLANEJAMENTO (/plan-issue): um DIFERENTE por invocação
|
||||
pnpm worktree kill [--force] # destrói o worktree atual + remove seus bancos gerados
|
||||
```
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ No opencode, isso é o comando **`/worktree next`** / **`/worktree kill`** (`.op
|
|||
|
||||
`plan` é o primo do `next`: worktree de **planejamento** para rodar a skill `/plan-issue` sem ocupar o main. **Cada invocação cria um DIFERENTE** (sessões paralelas): com `bag` opcional → branch `plans/plan-issue-<bag>` (sufixo `-2`/`-3` se o nome já estiver vivo); sem `bag` → próximo sequencial `plans/plan-issue-<n>` livre. Não é nomeado por Issue nenhuma — proposital, para um `next` posterior da próxima Issue nunca colidir em branch nem em slot (prefixo minúsculo `plans/…` vs branch `<Code>-<slug>` sempre uppercase-led). O fluxo desta skill (fila → próxima Issue → implementação) é só `next`.
|
||||
|
||||
No terminal interativo, para `--go` trocar de diretório de verdade, use a função `worktree()` de **`.agents/shell/worktree.sh`** (uma linha de `source` no profile): o script imprime `cd <dir>` e a função o aplica no shell que te chamou — node não consegue mudar o cwd do shell pai.
|
||||
**Default-go:** `next` e `plan` imprimem `cd <dir>` na última linha **por padrão**; `--stay` suprime; `--go` explícito continua aceito como no-op. No terminal interativo, use a função `worktree()` de **`.agents/shell/worktree.sh`** (uma linha de `source` no profile) para o `cd` ser aplicado de verdade no shell que te chamou — node não consegue mudar o cwd do shell pai. `kill` também imprime `cd <main>` no fim (a sessão nunca fica num diretório destruído).
|
||||
|
||||
## Fluxo quando invocado como skill (agentes que não têm opencode)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
description: Cria o worktree da próxima Issue claimável (next [--go]), um worktree novo de planejamento do /plan-issue (plan [bag] [--go]) ou destrói o worktree atual (kill [--force])
|
||||
description: Cria o worktree da próxima Issue claimável (next [--stay]), um worktree novo de planejamento do /plan-issue (plan [bag] [--stay]) ou destrói o worktree atual (kill [--force])
|
||||
---
|
||||
|
||||
Rode o script determinístico abaixo (não improvise a lógica) e reporte o resultado de forma concisa.
|
||||
|
||||
- `next [--go]`: cria (ou reutiliza) o worktree da próxima Issue claimável. Com `--go`, o script imprime `cd <dir>` na última linha — aplique-o na sessão de shell atual (`cd <dir>`) e reporte o novo diretório.
|
||||
- `plan [bag] [--go]`: cria um worktree de planejamento **diferente a cada invocação** (branch `plans/plan-issue-<bag>`, ou `plans/plan-issue-<n>` sequencial sem bag; sufixo `-2`/`-3` em colisão) para rodar `/plan-issue` sem ocupar o main; nunca conflita com `next`.
|
||||
- `kill [--force]`: destrói o worktree em que você está.
|
||||
- `next [--stay]`: cria (ou reutiliza) o worktree da próxima Issue claimável. Por padrão o script imprime `cd <dir>` na última linha — aplique-o na sessão de shell atual (`cd <dir>`) e reporte o novo diretório. `--stay` suprime o `cd` (não troque de diretório). `--go` explícito continua aceito como no-op.
|
||||
- `plan [bag] [--stay]`: cria um worktree de planejamento **diferente a cada invocação** (branch `plans/plan-issue-<bag>`, ou `plans/plan-issue-<n>` sequencial sem bag; sufixo `-2`/`-3` em colisão) para rodar `/plan-issue` sem ocupar o main; nunca conflita com `next`. Mesmo contrato de `cd` do `next`: aplique a última linha `cd <dir>` por padrão; `--stay` suprime.
|
||||
- `kill [--force]`: destrói o worktree em que você está e remove seus bancos. O script imprime `cd <main>` na última linha — aplique-o (`cd <main>`) para a sessão sair do diretório destruído.
|
||||
|
||||
!`node scripts/worktree.mjs $ARGUMENTS`
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ These are product/architecture constraints — not a ban on editing existing cod
|
|||
- `Consent.text` was reconciled from `varchar` to `jsonb` via `20260715_163500_consent_text_to_jsonb` (matching the `richText` field). Any future field-type change is a migration, not a manual DB edit.
|
||||
- To copy production content locally, use `pnpm db:pull` (`scripts/db-pull.mjs`): it only reads prod, only writes local, and excludes supporter PII (`contact`/`signature`/`subscription` row data).
|
||||
- `pnpm db:doctor` diagnoses local database connectivity for the dev AND test targets: it names who holds the port, points at exited/crash-looping containers, and flags Postgres containers sharing one data volume (two postmasters on one PGDATA corrupt the WAL — this destroyed the local cluster's checkpoint on 2026-07-25; the canonical setup is ONE compose container on 5432 hosting `teqo` + `teqo_test`). `pnpm dev` runs the same preflight via `guard-dev-db` and refuses to start with a named remedy instead of a later ECONNREFUSED. `db:start`/`db:stop` pin the compose project with `-p teqo`, so they always target that ONE container even from a worktree directory.
|
||||
- **Per-worktree environments (`pnpm worktree next`, 2026-08-08):** parallel agents each get a deterministic isolated env — no fighting over port 3000, the shared `teqo_test`, or a second Postgres container. `worktree next [--go]` provisions: a dev-server port `3100+slot` (slot = numeric part of the issue code, hash fallback, bumped on collision among live worktrees), plus dedicated databases `teqo_wt<slot>` (dev) and `teqo_wt<slot>_test` (tests) created on the shared container with the full migration chain applied; writes `.env.local` + `.env.test.local` (gitignored, marker `# teqo worktree env (generated by pnpm worktree next)`; manual envs are never overwritten) and copies `PAYLOAD_SECRET`/Blob/VAPID from the main repo. `worktree plan [bag] [--go]` (2026-08-08) is the sibling for the `/plan-issue` skill: **each invocation creates a DIFFERENT planning worktree** so parallel `/plan-issue` sessions never share one — branch `plans/plan-issue-<bag>` when a `bag` slug is given (suffixed `-2`/`-3` if that name is taken) or the next free sequential `plans/plan-issue-<n>` when not (`PLAN_BRANCH_PREFIX`/`planBranchName`, `scripts/lib/worktree.mjs`); the lowercase `plans/…` prefix deliberately never matches `next`'s uppercase-led `<Code>-<slug>`, so a later `next` never collides in branch or slot. `kill` drops the worktree's generated databases best-effort. Tests read `.env.test.local` after `.env.test` (vitest.setup.ts / playwright.config.ts / db-doctor / gate-ci mirror the layering), and `assertTestDatabase` accepts `teqo_test` or `teqo_<worktree>_test` on localhost only — still fail-closed on remote. Pure derivation lives in `scripts/lib/worktree-env.mjs` (unit-tested). Without Docker (Cursor Cloud) it degrades to the shared `teqo`/`teqo_test` with a warning.
|
||||
- **Per-worktree environments (`pnpm worktree next`, 2026-08-08; default-go 2026-08-10):** parallel agents each get a deterministic isolated env — no fighting over port 3000, the shared `teqo_test`, or a second Postgres container. `worktree next [--stay]` provisions: a dev-server port `3100+slot` (slot = numeric part of the issue code, hash fallback, bumped on collision among live worktrees), plus dedicated databases `teqo_wt<slot>` (dev) and `teqo_wt<slot>_test` (tests) created on the shared container with the full migration chain applied; writes `.env.local` + `.env.test.local` (gitignored, marker `# teqo worktree env (generated by pnpm worktree next)`; manual envs are never overwritten) and copies `PAYLOAD_SECRET`/Blob/VAPID from the main repo. **`next`/`plan` print `cd <dir>` as the last line BY DEFAULT** — the caller applies it (shell function `worktree()` in `.agents/shell/worktree.sh`, or the `/worktree` opencode command); `--stay` suppresses the cd, `--go` explicit stays accepted as a no-op (it was the old default), and `kill` ends with `cd <main>` so the shell never sits in a destroyed directory. `worktree plan [bag] [--stay]` (2026-08-08) is the sibling for the `/plan-issue` skill: **each invocation creates a DIFFERENT planning worktree** so parallel `/plan-issue` sessions never share one — branch `plans/plan-issue-<bag>` when a `bag` slug is given (suffixed `-2`/`-3` if that name is taken) or the next free sequential `plans/plan-issue-<n>` when not (`PLAN_BRANCH_PREFIX`/`planBranchName`, `scripts/lib/worktree.mjs`); the lowercase `plans/…` prefix deliberately never matches `next`'s uppercase-led `<Code>-<slug>`, so a later `next` never collides in branch or slot. `kill` drops the worktree's generated databases best-effort. Tests read `.env.test.local` after `.env.test` (vitest.setup.ts / playwright.config.ts / db-doctor / gate-ci mirror the layering), and `assertTestDatabase` accepts `teqo_test` or `teqo_<worktree>_test` on localhost only — still fail-closed on remote. Pure derivation lives in `scripts/lib/worktree-env.mjs` (unit-tested). Without Docker (Cursor Cloud) it degrades to the shared `teqo`/`teqo_test` with a warning.
|
||||
- To (re)populate news content, `pnpm db:seed:posts` fetches articles live from jorgesolla.com.br into the LOCAL db (same non-local `DATABASE_URL` guard as `pnpm dev`). It writes from a CLI process outside the deployed runtime, so after seeding — or after ANY direct-DB change — you must bust the production `posts` cache via `POST /api/revalidate`. See "Posts & Tags" below.
|
||||
- To (re)populate the TSE 2022 election baseline, `pnpm db:seed:tse` downloads TSE open data and imports the Bahia scope into the LOCAL db (same guard). Since the 2026-07-23 hardening, elections-tab reads are cached under the `election-tse` tag — after re-seeding a database the deployed app reads, bust it via `POST /api/revalidate?tag=election-tse`. See "Election baseline data (TSE 2022)" below.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
|
|||
|
||||
---
|
||||
|
||||
**Recently resolved (2026-08-10):** **OPS24** (Issue #569) — `--go` virou o **padrão** do worktree CLI e `kill` volta ao main: `worktree next`/`plan` imprimem `cd <dir>` na última linha **por padrão** (antes exigiam `--go`); `--stay` suprime; `--go` explícito continua aceito como no-op (não quebra hábito/automação). `kill` troca a mensagem "Volte ao main: cd …" (que nunca casava com o sed `s/^cd //p` da função shell) pela **linha máquina `cd <main>` como última linha** — depois do drop dos DBs, que rodava antes — e **não aceita `--stay`** (ficar em cwd destruído é exatamente o bug corrigido; `--go` no kill é no-op). Vale nas duas superfícies que aplicam o cd: função `worktree()` de `.agents/shell/worktree.sh` (agora sempre captura/applica, `--stay` retorna cedo) e comando `/worktree` do opencode (agente aplica o `cd` da última linha sempre, exceto `--stay`). Docs: header/uso do script, AGENTS.md ("Per-worktree environments"), skills `worktree-next-issue`/`local-database`. Sem migration/dados/access; derivação pura (`worktree-env.mjs`/`worktree.mjs` libs) intocada. Intenção [`worktree-go-default-e-kill-volta-ao-main.md`](plans/worktree-go-default-e-kill-volta-ao-main.md); impl [`worktree-go-default-e-kill-volta-ao-main-impl.md`](plans/worktree-go-default-e-kill-volta-ao-main-impl.md).
|
||||
|
||||
**Recently resolved (2026-08-10):** **B192** (Issue #533) — o Sollinha encerra respostas factuais com **follow-ups sugeridos como chips** (2–3, no mesmo slot dos chips de abertura do B191, acima do input; tocar envia como mensagem do usuário; substituídos a cada resposta; somem durante o streaming; mobile limita a 2). **Uma única chamada ao modelo:** o prompt (`systemPrompt.ts`) instrui encerrar com o bloco estável `**Sugestões de continuação:**` + lista markdown (só perguntas respondíveis pelas próprias tools, leader sem sugestão eleitoral — B180; bloco = instrução de interface, nunca citar em prosa) e o cliente extrai. **Contrato em fonte única:** `src/lib/sollinhaFollowUpSuggestions.ts` — `SOLLINHA_FOLLOW_UP_MARKER` (interpolado no prompt) + `splitSollinhaFollowUpBlock` (última ocorrência vence; body sempre mascarado do marcador em diante; itens `-/*/1./1)` aceitos, bold e duplicados limpos, teto 3; **fail-closed: <2 itens ou linha não-lista → sem chips**, resposta normal). UI: strip no render de todo text part assistant **antes** do markdown/links (bloco nunca pisca como prosa; mensagens antigas restauradas via B188 continuam mascaradas e os chips renascem por derivação); slot B191 estendido (catálogo vazio → follow-ups); tipo compartilhado renomeado `SollinhaOpeningQuestion → SollinhaChatChip`. **Strip defensivo no servidor:** `route.ts` remove o bloco das mensagens assistant ao reconverter para o modelo (próximo turno sem eco nem custo de contexto). Unit (16 casos do split) + e2e novo `campaignAiChatFollowUps.e2e.spec.ts` (wire format v7 como o mock do B187; bolha sem o bloco, chips, substituição, fail-closed, cap mobile). Sem migration/access/Consent/schema/endpoint novo. Intenção [`sollinha-follow-ups-respostas.md`](plans/sollinha-follow-ups-respostas.md); impl [`sollinha-follow-ups-respostas-impl.md`](plans/sollinha-follow-ups-respostas-impl.md).
|
||||
|
||||
**Recently resolved (2026-08-10):** **OPS23** (Issue #566) — a Action OPS18 (`plan-issue-ready-on-main-merge.yml`) falhava **sempre** no primeiro disparo real (PR #563, run 31381977772): o job roda `actions/setup-node@v5` sem `pnpm/action-setup` e o script é plain Node — com `package-manager-cache: true` (default do v5), o setup-node resolve o `packageManager` do package.json (`pnpm@10.11.0`) e procura o executável pnpm → `Unable to locate executable file: pnpm`, promote nunca rodava. Correção: `package-manager-cache: false` no setup-node (precedente exato dos jobs plain-Node: cooldown do `ci.yml`, `vercel-promote.yml`, `archive-cursor-agent.yml`; `pnpm/action-setup@v6` rejeitado — só existe onde há `pnpm install`). Varredura dos 21 usos de setup-node: `plan-issue-ready` era o único com o bug latente. Validação funcional = próximo merge de PR com `Related #N` (trigger `closed+merged→main` só dispara em merge real). Impl [`ops23-action-promote-related-setup-node-impl.md`](plans/ops23-action-promote-related-setup-node-impl.md).
|
||||
|
|
|
|||
78
docs/plans/worktree-go-default-e-kill-volta-ao-main-impl.md
Normal file
78
docs/plans/worktree-go-default-e-kill-volta-ao-main-impl.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Impl: Worktree CLI: `--go` vira o padrão (escape `--stay`) e `kill` volta ao main
|
||||
|
||||
Status: aprovado
|
||||
Atualizado em: 2026-08-10
|
||||
Issue: #569
|
||||
Intenção: docs/plans/worktree-go-default-e-kill-volta-ao-main.md
|
||||
Appetite restante: ~0,5 dia eng (herdado — mudança pequena, 7 arquivos de texto/CLI)
|
||||
|
||||
## Leitura da intenção
|
||||
|
||||
- **Outcome:** `worktree next`/`plan` sem flags aplicam o `cd` (hoje exigem `--go`); `--stay` suprime; `--go` vira no-op; `worktree kill` termina no main repo (cwd nunca num diretório destruído). Vale nas duas superfícies que aplicam o cd: função `worktree()` do shell e comando `/worktree` do opencode.
|
||||
- **O que NÃO negociar:** lógica determinística de branch/slot/provisionamento intacta; `kill` não ganha `--stay` (decidido no gate: kill sempre volta ao main); nada de detectar terminal vs opencode no script (o script só imprime o `cd`; quem aplica decide).
|
||||
- **O que reavaliar:** a hipótese da intenção manda "`cmdKill` imprime `cd <mainRoot>` no mesmo formato máquina". Verifiquei o formato real: `next`/`plan` imprimem `cd <dir>` na ÚLTIMA linha; `kill` hoje imprime `Volte ao main: cd ${mainRoot}` — linha que **não** casa com o sed `s/^cd //p` da função shell, por isso nada aplica. A correção é substituir a mensagem humana pela linha máquina (o contexto "Worktree destruído:" já diz o que aconteceu), não acrescentar as duas. Ajuste extra: a linha máquina do `kill` precisa ser a última do output — hoje o `dropWorktreeDatabases` (best-effort, imprime `[worktree] banco removido: …`) roda DEPOIS do print. O sed usa `tail -n 1` de linhas que casam `^cd `, então funcionaria mesmo assim, mas o contrato "última linha" fica limpo movendo o print para depois dos drops.
|
||||
|
||||
## Abordagem recomendada
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[worktree next/plan] --> B[flags: --stay suprime, --go no-op]
|
||||
A2[kill] --> B2[sempre imprime cd mainRoot como última linha]
|
||||
B --> C[worktree.mjs imprime cd dir por default]
|
||||
B2 --> C
|
||||
C --> D[worktree.sh: aplica cd por default; --stay não aplica]
|
||||
C --> E[/worktree opencode: agente aplica cd sempre]
|
||||
D --> F[terminal termina no worktree / no main]
|
||||
```
|
||||
|
||||
**Opções consideradas:** A | B | C
|
||||
**Recomendação:** A — inversão do flag na origem (script), com shell e opencode command consumindo a mesma linha máquina.
|
||||
**Rejeitadas:**
|
||||
|
||||
- **B — inverter só na função shell** (shell decide aplicar por default e passa `--stay` ao script): deixaria o opencode command e consumidores diretos do script com a semântica velha; a superfície `/worktree` também precisa do default-go. Rejeitada por inconsistência.
|
||||
- **C — detectar "terminal vs opencode" no script** (ex.: env `WORKTREE_TERMINAL`): anti-goal explícito da intenção; o contrato continua sendo "script imprime `cd`; quem aplica decide". Rejeitada.
|
||||
- **D — `kill --stay`** (ficar no cwd morto): anti-goal de produto (é exatamente o bug que se corrige). Rejeitada — `kill` com `--stay` morre com mensagem clara.
|
||||
|
||||
### Componentes / mudanças
|
||||
|
||||
- **`scripts/worktree.mjs`**:
|
||||
- Header (docblock, linhas 5-34) e bloco de uso (`pnpm worktree` sem args, linhas 486-504): `next [--stay]`, `plan [bag] [--stay]`, `kill [--force]`; nota de que `--go` segue aceito como no-op.
|
||||
- `cmdNext(go, skipMigrate)` / `cmdPlan(go, skipMigrate, bag)`: parâmetro `go` → `stay`; `if (go) console.log(\`cd ${dir}\`)`vira`if (!stay) console.log(\`cd ${dir}\`)` (linhas 329 e 400).
|
||||
- `cmdKill(force)`: troca `Volte ao main: cd ${mainRoot}` por `cd ${mainRoot}` como **última** linha (depois de `await dropWorktreeDatabases`); `flags.go` no kill vira no-op aceito (consistente com o novo status do flag); `flags.stay` no kill → `die('--stay não se aplica a kill — ele sempre volta ao main')`.
|
||||
- Dispatch (linhas 508-514): `cmdNext(Boolean(flags.stay), …)`, `cmdPlan(Boolean(flags.stay), …)`, `kill` valida `--stay`.
|
||||
- **`.agents/shell/worktree.sh`**: a função sempre captura o output e aplica o `cd` da linha máquina; `--stay` detectado nos args → repassa ao script e **não** aplica cd (retorno 0 após imprimir). Remove o ramo `has_go` (o `--go` nem precisa ser tratado — vira default). Funciona para `next`/`plan`/`kill` com o mesmo sed.
|
||||
- **`.opencode/commands/worktree.md`**: frontmatter + corpo — `next [--stay]`, `plan [bag] [--stay]`, `kill [--force]`; o agente aplica o `cd <dir>` da última linha **sempre** (default), suprimido só com `--stay`; `kill` também aplica o `cd <mainRoot>`.
|
||||
- **`AGENTS.md`** (seção "Per-worktree environments"): `worktree next [--go]` → `worktree next [--stay]`, `worktree plan [bag] [--go]` → `[bag] [--stay]`, `kill` volta ao main; nota `--go` no-op.
|
||||
- **`.agents/skills/worktree-next-issue/SKILL.md`**: linhas de uso (`next [--go]` → `[--stay]`) e a nota sobre a função `worktree()` (agora default).
|
||||
- **`.agents/skills/local-database/SKILL.md`**: menção `pnpm worktree next [--go]` → `[--stay]`.
|
||||
- **`docs/CHANGELOG-AGENTS.md`**: entrada curta da entrega (padrão OPS17+).
|
||||
- **Migration:** sem migration (nenhum schema).
|
||||
- **Access / Consent:** N/A — CLI de dev fora do runtime.
|
||||
- **UI:** Impeccable A — N/A (sem superfície de usuário de produto).
|
||||
|
||||
## Fases verificáveis
|
||||
|
||||
1. **Script** — `scripts/worktree.mjs` (flags + kill line) + shell function + opencode command + docs (AGENTS.md, skills, CHANGELOG). Verificação manual de contrato: `node scripts/worktree.mjs` sem args mostra o uso novo; `next --stay`/`--go`/sem flag imprimem/omitem a linha `cd` (a fila pode estar vazia → testar via `cmdPlan` que não depende da fila, ou pela mensagem de `die` da fila vazia — o que importa é o contrato de flags não quebrar).
|
||||
2. **Verificação real do kill** (a única parte que toca estado real): criar worktree de teste barato não é viável nesta sessão (kill precisa estar DENTRO de um worktree e destrói DBs) — o contrato do kill fica coberto pelo mesmo formato de linha `cd <mainRoot>` e pela lógica simples de print; validar o caminho feliz com um worktree de planejamento descartável se o ambiente permitir.
|
||||
3. **Gates** — `pnpm gate:fast` (lint + typecheck + unit); `pnpm push` (gate completo: format/knip/cycles/test/build) — este item é scripts/docs puro, sem testes de domínio novos.
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
||||
- Não adicionar um helper "puro" para a linha `cd <dir>` com unit test — a derivação não muda, o contrato é uma template string de 1 linha já pinada por 3 usos no mesmo arquivo; testes unitários do `cmdNext` exigiriam mock de git/pg que não existe no repo (as duas libs puras já são cobertas).
|
||||
- Não tocar `scripts/lib/worktree-env.mjs` / `worktree.mjs` (derivação pura) — nenhuma mudança de branch/slot/provisionamento.
|
||||
- Não criar "vários escapes" (`--no-go`, `--here`…) — a intenção pinou `--stay` como o único.
|
||||
- Não alterar o marker `GENERATED_ENV_MARKER` (pinado por teste de unidade).
|
||||
- OPS26 (abrir opencode no go) e OPS27 (`worktree new`) são itens próprios — esta entrega só muda a semântica de flags; OPS26 já nasce sobre o default-go.
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
- **Quebrar automação/scripts que dependem da ausência do cd sem `--go`** — mitigação: `--go` continua aceito como no-op e o formato da linha máquina é idêntico ao de hoje; quem não quer o cd usa `--stay` (novo) — documentado nas duas superfícies.
|
||||
- **`kill` imprimir a linha máquina antes do drop dos DBs** — mitigação: print movido para depois de `dropWorktreeDatabases`; a linha é a última do output (contrato do sed `tail -n 1`).
|
||||
- **Shell function: perda do streaming de output** (antes, sem `--go`, o node imprimia direto; agora sempre captura e reimprime) — mitigação: comportamento idêntico ao do `--go` de hoje (já capturava); `printf '%s\n' "$out"` preserva o texto; o `|| return $?` preserva o exit code do script.
|
||||
- **`kill --stay` ambíguo** — mitigação: die com mensagem clara (mesmo padrão do `--go` antigo), nunca silêncio.
|
||||
|
||||
## Aceite de engenharia
|
||||
|
||||
- [ ] Aceite de produto da intenção ainda coberto (default-go nas duas superfícies; `--stay`; kill volta ao main; `--go` no-op)
|
||||
- [ ] Invariantes AGENTS/engineering-standards (nenhuma mudança em branch/slot/provisionamento; docs atualizadas)
|
||||
- [ ] Testes de domínio previstos (unit/int): sem mudança de access/write paths — `pnpm test:unit` existente verde; verificação manual do contrato de flags/linha `cd`
|
||||
|
|
@ -2,14 +2,16 @@
|
|||
* `pnpm worktree` — worktree management determinístico em torno da fila de
|
||||
* claim do projeto (mesma fila de `agent:claim` / agent pool).
|
||||
*
|
||||
* pnpm worktree next [--go] [--no-migrate]
|
||||
* pnpm worktree next [--stay] [--no-migrate]
|
||||
* cria worktree a partir de origin/main para a
|
||||
* próxima Issue claimável; branch `<code>-<slug>`.
|
||||
* Com `--go`, imprime `cd <dir>` no fim — node
|
||||
* Por padrão imprime `cd <dir>` no fim — node
|
||||
* não muda o cwd do shell pai; o shell chamador
|
||||
* aplica (opencode/CDP usa o opencode command;
|
||||
* terminal interativo: função `worktree()` em
|
||||
* `.agents/shell/worktree.sh`, sourced no profile).
|
||||
* `--stay` suprime a linha `cd`; `--go` explícito
|
||||
* continua aceito como no-op (era o antigo padrão).
|
||||
* Também PROVISIONA o ambiente isolado do worktree:
|
||||
* porta do dev server + bancos próprios derivados
|
||||
* deterministicamente do branch (ver
|
||||
|
|
@ -17,7 +19,7 @@
|
|||
* paralelo não disputarem porta 3000 nem o
|
||||
* `teqo_test` compartilhado. `--no-migrate` pula a
|
||||
* aplicação das migrations nos bancos novos.
|
||||
* pnpm worktree plan [bag] [--go] [--no-migrate]
|
||||
* pnpm worktree plan [bag] [--stay] [--no-migrate]
|
||||
* cria um worktree de PLANEJAMENTO novo para rodar
|
||||
* a skill /plan-issue sem ocupar o main — cada
|
||||
* invocação cria UM DIFERENTE, para sessões de
|
||||
|
|
@ -30,7 +32,11 @@
|
|||
* Mesmo provisionamento isolado do `next`.
|
||||
* pnpm worktree kill [--force] destrói o worktree em que o shell atual está
|
||||
* (recusa worktree sujo sem `--force`) e remove os
|
||||
* bancos gerados do worktree (best-effort)
|
||||
* bancos gerados do worktree (best-effort); por
|
||||
* padrão termina imprimindo `cd <main>` para o
|
||||
* shell voltar ao worktree principal — o cwd nunca
|
||||
* fica num diretório destruído (não aceita
|
||||
* `--stay`; `--go` é no-op)
|
||||
*
|
||||
* Read-only no GitHub: `next` NUNCA claima (claim = `pnpm agent:claim`).
|
||||
* Dir raiz: `~/.cursor/worktrees/teqo/` (mesma casa dos worktrees do Cursor).
|
||||
|
|
@ -280,7 +286,7 @@ const provision = async ({ dir, branch, issue, env, skipMigrate, mainRoot, purpo
|
|||
runMigrate(dir, testUrl, payloadSecret)
|
||||
}
|
||||
|
||||
const cmdNext = async (go, skipMigrate) => {
|
||||
const cmdNext = async (stay, skipMigrate) => {
|
||||
const pick = nextClaimableIssue()
|
||||
if (!pick) {
|
||||
die('Fila vazia — nada `ready` desbloqueado. Rode `pnpm agent:status` para ver a fila.')
|
||||
|
|
@ -326,7 +332,7 @@ const cmdNext = async (go, skipMigrate) => {
|
|||
console.log(` banco dev: postgresql://teqo:teqo@localhost:5432/${env.devDatabase}`)
|
||||
console.log(` banco test: postgresql://teqo:teqo@localhost:5432/${env.testDatabase}`)
|
||||
|
||||
if (go) console.log(`cd ${dir}`)
|
||||
if (!stay) console.log(`cd ${dir}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -340,7 +346,7 @@ const cmdNext = async (go, skipMigrate) => {
|
|||
* worktree — parallel `/plan-issue` sessions never share one. Same isolated
|
||||
* env provisioning as `next`.
|
||||
*/
|
||||
const cmdPlan = async (go, skipMigrate, bag) => {
|
||||
const cmdPlan = async (stay, skipMigrate, bag) => {
|
||||
git(['fetch', 'origin'])
|
||||
|
||||
const entries = parseWorktreeList(git(['worktree', 'list', '--porcelain']))
|
||||
|
|
@ -397,7 +403,7 @@ const cmdPlan = async (go, skipMigrate, bag) => {
|
|||
console.log(` banco dev: postgresql://teqo:teqo@localhost:5432/${env.devDatabase}`)
|
||||
console.log(` banco test: postgresql://teqo:teqo@localhost:5432/${env.testDatabase}`)
|
||||
|
||||
if (go) console.log(`cd ${dir}`)
|
||||
if (!stay) console.log(`cd ${dir}`)
|
||||
}
|
||||
|
||||
/** Generated database names referenced by a worktree's own env files. */
|
||||
|
|
@ -475,9 +481,10 @@ const cmdKill = async (force) => {
|
|||
|
||||
console.log(`Worktree destruído: ${top}`)
|
||||
console.log(` branch removido: ${branch ?? '(detached — nada a remover)'}`)
|
||||
console.log(`Volte ao main: cd ${mainRoot}`)
|
||||
|
||||
await dropWorktreeDatabases(databaseNames)
|
||||
|
||||
console.log(`cd ${mainRoot}`)
|
||||
}
|
||||
|
||||
const { flags, positional } = parseArgs(process.argv.slice(2), new Set())
|
||||
|
|
@ -485,31 +492,33 @@ const subcommand = positional[0]
|
|||
|
||||
if (!subcommand) {
|
||||
console.log(
|
||||
'Uso: pnpm worktree next [--go] [--no-migrate] | plan [bag] [--go] [--no-migrate] | kill [--force]',
|
||||
'Uso: pnpm worktree next [--stay] [--no-migrate] | plan [bag] [--stay] [--no-migrate] | kill [--force]',
|
||||
)
|
||||
console.log(' next [--go] [--no-migrate]')
|
||||
console.log(' next [--stay] [--no-migrate]')
|
||||
console.log(' cria worktree da próxima Issue claimável (branch <code>-<slug>) e provisiona o')
|
||||
console.log(' ambiente isolado: porta de dev + bancos próprios (determinístico do branch);')
|
||||
console.log(' com --go imprime `cd <dir>` no fim (quem aplica o cd: opencode command, ou a')
|
||||
console.log(' função `worktree()` de .agents/shell/worktree.sh); --no-migrate pula migrations')
|
||||
console.log(`\n plan [bag] [--go] [--no-migrate]`)
|
||||
console.log(' por padrão imprime `cd <dir>` no fim (quem aplica o cd: opencode command, ou a')
|
||||
console.log(' função `worktree()` de .agents/shell/worktree.sh); --stay suprime o cd; --go')
|
||||
console.log(' explícito continua aceito como no-op; --no-migrate pula migrations')
|
||||
console.log(`\n plan [bag] [--stay] [--no-migrate]`)
|
||||
console.log(
|
||||
' cria um worktree de planejamento DIFERENTE a cada invocação (sessões /plan-issue',
|
||||
)
|
||||
console.log(' paralelas): com bag, branch plans/plan-issue-<bag> (sufixo -2/-3 se o nome já')
|
||||
console.log(' existir); sem bag, o próximo plans/plan-issue-<n> sequencial livre; o prefixo')
|
||||
console.log(' minúsculo plans/… nunca colide com o branch <code>-<slug> de `next`')
|
||||
console.log(' kill [--force] destrói o worktree em que você está (recusa sujo sem --force) e')
|
||||
console.log(' remove os bancos gerados do worktree (best-effort)')
|
||||
console.log(' kill [--force] destrói o worktree em que você está (recusa sujo sem --force),')
|
||||
console.log(' remove os bancos gerados do worktree (best-effort) e imprime')
|
||||
console.log(' `cd <main>` no fim — o shell sempre volta ao worktree principal')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
try {
|
||||
if (subcommand === 'next') await cmdNext(Boolean(flags.go), Boolean(flags['no-migrate']))
|
||||
if (subcommand === 'next') await cmdNext(Boolean(flags.stay), Boolean(flags['no-migrate']))
|
||||
else if (subcommand === 'plan')
|
||||
await cmdPlan(Boolean(flags.go), Boolean(flags['no-migrate']), positional[1])
|
||||
await cmdPlan(Boolean(flags.stay), Boolean(flags['no-migrate']), positional[1])
|
||||
else if (subcommand === 'kill') {
|
||||
if (flags.go) die('`--go` só faz sentido com `next`.')
|
||||
if (flags.stay) die('`--stay` não se aplica a `kill` — ele sempre volta ao main.')
|
||||
await cmdKill(Boolean(flags.force))
|
||||
} else die(`subcomando desconhecido: ${subcommand} (esperado: next | plan | kill)`)
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue