How Codex Job Results Are Stored and Retrieved in openai/codex-plugin-cc
Codex job results are persisted as JSON files in a per-repository state directory, indexed by a central state.json manifest, and retrieved via a two-step lookup that resolves the job ID against the manifest before loading the full payload.
When a Codex command such as review, task, or transfer completes in the openai/codex-plugin-cc repository, the companion script creates a durable job record that enables post-execution analysis and auditing. Understanding how Codex job results are stored and retrieved is essential for building integrations that consume Codex outputs programmatically or inspect historical runs via the CLI.
State Directory Structure and Location
Resolving the State Directory
The storage location is determined by state.resolveStateDir() in plugins/codex/scripts/lib/state.mjs. This function constructs a path from the repository root, a hash of that path, and either the CLAUDE_PLUGIN_DATA environment variable or a fallback temporary directory at /tmp/codex-companion.
File Layout and Persistence
Within the state directory, the system maintains a jobs/ subdirectory containing paired files for each execution:
<job-id>.json— Stores the full JSON payload including the raw result, rendered markdown, timestamps, and execution metadata. Written bystate.writeJobFile().<job-id>.log— Contains plain-text progress lines captured during execution, created bytracked-jobs.createJobLogFile().
A top-level state.json file acts as a manifest, maintaining a rolling array of the most recent jobs (default maximum of 50 entries) with lightweight metadata for status and cancel commands.
Job Record Creation and Storage
Writing the Job File
When tracked-jobs.runTrackedJob() executes, it creates an initial running record and persists it via writeJobFile(). After the Codex process terminates, the companion updates the file with the final state:
// After the Codex runner finishes (tracked-jobs.mjs)
writeJobFile(job.workspaceRoot, job.id, {
...runningRecord,
status: completionStatus,
threadId: execution.threadId,
turnId: execution.turnId,
phase: completionStatus === "completed" ? "done" : "failed",
completedAt,
result: execution.payload, // Full Codex output
rendered: execution.rendered, // Markdown version
});
Updating the State Manifest
Simultaneously, state.upsertJob() updates state.json to include the job's metadata (ID, status, timestamps), ensuring the status command can list recent jobs without parsing individual JSON files.
Retrieving Stored Results
The Result Sub-Command Flow
The result sub-command in codex-companion.mjs (around line 1055) implements a two-stage retrieval process:
const { workspaceRoot, job } = resolveResultJob(cwd, reference);
const storedJob = readStoredJob(workspaceRoot, job.id);
resolveResultJob()(injob-control.mjs) selects a finished job (status ofcompleted,failed, orcancelled) by ID or prefix match.readStoredJob()loads the<job-id>.jsonfile from the state directory.
Rendering and Presentation
For human consumption, renderStoredJobResult() in render.mjs formats the stored payload into a readable view containing the verdict, summary, findings, artifacts, and follow-up commands. For programmatic access, the --json flag returns the raw payload directly.
Programmatic Access Patterns
Fetching Results via CLI
To retrieve a specific job from the terminal:
# Display formatted result
codex result task-abc123
# Get raw JSON payload
codex result task-abc123 --json
Accessing Results via Node.js API
For integration with other tools, import the state and job-control modules:
import { resolveJobFile, readJobFile } from "./lib/state.mjs";
import { resolveResultJob } from "./lib/job-control.mjs";
async function getJobResult(cwd, jobId) {
const { workspaceRoot, job } = resolveResultJob(cwd, jobId);
const resultPath = resolveJobFile(workspaceRoot, job.id);
const payload = readJobFile(resultPath); // Contains `result` and `rendered`
return payload;
}
Key Source Files and Responsibilities
state.mjs— Handles state directory resolution,state.jsonmanifest management, and per-job file I/O (writeJobFile,readJobFile).tracked-jobs.mjs— Orchestrates job lifecycle, creates log files, and persists final results viawriteJobFile().job-control.mjs— ProvidesresolveResultJob()for job selection andreadStoredJob()for file retrieval.render.mjs— Transforms stored JSON into human-readable output viarenderStoredJobResult().codex-companion.mjs— CLI entry point that wires theresultcommand to the storage subsystem.
Summary
- Codex job results are stored as JSON files in a repository-specific state directory under
<state-dir>/jobs/<job-id>.json. - A
state.jsonmanifest tracks the most recent 50 jobs for quick listing and status checks. - Each job includes a companion
.logfile containing execution progress. - Retrieval requires resolving the job ID against the manifest, then loading the specific JSON payload via
readStoredJob(). - Results can be rendered as formatted markdown or exported as raw JSON using the
--jsonflag.
Frequently Asked Questions
Where are Codex job results physically stored?
The companion script writes results to a directory resolved by state.resolveStateDir() in plugins/codex/scripts/lib/state.mjs. This location combines the repository root path hash with the CLAUDE_PLUGIN_DATA environment variable or falls back to /tmp/codex-companion, creating a jobs/ subdirectory containing <job-id>.json and <job-id>.log files.
How long are job results retained?
The system maintains a rolling window of the most recent 50 jobs in state.json for quick reference, but the individual JSON and log files in the jobs/ directory persist indefinitely until manually deleted or the state directory is cleared.
Can I retrieve a job result using only a partial ID?
Yes. The resolveResultJob() function in job-control.mjs accepts job ID prefixes and resolves them to full IDs, provided the prefix uniquely identifies a finished job (completed, failed, or cancelled).
What is the difference between the result and rendered fields in the stored JSON?
The result field contains the raw, structured payload returned by the Codex model, while the rendered field contains a pre-formatted markdown representation suitable for terminal display without additional processing.
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 →