Check whether an agent's diff honored the task contract.
AI agents are fast enough to "helpfully" edit files you never asked them to touch. diff-contract compares the boundary you expected with the git diff you actually got, then fails when the agent crosses it.
From a checkout:
npm install
npm run build
node dist/cli.js --helpOnce published:
npx diff-contract --helpdiff-contract \
--expect tiny \
--allow "src/settings/**" \
--forbid "src/auth/**" \
--require-tests \
--fail-on-violationExample output:
diff-contract
Task: settings copy tweak
Contract: failed
Expected: tiny
Actual: large
Violation: yes
Changed: 3 files, +32 -14 across 2 directories
Violations:
- medium radius_exceeded: expected tiny, actual large
- high forbidden_path_changed: forbidden path changed (src/auth/session.ts)
Risk flags:
- high auth_security_changed: auth/security-sensitive path changed (src/auth/session.ts)
Review focus:
- src/auth/session.ts
Next checks:
- git diff -- src/auth/session.ts
This is not a semantic code reviewer. It does not decide whether the code is correct.
It answers a narrower question before review:
Did the agent stay inside the task contract?
Use it after an agent edits code and before you review, commit, or hand off the diff.
diff-contract [options]Options:
--expect <tiny|small|medium|large> Expected blast radius. Default: small
--task <text> Task description to include in output
--allow <glob> Allow changed paths matching this glob. Repeatable
--forbid <glob> Fail when changed paths match this glob. Repeatable
--require-tests Fail when source changes have no test changes
--contract <path> JSON contract file. Default: .diff-contract.json
--json Print JSON only
--write <path> Write markdown report
--fail-on-violation Exit 2 when the contract fails
--config <path> Alias for --contract
--fail-on-surprise Alias for --fail-on-violation
--help Show help
--version Show version
Create .diff-contract.json in your repo:
{
"expect": "small",
"allow": ["src/settings/**", "tests/settings/**"],
"forbid": ["src/auth/**", "package-lock.json"],
"requireTests": true,
"ignore": ["dist/**"]
}Then run:
diff-contract --fail-on-violationCLI flags are additive and can override expect.
diff-contract --expect small --json{
"task": null,
"expected": "small",
"actual": "medium",
"surprise": true,
"contractPassed": false,
"violations": [
{
"severity": "medium",
"code": "radius_exceeded",
"path": null,
"message": "expected small, actual medium"
}
],
"summary": {
"files": 1,
"additions": 2,
"deletions": 0,
"directories": 1
},
"riskFlags": [],
"reviewFocus": ["src/index.ts"],
"nextChecks": ["git diff -- src/index.ts"]
}The largest matching rule wins.
| Radius | Criteria |
|---|---|
tiny |
0-2 tracked files, <=50 changed lines, <=1 directory, no medium/high risk |
small |
<=5 tracked files, <=200 changed lines, <=2 directories, no high risk |
medium |
<=15 tracked files, <=800 changed lines, <=5 directories, or any medium risk |
large |
>15 tracked files, >800 changed lines, >5 directories, or any high risk |
High risk:
- dependency lockfile changed
- auth/security path changed
- migration/schema path changed
- CI/deploy/release path changed
- secret/env path changed
Medium risk:
- package manifest changed
- generated file changed
- public API/export surface changed
- source changed without tests
- tests/snapshots changed without source
- very large deletion block
Low risk:
- many directories changed
- untracked files present
Use --fail-on-violation to make the command enforceable:
diff-contract --contract .diff-contract.json --fail-on-violationExit codes:
0: command ran successfully1: unexpected internal error2: contract violation detected with--fail-on-violation3: invalid usage or not inside a git repo
diff-contract is intentionally narrower than blast-radius analyzers and AI code reviewers.
- It is not a dependency graph tool.
- It is not an LLM reviewer.
- It is not trying to infer intent from code.
- It checks deterministic boundaries: expected size, allowed paths, forbidden paths, and test requirements.
npm install
npm run build
npm test
npm pack --dry-run- no git mutation
- no automatic revert
- no automatic test execution
- no LLM calls
- no network dependency
- no claim that the code is correct
MIT