migrate-rollouts leaves persisted subagent threads with empty projected history
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
What version of Codex CLI is running?
codex-cli 0.148.0-alpha.9. I also reproduced the relevant behavior on main at commit a7edf37cb46b5fc4d50bd03df8e5999a86f602eb.
What subscription do you have?
Not applicable. This happens during an offline local-store migration.
Which model were you using?
No model call is involved.
What platform is your computer?
macOS arm64.
What terminal emulator and version are you using (if applicable)?
Not relevant to the reproduction.
Codex doctor report
Not attached. I can reproduce this with a synthetic rollout fixture, and a doctor report would include unrelated local-state information.
What issue are you seeing?
After migrating a persisted legacy subagent, its raw rollout can still contain history while thread/read and resume surfaces show an empty thread.
For a migrated subagent, the migration currently writes subagent_history_start_ordinal equal to expected_ordinal, which is the end of the rewritten rollout. Projection then hides every record below that boundary. Since no existing record is at or beyond the end-of-file boundary, none of the migrated history is exposed as thread turns or items.
This is easy to notice with a named or otherwise persisted delegation thread that is still visible in thread/list: the thread exists and its rollout is non-empty, but opening it shows no previous turns.
What steps can reproduce the bug?
I used an isolated temporary Codex home and a synthetic legacy subagent:
All identifiers, names, timestamps, and rollout contents below are synthetic.
- Create a legacy rollout with
SessionSource::SubAgent, a compacted checkpoint, and one child-local user/assistant turn. - Make the thread persisted and visible to
thread/list. - Run
codex migrate-rollouts --apply --thread 00000000-0000-7000-8000-000000000002against the temporary home. - Inspect the migrated session metadata, then read or resume the thread through the app-server.
The migrated metadata has subagent_history_start_ordinal == number of rollout lines. The retained rollout is non-empty, but the projected thread has no active turns.
What is the expected behavior?
Migration should keep inherited or bootstrap context hidden where necessary, while preserving the subagent's own displayable turns for thread/read and thread/resume.
The boundary should point to the first displayable child-local record instead of always pointing to the end of the rewritten file. If ephemeral workers and persisted user-facing subagents intentionally need different behavior, the migration should distinguish those cases.
Additional information
I traced the behavior to these paths:
- The migration passes its final
expected_ordinaltorewrite_subagent_history_boundary. - That helper stores the value as
subagent_history_start_ordinal. - Materialization suppresses records below the boundary (source).
- The current migration test asserts both
boundary == lines.len()and an empty projected turn list (test).
That test makes the migration-layer behavior clear, but I am not sure it captures the expected app-server behavior for a persisted thread that a user can reopen. A focused regression test could migrate a named synthetic subagent, exercise thread/read or thread/resume, and verify that its child-local turn remains visible while inherited context stays hidden.
I found this while looking at the same migration path as #38761, but kept it in a separate report because this issue is about subagent history projection rather than legacy name preservation.
If this is not the intended behavior for persisted subagents, I'm happy to work on the focused fix and regression test if a maintainer invites a PR.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the migration call in codex-rs/thread-store/src/local/rollout_migration.rs and the boundary handling in rollout_migration/publish.rs, then read projection in thread_history_materialization.rs. Run the focused migration test in rollout_migration_tests.rs and consider a persisted named subagent through thread/read or thread/resume. Done means child-local turns remain visible while inherited context stays hidden.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100