Course · Codex · Automation
Make CI runs reproducible with --ignore-user-config and --ignore-rules
The CI job from the last lesson runs, authenticates, and fails shut on schedule - and it still carries a hidden dependency. Every codex exec call, local or in CI, picks up two layers from whatever machine happens to run it: your personal config from ~/.codex/, and the execpolicy .rules files that decide which shell commands Codex may run unprompted. That’s exactly the right default when you’re the one running it. It becomes a liability the instant the run is supposed to mean the same thing everywhere - which is the whole premise of an unattended check.
Why the same command drifts
Section titled “Why the same command drifts”Your ~/.codex/config.toml holds your preferences: your default sandbox, your model effort, your saved profile. None of that exists on the runner - and if it somehow did, by way of a stray home directory or a teammate triggering the job from their own machine, it would be their preferences standing in for a shared gate. Your .rules file is the same story: a per-machine command policy quietly voting on what a check every contributor relies on is allowed to run. The same commit and the same prompt can produce different behaviour on two machines, not because the code changed, but because the machine did. For an interactive session that drift is invisible and mostly harmless. For a check you’re trusting to catch a bad import before it corrupts months of history, it’s exactly the thing worth killing.
The committed brief
Section titled “The committed brief”Here’s the trick this lesson turns on, and it’s the sharpest form the rules-file half of this chapter’s contract takes: only what’s committed gets to count as this run’s instructions. Your AGENTS.md chain - the one the rules chapter built up to 32 lines, roughly 284 estimated tokens loaded every session - is checked into the repo, so the commit pins it exactly the way it pins the code. Your personal config.toml and your machine’s .rules file aren’t committed anywhere; they’re opinions about how you like to work, not facts about what budgetcli needs. A reproducible run keeps the first and strips the second two:
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--ignore-user-configstops Codex loading~/.codex/config.toml. The run no longer inherits whatever personal defaults happen to sit on the box - only the flags and config you pass explicitly.--ignore-rulesskips the user and project execpolicy.rulesfiles - the command policy, not the instructions. Blast radius is then decided entirely by the sandbox and approval flags you already wrote into the workflow, not by a policy file that might have changed since you last looked at it.
What that second flag does and doesn’t take with it
Section titled “What that second flag does and doesn’t take with it”Read the name carefully, because it invites a costly misreading. --ignore-rules means the execpolicy .rules files - command policy. It does not touch AGENTS.md: the rules chain still loads under --ignore-rules, so budgetcli’s non-negotiables - money is integer cents, the fixed category list, the DD/MM/YYYY dates - still reach the agent every run. That’s almost always what you want, and it’s exactly why the AGENTS.md chain is safe to depend on here in a way nothing else is: it’s committed, so the commit pins it the same way it pins the code.
The trade you’re actually 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 outright instead of letting either be inherited from somewhere you’d have to go looking. What a run depends on should be sitting in the workflow file where you can see it, not three directories up in a file nobody diffed. Restating the cents rule directly in the prompt, as the example does, is belt-and-braces on top of the committed brief, not a replacement for it - a cheap habit worth keeping on anything that touches money.
Whether you reach for both flags is a judgement call, not a rule. --ignore-user-config is almost always right in CI - personal preference has no business deciding a shared gate. --ignore-rules is worth reaching for when a machine-local command policy shouldn’t get a vote in what that gate executes; if your project’s .rules file is itself committed, stable, and something you want the run to honour, keep it. The honest default for a check you’ll trust unattended: strip the user config, decide the rules flag on its own merits, and pin the sandbox and approval flags explicitly either way.
When the org owns the policy instead of you
Section titled “When the org owns the policy instead of you”One layer above all of this, on plans that support it: Codex has a managed-configuration layer an administrator can enforce across the CLI, app, and IDE, backed by a requirements.toml org-policy file constraining the security-sensitive settings - approval mode, sandbox, web-search and MCP allowlists. Where it’s in force, no unattended run, however it was launched, can set itself a looser sandbox or a more permissive approval mode than policy allows; a conflicting setting falls back to a compatible value and Codex says so. You don’t need any of this to ship budgetcli’s check, but it’s the lever an organisation pulls to make the posture you’ve been building by hand the default, not something every engineer has to remember on their own.
Your CI run is more reproducible now - fewer user- and project-policy surprises - but “reproducible” still has edges this lesson didn’t close. You still need to pin the CLI version, the model, dependencies, hooks, MCP servers, the runner image, network access, and any managed policy, whenever those affect the result. That covers the whole shape a single codex exec call can carry: one prompt, one result, one exit code. The last lesson is about the job that outgrows that shape entirely.