ディスパッチャーのコンテキストを圧迫したまま session を続けるのを避けるため、 monitoring 状態(active workers / 直近 polling cursor / pending escalations)を handover ファイルに書き出し、secretary の指示で /clear → /dispatcher-resume の 流れで新しいディスパッチャー session を開始する準備をする。 Secretary から DISPATCHER_HANDOVER peer message を受領したとき、または ディスパッチャー自身が context が長くなったと判断したときに使う。
org-start
組織を起動する。前回の状態を読み込んでブリーフィングし、 ディスパッチャーペインを起動する。ClaudeCode起動直後に1回実行する。
「起動して」「スタート」「始めて」等でも発動。キュレーターは起動しない(worker クローズ時の閾値チェックによるオンデマンド起動に移行済み)。
含まれるファイル(1)
- SKILL.md79.2 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
org-start: 組織の起動
ClaudeCode起動後に最初に実行するスキル。前回の状態復元とディスパッチャー起動を行う。
キュレーターは起動しない(オンデマンド化): 常駐キュレーター(spawn +
/loop 30m /org-curate)は廃止された。キュレーターはディスパッチャーが worker ペインクローズ時にtools/check_curate_threshold.pyの閾値超過を検出したときだけ一時起動される (.dispatcher/references/pane-close.mdStep 5)。 org-start ではcurator_pane_id/curator_peer_idをStateWriter.CLEARで明示クリアする (Block D-5)。curator 不在(null)が正常系である。 worker close が発生しない期間の閾値チェック飢餓ケースへの補助トリガー(org-start バックストップ)は Issue #502 でバックログ化されている。
前提: この Claude は
renga --layout opsで起動された窓口ペイン内で動作している。RENGA_SOCKET/RENGA_PANE_ID環境変数が継承されているので、mcp__renga-peers__*MCP ツール 14 種(spawn_pane/spawn_claude_pane/close_pane/focus_pane/list_panes/new_tab/send_message/list_peers/set_summary/check_messages/inspect_pane/poll_events/send_keys/set_pane_identity)でペイン操作・ピア通信・画面スクレイプ・lifecycle event 購読・raw キー入力まですべてカバーできる(renga 0.18.0+ で導入された構造。現行 org の renga transport サポート下限は 2.0.0)。 ただしこの 14 種はタブ scope が一様ではないので、主分類 3 つ + 例外で読む(各 capability トークンは独立判定 = 契約 T-§cap の independence rule。分類の正本はdocs/contracts/backend-interface-contract.mdT-§cap / T-§4.2):
- Group A(
caller_scope)= pane 制御:spawn_pane/spawn_claude_pane/focus_pane/list_panes/inspect_pane/send_keys。この capability を確立できた経路では caller のタブ内 (フォーカス非依存)に解決され、list_panesは caller のタブのみを返す(契約 T-§cap の Group A はspawn_codex_paneを含む 7 種。ここでは org の 14 種に現れる 6 種を挙げている)- Group B(
caller_scope_close_identity)= 別ゲート:close_pane/set_pane_identity。caller_scopeからは導出してはならない(close_paneは不可逆なので特に混同しない)- messaging(
cross_tab_peers)= タブ横断:send_message/list_peers。list_peersは全タブ列挙、send_messageは数値 id 宛ならタブ横断・stable name は送信側タブ内でのみ解決する。契約 T-§4.2 は messaging 到達性から pane 制御到達性を推論することを MUST で禁じている- 上記 3 分類に入らない 4 種(例外):
new_tabは Group A ではなく独立 capabilityspawn_tab側(tab 生成 / tab 指定 spawn)。poll_eventsは pane 制御でも messaging でもなく lifecycle surface で、そのタブ scope は契約 T-§3.1 が別に固定する。set_summary/check_messagesは自ペイン / 自キューに 対する操作であって、タブ横断 addressing の話ではない誤読時の分岐手順(
pane_not_foundの pane 制御文脈 / messaging 文脈)は.claude/skills/org-delegate/references/renga-error-codes.mdを参照。 ここでの 14 種は org が運用で行使する surface であって、プリフライトの必須集合とは別の数である:tools/check_renga_compat.pyは capability probe 用のserver_infoを加えた 15 種を必須(subset 判定、余剰ツールは正常)として検査する。server_infoは probe 専用で、.claude/settings.jsonの allowlist には意図的に未追加である(理由はdocs/design/renga-decoupling.mdを参照)。state DB 前提 (Issue #267 / M4):
.state/state.dbが唯一の SoT。 read 経路は DB のみ (markdown fallback は M4 で撤去)、構造化セクション (Status / Dispatcher / Curator / Worker Directory Registry / Active Work Items / Resume Instructions) の write 経路はStateWriter.transaction()経由(post-commit hook が.state/org-state.mdを DB から自動再生成、markdown 直接編集禁止 — drift_check が検出する)。free-form ノート(学び / Pending Lead 等) はnotes/配下に保存する。.state/journal.jsonlは M4 で廃止。 DB が無い場合はpython -m tools.state_db.importer --db .state/state.db --rebuild --no-strictで構築する。
輸送層(transport)両系 — 既定
renga/ opt-inbroker: 本スキルのmcp__renga-peers__*呼び出しは 既定renga(ORG_TRANSPORT無設定)で書いてあり、そのまま従えばよい(既定挙動は不変)。ORG_TRANSPORT=broker(opt-in・切戻し可)では MCP サーバー名がorg-brokerになり、ツールの 完全修飾名がmcp__renga-peers__*→mcp__org-broker__*に機械置換される(引数形・セマンティクスは同一なので手順の論理は変わらない)。輸送依存で手順が変わる点だけ broker 併記する:
- 受信モデル(push 一次 =
claude/channel/ pull フォールバック): renga は dispatcher / worker のメッセージが<channel source="renga-peers" …>として in-band で push される。broker は push 一次に再設計されている(runtime push-first 0.1.24+、transport-labdocs/design/broker-native-roles.md§9): 各ペイン同居の channel sidecar(server:org-broker-channel)が broker キューを ~1 秒間隔で claim→push し、notifications/claude/channelで本文を idle セッションへ注入する(renga の in-band push と同じ「受けたら即応答」契機)。pull はフォールバック層: sidecar 不在 / unhealthy(heartbeat timeout でdelivery_mode=PULL)/ channel 非対応ペイン / claude.ai login 不在時は、各役割が自身の cadence で能動的にcheck_messages(broker ではmcp__org-broker__check_messages)する(§9.6 読み替え表の役割別 cadence。ナッジが出れば契機になりうるが idle を起こさないため能動 poll が受信の正路。既存の「ナッジを見たらcheck_messages」prose は撤回せずこの fallback cadence として読む。ツール名以外の手順は同型)。契約面は Surface 8 で push 一次が ratified 済み(2026-06-15、S3。broker pull-only を supersede し pull は fallback として retain・renga 不変)。- spawn 儀式(folder-trust 承認 + dev-channel sidecar 承認の再導入): renga の
spawn_claude_paneは--dangerously-load-development-channels server:renga-peersを注入し「Load development channel?」を画面判定で承認する(Block D-1)。broker は--mcp-config <broker>を注入し承認プロンプトが Claude Code の folder-trust プロンプトに変わる(画面判定で機械承認)のに加えて、push 一次のため channel sidecar を--dangerously-load-development-channels server:org-broker-channelで load し dev-channel 承認プロンプト(spawn-flow 3-3b)を画面判定で 再導入機械承認する(ratified §5/§8.5 の folder-trust フローへの加算であり置換ではない。設計は broker-native-roles.md §9.5)。画面判定 =inspect_pane→claude-org-runtime dispatcher spawn-prompt-stepが返した 1 手だけをsend_keysする反復(runtime 0.1.43+)。盲目 Enter は禁止(folder-trust の初期カーソルは「No, exit」で、Enter 一発は起動したセッションを終了させる)。- エラー分岐(broker 追加コード): renga コード(
[split_refused]/[pane_not_found]/[cwd_invalid]等)に加え broker は[token_invalid]/[session_invalid]/[tool_not_authorized]/[no_backend](= adapter_unavailable)/[nudge_failed]/[peer_not_found]/[name_taken]を返しうる(未知コードは default-branch で escalate)。一覧は.claude/skills/org-delegate/references/renga-error-codes.mdの broker 節を参照。
new_tab/focus_paneは broker surface に無い(意図的除外。本フローは元々使わない)。契約面の正本はdocs/contracts/backend-interface-contract.mdSurface 8(broker auth & delivery、ratified 2026-06-14。push 一次への additive 改訂 S3 が ratified 済み(2026-06-15、「Ratified amendment」節)・既存 ratified 本文不変更)、設計 SoT は transport-labdocs/design/broker-native-roles.md§9(push 一次再設計)/docs/design/ja-migration-plan.md§5.2(ii)・§8。broker 実走(dogfood)は Epic #6 Issue G スコープで本スキルの既定経路ではない。(既定の二フレーム注記(Refs #604): ここでの「既定
renga」は運用既定(broker 実走 dogfood が Epic #6 Issue G まで未活性)の意。別にコード既定としてtools/transport.py: DEFAULT_TRANSPORTが runtime 0.1.28 (Epic #586) でbrokerにフリップ済みで、ja 生成器・transport.resolve()はこのコードフレームで render するため生成面は「既定broker」と表示する — 両フレームは指す対象(運用経路 vs コード定数)が異なり矛盾しない。総説は rootCLAUDE.md「輸送層(transport)両系」節。)
Step 0: 初期化
- transport 判定(最初のサブステップ — どの MCP 呼び出しよりも前に行う):
ORG_TRANSPORT環境変数を確認し、以降の手順で使う MCP サーバー名を確定する:echo "${ORG_TRANSPORT:-renga}"renga(無設定を含む既定): 本スキル記載のとおりmcp__renga-peers__*をそのまま使うbroker: 以降の手順(Step 0 の残りサブステップ〜Block D・失敗モードを含むすべて)のツールをmcp__org-broker__*で呼ぶとここで決めてから MCP 呼び出しに入る(冒頭注記の機械置換)。broker 環境で判定せずにmcp__renga-peers__*を先に叩くと「RENGA_PANE_ID not set — Claude Code was not launched by renga」エラーで初めて気づく無駄な往復になる
mcp__renga-peers__set_summary(broker ではmcp__org-broker__set_summary)で自分のサマリーを設定する: 「Secretary: 窓口」- ワーカー / ディスパッチャー / キュレーターが
list_peersで窓口を発見するために必須
- ワーカー / ディスパッチャー / キュレーターが
- messaging MCP の疎通確認:
mcp__renga-peers__list_panes(broker ではmcp__org-broker__list_panes)を呼び出す。- エラーなく応答が返れば MCP 有効。以降 messaging MCP ツールが使える前提で進む
- エラーが返る / ツール未登録の場合は Skill の実行を一時停止し、transport 別の
復旧をユーザーに促す(導入後にやり直してもらう):
- renga:
renga mcp installの実行を促す。詳細は README の「インストール」 セクション参照 - broker:
renga mcp installは renga 専用の復旧手順なので適用しない。 broker daemon の起動状態と--mcp-configに渡す broker 設定をユーザーに 確認してもらう
- renga:
- secretary ペイン identity の検証と自動リカバリ:
- caller pane id を先に確定する(
focused=trueを「自分」と読まない):list_panesのfocusedは「そのタブで今フォーカスされているペイン」であって caller 自身ではない。人間が フォーカスを移した瞬間focusedは別ペインを指すので、そこを起点にすると無関係のペインをsecretaryに改名しうる。identity の検証も修復も、契約docs/contracts/backend-interface-contract.mdT-§4.2 の caller pane id 取得規則に従って caller pane id 起点で行う:- 環境変数
RENGA_PANE_IDを caller pane id の起点にする(renga が起動したペイン内でのみ 継承される。冒頭「前提」で挙げた環境変数で、未設定はdetached相当):echo "${RENGA_PANE_ID:-unset}" mcp__renga-peers__list_panes(broker ではmcp__org-broker__list_panes)を列挙し、 同一 id のレコードが存在することを照合する(id recycle / 列挙不能の検出)。照合できた そのレコードが caller 自身であり、以降の期待値判定と修復はこのレコードだけを対象にする
- 環境変数
- 期待値:
name == "secretary"かつrole == "secretary" caller-id 未確定の場合 —RENGA_PANE_IDが未設定 /list_panesに同一 id のレコードが 無い / broker 等そもそも同等の caller-id を入手できない transport: 自己修復(下の自動リカバリ)を 実行せず、identity 未確認のまま人間に報告して続行判断を仰ぐ(勝手に fatal にしない)。target="focused"や裸nameのような相対セレクタへフォールバックしてはならない(契約 T-§4.2 Group B。誤タブの 別ペインを改名する経路になる)。broker の縮退の具体は下の broker 分岐を参照- 不一致の場合 —
renga --layout ops以外の経路で起動された / 旧ops.tomlで起動された既存セッションに attach 等:mcp__renga-peers__set_pane_identity(target="<RENGA_PANE_ID の値>", name="secretary", role="secretary")を呼んで自動修復(照合済みの数値 pane id で撃つ。target="focused"は使わない)- 成功すれば警告ログを events テーブルに残して続行(
bash tools/journal_append.sh secretary_identity_restored note=auto_recovered) - 失敗ケースの分岐:
name_in_use(broker ではname_taken)エラー: 既存の別ペインがsecretaryを占有している。ユーザーに状況を報告し、「現セッション継続なら全ワーカーにto_id="{numeric_pane_id}"で送信させる」「永続修復なら/org-suspend→ 終了 →renga --layout opsで再起動」の選択肢を提示name_invalid/ その他: ユーザーに原因を報告
- 一致している場合: そのまま続行
- broker(
ORG_TRANSPORT=broker)の場合: broker にはRENGA_PANE_IDに相当する caller pane id が無く、secretary 自身の pane record が存在しないこともある。したがって broker では上の caller pane id 取得規則が最初からcaller-id 未確定に落ち、set_pane_identityによる自動修復は行わない(相対セレクタで撃てば[pane_not_found] no pane for target 'focused'になるが、そもそも撃たない)。上記の不一致リカバリは再試行せず、この分岐に進む。この結論は下の「broker 論理エントリ注記」にも等しく及ぶ —list_panesに数値 id のエントリが在って手元で掴めていても例外にならない。数値 id であることは契約 T-§4.2 Group B の MUST の片方(相対セレクタでないこと)しか満たさず、もう片方(自タブと確立済みの列挙から採った id であり、かつ対象が caller であること)を満たさないからである。 このときmcp__org-broker__list_peersを identity 検証の充足根拠にはしない(下記のとおり、承認の有無によらずこの経路から得られるものは無い)。list_peersを引くのは messaging 経路が生きていることの確認までで、broker のsend_messageルーティングは peer 登録で成立し secretary の pane record を要求しないので、その確認自体には意味がある — ただしそれは identity の検証ではない(契約が MUST で禁じる「messaging 到達性から pane 制御到達性を推論する」に踏み込まないため、両者を明確に分ける)。このlist_peersの直前にも.claude/skills/org-delegate/references/capability-first-drive-operational-gate.mdを Read し、monitoring-read-onlyの分岐を適用する(同 reference §6 の表 #2b)。capability 形かつ未承認なら列挙を identity 検証の充足根拠にせず破棄し、name="secretary"の一致で満たされたとみなさない(予約名secretaryは別 org の並走タブに同名で実在しうる。共有 reference §3-B-1)。その場合は identity 未確認のまま人間に報告して続行判断を仰ぐ(勝手に fatal にしない)。承認済み(同 reference §2 のfirst_driveがrecorded)のときも同じ結論になる: §1-2-d のとおりlist_peersは caller を除外するので、name="secretary"に一致したレコードは同タブに 1 件だけ在っても自分ではありえず、自 identity の検証根拠にできない(承認の有無によらず、この経路が列挙から得られるものは無い)。なお現行org-brokerは本 amendment の capability を 1 つも広告しないので、この段落で言及したmonitoring-read-onlyの縮退(capability 形かつ未承認のときの列挙破棄)は broker 面では発火しない。caller-id 未確定の方は別物で、broker では常に発火する — capability の広告有無は first-drive gate を制御するだけで、欠けている out-of-band な caller pane id を供給しないからである。したがって broker 面でも上記の「自己修復せず人間へ報告して続行判断を仰ぐ」は必ず通るbroker 論理エントリ注記: broker の
list_panesに geometry が全て 0(w=0 h=0)でkindが null のエントリが現れることがある。これは残骸とは限らず、**adapter 実体を持たない論理エントリ(human-driven logical pane)**でありうる。典型は root secretary(窓口)自身の bookkeeping entry。扱いは次のとおり:inspect_paneは adapter 不在のため失敗する(socket close を観測する)。調査目的で叩かないclose_paneは[logical_pane] cannot close a human-driven logical paneで拒否される。残骸と決めつけて close を試みない- このエントリの
name/roleが期待値(secretary/secretary)とずれたまま残っている場合(手動テストの名残等)でも、set_pane_identityによる改名修復はしない。上の broker 分岐と同じ結論に落とし、ずれを人間に報告して続行判断を仰ぐ。理由は、そのエントリが caller である証明が形状からは立たないためである — geometry 全 0 /kindが null / root secretary の bookkeeping entry という形が示すのは「adapter 実体を持たない論理エントリである」ことだけで、「それが自分である」ことではない。数値 id で撃てば相対セレクタは避けられるが、それは MUST の片方にすぎず、caller である裏付けを欠いたまま撃てば無関係のペインをsecretaryに改名しうる
- caller pane id を先に確定する(
- プロジェクトレジストリの配置(未配置時のみ生成・既存は上書きしない、Issue #811):
registry/projects.mdは operator-local な生成ファイル(.gitignore済み)で、 コミットされているのはテンプレートregistry/projects.example.mdの方である。 fresh clone 直後は実体が無いので、ここで生成する:python3 tools/ensure_projects_registry.py # Mac/Linux py -3 tools/ensure_projects_registry.py # Windows native- 既存ファイルは絶対に上書きされない(operator の登録行がここにあるため)。冪等なので毎回実行してよい
- 出力が
created:なら新規生成、ok:なら既存のまま - 出力が
header drift:の場合、テンプレートに増えた列が手元ファイルに無い。 警告であり org-start は止めない(exit 0)。表示された列名をユーザーに伝え、 手元のregistry/projects.mdの表ヘッダー行とセパレーター行へ追記するよう案内する (既存データ行は編集不要。空セルは従来動作を保つ) error:で exit 1 の場合はテンプレート自体が壊れている。ユーザーに報告して続行する (後続の Block B / D はレジストリを読まないので org-start 自体は継続できるが、 ワーカー派遣前に解消が必要)- 既存 checkout の移行手順は
docs/operations/registry-projects-migration.mdを参照
registry/org-config.mdのworkers_dirを読み、ワーカーディレクトリの存在を確認する。 存在するディレクトリがあれば一覧をユーザーに報告する(削除は絶対にしない)。 禁止事項: ワーカーディレクトリは過去の作業成果や再利用可能なプロジェクトを含むため、 org-start 時に削除してはならない。org-delegate のディレクトリ保持ポリシーに従うこと。
Step 0.5: herdr backend 互換性 preflight (broker+herdr のみ・spawn 前 fatal gate、Issue #748)
位置の不変条件 (Block C2 と混同しない): 本ゲートは Step 0 の 6 サブステップがすべて完了した後、かつ Block A の dispatcher
spawn_claude_paneを発火する前に、同期実行する fatal gate である。Block C2(runtime drift)は Block A spawn と並列に回る warning で「未確認でも org-start は継続」する責務だが、本ゲートは真逆で、exit 1/2 なら Block A の spawn を発火せずに停止する。目的は herdr の self-update による無言の窓外昇格(daemon が runtime の対応 protocol 窓を超えて昇格)を dispatcher spawn 前に fail loud で検出し、protocol 不一致によるagent.startの wedge(Issue #151 の主症状。原因が「daemon 停止」と誤診断される)を未然に止めることにある。C2 と並列 warning にすると事故を再導入するので、必ず spawn の前で同期ブロックする。
ORG_TRANSPORT の値だけでは発動判定にならない(renga/broker のフレーム差があり、broker でも backend は herdr とは限らない)。ゲートは $ORG_BROKER_STATE_DIR(未設定時 .state/broker)の daemon.json sidecar に runtime が書く解決済み backend を読み、backend == "herdr" のときだけ発動する。非 broker(sidecar 不在)や非 herdr backend(tmux / wezterm / null)は skip(exit 0)で即通過する。
ホスト実行必須(sandbox 内は誤判定):
herdr --version/ herdr socket ping / daemon sidecar はホスト実体(PATH / プロセス namespace / Unix domain socket)に依存する。Claude Code の Bash sandbox 内で実行すると PATH・namespace 差で false negative/positive が出る。Block C2 と同様、dangerouslyDisableSandbox: trueを付けてホスト実行する。
- 互換性 preflight を ホスト実行する(Claude Code の Bash tool では
dangerouslyDisableSandbox: trueを付ける):py -3 tools/check_herdr_compat.py # Windows python3 tools/check_herdr_compat.py # Mac/Linux - exit code で分岐する(C2 の drift warning とは exit 契約を混ぜない):
- exit 0(compatible または skip): 互換確認済み、または非 broker / 非 herdr backend で非該当。そのまま Step 1〜3 の並列起動へ進む。
- exit 1(incompatible fatal): 解決した herdr wire protocol が runtime の
SUPPORTED_PROTOCOLSの窓外。Block A の spawn を発火せず停止し、stdout の診断ブロック(installed herdr / derived protocol / daemon ping protocol / runtime supported / daemon backend / state_dir / remediation)をそのままユーザーに提示する。remediation は「runtime を上げる」か「herdr を下げる」の 2 択。ユーザーがどちらかで不一致を解消してから /org-start を再実行してもらう。 - exit 2(unverified fatal): backend は herdr だが protocol を確定できない(socket ping 不通 かつ
herdr --versionも取得不可 / runtime import 不能等)。未確認を warning 継続にすると事故を再導入するので、exit 1 と同様に spawn を発火せず停止し、診断(多くは sandbox 内実行が原因)をユーザーに提示する。dangerouslyDisableSandbox: trueでのホスト再実行を促す。
設計メモ:
- 判定レイヤ(権威度順): (1) socket ping(一次・権威的) — daemon の herdr socket へ
pingを 1 往復し、daemon が実際に話している protocol 番号を得る(runtime adapter も同じ ping で確定する)。(2)herdr --version+ ローカル写像(fallback) — socket 不通時の代替。PATH binary の version から protocol をローカル写像(0.7.0-0.7.1→14 / 0.7.2-0.7.4→16 / 0.7.5〜→17)で導く。ただし PATH 上のherdrbinary が daemon の接続先と同一とは限らず、version 系列内の protocol bump も検出できない限界があるため、fallback を使った旨は stderr に明示される。- 対応窓の SoT は runtime: 対応 protocol の窓は ja 側に hard-code せず、
claude_org_runtime.terminal.herdr.SUPPORTED_PROTOCOLSを import して突き合わせる(compat API が将来 runtime に追加されればそれを優先、無ければ本 fallback)。runtime のリリースごとに窓が広がっても ja 側の記述は陳腐化しない。- stdout は診断ブロック / JSON 専用、限界注記・診断は stderr(
--jsonで機械可読出力も出せる)。exit code が唯一の分岐キー。- スクリプト本体:
tools/check_herdr_compat.py
Step 1〜3: 並列起動フェーズ
Issue #410 / Stage B: Step 0 (transport 判定 / set_summary / MCP 疎通 / identity 検証 / projects レジストリ配置 / workers_dir 確認の 6 サブステップすべて) が完了し、Step 0.5 の herdr 互換性 preflight が exit 0(compatible / skip)で通過した後に dispatcher の
spawn_claude_paneを発火し、Claude 起動待ち (〜30〜60s) と並列に Block B (前回状態の DB 読み込み) / Block C (ダッシュボード server 起動) を進める。serial 実行時の wall-clock 〜3 分を ~35s まで短縮する目的。Step 0.5 が exit 1/2 の場合は Block A を発火しない(spawn wedge 防止のための同期 fatal gate)。実行モデル: Secretary が以下の 3 ブロック (A/B/C) を発火し、最後に block D で合流する。block A は I/O bound(renga MCP の応答は数百 ms、その後は別プロセスである Claude の boot を待つだけ)なので B/C と wall-clock を完全に overlap できる。
Block A: ディスパッチャーペイン spawn (発火のみ、boot 完了は待たない)
ペイン配置は org-delegate/references/pane-layout.md に従う (renga 版)。curator はここでは spawn しない(オンデマンド化、本ファイル冒頭注記)。
- dispatcher 用
spawn_claude_pane:
返り値のmcp__renga-peers__spawn_claude_pane( target="focused", direction="horizontal", role="dispatcher", name="dispatcher", cwd=".dispatcher", permission_mode="bypassPermissions", model="sonnet" )"Spawned pane id=N."から dispatcher のpane_idを控える。引数の意味と落とし穴は本ファイル末尾「### 補足: spawn_claude_pane 引数の詳細」を参照。 - ここで spawn 結果のみブロックする(Claude の boot 完了は待たない)。spawn が
[<code>] <msg>で失敗していた場合は本ファイル末尾「### 失敗モード」へ。spawn が成功(pane_id 取得)なら、Block B / C と並列に進む。
Block B: 前回の状態確認
read 経路は DB only(Issue #267 / M4)。Block A の spawn 発火と並列で実行する(Block A は MCP 経由でペイン作成を依頼するだけで Claude の boot は別プロセスのため、ここで CPU / I/O を競合しない)。
.state/state.dbが存在するか確認する- 存在 → DB をクエリ:
python -c "from tools.state_db import connect; from tools.state_db.queries import get_org_state_summary; import json; \ conn = connect('.state/state.db'); \ print(json.dumps(get_org_state_summary(conn), ensure_ascii=False, indent=2, default=str))"active_runs/recent_events/run_status_counts/session.status/session.objectiveで前回状態を把握する - 存在しない → 初回起動と判断する。Secretary に importer 実行を促す:
python -m tools.state_db.importer --db .state/state.db --root . --rebuild --no-strict
- 存在 → DB をクエリ:
- session.status を確認する:
SUSPENDEDなら /org-resume の Phase 1〜3(ブリーフィング・照合・再開計画)を実行する。 Block A の spawn は既に発火済みなのでブリーフィング中に裏で Claude が boot している。 ブリーフィング完了後、Block D の合流で dispatcher が ready になるのを待ち、org-resume の Phase 4(ワーカー再派遣)を人間の承認に基づいて実行するACTIVEなら、前回のセッションが突然終了した可能性がある。 各ワーカーディレクトリの git 状態を確認し、現状を報告する
Block C: ダッシュボードサーバー起動
Block A の spawn 発火と並列。ダッシュボード server は別プロセス(Python HTTP server)で Claude pane と独立。
- ダッシュボードサーバーが起動しているか確認する:
cat .state/dashboard.pid 2>/dev/null && kill -0 $(cat .state/dashboard.pid) 2>/dev/null && echo "running" || echo "stopped" - 停止中であれば起動する:
python3 dashboard/server.py & # Mac/Linux py -3 dashboard/server.py & # Windows - ユーザーに案内する: 「ダッシュボードを起動しました → http://localhost:8099」
sandbox 注記(false negative 回避): Claude Code の Bash sandbox はネットワーク / プロセス namespace が隔離されているため、本 Block の常駐起動・疎通確認を sandbox 内で行うと誤判定する。ホスト側で server が port 8099 を正常に listen していても、sandbox 内では
curlが000を返し、ssに port が見えず、pgrep/kill -0はホストのプロセスを観測できない(「起動失敗」と誤判定して再起動ループに陥る)。また sandbox 内のnohup ... &で起動したプロセスはコマンド終了時に残らず常駐しない。server の起動(手順 2)と疎通確認(手順 1 やcurl/ss/pgrep/kill -0等)は sandbox 外で実行する(Claude Code の Bash tool ならdangerouslyDisableSandbox: trueを付ける、またはrun_in_backgroundによるホスト実行を使う)。
Block C2: claude-org-runtime バージョン drift 検出 (Issue #472)
Block A の spawn 発火と並列。claude-org-runtime の installed バージョンと PyPI の latest を比較し、drift があれば Step 4 の起動完了報告に 1 行 warning を添える。auto-upgrade は行わず通知のみ。バージョン番号は本ファイルにも script にも hard-code せず、すべて importlib.metadata と PyPI JSON API の動的取得値のみを使う(runtime のリリースごとに記述が陳腐化することを避けるため)。
sandbox 内では PyPI に届かない → ホスト実行する(#119 の真因): このチェックを Claude Code の Bash sandbox 内で実行するとネットワーク隔離で PyPI に到達できない。旧実装はこの到達不能を silent skip(何も出さず exit 0) していたため、pin ラグ(venv が古いまま)を「最新」と誤読し、既修正バグを worker に委譲する phantom dispatch を招いた(2026-07-08 #119: runtime 0.1.36 で既修正のバグを 0.1.34 venv で踏んだ)。根治として (1) スクリプトは到達不能を exit 2 + stderr 診断で顕在化し、(2) 本 Block は
dangerouslyDisableSandbox: trueを付けてホスト実行する(sandbox 内でしか回せない場合は exit 2 =「PyPI 未確認」となり、ネットワーク到達可能なホストで再実行するまで drift 未検証と扱う)。
- drift チェックを ホスト実行する(Claude Code の Bash tool では
dangerouslyDisableSandbox: trueを付ける):py -3 tools/check_runtime_version.py # Windows python3 tools/check_runtime_version.py # Mac/Linux - exit code で分岐する(stdout は drift 行専用、診断は stderr に出る):
- exit 0(up to date): installed == pin 窓内 latest。PyPI 到達確認済・drift 無し。warning 行なし。
- exit 1(drift): stdout の
[runtime drift] ...1 行を そのまま Step 4 起動完了報告の末尾に warning として転記する。あわせて stderr の測定 interpreter:行が示す Python パスを転記行に添える(下記「interpreter の併記」を参照)。 - exit 2(未確認): PyPI 到達不能 / 応答異常 / pin 窓外 /
packaging欠如、または installed が local install(file://パス / VCS / editable。PEP 610direct_url.jsonで判定)で PyPI 照合不能 の場合。stderr に理由診断が出る。drift は未検証なので Step 4 報告に「runtime drift: PyPI 未確認(要ホスト再実行)」を明示する(silent にしない)。local install 起因の場合はホスト再実行では解消しない(installed の実体がリリース版と限らないため)ので、stderr の診断がlocal install -- PyPI 照合不能を示すときは「runtime: local install のため PyPI 照合不能(要 PyPI 由来インストールで再確認)」と読み替えて報告する。それ以外(オフライン / sandbox 内)はdangerouslyDisableSandbox: trueで再実行する。 - exit 3(未インストール): この Python から runtime を import できない(別 venv / 未インストール)。stderr に note。Step 4 報告に「runtime 未インストール(要確認)」を出す。
interpreter の併記(バージョン報告は測った Python に相対): 本 Block が報告する installed は「このコマンドを実行した Python が解決したバージョン」であって、ホストの状態ではない。ホストには runtime が複数入りうる(プロジェクトの
.venvとシステムpython3が別バージョンで同居し、CLI shimclaude-org-runtimeの shebang はそのどちらか片方に束ねられる)。そのため drift 行を interpreter 抜きで転記すると「更新が遅れている」と読めてしまうが、実際には「もう片方を測った」だけということが起こる。script は 毎回・全 outcome で stderr の 1 行目に[runtime drift-check] 測定 interpreter: <sys.executable> (installed=<version>)を出すので、Step 4 報告で installed / 未インストールに言及するときは必ずこの Python パスを併記する(例:「system python3 で測って installed=X」)。上の実行例がpython3/py -3である以上、測っているのは既定でシステム側であって ja の.venvではない点に注意する。
設計メモ:
- latest 取得は PyPI JSON API (
https://pypi.org/pypi/claude-org-runtime/json) を urllib.request で叩く (timeout 8s)。pip index versionsは experimental で stderr に warning を吐くため採用しない- pin window: ja の
pyproject.tomlのclaude-org-runtime依存制約を regex で動的に読み取り、PyPI releases からその制約を満たす最新のみを latest として比較する(packaging.SpecifierSet利用)。上位 major / minor が出ても窓外 upgrade を促さない。packaging未インストール環境は exit 2(未確認・stderr 診断) に落ちる — 旧実装の silent skip は廃止した- local install 検出 (Issue #747): installed が
file://パス / VCS チェックアウト / editable から入っている場合、その version はリリース版と対応する保証がない(ローカルビルドは任意の version を名乗れる)。PEP 610direct_url.json(importlib.metadata.Distribution.read_textで読む。dist-info の直接 glob ではない)で判定し、該当時は PyPI 到達前に exit 2 + stderr 診断(local install -- PyPI 照合不能) に落とす。file://install を「最新・drift なし」と誤報告した事故の根治。https アーカイブ等の非ローカル direct URL は通常の PyPI 照合に委ねる- yanked release / prerelease は候補から除外する(pip が通常選ばないバージョンを latest にしないため)
- 「drift = 古い」も「drift = preview 入り (installed > latest の release channel ずれ)」も同じく exit 1 の 1 行で通知する。auto-upgrade はせず、対応はユーザー判断に委ねる
- stdout は drift 行のみ(Step 4 へ verbatim 転記できる)。offline / 未確認 / 未インストールの診断、および
測定 interpreterの provenance 行はすべて stderr に出す(spliceable な stdout を汚さない)- interpreter provenance:
測定 interpreter行は outcome に依らず毎回 stderr の先頭に出る(exit 0 も含む)。up to date も drift と同じく「測った Python に相対な判定」なので、片方だけ silent にすると同じ取り違えが exit 0 側で起きるため。判定そのものには影響しない開示専用の 1 行で、exit code 契約は不変- 警告コマンドには
pyproject.tomlから読み取った pin 制約をそのまま埋め込むので、ユーザーが貼り付けても窓外への upgrade にはならない- スクリプト本体:
tools/check_runtime_version.py
Block C3: secretary 滞留 watcher の常駐(broker のみ)
ORG_TRANSPORT=broker のときのみ実行する。renga では起動しない(broker の queue.jsonl が存在しないため。Step 0 の transport 判定が renga なら本 Block はまるごと skip する)。
broker transport では過去に「secretary 宛メッセージが claimed/delivered 記録付きで silent 消失する」障害があった(channel sidecar の二重走行レースが根因。runtime 側の observer lease で修正済み)。その再発・類似滞留に対する運用ガードとして、broker セッションでは org-start 直後に queue.jsonl の滞留 watcher を常駐させる。
- Claude Code の Bash tool で
run_in_background=trueを付けて 1 回呼ぶだけ:python3 tools/secretary_queue_watcher.py # Mac/Linux py -3 tools/secretary_queue_watcher.py # Windows - 動作: watcher は起動時点の
$ORG_BROKER_STATE_DIR/queue.jsonl末尾を起点に live-tail し、それ以降の secretary 宛message_enqueuedがdeliveredされないまま閾値(default 120 秒)を超えたら、滞留件数と経過秒を 1 行 print して exit する。background Bash の終了イベントで窓口が再起床するので、その出力を見たら使用中 transport のcheck_messages(broker ではmcp__org-broker__check_messages)で drain する。過去ログの通算 gap は数えない(既知の過去消失分が混入して誤検知になるため、本セッション中の新規レコードのみを対象にする)。 - 停止経路: watcher は起動時に
.state/secretary_queue_watcher.json(pid / cwd / cmdline / broker_state_dir を記録した sidecar)を書き、/org-suspend(Phase 3.6)//org-downがこれを identity 照合して停止する(pid recycle / 別 broker 誤停止を避けるため pid 単独では kill しない)。滞留検知で自 exit した場合や SIGTERM で停止した場合は watcher 自身が sidecar を掃除する。
sandbox 注記: 常駐プロセスなので Block C と同様 sandbox 内起動は不可、
run_in_backgroundによるホスト実行を使う。
スクリプト本体:
tools/secretary_queue_watcher.py(--owner/--stale-sec/--poll-secで調整可。ORG_BROKER_STATE_DIR未設定なら exit 1 で即終了する broker 専用ツール)
Sidebar: attention watcher の起動案内(optional, 明示起動推奨)
承認待ち / 判断待ち / CI 失敗 / silent stop / PR merged 等を OS notification + 音 + terminal bell で能動的に通知する watcher を別途常駐させられる。
/org-startからの自動起動はしない(OS 通知 backend は環境依存が強く、勝手に音が鳴ると不快になりやすいため。設計docs/design/attention-notification.md§11 Q1)。有効化したいユーザーには Step 4 の起動完了報告と合わせて
/org-attention-startの実行を案内する。skill が以下を一括で行う:
.state/attention.json未配置時はtools/templates/attention.example.jsonから自動コピー- dispatcher ペインの右側を vertical split し
claude-org-runtime attention watch ...を常駐起動- pane_id を
.state/attention_pane.jsonsidecar に記録(停止は/org-attention-stopで参照)1 回限りの動作確認は
claude-org-runtime attention scan --state-dir .state --config .state/attention.json --dry-run --json(--configを外すと runtime 中立の英語 default が出るので、ja テンプレートの導通確認には必ず付ける)。OS 別 backend 挙動・トラブルシューティング・別ターミナルからの素 CLI 起動手順はdocs/operations/attention-watch.mdを参照。
Block C4: role config drift 検出 (Issue #818)
Block A の spawn 発火と並列。各ロールの settings.local.json を schema に対して検証し、drift があれば Step 4 の起動完了報告に 1 行 warning を添える。自動修復は行わず通知のみ(修復は /org-setup)。Block C2 と同列の 非 fatal な warning ブロックであり、drift を検出しても org-start は止めない(Step 0.5 の fatal gate とは責務が違う)。
なぜ起動時に見る必要があるのか(guard の無言死):
settings.local.jsonは gitignored なので、CI の.github/workflows/tests.ymlが回すpython tools/check_role_configs.py --include-worker-settings .は--include-localを付けておらず、tracked ファイルしか検証しない。したがって hook パスのドリフト(.hooks/*.shの rename / 移動で hook command が実在しないパスを指し、guard が発火しないまま無言で無効化される)や必須 allow / deny の欠落、さらには settings ファイルそのものの不在(そのロールが hook 層ゼロで動く。bypassPermissionsのディスパッチャーでは hook が唯一の強制層)は CI では一切検出されない。この盲点を塞ぐ唯一の定期的な契機が org-start なので、起動のたびに--include-localで 1 回検証する。
検出される drift の種類: (a) schema 宣言済みの settings ファイルが存在しない(
settings file not found—/org-setup未実行 / 配布漏れ)、(b) 必須 allow / deny の欠落、(c) schema 未登録の allow エントリ混入、(d) 必須 hook の欠落、(e) hook command が実在しないパスを指す。個別 drift の対処フローはdocs/getting-started.mdの該当節にある。
- drift チェックを実行する:
py -3 tools/check_role_configs.py --include-local # Windows python3 tools/check_role_configs.py --include-local # Mac/Linux - exit code で分岐する(stdout 最終行が転記用の 1 行、診断は stderr):
- exit 0(clean): schema と一致。warning 行なし。
- exit 1(drift): stdout 最終行の
[role config drift] ...1 行を そのまま Step 4 起動完了報告の末尾に warning として転記する。個別の[ERROR] <path> :: <role> :: <理由>行は同じ stdout の上部に出ているので、ユーザーが詳細を求めたらそこから提示する(起動報告に全件は貼らない。実測で数十行になりうる)。 - exit 2(検証不能): checker 自体が完走できなかった(schema 不在 / JSON 構文エラー /
core_harness解決失敗等)。stderr に traceback とrole_configs: 検証不能 -- ...診断が出る。silent にせず「role config: 検証不能(guard 未検証・要確認)」の 1 行を報告に添える。drift の有無は 未測定であって clean ではない、という区別が本 exit code の存在意義である。
必ず窓口の cwd(ja live repo root)で実行する:
--rootの既定はスクリプト位置から導出される repo root なので、worktree 内から叩くとその worktree の.claude/settings.local.json(= ワーカー用の設定)を secretary の schema で検証してしまい、数十件の false positive になる。ロール別に個別検証したい場合は当該 worktree で--role workerを付ける(--roleは--include-localの意味を含む)。
設計メモ:
- stdout は転記用の 1 行と
[ERROR]明細のみ(spliceable に保つ)。件数サマリrole_configs: N error(s)と exit 2 の traceback / 診断はすべて stderr に出す。stdout への出力は cp932 /PYTHONIOENCODING=ascii等の非 UTF-8 コンソールでも落ちないよう ASCII 置換フォールバックを通す(転記用の 1 行がUnicodeEncodeErrorで消えると drift を検出したのに warning が付かない、という最悪形になるため)- exit 1 と exit 2 を分けている理由: 分けないと「checker が壊れて動かなかった」が「drift を検出した」と同じ signal になり、逆に traceback を素通りさせると壊れた guard が clean と読まれる。#818 が塞ぎたい失敗モードそのものなので、契約として
tests/test_check_role_configs.pyのExitCodeContractTestsで lock している- ネットワークに触れないので Block C2 と違い sandbox 内実行でも判定自体は正しい。ただし読み取りが sandbox の deny に掛かる環境では exit 2 に落ちうるので、その場合は
dangerouslyDisableSandbox: trueでホスト再実行する- スクリプト本体:
tools/check_role_configs.py。drift の修復は/org-setup(additive-only なので既存設定を壊さない)、個別の drift 種別ごとの対処はdocs/getting-started.mdの該当節を参照
Block D: dispatcher の合流 (起動プロンプト承認 / list_peers poll / 挨拶 / DB write / snapshot)
Block A の spawn 成功後、dispatcher ペインで Claude が boot している。
- 起動プロンプトを画面判定で承認する(盲目 Enter 禁止) — Claude Code の起動時プロンプト(folder-trust「Do you trust the files in this folder?」相当 / 「Load development channel?」)を、画面を見て 1 手ずつ承認する。folder-trust の初期カーソルは「No, exit」なので、表示を確かめずに送った Enter はそのまま dispatcher を終了させる(2026-09-25 にワーカーで実際に起きた。runtime 0.1.43 / suisya-systems/claude-org-runtime#185)。判定は runtime の
spawn-prompt-stepに任せ、窓口は返された手だけを打つ:- 開始時刻を ms で控える(
date +%s%3N)。previous= null mcp__renga-peers__inspect_pane(target="dispatcher", format="grid")を呼ぶ(linesは付けない。背の高いペインでは下端 N 行の外にダイアログが残る)- 判定を 1 回呼ぶ。stdin には JSON オブジェクト
{"screen": <2 の結果>, "previous_screen": <前回の inspect 結果か null>, "elapsed_ms": <開始からの経過 ms の整数>}を 1 つ渡す(画面テキストはシェルの引用へ直に埋め込まず、JSON 文字列としてエンコードしてファイル経由で渡す):claude-org-runtime dispatcher spawn-prompt-step --deadline-ms 120000 < "$TMPDIR/spawn-prompt-step.json" # PATH に無ければ: python3 -m claude_org_runtime.dispatcher.runner spawn-prompt-step --deadline-ms 120000 - 結果で分岐する:
- exit 0 /
action: "send_keys": 返されたsend_keysオブジェクトをそのままmcp__renga-peers__send_keys(target="dispatcher", ...)に渡す(enterを自分で足さない)。previous= null にして 1 秒待ち 2 へ - exit 0 /
action: "wait":previous= 今回の inspect 結果にして 1 秒待ち 2 へ - exit 0 /
action: "done": 承認完了。手順 2(list_peers poll)へ - exit 10(escalate)/ exit 2(入力不正)/ その他の非 0 / inspect・send_keys のエラー(
[key_unsupported]等): Enter を送らずに中止し、ペインの状態をユーザーに報告して判断を仰ぐ(下記「失敗モード」の起動失敗として扱う)
- exit 0 /
- 承認しないと
server:renga-peersチャネルが有効化されず、send_messageの channel push が届かない。未表示段階では判定がwaitを返すので、doneまで反復すればよい(Enter を「念のため再送」しない) - broker(
ORG_TRANSPORT=broker)の場合 — 承認は 2 段(folder-trust + dev-channel sidecar の再導入):spawn_claude_paneは--mcp-config <broker>(daemon)を注入し、初回に Claude Code の folder-trust プロンプトが出る。加えて push 一次のため channel sidecar を--dangerously-load-development-channels server:org-broker-channelで load するため「Load development channel?」プロンプトが再出現する(--mcp-config-only 設計で一旦消えた 3-3b 承認の再導入)。手順は上と同じループをmcp__org-broker__inspect_pane/mcp__org-broker__send_keysで回す(判定が 2 つのダイアログを順に扱う。順序は boot タイミング依存)。WezTerm adapter は Up / Down を送れず[key_unsupported]になるので、そのときも Enter に倒さず中止して報告する。承認しないと broker token のバインド/channel sidecar の登録が完了せず、次のlist_peers待ちが同様にタイムアウトする(ratified §5/§8.5 の folder-trust フローへの加算であり置換ではない。設計 broker-native-roles.md §9.5)
- 開始時刻を ms で控える(
- list_peers を poll し dispatcher の peer 登録を確認:
list_peersの直前に.claude/skills/org-delegate/references/capability-first-drive-operational-gate.mdを Read し、monitoring-read-onlyの分岐を適用する(同 reference §6 の表 #2)。mcp__renga-peers__list_peers # 結果に name="dispatcher" が現れるまで poll- 現れない場合、(a) Block D-1 の画面判定ループを 1 回回し直す(Enter を盲目で再送しない。判定が
doneなら承認は済んでいるので poll を続ける)、(b)[pane_not_found]等 fatal なら「失敗モード」セクションへ - capability 形かつ未承認のときの縮退(停止しない・待ち時間 0 分): 列挙を登録確認に使わず破棄する。
name="dispatcher"の一致だけで登録ゲートを開けない — 予約名は別 org の並走タブに同名で実在しうるので、name 一致は「まだ登録していない自分の子」のゲートを他 org のピアで開けてしまう(契約 T-§2.2「MUST NOT key a lookup … onnamealone」/共有 reference §3-B-1)- 代替の readiness 判定は「次の手順 3 の挨拶送信そのもの」で行う。
list_panesの生存とinspect_paneのプロンプト表示は Claude が起動したことしか示さず、MCP の peer 登録が済んだことを示さない。プロンプトが見えた時点で boot 完了とみなすと、手順 3 の一度きりの挨拶が[pane_not_found]で消える。したがって: (a)list_panesでペイン生存だけ確認し、(b) 手順 3 のsend_messageを送り、(c)[pane_not_found](broker では[peer_not_found])で失敗したら 2 秒間隔・最大 30 秒で再送する(その間も Enter は再送せず、承認が未完了なら Block D-1 の画面判定ループをdoneまで回す)、(d) 送達成功をもって「起動・登録・挨拶送信」が同時に確定する - この縮退中は「peer 未登録」を根拠に失敗モードの fatal 分岐へ進んではならない(後述の「spawn 成功・boot 中に peer 登録されない」参照)。30 秒の再送予算を使い切っても送達できないときだけ、従来のタイムアウト処理に落とす
- 報告は共有 reference §3-B の手順で 1 度だけ人間に上げる
- 代替の readiness 判定は「次の手順 3 の挨拶送信そのもの」で行う。
- capability 形かつ承認済み(共有 reference §2 の
first_driveがrecorded)のとき: 登録受理の判定を同 reference §1-2 の三値判定に委ね、name="dispatcher"が**「在」(同タブ・一意)のときだけ受理する。poll の継続・再送予算・受理後の挨拶送信・予算切れの失敗処理は §1-2-c のとおりこの経路の従来手順のまま**で、ここに重ねて書かない- この経路固有の適用結果は 1 つだけ: 予算切れ時に
unknownが残っていた場合は fatal に落とさない(close_pane+StateWriter.CLEARへ進まない)。タブ数 / ピア総数 / 他タブ判定数 / unknown 件数を添えて人間に報告し判断を仰ぐ(共有 reference §1-2-b: unknown を「peer 未登録」に潰さない)。「不在」「他タブのみ」で予算切れになったときは従来どおり「失敗モード」の分岐に落とす
- この経路固有の適用結果は 1 つだけ: 予算切れ時に
- 現れない場合、(a) Block D-1 の画面判定ループを 1 回回し直す(Enter を盲目で再送しない。判定が
- 挨拶メッセージを送信:
- dispatcher: 「あなたはディスパッチャーです。窓口からの DELEGATE メッセージを受け取り、ワーカーのペイン起動・指示送信・状態記録を代行してください。CLOSE_PANE メッセージを受けたらペインを閉じてください。」
- curator への挨拶は無い(常駐しない。オンデマンド起動時の指示送信は dispatcher が行う)
- Block B の DB 初期化完了を待つ — 並列実行の合流点。Block B-1 で
.state/state.db不在 →importer --rebuildが走った場合、schema 構築完了まではStateWriter.update_session()が失敗するので、Block D-5 の DB write 前に Block B が完了している必要がある。Block B が SUSPENDED ブリーフィング待ちで未完了なら、ブリーフィング完了 → DB schema 健全性確認 → ここに進む - DB transaction を 1 回にまとめて identity を記録する (
StateWriter.transaction()経由、markdown 直接編集禁止。post-commit hook が.state/org-state.mdを再生成)。dispatcher の identity を write し、curator の identity は常にStateWriter.CLEARで明示クリアする(オンデマンド化により常駐 curator は存在しない。旧仕様の SUSPENDED 状態から繰り越した stale なcurator_pane_id/curator_peer_idが残ると、ダッシュボードと balanced split target 選出が生きた curator がいる前提で誤判断する。StateWriter.update_session()は None 渡しを「未指定 = 保持」と解釈する契約なので、明示クリアが必須):
curator フィールドが null であることは正常系であり、suspend / handover / resume / dashboard はこれを前提に動く。python -c " from pathlib import Path from tools.state_db import connect from tools.state_db.writer import StateWriter conn = connect('.state/state.db') with StateWriter(conn, claude_org_root=Path('.')).transaction() as w: w.update_session( dispatcher_pane_id='<d_pane>', dispatcher_peer_id='<d_peer>', curator_pane_id=StateWriter.CLEAR, curator_peer_id=StateWriter.CLEAR, ) "- capability gate の縮退中(Block D-2 の列挙を破棄した場合)は
dispatcher_peer_idにStateWriter.CLEARを渡す。縮退中は<d_peer>を観測できていないので、推測値や他タブ由来の id を書くと以後の routing が壊れた宛先を掴む。ここで「未指定 = 保持」に倒してはならない:/org-startは dispatcher ペインを新規 spawn するので、DB に残っている値は前セッションの死んだペインのものであり、保持は stale の温存になる(生きたペインを引き継ぐ.claude/skills/dispatcher-resume/SKILL.mdStep 0 とはここが逆になる。あちらは同じペインが生きているので保持が正しい)。dispatcher_pane_idはlist_panesから確定できるので通常どおり書く。peer_id が null の間、dispatcher への送信は安定名to_id="dispatcher"で行う(rootCLAUDE.mdの既定の宛先指定と同じ)。この状態はユーザー報告に含める。
- capability gate の縮退中(Block D-2 の列挙を破棄した場合)は
- JSON スナップショットを 1 回だけ再生成する(dashboard 用、state-db cutover とは別経路):
py -3 dashboard/org_state_converter.py
補足: spawn_claude_pane 引数の詳細
spawn の引数の意味と落とし穴:
target: 分割対象のペイン。dispatcher はtarget="focused"(窓口ペインを分割)direction:"horizontal"= 上下分割(既存ペイン=上 / 新ペイン=下)、"vertical"= 左右分割(既存ペイン=左 / 新ペイン=右)role:mcp__renga-peers__list_panesで役割識別できるラベルname: 後続のsend_message(to_id="dispatcher", ...)等で参照する安定名。renga-peers は全桁数字の name を id として解釈するので、英字を含む名前を必ず付けるcwd: caller ペイン(= 窓口)の cwd を基点に相対解決される。cd X && claude ...をcommandに埋める旧方式は禁止(auto-upgrade が発動せず channel push が失われる落とし穴)permission_mode/model: renga がclaude --permission-mode {mode} --model {model} --dangerously-load-development-channels server:renga-peersを合成して実行する- 戻り値:
"Spawned pane id=N."テキスト。エラーは[<code>] <msg>形式(例:[split_refused]/[pane_not_found]/[cwd_invalid])。code 一覧と分岐は.claude/skills/org-delegate/references/renga-error-codes.mdを参照 .dispatcher/CLAUDE.md/.curator/CLAUDE.mdにそれぞれのロール用の指示が入っている(Secretary の CLAUDE.md とは別)
失敗モード
Block A の spawn 段階で分類する:
- dispatcher spawn 失敗 (
[split_refused]/[cwd_invalid]/ その他[<code>]) — 失敗をユーザーに報告し、原因解消後 /org-start を再実行する - spawn 成功・boot 中に peer 登録されない — Block D-2 の poll が timeout する。Block D-1 の画面判定ループを回し直す → 再 poll。3 回 retry してダメなら fatal: dispatcher 無しでは org-delegate / SECRETARY_RELAY が機能しないため、ペインを破棄し、dispatcher / curator 両 identity を
StateWriter.CLEARで消した上でユーザー報告し /org-start 再実行を促す- この破棄も Group B なので、下記 3 段を踏んでから撃つ(契約
docs/contracts/backend-interface-contract.mdT-§4.2「Fail-safe consequence for Group B」)。spawn 戻り値で pane_id が手元にあることは免除にならない — 未確立の列挙では、下の identity 照合の結果そのものを信用できないため:- 自タブ確立: (i) backend が Group B を自身の単一タブモデル内で解決する(
org-broker。同契約 §8.1 / §8.10)/ (ii)caller_scopeを確立できている(同 T-§cap)のいずれか 1 つが成立すること。どちらも不成立ならclose_paneを撃たない — その場合はペインを残したままStateWriter.CLEARとユーザー報告だけを行い、「dispatcher ペイン (id={N}) が残っているので手動で閉じてほしい」を報告に含める(相対セレクタへはフォールバックしない) - identity 照合:
mcp__renga-peers__list_panesで spawn 戻り値の pane_id がname == "dispatcher"かつrole == "dispatcher"をなお指しているか確認する。別ペインへ再割当て済み / 列挙に現れない場合は close せず、同じくStateWriter.CLEARと報告に倒す(pane_id recycle 対策) - 1 と 2 の両方を通ったときだけ
mcp__renga-peers__close_pane(target=<照合済みの数値 pane_id>)で破棄する
- 自タブ確立: (i) backend が Group B を自身の単一タブモデル内で解決する(
- capability 形かつ未承認の縮退中は、fatal 分岐の判定材料を差し替える(分岐を無効化するのではない): 縮退中は Block D-2 の列挙を破棄しているので「
list_peersに居ない = peer 未登録」という判定が成立せず(共有 reference §1-1 / §3-B-1)、それを根拠にclose_pane+StateWriter.CLEARを撃つと健全な dispatcher ペインと 2 つの identity を破棄する。代わりに Block D-2 の縮退手順で使う「挨拶send_messageの再送」が 30 秒の予算を使い切って一度も送達しなかったことを fatal の根拠にする(プロンプトが見えていても、送達できない dispatcher は org-delegate / SECRETARY_RELAY を担えないので起動失敗である)。その場合は従来どおり両 identity のStateWriter.CLEAR+ ユーザー報告を行い、ペインの破棄は上の 3 段(自タブ確立 → identity 照合 → 数値 close)を踏んでから撃つ(踏めなければ close せず、ペインが残る旨を報告に含める)。「ペインが boot していれば fatal にしない」ではない — 到達不能な dispatcher を抱えたまま/org-startを成功として報告してはならない
- この破棄も Group B なので、下記 3 段を踏んでから撃つ(契約
- 起動プロンプトの承認が終わらない — Block D-1 の判定が
doneを返す前に先へ進まない(プロンプト未表示の間はwaitが返る)。判定が escalate(exit 10)/ 入力不正(exit 2)/ ツールエラーで止まったら Enter に倒さず、ペインの状態を添えてユーザーに報告する(folder-trust で Enter を打つと「No, exit」が選ばれて dispatcher が終了する)。Block D-2 の peer 登録 poll が登録の ground truth
curator の spawn / boot 失敗モードは org-start には存在しない(spawn しないため)。オンデマンド起動時の失敗ハンドリングは .dispatcher/references/pane-close.md Step 5-3 / 5-4 を参照。
Stage A / Stage B の wall-clock 効果
| stage | 変更内容 | wall-clock |
|---|---|---|
| 改修前 | 状態復元 → ダッシュボード起動 → dispatcher 起動 (spawn+Enter+poll+greet+DB+snapshot) → curator 起動 (同様) を serial | 〜180s |
| Stage A 後 | dispatcher / curator 起動を 1 つの並列 block にまとめ、両 spawn / Enter / poll / greet / DB write / snapshot を一括化 | 〜90s |
| Stage A+B 後 | 上に加え、Step 0 完了直後に Block A の spawn を発火、Block B (state 復元) / Block C (dashboard 起動) と並列に Claude の boot wait を overlap | 〜35s |
| curator オンデマンド化後 | 起動対象は dispatcher のみ(curator の spawn / Enter / poll / greet が消滅) | さらに短縮 |
起動後の裏取り: dispatcher の /loop 監視宣言は実 invoke を打鍵で確認する
dispatcher は初回 DELEGATE 完了報告で「/loop 3m で監視します」と宣言したまま実 invoke せず idle に落ちることがある(宣言はターン終了と同時に失効し、次の契機がなければ実行されない)。窓口は初回 DELEGATE 完了報告や監視宣言を信用せず、使用中 transport の inspect_pane(target="dispatcher") で /loop の実行痕跡(loop 予約 / 監視サイクル出力)を確認し、無ければ send_keys で /loop 3m <監視ディレクティブ> を user turn として打鍵し再点火する。打鍵は text と Enter を別呼び出しに分ける 2 段(send_keys(target="dispatcher", text="/loop 3m ...") → inspect_pane で入力欄に乗ったことを確認 → send_keys(target="dispatcher", enter=true)。text+enter 同時送は draft 残りで未武装が再発しやすい。.dispatcher/references/spawn-flow.md 3-5a と同じ手順)で行う(いずれも Step 0 で判定した transport の完全修飾名で呼ぶ。監視ディレクティブは placeholder のまま打鍵せず、.claude/skills/dispatcher-resume/SKILL.md Step 5 のコードブロック正文をそのまま使う。手順詳細は .dispatcher/references/worker-monitoring.md)。send_message / check_messages 経由の指示では武装しない — 打鍵による user turn 注入が確実な武装手段である(ultracode 武装と同型の構造)。
Step 4: 準備完了の報告
人間に簡潔に報告する。起動するのはディスパッチャーのみ(キュレーターはオンデマンド)。
Block C2 の runtime drift 出力の扱い: Block C2 は exit code で結果を返す(stdout は drift 行専用、診断は stderr)。exit 1(drift)なら stdout の [runtime drift] ... 1 行を、下記いずれのテンプレートでも 末尾に空行を 1 つ挟んだ上でそのまま転記する。exit 0(PyPI 到達確認済・drift 無し)は warning 行を付けない。exit 2(PyPI 未確認 = オフライン / sandbox 内 / 応答異常 / pin 窓外 / packaging 欠如、または installed が local install(file:// / VCS / editable)で PyPI 照合不能)は silent にせず 「runtime drift: PyPI 未確認(要ホスト再実行)」の 1 行を報告に添える(#119 の silent 誤読を防ぐため)。ただし stderr 診断が local install -- PyPI 照合不能 を示す場合はホスト再実行では解消しないので「runtime: local install のため PyPI 照合不能(要 PyPI 由来インストールで再確認)」と読み替えて添える(#747: file:// install が「最新・drift なし」と誤報告された事故の根治)。exit 3(runtime 未インストール)は「runtime 未インストール(要確認)」を添える。sandbox 内実行だと exit 2 になりやすいので Block C2 は dangerouslyDisableSandbox: true でホスト実行すること。
Block C4 の role config drift 出力の扱い: Block C4 も exit code で結果を返す(stdout 最終行が転記用、診断は stderr)。exit 1(drift)なら stdout 最終行の [role config drift] ... 1 行を、下記いずれのテンプレートでも 末尾に空行を 1 つ挟んだ上でそのまま転記する(Block C2 の warning 行と両方出る場合は 1 行ずつ並べる)。exit 0(clean)は warning 行を付けない。exit 2(検証不能 = checker 自体が完走できなかった)は silent にせず「role config: 検証不能(guard 未検証・要確認)」の 1 行を添える — drift 無しではなく 未測定なので、clean と読ませてはならない。個別の [ERROR] 明細は起動報告には貼らず、ユーザーが詳細を求めたときに stdout 上部から提示する。
Sidebar: dispatcher の自己修復ビュー起動案内(broker フレーム時のみ、optional)
broker(tmux) backend では dispatcher ペインは detached tmux session として独立して存在する。窓口の隣のターミナルでこれを 1 回起動しておくと control plane を常に視界に保てる(restart / auto-compact fork でセッション名が変わっても自己修復で再 attach し直す):
tools/org-dispatcher-view.sh # read-only attach(既定・安全) tools/org-dispatcher-view.sh --rw # 読み書き attach(誤入力で dispatcher に直接打鍵されるので注意)read-only で十分だが、attach 中に
Ctrl-b sで同 socket 上の他のセッション(= worker / curator のペイン)に切り替えて覗ける。終了はCtrl-b dで detach してからプロンプトでCtrl-C。renga フレームでは tmux session モデルが写像しないので案内しない(renga なら画面そのものを見ればよい)。詳細はtools/org-dispatcher-view.shの--help。打鍵の読み替え: 上記
Ctrl-bは tmux prefix の既定値なので、prefix を変更している場合は設定した prefix に読み替える。さらに、この broker フレームのビューアを renga の画面の中で起動する場合はこの打鍵が届かない — renga の org サイドバーは既定で有効なあいだCtrl+B(tmux prefixCtrl-bと同じ物理入力)を消費して PTY へ渡さないため、Ctrl-b sでの切替もCtrl-b dでの detach もできなくなる(抜けられなくなるので起動前に対処する)。回避策(renga 設定[ui] org_sidebar = "off"、または内側 tmux prefix の変更)はdocs/operations/dispatcher-view.md「外側フレームが renga の場合」を参照。
前回の状態がある場合:
組織を起動しました。
前回の状態: {サマリー}
ディスパッチャーを起動しました(キュレーターは知見が溜まったときに自動で一時起動されます)。
何をしますか?
初回起動の場合:
組織を起動しました。
ディスパッチャーを起動しました(キュレーターは知見が溜まったときに自動で一時起動されます)。
プロジェクトはまだ登録されていません。何をしましょうか?
drift 検出時の warning 添付例 (上記テンプレートの末尾に転記。{installed} / {latest_in_window} / {pin} は実行時にスクリプトが PyPI と pyproject.toml から動的に決定するもので、本ファイルには hard-code しない):
...
何をしますか?
[runtime drift] claude-org-runtime: installed={installed} latest={latest_in_window} -- `python -m pip install --upgrade 'claude-org-runtime{pin}'` で更新できます
role config drift 検出時の warning 添付例 (Block C4 exit 1。{errors} / {findings} は実行時にスクリプトが決定するもので、本ファイルには hard-code しない。Block C2 の行と両方出るときは 1 行ずつ並べる):
...
何をしますか?
[role config drift] {errors} error(s) / {findings} finding(s) -- see the [ERROR] lines above
ユーザーが再実行コマンドを求めたら、上の手順 1 のコマンド(py -3 / python3 のプラットフォーム別)をそのまま案内する。スクリプトは再実行コマンドを出力しない: 貼って動く形は読み手のシェル(cmd.exe / PowerShell / Git Bash)ごとに quoting が非互換で、プロセス側からはどれで読まれるか知りようがないため、誤った形を出すより窓口が固定文で案内する方が確実。
Appendix: ClaudeCode 起動コマンド(役割別)
Block A / org-delegate Step 3 で使う spawn_claude_pane の役割別パラメータ。
renga 0.18.0+ では mcp__renga-peers__spawn_claude_pane が役割別の構造化フィールド(cwd / permission_mode / model / args[])を受け取り、--dangerously-load-development-channels server:renga-peers を自動付与する。旧方式の cd X && claude ... を spawn_pane に流し込むパターンは 禁止(renga の bare-claude auto-upgrade が発動せず channel push が届かなくなる落とし穴を再導入するため)。
共通引数:
permission_mode:autoリテラル直書き(ディスパッチャー除く)。CLAUDE.md には変数展開機構がないためregistry/org-config.mdの値を実行時に代入することはできない。値を変更する場合はregistry/org-config.md冒頭の同期注意セクションを参照cwd: 各ロール専用ディレクトリへの相対パス(caller pane の cwd 基準で解決される)
注: Secretary は
renga --layout opsで起動され、--permission-mode未指定のまま動作する(人間判断窓口のため)。registry/org-config.mdの「Role別の適用範囲」節を参照。
ディスパッチャー
cwd=".dispatcher"permission_mode="bypassPermissions"(固定。default_permission_modeの影響を受けない)model="sonnet"
理由: ディスパッチャーはワーカー起動時に mcp__renga-peers__spawn_claude_pane を発行する。auto モードの安全分類器はこの「子エージェント起動」を "Create Unsafe Agents" と判定してブロックするため、auto ではワーカー派遣が成立しない。
キュレーター(オンデマンド起動専用 — org-start からは spawn しない)
ディスパッチャーが worker クローズ時の閾値超過で spawn する(.dispatcher/references/pane-close.md Step 5-3):
cwd="../.curator"(caller は dispatcher、cwd=.dispatcher/基準の相対解決)permission_mode=automodel="sonnet"
理由: auto モードの safety classifier はセッションモデルと独立した専用モデルで動作し、承認判定はセッションモデルに依存しない(公式: https://www.anthropic.com/engineering/claude-code-auto-mode)。キュレーターの知見整理ワークロードは軽量・機械的側に分類できるため Sonnet で十分(ワーカーの既定 opus 方針とは独立の判断)。
ワーカー(org-delegate の Step 3 で使用)
既定は model="opus"(品質優先)。ただし oracle が強いタスクは、窓口が明示指定したときに限り model="sonnet"(Sonnet 5 メイン)を選んでよい。
判定軸は工数ラベル (S/M/L) ではなくタスクの oracle 強度と判断密度(ultracode 許可基準と同じ思想):
- Sonnet 5 メイン可: 次を両方満たす帯 —(a) 機械検証可能な正解が実装の外に固定されている(fixture 駆動の期待値・テストスイートの assertion・DB の行など)、(b) 設計判断が brief で事前固定済みで実装中に決め直す判断が残らない。
- opus 維持: (a)(b) のどちらかを欠くもの(機構の再導出が要る retarget 型 / 契約設計を含む / 実測しないと決められない決定が実装中に残る帯)。
窓口の指定は DELEGATE 本文の モデル: sonnet 行で伝わり、この行が無ければディスパッチャーは opus で spawn する(.dispatcher/references/spawn-flow.md 3-2)。
モデル選択は承認フローの安全性とは独立である。auto モードの分類器はワーカーのセッションモデルとは別の専用モデルで動作し、承認判定はワーカーが opus か sonnet かに依存しない(公式: https://www.anthropic.com/engineering/claude-code-auto-mode)。ディスパッチャーだけは bypassPermissions 固定なので分類器を経由せず、コスト最適化として sonnet 運用(この判断はワーカーには自動適用しない)。
通常:
cwd="{workers_dir}/{task_id}"(絶対パス推奨)permission_mode=automodel="opus"(既定。oracle が強いタスクで窓口が明示指定した場合のみ"sonnet")
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
/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 落として」等で発動。