Problem
With ArmorCodex installed via the installer (global ~/.codex/hooks.json + MCP server), if the user ALSO enables the armorcodex@armorcodex plugin from Codex's /plugins UI, every hook fires twice — once "completed", once "failed (exit code 1)":
- Global
~/.codex/hooks.json (installer) uses ABSOLUTE node /abs/.../bootstrap.mjs router -> works.
- Plugin
plugins/armorcodex/hooks/hooks.json (and .codex/hooks.json) uses RELATIVE node ./scripts/bootstrap.mjs router. Codex fires plugin hooks with CWD = user workspace, so ./scripts/bootstrap.mjs doesn't exist -> exit 1 -> "failed".
The failing hook confuses the agent (observed it web-searching for the intent tool) and looks broken to users.
Root cause
Two hook registration mechanisms are active simultaneously, and the plugin's hooks.json assumes CWD = plugin root (relative path), which isn't true when Codex invokes plugin hooks.
Fix options
- Installer model (global hooks + MCP) is the supported path -> the plugin should NOT also ship conflicting hooks, or the docs must say "don't enable the plugin; the installer wires hooks." (Immediate mitigation:
enabled = false for the plugin.)
- If the plugin path is meant to be supported, its
hooks/hooks.json + .codex/hooks.json must resolve the bootstrap path independent of CWD (absolute, or a Codex plugin-root variable if one exists) — a relative ./scripts/... is always wrong under a workspace CWD.
- Decide on ONE mechanism (installer global hooks OR plugin) and make them mutually exclusive.
Refs
Surfaced during ArmorCodex parity testing (#41).
Problem
With ArmorCodex installed via the installer (global ~/.codex/hooks.json + MCP server), if the user ALSO enables the
armorcodex@armorcodexplugin from Codex's/pluginsUI, every hook fires twice — once "completed", once "failed (exit code 1)":~/.codex/hooks.json(installer) uses ABSOLUTEnode /abs/.../bootstrap.mjs router-> works.plugins/armorcodex/hooks/hooks.json(and.codex/hooks.json) uses RELATIVEnode ./scripts/bootstrap.mjs router. Codex fires plugin hooks with CWD = user workspace, so./scripts/bootstrap.mjsdoesn't exist -> exit 1 -> "failed".The failing hook confuses the agent (observed it web-searching for the intent tool) and looks broken to users.
Root cause
Two hook registration mechanisms are active simultaneously, and the plugin's hooks.json assumes CWD = plugin root (relative path), which isn't true when Codex invokes plugin hooks.
Fix options
enabled = falsefor the plugin.)hooks/hooks.json+.codex/hooks.jsonmust resolve the bootstrap path independent of CWD (absolute, or a Codex plugin-root variable if one exists) — a relative./scripts/...is always wrong under a workspace CWD.Refs
Surfaced during ArmorCodex parity testing (#41).