# How Codex Creates and Rotates Job Log Files for Background Tasks

> Learn how Codex automatically creates and rotates job log files for background tasks. It prunes entries to the 50 most recent jobs, ensuring bounded disk usage and recent logs for debugging.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: how-to-guide
- Published: 2026-07-14

---

**Codex manages background task logs through a deterministic state directory system that automatically prunes entries to the most recent 50 jobs every time state is persisted, ensuring bounded disk usage while maintaining recent logs for debugging.**

The `openai/codex-plugin-cc` repository implements a self-cleaning logging system for background operations such as code reviews, adversarial scans, and custom commands. Each execution runs as a **job** with dedicated log files stored in a workspace-specific state directory. Understanding how Codex creates and rotates these job log files helps developers debug failed tasks and manage disk space in long-running environments.

## Resolving the State and Jobs Directory

Codex determines where to store logs through a hierarchical resolution process that begins with the workspace root and ends with a unique job-specific file path.

### Locating the Workspace State Directory

The function `resolveStateDir(cwd)` in `plugins/codex/scripts/lib/state.mjs` (lines 29-44) constructs a deterministic directory path based on the current working directory. By default, Codex stores state under `${HOME}/.cache/.../codex-companion`, though this can be overridden by setting the `CLAUDE_PLUGIN_DATA` environment variable. The function hashes the workspace path to create a unique slug, ensuring that different projects maintain isolated log environments.

### Determining the Jobs Subdirectory

Once the state directory is established, `resolveJobsDir(cwd)` (lines 50-52 in `state.mjs`) simply appends the constant `jobs` folder to the resolved state path. This separation keeps job logs distinct from other state files like configuration or metadata stores.

## Creating Job Log Files

Log creation happens on-demand when tasks begin writing output, with file paths resolved deterministically before any data is written.

### Resolving Individual Log Paths

The `resolveJobLogFile(cwd, jobId)` function (lines 183-186 in `state.mjs`) returns a formatted path: `${jobsDir}/${jobId}.log`. The file is created automatically upon the first write operation—typically via `fs.appendFileSync` or `fs.writeFileSync`—rather than being pre-allocated. This lazy creation ensures that empty log files never clutter the system if a job fails before producing output.

### Writing Log Entries During Execution

Throughout task execution, particularly within `plugins/codex/scripts/lib/job-control.mjs`, Codex appends structured log entries to the resolved path. Each entry typically includes ISO 8601 timestamps, reasoning summaries from the language model, and assistant messages. The following pattern demonstrates how skill implementations write to job logs:

```javascript
import { resolveJobLogFile } from "./plugins/codex/scripts/lib/state.mjs";
import fs from "node:fs";

const workspaceRoot = "/path/to/your/project";
const jobId = "review-2025-01-15-001";

const logPath = resolveJobLogFile(workspaceRoot, jobId);
// → /home/user/.cache/codex-companion/<hash>/jobs/review-2025-01-15-001.log

function log(jobId, message) {
  const path = resolveJobLogFile(workspaceRoot, jobId);
  const line = `[${new Date().toISOString()}] ${message}\n`;
  fs.appendFileSync(path, line, "utf8");
}

log(jobId, "Starting Codex review");
log(jobId, "Analyzing changed files for regression paths");

```

## Rotating Job Log Files

Codex implements **state-driven rotation**, meaning cleanup occurs automatically during persistence operations rather than through time-based or size-based triggers.

### Pruning Old Jobs (MAX_JOBS = 50)

When `saveState(cwd, state)` is called, it invokes `pruneJobs(state.jobs)` (lines 80-84 in `state.mjs`). This function sorts all jobs by their `updatedAt` timestamp and retains only the most recent **50 entries** defined by the `MAX_JOBS` constant. Jobs falling outside this window are removed from the state array, making them eligible for file system cleanup.

### Cleaning Up Orphaned Log Files

After pruning the state array, `saveState` performs physical file cleanup (lines 90-108 in `state.mjs`). The function identifies all files in the jobs directory matching `${id}.json` and `${id}.log` patterns, then deletes any files whose IDs no longer exist in the retained job list using the `removeFileIfExists` helper. This guarantees that every state save operation synchronizes the file system with the in-memory job registry.

```javascript
import { loadState, saveState } from "./plugins/codex/scripts/lib/state.mjs";

let state = loadState(workspaceRoot);
// ... modify state.jobs array ...

// This call automatically prunes to 50 jobs and deletes orphaned .log files
saveState(workspaceRoot, state);

```

## Summary

- **Deterministic paths**: Codex uses `resolveStateDir` and `resolveJobLogFile` in `state.mjs` to create predictable log locations based on workspace paths and job IDs.
- **Lazy file creation**: Log files are created on-demand when `fs.appendFileSync` is first called, not pre-allocated.
- **Hard limit**: The system enforces a maximum of **50 jobs** via `MAX_JOBS` in the `pruneJobs` function.
- **Synchronous cleanup**: Log rotation happens during every `saveState` call, removing `.log` and `.json` files for jobs that exceed the retention limit.
- **Location flexibility**: Default storage uses `${HOME}/.cache`, but `CLAUDE_PLUGIN_DATA` overrides this for custom deployments.

## Frequently Asked Questions

### Where are Codex job log files stored by default?

By default, Codex stores job log files in `${HOME}/.cache/.../codex-companion` under a hashed directory derived from the workspace path, specifically within a `jobs` subdirectory. You can override this location by setting the `CLAUDE_PLUGIN_DATA` environment variable before running Codex commands.

### How many background job logs does Codex retain?

Codex retains logs for the **50 most recent jobs** according to the `MAX_JOBS` constant defined in `state.mjs`. When `saveState` is called, the `pruneJobs` function sorts jobs by `updatedAt` and removes older entries from both the state registry and the file system.

### Does Codex compress or archive old job logs before deletion?

No, Codex does not implement compression or archiving. The rotation mechanism in `saveState` (lines 90-108 in `state.mjs`) permanently deletes orphaned `.log` files using `removeFileIfExists` once they fall outside the 50-job retention window. If you need historical logs, you must back them up before state persistence occurs.

### Can I manually trigger log rotation in Codex?

Yes, you can trigger rotation by calling `saveState(cwd, state)` from `plugins/codex/scripts/lib/state.mjs`. This function will prune jobs to the most recent 50 entries and delete any associated log files no longer present in the state. Simply loading the current state, making no modifications, and saving it will force an immediate cleanup of old logs.