Repo fences for coding agents and PRs.
ScopeFence turns prose boundaries — "only touch the payments service", "don't edit infra", "keep it under 200 lines" — into executable diff checks.
Coding agents and hurried developers make changes that wander outside the intended area of a repo. They touch the wrong files, broaden refactors, violate path boundaries, introduce forbidden imports, or blow past reviewable diff size.
Today, these boundaries mostly live in review comments and Slack messages. That is too loose.
ScopeFence makes them executable:
- Define repo boundaries in a small YAML policy file.
- Run
scopefence checkagainst any git diff. - Get a precise verdict with offending files and reasons.
# Install from git tag (or use pip install -e ".[dev]" for local development)
pip install git+https://github.com/JohnODowdAI/scopefence.git@v0.1.0-beta.1
# Scaffold a policy
scopefence init --id payments-agent --title "Keep changes inside payments" -o fences/payments.yaml
# Validate the policy
scopefence lint fences/payments.yaml
# Check a diff against the policy
scopefence check fences/payments.yaml --base-ref main --head-ref HEADversion: "0.1"
id: payments-agent
title: Keep changes inside payments service
diff:
include:
- "services/payments/**"
- "tests/payments/**"
exclude:
- "docs/**"
forbid:
- "infra/**"
- ".github/**"
budgets:
max_files_changed: 6
max_added_lines: 200
max_deleted_lines: 100
content:
forbid_patterns:
- name: no-debug-print
files:
- "services/payments/**/*.py"
regex: "\\bprint\\s*\\("
imports:
python:
- name: payments-cannot-import-admin
files:
- "services/payments/**/*.py"
forbid_modules:
- services.admin
- infra╭──────────────────────╮
│ ScopeFence: PASS │
╰──────────────────────╯
Policy: payments-agent (v0.1)
Diff: 2 files, +15 / -3 lines
Passed: allowed_scope, forbidden_paths, file_count_budget,
added_line_budget, deleted_line_budget, no-debug-print,
payments-cannot-import-admin
╭──────────────────────╮
│ ScopeFence: FAIL │
╰──────────────────────╯
Policy: payments-agent (v0.1)
Diff: 4 files, +45 / -10 lines
┌─────────────────────┬────────────────────────────────────┬──────────────────┐
│ Rule │ Reason │ Files │
├─────────────────────┼────────────────────────────────────┼──────────────────┤
│ allowed_scope │ 2 file(s) changed outside allowed │ shared/utils.py │
│ │ scope │ infra/deploy.py │
├─────────────────────┼────────────────────────────────────┼──────────────────┤
│ forbidden_paths │ 1 forbidden file(s) touched │ infra/deploy.py │
├─────────────────────┼────────────────────────────────────┼──────────────────┤
│ payments-cannot- │ Newly introduced forbidden │ services/ │
│ import-admin │ import(s) │ payments/ │
│ │ `services.admin` (matches │ service.py │
│ │ `services.admin`) │ │
└─────────────────────┴────────────────────────────────────┴──────────────────┘
- include: Changed files must fall inside these globs. Files outside are violations.
- exclude: Changed files matching these are ignored for scope purposes (e.g., docs).
- forbid: Changed files matching these always fail, regardless of include rules.
- max_files_changed: Maximum number of files allowed in the diff.
- max_added_lines: Maximum total added lines.
- max_deleted_lines: Maximum total deleted lines.
Inspect only added lines in matching changed files. This avoids blocking on legacy bad patterns already present in the file — only new violations are caught.
content:
forbid_patterns:
- name: no-debug-print
files: ["services/**/*.py"]
regex: "\\bprint\\s*\\("Detect newly introduced forbidden imports by comparing the base and head versions of changed Python files.
If a file already had import services.admin before this diff, it won't fail. Only new introductions are flagged. This is critical for incremental adoption — you can fence new drift without blocking on legacy code.
imports:
python:
- name: payments-cannot-import-admin
files: ["services/payments/**/*.py"]
forbid_modules:
- services.admin
- infraA forbidden module matches exact names and prefixes:
services.adminmatchesservices.adminandservices.admin.fooinframatchesinfraandinfra.deploy
Note on TYPE_CHECKING imports: Imports guarded by if TYPE_CHECKING: are currently treated the same as runtime imports. This is intentional — they still represent architectural drift even if they don't execute at runtime. If this becomes a friction point, it may become a policy option in a future release.
| Command | Description |
|---|---|
scopefence init |
Scaffold a starter fence policy |
scopefence lint <policy> |
Validate a policy file |
scopefence check <policy> |
Check a diff against a policy |
scopefence render <verdict.json> |
Re-render a saved verdict |
scopefence check policy.yaml \
--base-ref main \
--head-ref HEAD \
--github # emit GitHub step summary + annotations
--json # output verdict as JSONWhen no refs are provided, ScopeFence compares HEAD against the working tree. In GitHub Actions, it auto-detects GITHUB_BASE_REF and GITHUB_SHA.
Each scopefence check saves a run to .scopefence/runs/<timestamp>-<policy-id>/:
policy.snapshot.yaml— the policy as evaluateddiff.json— the normalized diff snapshotverdict.json— the machine-readable verdictsummary.md— the markdown summary
Copy examples/workflows/scopefence.yml to .github/workflows/scopefence.yml in your repo and update the policy path and install URL.
- name: ScopeFence
run: |
pip install git+https://github.com/JohnODowdAI/scopefence.git@v0.1.0-beta.1
scopefence check fences/payments-agent.yaml \
--base-ref ${{ github.event.pull_request.base.sha }} \
--head-ref ${{ github.sha }} \
--githubThe --github flag writes a job summary and emits file-level annotations for violations.
- Hosted service or dashboard
- GitHub App or check-run API integration
- LLM-based policy interpretation or semantic review
- Non-git SCM support
- Multi-language import analysis (only Python in v0.1)
- Automatic code remediation
- Plugin system
- JS/TS import fences
- CODEOWNERS-aware policies
- Richer PR comment integrations
- Severity levels per rule
- Policy packs for common repo layouts
- Agent wrapper integrations
git clone <repo>
cd scopefence
pip install -e ".[dev]"
pytest -vMIT