Project hooks in linked worktrees appear to be loaded from the primary checkout but executed from the linked worktree cwd
Hi Codex team,
I’ve been investigating a hook failure in the Codex Desktop app, and I believe there is an inconsistency in how project hooks are discovered and executed when the app is operating inside a linked Git worktree.
Summary
When Codex Desktop / app-server runs in a linked Git worktree, project hooks appear to be loaded from the primary checkout’s .codex/hooks.json, rather than from the current linked worktree’s own .codex/hooks.json.
However, the hook command itself is still executed with cwd set to the linked worktree.
This creates a mismatch between:
Hook config source:
/Users/<username>/<repository>/.codex/hooks.json
Hook execution cwd:
/Users/<username>/.codex/worktrees/c155/<repository>
As a result, hook configuration is sourced from the primary checkout, but any command-time repo-root resolution, such as git rev-parse --show-toplevel, resolves to the linked worktree.
Example
Given a hook command like this:
/Users/<username>/<repository>/.venv/bin/python "$(git rev-parse --show-toplevel)/.codex/hooks/subagent_dispatch_gate_prompt_hint.py"
At execution time, $(git rev-parse --show-toplevel) resolves to the linked worktree root:
/Users/<username>/.codex/worktrees/c155/<repository>
So although Codex loaded the hook configuration from:
/Users/<username>/<repository>/.codex/hooks.json
the actual script path becomes:
/Users/<username>/.codex/worktrees/c155/<repository>/.codex/hooks/subagent_dispatch_gate_prompt_hint.py
If the linked worktree’s current branch does not contain that hook script, the hook fails and blocks prompt submission:
can't open file .../.codex/hooks/subagent_dispatch_gate_prompt_hint.py: [Errno 2] No such file or directory
Local validation
I verified this against the backend bundled with the Codex Desktop app, not the Homebrew-installed CLI:
/Applications/Codex.app/Contents/Resources/codex app-server --listen stdio://
codex-cli 0.133.0-alpha.1
I called hooks/list from multiple working directories:
/Users/<username>/<repository>
/Users/<username>/.codex/worktrees/7c9a/<repository>
/Users/<username>/.codex/worktrees/d29f/<repository>
/Users/<username>/.codex/worktrees/f70e/<repository>
In all cases, the returned project hook sourcePath was:
/Users/<username>/<repository>/.codex/hooks.json
This also happened for linked worktrees that did not have their own .codex/hooks.json or .codex/hooks/ directory.
Source-level observation
From the Codex source, this appears to be intentional behavior in the app-server config loader rather than a transient cache issue.
Relevant files:
codex-rs/config/src/loader/mod.rs
codex-rs/hooks/src/engine/discovery.rs
The loader appears to special-case linked worktrees: the regular project-layer .codex directory remains associated with the current cwd / linked worktree, but the hook-specific hooks_config_folder is redirected to the primary checkout’s .codex directory.
Hook discovery then uses hooks_config_folder to load hooks.json.
The effective behavior is therefore:
project config layer dotCodexFolder = linked worktree .codex
project hooks config folder = primary checkout .codex
hook execution cwd = linked worktree
This means hook discovery and hook execution operate against different roots.
Impact
This affects any project hook command that uses git rev-parse --show-toplevel, pwd, or other cwd-based logic to locate hook scripts or repo-local helper files.
The failure mode is especially easy to hit in Codex Desktop because the app creates or uses linked worktrees under:
~/.codex/worktrees/<id>/...
A user may reasonably expect that if the current linked worktree branch does not contain a .codex/hooks.json, then no project hook from that branch should run. Instead, app-server loads the hook from the primary checkout and executes it from the linked worktree, which can cause the hook to reference files that do not exist on the linked worktree branch.
Questions / suggestions
Could you confirm whether this is the intended design?
If linked worktrees are intentionally meant to reuse hooks from the primary checkout, it would be helpful for the documentation to make this contract explicit:
The project hook discovery root may differ from the hook execution cwd / repo root.
It may also be useful to expose the hook source location to hook commands via environment variables, for example:
CODEX_HOOK_SOURCE_PATH=/Users/<username>/<repository>/.codex/hooks.json
CODEX_HOOK_CONFIG_DIR=/Users/<username>/<repository>/.codex
CODEX_WORKTREE_ROOT=/Users/<username>/.codex/worktrees/c155/<repository>
That would let hook authors reliably distinguish between:
the repository / checkout that provided the hook configuration
and:
the linked worktree where the hook is being executed
Alternatively, if project hooks are expected to follow the current linked worktree branch, then hooks/list and hook discovery should probably read:
<current-linked-worktree>/.codex/hooks.json
rather than:
<primary-checkout>/.codex/hooks.json
One-line summary
Codex Desktop / app-server currently appears to load project hooks from the primary checkout when running inside a linked worktree, while executing the hook command from the linked worktree cwd. This causes git rev-parse --show-toplevel inside the hook command to resolve to the linked worktree, so the hook config and hook script path can point to different branches, leading to missing-file failures that block prompt submission.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗