openai / openai/codex-plugin-cc

Companion writes all job state (broker.json, state.json, jobs/) into another plugin's data directory

Open
#631 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
33.3k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Environment

  • codex plugin 1.0.6 (Claude Code plugin, cache path ~/.claude/plugins/cache/openai-codex/codex/1.0.6)
  • codex CLI 0.146.0, Windows 11 Pro 10.0.26200, Claude Code with multiple plugins installed
  • A second companion-style plugin is installed alongside this one: a grok-build fork that shares the same companion architecture (its scripts/lib/state.mjs is the same file, same PLUGIN_DATA_ENV = "CLAUDE_PLUGIN_DATA" constant)

Summary

With two companion-style plugins installed, the codex companion wrote every piece of its persistent state for a session into the OTHER plugin's data directory. The codex plugin's own data directory stayed completely empty.

Observed on 2026-08-10, one Claude Code session, two codex jobs dispatched (both completed fine; the issue is only where their state landed):

  • ~/.claude/plugins/data/codex-openai-codex/ is empty (expected home, nothing ever written)
  • ~/.claude/plugins/data/grok-grok-build-cheema/state/<workspace>-33fd287a8d86b31d/broker.json is the CODEX broker's registration:
{
  "endpoint": "pipe:\\\\.\\pipe\\cxc-usneSC-codex-app-server",
  "pidFile": "C:\\Users\\...\\AppData\\Local\\Temp\\cxc-usneSC\\broker.pid",
  "logFile": "C:\\Users\\...\\AppData\\Local\\Temp\\cxc-usneSC\\broker.log",
  "sessionDir": "C:\\Users\\...\\AppData\\Local\\Temp\\cxc-usneSC",
  "pid": 21808
}

PID 21808 was verified as a live node .../openai-codex/codex/1.0.6/scripts/app-server-broker.mjs serve --endpoint pipe... process. The cxc- prefix and -codex-app-server pipe name make the ownership unambiguous.

  • Both codex job records (title "Codex Task", codex threadId/turnId fields, kindLabel: "rescue") live in that same foreign directory:

    • .../grok-grok-build-cheema/state/<workspace>-33fd287a8d86b31d/jobs/task-msnh1mgs-qj6lc7.{json,log}
    • .../grok-grok-build-cheema/state/<workspace>-33fd287a8d86b31d/jobs/task-msnhca4f-wq3dtr.{json,log}

    Each job JSON's own logFile field points into the grok plugin's data dir as well.

  • The shared state.json in that directory now interleaves both plugins' jobs in one array (2 "Codex Task" rescue jobs and 2 "Grok Task" delegate jobs from the same session).

Why this happens

scripts/lib/state.mjs resolves the state root from a generic environment variable rather than anything plugin-specific:

const PLUGIN_DATA_ENV = "CLAUDE_PLUGIN_DATA";           // line 9
...
const pluginDataDir = process.env[PLUGIN_DATA_ENV];      // line 41
const stateRoot = pluginDataDir ? path.join(pluginDataDir, "state") : FALLBACK_STATE_ROOT_DIR;
return path.join(stateRoot, `${slug}-${hash}`);

Whatever CLAUDE_PLUGIN_DATA value the long-lived broker process inherits at startup becomes the state root for everything it and its clients persist. In this session that value evidently pointed at the grok plugin's data dir (.../plugins/data/grok-grok-build-cheema). I have not captured the broker's environment directly, so whether the wrong value comes from hook invocation ordering in Claude Code or from env inheritance through the shell that spawned the broker is unconfirmed. The effect on disk is unambiguous, and because the grok fork ships the same state.mjs and reads the same variable, any confusion between the two plugins is invisible to both.

Impact

  1. Cross-plugin state clobbering: two independent companion processes read-modify-write the same state.json. Lost updates are possible on concurrent job activity (both plugins were active in the same minute in the observed session).
  2. Record eviction: state.mjs caps the jobs array at MAX_JOBS = 50 and prunes. One plugin's pruning can silently delete the other plugin's job history and log references.
  3. Broker discovery data lives where uninstalling or resetting the OTHER plugin deletes it.
  4. Operationally, codex job state is simply not where an operator (or a cleanup script keyed to codex-openai-codex) will look.

Suggested fix

Do not trust the generic env var alone. Either derive the data root from the plugin's own identity (e.g. resolve relative to CLAUDE_PLUGIN_ROOT, or append a hard-coded codex namespace segment: <data>/codex/state/...), or at minimum validate that the provided CLAUDE_PLUGIN_DATA path belongs to this plugin (contains the plugin's slug) and fall back to the tmpdir default with a warning when it does not. Namespacing also fixes the collision permanently for forks that share this codebase.

Repro sketch

  1. Install this plugin plus any second plugin forked from the same companion codebase (both read CLAUDE_PLUGIN_DATA).
  2. In one Claude Code session, run at least one job through each companion.
  3. Compare ~/.claude/plugins/data/codex-openai-codex/ (empty) with the other plugin's state/<workspace>-<hash>/ (contains codex broker.json, codex jobs, and a state.json mixing both plugins' jobs).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in scripts/lib/state.mjs, especially the PLUGIN_DATA_ENV lookup and state-root construction around lines 9 and 41. Reproduce with two companion-style plugins installed and compare their data directories while both run jobs. Done means each plugin's broker, state.json, job records, and logs remain in its own data directory without cross-plugin pruning or overwrites.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.