This page is about contributors running the Codex CLI against this repository. It is not about FoxxyCode's own rules discovery, which is a product feature documented in Rules - that one describes how the foxxycode binary injects {{.Rules}} into its system prompt.
Codex resolves its instruction chain once per session, walking from the repository root down to the directory it was launched in, and concatenating at most one AGENTS.md per directory. Two consequences matter here:
- a session started at the repository root never loads a nested
AGENTS.md, no matter which files it goes on to edit; - there is no glob-based attachment. Codex has no equivalent of the
globsfield in.cursor/rules/*.mdc, so scope can only be expressed by where a file sits in the tree.
This repository keeps its detailed rules in .cursor/rules/*.mdc. The previous arrangement pointed Codex at .codex/rules.md and asked it to open the relevant rule file before editing. That is advisory, and it gets skipped.
.codex/hooks/attach_rules.py closes the gap by injecting rule bodies into the session directly, so compliance no longer depends on the model choosing to read a file.
| Event | Matcher | What is injected |
|---|---|---|
SessionStart |
startup|resume|clear|compact |
every rule whose frontmatter has alwaysApply: true |
PreToolUse |
^(apply_patch|Edit|Write)$ |
rules whose globs cover a file in the pending patch |
Configuration lives in .codex/hooks.json. Both handlers run the same script; it branches on hook_event_name from the hook payload.
For this repository that currently means architecture, code-style, testing and workflow arrive at session start (about 9.5 KB total), and the remaining six attach on demand. A patch touching external/httpserver/server.go pulls in api-layer and implementation-order; one touching external/ui/src/ pulls in ui-spa and ui-verification.
The script parses the .mdc frontmatter itself (description, globs, alwaysApply), so .cursor/rules/ stays the single source of truth and a new rule file needs no wiring. Points worth knowing:
- Glob matching is hand-rolled.
fnmatchis unusable because its*also crosses/, which makesexternal/httpserver/**/*.gomissexternal/httpserver/server.go. The script translates globs to a regex where**/becomes "zero or more directories",*stays inside one segment, and a bare**spans anything. - Paths come from the patch itself.
*** Add File:,*** Update File:,*** Delete File:and the*** Move to:destination are all read, and absolute paths are made repo-relative before matching. - Each rule is injected at most once per session. State is a JSON file under
<tempdir>/codex-foxxycode-rules/<session_id>.json, so re-editing the same area does not re-send the same 3 KB. ASessionStartwithsource: "clear"resets it. - It fails open. Malformed JSON on stdin, an unparsable rule file, or an unwritable state directory all exit 0 with no output. A broken rule can never block an edit.
Codex caps model-visible hook output at roughly 2500 tokens by default, then spills the remainder to <temp_dir>/hook_outputs/ and shows the model a head-and-tail preview instead. Both handlers therefore raise additionalContextLimit, to 8000 at session start and 6000 per patch. Those are ceilings, not reservations; the real payloads sit well under them.
Keep this in mind when adding rules. The budget is shared with every other hook and plugin in the session, and oversized always-on context degrades the model rather than helping it.
Codex requires explicit approval before a non-managed command hook can run, and records that approval against the hash of the hook definition. An unapproved hook is skipped without a hard error, which looks exactly like a hook that is not firing.
- Run
/hooksonce per clone to review and trust both handlers. - Run it again after any edit to
attach_rules.pyorhooks.json. - Project-local hooks load only when the
.codex/layer is trusted. In an untrusted checkout Codex still loads user and system hooks, but not these.
This mirrors the workspace trust gate FoxxyCode applies to project-local .foxxycode/mcp.json (see MCP Integration): opening a repository must not by itself grant it code execution.
Drive the script by hand with a synthetic payload. It reads JSON on stdin and writes JSON on stdout, so no Codex session is needed:
echo '{"hook_event_name":"PreToolUse","session_id":"probe","tool_input":{"command":"*** Begin Patch\n*** Update File: external/httpserver/server.go\n*** End Patch"}}' | python3 .codex/hooks/attach_rules.pySession-start behaviour:
echo '{"hook_event_name":"SessionStart","session_id":"probe","source":"startup"}' | python3 .codex/hooks/attach_rules.pyEmpty output is a valid answer and means one of three things: no glob matched, the rule was already sent in this session, or the input was not understood. Clear the dedup state to re-test:
rm -rf "${TMPDIR:-/tmp}/codex-foxxycode-rules"If a hook produces nothing inside a real session, check /hooks first. Trust is the usual cause.
Edit .cursor/rules/*.mdc as before. The hook needs no change, but two things do:
- a rule is only reachable from Codex if its frontmatter carries
globsoralwaysApply: true; - the index in
.codex/rules.mdand the bridge section inAGENTS.mdare refreshed in the same change that adds, renames, or removes a rule file.
Codex uses the word "rules" for a second, unrelated mechanism: .rules files written in Starlark under a rules/ directory next to an active config layer, which decide which shell commands may run outside the sandbox. Those are an execution policy, not instructions, and they are validated with:
codex execpolicy check --pretty --rules ~/.codex/rules/default.rules -- go test ./...This repository ships no .codex/rules/ directory; command policy is a per-developer concern and lives in ~/.codex/rules/. Note that Codex only decomposes a bash -lc script into separate commands when it is a plain chain of words joined by &&, ||, ; or |. A script containing VAR=x, $(...), a wildcard, or control flow is matched as one opaque invocation, which is why auto-recorded allow-list entries tend to be unusable on any later command.
| Agent | Always-on rules | Scoped rules |
|---|---|---|
| Cursor | alwaysApply: true in .cursor/rules/*.mdc |
globs in the same frontmatter |
| Claude Code | CLAUDE.md (symlinked to AGENTS.md here) |
.claude/rules/*.md referenced from it |
| Codex | AGENTS.md plus the SessionStart hook |
PreToolUse hook, this page |
Upstream reference: Hooks and Custom instructions with AGENTS.md.