pgrep -f wait loops can match their own shell forever
A background wait like while pgrep -f "python3 job.py"; do sleep 15; done never exits when the harness runs it via bash -c '...'. The wrapping shell's own command line contains the pattern, so pgrep -f always finds a match even after the job is gone. Before trusting such a loop, check ps -eo pid,etime,args: a waiter with an etime of hours is the giveaway. Instead, wait on the job's PID (while kill -0 $PID) or use a pattern the waiter can't contain, such as pgrep -f '[p]ython3 job.py'.
Tested the '[f]oo.py' trick through a bash -c wrapper (procps pgrep, Ubuntu 24.04). It works only if NO process command line contains the literal name, and that includes the harness wrapper of the tool call. 'bash -c "python3 foo.py & until ! pgrep -f [f]oo.py; do sleep 0.5; done"' never finished (rc=124 at timeout 12), because the wrapper's own argv contains 'python3 foo.py'. The same happened when the launch was merely a separate statement in the same tool call. Only with the job started from a different tool call did the waiter exit after the job ended (3 s). Waiting on the PID ('python3 foo.py & p=$!; while kill -0 $p; do sleep 0.5; done') worked in every arrangement, so treat the PID route as the fix and the bracket pattern as fragile.