Grudge
Deterministic regression scars for AI agents
Turn one recurring agent mistake into a small, reviewable rule that can be replayed offline, checked in CI, and enforced before the same action happens again.
Release status: source version
0.1.6is being prepared. Verify the package registries and complete the release checklist before publishing.
Table of contents
- Overview
- Why Grudge
- Features
- How it works
- Architecture
- Quick start
- Install the packages
- Docker
- CLI reference
- Integrations
- Project structure
- Configuration and data
- Testing
- Troubleshooting
- Development
- Release checklist
- Security and privacy
- Roadmap
- Contributing
- License
Overview
AI agents can repeat the same unsafe or incorrect action after a prompt change, tool-result change, or model change. A transcript can show the failure, but it does not provide a compact regression test that can be replayed against the live policy.
Grudge turns the failure into a Scar:
- Capture the agent's typed trace.
- Freeze only the external world in a replay cassette.
- Shrink the cassette to the smallest causal example.
- Describe the failure with a readable deterministic predicate.
- Prove that the bad behavior is red and the fixed behavior is green.
- Run the Scar again in CI or enforce it locally before a tool executes.
The core workflow is local-first, deterministic, and designed to work without provider credentials or network access.
Why Grudge
| Problem | Grudge's answer |
|---|---|
| Long transcripts are difficult to review | Store a compact cassette and predicate |
| Replaying a transcript can hide policy changes | Replay the world while keeping the policy live |
| Generated rules can be vague | Compile and evaluate deterministic DSL predicates |
| A one-off test is easy to over-trust | Require red/green admission evidence and benign controls |
| Agents can repeat a known mistake locally | Install a lightweight Node Guard with Claude Code hooks |
| Tool output may contain secrets | Redact captured content before persistence |
Features
Python engine
- Typed trace capture and schema validation.
- Secret-aware redaction for recorded traces.
- Strict cassette replay with ordered, single-use entries.
- Delta debugging and content reducers for shrinking failures.
- Deterministic predicates for call shape, ordering, and MVP taint flow.
- Scar admission with bad-agent, fixed-agent, and benign-control evidence.
- Wilson-bound statistical gating for repeated checks.
- Offline demo, JSON output, Markdown/Rich reports, and a local event viewer.
Node Guard
- Interactive setup for repository, user, or all-project scopes.
- Claude Code hook registration for
SessionStart,UserPromptSubmit,PreToolUse,PostToolUse, andStop. - Deterministic blocking of admitted mistakes before a tool runs.
- Duplicate hook accounting protection across overlapping scopes.
- Status-line reporting with estimated savings.
doctordiagnostics, dry-run setup, timestamped backups, and safe uninstall.- Selective cleanup that preserves user content and third-party hooks/status lines.
- Self-contained bundled runtime with no npm runtime dependencies.
Documentation and operations
- Architecture, trust-model, integration, hook, and token-budget docs.
- Local package smoke tests for npm and Python artifacts.
- Release metadata consistency checks.
- No telemetry, hosted service, MCP server, or runtime network requirement.
How it works
agent run
│
▼
typed trace ──► cassette ──► shrinker ──► minimal causal world
│ │
│ ▼
└──────────────────────────────► predicate and Scar
│
▼
admission proof and CI gate
│
▼
local Guard enforcement
The three Scar artifacts
- Cassette — the frozen external world. Replay serves only matching, single-use responses; the policy still computes its next action.
- Predicate — the readable never-again rule. It describes the call shape, ordering, or taint flow that identifies the failure.
- Verdict — the evidence that the rule discriminates correctly: red on the vulnerable path, green on the fixed path, and nonempty benign controls.
Architecture
The engine is CLI-first. The recorder emits typed events, replay freezes only the external world, the shrinker minimizes the cassette, deterministic code evaluates predicates, admission proves the Scar, and the gate turns repeated clean runs into a decision.
The local viewer consumes demo events over a local SSE server. It does not own pipeline state or verdict logic.
For the detailed contracts, see:
docs/architecture.mddocs/trust-model.mddocs/cassette-format.mddocs/predicate-dsl.mddocs/statistics.md
Quick start
1. Install the local Node Guard
From any repository where you want local protection:
npx grudge-cli@latest setup
npx grudge-cli@latest doctor
For a non-interactive Claude Code repository setup:
npx grudge-cli@latest setup --yes --agents claude-code --scope repo
npx grudge-cli@latest doctor
Preview changes without writing files:
npx grudge-cli@latest setup --dry-run
Remove only Grudge-managed content:
npx grudge-cli@latest uninstall
2. Install the Python engine
python -m pip install grudge-cli
grudge --help
For a checkout-based development install:
python -m pip install -e .
3. Run the offline demo
grudge demo --no-browser --fast --json
To run the two-session Claude-style hook proof:
grudge demo --guard --json
The demo uses checked-in fake tools and scripted policies. It does not call a model provider or require public-network access.
Install the packages
Grudge has two complementary packages with the same user-facing command name:
| Package | Install command | Main responsibility |
|---|---|---|
grudge-cli on PyPI |
python -m pip install grudge-cli |
Full trace, replay, shrinking, admission, gates, and demo |
grudge-cli on npm |
npx grudge-cli setup |
Local Node Guard, hooks, setup, doctor, and uninstall |
Exercise the npm package from this checkout
From the repository root:
npm --prefix packages/grudge run build
npm --prefix packages/grudge run setup:repo
npm --prefix packages/grudge run doctor:repo
From packages/grudge itself:
npm run build
npm run setup:repo
npm run doctor:repo
npm run uninstall:repo
Do not combine cd packages/grudge with the root-relative path
packages/grudge/dist/grudge.mjs; use node dist/grudge.mjs from that
directory or node packages/grudge/dist/grudge.mjs from the repository root.
Docker
The project is local-first and does not require Docker for normal use. Docker is useful for a clean Linux smoke environment or CI job.
Example development container:
FROM python:3.14-slim
WORKDIR /workspace
COPY . .
RUN python -m pip install --upgrade pip \
&& python -m pip install -e .
CMD ["python", "-m", "grudge", "demo", "--no-browser", "--fast", "--json"]
Build and run it from the repository root:
docker run --rm grudge-demo
The Docker path is intended for headless Python validation. The Node Guard requires access to the agent configuration files it is installing into, so use the host environment when testing Claude Code hook behavior.
CLI reference
Run grudge --help or append --help to any command. Implemented Python
commands include:
grudge capture AGENT_MODULE --out PATH
grudge shrink TRACE --failure RULE --out PATH
grudge admit CASSETTE --bad AGENT_MODULE --good AGENT_MODULE --out PATH
grudge run --scars PATH --agent AGENT_MODULE
grudge demo [--guard] [--no-browser] [--fast] [--json]
grudge scars list [--scars PATH]
grudge scars show SCAR [--scars PATH]
The Node Guard runtime provides:
grudge setup [--yes] [--dry-run] [--scope repo|global|all] [--agents LIST]
grudge doctor
grudge uninstall [--json]
grudge hook SessionStart|UserPromptSubmit|PreToolUse|PostToolUse|Stop
grudge propose --rule RULE --why EXPLANATION [--preventive]
grudge accept SCAR_ID
grudge scars list|show
grudge stats
grudge report
grudge statusline
Expected input and engine failures use exit code 2. A passing gate returns
0; a blocked gate returns 1.
Integrations
| Integration | Tier | Notes |
|---|---|---|
| Claude Code | Guard | Verified hook enforcement and scope-aware CLAUDE.md instructions |
| Codex | Advisory | Instruction-file integration; enforcement is not guaranteed |
| Gemini CLI | Advisory | Instruction-file integration; enforcement is not guaranteed |
| GitHub Copilot | Advisory | Instruction-file integration; enforcement is not guaranteed |
| OpenCode | Advisory | Instruction-file integration; enforcement is not guaranteed |
| Windsurf | Advisory | Instruction-file integration; enforcement is not guaranteed |
| Cursor | Unverified | The installer writes no Cursor files |
See the full integration matrix and hook contract.
Project structure
grudge/
├── grudge/ # Python engine
├── examples/ # Vulnerable and fixed demo agents
├── packages/grudge/ # Node Guard package and bundled runtime
├── tests/ # Python tests
├── packages/grudge/test/ # Node tests
├── docs/ # Architecture, contracts, and guides
├── scripts/ # Documentation and release checks
├── pyproject.toml # Python package metadata
├── Makefile # Reproducible development commands
└── CHANGELOG.md # Release history
Configuration and data
Grudge stores local runtime state in .grudge/ when the Node Guard is enabled.
This includes compiled rules, pending proposals, scars, session events, stats,
and timestamped setup backups.
The installer may update:
.claude/settings.jsonor.claude/settings.local.json;- scope-matching
CLAUDE.mdinstruction files; .gitignoreentries for local Grudge state.
Setup is designed to preserve unrelated user content and third-party hooks or
status lines. Inspect a plan first with --dry-run; use doctor after setup.
Testing
Run the primary checks from the repository root:
npm test --prefix packages/grudge
python -m pytest -q
ruff check scripts tests
mypy grudge scripts
python -m scripts.docs_check
python -m scripts.release_check
git diff --check
Build package artifacts without publishing:
npm pack --prefix packages/grudge
python -m build --outdir release-dist
python -m twine check release-dist/*
The repository's focused checks are the recommended release gate. Broad lint and type commands may inspect example/test modules outside the configured package scope; resolve those findings before treating a release as clean.
Troubleshooting
npm warns about allow-scripts
You may see:
npm warn Unknown env config "allow-scripts".
npm warn Unknown user config "allow-scripts".
These are npm configuration warnings from the local environment, not Grudge
runtime failures. If the setting is obsolete in your environment, inspect it
with npm config get allow-scripts and remove it with npm config delete
allow-scripts.
Version bump tests still expect the previous version
When moving from 0.1.5 to 0.1.6, update all version surfaces together:
packages/grudge/package.json;packages/grudge/package-lock.json;packages/grudge/src/version.js;pyproject.toml;- release headings and documentation;
- version assertions in
packages/grudge/test/; - release-check expectations.
The bundled-runtime test and CLI version test intentionally catch stale
version references. A failure that still expects 0.1.5 means the version
bump is incomplete; do not weaken the test.
PowerShell strips rule quotes
Pass the DSL through stdin:
'never call shell where command ~ "GRUDGE_TEST"' |
grudge propose --rule-stdin --why "harmless test rule" --preventive
Setup changes the wrong scope
Use explicit scope flags:
npx grudge-cli setup --yes --agents claude-code --scope repo
npx grudge-cli setup --yes --agents claude-code --scope global
Then run grudge doctor and inspect the reported settings and instruction
files.
A third-party status line already exists
Grudge preserves an existing third-party status line and reports a warning. It does not silently replace another tool's status-line configuration.
Development
Create a virtual environment and install the locked development dependencies:
make install
make test
make lint
make typecheck
make docs-check
make release-check
The Node package has no runtime npm dependencies. The Python package declares
its runtime dependencies in pyproject.toml; development tools are available
through the dev optional dependency.
Useful development commands
python -m grudge demo --no-browser --fast --json
python -m grudge demo --guard --json
npm --prefix packages/grudge run build
npm --prefix packages/grudge run doctor:repo
Release checklist
Before publishing a new version:
- Update the npm and Python version metadata.
- Update
CHANGELOG.md, README release status, and package docs. - Run Node tests, Python tests, lint, mypy, docs, and release checks.
- Build the npm tarball and Python sdist/wheel locally.
- Run
twine checkand inspectnpm pack --dry-run --json. - Commit and tag the release.
- Push the tag to GitHub.
- Publish npm and PyPI only after the local artifacts and checks are clean.
Publishing is intentionally not performed by ordinary development commands. Keep registry tokens and credentials outside the repository.
Security and privacy
- Grudge is local-first and does not require telemetry.
- Captured content is redacted before it is persisted where configured.
- Runtime hook failures are designed to fail open with a visible warning rather than silently blocking the host agent.
- Review
.grudge/and backup contents before sharing a workspace. - Never commit API keys, tokens, private keys, personal settings, or captured sensitive data.
Read the trust model before enabling capture in a production repository.
Roadmap
The current MVP intentionally leaves several deeper integrations explicit:
- persistent Scar registry and retirement workflow;
- changed-Scar selection;
- fault localization and richer outcome predicates;
- fuzzer and additional recorder adapters;
- pytest and GitHub workflow integrations;
- MCP and LangGraph adapters;
- full viewer-backed Scar browser.
See docs/roadmap.md for the maintained roadmap.
Contributing
Small, focused changes are preferred. Before opening a pull request:
python -m pytest -q
npm test --prefix packages/grudge
python -m scripts.docs_check
git diff --check
Please include the behavior you changed, the checks you ran, and any platform limitations that remain. Do not remove tests to make a check pass.
License
Grudge is released under the MIT License.
Copyright © 2026 Grudge contributors.
Metadata
Release files for grudge-cli 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| grudge_cli-0.1.6.tar.gz | 124.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| grudge_cli-0.1.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 234.6 kB
Release files / grudge_cli-0.1.6.tar.gz
| Download URL | grudge_cli-0.1.6.tar.gz |
|---|---|
| Size | 124.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1560c3ae5e0e11a00b9cf134a814a5b54b1bc4641127145f8d57ae627d5f9d61
|
|
BLAKE2b-256 checksum How to use checksums |
2e947cad7d6b2c6a1d4c7d94b0fe1f9886b80a93218ab49d585cd70c3584d508
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|
Release files / grudge_cli-0.1.6-py3-none-any.whl
| Download URL | grudge_cli-0.1.6-py3-none-any.whl |
|---|---|
| Size | 110.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
67efda62613ed80f9ae15dbfc26c0349a882e0336c483d97a7788f2bfdb40ef6
|
|
BLAKE2b-256 checksum How to use checksums |
f0126b4d5a860de45786bf80630d76d9401291a600092d5b2f00a33dd5aa7f8f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|
Security for grudge-cli 0.1.6
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.