@@ -126,15 +126,19 @@ tasks:
126126 pkill -f "UTM.app/Contents/MacOS/UTM" >/dev/null 2>&1 || true
127127 sleep 2
128128 open -g -a UTM || true
129- # Wait until UTM is scriptable again before relying on utmctl.
129+ # Return non-zero if UTM never becomes scriptable, so callers can tell
130+ # "UTM never came back" from "UTM is up but doesn't see the VM".
130131 for _ in $(seq 1 10); do
131132 if osascript -e 'with timeout of 5 seconds' \
132133 -e 'tell application "UTM" to get name of every virtual machine' \
133134 -e 'end timeout' >/dev/null 2>&1; then
134- break
135+ return 0
135136 fi
136137 sleep 2
137138 done
139+ echo "❌ UTM did not become scriptable within ~20s after relaunch."
140+ echo " Open UTM manually, dismiss any prompt, then re-run."
141+ return 1
138142 }
139143
140144 if [ "{{.FORCE}}" = "true" ]; then
@@ -150,7 +154,7 @@ tasks:
150154 echo "Checking UTM VMs directory for existing golden VM...: {{.UTM_VMS_DIR}}/{{.VM_GOLDEN_NAME}}.utm"
151155 if [ -d "{{.UTM_VMS_DIR}}/{{.VM_GOLDEN_NAME}}.utm" ] || [ -f "{{.UTM_VMS_DIR}}/{{.VM_GOLDEN_NAME}}.utm" ]; then
152156 echo "✓ Golden VM exists on disk but UTM hasn't loaded it: {{.UTM_VMS_DIR}}/{{.VM_GOLDEN_NAME}}.utm"
153- reload_utm
157+ reload_utm || exit 1
154158 if utmctl status "{{.VM_GOLDEN_NAME}}" >/dev/null 2>&1; then
155159 echo "✓ UTM reloaded and now sees the golden VM."
156160 exit 0
@@ -224,7 +228,7 @@ tasks:
224228
225229 # We just wrote a VM bundle under a running UTM; reload it so it registers
226230 # the new golden VM, then let the caller (vm:reset) continue.
227- reload_utm
231+ reload_utm || exit 1
228232 if utmctl status "{{.VM_GOLDEN_NAME}}" >/dev/null 2>&1; then
229233 echo "✓ UTM reloaded and now sees the golden VM."
230234 exit 0
@@ -269,10 +273,21 @@ tasks:
269273 utmctl delete "{{.VM_NAME}}" 2>/dev/null || true
270274 # Wait until UTM drops the VM from its registry (status goes non-zero =
271275 # not found) so the clone below doesn't race a half-deleted entry.
272- for _ in $(seq 1 10); do
273- utmctl status "{{.VM_NAME}}" >/dev/null 2>&1 || break
276+ deleted=false
277+ for _ in $(seq 1 30); do
278+ if ! utmctl status "{{.VM_NAME}}" >/dev/null 2>&1; then
279+ deleted=true
280+ break
281+ fi
274282 sleep 1
275283 done
284+ # Abort rather than cloning over a half-deleted registry entry, which
285+ # leaves UTM in an inconsistent state that's hard to recover from.
286+ if [ "$deleted" != "true" ]; then
287+ echo "Error: working VM '{{.VM_NAME}}' still present after waiting for deletion." >&2
288+ echo "UTM may be slow or wedged. Delete it manually in the UTM app and re-run 'task vm:reset'." >&2
289+ exit 1
290+ fi
276291 fi
277292
278293 # Clone the golden VM
@@ -282,10 +297,21 @@ tasks:
282297 # Wait until UTM has registered the clone as a stopped VM before starting it.
283298 # Starting immediately after a clone can wedge UTM's Apple Event handler.
284299 echo "Waiting for UTM to register the cloned VM..."
300+ registered=false
285301 for _ in $(seq 1 15); do
286- [ "$(utmctl status "{{.VM_NAME}}" 2>/dev/null)" = "stopped" ] && break
302+ if [ "$(utmctl status "{{.VM_NAME}}" 2>/dev/null)" = "stopped" ]; then
303+ registered=true
304+ break
305+ fi
287306 sleep 1
288307 done
308+ # Abort rather than starting a clone UTM hasn't finished registering,
309+ # which can wedge UTM's Apple Event handler (the race this wait avoids).
310+ if [ "$registered" != "true" ]; then
311+ echo "Error: cloned VM '{{.VM_NAME}}' not registered as 'stopped' after waiting." >&2
312+ echo "UTM may still be registering the clone. Open the UTM app to confirm, then re-run 'task vm:reset'." >&2
313+ exit 1
314+ fi
289315
290316 echo "✓ VM cloned. Now starting it for initial configuration..."
291317 - task : vm:start
@@ -356,14 +382,20 @@ tasks:
356382 sleep 2
357383 open -g -a UTM || true
358384 # Wait until UTM is scriptable again before re-issuing the start.
385+ UTM_BACK=false
359386 for _ in $(seq 1 10); do
360387 if osascript -e 'with timeout of 5 seconds' \
361388 -e 'tell application "UTM" to get name of every virtual machine' \
362389 -e 'end timeout' >/dev/null 2>&1; then
390+ UTM_BACK=true
363391 break
364392 fi
365393 sleep 2
366394 done
395+ if [ "$UTM_BACK" != "true" ]; then
396+ echo "⚠️ UTM not scriptable ~20s after relaunch (possibly stuck on a permission prompt)."
397+ echo " Trying one final start anyway; if the VM stays stopped this run will abort next."
398+ fi
367399 else
368400 echo "VM still stopped; re-issuing start (try $START_ATTEMPTS/$MAX_START_ATTEMPTS)..."
369401 fi
0 commit comments