# How the Codex Plugin Handles Session Filtering for Job Visibility

> Learn how the Codex plugin handles session filtering for job visibility by tagging jobs with the current Claude session ID and filtering all operations against this value.

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

---

**The Codex plugin isolates jobs to the current Claude session by storing the session ID in `CODEX_COMPANION_SESSION_ID`, tagging every job with this ID, and filtering all operations—status checks, cancellations, and review gates—against this value.**

The `openai/codex-plugin-cc` repository implements strict **session filtering for job visibility** to prevent cross-session interference. When you run Claude Code with the Codex plugin enabled, the system ensures that commands like `codex:status` and `codex:cancel` only surface or affect jobs spawned during your current Claude session. This isolation relies on a coordinated chain of environment propagation, job metadata tagging, and filtered queries.

## Propagating Session IDs via Environment Variables

When a Claude session initializes, the plugin captures the unique session identifier and exposes it to downstream processes. This mechanism forms the foundation of the visibility boundary.

### Capturing the Session Identifier

The **session-lifecycle-hook** runs automatically when Claude starts. In `plugins/codex/scripts/session-lifecycle-hook.mjs` (lines 20-23), the hook retrieves the Claude session ID and exports it as an environment variable:

```javascript
process.env.CODEX_COMPANION_SESSION_ID = claudeSessionId;

```

This variable persists for the duration of the session and serves as the single source of truth for all session-based filtering.

## Tagging Jobs with Session Metadata

Every Codex job created through the plugin carries an immutable **sessionId** field that links it to its originating Claude session. This attribution occurs at job creation time.

### The Tracked Jobs Utility

In `plugins/codex/scripts/lib/tracked-jobs.mjs` (lines 62-66), the job creation logic reads the `CODEX_COMPANION_SESSION_ID` environment variable and embeds it into the job record:

```javascript
const job = {
  id: generateId(),
  sessionId: process.env.CODEX_COMPANION_SESSION_ID,
  // ... other fields
};

```

Once persisted, this `sessionId` field acts as the foreign key for all session-scoped queries.

## Filtering Operations by Current Session

With jobs properly tagged, the plugin filters every user-facing operation against the current value of `CODEX_COMPANION_SESSION_ID`. This ensures that commands executed in one Claude window cannot see or manipulate jobs from another.

### Status and Cancel Command Isolation

The **status** command queries the in-memory job registry and returns only records where `job.sessionId` matches the current environment variable. As demonstrated in `tests/runtime.test.mjs` (lines 1159-1163), the test suite validates that "status without a job id only shows jobs from the current Claude session."

Similarly, the **cancel** command ignores active jobs from other sessions unless you explicitly provide a job ID. The test at lines 1637-1658 confirms that cross-session cancellation attempts return "no active jobs" messages scoped to the current session.

### Review Gate Session Validation

The **stop-review-gate hook** enforces session isolation before allowing a review gate to close. In `plugins/codex/scripts/stop-review-gate-hook.mjs` (lines 45-47), the logic filters pending jobs by comparing each job's `sessionId` to `CODEX_COMPANION_SESSION_ID`:

```javascript
const pendingJobs = state.jobs.filter(job => 
  job.sessionId === process.env.CODEX_COMPANION_SESSION_ID
);

```

If no pending jobs match the current session, the gate closes automatically.

## Cleaning Up Sessions on Exit

When a Claude session terminates, the lifecycle hook removes all associated jobs to prevent stale entries from persisting into future sessions.

### The Cleanup Routine

In `plugins/codex/scripts/session-lifecycle-hook.mjs` (lines 42-75), the session-end handler iterates over the global `state.jobs` array, identifies records matching the ending session's ID, and terminates any still-running processes before removing them from state:

```javascript
for (const job of state.jobs) {
  if (job.sessionId === endingSessionId) {
    await terminateProcess(job);
    state.jobs.delete(job.id);
  }
}

```

## Practical Example of Session Isolation

The following workflow demonstrates how session filtering manifests in practice:

```bash

# Claude session "thr_1" starts: CODEX_COMPANION_SESSION_ID is set automatically

# Run a job in this session

$ codex:run analyze-code.js

# Job record stored with sessionId: "thr_1"

# Check status—only thr_1 jobs appear

$ codex:status

# → Active Codex job: thr_1-abc...

# From a different Claude session, same commands return empty

$ codex:status

# → No active Codex jobs for this session.

# Session end cleanup removes thr_1 jobs automatically

```

## Summary

- **Environment propagation**: The session-lifecycle-hook sets `CODEX_COMPANION_SESSION_ID` on startup, creating a visibility boundary for the entire session.
- **Job attribution**: The tracked-jobs utility tags every new job with the current session ID via the `sessionId` field.
- **Filtered queries**: Status, cancel, and review-gate operations in `runtime.test.mjs` and `stop-review-gate-hook.mjs` compare job metadata against the active session ID to exclude cross-session entries.
- **Automatic cleanup**: On session exit, `session-lifecycle-hook.mjs` purges all jobs associated with the terminating session, preventing stale job accumulation.

## Frequently Asked Questions

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

The plugin stores the Claude session ID in the `CODEX_COMPANION_SESSION_ID` environment variable and tags every Codex job with this value in the `sessionId` field. When listing or acting on jobs, the plugin filters the results to only include records where `job.sessionId` matches the current environment variable, effectively hiding jobs from other sessions.

### What happens to Codex jobs when a Claude session ends?

When the session ends, the session-lifecycle-hook iterates through all tracked jobs in `state.jobs` and removes those where `sessionId` matches the ending session. It also terminates any still-running processes associated with those jobs, ensuring no orphaned tasks persist into future sessions.

### Can I view or cancel jobs from a different Claude session using the Codex plugin?

By default, the `codex:status` and `codex:cancel` commands filter by the current session ID as implemented in the runtime tests. While you may target specific jobs by explicit ID, the plugin architecture discourages cross-session manipulation to maintain clean isolation boundaries and prevent accidental interference.

### Which source files handle the session filtering logic?

Session filtering spans four key files: `plugins/codex/scripts/session-lifecycle-hook.mjs` manages the environment variable and cleanup, `plugins/codex/scripts/lib/tracked-jobs.mjs` handles job tagging, `plugins/codex/scripts/stop-review-gate-hook.mjs` validates review gates against session IDs, and `tests/runtime.test.mjs` contains the test coverage verifying these behaviors.