# Codex Plugin Job Cancellation Cleanup: Operations and Source Code Analysis

> Explore Codex plugin job cancellation cleanup operations. Learn how OpenAI's plugin handles job termination, state persistence, and process removal for efficient resource management. Analyze the source code.

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

---

**When you cancel a job in the OpenAI Codex plugin, the system sends a turn interrupt to the shared app-server, terminates the running process, updates the job status to "cancelled", persists the state to storage, and removes the job from the active tracking list.**

The `openai/codex-plugin-cc` repository implements a robust cleanup sequence when the `/codex:cancel` command is invoked. This ensures that brokered tasks are safely halted and the internal job state remains consistent. Below is the complete breakdown of the cleanup operations as implemented in the source code.

## Step-by-Step Cleanup Operations

### Turn Interrupt Signal to App-Server

The first cleanup action occurs in `plugins/codex/scripts/codex-companion.mjs` at lines 1017–1018. Before terminating the process, the plugin sends a **turn interrupt** to the shared app-server. This aborts any pending LLM requests and prevents the server from continuing to process tokens for the job being cancelled.

### Process Termination and PID Clearing

If the job is running as a local process, the system terminates it and clears the process identifier. In `plugins/codex/scripts/lib/job-control.mjs`, the cancellation path explicitly sets `job.pid = null` after killing the process. This ensures the plugin does not attempt to interact with a defunct process during subsequent status checks.

### Status and Phase Updates

The job record is updated to reflect the termination. According to lines 992–1002 in `codex-companion.mjs`, the status field is set to `"cancelled"` and the phase is also set to `"cancelled"` on the returned payload. This dual update guarantees that both the internal state and the user-facing response indicate the job is no longer active.

### Persistent State Synchronization

After updating the job object, the changes are written to the persistent state store. The `job-control.mjs` module handles this persistence, ensuring that future queries via `/codex:status` return the cancelled status even if the session restarts. This write operation commits the `cancelled` state and the null PID to disk.

### Active Job List Removal

The plugin maintains a list of active jobs to prevent duplicate operations. In `plugins/codex/scripts/lib/tracked-jobs.mjs`, the cancelled job is filtered out from the active jobs collection. This cleanup prevents the system from attempting to cancel the same job twice and ensures that resource counters accurately reflect only running work.

### Response Payload Confirmation

The cancellation command returns a JSON payload confirming the operations. As verified in `tests/runtime.test.mjs`, the response includes `status: "cancelled"`, `turnInterruptAttempted: true`, and `turnInterrupted: true`. These fields provide programmatic confirmation that the interrupt signal was sent and the job state was updated.

## Code Examples

### Cancelling a Job via CLI

Execute the cancel command from the repository root to terminate the active job:

```bash
node plugins/codex/scripts/codex-companion.mjs cancel --json

```

### Programmatic Cancellation

Import the job control module to cancel a job within a Node.js application:

```javascript
import { cancelJob } from './plugins/codex/scripts/lib/job-control.mjs';

async function terminateJob(jobId) {
  const result = await cancelJob(jobId);
  console.log(`Job ${jobId} status: ${result.status}`);
  console.log(`Turn interrupted: ${result.turnInterrupted}`);
}

```

### Expected Cancellation Response

The JSON output confirms the cleanup operations completed:

```json
{
  "status": "cancelled",
  "jobId": "task-live",
  "turnInterruptAttempted": true,
  "turnInterrupted": true
}

```

## Key Source Files

The cleanup logic spans four primary files in the repository:

- **`plugins/codex/scripts/codex-companion.mjs`** – Implements the CLI entry point for the `cancel` command and assembles the JSON response with turn interrupt logic (lines 992–1002, 1017–1018).
- **`plugins/codex/scripts/lib/job-control.mjs`** – Contains core logic that updates job status to `"cancelled"` and clears the PID field.
- **`plugins/codex/scripts/lib/tracked-jobs.mjs`** – Manages the active job collection and filters out cancelled entries.
- **`tests/runtime.test.mjs`** – Validates the cancellation flow, confirming status updates and turn-interrupt handling.

## Summary

- **Turn interrupt** is sent first to abort pending LLM requests on the shared app-server.
- **Process termination** kills the local job process and clears the PID in `job-control.mjs`.
- **Status updates** set both the status and phase to `"cancelled"` in the job record.
- **Persistence** writes the cancelled state to storage for consistency across sessions.
- **Active list cleanup** removes the job from the tracked active jobs list to prevent duplicate cancellation attempts.
- **Confirmation** returns a JSON payload with `turnInterruptAttempted` and `turnInterrupted` flags set to `true`.

## Frequently Asked Questions

### What happens if I cancel a job that has already completed?

If the job is no longer active, the cancellation command will return an error indicating there is no active job to cancel. The `job-control.mjs` logic checks for active status before attempting termination, preventing unnecessary operations on completed or failed jobs.

### Does the turn interrupt affect other jobs running on the shared app-server?

No, the turn interrupt is targeted specifically at the turn associated with the job being cancelled. According to the implementation in `codex-companion.mjs`, the interrupt signal is scoped to the specific job context, leaving other concurrent jobs unaffected.

### Where is the cancelled job status stored permanently?

The job status is persisted through the state management layer in `job-control.mjs`. After cancellation, the updated job object (with `"cancelled"` status and `null` PID) is written to the persistent state store, ensuring the status survives plugin restarts and remains queryable via the status command.

### How can I verify that a job was successfully cancelled?

Check the JSON response from the cancel command for `status: "cancelled"` and `turnInterrupted: true`. Additionally, you can query the job status using the status command; a successfully cancelled job will show `"cancelled"` in both the status and phase fields, with a `null` PID value.