refactor(OPS26): simplify fixes do /simplify — marcador TEQO_ prefixado, helper printLaunchDirective, IFS pinado no launch, docs reconciliadas

This commit is contained in:
Francisco Solla 2026-08-10 10:58:55 -03:00
parent 344fe843df
commit da20ed6190
11 changed files with 41 additions and 31 deletions

View file

@ -12,10 +12,10 @@
# `next` com `/work-issue` já enviado, `plan` sem prompt (autocomplete completa
# `/plan-`). Sem `exec` de propósito: ao sair do opencode, o terminal volta ao
# shell dentro do worktree. Presets são constantes em scripts/lib/worktree.mjs;
# o marcador WORKTREE_TERMINAL=1 é o que separa esta superfície da do comando
# o marcador TEQO_WORKTREE_TERMINAL=1 é o que separa esta superfície da do comando
# `/worktree` do opencode (que nunca lança TUI). `--stay` suprime cd e launch.
#
# Instalação (uma linha no profile):
# Instalação (uma linha no profile; requer bash ou zsh — usa BASH_SOURCE, arrays e here-strings):
# source <repo>/.agents/shell/worktree.sh
#
# Uso (terminal interativo):
@ -46,7 +46,7 @@ worktree() {
done
local out
out="$(WORKTREE_TERMINAL=1 node "$root/scripts/worktree.mjs" "$@")" || return $?
out="$(TEQO_WORKTREE_TERMINAL=1 node "$root/scripts/worktree.mjs" "$@")" || return $?
printf '%s\n' "$out"
if [ "$stay" -eq 1 ]; then
@ -63,15 +63,15 @@ worktree() {
return 1
fi
# Diretiva `launch` (só existe quando WORKTREE_TERMINAL=1 e sem --stay): o
# script a gera a partir de constantes + dir slugificado (sem espaços), então
# o split por IFS abaixo é intencional e seguro — nunca eval. Falha do launch
# Diretiva `launch` (só existe quando TEQO_WORKTREE_TERMINAL=1 e sem --stay): o
# script a gera a partir de constantes + dir slugificado (sem espaços), então o
# split por IFS=' ' abaixo é intencional e seguro — nunca eval. Falha do launch
# (ex.: opencode fora do PATH) só avisa: o worktree já está pronto e utilizável.
local launch
launch="$(printf '%s\n' "$out" | sed -n 's/^launch //p' | tail -n 1)"
if [ -n "$launch" ]; then
local -a launch_args
read -r -a launch_args <<< "$launch"
IFS=' ' read -r -a launch_args <<< "$launch"
"${launch_args[@]}" || printf 'worktree: launch do opencode falhou (código %s) — o worktree segue pronto em %s.\n' "$?" "$PWD" >&2
fi
}

View file

@ -30,7 +30,7 @@ No opencode, isso é o comando **`/worktree next`** / **`/worktree kill`** (`.op
**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).
**Launch do opencode (OPS26):** só a função shell marca `WORKTREE_TERMINAL=1`; com o marcador, `next`/`plan` imprimem também a diretiva `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` **antes** do `cd`, e a função a executa depois do cd — `next` abre o TUI com `/work-issue` já enviado; `plan` abre sem prompt (`/plan-` + autocomplete do TUI). Sem `exec`: sair do opencode volta ao shell no worktree. Presets são constantes em `scripts/lib/worktree.mjs`. **O comando `/worktree` do opencode (e qualquer automação sem o marcador) nunca lança o TUI.**
**Launch do opencode (OPS26):** só a função shell marca `TEQO_WORKTREE_TERMINAL=1`; com o marcador, `next`/`plan` imprimem também a diretiva `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` **antes** do `cd`, e a função a executa depois do cd — `next` abre o TUI com `/work-issue` já enviado; `plan` abre sem prompt (`/plan-` + autocomplete do TUI). Sem `exec`: sair do opencode volta ao shell no worktree. Presets são constantes em `scripts/lib/worktree.mjs`. **O comando `/worktree` do opencode (e qualquer automação sem o marcador) nunca lança o TUI.**
## Fluxo quando invocado como skill (agentes que não têm opencode)

View file

@ -8,6 +8,6 @@ Rode o script determinístico abaixo (não improvise a lógica) e reporte o resu
- `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.
> **Este comando roda SEM o marcador `WORKTREE_TERMINAL`, então nunca recebe a diretiva `launch`** — só o `cd`. O launch do opencode (OPS26) acontece apenas na função `worktree()` de `.agents/shell/worktree.sh` (terminal interativo); nunca abra um TUI aninhado a partir daqui.
> **Este comando roda SEM o marcador `TEQO_WORKTREE_TERMINAL`, então nunca recebe a diretiva `launch`** — só o `cd`. O launch do opencode (OPS26) acontece apenas na função `worktree()` de `.agents/shell/worktree.sh` (terminal interativo); nunca abra um TUI aninhado a partir daqui.
!`node scripts/worktree.mjs $ARGUMENTS`

View file

@ -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; 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. **In the interactive terminal the shell function sets `WORKTREE_TERMINAL=1` and `next`/`plan` additionally print a `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` directive BEFORE the `cd` line, which the function executes after the cd (OPS26, 2026-08-10): `next` opens the opencode TUI in the worktree with `/work-issue` already sent (the OPS25 command runs the whole cycle), `plan` opens with the same presets and NO prompt (`/plan-` + TUI autocomplete — the opencode CLI's `--prompt` always auto-submits, there is no prefill-without-submit; gap registered for the opencode repo). The launch is without `exec` (exiting the TUI returns to the shell in the worktree, no auto-`kill`), the presets are constants in `scripts/lib/worktree.mjs` (`OPENCODE_PRESET_MODEL`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE`, unit-tested), and **the `/worktree` opencode command never launches a TUI** (no marker — no nested TUI). `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.
- **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. **In the interactive terminal the shell function sets `TEQO_WORKTREE_TERMINAL=1` and `next`/`plan` additionally print a `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` directive BEFORE the `cd` line, which the function executes after the cd (OPS26, 2026-08-10): `next` opens the opencode TUI in the worktree with `/work-issue` already sent (the OPS25 command runs the whole cycle), `plan` opens with the same presets and NO prompt (`/plan-` + TUI autocomplete — the opencode CLI's `--prompt` always auto-submits, there is no prefill-without-submit; gap registered for the opencode repo). The launch is without `exec` (exiting the TUI returns to the shell in the worktree, no auto-`kill`), `--stay` suppresses both the cd and the launch, the presets are constants in `scripts/lib/worktree.mjs` (`OPENCODE_PRESET_MODEL`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE`, unit-tested), and **the `/worktree` opencode command never launches a TUI** (no marker — no nested TUI). `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.

View file

@ -4,7 +4,7 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
---
**Recently resolved (2026-08-10):** **OPS26** (Issue #571) — `worktree next`/`plan` **no terminal** terminam com o TUI do opencode aberto no worktree: a função `worktree()` de `.agents/shell/worktree.sh` marca `WORKTREE_TERMINAL=1` e o script imprime a diretiva `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` **antes** do `cd` (que segue sendo a última linha — contrato OPS24 intacto); a função aplica o cd e executa a linha (sem `exec`: sair do TUI volta ao shell no worktree; sem auto-`kill`). `next` envia `/work-issue` (o comando OPS25 roda o ciclo completo); `plan` abre **sem prompt** — a CLI do opencode não tem pre-fill sem submit (`--prompt` sempre auto-envia; fallback acordado: `/plan-` + autocomplete; gap registrado para o repo do opencode). Presets são constantes em `scripts/lib/worktree.mjs` (`OPENCODE_PRESET_MODEL`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE`, `opencodeLaunchDirective` — pura, unit-testada; trocar de modelo é editar a constante). **Sem o marcador (comando `/worktree` do opencode, automação) a diretiva não é impressa** — nunca abre TUI aninhado; `--stay` suprime cd e launch; `kill` intocado. Docs: AGENTS.md ("Per-worktree environments"), header/uso do script, `.opencode/commands/worktree.md`, skill `worktree-next-issue`. Sem migration/dados/access/UI. Intenção [`worktree-go-abre-opencode-com-presets.md`](plans/worktree-go-abre-opencode-com-presets.md); impl [`worktree-go-abre-opencode-com-presets-impl.md`](plans/worktree-go-abre-opencode-com-presets-impl.md).
**Recently resolved (2026-08-10):** **OPS26** (Issue #571) — `worktree next`/`plan` **no terminal** terminam com o TUI do opencode aberto no worktree: a função `worktree()` de `.agents/shell/worktree.sh` marca `TEQO_WORKTREE_TERMINAL=1` e o script imprime a diretiva `launch opencode <dir> --model deepseek/deepseek-v4-flash --auto [--prompt /work-issue]` **antes** do `cd` (que segue sendo a última linha — contrato OPS24 intacto); a função aplica o cd e executa a linha (sem `exec`: sair do TUI volta ao shell no worktree; sem auto-`kill`). `next` envia `/work-issue` (o comando OPS25 roda o ciclo completo); `plan` abre **sem prompt** — a CLI do opencode não tem pre-fill sem submit (`--prompt` sempre auto-envia; fallback acordado: `/plan-` + autocomplete; gap registrado para o repo do opencode). Presets são constantes em `scripts/lib/worktree.mjs` (`OPENCODE_PRESET_MODEL`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE`, `opencodeLaunchDirective` — pura, unit-testada; trocar de modelo é editar a constante). **Sem o marcador (comando `/worktree` do opencode, automação) a diretiva não é impressa** — nunca abre TUI aninhado; `--stay` suprime cd e launch; `kill` intocado. Docs: AGENTS.md ("Per-worktree environments"), header/uso do script, `.opencode/commands/worktree.md`, skill `worktree-next-issue`. Sem migration/dados/access/UI. Intenção [`worktree-go-abre-opencode-com-presets.md`](plans/worktree-go-abre-opencode-com-presets.md); impl [`worktree-go-abre-opencode-com-presets-impl.md`](plans/worktree-go-abre-opencode-com-presets-impl.md).
**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).

View file

@ -27,7 +27,7 @@ Toda sessão de trabalho no repo começa invocando `/work-issue` (executar uma I
- O template instrui o agente a carregar a skill pelo nome exato e seguir o fluxo dela ponta a ponta, sem reescrever o conteúdo.
- `$ARGUMENTS` é repassado (ex.: `/plan-issue <lote>`).
- Cada comando leva `model: deepseek/deepseek-v4-flash` no frontmatter — sessão manual já abre no modelo de preferência (ver questão em aberto).
- Abertura automática a partir de `worktree next/plan` (OPS26) usa estes comandos como primeira mensagem.
- Abertura automática a partir de `worktree next/plan` (OPS26) usa estes comandos como primeira mensagem — `next` envia `/work-issue`; `plan` abre sem prompt (fallback acordado na OPS26: a CLI do opencode não tem pre-fill sem submit).
## Dados (intenção)

View file

@ -16,7 +16,7 @@ Appetite restante: ~0,5–1 dia eng (herdado; cabe folgado)
```mermaid
flowchart LR
S[worktree.sh função worktree] -->|WORKTREE_TERMINAL=1 + args| N[node scripts/worktree.mjs]
S[worktree.sh função worktree] -->|TEQO_WORKTREE_TERMINAL=1 + args| N[node scripts/worktree.mjs]
N -->|print| CD["launch opencode <dir> --model … --auto [--prompt /work-issue]<br/>cd <dir>"]
S -->|sed 's/^launch //p'| L[executa launch sem exec<br/>foreground, TUI assume o terminal]
S -->|sed 's/^cd //p'| D[cd <dir> primeiro]
@ -25,15 +25,15 @@ flowchart LR
C -->|aplica| D2[cd <dir>]
```
**Opções consideradas:** A (marcador de env `WORKTREE_TERMINAL=1` — linha `launch` só quando marcado) | B (script sempre imprime `launch`; cada chamador decide o que executar) | C (flag `--launch` no argv)
**Opções consideradas:** A (marcador de env `TEQO_WORKTREE_TERMINAL=1` — linha `launch` só quando marcado) | B (script sempre imprime `launch`; cada chamador decide o que executar) | C (flag `--launch` no argv)
**Recomendação:** A — o aceite #3 exige que o launch **não aconteça** quando o script roda sem marcador (o `/worktree` do opencode, automação): o marcador é o que separa as duas superfícies; a superfície opencode nunca recebe a linha, então seu texto não precisa ensinar a ignorá-la.
**Rejeitadas:** B — inverte o aceite (a linha sairia para o `/worktree` também; o comando precisaria aprender a descartá-la, e automação futura ingênua executaria TUI aninhado); C — o shell repassa `"$@"` intacto; uma flag extra exigiria strip antes do repasse e poderia vazar para a superfície opencode.
### Componentes / mudanças
- **`opencodeLaunchDirective`** (`scripts/lib/worktree.mjs`): função pura `({ dir, purpose, terminal }) → string | null` — devolve `null` sem marcador; linha `launch opencode <dir> --model <model> --auto` + `--prompt /work-issue` só para `purpose: 'next'` (plan **sem** prompt — fallback acordado). Constantes exportadas no lib (fonte única, unit-testável): `OPENCODE_PRESET_MODEL = 'deepseek/deepseek-v4-flash'`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE = { next: '/work-issue', plan: null }`, `WORKTREE_TERMINAL_ENV = 'WORKTREE_TERMINAL'`. Invariante documentada: dir é sempre `<root sem espaço>/<branch slugificado>` — sem espaços, word-splitting seguro.
- **`opencodeLaunchDirective`** (`scripts/lib/worktree.mjs`): função pura `({ dir, purpose, terminal }) → string | null` — devolve `null` sem marcador; linha `launch opencode <dir> --model <model> --auto` + `--prompt /work-issue` só para `purpose: 'next'` (plan **sem** prompt — fallback acordado). Constantes exportadas no lib (fonte única, unit-testável): `OPENCODE_PRESET_MODEL = 'deepseek/deepseek-v4-flash'`, `OPENCODE_SKILL_COMMAND_BY_PURPOSE = { next: '/work-issue', plan: null }`, `WORKTREE_TERMINAL_ENV = 'TEQO_WORKTREE_TERMINAL'`. Invariante documentada: dir é sempre `<root sem espaço>/<branch slugificado>` — sem espaços, word-splitting seguro.
- **`scripts/worktree.mjs`**: lê `process.env[WORKTREE_TERMINAL_ENV] === '1'`; em `cmdNext`/`cmdPlan`, no bloco `!stay`, imprime a linha `launch …` **antes** do `cd <dir>` (a linha `cd` continua sendo a **última** — contrato OPS24 preservado para todos os consumidores). Header do script + bloco de uso ganham uma linha sobre o launch.
- **`.agents/shell/worktree.sh`**: chama `WORKTREE_TERMINAL=1 node …`; depois do cd aplicado, extrai a última linha `launch …` (mesmo padrão sed do cd) e executa word-split (`$launch`, sem `eval` — formato fixo gerado pelo script, sem globs) **sem `exec`** (aceite #5: ao sair do TUI, volta ao shell no worktree); falha do launch (ex.: `opencode` ausente) imprime aviso e o worktree segue utilizável.
- **`.agents/shell/worktree.sh`**: chama `TEQO_WORKTREE_TERMINAL=1 node …`; depois do cd aplicado, extrai a última linha `launch …` (mesmo padrão sed do cd) e executa word-split (`$launch`, sem `eval` — formato fixo gerado pelo script, sem globs) **sem `exec`** (aceite #5: ao sair do TUI, volta ao shell no worktree); falha do launch (ex.: `opencode` ausente) imprime aviso e o worktree segue utilizável.
- **Migration:** sem migration (nenhuma mudança de schema/dados).
- **Access / Consent:** N/A (CLI de dev).
- **UI:** Impeccable A — N/A (sem UI de produto; TUI de terceiros).
@ -51,7 +51,7 @@ cd /home/<user>/.cursor/worktrees/teqo/OPS26-x
## Fases verificáveis
1. **Derivação pura** — lib (`opencodeLaunchDirective` + constantes) + unit tests novos em `tests/unit/worktree.unit.spec.ts` (next → prompt presente; plan → sem prompt; terminal=false → null; constantes pinadas). Quota: pequena.
2. **Script + shell** — `cmdNext`/`cmdPlan` imprimem a diretiva; função shell aplica marcador, cd e launch; textos (header/uso do script, header do .sh). Verificação manual: `WORKTREE_TERMINAL=1 node scripts/worktree.mjs …` (output shape) e simulação do parse sed com saída sintética (sem criar worktree/DBs reais).
2. **Script + shell** — `cmdNext`/`cmdPlan` imprimem a diretiva; função shell aplica marcador, cd e launch; textos (header/uso do script, header do .sh). Verificação manual: `TEQO_WORKTREE_TERMINAL=1 node scripts/worktree.mjs …` (output shape) e simulação do parse sed com saída sintética (sem criar worktree/DBs reais).
3. **Docs** — AGENTS.md (parágrafo Per-worktree), `.opencode/commands/worktree.md` (nota: comando nunca lança TUI), `.agents/skills/worktree-next-issue/SKILL.md` (linha default-go), `docs/CHANGELOG-AGENTS.md` (entrada única, padrão das entregas).
4. **Gates** — `pnpm gate:fast` na iteração; `pnpm push` na entrega (gate:ci + push).

View file

@ -30,7 +30,7 @@ flowchart LR
**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.
- **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. _(Reavaliado na OPS26: o marcador `TEQO_WORKTREE_TERMINAL` foi adotado lá para a diretiva `launch` — só a função shell marca, e a superfície `/worktree` nunca recebe a linha.)_
- **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

View file

@ -17,7 +17,7 @@ import { slugify } from '../../src/lib/slug.ts'
export const PLAN_BRANCH_PREFIX = 'plans/plan-issue'
/** Env var the interactive-terminal `worktree()` function sets to request the opencode launch. */
export const WORKTREE_TERMINAL_ENV = 'WORKTREE_TERMINAL'
export const WORKTREE_TERMINAL_ENV = 'TEQO_WORKTREE_TERMINAL'
/** Preset model for the opencode launch — change the preset by editing this constant. */
export const OPENCODE_PRESET_MODEL = 'deepseek/deepseek-v4-flash'
@ -118,7 +118,7 @@ export const planBranchName = ({ bag = '', taken = new Set() }) => {
*/
export const opencodeLaunchDirective = ({ dir, purpose, terminal = false }) => {
if (!terminal) return null
const prompt = OPENCODE_SKILL_COMMAND_BY_PURPOSE[purpose] ?? null
const prompt = OPENCODE_SKILL_COMMAND_BY_PURPOSE[purpose]
const args = [dir, '--model', OPENCODE_PRESET_MODEL, '--auto']
if (prompt) args.push('--prompt', prompt)
return `launch opencode ${args.join(' ')}`

View file

@ -13,7 +13,7 @@
* `--stay` suprime a linha `cd`; `--go` explícito
* continua aceito como no-op (era o antigo padrão).
* Chamado do terminal interativo (com
* `WORKTREE_TERMINAL=1`, que só a função shell
* `TEQO_WORKTREE_TERMINAL=1`, que só a função shell
* seta), imprime também a diretiva `launch
* opencode <dir> --model deepseek/deepseek-v4-flash
* --auto --prompt /work-issue` ANTES do `cd` — a
@ -87,11 +87,17 @@ const WORKTREES_ROOT = join(homedir(), '.cursor', 'worktrees', 'teqo')
/**
* True when the interactive terminal shell function (`.agents/shell/worktree.sh`)
* calls us: it sets `WORKTREE_TERMINAL=1` and executes the `launch` directive
* we print. Without the marker (the `/worktree` opencode command, automation)
* the launch line is never printed — no nested TUI.
* calls us: it sets `TEQO_WORKTREE_TERMINAL=1` and executes the `launch`
* directive we print. Without the marker (the `/worktree` opencode command,
* automation) the launch line is never printed — no nested TUI.
*/
const isTerminal = process.env[WORKTREE_TERMINAL_ENV] === '1'
const terminalShell = process.env[WORKTREE_TERMINAL_ENV] === '1'
/** Print the `launch` directive (only exists from the terminal shell); the `cd` line stays last. */
const printLaunchDirective = ({ dir, purpose }) => {
const line = opencodeLaunchDirective({ dir, purpose, terminal: terminalShell })
if (line) console.log(line)
}
/** Fixed compose project so `db:start`/provisioning always target ONE container. */
const SHARED_COMPOSE_PROJECT = 'teqo'
@ -362,8 +368,7 @@ const cmdNext = async (stay, skipMigrate) => {
console.log(` banco test: postgresql://teqo:teqo@localhost:5432/${env.testDatabase}`)
if (!stay) {
const launch = opencodeLaunchDirective({ dir, purpose: 'next', terminal: isTerminal })
if (launch) console.log(launch)
printLaunchDirective({ dir, purpose: 'next' })
console.log(`cd ${dir}`)
}
}
@ -437,8 +442,7 @@ const cmdPlan = async (stay, skipMigrate, bag) => {
console.log(` banco test: postgresql://teqo:teqo@localhost:5432/${env.testDatabase}`)
if (!stay) {
const launch = opencodeLaunchDirective({ dir, purpose: 'plan', terminal: isTerminal })
if (launch) console.log(launch)
printLaunchDirective({ dir, purpose: 'plan' })
console.log(`cd ${dir}`)
}
}
@ -536,7 +540,7 @@ if (!subcommand) {
console.log(' ambiente isolado: porta de dev + bancos próprios (determinístico do branch);')
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); no terminal (WORKTREE_TERMINAL=1)',
' função `worktree()` de .agents/shell/worktree.sh); no terminal (TEQO_WORKTREE_TERMINAL=1)',
)
console.log(' imprime também a diretiva `launch opencode …` (OPS26: abre o TUI com')
console.log(' deepseek/deepseek-v4-flash + auto + /work-issue enviado); --stay suprime cd e')

View file

@ -154,9 +154,15 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26)', () =>
it('pins the preset constants — changing the model is editing a constant', () => {
expect(OPENCODE_PRESET_MODEL).toBe('deepseek/deepseek-v4-flash')
expect(WORKTREE_TERMINAL_ENV).toBe('WORKTREE_TERMINAL')
expect(WORKTREE_TERMINAL_ENV).toBe('TEQO_WORKTREE_TERMINAL')
expect(OPENCODE_SKILL_COMMAND_BY_PURPOSE).toEqual({ next: '/work-issue', plan: null })
})
it('an unknown purpose degrades to a prompt-less launch (fail-safe direction)', () => {
expect(opencodeLaunchDirective({ dir, purpose: 'bogus', terminal: true })).toBe(
`launch opencode ${dir} --model deepseek/deepseek-v4-flash --auto`,
)
})
})
describe('worktreeEnvironment (per-worktree ports and databases)', () => {