Optimal way of writing a debugging-friendly recursion?

I use ocamldebug when the bug location is hidden deep inside a
large set of modules, but for simpler cases I prefer to debug by hand.

Say I have a snippet like this

let rec iterator_for_foobar x=
  if test1 x 
  then modify1 x 
  else 
  if test2 x
  then iterator_for_foobar(modify2 x)
  else 
  if test3 x 
  then modify3 x 
  else 
  if test4 x
  then iterator_for_foobar(modify4 x)
  else modify5 x ;;

let foobar x = iterator_for_foobar(modify6 x) ;;   

If I need to debug a snippet like the above by hand, I typically refactor
it a little bit to make it more easily debugged ; I replace the iterator:'a->'b
function above with a push_once:'a->'a function which then allows me to define
a push_several_times:int->'a->'a, and my debugging will mostly consist of viewing revelant
values of push_several_times.

Now, the only coding patterns I have found so far both have their drawbacks :

Choice 1: adding an adhoc local type

type iterator_state =
   Work_still_in_progress of type1
   |Computation_finished of type2 ;;

let push_once_for_foobar = 
   Computation_finished _ ->
    failwith("This should never happen")
  |Work_still_in_progress x->
    if test1 x 
    then Computation_finished(modify1 x) 
    else 
    if test2 x
    then Work_still_in_progress(modify2 x)
    else 
    if test3 x 
    then Computation_finished(modify3 x)
    else 
    if test4 x
    then Work_still_in_progress(modify4 x)
    else Computation_finished(modify5 x) ;;  

let iterator_for_foobar x =
    match x with
    Computation_finished y -> y
  |Work_still_in_progress _->
     iterator_for_foobar(push_once_for_foobar x) ;;

let foobar x = iterator_for_foobar(Work_still_in_progress(modify6 x)) ;;   

Choice 2: using an option in a pair

let push_once_for_foobar (opt_answer,x)= 
    if test1 x 
    then (Some(modify1 x),x) 
    else 
    if test2 x
    then (None,modify2 x)
    else 
    if test3 x 
    then (Some(modify3 x),x)
    else 
    if test4 x
    then (None,modify4 x)
    else (Some(modify5 x),x) ;;  

let iterator_for_foobar pair =
  let (opt_answer,x) = pair in 
    match opt_answer with
    Some answer -> answer
   |None->iterator_for_foobar(push_once_for_foobar pair) ;;

let foobar x = iterator_for_foobar(None,modify6 x) ;;   

Both choices 1 and 2 have the drawback that the refactoring is nontrivial
and cannot easily be automated (this is the major issue for me).

Choice 1 has the anti-pattern of a variant case which will never be encountered during the computation flow.
Choice 2 has the anti-pattern of forcing us to add a dummy value which will
never be used and does not “represent” anything.

Are there other patterns improving on both 1 and 2 (perhaps with more sophisticated
types such as GADTs ?)