Repository navigation
fix(codex): restore a worker at app start on a new thread - #49
Merged
aaroncoville merged 2 commits intoOct 12, 2026
Merged
Conversation
When the app starts, it restores the previous session's team by respawning each worker with resume. For Codex that means `codex resume <id>` with the worker's brief as the prompt, so the first thing every restored worker did was submit its brief into its old thread. That turn re-sent the whole thread's history to the model, often well over a hundred thousand tokens, only for the worker to re-read its inbox. It was paid once per Codex worker on every launch. The new-thread logic did not help: it only acts on a request, and this turn comes from the spawn. A worker's first start in a run of the app now leaves its recorded session alone and starts a new process, whose first thread is its own. The skip is logged as a codex-new-thread line with reason app-start and the session that was not resumed. The old session stays on disk. A resume that was asked for (requireResume) still attaches it, as does any later respawn in the same run. Other providers are unchanged. The trade-off: a follow-up to work from before the restart now reaches the new thread rather than the one that did that work. It has to read the earlier exchange back from the mail.
🚫 This PR is missing its before/after evidenceEvery pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.
How to fix it: edit the description, keep the A bug fix with no visible surface still needs it: show the failing behaviour, then the same steps passing. A terminal recording is fine. Genuinely nothing to show — a CI tweak, a typo, a dependency bump? A maintainer can apply the |
The app-start rule that opens a new thread for a restored Codex worker matched any Codex agent, including the orchestrator when it runs on Codex. The orchestrator is restored with resume because its thread is the floor's working context, which restarts used to lose. Skipping that resume would bring the loss back on every launch. The rule's reason does not apply to it either: the turn it avoids is a worker's brief being re-read in a thread full of history. The rule now leaves the orchestrator's session alone, and the test pins that.
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.
What & why
When the app starts, it restores the previous session's team by respawning each worker with resume. For a Codex worker that is
codex resume <id>with its brief as the prompt. So the first thing every restored Codex worker did was submit its brief into its old thread. That turn re-sent the whole thread's history to the model, often well over a hundred thousand tokens, only for the worker to re-read its inbox, and it was paid once per Codex worker on every launch. The new-thread logic from the previous change did not cover it, because it acts on a request and this turn comes from the spawn.A Codex worker's first start in a run of the app now leaves its recorded session alone and starts as a new process, whose first thread is its own:
codex-new-threadline withreason: "app-start"and thepreviousSessionIdthat was not resumed.requireResume, e.g. a restart chosen with resume) and any later respawn in the same run, such as a revive after sleep or a restart in place. A typed session id is also still honoured.Trade-off, by design: a follow-up to work from before the restart now reaches the new thread rather than the one that did that work, and the worker reads the earlier exchange back from its mail.
Type of change
Evidence
Before
The new tests, run against the code before this change:
In the app's own log, an
app-startwas followed by each Codex worker'sspawnwith nocodex-new-thread. That worker's first rollout turn after the launch was in its pre-restart session.After
Full suite: 1760 of 1762 pass, 2 skipped, 0 failing. On the base commit it was 1757 of 1759 pass, 2 skipped, 0 failing.
How I tested it
recordedSessionForexactly as written inindex.ts, not a copy of it.requireResume;resume !== trueguard;npm run typecheck: 0 errors.npm run build: succeeds. The builtout/main/index.jscontainsresumesOnSpawn.Credit (optional)
Discord:
X:
Checklist
npm run typecheckpasses.npm run test:focusedpasses.npm run buildsucceeds.commented-out code, or unrelated formatting churn in it.
DESIGN.md/tokens.ts— no ad-hoc colors,spacing, or fonts. (No UI.)
ATTRIBUTION.md. (No art.)