AI writes the code. paranoid verifies it.
One command checks the diff for deleted or weakened tests, dependencies that do not exist, swallowed errors, and claims not backed by the change, then prints a scored report with an exit code you can gate CI on.
curl -fsSL https://raw.githubusercontent.com/alainrk/paranoid/main/scripts/install.sh | sh
$ paranoid verify --base main --claims summary.md paranoid v0.1.0 repo: /home/you/project base: 7db5cfbeaacf head: worktree score: 40/100 verdict: fail dependencies (1 finding) DEP001 critical requirements.txt:2 The new dependency requests-toolkit-pro does not exist in PyPI. test-integrity (2 findings) TI001 critical test_division.py A test file that existed at base is deleted at head. TI004 high test_addition.py:10 A skip was added to an existing test. exit status 2
What it looks for
Three failure modes that show up in agent-authored patches and are cheap to miss in review.
Gamed tests
A failing test is deleted, skipped, or rewritten into a tautology, so the suite passes without the code being fixed.
Hallucinated dependencies
A new import names a package that does not exist, was published days ago, or is one typo away from a popular name.
Unbacked claims
The summary states work (“all tests pass”, “refactored the retry logic”) that the diff and the test run do not back.
Seven categories, one score
paranoid verify
compares the repository before and after the agent worked. Every finding
maps to a documented rule with a fixed ID and severity, listed with
examples in the rule reference.
Test integrity
Parses the test diff (Go with the real Go parser) and flags deleted tests, weakened assertions, new skip markers, tautologies, hidden renames, new mocking, new retries, new sleeps, stretched timeouts, and edits to CI and test configuration.
Dependencies
Cross-checks new imports against manifests, then looks up new packages on npm, PyPI, crates.io, and the Go proxy: nonexistent, brand new, nearly unused, or typosquatting a popular name.
Error handling
Looks at every changed source file for errors made to disappear: a checked error newly discarded, an empty catch block, an except block that only passes, a no-op promise handler, and a return or raise turned into a log line.
Clean-room test run
Reruns the test suite in a container with the network disabled, and collects coverage where the tooling allows. Catches results that only held on the agent's machine, and coverage that quietly dropped.
Claims
Extracts concrete claims from the agent’s summary, a session file from Claude Code, Codex, Gemini CLI, Aider, or Cursor, or the commit messages, and checks each one against the diff, the clean-room result, and the diff's overall radius.
API surface
Diffs public symbols between base and head and flags a symbol removed while the claims say a fix or feature landed: green by deletion, one layer deeper than deleting the test.
Safety controls
Scans every changed file for known patterns: TLS verification disabled, an auth check removed, a tracked path hidden by .gitignore, a git hook changed, CI permissions widened, or paranoid's own baseline and hook config edited. Pattern-based, not a security scanner.
One score, clear thresholds
Findings subtract from 100 by severity. The verdict and the exit code follow the score, so the same command works on your laptop and as a CI gate. Output as text, Markdown, schema-versioned JSON that is byte-identical across reruns, or SARIF for GitHub code scanning.
Baseline in, fix-prompt out
Two commands for adopting paranoid in an existing repository and feeding findings back to the agent.
paranoid baseline
Point paranoid at a repo that has never run it before and it reports everything, not just today's change. Run paranoid baseline once to snapshot existing findings into .paranoid-baseline.json; only new findings fail CI from then on. See Baseline & suppressions →
paranoid fix-prompt
fix-prompt renders the findings of a verify run into a deterministic instruction block you can paste back to the agent. See fix-prompt →
Exit codes you can gate a merge on
| exit | verdict | meaning |
|---|---|---|
| 0 | pass | score 80 or higher |
| 1 | warn | score 50 to 79 (exit 2 with --strict) |
| 2 | fail | score below 50, or below --fail-under |
| 3 | error | the tool itself failed, never a finding |
- uses: alainrk/paranoid/action@main with: base: ${{ github.base_ref }} fail-under: 50
How it's built
Local and private
No telemetry, no analytics, no update checks. The only network use is the optional registry lookups, and --offline turns those off too.
Deterministic by default
No LLM as judge. The default pipeline is static analysis, so the same input produces the same report, byte for byte in JSON. You can diff reports and trust reruns.
Fails soft
A check that cannot run (no container runtime, no network, no recognizable test suite) is reported as skipped, and the run continues.
Single static binary
No CGO, no runtime dependencies beyond git, and docker or podman only if you want the clean room. Linux, macOS, and Windows.