Skip to main content

gauntlet

ci PyPI python license

repo dhanizael/gauntlet · PyPI package gauntlet-guard · command gauntlet

The integrity layer for evaluating stateful AI agents: sealed holdouts, persistence-leak guards, and keep/revert verdicts.

YOUR AI DIDN'T GET SMARTER. IT GOT THE ANSWERS.

An agent can look dramatically better after it has seen the test. Gauntlet shows whether that score survives a fresh one — and finds the leak that made it fake.

Is this your problem?

Gauntlet is for you if both of these are true:

  • your agent carries state across sessions: a memory file (LESSONS.md, MEMORY.md, now.md), saved trajectories, a vector store, or result directories a later session can read;
  • you evaluate it more than once on the same tasks.

Then memory quietly becomes the open book for the next exam, and your eval measures memory and capability as one number. It takes one good feature ("learn from feedback") and one reused task set — no malice required:

  • MEMORY.md / LESSONS.md / now.md — "what I learned this run" writes, verbatim, the task you just evaluated it on;
  • saved trajectories & transcripts — the full prompt survives in a log directory the next session retrieves from;
  • result/eval output directories — fixtures copied into run artifacts stay agent-readable;
  • vector stores / retrieval memory — the paraphrase you thought was safe is 6 shingles away from a near hit;
  • generated helper artifacts — the "summary" file that quotes the task to explain it.

Stateless agent, one-shot evals? You don't need this. Gauntlet solves a problem only repeated evaluation of a stateful agent can have — and says so on the box.

Your machine, in 30 seconds

Zero setup, $0, no LLM. Find the memory surfaces your machine would leak through — names, sizes, dates only; the audit never reads file content. Sample below is real tool output (paths abbreviated to ~/ for width):

uvx --from gauntlet-guard gauntlet guard audit
gauntlet guard audit — agent memory surfaces (content-free: names, sizes, dates only)
roots: ~/dev
visited 11 entries in 0.0s

memory files (3)
  ~/dev/MEMORY.md                                          37 B  2026-09-18
  ~/dev/Projects/price-bot/.agent-state/now.md             43 B  2026-09-18
  ~/dev/Projects/price-bot/LESSONS.md                      61 B  2026-09-18

agent state dirs (1)
  ~/dev/Projects/price-bot/.agent-state                     -  2026-09-18

transcript/log dirs (1) ... vector stores (1) ...

next steps:
  seal your holdout:   gauntlet manifest add mf.jsonl --instance t-1 --seed s-1 prompt.txt
  scan them for leaks: gauntlet guard scan mf.jsonl --store <path from above>

Every line above is a place last month's eval could still be living.

The failure mode, in 8 seconds

uvx --from gauntlet-guard gauntlet demo

Eight seconds. $0. No LLM. Deterministic. The demo makes an agent look better on the same test, catches the leaked memory, and asks the claim to survive a fresh holdout.

A three-act Gauntlet proof: the same-test score reaches KEEP net 1.0; Gauntlet catches an exact leak in LESSONS.md; a fresh test returns provisional net 0.0.

Run the proof · Read the real self-audit · Inspect the protocol · Use the share card

Why not just keep the test set secret?

Because the test set is not the only copy of the test. A secret holdout protects against you leaking it — it does nothing about the agent leaking it into itself. "Learning from feedback" is a good feature, and it writes last week's task into next week's open book.

Gauntlet doesn't replace your eval harness; it wraps it: sealed runs, a content-free memory scan, retirement of any holdout that leaked, and a retest on a fresh instance — ending in a verdict, not a score.

The words, in plain language

word plain meaning
holdout / instance a task kept out of the agent's reach, registered from a seed
manifest the private key: fingerprints of your holdout tasks (never published)
slot one sealed, single-use trial workspace
blindpack the judge-facing pack — arms renamed to pseudonyms
shingle an 8-word sliding fingerprint used to spot textual overlap
verdict keep / revert / provisional — and it is the exit code

Quickstart

pipx install gauntlet-guard        # or: uv tool install gauntlet-guard
uvx --from gauntlet-guard gauntlet guard selftest   # prove the scanner before trusting it

# the loop, end to end:
gauntlet run init exp --tasks tasks.json --arms harness,raw --repeats 3
gauntlet run prep   exp --slot t-xxxxxxxx --fixtures fx/
gauntlet run exec   exp --slot t-xxxxxxxx -- python3 my_agent.py
gauntlet run blindpack exp --out pack              # pseudonyms only; judges see no arms
gauntlet run status exp
gauntlet grade    exp --primary harness --baseline raw
# exit code IS the verdict: 0 keep · 1 revert · 2 provisional · 3 integrity failure

What grading actually looks like (real output from examples/mini):

verdict: KEEP  (careful vs quick)
  pairs=8 net=1.0 CI95=[1.0, 1.0] excluded=0.0%
  reason: 95% CI lower bound 1.000 > margin 0.1
# the memory audit: seal the tasks, then catch them if they leak
gauntlet manifest add ~/.private/eval/manifest.jsonl --instance task-0001 --seed s-77 prompt.txt
gauntlet guard scan   ~/.private/eval/manifest.jsonl --store ~/agent/LESSONS.md --store ~/agent/logs/
# findings cite hash references, never content — safe to paste into issues

# a leaked holdout is dead: retire it, then generate a provably-fresh one
gauntlet holdout retire family.json --instance task-0001 --manifest mf.jsonl --private-dir ~/.private/holdout/
gauntlet holdout new    family.json --manifest mf.jsonl --private-dir ~/.private/holdout/

A worked example you can copy

examples/mini runs the entire protocol on two toy tasks in ~15 seconds — bash walkthrough.sh — and ends by planting a leak in a LESSONS.md and catching it:

verdict: KEEP  (careful vs quick)
  pairs=8 net=1.0 CI95=[1.0, 1.0] excluded=0.0%
grade exit code: 0   (0 keep · 1 revert · 2 provisional · 3 integrity failure)
[EXACT] .../notes/LESSONS.md :: instance mini-A (13/13 shingles, 100.0% overlap, ...)
guard scan exit code: 1   (1 = leakage found -> retire, regenerate)

Swap agent.py for your real agent; the loop stays identical.

We scanned ourselves first

The first real target was the author's own agent infrastructure: 12 sealed task instances against memory files, journals, working-state dirs, and eval results — 271 files, 3.1 seconds, zero false positives on the control scan. Fixtures had persisted verbatim into run-result directories: real contamination vectors, correctly calibrated (NEAR/TRACE). Full write-up: docs/CASE_STUDY.md.

Design principles

  1. The scanner must not leak. Findings are file, position, counts, hash references — never matched text (ADR-0002). The audit goes further: it never opens a file at all (ADR-0011).
  2. Manifests are private keys. Task content lives only in the manifest (ADR-0007).
  3. Deterministic first, judge second. Shingle/hash evidence outranks LLM opinion (ADR-0006).
  4. Leaks are lifecycle events. Detection → retire → regenerate from an unused seed. A holdout that leaked once is dead.
  5. Verdicts over scores. provisional means the claim failed to be proven (ADR-0005).
  6. Decisions written before implementation. Verdict rules frozen in docs/GRADE_DESIGN.md before the code existed.

Status

module what it is state
guard + manifest (v0.1) persistence-leak scanner, content-free reports shipped
run (v0.2) sealed isolated trials: opaque slots, drift forensics, blind packs shipped
grade (v0.3) preregistered verdict engine, bootstrap CI, exit codes as verdicts shipped
demo (v0.3.2) the three-act failure mode, $0, self-asserting in CI shipped
guard audit (v0.3.3) zero-setup memory-surface discovery, content-free shipped
holdout (v0.4) family templates, provably-fresh generation, retirement ledger shipped

Built from a private protocol that runs on its own author: doctrine changes stay PROVISIONAL until they beat the previous version on repeated, holdout-guarded evals.

Written down before it shipped

Decisions precede code and both are public: docs/adr/ records every frozen call (12 ADRs), ROADMAP.md pins non-goals as firmly as targets, SECURITY.md treats "silent untrustworthiness" as the vulnerability class, CONTRIBUTING.md states the gates honestly, and the negative-result issue template makes publishing a failure as easy as publishing a win.

Development

uv sync
uv run pytest                          # 106 tests, incl. the demo's self-assertion
uv run ruff check && uv run ruff format --check
uv run ty check src/
uv run gauntlet demo                   # the story must hold, or CI goes red

License

MIT

Metadata

Release files for gauntlet-guard 0.4.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 gauntlet-guard 0.4.0
File Size Uploaded
gauntlet_guard-0.4.0.tar.gz 35.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gauntlet-guard 0.4.0
File Interpreter ABI Platform
gauntlet_guard-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 78.4 kB

Release files / gauntlet_guard-0.4.0.tar.gz

Download URL gauntlet_guard-0.4.0.tar.gz
Size 35.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f00bb1a395ab08cac904f99d394754b90ac1e210a19a08c3d865fe09f5e7f3c6
BLAKE2b-256 checksum
How to use checksums
b032cf6d2587e1ae7762c91d4383f416a865f3af0371e287a117b1b651b702dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / gauntlet_guard-0.4.0-py3-none-any.whl

Download URL gauntlet_guard-0.4.0-py3-none-any.whl
Size 42.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
771c301c863ac47f9376c26646099a10b81d558584ab7aff1053876ee9ca3e48
BLAKE2b-256 checksum
How to use checksums
31cd27a804ce7dd919dc0b80a6bee58baba421b71674cde8103e459310cf9dd2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

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