Repository navigation
fix(hooks): speak Codex 0.155's hook protocol - #30
Merged
Merged
Conversation
find_workspace_root stopped at the first `.codex/hooks.json` above the
session's cwd, and a `--home` install puts one in ~/.codex -- so with the
default install every hook resolved to $HOME, and the Stop hook ran
`ix map` there on every turn. It now prefers the git root of the payload's
cwd and never returns $HOME.
Every automatic map goes through one guard: a git repository that is not
$HOME, already mapped (`ix status --format json --root` says
graphCompleted: true), outside a per-root debounce window; then exactly
`ix map <root> --silent` from the root with IX_AUTO_MAP=1, detached.
PostToolUse no longer runs `ix map <file>`, which the CLI rejects
("Map path is not a directory"); a write requests the guarded root map.
- drop `--limit` from the intercepted `ix locate` (no such option)
- delete the /v2 runtime client (call_runtime, get_runtime, /v2/ix_query,
/v2/ingest/map) and its secret scrubber; no Ix release serves /v2
- caches move from the shared $TMPDIR/ix-codex-hooks to a per-user state
dir (XDG_STATE_HOME / LOCALAPPDATA), created 0700, ownership-checked,
written atomically and never allowed to fail a hook; the briefing
window is per project
- the Stop hook is now one `git rev-parse` plus one `ix status` (3 s + 4 s
bounds) inside its 10 s timeout, instead of up to ~15 s
- tests: strict fake ix (rejects map <file>, locate --limit,
smells --path, unknown commands) and the guard matrix; test-local.sh
dry runs use a fake ix on PATH instead of the real backend
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ix-bug-investigator: `ix bugs` does not exist; use `ix bug list --format text` and say it is Ix Pro only - hooks.md / README / AGENTS.md: describe the guarded automatic map instead of a per-file `ix map` and an unconditional one at Stop - PLUGIN_SPEC: mark the /v2 runtime migration as abandoned - ci.yml: the comments described an MCP server this repo no longer ships Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- PreToolUse printed a top-level systemMessage. Codex turns that into a UI warning and never adds it to the model's input; only hookSpecificOutput.additionalContext reaches the model (codex-rs/hooks/src/events/pre_tool_use.rs parse_completed, rust-v0.155.1). Every hook that talks to the model now goes through one helper, model_context(), which emits that shape. - PreToolUse and PostToolUse matched Bash only, but Codex edits files with apply_patch (core/src/tools/hook_names.rs; Edit/Write are matcher aliases) and passes the raw patch as tool_input.command (core/src/tools/handlers/apply_patch.rs). Both now match "Bash|apply_patch". The touched files are read from the patch's Add/Update/Delete File and Move to headers (apply-patch/src/parser.rs grammar). Pre-edit runs ix impact on up to 3 of those files in parallel. - hooks.json ran each hook through "/bin/sh -lc". Codex already runs the command with the session shell and no login profile (session/mod.rs build_hooks_config, use_login_shell=false), so the inner -l was the only thing sourcing the profile. On this machine "/bin/sh -lc" aborts in /etc/profile before the hook starts. It is now "/bin/sh -c". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`codex features list` (0.155.1) shows `hooks` as stable and on by default. That has been true since 0.124 (openai/codex#19012). `codex_hooks` is now a legacy alias in codex-rs/features/src/legacy.rs, which Codex reports as deprecated. The installer no longer creates or edits config.toml. It only reads config.toml to warn when `[features] hooks` or `codex_hooks` is false. The repo's .codex/config.toml, whose only content was the flag, is removed. So are the TOML line editor and its suite, which existed only to write that flag. It now also tells the user about hook trust. Codex skips non-managed hooks until their exact definition is reviewed and trusted (hooks/src/engine/discovery.rs hook_trust_status), and `codex exec` skips them outright. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The suites asserted whatever shape the hooks printed, so a PreToolUse systemMessage that never reached the model passed. The new suite validates each hook's stdout against the output schemas generated by openai/codex@rust-v0.155.1, vendored under tests/fixtures/codex-0.155.1/. It also checks that the text meant for the model is in additionalContext, which is where Codex reads it. The suite also covers: - apply_patch path parsing - the hooks.json matchers, under Codex's exact-matcher rules - the non-login command, run with a profile that prints - the installer leaving config.toml alone Against the previous tree the suite fails with 8 failures and 5 errors. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
hooks.md records what was verified against codex 0.155.1: - the model-context channel - tool names and apply_patch - the non-login shell - the feature flag - hook trust It also sets out what a move to plugin-bundled hooks would involve. README and AGENTS now cover apply_patch, the trust prompt and the dropped flag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main's tree is identical to the merged sweep head 604cc65, which this branch already contains, so this branch's tree is kept unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Checked against Codex 0.155.1 (the binary, and openai/codex at
rust-v0.155.1):pre_tool_use.pyreturned its context assystemMessage. Inhooks/src/events/*.rsparse_completed,systemMessageonly becomes a UI warning;hookSpecificOutput.additionalContextis what reaches the model. The output structs also reject unknown fields.apply_patch(core/src/tools/hook_names.rs), but the hooks only matched Bash.codex_hooksis a deprecated alias ofhooks(features/src/legacy.rs), which has been stable and on by default since 0.124 (Mark codex_hooks stable openai/codex#19012).hooks.jsonused/bin/sh -lc, which sources the user's login profile on every hook. On the machine this was built on, the profile has a syntax error undersh, so no hook could run at all. Codex itself runs hooks with<shell> -c(session/mod.rsbuild_hooks_config).What changes
model_context()helper builds every hook's output, so model context goes inhookSpecificOutput.additionalContext. Stop has no model channel and keeps{"continue": true}.apply_patch:Bash|apply_patch.apply-patch/src/parser.rs. The patch arrives astool_input.command.ix impacton the first 3 files in parallel.config.toml. It only warns if hooks are turned off there..codex/config.toml, the TOML editor and its tests are removed./bin/sh -c.hooks/src/engine/discovery.rs, hooks in~/.codex/hooks.jsonare skipped until the user trusts them (trusted_hash). Changinghooks.json, as this PR does, means users must review them again. The installer now prints a notice.hooks.mdexplains what moving to plugin-bundled hooks would take; those need the same trust step.Tests
tests/test_codex_hook_protocol.pychecks hook output against Codex's own schemas, copied intotests/fixtures/codex-0.155.1/. Against the old hooks it gives 8 failures and 5 errors, including the oldsystemMessageshape.test-local.shand the header check pass.CODEX_HOME, a local mock Responses server (no real credentials) and a strict fakeix. With the hooks untrusted,codex execskipped every one: the fakeixwas never called. That confirms the trust step in practice. A positive run, with hook context actually reaching a model, wasn't done: it needs--dangerously-bypass-hook-trustor a real trust entry.Notes
Not fixed here:
"python" "launcher" namehook command likely fails under PowerShell (-NoProfile -Command).commandWindowswith&would fix it, but it's untested.startup|resumemissesclear,compactandfork.ix impactwith a path relative to that subdirectory, not to the workspace root.🤖 Generated with Claude Code