ディスパッチャーのコンテキストを圧迫したまま session を続けるのを避けるため、 monitoring 状態(active workers / 直近 polling cursor / pending escalations)を handover ファイルに書き出し、secretary の指示で /clear → /dispatcher-resume の 流れで新しいディスパッチャー session を開始する準備をする。 Secretary から DISPATCHER_HANDOVER peer message を受領したとき、または ディスパッチャー自身が context が長くなったと判断したときに使う。
pr-watch-pane
PR の CI / マージ監視 (tools/pr-watch.sh) を broker tmux セッション内の専用ペイン pr-watch-<PR> で回す。
窓口が PR 作成直後に `/pr-watch-pane <PR>` で起動すると、 ja-root cwd・sandbox 外で監視が走り、/clear や窓口セッション寿命と無関係に継続する。
pane name で冪等起動 (二重監視しない)、role=watcher で identity 登録、監視終了 (CI green / PR merged / timeout) で broker の tmux backend ならペイン自動 close (herdr / wezterm / renga のペインは tmux ペインでないため自動 close されず、窓口が 監視終端でイベント駆動 close する)。
Bash tool の background は session 寿命依存で長時間 watcher には不適なので本 skill が推奨経路。「CI 監視をペインで」「pr-watch をペインで回して」「PR <N> の CI を見張って」等で発動。
含まれるファイル(5)
- SKILL.md51.4 KB
- references/rationale.md9.0 KB
- references/transport-duality.md6.0 KB
- references/transport-duality.md.in430 B
- SKILL.md.in51.2 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
pr-watch-pane: CI / マージ監視を専用ペインで回す
tools/pr-watch.sh <PR> --repo <owner/repo> --merge-watch を broker tmux セッション
内の専用ペイン (name="pr-watch-<PR>") で起動する。Bash tool の background 起動は
session 寿命依存で、CI 監視のような長時間 watcher には不適(公式設計の対象外)。broker
ペイン spawn を経由することで sandbox 外・窓口セッション非依存(/clear や窓口の
context リセットと無関係)に監視が継続し、人間が tmux ペインで直接見えるので可視性も高い
(feedback-dispatcher-visibility 整合)。
監視結果は二経路で残る(どちらも pr_watch.py の既存挙動。本 skill は形を変えない):
.state/state.dbevents テーブル …ci_completed/pr_merge_watch_timeoutの canonical event 行(payload 形・CI_COMPLETED/PR_MERGED/PR_MERGE_WATCH_TIMEOUTのメッセージ形は不変)。これが判定の canonical 記録。.state/pr-watch-<PR>.log+ tmux スクロールバッファ … 人間可読の生ログ二段。
peer push は best-effort:
pr_watch.pyは CI 確定・マージ時に窓口へCI_COMPLETED/PR_MERGEDの peer message を送ろうとするが、これはtools/peer_notify.py経由の best-effort(broker send CLI 不在 / 解決先 transport が未構成の pane では no-op)。daemon が非既定 state dir(herdr dogfood 等)で動く環境では、pane env にORG_BROKER_STATE_DIRが無いと broker send が既定.state/brokerを掴んで push が欠落する (欠落しても canonical の events DB 行には影響しない)。待つべき正路は上記 events DB 行と 可視ペインであり、push の到達を merge gate の 前提にしない(org-pull-request の CI/merge gate は events DB を一次ソースにする)。
輸送層(transport)両系: 本文のツール参照は
mcp__org-broker__*(org-broker)で書いてあり、 active transport が既定面ならそのまま従えばよい。輸送層で手順が変わるのは 受信モデル / spawn 儀式 / エラー分岐 の 3 点だけで、opt-in 側の輸送層は削除せず切戻しの安全装置として 常時有効。両系の読み替え規則・契約の批准経緯・broker 固有エラーコードの一覧は.claude/skills/pr-watch-pane/references/transport-duality.mdを参照(上記 3 分岐に入るまで読む必要はない)。
本 skill の spawn 対象は「汎用 CLI ペイン」: 起動するのは
tools/pr-watch.shという shell コマンドであり、Claude セッション(spawn_claude_pane)ではなく 汎用mcp__org-broker__spawn_paneを使う。したがって輸送層の spawn 儀式(broker: folder-trust + channel sidecar の 2 段承認 / renga: dev-channel の 1 段承認)は本 skill では一切発生しない(--mcp-config/--dangerously-load-development-channelsの注入が無く、承認プロンプトが出ない)。attention watcher と同じ CLI ペイン扱いである (.claude/skills/pr-watch-pane/references/transport-duality.md末尾の「attention watcher は ... 2 段承認の対象外」注記と同義)。両系で 切り替わるのは ツール完全修飾名だけ:
- 既定 broker:
mcp__org-broker__spawn_pane(list_panes/set_pane_identity/inspect_pane/close_paneも同様にmcp__org-broker__*)- opt-in renga:
mcp__renga-peers__spawn_pane(同様にmcp__renga-peers__*)以下の手順は
mcp__org-broker__を active transport の完全修飾名に読み替えれば両系で論理同一。 spawn 直後の identity 登録・起動コマンドも両系共通(CLI ペインなので Enter 承認は不要)。
前提
- 本 skill は 窓口(secretary)専用。汎用
spawn_paneは broker の auth tier 上 secretary だけが持つ(dispatcher はspawn_claude_paneのみ。contractdocs/contracts/backend-interface-contract.mdSurface 8 の ops tier 定義)。 - 窓口の cwd は ja-root(リポジトリ root)。spawn する pr-watch は
.state/パスやtools/pr-watch.shを ja-root 基点で resolve する必要があるため、cwd trap を skill 側で吸収する(Step 1 でgit rev-parse --show-toplevelの絶対パスを spawn_pane のcwdに明示。窓口 cwd が何らかの理由で ja-root を外れていても正しく解決される)。 - 自己 close の実装は「watcher ペインが tmux ペインであること」前提。Step 3 の
commandはbash実行と、自己 close のtmux kill-pane -t "$TMUX_PANE"(自ペインを$TMUX_PANEで明示 指定して kill。socket は$TMUX継承をそのまま使い-Lで固定しない = tmux 上に立つ ペインであれば broker の既定 socket / 非既定 socket いずれでも正しい server に当たる。 Issue #647 提案 1 の「明示 target 指定」を実装した形)に依存する。tmux ペインでない環境では$TMUX/$TMUX_PANEがそもそも env に無く、-t ""へ展開されて2>/dev/null || trueで 無言失敗する(下記の分類を参照)。 - 自己 close は tmux ペインの低遅延経路であり、watcher ペインの実体が tmux ペインかどうかで
効き方が変わる(herdr ゾンビ残留の根治, Issue #751 / renga 残留, Issue #911)。broker は
backend として tmux / herdr / wezterm を取りうる(解決済み backend は
daemon.jsonsidecar が 持つ。契約はdocs/contracts/backend-interface-contract.mdSurface 8)。opt-inrengaのペインは tmux ペインではないので、transport の綴りではなく 「ペインの実体」で次の 2 分類に読み替える:- 自己 close が効く = broker の tmux backend: Step 3 末尾の
tmux kill-pane -t "$TMUX_PANE"が実ペインを即座に消す = 監視終端で watcher ペインが自動 close される低遅延経路 (従来挙動。温存する)。 - 自己 close が効かない = broker の herdr / wezterm backend(Windows native broker の別 GUI
ウィンドウ・tmux 非経由を含む)、および opt-in
renga: watcher ペインは tmux ペインではないためtmux kill-paneが no-op(|| trueで握り潰され silent)。self-close が効かず、監視終了後もペインがゾンビとして残留する(herdr / renga それぞれの実測ログは.claude/skills/pr-watch-pane/references/rationale.md§1)。この分類では自己 close に頼れないため、 監視終端で窓口がイベント駆動で watcher ペインを close する経路が正路になる (.claude/skills/org-pull-request/SKILL.mdの post-merge cleanup /PR_MERGE_WATCH_TIMEOUT/ CI 失敗確定の各終端で窓口が発火。掃除手順は下記 Step 5 の (a)/(b) split に従う = live pane は list_panes 確認済みの数値 pane_id で close、stale binding のみ broker 条件付き allowlist で name 指定)。close_paneは transport 抽象上 tmux / herdr / renga いずれでも呼べ、broker の tmux backend で既に self-close 済みなら[pane_not_found]が返る(自己クローズ済みで正常)。- renga では窓口 close も無条件には撃てない: Step 5 (a) の自タブ確立は (i) backend が
Group B を自身の single-tab モデル内で解決する(=
org-broker)/ (ii)caller_scopeを 確立できているのいずれかを要し(契約 T-§4.2「Fail-safe consequence for Group B」/ T-§cap)、 renga では (i) が成立しないため (ii) を確立できない限り close を撃たず、watcher ペインが 残る旨を報告して人手掃除に委ねる(相対セレクタへはフォールバックしない)。 Windows native の手動起動経路 (人間がtools/pr-watch.ps1 <PR>を!経由で起動)も従来どおり遮断しない(既存経路は不変)。
- renga では窓口 close も無条件には撃てない: Step 5 (a) の自タブ確立は (i) backend が
Group B を自身の single-tab モデル内で解決する(=
- 自己 close が効く = broker の tmux backend: Step 3 末尾の
Step 1: 引数解決と cwd / repo の確定
-
PR 番号
<PR>を引数から取る(必須・正の整数)。省略時はユーザーに PR 番号を尋ねて中断。 -
ja-root の絶対パスと owner/repo を決定的に解決する(cwd trap 吸収 + repo 明示):
git rev-parse --show-toplevel # -> JA_ROOT(絶対パス) gh repo view --json nameWithOwner -q .nameWithOwner # -> OWNER/REPO printenv ORG_TRANSPORT ORG_BROKER_STATE_DIR PATH # -> transport env(Refs #653 #658)gh repo viewが失敗する場合はユーザーに「リポジトリを自動解決できません。--repo OWNER/REPOを指定してください」と報告して中断。pr-watch.sh 自身も--repo省略時は auto-resolve するが、ここで明示することで pane の cwd に依らず repo を固定 する(cwd trap が repo 解決にも波及しないようにする)。- transport env の捕捉(Refs #653 #658 — env trap 吸収):
printenvで窓口 (secretary)ペインのORG_TRANSPORT/ORG_BROKER_STATE_DIR/PATHを確定させる。mcp__org-broker__spawn_paneは 汎用 CLI ペインを env 注入なしで立てるため(本 skill 上部の 注記)、これらを Step 3 のcommandに前置注入しないと spawn されたペインのtools/peer_notify.pyがORG_TRANSPORT未設定で silent no-op に落ちる。cwdが cwd trap を吸収するのと同じ理屈で、この env 捕捉が env trap を吸収する。ORG_TRANSPORTが空 / 未設定なら Step 3 でexport ORG_TRANSPORTを省く。 これは窓口ペインの解決結果をそのまま鏡写しにするための省略である(PR #73 障害の 経緯と、未設定の意味が renga → broker に変わった Refs #941 の経緯は.claude/skills/pr-watch-pane/references/rationale.md§3)。ORG_BROKER_STATE_DIRも set のときだけ前置する。
Step 2: 冪等チェック(同一 PR の二重監視を防ぐ)
mcp__org-broker__list_panes を呼び、name="pr-watch-<PR>" の live pane が既に存在するか確認する。
- 存在する → 既に監視中。spawn せず「PR #<PR> の CI 監視ペイン
(
pr-watch-<PR>, id={N}) は既に稼働中です」と報告して終了。再起動したい場合は先に Step 5 の手順でそのペインを close してから再実行するよう案内する。 - 存在しない → Step 3 へ。
pane name
pr-watch-<PR>が冪等キー。<PR>は数字だがpr-watch-プレフィックスで 英字を含むため許可文字[A-Za-z0-9_-]を満たし、全桁数字(= id 扱いで曖昧化)にもならない。
Step 3: 監視ペインの spawn
Step 3-0: 張り先(アンカー)を幾何で決める(Refs #335)
spawn の前に、どのペインを split 起点にするかを決定的ツールで求める。target は
固定しない(旧版の target="dispatcher" 固定が生んだ実害の経緯は
.claude/skills/pr-watch-pane/references/rationale.md §2)。
-
mcp__org-broker__list_panesを呼び、返却されたpanes配列(id/name/role/x/y/width/height)をそのまま JSON として控える。 -
その JSON を
tools/pick_watcher_anchor.pyに stdin で流し、候補リストを得る (既定入力が stdin なので中間ファイルを作らない。ファイル経由なら--panes-json <path>):python3 tools/pick_watcher_anchor.py --format text <<'PANES' {"panes": [ ... list_panes の返却をそのまま貼る ... ]} PANES(Windows は
py -3 tools/pick_watcher_anchor.py ...。POSIX 側はpythonエイリアスが 無い環境があるためpython3で呼ぶ。transport はORG_TRANSPORTから自動解決される ので通常は指定不要。明示するなら--transport {broker|renga}。) -
出力の
candidatesが rank 1 から順に試す張り先。各候補はtarget(pane name)とdirection(vertical/horizontal)を持つので、両方をそのまま次のspawn_pane引数に使う(direction も候補ごとに変わる。vertical固定ではない)。 direction は「両方向を評価し、親側に残る子が大きい方を採る」規則(runtime の_split_optionsと同一。同値性は unit test で固定)。窓口が direction を自分で 決めない(実害の経緯は.claude/skills/pr-watch-pane/references/rationale.md§2)。 -
candidatesが空(exit code 2 /capacity_exhausted: true)のときだけ、このタブに watcher を置く余地が本当に無い。spawn を試みず、rejectedの各行(どのペインが どの床値でどう落ちたか)を添えて人間に報告し、指示を仰ぐ。
出力の pane_cap_reached は助言であって判定ではない: 「このタブのペイン数が renga の
per-tab 上限(RENGA_MAX_PANES = 16)に達している」という事実で、renga ではこれ以降の
spawn が幾何によらず拒否される。既定の broker には per-tab 上限が無い(各ペインが独立
セッション)ので、capacity_exhausted はこの値では動かない。[split_refused] が続いた
ときの人間報告に添える材料として使う。
ツールが守る床値(MIN_PANE 20x5 / SECRETARY_MIN 120x30 / DISPATCHER_MIN_WIDTH 80)は
claude_org_runtime.dispatcher.runner の定数を import して 使う(skill 側にも
ツール側にも値を写経しない)。優先度は dispatcher →(既存 watcher ペイン。dispatcher
隣接を先)→ secretary、dispatcher が自身の comfort 幅を割っている場合だけ最後尾へ降格。
secretary を張り先に使うのはユーザー承認済みで、SECRETARY_MIN_* を
満たす split のみ候補になる。既存 watcher には attention watcher(role="attention")も
含む。worker / curator ペインは候補に入れない(稼働中の作業ビューポートを watcher が
食わないため)。
Step 3-1: spawn
mcp__org-broker__spawn_pane で CLI watcher ペインを起動する(<...> は Step 1 / 3-0 / 引数で確定した値に置換):
mcp__org-broker__spawn_pane(
target="<Step 3-0 の候補 target>",
direction="<Step 3-0 の候補 direction>",
role="watcher",
name="pr-watch-<PR>",
cwd="<JA_ROOT 絶対パス>",
command="nocorrect true 2>/dev/null || true; export ORG_TRANSPORT='<ORG_TRANSPORT>'; export ORG_BROKER_STATE_DIR='<ORG_BROKER_STATE_DIR>'; export PATH='<PATH>'; mkdir -p .state; bash tools/pr-watch.sh <PR> --repo <OWNER/REPO> --merge-watch --no-detach 2>&1 | tee -a .state/pr-watch-<PR>.log; tmux kill-pane -t \"$TMUX_PANE\" 2>/dev/null || true"
)
-
nocorrect true 2>/dev/null || true;前置(Issue #825 — zsh correctall 無言停止の根治。必須・置換禁止):commandは対話シェルに打鍵される形で実行されるため、pane の login shell が zsh で スペル自動訂正(setopt correctall)が有効だと、残留.state/pr-watch-*.logに類似した 新規ログ名がzsh: correct '...' to '...' [nyae]?の確認プロンプトを発火させ、 パイプライン全体が一度も走らないまま無言停止する。行頭のnocorrectは zsh の precommand modifier(reserved word)で、パース前に解釈されその行全体の全 word のスペル 訂正を無効化する(;連結の後続コマンドにも効く)。実発生ログと実機確認の記録は.claude/skills/pr-watch-pane/references/rationale.md§4。- payload を持たない犠牲文として独立させる理由(非 zsh 互換):
nocorrectは zsh 以外の シェルにコマンドとして存在しない。nocorrect export ...と payload に直結すると、bash 等が login shell の pane ではnocorrect: command not foundで export 文ごと失敗し、transport env 注入(次項)が silent に失われる。単独文nocorrect true 2>/dev/null || true;なら zsh では行全体の訂正抑止・bash 等では犠牲文 だけが無音で失敗し、;以降の後続は全て実行される。非対話シェル実行(zsh -c等)でも no-op で副作用はない。 2>/dev/nullは省略不可(Step 4 の誤殺防止): 非 zsh シェルでは犠牲文がnocorrect: command not foundを stderr に出す。この行が画面に残ると Step 4 の negative-signal 判定(command not found= 起動失敗)に誤マッチし、正常起動した watcher を健全なまま close する false positive になるため、犠牲文の stderr は行内で 捨てる。|| trueも省略不可(errexit 耐性): 非 zsh シェルの rc でset -e(errexit)が 有効だと、犠牲文の失敗(status 127)でシェルごと終了し export / mkdir / watcher 本体が 一切走らないまま pane が死ぬ。|| trueで犠牲文を非致命化する。<...>placeholder ではない固定文字列であり、窓口は値に置換せずそのまま残す。下記の 条件付き前置で export 文を省いた場合も、nocorrect true 2>/dev/null || true;は 常に行頭に残す。
- payload を持たない犠牲文として独立させる理由(非 zsh 互換):
-
env 前置注入(Refs #653 #658 — 必須。PR #73 障害の根治): 犠牲文に続く
export ORG_TRANSPORT=...; export ORG_BROKER_STATE_DIR=...; export PATH=...;は Step 1 でprintenv捕捉した窓口ペインの実値に置換する。これが無いと汎用 spawn ペインは transport env を継承せず、peer_notifyの broker/renga 経路がどちらも未設定分岐に落ちて push が silent no-op になる(events テーブルへのci_completed書き込みは成功するのに窓口へ届かない)。PATH注入は broker 経路が shell out するclaude-org-runtime broker sendCLI(venv 内)を pane の PATH で解決可能にするため。- 条件付き前置(窓口ペインの解決を鏡写しにする):
ORG_TRANSPORTが空 / 未設定ならexport ORG_TRANSPORT=...を丸ごと省く。未設定を渡された watcher ペインは窓口ペインと 同じresolve()を通って同じ transport(既定はbroker)に着地するので、省略は 「窓口と同じ解決結果を継がせる」ことを意味する。ORG_BROKER_STATE_DIRも set のときだけ前置する。空値を''で明示 export すると broker 経路が既定 state dir を 掴む誤動作を招くため、空なら export 文自体を書かない。 - 多層防御の位置づけ: この env 注入は path A(低遅延 push)の修理であって単独の保証では
ない。注入漏れ / push 失敗が起きても (B) ディスパッチャーの relay と
notify_failedの fail-loud 記録が「見逃しゼロ」を担保する(PR #73 障害の経緯・Refs #941 の理由付け変更・ 多層防御の内訳は.claude/skills/pr-watch-pane/references/rationale.md§3)。
- 条件付き前置(窓口ペインの解決を鏡写しにする):
-
target/direction: Step 3-0 のツールが返した候補をそのまま使う。broker では 各ペインが detached 独立セッションだが、addressing scope(同一タブ MUST、contract Surface 4.2)を満たすため既存ペインを起点に取る点は不変。- なぜ固定アンカーをやめたか(Refs #335): 旧版は
target="dispatcher"固定だった。 2026-08-30、幅 24 まで細った dispatcher に張ろうとして[split_refused]になり、 397x53 の secretary ペインが丸ごと空いているのに窓口が「タブ満杯」と誤読して worker 派遣を待機させる実害が出た。[split_refused]はその target を割れないという局所の 事実であって、タブ全体の容量を語らない。よって張り先は毎回list_panesの幾何から 決定的に選び直す(renga 側のコード分離は suisya-systems/renga#335)。
- なぜ固定アンカーをやめたか(Refs #335): 旧版は
-
role="watcher": list_panes で監視ペインを識別する表示ラベル(attention watcher のrole="attention"と同じく canonical 4 role 以外のラベル。broker では token の auth tier は spawn 時に固定で、role ラベルは tier を変えない — Surface 8)。 -
cwd: Step 1 で解決した JA_ROOT 絶対パス。これにより pane 内のtools/pr-watch.sh/.state/pr-watch-<PR>.logが ja-root 基点で resolve される(cwd trap 吸収)。 -
--no-detach必須(Issue #650):tools/pr-watch.shは Issue #641 対策で既定 setsid + nohup の自己 detach をするため、--no-detachを付けないと spawn 直後に親 bash が exit して broker pane が即時掃除され、watcher が孤児化する(pane が無いので/org-attach/Ctrl-b sでも覗けない)。--no-detachで前景動作させ、teeと末尾 self-close の 自己終了サイクルを成立させる。Ctrl-b sは tmux prefix の既定値前提: prefix を変更している場合は設定した prefix に 読み替える。さらに、覗く端末が renga の場合はこの打鍵が届かない — renga の org サイドバーは既定で有効なあいだCtrl+B(tmux prefixCtrl-bと同じ物理入力)を消費して PTY へ渡さないため、pane が生きていてもCtrl-b sでセッション切替できない。回避策 (renga 設定[ui] org_sidebar = "off"または tmux prefix の変更)はdocs/operations/dispatcher-view.mdの 「外側フレームが renga の場合」を参照。
-
command: pr-watch を前景実行し、stdout/stderr を.state/pr-watch-<PR>.logにtee -a(tmux スクロールバッファにも出る二段)。先頭mkdir -p .stateで fresh clone でも tee の 出力先を確保する。pr-watch 終了後にtmux kill-pane -t "$TMUX_PANE"で ペインを自己 close(broker の tmux backend でのみ効く低遅延経路。|| trueは tmux ペイン でない環境 = herdr / wezterm / renga で失敗を握り潰すためのもので、そこでは自動 close されず 窓口のイベント駆動 close が正路になる。前掲「前提」節の 2 分類)。$TMUX_PANEは placeholder ではなく実行時 env:<PR>/<OWNER/REPO>と違い$TMUX_PANEは窓口が値に置換してはならない。tmux ペインとして spawn された場合に shell へ 自動露出する自ペイン id(例%16)で、self-close 時に その pane 自身を明示 target 解決する。 旧形tmux kill-pane(target 無指定)は現在ペインを暗黙推定するが、-t "$TMUX_PANE"で 明示することで曖昧さを排す(Issue #647 提案 1 の「明示 target 指定」)。- tmux ペインでなければ未設定で無言失敗する(Issue #911): renga のペインでは
printenv TMUX TMUX_PANEがどちらも未設定を返すため、この行はtmux kill-pane -t ""に 展開され2>/dev/null || trueで無言失敗する(herdr / wezterm も同様に tmux ペインでない)。 この失敗はエラーとして表に出ないので、残留の検知を self-close の成否に依存させない。
- tmux ペインでなければ未設定で無言失敗する(Issue #911): renga のペインでは
- socket は
$TMUX継承をそのまま使う(-Lで固定しない):tmuxを-L無しで 起動すると、ペインが属する tmux server の socket を$TMUXから自動解決する。broker では それがclaude-org-brokersocket、非既定 socket 構成では別 socket になるが、いずれも$TMUXが正しい server を指すので kill が当たる。ここで-L claude-org-brokerと固定すると非既定 socket 下で 別 server に当たって self-close が無言で失敗 するため、socket は固定しない(renga では$TMUX自体が無いので、そもそもこの経路に乗らない)。 - self-close は(効いた場合も)tmux 層だけを掃除する:
kill-paneは broker socket 上の実 tmux ペイン を消すので、直後からlist_panesには現れなくなる。ただし broker daemon の pane 登録簿 (name binding)は self-close では pop されず stale に残りうる(daemon は自 pane の外部 kill を検知しないため)。stale binding が残ると同名pr-watch-<PR>の再 spawn が[name_taken]で弾かれる(list_panesには出ないのに、が症状)。この掃除は Step 5 の 手動 fallback で行う。そこで撃つmcp__org-broker__close_pane(target="pr-watch-<PR>")は、 数値 pane_id を列挙から取り直せないこの状態に限って裸 name を許可する transport 条件付きの allowlist 経路であり、無条件の name 指定ではない(3 条件と broker 以外での扱いは Step 5 の (b) が SoT)。 --merge-watch: CI green でCI_COMPLETEDを出した後もマージまで poll し続け(最大 24h)、マージでPR_MERGED、timeout でPR_MERGE_WATCH_TIMEOUTを出してから自己 close を 試みる。CI 確定だけで止めたい場合は呼び出し時に--merge-watchを外す(その場合は CI green / failed 確定で自己 close を試みる)。いずれも実際に閉じるのは broker の tmux backend のみで、 herdr / wezterm / renga では窓口のイベント駆動 close が掃除する。- pr-watch.sh の既存メッセージ形・event payload 形は 本 skill では一切変更しない(不変条件)。
既存の手動
tools/pr-watch.sh起動経路(人間の!経由など)も従来どおり動作する。
戻り値 "Spawned pane id=N." の N を控える。
spawn 失敗時の分岐(MCP 結果テキストの [<code>] で判定。詳細は
.claude/skills/org-delegate/references/renga-error-codes.md):
[split_refused]→ 候補リストを rank 順に 1 段ずつ降りて再試行する(各候補は 1 回だけ)。このコードは「その target を 今の幾何では割れない」というターゲット局所の失敗を含む(free-pane 不足だけでなく、 細ったアンカー 1 枚の MIN_PANE 割れでも返る)。1 件の拒否からタブ満杯と結論しない — 容量判断は Step 3-0 の幾何計算(list_panes× 床値)だけが行う:- Step 3-0 の
candidatesから次の rank を取り、そのtarget/directionで Step 3-1 の spawn を 1 回だけ再試行する。 - 再試行も
[split_refused]なら、さらに次の rank へ進む(candidatesの末尾まで)。 レイアウトが動いた疑いがあるときは Step 3-0 を 1 度だけ引き直してよい。 candidatesを使い切ったときだけ「監視ペインを作る空きがありません(ターミナルが 狭い / pane 上限)。ターミナルを広げるか不要ペインを閉じてから再実行してください」と 報告して中断する。報告には試した target 一覧と Step 3-0 のrejected行(どの床値で 落ちたか)を添える。
- Step 3-0 の
[pane_not_found](候補 target が既に消えている)→ レイアウトが spawn 前後で動いた race。Step 3-0(list_panes→ ツール)を 1 度だけ引き直して最新の rank 1 で再試行する。 引き直しても候補が空 / 同じく[pane_not_found]なら、/org-start未実行を含む想定外 として状況を人間に報告して中断。[name_in_use]/[name_taken]→ live pane と stale 登録簿を切り分ける(前 watcher の self-close で tmux ペインは消えたが broker 登録簿に name binding が残る「二層の不整合」を 自己回復する):mcp__org-broker__list_panesでname="pr-watch-<PR>"の live pane が実在するか再確認する。- live pane が在る → 冪等チェックを取りこぼした真の race。既に監視中として Step 2 の 「既に稼働中」報告に倒す(新規 spawn しない)。
- live pane が無い(
list_panesに出ない)→ self-close 済みの stale 登録簿 binding。 この状態は列挙にペインが出ないため 数値 pane_id を取得できず、Group B の 「数値 pane_id で撃つ」原則をそのまま適用できない。よってこの掃除だけは transport 条件付きの allowlist として裸 name のclose_paneを許可する(契約docs/contracts/backend-interface-contract.mdT-§4.2 の Group B 台帳が stale-binding 行に求める「使った mechanism」がこれ。条件の並びと 根拠の SoT は下記 Step 5 の (b))。以下 3 条件がすべて成立するときだけ許可される:- (1) いま Group B を駆動している backend が
close_pane/set_pane_identityを自身の single-tab モデル内で解決する(=org-broker)— 誤タブ hazard が構造的に生じない (契約 §8.1 / §8.10)。判定は「いま Group B を撃つのに使っている MCP ツールの 完全修飾名がmcp__org-broker__*である」という積極的な証拠で行う。DEFAULT_TRANSPORTから推定してはならない — 判定規則と根拠の SoT は下記 Step 5 の (b)。 確定できない場合は不成立として扱う - (2) 再 spawn が
[name_in_use]/[name_taken]で弾かれている — stale binding の症状 (本分岐の発火条件そのもの) - (3) その name が
mcp__org-broker__list_panesに現れない — live pane 不在(上記 1. の再確認結果) - 3 条件が揃う場合:
mcp__org-broker__close_pane(target="pr-watch-<PR>")で name 解決させて登録簿を pop し(ok closed=%Nが返る。[pane_not_found]は既に掃除済みで OK)、Step 3 の spawn を 1 度だけ再試行する。再試行でも[name_taken]が続く場合はユーザーに報告して中断 (想定外の登録簿状態)。 - broker 以外に解決する場合(
ORG_TRANSPORT=rengaの opt-in など)では close せず 報告して中断する(「live pane が無い」という前提が renga の legacy 解決では偽になりうる ため。機序は.claude/skills/pr-watch-pane/references/rationale.md§7)。自動再試行もせず、 stale binding を検出したが transport が broker でないため自動掃除しない旨をユーザーに 報告して指示を仰ぐ。
- (1) いま Group B を駆動している backend が
- broker 固有(
[no_backend]/[token_invalid]/[session_invalid]/[tool_not_authorized]/[peer_not_found]等)/ その他未知コード → 状況をユーザーに 報告して中断(default-branch escalate)。
Step 4: identity 登録と起動 health check
-
identity 確定(role=watcher の登録): spawn_pane の
name/roleが反映されたかmcp__org-broker__list_panesで確認する。name="pr-watch-<PR>"かつrole="watcher"が付いて いれば登録済み。万一いずれかが欠ける backend ではmcp__org-broker__set_pane_identity(target=<N>, name="pr-watch-<PR>", role="watcher")で補修する。- 補修も Group B なので自タブ確立が先(契約 T-§4.2「Fail-safe consequence for Group B」。
docs/contracts/backend-interface-contract.md):set_pane_identityはclose_paneと同じ Group B で、宛先は「自タブと独立に確立済みの 列挙から得た数値 pane id」に限られる。数値であることは MUST の片方にすぎず、id が spawn の戻り値として手元にあることも免除にならない(未確立の列挙では、直前のname/role確認の結果そのものを信用できないため)。確立手段は 2 つだけで、いずれか 1 つが成立 すればよい: (i) backend が Group B を自身の単一タブモデル内で解決する(org-broker。 契約 §8.1 / §8.10)/ (ii)caller_scopeを確立できている(契約 T-§cap。caller_scope_close_identityから導出しない)。どちらも成立しないなら補修を撃たず、 identity が未登録のまま監視が走っていること(Step 2 の冪等チェックと Step 5 の cleanup がname/roleで引けなくなること)をユーザーに報告する。target="focused"や裸 name の 相対セレクタへはフォールバックしない —set_pane_identityのtargetは既定が"focused"なので、人間がフォーカスを移していれば別ペインを改名する。 - 本ペインは Claude セッションではなく CLI プロセスなので Claude peer(peer_id)は 持たない。ここでの「peer 登録」= pane registry への name + role=watcher の登録 (spawn_pane / set_pane_identity が行う)を指す。MCP peer / dev-channel 登録は発生しない。
- 補修も Group B なので自タブ確立が先(契約 T-§4.2「Fail-safe consequence for Group B」。
-
即時クラッシュ / 対話プロンプト停止の検出(negative-signal のみ):
mcp__org-broker__inspect_pane(target=<N>, format="text", lines=<ペイン高さ以上>, include_cursor=true)で出力を全画面読む(Issue #825)。linesはmcp__org-broker__list_panesの geometry で分かる ペイン高さ以上、高さが取れなければ余裕を取ってlines=200を指定する。末尾 15-40 行だけの 読み取りは、画面上部に出た確認プロンプト + 下部空白を「静かな正常起動」と誤読する (見逃した実例は.claude/skills/pr-watch-pane/references/rationale.md§5)。include_cursor=trueは下記 「行末?+ 入力待ち」判定に必要なカーソル位置を返させるため必須(既定 false では カーソル情報が返らず、ブロック中のプロンプトと通常出力を区別できない)。 以下のいずれかを検出した場合のみ「起動失敗」と判定:command not found/is not recognized/No such file or directorygh: ... not found等 gh 不在 /Traceback (most recent call last)/ModuleNotFoundError- 出力末尾に shell prompt(
$/%末尾露出)= command が即時終了して shell に戻った - 対話プロンプト形跡(Issue #825 で追加):
[nyae]?(zsh correctall のzsh: correct '...' to '...' [nyae]?)/(y/n)/[y/N]のリテラル、または行末が?で 終わる行にcursorが乗ったまま(= その行で入力待ち)後続出力が無い。いずれもコマンドが 一度も走らないままシェル層の確認プロンプトで停止している形跡で、放置すると監視ゼロの 無言死になる(correctall 停止なら Step 3 のnocorrect前置漏れを疑う)。 - 上記いずれも無ければ(pr-watch の watch ループ出力 / 空 / 起動直後の静止)起動成功扱い とし、固定 sleep を入れて再 inspect する経路は持たない(健全な quiet start を誤殺しない)。
- 起動失敗時は死んだペインを掃除し、原因(
tools/pr-watch.sh/ghの導入、cwd、対話プロンプト 停止)をユーザーに報告して中断する。掃除は 自タブ確立 → identity 再確認 → 数値 close の 順で行う:- 自タブ確立(契約 T-§4.2「Fail-safe consequence for Group B」。
docs/contracts/backend-interface-contract.md): (i) backend が Group B を自身の単一タブモデル内で解決する(org-broker。契約 §8.1 / §8.10)/ (ii)caller_scopeを確立できている(契約 T-§cap)のいずれか 1 つが 成立すればよい。どちらも成立しないなら close を撃たず、死んだペイン (id=<N>) が 残る旨を報告に含めて手動掃除に委ねる(相対セレクタへはフォールバックしない)。id が spawn の戻り値として手元にあることは免除にならない — 未確立の列挙では次の identity 照合の結果そのものを信用できないため - identity 再確認(pane_id recycle 対策。他の全経路と揃える):
mcp__org-broker__list_panesで<N>のレコードを引き、name="pr-watch-<PR>"かつrole="watcher"をなお指している ことを確かめる。別ペインへ再割当て済み / 列挙に現れない場合は close せず、その旨を 報告に含める(本 Step の「identity 確定」で補修を撃てなかった backend ではこの照合が 通らないので、同じく close せず報告に倒す) - 両方を通ったときだけ
mcp__org-broker__close_pane(target=<N>)する([pane_not_found]/[pane_vanished]は直前に消えた扱いで skip)
- 自タブ確立(契約 T-§4.2「Fail-safe consequence for Group B」。
-
生存確認は副作用ベースで行う(
pgrep -f単独判定の禁止、Issue #825): 起動後に 「監視が本当に走っているか」を確かめるときは、プロセス一覧ではなく副作用を見る:pgrep -f "pr-watch"単独を生存判定に使わない。spawn されたペインのラッパーシェルの argv にはコマンド全文(pr-watch.shを含む)が乗るため、確認プロンプト停止中で パイプラインが一度も走っていなくてもシェル自身にマッチして偽陽性になる(誤認の実例は.claude/skills/pr-watch-pane/references/rationale.md§5)。- 一次の副作用は
.state/pr-watch-<PR>.logの生成: Step 3 のtee -aはパイプライン 開始と同時にログファイルを open/生成するため、fresh なログ名なら「ファイルが出来ている」= パイプラインが実際に走った確証になる(プロンプト停止ではコマンド自体が実行されないので ログは生成されない)。同名の残留ログがある再監視では生成有無で判別できないため、上記 2 の 全画面 inspect(プロンプト形跡なし)と組み合わせて判定する。 - 判定確定の副作用は
.state/state.dbevents テーブルのci_completed等の canonical event 行(本 skill 冒頭の二経路)。なおpr_watch.pyは起動 banner を出力しないため、 ログが空のまま静止していても quiet start でありうる(誤殺しない)。
Step 5: 監査記録と報告 / 手動 close
-
journal に起動を best-effort で記録する:
bash tools/journal_append.sh pr_watch_pane_started pr=<PR> repo=<OWNER/REPO> pane_id=<N>Windows native では
py -3 tools/journal_append.py pr_watch_pane_started pr=<PR> repo=<OWNER/REPO> pane_id=<N>。この行は監査ログではなく、watcher 生存判定の一次証拠である(Refs #978): 記録を落とすと
tools/watcher_restart_guard.pyは「watcher なし」 (missing)と判定する(画面にペインが見えていてもである — ペインの存在は監視の生存を意味 しない、が同ガードの前提)。best-effort とは「失敗しても spawn 自体は巻き戻さない」の意味 であって、省略してよいという意味ではない。記録したら、それが判定に反映されたことをその場で確認する:
python3 tools/watcher_restart_guard.py check --task <task_id>exit 0(
live)なら「最後の push より後に開始された watcher がある」ことが events の時系列 から機械的に確認できた状態。exit 3 が返る場合は 1. の記録が入っていないか、この watcher が 最後の push より前に起動している(=立て直しが必要)。cross-repo で task が引けない場合はcheck --pr <PR> --repo <OWNER/REPO>でもよい(run 行から baseline を逆引きする)。 -
ユーザーに報告する:
PR #<PR> の CI / マージ監視ペイン pr-watch-<PR> (id={N}) を起動しました。 - ログ: .state/pr-watch-<PR>.log(tmux スクロールバッファにも出力) - 監視終了(マージ / CI 失敗確定 / timeout のいずれか)で、broker の tmux backend では ペインが自動で閉じます。herdr / wezterm backend と renga では自己 close が効かず残留する ため、窓口が監視終端でイベント駆動 close します(org-pull-request の各終端処理)。 確定した CI 判定は `.state/state.db` の `ci_completed` 行とログに残るので、 ペインが閉じても判定は失われません(merge gate はそこを読む)。 - tmux で直接見るには `/org-attach` のコマンドを使ってください。 -
監視終端クローズ / 手動 close(Issue #647 提案 3 / Issue #751 / Issue #911): self-close の 効き方は watcher ペインの実体が tmux ペインかどうかで決まる(前掲「前提」節の 2 分類)。 herdr / wezterm backend と opt-in renga では self-close が no-op で watcher ペインが必ず残留するため、監視終端(PR_MERGED / PR_MERGE_WATCH_TIMEOUT / CI 失敗確定)で 窓口がイベント駆動で watcher ペインを掃除するのが正路になる (
.claude/skills/org-pull-request/SKILL.mdの各終端処理で発火。 renga では下記 (a) の自タブ確立がcaller_scope依存で、確立できなければ close を撃たず 残留を報告する = 無言残留を可視な残留に変えるところまでが本経路の効果)。 broker の tmux backend では self-close が tmux ペインを消すが登録簿の name binding は残りうる (前掲「self-close は(効いた場合も)tmux 層だけを掃除する」)。掃除は下記 (a)/(b) の split に従う。窓口の イベント駆動 close(監視終端)では、下記に加えて「spawn 時に控えた pane_id + 監視対象 head に束縛し、 終端イベントの head が追跡中 instance と一致するときだけ close する」freshness gate を必ず併用する (終端イベントが遅延 / 重複配送されて同一 PR の watcher が既に再起動済みの場合、nameで live pane を 再導出すると新 watcher を解決してその数値 pane_id を close しても replacement monitor を誤 close する。 束縛と freshness gate の SoT は.claude/skills/org-pull-request/references/watcher-cleanup.md)。 人間が現ペインを手動 close する (a)/(b) は現 instance が対象で自明なので freshness gate は不要:-
(a) tmux ペインが live のまま残った / 監視を途中で止めたい: まず照合に使う
mcp__org-broker__list_panesの列挙を自タブのものと確立する(契約docs/contracts/backend-interface-contract.mdT-§4.2「Fail-safe consequence for Group B」)。確立手段は 2 つだけで、いずれか 1 つが成立 すればよい: (i) backend が Group B を自身の単一タブモデル内で解決する(org-broker。 契約 §8.1 / §8.10)/ (ii)caller_scopeを確立できている(契約 T-§cap。caller_scope_close_identityから導出しない)。どちらも成立しないなら close を撃たず、 watcher ペインが残る旨をユーザーに報告して停止する(相対セレクタへはフォールバックしない)。 確立できたらname="pr-watch-<PR>"の live pane を確認し、その 数値 pane_id でmcp__org-broker__close_pane(target=<N>)する([pane_not_found]/[pane_vanished]は既に 閉じた扱いで skip)。name 指定ではなく list_panes で identity を確認した数値 pane_id を 使う(id recycle 時の誤 close を避ける)。- (a) は「live pane が列挙に出るから安全」ではない: (b) が carve-out を要するのは
列挙から数値 id を取り直せないためであって、(a) が数値 id を取れることは MUST の
片方(相対セレクタでないこと)を満たすにすぎない。もう片方の「その列挙が自タブの
ものか」は (a) でも別に確立しなければならず、未確立の列挙では
name/role照合の 結果そのものを信用できない(renga の legacy 解決が作る誤 close hazard の機序は.claude/skills/pr-watch-pane/references/rationale.md§7)。
- (a) は「live pane が列挙に出るから安全」ではない: (b) が carve-out を要するのは
列挙から数値 id を取り直せないためであって、(a) が数値 id を取れることは MUST の
片方(相対セレクタでないこと)を満たすにすぎない。もう片方の「その列挙が自タブの
ものか」は (a) でも別に確立しなければならず、未確立の列挙では
-
(b) stale 登録簿 binding(
list_panesには出ないのに再 spawn が[name_taken]): self-close で tmux ペインは消えたが broker 登録簿に name binding が残っている状態。 ペインが列挙に出ないので 数値 pane_id を取得できず、(a) の「list_panes で identity 照合 した数値 pane_id で撃つ」形が原理的に取れない。この一点に限り transport 条件付きの allowlist として裸 name のclose_paneを許可する(本節が 3 条件と根拠の SoT。契約docs/contracts/backend-interface-contract.mdT-§4.2。契約からこの mechanism が導かれる経緯は.claude/skills/pr-watch-pane/references/rationale.md§6)。 以下 3 条件がすべて成立するときだけ許可される:-
(1) いま Group B を駆動している backend が
close_pane/set_pane_identityを 自身の single-tab モデル内で解決する(=org-broker)— そのモデル内で name も 解決されるため誤タブ hazard が構造的に生じない(契約 §8.1 / §8.10)。判定は積極的な証拠でのみ行う(MUST): いま Group B を撃つのに使っている MCP ツールの 完全修飾名が
mcp__org-broker__*であること。これは呼び出しの直前に必ず確定している (mcp__org-broker__は起動時に片方へ解決済み)ので、常に判定できる。DEFAULT_TRANSPORTから推定してはならない(MUST NOT)。ORG_TRANSPORT無設定は 運用既定 renga の構成でもありうるのにtools/transport.pyのresolve()は無設定を コード既定brokerに解決するため、推定すると renga が Group B を駆動している環境で 裸 name の close を撃ち、別 org の同名 watcher を不可逆に閉じうる(失敗方向の非対称性・ 二フレームとの関係・Step 1 のprintenv省略が別論点である理由は.claude/skills/pr-watch-pane/references/rationale.md§6) -
確定できないときは carve-out を取らない(fail-safe):
ORG_TRANSPORTに未知値が 入っていて解決がValueErrorになる等で「いま何が駆動しているか」を確定できない場合は、 条件 (1) を不成立として扱い、下の「broker 以外」と同じくユーザーに報告して指示を仰ぐ -
(2) 再 spawn が
[name_in_use]/[name_taken]で弾かれている — stale binding の症状 -
(3) その name が
mcp__org-broker__list_panesに現れない — live pane 不在(=列挙から数値 pane_id を取れない、この allowlist が必要になっている当の条件) -
3 条件が揃う場合:
mcp__org-broker__close_pane(target="pr-watch-<PR>")する(broker が name → stale pane_id を解決し登録簿を pop、ok closed=%Nが返る。[pane_not_found]は既に 掃除済みで OK)。掃除後は同名 spawn が通る。Step 3 の[name_taken]分岐はこの (b) を 自己回復するが、手動でも同手順で掃除できる。 -
broker 以外に解決する場合(
ORG_TRANSPORT=rengaの opt-in など)では裸 name に フォールバックしない(「live pane が無いので誤 close の余地が無い」という前提が renga の legacy 解決では偽になりうるため。機序は.claude/skills/pr-watch-pane/references/rationale.md§7)。 この経路では close せず、stale binding を検出した旨とpr-watch-<PR>の 再 spawn が弾かれる状態であることをユーザーに報告して指示を仰ぐ。 -
控えた pane_id での close にも倒さない:
list_panesで再確認できない retained id を close に使うのは (a) が防いでいる pane_id recycle hazard を素通りさせるため、本 skill は 採らない([name_taken]が続く場合と同じくユーザー報告に倒す)。
-
-
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
/dispatcher-handover で書き出した handover ファイルを読み込み、 ディスパッチャーを新しい session で復帰させる。/clear 直後の最初のターンで使う。 state.db の dispatcher_pane_id / dispatcher_peer_id を atomic に更新し、 worker monitoring の /loop 3m を再開する。 「ディスパッチャー復帰」「resume」「引き継ぎから再開」と secretary から指示された ときに使う。/org-start ではない(ワーカー・窓口・キュレーターは生きている前提)。
プロジェクトのゴール台帳(registry/goals/<owner>/<repo>.md)を人間と対話して新規作成・改訂する。 README と open Issue からゴール候補を推定して提示し、「何を目指すか」「何が起きていたら未達か」を 短く質問して条項を固め、書式検証(tools/work_discovery_goals.py validate)を通してから保存する。 「ゴール台帳を書きたい」「ゴールを設定して」「ゴール未設定と出た」「台帳エラーを直して」 「このプロジェクトの目標を決めたい」等や、/work-discovery の「ゴール未設定」「ゴール台帳エラー」案内を 受けて窓口が起動する。候補の提示そのものは /work-discovery の役目で本スキルではない。
組織の生きているペイン(secretary / dispatcher / worker)へ tmux で**直接入る** ための **コマンドを出力するだけ** の read-only スキル。`mcp__org-broker__list_panes`(論理ペイン) と `/usr/bin/tmux -L claude-org-broker list-panes -a`(pane_id ↔ session マッピング)を pane_id(%N)で突き合わせ、各ペインに role + name(task_id) ラベル付きの attach コマンドを 生成する(読取専用 `-r` / 書込は `-r` 無し / デタッチは `Ctrl-b d`)。 「ワーカーのペインを見たい」「ワーカーのペインに入りたい」「dispatcher の様子を直接見たい」 「ペインに入りたい」「あのペインを直接覗きたい」「attach コマンド教えて」 「tmux でつなぎたい」「worker の画面に入る」等で発動。 自分で attach はせず・ペインも一切変更しない(コマンド文字列とペイン一覧表を表示するのみ)。 作業委譲(org-delegate)・ダッシュボードで状況一覧(org-dashboard)・watcher 停止(org-attention-stop) には発動しない。
attention notification watcher(承認待ち / 判断待ち / CI 失敗等を OS 通知 + 音で能動通知)を dispatcher ペインの右側に split で常駐起動する。`.state/attention.json` が未配置なら ja 既定テンプレートを `tools/templates/attention.example.json` から自動コピーする。 起動後のペイン id は `.state/attention_pane.json` に記録し、`/org-attention-stop` から参照する。 「attention 起動」「通知監視を始めて」「watcher を立てて」等で発動。 `/org-start` からの auto-start はしない(明示起動推奨ポリシー)。
`/org-attention-start` で起動した attention watcher ペインを停止する。 `.state/attention_pane.json` に記録された pane_id を `list_panes` の name/role で identity 確認し、いまも attention watcher を指している場合だけ `close_pane`(pane 破棄)で 破棄する(pane_id が別ペインへ再割当て済みなら close せず stale sidecar として削除)。 「attention 止めて」「通知監視を停止」「watcher 落として」等で発動。