Bellbook 0.6.0 is out: seven named queries over lineage, evidence, and standing - on every surface, over a live log or a portable receipt. Release notes.

Two days ago I ran Bellbook against a real workload for the first time: a best-of-N round over a canary repository, three candidates generated, evaluated, one selected, the line continued. The write side held up. Every claim landed in the log, the receipt validated, and when I later retracted the evaluation the winner rested on, the taint and the standing collapse propagated exactly as the spec says they must.

Then I tried to read the log back, and the honest finding was uncomfortable: the record knew everything and could tell me almost nothing. “Which candidate won, and on what evidence?” meant walking records by hand, chasing Use refs into evaluation payloads. “What does the current line actually rest on?” meant reconstructing the descent one parent pointer at a time. The log was a witness with perfect memory and no voice.

That gap is what 0.6.0 closes.

Seven questions, not a query language

The obvious move would have been a query engine - filters, predicates, some pattern language over payloads. We deliberately did not build that, and the reason is on the record: RFC-0001 pre-registered that a general query surface is only justified by external read-side adoption, and that gate has not been reached. Nothing in this release re-opens it.

What shipped instead is a closed, named set. RFC-0002 names the seven questions the field test actually asked, and fixes their semantics:

  • descent - the line of descent from a candidate back to its roots
  • descendants - everything downstream of a record
  • siblings - a candidate’s generation
  • frontier - what is still open: candidates never considered, winners not yet continued
  • standing - one record’s standing, taint, retraction status, and any restorations
  • evidence - what a selection, or a whole line, rests on
  • selected - the selections under an exact objective, with chosen candidates and their evidence

Every query is deterministic and read-only, derived from what replay already computes - nothing is stored, nothing is indexed. And two properties matter more than the list itself.

Queries answer only over verified history. A log or receipt that fails replay returns an error, not answers. There is no “query it anyway” mode, because an answer derived from history that does not verify is not an answer, it is a guess wearing the record’s clothes.

Nothing is ranked and nothing is silently filtered. A retracted evaluation still appears in an evidence report - annotated as retracted. A compromised candidate still shows up on the frontier - marked compromised. Bellbook’s job is to preserve what happened and let the reader judge; a query surface that quietly dropped the embarrassing records would be an editorial layer pretending to be an evidence layer.

The same answer, everywhere

The set is implemented on every surface, and the surfaces are held to byte-level agreement:

bellbook query selected "best-of-n" --receipt receipt.json
bellbook query evidence CANDIDATE_ID --log ./mylog --rules rules.json

The same queries exist as methods on the Python Writer and Receipt, and as a queries module in the Rust core. The --json output over a live log and over an exported receipt is asserted identical in CI. The conformance corpus gained query-cases.json - executable answers, with a build-failing check that every query name has vectors - and the independent Python validator, which shares no code with the Rust reference, implements the whole set from scratch and agrees with it byte for byte. That is the same cross-implementation discipline the verdicts have lived under since the corpus existed: determinism is a claim until a second implementation checks it.

The test that graded itself

My favorite part of this release is that it was graded before it was built. RFC-0002 pre-registered its own success criterion: re-running the field test must require zero hand-walking of records - every question must map to a named query, or the set is the wrong shape.

That proof is now a CI test. The whole canary story - adopt a baseline on a benchmark, run a best-of-N round, discover the benchmark was broken, retract it, repair on fresh evidence - is replayed through the CLI, and every question the original field test answered by hand is answered by bellbook query alone. Who won and why: one query. The winner’s full line of descent: one query. What the line rests on, with the broken benchmark surfacing as retracted evidence: one query. The standing of the original adoption after the repair - unsound, tainted, restored on the record, because restoration restores standing, never innocence: one query. What is still open: one query.

If a future change to the named set makes any of those questions need a manual walk again, the build fails. The gap I found in the field test cannot quietly reopen.

Tie-breaks became evidence too

The field test surfaced one more gap, subtler than the rest. Two candidates passed the only criterion; one was chosen; the reason it won lived in the selection’s free-text rationale. A verifier looking at that record sees two green candidates and a coin flip - a valid selection whose stated evidence does not distinguish the winner.

The fix is a pattern, not a payload change: record the discriminating fact as its own Evaluation under its own criterion - completeness, say - and let the selection use it. Now the tie-break is evidence, the selection genuinely discriminates, and the queries surface it forever after. rationale stays exactly what it always was: a recorded statement, useful to humans, verified by no one. The best-of-N quickstart documents it.

No spec change, again

For the third release in a row, the spec epoch is unchanged: 0.6.0 still implements spec v0.3, and every 0.3, 0.4, and 0.5 receipt validates identically. That is not stagnation; it is the point of separating the wire format from the surfaces around it. Retraction reached every surface in 0.5 without touching a byte of the format. The read side arrived in 0.6 the same way. The records were always enough - what grew is the number of honest ways to reach them.

The semantics of the named set are pinned three ways, so no single document can drift: the RFC for edge cases, a short section in the SPEC for existence and boundaries, and the corpus for executable answers.

Try it

cargo add bellbook        # 0.6.0
pip install bellbook      # 0.6.0, same core

bellbook rules init --author agent:provider --out rules.json
# ... record candidates, evaluations, selections ...
bellbook export --log ./mylog --rules rules.json --out receipt.json
bellbook query frontier --receipt receipt.json

The record has a voice now. It still only says what it can prove.