# Codex Hooks CPU Refactor

Audience: Codex hook maintainers and AI coding agents.

## Scope and invariants

- Scope only `/home/user/.codex` hook configuration, account hook links, and listed hook scripts.
- MUST preserve every matcher, event, decision, exit status, diagnostic, and side effect.
- MUST keep `block-background-bash.mjs` and all slopgate gates enabled at current firing frequency.
- MUST NOT change `/home/user/.claude`.
- Treat enforcement refactors as fail-closed migrations: prove allow, deny, malformed-input, and side-effect parity before activation.

## Active registration inventory

`/home/user/.codex/config.toml` enables hooks through `[features] hooks = true`. Canonical definitions live in `/home/user/.codex/hooks.json`.

| Hook | Behavior | Event / matcher | Frequency | Runtime and spawned work | Measured rough wall cost |
|---|---|---|---|---|---:|
| `/home/user/.codex/hooks/block-background-bash.mjs` | Denies `tool_input.run_in_background === true`; otherwise emits neutral PreToolUse output | PreToolUse / `Bash\|shell\|local_shell\|shell_command\|command_execution` | Every matching shell call | Explicit `/usr/bin/bun`; shebang says Node but is bypassed | 84.8 ms/call |
| `/home/user/Projects/slopgate/hooks/commit-hook.sh` | Parses Bash command; runs staged slopgate only for `git commit` in configured repos | PreToolUse / `Bash` | Every Bash call; full scan only commits | Bash + `cat` + Bun JSON parser + `grep`; commit path execs Bun slopgate | 113.3 ms/call on non-commit |
| `/home/user/Projects/slopgate/hooks/baseline-guard.sh` | Blocks direct baseline/suppression writes, baseline CLI, and baseline removal/move; records deny stats | PreToolUse / `Bash\|Edit\|Write` | Every Bash/Edit/Write | Bash + `cat` + 2–3 cold Node parsers + 1–2 `grep`; deny path adds Node stats writer | 633.9 ms/call on allowed Bash |
| `/home/user/Projects/slopgate/hooks/edit-hook.sh` | Scans edited TS/TSX/Astro files when repo has slopgate config | PostToolUse / `Edit\|Write` | Every Edit/Write; scan only eligible source | Bash + `cat` + Bun parser; eligible path adds Git/path utilities and Bun slopgate, capped at 5 s | 108.2 ms/call on ineligible file |
| `/home/user/.codex/memory-bridge/sessionstart.mjs` | Resolves worktree/project root and emits existing memory-file index | SessionStart / all sessions | Once per session | Explicit `/usr/bin/bun`; up to three synchronous Git commands + file existence checks | 92.8 ms/session in Git repo |
| `/home/user/Projects/slopgate/hooks/session-start.sh` | Writes model/session attribution file keyed by project root | SessionStart / all sessions | Once per session | Bash + Git/realpath + cold Node | 342.5 ms/session |

Measurement: serial cold invocations on this host, 2026-07-18; 10–100 samples per hook. Empty-runtime baseline was Node 208.5 ms versus Bun 4.2 ms. Ordinary Bash invokes three top-level hooks and roughly 12 total processes after wrapper/parser/grep children. Combined measured cost was 821.5 ms/Bash call. Treat figures as comparative, not stable latency guarantees.

## Redundancy and drift findings

1. Account hook definitions are already consolidated. These paths are symlinks to `/home/user/.codex/hooks.json`:
   - `/home/user/.systray-ai/accounts/avi/CODEX_HOME/hooks.json`
   - `/home/user/.systray-ai/accounts/roy/CODEX_HOME/hooks.json`
   - `/home/user/.systray-ai/accounts/rafa/CODEX_HOME/hooks.json`
   - `/home/user/.systray-ai/accounts/new-account/CODEX_HOME/hooks.json`
2. All five logical paths resolve to SHA-256 `e096aec43bb22fc9a37195093180e889ff6fba96d7efeda3e40fc69e759818f5`. No duplicate-file drift exists.
3. `[hooks.state]` held 65 registrations: 30 map to current hook indices; 35 reference removed events/groups/hooks. Removed events included `PreCompact`, `UserPromptSubmit`, and `Stop`; removed indices included obsolete PreToolUse/PostToolUse/SessionStart entries.
4. `hooks.json.bak` referenced an old slopgate worktree. `hooks.json.bak-node` referenced removed context-mode and memory hooks plus Node commands. Neither backup was registered or referenced.
5. Active gates do not duplicate decisions:
   - background gate controls observability/lifecycle;
   - commit hook runs staged code-quality enforcement;
   - baseline guard prevents enforcement-state tampering.
   Merging decisions or dropping one changes intent.
6. All three Bash PreToolUse hooks independently consume and parse identical stdin. Shared parse/dispatch can remove process churn, but matcher coverage differs and must remain exact.
7. Naïve Node→Bun replacement is behavior-breaking:
   - `baseline-guard.sh`: Bun `-e` argument indexing made all three deny cases return allow in a six-case parity probe.
   - `session-start.sh`: Bun exited before its stdin callback wrote attribution state.
   No active interpreter was changed.

## Ranked changes

| Rank | Change | CPU win | Risk | Disposition |
|---:|---|---|---|---|
| 1 | Replace three Bash PreToolUse registrations with one dispatcher that parses stdin once, then executes all three existing decisions independently and combines results without short-circuiting coverage | Very high: removes repeated shells, parsers, and greps from every Bash call | High: matcher/exit/output composition can alter enforcement | Human review only |
| 2 | Rewrite `baseline-guard.sh` as one Bun program or one awaited-stdin Bun invocation; preserve regexes, deny exit 2, diagnostics, and stats side effect | High: removes 2–3 Node cold starts per Bash/Edit/Write | High: enforcement parity already failed for mechanical swap | Human review only |
| 3 | Rewrite `session-start.sh` around awaited Bun stdin and synchronous write; preserve file schema/path and fail-open behavior | Medium per session | Medium: mechanical swap silently lost attribution | Human review only |
| 4 | Make `commit-hook.sh` and `edit-hook.sh` consume JSON through shared dispatcher parse output when Rank 1 lands | Medium | Medium: standalone event behavior must remain supported | Human review only |
| 5 | Prune nonexistent `[hooks.state]` registrations | Small startup/config-parse win; large clarity win | Low | Applied |
| 6 | Delete stale `hooks.json.bak*` files | No runtime CPU win; removes restoration/drift hazard | Low | Applied |
| 7 | Consolidate account hook files | None: already symlinked | Unnecessary | No change |

## Required parity gate for deferred work

Before activating Ranks 1–4:

1. Capture fixtures for every current allow/deny branch, malformed JSON, missing repo/config, eligible/ineligible edits, commit/non-commit, every registered matcher, and stats/session file side effects.
2. Run old and candidate implementations against identical isolated HOME/repo fixtures.
3. Require exact parity for exit status, stdout, stderr, output JSON, filesystem writes, and gate invocation count.
4. Benchmark at least 100 warm and 100 cold calls per event.
5. Activate one account/session canary; verify Codex matcher dispatch and trust-state behavior.
6. Roll out without reducing registrations or matcher coverage.

## Applied safe changes

- `/home/user/.codex/config.toml`: removed 35 state tables whose event/group/hook indices do not exist in current definitions; retained all 30 live registrations across five logical hook paths.
- `/home/user/.codex/hooks.json.bak`: deleted obsolete worktree-target backup.
- `/home/user/.codex/hooks.json.bak-node`: deleted obsolete removed-hook/Node backup.

No hook definition, matcher, script, interpreter, decision, exit behavior, or firing frequency changed.
