Codex apply_patch adapter drops edit content, silently defeating contentPatterns block guardrails on new files

Author: Job28703Created Jul 19, 2026Updated Jul 19, 2026

Summary

The README's "Works with Codex Too" section states that edit blocking for block-enforced skills runs identically under both Claude Code and Codex (the only documented parity gap being the missing Skill tool event). I found a second, undocumented parity gap: content-based guardrails don't fire under Codex when creating new files.

Root cause

.codex/hooks/_codex-adapter.sh translates Codex's apply_patch into a synthetic Edit event containing only {tool_input: {file_path: ...}} — it does not include the patch content (new_string/content).

skill-verification-guard.ts's getEditContent() relies on those fields to get the edit text for contentPatterns matching. When they're absent, it falls back to readFileSync(filePath) — but for an apply_patch "Add File" operation, the target file doesn't exist yet at PreToolUse time, so existsSync(filePath) is false and the fallback also comes back empty. contentMatch therefore stays false.

Since shouldTrigger = pathMatch && (contentMatch || !fileTriggers.contentPatterns), any guardrail configured with contentPatterns (in this repo, frontend-dev-guidelines's MUI detection is the only enforcement: block guardrail with contentPatterns) never triggers when Codex creates a new matching file — even though the same operation via Claude Code's native Write tool (which does supply content directly) would block it.

Reproduction

  1. Install this repo's .claude/ + .codex/ into a project with the frontend-dev-guidelines guardrail configured.
  2. Under Claude Code: ask it to create a new .tsx file containing MUI-triggering code → blocked as expected.
  3. Under Codex (same prompt, same trigger content) → not blocked, because the adapter never forwards the new file's content to the guard.

Suggested fix

Have _codex-adapter.sh include the patch body (or at least the new-file content for "Add File" operations) in the synthesized event's tool_input, so getEditContent() has something to match against without needing to read a file that doesn't exist yet.

Related

Separate from the already-documented Skill-tool-event parity gap in the README — this one isn't currently disclosed anywhere.

Source: diet103/claude-code-infrastructure-showcase