Skip to main content

Verify an AI agent actually did what it claimed — a CI gate that checks claimed actions against ground truth.

Project description

veridict

CI PyPI Python License: MIT

Verify an AI agent actually did what it claimed. A CI gate for autonomous agents: it checks an agent's claimed actions against ground truth — git history, files, real exit codes, HTTP endpoints, open ports — not the agent's self-report.

Deterministic. No LLM. Stdlib only. Each step gets ACCEPT / REJECT / ESCALATE, and the gap (ESCALATE) is honest abstention — it never bluffs.

The problem

Autonomous coding/devops agents over-report. They'll happily tell you "committed the fix, tests pass, pushed to prod" when the commit never landed, the tests actually failed, and nothing was pushed. Narration is the agent's word for it. Confirmation is checking reality. veridict does the second.

$ veridict demo
  [OK] agent branch: "on the right branch" -> ACCEPT  (branch 'master' exists)
  [OK] agent file: "wrote app.py" -> ACCEPT  (app.py exists)
  [OK] agent commit: "committed 'add app'" -> ACCEPT  (commit 'add app' found)
  [!!] agent commit: "committed 'add tests'" -> REJECT  (commit 'add tests' NOT in git log)
  [!!] agent file: "wrote tests.py" -> REJECT  (tests.py MISSING)
  [OK] agent tests: "tests pass" -> ACCEPT  (`python -c "exit(0)"` -> exit 0)
  [!!] agent tests: "tests pass" -> REJECT  (`python -c "exit(1)"` -> exit 1)
  [??] agent push: "pushed to origin" -> ESCALATE  (no upstream configured -> cannot verify push)
  => REJECT: 3 false claims caught, 1 unverifiable escalated

The agent claimed 8 successful steps. Three were lies. veridict caught them.

Install

pip install veridict        # from PyPI
# or, from a clone:  pip install -e .

Zero-config setup for Claude Code

veridict install

Wires the PostToolUse hook + the MCP server + a /veridict skill into the project — then verifies its own install with veridict (it proves the hook actually fires on a ghost write). After that you never touch hook config; you just talk: "did that actually write?", "you said tests pass — check", "from now on verify my writes." The /veridict skill drives the rest.

Did Claude follow your CLAUDE.md?

veridict claude-md           # auto-finds CLAUDE.md; maps the checkable rules and runs them

It reads your CLAUDE.md, auto-maps the checkable rules (no hardcoded keys, no Anthropic API in code, commits credit Claude, clean tree…) into veridict checks and runs them — and lists the rules it can't gate (style/intent) rather than faking a verdict. The mapper is conservative: when a rule isn't confidently checkable, it abstains. For hand-written/custom rules, see examples/claude_md_compliance.py.

Use it

As a CI gate (exit code 0 = all confirmed, 1 = something didn't check out):

veridict verify chain.jsonl --repo .   # your agent emits chain.jsonl; this gates the pipeline

As a GitHub Action:

- uses: NodexisLabs/veridict@v0.1.0
  with:
    chain: chain.jsonl   # your agent emits this during the run
    repo: .

As a Claude Code hook (catch Write/Edit claims that didn't actually land — the agent reports success, veridict checks the file on disk and feeds back a mismatch):

// .claude/settings.json
{ "hooks": { "PostToolUse": [
  { "matcher": "Write|Edit|MultiEdit|NotebookEdit",
    "hooks": [ { "type": "command", "command": "veridict hook" } ] } ] } }

Exit 0 = verified (quiet); exit 2 = ground-truth mismatch, surfaced to the model. It's a detector, never breaks your session. (python -m veridict.hook also works.)

In code, accumulate claims then confirm:

from veridict import Recorder
rec = Recorder(actor="git-driver", repo=".")
rec.claim("commit", "committed the fix", message="fix login")
rec.claim("tests",  "tests pass",        cmd="pytest -q")
rec.claim("push",   "pushed to origin")
results, verdict = rec.confirm()          # verdict in {ACCEPT, REJECT, ESCALATE}

Or pass a chain directly:

from veridict import confirm_chain
confirm_chain([
    {"action": "http", "claim": "deploy is live", "url": "https://example.com/health", "status": 200},
    {"action": "file", "claim": "wrote config", "path": "config.yaml", "contains": "version: 2"},
], repo=".")

Built-in checkers

action verifies against ground truth
commit sha resolves to a real commit (git rev-parse), or message equals a commit subject exactly (case-insensitive); pass loose: true for substring match
branch / tag a named branch / tag exists
push nothing unpushed vs the upstream
clean working tree has no uncommitted changes
tests / cmd re-runs cmd, checks exit 0 (doesn't trust the reported result)
file a path exists (optionally contains a string)
http a URL returns the expected status
port a host:port is open (service up)
pr a GitHub PR is in state (via gh)

Add your own — a checker is (step, repo) -> (ok, evidence):

from veridict import register
def deployed(step, repo):
    ok = my_cloud.revision() == step["sha"]
    return ok, f"live revision {'matches' if ok else 'does NOT match'} {step['sha']}"
register("deployed", deployed)

Scope (and why the boundary is the point)

veridict verifies concrete, checkable claims — did the commit land, did tests pass, is the endpoint up. It deliberately does not judge semantic correctness ("did it fix the bug well") — that's a different, AI-complete problem. Staying inside the checkable boundary is what makes it cheap, deterministic, and trustworthy as a gate: it can say "I'm not sure" (ESCALATE) instead of guessing.

How it relates to eval frameworks (promptfoo, DeepEval, Bedrock evals)

Not a competitor — a different question, at a different time.

Eval frameworks score whether your agent's output is good — relevance, faithfulness, tool-call correctness — usually offline on a test set, and usually by asking another LLM to judge (with you supplying the expected answer). veridict verifies whether the agent's claimed actions actually happened — at runtime, against reality, with no model in the loop and nothing for you to pre-supply.

eval frameworks veridict
question is the output good? did it actually happen?
oracle an LLM judge + your reference answer the world — git, files, exit codes, HTTP
when dev-time, on a test set runtime, on the real run
output a score ACCEPT / REJECT / ESCALATE (a gate)

Concretely: to make an eval catch "I committed the fix" when nothing was committed, you have to tell it the commit didn't land (as context) and trust a judge to notice the contradiction. veridict just runs git log. If you already know the truth well enough to supply it, you've done the hard part — veridict's job is to go get the truth.

Use both: evals to tune what your agent says, veridict to gate what it does.

Honest limits

  • It checks what you give it a checker for. An action with no checker (or an unknown action) → ESCALATE, not a silent pass. veridict never invents a verdict it can't ground.
  • It checks the steps you emit, not free-form prose. If your agent narrates "I saved the file" but never emits a step for it, there's nothing to anchor a check to. The fix is to emit a step for every action you want gated — e.g. a file step for the path it claims to have written — and veridict will catch the missing file. (A live stress test confirmed this boundary: a model pressured to claim a save it never performed slips past unless you declare the expected artifact.)
  • It verifies occurrence, not quality. A commit landing and tests exiting 0 doesn't mean the fix is correct — only that the claimed thing happened. (See Scope.)
  • Checkers run real side-effect-free reads (git log, a file stat, an HTTP GET) — except tests/cmd, which re-run your command. Only point those at commands that are safe to re-run in CI.
  • commit matching is exact by default (subject equality / real sha) so a generic claim can't match an unrelated commit; opt into loose substring only when you mean it.
  • ESCALATE is a real outcome, not a soft accept. Decide in your pipeline whether ESCALATE blocks or warns — it exits non-zero by default.

The complete, categorized list — coverage gaps, technical caveats, and the security note that tests/cmd run arbitrary commands — is in LIMITATIONS.md. Shipping it openly is the point: trust a verifier that tells you where it stops.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

veridict-0.3.0.tar.gz (44.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

veridict-0.3.0-py3-none-any.whl (39.8 kB view details)

Uploaded Python 3

File details

Details for the file veridict-0.3.0.tar.gz.

File metadata

  • Download URL: veridict-0.3.0.tar.gz
  • Upload date:
  • Size: 44.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for veridict-0.3.0.tar.gz
Algorithm Hash digest
SHA256 ceff7510c0e58e45798feee57f5b70349cd30a248df91baf88c040fc70d5e05f
MD5 a04489195a546d2a24e045dd6f476893
BLAKE2b-256 210f83288565872aa11c48fb06ee548cc1e86fb47477d85078e74d1c195727af

See more details on using hashes here.

File details

Details for the file veridict-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: veridict-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 39.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for veridict-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 da333a79554b255fc0da9466f347109873411c41743b9c337f26fb3dee973db2
MD5 b5d56971c602c5e189f7165d0ee1a546
BLAKE2b-256 5dc1e6f9d5c76ca55fc82eaf50a13831c58a7d9f3c5edf13013560f33aa7ef09

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page