Statistical pass/fail gating for stochastic tests: run N times, pass only when the Wilson lower confidence bound clears the bar. Built for agent evals; stdlib only.
Project description
lcb-gate
Statistical pass/fail gating for stochastic tests. Run the trial N times; pass only when the Wilson lower confidence bound of the pass rate clears your threshold. Built for agent evals — anything where a single green run proves almost nothing. Stdlib only, no dependencies.
pip install lcb-gate
The demo ships in the repo (not the wheel):
git clone https://github.com/CiphemonJY/lcb-gate && cd lcb-gate
python examples/demo.py # why a single green run lies, in three acts
The problem
Agent behaviors, LLM evals, and flaky integration tests are stochastic. CI treats them as deterministic: one green run → merged. Two failure modes follow:
- The lucky pass. A 75%-reliable agent task passes a single CI run 75% of the time. You ship a coin flip and call it tested.
- The winner's curse. You try 20 variants, one "beats the baseline" on a single eval run, and you promote it. Most single-run wins are noise: in one production self-improvement pipeline, fresh-seed re-verification killed 85% of apparent improvements found by a first-pass scan.
Averages don't fix this — a point estimate without n is a vibe. What fixes it is a lower confidence bound: "with 95% confidence the true pass rate is at least X". Gate on X.
Usage
As a library
from lcb_gate import run_gate
result = run_gate(lambda i: run_my_agent(seed=i).succeeded, n=25, threshold=0.9)
print(result)
# PASS: 25/25 passed (gate n=25); LCB 0.902 >= 0.9 @ 95% confidence
assert result.passed
run_gate stops early — in either direction — the moment the verdict is
mathematically settled, so hopeless runs don't burn the full budget. The
verdict is identical to running all n trials.
As a pytest fixture
def test_agent_completes_checkout(lcb):
lcb.check(lambda i: run_agent_eval(seed=i).ok, n=25, threshold=0.9)
The plugin registers automatically on install. Failures explain themselves:
Failed: lcb-gate FAIL: 21/30 passed (gate n=30); LCB 0.551 < 0.9 @ 95% confidence
Candidate vs champion (promotion gates)
"Is the new prompt/model/policy actually better?" is the same statistics with a different threshold — the win-rate LCB must exceed 0.5:
from lcb_gate import compare
result = compare(candidate=eval_new, champion=eval_old, n=400)
print(result)
# BETTER: candidate won 243.0/400 paired trials (60.8%); win-rate LCB 0.567 > 0.5 @ 95%
Both callables receive the same seed per trial (common random numbers):
pairing removes the noise both variants share, which is where most of the
statistical power comes from. Ties count as half a win. The honest default
verdict is NOT PROVEN — an underpowered comparison never certifies.
def test_new_prompt_beats_production(lcb):
lcb.check_better(eval_new, eval_old, n=400)
Sizing your gate
A perfect record at small n still can't clear a high bar — by design. Minimum trials for a flawless run to pass, at 95% confidence:
| threshold | min n (all passing) |
|---|---|
| 0.80 | 11 |
| 0.90 | 25 |
| 0.95 | 52 |
| 0.99 | 268 |
min_trials(threshold) computes this. If your eval budget can't afford the
n, lower the threshold honestly rather than pretending n=5 certifies 99%.
CI for agents — the full stack
This gate answers "does the behavior hold up statistically across runs?" Its sibling project grounding-gate answers "was each claim structurally grounded within a run?" Together:
- Per-trace, structural: grounding-gate rejects terminals whose claims were never observed (zero tokens, zero LLM calls).
- Across-runs, statistical: lcb-gate runs the eval N times and certifies the pass rate's lower bound.
- On promotion:
compare()with paired seeds gates champion swaps.
A GitHub Actions job needs nothing special — the gate lives inside the tests:
- run: pip install lcb-gate
- run: pytest tests/agent_evals -q # each test is an N-run certified gate
Roadmap
- Sequential probability ratio test (SPRT) mode — even fewer trials at the same error rates.
@pytest.mark.lcb(n=..., threshold=...)marker API.- JSON report artifact for tracking LCBs over time.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lcb_gate-0.1.0.tar.gz.
File metadata
- Download URL: lcb_gate-0.1.0.tar.gz
- Upload date:
- Size: 9.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccdbc23b418493732a106298968d3e44735fae33ed24eb548a1661d0ebec5778
|
|
| MD5 |
192145f3a2b902b7fc9cac56a8ce0698
|
|
| BLAKE2b-256 |
3571abeced0728e9f53624487d4e31d117b735e64baf0d5871a65ea61f3e3edb
|
Provenance
The following attestation bundles were made for lcb_gate-0.1.0.tar.gz:
Publisher:
release.yml on CiphemonJY/lcb-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lcb_gate-0.1.0.tar.gz -
Subject digest:
ccdbc23b418493732a106298968d3e44735fae33ed24eb548a1661d0ebec5778 - Sigstore transparency entry: 2138974552
- Sigstore integration time:
-
Permalink:
CiphemonJY/lcb-gate@d4a5892a901d5379c54db492d92b6db17d0002ff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/CiphemonJY
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d4a5892a901d5379c54db492d92b6db17d0002ff -
Trigger Event:
push
-
Statement type:
File details
Details for the file lcb_gate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lcb_gate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16a521dfe64976433d1cfc0f189dc68510907db8e27b5e05ad10dd9d6f324e82
|
|
| MD5 |
85fa313dbc4c67fdc8b2dce4458ae962
|
|
| BLAKE2b-256 |
636e206aa63e0ed5e29b5a7c9a91612db02e6cd4d446c5772555db1e84effaa5
|
Provenance
The following attestation bundles were made for lcb_gate-0.1.0-py3-none-any.whl:
Publisher:
release.yml on CiphemonJY/lcb-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lcb_gate-0.1.0-py3-none-any.whl -
Subject digest:
16a521dfe64976433d1cfc0f189dc68510907db8e27b5e05ad10dd9d6f324e82 - Sigstore transparency entry: 2138974602
- Sigstore integration time:
-
Permalink:
CiphemonJY/lcb-gate@d4a5892a901d5379c54db492d92b6db17d0002ff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/CiphemonJY
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d4a5892a901d5379c54db492d92b6db17d0002ff -
Trigger Event:
push
-
Statement type: