Skip to content

home folder codex requirements

Mike Crowe edited this page Jul 5, 2026 · 3 revisions

Surfacing Codex CLI project history out of an isolated container

Status: research / decided Date: 2026-07-03 Companions: home-folder-claude-requirements.md, home-folder-omp-requirements.md Governing principle: containerize the configuration, not the storage — conversations, usage, and resume state must persist to the host and stay coherent regardless of which harnessed stack (tool configuration) launched the session. See cross-harness-identity-and-rules.md. Decision: Surface the date-keyed sessions/YYYY/MM/DD/rollout-*.jsonl tree whole (collision-free, like Claude's UUID-keyed dirs). The threads table in state_5.sqlite is load-bearing (non-empty, used for resume/listing) — unlike omp's empty threads table, this needs a guarded, cwd-filtered teardown export, not a hands-off "leave it fresh." Run the container at the host project path (path mirroring), same decision as Claude and omp.

Headline difference from Claude and omp

Codex's session rollouts are date-keyed, not project-keyed:

~/.codex/sessions/2026/04/15/rollout-2026-04-15T18-32-01-<uuid>.jsonl

There's no per-project subdirectory to mount narrowly — you can't know the date ahead of time. This is the same shape as Claude's UUID-keyed dirs (file-history/, tasks/, ...): mount the whole sessions/ tree, accept read-side cross-project visibility as the cost.

Unlike omp, though, codex's SQLite thread index is genuinely in use: state_5.sqlite's threads table had 3 rows on this host (verified), each with cwd, rollout_path, title, tokens_used, and more — this is what powers codex resume/session listing. omp's equivalent table was empty and could safely be left alone; codex's can't be.

Layout of ~/.codex (skimmed 2026-07-03)

Path Kind Keyed by Surface?
sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl History — full transcript per session date + session UUID (embeds cwd per line, confirmed) Yes — mount whole
state_5.sqlite (+-shm/-wal) threads table Resume/listing index — id, rollout_path, cwd, title, tokens_used, git_sha, git_branch, ... shared DB, cwd column Yes — guarded teardown export by cwd, not mount (load-bearing, unlike omp)
history.jsonl History — global prompt log, {session_id, ts, text} global append, session_id (join to threads.cwd for per-project) Yes, guarded — same append-only-file hazard as Claude's history.jsonl
auth.json Auth secret (auth_mode, tokens.{id_token,access_token,refresh_token}, account_id) — dedicated, small, single-purpose file Handled by the auth-seed layer (ro-mount candidate — same shape as Claude's .credentials.json), not this doc's concern
memories/ Codex's own long-term memory feature Empty on this host; unverified whether it should surface — revisit once populated
context-mode/ Third-party MCP plugin's own session DBs (unrelated to core codex) plugin-managed No — plugin's own concern
logs_2.sqlite (+-shm/-wal) Telemetry/debug logs table No — not conversation history
config.toml, AGENTS.md, skills/, cache/, models_cache.json, sandbox/, shell_snapshots/, installation_id, version.json, .tmp/ Config / cache No

Why the thread index can't be ignored like omp's

omp's agent.db.threads was empty (0 rows) on the host it was researched on — resume worked off the file-based rollouts alone, so leaving the container with a fresh agent.db was safe. Codex's threads table is actively populated (3 rows here, real cwd/title/tokens_used data) — giving a container a fresh, empty state_5.sqlite would mean codex resume inside the container can't see history that is on disk in sessions/, and conversely, a session run in the container wouldn't appear in the host's resume list without an export. This needs the same guarded, cwd-filtered INSERT-at-teardown pattern already used for Claude's history.jsonl and omp's history.db — not a "just don't touch it" pass.

Implementation guidance

  1. Path mirroring (same decision as Claude/omp): run the container at the identical host absolute path so the embedded cwd in rollout lines and threads.cwd line up with no remap.
  2. Mount sessions/ whole, rw — date-keyed, collision-free (new sessions only ever add new date/uuid files).
  3. threads table → guarded teardown export by cwd, parallel to Claude's history.jsonl merge and omp's history.db export: INSERT ... SELECT ... WHERE cwd = '<project>' into the host state_5.sqlite, wrapped so a schema mismatch logs and no-ops. Ship disabled until the schema is pinned (it already carries a nontrivial column count from migrations — cli_version, agent_nickname, memory_mode, etc. — confirm none of that needs container-side values before merging).
  4. history.jsonl → guarded append-merge filtered on session IDs that belong to this project (join against the exported threads rows, or filter by cwd once available) — same hazard class as Claude's global prompt log.
  5. auth.json → ro-mount, same pattern as Claude's .credentials.json (dedicated file, auth_mode/tokens/account_id — narrow, no co-mingled state).
  6. Data-driven mount manifest + oracle test, same as the other three docs.

Risks / open questions

  • memories/ is empty on this host — codex ships a long-term-memory feature but nothing has populated it here. Revisit once there's real content to classify.
  • threads schema has accumulated many migration columns (agent_nickname, agent_role, memory_mode, reasoning_effort, agent_path, ...) — the guarded export needs to tolerate columns this research pass didn't fully characterize; treat unknown columns as pass-through, not a reason to fail the merge.
  • logs_2.sqlite was assumed non-history based on table name (logs) alone — not opened in detail. Low risk, but not fully verified empty of anything conversation-relevant.

Verification done

  • fd skim of ~/.codex to depth 3 (excluding the unrelated context-mode/ plugin subtree).
  • Confirmed sessions/YYYY/MM/DD/rollout-*.jsonl naming and that rollout lines embed "cwd" (grepped directly, confirmed real project path).
  • Confirmed state_5.sqlite threads schema and non-empty (3 rows, real cwd/title data) — the key difference from omp's empty threads table.
  • Confirmed history.jsonl schema: {session_id, ts, text}.
  • Confirmed auth.json is a small, dedicated file (not co-mingled with other state).
  • state_5.sqlite/logs_2.sqlite both have -shm/-wal files present — WAL mode, same concurrent-writer hazard as Claude/omp's SQLite stores.

Clone this wiki locally