Add a detached effective-context fork for oversized Desktop threads
What variant of Codex are you using?
Codex App on macOS. The proposed primitive belongs in app-server so the App, CLI, and IDE clients can reuse the same semantics.
What feature would you like to see?
Summary
Add a detached effective-context fork for very long threads.
After a manual or automatic compaction has completed, the user should be able to continue in a new task that inherits the source thread's latest effective model context, but does not inherit or expose the source thread's complete conversation history.
This is a performance and working-set feature, not a request for a new handoff summary or different compaction quality.
Motivation
Very long, tool-heavy Codex App threads can become progressively expensive for the Desktop renderer and app-server to hydrate, parse, serialize, and retain. This can remain a problem after model-context compaction because the persisted/UI-visible transcript is still large even though the model now works from a bounded compacted state.
Existing choices do not provide the desired boundary:
- A normal fork inherits the conversation history. Paginated reference-backed forks avoid copying the entire parent JSONL, but their logical history still traverses the parent lineage.
- A new task has a small transcript, but loses the already-computed effective model context.
- A generated
HANDOFF.mdor handoff prompt performs another lossy summarization step and is aimed at continuity quality rather than local history hydration cost.
The desired operation is conceptually a shallow/compact fork:
source full transcript
|
| latest usable compacted replacement history + surviving suffix
v
new task with bounded model context and no inherited visible turn history
Proposed semantics
An illustrative app-server request could extend thread/fork with an enum such as:
{
"method": "thread/fork",
"params": {
"threadId": "<source-thread-id>",
"historyScope": "effectiveContext",
"excludeTurns": true,
"deferGoalContinuation": true
}
}
The exact API name is not important. The important behavior is:
- Require the source thread to be idle and use the latest successfully persisted effective model context.
- Seed the child with the latest usable
Compacted.replacement_historyplus the surviving post-compaction suffix needed for the next model request. - Persist that bounded snapshot in the child instead of copying the complete source rollout or retaining a
history_basethat exposes the parent turns. - Preserve normal fork metadata behavior:
forkedFromId, cwd/workspace roots, current configuration and instructions, permissions, environments, and optionally the current Goal. - Leave the source thread unchanged and available for audit.
- After switching the App to the child, stop hydrating/subscribing to the source so its renderer/app-server state can be released.
- For legacy histories, either migrate to paginated history first or return a clear unsupported result. Do not silently fall back to a full-history copy/read for this mode.
The App could expose this as Continue in compact fork or Continue in a lightweight task after a successful compaction. Automatic rollover is not required for the initial version.
Acceptance criteria
- The child's first model request receives the same effective conversation state as the source would receive immediately after compaction, apart from intentionally refreshed thread-local context.
- The child rollout size is proportional to the effective model context, not the full source rollout.
thread/turns/liston the child does not traverse or return source turns.- Cold resume of the child preserves the same effective context.
- The source rollout is not rewritten or deleted.
- Navigating to the child does not require the App to hydrate the source transcript.
Additional information
This appears close to existing app-server building blocks:
thread/forkalready supports paginated reference-backed forks and bounded model-context reconstruction: https://github.com/openai/codex/pull/35220- Legacy-to-paginated rollout migration has landed: https://github.com/openai/codex/pull/37175
- CLI/background migration tooling has landed: https://github.com/openai/codex/pull/37348
- Current
load_latest_model_contextreverse-scans paginated rollouts to a usable replacement-history checkpoint: https://github.com/openai/codex/blob/4c89139da96f3e982f123c8b739e3de9bc4570be/codex-rs/thread-store/src/local/model_context.rs#L26-L70 - Current fork processing already obtains
prepared_fork.model_context, but persists paginated forks as parent-history references: https://github.com/openai/codex/blob/4c89139da96f3e982f123c8b739e3de9bc4570be/codex-rs/app-server/src/request_processors/thread_processor.rs#L4120-L4194
Related issues cover different parts of the problem:
- #25251 reports the Desktop CPU/memory impact of opening a large thread. This proposal adds an explicit escape hatch after successful compaction.
- #34268 and #35647 cover fork storage amplification. Reference-backed paginated forks address physical duplication, but still retain logical parent history.
- #36584 proposes a
HANDOFF.mdworkflow motivated by context drift. This proposal transfers the already-effective compacted model state without generating another summary. - #22641 proposed an automatic fresh-session handoff prompt. This proposal instead uses normal fork metadata plus the actual effective model context, and is motivated by Desktop/session engineering cost.
- #30194 covered recovery when compaction fails or cannot reclaim context. This proposal assumes compaction succeeded and detaches the new task from the oversized UI-visible transcript.
The repository accepts external PRs by invitation only. If this direction aligns with the intended thread-storage architecture, would the maintainers consider inviting a focused backend-only PR for the experimental app-server primitive? The Desktop button/navigation could remain an internal follow-up.
1 Comment
Hi @ai-jz, that rollout size is valuable stress evidence. Codex Rescue Alpha5 scans large local session histories with bounded memory and produces aggregate size, media, and compaction diagnostics without loading the whole file into RAM or dumping full payloads.
If you still have an affected closed rollout, you can test it locally:
No raw session upload is needed; sanitized aggregate output is sufficient.