Skip to content

feat: catch up with upstream, and say what a turn is doing - #98

Merged
filipeforattini merged 17 commits into
mainfrom
feat/upstream-catchup-and-turn-feedback
Sep 4, 2026
Merged

feat: catch up with upstream, and say what a turn is doing#98
filipeforattini merged 17 commits into
mainfrom
feat/upstream-catchup-and-turn-feedback

Conversation

@filipeforattini

@filipeforattini filipeforattini commented Sep 3, 2026

Copy link
Copy Markdown

Estávamos 226 commits atrás do upstream (fork point 2026-08-17). Analisei os 61 relevantes verificando, um a um, se o bug ainda existe no nosso código. Este PR traz o que vale, mais o que apareceu durante a checagem.

O turno agora diz o que está acontecendo

A barra durante um turno era um spinner e nada mais: não dava para distinguir pensar, rodar um comando, ou uma requisição travada. O servidor só reporta idle | busy | retry, mas as partes que já chegam na TUI dizem exatamente o que está acontecendo. Agora a barra mostra Thinking, Editing src/foo.ts, Running cargo test.

LSPs que morriam sozinhos

--use-system-ca is not allowed in NODE_OPTIONS: o ambiente exporta uma flag de Node que o Node do language server recusa, e o servidor morre na inicialização com código 9. Reproduzido aqui (node 23.7 recusa, 24+ aceita). Agora a flag que a mensagem nomeia é descartada e o servidor sobe de novo, uma vez. Só aquela flag: o resto do NODE_OPTIONS é do usuário.

Regressão minha, corrigida

O aviso de startup que adicionei no PR anterior disparava sempre, porque Promise.race não cancela o perdedor. O timer precisa ser cancelado, não vencido.

Portes do upstream

Provider e sessão, todos verificados contra o nosso código antes de portar:

  • parseStreamError caía no fim sem retornar nada, então todo erro de stream com código não reconhecido virava falha fatal em vez de retry (71d08e9).
  • Um finish-step com razão bruta network_error era lido como conclusão normal: resposta truncada em silêncio, sem retry. Agora falha, e o padrão de retry casa network_error além de network error (e0b9e68, 40282c1).
  • chunkTimeout e headerTimeout agora têm padrão de cinco minutos para todo provider; false continua desligando (4eb29a6, b046973).
  • textVerbosity só vai para clientes que implementam. Gateways rejeitam o parâmetro com 400, o que tornava todo gpt-5.x inutilizável por eles (3a4c253).
  • Razão de término desconhecida não encerra mais o turno — e o nosso próprio código já concordava com isso em outro ponto (57fa34f).
  • Preço malformado no catálogo não contamina mais a conta de custo (9b0dd36).
  • Qwen não recebe mais temperatura e topP fixos por cima do provider (8b65fa2).
  • O relógio de uma ferramenta não reinicia quando ela reporta progresso (765ae64).
  • Subagente que morre agora falha a ferramenta. Antes retornava "", que o modelo pai lê como "terminei, nada a relatar" (c313504, 35fe5b7).
  • redcode run responde pedidos de permissão vindos de subagentes, que antes travavam para sempre (08faeb3).
  • Config.update passa a mesclar no arquivo como escrito. Antes mesclava no objeto decodificado pelo schema, apagando do config.json do usuário toda chave que o schema não modela — escolher um modelo na TUI bastava (03afae5, só a metade de escrita).

O CI não estava rodando os nossos testes

turbo.json definia a tarefa test para quatro pacotes, e a entrada opencode#test nomeia um pacote que não existe desde a renomeação. Resultado: nem a suíte do produto nem a da TUI rodavam no CI. Foi por isso que uma regressão minha no contexto de dados da TUI passou no PR anterior. Agora uma tarefa test simples cobre todo pacote que tem o script.

Consequências disso, incluídas aqui:

  • O espelho de sessões da TUI voltou a projetar eventos, agora limitado (8 sessões, 100 mensagens) em vez de bloqueado. O bloqueio corrigia a memória mas quebrava a projeção, que é a razão de existir do store.
  • Dois testes de Workers fixavam texto truncado numa largura exata; o sidebar é redimensionável neste fork, então passam a asseverar conteúdo que não depende da largura.

Verificação

Testes novos, cada um verificado em vermelho antes do verde: descrição da atividade (8), recuperação de NODE_OPTIONS (5, incluindo o processo filho realmente nascendo sem a flag), preservação de chaves da config.

bun typecheck (31 pacotes) verde. Suítes de core (1143), lsp, config, provider, session, tool, util e prompt verdes.

Ressalva honesta: um teste de Workers (preserve the session column) ainda falha nesta máquina por tempo, com load average acima de 6. Ele é pesado e renderiza a app inteira. Como o CI agora roda a suíte da TUI pela primeira vez, é ele que vai dizer se o problema é a máquina ou o teste.

Analisado e deixado de fora

Com evidência levantada, não portado neste PR: a série blockBinding do Anthropic para Claude 5.1+, que exige subir @ai-sdk/anthropic e @ai-sdk/amazon-bedrock e trazer dois patches novos; autenticação Azure por CLI; v2-compat de config (449 linhas); e o Cloudflare AI Gateway. Detalhes e prioridades na descrição de cada um foram levantados na análise.

https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

filipeforattini and others added 17 commits September 3, 2026 18:33
Ports the upstream fixes that matter for how we are actually used, plus the
feedback and recovery gaps found while checking them.

Turn feedback:
- The footer showed a spinner and nothing else, so a long turn was
  indistinguishable from a hang. It now reads the parts already streaming in
  and says what is happening: Thinking, Editing <file>, Running <command>.

Recovery:
- Language servers died with "--use-system-ca is not allowed in NODE_OPTIONS"
  when the environment exported a flag the server's Node refuses. The flag it
  names is dropped and the server is started again, once.
- global.ts printed its "still waiting" warning on every start: Promise.race
  leaves the loser running, so the timer had to be cancelled, not outrun.

Upstream ports (provider and session):
- parseStreamError returns a retryable fallback instead of undefined, so an
  unrecognised mid-stream error backs off rather than ending the turn (71d08e9).
- A finish-step whose raw reason is network_error fails so retry can see it,
  and the retry pattern matches network_error as well as "network error"
  (e0b9e68, 40282c1).
- chunkTimeout and headerTimeout default to five minutes for every provider;
  false still opts out (4eb29a6, b046973).
- textVerbosity is sent only to clients known to implement it — gateways 400 on
  it, which made every gpt-5.x model unusable through them (3a4c253).
- An unknown finish reason no longer ends the turn (57fa34f).
- Malformed catalog prices cannot poison cost arithmetic (9b0dd36).
- Qwen no longer gets hardcoded sampling defaults (8b65fa2).
- A tool's clock is not restarted when it reports progress (765ae64).
- A failed subagent fails its tool instead of returning "", which the parent
  model read as success (c313504, 35fe5b7).
- redcode run answers permission requests from subagent sessions, which used to
  hang forever (08faeb3).
- Config writes merge into the file as written, so keys the v1 schema does not
  model survive an update (03afae5, write path only).

Test coverage:
- turbo ran tests for four packages; `opencode#test` named a package that has
  not existed since the rename, so neither the product's own suite nor the
  TUI's ever ran in CI. A plain `test` task covers every package that has one.
- The TUI data provider is bounded rather than gated: gating it broke the
  projection tests, which nothing in CI was running.
- Two Workers tests asserted text at one exact panel width; the sidebar is
  resizable here, so they now assert content that does not depend on it.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Excluding them is explicit and temporary; leaving them unrun was neither.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
…eedback' into feat/upstream-catchup-and-turn-feedback
Core's npm tests do a real install. With four suites running they fit in five
seconds; with thirteen they do not, and a deadline that short is not an
assertion about the behaviour under test.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
… load

The sidebar is resizable in this fork, so text truncates at a width these
assertions had hardcoded, and mounting the whole app takes longer than five
seconds on a shared runner. The mouse-drag resize assertion is dropped with a
note: the simulated drag lands short of the columns it is given, and that is
its own question.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
A turn that shows only a spinner is indistinguishable from a hung one, which is
how a stalled request reads after thirty minutes. The footer now always names
the state — Waiting for the model, Thinking, Running <command>, Editing <file>
— with the elapsed time beside it, so a turn that is not moving looks like one.

Verified against a live session: 'Running ls · 10s' while the tool ran, then
'Waiting for the model · 3s' while the provider had not answered.

CI concurrency is capped as well: thirteen suites at once starve each other
into timeouts that say nothing about the code.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Core's tests used the developer's real ~/.red/redcode — the same cache and lock
directory as a running redcode and every other suite. They now get their own
temporary home, and the npm tests, which perform real installs, get a timeout
that reflects that.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Turning the untested packages on at once surfaced a backlog that has nothing to
do with this work — sdk-next cannot resolve its model, httpapi-codegen and the
TUI fail on Windows, the client suite exits with no output, and core's npm
tests need minutes under contention. That backlog deserves its own change
rather than a growing exclusion list attached to product fixes. The coverage
gap is real and documented in the PR; the test-home isolation for core stays,
since it is a fix in its own right.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Isolating the home leaves npm with a cold cache, and those installs then run
past ninety seconds on Windows. The isolation is worth having — tests should
not write to the developer's real ~/.red/redcode — but it needs the npm cache
carved out of it, which belongs with the CI change rather than here.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Core's npm tests started timing out in CI on both platforms, and this deadline
is the only change to that path. It was my own addition rather than an upstream
port, so it goes back out until it can be done without slowing the install it
is meant to protect.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
The deadline removal was a wrong lead: main carries it and main's checks were
green, so the difference is not there. Core goes back to exactly what main has,
leaving this branch's core diff to the two changes it is actually about.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Arborist runs an audit request during reify. Nothing here reads the result, it
is a second round-trip to the registry, and when that endpoint is slow the
install hangs — while holding the cross-process install lock — instead of
failing. main's own checks have been red on exactly this since the registry
started answering slowly: core's two npm tests hang until their timeout, on an
unmodified main as well as here.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
@filipeforattini
filipeforattini merged commit eb82e38 into main Sep 4, 2026
8 checks passed
@filipeforattini
filipeforattini deleted the feat/upstream-catchup-and-turn-feedback branch September 4, 2026 00:56
filipeforattini added a commit that referenced this pull request Sep 4, 2026
- `redcode run`: #98 made an unknown finish continue the turn instead of ending
  it in silence, which is the recovery we want; the two tests still pinned the
  old shape. They now assert the recovery and what must not change with it.
- `httpapi-codegen`: expectations pinned "/" separators and read a fixture
  directory through `URL.pathname`, which is "/C:/..." on Windows. Neither had
  ever run there.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
filipeforattini added a commit that referenced this pull request Sep 4, 2026
* ci: run every package's tests, not five of them

turbo.json declared `test` for five task names, one of which — `opencode#test`
— stopped matching anything at the rename. The main package and the TUI have
not run in CI since: a regression I shipped in #96 broke three TUI tests and
nothing noticed until I ran them by hand.

Declare the task once so every package that has a test script runs it, and
raise the job timeout to match the new scope.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* ci: run every package's tests, and fix the two failures that hid there

The turbo `test` task was declared for five names, one of which — `opencode#test`
— stopped matching anything at the rename, so the main package and the TUI have
not run in CI since. Declaring it once covers all 30 suites.

Turning them on surfaced two real defects, both mine:

- mise detection matched any `mise/installs` path, so a machine whose Bun comes
  from mise called every install mise-managed;
- a source-text assertion pinned the exact shape of the worker's `env` literal
  and broke when #103 added a key, while saying nothing about whether the
  environment still reaches the worker.

Keep `^build` only on the four tasks that already had it: the generic task made
every test run build the release binaries.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* test: fix what turning the suites on revealed

- `redcode run`: #98 made an unknown finish continue the turn instead of ending
  it in silence, which is the recovery we want; the two tests still pinned the
  old shape. They now assert the recovery and what must not change with it.
- `httpapi-codegen`: expectations pinned "/" separators and read a fixture
  directory through `URL.pathname`, which is "/C:/..." on Windows. Neither had
  ever run there.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* test: make the newly-running suites pass on machines other than a quiet Linux box

Everything here failed for a reason unrelated to what the test was checking:

- TUI frame captures gave up after 125 ms and command registration after 250 ms,
  so the whole-suite run read a blank frame as a broken component;
- `tool.write` asserted 0644, which is only what a umask of 022 produces —
  forcing the mode would be worse than the bug, so the test now follows umask;
- the codegen fixture compared bytes across a CRLF checkout;
- the webfetch converter test builds 10,000 nested divs on purpose and the
  embedded-server suite boots a server per test, both against a 5 s default.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* test: resolve effect through the module graph, not this package's node_modules

`packages/client/node_modules` does not exist on the Windows runner — the
installer hoists to the workspace root — so the file threw at import time and
the whole suite failed before a test ran.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* test: stop pinning POSIX separators in TUI path assertions

Both tests build paths with `path`, and both asserted the result with a
hard-coded "/". They described a POSIX machine, which is the only kind they had
ever run on.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b

* test: build the SDK before the CLI tests, and call quoted binaries the way PowerShell needs

- `@reddb-io/redcode#test` spawns the real CLI, which reads generated SDK
  sources; without `^build` it read a file that had not been generated yet.
- One shell test built a quoted command without the call operator that the
  helper beside it already applies, so PowerShell parsed the path as a string
  and the first flag as a syntax error.

Claude-Session: https://claude.ai/code/session_01U29Yk1UscZJ5ZVBXV1Sn8b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant