From 9dd8022b6a120bb2a36babb867a38ab1535407b5 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 10 Aug 2026 14:36:08 -0300 Subject: [PATCH] =?UTF-8?q?feat(OPS31):=20worktree=20plan=20abre=20o=20ope?= =?UTF-8?q?ncode=20com=20/plan-issue=20j=C3=A1=20enviado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/shell/worktree.sh | 4 +- .agents/skills/worktree-next-issue/SKILL.md | 2 +- AGENTS.md | 2 +- docs/CHANGELOG-AGENTS.md | 3 + ...-plan-abre-opencode-com-plan-issue-impl.md | 66 +++++++++++++++++++ scripts/lib/worktree.mjs | 18 +++-- scripts/worktree.mjs | 10 +-- tests/unit/worktree.unit.spec.ts | 6 +- 8 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 docs/plans/worktree-plan-abre-opencode-com-plan-issue-impl.md diff --git a/.agents/shell/worktree.sh b/.agents/shell/worktree.sh index 5efd229b..2906a14d 100644 --- a/.agents/shell/worktree.sh +++ b/.agents/shell/worktree.sh @@ -9,8 +9,8 @@ # No terminal (esta função), o script também imprime a diretiva `launch # opencode --model deepseek/deepseek-v4-flash --auto [--prompt …]` (OPS26): # a função executa o cd e então a linha, e o TUI do opencode abre no worktree — -# `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 +# `next` com `/work-issue` já enviado, `plan` com `/plan-issue` já enviado +# (OPS31). 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 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. diff --git a/.agents/skills/worktree-next-issue/SKILL.md b/.agents/skills/worktree-next-issue/SKILL.md index d4bc1037..53957bf7 100644 --- a/.agents/skills/worktree-next-issue/SKILL.md +++ b/.agents/skills/worktree-next-issue/SKILL.md @@ -31,7 +31,7 @@ No opencode, isso é o comando **`/worktree next`** / **`/worktree kill`** (`.op **Default-go:** `next`, `plan` e `new` imprimem `cd ` 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
` no fim (a sessão nunca fica num diretório destruído). -**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 --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 + OPS31):** só a função shell marca `TEQO_WORKTREE_TERMINAL=1`; com o marcador, `next`/`plan`/`new` imprimem também a diretiva `launch opencode --model deepseek/deepseek-v4-flash --auto [--prompt ]` **antes** do `cd`, e a função a executa depois do cd — `next` abre o TUI com `/work-issue` já enviado (o comando OPS25 roda o ciclo completo); `plan` abre com `/plan-issue` já enviado (OPS31: o TUI já cai no fluxo de planejamento); `new` abre sem `--prompt` ("apenas conversar"). 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) diff --git a/AGENTS.md b/AGENTS.md index 7fd6214b..bbfe228b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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` (dev) and `teqo_wt_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`/`new` print `cd ` 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
` so the shell never sits in a destroyed directory. **In the interactive terminal the shell function sets `TEQO_WORKTREE_TERMINAL=1` and `next`/`plan`/`new` additionally print a `launch opencode --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` and `new` open with the same presets and NO prompt (`plan`: `/plan-` + TUI autocomplete; `new`: "apenas conversar", no skill — 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-` when a `bag` slug is given (suffixed `-2`/`-3` if that name is taken) or the next free sequential `plans/plan-issue-` when not (`PLAN_BRANCH_PREFIX`/`planBranchName`, `scripts/lib/worktree.mjs`); the lowercase `plans/…` prefix deliberately never matches `next`'s uppercase-led `-`, so a later `next` never collides in branch or slot. `worktree new [bag] [--stay]` (OPS27, 2026-08-10) is the NEUTRAL sibling: a worktree with **no pre-defined function\*\* (explore an idea, chat, plan without registering anything) — also a DIFFERENT one per invocation, branch `work/` (suffixed `-2`/`-3` on collision) or the next free sequential `work/` (`WORK_BRANCH_PREFIX`/`workBranchName`, same `namespaceBranchName` core as `plan`); the lowercase `work/…` prefix collides with neither `next` nor `plan`, and the provisioning is identical with `purpose: 'new'` (env comment `gerado por pnpm worktree new`; the frozen env marker keeps its historical `…next` text). `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__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` (dev) and `teqo_wt_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`/`new` print `cd ` 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
` so the shell never sits in a destroyed directory. **In the interactive terminal the shell function sets `TEQO_WORKTREE_TERMINAL=1` and `next`/`plan`/`new` additionally print a `launch opencode --model deepseek/deepseek-v4-flash --auto [--prompt ]` directive BEFORE the `cd` line, which the function executes after the cd (OPS26, 2026-08-10; OPS31 for `plan`): `next` opens the opencode TUI in the worktree with `/work-issue` already sent (the OPS25 command runs the whole cycle), `plan` opens with `/plan-issue` already sent (the TUI lands straight in the planning flow), `new` opens with no prompt ("apenas conversar", no skill — the opencode CLI's `--prompt` always auto-submits, there is no prefill-without-submit; that stays an upstream-only feature request, annotated in the opencode proposition). 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-` when a `bag` slug is given (suffixed `-2`/`-3` if that name is taken) or the next free sequential `plans/plan-issue-` when not (`PLAN_BRANCH_PREFIX`/`planBranchName`, `scripts/lib/worktree.mjs`); the lowercase `plans/…` prefix deliberately never matches `next`'s uppercase-led `-`, so a later `next` never collides in branch or slot. `worktree new [bag] [--stay]` (OPS27, 2026-08-10) is the NEUTRAL sibling: a worktree with **no pre-defined function\*\* (explore an idea, chat, plan without registering anything) — also a DIFFERENT one per invocation, branch `work/` (suffixed `-2`/`-3` on collision) or the next free sequential `work/` (`WORK_BRANCH_PREFIX`/`workBranchName`, same `namespaceBranchName` core as `plan`); the lowercase `work/…` prefix collides with neither `next` nor `plan`, and the provisioning is identical with `purpose: 'new'` (env comment `gerado por pnpm worktree new`; the frozen env marker keeps its historical `…next` text). `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__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. diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index a9039a9d..a70f8861 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -5,6 +5,9 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- **Recently resolved (2026-08-10):** **OPS22** (Issue #562) — CI de `main` vermelho determinístico desde o merge do B188 (05:54): os 3 e2e da agenda mobile (`campaignAgendaMobile` :96/:242/:295) falhavam com todas as retries → deploy bloqueado. **Root cause (reproduzida no prod build local + error-contexts com o drawer aberto):** o login e2e roda em **viewport desktop** → o settle de B167 abre o chat (`open=true`) → o **efeito persist do B188** (status `ready` já nasce) grava `{open:true}` no `sessionStorage` → o spec muda para 390px na **mesma aba** → o **restore do B188** reabre o **drawer mobile sozinho** → o modal Radix **aria-hide + inert** a página e cobre o viewport (combobox fora da a11y tree no :242, swipe cai no drawer no :96, clique do seletor bloqueado no :295; teclado/:173 passam por não usarem hit-testing). **Defeito real de produto** (aba desktop → mobile na mesma aba faz o chat "saltar"), não só timing de teste. **Fix na origem:** `SollinhaChatSession` ganhou **`openBy: 'user' | 'settle'`** (opcional — sessão legada sem o campo = `'settle'`, fail-closed; sem bump de versão) e o provider agora distingue **intenção do usuário** (`setOpen`/`toggle` do context value marcam `userToggledOpenRef`; FAB/header/drawer já passam por eles) de **verdade de layout** (settle B167 usa o setter cru): o **restore espera o `measured`** (viewport real) e honra `open:true` em mobile **só quando `openBy==='user'`**; o persist grava `openBy` conforme a intenção. Todos os contratos B188/resize mantidos (reload restaura conversa; drawer aberto por usuário volta aberto; visita mobile nova fechada; crossing ao vivo B167 intocado). Testes: unit (round-trip/invalidez/legado de `openBy`) + **e2e pin OPS22** no spec B188 (login desktop → poll do persist `open:true` → viewport 390 → `/campanha/agenda` → sem dialog + botão do Sollinha visível); spec da agenda intacto (4/4 no prod build). Sem migration/access/Consent/schema. Intenção [`ci-e2e-agenda-mobile-regressao-b188.md`](plans/ci-e2e-agenda-mobile-regressao-b188.md); impl [`ci-e2e-agenda-mobile-regressao-b188-impl.md`](plans/ci-e2e-agenda-mobile-regressao-b188-impl.md). +**Recently resolved (2026-08-10):** **OPS31** (Issue #593) — `worktree plan` no terminal agora abre o TUI do opencode com `/plan-issue` **já enviado** (a revisão da proposição do opencode mostrou que a necessidade real do fluxo era auto-envio, não pre-fill: `--prompt` sempre auto-submete, e o `next` já provava com `/work-issue` — o `plan` só ficou sem prompt por uma premissa equivocada da OPS26). Mudança de valor no mapa existente: `OPENCODE_SKILL_COMMAND_BY_PURPOSE.plan: null → '/plan-issue'` (`scripts/lib/worktree.mjs`); `next` (`/work-issue`) e `new` (prompt-less, "apenas conversar") intocados; o mecanismo OPS26 já injetava `--prompt` quando o valor existia — zero código novo. Textos correlatos atualizados (docblock/uso do script, `worktree.sh`, AGENTS.md, skill `worktree-next-issue`); o tom do gap mudou: prefill-without-submit segue como feature request upstream (proposição `~/Code/propositions/opencode/prefill-prompt-sem-submit.md` anotada com o desfecho — edição local, fora do repo), não mais fallback do Teqo; entradas OPS26/OPS27 do changelog permanecem como histórico. Unit: teste "plan launches with `/plan-issue` sent" + pin de constantes `{ next: '/work-issue', plan: '/plan-issue', new: null }`. Sem migration/dados/access/UI. Intenção [`worktree-plan-abre-opencode-com-plan-issue.md`](plans/worktree-plan-abre-opencode-com-plan-issue.md); impl [`worktree-plan-abre-opencode-com-plan-issue-impl.md`](plans/worktree-plan-abre-opencode-com-plan-issue-impl.md). + +**Recently resolved (2026-08-10):** **B193** (Issue #542) — card de município mobile denso + edit-where-you-see + última atualização expandível. O card de `/campanha/municipios` no mobile deixou o grid de labels e virou composição do wireframe: cabeçalho nome+território com **posição de voto 2022 à direita** (percentual em cima; colocação · votos embaixo), **barra de cenários** (pessimista·central·otimista + marcador do ativo — `VoteEstimateScenarioStrip` como trigger da sheet de votos existente), **chips** classe/tendência/nível (tendência e nível com label à esquerda, dentro do trigger — tocar no label abre a sheet; classe com razão via tooltip que abre no toque), **3 grupos de avatares com label** (Assessores/Lideranças/Dobradinhas — um avatar por entidade, wrap, sem cap; leitura de assessores para não-coordenador), rodapé **"Última atualização há X dias" + chevron que expande o limite inferior** revelando o último card de atualização (badges + corpo + autor · data, sem moldura) + CTA "Registrar atualização" (sheet existente); **sem atualização → o rodapé vira CTA direto** (sem chevron/expansão); prioritário = **borda lateral direita 6px** `border-primary` no lugar do flag; Salvador read-only; "Cobertura" (conta da cadeira) sai do card (fica na tabela/detalhe — gate humano); rodapé mede a idade da **atualização** (não o frescor update∨pledge — coluna desktop intacta). **C109 corrigida no caminho (a "sheet quebra ao abrir" era real):** os callbacks dos controles (autosave `onOpenChange`, `handleOpenChange` do nível) são recriados por render — qualquer re-render com a sheet aberta (digitar no sheet!) fazia o host descartar a sessão e o corpo sumia; sessões do host agora são **keyed por id de sessão** (imune a identidade) e o **footer customizado** (submit + `DialogClose`) passou a ser **registrado e renderizado pelo host dentro da árvore do Drawer** (portal perdia o Dialog context — o e2e antigo só abria a sheet de tendência, sem footer custom, por isso a C109 não reproduzia). Controles ganharam prop `trigger`/`triggerClassName` (precedente `RelationChipCell.trigger`); `MunicipalityRelationAvatarStack` ganhou modo wrap. Dado: `MunicipalityListViewModel.lastUpdate` (batch por página via `loadMunicipalityLastUpdates`, scoped por portfólio). E2E novos (390×844): barra abre sheet e reflete valores salvos, chip Nível abre a sheet, borda de prioridade (6px + cor), expansão/CTA vazio. Sem migration/access/Consent. Intenção [`municipios-card-mobile-edit-where-you-see.md`](plans/municipios-card-mobile-edit-where-you-see.md); impl [`municipios-card-mobile-edit-where-you-see-impl.md`](plans/municipios-card-mobile-edit-where-you-see-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 --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). diff --git a/docs/plans/worktree-plan-abre-opencode-com-plan-issue-impl.md b/docs/plans/worktree-plan-abre-opencode-com-plan-issue-impl.md new file mode 100644 index 00000000..d341fd53 --- /dev/null +++ b/docs/plans/worktree-plan-abre-opencode-com-plan-issue-impl.md @@ -0,0 +1,66 @@ +# Impl: Worktree plan abre o opencode com /plan-issue já enviado + +Status: aprovado +Atualizado em: 2026-08-10 +Issue: #593 +Intenção: docs/plans/worktree-plan-abre-opencode-com-plan-issue.md +Appetite restante: ~0,5 dia — cabe folgado (1 constante + textos + testes + anotação local) + +## Leitura da intenção + +- **Outcome:** `worktree plan [bag]` no terminal (sem `--stay`) imprime a diretiva `launch opencode --model --auto --prompt /plan-issue` antes do `cd`; o TUI abre com a skill `plan-issue` já enviada e em execução, sem digitação. `--stay` e o comando `/worktree` continuam como estão (nunca TUI aninhado). Proposição `~/Code/propositions/opencode/prefill-prompt-sem-submit.md` revisada/anotada com o desfecho. Textos correlatos que dizem "plan sem prompt — `/plan-` + autocomplete" passam a dizer "`/plan-issue` enviado". +- **O que NÃO negociar:** simetria com `next` (auto-envio, mesmo se o upstream ganhar prefill — decisão do gate); `next` intacto (`/work-issue`); `new` intacto (prompt-less, "apenas conversar"); `--stay` suprime cd + launch; superfície `/worktree` nunca lança TUI; presets continuam constantes em `scripts/lib/worktree.mjs`. +- **O que reavaliar:** o tom do comentário "gap registered for the opencode repo" no lib — a intenção manda sair ou mudar de tom; o gap upstream (prefill-sem-submit) **continua existindo** como feature request do opencode, mas deixa de ser fallback do Teqo. + +## Abordagem recomendada + +```mermaid +flowchart LR + M[OPENCODE_SKILL_COMMAND_BY_PURPOSE] -->|plan: /plan-issue| D[opencodeLaunchDirective] + D -->|terminal:true| L["launch opencode --model … --auto --prompt /plan-issue"] + L -->|worktree.sh| T[TUI abre com a skill plan-issue em execução] + T -->|--prompt auto-envia| S[fluxo /plan-issue começa sem digitação] +``` + +**Opções consideradas:** A (mudança de valor da constante `plan: null → '/plan-issue'` + textos + testes + anotação da proposição — o mecanismo OPS26 já injeta `--prompt` quando o valor existe) | B (novo mecanismo de draft/prefill por propósito no script do Teqo, ex. campo que distinguisse auto-envio de pré-preenchimento) | C (flag nova no CLI do opencode a partir do Teqo — `--prompt-draft` etc.) +**Recomendação:** A — o mecanismo de launch já existe e é simétrico ao `next`; a necessidade real (revisada na proposição) é **auto-envio**, que `--prompt` cobre hoje; prefill-sem-submit é feature upstream, não bloqueio do Teqo (anti-goal explícito da intenção: não virar mecanismo de prefill-draft). +**Rejeitadas:** B — duplica o que `--prompt` já faz e inventa cerimônia para um uso único; o gate decidiu manter auto-envio mesmo quando o upstream ganhar prefill. C — fora do escopo do repo (anti-goal: prefill é feature do opencode). + +### Componentes / mudanças + +- **`OPENCODE_SKILL_COMMAND_BY_PURPOSE`** (`scripts/lib/worktree.mjs:34`): `plan: null → plan: '/plan-issue'`; `next`/`new` intocados. Comentário do bloco (linhas 22–33): `plan` passa a enviar `/plan-issue`; o tom do gap muda — prefill-without-submit segue como feature request do opencode (proposição anotada), não é mais fallback do Teqo; `new` continua "apenas conversar" sem skill. +- **`tests/unit/worktree.unit.spec.ts`**: teste "plan launches with the same presets but NO prompt" (linha 151) vira "plan launches with `/plan-issue` sent"; pin de constantes (linhas 163–171) espera `{ next: '/work-issue', plan: '/plan-issue', new: null }`; `new` prompt-less e unknown-purpose fail-safe **intocados** (continuam corretos). +- **`scripts/worktree.mjs`**: docblock do `plan` (linhas 43–46) e usage do help (linha 617) — "sem `--prompt` / autocomplete" → "`--prompt /plan-issue` enviado". Bloco do `new` (linhas 57–59, 623) intocado. +- **`.agents/shell/worktree.sh`**: comentário do header (linhas 12–13) — `plan` passa a "com `/plan-issue` já enviado". +- **`AGENTS.md`** (linha 37, parágrafo Per-worktree): `plan` sai do "NO prompt (`plan`: `/plan-` + TUI autocomplete)" → "`plan` sends `/plan-issue`"; nota do gap reduzida ao upstream (proposição anotada). +- **`.agents/skills/worktree-next-issue/SKILL.md`** (linha 34): `plan` abre com `/plan-issue` já enviado; mesma redução de tom do gap. +- **`docs/CHANGELOG-AGENTS.md`**: entrada **nova** OPS31 (curta, padrão das entregas). Entradas OPS26/OPS27 **permanecem como histórico** (registram o estado na entrega; decisão abaixo). +- **`~/Code/propositions/opencode/prefill-prompt-sem-submit.md`** (fora do repo — edição local, sem commit): bloco de desfecho datado — a necessidade real do fluxo Teqo era auto-envio, coberto por `--prompt` (OPS31 envia `/plan-issue`); prefill-sem-submit segue como feature request upstream; seção "Impacto no Teqo" atualizada. +- **Migration:** sem migration. **Access / Consent:** N/A. **UI:** Impeccable A — N/A (CLI/shell, sem superfície de produto). + +## Fases verificáveis + +1. **Constante + testes** — `scripts/lib/worktree.mjs` (valor + comentário) e `tests/unit/worktree.unit.spec.ts` (2 testes). Quota: pequena. Rodar só o spec: `pnpm exec vitest run tests/unit/worktree.unit.spec.ts`. +2. **Textos correlatos + proposição** — `scripts/worktree.mjs` (docblock/usage), `worktree.sh`, `AGENTS.md`, skill `worktree-next-issue`, CHANGELOG (entrada nova), proposição anotada. Verificação manual da diretiva: `TEQO_WORKTREE_TERMINAL=1 node scripts/worktree.mjs plan --stay --dry-run`? Não — o script cria worktree real; verificar via simulação da função pura (`opencodeLaunchDirective({ purpose: 'plan', terminal: true })`) que o teste de fase 1 já cobre + leitura da linha impressa não muda (o lib gera). +3. **Gates** — `pnpm gate:fast` na iteração; `pnpm push` na entrega. + +## Rabbit holes / Não escopo (engenharia) + +- Implementar prefill-draft no opencode (feature upstream; só anotação na proposição). +- Mudar o launch do `next` (`/work-issue`) ou do `new` (prompt-less) — fora de escopo. +- `docs/plans/*` de entregas passadas (OPS25/OPS26) e entradas OPS26/OPS27 do CHANGELOG: histórico congelado, sem edição retroativa. +- `.opencode/commands/worktree.md`: sem mudança — não descreve o prompt do `plan` (a nota do marcador continua válida). + +## Riscos e mitigação + +- **Quebrar `next` por engano:** valor do mapa muda só em `plan`; teste de pin cobre os três (se o pin quebrar, a CI falha na fase 1). +- **Texto "gap" ambíguo (leitor acha que prefill não existe mais):** comentário do lib mantém menção curta ao upstream com o novo tom (proposição anotada = fonte). +- **Contrato "cd última linha" quebrado:** nenhuma mudança na ordem de impressão (a diretiva continua antes do `cd`); só o valor de `--prompt`. +- **Word-splitting da linha launch:** `/plan-issue` não tem espaços; invariante existente preservada. +- **CHANGELOG histórico divergente do runtime atual:** entrada OPS31 registra a supersessão explícita do comportamento OPS26; leitor do histórico tem a data e o desfecho. + +## Aceite de engenharia + +- [x] Aceite de produto da intenção ainda coberto (diretiva com `--prompt /plan-issue` no `plan`; `--stay` e `/worktree` intocados; proposição anotada; textos correlatos atualizados) +- [x] Invariantes AGENTS/engineering-standards (nenhuma mudança em src/, migrations, access, Consent, transações; identificadores em inglês, copy pt-BR) +- [x] Testes de domínio previstos (unit da derivação pura — 2 testes; sem int/e2e — nenhum caminho de escrita/access muda) diff --git a/scripts/lib/worktree.mjs b/scripts/lib/worktree.mjs index 7f337ab4..f74e8103 100644 --- a/scripts/lib/worktree.mjs +++ b/scripts/lib/worktree.mjs @@ -24,14 +24,18 @@ export const OPENCODE_PRESET_MODEL = 'deepseek/deepseek-v4-flash' /** * Skill command sent as the launch's initial message per purpose. `next` - * sends `/work-issue` (the OPS25 command executes the full cycle); `plan` - * and `new` send nothing — the opencode CLI's `--prompt` always auto-submits, - * there is no prefill-without-submit flag, and the TUI autocomplete completes - * `/plan-` instead (`new` is just "apenas conversar", no skill at all; gap - * registered for the opencode repo). Switch `plan` to `/plan-issue` here if - * the CLI ever gains prefill-without-submit. + * sends `/work-issue` (the OPS25 command executes the full cycle), `plan` + * sends `/plan-issue` (OPS31 — auto-submit is the real need of the flow) and + * `new` sends nothing ("apenas conversar", no skill at all). The opencode + * CLI's `--prompt` always auto-submits, there is no prefill-without-submit + * flag; prefill stays an upstream-only feature request (proposition + * annotated), not a Teqo fallback. */ -export const OPENCODE_SKILL_COMMAND_BY_PURPOSE = { next: '/work-issue', plan: null, new: null } +export const OPENCODE_SKILL_COMMAND_BY_PURPOSE = { + next: '/work-issue', + plan: '/plan-issue', + new: null, +} /** * Prefix of every neutral-worktree branch (`pnpm worktree new`). Lowercase-led diff --git a/scripts/worktree.mjs b/scripts/worktree.mjs index b4c91a29..f119774b 100644 --- a/scripts/worktree.mjs +++ b/scripts/worktree.mjs @@ -41,9 +41,9 @@ * deles (branch nem slot) colide com um `next` * posterior (prefixo minúsculo `plans/…`). * Mesmo provisionamento isolado do `next`; no - * terminal, mesma diretiva `launch` — porém SEM - * `--prompt` (a CLI do opencode não tem pre-fill - * sem submit; `/plan-` + autocomplete do TUI). + * terminal, mesma diretiva `launch` — com + * `--prompt /plan-issue` já enviado (OPS31: o TUI + * abre no fluxo de planejamento, sem digitação). * pnpm worktree new [bag] [--stay] [--no-migrate] * cria um worktree NEUTRO novo — sem função * pré-definida (explorar ideia, conversar, ou @@ -614,7 +614,9 @@ if (!subcommand) { console.log( ' minúsculo plans/… nunca colide com o branch - de `next`; no terminal,', ) - console.log(' mesma diretiva `launch` porém sem --prompt (autocomplete completa /plan-)') + console.log( + ' mesma diretiva `launch` com --prompt /plan-issue enviado (abre no fluxo de planejamento, sem digitação)', + ) console.log(`\n new [bag] [--stay] [--no-migrate]`) console.log(' cria um worktree NEUTRO (sem função pré-definida) DIFERENTE a cada invocação:') console.log(' com bag, branch work/ (sufixo -2/-3 se o nome já existir); sem bag, o') diff --git a/tests/unit/worktree.unit.spec.ts b/tests/unit/worktree.unit.spec.ts index dee512ab..e104483a 100644 --- a/tests/unit/worktree.unit.spec.ts +++ b/tests/unit/worktree.unit.spec.ts @@ -148,9 +148,9 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26)', () => ) }) - it('plan launches with the same presets but NO prompt (no prefill-without-submit)', () => { + it('plan launches with the same presets and /plan-issue sent (OPS31)', () => { expect(opencodeLaunchDirective({ dir, purpose: 'plan', terminal: true })).toBe( - `launch opencode ${dir} --model deepseek/deepseek-v4-flash --auto`, + `launch opencode ${dir} --model deepseek/deepseek-v4-flash --auto --prompt /plan-issue`, ) }) @@ -165,7 +165,7 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26)', () => expect(WORKTREE_TERMINAL_ENV).toBe('TEQO_WORKTREE_TERMINAL') expect(OPENCODE_SKILL_COMMAND_BY_PURPOSE).toEqual({ next: '/work-issue', - plan: null, + plan: '/plan-issue', new: null, }) })