fix: policy/*.codexpolicy -> rules/*.rules (#7888)

We decided that `*.rules` is a more fitting (and concise) file extension
than `*.codexpolicy`, so we are changing the file extension for the
"execpolicy" effort. We are also changing the subfolder of `$CODEX_HOME`
from `policy` to `rules` to match.

This PR updates the in-repo docs and we will update the public docs once
the next CLI release goes out.

Locally, I created `~/.codex/rules/default.rules` with the following
contents:

```
prefix_rule(pattern=["gh", "pr", "view"])
```

And then I asked Codex to run:

```
gh pr view 7888 --json title,body,comments
```

and it was able to!
This commit is contained in:
Michael Bolin
2025-12-11 14:46:00 -08:00
committed by GitHub
parent bacbe871c8
commit e0d7ac51d3
11 changed files with 58 additions and 54 deletions

View File

@@ -20,14 +20,14 @@ prefix_rule(
```
## CLI
- From the Codex CLI, run `codex execpolicy check` subcommand with one or more policy files (for example `src/default.codexpolicy`) to check a command:
- From the Codex CLI, run `codex execpolicy check` subcommand with one or more policy files (for example `src/default.rules`) to check a command:
```bash
codex execpolicy check --policy path/to/policy.codexpolicy git status
codex execpolicy check --rules path/to/policy.rules git status
```
- Pass multiple `--policy` flags to merge rules, evaluated in the order provided, and use `--pretty` for formatted JSON.
- Pass multiple `--rules` flags to merge rules, evaluated in the order provided, and use `--pretty` for formatted JSON.
- You can also run the standalone dev binary directly during development:
```bash
cargo run -p codex-execpolicy -- check --policy path/to/policy.codexpolicy git status
cargo run -p codex-execpolicy -- check --rules path/to/policy.rules git status
```
- Example outcomes:
- Match: `{"matchedRules":[{...}],"decision":"allow"}`

View File

@@ -154,7 +154,7 @@ mod tests {
#[test]
fn appends_rule_and_creates_directories() {
let tmp = tempdir().expect("create temp dir");
let policy_path = tmp.path().join("policy").join("default.codexpolicy");
let policy_path = tmp.path().join("rules").join("default.rules");
blocking_append_allow_prefix_rule(
&policy_path,
@@ -162,8 +162,7 @@ mod tests {
)
.expect("append rule");
let contents =
std::fs::read_to_string(&policy_path).expect("default.codexpolicy should exist");
let contents = std::fs::read_to_string(&policy_path).expect("default.rules should exist");
assert_eq!(
contents,
r#"prefix_rule(pattern=["echo", "Hello, world!"], decision="allow")
@@ -174,7 +173,7 @@ mod tests {
#[test]
fn appends_rule_without_duplicate_newline() {
let tmp = tempdir().expect("create temp dir");
let policy_path = tmp.path().join("policy").join("default.codexpolicy");
let policy_path = tmp.path().join("rules").join("default.rules");
std::fs::create_dir_all(policy_path.parent().unwrap()).expect("create policy dir");
std::fs::write(
&policy_path,
@@ -201,7 +200,7 @@ prefix_rule(pattern=["echo", "Hello, world!"], decision="allow")
#[test]
fn inserts_newline_when_missing_before_append() {
let tmp = tempdir().expect("create temp dir");
let policy_path = tmp.path().join("policy").join("default.codexpolicy");
let policy_path = tmp.path().join("rules").join("default.rules");
std::fs::create_dir_all(policy_path.parent().unwrap()).expect("create policy dir");
std::fs::write(
&policy_path,

View File

@@ -14,9 +14,9 @@ use crate::RuleMatch;
/// Arguments for evaluating a command against one or more execpolicy files.
#[derive(Debug, Parser, Clone)]
pub struct ExecPolicyCheckCommand {
/// Paths to execpolicy files to evaluate (repeatable).
#[arg(short = 'p', long = "policy", value_name = "PATH", required = true)]
pub policies: Vec<PathBuf>,
/// Paths to execpolicy rule files to evaluate (repeatable).
#[arg(short = 'r', long = "rules", value_name = "PATH", required = true)]
pub rules: Vec<PathBuf>,
/// Pretty-print the JSON output.
#[arg(long)]
@@ -35,7 +35,7 @@ pub struct ExecPolicyCheckCommand {
impl ExecPolicyCheckCommand {
/// Load the policies for this command, evaluate the command, and render JSON output.
pub fn run(&self) -> Result<()> {
let policy = load_policies(&self.policies)?;
let policy = load_policies(&self.rules)?;
let matched_rules = policy.matches_for_command(&self.command, None);
let json = format_matches_json(&matched_rules, self.pretty)?;

View File

@@ -54,7 +54,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let cmd = tokens(&["git", "status"]);
let evaluation = policy.check(&cmd, &allow_all);
@@ -129,8 +129,8 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("first.codexpolicy", first_policy)?;
parser.parse("second.codexpolicy", second_policy)?;
parser.parse("first.rules", first_policy)?;
parser.parse("second.rules", second_policy)?;
let policy = parser.build();
let git_rules = rule_snapshots(policy.rules().get_vec("git").context("missing git rules")?);
@@ -194,7 +194,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let bash_rules = rule_snapshots(
@@ -259,7 +259,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let rules = rule_snapshots(policy.rules().get_vec("npm").context("missing npm rules")?);
@@ -323,7 +323,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let match_eval = policy.check(&tokens(&["git", "status"]), &allow_all);
assert_eq!(
@@ -367,7 +367,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let commit = policy.check(&tokens(&["git", "commit", "-m", "hi"]), &allow_all);
@@ -403,7 +403,7 @@ prefix_rule(
)
"#;
let mut parser = PolicyParser::new();
parser.parse("test.codexpolicy", policy_src)?;
parser.parse("test.rules", policy_src)?;
let policy = parser.build();
let commands = vec![