Repository navigation
Description
Before submitting
- I searched existing issues and did not find a native Codex duplicate.
- I included source-level diagnosis and a local mock-model reproduction.
Area
apps/server — native Codex adapter
Problem
Resuming a native Codex conversation after recreating its runtime appends another copy of unchanged T3 application instructions. Earlier instruction messages remain in the conversation. This is model-visible duplication, not just repeated RPC parameters or a transcript display artifact.
The underlying mechanism is in Codex's in-memory AdditionalContextStore; T3's per-turn additionalContext delivery exposes it. This report does not establish a subscription-usage multiplier or claim that every normal turn duplicates instructions.
Versions and environment
- Serving desktop package: T3 Code 0.1.2, build commit 044f072 (from installed app.asar package metadata).
- Native provider: Codex app-server 0.164.0, Linux x86_64 / NixOS.
- Observed in a normal GPT-6.1-Sol conversation; independently reproduced against the installed Codex binary using an isolated home and a loopback mock Responses endpoint, without paid inference.
- Corresponding T3 code is also present in main at e856833.
Steps to reproduce
- Start
codex app-serverwith a fresh temporaryCODEX_HOME, a custom local mock Responses provider, and no real credentials. - Initialize with
capabilities.experimentalApi: true, thenthread/start. - Run two completed
turn/startrequests against the same thread with identical application context:
{
"threadId": "<thread-id>",
"input": [{"type": "text", "text": "Say hi"}],
"additionalContext": {
"t3_code_repro": {
"kind": "application",
"value": "UNIQUE_T3_CONTEXT_MARKER"
}
}
}- Stop that isolated app-server, start another against the same temporary home, initialize, and call
thread/resumewith the original thread ID andexcludeTurns: true(matching T3). - Run two more completed turns with the identical context.
- Capture each outgoing
/v1/responsesrequest and count developer messages ininputcontaining the marker.
A local Rust JSON-RPC driver plus HTTP/SSE mock executed this sequence. The mock returns response.created, an assistant response.output_item.done, and response.completed for each request. The driver and captured requests were kept local; raw production logs were not uploaded.
Expected behavior
Unchanged T3 context already present in retained history should not become a second developer message merely because the native runtime was recreated and the conversation resumed. Legitimate refreshes after context changes or compaction must remain supported.
Actual behavior
Captured outgoing model input:
turn 1: 1 model-visible copies
turn 2: 1 model-visible copies
[restart isolated app-server; thread/resume]
turn 3: 2 model-visible copies
turn 4: 2 model-visible copies
The real T3 protocol log and native rollout agree:
2026-10-25T21:47:08Z turn/start seven T3 additionalContext entries
2026-10-25T21:47:09Z rollout first set of seven developer messages
2026-10-26T01:28:01Z thread/resume same native conversation
2026-10-26T01:28:01Z turn/start same seven additionalContext entries
2026-10-26T01:28:02Z rollout second, byte-identical set of seven messages
[later turns send the same map without appending further copies]
The duplicated set is t3_code_orchestration_01 through _05, t3_code_runtime, and t3_code_tools: 11,927 characters per set in this conversation. No compaction or thread/inject_items occurred on the observed duplication path.
Source-level root cause
T3, pinned to the installed build:
buildCodexTurnStartParamsconstructs the complete T3 context map for each turn.resumeThreadresumes the same native conversation.- T3's separate compaction restoration path caches context in memory and explicitly injects it after compaction; that was not the trigger here.
Codex, pinned to the 0.164.0 release commit 6aaac694803004a63c6f393d153c96d22521a6b9:
AdditionalContextStore::mergeemits a message whenever a key/value differs from its in-memory map, then replaces that map.SessionStateinitializesadditional_contextwithAdditionalContextStore::default().- Resume reconstruction restores history and other state, but does not rebuild that map from retained application context.
- The next submitted turn merges the full incoming context against the empty map, so all entries are appended again.
The natural underlying fix is to persist/restore Codex's keyed context state consistently with retained history, or provide a supported resume-time seeding mechanism. A T3 workaround would need to preserve context updates and compaction behavior; simply deleting per-turn context is not a complete fix.
Impact and validation limits
Extra instruction copies consume model context and introduce new input after runtime reconstruction. The mock verifies actual model input; it does not quantify quota charging, cache-hit changes, or explain every reported usage discrepancy. Production evidence establishes one resume-triggered duplicate; repeated-cycle accumulation was not separately tested.
Related: #24465 and #24185 concern ACP instruction delivery and use different provider code paths. This report is native Codex, not Copilot ACP.
Investigation and report prepared with Codex GPT-6.1-Sol through T3 Code.
Activity
DKLokeswaran commentedon Nov 2, 2026
Confirming this resume path can hard-fail, not just duplicate.
Setup (custom):
opencode-go/muse-spark-1.3-contributordriven through the Codex harness via opencodex (ocx). I know that's not a supported matrix — mentioning it because it makes the underlying resume bug visible as a rejection.0.1.7-nightly.20261102.4066ondkl-home-server(linux x64, systemd service on:3773), web client0.166.0,modelProvider: openaithread/resume(excludeTurns:true)What happened: first turn (short history + 5
kind:applicationentries:t3_code_orchestration_01/02/03,t3_code_runtime,t3_code_tools) succeeded. Resumed second turn with the same 5 entries failed in ~2s:{"model":"muse-spark-1.3-contributor","error":{"code":null,"message":"`input[14]` did not match any supported type","param":"input[14]","type":"invalid_request_error"}}Provider log shows
thread/resume→turn/startwith the identicaladditionalContextmap. Switching the same thread to the opencode harness with the identical model string succeeded.Reading: matches this issue's root cause (resume doesn't rebuild
AdditionalContextStore, next turn re-merges unchanged context). Stock models tolerate it as duplication; this stricter endpoint rejects the re-appended item atinput[14]. Fix here should also fix this symptom.Happy to pull fuller redacted protocol excerpts if useful. Investigated with Muse Spark via
t3 triage.