本文へ移動
cccskills
無料GitHub で公開

org-suspend

組織を中断し、全状態をディスクに保存する。「中断」「保存して終了」 「閉じたい」「一旦やめる」「今日は終わり」と言われたときに使う。

インストール方法を見る

含まれるファイル(2)

  • SKILL.md73.7 KB
  • SKILL.md.in69.5 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

org-suspend: 組織の中断

全ワーカーの状態を収集し、ディスクに保存し、全ペインを停止する。

curator 不在は正常系(オンデマンド化): キュレーターは常駐しない。state.db の curator_pane_id / curator_peer_id は null が正常で、list_panes / list_peers に curator が見えないことは異常ではない。curator ペインが存在するのは「worker クローズ起点の オンデマンド curate が実行中に suspend が重なった」一時的なケースのみで、その場合だけ Phase 4 の停止対象に含める。

責務境界(/org-suspend と /org-down): /org-suspend は 「状態保存 + ja 管理下の補助プロセス(dashboard / secretary_queue_watcher / attention watcher)とペインの停止」までを担い、claude-org-runtime org down(broker daemon の停止)は 呼ばない。suspend 単体は「また /org-start で再開する」前提の中断であり、broker daemon は 走らせたままにする(端末を閉じても daemon はすぐ再開できるよう生存する)。daemon ごと完全に 落とすのは /org-down の責務で、/org-down が suspend の成功を確認した 後にのみ org down を実行する。

ペイン操作は mcp__org-broker__* MCP ツール経由で行う。pane_exited 相当の lifecycle イベントは mcp__org-broker__poll_events で long-poll、画面スクレイプ は mcp__org-broker__inspect_pane で取得、raw キー入力は mcp__org-broker__send_keys。

輸送層(transport)両系 — 既定 broker / opt-in renga: 本ファイル(および各スキル)の mcp__org-broker__* 呼び出しは 既定 broker(ORG_TRANSPORT 無設定)で書いてあり、そのまま従えばよい(既定挙動)。ORG_TRANSPORT=renga(opt-in・切戻し可)では MCP サーバー名が renga-peers になり、ツールの 完全修飾名が mcp__org-broker__* → mcp__renga-peers__* に機械置換される(引数形・セマンティクスは同一なので手順の論理は変わらない)。輸送依存で手順が変わる点だけ renga 併記する:

  • 受信モデル: 既定 broker は push 一次(各ペイン同居の channel sidecar server:org-broker-channel が broker キューを ~1 秒間隔で claim→notifications/claude/channel で idle セッションへ本文注入。pull = ナッジ + check_messages は sidecar 不在 / unhealthy / channel 非対応ペイン(codex pull-peer)/ claude.ai login 不在時のフォールバック層)。ORG_TRANSPORT=renga 時は dispatcher / worker メッセージが <channel source="renga-peers" …> として in-band で push される。
  • spawn 儀式: 既定 broker は --mcp-config <broker> 注入による Claude Code folder-trust プロンプトの画面判定による機械承認に加え、push 一次のため channel sidecar を --dangerously-load-development-channels server:org-broker-channel で load し dev-channel 承認プロンプトを画面判定で機械承認する(2 段承認)。ORG_TRANSPORT=renga 時は --dangerously-load-development-channels server:renga-peers の「Load development channel?」を画面判定で承認する 1 段。 画面判定 = inspect_pane → claude-org-runtime dispatcher spawn-prompt-step が返した 1 手だけを send_keys する反復(runtime 0.1.43+)。盲目 Enter は禁止(folder-trust の初期カーソルは「No, exit」で、Enter 一発はワーカーを終了させる)。手順は spawn-flow 3-3b。
  • エラー分岐: 既定 broker は shared codes(pane_not_found / last_pane / invalid-params)に加え broker 固有 [token_invalid] / [session_invalid] / [tool_not_authorized] / [no_backend](= adapter_unavailable)/ [nudge_failed] / [peer_not_found] / [name_taken] を返しうる(未知コードは default-branch で escalate)。ORG_TRANSPORT=renga 時は broker 固有コードは発生しない。

new_tab / focus_pane は broker surface に無い(意図的除外)。契約面の正本は docs/contracts/backend-interface-contract.md Surface 8 + push-primary amendment(broker push 一次が 既定の契約、pull は fallback として retain)。opt-in renga は削除せず常時有効な切戻しの安全装置として維持する。broker 実走(dogfood)は Epic #6 Issue G スコープで本ファイルの既定運用経路ではない(二フレーム注記(Refs #604): ここの「既定 broker」はコード既定(tools/transport.py: DEFAULT_TRANSPORT、生成面はこれで render)。運用既定は broker dogfood が Epic #6 Issue G まで未活性のため renga で、両者は指す対象が異なり矛盾しない。総説は root CLAUDE.md。)

Phase 1: ワーカー状態収集

  1. id 台帳を作る。messaging とペイン制御は別々の面なので、id もそれぞれの面から取る (契約 Surface 4 の批准済み amendment (2026-08-09) T-§4.2 は「到達性は面ごとに別々に確立する」ことを MUST に している。list_peers にレコードがあることは、そのピアがペイン制御で addressable であることの 証拠にならない):
    • mcp__org-broker__list_peers — messaging 用の peer id。各レコードの id / name / role / cwd と、 マルチタブ対応 backend が返す optional な same_tab / tab / tab_name を控える(この 3 つは cross_tab_peers capability を広告する backend でのみ載る。非広告 backend では欠落する)。 list_peers のタブ範囲を決めるのは cross_tab_peers であって caller_scope ではない (契約 T-§cap は capability を互いに独立と規定する。caller_scope が変えるのは list_panes と Group A の pane 制御呼び出しだけで、list_peers には影響しない。close_pane / set_pane_identity は caller_scope の管轄外=別 token — 下記 (G) を参照):

      • cross_tab_peers を広告する backend: list_peers は全タブを列挙し、same_tab は 呼び出し側から見た値として返る(契約 T-§2.2 / T-§2.2-fields)。フォーカスに依存しないので フォーカスの確認は不要
      • cross_tab_peers を広告しない backend(旧版・renga 1.x・現行 org-broker、および caller_scope だけを広告する backend): ratified の messaging 規則どおり フォーカス中のタブに追随する。marker 全欠落が保証するのは「その列挙が単一タブに閉じて いる」ことだけで(契約 T-§cap の conformance MUST)、それが窓口のタブであることまでは 保証しない。/org-suspend を走らせる前にフォーカスが窓口ペインのタブにあることを確認する。 列挙されたタブを呼び出し側に紐付けられない場合は broadcast しない(別 org のタブへ SUSPEND / SHUTDOWN を撃つ経路になる)。紐付けの確認は、list_panes に窓口ペイン自身が 居ること、あるいは列挙に自組織の既知 identity(state.db の dispatcher_peer_id 等)が 含まれることで足りる
    • mcp__org-broker__list_panes — ペイン制御用の pane id。各レコードの id / name / role を控える。 どのタブが返るかは capability による(契約 T-§cap は caller_scope と cross_tab_peers を 独立と規定する): caller_scope を広告する backend では caller のタブが返る(list_panes は Group A なのでこの token だけで足りる)。広告しない backend(旧版、および cross_tab_peers だけを広告する backend)では ratified §4.3 の visibility consequence どおりフォーカス中のタブが返るので、/org-suspend を走らせる窓口ペインの タブがフォーカスされていることが前提になる。フォーカスが別タブにあると別タブのペイン id を 台帳に取り込み、後段で無関係なペインを inspect_pane / close_pane しうる。前提が成り立つか 確認できない場合は pane 制御を行わず、pending として人間に報告する

    • pane 制御の 2 グループ(G)— 以降この記号で参照する: 契約 T-§4.2 は pane 制御ツールを 管轄 capability の異なる 2 群に分ける。分ける理由はリリース時期ではなくサーバー世代の スキューである(4 トークンは 1 リリースで出ており renga src/ipc/mod.rs:123-128 の SERVER_CAPABILITIES は 4 定数を並べる。それでもトークンが別なのは、先行 3 トークンを 広告する #290 世代の server が未知の from_pane を落として close / set_pane_identity をユーザーが見ているタブに解決し不可逆にペインを閉じるからで、 上流は caller_scope_close_identity を "a token of its own" と書いている — renga docs/api-surface-v1.0.md:576-582)。契約 T-§cap "Independence" は caller_scope から caller_scope_close_identity を推論することを MUST NOT としている。「caller_scope があるからあらゆる pane 制御がフォーカス非依存」 という推論をしてはならない:

      • Group A — caller_scope 管轄(suisya-systems/renga#288 の 7 ツール): list_panes / spawn_pane / spawn_claude_pane / spawn_codex_pane / focus_pane / inspect_pane / send_keys。この token を広告する backend では相対セレクタ (リテラル "focused" / 裸の name)が caller のタブに解決され、フォーカス非依存になる
      • Group B — caller_scope_close_identity 管轄(suisya-systems/renga#296): close_pane / set_pane_identity の 2 本。caller_scope はこの 2 本を覆わない。 caller_scope(さらに cross_tab_peers / spawn_tab)を広告する backend であっても、 この token が無ければ "focused" は user が見ているタブに解決され、裸の name は アクティブタブを最初に探し、そこで見つからなかったときだけ他タブを index 順に走査して 最初の一致を採る(無差別の全タブ検索ではない。危険なのはこのフォールスルーと、優先される タブが caller ではなく user のタブである点。契約 T-§4.2 "Pane-control scope, Group B")
      • (G-MUST) caller_scope_close_identity が確立していない間は、close_pane / set_pane_identity に相対セレクタ(リテラル "focused" / 裸の name)を使ってはならない (MUST NOT)(契約 T-§4.2 "Fail-safe consequence for Group B (MUST)")。自タブのものだと 独立に確立済みの列挙から採った数値 pane id でのみアドレスする(MUST)。誤爆した場合、 close_pane は不可逆で(人間が入力中のペインを落とす / 並走する別 org の同名 worker-* を 閉じる)、しかもエラーを返さない。契約はこの MUST に carve-out を 1 つだけ置いているが (stale-binding cleanup = 再 spawn が [name_taken] で弾かれるのに当の name が list_panes に 出ない状態)、本スキルはその条件に当たらないので取らない — /org-suspend が閉じるのは列挙に 居るペインだけで、[name_taken] 起点の再 spawn 経路を持たないため、数値 pane_id は常に列挙から 採れる
      • (G-OWNTAB) 上の MUST が言う「自タブのものだと独立に確立済みの列挙」は、mcp__org-broker__list_panes の列挙を指す。name / role の一致が見るのは「その id が期待どおりのペインか」だけで、 その列挙が自分のタブのものかは見ていないので、確立は照合の前段に置く。確立手段は次の 2 つ だけで、いずれか 1 つが成立したときにその列挙を自タブと確立済みとみなす(規範の出所は 契約 T-§4.2 の MUST。同じ規則を harness 側で書き下したものが .dispatcher/references/pane-close.md の 「照合の前に「その列挙が自タブのものか」を確立する」節で、両者は同一規則であり片方だけを 緩めない):
        1. backend が Group B を自身の単一タブモデル内で解決する — org-broker は Group B も 単一タブモデル内で解決するので、列挙は構成上つねに自タブのもの(契約 §8.1 / §8.10)
        2. caller_scope を確立できている — list_panes が caller のタブにフォーカス非依存で 解決する(契約 T-§cap。caller_scope_close_identity とは別トークンで、一方から他方を 導出しない)。確立できたときだけ数える: 広告を確かめられない / 確定できないときは T-§cap の fail-safe default で不成立に倒れる(version 文字列やサーバー名から推定しない)
        same_tab は確立手段に数えない。same_tab / tab は契約 T-§2.2-fields が list_peers のレコードに足すフィールドで、pane 制御の起点である list_panes のレコードには 載らない。list_peers 側の same_tab を根拠に pane 制御を許すのは、契約が MUST で禁じる 「messaging 到達性から pane 制御到達性を推論する」ことそのものである(同契約 「The two scopes are not interchangeable (MUST)」)。same_tab が使えるのは否定方向の 絞り込みだけで、same_tab == False のレコードに pane_id を割り当てないという狭める 使い方は正しく、「自タブと確立できた」と広げる使い方は license されない。 2 つのいずれも成立しなければ Group B を撃たず、pending として人間に報告する (相対セレクタへフォールバックしない)
      • かつて SHOULD だった経緯(2026-08-07 の人間判断、ja#823 — 記録として保持、discharge 済み): 当時この規則は SHOULD として書かれていた。危険は実在するが本 amendment が持ち込んだもの ではなく以前から存在しており、かつ org 自身の正準手順が Group B を相対セレクタで撃つ箇所を 抱えていた(当時の実測は契約 T-§4.2 の台帳のとおり 8 ファイル 11 呼び出し)。不可逆操作の 手順を未検証のまま変える方がリスクが高いと人間が判断し、コード既定の輸送層 org-broker には この失敗モードが無い(capability を広告せず単一タブ規約で解決する)ことがその判断を可逆に していた。その呼び出し箇所の移行は 2026-08-09 に完了し、契約 T-§4.2 は同日 MUST へ in-place で 引き上げられた(契約 T-§4.2 "Why SHOULD and not MUST" は当時の決定の記録として過去形で保持され、 台帳の全 row が discharge 済み)。したがって本スキルが従うのは上の (G-MUST) であって、この段落は 当時の状態を説明する記録である
      • capability gate 実装済みクライアントでは、Group B の呼び出しが token 欠落時に [server_too_old] で fail-closed する。これは黙って握り潰さず、未実行として pending に残し人間へ報告する(契約 T-§6 clause (b)。renga では回復手順は「daemon を 2.0.0 以降へ更新して再起動し server_info で再 probe」であり、旧挙動での継続ではない)
    • フォーカス前提(F)— 以降この記号で参照する: caller_scope を広告しない backend では list_panes と pane 制御呼び出しはフォーカス中のタブに向く。したがって (F-1) list_panes にピアが居ないことは、それ単独では消滅の証拠にならない (フォーカスが別タブへ移っただけで、生存中の同タブピアが「不在」になる)。 (F-2) pane 制御呼び出しが返す [pane_not_found] も、同じ理由で単独では閉鎖の証拠にならない。 消滅を確定してよいのは (i) pane_exited を観測した、または (ii) 列挙が窓口のタブのものだと確認できているときだけで、どちらも取れなければ indeterminate として残す。F の解除も (G) のグループ別である: caller_scope 広告 backend では Group A(list_panes / inspect_pane / send_keys 等)がフォーカス非依存になるので F-1 は効かない。一方 F-2 は close_pane について残る — caller_scope_close_identity が未確立なら close_pane の [pane_not_found] は依然フォーカス(user 可視タブ)由来でありうる。 契約 T-§4.3 の visibility supersession が完全になるのは 2 token 揃ったときだけで、 caller_scope 単独では §4.3 の文言が Group B に対して verbatim で立つ。

    • 2 つを突合して 1 エントリ = {peer_id, pane_id(割り当てられない場合は未設定), name, role, cwd, same_tab, tab} の台帳にする。 to_id には必ず peer_id、target には必ず pane_id を渡す。2 つの id が同じ値空間だとは 仮定しない(backend 定義。契約 §4.1 は id を "opaque" と規定しており、面をまたいだ流用は契約が 保証していない)

    • pane_id の出所は必ず mcp__org-broker__list_panes(pane 制御面)である。list_peers の台帳は messaging(to_id)用に保ち、そこから pane 制御用の id を導かない — 契約が MUST で禁じる 「messaging 到達性から pane 制御到達性を推論する」ことになるため(「The two scopes are not interchangeable (MUST)」)。割り当ては列挙面のゲート → レコード面の絞り込みの 2 段で決める:

      • (i) 列挙面のゲート((G-OWNTAB)): mcp__org-broker__list_panes の列挙を自タブと確立できないなら、 どのエントリにも pane_id を割り当てない。確立手段は単一タブ backend / caller_scope の 2 つだけで、same_tab はここに数えない。確立できないまま台帳に id を載せると、後段の identity 照合が「期待どおりのペインか」しか見ないため、別タブのペインを照合済みとして close_pane しうる
      • (ii) レコード面の絞り込み(same_tab は否定方向にだけ使う): (i) が成立した列挙の中で list_peers のレコードと突合するとき、same_tab == False / 欠落のレコードを除外する
    • 突合(join)キーは name だけで、role は使わない。role は非一意で(worker が複数居れば 必ず衝突する)join キーとして成立しない。name も全タブ列挙では一意ではないので (契約 T-§2.2: §1.8 の名前一意性は "this tab" 限定。worker-{task_id} 等の予約名は 2 org 並走で 構造的に衝突する)、name join が効くのは list_panes 側が単一タブに閉じているからであって、 name が無条件に一意だからではない。2.0 系の list_peers は全タブを返す一方 list_panes は 単一タブ分しか返さない(契約 §1.5 は本 amendment で非 supersede。どのタブかは上記のとおり capability 依存)ため、(ii) を誤ると別タブのピアのレコードに自タブの同名 ペインの pane_id が付き、Phase 2 が別ワーカーのコンソールをスクレイプし Phase 4 の lifecycle 追跡が壊れる:

      • same_tab == False のレコード → pane_id を絶対に割り当てない(pane 制御面では到達できない。 契約 T-§4.2)
      • 2.0 系だが same_tab 欠落(‡ のケース)→ 「判定できない場合は他タブ扱いに倒す」規則どおり pane_id を割り当てない
      • 上の 2 つで除外されなかったレコード(same_tab == True が確定した 2.0 系のレコード、および 旧版 fallback の列挙 = 全レコードで same_tab / tab が両方欠落)→ (i) が成立している列挙に 同名レコードが在れば、その list_panes レコードの id を pane_id として割り当てる。 same_tab == True はこの割り当てを license する根拠ではない — license するのは (i) であり、 same_tab はそこから他タブのピアを外す negative filter として働いているだけである
      • pane_id が**未設定のエントリは「pane 制御では触れない」**ことを意味する((i) が不成立で 台帳全体に id を載せなかった場合を含む)。Phase 2 ではスクレイプを 諦めて git 情報に倒し、Phase 4 では close_pane を撃たずに pending に残す(各 Phase の該当箇所と 同じ結論)
    • この台帳を Phase 2 / Phase 4 まで引き回す。宛先解決・pending 集合はすべて台帳エントリを primary key にし、name は人間可読の補助ラベルに留める

  2. 直後の「宛先選定の真理値表(Phase 1 / Phase 4 共通)」で broadcast 対象を決める (Phase 1 の対象 role 集合は R = {worker, dispatcher})。版判定が 2.0 系だった場合は、同節の operational gate に従って手順 3 の送信前に人間へ報告し、確認を得てから進む(旧版 fallback では gate を発動せず、そのまま手順 3 へ進む)
  3. 対象ピアの peer_id を to_id に渡して mcp__org-broker__send_message で以下を送信:
    SUSPEND: 現在の状態を報告してください。
    1. これまでに完了したこと
    2. 変更したファイル(コミット済み/未コミット)
    3. 次にやろうとしていたこと
    4. ブロッカーや未解決の問題
    
  4. 30 秒間 mcp__org-broker__check_messages で応答を待つ(5 秒間隔でポーリング)
  5. 応答があったワーカーの報告を、送信先の台帳エントリに紐付けて記録する(受信メッセージの from_id を peer_id と突合し、name 一致は補助にする)

宛先選定の真理値表(Phase 1 / Phase 4 共通)

list_peers の各ピアレコードについて、same_tab / tab / role の 3 つで broadcast 可否と 宛先指定の形を決める。Phase 1(SUSPEND)と Phase 4(SHUTDOWN)はこの 1 つの表を共有し、 対象 role 集合 R だけをフェーズごとに差し替える:

  • Phase 1(状態報告の依頼): R = {worker, dispatcher}。curator には状態報告を求めないので含めない
  • Phase 4(停止指示): 停止順序どおりステップごとに R = {worker} → R = {dispatcher} → R = {curator}(curator ペインが存在する場合のみ)

role が欠落 / null / 空文字列 / 未知の文字列のピア、および role == "secretary" のピアは どの R にも入らない。窓口自身が列挙に含まれるかは backend 次第だが(契約 §2.2 は caller を除外すると 書く一方、除外しない実装もありうる)、自己除外は role gate が担うので列挙の挙動に依存しない。 他タブに別 org の secretary が居ても同じく R 外で除外される。

role gate で外した同タブピアは「黙って落とす」のではなく unresolved として追跡する: 契約 §2.2 は role を optional と規定しているので、実体はワーカーなのに role が欠落 / null の レコードを適合 backend が返しうる。これを broadcast 対象から外すのは安全側で正しいが、そのまま 忘れると Phase 4 が「組織を中断しました」と報告する裏でそのワーカーが走り続ける。 したがって、同タブでありながら R 外で除外したピア(role 欠落 / null / 空文字列 / 未知)は 台帳に unresolved として残し、Phase 4 の最終報告で「role が判定できず停止操作の対象外にしたピア」 として peer_id / name / cwd 付きで人間に提示する。role == "secretary" で外したものは 窓口自身なので unresolved に入れない。他タブの除外ピアは † の他タブ報告に含める。

版判定は列挙 1 回につき 1 度だけ決める(レコード単位で切り替えない)。list_peers の結果全体を見て、 same_tab / tab のどちらか一方でも載っているレコードが 1 件でもあれば、その列挙全体を 2.0 系として 扱う。全レコードが両方欠落しているときだけ旧版 fallback に落とす(契約 T-§2.2-fields の検出規則は 「backend についての証拠」であり、混在列挙で一部のピアだけ旧版規則を当てるのは禁止)。下表の「版判定」 列は、こうして決まった版のもとで各レコードがどの行に当たるかを読む。

#same_tabtabrole版判定broadcast理由
1True値あり∈ R2.0 系する(peer_id 宛・† 同タブ = 所属 signal)自タブの対象役割。tab 情報が揃っており同タブが確定している
2True値あり∉ R2.0 系しない役割が対象外 / 不明。SUSPEND・SHUTDOWN は org の役割にだけ意味を持つ
3TrueNone∈ R2.0 系(片方欠落)する(peer_id 宛・† 同タブ = 所属 signal)same_tab=True だけで同タブが確定する。tab 欠落は旧版の徴候ではない
4TrueNone∉ R2.0 系(片方欠落)しない役割で除外(版判定より役割判定が優先)
5False値あり∈ R2.0 系しない(† 他タブは対象外・人間に報告)他タブのピアは、この org が spawn したものではない(T-§4.2 retained の同一タブ launch 規則の対偶)。所属を証明する signal も無いので送らない
6False値あり∉ R2.0 系しない他タブの無関係ペインである可能性が最も高い
7FalseNone∈ R2.0 系(片方欠落)しない(† 他タブは対象外・人間に報告)same_tab=False だけで他タブが確定する。行 5 と同じ理由
8FalseNone∉ R2.0 系(片方欠落)しない役割で除外
9None値あり∈ R2.0 系(片方欠落)原則 しない(行 5 と同じ)。tab が caller 自身のタブ値と一致すると確認できたときだけ行 1 と同じ扱い(‡)tab が載る時点で 2.0 系。same_tab 欠落は unknown であり、name 一致で同タブと結論してはならない(‡)
10None値あり∉ R2.0 系(片方欠落)しない役割で除外
11NoneNone∈ R旧版 fallback(cross_tab_peers 非広告。renga 1.4 系および現行 org-broker はここ)する(peer_id 宛・† 単一タブ = 所属 signal。フォーカス前提の確認が済んでいること)tab 情報が無い=列挙が単一タブに閉じている(契約 T-§cap の conformance MUST)ので、従来どおり role gate だけで決める(現行挙動と同一)。ただしその単一タブが窓口のタブであることは手順 1 のフォーカス確認で担保する
12NoneNone∉ R旧版 fallbackしない旧版経路でも未知 role は対象に含めない。役割が判定できないペインへの broadcast は org 外プロセスへの誤爆になる

旧版 fallback の発動条件は 1 つだけ: 列挙内の全レコードで same_tab と tab が 両方 None のときに限る(行 #11 / #12)。片方でも値が載っていれば 2.0 系として扱う。特に same_tab=False は 「他タブに居ることが分かっている」という 2.0 系の積極的な情報であり、旧版扱いして role 判定だけに 戻してはならない(他タブのピアを名前宛で取りこぼす)。

2.0 系と判定したら、broadcast の前に人間へ報告して確認を取る(operational gate): ゲート本文は list_peers を呼ぶ全経路の共有 reference に切り出してある。list_peers の直前に .claude/skills/org-delegate/references/capability-first-drive-operational-gate.md を Read し、interactive-action の分岐を適用する(/org-suspend は人間が起動する flow なので 人間が画面の前に居り、送信前に停止して確認を取れる。同 reference §6 の表 #1)。

同 reference が定めることのうち、/org-suspend にとって要点だけ再掲する(正本は reference 側。 ここに手順を複製しない):

  • 版判定が capability 形なら、それを旧版形として読み替えない(reference §1-1)。「未承認だから 非広告経路として解釈する」は契約が名指しで禁じている誤り。承認前に控えるのは cross-tab addressing に 依拠した行動であって、解釈ではない
  • 報告に必ず含める: 見えているタブ数 / 列挙されたピア総数 / うち他タブ判定の件数、および broadcast 対象に選んだピアの name / peer_id / cwd の一覧(人間が誤爆リスク=別 org を巻き込まないかを 判断できる材料にする)
  • 人間の確認を得てから先へ進む。確認前は 1 通も出さない
  • 他タブピアは † のとおりそもそも broadcast 対象に入らない。gate の役割は「他タブに何が見えているか」を 人間に見せて、停止対象外にした集合が妥当か判断してもらうことにある
  • gate は初回駆動に対して 1 度だけで、/org-suspend の実行ごとではない。照会と記録は reference §2 / §3-A のとおり tools/capability_gate.py と tools/journal_append.sh で行う。以後の /org-suspend(.claude/skills/org-down/SKILL.md 経由の suspend を含む)はこの記録に従う
  • 記録がある場合でも、他タブ判定のピアが前回より増えていたときはその差分を報告する(gate の 再発動ではなく、停止対象外にした集合の変化を人間に見せるため)
  • production-activation gate(reference §4)は /org-suspend を停止させない。未記録の間は他タブ ピアを対象外にしたまま同タブ集合で続行する(下の真理値表の行 5〜10 がまさにその形)。止まるのは first drive gate だけである
  • 旧版 fallback(行 #11 / #12。現在配備されている全 backend — org-broker を含む — はここを通る)では この gate を発動しない。従来どおり無停止で broadcast に進む(既存運用は一切変わらない)

宛先は必ず peer_id: broadcast 対象に決まったピアへは send_message(to_id="<peer_id>") で送る。 to_id に name を渡すと 2.0 系では名前解決が送信者タブ内に限定されるため、他タブのピアは [pane_not_found] になる(list_peers 由来の id 宛はタブ横断で解決される)。旧版 fallback の行でも peer_id 宛は同じく有効なので、経路を 1 本に揃えるため版によらず常に peer_id を使う。

†(broadcast 対象の所属判定 — 同タブのみが自組織): SUSPEND / SHUTDOWN は同タブのピアにだけ送る。 他タブのピアには送らず、人間に報告する。根拠は identity 照合ではなく構造である:

  • 同タブのピア(same_tab=True、および旧版 fallback の列挙全体)は自組織のもの。caller のタブの ペイン群は、この org が組み上げたレイアウトである(旧版 fallback では、その単一タブが窓口のタブで あることを手順 1 のフォーカス前提の確認で担保したうえで成り立つ)。契約 T-§4.2 は ratified §4.2 の 「harnesses MUST launch every orchestrator-spawned pane in the same tab」を retained(非 supersede) として維持しており、この org が spawn したペインは全て同一タブに居ることが契約上保証されている。 したがってここは role gate だけで判定し、追加の identity 一致を要求しない(要求すると、identity を state.db に記録しない役割 — 後述の on-demand curator — を永久に取りこぼし、現在配備の backend の挙動も 変わってしまう)
  • 他タブのピアは、同じ契約規則の対偶により、この org が spawn したものではない。よって broadcast 対象から外す。role が対象役割でも送らない

なぜ identity 照合で他タブピアを「自組織」と認定しないのか: 他タブピアの所属を証明できる signal が 現状存在しないためである。worker-{task_id} の task_id は人間が付ける slug で、別 checkout の org が 独立に同じ値を使いうる(runs.task_id の UNIQUE 制約はその DB の中だけの話で、org を跨いで一意で あることは何も保証しない)。cwd も証明にならない — ja root 配下には人間が開いたペインや pr-watch ペインが生えるし、別 checkout の org が似た階層構造を持つこともある。runs.worker_dir_id は nullable (tools/state_db/schema.sql)なので cwd 照合を必須にもできない。証明できない以上、送らないのが唯一の 安全な選択であり、「たぶん自組織だろう」で SUSPEND / SHUTDOWN を撃つと別 org を止める。

将来この規則を再訪する条件: worker を意図的に別タブへ配置する運用(マルチタブ配置ポリシー)を 導入するときは、本規則と併せて session スコープの identity をピアに刻む仕組みが必要になる。 それが無いまま他タブへ broadcast を許すと、上の証明不能性がそのまま誤爆になる。

他タブピアの扱い(役割共通):

  • broadcast しない。SUSPEND も SHUTDOWN も送らない
  • close_pane も撃たない(そもそも pane 制御面では到達できない。契約 T-§4.2 と下記 §)
  • 人間に報告する: 「他タブに role が {worker,dispatcher,curator} のピアが N 件見えているが、 自組織のものと確認できないため停止操作の対象外にした」ことを、name / peer_id / cwd 付きで 提示する。人間がそれを自組織のものだと判断した場合の対応(手動停止など)は人間の判断に委ねる
  • curator の補足: on-demand curator の identity は設計上 state.db に書かれない (org-start は curator_pane_id / curator_peer_id を StateWriter.CLEAR で明示クリアし、 .dispatcher/CLAUDE.md は「state.db に書かない(null が正常系。 生存確認は list_panes のみ)」と規定する)。同タブの curator はタブ所属で自組織と判定できるので 従来どおり graceful に SHUTDOWN を受け取る(オンデマンド curate と suspend が重なったケースは この経路で停止する)

§(他タブピアの停止確認に関する注記): same_tab=False と判定されたピアは、messaging では到達 できてもペイン制御では到達できない(契約 T-§4.2: pane 制御は caller のタブ内に留まる)。Phase 4 で このピアの close_pane が [pane_not_found] を返しても閉鎖の証拠にはならないので、Phase 4 の 「[pane_not_found] の扱い」節の他タブ例外に従う。

‡(same_tab 欠落時の扱い — 名前一致で「同タブ」と結論してはならない): 2.0 系と判定した列挙で same_tab が載らないレコードは unknown であり、他タブ扱いに倒す(§ の制約と † の identity 確認を 両方適用する安全側)。

mcp__org-broker__list_panes に同じ name / role のペインが在ることを「同タブである」の根拠に使ってはならない。 name は全タブ列挙では一意でなく(契約 T-§2.2。予約名は 2 org 並走で構造的に衝突する)、role はさらに 非一意なので、他タブのピアが自タブのペインと name / role を共有していても矛盾しない。この一致を 同タブの証拠に使うと、他タブのピアが同タブと誤分類され、† の identity 確認を素通りして別 org へ SUSPEND / SHUTDOWN が飛ぶ(本節が防ごうとしている事象そのもの)。

同タブと積極的に結論してよいのは、same_tab == True が載っているか、tab の値が caller 自身のタブの 値と一致することを確認できた場合だけである。どちらも取れなければ unknown のまま他タブ扱いにする。

Phase 2: 未応答ワーカーのスクレイプ

応答がなかったワーカーについて:

  1. .state/workers/ から該当ワーカーの状態ファイルを読み、Pane Name と Directory を取得
  2. Pane Name を Phase 1 の台帳で pane_id に解決する。inspect_pane はペイン制御面なので、 使うのは list_panes 由来の pane_id であって peer_id ではない:
    • 台帳に pane_id が無いピア(list_panes に居ない、または Phase 1 の列挙面のゲート (i) が 不成立で台帳全体に id を載せなかった)は 3 通りに分かれる。いずれもスクレイプは 諦め、Step 4 の git 情報だけで状態を推定するが、記録する状態が違う:
      • same_tab=False / ‡ で他タブと導出されたピア → 「他タブに居るのでスクレイプ不能」。生存して いる可能性があるので消滅と読み替えない
      • それ以外で、F-1 の (i) pane_exited 観測 or (ii) 列挙が窓口タブのものと確認済み →「消滅済み」
      • それ以外で F-1 のどちらも取れない → indeterminate。フォーカスが別タブへ移っただけで 生存中のピアが不在に見えている可能性があるので、消滅済みとして記録せず判定不能のまま残し、 Phase 4 の最終報告に載せる。列挙面のゲート (i) が不成立のケースはここに落ちる — 列挙を 自タブと確立できていない以上 F-1 の (ii) も同時に満たせないため
    • Phase 1 から時間が経っているので、使う直前に mcp__org-broker__list_panes を取り直し、その pane_id の name / role が台帳と一致することを確認してから叩く。一致しなければ pane_id が別ペインへ再割当てされているので、そのペインには触れずスクレイプを諦める (Phase 3.7 と同じ identity 照合の規律。無関係なペインの画面を当該ワーカーの 「Current State at Suspend」に書き込むのを防ぐ)
  3. 画面内容スクレイプで最新のコンソール出力を読む:
    mcp__org-broker__inspect_pane(target="<pane_id>", format="text")
    
    target="worker-{task_id}" の名前指定はしない — 2.0 系では名前解決が呼び出し側のタブ内に 限定され、同名ペインが別タブに存在しうるため。画面表示だけでは不十分な場合は、次の Step 4 の git 情報で補完する
  4. ワーカーの作業ディレクトリで以下を実行:
    • git status
    • git diff --stat
    • git log --oneline -5
  5. これらの情報からワーカーの状態を推定する

Phase 3: 状態書き込み

state-db cutover (M4, Issue #267): .state/state.db が唯一の SoT。 構造化セクション (Status / Updated / Suspended / Dispatcher / Curator / Worker Directory Registry / Active Work Items / Resume Instructions) は 必ず StateWriter 経由で書く。transaction() の post-commit hook が .state/org-state.md を DB から自動再生成する (markdown 直接編集禁止 — drift_check で検出される)。free-form な session notes / Pending Lead / 学び等は notes/ 配下に保存する (notes/README.md 参照)。 .state/journal.jsonl は M4 で廃止 (events テーブルが SoT)。 DB が古い場合は python -m tools.state_db.importer --db .state/state.db --rebuild --no-strict で再構築する。

  1. 既存の org-state.md を org-state.prev.md にコピー(バックアップ)

  2. DB に Status / Suspended を書く (StateWriter.transaction() 経由。post-commit hook が .state/org-state.md を自動再生成、regen 失敗時も DB は確定済みで stderr 警告のみ):

    python -c "
    from datetime import datetime, timezone
    from pathlib import Path
    from tools.state_db import connect
    from tools.state_db.writer import StateWriter
    ts = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%fZ')
    conn = connect('.state/state.db')
    with StateWriter(conn, claude_org_root=Path('.')).transaction() as w:
        w.update_session(status='SUSPENDED', suspended_at=ts, updated_at=ts)
    "
    
    • bash / zsh / PowerShell すべて "..." 内の改行をそのまま透過するので multi-line でも cross-shell。Windows CMD は heredoc 不可なので py -3 -c "ts=...; conn=...; w=...; w.begin(); w.update_session(...); w.commit()" の単行 fallback を使う(その場合 transaction() の rollback / regen 自動 swallow は失われるので追加で try/except を書く)
    • 同コマンドが .state/org-state.md の Status 行を SUSPENDED に切り替える (DB 由来で再生成)
    • free-form な「Resume Instructions の補足説明」「Pending Lead」「学び」等は notes/ に保存する (notes/README.md 参照)。markdown 直接編集は drift_check で検出される。update_session(resume_instructions=...) は構造化セクションとして DB に書く
  3. 各 Work Item の状態を更新する場合は upsert_run(task_id=..., status=...) を transaction() 内で呼ぶ

  4. 各ワーカーの .state/workers/worker-{id}.md を更新:

    • Current State at Suspend セクションを追加/更新
    • Progress Log に中断時の状態を追記
  5. suspend イベントを DB に追記 (tools/journal_append.py は M4 で DB-only ルーティング。ts は自動付与):

    py -3 tools/journal_append.py suspend \
        reason=user_requested \
        --json '{"active_workers": ["worker-xxx"], "pending_items": ["blog-redesign"]}'
    

    event 名と payload key の規約は docs/journal-events.md を参照。

Phase 3.5: ダッシュボードサーバー停止

kill $(cat .state/dashboard.pid 2>/dev/null) 2>/dev/null || true

注: この blind kill は「/org-start で再開する前提の中断」なので簡素なまま残す。daemon ごと 落とす /org-down では、pid recycle による誤 kill を避ける stale-pid-safe な 停止(/proc / Get-CimInstance の CommandLine 照合)に差し替える。

Phase 3.6: secretary_queue_watcher の停止(broker のみ)

broker 面(ORG_TRANSPORT=broker)で org-start Block C3 が run_in_background で 常駐させた滞留 watcher を停止する。renga では watcher が存在しない(queue.jsonl 非依存)ので、 transport が renga なら本 Phase はまるごと skipする。

watcher は起動時に .state/secretary_queue_watcher.json へ自分の pid / cwd / cmdline / started_at / broker_state_dir を記録している。停止は pid 単独で kill せず、(a) 記録された broker_state_dir が 現在の ORG_BROKER_STATE_DIR と一致し(別 org / 別 broker の watcher 誤停止防止)、かつ (b) pid が 生存し live argv(Linux/WSL は /proc/<pid>/cmdline、macOS/BSD は ps -p <pid> -o args= フォールバック)が 本 watcher であることを照合できたときだけ SIGTERM する。照合が外れたら kill せず sidecar を stale として 削除する(誤 kill 防止)。この照合ロジックは helper に入っているので、POSIX では 1 行呼ぶだけでよい:

Mac / Linux / WSL:

python3 tools/secretary_queue_watcher.py --stop   # Windows で console python を使う場合は py -3 ...

出力の 1 行(STOP: ...)で結果を確認する(SIGTERM を送信し停止 / stale sidecar を削除 / 既に停止済み)。exit 0 が正常系(停止・stale 掃除・既停止のいずれも 0)。macOS は ps フォールバックで identity 照合できるので --stop がそのまま効く。exit 2(identity 未確認)は /proc も ps も無い環境 (Windows native)でのみ出るシグナルで、その場合は次の PowerShell 手順を使う。

Windows native(PowerShell) — /proc が無く helper の argv 照合が使えないので、 Get-CimInstance Win32_Process の CommandLine で identity を照合してから Stop-Process する (kill -0 / kill -TERM の直訳ではなく Windows 別手順):

$pf = ".state\secretary_queue_watcher.json"
if (Test-Path $pf) {
  $rec = Get-Content $pf -Raw | ConvertFrom-Json
  $wpid = [int]$rec.pid
  $ownOk = $false
  try {
    if ($env:ORG_BROKER_STATE_DIR) {
      $ownOk = ((Resolve-Path $rec.broker_state_dir).Path -eq (Resolve-Path $env:ORG_BROKER_STATE_DIR).Path)
    } else {
      $ownOk = ((Resolve-Path $rec.cwd).Path -eq (Get-Location).Path)
    }
  } catch { $ownOk = $false }
  $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$wpid" -ErrorAction SilentlyContinue
  $idOk = $proc -and ($proc.CommandLine -match 'secretary_queue_watcher\.py')
  if ($ownOk -and $idOk) {
    Stop-Process -Id $wpid -Force
    Write-Output "secretary_queue_watcher (pid=$wpid) stopped"
  } else {
    Write-Output "watcher pid stale / different org / not running; not killing, removing stale sidecar"
  }
  Remove-Item $pf -ErrorAction SilentlyContinue
}

Phase 3.7: attention watcher の停止(ペイン teardown の前)

attention watcher は dispatcher ペインの右 split に常駐する CLI ペインなので、Phase 4 のペイン 一括 teardown より前に停止する(dispatcher を先に閉じると attention ペインが孤児化 / pane_id recycle され、後続の識別を壊すため)。attention watcher を起動していないセッションでは sidecar も live pane も無く、本 Phase は no-op。

停止は /org-attention-stop と同じ identity 照合を使う (sidecar の pane_id を無検証で close_pane しない。pane_id が別ペインへ再割当てされていると 無関係なペインを kill する — Issue #468):

  1. mcp__org-broker__list_panes で name="attention" または role="attention" の live pane を 全て集める(= 確認済み attention ペイン集合。各レコードの id(= pane 制御用の pane_id) を控える)。 この列挙も (G-OWNTAB) の 2 手段で自タブのものと確立してから使う — 確立できなければ close へ 進まない(相対セレクタへフォールバックしない)。以降の手順 5 / 6 は gone / recycled と同じ 「close しなかった」経路として扱い、attention watcher を停止できなかったことを人間への報告に 明記する(Phase 4 手順 7 の部分中断報告に載せる)
  2. .state/attention_pane.json を Read で開けたら pane_id を読む(= sidecar pane_id)。無ければ 「sidecar 無し」
  3. sidecar pane_id を list_panes が返す name/role で分類する(sidecar 記録の name は信用しない):
    • verified(確認済み集合に含まれる)→ その pane はいまも本物の watcher。close 対象
    • recycled(list_panes にあるが name/role が attention でない)→ pane_id が別ペインへ再割当て済み。 絶対に close しない
    • gone(list_panes に無い)→ 既に消滅。close しない
  4. 確認済み集合の各ペインを list_panes 由来の pane_id で mcp__org-broker__close_pane(target="<pane_id>") する (target="attention" の name 指定はしない — role だけ持つ孤児に当たらないため)。 ここは pane 制御文脈で、かつ対象は自タブと確立済みの mcp__org-broker__list_panes(手順 1)由来= caller のタブに居ることが確定 しているので、[pane_not_found] / [pane_vanished] は既に閉じた扱いで skip してよい(close の 目的は消滅であり、既に消えていれば結果は同じ。他タブのペインに対する例外と messaging 文脈での 扱いは Phase 4 の「[pane_not_found] の扱い — messaging と pane 制御で分岐する」を参照)
  5. sidecar があれば 分類によらず必ず削除する:
    rm -f .state/attention_pane.json     # Windows native は del .state\attention_pane.json
    
  6. journal に 1 行追記する。実際に close したペインがある場合のみ pane_id を載せ、recycled / gone で close を 1 つも行わなかった場合は reason=stale_sidecar にする(無関係なペインを停めた誤記録を防ぐ):
    bash tools/journal_append.sh attention_watch_stopped pane_id=<N>          # close した場合
    bash tools/journal_append.sh attention_watch_stopped reason=stale_sidecar # close しなかった場合
    

分類別の詳細な挙動と報告文は /org-attention-stop を参照(本 Phase は その要点を suspend フローに埋め込んだもの)。

Phase 4: 全ペイン停止

停止順序が重要。ワーカー → ディスパッチャー → キュレーターの順で停止する。

このフェーズは全ステップを Phase 1 の台帳の id で回す: mcp__org-broker__send_message の to_id は peer_id、mcp__org-broker__close_pane の target は pane_id、pending 集合と poll_events の突合は台帳 エントリを主キーにする。name は人間可読のラベルとしてのみ扱い、宛先解決には使わない — 2.0 系では 名前解決が呼び出し側のタブ内に限定され、同名ペインが別タブに存在しうるため。

[pane_not_found] の扱い — messaging と pane 制御で分岐する

本フェーズ(および Phase 1 / Phase 2)の送信・ペイン制御すべてに適用する分岐。分岐の第一軸は 呼び出し文脈、第二軸は同タブ / 他タブである(世代では分けない)。正本は .claude/skills/org-delegate/references/renga-error-codes.md。

  • messaging 文脈(mcp__org-broker__send_message。broker では [peer_not_found] が同じ意味で返る): 受けても即「ワーカー閉鎖」と断定しない。2.0 系では名前解決が送信者タブ内に限定され、また 宛先台帳が古く id が再割当て / 消滅していると、生存中のペインでもこのコードが返るため、次の順で 切り分ける:
    1. mcp__org-broker__list_peers を取り直す(2.0 系は全タブ列挙なので他タブのワーカーもここで拾える)
    2. 宛先の選び直しは名前で行わない。全タブ列挙では name が一意でなく(契約 T-§2.2)、 同名レコードが複数返りうるので、名前一致で拾うと別 org のピアへ SUSPEND / SHUTDOWN を 送りうる。次の優先順で 1 件に絞る(.claude/skills/org-delegate/references/renga-error-codes.md の messaging 分岐と同一規則):
      • a. 台帳に控えてある peer_id がまだ列挙に在れば、その id をそのまま使う
      • b. その peer_id が消えている場合(再接続で id が振り直された等)、名前一致レコードは 候補にすぎない。採用してよいのは名前が一意に定まる範囲に閉じていると分かっている ときだけで、次のどちらかを満たす場合:
        • same_tab: true を確認できたレコード(同一タブ内でのみ名前は一意 — 契約 §1.8)
        • 旧版 fallback の列挙(全レコードで marker 欠落)で、その名前のレコードが 1 件だけの場合。 marker 無し列挙は caller のタブに閉じていることが契約 T-§cap の conformance MUST で保証され、 そのタブが窓口のタブであることは手順 1 のフォーカス前提で確認済みなので、名前は一意に働く。 現在配備の backend(org-broker / renga 1.x)はここを通る — この枝が無いと、再接続で id が変わった生存ワーカーを retarget できず indeterminate に落として取り逃がす
      • c. 上のどちらも満たさない(他タブの同名しか無い / 同名が複数残る)→ 再送しない。 lifecycle 確認(次項)へ進む
    3. 絞れた peer_id で 1 回だけ再送する(ループにしない — 2 回目以降も結果は同じで、 本当に閉じていた場合の検知が遅れるだけ)
    4. peer_id 宛でも失敗した / 2-c で再送できなかったときに初めて lifecycle 確認へ進む。ここで確認を省いて いきなり閉鎖確定にしない(契約 T-§2.1 の復旧順は確認段を含む):
      • 同タブのピア: pane_exited の観測をもって消滅を確定する。mcp__org-broker__list_panes の不在を根拠に するのは F-1 の (ii) を満たすときだけで、満たせなければ indeterminate として残す
      • 他タブのピア(same_tab=False / ‡ で他タブと導出): list_panes には原理的に出ないので 不在は証拠にならない。pane_exited の観測か、後続 mcp__org-broker__list_peers からの消失で確定する
      • どちらも取れなければ indeterminate。閉鎖に倒さず journal に記録して人間に報告する
    5. 消滅が確定したら pending 集合から外す。worker 状態ファイルの Status: pane_closed は窓口が 書かない — この terminal transition の書き手はディスパッチャーである (docs/contracts/state-semantics-contract.md)。 窓口は Progress Log への追記と journal 記録に留める
  • pane 制御文脈(mcp__org-broker__close_pane / mcp__org-broker__inspect_pane / mcp__org-broker__list_panes の target):
    • 同タブのペインに pane_id 宛で [pane_not_found] / [pane_vanished] が返ったら、従来どおり 「既に閉じた」扱いで skip してよい(close は消滅させるのが目的なので、既に消えていれば結果は同じ)
    • 他タブのペインは例外 — 閉鎖と読んではならない。契約 T-§4.2 はタブ横断の pane-addressed control が pane_not_found を返すと規定しているので、生存中でも同じコードが返る。 このコードを「閉じた」と読むと、稼働中のワーカーを残したまま「全ペイン停止」を完了報告する。 該当ピアは pending に残し、journal に記録して人間に報告する(停止できない残存)
    • 名前宛で受けた場合はこの結論を出さず、pane_id に解決し直してから判断する
    • close_pane / set_pane_identity はそもそも名前宛・"focused" 宛で撃たない((G-MUST))。 この 2 本は Group B なので、caller_scope が広告されていても caller_scope_close_identity が確立していなければ相対セレクタが user 可視タブ / 全タブに 解決されうる。(G-OWNTAB) の 2 手段で自タブと確立した mcp__org-broker__list_panes 由来の数値 pane_id でのみアドレスする

停止手順

  1. mcp__org-broker__list_peers と mcp__org-broker__list_panes を取り直し、Phase 1 の台帳を最新化する(Phase 1 から 時間が経っているため、peer_id / pane_id の両方を取り直す)。再構築でも Phase 1 手順 1 の join 規則をそのまま適用する(pane_id の出所は mcp__org-broker__list_panes だけ / 列挙を自タブと 確立できなければどのエントリにも pane_id を割り当てない((G-OWNTAB)) / role は join キーに しない / 他タブ・same_tab 欠落のエントリには pane_id を割り当てない)。他タブ判定のピアが Phase 1 から増えていた場合は、真理値表の operational gate に従ってその差分を人間に報告し、 確認を得てから停止指示へ進む

    • この取り直しが (G-OWNTAB) の確立段でもある: 以降の mcp__org-broker__close_pane はすべてこの列挙から 採った数値 pane_id で撃つので、列挙を自タブと確立できたかをここで判定して以降の全 close に 引き回す(単一タブ backend = org-broker / caller_scope の 2 手段だけ。list_peers の same_tab は確立手段に数えない)。いずれも成立しなければ close を 1 つも撃たない — 台帳の 全エントリが「pane_id を持たないエントリ」と同じ扱いになり、pending のまま手順 7 の部分中断 報告に載せる(相対セレクタへフォールバックしない)
  2. ワーカーを先に停止: Phase 1 の宛先選定の真理値表を R = {worker} で適用し、選ばれた ワーカーの peer_id を to_id に渡して mcp__org-broker__send_message で終了を指示: 「SHUTDOWN: 作業を終了してください。」

  3. ワーカーペインが閉じたことを確認 — 2-pass 構造で実施:

    Pass 1 (polite shutdown の観察、最大 10 秒):

    mcp__org-broker__poll_events で pane_exited を long-poll する。types=["pane_exited"] フィルタで他 type を除外しつつ、deadline 内でループして待機対象が全て閉じたら break:

    pending = {SHUTDOWN を送った全ワーカーの台帳エントリ set}
    cursor = None                           # 初回は since 省略
    deadline = now + 10 秒
    while pending not empty and now < deadline:
        remaining_ms = (deadline - now) ミリ秒
        result = mcp__org-broker__poll_events(
            since=cursor,
            timeout_ms=min(remaining_ms, 10000),
            types=["pane_exited"]
        )
        cursor = result.next_since
        for ev in result.events:
            # 突合は id で行う。契約 T-§3.1 が canonical 名を固定している
            ev_pane = ev.id ?? ev.pane_id          # canonical: ev.id (list_panes と同じ id 空間)
            ev_peer = ev.peer_id ?? ev.agent_id    # canonical: ev.peer_id (list_peers と同じ id 空間)
            # id 突合は「両辺が非 null」を前提にする。name のみのイベントでは ev_pane / ev_peer が
            # どちらも null になり、pane_id 未設定 (他タブ) のエントリと null == null で誤一致して
            # 無関係な生存ワーカーを pending から external してしまう
            hit = pending の中で
                    (ev_pane is not None かつ entry.pane_id == ev_pane) または
                    (ev_peer is not None かつ entry.peer_id == ev_peer)
            if hit is None and ev.name is not None:
                # name フォールバックは「同タブ確定エントリ」に限定する(下記)
                hit = pending の中で entry.name == ev.name かつ entry が同タブ確定
            if hit: pending.remove(hit)
    # deadline 到達 or pending が空で抜ける
    
    • 突合キーは id。ratified §3.1 は per-event フィールド名を規定していないが、批准済み amendment (2026-08-09) の T-§3.1 が canonical 名を id(pane 側)/ peer_id(peer 側)に固定している。上の疑似コードが ev.pane_id / ev.agent_id も見るのは、canonical 名を出さない backend への**寛容(tolerance)**で あって、これらが正の名前だという意味ではない。pane 側 id と peer 側 id のどちらか一方でも 一致したら hit とする(他タブピアは list_panes に出ないので pane 側だけでは join できない)
    • name 単独を突合キーにしてはならない。2.0 系の名前はタブ内一意でしかないので、別タブの 同名ペインの pane_exited で自組織の生存ワーカーを誤って retire する。name フォールバックは same_tab=True / 旧版 fallback で同タブと確定しているエントリに限って使う(他タブ対象が pending に居る間は name フォールバックを無効にしてもよい)
    • 初回 since 省略で「今以降のイベントだけ」セマンティクス(過去の pane_exited を replay しない)
    • types=["pane_exited"] filter は cursor を全 type で advance させるので重複 scan なし
    • filter 不一致イベント到着で long-poll が early return (events:[] + advanced cursor) するため、空応答時は deadline までループ継続
    • 10 秒以内に閉じなかった残留ワーカーは Pass 2 へ

    Pass 2 (残留ワーカーへのフォールバック + 再確認、最大 5 秒):

    • close の前に (G-OWNTAB) が成立していることを確かめる(手順 1 で判定済み。成立していなければ この Pass 2 では 1 つも close せず、pending をそのまま次の再確認へ送る)
    • close の直前に identity 照合する(Phase 3.7 の attention watcher 停止と同じ idiom。台帳を 取り直してから時間が経つと pane_id が別ペインへ再割当てされうるため、控えた id が期待どおりの ペインをなお指していることを毎回確かめる):
      1. mcp__org-broker__list_panes を実行する
      2. 控えた pane_id のレコードが name == "worker-{task_id}" かつ role == "worker" をなお 指している → その数値 id で close する
      3. レコードはあるが name / role が一致しない(別ペインへ再割当て済み)→ close しない (相対セレクタへフォールバックしない)。pending に残したまま次の再確認へ送る
      4. レコードが列挙に無い → close 対象を確認できないので close しない。消滅の確定は F-1 の (ii) を満たすときだけで、満たせなければ「判定不能」に倒す
    • pending に残っているエントリのうち、pane_id を持ち、上の照合を通ったもの (= caller のタブに居ることが確定したペイン)それぞれに対して:
      mcp__org-broker__close_pane(target="<照合済みの数値 pane_id>")
      
      でペインを明示破棄する(worker-{task_id} のような相対セレクタでは撃たない — (G-MUST))。成功時は "Closed pane id=N." テキストが返る。ここは pane 制御文脈なので [pane_not_found] / [pane_vanished] は既に閉じた扱いで skip してよい(.claude/skills/org-delegate/references/renga-error-codes.md 参照)。[last_pane] はワーカー停止段階では通常発生しない(窓口/ディスパッチャー/キュレーターが残っているため)
    • pane_id を持たないエントリ(他タブのワーカー、および (G-OWNTAB) 不成立で id を割り当てられ なかったエントリ)には close_pane を撃たない。タブ横断の pane 制御は原理的に不可で、返る [pane_not_found] は閉鎖の証拠にならない(契約 T-§4.2)。 このエントリは pending に残したまま次の再確認へ送る
    • その後、同じ poll_events ループを timeout_ms=5000 / deadline 5 秒で再度回し、close_pane 由来の pane_exited を消化する
    • Pass 2 後もまだ pending が空でなければ mcp__org-broker__list_panes / mcp__org-broker__list_peers で生存確認する(id 突合。list_panes は 1 タブ分しか返さないため、他タブのワーカーは list_peers からの消失で判定する。list_panes の不在を停止の証拠にするのは F-1 の (ii) を満たすときだけで、満たせなければ「判定不能」に倒す)。残存または判定不能なら、閉鎖扱いにせず journal に記録して人間に報告する(強制終了は現状未サポート。「停止できなかったワーカーが居る」ことを報告に明記し、全ペイン停止を偽って完了報告しない)
  4. ディスパッチャーを停止: 真理値表を R = {dispatcher} で適用し、選ばれたディスパッチャーの peer_id を to_id に渡して mcp__org-broker__send_message で終了を指示: 「SHUTDOWN: 作業を終了してください。」

  5. キュレーターを停止(存在する場合のみ): curator は常駐しないため、通常このステップは no-op。curator の存在確認は mcp__org-broker__list_peers の role == "curator"(name == "curator" は補助) で行い、peer_id を得る(list_panes は呼び出し側のタブしか返さないので、別タブで起動した curator を取りこぼす)。存在した場合(オンデマンド curate 実行中に suspend が重なったケース) のみ真理値表を R = {curator} で適用し、その peer_id 宛に send_message で終了を指示: 「SHUTDOWN: 作業を終了してください。」(curate は move-then-mark 設計のため途中停止でも 破壊的な中間状態は残らない)

  6. ディスパッチャー(および存在した場合のみキュレーター)も (3) と同じ 2-pass 構造で確認する。 pending 集合は名前ではなく台帳エントリの集合にする(pending = {dispatcher のエントリ}、 curator が存在した場合はそのエントリも追加)。pane_exited の突合も (3) と同じく pane 側 id / peer 側 id のいずれか一致で行い、ev.role == "dispatcher" / "curator" と ev.name は同タブ確定エントリに対する補助に留める:

    • Pass 1: poll_events(types=["pane_exited"], timeout_ms=10000) 相当ループ
    • Pass 2: (3) と同じ前段((G-OWNTAB) の成立確認 → close 直前の identity 照合。期待する name / role は dispatcher / curator に読み替える)を通したうえで、pending に残った エントリのうち pane_id を持ち照合を通ったものに mcp__org-broker__close_pane(target="<照合済みの数値 pane_id>") を送り(相対セレクタでは撃たない)、 poll_events ループ (timeout_ms=5000) で再確認。pane_id を持たない(他タブの / (G-OWNTAB) 不成立の)エントリや 照合が外れたエントリには撃たず、(3) と同じく 残存として人間に報告する

最後のペイン (窓口) の扱い: ディスパッチャー(と存在した場合のキュレーター)を閉じた時点で、 窓口ペインが窓口タブに残る最後のペインになる。窓口は自分自身を mcp__org-broker__close_pane しない。 その根拠は次の 2 つで、どちらも「最後の 1 ペインだから backend が閉じさせてくれない」ことには 依存しない構造的な理由である:

  • 窓口は人間との唯一の接点である: 自分を閉じると、この後の「人間に報告」ステップで suspend 完了を 渡す相手の画面が消える。報告前に自分を殺すと、人間から見て「中断できたのか分からないまま画面が 消えた」になる
  • 自己終了は人間の責務境界にある: 窓口プロセスの終了は人間の exit / /exit / 端末クローズに 委ねる。org-suspend が担うのは「状態保存 + ja 管理下の補助プロセスとペインの停止」までで、窓口 ペインを閉じる責任は負わない(冒頭の「責務境界(/org-suspend と /org-down)」で引いた線と同じ)

[last_pane] はエラーハンドリングとしてのみ残す: 何らかの経路で窓口ペインに close_pane が飛び [last_pane] が返った場合は、強制再試行せずそのまま人間の exit に委ねる。ただしこのコードを 自己 close の安全網として当てにしてはならない — このコードの意味は .claude/skills/org-delegate/references/renga-error-codes.md のとおり「唯一のタブの唯一のペイン」であり、他のタブが存在する状況で窓口タブの最後のペインが 保護されるかは 2.0 系では未確認である(保護されない読みが素直だが、いずれにせよ当てにしない)。 自己 close をしない根拠は上の 2 つだけである。

  1. 人間に報告。pending と unresolved が両方空のときだけ「中断しました」と言い切る。 どちらかに残りがあれば部分中断として報告し、完了を偽らない:

    全て停止できた場合:

    組織を中断しました。
    - 保存済み: {N}件の作業アイテム
    - 状態ファイル: .state/org-state.md
    /org-start で再開できます。
    

    停止できなかった / 判定できなかったピアが残る場合(pending が空でない、unresolved が 空でない、他タブの対象 role ピアを検出した、Phase 3.7 で attention watcher を停止できなかった、 のいずれか):

    組織を部分的に中断しました(停止しきれていないペインがあります)。
    - 保存済み: {N}件の作業アイテム
    - 状態ファイル: .state/org-state.md
    - 停止できなかったワーカー: {peer_id / name / cwd を列挙}
    - role が判定できず対象外にしたピア: {peer_id / name / cwd を列挙}
    - 他タブに見えたが自組織と確認できずに対象外にしたピア: {peer_id / name / cwd を列挙}
    - 停止できなかった attention watcher: {pane_id を列挙}
    これらは走り続けている可能性があります。手動での確認 / 停止をお願いします。
    /org-start で再開できますが、上記のペインが残っている前提でご判断ください。
    
    • 該当が無いセクションは行ごと省く。1 件でも残っていれば「組織を中断しました」の文面は使わない (全ペイン停止を偽って報告しないこと自体が本フェーズの目的)

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

dispatcher-handover

無料日本語概要

ディスパッチャーのコンテキストを圧迫したまま session を続けるのを避けるため、 monitoring 状態(active workers / 直近 polling cursor / pending escalations)を handover ファイルに書き出し、secretary の指示で /clear → /dispatcher-resume の 流れで新しいディスパッチャー session を開始する準備をする。 Secretary から DISPATCHER_HANDOVER peer message を受領したとき、または ディスパッチャー自身が context が長くなったと判断したときに使う。

suisya-systems/claude-org-ja52026年10月12日 更新

dispatcher-resume

無料日本語概要

/dispatcher-handover で書き出した handover ファイルを読み込み、 ディスパッチャーを新しい session で復帰させる。/clear 直後の最初のターンで使う。 state.db の dispatcher_pane_id / dispatcher_peer_id を atomic に更新し、 worker monitoring の /loop 3m を再開する。 「ディスパッチャー復帰」「resume」「引き継ぎから再開」と secretary から指示された ときに使う。/org-start ではない(ワーカー・窓口・キュレーターは生きている前提)。

suisya-systems/claude-org-ja52026年10月12日 更新

goal-setup

無料日本語概要

プロジェクトのゴール台帳(registry/goals/<owner>/<repo>.md)を人間と対話して新規作成・改訂する。 README と open Issue からゴール候補を推定して提示し、「何を目指すか」「何が起きていたら未達か」を 短く質問して条項を固め、書式検証(tools/work_discovery_goals.py validate)を通してから保存する。 「ゴール台帳を書きたい」「ゴールを設定して」「ゴール未設定と出た」「台帳エラーを直して」 「このプロジェクトの目標を決めたい」等や、/work-discovery の「ゴール未設定」「ゴール台帳エラー」案内を 受けて窓口が起動する。候補の提示そのものは /work-discovery の役目で本スキルではない。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attach

無料日本語概要

組織の生きているペイン(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) には発動しない。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attention-start

無料日本語概要

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 はしない(明示起動推奨ポリシー)。

suisya-systems/claude-org-ja52026年10月12日 更新

org-attention-stop

無料日本語概要

`/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 落として」等で発動。

suisya-systems/claude-org-ja52026年10月12日 更新

suisya-systems のスキルをすべて見る

このスキルの問題を報告する