Skip to content

Make CI runs reproducible with --ignore-user-config and --ignore-rules

The CI job from the last lesson works, but it carries a hidden dependency. Every codex exec run - local or in CI - picks up two layers from the machine it happens to be on: your personal config from ~/.codex/, and the execpolicy .rules files, the command-policy files Codex reads from your home directory and the project. That’s the right default when you run it - your preferences and your project’s notion of which commands are safe both apply without you asking. But it’s a liability the moment the run is supposed to be identical everywhere.

Think about what’s actually loading into that CI run. Your ~/.codex/config.toml holds your preferences - your default sandbox, your model effort, your chosen profile. None of that exists on the runner; if it did (say a teammate triggers the job from their own setup, or you mount a home dir), it would be their preferences, not yours. Your home-directory .rules file is the same story: a per-machine command policy deciding what a shared gate may execute. So the same commit and the same prompt can produce different behaviour on your laptop versus the runner - not because the code changed, but because the machine’s layers did.

For an interactive session that drift is invisible and harmless. For an unattended check you want to trust as a gate, it’s exactly the thing to kill. Reproducibility here is the same context-engineering idea the whole course turns on, pointed at CI: pin the context that goes in, and you can rely on the behaviour that comes out.

codex exec takes two flags that strip those layers away (CLI reference):

  • --ignore-user-config prevents Codex from loading ~/.codex/config.toml. The run no longer inherits whatever personal defaults happen to be on the box - it uses only the flags and config you pass explicitly. This is what keeps a runner’s stray home directory, or your own machine-specific tweaks, from leaking into the result.
  • --ignore-rules skips loading the user and project execpolicy .rules files - the command policy that decides which shell commands Codex may run unprompted. The run’s blast radius is then decided entirely by the sandbox and approval flags you passed, not by a policy file that might have changed since you last looked.

Layer them onto the CI command from the last lesson and you get a run with fewer hidden configuration inputs:

codex exec "Recategorise new transactions, then run the suite. Money is integer cents - never float." \
--ignore-user-config \
--ignore-rules \
--ask-for-approval never \
--sandbox workspace-write

Mind what --ignore-rules does and doesn’t take with it

Section titled “Mind what --ignore-rules does and doesn’t take with it”

Be precise about which “rules” that second flag means, because the name invites a costly misreading. It means execpolicy .rules files, the command policy. It does not touch AGENTS.md: your rules chain still loads under --ignore-rules, so budgetcli’s non-negotiables - money is integer cents, the category taxonomy, how dates are handled - still reach the agent. That’s almost always what you want in CI, and it’s why the AGENTS.md chain is safe to depend on here: it’s committed, so the commit pins it the same way it pins the code.

The trade you are making is about permission, not instruction. Strip the .rules files and the sandbox and approval flags become the only thing bounding what the run may execute - which is exactly why the command above states --sandbox workspace-write and --ask-for-approval never explicitly rather than letting either be inherited. What the run depends on should be staring at you in the workflow, not hiding in a file three directories up. (Restating the cents rule in the prompt, as the example does, is belt-and-braces rather than a requirement - a cheap habit when the job is touching money.)

Whether you reach for both flags or just one is a judgement call. --ignore-user-config is almost always right in CI - personal config has no business deciding a shared gate. --ignore-rules you reach for when a machine-local command policy shouldn’t get a vote in what a shared gate executes; if your project’s .rules file is committed, stable, and you want the run to honour it, keep it. The honest default for a gate you’ll trust unattended is to strip the user config, decide the rules flag on its merits, and pin the sandbox and approval flags explicitly either way.

One layer above all of this, for teams on the plans that support it: Codex has a managed-configuration layer an administrator can enforce across the CLI, app, and IDE, and a requirements.toml org-policy file that constrains the security-sensitive settings - approval mode, sandbox, web-search and MCP allowlists. Where that’s in force, an admin can guarantee the floor: no unattended run, however it was launched, gets to set itself a looser sandbox or a more permissive approval mode than policy allows. If a setting conflicts with the requirements, Codex falls back to a compatible value and tells you. You don’t have to set any of this up to ship budgetcli’s check - but it’s worth knowing the lever exists, because it’s how an organisation makes the safe posture you’ve been building by hand a default, not a thing every engineer has to remember.

Your CI run is now more reproducible: it has fewer user- and project-policy surprises, but you still need to pin the CLI version, model, dependencies, hooks, MCP servers, runner image, network, and managed policy when those affect the result. That covers the whole span of jobs a single codex exec call can carry - one prompt, one result, one exit code. The last lesson looks at what happens when a job outgrows that shape: when you need programmatic thread control or broader orchestration, and a shell script around exec starts buckling. That’s the Codex SDK.