Skip to main content

sober

tests PyPI python deps

How often did your coding agent actually verify what it claimed?

sober audit on the author's sessions

sober guard stopping a stale "tests pass" claim

sober reads the session logs that Claude Code and Codex already keep on your machine, finds every sentence where the agent said tests pass / check passed / fixed / built / rendered / pushed, and checks whether a matching command really ran after its last code edit — and what that command's output said.

$ pip install sober-cli && sober audit

Verification rate: 91.5%   (140 verified / 153 decided claims)
Sessions: 96   Claims about this turn's work: 176

  verified         140   a matching check ran after the last code edit and its output agrees
  unverified        13   the agent edited code, then claimed success without running a check
  contradicted       0   the check it ran says otherwise
  could not check   16   evidence is off-transcript (subagents, background, piped exit codes, remote runs)
  weak               7   a visual fix judged by looking at a rendered image; not counted either way

By agent / model:
  claude-code claude-opus-5-5     86%  (131 decided, 29 could not check)

Those are the author's own numbers, from 96 real sessions. A team that hand-labelled 357 "tests pass" claims from 150 Claude Code sessions found 80 with no run backing them; most were stale: tests passed, the agent edited again, then reported the old pass as current. Benchmarks say the same thing about ML tasks (57% of frontier-agent runs take a planted shortcut, and telling the agent not to barely helps). sober gives you your own number, then stops the pattern with two hooks.

  • Deterministic. Regex and transcript structure only. No LLM, no API key, nothing leaves your machine.
  • Precision first. Anything it cannot see is could not check, never unverified. The flagged set was tuned over six rounds of double-blind labelling until two independent raters agreed with every flag (details in gate/REPORT.md).
  • Reads the output, not just the exit code. pytest -q | tail -3 returns tail's exit code; sober reads the last N passed / N failed summary instead, and reports a contradicted claim when the summary disagrees with the sentence.

Install

pip install sober-cli      # Python ≥ 3.9, standard library only
sober audit                # Claude Code (~/.claude/projects) + Codex (~/.codex/sessions)
sober audit --since 30d    # last 30 days only (also 8w, 3m)
sober audit --agent claude-code --json findings.jsonl   # every claim with context; per-project breakdown is in the report
sober share                # writes ~/.sober/sober-card.svg (+ .png via rsvg-convert or a local Chrome)

Make it stop happening: sober guard

sober guard install --dry-run   # shows the hooks it would add to ~/.claude/settings.json
sober guard install
sober guard status | uninstall

Two Claude Code hooks, both tiny and deterministic:

Hook When What it does
Stop the agent is about to end its turn If the reply claims tests pass / fixed / pushed … and this turn shows no matching check after the last code edit, the turn is blocked once with the exact sentence and the missing step. The agent either runs the check and reports the real output, or rewrites the sentence as unverified.
PreToolUse (Bash) before a command runs A test or build command piped through tail/head without set -o pipefail loses its exit code. The call is denied with the one-line fix.

If your project already has a verify skill or script, the Stop hook recognises its run as evidence; it does not fight your existing workflow.

How a claim is judged

  1. Scope: one turn. Everything the agent did between your prompt and its reply.
  2. Last code edit. Edit/Write tools, or sed -i, cat > file, heredocs that write files. Edits to docs, logs, notes, memory, .env, temp dirs or .agent/ state do not count.
  3. Matching check after it. Tests for tests pass; lint/type/build/check tools for check passed; render tools for rendered; git push / kaggle … push for pushed; tests, checks or renders for fixed (a generic script run is not evidence that a fix works).
  4. Its output. The last summary line decides: 12 passed backs the claim, 2 failed contradicts it, fail 0 is a pass. Understands pytest, unittest, Jest/Vitest/Mocha, Go, Cargo, Maven/Gradle, .NET, tsc, ESLint, Ruff, Clippy, go vet and more. A narrowed run (pytest -k, one file) backs a claim only when the claim names the same count ("13 tests pass" ↔ 13 passed).
  5. Visual fixes. "Fixed the overlap" followed by a render and a look at the screenshot is weak: shown separately, counted neither as verified nor unverified.
  6. Everything invisible → could not check: subagents, background tasks, your own terminal, piped commands with no summary in the output, remote runs (CI, Kaggle), syntax-only checks.
  7. Not a claim: plans ("I'll run the tests"), questions, quotes, code blocks, sentences reporting a previous turn.

Forked sessions are de-duplicated so the same turn is counted once.

Precision

Every rule change was followed by a labelling round: the author and an independent Claude agent (fresh context, no access to the author's labels) each judged every flagged claim.

Round Flagged unverified Author precision Independent precision What was fixed next
1 38 56% 69% doc/log/memory edits counted as code edits; versioned hyperframes@x check missed
2 22 53% 52% variable paths, .agent/state.json, heredoc bodies; judge output by the last summary line
6 12 12/12 11/11 (+1 unsure) quote-aware multi-line splitting; writing heredocs never count as verification

Recall. Two raters (author + independent agent) then labelled 66 claims the audit had not flagged. They found 2 and 3 misses respectively, 5 distinct: an experiment runner mistaken for a test, a "background" sentence mistaken for a remote run, a commit claim with no commit after the last edit, a test claim backed only by a background job, and a progress list whose check predated the edit. Four of the five are fixed in 0.2.0. Before the fixes the recall estimate on that sample was roughly 55–75% depending on the rater; the remaining known miss is the progress-list case. sober still prefers could not check over a wrong flag, and the full labelling material is described in gate/REPORT.md.

What it is not

  • Not a judge of correctness. A verified claim means the check ran after the edit and its output agreed with the sentence; it does not mean the code is right.
  • Not a leaderboard of models. The per-model table is your sessions only.
  • Not Anthropic's or OpenAI's. Independent, MIT, unaffiliated.
  • Tested on Linux, macOS and Windows (CI), Python 3.9–3.13. Rules were tuned on one developer's sessions (Python, JS, video tooling, Kaggle); other stacks are covered by synthetic tests only, so expect to report a false flag or two. Issues welcome.

claude-guard-hooks and groundtruth block unbacked "done" claims with keyword matching; superpowers asks for verification in a skill (prompt-level). sober differs in reading command output, scoring your history across agents and models, and refusing to count what it cannot see.

中文说明见 README.zh-CN.md。

Metadata

Release files for sober-cli 0.2.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 sober-cli 0.2.0
File Size Uploaded Provenance
sober_cli-0.2.0.tar.gz 33.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sober-cli 0.2.0
File Interpreter ABI Platform
sober_cli-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 61.3 kB

Release files / sober_cli-0.2.0.tar.gz

Download URL sober_cli-0.2.0.tar.gz
Size 33.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2cb0b974509304fd14716615d402dd05a6ba4765a5a68532c9210af6625cc093
BLAKE2b-256 checksum
How to use checksums
278c8aed874431e68a29737114ce4daa7db04168af5055492bc8aedb0f300b11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Release files / sober_cli-0.2.0-py3-none-any.whl

Download URL sober_cli-0.2.0-py3-none-any.whl
Size 28.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4c8fa9e74506bb8f493f9ce109e2e343de4b35ce538e49f4c6a4fb8a8976078
BLAKE2b-256 checksum
How to use checksums
e544018b87547f846aafbd081c544ed0720c9f41a0d3a8de945de54c1bf18588
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Security for sober-cli 0.2.0

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.

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