Guide
Explorer queries over typed Matchless IR
Explorer statically analyzes compiled Bosatsu. Bosatsu checks recursion for termination; external implementations and the host IO runtime remain trust boundaries. Explorer walks typed Matchless IR (the compiled form) and exposes bindings, IO sites, dependencies, reads, and argument influence as a queryable graph. It does not execute the target program or infer facts from source text.
Code & run Run the two order functions
Yichus is the analysis and tooling layer built on top of Bosatsu. Explorer is its main investigation surface. Start in the browser playground, use the same tools from the CLI, or write a Bosatsu program that does the investigation for you. The setup guide covers the local build, and the Explorer reference defines the command and result vocabulary used here.
Literal-root and return-data signals distinguish two dataflows
A well-typed output can still come from a constant rather than a state read. Explorer
reports that structural difference without assigning a product-level judgment: one binding
has a return-data path from a read, while the other has a
literal-root and no upstream read.
State read with return-data provenance
counter: IO[State[Int]] = state(0)
def read_counter() -> IO[Int]:
c <- counter.flat_map()
read(c)
current: IO[Int] = read_counter()
This card is an explanation of the return-data signal,
not a captured Explorer payload.
return-data: the compiled model contains a potential data path from a state read to the output.
Constant with literal-root provenance
fabricatedTotal = 42
response = fabricatedTotal
This card is an explanation of the literal-root signal,
not a captured Explorer payload.
literal-root: the output comes entirely from hardcoded constants.
Both bindings produce a value, but only the left example is structurally connected to a state read. The overview command below returns the relevant per-binding signals; use the traces in the next section when you need the full upstream path.
yichus explore --overview Demo/ExplorerPlayground/SuspiciousFabrication --overlay signals \
demos/explorer-playground/suspicious-fabrication.bosatsu
suspicious-fabrication.bosatsu, and click
Analyze.
Continue to the trace section for the dependency edges behind those summary signals.
Trace upstream dependencies from an output binding
A trace follows upstream dependencies from an output binding through typed IR nodes. Each node reports its role, signal tags, reads, and argument influence. Compare the two commands below, then use the trace-flow reference when you need exact influence-label semantics.
Trace with a state-read dependency
yichus explore \
--trace Demo/ExplorerPlayground/TrustworthyCounter/current \
demos/explorer-playground/trustworthy-counter.bosatsu
current traces back through read →
counter → state(0). This is a static dependency
path involving a state read, not a recording of values observed during execution.
Trace with only a constant dependency
yichus explore \
--trace Demo/ExplorerPlayground/SuspiciousFabrication/response \
demos/explorer-playground/suspicious-fabrication.bosatsu
response ends at the fabricatedTotal constant binding shown
above. Inspect the trace for its literal origin and the absence of a state-read
dependency; the graph describes compiled structure, not an execution.
Each binding in the trace carries four pieces of structural evidence:
- Role
-
What the binding does:
terminal-io(observable output),state-init(creates a state cell),intermediate-io(reads and feeds downstream), orpure(no IO). - Signal tags
-
Structural flags:
dead-input(a read that never reaches the output),guard-only-arg(argument used only as a branch selector),literal-root(output from constants only). - Read provenance
-
For each state read: does it reach the output as
return-data, only as aguard-onlybranch condition, or is itdetachedentirely? - Argument influence
- For each function argument: does it flow through as data, or is it only used as a guard?
--trace-flow instead of --trace to get a flat edge list
you can pipe into jq.
Use a query program when the same structural check must run across many bindings or targets.
Compile a structural check as a Bosatsu query
Interactive tools support exploration. A query program makes a known structural policy
repeatable over the complete ProgramData value.
ProgramData to ClassifyResult. The function can filter bindings,
inspect structural facts, and return a label plus evidence. Explorer supplies facts; the
query author defines the policy.
Query that classifies upstream derivation
package MyCheck
from Yichus/Explorer/Query import (
ClassifyResult, ProgramData,
classify_result, has_any_io,
terminal_io_without_deps,
bindings_with_signal,
)
def classify(data: ProgramData) -> ClassifyResult:
match has_any_io(data):
case False:
classify_result("pure-computation",
["pure computation, no IO"])
case True:
no_deps = terminal_io_without_deps(data)
match no_deps:
case [_, *_]:
classify_result("no-upstream-derivation",
["output has no upstream derivation"])
case []:
dead = bindings_with_signal(data, "dead-input")
match dead:
case [_, *_]:
classify_result("dead-input-detected",
["reads that never reach the output"])
case []:
classify_result("upstream-derivation-present",
["real upstream derivation"])
This program checks three things: is there IO? Does any output lack upstream dependencies? Are there dead inputs? It runs against the full binding graph and returns a structured answer.
Run the query against two targets
yichus explore --query my-check.bosatsu \
demos/explorer-playground/suspicious-fabrication.bosatsu
# classification: "no-upstream-derivation"
# evidence: ["output has no upstream derivation"]
yichus explore --query my-check.bosatsu \
demos/explorer-playground/trustworthy-counter.bosatsu
# classification: "upstream-derivation-present"
# evidence: ["real upstream derivation"]
BindingEntry fields available to a query
Each BindingEntry in the program data carries:
- Role, signal tags, dependency count, literal count
reads: List[ReadEntry], per-read provenance with influence kindarguments: List[ArgEntry], per-argument data flow infodependencies: List[DependencyEdge], edges to other bindings this one depends on, withtargetBindingIdandedgeKindhasBranchEquivalence, the analyzer’s compiled branch-equivalence flag
The simple program above uses summary checks. When you need more precision, inspect individual reads, arguments, or follow the dependency graph:
# Does any read actually reach the output as data?
has_return_data_read(entry)
# Follow dependency edges to another binding
deps = get_dependencies(entry)
any_dep(deps, dep -> str_eq(get_dep_target(dep), "MyPkg/counter"))
# Find a binding by id and inspect it
match find_binding(data, "MyPkg/counter"):
case Some(dep_entry): has_return_data_read(dep_entry)
case None: False
Graph traversal: follow dependencies transitively
Because each binding carries its dependency edges, you can write programs that walk the graph instead of only inspecting individual nodes. This query checks whether every terminal-io output has a transitive path back to a real state read:
def step_one(data: ProgramData, work: List[String], visited: List[String])
-> (List[String], List[String], Bool):
match work:
case []: ([], visited, False)
case [id, *rest]:
# ... check if id has a return-data read, or expand its deps
match find_binding(data, id):
case Some(entry):
match has_return_data_read(entry):
case True: ([], visited, True) # found real data!
case False:
# Add dependency targets to work list
new_work = get_dependencies(entry).foldl_List(rest, ...)
(new_work, [id, *visited], False)
def traverse(data, work, visited, fuel):
recur fuel:
case []: False
case [_, *remaining]:
(next, vis, found) = step_one(data, work, visited)
match found:
case True: True
case False: traverse(data, next, vis, remaining)
The recur fuel: pattern consumes a fuel list, so the compiler can
check that this recursion terminates while allowing
multi-hop traversal. Open the
full working
eval/queries/graph-traversal.bosatsu query.
eval/queries/. Read them, modify them, or write your own from scratch.
explorer_overview,
explorer_trace, and explorer_connections; a query replaces a
fixed manual sequence with one explicit program without claiming a universal call count.
Review the
shipped query directory
for complete programs and use the
query reference
for the input fields and invocation forms.
classify but you can use any name with
--entry-point check_permissions on the CLI or the entryPoint
parameter in MCP. In the playground, paste the query source, select a target, and run it.
eval/queries/perm-gate-check.bosatsu against
demos/explorer-playground/permission-leak-demo.bosatsu. That query is anchored
to this demo's secret_cell and can_see_secret bindings, walks the
dependency graph, and checks that secret-returning exports still flow through the gate. To
reuse it on another program, update the gate_binding and
secret_binding constants at the top of the query.
The command table below maps other graph questions to their Explorer operations.
Explorer operations and their graph questions
Explorer has eight tools. Each answers a different question about the program graph.
| When you want to… | Use | CLI |
|---|---|---|
| Get a high-level picture | overview |
--overview |
| Navigate the binding graph | explore |
--path --depth |
| See what feeds or depends on a binding | connections |
--connections |
| Find bindings matching a pattern | search |
--search |
| Understand how two bindings relate | path |
--path-from <from> --path-to <to> |
| See the full upstream tree with provenance | trace |
--trace |
| Get raw edges for scripting | trace_flow |
--trace-flow |
| Automate the entire investigation | query |
--query |
Browser, CLI, and MCP surfaces
All eight tools are available from three surfaces. The
browser playground compiles
Bosatsu in the browser and renders the graph visually. The CLI
(yichus explore) produces JSON you can pipe to
jq or scripts, which suits automation. The MCP server
(yichus mcp my-program.bosatsu) exposes all eight tools over the MCP protocol
so agents can call the corresponding ExplorerWorkspace operation contracts.
See the reference for each surface's invocation and output shape.
Overview, trace, and query sequence
# 1. Start with the big picture
yichus explore --overview Demo/ExplorerPlayground/MixedTrust \
demos/explorer-playground/mixed-trust.bosatsu
# 2. Spot something suspicious — drill in
yichus explore --trace Demo/.../MixedTrust/reviewSummary \
demos/explorer-playground/mixed-trust.bosatsu
# 3. Compare with a trustworthy binding
yichus explore --trace Demo/.../MixedTrust/readScore \
demos/explorer-playground/mixed-trust.bosatsu
# 4. Automate the check for next time
yichus explore --query my-check.bosatsu \
demos/explorer-playground/mixed-trust.bosatsu
This example starts broad, inspects two bindings, then saves a repeatable query. Each operation is available in the playground, CLI, and MCP surface. Use the operation reference for flags and result shapes.
Run the six Explorer sample programs
Choose a surface based on the task.
Browser: Open the
playground, pick the state-read counter sample,
click Analyze, then switch to the constant-derived sample and compare.
CLI: Run the two commands below, then drill with --trace into
the binding that catches your eye.
Query: Copy the program from the query section,
save it as my-check.bosatsu, and run it with --query against any
Bosatsu file.
yichus explore --overview Demo/ExplorerPlayground/TrustworthyCounter \
demos/explorer-playground/trustworthy-counter.bosatsu
yichus explore --overview Demo/ExplorerPlayground/SuspiciousFabrication --overlay signals \
demos/explorer-playground/suspicious-fabrication.bosatsu
Six samples live in demos/explorer-playground/. Each demonstrates a
different pattern. The filenames below are literal repository identifiers retained for
copyable commands; words inside a filename are not Explorer verdicts.
| File | What it demonstrates |
|---|---|
trustworthy-counter.bosatsu |
State reads whose values reach the output |
suspicious-fabrication.bosatsu |
IO is declared while the output remains constant-derived |
dead-inputs.bosatsu |
A state read that never reaches the output, a dead input |
mixed-trust.bosatsu |
A state-read path alongside a constant summary binding |
pure-computation.bosatsu |
No IO sites, only pure arithmetic |
permission-leak-demo.bosatsu |
A secret reaches the export without flowing through the permission gate |
readScore and reviewSummary in the overview, then trace both.
The first has a read dependency; the second is constant-derived. Return to the
trace field definitions if an influence label is unclear.
Where this fits
What this is about: Fact Tooling