[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C34KRE0FVYVQXENX3RACR2":3,"skill-same:01M6C34KRE0FVYVQXENX3RACR2":149},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":51,"repositoryHtmlUrl":52,"repositoryHomepageUrl":6,"ownerHtmlUrl":53,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":54,"install":55,"resources":68},"01M6C34KRE0FVYVQXENX3RACR2","cmux",null,"Control cmux terminal multiplexer via its Unix socket API. Use when needing to: (1) List, create, select, or close workspaces; (2) Split panes and manage surfaces; (3) Send text or key presses to terminals; (4) Read terminal screen text from a surface; (5) Create notifications; (6) Set sidebar status, progress bars, or log entries; (7) Query system state. Requires cmux CLI on PATH or Unix socket at \u002Ftmp\u002Fcmux.sock (overridable via CMUX_SOCKET_PATH).","jleechanorg\u002Fjleechan-skills","jleechanorg","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F221404966?v=4","01M6C1VJ99XG950X1DWPEBCBGX","01M6C1VJDK5YVDNT3362RT5WSB",6,"MIT","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002FSKILL.md",[],[5,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"list","send","at","socket","split","multiplexer","close","screen","manage","workspaces","log","use","cli","read","select","panes","text","system","api","SET","notifications","state","create","status","terminal","2026-11-10 10:12:30",false,"---\nname: cmux\ndescription: \"Control cmux terminal multiplexer via its Unix socket API. Use when needing to: (1) List, create, select, or close workspaces; (2) Split panes and manage surfaces; (3) Send text or key presses to terminals; (4) Read terminal screen text from a surface; (5) Create notifications; (6) Set sidebar status, progress bars, or log entries; (7) Query system state. Requires cmux CLI on PATH or Unix socket at \u002Ftmp\u002Fcmux.sock (overridable via CMUX_SOCKET_PATH).\"\n---\n\n## ⚠️ Submit Discipline (MANDATORY — read this before every cmux steer)\n\n`cmux send` does **NOT** press Enter. This is the #1 recurring cmux failure mode\n(verified 2026-07-16: user explicitly flagged \"you always forget to send\" after the\nfable iOS pivot bootstrap). The **4-step ritual** below is a hard contract for every\nsend to a cmux surface. Skip ANY step and the message sits in the input buffer\nwithout ever reaching the agent.\n\n### ⚠️ Output Contract — typed text + terminal response (MANDATORY)\n\nEvery reply that reports a `cmux send` action MUST include, in the same reply:\n\n1. **The exact text that was typed** — verbatim copy of the string passed to `cmux send`.\n2. **The cmux terminal response** — verbatim transcript of what `cmux capture-pane` \u002F\n   `cmux read-screen` returned AFTER the `cmux send-key enter` settle window\n   (typically 5-15s). Specifically, the agent's first action after absorption.\n3. **Submission status** — explicit verdict: \"submitted (churning label X)\",\n   \"not submitted (text still at ❯ prompt)\", or \"blocked (no churn, retried N times)\".\n\n**Treat as not working until we see a response.** A reply that does NOT include\nboth the typed text AND a terminal response is invalid evidence that the\nsteer landed. The operator cannot distinguish a successful send from a failed\nsend that left text in the input buffer.\n\nCanonical contract + echo-back template: `references\u002Foutput-contract-mandatory.md`.\n\n### ⚠️ LLM-Provenance Caveat (MANDATORY footer)\n\nEvery reply that quotes cmux output, terminal text, or agent actions produced\nby another LLM (the worker agent OR the assistant's own synthesis of agent\noutput) MUST end with this verbatim footer:\n\n> *This was generated from another LLM and not the actual user, so feel free\n> to push back if you disagree and we can discuss.*\n\nFull caveat rules + scope (when to apply, when not to apply):\n`references\u002Foutput-contract-mandatory.md` § \"LLM-Provenance Caveat\".\n\n### The 4-step ritual\n\n```bash\n# STEP 1 — Type the text. OK response only proves socket acceptance, NOT submission.\ncmux send --workspace workspace:N --surface surface:M \"your message\"\n\n# STEP 2 — Press Enter. send does NOT auto-press Enter.\ncmux send-key --workspace workspace:N --surface surface:M enter\n\n# STEP 3 — Wait 5-15 seconds for the agent to start processing.\nsleep 8\n\n# STEP 4 — Verify with churning label (THE ONLY definitive proof).\ncmux capture-pane --workspace workspace:N --surface surface:M --lines 25\n# Look for one of:\n#   - \"Working (Xs • esc to interrupt)\"\n#   - \"Forming… (Xs · thinking)\"\n#   - \"Precipitating… (Xs · ↓ tokens)\"\n#   - \"Brewed \u002F Churned \u002F Cooked for Xm\"\n# If you see ANY active churning label → SUBMITTED.\n# If the text is still sitting at the ❯ prompt → NOT submitted, repeat step 2.\n# If \"Stopped\" \u002F \"Done\" \u002F nothing → no churn, investigate.\n```\n\n### Echo-back proof (MANDATORY)\n\nEvery cmux steering action MUST be followed by an **echo-back proof** in the same\nturn or the immediate next turn to your operator (Slack thread, terminal reply,\nor whichever channel triggered the steer):\n\n> ◀ sent to surface:55 (LEFT\u002Fclaudec) at \u003CHH:MM:SS PT> — 4-step ritual complete;\n> churning label \"Forming… 9s · ↓ 4.9k tokens\" confirmed via capture-pane.\n\n**Banned** (these are the failure modes the user keeps flagging):\n- \"I sent the message\" (no Enter proof)\n- \"The agent should have received it\" (no churning label)\n- `cmux send` with no follow-up `cmux send-key enter`\n- Sending to a surface that hasn't been focused (the global focus may be on a\n  different workspace; use the raw RPC `surface.focus` if needed)\n\n### Worktree-pointer strategy for long briefs\n\nFor task briefs >200 chars (e.g. orchestrating iOS app pivot, multi-PR review),\ndo NOT paste the full text into the input. Write the brief to a file in the\nagent's cwd (e.g. `.cmux-\u003Ctask>-brief.md`) and send a 1-2 line pointer. This\navoids the autocompleter contamination pitfall where shell-style tokens inside\nlong text trigger tab completion mid-stream.\n\n### Canonical reference\n\nFull recipe + edge cases + the 2026-06-25 worked example live at:\n`~\u002F.hermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsend-submit-proof-2026-06-25.md`\n\nThis rule was added 2026-07-16 after the fable iOS pivot bootstrap surfaced\n\"you always forget to send\" \u002F \"make sure you press submit and the work starts\non the cmux input\" (Slack ts 1784185650.528089). Apply it uniformly to every\ncmux-touching skill.\n\n# cmux\n\nControl cmux terminal multiplexer programmatically via its Unix socket API or CLI.\n\n**Skill last updated:** 2026-06-26 — added two new launchd-scripting pitfalls: (1) `--socket` is a GLOBAL flag (must come right after `cmux`, not after the subcommand — putting it after `cmux tree` errors with `tree: unknown flag --socket`), and (2) `CMUX_SOCKET_PATH` env var is silently IGNORED by the cmux CLI in launchd-style env without an interactive shell context (CLI falls back to baked-in stale default `\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock`). Verified by fixing `scripts\u002Fcmux-surface-report-4h.sh` false-positive \"RPC wedged\" skip alert at 2026-06-26T00:03:51Z. See new \"Sub-pattern: cmux CLI flag-order gotchas in launchd scripts\" below.\nFor the canonical command list, see `references\u002Fcli-cheatsheet.md` and the upstream contract at\n\u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fdocs\u002Fcli-contract.md>. When in doubt, run\n`cmux docs api` or `cmux \u003Ccommand> --help` to confirm.\n\n## ⚠️ Visual Screenshot of cmux Window — macOS TCC Blocker\n\n**The cmux window is not capturable by default.** cmux is built on Electron and marks its window as `kCGWindowSharingState=0` (non-shareable). `screencapture -l \u003Cwid>` returns \"could not create image from window\", and `SCScreenshotManager.captureImage` (ScreenCaptureKit) returns `Error -3801 \"The user declined TCCs for application, window, display capture\"` because the calling process does not have Screen Recording permission.\n\n**When the user asks for \"a screenshot of the message received\" or \"show me the cmux window\":** try cmux's own APIs FIRST (see \"When the user asks for visual proof\" sub-pattern below) before falling through to macOS screencapture. If those fail, the substitute proof is `cmux capture-pane --scrollback --lines N`, which reads the terminal text via the cmux socket API and bypasses macOS TCC entirely.\n\n**The \"Terminal\" in \"Screen Recording → Terminal\" is the WRONG fix for hermes-gateway-driven captures.** macOS TCC grants Screen Recording to a specific process, not to a logical concept like \"the terminal\". If the capture call originates from a script running under `hermes gateway` (e.g. Python 3.14 from Homebrew), the **caller** is `Python.framework\u002FVersions\u002F3.14\u002F...\u002FPython.app` — granting permission to Terminal.app or iTerm.app will NOT let hermes-gateway-driven captures work. The fix is: System Settings → Privacy & Security → Screen Recording → add the path to the binary that actually calls `screencapture` \u002F `peekaboo image`. For hermes-driven sessions, that's `\u002Fopt\u002Fhomebrew\u002FCellar\u002Fpython@3.14\u002F3.14.4\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.14\u002FResources\u002FPython.app` (or whatever Python your hermes gateway runs under). After granting, **relaunch the caller process** — TCC grants do not apply to already-running processes.\n\n**Pitfall — `tccutil reset ScreenCapture` alone is NOT enough.** `tccutil reset ScreenCapture` clears the existing grant, but the next call from a process that already tried to capture will still fail (the running process already saw the denial; the reset only affects the NEXT registration). To get the new permission to take effect, the calling process must be relaunched. For hermes gateway, that means killing + relaunching the session, which kills the agent that's trying to capture. **Plan accordingly**: deliver the text-based substitute proof, and offer the user a one-time manual fix that takes effect on the NEXT capture.\n\n**Diagnosis recipe (run in this order)**:\n```bash\n# 1. Find the cmux window id + sharing state — try swift first\nswift -e 'import Cocoa; let wl = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID) as? [[String:Any]] ?? []; for w in wl { if (w[\"kCGWindowOwnerName\"] as? String ?? \"\").lowercased().contains(\"cmux\") { let s = w[\"kCGWindowSharingState\"] as? Int ?? -1; print(\"wid=\\(w[\"kCGWindowNumber\"] ?? 0) sharing=\\(s) bounds=\\(w[\"kCGWindowBounds\"] ?? [:])\") } }'\n# Expect: sharing=0 (non-shareable) for the main content window\n\n# 2. Try screencapture\nscreencapture -x -l \u003Cwid> \u002Ftmp\u002Ftest.png\n# Expect: \"could not create image from window\"\n\n# 3. Fall back to cmux capture-pane (always works, no TCC)\ncmux capture-pane --workspace \u003Cw> --surface \u003Cs> --scrollback --lines 30\n```\n\n**No-swift fallback (added 2026-06-23):** When the parent shell has no swift (e.g. a non-dev hermes process), use the ctypes + CoreGraphics + Foundation + CoreFoundation recipe to enumerate windows. See `references\u002Fmacos-window-enumeration-ctypes.md` for the full Python recipe and the AppleScript `count`-on-CFArray pitfall that rules out osascript.\n\n**Reply shape when TCC blocks screencapture**:\n1. State the blocker explicitly (don't hand-wave — name the TCC error AND identify the calling process)\n2. Show the captured `cmux capture-pane` output as proof substitute\n3. Offer the one-time manual fix: name the **caller's** binary path (not \"Terminal\"), say to grant Screen Recording to that, then relaunch\n4. Do NOT claim \"screenshot delivered\"\n\n**Bug-ref:** 2026-06-19 — `workspace:16 surface:23 \"mobile load\"` (mobile slow-load investigation). User asked \"press send and show me screenshot of msg received.\" After 4 capture attempts (screencapture -l, -R with bounds, -D with display id, ScreenCaptureKit via swift), confirmed `kCGWindowSharingState=0` for the cmux window. Used `cmux capture-pane --scrollback` as the substitute proof and the user accepted the result. The agent was successfully driven and produced a full root-cause analysis (40.8s slow-load traced to autoscaling queue behind LLM turns pegging 4 vCPUs, fix: lower dev `containerConcurrency` 40 → 8-10 and `minScale` 1 → 2-3).\n\n**Bug-ref:** 2026-06-23 — PR #7848 fastembed. User asked \"cmux should already have visual capture, did you try it?\" I went straight to `screencapture -x` (4 failed attempts including region capture, window-list, and screencapture -l). The right move was to start with `cmux --help | grep -iE \"screenshot|capture|image\"` — cmux DOES have `cmux browser screenshot`, but it's Chromium-only (no terminal surface). Knowing that up front would have saved 4 failed capture attempts. The \"When the user asks for visual proof\" sub-pattern below codifies this.\n\n## Sub-pattern: \"User asks for visual proof of cmux (screenshot of message received \u002F show me the cmux window)\"\n\nThe user wants to SEE what's happening in the cmux window — typically a `❯` prompt, a churn\nspinner, or an error message. The temptation is to jump straight to `screencapture`, but\nthe **right first move is to enumerate cmux's own visual-capture APIs**:\n\n```bash\n# 1. Discover what cmux itself offers (verified on 2026-06-23)\ncmux --help 2>&1 | grep -iE \"screenshot|capture|image|visual|frame|render\"\n# Known cmux visual subcommands in 2026-06-23:\n#   - `browser screenshot [--out \u003Cpath>]`  ← Chromium browser surfaces ONLY\n#   - `feedback --image \u003Cpath>`            ← attach an image to a feedback report, NOT capture\n#   - `capture-pane ...`                   ← text only (the always-works substitute)\n#   - NO `cmux screenshot` for terminal surfaces\n```\n\n**If `cmux browser screenshot` exists, check the target surface type BEFORE calling.** Surfaces\nhave types: terminal vs browser. To enumerate:\n\n```bash\ncmux list-pane-surfaces --workspace \u003Cw> --pane \u003Cp> 2>&1\n# or via tree\ncmux tree --all --window \u003Cwin> 2>&1 | grep -B1 -A2 \"surface:\"\n# Look for type: terminal | browser annotations in the output\n```\n\nIf the target surface is **browser** (Chromium), `cmux browser screenshot --out \u002Ftmp\u002Fcmux.png`\nwill work — no TCC blocker because the Chromium renderer is in-process. If the target surface\nis **terminal** (Ghostty-backed), `browser screenshot` will error with \"not a browser surface\"\nor similar. **Fall through to `screencapture` only after confirming surface type.**\n\n**If `cmux` has no terminal-screenshot API (the 2026-06-23 reality):** the substitute proof is\n`cmux capture-pane --scrollback --lines N`. State the TCC blocker explicitly and use the\ntext capture as proof. **Do not** offer to \"try screencapture\" again — the window is\nnon-shareable (`kCGWindowSharingState=0`) and screencapture will fail.\n\n**Bug-ref: 2026-06-23 — PR #7848 fastembed steering.** User asked \"cmux should already have\nvisual capture, did you try it?\" I went straight to `screencapture -x -t png` (4 failed\nattempts) instead of starting with `cmux --help | grep -iE \"screenshot\"`. The right answer\ncame in one grep: cmux has `browser screenshot` (Chromium only) and `capture-pane` (text only)\n— no terminal-surface PNG API. Knowing that up front would have saved 4 capture attempts\nplus the diagnostic overhead of ctypes + AppleScript + osascript probing.\n\n## Sub-pattern: \"Idle-agent prompt contamination (autocompleter poisons pastes)\"\n\nWhen the worker is sitting at an empty `❯ ` prompt (idle, done with previous task), Claude\nCode's ghost-text autocompleter can fire on the prompt and inject text into the buffer\nwhen you paste a long message via `cmux send`. Symptom: the prompt at idle `❯ ` is empty,\nyou `cmux send` your message, but `read-screen` shows the buffer has both your text AND\ntrailing garbage like `also i always need intent classifier started up` (the autocompleter's\nbest-guess continuation of your paste). The whole contaminated buffer submits as one message\nand the agent parses your text + the garbage together.\n\n**Defense (apply in order):**\n\n1. **Keep idle-agent pastes SHORT (1-2 lines).** The 2026-06-12 coordination-notes pitfall\n   applies here too: shorter pastes are less likely to trigger the autocompleter's\n   \"complete this for you\" reflex. Split the task across multiple short sends, or use a\n   pointer to a shared artifact (PR comment, bead, Slack thread) where the agent can re-read\n   the long form.\n2. **Before sending, read-screen and confirm the prompt is empty.** If there's already\n   `❯ something`, send `ctrl+u` (multiple times) until read-screen shows empty `❯ `.\n3. **After sending, read-screen IMMEDIATELY (don't wait 5-10s).** If the visible buffer\n   text is longer than what you sent, abort the submission with `ctrl+c` + clear with\n   backspaces, then re-send a shorter version.\n4. **If the contaminated message already submitted,** the agent may parse it OR burn context\n   trying — in either case, post a follow-up clarification in the surface to anchor the\n   agent on the REAL intent and not the autocompleter's hallucination. The Opus session in\n   the 2026-06-23 case did parse past the garbage correctly (\"always init the classifier,\n   gate only the blocking wait\") but burned ~12k tokens on the corrupted payload.\n\n**Bug-ref: 2026-06-23 — PR #7848 fastembed revert.** I sent an 816-char operator note into\n`workspace:28 surface:60` (idle Opus session). The autocompleter appended\n\"also i always need invetn clasisifer runs always need intent classifier started up\" to the\nbuffer. The Opus read past the garbage and inferred the right intent, but burned ~12k tokens\non a corrupted payload AND the prompt was contaminated for follow-up steering. The lesson:\nwhen the user says \"steer this agent to fix X\", the message length budget is 1-2 lines even\nwhen the task description is 5 lines — split the task across multiple short sends, or use a\npointer to a shared artifact.\n\n## ⚠️ Preflight Validation\n\n**The current cmux CLI does NOT support `--json` as a global flag, and `list-surfaces` is NOT a\ncommand.** These were true in older cmux versions and may still appear in older skills \u002F muscle\nmemory. Always prefer `cmux \u003Ccommand> --help` before assuming a flag exists.\n\n**`$CMUX_SOCKET_PATH` default (`\u002Ftmp\u002Fcmux.sock`) is a placeholder, not a real path.** The real\ncmux socket is at a build-date-suffixed path (e.g. `\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock`). The\n`cmux` CLI hides this, but any raw-socket code (`nc -U`, Python `socket.AF_UNIX`, etc.) will\nsilently fail.\n\n**⚠️ `cmux identify` is NOT enough to find the workspace you need.** It returns the canonical\nsocket for the currently-running production cmux app, which on this machine has only 2\nworkspaces (both idle bash logins). All the actual agent workspaces live in the **dev build\nsocket** (`\u002Ftmp\u002Fcmux-debug-\u003Cbuild-tag>.sock`). For full workspace enumeration across ALL\nrunning cmux sessions (prod + dev + any future builds), use the multi-app probe recipe in\n`references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md` — this is the FIRST move when the\nuser names a specific workspace, NOT a fallback. See the new sub-pattern below for the\n2026-06-25 bug-ref.\n\n### Sub-pattern: `cmux` CLI itself fails with `Socket not found at \u002Ftmp\u002Fcmux-debug-\u003CX>.sock`\n\nThe CLI itself can fail this way even when the user has NOT set `CMUX_SOCKET_PATH` — verified\n2026-06-24: `cmux identify` returned\n`Error: Socket not found at \u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock` while a real socket existed at\n`\u002Ftmp\u002Fcmux-debug-may-18.sock`. Cause: a stale socket path is baked into the CLI's launchd-supplied\ndefault (different build's suffix). The CLI is not falling back to discovery.\n\n**Workaround** — bypass the CLI's hardcoded default by passing `--socket` (verified working):\n\n```bash\nls -1 \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock 2>\u002Fdev\u002Fnull\n# Find the real one, e.g. \u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\n\ncmux --socket \u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock identify\n# Or set the env var to the resolved path:\nCMUX_SOCKET_PATH=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock cmux list-workspaces --id-format both\n```\n\nIf `--socket` is also rejected, fall back to raw JSON-RPC over the resolved socket:\n```bash\nSOCKET=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\nprintf '{\"id\":\"q\",\"method\":\"workspace.list\",\"params\":{}}\\n' | nc -U -w 3 \"$SOCKET\"\n```\n\nThis is a different failure mode from the \"your env var is wrong\" case in\n`references\u002Fsocket-discovery-2026-06-21.md` — that one covers raw-socket callers using the\nplaceholder default; this one covers the CLI itself shipping with a stale default.\n\n### ⚠️ Sub-pattern: `cmux identify` succeeds but the workspace you need is NOT there\n\n**This is the trap that wastes the most turns. Read this FIRST when the user names a\nspecific workspace (e.g. \"find the agy workspace\", \"where is workspace:72\").**\n\n`cmux identify` returns the **canonical** socket for the currently-running cmux app —\nwhich on macOS is typically the *production* `\u002FApplications\u002Fcmux.app` instance using\n`$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock`. That instance often has only **2 workspaces**\n(both idle bash logins in `~`). All the actual agent workspaces (`workspace:69 hermes`,\n`workspace:72 w: misc - agy`, `workspace:70 daily jobs + deploy`, etc.) live in the\n**dev build** socket at `\u002Ftmp\u002Fcmux-debug-\u003Cbuild-tag>.sock` (e.g. `\u002Ftmp\u002Fcmux-debug-may-18.sock`).\n\n**`cmux identify` is authoritative for \"where is the focused surface in the canonical\nsocket\" — NOT for \"where are the agent workspaces\".** Treating it as the latter leads\nto a long sequence of `cmux list-workspaces` calls that return 2 workspaces, then\nconcluding \"the workspace the user named doesn't exist in this cmux session.\"\n\n**The right first move — always run the multi-app probe before concluding a workspace\ndoesn't exist.** Even for ONE-shot interactive scripts:\n\n```bash\n# Run the multi-app probe from references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md\n# to find ALL live cmux sessions, not just the canonical one:\ndeclare -a CANDIDATES=()\nwhile IFS= read -r s; do\n  CANDIDATES+=(\"$s\")\ndone \u003C \u003C(ls -1t \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock \\\n              ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock 2>\u002Fdev\u002Fnull | awk '!seen[$0]++')\n\n# Probe each. Pick the first one that returns valid JSON from `cmux identify`.\n# Then run `cmux --socket \u003Cpath> list-workspaces --id-format both` to see ALL\n# workspaces across that session.\nfor cand in \"${CANDIDATES[@]}\"; do\n  [ -S \"$cand\" ] || continue\n  probe=$(timeout 5 cmux --socket \"$cand\" identify 2>\u002Fdev\u002Fnull || true)\n  if [ -n \"$probe\" ]; then\n    echo \"=== $cand ===\"\n    cmux --socket \"$cand\" list-workspaces --id-format both 2>&1 | head -5\n  fi\ndone\n```\n\nIf you find a workspace in the dev socket that you didn't see in the canonical\nsocket, that's the dev session your work belongs in. Use `--socket \u003Cdev-path>` for\nall subsequent `cmux` calls.\n\n**Bug-ref 2026-06-25 — AGY persona-bleed probe sequence.** User asked: \"find the cmux\nworkspace for agy using agy locally for inference.\" First probe used `cmux.sock`\n(canonical) — got 2 workspaces, neither named agy. Concluded \"the AGY workspace\ndoesn't exist in this cmux session\" and burned 2-3 turns on the wrong track.\nUser pushed back: *\"find the socket again, it should be working.\"* Re-ran the\nmulti-app probe and found the AGY workspace at `workspace:72 \"w: misc - agy\"` in\nthe dev socket `\u002Ftmp\u002Fcmux-debug-may-18.sock` (along with all 28 agent workspaces).\nLesson: when the user names a specific workspace, the multi-app probe is the FIRST\nmove, not a fallback for when `cmux identify` returns clean JSON.\n\n### Sub-pattern: cmux CLI flag-order gotchas in launchd scripts (verified 2026-06-26)\n\nTwo non-obvious footguns hit when scripting cmux from `launchd` jobs (no interactive\nshell context):\n\n**1. `--socket` is a GLOBAL flag, not a subcommand flag.**\n\n```bash\n# WRONG — errors with \"tree: unknown flag '--socket'. Known flags: --all --workspace --window --json\"\ncmux tree --window window:1 --socket \u002Ftmp\u002Fcmux-debug-may-18.sock\n\n# CORRECT — global flag, comes right after `cmux`\ncmux --socket \u002Ftmp\u002Fcmux-debug-may-18.sock tree --window window:1\n```\n\nVerified 2026-06-26 with `cmux 0.64.16`. If you put `--socket` after the subcommand,\nthe subcommand's own flag parser rejects it AND the call silently returns empty,\nwhich a defensive script will treat as \"no data\" rather than \"bad invocation.\" Always\nput `--socket` right after `cmux`.\n\n**2. `CMUX_SOCKET_PATH` env var is silently IGNORED by the CLI in launchd-style env.**\n\n```bash\n# WRONG — in launchd-style env, the CLI ignores CMUX_SOCKET_PATH and falls back to\n# its baked-in stale default (\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock).\n# cmux tree --window window:1 → empty (no error, just empty)\nenv CMUX_SOCKET_PATH=\u002Ftmp\u002Fcmux-debug-may-18.sock cmux tree --window window:1\n\n# CORRECT — use --socket flag explicitly\ncmux --socket \u002Ftmp\u002Fcmux-debug-may-18.sock tree --window window:1\n```\n\nWhy `CMUX_SOCKET_PATH` doesn't work in launchd: the CLI's resolution algorithm\nappears to be (a) read `\u002Ftmp\u002Fcmux-last-socket-path` if it exists, (b) else fall back\nto the baked-in build default. In a launchd tick, that pointer file may be stale\n(pointing at the prod socket with 2 idle bash workspaces) or missing entirely.\nSetting `CMUX_SOCKET_PATH` in the env does NOT override the pointer file lookup —\nverified by setting it explicitly in `env -i PATH=... HOME=...` and still getting\nthe stale-default error. **For any launchd\u002Fcron script: use `--socket \u003Cpath>`, never\n`CMUX_SOCKET_PATH`.**\n\n**Bug-ref 2026-06-26 — cmux-surface-report-4h.sh false-positive skip alert.**\nCron tick at `2026-06-26T00:03:51Z` fired `:warning: cmux RPC wedged (tree --window\nAND --all both empty)` to `#home`. Two causes:\n- `\u002Ftmp\u002Fcmux-last-socket-path` pointed at `$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock`\n  (prod socket, 2 workspaces) while all agent work lives on the dev socket.\n- The script used `env CMUX_SOCKET_PATH=$SOCK cmux tree --window window:1`, which\n  in launchd-style env falls back to the baked-in stale default and returns empty.\n\nFix shipped in `scripts\u002Fcmux-surface-report-4h.sh` (commit 351e9cf7c8): multi-app\nprobe at socket-resolve time + `cmux --socket \"$SOCK\" ...` for every CLI call.\nVerified end-to-end: tick at `2026-06-26T00:28:21Z` posted full 35-surface report\nto `C0AJQ5M0A0Y` (ts `1782433701.994439`) instead of skipping.\n\n### Sub-pattern: Two cmux sessions can be live at the same time (verified 2026-06-24)\n\nThe `cmux-last-socket-path` and the `~\u002F.local\u002Fbin\u002Fcmux` shim point to **one** session at a time\n(usually the most recently launched). But on the same host, **two** cmux host apps can be\nrunning concurrently — e.g. `\u002FApplications\u002Fcmux.app` (prod) and `\u002FApplications\u002Fcmux DEV\nmay-18.app` (a debug build) both have IPC sockets listening.\n\n**Diagnostic — enumerate ALL live cmux sessions:**\n\n```bash\nls -1 \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock 2>\u002Fdev\u002Fnull\n# Plus user-state cmux sockets:\nls -1 ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock 2>\u002Fdev\u002Fnull\nls -1 ~\u002F.local\u002Fstate\u002Fcmux\u002F*-last-socket-path 2>\u002Fdev\u002Fnull\n\n# For each, test if anything is actually listening:\nfor s in \u002Ftmp\u002Fcmux-debug-*.sock ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock; do\n  if [ -S \"$s\" ]; then\n    r=$(printf '{\"id\":\"q\",\"method\":\"ping\",\"params\":{}}\\n' | nc -U -w 2 \"$s\" 2>&1)\n    echo \"$s → $r\"\n  fi\ndone\n# Each entry that returns a JSON response (even `{\"error\":\"method_not_found\"}`) is a LIVE session.\n# The shim may be pointing at a dead one; the other is the real one.\n```\n\n**Bug-ref 2026-06-24 — cmux review of cost\u002Fcaching workspaces:**\n- Session A (debug build): `\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock` — 27 workspaces including all\n  the cost\u002Fcaching work (workspace:88 \"cost: reviewer\", workspace:68 \"cost: implicit cache\",\n  workspace:81 \"cost: system inst\", workspace:73 \"local cache\", workspace:84 \"proxy: context\")\n- Session B (prod `cmux.app`): `$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock` — 2 workspaces\n  (workspace:1 and workspace:2, both idle bash logins)\n- The shim was pointing at a *third* dev CLI (`\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock`) that\n  didn't exist on disk. Every `cmux \u003Cverb>` failed with \"Socket not found\" until I bypassed\n  with `--socket` and tried both real sessions.\n\n**Rule:** when the shim fails, enumerate ALL cmux*.sock files and probe each with `nc -U\nping`. Both prod AND dev sessions may be live, with different workspace sets. The user's\n\"the cost work\" might be in the debug session, not the prod one (or vice versa).\n\n**When reporting a \"what are the terminals doing\" summary, check both sessions** — the\nworkspaces you care about may be in the non-default one. The `cmux-terminal-review` skill\nassumes one socket; for two-session reality, run the inventory against both and merge\nthe results before categorizing.\n\n**For unattended\u002Flaunchd jobs that MUST work with any cmux build** (where you can't guarantee\nwhich build will be running), use the multi-app probe recipe that iterates ALL candidate sockets\nacross `\u002Ftmp` + `\u002Fprivate\u002Ftmp` + `~\u002F.local\u002Fstate\u002Fcmux\u002F` and picks the first one whose\n`cmux --socket \u003Cpath> identify` returns valid JSON. See\n`references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md` for the full bash template.\nVerified 2026-06-24 — supersedes `socket-discovery-2026-06-21.md` for unattended runs.\n\nCommon traps (verified 2026-06-11):\n\n- `cmux list-surfaces ...` → **does not exist**. Real options: `cmux tree --all`,\n  `cmux list-panes --workspace \u003Cid>`, `cmux list-pane-surfaces --workspace \u003Cid> --pane \u003Cid>`.\n- `cmux list-surface` (singular) → wrong subcommand; also doesn't exist.\n- `cmux --json \u003Ccommand>` → `--json` is not a global flag. Per-command JSON lives behind\n  `cmux \u003Ccommand> --json` (works for some commands like `identify`, `top`, `list-workspaces`,\n  `auth status`, `ssh-session-list`).\n- `cmux --workspace \u003Cid> --surface \u003Cid> \u003Ccommand>` → wrong ordering. The actual pattern is\n  `cmux \u003Ccommand> --workspace \u003Cid> --surface \u003Cid>` (flags after the subcommand).\n- `cmux send \"text\"` (bare) → in current CLI `send` requires `--workspace` and `--surface`\n  (no default to caller surface). Use `cmux send --workspace \u003Cw> --surface \u003Cs> \"text\"`.\n- `cmux send-surface --surface \u003Cid> \"cmd\"` → `send-surface` does not exist. Use\n  `cmux send --surface \u003Cid> \"cmd\"`.\n- `cmux send-key enter` → exists, but keys must match the documented set: `enter`, `tab`,\n  `escape`, `backspace`, `delete`, `up`, `down`, `left`, `right`, etc.\n- `cmux new-split` → exists but takes a positional direction (`left`\u002F`right`\u002F`up`\u002F`down`),\n  not `--direction`. It also needs `--workspace` and a surface to split from\n  (e.g. `cmux new-split --workspace \u003Cw> --surface \u003Cs> right`).\n- **`cmux select-workspace --workspace 5` does NOT focus `workspace:5`.** The bare\n  integer is interpreted as an **index** (positional, sorted by tab order — NOT by\n  `ref` number). The index list is not sorted by `ref`, so `index 5` can map to\n  any workspace. Verified 2026-06-13: `select-workspace --workspace 5` returned\n  `OK workspace:12` and switched focus there instead of the user's `workspace:5\n  \"bq logging\"`. The trap is silent — it does return `OK workspace:\u003CX>`, just not\n  the X you expected. **Always use the explicit ref form** `--workspace workspace:5`\n  when you mean a specific ref. If you only have the integer from\n  `cmux list-workspaces`, run `cmux list-workspaces --id-format both` first and use\n  the ref (`workspace:N`), not the integer. Full recipe (including the\n  raw-socket alt when the CLI is misbehaving): see\n  `references\u002Fworkspace-routing-trap.md`.\n\n### ⚠️ `cmux read-screen --workspace 62` (bare integer) FAILS — use `--workspace workspace:62`\n\nSame trap class as `select-workspace` above, but for the read\u002Finspect family.\nVerified 2026-06-25 (lvl\u002Fcost recap probe): `cmux read-screen --workspace 62` (and\n`--workspace 68`, `--workspace 81`, etc.) all returned `Error: Workspace index not\nfound`, even though `cmux list-workspaces` clearly showed `workspace:62 \"lvl:\nrefactor\"`. Same for `--workspace 88`, `--workspace 89` — every bare-integer read\nfailed silently with that one-line error.\n\n**Why:** the bare integer is parsed as a positional **index**, but `--workspace 62`\ntriggers a different code path than `select-workspace` does — `select-workspace`\nswallows the index-vs-ref ambiguity and silently re-targets to whatever index 62\nis, while `read-screen` \u002F `read-pane` \u002F `focus-pane` reject the bare integer with\n`Error: Workspace index not found` and never read anything.\n\n**Fix:** always pass the **ref form** `--workspace workspace:62`. Verified working:\n\n```bash\n# BAD — all of these fail with \"Workspace index not found\"\ncmux read-screen --workspace 62 --surface 1\ncmux tree --workspace 62\ncmux list-panes --workspace 62\n\n# GOOD — ref form works\ncmux read-screen --workspace workspace:62 --surface surface:1\ncmux tree --workspace workspace:62\ncmux list-panes --workspace workspace:62\n```\n\n**Recipe (always):** copy the `workspace:N` ref straight from `cmux list-workspaces`\noutput — never type the integer. The same applies to surface refs (`surface:M`,\nnot `1`) and pane refs (`pane:M`, not `1`). If you only have an integer from a\nprior session, run `cmux list-workspaces --id-format both` to get the ref →\nindex mapping, then use the ref.\n\n**Bug-ref:** 2026-06-25 — lvl\u002Fcost recap probe across 6 workspaces\n(`lvl: refactor`, `cost: implicit cache`, `cost: system inst`, `cost: reviewer`,\n`local cache`, `rag: extended`). First probe batch used `--workspace 62` etc.\nand failed with the same error 6 times. Re-ran with `--workspace workspace:62`\nand every read succeeded in one call each.\n\nIf a command fails with `Error: Unknown command:` or `Usage:` dump, run `cmux \u003Ccommand> --help`\nto see the real syntax — do not guess.\n\n**`surface.readScreen` does not exist as a method.** The right\nJSON-RPC method is `surface.read_text` — but see the warning in\n\"Reading a Non-Focused Surface\" above: it ignores ref params.\n**`surface.focus` requires `surface_id` (UUID), not `surface_ref`.**\n**`surface.list` ignores `workspace_ref` — returns the focused\nworkspace's data.** Full transcript: `references\u002Fsurface-read-routing-bug.md`.\n\n## ⚠️ Status Bar Interpretation — NOT Frozen\n\nClaude Code status bar states to interpret correctly:\n- **`⏵⏵ bypass permissions on`** in status bar = normal Claude Code prompt UI, NOT a blocking dialog — Claude Code is actively working around it\n- **Active churning timestamps** (e.g., \"Churned for 9m 41s\", \"Sautéed for 2m 38s\") = genuinely working workspace, NOT frozen\n- **\"Still running\" indicator** = work in progress, not stalled\n- **Idle bash shell** (fresh login prompt) = workspace is done or waiting for input, NOT frozen\n- **ctx XX% progress** = active context usage, workspace is alive\n\n**What IS actually blocked:**\n- `bypass permissions on (shift+tab to cycle)` with NO active churning\u002Ftime-on-task and NO progress indicator = may be a real stall\n- \"Index error\" or \"workspace unreachable\" = workspace handle drift, genuinely blocked\n- Shell at `claude`\u002F`claudem` typed but no Claude Code active = shell-level stall, needs restart\n- \"Add a follow-up\" dialog open in Composer 2 Fast = blocked on human follow-up, not frozen\n\n**Key rule:** A workspace with an active time-on-task label (\"Churned for Xm\", \"Crunched for Xm\") is working, regardless of what the status bar shows. Only report as \"frozen\" when there's evidence of no activity AND no active time-on-task.\n\n## Reading a Non-Focused Surface (verified 2026-06-13)\n\n`cmux read-screen --workspace X --surface Y` and the raw\n`surface.read_text {workspace_ref, surface_ref}` JSON-RPC method are\n**both broken** in the current build — they return the focused surface's\ncontent, not the target. The CLI also rejects many valid targets with\n`Error: invalid_params: Surface is not a terminal` (it routes to a\nwrong internal handle before the type check).\n\n**The reliable recipe — focus-then-read:**\n\n```bash\n# 1. Discover the target surface's UUID\n#    Focus the workspace, then raw-socket call surface.list (the\n#    workspace_ref param is ignored, so you must focus first)\ncmux select-workspace --workspace N\nSOCKET=$(cmux identify | python3 -c \"import json,sys;print(json.load(sys.stdin)['socket_path'])\")\nUUID=$(printf '{\"id\":\"q\",\"method\":\"surface.list\",\"params\":{}}\\n' \\\n  | nc -U -w 3 \"$SOCKET\" \\\n  | python3 -c \"import json,sys; d=json.load(sys.stdin); print([s['id'] for s in d['result']['surfaces'] if s['ref']=='surface:M'][0])\")\n\n# 2. Focus the target surface (param is surface_id=UUID, NOT surface_ref)\nprintf '{\"id\":\"q\",\"method\":\"surface.focus\",\"params\":{\"surface_id\":\"%s\"}}\\n' \"$UUID\" \\\n  | nc -U -w 3 \"$SOCKET\" >\u002Fdev\u002Fnull\n\n# 3. Read the now-focused surface (no --surface flag)\ncmux read-screen --scrollback --lines 50\n```\n\n`surface.focus` requires `surface_id` (the UUID). It rejects\n`surface_ref` and bare integers. See\n`references\u002Fsurface-read-routing-bug.md` for the full transcript and\nupstream-bug filing notes.\n\n**Faster \"is this worker alive?\" diagnosis without per-surface focus:**\n\n1. `cmux tree --all` — pane titles include active time-on-task labels\n   (e.g., \"Churned for 9m 41s\", \"Flibbertigibbeting… 12m 34s\",\n   \"Sautéed for 2m 38s\"). A pane WITHOUT such a label is more likely\n   dead\u002Fstalled. No read needed.\n2. `tail -f \u002Ftmp\u002Fcmux-debug-may-18.log` (or current build's log) — look\n   for `workspace.gitProbe.apply` and `workspace.prRefresh.apply`\n   lines; their presence proves the surface is producing events.\n\n## \"What is this workspace doing?\" — The Discovery Recipe\n\nThis is the most common cmux task from the gateway: user names a workspace (e.g. \"agentf\") and\nyou need to (a) find it, (b) see what surface is focused, (c) read the actual screen text, and\noptionally (d) steer. Verified sequence (2026-06-11):\n\n```bash\n# 1. Find the workspace by partial title\ncmux list-workspaces                       # text output, refs like \"workspace:10\"\ncmux list-workspaces --id-format both      # adds UUIDs\n\n# 2. Get the pane\u002Fsurface tree (titles + tty + focus state)\ncmux tree --all                            # whole window\ncmux tree --all --window \u003Cwin_ref>         # single window\n\n# 3. Confirm focus + identify a specific surface\ncmux identify                              # caller context + currently focused\ncmux identify --workspace \u003Cw> --surface \u003Cs>  # one surface's context\n\n# 4. Read the actual terminal text (the critical step) — only works\n#    reliably for the FOCUSED surface. For non-focused surfaces see\n#    \"Reading a Non-Focused Surface\" above (focus-then-read recipe).\ncmux read-screen --workspace \u003Cw> --surface \u003Cs>           # visible viewport only\ncmux read-screen --workspace \u003Cw> --surface \u003Cs> --scrollback --lines 200   # last 200 lines of scrollback\n\n## \"Should I steer now or wait?\" — The In-Flight Worker Decision Rule\n\nWhen the user says \"steer this worker\" \u002F \"use natural language to fix\n[problem X]\", the worker may already be in the middle of investigating\nor fixing something — possibly the same thing, possibly something else.\nSteering without checking risks context bleed and double-work.\n\n**Rule (verified pattern, 2026-06-13; extended 2026-06-21):**\n\n1. **Read the focused surface** of the target workspace\n   (`cmux read-screen --scrollback --lines 60`).\n2. **Check for active subagents** in the bottom 30 lines of viewport\n   — Claude Code shows them as:\n   - `✻ Waiting for N background agents to finish`\n   - `◯ \u003Csubagent-name>  \u003Ctask>  \u003Ctime>  \u003Ctokens>`\n   - `✻ \u003Cchurn-phrase> (\u003Ctime>  · ↓ \u003Ctokens>)`\n3. **Decide:**\n   - **Worker is idle and at a prompt** (`❯` with empty input) →\n     safe to steer now. Send the natural-language instruction and\n     press Enter.\n   - **Worker is actively churning** (churn label + time counter\n     advancing) → wait. Note the churn time; if it exceeds ~5 min\n     with no progress visible, the worker may be stuck and may need\n     an Escape + re-steer.\n   - **Worker has 1+ in-flight subagents** → do NOT steer. Wait for\n     subagents to finish (the parent will then continue). If the\n     new task is URGENT and unrelated, ask the user to confirm\n     \"interrupt the current investigation\" before sending Escape +\n     new message.\n   - **Worker hit a session rate limit** (\"You've hit your session\n     limit · resets \u003Ctime>\") → can't steer this session. Either\n     wait for reset or open a new surface and start fresh.\n\n4. **Before steering: read the worker's recent task context, not just\n   its current state** (verified pitfall 2026-06-21). Scroll back\n   further than 60 lines — the worker's last 200 lines typically\n   contain the \"user's request was: …\" recap block. **If the worker\n   is already working on the same problem the new steering message\n   is about, do NOT send a from-scratch instruction.** Instead,\n   send a *verification-first* steer that:\n   - Quotes the user's exact words and channel context.\n   - Asks the worker to (a) confirm whether its current PR\u002Fbranch\n     covers the new request, (b) state what additional work is\n     needed if any, (c) report back. **Do not** reissue the\n     original task — that re-runs it and risks duplicate PRs.\n   - Explicitly tells the worker \"Do NOT merge \u002F Do NOT fake-approve\n     CR \u002F Do NOT resolve threads\" if the existing PR is still\n     mid-review.\n   - Asks the worker to post a brief in-thread status note so the\n     user knows steering landed.\n\n   This pattern preserves the worker's progress (which may be\n   hours of design + test work) and converts a \"do this from\n   scratch\" steering into a \"verify + amplify\" steering. Burned\n   once on 2026-06-21: a user said \"find the hermes workspace and\n   steer it to fix this routing bug\" — the worker already had\n   PR #665 open covering exactly the same routing bug. A naive\n   \"fix the routing bug\" steer would have either duplicated the\n   work or trampled the existing branch. The verification-first\n   steer landed cleanly in 6s.\n\n**Steering template (Claude Code worker):**\n\n```bash\nWS=4; SURF=45\ncmux select-workspace --workspace $WS\ncmux send --workspace $WS --surface surface:$SURF \"$(cat \u003C\u003C'EOF'\nAfter your current investigation finishes, also start a\n\u003Crepro|\u002Frepro|\u002Froot-cause-first\u002Fetc.> worker for\n\u003Cbug description + URL>. Prefer \u003Cfix approach> over \u003Canti-pattern>.\nReport findings here when done.\nEOF\n)\"\ncmux send-key --workspace $WS --surface surface:$SURF enter\n# Verify: spinner\u002Fchurn label should appear within ~5s\nsleep 5 && cmux read-screen --scrollback --lines 15\n```\n\n**Why this rule exists:** in 2026-06-13 a user asked \"investigate\nwhy workers are dead, then steer the ao workspace to fix\n\u003Ccharacter-creation bug>\". The ao worker was already mid-investigation\non the worker-death question (agy failure investigation, 16+ min\nrunning). Steering immediately would have aborted a high-value\ninvestigation and risked context bleed between two different bugs.\nThe right move was to report findings + ask the user to confirm\n\"wait for current subagents, or interrupt and steer now?\".\n\n## Steer (if appropriate) — Sending the actual message\n\nOnce you've decided the worker is safe to steer (see the In-Flight\nWorker Decision Rule above), the actual send is straightforward:\n\n```bash\ncmux send --workspace \u003Cw> --surface \u003Cs> \"your message\"   # types text\ncmux send-key --workspace \u003Cw> --surface \u003Cs> enter         # presses Enter\n```\n\n## Multi-surface parallel steering (verified 2026-06-24, 4 steers in one batch)\n\nWhen the user gives a directive that maps to **N independent workspaces**\n(\"steer all the cost workspaces\", \"kick off the next steps in\neach PR review surface\", \"fan out the Slack-pivot instructions\"),\n**send the steers in one parallel batch** then verify each after a\nshared settle period. This is faster than serial and safer than\nsending all-then-verifying one at a time.\n\n**The pattern:**\n\n```bash\nexport CMUX_SOCKET_PATH=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\n\n# 1. Identify all target (ws, surface) pairs from the user's directive\nTARGETS=(\"81 229\" \"73 139\" \"84 223\" \"70 132\")  # (workspace surface) tuples\n\n# 2. Filter: only steer surfaces that are SAFE per the In-Flight Worker Rule\n#    - empty ❯ prompt\n#    - no active churning label\n#    - not mid-Edit on a different file\n#    Skim each surface's recent output first; skip the busy ones.\nSAFE_TARGETS=(\"81 229\" \"73 139\" \"84 223\" \"70 132\")  # after triage\n\n# 3. Send all steers + Enter keys in one batch (parallel-safe: each\n#    cmux call is its own short-lived subprocess)\nfor pair in \"${SAFE_TARGETS[@]}\"; do\n  set -- $pair\n  ws=$1; surf=$2\n  cmux send --workspace=workspace:$ws --surface=surface:$surf \"your steer here\"\n  cmux send-key --workspace=workspace:$ws --surface=surface:$surf enter\ndone\n\n# 4. Single settle wait, then verify EACH surface absorbed the steer\nsleep 10\nfor pair in \"${SAFE_TARGETS[@]}\"; do\n  set -- $pair\n  ws=$1; surf=$2\n  echo \"=== w${ws}\u002Fs${surf} ===\"\n  cmux read-screen --workspace=workspace:$ws --surface=surface:$surf --lines 15 \\\n    2>&1 | grep -vE '^\\s*Error:' | tail -8\ndone\n```\n\n**What \"absorbed\" looks like:**\n- An active churning label appears (e.g. `✻ Beboppin'…`, `Brewed for`, `Creating follow-up branch…`)\n- The worker's first action matches the steer (new branch name, new commit, new tool call)\n- A new task list item appears with the steer's content\n\n**What \"stuck\" looks like (the steer landed but worker didn't act):**\n- Message text is visible at the prompt but no churn label\n- An API error appears: `Unable to connect to API (ConnectionRefused)`, `You've hit your session limit`\n- Worker reverts to a previous task or context\n\n**Bug-ref 2026-06-24 — parallel steer of 4 cost workspaces** (workspace:88\n\"cost: reviewer\", workspace:68 \"cost: implicit cache\", workspace:81 \"cost:\nsystem inst\", workspace:73 \"local cache\", workspace:84 \"proxy: context\",\nworkspace:70 \"daily jobs + deploy\"):\n- 4 of 6 steers absorbed cleanly (w81 began executing the \u002Fgoal I sent,\n  w84 created a new branch + PR within 2 min, w70 sat at the prompt\n  holding the operator runbook message)\n- 1 was correctly skipped (w88 \"cost: reviewer\" had 4 active teammates\n  on a fan-out — In-Flight Worker Rule fired, no steer)\n- 1 surfaced a separate agent-side issue (w73 \"local cache\" hit\n  `API Error: Unable to connect to API (ConnectionRefused)` — Claude\n  API auth problem on the worker's CLI, not a steer problem)\n\n**The settle-wait before verify** is critical: 5s is too short for the\nworker to react. 10s is the sweet spot for a healthy agent; 30s for\none mid-thought on a 50+ minute run. After verify, if a steer didn't\nabsorb (no churn, no action, message still visible), DO NOT resend\nthe same message — that risks double-submission. Instead, treat it\nas a separate issue and surface to the user.\n\n**Don't:**\n- Don't send 4 steers + 4 separate verify-reads in 4 sequential cmux\n  calls — that's 8 round-trips. The batch pattern above is 4 sends\n  + 4 enter-keys + 4 verify-reads in 2 batched shells.\n- Don't steer a worker that's mid-Edit on a different file (per\n  In-Flight Worker Rule) just because the user said \"all of them.\"\n  Filter first; surface the skipped ones in your report.\n- Don't resend the same message if a steer didn't absorb — that's\n  the double-submission anti-pattern. Investigate the surface state\n  first.\n\n**Handles:** `workspace:10`, `pane:17`, `surface:19`, `window:1` are all valid refs. UUIDs\nwork too (`--id-format both`). Indexes (1-based across the window) also work but refs are\nunambiguous.\n\n**Default socket path:** the cmux app uses `\u002Ftmp\u002Fcmux-debug-may-18.sock` or similar (the\nfilename is build-date-suffixed). `cmux` CLI figures it out automatically. The\n`CMUX_SOCKET_PATH` env var is for raw socket access (Python client) — set it to the real\nsocket path returned by `cmux identify` (field `socket_path`).\n\n## \"Find the cmux workspace for X\" — Load `cmux-find-workspace-by-topic`\n\n**Before doing anything else when the user says \"find the cmux workspace for X\":** load the dedicated skill `cmux-find-workspace-by-topic`. It codifies the multi-socket probe + keyword grep recipe that found `workspace:72 \"w: misc - agy\"` in 3 seconds (2026-06-25) after the shim's dead-default hid it. Don't start from `cmux list-workspaces` — that may be pointed at a dead dev CLI.\n\n## Sub-pattern: \"User asks for meta \u002F skillify \u002F audit of cmux workflows\" (added 2026-06-26)\n\nWhen the user asks for **meta work on cmux** (not actual steering\u002Ffinding):\n\n> \"skillify cmux\", \"improve hermes confusion with cmux workspace finding\",\n> \"audit the cmux skills\", \"review cmux routing\", \"\u002Flearn cmux workflow\",\n> \"do not do the actual work, do meta work and \u002Fskillify\"\n\n…**DO NOT propose new cmux skills first.** Three cmux skills already exist and cover the territory:\n\n| Skill | When it fires |\n|---|---|\n| `cmux` (this one) | CLI\u002Fsocket reference, multi-session probe, send→submit→proof, focus-then-read, in-flight worker rule, stale-screen trap |\n| `cmux-find-workspace-by-topic` | Multi-socket probe + keyword grep (the dead-shim-hides-the-workspace trap) |\n| `cmux-mcp-server-options` | Community MCP server list, install recipe, decision guide, \"Hermes doesn't need one today\" |\n\n**The right first move (verified 2026-06-26):**\n\n1. **Load the 3 existing skills** and audit what's already covered. Most \"new\" proposals are already addressed (multi-socket probe, focus-then-read, `--socket` global flag, launchd env var ignore, send→submit→proof ritual, parallel steering, in-flight worker rule, stale-screen trap, workspace-routing trap, MCP server list).\n2. **Identify the SPECIFIC gap** the user is actually hitting (e.g. \"skills don't auto-load\" vs \"no workspace catalog\" vs \"skills overlap\" — each has a different fix).\n3. **Then propose** 1-2 targeted skills or skill patches — not 4 speculative ones.\n\n**Reply shape for meta audits** (per user preference `Reply shape: evidence + 2-4 next-step options + STOP`):\n- Lead with evidence: \"skills already cover X, Y, Z (verified [date]). Real gap: [specific thing].\"\n- Don't list every potential new skill — list only the ones that pass the \"this would have prevented a real session burn\" test.\n- End with 2-4 next-step options + STOP, NOT a long speculation list.\n\n**Bug-ref 2026-06-26 — meta audit of cmux confusion.** User said: *\"do not do the actual work discussed, do meta work and \u002Fskillify and \u002Flearn to improve hermes confusion with cmux workspace finding and steering and \u002Fresearch to see if cmux has an mcp server.\"* I loaded the 3 cmux skills, verified the MCP server registry state, and proposed 4 NEW skills (`cmux-steering-workflow`, `cmux-workspace-catalog`, etc.) before checking the existing skill coverage. Three of those four were already covered by the parent + 2 subs. The real gap was \"skills don't auto-load on meta-audit phrasing\" — fixed by broadening `cmux-find-workspace-by-topic` description triggers, not by adding new skills. The lesson: **meta audits are mostly skill audits, not skill creation. Lead with coverage check, then propose only the gaps that survive it.**\n\n## MCP servers for cmux (community, not upstream)\n\ncmux itself does NOT ship an MCP server (verified v0.64.17, 2026-06-23). Multiple community MCP servers wrap the cmux CLI\u002Fsocket — see the dedicated `cmux-mcp-server-options` skill for the verified list, install recipe, and decision guide (`@jsamuel1\u002Fcmux-mcp` is the hardened fork most AI agents use).\n\nFor Hermes's own automation today, use the CLI over shell — Hermes doesn't currently register MCP clients for itself.\n\n## Socket Connection (raw JSON-RPC)\n\n```bash\n# Default socket — confirm with `cmux identify` (look at socket_path field)\nSOCKET_PATH=\"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\"\n```\n\nSend JSON-RPC requests (newline-delimited):\n```json\n{\"id\":\"req-1\",\"method\":\"workspace.list\",\"params\":{}}\n{\"id\":\"req-2\",\"method\":\"workspace.tree\",\"params\":{\"all\":true}}\n{\"id\":\"req-3\",\"method\":\"surface.readScreen\",\"params\":{\"workspace_ref\":\"workspace:10\",\"surface_ref\":\"surface:19\",\"scrollback\":true,\"lines\":200}}\n```\n\n`scripts\u002Fcmux_client.py` is a small Python wrapper that does this over a Unix socket\n(`CMUX_SOCKET_PATH` env, default `\u002Ftmp\u002Fcmux.sock`). Use it from inline scripts.\n\n## Error \u002F stderr Handling\n\n```python\n# Always emit terminal status even on error\ntry:\n    result = subprocess.run([\"cmux\", \"tree\", \"--all\"], capture_output=True, text=True, timeout=10)\nexcept Exception as exc:\n    msg = f\":fire: cmux error: `{exc}`\"\n    slack.post_message(channel_id, msg, thread_ts=thread_ts)\n    return\n\n# Truncate large output for Slack display\nif len(result.stderr) > 2000 or result.stderr.count(\"\\n\") > 20:\n    summary = truncate_output(result.stderr)\n    slack.post_message(channel_id, f\":warning: cmux output truncated:\\n```\\n{summary}\\n```\", ...)\n```\n\n## CLI Quick Reference (current cmux)\n\n> **The most up-to-date command list lives in `references\u002Fcli-cheatsheet.md`.** The tables below\n> are the high-traffic subset verified on 2026-06-11.\n\n### Discovery (read-only)\n\n| Action | Command |\n| --- | --- |\n| List workspaces | `cmux list-workspaces` (add `--id-format both` for UUIDs) |\n| Workspace tree | `cmux tree --all` |\n| List windows | `cmux list-windows` |\n| Current window | `cmux current-window` |\n| List panes in a workspace | `cmux list-panes --workspace \u003Cw>` |\n| List surfaces in a pane | `cmux list-pane-surfaces --workspace \u003Cw> --pane \u003Cp>` |\n| Identify caller\u002Ffocus | `cmux identify` (add `--workspace \u003Cw> --surface \u003Cs>`) |\n| Read screen text | `cmux read-screen --workspace \u003Cw> --surface \u003Cs> [--scrollback --lines N]` |\n| Surface health | `cmux surface-health [--workspace \u003Cw> --surface \u003Cs>]` |\n| Capabilities | `cmux capabilities` |\n| Stream events | `cmux events [--name \u003Cname>] [--limit N]` (can block; set a timeout) |\n\n### Workspace \u002F window control\n\n| Action | Command |\n| --- | --- |\n| Create workspace | `cmux new-workspace` (add `--cwd \u003Cpath>`, `--command \u003Ccmd>`) |\n| Select workspace | `cmux select-workspace --workspace \u003Cid>` |\n| Close workspace | `cmux close-workspace --workspace \u003Cid>` |\n| Rename workspace | `cmux rename-workspace --workspace \u003Cid> \"name\"` |\n| Reorder | `cmux reorder-workspace --workspace \u003Cid> --index N` |\n| Workspace context menu | `cmux workspace-action --action \u003Cname> --workspace \u003Cid>` (e.g. `pin`, `set-color`) |\n| Open path\u002FURL | `cmux open \u003Cpath-or-url> [--workspace \u003Cid>]` |\n| SSH workspace | `cmux ssh user@host [-A]` |\n\n### Panes & surfaces\n\n| Action | Command |\n| --- | --- |\n| New split | `cmux new-split --workspace \u003Cw> --surface \u003Cs> \u003Cdirection>` (`left`\u002F`right`\u002F`up`\u002F`down`) |\n| New pane | `cmux new-pane --workspace \u003Cw> --surface \u003Cs> terminal\\|browser` |\n| New surface | `cmux new-surface --workspace \u003Cw> --pane \u003Cp> terminal\\|browser` |\n| Focus pane | `cmux focus-pane --workspace \u003Cw> --pane \u003Cp>` |\n| Close surface | `cmux close-surface --workspace \u003Cw> --surface \u003Cs>` |\n| Move surface | `cmux move-surface --workspace \u003Cw> --surface \u003Cs> --to-workspace \u003Cw2>` |\n| Split off | `cmux split-off --workspace \u003Cw> --surface \u003Cs> \u003Cdirection>` |\n\n### Input (steering a terminal)\n\n| Action | Command |\n| --- | --- |\n| Send text | `cmux send --workspace \u003Cw> --surface \u003Cs> \"echo hello\"` |\n| Send key | `cmux send-key --workspace \u003Cw> --surface \u003Cs> enter` |\n| Keys | `enter`, `tab`, `escape`, `backspace`, `delete`, `up`, `down`, `left`, `right` (and more — `cmux send-key --help`) |\n\n`cmux send` does NOT auto-press Enter. To execute a command, send the text then `send-key enter`.\n\n### Sidebar & notifications\n\n| Action | Command |\n| --- | --- |\n| Notify | `cmux notify --title \"T\" --body \"B\" [--workspace \u003Cw>]` |\n| List notifications | `cmux list-notifications` |\n| Set status pill | `cmux set-status \u003Ckey> \"\u003Cvalue>\"` |\n| Clear status pill | `cmux clear-status \u003Ckey>` |\n| Set progress | `cmux set-progress 0.5 --label \"Building...\"` |\n| Clear progress | `cmux clear-progress` |\n| Log entry | `cmux log \"msg\" --level info\\|warn\\|error` |\n\n### Hooks & agent integration\n\n| Action | Command |\n| --- | --- |\n| Install agent hooks | `cmux hooks setup [--agent \u003Cname>]` |\n| Uninstall | `cmux hooks uninstall [--agent \u003Cname>]` |\n| Feed TUI | `cmux feed tui` |\n| Browser automation | `cmux browser \u003Csubcommand>` (snapshot, click, type, eval, screenshot, etc.) |\n\n## Python Client\n\n```python\nimport json\nimport os\nimport socket\n\nSOCKET_PATH = os.environ.get(\"CMUX_SOCKET_PATH\", \"\u002Ftmp\u002Fcmux.sock\")\n\ndef rpc(method, params=None, req_id=1):\n    payload = {\"id\": req_id, \"method\": method, \"params\": params or {}}\n    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:\n        sock.connect(SOCKET_PATH)\n        sock.sendall(json.dumps(payload).encode(\"utf-8\") + b\"\\n\")\n        return json.loads(sock.recv(65536).decode(\"utf-8\"))\n\n# List workspaces\nprint(rpc(\"workspace.list\", req_id=\"ws\"))\n\n# Send notification\nprint(rpc(\"notification.create\", {\"title\": \"Hello\", \"body\": \"From Python!\"}))\n```\n\nThe wrapper lives at `scripts\u002Fcmux_client.py` (this skill's scripts\u002F dir).\n\n## Shell Helper\n\n```bash\ncmux_cmd() {\n    SOCK=\"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\"\n    printf \"%s\\n\" \"$1\" | nc -U \"$SOCK\"\n}\n\ncmux_cmd '{\"id\":\"ws\",\"method\":\"workspace.list\",\"params\":{}}'\n```\n\n## Check if cmux is Available\n\n```bash\n[ -S \"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\" ] && echo \"cmux socket available\"\ncommand -v cmux &>\u002Fdev\u002Fnull && echo \"cmux CLI available\"\n```\n\n## Sub-pattern: \"Verify a TUI-only feature works (Claude Code \u002Fadvisor, \u002Fconfig, \u002Fmodel, etc.)\"\n\n**Load the dedicated skill `test-tui-claude-feature-via-cmux` first** when the\nuser asks \"does \u002Ffeature work.\" It ships a wrapper script\n(`scripts\u002Ftest-tui-feature.sh`), 6\u002F6 unit tests, and a worked example — using\nit is faster and more reliable than re-deriving the recipe from this\nsub-pattern. The cmux sub-pattern here is the **primitive** that skill is\nbuilt on; the dedicated skill is the right entry point for the user-facing\nquestion.\n\nThe summary of why the dedicated skill exists (kept here for the\n\"load cmux first\" path):\n\n> `claude --print \"\u002Ffeature\"` will always return\n> `\u002F\u003Cfeature> isn't available in this environment` for TUI slash\n> commands, because `--print` is non-interactive mode. That error\n> specifically means \"I cannot show you this in non-interactive mode,\"\n> **not** \"this feature is broken.\" Treating it as a feature-gate failure\n> leads to 30+ minute rabbit holes reading minified binary strings\n> (`isFirstPartyApiBackend`, `xr()`, `VW()`, `oct()`,\n> `isFirstPartyAnthropicBaseUrl`) and trying every env-var combination.\n> The user's literal words on 2026-06-23, when this happened with\n> `\u002Fadvisor`: *\"are you opening claude code itself and typing \u002Fadvisor?\n> … use cmux stop being lazy.\"*\n\nThe full recipe lives in [`references\u002Ftui-feature-testing-recipe.md`](.\u002Freferences\u002Ftui-feature-testing-recipe.md)\nand in the dedicated skill `test-tui-claude-feature-via-cmux`. The\nsix-step primitive (spawn → discover surface → wait for `❯` → send →\nread → cleanup) is the same; the dedicated skill wraps it in a tested\nscript and adds the `isn't available in this environment` non-interactive\ndetector that signals a malformed slash command or a not-yet-ready\nprompt.\n\n**Surface-rediscovery pitfall (verified 2026-06-23):** `cmux list-pane-surfaces`\nreturns `surface:N` for the first surface in the first pane of the target\nworkspace, but a new-workspace spawn can produce a different surface number\nthan expected. The \u002Fadvisor verification session used `workspace:31 surface:66`;\na hardcoded `surface:65` from a prior session would have either misrouted or\nerrored with `Surface is not a terminal`. **Always re-discover the surface\nfrom the spawn output — never hardcode a surface number from a prior\nworkspace.** The dedicated skill's helper script does this automatically.\n\n**Companion skill note:** the dedicated `test-tui-claude-feature-via-cmux`\nskill lives at `~\u002F.hermes_prod\u002Fskills\u002Ftest-tui-claude-feature-via-cmux\u002F`\nand is the **preferred entry point** when the user asks \"does \u002Ffeature\nwork.\" Load it first; fall back to the inline cmux recipe only when\nthe dedicated skill's wrapper doesn't fit (e.g. multi-step dialog\nnavigation that needs a custom key sequence).\n\n### Sub-pattern: \"Workspace screen says X, but GitHub says Y\" (verified 2026-06-25)\n\n**The trap:** cmux workspace displays are a **stale proxy** for the live source-of-truth\n(GitHub PR state, local file state, etc.). When you `cmux read-screen` a workspace\ntalking about PR #7907, the screen text might say \"green\u002Funstable (author churning)\"\nwhile GitHub's actual `commits\u002F\u003Csha>\u002Fcheck-runs` shows 4 consecutive Green Gate\nSUCCESS runs in the last 3 hours. The screen is a *snapshot of the agent's last\nrecap message*, which can be 30-90+ minutes old — the agent has been doing real work\nsince then but hasn't posted an updated summary.\n\n**Bug-ref 2026-06-25 — lvl\u002Fcost recap probe** (6 workspaces):\n- workspace:88 \"cost: reviewer\" screen showed:\n  - `#7907 — OPEN, head d64d96899, green\u002Funstable (author churning)`\n  - `#7920 — OPEN, head 8e0985afc, mergeable=clean (3 blockers fixed)`\n- Both screens were STALE. Live GitHub state:\n  - `#7907` head `d64d9689` had 4 Green Gate SUCCESS runs in the last 3 hours\n    (freshest \u003Chttps:\u002F\u002Fgithub.com\u002F$GITHUB_REPOSITORY\u002Factions\u002Fruns\u002F28199477760|run 28199477760> 21:12Z) — fully green\n  - `#7920` head `8e0985afc` had 3 Green Gate SUCCESS runs in the last 30 min\n    (freshest \u003Chttps:\u002F\u002Fgithub.com\u002F$GITHUB_REPOSITORY\u002Factions\u002Fruns\u002F28199013911|run 28199013911> 20:38Z) — fully green\n- The \"author churning\" label in workspace:88 referred to the local worker's\n  activity log, not the PR's check-runs. **The user's recommendation would have\n  been wrong** (\"#7907 needs more work\") if I'd trusted the screen.\n\n**Rule:** when a workspace screen quotes a PR's check status, **always cross-check\nagainst the live GitHub state** before acting on it. The canonical cross-check:\n\n```bash\nBR=$(gh pr view \u003CN> --repo \u003Cowner>\u002F\u003Crepo> --json headRefName -q .headRefName)\nSHA=$(git ls-remote https:\u002F\u002Fgithub.com\u002F\u003Cowner>\u002F\u003Crepo>.git refs\u002Fheads\u002F$BR 2>\u002Fdev\u002Fnull | awk '{print $1}')\necho \"branch=$BR head=$SHA\"\ngh api \"repos\u002F\u003Cowner>\u002F\u003Crepo>\u002Fcommits\u002F$SHA\u002Fcheck-runs\" \\\n  --jq '[.check_runs[] | {name, conclusion, started_at, html_url}]\n        | sort_by(.started_at) | reverse | .[0:8]'\n```\n\n**Per CLAUDE.md \"7-green verification\"** — `gh pr checks` is NOT sufficient for\nGreen Gate; must read workflow logs for gate-by-gate PASS\u002FFAIL. For a faster\n\"is this PR fundamentally green?\" answer, the `check-runs` API with `.conclusion`\n(not `.state` — `.state` is null for Actions and only populated for legacy commit\nstatuses) is the right primitive. Look for `conclusion: \"success\"` on the \"Green\nGate\" named check_run.\n\n**When to trust the screen:** when the workspace screen shows a recent\n*churning label with advancing time* (`Churned for 12m 34s`, `Brewed for 5m 12s`)\nor an active subagent, the worker IS doing something — the screen reflects\ncurrent state. The stale-screen trap fires when the screen shows a *recap line*\nthat's actually a few hours old.\n\n**Also applies to mergeable state** (`workspace:88` showed `#7920 mergeable=clean`,\nlive GitHub said `MERGEABLE` — that one happened to match, but `mergeable_state`\nin GitHub API can lag too). Always pull `mergeable` via `gh pr view --json mergeable`\nas the source-of-truth.\n\n### ⚠️ THE #1 RECURRING FAILURE — Send → Submit → Proof\n\n`cmux send` does NOT press Enter. The user has explicitly flagged this as\n\"you always forget to send\" at least 5 times in the last 30 days (2026-06-09,\n2026-06-19, 2026-06-20, 2026-06-23, 2026-06-25). The canonical 4-step ritual\n(send → send-key enter → sleep → verify churning label) lives at\n**`references\u002Fsend-submit-proof-2026-06-25.md`** — load it before steering any\nidle agent. The user's framing phrase \"did you press send\" \u002F \"show me proof\"\nmaps directly to this reference.\n\nFor long task briefs (>200 chars), use the **worktree-pointer strategy** —\nwrite the brief to a file in the worker's cwd and send a short 1-2 line\npointer to it. Documented in the same reference.\n\n## Related references\n\n- **`references\u002Fsend-submit-proof-2026-06-25.md`** — the canonical 4-step send→submit→proof ritual + worktree-pointer strategy. **Load this before steering any idle agent.**\n- **`references\u002Fcli-cheatsheet.md`** — full command list grouped by intent (canonical reference).\n- **`references\u002Fsocket-discovery-2026-06-21.md`** — original recipe, `cmux identify` as canonical resolver.\n  Use for ONE-shot interactive scripts; **for launchd jobs and any unattended run, use\n  `multi-app-socket-probe-recipe-2026-06-24.md` instead** (probes ALL candidate sockets so\n  any cmux build\u002Fapp combo works).\n- **`references\u002Fsurface-read-routing-bug.md`** — non-focused surface read API bugs + focus-then-read workaround (2026-06-13).\n- **`references\u002Fworkspace-routing-trap.md`** — `select-workspace` index-vs-ref footgun + the \"find a workspace by partial title\" recipe that always works (2026-06-13).\n- **`references\u002Ftui-feature-testing-recipe.md`** — \"does \u002Ffeature work\" verification via cmux (full transcript, surface-discovery pitfall, the `isn't available in this environment` non-interactive-mode detector, 2026-06-23 \u002Fadvisor Opus 4.8 case).\n- **`references\u002Fdrift-aware-steer-recipe.md`** — \"worker is already on it\" verification-first pattern with drift-detection (PR body vs code grep) + dual-channel steer (PR comment for detail + short cmux send as pointer). Verified 2026-06-24 driving the daily-GCP-slack-alerts work through `workspace:70 \"daily jobs + deploy\"`.\n- **`references\u002Fios-pairing.md`** — iOS app pairing diagnosis: `auth.status.signed_in` is the canonical blocker; `mobile.host.status.host_service.is_running` follows from auth; `mobile.attach_ticket.create` mints the QR once host is up. Non-restart sign-in via `auth.begin_sign_in` → browser URL → `cmux-dev-dev-fork:\u002F\u002Fauth-callback` URL scheme (scheme varies by build: `cmux:\u002F\u002F`, `cmux-dev:\u002F\u002F`, `cmux-dev-dev-fork:\u002F\u002F`). \"Latest stable\" trap: installed `\u002FApplications\u002Fcmux.app` may be older than the GitHub release — always verify. Load when the user says \"pair my iOS app\", \"QR isn't showing\", \"trying to use Cmux iOS\", or \"do not restart it\".\n- **`scripts\u002Fcmux_client.py`** — minimal Python socket client (rpc helper).\n- **Companion skill:** `test-tui-claude-feature-via-cmux` (`~\u002F.claude\u002Fskills\u002Ftest-tui-claude-feature-via-cmux\u002F`) — domain-specific test recipe built on cmux; ships `scripts\u002Ftest-tui-feature.sh` wrapper + 6\u002F6 unit tests. **Load this skill when the user asks \"does \u002Ffeature work\" rather than re-deriving the cmux recipe from scratch each time.**\n- **Companion skill:** `repo-top5-audit-and-dispatch` (`~\u002F.hermes_prod\u002Fskills\u002Fdevops\u002Frepo-top5-audit-and-dispatch\u002F`), reference `references\u002F2026-06-25-workstream-scope-completeness-audit.md` — the \"are those all the PRs related to X?\" multi-keyword `gh search prs` sweep + live-GitHub cross-check recipe, used after this cmux sub-pattern to verify each PR the screen recap mentioned.\n- Upstream contract: \u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fdocs\u002Fcli-contract.md>\n- Upstream skill: \u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fskills\u002Fcmux\u002FSKILL.md>\n","## ⚠️ Submit Discipline (MANDATORY — read this before every cmux steer)\n\n`cmux send` does **NOT** press Enter. This is the #1 recurring cmux failure mode\n(verified 2026-07-16: user explicitly flagged \"you always forget to send\" after the\nfable iOS pivot bootstrap). The **4-step ritual** below is a hard contract for every\nsend to a cmux surface. Skip ANY step and the message sits in the input buffer\nwithout ever reaching the agent.\n\n### ⚠️ Output Contract — typed text + terminal response (MANDATORY)\n\nEvery reply that reports a `cmux send` action MUST include, in the same reply:\n\n1. **The exact text that was typed** — verbatim copy of the string passed to `cmux send`.\n2. **The cmux terminal response** — verbatim transcript of what `cmux capture-pane` \u002F\n   `cmux read-screen` returned AFTER the `cmux send-key enter` settle window\n   (typically 5-15s). Specifically, the agent's first action after absorption.\n3. **Submission status** — explicit verdict: \"submitted (churning label X)\",\n   \"not submitted (text still at ❯ prompt)\", or \"blocked (no churn, retried N times)\".\n\n**Treat as not working until we see a response.** A reply that does NOT include\nboth the typed text AND a terminal response is invalid evidence that the\nsteer landed. The operator cannot distinguish a successful send from a failed\nsend that left text in the input buffer.\n\nCanonical contract + echo-back template: `references\u002Foutput-contract-mandatory.md`.\n\n### ⚠️ LLM-Provenance Caveat (MANDATORY footer)\n\nEvery reply that quotes cmux output, terminal text, or agent actions produced\nby another LLM (the worker agent OR the assistant's own synthesis of agent\noutput) MUST end with this verbatim footer:\n\n> *This was generated from another LLM and not the actual user, so feel free\n> to push back if you disagree and we can discuss.*\n\nFull caveat rules + scope (when to apply, when not to apply):\n`references\u002Foutput-contract-mandatory.md` § \"LLM-Provenance Caveat\".\n\n### The 4-step ritual\n\n```bash\n# STEP 1 — Type the text. OK response only proves socket acceptance, NOT submission.\ncmux send --workspace workspace:N --surface surface:M \"your message\"\n\n# STEP 2 — Press Enter. send does NOT auto-press Enter.\ncmux send-key --workspace workspace:N --surface surface:M enter\n\n# STEP 3 — Wait 5-15 seconds for the agent to start processing.\nsleep 8\n\n# STEP 4 — Verify with churning label (THE ONLY definitive proof).\ncmux capture-pane --workspace workspace:N --surface surface:M --lines 25\n# Look for one of:\n#   - \"Working (Xs • esc to interrupt)\"\n#   - \"Forming… (Xs · thinking)\"\n#   - \"Precipitating… (Xs · ↓ tokens)\"\n#   - \"Brewed \u002F Churned \u002F Cooked for Xm\"\n# If you see ANY active churning label → SUBMITTED.\n# If the text is still sitting at the ❯ prompt → NOT submitted, repeat step 2.\n# If \"Stopped\" \u002F \"Done\" \u002F nothing → no churn, investigate.\n```\n\n### Echo-back proof (MANDATORY)\n\nEvery cmux steering action MUST be followed by an **echo-back proof** in the same\nturn or the immediate next turn to your operator (Slack thread, terminal reply,\nor whichever channel triggered the steer):\n\n> ◀ sent to surface:55 (LEFT\u002Fclaudec) at \u003CHH:MM:SS PT> — 4-step ritual complete;\n> churning label \"Forming… 9s · ↓ 4.9k tokens\" confirmed via capture-pane.\n\n**Banned** (these are the failure modes the user keeps flagging):\n- \"I sent the message\" (no Enter proof)\n- \"The agent should have received it\" (no churning label)\n- `cmux send` with no follow-up `cmux send-key enter`\n- Sending to a surface that hasn't been focused (the global focus may be on a\n  different workspace; use the raw RPC `surface.focus` if needed)\n\n### Worktree-pointer strategy for long briefs\n\nFor task briefs >200 chars (e.g. orchestrating iOS app pivot, multi-PR review),\ndo NOT paste the full text into the input. Write the brief to a file in the\nagent's cwd (e.g. `.cmux-\u003Ctask>-brief.md`) and send a 1-2 line pointer. This\navoids the autocompleter contamination pitfall where shell-style tokens inside\nlong text trigger tab completion mid-stream.\n\n### Canonical reference\n\nFull recipe + edge cases + the 2026-06-25 worked example live at:\n`~\u002F.hermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsend-submit-proof-2026-06-25.md`\n\nThis rule was added 2026-07-16 after the fable iOS pivot bootstrap surfaced\n\"you always forget to send\" \u002F \"make sure you press submit and the work starts\non the cmux input\" (Slack ts 1784185650.528089). Apply it uniformly to every\ncmux-touching skill.\n\n# cmux\n\nControl cmux terminal multiplexer programmatically via its Unix socket API or CLI.\n\n**Skill last updated:** 2026-06-26 — added two new launchd-scripting pitfalls: (1) `--socket` is a GLOBAL flag (must come right after `cmux`, not after the subcommand — putting it after `cmux tree` errors with `tree: unknown flag --socket`), and (2) `CMUX_SOCKET_PATH` env var is silently IGNORED by the cmux CLI in launchd-style env without an interactive shell context (CLI falls back to baked-in stale default `\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock`). Verified by fixing `scripts\u002Fcmux-surface-report-4h.sh` false-positive \"RPC wedged\" skip alert at 2026-06-26T00:03:51Z. See new \"Sub-pattern: cmux CLI flag-order gotchas in launchd scripts\" below.\nFor the canonical command list, see `references\u002Fcli-cheatsheet.md` and the upstream contract at\n\u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fdocs\u002Fcli-contract.md>. When in doubt, run\n`cmux docs api` or `cmux \u003Ccommand> --help` to confirm.\n\n## ⚠️ Visual Screenshot of cmux Window — macOS TCC Blocker\n\n**The cmux window is not capturable by default.** cmux is built on Electron and marks its window as `kCGWindowSharingState=0` (non-shareable). `screencapture -l \u003Cwid>` returns \"could not create image from window\", and `SCScreenshotManager.captureImage` (ScreenCaptureKit) returns `Error -3801 \"The user declined TCCs for application, window, display capture\"` because the calling process does not have Screen Recording permission.\n\n**When the user asks for \"a screenshot of the message received\" or \"show me the cmux window\":** try cmux's own APIs FIRST (see \"When the user asks for visual proof\" sub-pattern below) before falling through to macOS screencapture. If those fail, the substitute proof is `cmux capture-pane --scrollback --lines N`, which reads the terminal text via the cmux socket API and bypasses macOS TCC entirely.\n\n**The \"Terminal\" in \"Screen Recording → Terminal\" is the WRONG fix for hermes-gateway-driven captures.** macOS TCC grants Screen Recording to a specific process, not to a logical concept like \"the terminal\". If the capture call originates from a script running under `hermes gateway` (e.g. Python 3.14 from Homebrew), the **caller** is `Python.framework\u002FVersions\u002F3.14\u002F...\u002FPython.app` — granting permission to Terminal.app or iTerm.app will NOT let hermes-gateway-driven captures work. The fix is: System Settings → Privacy & Security → Screen Recording → add the path to the binary that actually calls `screencapture` \u002F `peekaboo image`. For hermes-driven sessions, that's `\u002Fopt\u002Fhomebrew\u002FCellar\u002Fpython@3.14\u002F3.14.4\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.14\u002FResources\u002FPython.app` (or whatever Python your hermes gateway runs under). After granting, **relaunch the caller process** — TCC grants do not apply to already-running processes.\n\n**Pitfall — `tccutil reset ScreenCapture` alone is NOT enough.** `tccutil reset ScreenCapture` clears the existing grant, but the next call from a process that already tried to capture will still fail (the running process already saw the denial; the reset only affects the NEXT registration). To get the new permission to take effect, the calling process must be relaunched. For hermes gateway, that means killing + relaunching the session, which kills the agent that's trying to capture. **Plan accordingly**: deliver the text-based substitute proof, and offer the user a one-time manual fix that takes effect on the NEXT capture.\n\n**Diagnosis recipe (run in this order)**:\n```bash\n# 1. Find the cmux window id + sharing state — try swift first\nswift -e 'import Cocoa; let wl = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID) as? [[String:Any]] ?? []; for w in wl { if (w[\"kCGWindowOwnerName\"] as? String ?? \"\").lowercased().contains(\"cmux\") { let s = w[\"kCGWindowSharingState\"] as? Int ?? -1; print(\"wid=\\(w[\"kCGWindowNumber\"] ?? 0) sharing=\\(s) bounds=\\(w[\"kCGWindowBounds\"] ?? [:])\") } }'\n# Expect: sharing=0 (non-shareable) for the main content window\n\n# 2. Try screencapture\nscreencapture -x -l \u003Cwid> \u002Ftmp\u002Ftest.png\n# Expect: \"could not create image from window\"\n\n# 3. Fall back to cmux capture-pane (always works, no TCC)\ncmux capture-pane --workspace \u003Cw> --surface \u003Cs> --scrollback --lines 30\n```\n\n**No-swift fallback (added 2026-06-23):** When the parent shell has no swift (e.g. a non-dev hermes process), use the ctypes + CoreGraphics + Foundation + CoreFoundation recipe to enumerate windows. See `references\u002Fmacos-window-enumeration-ctypes.md` for the full Python recipe and the AppleScript `count`-on-CFArray pitfall that rules out osascript.\n\n**Reply shape when TCC blocks screencapture**:\n1. State the blocker explicitly (don't hand-wave — name the TCC error AND identify the calling process)\n2. Show the captured `cmux capture-pane` output as proof substitute\n3. Offer the one-time manual fix: name the **caller's** binary path (not \"Terminal\"), say to grant Screen Recording to that, then relaunch\n4. Do NOT claim \"screenshot delivered\"\n\n**Bug-ref:** 2026-06-19 — `workspace:16 surface:23 \"mobile load\"` (mobile slow-load investigation). User asked \"press send and show me screenshot of msg received.\" After 4 capture attempts (screencapture -l, -R with bounds, -D with display id, ScreenCaptureKit via swift), confirmed `kCGWindowSharingState=0` for the cmux window. Used `cmux capture-pane --scrollback` as the substitute proof and the user accepted the result. The agent was successfully driven and produced a full root-cause analysis (40.8s slow-load traced to autoscaling queue behind LLM turns pegging 4 vCPUs, fix: lower dev `containerConcurrency` 40 → 8-10 and `minScale` 1 → 2-3).\n\n**Bug-ref:** 2026-06-23 — PR #7848 fastembed. User asked \"cmux should already have visual capture, did you try it?\" I went straight to `screencapture -x` (4 failed attempts including region capture, window-list, and screencapture -l). The right move was to start with `cmux --help | grep -iE \"screenshot|capture|image\"` — cmux DOES have `cmux browser screenshot`, but it's Chromium-only (no terminal surface). Knowing that up front would have saved 4 failed capture attempts. The \"When the user asks for visual proof\" sub-pattern below codifies this.\n\n## Sub-pattern: \"User asks for visual proof of cmux (screenshot of message received \u002F show me the cmux window)\"\n\nThe user wants to SEE what's happening in the cmux window — typically a `❯` prompt, a churn\nspinner, or an error message. The temptation is to jump straight to `screencapture`, but\nthe **right first move is to enumerate cmux's own visual-capture APIs**:\n\n```bash\n# 1. Discover what cmux itself offers (verified on 2026-06-23)\ncmux --help 2>&1 | grep -iE \"screenshot|capture|image|visual|frame|render\"\n# Known cmux visual subcommands in 2026-06-23:\n#   - `browser screenshot [--out \u003Cpath>]`  ← Chromium browser surfaces ONLY\n#   - `feedback --image \u003Cpath>`            ← attach an image to a feedback report, NOT capture\n#   - `capture-pane ...`                   ← text only (the always-works substitute)\n#   - NO `cmux screenshot` for terminal surfaces\n```\n\n**If `cmux browser screenshot` exists, check the target surface type BEFORE calling.** Surfaces\nhave types: terminal vs browser. To enumerate:\n\n```bash\ncmux list-pane-surfaces --workspace \u003Cw> --pane \u003Cp> 2>&1\n# or via tree\ncmux tree --all --window \u003Cwin> 2>&1 | grep -B1 -A2 \"surface:\"\n# Look for type: terminal | browser annotations in the output\n```\n\nIf the target surface is **browser** (Chromium), `cmux browser screenshot --out \u002Ftmp\u002Fcmux.png`\nwill work — no TCC blocker because the Chromium renderer is in-process. If the target surface\nis **terminal** (Ghostty-backed), `browser screenshot` will error with \"not a browser surface\"\nor similar. **Fall through to `screencapture` only after confirming surface type.**\n\n**If `cmux` has no terminal-screenshot API (the 2026-06-23 reality):** the substitute proof is\n`cmux capture-pane --scrollback --lines N`. State the TCC blocker explicitly and use the\ntext capture as proof. **Do not** offer to \"try screencapture\" again — the window is\nnon-shareable (`kCGWindowSharingState=0`) and screencapture will fail.\n\n**Bug-ref: 2026-06-23 — PR #7848 fastembed steering.** User asked \"cmux should already have\nvisual capture, did you try it?\" I went straight to `screencapture -x -t png` (4 failed\nattempts) instead of starting with `cmux --help | grep -iE \"screenshot\"`. The right answer\ncame in one grep: cmux has `browser screenshot` (Chromium only) and `capture-pane` (text only)\n— no terminal-surface PNG API. Knowing that up front would have saved 4 capture attempts\nplus the diagnostic overhead of ctypes + AppleScript + osascript probing.\n\n## Sub-pattern: \"Idle-agent prompt contamination (autocompleter poisons pastes)\"\n\nWhen the worker is sitting at an empty `❯ ` prompt (idle, done with previous task), Claude\nCode's ghost-text autocompleter can fire on the prompt and inject text into the buffer\nwhen you paste a long message via `cmux send`. Symptom: the prompt at idle `❯ ` is empty,\nyou `cmux send` your message, but `read-screen` shows the buffer has both your text AND\ntrailing garbage like `also i always need intent classifier started up` (the autocompleter's\nbest-guess continuation of your paste). The whole contaminated buffer submits as one message\nand the agent parses your text + the garbage together.\n\n**Defense (apply in order):**\n\n1. **Keep idle-agent pastes SHORT (1-2 lines).** The 2026-06-12 coordination-notes pitfall\n   applies here too: shorter pastes are less likely to trigger the autocompleter's\n   \"complete this for you\" reflex. Split the task across multiple short sends, or use a\n   pointer to a shared artifact (PR comment, bead, Slack thread) where the agent can re-read\n   the long form.\n2. **Before sending, read-screen and confirm the prompt is empty.** If there's already\n   `❯ something`, send `ctrl+u` (multiple times) until read-screen shows empty `❯ `.\n3. **After sending, read-screen IMMEDIATELY (don't wait 5-10s).** If the visible buffer\n   text is longer than what you sent, abort the submission with `ctrl+c` + clear with\n   backspaces, then re-send a shorter version.\n4. **If the contaminated message already submitted,** the agent may parse it OR burn context\n   trying — in either case, post a follow-up clarification in the surface to anchor the\n   agent on the REAL intent and not the autocompleter's hallucination. The Opus session in\n   the 2026-06-23 case did parse past the garbage correctly (\"always init the classifier,\n   gate only the blocking wait\") but burned ~12k tokens on the corrupted payload.\n\n**Bug-ref: 2026-06-23 — PR #7848 fastembed revert.** I sent an 816-char operator note into\n`workspace:28 surface:60` (idle Opus session). The autocompleter appended\n\"also i always need invetn clasisifer runs always need intent classifier started up\" to the\nbuffer. The Opus read past the garbage and inferred the right intent, but burned ~12k tokens\non a corrupted payload AND the prompt was contaminated for follow-up steering. The lesson:\nwhen the user says \"steer this agent to fix X\", the message length budget is 1-2 lines even\nwhen the task description is 5 lines — split the task across multiple short sends, or use a\npointer to a shared artifact.\n\n## ⚠️ Preflight Validation\n\n**The current cmux CLI does NOT support `--json` as a global flag, and `list-surfaces` is NOT a\ncommand.** These were true in older cmux versions and may still appear in older skills \u002F muscle\nmemory. Always prefer `cmux \u003Ccommand> --help` before assuming a flag exists.\n\n**`$CMUX_SOCKET_PATH` default (`\u002Ftmp\u002Fcmux.sock`) is a placeholder, not a real path.** The real\ncmux socket is at a build-date-suffixed path (e.g. `\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock`). The\n`cmux` CLI hides this, but any raw-socket code (`nc -U`, Python `socket.AF_UNIX`, etc.) will\nsilently fail.\n\n**⚠️ `cmux identify` is NOT enough to find the workspace you need.** It returns the canonical\nsocket for the currently-running production cmux app, which on this machine has only 2\nworkspaces (both idle bash logins). All the actual agent workspaces live in the **dev build\nsocket** (`\u002Ftmp\u002Fcmux-debug-\u003Cbuild-tag>.sock`). For full workspace enumeration across ALL\nrunning cmux sessions (prod + dev + any future builds), use the multi-app probe recipe in\n`references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md` — this is the FIRST move when the\nuser names a specific workspace, NOT a fallback. See the new sub-pattern below for the\n2026-06-25 bug-ref.\n\n### Sub-pattern: `cmux` CLI itself fails with `Socket not found at \u002Ftmp\u002Fcmux-debug-\u003CX>.sock`\n\nThe CLI itself can fail this way even when the user has NOT set `CMUX_SOCKET_PATH` — verified\n2026-06-24: `cmux identify` returned\n`Error: Socket not found at \u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock` while a real socket existed at\n`\u002Ftmp\u002Fcmux-debug-may-18.sock`. Cause: a stale socket path is baked into the CLI's launchd-supplied\ndefault (different build's suffix). The CLI is not falling back to discovery.\n\n**Workaround** — bypass the CLI's hardcoded default by passing `--socket` (verified working):\n\n```bash\nls -1 \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock 2>\u002Fdev\u002Fnull\n# Find the real one, e.g. \u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\n\ncmux --socket \u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock identify\n# Or set the env var to the resolved path:\nCMUX_SOCKET_PATH=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock cmux list-workspaces --id-format both\n```\n\nIf `--socket` is also rejected, fall back to raw JSON-RPC over the resolved socket:\n```bash\nSOCKET=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\nprintf '{\"id\":\"q\",\"method\":\"workspace.list\",\"params\":{}}\\n' | nc -U -w 3 \"$SOCKET\"\n```\n\nThis is a different failure mode from the \"your env var is wrong\" case in\n`references\u002Fsocket-discovery-2026-06-21.md` — that one covers raw-socket callers using the\nplaceholder default; this one covers the CLI itself shipping with a stale default.\n\n### ⚠️ Sub-pattern: `cmux identify` succeeds but the workspace you need is NOT there\n\n**This is the trap that wastes the most turns. Read this FIRST when the user names a\nspecific workspace (e.g. \"find the agy workspace\", \"where is workspace:72\").**\n\n`cmux identify` returns the **canonical** socket for the currently-running cmux app —\nwhich on macOS is typically the *production* `\u002FApplications\u002Fcmux.app` instance using\n`$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock`. That instance often has only **2 workspaces**\n(both idle bash logins in `~`). All the actual agent workspaces (`workspace:69 hermes`,\n`workspace:72 w: misc - agy`, `workspace:70 daily jobs + deploy`, etc.) live in the\n**dev build** socket at `\u002Ftmp\u002Fcmux-debug-\u003Cbuild-tag>.sock` (e.g. `\u002Ftmp\u002Fcmux-debug-may-18.sock`).\n\n**`cmux identify` is authoritative for \"where is the focused surface in the canonical\nsocket\" — NOT for \"where are the agent workspaces\".** Treating it as the latter leads\nto a long sequence of `cmux list-workspaces` calls that return 2 workspaces, then\nconcluding \"the workspace the user named doesn't exist in this cmux session.\"\n\n**The right first move — always run the multi-app probe before concluding a workspace\ndoesn't exist.** Even for ONE-shot interactive scripts:\n\n```bash\n# Run the multi-app probe from references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md\n# to find ALL live cmux sessions, not just the canonical one:\ndeclare -a CANDIDATES=()\nwhile IFS= read -r s; do\n  CANDIDATES+=(\"$s\")\ndone \u003C \u003C(ls -1t \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock \\\n              ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock 2>\u002Fdev\u002Fnull | awk '!seen[$0]++')\n\n# Probe each. Pick the first one that returns valid JSON from `cmux identify`.\n# Then run `cmux --socket \u003Cpath> list-workspaces --id-format both` to see ALL\n# workspaces across that session.\nfor cand in \"${CANDIDATES[@]}\"; do\n  [ -S \"$cand\" ] || continue\n  probe=$(timeout 5 cmux --socket \"$cand\" identify 2>\u002Fdev\u002Fnull || true)\n  if [ -n \"$probe\" ]; then\n    echo \"=== $cand ===\"\n    cmux --socket \"$cand\" list-workspaces --id-format both 2>&1 | head -5\n  fi\ndone\n```\n\nIf you find a workspace in the dev socket that you didn't see in the canonical\nsocket, that's the dev session your work belongs in. Use `--socket \u003Cdev-path>` for\nall subsequent `cmux` calls.\n\n**Bug-ref 2026-06-25 — AGY persona-bleed probe sequence.** User asked: \"find the cmux\nworkspace for agy using agy locally for inference.\" First probe used `cmux.sock`\n(canonical) — got 2 workspaces, neither named agy. Concluded \"the AGY workspace\ndoesn't exist in this cmux session\" and burned 2-3 turns on the wrong track.\nUser pushed back: *\"find the socket again, it should be working.\"* Re-ran the\nmulti-app probe and found the AGY workspace at `workspace:72 \"w: misc - agy\"` in\nthe dev socket `\u002Ftmp\u002Fcmux-debug-may-18.sock` (along with all 28 agent workspaces).\nLesson: when the user names a specific workspace, the multi-app probe is the FIRST\nmove, not a fallback for when `cmux identify` returns clean JSON.\n\n### Sub-pattern: cmux CLI flag-order gotchas in launchd scripts (verified 2026-06-26)\n\nTwo non-obvious footguns hit when scripting cmux from `launchd` jobs (no interactive\nshell context):\n\n**1. `--socket` is a GLOBAL flag, not a subcommand flag.**\n\n```bash\n# WRONG — errors with \"tree: unknown flag '--socket'. Known flags: --all --workspace --window --json\"\ncmux tree --window window:1 --socket \u002Ftmp\u002Fcmux-debug-may-18.sock\n\n# CORRECT — global flag, comes right after `cmux`\ncmux --socket \u002Ftmp\u002Fcmux-debug-may-18.sock tree --window window:1\n```\n\nVerified 2026-06-26 with `cmux 0.64.16`. If you put `--socket` after the subcommand,\nthe subcommand's own flag parser rejects it AND the call silently returns empty,\nwhich a defensive script will treat as \"no data\" rather than \"bad invocation.\" Always\nput `--socket` right after `cmux`.\n\n**2. `CMUX_SOCKET_PATH` env var is silently IGNORED by the CLI in launchd-style env.**\n\n```bash\n# WRONG — in launchd-style env, the CLI ignores CMUX_SOCKET_PATH and falls back to\n# its baked-in stale default (\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock).\n# cmux tree --window window:1 → empty (no error, just empty)\nenv CMUX_SOCKET_PATH=\u002Ftmp\u002Fcmux-debug-may-18.sock cmux tree --window window:1\n\n# CORRECT — use --socket flag explicitly\ncmux --socket \u002Ftmp\u002Fcmux-debug-may-18.sock tree --window window:1\n```\n\nWhy `CMUX_SOCKET_PATH` doesn't work in launchd: the CLI's resolution algorithm\nappears to be (a) read `\u002Ftmp\u002Fcmux-last-socket-path` if it exists, (b) else fall back\nto the baked-in build default. In a launchd tick, that pointer file may be stale\n(pointing at the prod socket with 2 idle bash workspaces) or missing entirely.\nSetting `CMUX_SOCKET_PATH` in the env does NOT override the pointer file lookup —\nverified by setting it explicitly in `env -i PATH=... HOME=...` and still getting\nthe stale-default error. **For any launchd\u002Fcron script: use `--socket \u003Cpath>`, never\n`CMUX_SOCKET_PATH`.**\n\n**Bug-ref 2026-06-26 — cmux-surface-report-4h.sh false-positive skip alert.**\nCron tick at `2026-06-26T00:03:51Z` fired `:warning: cmux RPC wedged (tree --window\nAND --all both empty)` to `#home`. Two causes:\n- `\u002Ftmp\u002Fcmux-last-socket-path` pointed at `$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock`\n  (prod socket, 2 workspaces) while all agent work lives on the dev socket.\n- The script used `env CMUX_SOCKET_PATH=$SOCK cmux tree --window window:1`, which\n  in launchd-style env falls back to the baked-in stale default and returns empty.\n\nFix shipped in `scripts\u002Fcmux-surface-report-4h.sh` (commit 351e9cf7c8): multi-app\nprobe at socket-resolve time + `cmux --socket \"$SOCK\" ...` for every CLI call.\nVerified end-to-end: tick at `2026-06-26T00:28:21Z` posted full 35-surface report\nto `C0AJQ5M0A0Y` (ts `1782433701.994439`) instead of skipping.\n\n### Sub-pattern: Two cmux sessions can be live at the same time (verified 2026-06-24)\n\nThe `cmux-last-socket-path` and the `~\u002F.local\u002Fbin\u002Fcmux` shim point to **one** session at a time\n(usually the most recently launched). But on the same host, **two** cmux host apps can be\nrunning concurrently — e.g. `\u002FApplications\u002Fcmux.app` (prod) and `\u002FApplications\u002Fcmux DEV\nmay-18.app` (a debug build) both have IPC sockets listening.\n\n**Diagnostic — enumerate ALL live cmux sessions:**\n\n```bash\nls -1 \u002Ftmp\u002Fcmux*.sock \u002Fprivate\u002Ftmp\u002Fcmux*.sock 2>\u002Fdev\u002Fnull\n# Plus user-state cmux sockets:\nls -1 ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock 2>\u002Fdev\u002Fnull\nls -1 ~\u002F.local\u002Fstate\u002Fcmux\u002F*-last-socket-path 2>\u002Fdev\u002Fnull\n\n# For each, test if anything is actually listening:\nfor s in \u002Ftmp\u002Fcmux-debug-*.sock ~\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock; do\n  if [ -S \"$s\" ]; then\n    r=$(printf '{\"id\":\"q\",\"method\":\"ping\",\"params\":{}}\\n' | nc -U -w 2 \"$s\" 2>&1)\n    echo \"$s → $r\"\n  fi\ndone\n# Each entry that returns a JSON response (even `{\"error\":\"method_not_found\"}`) is a LIVE session.\n# The shim may be pointing at a dead one; the other is the real one.\n```\n\n**Bug-ref 2026-06-24 — cmux review of cost\u002Fcaching workspaces:**\n- Session A (debug build): `\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock` — 27 workspaces including all\n  the cost\u002Fcaching work (workspace:88 \"cost: reviewer\", workspace:68 \"cost: implicit cache\",\n  workspace:81 \"cost: system inst\", workspace:73 \"local cache\", workspace:84 \"proxy: context\")\n- Session B (prod `cmux.app`): `$HOME\u002F.local\u002Fstate\u002Fcmux\u002Fcmux.sock` — 2 workspaces\n  (workspace:1 and workspace:2, both idle bash logins)\n- The shim was pointing at a *third* dev CLI (`\u002Ftmp\u002Fcmux-debug-fix-agy-hook-deny.sock`) that\n  didn't exist on disk. Every `cmux \u003Cverb>` failed with \"Socket not found\" until I bypassed\n  with `--socket` and tried both real sessions.\n\n**Rule:** when the shim fails, enumerate ALL cmux*.sock files and probe each with `nc -U\nping`. Both prod AND dev sessions may be live, with different workspace sets. The user's\n\"the cost work\" might be in the debug session, not the prod one (or vice versa).\n\n**When reporting a \"what are the terminals doing\" summary, check both sessions** — the\nworkspaces you care about may be in the non-default one. The `cmux-terminal-review` skill\nassumes one socket; for two-session reality, run the inventory against both and merge\nthe results before categorizing.\n\n**For unattended\u002Flaunchd jobs that MUST work with any cmux build** (where you can't guarantee\nwhich build will be running), use the multi-app probe recipe that iterates ALL candidate sockets\nacross `\u002Ftmp` + `\u002Fprivate\u002Ftmp` + `~\u002F.local\u002Fstate\u002Fcmux\u002F` and picks the first one whose\n`cmux --socket \u003Cpath> identify` returns valid JSON. See\n`references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md` for the full bash template.\nVerified 2026-06-24 — supersedes `socket-discovery-2026-06-21.md` for unattended runs.\n\nCommon traps (verified 2026-06-11):\n\n- `cmux list-surfaces ...` → **does not exist**. Real options: `cmux tree --all`,\n  `cmux list-panes --workspace \u003Cid>`, `cmux list-pane-surfaces --workspace \u003Cid> --pane \u003Cid>`.\n- `cmux list-surface` (singular) → wrong subcommand; also doesn't exist.\n- `cmux --json \u003Ccommand>` → `--json` is not a global flag. Per-command JSON lives behind\n  `cmux \u003Ccommand> --json` (works for some commands like `identify`, `top`, `list-workspaces`,\n  `auth status`, `ssh-session-list`).\n- `cmux --workspace \u003Cid> --surface \u003Cid> \u003Ccommand>` → wrong ordering. The actual pattern is\n  `cmux \u003Ccommand> --workspace \u003Cid> --surface \u003Cid>` (flags after the subcommand).\n- `cmux send \"text\"` (bare) → in current CLI `send` requires `--workspace` and `--surface`\n  (no default to caller surface). Use `cmux send --workspace \u003Cw> --surface \u003Cs> \"text\"`.\n- `cmux send-surface --surface \u003Cid> \"cmd\"` → `send-surface` does not exist. Use\n  `cmux send --surface \u003Cid> \"cmd\"`.\n- `cmux send-key enter` → exists, but keys must match the documented set: `enter`, `tab`,\n  `escape`, `backspace`, `delete`, `up`, `down`, `left`, `right`, etc.\n- `cmux new-split` → exists but takes a positional direction (`left`\u002F`right`\u002F`up`\u002F`down`),\n  not `--direction`. It also needs `--workspace` and a surface to split from\n  (e.g. `cmux new-split --workspace \u003Cw> --surface \u003Cs> right`).\n- **`cmux select-workspace --workspace 5` does NOT focus `workspace:5`.** The bare\n  integer is interpreted as an **index** (positional, sorted by tab order — NOT by\n  `ref` number). The index list is not sorted by `ref`, so `index 5` can map to\n  any workspace. Verified 2026-06-13: `select-workspace --workspace 5` returned\n  `OK workspace:12` and switched focus there instead of the user's `workspace:5\n  \"bq logging\"`. The trap is silent — it does return `OK workspace:\u003CX>`, just not\n  the X you expected. **Always use the explicit ref form** `--workspace workspace:5`\n  when you mean a specific ref. If you only have the integer from\n  `cmux list-workspaces`, run `cmux list-workspaces --id-format both` first and use\n  the ref (`workspace:N`), not the integer. Full recipe (including the\n  raw-socket alt when the CLI is misbehaving): see\n  `references\u002Fworkspace-routing-trap.md`.\n\n### ⚠️ `cmux read-screen --workspace 62` (bare integer) FAILS — use `--workspace workspace:62`\n\nSame trap class as `select-workspace` above, but for the read\u002Finspect family.\nVerified 2026-06-25 (lvl\u002Fcost recap probe): `cmux read-screen --workspace 62` (and\n`--workspace 68`, `--workspace 81`, etc.) all returned `Error: Workspace index not\nfound`, even though `cmux list-workspaces` clearly showed `workspace:62 \"lvl:\nrefactor\"`. Same for `--workspace 88`, `--workspace 89` — every bare-integer read\nfailed silently with that one-line error.\n\n**Why:** the bare integer is parsed as a positional **index**, but `--workspace 62`\ntriggers a different code path than `select-workspace` does — `select-workspace`\nswallows the index-vs-ref ambiguity and silently re-targets to whatever index 62\nis, while `read-screen` \u002F `read-pane` \u002F `focus-pane` reject the bare integer with\n`Error: Workspace index not found` and never read anything.\n\n**Fix:** always pass the **ref form** `--workspace workspace:62`. Verified working:\n\n```bash\n# BAD — all of these fail with \"Workspace index not found\"\ncmux read-screen --workspace 62 --surface 1\ncmux tree --workspace 62\ncmux list-panes --workspace 62\n\n# GOOD — ref form works\ncmux read-screen --workspace workspace:62 --surface surface:1\ncmux tree --workspace workspace:62\ncmux list-panes --workspace workspace:62\n```\n\n**Recipe (always):** copy the `workspace:N` ref straight from `cmux list-workspaces`\noutput — never type the integer. The same applies to surface refs (`surface:M`,\nnot `1`) and pane refs (`pane:M`, not `1`). If you only have an integer from a\nprior session, run `cmux list-workspaces --id-format both` to get the ref →\nindex mapping, then use the ref.\n\n**Bug-ref:** 2026-06-25 — lvl\u002Fcost recap probe across 6 workspaces\n(`lvl: refactor`, `cost: implicit cache`, `cost: system inst`, `cost: reviewer`,\n`local cache`, `rag: extended`). First probe batch used `--workspace 62` etc.\nand failed with the same error 6 times. Re-ran with `--workspace workspace:62`\nand every read succeeded in one call each.\n\nIf a command fails with `Error: Unknown command:` or `Usage:` dump, run `cmux \u003Ccommand> --help`\nto see the real syntax — do not guess.\n\n**`surface.readScreen` does not exist as a method.** The right\nJSON-RPC method is `surface.read_text` — but see the warning in\n\"Reading a Non-Focused Surface\" above: it ignores ref params.\n**`surface.focus` requires `surface_id` (UUID), not `surface_ref`.**\n**`surface.list` ignores `workspace_ref` — returns the focused\nworkspace's data.** Full transcript: `references\u002Fsurface-read-routing-bug.md`.\n\n## ⚠️ Status Bar Interpretation — NOT Frozen\n\nClaude Code status bar states to interpret correctly:\n- **`⏵⏵ bypass permissions on`** in status bar = normal Claude Code prompt UI, NOT a blocking dialog — Claude Code is actively working around it\n- **Active churning timestamps** (e.g., \"Churned for 9m 41s\", \"Sautéed for 2m 38s\") = genuinely working workspace, NOT frozen\n- **\"Still running\" indicator** = work in progress, not stalled\n- **Idle bash shell** (fresh login prompt) = workspace is done or waiting for input, NOT frozen\n- **ctx XX% progress** = active context usage, workspace is alive\n\n**What IS actually blocked:**\n- `bypass permissions on (shift+tab to cycle)` with NO active churning\u002Ftime-on-task and NO progress indicator = may be a real stall\n- \"Index error\" or \"workspace unreachable\" = workspace handle drift, genuinely blocked\n- Shell at `claude`\u002F`claudem` typed but no Claude Code active = shell-level stall, needs restart\n- \"Add a follow-up\" dialog open in Composer 2 Fast = blocked on human follow-up, not frozen\n\n**Key rule:** A workspace with an active time-on-task label (\"Churned for Xm\", \"Crunched for Xm\") is working, regardless of what the status bar shows. Only report as \"frozen\" when there's evidence of no activity AND no active time-on-task.\n\n## Reading a Non-Focused Surface (verified 2026-06-13)\n\n`cmux read-screen --workspace X --surface Y` and the raw\n`surface.read_text {workspace_ref, surface_ref}` JSON-RPC method are\n**both broken** in the current build — they return the focused surface's\ncontent, not the target. The CLI also rejects many valid targets with\n`Error: invalid_params: Surface is not a terminal` (it routes to a\nwrong internal handle before the type check).\n\n**The reliable recipe — focus-then-read:**\n\n```bash\n# 1. Discover the target surface's UUID\n#    Focus the workspace, then raw-socket call surface.list (the\n#    workspace_ref param is ignored, so you must focus first)\ncmux select-workspace --workspace N\nSOCKET=$(cmux identify | python3 -c \"import json,sys;print(json.load(sys.stdin)['socket_path'])\")\nUUID=$(printf '{\"id\":\"q\",\"method\":\"surface.list\",\"params\":{}}\\n' \\\n  | nc -U -w 3 \"$SOCKET\" \\\n  | python3 -c \"import json,sys; d=json.load(sys.stdin); print([s['id'] for s in d['result']['surfaces'] if s['ref']=='surface:M'][0])\")\n\n# 2. Focus the target surface (param is surface_id=UUID, NOT surface_ref)\nprintf '{\"id\":\"q\",\"method\":\"surface.focus\",\"params\":{\"surface_id\":\"%s\"}}\\n' \"$UUID\" \\\n  | nc -U -w 3 \"$SOCKET\" >\u002Fdev\u002Fnull\n\n# 3. Read the now-focused surface (no --surface flag)\ncmux read-screen --scrollback --lines 50\n```\n\n`surface.focus` requires `surface_id` (the UUID). It rejects\n`surface_ref` and bare integers. See\n`references\u002Fsurface-read-routing-bug.md` for the full transcript and\nupstream-bug filing notes.\n\n**Faster \"is this worker alive?\" diagnosis without per-surface focus:**\n\n1. `cmux tree --all` — pane titles include active time-on-task labels\n   (e.g., \"Churned for 9m 41s\", \"Flibbertigibbeting… 12m 34s\",\n   \"Sautéed for 2m 38s\"). A pane WITHOUT such a label is more likely\n   dead\u002Fstalled. No read needed.\n2. `tail -f \u002Ftmp\u002Fcmux-debug-may-18.log` (or current build's log) — look\n   for `workspace.gitProbe.apply` and `workspace.prRefresh.apply`\n   lines; their presence proves the surface is producing events.\n\n## \"What is this workspace doing?\" — The Discovery Recipe\n\nThis is the most common cmux task from the gateway: user names a workspace (e.g. \"agentf\") and\nyou need to (a) find it, (b) see what surface is focused, (c) read the actual screen text, and\noptionally (d) steer. Verified sequence (2026-06-11):\n\n```bash\n# 1. Find the workspace by partial title\ncmux list-workspaces                       # text output, refs like \"workspace:10\"\ncmux list-workspaces --id-format both      # adds UUIDs\n\n# 2. Get the pane\u002Fsurface tree (titles + tty + focus state)\ncmux tree --all                            # whole window\ncmux tree --all --window \u003Cwin_ref>         # single window\n\n# 3. Confirm focus + identify a specific surface\ncmux identify                              # caller context + currently focused\ncmux identify --workspace \u003Cw> --surface \u003Cs>  # one surface's context\n\n# 4. Read the actual terminal text (the critical step) — only works\n#    reliably for the FOCUSED surface. For non-focused surfaces see\n#    \"Reading a Non-Focused Surface\" above (focus-then-read recipe).\ncmux read-screen --workspace \u003Cw> --surface \u003Cs>           # visible viewport only\ncmux read-screen --workspace \u003Cw> --surface \u003Cs> --scrollback --lines 200   # last 200 lines of scrollback\n\n## \"Should I steer now or wait?\" — The In-Flight Worker Decision Rule\n\nWhen the user says \"steer this worker\" \u002F \"use natural language to fix\n[problem X]\", the worker may already be in the middle of investigating\nor fixing something — possibly the same thing, possibly something else.\nSteering without checking risks context bleed and double-work.\n\n**Rule (verified pattern, 2026-06-13; extended 2026-06-21):**\n\n1. **Read the focused surface** of the target workspace\n   (`cmux read-screen --scrollback --lines 60`).\n2. **Check for active subagents** in the bottom 30 lines of viewport\n   — Claude Code shows them as:\n   - `✻ Waiting for N background agents to finish`\n   - `◯ \u003Csubagent-name>  \u003Ctask>  \u003Ctime>  \u003Ctokens>`\n   - `✻ \u003Cchurn-phrase> (\u003Ctime>  · ↓ \u003Ctokens>)`\n3. **Decide:**\n   - **Worker is idle and at a prompt** (`❯` with empty input) →\n     safe to steer now. Send the natural-language instruction and\n     press Enter.\n   - **Worker is actively churning** (churn label + time counter\n     advancing) → wait. Note the churn time; if it exceeds ~5 min\n     with no progress visible, the worker may be stuck and may need\n     an Escape + re-steer.\n   - **Worker has 1+ in-flight subagents** → do NOT steer. Wait for\n     subagents to finish (the parent will then continue). If the\n     new task is URGENT and unrelated, ask the user to confirm\n     \"interrupt the current investigation\" before sending Escape +\n     new message.\n   - **Worker hit a session rate limit** (\"You've hit your session\n     limit · resets \u003Ctime>\") → can't steer this session. Either\n     wait for reset or open a new surface and start fresh.\n\n4. **Before steering: read the worker's recent task context, not just\n   its current state** (verified pitfall 2026-06-21). Scroll back\n   further than 60 lines — the worker's last 200 lines typically\n   contain the \"user's request was: …\" recap block. **If the worker\n   is already working on the same problem the new steering message\n   is about, do NOT send a from-scratch instruction.** Instead,\n   send a *verification-first* steer that:\n   - Quotes the user's exact words and channel context.\n   - Asks the worker to (a) confirm whether its current PR\u002Fbranch\n     covers the new request, (b) state what additional work is\n     needed if any, (c) report back. **Do not** reissue the\n     original task — that re-runs it and risks duplicate PRs.\n   - Explicitly tells the worker \"Do NOT merge \u002F Do NOT fake-approve\n     CR \u002F Do NOT resolve threads\" if the existing PR is still\n     mid-review.\n   - Asks the worker to post a brief in-thread status note so the\n     user knows steering landed.\n\n   This pattern preserves the worker's progress (which may be\n   hours of design + test work) and converts a \"do this from\n   scratch\" steering into a \"verify + amplify\" steering. Burned\n   once on 2026-06-21: a user said \"find the hermes workspace and\n   steer it to fix this routing bug\" — the worker already had\n   PR #665 open covering exactly the same routing bug. A naive\n   \"fix the routing bug\" steer would have either duplicated the\n   work or trampled the existing branch. The verification-first\n   steer landed cleanly in 6s.\n\n**Steering template (Claude Code worker):**\n\n```bash\nWS=4; SURF=45\ncmux select-workspace --workspace $WS\ncmux send --workspace $WS --surface surface:$SURF \"$(cat \u003C\u003C'EOF'\nAfter your current investigation finishes, also start a\n\u003Crepro|\u002Frepro|\u002Froot-cause-first\u002Fetc.> worker for\n\u003Cbug description + URL>. Prefer \u003Cfix approach> over \u003Canti-pattern>.\nReport findings here when done.\nEOF\n)\"\ncmux send-key --workspace $WS --surface surface:$SURF enter\n# Verify: spinner\u002Fchurn label should appear within ~5s\nsleep 5 && cmux read-screen --scrollback --lines 15\n```\n\n**Why this rule exists:** in 2026-06-13 a user asked \"investigate\nwhy workers are dead, then steer the ao workspace to fix\n\u003Ccharacter-creation bug>\". The ao worker was already mid-investigation\non the worker-death question (agy failure investigation, 16+ min\nrunning). Steering immediately would have aborted a high-value\ninvestigation and risked context bleed between two different bugs.\nThe right move was to report findings + ask the user to confirm\n\"wait for current subagents, or interrupt and steer now?\".\n\n## Steer (if appropriate) — Sending the actual message\n\nOnce you've decided the worker is safe to steer (see the In-Flight\nWorker Decision Rule above), the actual send is straightforward:\n\n```bash\ncmux send --workspace \u003Cw> --surface \u003Cs> \"your message\"   # types text\ncmux send-key --workspace \u003Cw> --surface \u003Cs> enter         # presses Enter\n```\n\n## Multi-surface parallel steering (verified 2026-06-24, 4 steers in one batch)\n\nWhen the user gives a directive that maps to **N independent workspaces**\n(\"steer all the cost workspaces\", \"kick off the next steps in\neach PR review surface\", \"fan out the Slack-pivot instructions\"),\n**send the steers in one parallel batch** then verify each after a\nshared settle period. This is faster than serial and safer than\nsending all-then-verifying one at a time.\n\n**The pattern:**\n\n```bash\nexport CMUX_SOCKET_PATH=\u002Fprivate\u002Ftmp\u002Fcmux-debug-may-18.sock\n\n# 1. Identify all target (ws, surface) pairs from the user's directive\nTARGETS=(\"81 229\" \"73 139\" \"84 223\" \"70 132\")  # (workspace surface) tuples\n\n# 2. Filter: only steer surfaces that are SAFE per the In-Flight Worker Rule\n#    - empty ❯ prompt\n#    - no active churning label\n#    - not mid-Edit on a different file\n#    Skim each surface's recent output first; skip the busy ones.\nSAFE_TARGETS=(\"81 229\" \"73 139\" \"84 223\" \"70 132\")  # after triage\n\n# 3. Send all steers + Enter keys in one batch (parallel-safe: each\n#    cmux call is its own short-lived subprocess)\nfor pair in \"${SAFE_TARGETS[@]}\"; do\n  set -- $pair\n  ws=$1; surf=$2\n  cmux send --workspace=workspace:$ws --surface=surface:$surf \"your steer here\"\n  cmux send-key --workspace=workspace:$ws --surface=surface:$surf enter\ndone\n\n# 4. Single settle wait, then verify EACH surface absorbed the steer\nsleep 10\nfor pair in \"${SAFE_TARGETS[@]}\"; do\n  set -- $pair\n  ws=$1; surf=$2\n  echo \"=== w${ws}\u002Fs${surf} ===\"\n  cmux read-screen --workspace=workspace:$ws --surface=surface:$surf --lines 15 \\\n    2>&1 | grep -vE '^\\s*Error:' | tail -8\ndone\n```\n\n**What \"absorbed\" looks like:**\n- An active churning label appears (e.g. `✻ Beboppin'…`, `Brewed for`, `Creating follow-up branch…`)\n- The worker's first action matches the steer (new branch name, new commit, new tool call)\n- A new task list item appears with the steer's content\n\n**What \"stuck\" looks like (the steer landed but worker didn't act):**\n- Message text is visible at the prompt but no churn label\n- An API error appears: `Unable to connect to API (ConnectionRefused)`, `You've hit your session limit`\n- Worker reverts to a previous task or context\n\n**Bug-ref 2026-06-24 — parallel steer of 4 cost workspaces** (workspace:88\n\"cost: reviewer\", workspace:68 \"cost: implicit cache\", workspace:81 \"cost:\nsystem inst\", workspace:73 \"local cache\", workspace:84 \"proxy: context\",\nworkspace:70 \"daily jobs + deploy\"):\n- 4 of 6 steers absorbed cleanly (w81 began executing the \u002Fgoal I sent,\n  w84 created a new branch + PR within 2 min, w70 sat at the prompt\n  holding the operator runbook message)\n- 1 was correctly skipped (w88 \"cost: reviewer\" had 4 active teammates\n  on a fan-out — In-Flight Worker Rule fired, no steer)\n- 1 surfaced a separate agent-side issue (w73 \"local cache\" hit\n  `API Error: Unable to connect to API (ConnectionRefused)` — Claude\n  API auth problem on the worker's CLI, not a steer problem)\n\n**The settle-wait before verify** is critical: 5s is too short for the\nworker to react. 10s is the sweet spot for a healthy agent; 30s for\none mid-thought on a 50+ minute run. After verify, if a steer didn't\nabsorb (no churn, no action, message still visible), DO NOT resend\nthe same message — that risks double-submission. Instead, treat it\nas a separate issue and surface to the user.\n\n**Don't:**\n- Don't send 4 steers + 4 separate verify-reads in 4 sequential cmux\n  calls — that's 8 round-trips. The batch pattern above is 4 sends\n  + 4 enter-keys + 4 verify-reads in 2 batched shells.\n- Don't steer a worker that's mid-Edit on a different file (per\n  In-Flight Worker Rule) just because the user said \"all of them.\"\n  Filter first; surface the skipped ones in your report.\n- Don't resend the same message if a steer didn't absorb — that's\n  the double-submission anti-pattern. Investigate the surface state\n  first.\n\n**Handles:** `workspace:10`, `pane:17`, `surface:19`, `window:1` are all valid refs. UUIDs\nwork too (`--id-format both`). Indexes (1-based across the window) also work but refs are\nunambiguous.\n\n**Default socket path:** the cmux app uses `\u002Ftmp\u002Fcmux-debug-may-18.sock` or similar (the\nfilename is build-date-suffixed). `cmux` CLI figures it out automatically. The\n`CMUX_SOCKET_PATH` env var is for raw socket access (Python client) — set it to the real\nsocket path returned by `cmux identify` (field `socket_path`).\n\n## \"Find the cmux workspace for X\" — Load `cmux-find-workspace-by-topic`\n\n**Before doing anything else when the user says \"find the cmux workspace for X\":** load the dedicated skill `cmux-find-workspace-by-topic`. It codifies the multi-socket probe + keyword grep recipe that found `workspace:72 \"w: misc - agy\"` in 3 seconds (2026-06-25) after the shim's dead-default hid it. Don't start from `cmux list-workspaces` — that may be pointed at a dead dev CLI.\n\n## Sub-pattern: \"User asks for meta \u002F skillify \u002F audit of cmux workflows\" (added 2026-06-26)\n\nWhen the user asks for **meta work on cmux** (not actual steering\u002Ffinding):\n\n> \"skillify cmux\", \"improve hermes confusion with cmux workspace finding\",\n> \"audit the cmux skills\", \"review cmux routing\", \"\u002Flearn cmux workflow\",\n> \"do not do the actual work, do meta work and \u002Fskillify\"\n\n…**DO NOT propose new cmux skills first.** Three cmux skills already exist and cover the territory:\n\n| Skill | When it fires |\n|---|---|\n| `cmux` (this one) | CLI\u002Fsocket reference, multi-session probe, send→submit→proof, focus-then-read, in-flight worker rule, stale-screen trap |\n| `cmux-find-workspace-by-topic` | Multi-socket probe + keyword grep (the dead-shim-hides-the-workspace trap) |\n| `cmux-mcp-server-options` | Community MCP server list, install recipe, decision guide, \"Hermes doesn't need one today\" |\n\n**The right first move (verified 2026-06-26):**\n\n1. **Load the 3 existing skills** and audit what's already covered. Most \"new\" proposals are already addressed (multi-socket probe, focus-then-read, `--socket` global flag, launchd env var ignore, send→submit→proof ritual, parallel steering, in-flight worker rule, stale-screen trap, workspace-routing trap, MCP server list).\n2. **Identify the SPECIFIC gap** the user is actually hitting (e.g. \"skills don't auto-load\" vs \"no workspace catalog\" vs \"skills overlap\" — each has a different fix).\n3. **Then propose** 1-2 targeted skills or skill patches — not 4 speculative ones.\n\n**Reply shape for meta audits** (per user preference `Reply shape: evidence + 2-4 next-step options + STOP`):\n- Lead with evidence: \"skills already cover X, Y, Z (verified [date]). Real gap: [specific thing].\"\n- Don't list every potential new skill — list only the ones that pass the \"this would have prevented a real session burn\" test.\n- End with 2-4 next-step options + STOP, NOT a long speculation list.\n\n**Bug-ref 2026-06-26 — meta audit of cmux confusion.** User said: *\"do not do the actual work discussed, do meta work and \u002Fskillify and \u002Flearn to improve hermes confusion with cmux workspace finding and steering and \u002Fresearch to see if cmux has an mcp server.\"* I loaded the 3 cmux skills, verified the MCP server registry state, and proposed 4 NEW skills (`cmux-steering-workflow`, `cmux-workspace-catalog`, etc.) before checking the existing skill coverage. Three of those four were already covered by the parent + 2 subs. The real gap was \"skills don't auto-load on meta-audit phrasing\" — fixed by broadening `cmux-find-workspace-by-topic` description triggers, not by adding new skills. The lesson: **meta audits are mostly skill audits, not skill creation. Lead with coverage check, then propose only the gaps that survive it.**\n\n## MCP servers for cmux (community, not upstream)\n\ncmux itself does NOT ship an MCP server (verified v0.64.17, 2026-06-23). Multiple community MCP servers wrap the cmux CLI\u002Fsocket — see the dedicated `cmux-mcp-server-options` skill for the verified list, install recipe, and decision guide (`@jsamuel1\u002Fcmux-mcp` is the hardened fork most AI agents use).\n\nFor Hermes's own automation today, use the CLI over shell — Hermes doesn't currently register MCP clients for itself.\n\n## Socket Connection (raw JSON-RPC)\n\n```bash\n# Default socket — confirm with `cmux identify` (look at socket_path field)\nSOCKET_PATH=\"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\"\n```\n\nSend JSON-RPC requests (newline-delimited):\n```json\n{\"id\":\"req-1\",\"method\":\"workspace.list\",\"params\":{}}\n{\"id\":\"req-2\",\"method\":\"workspace.tree\",\"params\":{\"all\":true}}\n{\"id\":\"req-3\",\"method\":\"surface.readScreen\",\"params\":{\"workspace_ref\":\"workspace:10\",\"surface_ref\":\"surface:19\",\"scrollback\":true,\"lines\":200}}\n```\n\n`scripts\u002Fcmux_client.py` is a small Python wrapper that does this over a Unix socket\n(`CMUX_SOCKET_PATH` env, default `\u002Ftmp\u002Fcmux.sock`). Use it from inline scripts.\n\n## Error \u002F stderr Handling\n\n```python\n# Always emit terminal status even on error\ntry:\n    result = subprocess.run([\"cmux\", \"tree\", \"--all\"], capture_output=True, text=True, timeout=10)\nexcept Exception as exc:\n    msg = f\":fire: cmux error: `{exc}`\"\n    slack.post_message(channel_id, msg, thread_ts=thread_ts)\n    return\n\n# Truncate large output for Slack display\nif len(result.stderr) > 2000 or result.stderr.count(\"\\n\") > 20:\n    summary = truncate_output(result.stderr)\n    slack.post_message(channel_id, f\":warning: cmux output truncated:\\n```\\n{summary}\\n```\", ...)\n```\n\n## CLI Quick Reference (current cmux)\n\n> **The most up-to-date command list lives in `references\u002Fcli-cheatsheet.md`.** The tables below\n> are the high-traffic subset verified on 2026-06-11.\n\n### Discovery (read-only)\n\n| Action | Command |\n| --- | --- |\n| List workspaces | `cmux list-workspaces` (add `--id-format both` for UUIDs) |\n| Workspace tree | `cmux tree --all` |\n| List windows | `cmux list-windows` |\n| Current window | `cmux current-window` |\n| List panes in a workspace | `cmux list-panes --workspace \u003Cw>` |\n| List surfaces in a pane | `cmux list-pane-surfaces --workspace \u003Cw> --pane \u003Cp>` |\n| Identify caller\u002Ffocus | `cmux identify` (add `--workspace \u003Cw> --surface \u003Cs>`) |\n| Read screen text | `cmux read-screen --workspace \u003Cw> --surface \u003Cs> [--scrollback --lines N]` |\n| Surface health | `cmux surface-health [--workspace \u003Cw> --surface \u003Cs>]` |\n| Capabilities | `cmux capabilities` |\n| Stream events | `cmux events [--name \u003Cname>] [--limit N]` (can block; set a timeout) |\n\n### Workspace \u002F window control\n\n| Action | Command |\n| --- | --- |\n| Create workspace | `cmux new-workspace` (add `--cwd \u003Cpath>`, `--command \u003Ccmd>`) |\n| Select workspace | `cmux select-workspace --workspace \u003Cid>` |\n| Close workspace | `cmux close-workspace --workspace \u003Cid>` |\n| Rename workspace | `cmux rename-workspace --workspace \u003Cid> \"name\"` |\n| Reorder | `cmux reorder-workspace --workspace \u003Cid> --index N` |\n| Workspace context menu | `cmux workspace-action --action \u003Cname> --workspace \u003Cid>` (e.g. `pin`, `set-color`) |\n| Open path\u002FURL | `cmux open \u003Cpath-or-url> [--workspace \u003Cid>]` |\n| SSH workspace | `cmux ssh user@host [-A]` |\n\n### Panes & surfaces\n\n| Action | Command |\n| --- | --- |\n| New split | `cmux new-split --workspace \u003Cw> --surface \u003Cs> \u003Cdirection>` (`left`\u002F`right`\u002F`up`\u002F`down`) |\n| New pane | `cmux new-pane --workspace \u003Cw> --surface \u003Cs> terminal\\|browser` |\n| New surface | `cmux new-surface --workspace \u003Cw> --pane \u003Cp> terminal\\|browser` |\n| Focus pane | `cmux focus-pane --workspace \u003Cw> --pane \u003Cp>` |\n| Close surface | `cmux close-surface --workspace \u003Cw> --surface \u003Cs>` |\n| Move surface | `cmux move-surface --workspace \u003Cw> --surface \u003Cs> --to-workspace \u003Cw2>` |\n| Split off | `cmux split-off --workspace \u003Cw> --surface \u003Cs> \u003Cdirection>` |\n\n### Input (steering a terminal)\n\n| Action | Command |\n| --- | --- |\n| Send text | `cmux send --workspace \u003Cw> --surface \u003Cs> \"echo hello\"` |\n| Send key | `cmux send-key --workspace \u003Cw> --surface \u003Cs> enter` |\n| Keys | `enter`, `tab`, `escape`, `backspace`, `delete`, `up`, `down`, `left`, `right` (and more — `cmux send-key --help`) |\n\n`cmux send` does NOT auto-press Enter. To execute a command, send the text then `send-key enter`.\n\n### Sidebar & notifications\n\n| Action | Command |\n| --- | --- |\n| Notify | `cmux notify --title \"T\" --body \"B\" [--workspace \u003Cw>]` |\n| List notifications | `cmux list-notifications` |\n| Set status pill | `cmux set-status \u003Ckey> \"\u003Cvalue>\"` |\n| Clear status pill | `cmux clear-status \u003Ckey>` |\n| Set progress | `cmux set-progress 0.5 --label \"Building...\"` |\n| Clear progress | `cmux clear-progress` |\n| Log entry | `cmux log \"msg\" --level info\\|warn\\|error` |\n\n### Hooks & agent integration\n\n| Action | Command |\n| --- | --- |\n| Install agent hooks | `cmux hooks setup [--agent \u003Cname>]` |\n| Uninstall | `cmux hooks uninstall [--agent \u003Cname>]` |\n| Feed TUI | `cmux feed tui` |\n| Browser automation | `cmux browser \u003Csubcommand>` (snapshot, click, type, eval, screenshot, etc.) |\n\n## Python Client\n\n```python\nimport json\nimport os\nimport socket\n\nSOCKET_PATH = os.environ.get(\"CMUX_SOCKET_PATH\", \"\u002Ftmp\u002Fcmux.sock\")\n\ndef rpc(method, params=None, req_id=1):\n    payload = {\"id\": req_id, \"method\": method, \"params\": params or {}}\n    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:\n        sock.connect(SOCKET_PATH)\n        sock.sendall(json.dumps(payload).encode(\"utf-8\") + b\"\\n\")\n        return json.loads(sock.recv(65536).decode(\"utf-8\"))\n\n# List workspaces\nprint(rpc(\"workspace.list\", req_id=\"ws\"))\n\n# Send notification\nprint(rpc(\"notification.create\", {\"title\": \"Hello\", \"body\": \"From Python!\"}))\n```\n\nThe wrapper lives at `scripts\u002Fcmux_client.py` (this skill's scripts\u002F dir).\n\n## Shell Helper\n\n```bash\ncmux_cmd() {\n    SOCK=\"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\"\n    printf \"%s\\n\" \"$1\" | nc -U \"$SOCK\"\n}\n\ncmux_cmd '{\"id\":\"ws\",\"method\":\"workspace.list\",\"params\":{}}'\n```\n\n## Check if cmux is Available\n\n```bash\n[ -S \"${CMUX_SOCKET_PATH:-\u002Ftmp\u002Fcmux.sock}\" ] && echo \"cmux socket available\"\ncommand -v cmux &>\u002Fdev\u002Fnull && echo \"cmux CLI available\"\n```\n\n## Sub-pattern: \"Verify a TUI-only feature works (Claude Code \u002Fadvisor, \u002Fconfig, \u002Fmodel, etc.)\"\n\n**Load the dedicated skill `test-tui-claude-feature-via-cmux` first** when the\nuser asks \"does \u002Ffeature work.\" It ships a wrapper script\n(`scripts\u002Ftest-tui-feature.sh`), 6\u002F6 unit tests, and a worked example — using\nit is faster and more reliable than re-deriving the recipe from this\nsub-pattern. The cmux sub-pattern here is the **primitive** that skill is\nbuilt on; the dedicated skill is the right entry point for the user-facing\nquestion.\n\nThe summary of why the dedicated skill exists (kept here for the\n\"load cmux first\" path):\n\n> `claude --print \"\u002Ffeature\"` will always return\n> `\u002F\u003Cfeature> isn't available in this environment` for TUI slash\n> commands, because `--print` is non-interactive mode. That error\n> specifically means \"I cannot show you this in non-interactive mode,\"\n> **not** \"this feature is broken.\" Treating it as a feature-gate failure\n> leads to 30+ minute rabbit holes reading minified binary strings\n> (`isFirstPartyApiBackend`, `xr()`, `VW()`, `oct()`,\n> `isFirstPartyAnthropicBaseUrl`) and trying every env-var combination.\n> The user's literal words on 2026-06-23, when this happened with\n> `\u002Fadvisor`: *\"are you opening claude code itself and typing \u002Fadvisor?\n> … use cmux stop being lazy.\"*\n\nThe full recipe lives in [`references\u002Ftui-feature-testing-recipe.md`](.\u002Freferences\u002Ftui-feature-testing-recipe.md)\nand in the dedicated skill `test-tui-claude-feature-via-cmux`. The\nsix-step primitive (spawn → discover surface → wait for `❯` → send →\nread → cleanup) is the same; the dedicated skill wraps it in a tested\nscript and adds the `isn't available in this environment` non-interactive\ndetector that signals a malformed slash command or a not-yet-ready\nprompt.\n\n**Surface-rediscovery pitfall (verified 2026-06-23):** `cmux list-pane-surfaces`\nreturns `surface:N` for the first surface in the first pane of the target\nworkspace, but a new-workspace spawn can produce a different surface number\nthan expected. The \u002Fadvisor verification session used `workspace:31 surface:66`;\na hardcoded `surface:65` from a prior session would have either misrouted or\nerrored with `Surface is not a terminal`. **Always re-discover the surface\nfrom the spawn output — never hardcode a surface number from a prior\nworkspace.** The dedicated skill's helper script does this automatically.\n\n**Companion skill note:** the dedicated `test-tui-claude-feature-via-cmux`\nskill lives at `~\u002F.hermes_prod\u002Fskills\u002Ftest-tui-claude-feature-via-cmux\u002F`\nand is the **preferred entry point** when the user asks \"does \u002Ffeature\nwork.\" Load it first; fall back to the inline cmux recipe only when\nthe dedicated skill's wrapper doesn't fit (e.g. multi-step dialog\nnavigation that needs a custom key sequence).\n\n### Sub-pattern: \"Workspace screen says X, but GitHub says Y\" (verified 2026-06-25)\n\n**The trap:** cmux workspace displays are a **stale proxy** for the live source-of-truth\n(GitHub PR state, local file state, etc.). When you `cmux read-screen` a workspace\ntalking about PR #7907, the screen text might say \"green\u002Funstable (author churning)\"\nwhile GitHub's actual `commits\u002F\u003Csha>\u002Fcheck-runs` shows 4 consecutive Green Gate\nSUCCESS runs in the last 3 hours. The screen is a *snapshot of the agent's last\nrecap message*, which can be 30-90+ minutes old — the agent has been doing real work\nsince then but hasn't posted an updated summary.\n\n**Bug-ref 2026-06-25 — lvl\u002Fcost recap probe** (6 workspaces):\n- workspace:88 \"cost: reviewer\" screen showed:\n  - `#7907 — OPEN, head d64d96899, green\u002Funstable (author churning)`\n  - `#7920 — OPEN, head 8e0985afc, mergeable=clean (3 blockers fixed)`\n- Both screens were STALE. Live GitHub state:\n  - `#7907` head `d64d9689` had 4 Green Gate SUCCESS runs in the last 3 hours\n    (freshest \u003Chttps:\u002F\u002Fgithub.com\u002F$GITHUB_REPOSITORY\u002Factions\u002Fruns\u002F28199477760|run 28199477760> 21:12Z) — fully green\n  - `#7920` head `8e0985afc` had 3 Green Gate SUCCESS runs in the last 30 min\n    (freshest \u003Chttps:\u002F\u002Fgithub.com\u002F$GITHUB_REPOSITORY\u002Factions\u002Fruns\u002F28199013911|run 28199013911> 20:38Z) — fully green\n- The \"author churning\" label in workspace:88 referred to the local worker's\n  activity log, not the PR's check-runs. **The user's recommendation would have\n  been wrong** (\"#7907 needs more work\") if I'd trusted the screen.\n\n**Rule:** when a workspace screen quotes a PR's check status, **always cross-check\nagainst the live GitHub state** before acting on it. The canonical cross-check:\n\n```bash\nBR=$(gh pr view \u003CN> --repo \u003Cowner>\u002F\u003Crepo> --json headRefName -q .headRefName)\nSHA=$(git ls-remote https:\u002F\u002Fgithub.com\u002F\u003Cowner>\u002F\u003Crepo>.git refs\u002Fheads\u002F$BR 2>\u002Fdev\u002Fnull | awk '{print $1}')\necho \"branch=$BR head=$SHA\"\ngh api \"repos\u002F\u003Cowner>\u002F\u003Crepo>\u002Fcommits\u002F$SHA\u002Fcheck-runs\" \\\n  --jq '[.check_runs[] | {name, conclusion, started_at, html_url}]\n        | sort_by(.started_at) | reverse | .[0:8]'\n```\n\n**Per CLAUDE.md \"7-green verification\"** — `gh pr checks` is NOT sufficient for\nGreen Gate; must read workflow logs for gate-by-gate PASS\u002FFAIL. For a faster\n\"is this PR fundamentally green?\" answer, the `check-runs` API with `.conclusion`\n(not `.state` — `.state` is null for Actions and only populated for legacy commit\nstatuses) is the right primitive. Look for `conclusion: \"success\"` on the \"Green\nGate\" named check_run.\n\n**When to trust the screen:** when the workspace screen shows a recent\n*churning label with advancing time* (`Churned for 12m 34s`, `Brewed for 5m 12s`)\nor an active subagent, the worker IS doing something — the screen reflects\ncurrent state. The stale-screen trap fires when the screen shows a *recap line*\nthat's actually a few hours old.\n\n**Also applies to mergeable state** (`workspace:88` showed `#7920 mergeable=clean`,\nlive GitHub said `MERGEABLE` — that one happened to match, but `mergeable_state`\nin GitHub API can lag too). Always pull `mergeable` via `gh pr view --json mergeable`\nas the source-of-truth.\n\n### ⚠️ THE #1 RECURRING FAILURE — Send → Submit → Proof\n\n`cmux send` does NOT press Enter. The user has explicitly flagged this as\n\"you always forget to send\" at least 5 times in the last 30 days (2026-06-09,\n2026-06-19, 2026-06-20, 2026-06-23, 2026-06-25). The canonical 4-step ritual\n(send → send-key enter → sleep → verify churning label) lives at\n**`references\u002Fsend-submit-proof-2026-06-25.md`** — load it before steering any\nidle agent. The user's framing phrase \"did you press send\" \u002F \"show me proof\"\nmaps directly to this reference.\n\nFor long task briefs (>200 chars), use the **worktree-pointer strategy** —\nwrite the brief to a file in the worker's cwd and send a short 1-2 line\npointer to it. Documented in the same reference.\n\n## Related references\n\n- **`references\u002Fsend-submit-proof-2026-06-25.md`** — the canonical 4-step send→submit→proof ritual + worktree-pointer strategy. **Load this before steering any idle agent.**\n- **`references\u002Fcli-cheatsheet.md`** — full command list grouped by intent (canonical reference).\n- **`references\u002Fsocket-discovery-2026-06-21.md`** — original recipe, `cmux identify` as canonical resolver.\n  Use for ONE-shot interactive scripts; **for launchd jobs and any unattended run, use\n  `multi-app-socket-probe-recipe-2026-06-24.md` instead** (probes ALL candidate sockets so\n  any cmux build\u002Fapp combo works).\n- **`references\u002Fsurface-read-routing-bug.md`** — non-focused surface read API bugs + focus-then-read workaround (2026-06-13).\n- **`references\u002Fworkspace-routing-trap.md`** — `select-workspace` index-vs-ref footgun + the \"find a workspace by partial title\" recipe that always works (2026-06-13).\n- **`references\u002Ftui-feature-testing-recipe.md`** — \"does \u002Ffeature work\" verification via cmux (full transcript, surface-discovery pitfall, the `isn't available in this environment` non-interactive-mode detector, 2026-06-23 \u002Fadvisor Opus 4.8 case).\n- **`references\u002Fdrift-aware-steer-recipe.md`** — \"worker is already on it\" verification-first pattern with drift-detection (PR body vs code grep) + dual-channel steer (PR comment for detail + short cmux send as pointer). Verified 2026-06-24 driving the daily-GCP-slack-alerts work through `workspace:70 \"daily jobs + deploy\"`.\n- **`references\u002Fios-pairing.md`** — iOS app pairing diagnosis: `auth.status.signed_in` is the canonical blocker; `mobile.host.status.host_service.is_running` follows from auth; `mobile.attach_ticket.create` mints the QR once host is up. Non-restart sign-in via `auth.begin_sign_in` → browser URL → `cmux-dev-dev-fork:\u002F\u002Fauth-callback` URL scheme (scheme varies by build: `cmux:\u002F\u002F`, `cmux-dev:\u002F\u002F`, `cmux-dev-dev-fork:\u002F\u002F`). \"Latest stable\" trap: installed `\u002FApplications\u002Fcmux.app` may be older than the GitHub release — always verify. Load when the user says \"pair my iOS app\", \"QR isn't showing\", \"trying to use Cmux iOS\", or \"do not restart it\".\n- **`scripts\u002Fcmux_client.py`** — minimal Python socket client (rpc helper).\n- **Companion skill:** `test-tui-claude-feature-via-cmux` (`~\u002F.claude\u002Fskills\u002Ftest-tui-claude-feature-via-cmux\u002F`) — domain-specific test recipe built on cmux; ships `scripts\u002Ftest-tui-feature.sh` wrapper + 6\u002F6 unit tests. **Load this skill when the user asks \"does \u002Ffeature work\" rather than re-deriving the cmux recipe from scratch each time.**\n- **Companion skill:** `repo-top5-audit-and-dispatch` (`~\u002F.hermes_prod\u002Fskills\u002Fdevops\u002Frepo-top5-audit-and-dispatch\u002F`), reference `references\u002F2026-06-25-workstream-scope-completeness-audit.md` — the \"are those all the PRs related to X?\" multi-keyword `gh search prs` sweep + live-GitHub cross-check recipe, used after this cmux sub-pattern to verify each PR the screen recap mentioned.\n- Upstream contract: \u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fdocs\u002Fcli-contract.md>\n- Upstream skill: \u003Chttps:\u002F\u002Fraw.githubusercontent.com\u002Fmanaflow-ai\u002Fcmux\u002Fmain\u002Fskills\u002Fcmux\u002FSKILL.md>\n",{"name":5,"description":7},"main","42ccd02cbf5e8e47839dc3eab6fc754dc6b339cf","hermes\u002Fskills\u002Fcmux",0,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills","https:\u002F\u002Fgithub.com\u002Fjleechanorg","2026-11-12 14:45:07",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add jleechanorg\u002Fjleechan-skills --skill cmux",{"tool":61,"command":62},"bunx","bunx skills add jleechanorg\u002Fjleechan-skills --skill cmux",{"tool":64,"command":65},"pnpm","pnpm dlx skills add jleechanorg\u002Fjleechan-skills --skill cmux","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002FSKILL.md",true,[69,75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,141,145],{"path":70,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":73,"sourceHtmlUrl":74},"_meta.json","REFERENCE","参考文档",123,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002F_meta.json",{"path":76,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":77,"sourceHtmlUrl":78},".clawhub\u002Forigin.json",136,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002F.clawhub\u002Forigin.json",{"path":80,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fagent-screen-capture-tcc.md",8710,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fagent-screen-capture-tcc.md",{"path":84,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fcli-cheatsheet.md",10634,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fcli-cheatsheet.md",{"path":88,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":89,"sourceHtmlUrl":90},"references\u002Fdrift-aware-steer-recipe.md",10916,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fdrift-aware-steer-recipe.md",{"path":92,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":93,"sourceHtmlUrl":94},"references\u002Fios-pairing.md",8034,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fios-pairing.md",{"path":96,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":97,"sourceHtmlUrl":98},"references\u002Fmulti-app-socket-probe-recipe-2026-06-24.md",4655,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fmulti-app-socket-probe-recipe-2026-06-24.md",{"path":100,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":101,"sourceHtmlUrl":102},"references\u002Fopen-two-surfaces-claudec-codexc-2026-07-16.md",7927,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fopen-two-surfaces-claudec-codexc-2026-07-16.md",{"path":104,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":105,"sourceHtmlUrl":106},"references\u002Foutput-contract-mandatory.md",5436,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Foutput-contract-mandatory.md",{"path":108,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":109,"sourceHtmlUrl":110},"references\u002Fscreencapture-failure-2026-06-09.md",6592,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fscreencapture-failure-2026-06-09.md",{"path":112,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":113,"sourceHtmlUrl":114},"references\u002Fsend-key-broken-workarounds.md",4609,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsend-key-broken-workarounds.md",{"path":116,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":117,"sourceHtmlUrl":118},"references\u002Fsend-submit-proof-2026-06-25.md",10598,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsend-submit-proof-2026-06-25.md",{"path":120,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":121,"sourceHtmlUrl":122},"references\u002Fsocket-discovery-2026-06-21.md",3770,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsocket-discovery-2026-06-21.md",{"path":124,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":125,"sourceHtmlUrl":126},"references\u002Fsurface-read-routing-bug.md",6255,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fsurface-read-routing-bug.md",{"path":128,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":129,"sourceHtmlUrl":130},"references\u002Ftui-feature-testing-recipe.md",8227,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Ftui-feature-testing-recipe.md",{"path":132,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":133,"sourceHtmlUrl":134},"references\u002Fworkspace-routing-trap.md",6108,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Freferences\u002Fworkspace-routing-trap.md",{"path":136,"resourceType":137,"resourceTypeDesc":138,"sizeBytes":139,"sourceHtmlUrl":140},"scripts\u002Fcmux_client.py","SCRIPT","脚本",3145,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Fscripts\u002Fcmux_client.py",{"path":142,"resourceType":137,"resourceTypeDesc":138,"sizeBytes":143,"sourceHtmlUrl":144},"tests\u002Ftest_cmux_submit_discipline.py",6585,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Ftests\u002Ftest_cmux_submit_discipline.py",{"path":146,"resourceType":137,"resourceTypeDesc":138,"sizeBytes":147,"sourceHtmlUrl":148},"tests\u002Ftest_send_and_submit.py",5768,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fcmux\u002Ftests\u002Ftest_send_and_submit.py",[150,166,187,201,215,230],{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":156,"publishedAt":43},"01M716Y5MTJ6GYJZ2ATXCJK8G4","instinct","Use when the user invokes \u002Finstinct or \u002Fin, or asks to \"message Instinct\", \"ask Instinct\", \"tell Instinct\", \"check Instinct\", or read replies from the configured peer assistant. iMessage first, WhatsApp Web as fallback.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Finstinct\u002FSKILL.md",[],[152,157,158,159,160,29,161,162,163,31,164,165],"user","when","replies","check","identity","whatsapp","gmail","message","ask",{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":177,"publishedAt":43},"01M716Y4HF7FVVMJSA2CCVN5VF","hr","Compatibility alias for history-resume; produce the same fresh-state, provenance-aware resume briefing.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhr\u002FSKILL.md",[172],{"categoryNo":173,"categoryName":174,"taxonomyType":175,"taxonomyTypeDesc":176},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[168,178,179,180,181,182,183,184,185,39,186],"resume","compatibility","provenance","for","history","produce","alias","briefing","fresh",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":194,"publishedAt":43},"01M716Y1FFNFKFJYYKF8HQA6T8","history-resume","Resume briefing for the current repository branch or worktree using fresh state and admitted history sources.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhistory-resume\u002FSKILL.md",[193],{"categoryNo":173,"categoryName":174,"taxonomyType":175,"taxonomyTypeDesc":176},[182,178,195,196,157,197,185,198,181,199,200,39,186],"history resume","branch","analysis","repository","worktree","sources",{"skillNo":202,"name":203,"nameZh":6,"description":204,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":205,"categories":206,"keywords":207,"publishedAt":43},"01M716XWXJ8EPHK4RH9T14TR85","grok","Alias for the canonical grokbot browser conversation workflow; \u002Fgrok talks to the Grok bot the same way \u002Fdot talks to Dot.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgrok\u002FSKILL.md",[],[203,208,209,210,181,211,184,212,213,214],"dot","bot","workflow","canonical","conversation","browser","grokbot",{"skillNo":216,"name":217,"nameZh":6,"description":218,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":219,"categories":220,"keywords":221,"publishedAt":43},"01M70N9Q07DTE0EW5KFXMMMSJ0","codexa-bench","Compare `codexa` and normal `codex` on a fair split of comparable work by default; use `--strict` for duplicate runs of the exact same task.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fcodexa-bench\u002FSKILL.md",[],[222,223,22,29,224,181,225,226,227,228,229],"codexa bench","work","default","duplicate","task","compare","normal","codex",{"skillNo":231,"name":232,"nameZh":6,"description":233,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":234,"licenseFileUrl":235,"sourceHtmlUrl":236,"categories":237,"keywords":249,"publishedAt":43},"01M6C24S2X60SP8BBFGAEKPEGW","frontend-design","Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML\u002FCSS layouts, or when styling\u002Fbeautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FSKILL.md",[238,243,246],{"categoryNo":239,"categoryName":240,"taxonomyType":241,"taxonomyTypeDesc":242},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":244,"categoryName":245,"taxonomyType":241,"taxonomyTypeDesc":242},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":247,"categoryName":248,"taxonomyType":175,"taxonomyTypeDesc":176},"01JBSEEDCAT000000000000032","设计师",[250,251,252,29,253,254,255,157,256,257,258,259,260,261,262,263,264,265,266,267,268,40,269,270,271,272,158,273],"frontend","design","frontend design","generic","ui design","react","html","components","code","artifacts","ui","aesthetics","Production","web components","landing","applications","quality","web","css","examples","with","react components","web ui","pages"]