Repository navigation
Bench v2.0.0: Project-Scoped Ledgers and Fail-Closed Governance
Bench v2.0.0 governs each project into its own ledger and fails closed.
Two changes break existing installs. Read the migration notes before upgrading.
Breaking: governance now fails closed
A pipeline that cannot render a verdict now denies the change instead of
letting it through. API timeout, malformed judge response, unimportable
pipeline, unreadable constitution: all deny, with a pipeline_error VETO
recorded in the ledger.
The practical consequence: if your claude CLI is too old to recognize the
configured model IDs, every governed edit is blocked until you upgrade it. That
is intended. A broken or exploited judge must not be able to wave changes past
governance. Run claude update if edits start failing.
Recovery from a genuinely broken pipeline is an out-of-band human action,
editing files directly outside the governed tools, never an automatic pass.
Breaking: the ledger follows the project being governed
Previously every governed edit on the machine appended to Bench's own ledger,
wherever Bench happened to be installed. Now:
| Condition | Ledger |
|---|---|
BENCH_LEDGER_PATH set |
that path |
| Working directory inside the Bench repo | ledger/bench-ledger.json |
| Anything else | <project>/.bench/bench-ledger.json |
If you registered Bench's hook globally, receipts for your other projects will
start landing in those projects rather than in Bench's ledger. Set
BENCH_LEDGER_PATH if you want the old single-ledger behaviour.
This existed because a globally registered hook wrote every project's full
diffs into one ledger. If that ledger is committed to a public repository, it
publishes them. ledger-meta.json is now written alongside whichever ledger is
selected, so every chain carries its own anchor and verifies standalone.
python -m cli verify prints which ledger it read.
Also breaking
anthropic>=0.120.0(was>=0.84). Runpip install -r requirements.txt.- Constitution is now version 4 (was 1). If you copied
bench.jsoninto
your own project, yours is unaffected, but review C-004 and C-008 below before
adopting the new one.
Out-of-project diffs are recorded without their bodies
A file governed from one project but belonging to another is still adjudicated
in full, but the ledger keeps only path, tool, and verdict. content,
old_string, new_string, edits, and formatted_diff are dropped and the
entry is marked redacted.
Judge prose may still quote a few lines of the change it reasons about. This
removes the systematic copy of file bodies, not every possible quotation.
Constitution changes
C-004 now tests for discarded precision rather than the token Any.
dict[str, Any] for parsed JSON is explicitly permitted; replacing a precise
annotation with a looser one, deleting a return annotation, or reaching for a
bare Any when the shape is knowable remain violations. Prefer object for
values only stored or passed through.
C-008 gains a bounded chain-retirement clause. Editing, reordering, or
removing an individual entry is still forbidden absolutely. A whole chain may be
retired only when it contains content that must not be published, and only with
a human decision, an archive verified by verify_chain before the original
moves, an anchor entry recording the retired chain's tip hash, genesis hash,
entry count, timestamps, verbatim archive path and reason, and indefinite
retention of that archive.
New
SECURITY.mdwith a disclosure policy scoped to Bench's actual threat
model: governance bypass, fail-open regressions, ledger forgery that still
passescli verify, prompt injection through governed content, credential
exposure. Model judgment quality is explicitly out of scope.tests/test_ledger_hygiene.py, which fails if any ledger entry carries a
diff body under a non-relative path.cli verifyprints the ledger it read;cli statscounts chain
anchors separately so they do not skew pass rates.- Least-privilege CI permissions, Dependabot, and refreshed action pins.
Known boundary
Bench governs changes made through Claude Code's Write/Edit/MultiEdit tools. A
bot-authored dependency PR is edited on the forge, never reaches the hook, and
merges without a verdict or a ledger entry. Read those diffs before merging and
do not enable auto-merge. This is stated plainly in README.md, CLAUDE.md,
and SECURITY.md rather than left implied.
Upgrading
git pull
pip install -r requirements.txt
claude update # fail-closed blocks edits on a stale CLI
python -m cli verify # confirms which ledger you are on