Skip to content

backend: switch the agent CLI without restarting - #10

Merged
tdwd merged 1 commit into
mainfrom
codex-backend-switch
Sep 9, 2026
Merged

tdwd merged 1 commit into
mainfrom
codex-backend-switch

Conversation

@tdwd

@tdwd tdwd commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

/backend swaps the subprocess in place, which is the feature this whole line of work was for.

No re-exec. restartResuming exists for launch flags that cannot change any other way; a backend switch replaces the process anyway, so using it would discard the scrollback to achieve what the running process can already do.

Three things make the swap safe

  • Close runs on its own goroutine. It blocks on Wait, and doing that inline is the same deadlock main tears down after p.Run to avoid.
  • The new engine is piped into the running program, reached through progRef. The program is built from the model, so the model cannot be handed it at construction, and every later model is a value copy. A pointer to a holder survives that — the same trick as model.content.
  • A failed spawn leaves the old engine untouched and running. A missing CLI costs an error line, not the session.

The bug self-review caught

The engine you switch away from keeps draining into the same program until it exits. Its trailing frames, and the EOF that follows, would land in the new session — the EOF in particular printing — session ended — into a session that had just started. Stale frames are now ignored by backend, and the test for it is discriminating.

What does not survive

The conversation. A session id from one backend means nothing to the other, so the new one starts fresh and the divider says so rather than leaving you to notice.

resetForBackend clears the rest, each for a reason: a model list from the wrong backend offers rows that cannot be selected, a carried-over session id would be filed under the new backend, and ctxLimit returns to the -ctx floor because the window it grew to described a model that is gone. The launch --resume is stripped from ExtraArgs too, or switching a resumed session hands one backend an id the other invented.

Verified

go vet, full suite. Every declined path is tested for saying why and for leaving the old engine running — a silent decline reads as "switched" and is not.

Not verified: the swap driven through the real TUI. The switch is exercised with a stand-in subprocess, but nobody has pressed /backend in a terminal.

/backend swaps the subprocess in place. No re-exec: restartResuming exists for
launch flags that cannot change any other way, and a backend switch replaces the
process anyway, so using it would discard the scrollback to achieve what the
running process can already do.

Three things make the swap safe.

Close runs on its own goroutine. It blocks on Wait, and doing that inline is the
same deadlock main tears down after p.Run to avoid.

The new engine is piped into the program that is already running, reached
through progRef. The program is built from the model, so the model cannot be
handed it at construction, and every later model is a value copy — a pointer to
a holder survives that, the same trick as model.content.

A failed spawn leaves the old engine untouched and running, so a missing CLI
costs an error line rather than the session.

The engine we switch away from keeps draining into the same program until it
exits, so its trailing frames and its EOF are now ignored. Without that the old
subprocess announces "session ended" into the session that just started.

What does not survive is the conversation. A session id from one backend means
nothing to the other, so the new one starts fresh and the divider says so
rather than leaving it to be noticed. resetForBackend clears the rest: a model
list from the wrong backend offers rows that cannot be selected, a carried-over
session id would be filed under the new backend, and ctxLimit returns to the
-ctx floor. The launch --resume is stripped too, or switching a resumed session
hands one backend an id the other invented.
@tdwd
tdwd merged commit 250f633 into main Sep 9, 2026
1 check passed
@tdwd
tdwd deleted the codex-backend-switch branch September 9, 2026 13:18
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