not sure why sleep would be an issue, but one problem you will find when using in_background is that calls are queued up, rather than executing in order. So, ehile you might think you are doing a depth first search, you code really does a breadth first. If your go_action is also in_backgrounded, you might have issues, where code does not execute in the order you planned.
I suspect if you get another touch event while go is running, this could cause issues, because you might be inserting a call to go where it doesn't belong. i.e the loop has queued up 4 calls to go(), but then a touch event wueues up another square to go(), then the 4 calls run, each queueing up other calls, then the extra touch runs, on the original square. Now everything is in a funky state.
You might consider a non-backgrounded function, with ui.animate() to set colors. perhaps with a completion argument to call the next iteration.
Another option would be to use a deque, and rather than recursing, you would add neighbors to the deque. Though recursion makes for nice compact code, it is often frowned upon in real world applications. A method I like for this sort of problem is a single worker loop with a deque. You populate the start square, then loop until the deque is empty. Depending on which side you pop/append you can make this do depth or breadth first.while len(mydeque): square =mydeque.pop() #process square.......... change color, etc for n in square.white_neighbors(): if n not in mydeque: mydeque.append(n)