Fresh thread forks retain inherited open-turn markers and cannot establish one active turn
Severity: P2 — Warning
Type: Bug
Evidence state: Confirmed
Classification rationale: This is a confirmed P2 bug because a fresh recovery thread receives a new thread identity but persists unresolved task lifecycle markers from its source history alongside its own active turn. Consumers that correctly require one unambiguous active turn must reject the thread, blocking reliable autonomous recovery without corrupting user data or production state.
Problem
A fresh Codex App thread created to recover from an interrupted parent copies unresolved task_started records from the source rollout into the new rollout. Once its own task starts, the child file contains multiple apparently open turns under the child thread identity, so deterministic tooling cannot identify the one current task.
Evidence
- Runtime — A read-only projection of the fresh rollout showed two inherited unmatched task_started records and the fresh task own unmatched task_started record, each with a later turn_context bound to the same workspace. —
Sanitized local rollout event projection observed in Codex Desktop on 2026-07-20 - Runtime — The fresh thread had a distinct thread identity and was active in the App, but deterministic active-turn resolution returned CODEX_TURN_UNAVAILABLE before any controller history mutation. —
Read-only Codex App task inventory and release-controller preflight observed on 2026-07-20 - Code — Existing issue 34268 independently documents that full-history forks serialize inherited parent rollout records into child rollouts; the new evidence shows a correctness consequence when those inherited records include unmatched lifecycle markers. —
https://github.com/openai/codex/issues/34268
Impact
Interrupted long-running workflows cannot recover through a fresh thread while preserving deterministic task identity. This blocks automation that binds merges, deploys, or other effects to one current task and can pressure operators toward unsafe manual JSONL repair or less reliable identity checks.
Root cause or gap
Confidence: Confirmed
The persisted fork contract does not distinguish inherited task lifecycle markers from lifecycle events owned by the new thread. Unmatched source task_started records therefore remain operationally live after being copied beneath a new session identity.
Expected behavior
A new or forked thread may inherit conversational context, but inherited lifecycle events must not be indistinguishable from current child lifecycle events. The child should expose exactly one unambiguous current active turn while it is running, including when the source thread ended with interrupted turns.
Actual behavior
The newly created rollout contained two inherited task_started plus turn_context pairs without matching task_complete records, followed by the fresh task own task_started and turn_context. A bounded read-only resolver found three open turns and failed closed with CODEX_TURN_UNAVAILABLE. Granting the fresh task filesystem permission did not change this result.
Reproduction or deterministic trace
- Start with a persisted Codex Desktop thread whose rollout contains one or more task_started plus turn_context pairs without matching task_complete records after an interruption or app update.
- Create a fresh recovery/background thread from that task while preserving its history or delegated context.
- Allow the fresh thread to start its own turn.
- Inspect only lifecycle event kinds in the new child rollout and project open turns as task_started minus task_complete with a later turn_context.
- Observe that inherited unmatched turns and the child current turn are all open in the child rollout, so a resolver requiring exactly one open turn cannot identify the current task.
Likely solution direction
Confidence: Likely
Preserve the conversational history needed by the fork while filtering, terminalizing, or explicitly namespacing inherited lifecycle markers so they cannot participate in the child current-turn projection. Add an app-server regression fixture with unmatched source turns and verify one child-current active turn without rewriting the source rollout.
Likely affected surface
| Surface | Confidence | Reason |
| --- | --- | --- |
| Full-history fork initial-history persistence in codex-rs core session handling | Likely | Related source evidence shows parent rollout items are persisted into child history, which is the boundary where lifecycle ownership must remain distinguishable. |
| Codex App or app-server thread creation and delegation path | Likely | The failure was reproduced through an App-created fresh background thread with a distinct thread identity. |
| Rollout lifecycle-event tests | Confirmed | A deterministic regression requires a source rollout with unmatched starts and a child rollout with one current active turn. |
Acceptance criteria
- [ ] Forking or creating a recovery thread from a source rollout with unmatched task_started records yields exactly one child-current open turn while the child is running.
- [ ] Inherited lifecycle history remains available for audit or conversational reconstruction without being interpreted as current child execution.
- [ ] A child task can establish deterministic thread and turn identity without editing either source or child JSONL manually.
- [ ] Completed and cleanly interrupted source histories continue to fork correctly.
- [ ] The behavior is covered for Codex Desktop or app-server thread creation, not only multi-agent subagent spawning.
Unknowns and non-goals
Unknowns
- Whether the fresh App thread used the same internal full-history fork implementation as multi-agent V2 or a parallel app-server path.
- Whether inherited lifecycle markers should be omitted, wrapped as historical evidence, or explicitly terminalized in child persistence.
Non-goals
- Editing or repairing existing user session JSONL files.
- Removing useful conversational history from forks.
- Weakening effect authorization or accepting ambiguous active-turn identity.
- Changing cybersecurity content safeguards.
Provenance and duplicates
- Provenance: Observed during a user-authorized autonomous Release Control recovery after a Codex Desktop update on 2026-07-20.
- Duplicate search:
related_not_duplicate— Issue 34268 covers storage amplification from copied fork history, 20392 covers hydrating a rollout ending with a dangling start, 24300 covers sandbox downgrade, and 34324 covers degraded parent UI. None owns the correctness defect where a distinct fresh child rollout treats inherited unmatched lifecycle records as multiple current open turns. - Canonical issue: None
- Prior resolution evidence: None
- Recurrence evidence: None
- Closure premise changed: false
- Related: https://github.com/openai/codex/issues/34268, https://github.com/openai/codex/issues/34324, https://github.com/openai/codex/issues/20392, https://github.com/openai/codex/issues/24300
- Withheld evidence: Private thread and turn identifiers, Absolute local session paths, Raw private conversation and tool output
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗