thread/resume and thread/fork ignore developerInstructions override on first resumed turn
What issue are you seeing?
When thread/resume or thread/fork is given new plain thread-level developerInstructions, Codex accepts and stores the override in runtime session state, but the first resumed/forked model turn may not actually receive it.
The issue appears on cold resume/fork paths that reconstruct prior rollout state. In that case, the first subsequent turn can be built from replay baseline plus steady-state context diffs. That diff path does not emit updates for plain thread-level developer_instructions, so the model can still see stale instructions from the original thread instead of the override supplied to thread/resume or thread/fork.
This was reproduced with targeted app-server tests that assert on the actual first /responses request after resume/fork. In both cases, the request still contained the old developer instructions A, while the new override B was missing from the latest developer-visible update.
What steps can reproduce the bug?
- Start a thread with plain thread-level
developerInstructions = "A". - Run at least one turn so the thread has persisted rollout/history state.
- Stop the session.
- Call
thread/resumeorthread/forkwithdeveloperInstructions = "B". - Start the first turn on the resumed/forked thread.
- Inspect the first
/responsesrequest sent to the model.
Observed result:
- historical developer context still includes
A - the latest developer-visible update does not include
B - the first resumed/forked turn therefore behaves according to stale instructions instead of the override
Minimal repro shape:
thread/start(developerInstructions="A")
turn/start("seed history")
shutdown session
thread/resume(developerInstructions="B")
turn/start("first resumed turn")
or
thread/start(developerInstructions="A")
turn/start("seed history")
shutdown session
thread/fork(developerInstructions="B")
turn/start("first forked turn")
What is the expected behavior?
If a caller supplies new developerInstructions to thread/resume or thread/fork, the next model turn should see those instructions as the active developer instructions.
In other words, thread/start.developerInstructions, thread/resume.developerInstructions, and thread/fork.developerInstructions should all define the effective developer/system prompt for subsequent turns.
Additional information
Root cause from investigation:
- resume/fork reconstructs a prior
reference_context_itemfrom persisted rollout state - that causes the next turn to go through
build_settings_update_items(...)instead of full initial-context injection - full initial-context injection includes plain thread-level developer instructions
- the steady-state diff path does not currently emit diffs for plain thread-level
developer_instructions
So the new value is stored in session state but never becomes model-visible on the first resumed/forked turn.
This is separate from the already-running-thread behavior where app-server explicitly ignores resume overrides for loaded threads.
Smallest likely fix:
- add explicit diff handling for plain thread-level
developer_instructionsincodex-rs/core/src/context_manager/updates.rs
Patch draft: https://github.com/openai/codex/compare/main...CyberShadow:codex:fix-resume-and-fork
Downstream project impact:
- The CyDo
SwitchModetool allows agents to switch modes (e.g. personalities) mid-conversation, varying parameters including the system prompt. This issue's impact is that the agent is unable to see the new prompt, and thus is not able to see its operating instructions.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗