Yichus computes machine-checkable facts about programs, without heuristics or guesswork. Every fact is one of two things, and says which: a structural fact of the compiled program (exact by construction), or an observation of real, seeded runs (always labeled “bounded observation, not proof”). The catch: it works only on Bosatsu, a small language where every program terminates and every side effect is declared; those two guarantees are what make this possible. If you want code reviews (AI reviews above all) whose claims can be verified or refuted mechanically, this is for you.
Here is a small pricing module (it ships in the repository at
demos/pricing,
so every command on this page runs as written). It has three real
problems, plus a fourth finding that only running the code can
surface.
struct Order(id: Int, qty: Int, discount_code: Int)
def quote(o: Order) -> Int:
_ = o # the order is thrown away...
42 # ...and the "computed" price is hard-coded
def shipping_fee(subtotal: Int, region: Int) -> Int:
match cmp_Int(region, 0):
case GT: add(subtotal, 5)
case _: add(subtotal, 5) # both branches identical: region decides nothing
def surge_bonus(x: Int) -> Int:
match cmp_Int(x, 1000):
case GT: x # looks input-dependent...
case _: 7 # ...but this branch always wins in practice
One command, yichus explore --agenda demos/pricing/*.bosatsu
(the “agenda” is the tool’s list of noteworthy facts,
one card each), reports this verbatim:
quote:
“takes arguments, none of whose data reaches the produced value; every
value the result can take originates in literals”
The price is fake. No opinion offered; the fact is checkable.
Order.discount_code:
“the field is constructed but no compiled extraction of it exists
anywhere (reads: [])”
Everyone fills in the discount code; nothing ever reads it.
shipping_fee(region):
“the parameter only selects among branches; its data never reaches the
produced value”
Region picks a branch, but both branches compute the same thing.
surge_bonus:
“across 8 seeded runs (seed 6321458022832390279),
Demo/Pricing/surge_bonus always produced 7; bounded observation, not
proof; parameter(s) x statically reach the produced value and no
literal-result fact applies — the constancy is not statically
explained”
Static analysis alone can’t see this one, so the tool also runs the code.
Every fact is located and machine-checkable; the run-based ones carry the seed their inputs were generated from. None of them says “bug”. You decide which of these count as bugs. Whether each fact is true has already been settled mechanically.
Eleven kinds of fact, each with real code and the tool’s actual output. The first eight are computed statically, so they are exact by construction. Running the code on seeded, type-derived inputs produces the last three, which always carry the label “bounded observation, not proof.”
The static facts are computed on the compiled program’s
dataflow structure: a value reaches a result if there is a
chain of data edges from one to the other, and influences
adds control edges (picking a branch counts). Structure
over-approximates real influence, since every way a value could
matter shows up as an edge. That is why the negative facts are
the guarantees: “never reaches” and
“influences nothing” mean no execution can make the value
matter. A bare “reaches” only says a path
exists on paper. So constant-despite-reach gets its own
fact type: the structure says the input could matter, the observed
runs say it didn’t, and the fact records that disagreement.
dead-field a record field that is written but never readstruct Order(id: Int, qty: Int, discount_code: Int) # id and qty are read by accessors; discount_code is read by nothing
Data being diligently maintained that cannot affect anything.
unused-parameter a parameter that influences nothing in its functiondef quote(o: Order) -> Int: _ = o 42
The function promises to consider its input and doesn’t.
guard-only-parameter a parameter that picks branches but never reaches the resultdef shipping_fee(subtotal: Int, region: Int) -> Int:
match cmp_Int(region, 0):
case GT: add(subtotal, 5)
case _: add(subtotal, 5)
Legitimate for dispatch helpers; suspicious when the spec says the value should flow into the answer.
discarded-argument a call that passes a value into a parameter the callee throws awaydef summary(o: Order) -> Int: quote(o) # quote discards o entirely
The caller passes data the callee never uses, and the fact points at the exact call.
literal-result a function whose every possible result is built from constantsdef quote(o: Order) -> Int: _ = o 42
The signature of a faked computation, including ones laundered through helper calls. The analysis follows values across function boundaries.
detached-read a state read whose value influences nothingrefresh: IO[Int] = ( c <- cell.flat_map() _ <- c.read().flat_map() # read the cell... pure(7) # ...ignore it, return 7 )
A state cell is a mutable variable managed by the runtime. This is code that looks like it consults state and doesn’t.
blind-write a state write with no read of that cell in the same codereset: IO[Unit] = ( c <- cell.flat_map() c.write(9) # overwrites whatever was there, unconditionally )
Fine for a reset; a data-loss hazard in a read-modify-write that forgot the read.
package-reach one package can transitively reach another, with the import pathpackage Demo/Report from Demo/Pricing import Order, quote
These facts power layering rules like “the domain layer must never reach the notification layer”. When a rule is broken, you get the exact import chain that breaks it.
run-evidence what happened when the code ran on seeded inputsdef quote(o: Order) -> Int: _ = o 42
Every pure function can be run in isolation, because Bosatsu guarantees it terminates. Every run-based fact carries the seed its inputs were generated from. Healthy functions get the opposite fact: “runs 0 and 1 (seed 6321458022832390279) of Demo/Pricing/line_total produced 28 and -39 — the result depends on its inputs.”
unexecuted-reference referenced by code that ran, but only at a spot that never executeddef pick_quote(x: Int) -> Int:
match cmp_Int(x, 999):
case GT: fallback_quote(x) # this branch never runs
case _: x
Dead code that a “who references this?” search would call alive.
constant-despite-reach inputs reach the result on paper; observed constant in practicedef surge_bonus(x: Int) -> Int:
match cmp_Int(x, 1000):
case GT: x
case _: 7
The static story and the observed story disagree, which is the kind of divergence worth a human look.
A review (yours or an AI’s) is submitted as claims, and the verifier answers from the facts. Three claims about the module above:
{"claims": [
{"kind": "literal-result", "package": "Demo/Pricing", "binding": "quote"},
{"kind": "no-detached-read", "package": "Demo/Report"},
{"kind": "dead-field", "package": "Demo/Pricing", "struct": "Order", "field": "qty"}
]}
yichus verify-claims claims.json demos/pricing/*.bosatsu returns (actual output):
The exit code is 1 because one claim was refuted, so a wrong review
fails the same way a failing test does. Look at the second verdict:
“nothing is wrong here” arrives backed by an enumerated count
of the read sites examined. Every fact kind has a claim kind, plus no-<kind>
absence forms; the full vocabulary is in the
agent tooling guide.
yichus fact-diff computes all the facts for two versions of a
program and reports which facts appeared or disappeared. Here someone
replaced quote’s real computation with the hard-coded 42
(actual output, trimmed):
quote:
“every value the result can take originates in literals”
blast radius: 2 consumers (Demo/Pricing/summary → quote, Demo/Report/report_total → quote)summary → quote(o):
“the passed value is discarded there”quote:
“runs 0 and 1 (seed 6321458022832390279) of Demo/Pricing/quote produced 5 and -9 — the result depends on its inputs”The change’s real story: the price became a constant, the callers’ data now goes nowhere, and the “depends on its inputs” evidence vanished. Each fact lists who is affected downstream (“blast radius: 2 consumers”), and claims like “no new dead fields” fail the change with exit code 1, same as the verifier.
Measured on planted-defect codebases with independent AI reviewers, scored against a hidden key. The strongest results:
Needs git, a JVM (17+), and
sbt;
sbt assembly writes yichus.jar under
target/. The pricing module above ships in the repo, so
this works end to end:
# one-time setup git clone https://github.com/snoble/yichus && cd yichus sbt assembly alias yichus="java -jar $(pwd)/target/scala-*/yichus.jar" yichus explore --agenda demos/pricing/*.bosatsu # every fact, as cards yichus explore --agenda --agenda-type dead-field \ demos/pricing/*.bosatsu # one fact type # save the three-claim JSON from the section above as claims.json: yichus verify-claims claims.json demos/pricing/*.bosatsu # fact-diff wants a "before": copy the module, then edit # old/pricing.bosatsu to give quote a real computation — the page's # diff above is exactly that edit, seen in reverse: cp -r demos/pricing old yichus fact-diff --before old/pricing.bosatsu \ --after demos/pricing/pricing.bosatsu
There is also yichus tla, a separate feature that
model-checks declared state-machine invariants and reports
holds/violated/inconclusive verdicts with counterexample traces. See the
spec-first
verification demo.
All the benchmark data, including the losses →
Explore the facts live in the playground →