Skip to content

fix(codex): restore a worker at app start on a new thread - #49

Merged
aaroncoville merged 2 commits into
theme/sixth-historyfrom
feat/codex-fresh-thread-on-start
Oct 12, 2026
Merged

aaroncoville merged 2 commits into
theme/sixth-historyfrom
feat/codex-fresh-thread-on-start

Conversation

@aaroncoville

@aaroncoville aaroncoville commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

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:

  • Logged: a codex-new-thread line with reason: "app-start" and the previousSessionId that was not resumed.
  • Still resumed: an explicitly requested resume (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.
  • Also still resumed: the orchestrator, when it runs on Codex. Its thread is the floor's working context rather than a brief being re-read.
  • Unchanged: other providers, and the old session's files, which stay on disk.

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

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

Before

The new tests, run against the code before this change:

not ok 27 - a worker’s first start since the app did resumes only when asked to
  error: 'threads.resumesOnSpawn is not a function'
not ok 28 - a Codex worker restored at app start opens a new thread, and says so
  error: 'function recordedSessionFor( located in index.ts'
not ok 29 - an asked-for resume, another provider, or no session is left as it was
  error: 'function recordedSessionFor( located in index.ts'

In the app's own log, an app-start was followed by each Codex worker's spawn with no codex-new-thread. That worker's first rollout turn after the launch was in its pre-restart session.

After

ok 27 - a worker’s first start since the app did resumes only when asked to
ok 28 - a Codex worker restored at app start opens a new thread, and says so
ok 29 - an asked-for resume, the orchestrator, another provider, or no session is left as it was

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

  • OS: macOS (arm64), Node 22.
  • Steps:
    • Wrote the three tests first and saw them fail, as shown above. The wiring test runs recordedSessionFor exactly as written in index.ts, not a copy of it.
    • Broke the code on purpose in eight ways, and each one turned the suite red:
      • always resume;
      • ignore the "already started" set;
      • never record a start;
      • apply the rule to every provider;
      • ignore requireResume;
      • drop the log line;
      • drop the resume !== true guard;
    • drop the orchestrator exception.
    • npm run typecheck: 0 errors. npm run build: succeeds. The built out/main/index.js contains resumesOnSpawn.
  • Takes effect after the next pull and restart of the app.

Credit (optional)

Discord:

X:

Checklist

  • Before and after evidence is attached above, under both headings.
  • npm run typecheck passes.
  • npm run test:focused passes.
  • npm run build succeeds.
  • This PR is one change. Unrelated fixes belong in their own PR.
  • I read the diff myself before opening this, and there is no debug output,
    commented-out code, or unrelated formatting churn in it.
  • Any new UI derives from DESIGN.md / tokens.ts — no ad-hoc colors,
    spacing, or fonts. (No UI.)
  • If I added art, it's my own or compatibly licensed, and listed in
    ATTRIBUTION.md. (No art.)

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.
@github-actions

Copy link
Copy Markdown

🚫 This PR is missing its before/after evidence

Every pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.

  • Before — no image or video under that heading
  • After — no image or video under that heading

How to fix it: edit the description, keep the ### Before and ### After headings from the template, and drag an image or video under each. GitHub uploads it inline. This check re-runs the moment you save.

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 no-visual-change label. Please don't ask unless it truly has no observable effect.

📖 CONTRIBUTING.md → Evidence is mandatory

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.
@aaroncoville
aaroncoville merged commit e080955 into theme/sixth-history Oct 12, 2026
0 of 2 checks passed
@aaroncoville
aaroncoville deleted the feat/codex-fresh-thread-on-start branch October 12, 2026 14:45
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