Goal mode replay: thread_goal_updated events can reference another thread and cause missing Goal/user bubbles
Summary
Some long sessions that used Goal mode contain event_msg records oftype: "thread_goal_updated" whose threadId does not match the transcript's
own session_meta.payload.id. Some get_goal tool outputs inside the transcript
also return a goal attached to a different thread.
When the IDE renderer restores such a transcript and combines normal message
records, event records, and Goal state records, it appears to sometimes hide or
drop the original user /goal ... bubble or related Goal UI history.
The raw user content usually still exists in JSONL, so this looks like a
replay/render/state-merging bug rather than data loss.
Environment
- Component: Codex VS Code IDE extension + bundled
codex app-server - Extension:
openai.chatgpt/Codex - OpenAI's coding agent - Extension version:
26.5616.71553 - Bundled CLI:
codex-cli 0.142.0 - OS: WSL2, Ubuntu 24.04.4 LTS
- Kernel:
6.6.87.2-microsoft-standard-WSL2 CODEX_HOME: default~/.codex- State DBs observed:
~/.codex/state_5.sqlite,~/.codex/goals_1.sqlite,~/.codex/logs_2.sqlite
Expected behavior
- A transcript should not contain
thread_goal_updatedevents for another thread. get_goalshould return the goal for the active thread only.- The IDE renderer should not hide or drop original user messages because Goal
state events are mismatched.
- If slash commands are intentionally hidden as control events, the Goal text
should still be visible in a stable Goal UI element attached to the correct thread.
Actual behavior
Goal audit results:
- 40 transcripts scanned.
- 13 HIGH findings.
- 7 MEDIUM findings.
- Current
~/.codex/goals_1.sqlitehad only 1 row, while many historical
transcripts rely on replayed thread_goal_updated events.
Representative affected session:
- Transcript thread id:
019ebc43-5a39-7793-b560-2c69294bf46f - Original
/goal ...user message exists in the JSONL as both: response_itemwithpayload.type = "message"androle = "user"event_msgwithpayload.type = "user_message"thread_goal_updatedrecords in the same transcript: 3,495- Mismatched
thread_goal_updated.threadIdrecords: 2,942 - Mismatched thread id observed:
019eafe2-284b-73d1-a9cd-9a962733d147 get_goaloutputs returning the other thread id: 16- A
create_goalcall failed with:
cannot create a new goal because this thread already has a goal
Other sessions show similar patterns where:
- Goal update events reference a different thread.
- Some transcripts contain Goal state updates without a visible
/goalcommand. - User messages exist in one local record stream but are absent from another
replay stream, making renderer behavior dependent on which source it trusts.
Reproduction outline
- Use Goal mode in a long-running Codex IDE session.
- Continue work across many turns and compactions.
- Open or replay other long sessions that also have Goal mode history.
- Inspect transcript JSONL for:
session_meta.payload.idevent_msg.payload.type == "thread_goal_updated"event_msg.payload.threadIdresponse_itemfunction_call_outputfromget_goal
- Observe that some Goal events or
get_goaloutputs refer to another thread id. - Open the session in VS Code.
- Observe missing/hidden original
/goaluser bubbles or incorrect Goal UI state.
Suggested fix direction
- Validate
threadIdon every Goal event before appending or replaying it. - During transcript replay, discard or quarantine Goal events whose
threadId
differs from the transcript's session_meta.payload.id.
- Make
get_goalstrictly scoped to the active thread/session. - Add regression tests for restoring multiple Goal-mode transcripts in the same
app-server process.
- Ensure slash command hiding does not make the original Goal objective
unrecoverable from the visible UI.
Privacy note
The affected transcript contents include private project work, so I cannot
attach full raw JSONL files publicly. I can provide:
- Redacted structural samples.
- The audit scripts used to compute the statistics.
- Small synthetic repro transcripts, if needed.
- Exact local counts, versions, and stack traces.