@@ -277,8 +277,11 @@ impl BitMachine {
277277 }
278278
279279 ' main_loop: loop {
280- // Make a copy of the input frame to give to the tracker.
280+ // Capture read and write frames before the node action, to give to the tracker.
281+ // The read frame cursor is where the node's input begins.
282+ // The write frame cursor is where the node's output begins.
281283 let input_frame = self . read . last ( ) . map ( Frame :: shallow_copy) ;
284+ let output_frame = self . write . last ( ) . map ( Frame :: shallow_copy) ;
282285 let mut jet_result = Ok ( ( ) ) ;
283286
284287 match ip. inner ( ) {
@@ -391,16 +394,15 @@ impl BitMachine {
391394 // describes the Bit Machine "input" to the current node,
392395 // no matter the node.
393396 let read_iter = input_frame
394- . map ( |frame| frame. as_bit_iter ( & self . data ) )
397+ . map ( |frame| frame. as_bit_iter_from_cursor ( & self . data ) )
395398 . unwrap_or ( crate :: BitIter :: from ( [ ] . iter ( ) . copied ( ) ) ) ;
396399 // See the docs on `tracker::NodeOutput` for more information about
397400 // this match.
398401 let output = match ( ip. inner ( ) , & jet_result) {
399402 ( node:: Inner :: Unit | node:: Inner :: Iden | node:: Inner :: Witness ( _) , _)
400403 | ( node:: Inner :: Jet ( _) , Ok ( _) ) => NodeOutput :: Success (
401- self . write
402- . last ( )
403- . map ( |r| r. as_bit_iter ( & self . data ) )
404+ output_frame
405+ . map ( |frame| frame. as_bit_iter_from_cursor ( & self . data ) )
404406 . unwrap_or ( crate :: BitIter :: from ( [ ] . iter ( ) . copied ( ) ) ) ,
405407 ) ,
406408 ( node:: Inner :: Jet ( _) , Err ( _) ) => NodeOutput :: JetFailed ,
@@ -430,7 +432,7 @@ impl BitMachine {
430432 let out_frame = self . write . last_mut ( ) . unwrap ( ) ;
431433 out_frame. reset_cursor ( ) ;
432434 let value = Value :: from_padded_bits (
433- & mut out_frame. as_bit_iter ( & self . data ) ,
435+ & mut out_frame. as_bit_iter_from_cursor ( & self . data ) ,
434436 & program. arrow ( ) . target ,
435437 )
436438 . expect ( "Decode value of output frame" ) ;
@@ -669,6 +671,37 @@ mod tests {
669671 . exec ( & prog, & env)
670672 }
671673
674+ #[ test]
675+ #[ cfg( feature = "human_encoding" ) ]
676+ fn set_tracker_cursor_regression ( ) {
677+ // When a `case` node executes inside another `case` or `drop`, the read frame cursor
678+ // is no longer at the frame's start. Before this fix, `exec_with_tracker` passed an
679+ // iterator starting at frame.start to the tracker, so `SetTracker` read the wrong bit
680+ // and recorded the wrong branch, leading to an assertl/assertr mismatch after pruning.
681+ //
682+ // Program: comp (pair (injl (injr unit)) unit) (case (case unit unit) unit)
683+ // Intermediate frame bits: [0=L-tag of outer, 1=R-tag of inner]
684+ // Outer case: peek bit 0 = 0 (LEFT), fwd(1), cursor moves to bit 1.
685+ // Inner case: should read bit 1 = 1 (RIGHT).
686+ // Bug: tracker received iterator from bit 0, read 0 (LEFT) → pruned wrong branch.
687+ use crate :: human_encoding:: Forest ;
688+ use crate :: types;
689+ use std:: collections:: HashMap ;
690+
691+ types:: Context :: with_context ( |ctx| {
692+ let s = "main := comp (pair (injl (injr unit)) unit) (case (case unit unit) unit)" ;
693+ let program = Forest :: parse :: < crate :: jet:: Core > ( s)
694+ . expect ( "parse" )
695+ . to_witness_node ( & ctx, & HashMap :: new ( ) )
696+ . expect ( "main root" )
697+ . finalize_pruned ( & CoreEnv :: new ( ) )
698+ . expect ( "finalize and prune" ) ;
699+ let mut mac = BitMachine :: for_program ( & program) . expect ( "for_program" ) ;
700+ mac. exec ( & program, & CoreEnv :: new ( ) )
701+ . expect ( "pruned execution should succeed" ) ;
702+ } ) ;
703+ }
704+
672705 #[ test]
673706 #[ cfg( feature = "elements" ) ]
674707 fn crash_regression1 ( ) {
0 commit comments