feat: catch up with upstream, and say what a turn is doing - #98
Merged
Conversation
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
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 mostraThinking,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 doNODE_OPTIONSé do usuário.Regressão minha, corrigida
O aviso de startup que adicionei no PR anterior disparava sempre, porque
Promise.racenã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:
parseStreamErrorcaí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).finish-stepcom razão brutanetwork_errorera lido como conclusão normal: resposta truncada em silêncio, sem retry. Agora falha, e o padrão de retry casanetwork_erroralém denetwork error(e0b9e68, 40282c1).chunkTimeouteheaderTimeoutagora têm padrão de cinco minutos para todo provider;falsecontinua desligando (4eb29a6, b046973).textVerbositysó vai para clientes que implementam. Gateways rejeitam o parâmetro com 400, o que tornava todogpt-5.xinutilizável por eles (3a4c253)."", que o modelo pai lê como "terminei, nada a relatar" (c313504, 35fe5b7).redcode runresponde pedidos de permissão vindos de subagentes, que antes travavam para sempre (08faeb3).Config.updatepassa a mesclar no arquivo como escrito. Antes mesclava no objeto decodificado pelo schema, apagando doconfig.jsondo 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.jsondefinia a tarefatestpara quatro pacotes, e a entradaopencode#testnomeia 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 tarefatestsimples cobre todo pacote que tem o script.Consequências disso, incluídas aqui:
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
blockBindingdo Anthropic para Claude 5.1+, que exige subir@ai-sdk/anthropice@ai-sdk/amazon-bedrocke trazer dois patches novos; autenticação Azure por CLI;v2-compatde 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
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.