# How Codex-Plugin-CC Manages Job State and Persists Job Data

> Learn how Codex-Plugin-CC manages job state with a file-based state machine and persists job data atomically to disk in its dedicated workspace directories.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-08-01

---

**Codex-Plugin-CC uses a file-based state machine where each workspace gets a dedicated state directory containing a global [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) file and per-job JSON and log files, with all mutations atomically persisted to disk.**

The `openai/codex-plugin-cc` repository implements lightweight job state management through a deterministic directory structure and simple JSON serialization. This design enables durable tracking of review, rescue, and cancel operations across process restarts without requiring an external database.

## Architecture Overview

The job state management flow consists of three coordinated layers that handle directory resolution, job lifecycle tracking, and persistent storage.

### Layer 1: State Directory Resolution

Every workspace receives a unique, deterministic state directory computed by `resolveStateDir()` in `plugins/codex/scripts/lib/state.mjs`. This function builds a path combining the plugin's data directory with a workspace-specific hash.

```javascript
// From plugins/codex/scripts/lib/state.mjs
const stateDir = resolveStateDir(workspace);
// Returns: <pluginDataDir>/state/<workspace-hash>

```

The companion function `resolveStateFile()` points to `<stateDir>/state.json`, which serves as the single source of truth for all job metadata.

### Layer 2: Job Creation and File Allocation

When a new job initiates, `createJob()` in `plugins/codex/scripts/lib/job-control.mjs` performs three critical operations:

- Allocates a unique job ID
- Generates file paths for the job's JSON metadata and plain-text log
- Appends the job record to the in-memory `state.jobs` array

```javascript
import { createJob, saveState } from "./job-control.mjs";

const job = createJob(state, {
  id: "review-123",
  name: "review",
  status: "running"
});

// Job files resolved automatically:
// - <stateDir>/jobs/review-123.json
// - <stateDir>/jobs/review-123.log
await saveState(state);

```

### Layer 3: State Mutation and Atomic Persistence

All status updates flow through `updateJob()` and `finaliseJob()`, with `saveState()` flushing changes to disk after every mutation. This guarantees durability: a crash at any point leaves [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) in a valid, restorable condition.

```javascript
import { updateJob, finaliseJob, saveState } from "./job-control.mjs";

// Mark job as finished
updateJob(state, "review-123", {
  status: "finished",
  endTime: new Date().toISOString()
});
await saveState(state);

```

## Where Job Data Is Persisted

The persistence model uses a hierarchical file structure with clear separation between global state and individual job records.

### Directory Structure

```

<stateDir>/
├── state.json          # Global state: workspace info + jobs array

└── jobs/
    ├── <jobId>.json    # Individual job metadata (mirrors state.jobs entry)

    └── <jobId>.log     # Plain-text execution log

```

### State File Schema

The [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) file contains a top-level object with workspace identification and a jobs array:

```json
{
  "workspace": "/path/to/repository",
  "jobs": [
    {
      "id": "review-abc",
      "name": "review",
      "status": "running",
      "logFile": "/path/to/state/jobs/review-abc.log",
      "startTime": "2024-01-15T09:30:00Z",
      "endTime": null
    }
  ],
  "lastTurnStart": {}
}

```

### Per-Job Files

Each job maintains two dedicated files under `<stateDir>/jobs/`:

- **`<jobId>.json`**: JSON serialization of the job record (redundant with [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) entry, enables direct job loading)
- **`<jobId>.log`**: Append-only text stream written by runtime code in `process.mjs`

## Key Implementation Files

| File | Purpose |
|------|---------|
| `plugins/codex/scripts/lib/state.mjs` | Directory resolution, state loading/saving primitives |
| `plugins/codex/scripts/lib/job-control.mjs` | Job lifecycle management: creation, updates, finalization |
| `plugins/codex/scripts/lib/tracked-jobs.mjs` | In-memory registry of active jobs tied to persisted state |
| `tests/state.test.mjs` | Unit tests validating directory layout and JSON schema |
| `tests/runtime.test.mjs` | Integration tests covering full job lifecycle |

## Practical Code Examples

### Restoring State After Restart

```javascript
import { resolveStateFile } from "./state.mjs";
import fs from "fs";

const statePath = resolveStateFile(workspace);

// Reconstruct full state from disk
const state = fs.existsSync(statePath)
  ? JSON.parse(fs.readFileSync(statePath, "utf8"))
  : { workspace, jobs: [] };

// Resume or clean up interrupted jobs
const runningJobs = state.jobs.filter(j => j.status === "running");
console.log(`Found ${runningJobs.length} jobs to recover`);

```

### Appending to Job Logs

The runtime writes to log files directly using standard file operations, with paths resolved through `resolveJobLogFile()`:

```javascript
import { resolveJobLogFile } from "./job-control.mjs";
import fs from "fs";

const logPath = resolveJobLogFile(state, jobId);
fs.appendFileSync(logPath, `[${new Date().toISOString()}] Processing file...\n`);

```

## Summary

- **Directory resolution**: `resolveStateDir()` creates deterministic, workspace-isolated storage locations
- **Global state**: [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) maintains the complete job registry and workspace metadata
- **Job isolation**: Each job receives dedicated JSON and log files under `<stateDir>/jobs/`
- **Durability guarantee**: Every state mutation triggers `saveState()`, atomically rewriting [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json)

## Frequently Asked Questions

### How does Codex-Plugin-CC handle state recovery after a crash?

The system reads [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) on startup and reconstructs the in-memory state object. Any jobs previously marked as `running` can be identified and handled appropriately—either resumed or marked for cleanup—based on application-specific logic in the broker layer.

### Where is the state directory located in production versus testing?

Production resolves to `path.join(pluginDataDir, "state", "<workspace-hash>")` within the plugin's data folder. Tests use temporary directories to ensure isolation, with `resolveStateDir()` accepting workspace paths that trigger this temporary resolution mode.

### Why store both [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) and individual job JSON files?

The global [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) enables fast loading of complete state, while `<jobId>.json` files support direct access patterns and serve as redundancy. This dual structure simplifies debugging and allows external tools to inspect specific jobs without parsing the full state document.

### Is job state shared across multiple concurrent processes?

No. The file-based design assumes single-process access. Concurrent modifications risk file corruption; the implementation targets the plugin's single-user, single-process execution model.