# How Background Job Execution Works in the Codex Plugin: A Deep Dive into the OpenAI Codex Companion

> Explore how the Codex plugin handles background job execution. Learn how detached Node.js processes, JSON state files, and log streaming enable long-running tasks to persist post CLI exit.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: deep-dive
- Published: 2026-07-07

---

**The Codex plugin implements background job execution by spawning detached Node.js child processes that persist job state to JSON files and stream progress to dedicated log files, allowing long-running tasks to survive after the parent CLI exits.**

The `openai/codex-plugin-cc` repository provides a robust asynchronous job system for long-running tasks such as code reviews and rescue operations. When users invoke the Codex companion with the `--background` flag, the system transitions from synchronous execution to a persistent background model that tracks progress via JSON state files and per-job logs. This architecture ensures that expensive operations continue even after the terminal session ends or the parent process exits.

## Core Architecture of Background Job Execution

The background job system spans four primary modules that coordinate state persistence, process management, and progress tracking:

1. **`state.mjs`** – Handles JSON-based persistence of job metadata in the user’s temporary directory.
2. **`tracked-jobs.mjs`** – Manages log file creation, job record construction, and the execution wrapper `runTrackedJob`.
3. **`codex-companion.mjs`** – Entry point that enqueues tasks and spawns detached workers via `enqueueBackgroundTask`.
4. **`job-control.mjs`** – Provides status snapshots and phase inference for the `/codex:status` command.

## Job State Management and Persistence

### State Storage in `state.mjs`

All background job metadata lives in a JSON-based state directory under the user’s temporary folder. The `state.mjs` module exports utilities to load, save, prune, and upsert job records. When a job transitions from foreground to background, the system writes a structured record containing the job ID, status, timestamps, PID, and log file path.

### Tracking Progress with `tracked-jobs.mjs`

The `tracked-jobs.mjs` file defines `createTrackedProgress`, which initializes a unique log file for each job, and `writeJobFile`, which serializes the job record to disk. These functions ensure that both the worker process and subsequent CLI invocations can locate and update the same job state.

## Enqueueing and Spawning Detached Workers

### The `enqueueBackgroundTask` Flow

Located in `codex-companion.mjs`, the `enqueueBackgroundTask` function orchestrates the transition from foreground to background. It performs the following steps:

- Calls `createTrackedProgress` to reserve a log file path.
- Writes "Queued for background execution." to the log via `appendLogLine`.
- Invokes `spawnDetachedTaskWorker` to fork a new Node process.
- Persists a "queued" job record using `writeJobFile` and `upsertJob`, capturing the child PID.

### Process Detachment with `spawnDetachedTaskWorker`

The `spawnDetachedTaskWorker` function constructs a detached child process using Node’s `spawn` with specific options that ensure survival after parent exit:

```javascript
import { spawn } from "child_process";
import path from "path";

function spawnDetachedTaskWorker(cwd, jobId) {
  const script = path.join(ROOT_DIR, "scripts", "codex-companion.mjs");
  const child = spawn(process.execPath, [script, "task-worker", "--cwd", cwd, "--job-id", jobId], {
    cwd,
    env: process.env,
    detached: true,
    stdio: "ignore",
    windowsHide: true,
  });
  child.unref();
  return child;
}

```

The `detached: true` and `stdio: "ignore"` options allow the parent to exit immediately while the worker continues execution. The `windowsHide` flag ensures clean operation on Windows systems.

## The Detached Worker Lifecycle

### Running Jobs with `runTrackedJob`

Once detached, the child process runs `codex-companion.mjs` with the `task-worker` sub-command. It invokes `runTrackedJob` from `tracked-jobs.mjs`, which executes the actual command and manages the job lifecycle:

```javascript
import { runTrackedJob } from "./tracked-jobs.mjs";

async function workerMain(job, runner) {
  // runner receives a progress reporter bound to the job’s log file
  const execResult = await runTrackedJob(job, () => runner(progress), { logFile: job.logFile });
  // execResult contains { exitStatus, payload, rendered, ... }
}

```

### Log Appending and Status Updates

During execution, the worker updates the job record via `appendLogLine` and `appendLogBlock`. When the runner finishes, `runTrackedJob` atomically updates the status to `completed` or `failed`, records timestamps, captures output, and clears the PID field.

## Querying Job Status and Results

The `job-control.mjs` module provides the `enrichJob` function, which reads the per-job log preview and infers the current phase (e.g., "queued", "running", "completed"). This powers the `/codex:status` command, allowing users to query elapsed times and current states even for jobs started in previous terminal sessions.

## Summary

- **Persistence**: Job state is stored as JSON in the user’s temporary directory via `state.mjs`, ensuring data survives process restarts.
- **Detachment**: Background jobs are spawned using `spawnDetachedTaskWorker` with `detached: true` and `stdio: "ignore"` to isolate them from the parent process.
- **Tracking**: `tracked-jobs.mjs` provides `createTrackedProgress` and `runTrackedJob` to manage log files and lifecycle updates.
- **Enqueueing**: The `enqueueBackgroundTask` function in `codex-companion.mjs` coordinates the handoff from foreground to background.
- **Monitoring**: `job-control.mjs` enables status queries and progress previews through the `enrichJob` helper.

## Frequently Asked Questions

### How does the Codex plugin detach a background job from the parent process?

The plugin uses Node.js’s `child_process.spawn` with the `detached: true` option in `spawnDetachedTaskWorker` (located in `codex-companion.mjs`). This creates a new process group independent of the parent, while `stdio: "ignore"` and `child.unref()` ensure the parent can exit without waiting for the child to finish.

### Where does the Codex plugin store background job state and logs?

Job metadata is persisted as JSON files in a state directory under the user’s temporary folder, managed by `state.mjs`. Per-job logs are created by `createTrackedProgress` in `tracked-jobs.mjs` and updated via `appendLogLine` during execution.

### Can I check the status of a background job after the CLI exits?

Yes. Because job state is written to disk, the `job-control.mjs` module can read the same JSON records and log files in subsequent CLI invocations. The `/codex:status` command uses `enrichJob` to display current phase, elapsed time, and log previews.

### How does the plugin handle job completion or failure in detached mode?

The `runTrackedJob` function in `tracked-jobs.mjs` captures the exit status of the underlying command and updates the job record to `completed` or `failed`. It records timestamps, persists final output, and clears the PID field, allowing `job-control.mjs` to report the final state accurately.