ディスパッチャーのコンテキストを圧迫したまま session を続けるのを避けるため、 monitoring 状態(active workers / 直近 polling cursor / pending escalations)を handover ファイルに書き出し、secretary の指示で /clear → /dispatcher-resume の 流れで新しいディスパッチャー session を開始する準備をする。 Secretary から DISPATCHER_HANDOVER peer message を受領したとき、または ディスパッチャー自身が context が長くなったと判断したときに使う。
dispatcher-resume
/dispatcher-handover で書き出した handover ファイルを読み込み、 ディスパッチャーを新しい session で復帰させる。/clear 直後の最初のターンで使う。
state.db の dispatcher_pane_id / dispatcher_peer_id を atomic に更新し、 worker monitoring の /loop 3m を再開する。「ディスパッチャー復帰」「resume」「引き継ぎから再開」と secretary から指示された ときに使う。
/org-start ではない(ワーカー・窓口・キュレーターは生きている前提)。
含まれるファイル(2)
- SKILL.md33.8 KB
- SKILL.md.in30.3 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
dispatcher-resume: ディスパッチャーの復帰
/dispatcher-handover で書き出した .state/dispatcher-handover.md を読み込み、
ディスパッチャーとして最低限の自覚(組織員としての立ち位置・進行中の派遣・監視
対象ワーカー)を復元し、/loop 3m の worker monitoring を再開する。
前提:
- ワーカー / 窓口 / キュレーターのペインは前 session から生きたまま残っている。 新たに spawn しない(/org-start ではない)。
- 自ペイン(name=
dispatcher)も生きている。secretary がsend_keysで/clear→/dispatcher-resumeを打鍵した直後の状態。pane_id / peer_id は 変わっていないはずだが、必ず観測して state.db を atomic 更新する。- state DB (
.state/state.db) はそのまま使う。- 監視 gap を埋める内部状態ファイル(
.state/dispatcher-event-cursor.txt(backend session に束縛された poll_events cursor) /.state/dispatcher/worker-idle-state.json/.state/dispatcher/curate-inflight.json(存在時) /.state/pending_decisions.json)は 前 session から残っている。新規作成・初期化しない(既存値からそのまま継続)。- handover ファイルが存在しないか古すぎる場合は、
/org-startを案内して停止する。
輸送層(transport)両系 — 既定
broker/ opt-inrenga: 本ファイル(および各スキル)の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.mdSurface 8 + push-primary amendment(broker push 一次が 既定の契約、pull は fallback として retain)。opt-inrengaは削除せず常時有効な切戻しの安全装置として維持する。broker 実走(dogfood)は Epic #6 Issue G スコープで本ファイルの既定運用経路ではない(二フレーム注記(Refs #604): ここの「既定broker」はコード既定(tools/transport.py: DEFAULT_TRANSPORT、生成面はこれで render)。運用既定は broker dogfood が Epic #6 Issue G まで未活性のためrengaで、両者は指す対象が異なり矛盾しない。総説は rootCLAUDE.md。)
Step 0 の前: 残存 loop 予約 / stale 再発火ガード(重複起動の早期遮断)
/dispatcher-resume は handover を 1 回だけ消費する one-shot である(Step 7 で
.state/dispatcher-handover.md を .consumed.md に rename する)。だが Step 5 で起動する
/loop 3m の予約が、過去のバグや手動操作で /dispatcher-resume 自身を反復対象に握って
いると、監視サイクルのたびに本 skill が再発火する。この stale 再発火 / 重複起動 を
Step 0 に入る前に遮断する(恒久対策の経緯は
knowledge/raw/2026-06-19-dispatcher-resume-loop-recursion.md)。
- live / consumed の handover 状態を 存在ベースで判定する(
nowとの時刻比較に 依存しない。cold-start vs resume の分岐が live.mdのみを対象とする.dispatcher/CLAUDE.mdの規約と揃える):python3 -c " import os md = '../.state/dispatcher-handover.md' cm = '../.state/dispatcher-handover.consumed.md' if os.path.exists(md): print('resume') # 正規の resume: live handover あり elif os.path.exists(cm): print('already_consumed') # 既に消費済み: 残存 loop / 重複起動の疑い else: print('no_handover') # handover が一度も無い: 真の cold-start 候補 " already_consumed(live.mdなし・.consumed.mdあり): live handover が無い resume 起動だが.consumed.mdが残っている。これは「(i) 直前に成功した resume の残存 loop 予約による再発火 / 重複起動(組織は健全・監視ループ稼働中)」か「(ii) 組織が止まって いて本来 cold-start が要る状況での誤起動」のいずれか。存在判定だけで no-op に倒すと (ii) の cold-start 案内を恒久的に隠すので、監視対象が実際に live かを観測して分岐する:mcp__org-broker__list_panes/mcp__org-broker__list_peersで 監視対象が live かを確認する: active な worker ペイン(role == "worker")が 1 つ以上ある、または.state/dispatcher/curate-inflight.jsonが存在する(= 監視ループが回っているべき状態)。list_peersの直前に.claude/skills/org-delegate/references/capability-first-drive-operational-gate.mdを Read し、monitoring-read-onlyの分岐を適用する(同 reference §6 の表 #5)。capability 形 かつ未承認なら列挙を live 判定に使わず破棄し、list_panesのrole == "worker"とcurate-inflight.jsonの有無だけで分岐する(停止しない・待ち時間 0 分)。 承認済み(同 reference §2 のfirst_driveがrecorded)のときは §1-2 の三値判定を通し、 「在」と出たワーカーだけを live として数える — 他タブの別 org のワーカーを live と誤認すると (ii) の cold-start 経路が抑止される。「unknown」は live に数えないが、(ii) にも倒さない: 判定不能を「監視対象なし」と読み替えると誤った/org-start案内で健全な組織を壊しうる (不可逆側)。終端処理は (i) と同じ(早期 exit + Step 5 の monitoring ディレクティブで/loopを再 arm し、残存予約を空のまま放置しない)とし、secretary にはDISPATCHER_RESUME_INDETERMINATE: 監視対象の同タブ性を確定できず(unknown N 件)。 live 判定を保留して監視ループのみ再開しました。を送って判断を仰ぐ。 unknown のワーカーは監視対象・active のまま残す(.state/workers/worker-*.mdをStatus: pane_closedにせずWORKER_PANE_EXITEDも送らず.state/dispatcher/worker-idle-state.jsonの record も消さない)。再評価は再 arm した/loop 3mの通常サイクルが行う。 どの分岐にも落ちないまま skill を終えない(一度きりの skill が loop に再発火され続ける)。- (i) 監視対象が live = stale 再発火 / 重複起動: 組織は健全で cold-start 不要。
DISPATCHER_RESUME_FAILEDを 送らず(誤った/org-start案内を出さない)、 Step 1 以降に進まず早期 exit する。残存予約を空のまま放置しない: 監視/loopが/dispatcher-resumeを反復対象に握っている兆候があれば、Step 5 の monitoring 専用 ディレクティブ(INVARIANT(loop-prompt) 準拠の/loop 3m ...)で loop を明示的に再 arm して予約を上書きする(重複起動はしない=単一 loop に収束)。既に monitoring ディレクティブ で回っていれば当該サイクルは no-op heartbeat(何も出力しない)。どちらの分岐でも予約を skill 自身に握らせたまま放置しない。再発火の事実だけ journal に残す:bash ../tools/journal_append.sh anomaly_observed \ source=dispatcher_resume worker=dispatcher kind=stale_loop_refire confidence=n/a \ note=handover_already_consumed - (ii) 監視対象が無い = 本来 cold-start が要る誤起動: stale 再発火ではない。no-op に
倒さず Step 1 の「handover ファイルなし」分岐へフォールスルーし
DISPATCHER_RESUME_FAILED→ cold-start 案内を出す(cold-start 案内を隠さない)。
resume(live.mdあり): 通常どおり Step 0 へ進む。no_handover(live.mdも.consumed.mdも無し): 通常どおり Step 0 → Step 1 へ 進み、Step 1 の「handover ファイルなし」分岐でDISPATCHER_RESUME_FAILED→ cold-start 案内に落ちる。
Step 0: 自分の identity を確認する
-
mcp__org-broker__set_summaryで「Dispatcher: 監視(resumed)」をセット -
caller pane id を確定する(
focused: trueは「そのタブで今フォーカスされているペイン」で あって caller 自身ではない。人間がフォーカスを移した瞬間、別ペインをdispatcherに改名し、 その id をdispatcher_pane_idとして DB に焼き付けてしまう): 環境変数RENGA_PANE_ID(echo "${RENGA_PANE_ID:-unset}")を起点に、mcp__org-broker__list_panesへ同一 id のレコードが 在ることを照合する。ここで確定した id が Step 2 で書くdispatcher_pane_idであり、以後の routing はこれを掴む。規則の正本はdocs/contracts/backend-interface-contract.mdT-§4.2 の caller pane id 取得規則- broker(
ORG_TRANSPORT=broker)ではRENGA_PANE_IDを読まず、最初からcaller-id 未確定に 倒す: broker にはRENGA_PANE_IDに相当する caller pane id を out of band で供給する surface が 無いので、この取得規則は broker では常に未確定に落ちる。list_panesに数値 id のエントリが在って 手元で掴めていても例外にならない(数値 id は MUST の片方=相対セレクタでないことしか満たさず、 もう片方=自タブと確立済みの列挙から採った id でありその対象が caller であること、を満たさない)。 したがって broker では下のcaller-id 未確定分岐へそのまま進み、dispatcher_pane_idを渡さない
- broker(
-
照合できたレコードの name/role を確認:
- 期待値:
name == "dispatcher"かつrole == "dispatcher" - 不一致なら
mcp__org-broker__set_pane_identity(target="<RENGA_PANE_ID の値>", name="dispatcher", role="dispatcher")で修復(相対セレクタでは撃たない) caller-id 未確定(RENGA_PANE_ID未設定 /list_panesに同一 id 無し / broker 等で同等の caller-id を入手できない): 自己修復を実行せず、dispatcher_pane_idも渡さないまま (Step 2 のupdate_session()は未指定を「保持」と解釈するので既存値が温存され、これ自体が fail-closed になる)Step 1 へ進み、identity 未確認であることを Step 6 のブリーフィングで secretary に明示して続行判断を仰ぐ(target="focused"や裸nameへフォールバックしない)
- 期待値:
-
mcp__org-broker__list_peersでname == "dispatcher"のpeer_idを取得。 呼び出しの直前に.claude/skills/org-delegate/references/capability-first-drive-operational-gate.mdを Read し、monitoring-read-onlyの分岐を適用する(同 reference §6 の表 #4)。capability 形 かつ未承認なら列挙からpeer_idを採らず破棄し、2 で確定した caller pane id で identity を 確定して進む(停止しない・待ち時間 0 分)。 このとき Step 2 の DB 書き込みではdispatcher_peer_idを渡さない(update_session()は None / 未指定を「保持」と解釈する契約なので、既存値がそのまま残る)。placeholder や 観測できなかった値を書くと、以後の routing が壊れた id を掴む。dispatcher_pane_idは 2 で確定した caller pane id をそのまま書く(caller-id 未確定のときは渡さない)。Step 6 のブリーフィングではpeer=<未取得: capability gate 縮退中>と明示して secretary に伝える。capability 形かつ承認済み(同 reference §2 の
first_driveがrecorded)のときも、自分のpeer_idは名前引きで確定しない(§1-2-d)。mcp__org-broker__list_peersは caller を除外するので、name == "dispatcher"に一致したレコードは同タブに 1 件だけ在っても自分ではありえず、 他タブのみ / 同タブ性 unknown /same_tab=Trueが複数の各分岐と合わせて、名前引きの結果が 何であれpeer_idは未確定のまま先へ進む。したがって Step 2 の DB 書き込みではdispatcher_peer_idを渡さない(update_session()の「未指定 = 保持」で既存値を温存する。 生きた同一ペインを引き継ぐ resume では保持が正しく、渡さないこと自体が fail-closed になる)。 更新するのは 2 で確定した caller pane id(dispatcher_pane_id)だけである。Step 6 の ブリーフィングではpeer=<未取得: 名前引きでは自 peer を同定できない>と明示して secretary に伝える。
Step 1: handover ファイルを読み込む
ディスパッチャーの cwd は .dispatcher/ なので、リポジトリ root のパスを 1 階層上に解決する。
.state/dispatcher-handover.mdが存在するか確認:ls -la ../.state/dispatcher-handover.md 2>&1- 存在しない → secretary に通知して停止:
DISPATCHER_RESUME_FAILED: handover ファイルがありません。 /org-start でディスパッチャーを cold start してください。
- 存在しない → secretary に通知して停止:
- フロントマター
created_atを見て鮮度を判定する。created_atは決定的 UTC で書かれている (/dispatcher-handover) ので、比較用のnowも決定的 UTC で取得する (date -u +%Y-%m-%dT%H:%M:%SZ、PowerShell 環境は(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))。local(JST)時刻でnowを 取るとnow - created_atが約 9 時間ぶれ、24h / 7d 境界付近で鮮度を誤判定するため:- 24 時間以内 → そのまま採用
- 24 時間超〜7 日以内 → secretary に警告(「handover が古い、続行する旨」)
- 7 日超 → 採用せず
/org-startへの切り替えを推奨し停止
- ファイル本文を Read で取り込む。書かれている内容は次 session の自分にとっての 「事実」として扱う(後の Step 3 で state.db と照合する)。
Step 2: state.db の dispatcher identity を atomic 更新する
Step 0 で観測した pane_id / peer_id を StateWriter.transaction() 経由で
1 トランザクションで 書く(post-commit hook が .state/org-state.md を再生成
してくれる)。これが acceptance の「state.db identity が atomic 更新」要件の本体。
python3 -c "
import sys, os
sys.path.insert(0, os.path.abspath('..'))
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='<observed_pane_id>',
dispatcher_peer_id='<observed_peer_id>',
)
"
Step 0 で peer_id が未確定だった場合(capability gate の縮退中、または承認済み路で名前引きから
自 peer を同定できなかった場合)は、dispatcher_peer_id を渡さない形で呼ぶ。上の例をそのまま
使うと観測していない値を書き込むことになる:
python3 -c "
import sys, os
sys.path.insert(0, os.path.abspath('..'))
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='<observed_pane_id>',
)
"
Step 0 が caller-id 未確定 に落ちていた場合は、同じ理由で dispatcher_pane_id も渡さない
(両方とも未確定で update_session() に渡す引数が残らないなら、この Step 2 自体を skip する)。
focused から採った id で埋めてはならない — 人間のフォーカス位置に依存した別ペインの id を、
以後の routing の宛先として恒久的に焼き付けることになる。
update_session() は未指定を「保持」と解釈する契約なので、既存値がそのまま残る(生きた同一ペインを
引き継ぐ resume ではこれが正しい。詳細は Step 0 手順 3 / 手順 4 と共有 reference §1-2-d)。
dispatcher_pane_id/dispatcher_peer_idは 文字列で書く(schema は TEXT、 既存/org-startも文字列を入れているので型を揃える)transaction()内なので片方だけ書いて失敗しても DB は半端な状態にならない- 観測値が handover frontmatter と異なっても、現観測(list_panes / list_peers)を 真値として優先する。差分があった旨を secretary 宛 message に含める
Step 3: state.db で現状を再取得し handover と照合する
python3 -c "
import sys, os
sys.path.insert(0, os.path.abspath('..'))
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[]が handover の「監視対象のワーカー」セクションと整合するかactive_worker_dirs[]のワーカーディレクトリ存在
handover には書かれていないが state.db に active で残っている worker、または
handover には書かれているが state.db / list_panes で消えている worker があれば、
secretary に 報告して 判断を仰ぐ(勝手に再 spawn / status 変更しない)。
Step 4: ペイン生存確認
mcp__org-broker__list_peers を再度呼び、handover に記載のワーカー名が現存するか
確認する。消えていれば secretary に WORKER_PANE_EXITED: worker-{task_id} (resume 時点で不存在) を通知(reconcile は窓口の責務)。
list_peers の直前に
.claude/skills/org-delegate/references/capability-first-drive-operational-gate.md
を Read し、monitoring-read-only の分岐を適用する(同 reference §6 の表 #6)。capability 形かつ
未承認なら列挙を不存在の根拠にせず破棄し、mcp__org-broker__list_panes と events テーブルの報告痕跡で判定する。
確定できなければ WORKER_PANE_EXITED を送らない(縮退中の「列挙に居ない」は死亡の証拠ではなく、
別 org の同名ピアと取り違えうる。同 reference §3-B-1)。停止しない・待ち時間 0 分。
capability 形かつ承認済み(同 reference §2 の first_drive が recorded)のときは、§1-2 の三値判定を
handover 記載の各ワーカー名に適用する。「在」は生存として扱う。「不在」だけでは WORKER_PANE_EXITED を
送らず、送信可否は §1-2-c の pane 面裏取りが取れたかに従う。「unknown」でも送らず、indeterminate として
secretary に報告する。(判定条件・三値の定義・評価順の正本は §1-2。ここに重ねて書かない)
unknown / indeterminate のワーカーは監視対象・active のまま残し、Step 5 で再開する /loop 3m の
通常サイクルが再評価する。
Step 5: 監視ループの再開
以下をこの順序で評価する:
- inflight 再生成(先に実行): Step 4 の
list_peers/list_panesにname == "curator"のペインが生きているのに.state/dispatcher/curate-inflight.jsonが無い場合(前 session が spawn 直後の inflight 書き込み前に途切れた等)、untracked curator を放置しないようstarted_at = <決定的 UTC>/pane_id = <下記の pane id(backend が返した型のまま)>/reasons: []/extended: false/curate_result: null/last_inspect_hash: null/last_inspect_ts: nullで inflight を再生成する(フィールドの正本は.dispatcher/references/pane-close.md5-3 の JSON。trigger_task_idは inflight を失った前 session の CLOSE_PANE 対象を指すフィールドで resume 側に値の出所が無いため書かない — Step 5.3 の完了受領 / timeout 判定はこの フィールドを読まない。curate_resultは受領したCURATE_*を次サイクルへ持ち越す フィールドで、resume 時点では何も受領していないので初期値nullを明示して書く — Step 5.3 (a) が close できないまま inflight を保持するときだけ"done"/"skipped"/"error"に更新される)。started_atはdate -u +%Y-%m-%dT%H:%M:%SZ(PowerShell 環境は(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))の出力をそのまま埋め、 手書きの local(JST)時刻をZで書かない(再生成 started_at が未来時刻になると Step 5.3 のnow - started_atが負値となり curator が永久に孤立する。決定的取得の正本は同 5-3)。pane_idは生存確認に使ったmcp__org-broker__list_panesの列挙でname == "curator"かつrole == "curator"を指しているレコードのidを、mcp__org-broker__list_panesが返した JSON の型を 変えずに書く(列挙が手元にあるのでその場で採れる)。tmux backend は"%3"のような文字列、 WezTerm backend は整数を返すので、引用符の有無を型に合わせる — 文字列を引用符無しで 書くと JSON が構文非妥当になり、Step 5.3 も次の resume もこのファイルを読めず curator の 追跡が失われる(正本は同 5-3 の注記)。pane_idを欠いた形で再生成しない — Step 5.3 (a)/(b) の curator close はこのpane_idを identity 照合の起点にするので、欠けると schema 追加前の 旧形式ファイルと同じ扱いになり、close が列挙引き直しの fallback 経路に落ちる (.dispatcher/references/worker-monitoring.mdStep 5.3)。list_peersにのみcurator名がありmcp__org-broker__list_panesの列挙に該当レコードが無い場合は 再生成せず secretary に informational として報告する(数値 pane id を採れないうえ、 messaging 面の名前一致は同タブの pane 生存の根拠にならない。Step 4 と同じ規律で、 契約 T-§4.2 が pane 制御可能性の推論を禁じている)。 以降の判定はこの再生成後の状態で行う(= このケースは必ず 2 の再開条件を満たす) /loop 3m再開条件: handover のactive_worker_count > 0、state.db の active worker dirs が非空、またはcurate-inflight.jsonが存在する (1 の再生成分を含む。オンデマンド curate の完了監視が引き継ぎ対象。.dispatcher/references/worker-monitoring.mdStep 5.3)のいずれかを満たせば 下記の monitoring 専用ディレクティブを渡して/loop 3mで worker monitoring を 再開する(prompt を省略しない)。この再開は「/loop で監視を再開します」と 宣言するだけでは武装されない — 宣言はターン終了と同時に失効するため、必ず 本ターン内で/loopを実 invoke すること(窓口側もinspect_paneで実行痕跡を 裏取りし、無ければsend_keysで再点火する運用である):
/loop 3m references/worker-monitoring.md(ディスパッチャー cwd .dispatcher/ 基準)の「監視ループ 1 サイクル」を 1 回だけ実行する(poll_events → check_messages → list_panes → inspect_pane → stall / relay-gap / pane_output 評価)。anomaly / stall / relay-gap / pane_exited を検出したサイクルのみ secretary へ通知し、検出が無ければ何も出力しない(毎サイクルの状況サマリや自然言語の状況描写を書かない)。cadence は 3 分以上を保ち短縮しない。スラッシュコマンドや本 skill を反復対象にしない。
- 監視ループの 1 サイクル目で
mcp__org-broker__poll_eventsは.state/dispatcher-event-cursor.txtの前 cursor(前 session 終了時点)から resume する。これで「pane が閉じている間に来た pane_exited は次回 poll で必ず拾える」 semantics が維持される(cursor 仕様)。ただし resume するのは cursor に記録されたsession_keyが現 backend session と一致したときだけで、一致しなければ 1 サイクル目が cursor を破棄して「今以降」から張り直し、窓口へEVENT_CURSOR_RESETを 1 行報告する (/clearを挟む間に renga / broker daemon が再起動していた場合がこれ。判定と手当ての 正本は.dispatcher/references/worker-monitoring.md「監視ループ 1 サイクル」Step 1)。本 skill は cursor ファイルを読まず・書かず・消さない — 束縛の照合は監視ループ側の責務で、resume 側で先回りして初期化してはならない mcp__org-broker__check_messagesの 1 サイクル目で前 session 中にキューに溜まった worker → dispatcher peer message を drain する(broker =mcp__org-broker__check_messages。 broker は push 一次に再設計済だが、/clear中に channel sidecar 経由で注入できなかった 滞留分はキューに残るため、resume 1 サイクル目のcheck_messagesフォールバック drain で 取りこぼさず回収する=ツール名のみ変わり drain 論理は同型。§9.6 / §9.3 lease-reap).state/dispatcher/worker-idle-state.jsonは前 session のidle_streak_cyclesを 保持しているので stall 検出の連続性も維持される
監視対象が 0 件(active worker dir も 0、active_runs も 0、curate-inflight.json も
無し、かつ 1 の評価後なので list_panes に curator ペインも不在)の場合のみ /loop を
始動せず、idle 状態を secretary に通知して待機する:
DISPATCHER_RESUMED_IDLE: 監視対象なしで resume 完了。DELEGATE 待機。
Step 6: secretary にブリーフィングを返す
handover + state.db の現状を統合した上で、secretary に簡潔に報告:
DISPATCHER_RESUMED: ディスパッチャー復帰完了。
- pane=<observed_pane_id> / peer=<observed_peer_id> (state.db 更新済)
- 監視対象ワーカー: <task_id list>
- pending decisions: <count>
- handover との差分: <あれば 1 行、無ければ「なし」>
- 監視ループ: /loop 3m 再開 (or idle)
- Step 0 が
caller-id 未確定に落ちていた場合はpane=<未確定: caller pane id を同定できない>と 書き、(state.db 更新済)は付けない(前値保持のままであることを secretary に伝える)
Step 7: handover ファイルを consumed 状態に切り替える
resume が成功したら、.state/dispatcher-handover.md を .state/dispatcher-handover.consumed.md に rename する。これにより:
.dispatcher/CLAUDE.md起動時の自動分岐(handover ファイルが直近 7 日以内に存在すれば resume)が「次の/org-startcold-start 時にも誤って resume に分岐する」事故を防ぐ- 直近 1 件のみ参照用に
.consumed.md形式で残る(次の/dispatcher-handoverが新たに.mdを書いた時点で.prev.mdバックアップに置き換わる、または上書きされる)
mv ../.state/dispatcher-handover.md ../.state/dispatcher-handover.consumed.md
- 既に
.consumed.mdがあれば上書きで構わない(直近 1 件のみ保持) .state/dispatcher-handover.prev.mdは前回の/dispatcher-handoverで書かれたバックアップ。読み込み済みでも消さない
イベント記録
ディスパッチャーの cwd は .dispatcher/ なので 1 階層上を呼ぶ:
bash ../tools/journal_append.sh dispatcher_resumed \
pane_id=<observed_pane_id> peer_id=<observed_peer_id> \
active_workers=<count> note=resumed_from_handover
やってはいけないこと
- 新規にディスパッチャー / キュレーターを spawn する(既に生きている)
- ワーカーに勝手に SUSPEND / SHUTDOWN を送る
.state/dispatcher-event-cursor.txt/worker-idle-state.json/curate-inflight.json/pending_decisions.jsonを初期化 / 削除する(前 session からの監視連続性が壊れる)- handover の内容と state.db の現状が食い違うときに、勝手にどちらかへ寄せる (必ず secretary に報告して判断を仰ぐ)
- atomic 更新を分割して書く(必ず
StateWriter.transaction()1 ブロックで完結させる) - Step 5 の
/loop 3mを prompt 省略で起動する //dispatcher-resume(本 skill 自身)や 他のスラッシュコマンドを/loopの反復対象に渡す(skill が 3 分ごとに自己再帰する。 必ず monitoring 専用ディレクティブを明示的に渡す。Step 5 の INVARIANT(loop-prompt) 参照)
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
プロジェクトのゴール台帳(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 落として」等で発動。
窓口が人間から「承認スコープの輪郭」を受けて、その内側で triage → worker 派遣 → iteration → verify → push → PR 作成 → CI 監視 を完了駆動で自走し、PR ごとに merge gate で必ず停止するベルトコンベア。/work-discovery + /org-delegate + verify + /org-pull-request + /org-escalation を呼ぶ薄い orchestrator。 発動条件: 人間が「この範囲は自走してよい」というスコープ承認を明示し、複数候補を 完了駆動で順次流したいとき(例「triage 上位の S 級バグ修正を空き pane 分まで自走、 merge は都度わたしが判断」)。起動主体は窓口に限定。スコープ外候補・判断境界・退出条件に 触れたら必ず halt する。merge 自動化はしない。