Plugin version: 1.0.6 (db52e28, tag v1.0.6)
Codex CLI: 0.147.0
Platform: Windows 11
Summary
/codex:review and /codex:adversarial-review start their app-server threads with
ephemeral: true. Ephemeral threads are never written to ~/.codex/sessions/**/*.jsonl and never
registered in ~/.codex/state_5.sqlite, so a review leaves no durable record that it ran — no
rollout to resume from, nothing in codex resume, and nothing for usage/cost tooling to attribute.
/codex:rescue (task) is unaffected: it is the only caller that opts into persistence.
| Entry point |
Path |
Thread |
Durable record |
/codex:rescue, task |
runAppServerTurn + persistThread: true |
persisted |
yes |
/codex:review |
runAppServerReview |
ephemeral |
no |
/codex:adversarial-review |
runAppServerTurn, no persistThread |
ephemeral |
no |
Where it comes from
plugins/codex/scripts/lib/codex.mjs:
- L70 —
buildThreadParams: ephemeral: options.ephemeral ?? true (default for every thread/start)
- L1013 —
runAppServerReview: ephemeral: true, hardcoded, no way to override
- L1109 —
thread/resume: ephemeral: false
- L1117 —
runAppServerTurn: ephemeral: options.persistThread ? false : true
plugins/codex/scripts/codex-companion.mjs:
- L493 —
executeTaskRun passes persistThread: true (the only opt-in in the codebase)
- L411 —
executeReviewRun, adversarial branch, calls runAppServerTurn without
persistThread, so it inherits the ephemeral default
There is no environment variable, CLI flag, or plugin setting that changes this. ephemeral occurs
at exactly those four lines and nowhere else in the plugin.
Reproduce
- In a git repo, run
/codex:adversarial-review (or /codex:review) and let it finish.
ls ~/.codex/sessions/**/*.jsonl — no new rollout.
- Query
~/.codex/state_5.sqlite: select id, cwd, created_at from threads order by created_at desc limit 5;
— the review's thread id is absent.
- Grep
~/.codex/logs_2.sqlite for the thread id — the run is there, including
token usage turn_id=... total_usage_tokens=.... Trace logs are the only surviving evidence.
Observed here: an adversarial review ran 2026-08-22 03:51–03:53 (thread
01a0272a-d8c5-73e0-a8fd-bc1e1fefbfa8, gpt-5.6-sol, ~18k tokens and climbing). Nothing in
sessions/, nothing in threads. A task run on 2026-08-14 in the same ~/.codex persisted
normally, which is what isolated the difference.
Why it matters
- Usage attribution. Anything that reconciles Codex spend by reading rollouts — internal
chargeback, per-repo cost reporting, seat justification — silently undercounts, and reviews are
a large share of real plugin usage. The failure is invisible: no error, no partial record.
- No resume or audit trail. A long adversarial review that produces a finding you want to
revisit cannot be resumed or re-read; only the rendered output in the Claude Code transcript
survives.
- Asymmetry is surprising.
task persists, review does not, and nothing in the docs or command
help says so.
Suggested fix
Either of:
- Persist by default. Drop
ephemeral: true at L1013 and pass persistThread: true from
executeReviewRun (L411). Reviews are user-initiated, named, and scoped to a repo — the same
things that justify persisting task threads.
- Expose an opt-in. Add
--persist to the review commands (and/or honour a
CODEX_PLUGIN_PERSIST_THREADS=1 env var) threaded through to buildThreadParams.
The first is preferable — an opt-in that defaults off leaves the accounting gap in place for
everyone who does not know to look for it.
Plugin version: 1.0.6 (
db52e28, tagv1.0.6)Codex CLI: 0.147.0
Platform: Windows 11
Summary
/codex:reviewand/codex:adversarial-reviewstart their app-server threads withephemeral: true. Ephemeral threads are never written to~/.codex/sessions/**/*.jsonland neverregistered in
~/.codex/state_5.sqlite, so a review leaves no durable record that it ran — norollout to resume from, nothing in
codex resume, and nothing for usage/cost tooling to attribute./codex:rescue(task) is unaffected: it is the only caller that opts into persistence./codex:rescue,taskrunAppServerTurn+persistThread: true/codex:reviewrunAppServerReview/codex:adversarial-reviewrunAppServerTurn, nopersistThreadWhere it comes from
plugins/codex/scripts/lib/codex.mjs:buildThreadParams:ephemeral: options.ephemeral ?? true(default for everythread/start)runAppServerReview:ephemeral: true, hardcoded, no way to overridethread/resume:ephemeral: falserunAppServerTurn:ephemeral: options.persistThread ? false : trueplugins/codex/scripts/codex-companion.mjs:executeTaskRunpassespersistThread: true(the only opt-in in the codebase)executeReviewRun, adversarial branch, callsrunAppServerTurnwithoutpersistThread, so it inherits the ephemeral defaultThere is no environment variable, CLI flag, or plugin setting that changes this.
ephemeraloccursat exactly those four lines and nowhere else in the plugin.
Reproduce
/codex:adversarial-review(or/codex:review) and let it finish.ls ~/.codex/sessions/**/*.jsonl— no new rollout.~/.codex/state_5.sqlite:select id, cwd, created_at from threads order by created_at desc limit 5;— the review's thread id is absent.
~/.codex/logs_2.sqlitefor the thread id — the run is there, includingtoken usage turn_id=... total_usage_tokens=.... Trace logs are the only surviving evidence.Observed here: an adversarial review ran 2026-08-22 03:51–03:53 (thread
01a0272a-d8c5-73e0-a8fd-bc1e1fefbfa8,gpt-5.6-sol, ~18k tokens and climbing). Nothing insessions/, nothing inthreads. Ataskrun on 2026-08-14 in the same~/.codexpersistednormally, which is what isolated the difference.
Why it matters
chargeback, per-repo cost reporting, seat justification — silently undercounts, and reviews are
a large share of real plugin usage. The failure is invisible: no error, no partial record.
revisit cannot be resumed or re-read; only the rendered output in the Claude Code transcript
survives.
taskpersists, review does not, and nothing in the docs or commandhelp says so.
Suggested fix
Either of:
ephemeral: trueat L1013 and passpersistThread: truefromexecuteReviewRun(L411). Reviews are user-initiated, named, and scoped to a repo — the samethings that justify persisting
taskthreads.--persistto the review commands (and/or honour aCODEX_PLUGIN_PERSIST_THREADS=1env var) threaded through tobuildThreadParams.The first is preferable — an opt-in that defaults off leaves the accounting gap in place for
everyone who does not know to look for it.