diff --git a/.claude/skills/factory-zoom/SKILL.md b/.claude/skills/factory-zoom/SKILL.md index 214deb1..dfab510 100644 --- a/.claude/skills/factory-zoom/SKILL.md +++ b/.claude/skills/factory-zoom/SKILL.md @@ -26,7 +26,8 @@ re-verified, then `factory-report`. Needs this Mac: `cliclick`, the Zoom client, `npm run cf:deploy:dev` again so dev is back to the branch's committed values. Set each spec's `verified_at` (sha tested), `verified_on`, `status: current` (or `stale` on a failure), update `INDEX.md`, `npm run test:specs`, commit - `test(zoom-client): verify on (#)`, push. + `test(zoom-client): verify on (#)`, push. Quit Zoom last + (`zoom-down.sh`), so the user can see the Mac is free. 7. **Unlock and route.** Remove `zoom:testing` and `needs:zoom-test`. - All ✅/⏭ → run `factory-report`. - Any ❌ → comment `**zoom** —` with the failing step IDs, what was seen, and the evidence, diff --git a/.claude/skills/zoom-app-testing/SKILL.md b/.claude/skills/zoom-app-testing/SKILL.md index 3daf921..87e10d6 100644 --- a/.claude/skills/zoom-app-testing/SKILL.md +++ b/.claude/skills/zoom-app-testing/SKILL.md @@ -64,7 +64,8 @@ launches Zoom, starts the test meeting (`ZOOM_TEST_PMI` from `meeting.local`, gi Zoom's Start Meeting shortcut) and waits for the meeting window. Then open Toastmasters Timer from Apps with the per-step loop below. Ask the user only when it exits 2 or a snap shows a Zoom sign-in screen or an app authorization prompt. Before the first click, say once in chat -that you're taking the Mac and wait 60 seconds. +that you're taking the Mac and wait 60 seconds. The run hands the Mac back by quitting Zoom +at the end (§6). Then start, each with `run_in_background: true`: - `$S/tail.sh` — Worker log into the run folder (auto-reconnects). @@ -140,8 +141,15 @@ edit, and restore from the backup when the step is done. At the end: mention that a stopped capture is intentional when its notification arrives. - Remind the user of anything still flipped (e.g. `ENTITLEMENT_ENFORCE: "1"` refuses sync for other dev testers) and ask before reverting/redeploying. +- **Quit Zoom last:** `$S/zoom-down.sh` ends the test meeting and quits the client. A closed + Zoom is the user's signal that the run is over and the Mac is theirs again; left open, they + can't tell whether to wait. Then say so in chat in one line ("Zoom test finished — Zoom is + closed, the Mac is yours."). Do this however the run ended (pass, fail, blocked), except + when it paused because the user took the Mac: leave Zoom as it is then, since the run + resumes. ## 7. Reporting back -Link `test-runs//run.md`. Lead with what failed and why, then findings, then what passed +Open with one line saying the run is over and Zoom is closed. Link `test-runs//run.md`. +Lead with what failed and why, then findings, then what passed in one line, then exactly what you need from the user next. Don't restate the whole log. diff --git a/.claude/skills/zoom-app-testing/scripts/zoom-down.sh b/.claude/skills/zoom-app-testing/scripts/zoom-down.sh new file mode 100755 index 0000000..fcf3876 --- /dev/null +++ b/.claude/skills/zoom-app-testing/scripts/zoom-down.sh @@ -0,0 +1,25 @@ +#!/bin/zsh +# zoom-down.sh — the last step of a run: end the test meeting and quit Zoom. +# A closed Zoom is the user's signal that the run is over and the Mac is theirs again; a Zoom +# left open makes them wait or guess. Run it after the summary is written and the helpers are +# stopped, then say in chat that Zoom is closed. +# Not for a run that paused because the user took the Mac: leave Zoom as it is then. +# Exit 0 = Zoom is not running, 1 = it would not quit. +HERE="${0:A:h}" # captured here: inside a function, zsh's $0 is the function's name +source "$HERE/_env.sh" +note() { [ -n "$RUN" ] && [ -d "$RUN" ] && "$HERE/log.sh" "$1"; echo "$1"; } +running() { pgrep -x zoom.us >/dev/null; } +gone_within() { for _ in {1..$1}; do running || return 0; sleep 1; done; ! running; } + +running || { note "ℹ️ Teardown — Zoom was not running."; exit 0; } + +# Zoom 7.2.5 ignored the Apple Event quit (2026-11-14) and SIGTERM closed it cleanly, ending +# the meeting. The meeting is the test PMI with nobody else in it, so nothing is lost. +osascript -e 'tell application "zoom.us" to quit' >/dev/null 2>&1 +gone_within 5 && { note "ℹ️ Teardown — Zoom quit."; exit 0; } +pkill -TERM -x zoom.us +gone_within 10 && { note "ℹ️ Teardown — Zoom quit (SIGTERM; meeting ended)."; exit 0; } +pkill -KILL -x zoom.us +gone_within 5 && { note "ℹ️ Teardown — Zoom quit (SIGKILL; meeting ended)."; exit 0; } +note "❌ Teardown — Zoom is still running; quit it by hand." +exit 1 diff --git a/CLAUDE.md b/CLAUDE.md index 8c2a9d4..0daa84e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,7 +60,9 @@ Do these without asking when they are part of finishing a change: factory labels. Before the first click in the Zoom client, say once in chat that you're taking the Mac and -wait 60 seconds (no wait if the user said they're away). If `click.sh` refuses because Zoom +wait 60 seconds (no wait if the user said they're away). When the run ends, quit Zoom +(`zoom-down.sh`) and say the Mac is free again: a closed Zoom is how the user knows the run +is over. If `click.sh` refuses because Zoom isn't frontmost, the user is on the Mac: stop and wait for them. Still ask before: merging, anything on `master` or production, deleting data, sending