openai / openai/codex-plugin-cc
Companion writes all job state (broker.json, state.json, jobs/) into another plugin's data directory
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.mjsis the same file, samePLUGIN_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.jsonis 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/turnIdfields,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
logFilefield points into the grok plugin's data dir as well. -
The shared
state.jsonin 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
- 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). - 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.
- Broker discovery data lives where uninstalling or resetting the OTHER plugin deletes it.
- 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
- Install this plugin plus any second plugin forked from the same companion codebase (both read
CLAUDE_PLUGIN_DATA). - In one Claude Code session, run at least one job through each companion.
- Compare
~/.claude/plugins/data/codex-openai-codex/(empty) with the other plugin'sstate/<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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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