Reframed from an implementation issue to a spike (2026-08-22) after an adversarial review found engine-side Codex resume is unreliable upstream and in tension with this repo's own decisions. Do NOT build the resume path until this spike passes.
Depends on #729 (the --json thread.started id is the only source of a session id).
Why this is a spike, not a build
Three independent problems, none of which #730's original "just persist the id and call resume" plan addressed:
-
Engine-side resume may silently no-op. codex exec resume <id> with an id captured from a --json exec run is reported upstream to silently start a fresh thread with no prior context (openai/codex#15538), and exec sessions may be treated as ephemeral so the id is not reliably resumable (openai/codex#3817). A resume that exits 0 but starts cold is worse than starting clean — it's confidently wrong.
-
The codebase already measured and rejected Codex resume. workers/api/src/lib/coding-engines.ts (DEFAULT_ENGINES comment) and docs/coding-engines.md § "Why no shipped preset resumes" document a measured failure: codex exec resume --last followed an unrelated codex exec that ran in the same directory. Session-id pinning is the proposed answer to that specific failure — the spike must prove it actually avoids it, not assume it.
-
The argv contract has no slot for a resume subcommand. runOneShot (headless.ts) is "preset command as prefix + turn text appended as the final positional." codex exec resume <id> "<turn>" inserts a subcommand and a positional between the prefix and the turn text. cmdArgs is computed once at construction and is immutable per session, so this cannot be done by editing the preset — it needs a dedicated resume argv path. The docs also note codex exec resume accepts --dangerously-bypass-approvals-and-sandbox but not --sandbox <mode>, so the write-flag would have to change too.
Strategic tension with #693 (must be resolved, not ignored)
#693 ("The platform owns the conversation — the engine's own memory is an optimisation") explicitly names Codex's lack of continuity and proposes the platform seed each turn from coding_timeline, demoting the engine's own session store. #730 does the opposite — it persists a Codex-owned session id in the runner's state file and leans on the engine's resume, re-creating for Codex exactly the machine-bound, opaque continuity #693 wants to move away from.
Decision recorded: pursue engine-side resume only as a pragmatic stopgap because #693 is deferred (P3: later), and revisit onto the platform-owned store when #693's seed-from-timeline slice lands. If the spike below fails, prefer #693's approach outright rather than shipping an unreliable resume.
Spike deliverables (this issue)
- On a machine with a known codex-cli version (record it), run two consecutive
codex exec --json turns where the second resumes the first by captured thread_id. Prove context carries — not that the command exits 0. Capture the raw JSONL of both turns as a fixture.
- Confirm whether
codex exec resume <id> --json "<turn>" accepts the flags/positions we need (write flag, --json, turn text) without conflict.
- Repeat test (1) after an unrelated
codex exec runs in the same CWD — reproduce or rule out the --last-class hijack for the explicit-id form.
Exit criteria
Refs: #729, #693, #556; upstream openai/codex#15538, #3817.
Reframed from an implementation issue to a spike (2026-08-22) after an adversarial review found engine-side Codex resume is unreliable upstream and in tension with this repo's own decisions. Do NOT build the resume path until this spike passes.
Depends on #729 (the
--jsonthread.startedid is the only source of a session id).Why this is a spike, not a build
Three independent problems, none of which #730's original "just persist the id and call
resume" plan addressed:Engine-side resume may silently no-op.
codex exec resume <id>with an id captured from a--jsonexec run is reported upstream to silently start a fresh thread with no prior context (openai/codex#15538), and exec sessions may be treated as ephemeral so the id is not reliably resumable (openai/codex#3817). A resume that exits 0 but starts cold is worse than starting clean — it's confidently wrong.The codebase already measured and rejected Codex resume.
workers/api/src/lib/coding-engines.ts(DEFAULT_ENGINEScomment) anddocs/coding-engines.md§ "Why no shipped preset resumes" document a measured failure:codex exec resume --lastfollowed an unrelatedcodex execthat ran in the same directory. Session-id pinning is the proposed answer to that specific failure — the spike must prove it actually avoids it, not assume it.The argv contract has no slot for a resume subcommand.
runOneShot(headless.ts) is "preset command as prefix + turn text appended as the final positional."codex exec resume <id> "<turn>"inserts a subcommand and a positional between the prefix and the turn text.cmdArgsis computed once at construction and is immutable per session, so this cannot be done by editing the preset — it needs a dedicated resume argv path. The docs also notecodex exec resumeaccepts--dangerously-bypass-approvals-and-sandboxbut not--sandbox <mode>, so the write-flag would have to change too.Strategic tension with #693 (must be resolved, not ignored)
#693 ("The platform owns the conversation — the engine's own memory is an optimisation") explicitly names Codex's lack of continuity and proposes the platform seed each turn from
coding_timeline, demoting the engine's own session store. #730 does the opposite — it persists a Codex-owned session id in the runner's state file and leans on the engine'sresume, re-creating for Codex exactly the machine-bound, opaque continuity #693 wants to move away from.Decision recorded: pursue engine-side resume only as a pragmatic stopgap because #693 is deferred (
P3: later), and revisit onto the platform-owned store when #693's seed-from-timeline slice lands. If the spike below fails, prefer #693's approach outright rather than shipping an unreliable resume.Spike deliverables (this issue)
codex exec --jsonturns where the secondresumes the first by capturedthread_id. Prove context carries — not that the command exits 0. Capture the raw JSONL of both turns as a fixture.codex exec resume <id> --json "<turn>"accepts the flags/positions we need (write flag,--json, turn text) without conflict.codex execruns in the same CWD — reproduce or rule out the--last-class hijack for the explicit-id form.Exit criteria
runOneShot, and the runner-side persistence keyed likewriteState/readState, with the [design] The platform owns the conversation — an engine's own memory is an optimisation, not the source of truth #693 revisit note attached.docs/coding-engines.mdso it isn't re-attempted.Refs: #729, #693, #556; upstream
openai/codex#15538,#3817.