Triage
Confirmed on main at 5a61f50cc. This is not a duplicate of #5629, #3550, or #8280. Those share the "Provider still needs an update" toast, but the update command there did not actually update the binary T3 Code later probed. Here opencode upgrade is the right command, it exits 0, and opencode --version is already the new build. Only the version used for verification is stale.
For a local OpenCode install (Server URL empty), post-update verification calls refreshInstance, which runs checkOpenCodeProviderStatus:
opencode --versionstarts a new process and parses the on-disk version (OpenCodeProvider.tsaround the--versionprobe).- Inventory is loaded through
OpenCodeServerOwner.withServer. If the owned child is still running, that handle is reused. It is only closed afterOPENCODE_SERVER_IDLE_TTL("30 seconds") once nothing is borrowing it, and the verification probe itself is a borrower, so it resets that timer. A live turn keeps the old process until the turn ends and the idle timeout fires (OpenCodeServerOwner.ts). - The handle's
versionis whatever/global/healthreturned when that process was spawned (verifyOpenCodeServerVersioninstartOpenCodeServerProcess). It is not read again on the next borrow.opencode upgradereplaces the binary; the running process keeps the old code. - On a successful inventory, that startup version overwrites the CLI probe (
version = inventoryExit.value.version). The snapshot,~/.t3/caches/opencode.json, and the version advisory all follow the old server.behind_latestmakesProviderMaintenanceRunnerrecordstatus: "unchanged", and the client shows "Provider still needs an update" / "OpenCode still appears outdated."
That overwrite came from #8480, which started reporting the serving process instead of the CLI. That matches an external server, and it matches a local server at spawn time. It is wrong for the window after a successful upgrade of a T3-owned helper.
The native update path is doing the right thing on this install. isOpenCodeNativeCommandPath matches a real path under ~/.opencode/bin/opencode, and the command that runs is the resolved binary plus upgrade (OpenCodeDriver.ts). The cached ~/.local/bin/opencode upgrade is that branch, not the old npm guess. #9325 (which closed #5629) only selects the installer that owns the binary. It does not restart or ignore a stale owned opencode serve. #3550 was closed not_planned on a Codex/Bun hypothesis. #8280 was a Claude/Homebrew hang where the suggested upgrade was already a no-op. None of those change this path.
The reported self-heal matches the owner: once the old child is idle long enough to be replaced, the next health check is the new binary and the advisory clears. Refreshing after that idle period, or restarting T3 Code, is a valid workaround. The install is already updated.
A fix should not kill a helper that a turn is still borrowing. After opencode upgrade exits 0, verification for a T3-owned local server should treat the CLI probe as the installed version when it is newer than the cached server version, or restart the owned server only when this probe is the sole borrower and then re-read /global/health. An in-flight turn can keep the old process until it goes idle.