LLMSkillHub
mirroredCopied from openwong2kim/wmux under MIT. openwong2kim has not claimed this page, and is not affiliated with LLM SkillHub.

openwong2kim / verify-wmux

Launch and drive the real wmux desktop app (Electron main window) to prove a change works the way a user sees it: start an isolated instance, check it with doctor, click/press by data-testid or ARIA label over CDP, capture ARIA snapshots and screenshots, then stop only what was launched. Use whenever you need to run, launch, screenshot or verify wmux UI behavior, reproduce a UI bug, or say a wmux feature works; read references/features/ for the per-feature recipes.

0
SKILL.md
12.7 KB

<!--
Generated by following .claude/skills/create-verification-skill (adapted from
pstack, https://github.com/cursor/plugins, MIT, Copyright (c) 2026 Lauren Tan,
and pstack-claude, https://github.com/anishekkamal/pstack-claude, MIT,
Copyright (c) 2026 Lauren Tan, Copyright (c) 2026 Michael Denyer). The proof
standards and verdict wording are adapted from pstack-claude
plugins/pstack/skills/verify-web/references/proof-standards.md. See THIRDPARTYNOTICES.md.
-->

verify-wmux

Status: proven on macOS against the installed /Applications/wmux.app 4.3.0: launch, doctor, drive (Settings first, then one sweep over all ten feature files), ARIA snapshots and CDP screenshots, stop --purge, evidence kept. Not yet run on Windows or Linux, nor against a dev build; the PC switcher was proven only in its absent state.

wmux is an Electron terminal multiplexer. The surface a user touches is the main window (sidebar, panes with xterm terminals, rail pages, settings, dialogs). Secondary surfaces (the wmux CLI, the MCP server, the phone web client) are not driven by this skill; prove those with a terminal transcript or the repo's own harnesses (e2e/, rig/).

Everything runs through one driver, scripts/control-wmux.mjs (Node 22+, uses the repo's playwright-core dependency; run npm ci once in a fresh checkout). Every command prints one JSON object and exits non-zero on failure. Read [`references/features/README.md`](references/features/README.md) before driving, then the feature file that matches the request.

Launch

node scripts/control-wmux.mjs launch                       # /Applications/wmux.app, suffix -verify-<random>
node scripts/control-wmux.mjs launch --app <path/to/wmux>  # any other packaged build's executable
node scripts/control-wmux.mjs launch --suffix -verify-mytest --timeout 90000

What it does: spawns the app executable directly (its PID is recorded) with WMUX_DATA_SUFFIX=<suffix>, which isolates the socket, MCP socket, auth token, TCP port file, data dir ~/.wmux<suffix>/, the daemon and Electron userData (src/shared/constants.ts dataSuffix(), src/main/index.ts appInit). The launching environment is scrubbed of WMUX_* pane identity and CLAUDE* session variables first, because an agent usually runs inside the owner's wmux. ELECTRON_ENABLE_LOGGING=1 sends the renderer console into app.log.

Ready means: the CDP port is found and the main window has rendered #root. wmux picks its own CDP port in 18800-18899 at startup (src/main/browser-session/cdpPort.ts claimCdpPort); a --remote-debugging-port flag is not honored. The driver finds the port from the claim file ~/.cache/wmux-cdp/wmux-cdp-<port>.lock whose content is the launched PID, and cross-checks the [WinMux] CDP requested on port N / CDP listening on port N log lines.

Refusals (by design): an empty suffix (the owner's real instance), -dev (every dev build's default), -portable (any packaged copy outside /Applications), a suffix whose socket or daemon is already live, and a second launch while the recorded instance is still running. Every other command reads .claude/skills/verify-wmux/.run/state.json and refuses without it, and re-checks before each attach that the CDP claim still names the launched PID. The driver can only ever touch an instance it started.

Doctor

node scripts/control-wmux.mjs doctor

Read-only. Reports healthy plus problems[]: app PID alive; app version vs checkout version (skew means the feature map's handles may not exist in the running build); CDP disabled (WMUX_DISABLE_CDP=true or ~/.wmux<suffix>/config.json browser.cdp.enabled:false, both stop all driving); CDP port claimed by our PID and answering /json/version; CDP listening logged (vs unverified); daemon PID; whether the first-run wizard or settings is open; window visibilityState/focus; renderer console error count. Run it before the first drive, after anything surprising, and after every failed drive. A failing doctor means fix or relaunch, not retry the drive. Output is also saved as doctor.json in the evidence dir.

Drive

node scripts/control-wmux.mjs click --testid first-run-wizard-skip
node scripts/control-wmux.mjs click --label Fleet                  # aria-label, exact
node scripts/control-wmux.mjs click --role menuitem --name "Settings ⌘,"
node scripts/control-wmux.mjs click --text "Skip for now" [--nth 0]
node scripts/control-wmux.mjs press --key Meta+Comma                # Playwright key syntax
node scripts/control-wmux.mjs press --key Enter --label "Terminal input"   # focus target first
node scripts/control-wmux.mjs type --text 'echo hi' --label "Terminal input" # keyboard.type into the target (appends)
node scripts/control-wmux.mjs hover --selector '[data-sidebar-row]' --nth 0    # reveal hover-only controls
node scripts/control-wmux.mjs wait-settle --testid settings-page-title [--state visible|hidden] [--timeout 10000]
node scripts/control-wmux.mjs eval --js "document.querySelector('[data-testid=settings-page-title]')?.textContent"
node scripts/control-wmux.mjs inspect --js "..."                    # alias of eval
  • Targets, best first: --testid (data-testid), --label (aria-label), --role + --name, --text, --selector (CSS, last resort; never a generated class). Never screen coordinates.
  • A target matching several elements fails and says how many; scope it or pass --nth.
  • A click blocked by a modal fails with the element that intercepts pointer events (for example onboarding-backdrop); close that dialog through its own control.
  • wait-settle waits for the target state, then for 400 ms of DOM quiet (MutationObserver, which keeps working when a hidden or locked window pauses rAF). Use it after every action instead of sleeps.
  • eval/inspect is for read-only inspection (a value, a count, a store read). Never use it to put the app into the state you were asked to reach; that skips the code the user runs.

Evidence

node scripts/control-wmux.mjs snapshot [--testid ID | --selector CSS | --role R --name N] --as settings-general
node scripts/control-wmux.mjs screenshot --as settings-general
node scripts/control-wmux.mjs console [--errors] [--tail 50]

Artifacts go to .claude/skills/verify-wmux/evidence/<run-id>/ (git-ignored): numbered NNN-<as>.aria.yml (accessibility tree with the instance suffix and window title in the header) and NNN-<as>.png, plus app.log (main + renderer console), doctor.json and timeline.jsonl (every command and outcome). Screenshots use CDP Page.captureScreenshot; when the screen is locked or the window occluded it may time out (screenshot: unavailable) or come back near-empty (suspectBlank: true): then the ARIA snapshot plus an eval read-back is the evidence, and say so.

Proof standards:

  1. Drive the real user path (button, shortcut, menu). Internal setters, test-only hooks and eval that writes state do not count. Arranging a precondition is fine; injecting the symptom is not.
  2. Capture the action and the resulting state: a snapshot or screenshot right after the action, of the region that changed. A final screenshot alone does not show causation.
  3. Confirm side effects from a second, read-only view: reopen the panel, read the value back with eval, read the file under ~/.wmux<suffix>/.
  4. Keep identity visible: the snapshot header carries the suffix; doctor output records app, version and port.
  5. Every mapped entry point, or an explicit skip: if a feature has a button and a shortcut, proving one does not prove the other. Report skipped: <entry>, reason: <why>.
  6. Report exactly one verdict with the claim restated so it can fail:
VERIFIED | NOT VERIFIED | INCONCLUSIVE
Claim: <falsifiable claim>
Evidence: <artifact paths, read-back values>
Skipped: <entry points not driven, with reason> (or none)
Confidence: high | medium | low, and why

A clean NOT VERIFIED is a useful result. Do not upgrade INCONCLUSIVE because the code looks right.

Cleanup

node scripts/control-wmux.mjs stop          # app + its daemon; data dir kept for inspection
node scripts/control-wmux.mjs stop --purge  # also delete the suffixed data this run created

stop SIGTERMs the launched app PID (SIGKILL after 10 s), then stops the daemon that instance spawned (a normal quit only detaches from it): first wmux daemon stop over the instance's own suffixed socket, then, only if still alive, SIGTERM to the PID in ~/.wmux<suffix>/daemon.pid. It boots out finished launchd jobs labelled com.wmux.daemon<suffix>.* (macOS). --purge removes only suffixed paths that did not exist before launch. Never pkill, killall, or kill by name. Evidence is never removed; stop lists what survived. Run stop after every attempt, failed ones included; the state moves to .run/stopped-<run-id>.json.

Record the verdict (verifying a PR)

When the claim is a pull request, end with the verdict line above in your report. A pane has no tool that records a verdict: the agent factory's verifier bot records its own (pr_verdict in bot mode), and the factory's merge lane merges a PR only on a pass at exactly the head being merged.

Helpers

CommandPurpose
launchstart an isolated instance, wait for CDP + rendered root
doctorread-only health and identity check
snapshotARIA tree of body or one target, saved to evidence
screenshotCDP PNG of the main window, saved to evidence
click / hover / press / typeact by handle / key / characters
eval (inspect)read-only JS expression in the main window
wait-settlewait for a target state and DOM quiet
consolerenderer console lines from app.log
stoptear down what launch started

Pure rules (suffix refusal, CDP claim parsing, env scrub, console parsing) live in scripts/lib/control-wmux-lib.mjs and are tested by scripts/__tests__/controlWmux.test.mjs.

Gotchas

  • A fresh suffix is a fresh profile: the first-run wizard ("Welcome to wmux") covers the window, an onboarding tour ("Every agent, one board", Step 1 / 4) can follow, and a "Keyboard shortcuts" card appears. Reset to the baseline in [`references/features/README.md`](references/features/README.md) before driving.
  • Isolation covers wmux's own paths only. Things outside them are shared with the owner: ~/.claude/settings.json, ~/.claude.json, ~/.wmux/hooks/ (Claude Code integration), the ~/.local/bin/wmux CLI shim, Keychain, the network. The feature files list which actions write there; do not drive them.
  • A packaged build outside /Applications (for example out/ from npm run package) launched with our suffix still runs the macOS CLI-shim repair about 3 s after boot and may repoint ~/.local/bin/wmux at itself (src/main/index.ts, PORTABLE_DATA_SUFFIX guard). Prefer /Applications/wmux.app; if you must use a copy, check the shim afterwards.
  • Version skew: handles come from this checkout's src/renderer; the installed app may be older. Doctor reports it. A missing handle under skew is not drift.
  • macOS "wmux is not responding"/memory dialogs or a locked screen can stall the window; CDP DOM reads keep working, screenshots may not.
  • The quick-launch window (launcher.html) is a second page target; the driver always picks index.html.

Supersedes (follow-up, not done here)

These existing scripts drive the UI over CDP against whatever wmux is already running (several scan the 18800-18899 range and attach to the first answer, which can be the owner's instance; some write evidence to hard-coded Windows paths), and each re-implements attach and evidence. Their scenarios should move into feature files driven by control-wmux; they are left untouched for now:

  • scripts/channel-dock-dogfood.mjs, scripts/channel-members-dogfood.mjs, scripts/channels-company-dogfood.mjs
  • scripts/s-c1-fleet-dogfood.mjs, scripts/s-c2-fleet-deepening-dogfood.mjs
  • scripts/x3-dogfood-main.mjs, scripts/x3-dogfood-linkclick.mjs, scripts/x3-dogfood-restore.mjs, scripts/x3-dogfood-cleanup.mjs
  • scripts/pane-stacking-dogfood.mjs, scripts/v281-gui-probe.mjs, scripts/homescreen-launch-probe.mjs
  • scripts/a2a-state-query-dogfood.mjs, scripts/b1-plugin-host-dogfood.mjs, scripts/lanlink-pr5-cdp-dogfood.mjs, scripts/managed-chat-fault-probe.mjs, scripts/phase3-retention-probe.mjs

Keep the map honest with /maintain-verification-skill.