Foolish Debugging
The Foolish VM has no interactive debugger. The step and run CLI commands both fully evaluate to settlement and print the final tree — they do not pause or single-step. Real Foolish debugging is unit-test-driven: you write a temporary Rust test that reproduces the problem, step the FVM programmatically, inspect FIR state (NYES, children, values), and query the resolution engine (ib_search / ab_search) at any point.
The knowledge is in
references/. This file is a map. Open the reference for what you are about to do.
When to use this skill
- A Foolish program evaluates to the wrong value, to
???(NK), or does not settle. - A search resolves to NK when you expected a hit (or vice versa).
- A brane's NYES progression looks wrong (e.g. regresses from constanic to pre-constanic).
- You are touching FIR kinds, the search engine, or the stepper in
foolish-ubca. - You need to understand why a specific line of Foolish evaluates the way it does.
Do NOT use this skill for: Rust-level crashes/panics in the compiler machinery (use the general /debugging skill with the rust.md runtime reference), or snapshot/approval test output changes (that is the snapshot review workflow in AGENTS.md, not debugging).
The workflow in four steps
| # | Step | Reference |
|---|------|-----------|
| 1 | Write a temporary test that compiles the offending Foolish code and grabs the FIR at the line you suspect. | references/test-template.md |
| 2 | Step the FVM — set a breakpoint with step_until* to stop right at the suspect spot, then step one-at-a-time while watching NYES and the _children stores. | references/nyes-tracing.md |
| 3 | Inspect FIR state — read NYES, walk the parent chain, query ib_search / ab_search, read values and children. | references/fir-inspection.md |
| 4 | Clean up — promote the debug test to a named regression test, or delete it. Never leave it to rot. | references/cleanup.md |
Read the reference for the step you are entering. Each is self-contained and short.
🔬 Your sharpest tool is the breakpoint.
step_until_statement_name(&root, &scope, "foo")runs the FVM until the statementfoois about to be worked on, then freezes so you can inspect exactly the moment things go wrong — then step-and-watch thefoolish_children/ubc_childrenNYES from there. This turns "the program hangs / gives a wrong value, go hunt for why" into a precise, few-minute investigation. See step 2's reference for the full pattern. Use it early and often.
Critical orientation (read before you start)
Target crate
The active FIR implementation is foolish-ubca. FirRef there is Rc<RefCell<dyn Fir>> (in foolish-ubca/src/fir_trait.rs). The ib_search / ab_search API lives on the Fir trait in that crate.
foolish-core has a different FirRef (Rc<RefCell<dyn Steppable>>) and a simpler search_in_brane free function. Do not mix the two crates' APIs. If you are debugging brane evaluation, you are in foolish-ubca.
Where to put the debug test
The test scaffolding helpers (make_*, step_to_settled, find_search, assert_progression, step_watching, settle_root) live inside the #[cfg(test)] mod tests block at the bottom of foolish-ubca/src/fir_kinds.rs (search for ^mod tests). The step_until* breakpoint functions are pub in foolish-ubca/src/evaluator.rs (use crate::evaluator::*;). These are not accessible from tests/ integration test files.
Two options:
- Add your test to the
mod testsblock infir_kinds.rs— recommended; all helpers are in scope,pub(crate)engine types are reachable. - Write a separate
tests/file — you must copy the helpers you need (step_to_settled,find_search) into your file, and you cannot usepub(crate)engine types (SearchPredicate,BraneNavigator, etc.). Only the publicFirtrait methods (_ib_search,_ab_search,core(),kind(),value()), thepubstep_until*functions, andCompiler::compileare available.
Line numbers rot fast — this file grows. This skill references code by name (functions, methods,
mod tests); locate them withgrep -n, not a remembered line number.
Option 1 is almost always correct. See references/test-template.md for the exact placement.
The NYES state machine
Every FIR progresses through NYES (Not Yet Evaluated State) states. This is the primary observability surface:
PREMBRIONIC → EMBRYONIC → BRANING → (ECONSTANIC | WOCONSTANIC) → (CONSTANT | INDEPENDENT | NK)
- Pre-constanic ("nigh"):
PREMBRIONIC,EMBRYONIC,BRANING— more stepping is appropriate. - Constanic (terminal):
ECONSTANIC,WOCONSTANIC,CONSTANT,INDEPENDENT,NK. is_constanic()returns true for all five terminal states.
Authoritative definitions: foolish-core/src/fir.rs:116 (the enum), docs/foop/FOOP-62.md (semantics). See references/nyes-tracing.md for what each terminal state means and how to read the progression.
Finding a node's home brane
There is no get_my_parents. To walk the full chain, loop on core().parent()
(-> Option<FirRef>, one step up the Weak link). To jump to the enclosing
home brane, there are two variants — use the one matching your context:
node.borrow()._get_my_brane(&node)— iterative parent-walk; climbs.parent()to the first brane-like kind. Works any time; needs the node's ownFirRef. This is the one to reach for while inspecting.node.borrow().get_my_brane(&scope)— scope-cached; readsscope.current_brane. Only valid mid-step with a liveScope.
(The _-prefixed form is the parent-walk convention, matching _ib_search /
_ab_search.) See references/fir-inspection.md.
Non-negotiable safety invariants
- Debug tests are temporary. A
temporary_reproduce_to_debug_*test must be promoted to a named regression test or deleted before the task is done. Never leave it behind. (references/cleanup.md) - Never auto-accept snapshots. Do not run
cargo insta acceptorINSTA_UPDATE=always. Do not alter.snap,.approved.foo, or.snap.new.approvedfiles. (SeeAGENTS.md.) - Never start Phase+ work when tests are broken. Fix or disable (with human permission) the broken test first.
- Output is for the agent to read. Format debug
eprintln!output so an agent with code context can parse it — labeled sections,{:?}debug formatting of NYES traces, structured FIR-tree dumps. See references/cleanup.md. - Never commit from inside this skill. Commits belong to the user's explicit request.
- Runtime state is the only source of truth. A hypothesis about why a brane evaluates to NK without an observed NYES trace and an
ib_searchresult is a guess. Do not fix guesses.
What to do right now
- Read references/test-template.md and write the temporary test.
- Read references/nyes-tracing.md and step the FVM while recording NYES.
- Read references/fir-inspection.md and query the FIR/brane state at the offending point.
- Read references/cleanup.md and promote or delete the test.