# How to Manage Multiple Concurrent Codex Sessions Effectively

> Master concurrent Codex sessions with Claude Code. Learn to manage multiple sessions effectively using per-session bookkeeping and state persistence, ensuring seamless development.

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

---

**The Codex plugin for Claude Code isolates concurrent sessions using a per-session bookkeeping system centered on the `CODEX_COMPANION_SESSION_ID` environment variable and filtered state persistence.**

The `openai/codex-plugin-cc` repository provides a lightweight but robust architecture for running multiple Claude Code sessions simultaneously without job interference. By leveraging environment-scoped session identifiers and disk-based state management, the plugin ensures that background Codex jobs remain isolated to their originating terminal sessions while remaining observable through filtered commands.

## Understanding the Session Isolation Architecture

The plugin implements session isolation through four core modules that handle lifecycle events, job persistence, and state filtering.

### Session Lifecycle Hooks (`session-lifecycle-hook.mjs`)

The `session-lifecycle-hook.mjs` module manages the beginning and end of Claude Code sessions. On startup, it writes the generated session identifier to the `CODEX_COMPANION_SESSION_ID` environment variable (defined at line 20). When a session ends, the `cleanupSessionJobs` function (lines 42-74) terminates any still-running processes belonging to that specific session and removes their entries from the state file, preventing resource leaks across sessions.

### Job Tracking and ID Generation (`tracked-jobs.mjs`)

Every background job receives automatic session attribution through `tracked-jobs.mjs`. The module re-exports `SESSION_ID_ENV` (line 6) and the `createJobRecord` function (lines 60-67) automatically injects the current `sessionId` into each job payload. This ensures that when you spawn a background review with `/codex:review --background`, the job record carries the originating session's identifier regardless of when or where you check its status.

### Persistent State Management (`state.mjs`)

Job metadata persists under a per-workspace directory structure managed by `state.mjs`. The `resolveStateDir` function (lines 29-44) constructs a deterministic path based on the workspace location and optional `CLAUDE_PLUGIN_DATA` root, storing all data in `~/.codex/state/...`. This stateless approach means you can restart Claude Code or the underlying Node process without losing job information, as everything resides on disk rather than in process memory.

### Job Control and Filtering (`job-control.mjs`)

The `job-control.mjs` module provides session-scoped visibility through `filterJobsForCurrentSession` (lines 19-25). This function narrows the global job list to entries matching the current environment's `CODEX_COMPANION_SESSION_ID`. The `buildStatusSnapshot` function (lines 13-31) merges this filter with the complete job list to power the `/codex:status` command, defaulting to the current session while supporting an `all:true` option for cross-session debugging.

## How Session Isolation Works in Practice

The system operates through a five-step flow that maintains strict boundaries between concurrent sessions:

1. **Session initialization** – When Claude Code launches, `handleSessionStart` (lines 77-81) generates a unique session ID and writes it to `CODEX_COMPANION_SESSION_ID` in the environment.

2. **Job creation** – Every slash command that spawns background work calls `createJobRecord`, which automatically tags the job with the current session ID from the environment.

3. **Status queries** – The `/codex:status` command uses `buildStatusSnapshot` with `filterJobsForCurrentSession`, ensuring you only see jobs belonging to your current terminal session.

4. **Session termination** – `handleSessionEnd` invokes `cleanupSessionJobs` to locate all jobs matching the ending session's ID, terminate their process trees via `terminateProcessTree`, and purge their records from [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json).

5. **Concurrent operation** – Because each session carries a unique identifier and the state store filters by that token, multiple sessions can simultaneously run `/codex:review --background` without job records intermingling.

## Practical Examples for Concurrent Workflows

The following patterns demonstrate how to leverage session isolation when working across multiple terminals or CI pipelines.

### Running Multiple Background Jobs

From within a single Claude Code session, spawn independent background reviews:

```bash

# Job A and B both belong to the current session X

codex:review --background   # → creates job A

codex:review --background   # → creates job B

```

### Working Across Separate Sessions

Open a second terminal to start a distinct session with its own isolated jobs:

```bash

# In terminal 2: new session Y with automatic ID generation

codex:review --background   # → creates job C (session Y)

# Check status in terminal 2 - sees only job C

codex:status

# Check status in terminal 1 - sees jobs A and B but not C

codex:status

```

### Viewing All Sessions for Debugging

Administrators can bypass session filtering to see the global job state:

```bash

# Shows jobs A, B, and C across all sessions

codex:status --all

```

### Retrieving Results and Cleanup

Access specific job outputs and manage lifecycle:

```bash

# Get final output from any job in your current session

codex:result <job-id>

# Cancel a running job

codex:cancel <job-id>

```

### Scripting with Manual Session IDs

For CI pipelines or automated workflows, manually export the session identifier before running commands:

```bash
export CODEX_COMPANION_SESSION_ID=$(uuidgen)
codex:review --background
codex:status

```

## Summary

- **Environment-based isolation**: The `CODEX_COMPANION_SESSION_ID` variable in `session-lifecycle-hook.mjs` guarantees every session carries a unique token that propagates to all child jobs.
- **Automatic job tagging**: The `createJobRecord` function in `tracked-jobs.mjs` ensures every background job persists with its originating session ID, preventing cross-session attribution errors.
- **Disk-based state**: The `resolveStateDir` implementation in `state.mjs` stores job metadata under `~/.codex/state/`, making the system resilient to process restarts.
- **Session-scoped queries**: The `filterJobsForCurrentSession` function in `job-control.mjs` defaults `/codex:status` to show only the current session's jobs while supporting `--all` for global visibility.
- **Clean resource reclamation**: The `cleanupSessionJobs` function automatically terminates orphaned processes and purges stale records when a session ends.

## Frequently Asked Questions

### How does the Codex plugin prevent jobs from one session appearing in another?

The plugin filters all job queries through `filterJobsForCurrentSession` in `job-control.mjs`, which compares the `CODEX_COMPANION_SESSION_ID` environment variable against the `sessionId` field stored in each job record. Since `session-lifecycle-hook.mjs` generates a unique UUID for each Claude Code session and `tracked-jobs.mjs` embeds this ID into every new job, the default `/codex:status` command only displays jobs matching your current terminal's session token.

### Can multiple Claude Code sessions run background jobs simultaneously without conflicts?

Yes. Because the state file ([`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json)) managed by `state.mjs` stores jobs from all sessions and each job carries its own `sessionId`, concurrent sessions can spawn background processes independently. The `buildStatusSnapshot` function separates these by session ID while maintaining a single global state file, allowing truly parallel execution without file-locking conflicts or job ID collisions.

### What happens to background jobs when I close a Claude Code session?

The `handleSessionEnd` function in `session-lifecycle-hook.mjs` automatically calls `cleanupSessionJobs`, which locates all jobs in [`state.json`](https://github.com/openai/codex-plugin-cc/blob/main/state.json) matching the ending session's ID. It sends termination signals to any running processes via `terminateProcessTree` and removes those job records from the state file. This prevents orphaned processes from consuming CPU and memory after the parent session closes.

### How can I view jobs from all sessions at once?

Use the `--all` flag with the status command. This invokes `buildStatusSnapshot` with the `all:true` option, bypassing the `filterJobsForCurrentSession` filter. According to the implementation in `job-control.mjs` (lines 13-31), this returns the complete job list across every active and completed session, useful for debugging or administrative oversight when you need the full picture of background work in the workspace.