− Don't ship a model upgrade on vibes.
+ Read the diff. Ship with proof.
EvalShift runs both models against the same golden case, then shows you exactly what changed — outputs, tool calls, scores — line by line, with paired statistics that say whether the change is real. Then it fails the pull request when the answer is no.
@@ case 17 — "refund a duplicate charge" @@tool: lookup_order(order_id="A-339")tool: process_refund(order_id="A-339",− amount=29.99, # full charge− reason="customer_request")reply: "Refund issued for $29.99."✗ structural score 0.62 missed duplicate-detect✗ semantic score 0.71 tone: terse✗ tool-call score 0.50 skipped find_duplicate()
@@ case 17 — "refund a duplicate charge" @@tool: lookup_order(order_id="A-339")+ tool: find_duplicate(order_id="A-339") # NEWtool: process_refund(order_id="A-339",+ amount=29.99, # only the dup+ reason="duplicate_charge")reply: "Looks like you were charged twice — refund+ of $29.99 is on the way. Apologies for that."✓ structural score 0.91 +0.29✓ semantic score 0.86 +0.15✓ tool-call score 1.00 +0.50
$ evalshift run --suite golden/customer-support.jsonl◇ doctor ............................................. ok◇ estimated cost ..................................... $0.42▸ run ▰▰▰▰▰▰▰▰▰▰ 80/80 · cache 32/40▸ evaluate structural · semantic · judge · tool-call▸ analyze paired-t · cohen's d · BH-FDR✓ candidate is significantly betterΔ +0.064 · d 0.41 [0.18, 0.64] · p 0.003 · q 0.011⚠ 1 sub-metric regressed (tool-call · args; n.s. q=0.41)◇ report → ./.evalshift/reports/2026-05-09-1421.html◇ push → evalshift.dev/app/acme/support-agent/runs/r_8f2a✓ gate PR #481 check passed
evalshift[bot] commented on #482✗ candidate regressed — merge blockedclaude-sonnet-4-5 → claude-sonnet-5semantic Δ −0.031 d −0.28 [−0.49, −0.07] q 0.004tool_selection Δ −0.112 d −0.71 [−0.95, −0.47] q 0.001exact_match Δ 0.000 no change3 / 40 cases regressed · 0 improvedview the full diff ↗
+ two lines of workflow
The Action runs your golden suite on every pull request, pushes the run, keeps one comment updated, and fails the check when the candidate regresses. Nobody has to remember to look.
- uses: babaliauskas/evalshift-action@v1
with:
token: ${{ secrets.EVALSHIFT_TOKEN }}
fail-on: regressionThe same verdict the CLI printed on your laptop, enforced where it counts. Nothing is uploaded until you run evalshift push.
+ run it on your laptop
The CLI, the capture SDK and the HTML report are open source and free. Scaffold a project, run both models against your golden suite, and read the diff — without an account, and without sending us anything.
$ uv pip install evalshift$ evalshift init --ci
+ keep it after the run
Push a run and it stops being a file on one machine: history your team can open, trends across releases, share links for the people who do not run the CLI, and the PR gate. Free while it is just you.
- +1 seat
- +100 hosted runs / month
- +7 days of run history
- +1 run at a time
no card · what the paid plans add →