From cfa16d56ca9eeacb01f09d1eba95fc5e3b9bb3e1 Mon Sep 17 00:00:00 2001 From: onevcat Date: Sat, 22 Aug 2026 18:05:20 +0900 Subject: [PATCH] docs(cli): gate actions on the identity lookup result, not the bare variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A stale PROWL_PANE_ID that names a pane in another instance is non-empty and differs from the target, so a guard on the variable alone still ran the side effect. The action guards now require the lookup result (me) and the complete loop computes it first. Provenance wording says the process ancestry must reach the pane's shell — agents and their tool shells are descendants, not immediate children. Claude-Session: https://claude.ai/code/session_01YUytym5xEn5FKMhWjSkNkm --- docs/components/cli.md | 14 ++++++++------ skills/prowl-cli/SKILL.md | 10 +++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/components/cli.md b/docs/components/cli.md index 319baabe..5f33a795 100644 --- a/docs/components/cli.md +++ b/docs/components/cli.md @@ -98,8 +98,8 @@ The variable is inherited, not verified: a process that scrubbed its environment different pane reports the pane its server started in. A value that matches no `pane.id` usually means `prowl` reached a different Prowl instance than the one hosting your pane (see [Transport & app launch](#transport--app-launch)). A match proves the pane -exists, not that you run in it: trust the value only when your process is a direct child -of the pane's shell — under tmux/screen or a detached wrapper it names the pane the +exists, not that you run in it: trust the value only when your process ancestry reaches +the pane's shell — under tmux/screen or a detached wrapper it names the pane the server started in, so identify your pane by other means (`prowl agents --json` for the pane hosting your agent session, a unique `pane.cwd`) and pass it explicitly. Keep every step that depends on knowing yourself inside the success branch. When it is unset or @@ -147,9 +147,10 @@ good for coordination but lags a screen by ~2–3 s and can flip to idle **befor TUI finishes painting — confirm with `read --wait-stable`. Your own pane is `$PROWL_PANE_ID` (see [Identity](#identity-which-pane-am-i)); gate -every action on a target behind the check, which fails closed when the id is unset: +every action on a target behind the identity lookup result `me` from that section — +not the bare variable, which could be stale — so the check fails closed: ```bash -[ -n "$PROWL_PANE_ID" ] && [ "$pane" != "$PROWL_PANE_ID" ] && prowl send --pane "$pane" '…' --json +[ -n "$me" ] && [ "$pane" != "$PROWL_PANE_ID" ] && prowl send --pane "$pane" '…' --json ``` ### `prowl agents` @@ -493,9 +494,10 @@ artifacts and terminal excerpts do not appear in `git status`. ## A complete loop (run, read, clean up) ```bash +me="$(prowl list --json | jq -c --arg p "$PROWL_PANE_ID" '.data.items[] | select(.pane.id == $p)')" pane="$(prowl create tab MyApp --json | jq -r '.data.target.pane.id')" -if [ -z "$PROWL_PANE_ID" ] || [ "$pane" = "$PROWL_PANE_ID" ]; then - echo "refusing: no verified pane of my own, or \$pane is me" >&2 +if [ -z "$me" ] || [ "$pane" = "$PROWL_PANE_ID" ]; then + echo "refusing: self identity is unverified, or \$pane is me" >&2 else prowl send --pane "$pane" 'swift build' --capture --timeout 300 --json prowl read --pane "$pane" --last 100 --wait-stable --json diff --git a/skills/prowl-cli/SKILL.md b/skills/prowl-cli/SKILL.md index d3a18a65..5a100025 100644 --- a/skills/prowl-cli/SKILL.md +++ b/skills/prowl-cli/SKILL.md @@ -35,17 +35,17 @@ else fi ``` -Gate every action on a target behind the same check — a bare predicate line does not stop an interactive shell: +Gate every action on a target behind that lookup result (`$me`), never behind the bare variable — a stale id that points at another instance would otherwise pass — and keep the dependent commands inside the branch, because a bare predicate line does not stop an interactive shell: ```bash -if [ -z "$PROWL_PANE_ID" ] || [ "$pane" = "$PROWL_PANE_ID" ]; then - echo "refusing: no verified pane of my own, or \$pane is me" >&2 +if [ -z "$me" ] || [ "$pane" = "$PROWL_PANE_ID" ]; then + echo "refusing: self identity is unverified, or \$pane is me" >&2 else prowl send --pane "$pane" 'git status --short' --capture --timeout 30 --json fi ``` -The variable is inherited, not verified: it is missing after `sudo`/`ssh`/containers and can name the wrong pane inside a tmux/screen session attached from elsewhere. A set value that matches no `pane.id` usually means `prowl` is talking to a different Prowl instance than the one hosting your pane (two apps running; see `PROWL_CLI_SOCKET` under Pitfalls). A match only proves that pane exists, not that you are running in it: trust the value only when your process is a direct child of the pane's shell (no tmux/screen server or detached wrapper in between); under tmux/screen or a detached wrapper, identify your pane by other means — `prowl agents --json` for the pane hosting your own agent session, or a unique `pane.cwd` — and pass it explicitly. If it is unset or matches nothing, stop rather than guess: `pane.cwd` only narrows the candidates — several panes usually share one cwd — and may stand in for you only when the match is unique. Never assume the focused pane is you — `open` and `focus` move focus, and the user may be looking anywhere. +The variable is inherited, not verified: it is missing after `sudo`/`ssh`/containers and can name the wrong pane inside a tmux/screen session attached from elsewhere. A set value that matches no `pane.id` usually means `prowl` is talking to a different Prowl instance than the one hosting your pane (two apps running; see `PROWL_CLI_SOCKET` under Pitfalls). A match only proves that pane exists, not that you are running in it: trust the value only when your process ancestry reaches the pane's shell (no tmux/screen server or detached wrapper in between); under tmux/screen or a detached wrapper, identify your pane by other means — `prowl agents --json` for the pane hosting your own agent session, or a unique `pane.cwd` — and pass it explicitly. If it is unset or matches nothing, stop rather than guess: `pane.cwd` only narrows the candidates — several panes usually share one cwd — and may stand in for you only when the match is unique. Never assume the focused pane is you — `open` and `focus` move focus, and the user may be looking anywhere. ## Safe Default Workflow @@ -172,7 +172,7 @@ done ## Handing Off Your Task -`prowl handoff to --brief -` hands your task to another agent. Run it from your own pane (the calling pane is the source — no selector needed) and pipe your briefing on stdin. Prowl finds the calling pane through process ancestry, so a direct shell or agent child works; under tmux/screen or a detached wrapper that resolution fails with `SOURCE_REQUIRED`, and in exactly those setups `$PROWL_PANE_ID` is not trustworthy either (it names the pane the tmux server started in, which may still exist) — identify your pane by other means (`prowl agents --json`, a unique `pane.cwd`) and pass it with `--pane` explicitly. +`prowl handoff to --brief -` hands your task to another agent. Run it from your own pane (the calling pane is the source — no selector needed) and pipe your briefing on stdin. Prowl finds the calling pane through process ancestry, so any descendant of the pane's shell (an agent, its tool shell) works; under tmux/screen or a detached wrapper that resolution fails with `SOURCE_REQUIRED`, and in exactly those setups `$PROWL_PANE_ID` is not trustworthy either (it names the pane the tmux server started in, which may still exist) — identify your pane by other means (`prowl agents --json`, a unique `pane.cwd`) and pass it with `--pane` explicitly. ```bash prowl handoff to codex --brief - <<'EOF' -- 2.51.2