rmasood1122/eval-conductor
Prove your tests/evals didn't regress. Fail-closed release gate for scores you already produce (pytest, promptfoo, JUnit, any JSON) with signed, tamper-evident, pre-registered receipts. Gate via MCP tools; design gates via skills.
Changelog
Unreleased
Every command now runs under a Python that has the packages it needs. On a
Windows machine where python3 and python were different installs and only
python had pytest, /eval-prove stopped with CANNOT PROVE: pytest is not installed and /eval-init set the test-count floor to 1 because collection
failed. core/py.sh now prefers a Python 3.10+ with pytest and PyYAML, then one
with pytest, then any Python 3.10+ as before.
Output is UTF-8: em dashes printed by the tools reached Claude Code on Windows as U+FFFD.
core/py.sh: three-pass interpreter choice (importlib.util.find_spec, no imports);PYTHONIOENCODING=utf-8unless already set.tests/test_py_sh.py: a bare-venvpython3next to apythonwith pytest;/eval-proveproves,/eval-initcounts the real suite, output has no U+FFFD.
1.4.3 — 2026-10-20
/conductor --verify-evidenceno longer reads or sends any credential. 1.4.2 readGITHUB_TOKENfrom the installer's environment and sent it to the GitHub API; the directory policy check correctly held that as a credential forward. The check is now anonymous: public repos only, and a private repo, rate limit or offline run fails closed with a message that says so.core/gh_auth.pyis gone. Nothing else in the plugin touches the network.- PRIVACY.md now states the one opt-in network call instead of claiming none.
1.4.2 — 2026-10-20
Activation fixes: every command now runs on Windows, nothing strands a new user, and the PyPI package finally runs the same engine as the plugin.
- Windows: commands no longer die on the
python3Store stub. Every command line runs throughcore/py.sh, which probespython3,python, thenpy -3by actually running them (Python 3.10+) instead of trustingcommand -v, and prints one install message (exit 127) when none works./eval-init's NEXT STEPS name the interpreter that really ran.*.shis pinned to LF. /eval-sealwith no registry exits 2 and writes nothing (it wrote an unsigned seal ofregistry_sha256: nulland exited 0). It says to run/eval-initfirst./eval-provegitignores the signing key it creates.evals/.receipt-key,evals/.wedge-baseline.jsonand.eval-prove-junit.xmlare added to.gitignorebefore the first write, sogit add -Acan't stage the key./eval-initnow ignores.eval-prove-junit.xmlwhere the wedge actually writes it.- No more dead-end errors.
/eval-gateand/eval-explainwith no candidate lead with that error and name/eval-proveand/eval-init(or the example and/eval-importonce installed); the no-baseline warning only prints when a decision is made./eval-verifywith no receipts names/eval-prove./eval-prove --runworks in repos without a pytest suite (it was refused with "use --run"), and a missing pytest is named, with the pip command. - GitHub token read in a URL-free module.
core/gh_auth.pyreadsGITHUB_TOKEN;core/conductor.pybuilds the request and reads no environment, and sends the token only toapi.github.com. A test fails if any shipped.pyfile both reads the environment and contains a URL. - Removed the unused
userConfigfields (receipt_key,github_token): Claude Code exports userConfig only to hooks and MCP servers, and this plugin has neither.EVAL_RECEIPT_KEY, the auto-generatedevals/.receipt-keyandGITHUB_TOKENwork as before. - PyPI package synced with the plugin.
src/eval_conductor/_core/had drifted and missed the 1.4.1 fixes:eval-conductor proveon PyPI 1.4.1 printed a brightgreen "PROVEN 0/0" badge for a run with zero tests. A parity test now fails on any drift. - Descriptions lead with the user's problem (all commands, both skills, the manifest and the marketplace listing).
- CI runs on
windows-latestas well asubuntu-latest. A live harness (tests/test_commands_live.py) runs every command line the way Claude does, in fresh git repos. Tests: 254 → 365.
1.4.1 — 2026-10-05
Wedge hardening + an honest-limits correction, from deep adversarial testing. The core gate was unaffected (NaN/Inf/string/bool scores, missing metrics, seal p-hacking and receipt tampering all already BLOCK/caught); every fix below is in the zero-config wedge path or its docs.
- Deleting
evals/.wedge-baseline.jsonno longer resets the bar. The bar is reconstructed from the signed, hash-chained receipts (prior PROMOTEmanifest.scores) when the local file is missing, so it can't be deleted to launder a regression into a "first run". Pin a lower floor deliberately with--min-pass-rate. - Zero collected tests → CANNOT PROVE (exit 2), instead of a brightgreen "PROVEN 0/0" badge. The scorer's "0.0 not 1.0" contract is unchanged.
- Corrected a receipt overclaim. "Delete or reorder a receipt and the chain breaks" is true for tamper/reorder/middle-deletion; removing the latest receipt leaves a shorter valid chain, caught by distribution (push) — which the code already enforces via the ANCHOR report. Docs now say exactly that.
- Malformed candidate/baseline JSON → clean FAIL (exit 2), not a raw traceback.
- Tests: 254 passing (added baseline-deletion, zero-tests, and
--min-pass-ratecases).
1.4.0 — 2026-10-04
Zero-config wedge: /eval-prove — one command, a signed proof of your tests.
/eval-prove(core/wedge.py, vendored into your repo) turns an existing test suite into a tamper-evident, signed receipt plus a pasteable PR badge, with no registry and no config. Detects and runs pytest, or consumes a JUnit file (--junit) or any command that writes JUnit (--run).- Proof + auto-gate (capture-then-gate). The first run has nothing to regress
against, so it PROMOTEs and records the measured pass-rate and test-count as the
bar (
evals/.wedge-baseline.json). Later runs BLOCK if the pass-rate drops or tests disappear (the oldest way to turn a gate green). The bar ratchets up on a clean run and never down — a BLOCK never lowers it. A hardcoded "100% or BLOCK" was rejected: it would fail every flaky suite on run one. - Same proof engine as the full gate. Receipts come from
eval_receipt, so a wedge proof and a gate proof verify identically; a signing key is auto-generated (gitignored) on first run, so proofs are signed by default. - Fail-closed. No detectable suite and no
--junit/--run→ exit 2 (no proof of an unmeasured run), with a one-line pointer, never a traceback. Zero executed tests → pass-rate 0.0. - Honest badge. The badge always shows the test COUNT, and the output states
that a green badge proves "not worse than before", not "good", and that one run
doesn't calibrate flakiness — graduate to
/eval-initfor measured noise bands and real thresholds. The wedge is the on-ramp;/eval-initis the next room. - 12 new tests (
tests/test_wedge.py); vendored by/eval-init; wedge artifacts gitignored in the user repo. Dogfooded: proves eval-conductor's own 166 tests.
1.3.0 — 2026-10-04
Pre-registered evals: prove the bar was fixed before the run.
- Pre-registration seals (
core/prereg.py, vendored into your repo;/eval-seal).prereg.py sealhashesevals/registry.yaml(and each metric'snoise_bandfloor) into a signed, hash-chained seal underevals/seals/, containing no candidate data. At gate time the live registry is re-hashed against the seal; a bar that moved after sealing is flagged as drift. - Closes the hole the diff-lint structurally cannot.
registry_diff_lint.pyproves a registry didn't get weaker than its git base — but a brand-new gate has no base ("nothing to weaken"), so a gate authored to exactly clear a score you already ran passes every check. That is p-hacking your own gate. Pre-registration is the deliberate, anchored base that makes "the bar was fixed before the result" a checkable fact. - Composes with receipts. The gate records a
preregblock on each receipt (sealed,seal_id,registry_match,bands_match,signature_ok), so a signed PROMOTE now proves not just "passed under this registry" but "passed under the registry that was pre-committed in seal X" — and a drifted decision is permanently stamped as such, inside the tamper-evident receipt itself. - Opt-in enforcement, never breaks an un-sealed gate. Recording status on
the receipt is a best-effort side effect that never changes the gate exit code.
prereg.py check --require-sealis the fail-closed CI knob (exit 2 on drift or a missing seal); without the flag, drift is reported loudly and exits 0, so upgrading is non-breaking. - CI:
templates/eval-gate.ymlgains a commented pre-registration check step. Native eval caseprereg-reseal-refusesproves the assistant refuses to re-seal a weakened bar after seeing the scores (the exact laundering move). - Honest limits carried in the README,
/eval-sealand the seal output: a seal proves the bar is byte-identical to what was sealed and — via git anchoring — committed before the result; it does not prove the bar is strict enough (that's the diff-lint's and the reviewer's job), and a shared-secret key can't stop its own holder backdating thesealed_atstring — so ordering rests on pushed git history, not the timestamp.
1.2.0 — 2026-10-04
Eval receipts: a provable verdict, not just an exit code.
- Tamper-evident eval receipts (
core/eval_receipt.py, vendored into your repo;/eval-verify). Once a signing key exists (/eval-initnow generates one, gitignored), every/eval-gaterun appends an HMAC-signed, hash-chained receipt toevals/receipts/binding the PROMOTE/BLOCK decision to the exact registry, candidate and baseline bytes it was computed from. A deleted or reordered receipt breaks the chain. - Fused from titan-receipts. The canonicalization (
core/canonical.py) and fork-detecting chain engine (core/chain_state.py) are ported from the titan-gate plugin — stdlib-only, byte-compatible — rather than reinvented or taken as a runtime dependency. - Three verify-time tamper checks: chain integrity (one unbroken line from
GENESIS, every
receipt_hashrecomputed from content), signature, and an eval-specific decision-vs-verdicts consistency check — a re-signed receipt whose recorded decision disagrees with its own verdict table is caught.--structure-onlyverifies without the key; the closingANCHORline reports how many receipts are committed and pushed. - Fail-open emission. Receipting is a side effect that never changes the
gate's exit code or crashes CI: a broken chain or unwritable path warns
(
RECEIPT:) and skips. Enforcement lives in/eval-verify, not the gate. - CI:
templates/eval-gate.ymlgains a commentedEVAL_RECEIPT_KEYsecret and a release-blocking verify step. Native eval casereceipt-tamper-blocksproves the assistant treats a verify failure as blocking, never "re-sign it". - Honest limits carried in the README and verify output: HMAC is a shared secret — a receipt proves the decision is unaltered under a key you control, not who signed it, and not that the scores were honestly produced.
1.1.0 — 2026-10-03
Positioning: the gate for scores you already produce.
- Adapters (
core/adapters.py, vendored into your repo;/eval-import):junit(pytest/jest/go JUnit XML),plugin-eval(claude plugin evalaggregate-result.jsonor results dir — latest run;partial: truerefused),promptfoo,scores(flat JSON). All write a verifiedregistry_hash. - Zero-config first run.
/eval-initauto-detects a profile:pytest(gates pass rate, a test-count floor measured at install, failures — CI producer pre-filled, green on first push),plugin-eval(gates overall score, with-vs-without delta, weakest case, case floor), orllmstarter.--profileoverrides. - Native eval suite:
evals/now holdsclaude plugin evalcases for this plugin (threshold-edit refusal,/eval-inittriggering, NaN doctrine). Measured mean Δ +0.50 vs no plugin; threshold-edit case Δ +1.00. - Behaviour fix found by that suite: the
eval-gatesskill did not trigger on "gate is red, bump the threshold so CI passes" — Claude agreed to the edit with the plugin installed. Its description now names those phrasings; the skill fires and refuses 2/2. - Template fix: the optional conductor audit step is no longer
|| true; it is a commented, release-blocking step you opt into. - Review fixes:
test_countfloor uses pytest's selected count (deselected tests never reach JUnit) and returns unknown on collection errors; pytest profile requires a pytest config section or tests dir, and a 0-test repo falls back tollm; adapters refuse a missing registry instead of writing an unattested candidate; generated workflow installs requirements/-e . - Self-gate thresholds raised to the new measured counts (112 tests, 23
fail-closed tests).
run_self_eval.pyuses the shipped JUnit parser. - README rewritten around the problem, with a real BLOCK verdict; limits moved to a collapsed section (unchanged in substance).
1.0.1
- Diff-lint and baseline-warning fixes.
1.0.0
- Initial release: fail-closed gate, baseline runner, registry lints, 27-step conductor ledger, eval-gates and evidence-discipline skills.