From a2db543e0c94f94255cf819ce2296fe6f2d208dc Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Sun, 23 Aug 2026 22:41:51 -0300 Subject: [PATCH] =?UTF-8?q?ops(OPS82):=20preset=20do=20launch=20opencode?= =?UTF-8?q?=20=E2=86=92=20DeepSeek=20V4=20Flash=200731=20(mais=20barato)?= =?UTF-8?q?=20+=20variantes=20low/high/max=20+=20remo=C3=A7=C3=A3o=20dupli?= =?UTF-8?q?cata=20cheapest-inference=20(Closes=20#817)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/shell/worktree.sh | 2 +- .agents/skills/worktree-next-issue/SKILL.md | 2 +- docs/CHANGELOG-AGENTS.md | 4 +- docs/changelog/2026-08-23-ops82.md | 1 + ...r-variantes-deepseek-v4-flash-0731-impl.md | 108 ++++++++++++++++++ opencode.json | 55 +-------- scripts/lib/worktree.mjs | 5 +- scripts/worktree.mjs | 7 +- tests/unit/worktree.unit.spec.ts | 16 +-- 9 files changed, 132 insertions(+), 68 deletions(-) create mode 100644 docs/changelog/2026-08-23-ops82.md create mode 100644 docs/plans/expor-variantes-deepseek-v4-flash-0731-impl.md diff --git a/.agents/shell/worktree.sh b/.agents/shell/worktree.sh index 65ac90fb..b516ea86 100644 --- a/.agents/shell/worktree.sh +++ b/.agents/shell/worktree.sh @@ -7,7 +7,7 @@ # shell que o chamou. # # No terminal (esta função), o script também imprime a diretiva `launch -# opencode --model vercel/deepseek/deepseek-v4-flash --auto [--prompt "…"]` (OPS26): +# opencode --model vercel/deepseek/deepseek-v4-flash-0731 --auto [--prompt "…"]` (OPS26): # a função executa o cd e então a linha (tokenizada por xargs — honra as aspas # do prompt, nunca eval), e o TUI do opencode abre no worktree — `next` com # `/work-issue --issue ` já enviado (OPS33: a Issue claimada vai no prompt), diff --git a/.agents/skills/worktree-next-issue/SKILL.md b/.agents/skills/worktree-next-issue/SKILL.md index b6923b86..d03c9d87 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 + OPS33 + 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 vercel/deepseek/deepseek-v4-flash --auto [--prompt "/work-issue --issue "]` **antes** do `cd`, e a função a executa depois do cd — `next` abre o TUI com `/work-issue --issue ` já enviado (a Issue claimada vai no prompt; a skill lê o resto do Forgejo); `plan` abre com `/plan-issue` já enviado (OPS31: o TUI já cai no fluxo de planejamento); `new` abre sem `--prompt` ("apenas conversar"). O preset roteia pelo **Vercel AI Gateway** (OPS78: rota com cache/estabilidade/failover; variantes low/high/max do model override em `opencode.json`, selecionáveis no TUI e sticky por model). 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 + OPS33 + 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 vercel/deepseek/deepseek-v4-flash-0731 --auto [--prompt "/work-issue --issue "]` **antes** do `cd`, e a função a executa depois do cd — `next` abre o TUI com `/work-issue --issue ` já enviado (a Issue claimada vai no prompt; a skill lê o resto do Forgejo); `plan` abre com `/plan-issue` já enviado (OPS31: o TUI já cai no fluxo de planejamento); `new` abre sem `--prompt` ("apenas conversar"). O preset roteia pelo **Vercel AI Gateway** (OPS78: rota com cache/estabilidade/failover; variantes low/high/max do model override em `opencode.json`, selecionáveis no TUI e sticky por model). 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/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 08293293..d0cfd781 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,9 +4,11 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**OPS79 — última migração de dados em produção: plataforma antiga → nova (vertical campanha) (2026-08-23):** com a fonte congelada pelo OPS80 (zero escritas no Neon desde 2026-08-23 22:30 UTC), reconciliação e migração do residual. **Descoberta-chave:** a vertical campanha JÁ estava 100% íntegra no `teo_1313` (aceite do OPS51 confirmado por reconciliação repetível — diff 0 em contagens e junta de IDs das 13 coleções; `municipality_rels` só 3 links de edição de portfólio pós-OPS51, absorvidos como não-dado). O único residual real entre Neon e target era a `signature` pública 1486 (petition `fim-escala-6x1`) com seu contato + subscription, cujo contato originário (Juares) **não existia** no target (o id 2221 do target é outra pessoa). **Ferramentas:** `scripts/reconcile-campaign-vertical.mjs` (read-only, 19/19 PASS, nunca imprime PII) + `scripts/migrate-signature-orphan.mjs` (dry-run/apply, guard `OPS79_MIGRATE_CONFIRM=1`, transação única, PII mascarada nos logs). **Execução 100% no homeserver:** aplicado contact novo **2225** + signature **1486** preservada + subscription **1492** + sequences ajustadas; pós-verificação `signature` 1485=1485, reconcile 19/19, smoke `/` `/campanha` `/admin` 200. Runbook (rollback = DELETE das 3 linhas + setval) em `docs/ops/teqo-1313-deploy.md` §OPS79. Sem migration de schema, sem access/Consent novo, sem UI. Closes #797. + **E2E-FULL-FLAKE-C142MUN — endurecer write-chrome assertions sob 4 workers (2026-08-23):** as 3 asserts de `campaignPermissionProfile.e2e.spec.ts:85` (carteira FAB visível), `:101` (tudo FAB visível) e `campaignMunicipalities.e2e.spec.ts:115` (checkbox de advisor no `/editar`) falhavam consistentemente no `deploy.yml → verify` (suíte full, 4 workers, prod build) por latência de render/hydration do shell streamed sob carga — a sonda SSR prova que o FAB está correto no HTML; o falha é que `networkidle` finda antes do chunk `S:` commitar, e a assert `toBeVisible`/`.check()` usa o budget default que é insuficiente no runner hosted. Fix: adicionar `page.waitForFunction(() => document.querySelectorAll('div[id^="S:"]').length === 0)` (padrão C106 já consolidado nos irmãos do arquivo) antes das asserts de presença — dois bursts de spec, sem mudança de produto/access/migration. Verificado: `gate:fast` (2439 unit tests, all green); e2e das 2 specs isoladas + repeat-each=3 em 4 workers: 21/21 passed. Sem alteração de access, sem migration, sem redução de workers do full. -**OPS79 — última migração de dados em produção: plataforma antiga → nova (vertical campanha) (2026-08-23):** com a fonte congelada pelo OPS80 (zero escritas no Neon desde 2026-08-23 22:30 UTC), reconciliação e migração do residual. **Descoberta-chave:** a vertical campanha JÁ estava 100% íntegra no `teo_1313` (aceite do OPS51 confirmado por reconciliação repetível — diff 0 em contagens e junta de IDs das 13 coleções; `municipality_rels` só 3 links de edição de portfólio pós-OPS51, absorvidos como não-dado). O único residual real entre Neon e target era a `signature` pública 1486 (petition `fim-escala-6x1`) com seu contato + subscription, cujo contato originário (Juares) **não existia** no target (o id 2221 do target é outra pessoa). **Ferramentas:** `scripts/reconcile-campaign-vertical.mjs` (read-only, 19/19 PASS, nunca imprime PII) + `scripts/migrate-signature-orphan.mjs` (dry-run/apply, guard `OPS79_MIGRATE_CONFIRM=1`, transação única, PII mascarada nos logs). **Execução 100% no homeserver:** aplicado contact novo **2225** + signature **1486** preservada + subscription **1492** + sequences ajustadas; pós-verificação `signature` 1485=1485, reconcile 19/19, smoke `/` `/campanha` `/admin` 200. Runbook (rollback = DELETE das 3 linhas + setval) em `docs/ops/teqo-1313-deploy.md` §OPS79. Sem migration de schema, sem access/Consent novo, sem UI. Closes #797. +**OPS82 — default de launch do worktree opencode → DeepSeek V4 Flash 0731 (mais barato) + variantes expostas + limpeza duplicatas Cheapest Inference (2026-08-23):** continuação da OPS78: o launch de `pnpm worktree next/plan/new` passou do preset `vercel/deepseek/deepseek-v4-flash` (input $0.13/M) para **`vercel/deepseek/deepseek-v4-flash-0731`** (input $0.076/M — mesma família, razão forte, ~41% mais barato). Mudança config-only (mecanismo OPS26 "trocar modelo = editar a constante"): `OPENCODE_PRESET_MODEL` em `scripts/lib/worktree.mjs` → `deepseek-v4-flash-0731`; o repo `opencode.json` ganhou o `provider.vercel.models["deepseek/deepseek-v4-flash-0731"].variants` com low/high/max (espelha o sibling OPS78) — o catálogo do opencode traz esse model key com `reasoning_options` vazio, então o override é o que torna as variantes selecionáveis no TUI. No mesmo repo `opencode.json`, **removido o bloco `provider.cheapest-inference` (com hífen) inteiro** — duplicata do global `~/.config/opencode/opencode.jsonc`, que já define `cheapestinception` (sem hífen) com os mesmos `deepseek-v4-flash` + `mimo-v2.5` **e** com variantes low/high/max; agora o TUI mostra **um único** DeepSeek e **um único** MiMo no Cheapest Inference. 8 literais + pin da constante em `tests/unit/worktree.unit.spec.ts` atualizados; textos em `scripts/worktree.mjs` (docblock/help — o help agora interpola `OPENCODE_PRESET_MODEL`, importado), `.agents/shell/worktree.sh` e `worktree-next-issue/SKILL.md` sincronizados; o help deixou de duplicar o literal. Anti-goals preservados: cardápio de execução de Issues intocado (`work-issue.md`/`plan-issue.md` + pin `opencodeCommands.unit.spec.ts` seguem com o provider direto), config global do humano intocada, docs históricos congelados, usos diretos `deepSeek('deepseek-v4-flash')` no app (Sollinha/`campaignDemandTitle.ts`) intactos. Verificação: `pnpm gate:fast` verde; `opencode debug config` confirma variantes do 0731 ativas e `cheapest-inference` do repo ausente; PR em main com auto-merge. **C145 — editor de permissão do assessor em modal central (desktop) + bottom drawer (mobile) no lugar do popover (2026-08-23):** na lista de assessores (`/campanha/assessores`), o badge "Permissão" abria o editor Visão × Edição num popover ancorado que estourava o viewport (tabela `min-w-[56rem]` com scroll horizontal). Troca do invólucro contida em `AdvisorPermissionBadge.tsx` (ponto único dos dois usos — linha de dados com `advisorId`+`formAction` e linha de rascunho com `onDraftChange` local): o badge agora abre um **Dialog central** no desktop e um **bottom drawer** no celular, selecionados por `useNarrowMeasured(640)` vivendo no próprio badge (não na tabela). O `AdvisorPermissionEditor` entra com props idênticas às de hoje — mesmos combos Visão/Edição (`advisor-permission-visibility`/`advisor-permission-editing`), nota, footer Cancelar/Salvar e regras de coerência intactos; `onSaved`/`onCancel` fecham o overlay e o badge reflete o novo perfil; a linha de rascunho segue via `onDraftChange` local, sem escrita nova no servidor. A11y: `DialogTitle`/`DrawerTitle` sr-only "Permissão da conta" cumprem o Radix sem duplicar o heading visível do editor; guards de altura (`max-h-[calc(100dvh-2rem)]`+scroll no dialog, capa do drawer) garantem o "nunca estoura" do aceite. Editor embutido no detalhe `assessores/[id]/page.tsx` intocado; import de `Popover` removido. Débito adiado com gatilho: o padrão "Dialog/Drawer keyed em `isNarrow`" se repete em >3 overlays — extrair primitivo `ResponsiveSheet` só quando uma 3ª superfície precisar do mesmo wrapper. Verificação: lint/typecheck/unit + int 830 verdes; `gate:push` na entrega; PR em main com auto-merge. diff --git a/docs/changelog/2026-08-23-ops82.md b/docs/changelog/2026-08-23-ops82.md new file mode 100644 index 00000000..1bd65872 --- /dev/null +++ b/docs/changelog/2026-08-23-ops82.md @@ -0,0 +1 @@ +**OPS82 — default de launch do worktree opencode → DeepSeek V4 Flash 0731 (mais barato) + variantes expostas + limpeza duplicatas Cheapest Inference (2026-08-23):** continuação da OPS78: o launch de `pnpm worktree next/plan/new` passou do preset `vercel/deepseek/deepseek-v4-flash` (input $0.13/M) para **`vercel/deepseek/deepseek-v4-flash-0731`** (input $0.076/M — mesma família, razão forte, ~41% mais barato). Mudança config-only (mecanismo OPS26 "trocar modelo = editar a constante"): `OPENCODE_PRESET_MODEL` em `scripts/lib/worktree.mjs` → `deepseek-v4-flash-0731`; o repo `opencode.json` ganhou o `provider.vercel.models["deepseek/deepseek-v4-flash-0731"].variants` com low/high/max (espelha o sibling OPS78) — o catálogo do opencode traz esse model key com `reasoning_options` vazio, então o override é o que torna as variantes selecionáveis no TUI. No mesmo repo `opencode.json`, **removido o bloco `provider.cheapest-inference` (com hífen) inteiro** — duplicata do global `~/.config/opencode/opencode.jsonc`, que já define `cheapestinception` (sem hífen) com os mesmos `deepseek-v4-flash` + `mimo-v2.5` **e** com variantes low/high/max; agora o TUI mostra **um único** DeepSeek e **um único** MiMo no Cheapest Inference. 8 literais + pin da constante em `tests/unit/worktree.unit.spec.ts` atualizados; textos em `scripts/worktree.mjs` (docblock/help — o help agora interpola `OPENCODE_PRESET_MODEL`, importado), `.agents/shell/worktree.sh` e `worktree-next-issue/SKILL.md` sincronizados; o help deixou de duplicar o literal. Anti-goals preservados: cardápio de execução de Issues intocado (`work-issue.md`/`plan-issue.md` + pin `opencodeCommands.unit.spec.ts` seguem com o provider direto), config global do humano intocada, docs históricos congelados, usos diretos `deepSeek('deepseek-v4-flash')` no app (Sollinha/`campaignDemandTitle.ts`) intactos. Verificação: `pnpm gate:fast` verde; `opencode debug config` confirma variantes do 0731 ativas e `cheapest-inference` do repo ausente; PR em main com auto-merge. diff --git a/docs/plans/expor-variantes-deepseek-v4-flash-0731-impl.md b/docs/plans/expor-variantes-deepseek-v4-flash-0731-impl.md new file mode 100644 index 00000000..099e589b --- /dev/null +++ b/docs/plans/expor-variantes-deepseek-v4-flash-0731-impl.md @@ -0,0 +1,108 @@ +# Impl: Default de launch do worktree passa a usar o DeepSeek V4 Flash 0731 (mais barato) + limpeza de duplicatas do Cheapest Inference + +Status: aprovado +Atualizado em: 2026-08-23 +Issue: #817 +Intenção: docs/plans/expor-variantes-deepseek-v4-flash-0731.md +Appetite restante: ~0,5–1 dia (config-only; sem migration, sem code além da constante e do bloco de config) + +## Leitura da intenção + +- **Outcome:** (1) a variante `max` do DeepSeek V4 Flash 0731 fica selecionável no TUI; (2) o preset de launch do worktree emite `--model vercel/deepseek/deepseek-v4-flash-0731` (input $0.076/M vs $0.13/M do `-flash`); (3) o TUI mostra um único DeepSeek e um único MiMo no Cheapest Inference (sem duplicatas). Verificável por `opencode debug config` (override com variantes no model key 0731), pela linha de launch emitida + literais/pin dos testes unit, e abrindo o seletor de modelo no TUI. +- **O que NÃO negociar:** anti-goals da intenção — NÃO mudar o cardápio de execução de Issues (`work-issue.md`/`plan-issue.md` nem o pin de `opencodeCommands.unit.spec.ts`); NÃO tocar no `~/.config/opencode/opencode.jsonc` global do humano (o provider `cheapestinception` global, sem hífen, é a fonte dele e carrega os models); NÃO editar docs históricos (`docs/plans`, `docs/CHANGELOG-AGENTS.md`, `docs/changelog/*`). Não tocar nos usos diretos do provider `deepseek/deepseek-v4-flash` fora do launch do worktree (`src/utilities/ai/campaignDemandTitle.ts`, `src/app/(campaign)/campanha/api/ai-chat/route.ts` — uso direto legítimo, fora de escopo). +- **O que reavaliar:** a hipótese de "Direção no codebase" da intenção estava errada em um ponto verificado ao vivo: o bloco `cheapest-inference` (com hífen) do `opencode.json` do repo ocupa as linhas **11–70** (fecha em `},` na 70), e não 11–33 como o plano dizia — e hoje ele **já define variantes** low/high/max nos models. O bloco inteiro (11–70) é o que sai. Reavaliado também: o default dentro do bloco repo é `deepseek-v4-flash` (sem namespace) e o `vercel.models` atual vive nas linhas 71–87; o sibling 0731 entra ao lado do `deepseek/deepseek-v4-flash` existente (73–85). O restante da direção da intenção confirmou-se exato após leitura dos arquivos. + +## Abordagem recomendada + +```mermaid +flowchart LR + P["pnpm worktree next/plan/new"] --> L["scripts/lib/worktree.mjs:28\nOPENCODE_PRESET_MODEL = vercel/deepseek/deepseek-v4-flash-0731"] + L --> D["opencodeLaunchDirective --model vercel/deepseek/deepseek-v4-flash-0731"] + D --> T["TUI opencode"] + C["opencode.json\nprovider.vercel.models + sibling 0731\n(low/high/max) · provider.cheapest-inference REMOVIDO (11-70)"] --> T + G["~/.config/opencode/opencode.jsonc\ncheapestinception (global, single source das variantes)"] --> T + T -->|"max sticky (model.json)"| V["Vercel AI Gateway\ndeepseek-v4-flash-0731 · $0.076/M input"] +``` + +**Opções consideradas (Parte 1 — variantes do 0731):** A | B | C +**Recomendação:** A — expor as **três** variantes low/high/max no override `provider.vercel.models["deepseek/deepseek-v4-flash-0731"]`, espelhando exatamente o bloco do sibling `deepseek/deepseek-v4-flash` (OPS78). O model key existe no catálogo cache (input $0.076/M) mas com `reasoning_options` vazio — sem o override as variantes não ficam selecionáveis no TUI. Espelhar o bloco mantém o mecanismo uniforme entre os dois keys Vercel, sem custo extra e sem inventar formato. +**Rejeitadas:** + +- **B — expor só `max`:** assimétrico com o sibling `-flash` que já expõe low/high/max; o custo de listar as três no mesmo bloco é zero, e só-`max` quebra a uniformidade do mecanismo de variantes que a OPS78 estabeleceu. +- **C — não fazer override (confiar no catálogo):** o `reasoning_options` do 0731 no cache é vazio — as variantes simplesmente não aparecem no TUI; falha o outcome 1. + +**Opções consideradas (Parte 3 — remoção do duplicado):** A | B | C +**Recomendação:** A — remover o bloco `cheapest-inference` inteiro (linhas 11–70) do `opencode.json` do repo. O global `~/.config/opencode/opencode.jsonc` já define `cheapestinception` (sem hífen) com os mesmos `deepseek-v4-flash` + `mimo-v2.5` **e com variantes** — é o owner da preocupação e fica como fonte única; o bloco repo é um twin com variantes redundantes. Depois da remoção, `provider` do repo fica só com `vercel`. +**Rejeitadas:** + +- **B — remover o global e manter o do repo:** anti-goal explícito da intenção; o global do humano é intocado (carrega a config de auth do provider). +- **C — manter ambos, sincronizando variantes:** viola engineering-standards ("edit the owner, don't twin") e mantém os dois DeepSeek + dois MiMo no TUI — falha o outcome 3. + +**Decisão única (Parte 2 — swap do preset), textos inclusos:** A) trocar a constante `OPENCODE_PRESET_MODEL` E todos os pontos que duplicam o literal (lista verificada abaixo) na mesma mudança | B) trocar só a constante e deixar os textos. **Recomendação: A** — o literal duplicado em `scripts/worktree.mjs`, `.agents/shell/worktree.sh` e `.agents/skills/worktree-next-issue/SKILL.md` descreve o modelo de launch; ficarem desatualizados mentem pro dev e para a documentação do skill. **Rejeitada: B** porque deixa o repo inconsistentemente apontando para o modelo caro em 4 lugares. + +### Componentes / mudanças + +- **`opencode.json`** (`:11-70`): **remover inteiro** o bloco `provider.cheapest-inference` (com hífen): `deepseek-v4-flash` + `mimo-v2.5`. **`opencode.json`** (`:71-87`, dentro de `provider.vercel.models`): adicionar como sibling após `deepseek/deepseek-v4-flash` (73–85): + ```json + "deepseek/deepseek-v4-flash-0731": { + "variants": { + "low": { "reasoningEffort": "low" }, + "high": { "reasoningEffort": "high" }, + "max": { "reasoningEffort": "max" } + } + } + ``` + (mesmo formato exato do fix de variantes da OPS78; o provider `vercel` é built-in/models.dev — sem npm/options adicionais.) +- **`OPENCODE_PRESET_MODEL`** (`scripts/lib/worktree.mjs:28`): valor → `'vercel/deepseek/deepseek-v4-flash-0731'`; atualizar o comentário `:22-27` (mencionar que o preset é o 0731, variante mais barata no gateway, variantes low/high/max no override). É a fonte única da string (OPS26), emitida em `opencodeLaunchDirective` — nenhum outro código lê o literal. +- **`tests/unit/worktree.unit.spec.ts`** (`:151,159,165,171,177,180,185,196`): atualizar os 8 literais da linha de launch para `vercel/deepseek/deepseek-v4-flash-0731`; `:185` pina a própria constante (`expect(OPENCODE_PRESET_MODEL).toBe(...)`) — trocar o literal esperado. **NÃO** mexer em `tests/unit/opencodeCommands.unit.spec.ts` (pina o frontmatter dos comandos de execução — anti-goal). +- **Textos (string atualizada, sentido OPS26 preservado):** + - `scripts/worktree.mjs:27` (docblock) e `:779` (help `console.log`) + - `.agents/shell/worktree.sh:10` (comentário header) + - `.agents/skills/worktree-next-issue/SKILL.md:34` (prosa do skill sobre a diretiva de launch) +- **Migration:** sem migration — configuração/tooling apenas; nenhum schema de Payload muda. +- **Access / Consent:** N/A — não há collection nem dado de cidadão envolvido; não afeta login de campanha nem LGPD. +- **UI:** N/A — TUI do opencode é consumidor, não produto deste plano. + +### Dados → forma (se aplicável) + +N/A — tooling. Não há dado de negócio sendo modelado: são uma constante de config + remoção/acréscimo de bloco no `opencode.json`. (Perguntas de data-presentation não se aplicam.) + +## Fases verificáveis + +1. **Config (`opencode.json`)** — remover `provider.cheapest-inference` (11–70); adicionar o sibling 0731 com variantes em `provider.vercel.models`. Verificar: `node -e` para garantir JSON válido e `opencode debug config` mostrando SO o provider `vercel` (repo) + override de variantes no model key 0731. Quota: meia fase. +2. **Preset + literais + testes** — `scripts/lib/worktree.mjs:22-28` (constante + comentário); `tests/unit/worktree.unit.spec.ts` (8 literais: `:151,159,165,171,177,180,185,196`); textos `scripts/worktree.mjs:27,:779`, `.agents/shell/worktree.sh:10`, `.agents/skills/worktree-next-issue/SKILL.md:34`. Quota: meia fase. +3. **Gates** — `pnpm test:unit` (foco em `worktree.unit.spec.ts`); `pnpm gate:fast` (guards → lint → format → typecheck → knip → cycles → test:unit) verde; validação manual: `opencode run --model vercel/deepseek/deepseek-v4-flash-0731 --variant max` responde; launch real de teste (`pnpm worktree next` em branch descartável) emite `--model vercel/deepseek/deepseek-v4-flash-0731`; push via `pnpm push` → CI PR (checks). + +## Rabbit holes / Não escopo (engenharia) + +- NÃO mudar `.opencode/commands/work-issue.md` / `plan-issue.md` (frontmatter `model:`) nem o pin `tests/unit/opencodeCommands.unit.spec.ts` — cardápio de execução de Issues intacto (anti-goal). +- NÃO tocar no `~/.config/opencode/opencode.jsonc` do humano — o provider `cheapestinception` global é fonte de auth e models; a remoção é só no repo. +- NÃO editar `docs/CHANGELOG-AGENTS.md`, `docs/changelog/*` nem planos históricos — congelados (a entrada de changelog desta entrega é criada depois, no fluxo padrão `docs/changelog/-.md` + `pnpm changelog:build`). +- NÃO renomear o provider global nem "unify" os dois `cheapestinception`/`cheapest-inference` fora da remoção do bloco repo — o gap de nome é de produto e já está resolvido pela remoção. +- NÃO forçar a variante via `model.agent`/default (mesma rejeição da OPS78): variante fica selecionável e sticky no TUI, não imposta. +- NÃO inventar flag `--variant` na linha de launch nem sufixo `:max` no `--model` (TUI não suporta; formato estritamente `provider/model`). +- Não escopo: outros model keys de outros providers, inventário de catálogo, usos diretos de `deepseek/deepseek-v4-flash` no app (`campaignDemandTitle.ts`, `ai-chat/route.ts`). + +## Riscos e mitigação + +- **`opencode.json` inválido após a edição (hard-fail config do opencode)** → mitigação: paridade tipo JSON checada com `node -e "JSON.parse(...)"` antes de commit, e `opencode debug config` rodado na Fase 1; se o loader validar o bloco de models, a dúvida morre ali antes da Fase 3. +- **Sticky variant preso ao model key antigo (`~/.local/state/opencode/model.json`)** → trocar o preset para o 0731: a variante persistida por model key não migra sozinha; na primeira sessão o dev cicla a variante uma vez (keybind variant_cycle) e ela fica sticky no novo key. Não é ação do plano (comportamento upstream), só documentar no PR. +- **Duplicate literais fora da lista verificada** → mitigação: o swap de Fase 2 cobre o conjunto verificado (8 testes + 4 textos + constante); testes unit pínam a linha de launch e `:185` pina a constante — deriva volta na CI. Rodar `rg "vercel/deepseek/deepseek-v4-flash-0731"` pós-mudança para conferir o agreeing, e `rg "deepseek-v4-flash"` para confirmar que só ficaram os usos diretos intencionais (frontmatter dos comandos + app). +- **Remover `cheapest-inference` e o global não cobrir algo (ex.: npm do provider)** → caminho do explorer verificado: o global já define `cheapestinception` com os mesmos `deepseek-v4-flash` + `mimo-v2.5` com variantes. Mitigação: confirmar no `opencode debug config` que os models globais continuam listados depois da remoção; reverter a remoção é um `git checkout` de um bloco isolado. + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção coberto: variante `max` (e low/high) do 0731 selecionável no TUI via override; preset emite `--model vercel/deepseek/deepseek-v4-flash-0731`; TUI mostra um único DeepSeek e um único MiMo no Cheapest Inference após remoção do bloco repo; verificável por `opencode debug config`, linha de launch + testes unit atualizados. +- [ ] Anti-goals preservados: `work-issue.md`/`plan-issue.md` e `opencodeCommands.unit.spec.ts` intactos; `~/.config/opencode/opencode.jsonc` intacto; docs históricos intactos; usos diretos `deepseek-v4-flash` do app intactos; config-only (sem migration, sem code além da constante). +- [ ] Invariantes AGENTS/engineering-standards: constante single-source mantida (OPS26), provider duplicado removido (owner global fica como fonte única — sem twin), testes de domínio atualizados onde o literal muda. +- [ ] `pnpm test:unit` verde (spec `worktree.unit.spec.ts`) e `pnpm gate:fast` verde; `opencode debug config` validado antes de marcar aprovado/executar. + +## Self-score decision-quality + +1. **Decisões caras têm rejeitadas?** Sim — Parte 1 (A/B/C com C falhando o outcome) e Parte 3 (A/B/C, com B sendo anti-goal), Parte 2 decidida em A vs B com rejeitada registrada. +2. **Abordagem cabe no appetite da intenção?** Sim — config-only, duas edições em `opencode.json` + swap mecânico de literais; ~0,5–1 dia mantido. +3. **Rabbit holes nomeados?** Sim — cardápio de execução, global do humano, docs históricos, renome/unificação de providers, variante forçada, flag inexistente. +4. **Depth check: reusa shells/helpers existentes?** Sim — nenhum helper novo; espelha o override da OPS78 e reusa a constante single-source OPS26 + os pins de teste existentes. +5. **Intenção (aceite de produto) permanece satisfeita?** Sim — a engenharia não reescreveu o outcome; a única divergência da intenção (extensão real do bloco 11–70 vs 11–33) não muda o resultado, só o escopo exato da remoção. + +**Score: 5/5** diff --git a/opencode.json b/opencode.json index 1d4cc798..b561d404 100644 --- a/opencode.json +++ b/opencode.json @@ -8,52 +8,9 @@ } }, "provider": { - "cheapest-inference": { - "npm": "@ai-sdk/openai-compatible", - "name": "Cheapest Inference", - "options": { - "baseURL": "https://api.cheapestinference.com/v1" - }, + "vercel": { "models": { - "deepseek-v4-flash": { - "name": "DeepSeek V4 Flash", - "reasoning": true, - "interleaved": { - "field": "reasoning_content" - }, - "options": { - "thinking": { - "type": "enabled" - } - }, - "limit": { - "context": 1000000, - "output": 384000 - }, - "variants": { - "low": { - "reasoningEffort": "low" - }, - "high": { - "reasoningEffort": "high" - }, - "max": { - "reasoningEffort": "max", - "textVerbosity": "low", - "reasoningSummary": "auto" - } - } - }, - "mimo-v2.5": { - "name": "MiMo v2.5", - "reasoning": true, - "interleaved": { - "field": "reasoning_content" - }, - "limit": { - "context": 1000000, - "output": 128000 - }, + "deepseek/deepseek-v4-flash": { "variants": { "low": { "reasoningEffort": "low" @@ -65,12 +22,8 @@ "reasoningEffort": "max" } } - } - } - }, - "vercel": { - "models": { - "deepseek/deepseek-v4-flash": { + }, + "deepseek/deepseek-v4-flash-0731": { "variants": { "low": { "reasoningEffort": "low" diff --git a/scripts/lib/worktree.mjs b/scripts/lib/worktree.mjs index db67fd56..042cefba 100644 --- a/scripts/lib/worktree.mjs +++ b/scripts/lib/worktree.mjs @@ -22,10 +22,11 @@ export const WORKTREE_TERMINAL_ENV = 'TEQO_WORKTREE_TERMINAL' /** * Preset model for the opencode launch — change the preset by editing this * constant. OPS78: routes through the Vercel AI Gateway (cache/stability/ - * failover) instead of the direct provider; the low/high/max variants are + * failover) instead of the direct provider. OPS82: the preset is the cheaper + * `-0731` flavor (input $0.076/M vs $0.13/M); the low/high/max variants are * exposed by the `provider.vercel` override in `opencode.json`. */ -export const OPENCODE_PRESET_MODEL = 'vercel/deepseek/deepseek-v4-flash' +export const OPENCODE_PRESET_MODEL = 'vercel/deepseek/deepseek-v4-flash-0731' /** * Skill command sent as the launch's initial message per purpose. `next` diff --git a/scripts/worktree.mjs b/scripts/worktree.mjs index 1ec72ef1..97eec544 100644 --- a/scripts/worktree.mjs +++ b/scripts/worktree.mjs @@ -24,7 +24,7 @@ * Chamado do terminal interativo (com * `TEQO_WORKTREE_TERMINAL=1`, que só a função shell * seta), imprime também a diretiva `launch - * opencode --model vercel/deepseek/deepseek-v4-flash + * opencode --model vercel/deepseek/deepseek-v4-flash-0731 * --auto --prompt "/work-issue --issue "` ANTES * do `cd` — a função shell executa o cd e então a * linha, e o TUI do opencode abre no worktree com @@ -135,6 +135,7 @@ import { } from './lib/worktree-env.mjs' import { branchNameForIssue, + OPENCODE_PRESET_MODEL, opencodeLaunchDirective, planBranchName, workBranchName, @@ -775,9 +776,7 @@ if (!subcommand) { console.log(' opencode command, ou a função `worktree()` de .agents/shell/worktree.sh);') console.log(' no terminal (TEQO_WORKTREE_TERMINAL=1) imprime também a diretiva') console.log(' `launch opencode … --prompt "/work-issue --issue "` (OPS26+OPS33:') - console.log( - ' abre o TUI com vercel/deepseek/deepseek-v4-flash (gateway) + auto + a Issue claimada já', - ) + console.log(` abre o TUI com ${OPENCODE_PRESET_MODEL} (gateway) + auto + a Issue claimada já`) console.log(' informada); --stay suprime cd e launch (o claim ainda acontece); --go') console.log(' explícito continua aceito como no-op; --no-migrate pula migrations e o') console.log(' seed mínimo (db:seed:minimal) nos bancos novos (OPS28: paridade com a CI)') diff --git a/tests/unit/worktree.unit.spec.ts b/tests/unit/worktree.unit.spec.ts index dac9351f..474c38c0 100644 --- a/tests/unit/worktree.unit.spec.ts +++ b/tests/unit/worktree.unit.spec.ts @@ -148,7 +148,7 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26 + OPS33) it('next launches with the preset model, --auto and the /work-issue command sent (value always quoted)', () => { expect(opencodeLaunchDirective({ dir, purpose: 'next', terminal: true })).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto --prompt "/work-issue"`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto --prompt "/work-issue"`, ) }) @@ -156,33 +156,33 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26 + OPS33) expect( opencodeLaunchDirective({ dir, purpose: 'next', terminal: true, issueNumber: 595 }), ).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto --prompt "/work-issue --issue 595"`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto --prompt "/work-issue --issue 595"`, ) }) it('plan launches with the same presets and /plan-issue sent (OPS31, value quoted)', () => { expect(opencodeLaunchDirective({ dir, purpose: 'plan', terminal: true })).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto --prompt "/plan-issue"`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto --prompt "/plan-issue"`, ) }) it('new launches prompt-less too — "apenas conversar", no skill sent', () => { expect(opencodeLaunchDirective({ dir, purpose: 'new', terminal: true })).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto`, ) }) it('plan/new ignore the issueNumber — only next carries the claimed issue', () => { expect(opencodeLaunchDirective({ dir, purpose: 'plan', terminal: true, issueNumber: 7 })).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto --prompt "/plan-issue"`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto --prompt "/plan-issue"`, ) expect(opencodeLaunchDirective({ dir, purpose: 'new', terminal: true, issueNumber: 7 })).toBe( - `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash --auto`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto`, ) }) it('pins the preset constants — changing the model is editing a constant', () => { - expect(OPENCODE_PRESET_MODEL).toBe('vercel/deepseek/deepseek-v4-flash') + expect(OPENCODE_PRESET_MODEL).toBe('vercel/deepseek/deepseek-v4-flash-0731') expect(WORKTREE_TERMINAL_ENV).toBe('TEQO_WORKTREE_TERMINAL') expect(OPENCODE_SKILL_COMMAND_BY_PURPOSE).toEqual({ next: '/work-issue', @@ -193,7 +193,7 @@ describe('opencodeLaunchDirective (terminal-only opencode launch, OPS26 + OPS33) 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 vercel/deepseek/deepseek-v4-flash --auto`, + `launch opencode ${dir} --model vercel/deepseek/deepseek-v4-flash-0731 --auto`, ) }) })