verify
GitHub用于验证 BetterCmdTab 产品变更,通过构建应用并作为见证者捕获运行时证据,确认用户操作与预期结果的一致性。
Trigger Scenarios
Install
npx skills add rokartur/BetterCmdTab --skill verify -g -y
SKILL.md
Frontmatter
{
"name": "verify",
"description": "Witness BetterCmdTab product-source changes at the live macOS surface when a diff needs end-to-end runtime evidence."
}
Verify BetterCmdTab
A witness is a captured observation from the freshly built app after real user input crosses every changed seam. Build and test results are setup; the witness decides the verdict.
1. Bind the claim
Fix the scope as an explicit BASE..TARGET range or as the working tree.
Inventory committed, staged, unstaged, and untracked files with the matching
git diff --name-status commands plus
git ls-files --others --exclude-standard. Map every changed product file to:
- the user action that reaches it; and
- the observable outcome that should change or remain unchanged.
Treat tests as specifications for what to drive. A diff with no runtime surface
(docs, tests, or compile-time metadata only) is SKIP.
Complete when: the range is stated and every inventoried product file belongs to one runtime claim or has an explicit non-runtime reason.
2. Build and launch a witness process
Use the Debug lane by default. Use the Release lane for Liquid Glass/macOS 26,
release-configuration, or performance claims. Signing, notarization, and
updater installation are approval-gated release work outside this skill; return
BLOCKED and ask before invoking release tooling. An ordinary Xcode Release
build is not evidence for them.
VERIFY_ROOT="$PWD/build/verify"
SCHEME="BetterCmdTab Debug"
CONFIGURATION="Debug"
PRODUCT="BetterCmdTab Debug"
BUNDLE_ID="pro.bettercmdtab.BetterCmdTab.debug"
# Release lane:
# SCHEME="BetterCmdTab"; CONFIGURATION="Release"; PRODUCT="BetterCmdTab"
# BUNDLE_ID="pro.bettercmdtab.BetterCmdTab"
DERIVED_DATA="$VERIFY_ROOT/DerivedData-$CONFIGURATION"
mkdir -p "$VERIFY_ROOT"
if ! xcodebuild -project BetterCmdTab.xcodeproj \
-scheme "$SCHEME" -configuration "$CONFIGURATION" \
-derivedDataPath "$DERIVED_DATA" build \
>"$VERIFY_ROOT/build.log" 2>&1; then
tail -80 "$VERIFY_ROOT/build.log"
exit 1
fi
APP="$DERIVED_DATA/Build/Products/$CONFIGURATION/$PRODUCT.app"
EXEC="$APP/Contents/MacOS/$PRODUCT"
test -x "$EXEC"
Every BetterCmdTab process installs global handlers. Before launch, identify all
running BetterCmdTab processes. Gracefully clean up a recorded prior witness;
if an installed copy is running, ask before quitting it, record its executable
path and running state, and restore that exact copy afterward. A declined quit
makes runtime verification BLOCKED.
Launch the bundle executable directly with isolated preferences and config.
Keep its unique state for restarts and crash recovery. At the first launch of a
verification session, an existing state-path means the prior run needs
recovery. An intentional restart in the current session reuses
the recorded state and skips this initializer.
test ! -e "$VERIFY_ROOT/state-path" || {
echo "Recover the prior witness first: $VERIFY_ROOT/state-path" >&2
exit 1
}
STATE="$(mktemp -d "$VERIFY_ROOT/state.XXXXXX")"
printf '%s\n' "$STATE" >"$VERIFY_ROOT/state-path"
printf '%s\n' "$EXEC" >"$VERIFY_ROOT/executable-path"
printf '%s\n' "$BUNDLE_ID" >"$VERIFY_ROOT/bundle-id"
mkdir -p "$STATE/home" "$STATE/xdg"
CFFIXED_USER_HOME="$STATE/home" \
XDG_CONFIG_HOME="$STATE/xdg" \
"$EXEC" -GitHubUpdater.checkInterval manual \
>"$VERIFY_ROOT/app.stdout.log" 2>&1 &
echo $! >"$VERIFY_ROOT/app.pid"
Open Settings through the recorded process with:
osascript -e "tell application id \"$BUNDLE_ID\" to reopen"
The switcher becomes ready only after Accessibility trust. Panel appearance
after the real chord is the readiness check. If macOS prompts for Accessibility
or Automation, ask whether to grant it; the grant is persistent shared TCC
state and belongs in the report. Without approval, return BLOCKED; preserve
TCC as-is.
Complete when: the recorded PID resolves to EXEC and is the sole
BetterCmdTab process, the intended surface is drivable, verification state is
outside the user's defaults and XDG config, and any TCC change was approved.
3. Drive the changed seam
Reach the behavior through its real boundary: CGEvent/Carbon input, an AppKit control, a watched file, or an Accessibility window action. Calling an internal Swift function is a test, not a witness.
| Changed area | Drive | Witness |
|---|---|---|
App/, System/ |
Fresh launch, reopen, menu action, or relevant permission transition | Window/menu/lifecycle state and filtered unified-log lines |
Input/, Switcher/ |
Hold and send the actual configured chord with System Events; use a real password field for Secure Event Input and ask the user for a real trackpad gesture | Cropped panel capture plus the selected/cancelled app or window outcome |
Settings/, Preferences |
Reopen Settings, operate the control, restart with the same isolated state, then exercise its downstream behavior | Restored control state and the changed live behavior |
Catalog/, Windows/ |
Prepare named real apps/windows, open the switcher, then select or act on one | Visible rows and resulting frontmost app/window state |
ConfigFile, import/export |
Seed $STATE/xdg/bettercmdtab/config.json or use the real import/export UI |
File contents and the live setting/behavior after reload |
| updater/About | Use the manual UI action against a safe target | UI result and relevant unified-log lines |
| hot-path performance | Use separate clean base/target worktrees and SHA-specific Release build/state roots; drive identical seeded scenarios under Instruments with Log.reveal |
Equal sample counts and median/p95 latency, CPU, or allocation numbers |
For each independent claim, drive the claimed path and one adjacent probe:
cancel, repeat, stale state, empty input, rapid input, or the neighboring error
case. Inspect visual captures for clipping, stale/blank frames, focus, and
contrast. A changed macOS 13 fallback requires a real macOS 13 host/VM;
otherwise that claim is BLOCKED.
Destructive actions use a temporary target or dry run. Publishing, notarizing, installing an update, deleting user data, or changing shared system state requires approval before the action.
Complete when: every runtime claim has an observed expected outcome and its own adjacent probe has an observed outcome at the same surface.
4. Preserve evidence and restore the machine
Capture the smallest useful window/region with screencapture; read every image
before using it as evidence. Capture nonvisual outcomes from app state or
Unified Logging filtered to the witness process. Keep the relevant output
inline; a local path alone is not evidence for a remote reader.
Request a graceful AppKit quit for the exact recorded PID, then wait for it to disappear:
VERIFY_ROOT="$PWD/build/verify"
ROOT_REAL="$(realpath "$VERIFY_ROOT")"
test "$ROOT_REAL" = "$(realpath "$PWD")/build/verify"
PID="$(cat "$VERIFY_ROOT/app.pid")"
EXEC="$(realpath "$(cat "$VERIFY_ROOT/executable-path")")"
case "$EXEC" in "$ROOT_REAL"/*) ;; *) exit 1 ;; esac
swift -e 'import AppKit
import Darwin
import Foundation
func normalized(_ url: URL) -> String {
url.resolvingSymlinksInPath().standardizedFileURL.path
}
let pid = pid_t(CommandLine.arguments[1])!
let executable = URL(fileURLWithPath: CommandLine.arguments[2])
let bundle = executable.deletingLastPathComponent()
.deletingLastPathComponent().deletingLastPathComponent()
guard let app = NSRunningApplication(processIdentifier: pid),
let actualBundle = app.bundleURL,
normalized(actualBundle) == normalized(bundle) else {
fatalError("Recorded PID no longer belongs to the witness")
}
guard app.terminate() else { fatalError("AppKit termination request failed") }
let deadline = Date().addingTimeInterval(5)
while !app.isTerminated && Date() < deadline {
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
}
exit(app.isTerminated ? 0 : 1)
' "$PID" "$EXEC"
Signals bypass in-process symbolic-hotkey restoration. If AppKit quit fails or the process vanished abnormally, follow RECOVERY.md before removing state. After a clean quit, use the validated cleanup block in recovery step 3, then restore the approved installed app path.
Complete when: each reported step has reviewed evidence, the witness PID is gone after AppKit quit or recovery, state metadata is removed, native ⌘Tab works, and the installed app has its original running/stopped state.
5. Report the verdict
## Verification: <one-line claim>
**Verdict:** PASS | FAIL | BLOCKED | SKIP
**Build:** <scheme/configuration, ref, executable path>
**Claim:** <expected user-visible behavior>
### Claim 1: <expected behavior>
1. ✅ <real action> → <observation and evidence>
2. 🔍 <adjacent probe> → <observation and evidence>
<Repeat per independent claim.>
**Witness:** <attached screenshots or inline state/log excerpts>
**Findings:** <runtime surprises, friction, or “none”>
PASS: every claim was witnessed and the probe held.FAIL: the running app contradicted a claim, regressed adjacent behavior, or produced ambiguous evidence.BLOCKED: the surface could not be reached; include the exact build, permission, automation, or environment failure.SKIP: no runtime surface exists; give the reason in one line.
Complete when: the verdict follows these definitions, every claim from step 1 is accounted for, and the evidence reaches the reader.
Version History
- 7d5fa2e Current 2026-08-02 23:43


