exec_command PreToolUse payload drops workdir and misclassifies isolated worktrees
What version of Codex CLI is running?
codex-cli 0.144.5 from codex doctor --json; the active TUI session metadata reports cli_version: 0.145.0 after the update check selected 0.145.0.
What subscription do you have?
ChatGPT-authenticated operator-managed account (exact plan not exposed to this session).
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Linux 7.0.11-76070011-generic x86_64 x86_64 (Pop!_OS 24.4.0), tmux 3.4.
Codex doctor report
Overall status ok. Relevant fields:
{
"codexVersion": "0.144.5",
"overallStatus": "ok",
"auth": "ChatGPT",
"features": ["unified_exec", "code_mode_host", "hooks"],
"latestVersion": "0.145.0",
"platform": "linux-x86_64"
}
What issue are you seeing?
exec_command accepts and executes an explicit workdir, but its PreToolUse hook payload discards that field and exposes only the command. Hook policy therefore sees the outer session cwd and can falsely classify a safe linked-worktree mutation as a primary-checkout mutation.
This also reproduces through code mode / nested functions.exec -> exec_command: the outer custom-tool transcript retains the nested workdir, the command executes in that worktree, but the corresponding PreToolUse event contains the session's primary-checkout cwd.
Current upstream root cause at 44d76c6a6dd04fa2efc302b906ac8774267a1272:
codex-rs/core/src/tools/handlers/unified_exec/exec_command.rs, ExecCommandHandler::pre_tool_use_payload, parses ExecCommandArgs and then constructs:
tool_input: serde_json::json!({ "command": args.cmd }),
args.workdir is intentionally omitted. The focused test exec_command_pre_tool_use_payload_uses_raw_command in unified_exec_tests.rs locks in the lossy shape.
The execution path later resolves and honors args.workdir, so execution and hook-policy provenance disagree.
What steps can reproduce the bug?
- Start Codex with
unified_execand hooks enabled from a repository's primary checkout. - Register a
PreToolUsehook matchingBashthat records stdin JSON. - Create a linked worktree at
/tmp/repro-worktree. - Invoke
exec_commandwithcmd: "git status --short"andworkdir: "/tmp/repro-worktree". - Observe that the command runs in
/tmp/repro-worktree, but hook stdin containstool_input.commandonly; commoncwdremains the session's primary checkout. - The same loss occurs when
exec_commandis invoked inside code mode /functions.exec.
A unit-level reproduction is to extend exec_command_pre_tool_use_payload_uses_raw_command with "workdir": "/tmp/repro-worktree"; the expected hook payload cannot currently include it.
What is the expected behavior?
The PreToolUse payload for unified exec should preserve the model-facing execution provenance needed for policy evaluation, at minimum:
{
"tool_name": "Bash",
"tool_input": {
"command": "git status --short",
"workdir": "/tmp/repro-worktree"
}
}
Omit workdir only when it was absent from the invocation. Direct and code-mode/nested unified exec should produce the same hook contract. PostToolUse should remain correlated with the same effective target.
1 Comment
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action