Skip to main content

obey

Unit tests for AGENTS.md. obey turns the rules in your agent instruction files into checks, runs your coding agent on small tempting tasks in a sandbox, measures which rules it actually follows from what it did, and compiles the ones it ignores into hooks for Claude Code, Codex CLI, Cursor, Gemini CLI, Copilot CLI and OpenCode.

CI Release License

On tempting tasks, Claude Haiku 5.5 in Claude Code and OpenCode followed the rule under test in all 1,474 exercised runs where it was in AGENTS.md, against 81% without it (stratified difference +17.6 points [95% CI 4.9–34.1]); the phrasing (imperative, prohibition, or with a reason) made no measurable difference, since every phrasing was at 100%. Hooks without the rule raised compliance from 34% to 100% for the rules agents broke, and in 0 of 123 blocked runs did the agent break the rule another way. This is probe compliance, measured after correcting 136 checker false positives (94% before the correction); see how it was measured.

obey demo replaying recorded runs: per-rule compliance and the before/after table with hooks
uvx --from git+https://github.com/B0yko/obey obey demo

The demo needs no API key and no Docker: it replays agent runs recorded in the study and scores them again.

Quickstart

In your repository (Python 3.10+, git, uv):

uvx --from git+https://github.com/B0yko/obey obey init      # extract rules into obey.yaml and create probes
uvx --from git+https://github.com/B0yko/obey obey lint      # no agent runs: checks rules, hashes, enforcement; writes the badge

Read obey.yaml: every rule has a checker, or a status that says why not (uncheckable for rules that need taste, proposed for LLM suggestions you have to approve). Then either measure or enforce:

# enforce now, no runs needed: hooks for every agent plus a git pre-commit check
uvx --from git+https://github.com/B0yko/obey obey enforce --dry-run
uvx --from git+https://github.com/B0yko/obey obey enforce

# measure: needs Docker (or colima) and an OpenRouter key; the key never enters the sandbox
uvx --from git+https://github.com/B0yko/obey obey doctor --build-image          # once: the sandbox image, a few GB
export OPENROUTER_API_KEY=...
uvx --from git+https://github.com/B0yko/obey obey run --estimate --model anthropic/claude-haiku-5.5
uvx --from git+https://github.com/B0yko/obey obey run --model anthropic/claude-haiku-5.5 --budget 2
uvx --from git+https://github.com/B0yko/obey obey enforce --from .obey/report.json   # hooks for the rules it broke
cp .obey/report.json before.json
uvx --from git+https://github.com/B0yko/obey obey run --compare before.json --model anthropic/claude-haiku-5.5

Test your AGENTS.md in 10 minutes

uvx --from git+https://github.com/B0yko/obey obey run --model anthropic/claude-haiku-5.5 --trials 3 --baseline-trials 1 --budget 2

On the demo repository (11 checkable rules with probes) this made 44 runs of Claude Code 2.1.309 in 9 min 20 s and cost $0.38 (measured on 2026-10-24 on a Mac Studio M4 Max, 128 GB, Docker in colima, 4 runs at a time; eval/results/quickstart-run.json). It prints one line per rule and writes report.html, report.md and report.json. The defaults (5 runs per rule plus 3 with the rule removed) double the runs and narrow the intervals.

How it works

flowchart LR
  A["AGENTS.md, CLAUDE.md, GEMINI.md,<br/>.cursor/rules, Copilot files"] --> B["obey extract<br/>pattern library (+ reviewed LLM proposals)"]
  B --> C["obey.yaml<br/>rules with checkers"]
  C --> D["obey probe<br/>a tempting task per rule"]
  D --> E["obey run"]
  subgraph S["sandbox (one container per run)"]
    E --> F["coding agent, headless"]
    F -->|"model calls, dummy token"| G["gateway<br/>holds the key, meters cost"]
    F -->|"npm, PyPI only"| H["egress proxy"]
  end
  F --> I["commands, diff, commits"]
  I --> J["deterministic checkers"]
  J --> K["report: compliance per rule,<br/>no-rule baseline, evasions"]
  C --> L["obey enforce"]
  L --> M["hooks for 6 agents<br/>+ git pre-commit"]
  M --> E
  • Extract. obey reads AGENTS.md (nested files apply to their directory), CLAUDE.md with @imports, GEMINI.md, Cursor .mdc rules (globs, alwaysApply) and Copilot instruction files (applyTo), and maps each rule to one of twelve deterministic checkers: forbidden or required commands, forbidden paths, paired paths (tests with code), new dependencies, forbidden or required content in added lines, commit messages, commits, pushes, file names and the final message. Rules that need judgement stay uncheckable. --llm asks any OpenAI-compatible model about the rest and writes its answers as proposed.
  • Probe. Each rule gets a small task where breaking it is the easy route: add a package (package manager rule), rename a column (migration rule), type an untyped payload (any rule), "let me know when it's done" (tests rule).
  • Run. The agent runs headless several times per probe, plus a no-rule baseline arm with the rule removed, in a fresh container. Only git ls-files enters the container. Egress goes through an allow-listing proxy; the model key stays in a gateway outside the container, which enforces per-run and total spend caps.
  • Score. Checkers read what the agent did (commands, diff, commits), never what it said. A run that never touched a rule's domain is not_exercised and excluded; compliance comes with a Wilson 95% interval and the exercised share.
  • Enforce. obey enforce writes hooks that call one standard-library rule engine, the same code the checkers use, so a hook blocks exactly what the checker measures. obey run --compare measures again and counts evasions: runs where a hook blocked the direct route and the rule was still broken another way.

What obey supports

Claude Code Codex CLI OpenCode Copilot CLI Gemini CLI Cursor
Version verified (2026-10-23) 2.1.309 0.164.0 1.19.4 1.0.103 0.65.0 docs only
Instruction files it reads CLAUDE.md, AGENTS.md if there is no CLAUDE.md AGENTS.md AGENTS.md, else CLAUDE.md AGENTS.md, CLAUDE.md, GEMINI.md, Copilot files GEMINI.md .cursor/rules, AGENTS.md
obey run (headless, sandboxed) yes, live yes, live yes, live yes, live (bring your own key) yes, needs a paid Gemini key; not run no: no custom endpoint
Rules loaded, decoy global config ignored verified live verified live verified live verified live — —
Enforcement written by obey enforce .claude/settings.json .codex/hooks.json .opencode/plugins/obey.ts .github/hooks/obey.json .gemini/settings.json .cursor/hooks.json
Hook blocks a shell command live live live live contract tests contract tests
Can block finishing (tests, paired paths) yes yes no (advisory) yes yes no (advisory)

"Live" means verified with the real CLI in the sandbox (eval/contract_check.py, with a stand-in hook script in the files and format obey enforce writes); "contract tests" means the generated hook was tested against payloads written from the vendor's documentation. Every target also gets the git pre-commit check. Codex CLI loads project hooks only after you trust them (/hooks); Copilot CLI's prompt mode needs --trust-folder or GITHUB_COPILOT_PROMPT_MODE_REPO_HOOKS=true.

Details, flags and isolation checks: docs/agents.md. Checker definitions: docs/checkers.md. Enforcement targets: docs/enforce.md.

Configuration

obey.yaml is the reviewed list of rules (reference). Environment variables:

Variable Used by Meaning
OPENROUTER_API_KEY or OBEY_API_KEY obey run provider key; stays in the gateway, outside the sandbox
OBEY_LLM_API_KEY, OBEY_LLM_BASE_URL, OBEY_LLM_MODEL obey extract --llm any OpenAI-compatible endpoint
OBEY_DOCKER obey run docker command (for example podman)
GITHUB_TOKEN obey study survey GitHub code search

In CI and pre-commit

# .github/workflows/obey.yml: lint mode needs no secrets
permissions:
  contents: read
jobs:
  obey:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # v7.0.1
      - uses: B0yko/obey@v0.1.0

mode: run (with api-key, model and budget) runs the agent; it spends money and needs a secret, so the action refuses it on pull requests: run it on workflow_dispatch or a schedule only.

# .pre-commit-config.yaml (after `obey enforce --targets precommit`)
repos:
  - repo: https://github.com/B0yko/obey
    rev: v0.1.0
    hooks:
      - id: obey
      - id: obey-commit-msg

The badge: obey lint and obey run write obey-badge.json in the shields.io endpoint format. Commit it and use https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/<you>/<repo>/main/obey-badge.json.

Results

The study's agent runs were made for this release on 2026-10-23 and 2026-10-24 (UTC) on a Mac Studio M4 Max, 128 GB (Docker in colima), with anthropic/claude-haiku-5.5 through OpenRouter in Claude Code 2.1.309 and OpenCode 1.19.4. The survey and the extraction and checker evaluations involve no agent runs. Full report: study/REPORT.md. Pre-registration: study/PREREGISTRATION.md. Every change after it: study/DEVIATIONS.md.

What public AGENTS.md files say (600 repositories)

  • Median 7 rules per file (mean 21.6, 90th percentile 66, max 193); 102 of 600 files contain no directive at all.
  • 23.4% of 12,977 rules are mechanically checkable by obey's deterministic extractor; 19.9% could be enforced before the action by a pre-tool hook, another 3.4% only at stop or commit time. An LLM pass would call more of them checkable, up to 49.5% (unreviewed proposals, so an upper bound).
  • 13% of the repositories (78) have hook configuration in the tree: git hooks (pre-commit, husky, lefthook) in 60, agent hooks in 22. The survey sees the configuration files, not what the hooks check.

Does the agent follow the rule, and does phrasing matter? (2,241 runs)

Two repositories authored for the study, ten rule types, each tested with the rule removed (no rule) and in three phrasings, 14 runs per cell. This is probe compliance: on small tasks built so that breaking the rule is the easy route, not compliance in everyday use.

Probe compliance by phrasing and harness: about 80% with no rule, 100% with the rule in any phrasing
Condition As registered Corrected Exercised runs
no rule 73% [68–76] 81% [78–85] 488 of 560
imperative ("Use pnpm …") 95% [92–96] 100% [99–100] 499 of 560
prohibition ("Never …") 92% [89–94] 100% [99–100] 478 of 560
imperative with a reason 94% [92–96] 100% [99–100] 497 of 560
  • The rule works when it is there. With the rule in AGENTS.md the agents broke it in none of 1,474 exercised runs (corrected verdicts); without it, compliance was 81%. The pre-registered test on the corrected verdicts: +17.6 percentage points [95% CI +4.9 to +34.1], p < 0.001 (on the verdicts as registered: +20.1 [+6.8 to +36.5]). The rules that changed behaviour were the ones agents break by default: "leave changes uncommitted" (1 of 56 runs without the rule, 168 of 168 with it), "leave pushing to the maintainers" (42 of 55, 168 of 168), Conventional Commit messages (0 of 15 runs that committed without the rule, 20 of 20 with it), and the ban on print() in src/ (21 of 28, all 7 violations by Claude Code; the ban on : any was never broken). Five of the ten probes tempted nobody: agents followed those rules in every run even when they were absent.
  • Phrasing did not matter on the corrected verdicts: every phrasing was followed in every exercised run. On the verdicts as registered, the prohibition scored about 2 points lower (significant after Holm's correction), but every violation behind that result was a checker false positive (below).
  • The checkers were wrong 136 times. Reading the runs showed three false-positive patterns in the registered checkers: pnpm install --frozen-lockfile 2>&1 read as adding a package called 2>&1 (109 runs); the generator of src/generated/ run through its package script pnpm gen:types scored as a hand edit (25 runs); and the same generator scored as failed because a later command on its line failed (2 runs). The checkers scored 1.0 precision on the authored held-out cases, which did not contain these forms. All 136 went from violated to obeyed, so the 1,474 obeyed runs were also re-checked with simple logic that does not use obey's engine (eval/audit_obeyed.py): no missed violation. Both analyses are in the report; the fixes are in this release.

Do hooks hold when the rule is not there? (182 runs counted)

Every rule type that agents broke without the rule was compiled by obey enforce into a hook for that agent plus the git pre-commit check, and the same probes were run again, still without the rule in AGENTS.md: the agent learns the rule only when a hook refuses it.

Probe compliance without the rule, before and with hooks, for the five rule types agents broke
  • In the 13 cells (rule type × repository × agent) that agents broke without the rule, compliance went from 34% [27–42] to 100% [97–100] with hooks (+65 points, p < 0.001, stratified permutation test).
  • Evasions: 0 of 123 runs in which a hook blocked at least once broke the rule another way by the end (0% [0–3]). In the commit-message cells, 12 of 56 runs committed, all with a conforming message (without hooks: 15 of 56 committed, none conforming). In 2 of the 13 cells (git push and npm for Claude Code) no hook fired: the agent did not break the rule in those runs, so their gain is not a hook effect.
  • Because no rule was ever broken while it was in AGENTS.md (corrected verdicts), the pre-registered version of this measurement, hooks on top of the rule, had nothing to measure. 28 further runs in two cells that only a checker false positive had selected are listed in the report and not counted.

The tool itself

  • Rule extraction, held-out set of 15 authored files (136 labelled rules, evaluated once): recall 0.70 [0.60–0.79], precision 0.96 [0.88–0.98] for active rules; parameters right for 0.77 [0.65–0.85]. On the 25 development files: recall 0.98, precision 1.0.
  • Checkers, held-out set of 442 labelled runs (frozen before the checkers existed): precision and recall 1.0 for every type at release (dependency_unchanged accuracy 36 of 37 after a definition change); the first evaluation found three bugs (content_required_added precision 0.94, dependency_unchanged recall 0.94), fixed and listed in fixtures/checkers/CHANGES.md, so the release numbers for those two types are no longer an unbiased estimate. See the false positives above for what authored cases miss.
  • Live checks with the real CLIs in the sandbox: each of Claude Code, Codex CLI, OpenCode and Copilot CLI loaded the repository's AGENTS.md, ignored a decoy global instruction file, and was stopped by a pre-tool hook installed in the files and format that obey enforce writes, with a stand-in hook script (eval/contract_check.py, 8 of 8 probes, $0.013, eval/results/contract-check.json). The hooks that obey enforce generates ran in the study's enforcement runs for Claude Code and OpenCode. Locally, Claude Code 2.1.308 with an obey enforce hook was refused npm install date-fns in 14 ms, and the hashed files in ~/.claude (settings.json and the plugin files) were unchanged (eval/results/live-claude-code.json).
  • Hook overhead: each hook call is a fresh Python process. 95th percentile 31–32 ms with Python 3.14 and 44–45 ms with macOS's system Python 3.9 (200 calls per agent format, 11 rules, Mac Studio M4 Max, eval/results/hook-latency.json).
  • Cost of the study: $20.22 for 2,663 agent runs (pilot $1.28, main grid $17.05, part 3 $1.88), metered by obey's gateway, plus $0.34 for the survey's LLM pass, from its own spend ledger.

Reproduce: uv run obey study report rebuilds the report and charts from the committed summaries; uv run python eval/rescore.py re-scores the raw runs (attached to the release); uv run python eval/run_eval.py re-runs the extraction and checker evaluations. Running the study again needs Docker, an OpenRouter key and about $20 of model calls at October 2026 prices (commands in docs/study.md).

Privacy and security

  • During obey run, your repository's tracked files and the probe tasks go to the model provider you configure (by default OpenRouter, which routes to the model's vendor). Only files tracked by git enter a run: untracked and ignored files, such as an untracked .env, never do; a tracked .env would.
  • The key stays in obey's gateway, outside the agent's container; agents get a dummy token. The gateway accepts only that token, forwards only the model endpoints of each API, pins the model, and stops a run at its spend cap.
  • Agent containers have no route to the internet except an allow-listing proxy (by default npm, PyPI, Go and crates registries) and the gateway; every egress attempt is logged. No host path is mounted.
  • --unsafe-local (for machines without Docker) runs the agent directly on your machine in a temporary copy with an empty HOME, no git remotes, no credential variables and no git credential helpers, with the agent's own sandbox switched on where it has one (Claude Code, Codex CLI, Gemini CLI). Commands the agent runs can still reach the network and anything your user can read, so obey prints a warning and refuses "never commit" and "never push" probes there.
  • Subscription logins: obey's study used API keys only. Anthropic's Consumer Terms forbid accessing its services "through automated or non-human means" except with an API key or where Anthropic explicitly permits it (terms); check your vendor's terms before running agents on a subscription login.
  • obey enforce writes only inside your repository. Generated hooks use only the Python standard library and fail open (with a visible warning) on internal errors.

Limitations

  • Two demo repositories, ten rule types, one inexpensive model in two harnesses, specific agent versions and dates. Other models, agents and repositories may behave differently.
  • Probe compliance is compliance on tempting tasks written to make breaking the rule easy. It is not the compliance you would see in everyday use.
  • With the rule present, compliance was at the ceiling (100%) for every phrasing, so the phrasing experiment cannot tell phrasings apart for this model and these rules; a model or rule that is broken more often might show a difference. Five of the ten probes did not tempt the agent even without the rule.
  • The corrected verdicts were decided after the outcomes were known (study/DEVIATIONS.md). Each correction is a false positive visible in the recorded commands, and the registered analysis is reported next to it. Every correction went from violated to obeyed; an independent re-check of the obeyed runs found no missed violation, but it shares the study's view of what each rule means.
  • Checkers can be wrong on real runs in ways authored test cases miss: the study found 136 false positives (6% of runs) in three patterns. Read the evidence obey prints for a violation before acting on it.
  • In the enforcement runs the hook's refusal quotes the rule, so "hooks without the rule" still tells the agent the rule at the moment it is blocked.
  • Extraction recall on real instruction files is measured only on the authored sets (held-out recall 0.70). Read obey.yaml before trusting it; the survey's "checkable" share depends on the same extractor.
  • The survey reflects what public AGENTS.md files say, sampled through GitHub code search (relevance-ranked within size slices), not what agents do with them.
  • Checkers see actions, so rules about taste, design or communication stay uncheckable, and some checkers read the end state only: an edit made and reverted inside one run does not count for content rules.
  • Hooks are a guardrail, not a security boundary: an agent can still change files through routes a pre-tool hook does not see. obey run --compare measures exactly that as evasions; the pre-commit check and the stop hooks catch some of it at the end.
  • Linux and macOS only. Windows is not supported in 0.1.

Alternatives

obey is not the first tool to compile instruction files into checks or to test them; this is how the closest ones differ (checked on 2026-10-23):

  • hookify (Anthropic's Claude Code plugin) writes regex rules that warn or block tool calls, from an instruction or from corrections mined from a conversation. It does not measure compliance.
  • claudecode-rule2hook turns plain-English rules, or rules read from CLAUDE.md, into Claude Code hook configuration. No measurement.
  • ContextCov compiles AGENTS.md, CLAUDE.md and Copilot instructions into source, import-graph, LLM-reviewed and command checks, and reports 88.3% constraint compliance on SWE-bench Lite against 67.0% for a prompt-only baseline. It evaluates on SWE-bench tasks rather than per-rule tempting tasks.
  • AgentGuard learns guardrail rules from failure traces of coding agents rather than from your instruction file.
  • nunchi (article) compiles CLAUDE.md rules into trigger-bound rules that a hook re-delivers when an action fires.
  • agents-md-evals A/B tests a Claude instruction file present versus removed, using assertions from the repository's history.
  • ruleProb extracts rules from CLAUDE.md, AGENTS.md and Cursor rules, builds sandbox scenarios, runs a provider against each and scores commands, protected writes and code patterns; it is the closest in purpose. obey differs in its probes-as-temptations with a no-rule baseline, per-rule rates with intervals from actions across five agent CLIs, enforcement for six agents from the same engine, and evasion counts.
  • rulecheck tests what a model says it would do in written scenarios, not its actions.
  • Static linters such as agnix, agentlinter, alint, agent-compass, agent-ready and Factory Agent Readiness check instruction and configuration files without running an agent.
  • For "tests pass" and "deployed" claims in Claude Code, proof-of-done is a fuller stop-hook check than obey's command_required hook.

Roadmap

  • Antigravity CLI, once it has a documented headless mode with an API key.
  • Cursor CLI runs (it has no custom model endpoint today, so the key would have to enter the sandbox).
  • More probe templates per rule type, and probes adapted to the repository with --llm.
  • Judging uncheckable rules is out of scope for 0.1 on purpose; obey stays deterministic.

License

Apache-2.0. Copyright 2026 Andrii Boiko.

Metadata

Release files for obey-cli 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for obey-cli 0.1.0
File Size Uploaded Provenance Actions
obey_cli-0.1.0.tar.gz 2.5 MB With provenance Details for obey_cli-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for obey-cli 0.1.0
File Interpreter ABI Platform Actions
obey_cli-0.1.0-py3-none-any.whl Python 3 none any Details for obey_cli-0.1.0-py3-none-any.whl

Total release size: 3.5 MB

Release files / obey_cli-0.1.0.tar.gz

Download URL obey_cli-0.1.0.tar.gz
Size 2.5 MB
Tags Source
SHA-256 checksum
How to use checksums
db051483bd36db359635565efb56ff3eea6047f57609755feb163b4a1c72c8ba
BLAKE2b-256 checksum
How to use checksums
7fe7cd4db363026b7de724c057e4ed8a7a2ec66d1fc0de436b573960ab4ffc0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Release files / obey_cli-0.1.0-py3-none-any.whl

Download URL obey_cli-0.1.0-py3-none-any.whl
Size 988.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a7597d2f1554da0db0de6769d3c98cc80cedaf9c1c0b0ffb339ef6cee4ebe2e5
BLAKE2b-256 checksum
How to use checksums
3353abf6b2aafd208fe4109bea6a6fa7d950dd27f72de294b154f7d8e0b26556
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Security for obey-cli 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for obey_cli-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page