Codex-Plugin-CC State Persistence: Where and How Plugin Data is Stored
The codex-plugin-cc persists all runtime configuration, job metadata, and per-job files to a JSON file named state.json located in a workspace-specific directory under the OS temporary directory by default, or under the path specified by the CLAUDE_PLUGIN_DATA environment variable.
The openai/codex-plugin-cc repository implements a deterministic persistence layer to maintain plugin state across process restarts. All state management logic lives in plugins/codex/scripts/lib/state.mjs, which handles directory resolution, atomic updates, and automatic pruning of stale job data. Understanding this codex-plugin-cc state persistence mechanism is essential for debugging, backup strategies, and containerized deployments.
State Storage Location and Directory Resolution
The plugin isolates state per workspace to prevent collisions between different repositories on the same machine.
Workspace-Specific Directory Structure
The resolveStateDir(cwd) function (lines 29‑44 in plugins/codex/scripts/lib/state.mjs) generates a unique directory name derived from the workspace root:
- Finds the workspace root via
resolveWorkspaceRoot(implemented inworkspace.mjs). - Normalizes the path using
realpathSyncto resolve symlinks. - Creates a slug combining the folder name and the first 16 characters of a SHA‑256 hash of the absolute path.
- Selects the root state directory:
- If
CLAUDE_PLUGIN_DATAis set, uses<env>/state. - Otherwise falls back to
os.tmpdir()/codex‑companion.
- If
- Returns the fully resolved path as
<stateRoot>/<slug‑hash>.
This guarantees that two repositories named project in different locations never share state.
Environment Variable Override
Set CLAUDE_PLUGIN_DATA to redirect all state storage to a custom location. This is critical for CI pipelines, Docker containers with mounted volumes, or user scripts requiring persistent storage outside the temporary directory.
State File Structure and Schema
The Main State File
The constant STATE_VERSION = 1 (line 8) guards against future schema changes. The plugin writes all data to a file named state.json (line 11), located at the path returned by resolveStateFile(cwd) (lines 46‑48), which simply joins the state directory with the filename.
Jobs Subdirectory
Per-job artifacts reside in a jobs subfolder (lines 12‑13). The function resolveJobsDir(cwd) (lines 50‑52) constructs this path, while ensureStateDir(cwd) (lines 54‑56) recursively creates both the state directory and the jobs subdirectory if they do not exist.
Core Persistence Operations
Loading State
The loadState(cwd) function (lines 58‑78) reads state.json from disk. If the file does not exist, it returns a fresh object from defaultState(). If JSON parsing fails due to corruption, the function gracefully falls back to defaults rather than crashing.
Saving and Compacting State
saveState(cwd, state) (lines 92‑116) handles persistence with several safeguards:
- Merges the new state with defaults to ensure schema completeness.
- Prunes the job list to
MAX_JOBS(50 entries), removing the oldest entries. - Deletes stale job files and logs associated with pruned entries.
- Writes compacted, pretty‑printed JSON to
state.json.
Atomic Updates
For thread‑safe modifications, updateState(cwd, mutate) (lines 118‑122) implements an atomic load‑mutate‑save pattern. It loads the current state, applies the caller’s mutation function, and persists the result via saveState.
Job-Specific Persistence
Individual job payloads and logs are stored separately from the main state file to prevent state.json from growing unbounded.
writeJobFile(cwd, jobId, data)andreadJobFile(cwd, jobId)(lines 66‑91) serialize job payloads to<id>.jsoninside thejobsdirectory.resolveJobFile(cwd, jobId)returns the absolute path to a job’s JSON file.resolveJobLogFile(cwd, jobId)(lines 166‑191) returns the path to<id>.logfor streaming runtime logs.
Configuration Persistence
Plugin configuration values are stored within the config subtree of state.json.
getConfig(cwd)(lines 53‑64) retrieves the current configuration object.setConfig(cwd, key, value)updates a specific key and persists the change.
Practical Usage Examples
The following example demonstrates typical interactions with the persistence layer:
import {
loadState,
upsertJob,
setConfig,
getConfig,
writeJobFile,
resolveJobLogFile,
} from "./lib/state.mjs";
// 1. Read the current config (e.g., stop‑review‑gate flag)
const { stopReviewGate } = getConfig(process.cwd());
// 2. Toggle a config key
await setConfig(process.cwd(), "stopReviewGate", !stopReviewGate);
// 3. Create a new job entry and persist it
const jobId = generateJobId("review");
await upsertJob(process.cwd(), {
id: jobId,
status: "queued",
description: "Running review on the latest commit",
});
// 4. Store arbitrary payload for that job
await writeJobFile(process.cwd(), jobId, { steps: [], result: null });
// 5. Get the path to the job’s log file (the runtime writes to it directly)
const logPath = resolveJobLogFile(process.cwd(), jobId);
All functions ultimately resolve paths via resolveStateDir and read/write JSON through the loadState and saveState mechanisms described above.
Summary
- Location: State resides in
state.jsonwithin a directory derived from the workspace path hash, defaulting to the OS temp directory (/tmp/codex‑companion) unless overridden byCLAUDE_PLUGIN_DATA. - Schema: Versioned JSON (
STATE_VERSION = 1) with a separatejobssubdirectory for per‑job files. - API: Core functions in
plugins/codex/scripts/lib/state.mjsincludeloadState,saveState,updateState,writeJobFile, andresolveJobLogFile. - Limits: The plugin retains a maximum of 50 jobs (
MAX_JOBS), automatically pruning older entries and their associated files on save. - Resilience: The loader gracefully handles missing or corrupted JSON files by falling back to default state.
Frequently Asked Questions
Where does codex-plugin-cc store its state by default?
By default, the plugin stores state in the OS temporary directory under codex‑companion/<workspace‑slug>/state.json. The exact path is determined by hashing the canonical workspace root path to ensure isolation between different repositories.
How can I change the state storage location for codex-plugin-cc?
Set the CLAUDE_PLUGIN_DATA environment variable to an absolute path. The plugin will create a state subdirectory inside that path and store all data there, bypassing the default temporary directory. This is essential for Docker containers or CI environments requiring persistent volumes.
What happens if the state.json file becomes corrupted?
The loadState function in plugins/codex/scripts/lib/state.mjs catches JSON parse errors and returns a fresh default state object. This prevents the plugin from crashing on startup due to disk corruption or manual file edits, though it will result in loss of previous job history.
How many jobs does the plugin keep in state?
The plugin maintains a maximum of 50 jobs in the state file, defined by the MAX_JOBS constant. When saving state, saveState automatically prunes entries beyond this limit and deletes their associated .json payload and .log files from the jobs subdirectory to prevent unbounded disk growth.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →