Skip to content

Commit cae0e14

Browse files
Document Issue 4: cross-plugin \${CLAUDE_PLUGIN_ROOT} reference (lessons learned)
The 2026-04-08~09 fix for the broken `${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs` reference is now documented in known-issues.md as Issue 4. The entry covers: - Symptoms: /onto:review and /onto:onboard always halted in Codex mode with MODULE_NOT_FOUND, regardless of actual Codex install state. - Root cause: ${CLAUDE_PLUGIN_ROOT} expands to the *calling* plugin's root, not the codex plugin's root. Onto's templates copied codex's own internal pattern without realizing the variable's per-caller semantics. - Verification of scope: full grep across cowork/ (48 repos) found this pattern in only 3 source files, all in onto / onto-prototype. Other repos using ${CLAUDE_PLUGIN_ROOT} (ouroboros, oh-my-claudecode) all reference their OWN plugin's scripts/, which is the correct usage. - Resolution: A+D combination fix A — Skill tool invokes /codex:setup slash command (works today) D — $CODEX_COMPANION_PATH env var (forward-compatible, unset today) - Decision NOT to file upstream issue with codex plugin owners: 1. Slash command pattern is the established norm; another third-party plugin (parthpm/adversarial-dev-plugin) uses it successfully. 2. Codex plugin team is already strengthening this path via openai/codex-plugin-cc#156 (removing disable-model-invocation on /codex:adversarial-review for Skill tool delegation). 3. The bug was bad copy-paste, not a missing feature. Filing would likely be closed as "use the slash command". - Lesson for future plugin work: never use \${CLAUDE_PLUGIN_ROOT} for cross-plugin references. Use the other plugin's slash command via Skill tool instead. Documented as a 4-point rule in the entry's "Lesson for future plugin work" section. This is a documentation-only commit. No code changes — the actual fixes are in dc7f111 (cross-plugin path fix) and 615f90d (chatgpt OAuth fail-fast). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 615f90d commit cae0e14

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

development-records/tracking/20260330-known-issues.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,88 @@ LLM이 연속된 Bash 출력에서 echo 마커(`---PROJECT_ONTO---`, `---GLOBAL_
238238

239239
- `domain_scope.md` 1회 작성 후 폐기
240240
- 전체 create-domain 프로세스 재시작 (외부 리서치 포함 ~6분 추가 소요)
241+
242+
---
243+
244+
## Issue 4: Cross-plugin `${CLAUDE_PLUGIN_ROOT}` reference broke `/onto:review` and `/onto:onboard` Codex mode (2026-04-09)
245+
246+
### Status: Resolved, fix committed (`dc7f111`)
247+
248+
### Observed Symptoms
249+
250+
`/onto:review` and `/onto:onboard` always halted on the Codex readiness check, falling through to the failure branch unconditionally regardless of whether Codex was actually installed and authenticated.
251+
252+
The user-visible error was a `MODULE_NOT_FOUND` from Node trying to load `codex-companion.mjs` at a non-existent path. The path looked like:
253+
254+
```
255+
/Users/<user>/.claude-2/plugins/cache/onto/onto/<sha>/scripts/codex-companion.mjs
256+
```
257+
258+
…but the onto plugin doesn't have a `scripts/` directory at all. The actual `codex-companion.mjs` lives in the **codex** plugin (`cache/openai-codex/codex/1.0.2/scripts/`).
259+
260+
The bug had been latent for the entire history of the `commands/review.md` and `processes/onboard.md` templates — Codex mode never worked from the onto plugin.
261+
262+
### Root Cause
263+
264+
Both onto plugin templates contained:
265+
266+
```markdown
267+
node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" setup --check
268+
```
269+
270+
The `${CLAUDE_PLUGIN_ROOT}` variable in Claude Code slash commands expands to the **calling** plugin's root, not to the codex plugin's root. So when the user invoked `/onto:review`:
271+
272+
1. Claude Code expanded `${CLAUDE_PLUGIN_ROOT}``/cache/onto/onto/<sha>` (onto plugin root)
273+
2. Node tried to load `<onto root>/scripts/codex-companion.mjs`
274+
3. That file does not exist (only the **codex** plugin has it)
275+
4. `MODULE_NOT_FOUND` → halt branch
276+
277+
The pattern was likely copy-pasted from the **codex** plugin's own `commands/setup.md`, which uses the identical `${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs` pattern. That pattern works correctly for `/codex:setup` because `${CLAUDE_PLUGIN_ROOT}` then expands to the codex plugin's root, where the file does exist. The pattern is non-portable across plugins, but the source code is identical at the syntactic level — a classic "looks the same, means different things in different contexts" failure mode.
278+
279+
### Verification of scope (2026-04-08 sweep)
280+
281+
A full grep across `cowork/` (48 repos) found this exact pattern in only 3 source files (all in onto / onto-prototype):
282+
- `cowork/onto/processes/onboard.md:161`
283+
- `cowork/onto-prototype/processes/onboard.md:161`
284+
- `cowork/onto-prototype/commands/review.md:22`
285+
286+
Other repositories that use `${CLAUDE_PLUGIN_ROOT}` (`ouroboros`, `oh-my-claudecode`) reference their own plugin's `scripts/` files, which is the **correct** use of the variable. Only the cross-plugin pattern was broken.
287+
288+
### Resolution
289+
290+
Applied an **A+D combination fix** ([`dc7f111`](../../../../onto/commit/dc7f111), [`f530bb7`](../../../../onto-prototype/commit/f530bb7) in onto-prototype):
291+
292+
**A — Slash command fallback** (works today): when the env var is unset, invoke `/codex:setup` via the Skill tool. The codex plugin is the canonical source of its readiness check; delegating to its slash command keeps onto out of cross-plugin path resolution entirely.
293+
294+
**D — Env var contract** (forward-compatible): if `$CODEX_COMPANION_PATH` is set and points to an existing file, run it directly. Codex plugin owners would export this env var as the canonical cross-plugin handoff. Until they do, the slash command path handles every install. Decided not to file this contract as an upstream issue (see "Decision: don't file upstream" below).
295+
296+
The new flow:
297+
298+
```markdown
299+
1. If $CODEX_COMPANION_PATH set + file exists → node "$CODEX_COMPANION_PATH" setup --check
300+
2. Otherwise → invoke /codex:setup slash command via Skill tool
301+
3. Either path → check ready: true → branch to success or failure
302+
```
303+
304+
Active plugin install caches at `~/.claude-{1,2}/plugins/cache/onto/onto/1207d86596f7/` were also hot-patched in the fix session so the bug stopped immediately for the developer's environment.
305+
306+
### Decision: don't file upstream
307+
308+
After investigation (2026-04-08), filing a feature request on `openai/codex-plugin-cc` for the env var contract was **rejected** as low-value:
309+
310+
1. **The slash command pattern is the established norm.** A real third-party plugin (`parthpm/adversarial-dev-plugin`) integrates with codex by invoking `/codex:setup` and `/codex:adversarial-review --wait` via Skill tool, NOT by reaching for `codex-companion.mjs` directly. They never hit the bug we hit.
311+
2. **Codex plugin team is already strengthening this path.** Issue [openai/codex-plugin-cc#156](https://github.com/openai/codex-plugin-cc/issues/156) (open) removes `disable-model-invocation` from `/codex:adversarial-review`, enabling Skill tool delegation from third-party plugins. This is the same path onto now uses.
312+
3. **Our bug was bad copy-paste, not a missing feature.** We blindly copied codex's own internal `${CLAUDE_PLUGIN_ROOT}` pattern without realizing the variable expands per-caller. Filing "please add an env var so I don't have to use the existing slash command pattern" would likely be closed as "use the slash command".
313+
314+
The env var path remains in the fix as forward-compatible scaffolding — if codex plugin ever adopts the contract, onto picks it up automatically with no code change. But the slash command path is the canonical answer.
315+
316+
### Lesson for future plugin work
317+
318+
**Never use `${CLAUDE_PLUGIN_ROOT}` to reference resources in another plugin.** The variable expands to the calling plugin's own root, so cross-plugin references silently break. When integrating with another plugin:
319+
320+
1. **Preferred**: invoke the other plugin's slash command (`/{other}:{command}`) via the Skill tool. The other plugin owns its own resource resolution.
321+
2. **Alternative (if env var contract exists)**: read a documented env var the other plugin exports.
322+
3. **Anti-pattern**: hardcode `${CLAUDE_PLUGIN_ROOT}/scripts/...` assuming the variable points to the other plugin. It does not.
323+
4. **Anti-pattern**: vendor a copy of the other plugin's scripts. License + drift cost.
324+
325+
This rule applies not just to codex-companion but to ANY cross-plugin file reference. The common failure mode: you tested the new plugin in a dev workspace where the path happened to resolve, then it broke in production where plugin layouts differ.

0 commit comments

Comments
 (0)