作成日: 2026-06-10
関連 issue: #66(handover_resume バグ)、#59(transfer/accept バグ)
参照: Handover capability 仕様書
ステータス: 検討中 — このドキュメントを確定させてから実装に入ること
issue #66 の修正範囲が広く、仕様書・コード・issue の三者の整合を取らないまま実装すると設計が崩れる。
実装前にこのドキュメントで変更方針を確定させる。
_lifecycle_candidates の issue-level 除外問題場所: butler/handover_backend.py 466〜470 行
if issue_id is None:
if item.get("issue_id") is not None:
continue # issue付きスナップショットを全除外してしまう
resume_handover() を引数なしで呼ぶと、全 issue-level snapshot が除外され、古い project-level snapshot が返る。
仕様書の意図(§11.4 手順 2〜4)は正しく書かれている:
2. issue_id があれば issue スコープで検索
3. issue_id がなく default issue があれば default issue スコープで検索
4. issue_id も default issue もない場合だけ project-level にフォールバック
実装がステップ 3 を欠落させており、issue_id=None → 即 project-level になっている。
unknown になるButler が MCP clientInfo.name を無視しているため、source_brain / target_brain が常に unknown になる。
これが #59(transfer/accept 引き継ぎ失敗)の根本原因でもある。
any フォールバック(target_brain=current Brain の exact match 優先、なければ any)index.json)issue_id の扱い(issue scope → default issue scope → project-level の優先順)| 箇所 | 現状 | 変更後 |
|---|---|---|
§11.3〜11.6 パラメータ表の client |
Brain が手動で渡す任意パラメータ | clientInfo.name から自動設定。上書き用 override として残す |
§11.3 handover_suspend の target_brain |
「省略時は client と同じ値」(手動) |
source_brain = target_brain = clientInfo.name を自動設定 |
§11.4 handover_resume 手順 5 |
client で current Brain を識別 |
session.brain_id(自動)で識別 |
§11.5 handover_transfer の target_brain |
Brain が Brain B の名前を手動指定 | Brain レジストリから参照して指定 |
§11.6 handover_accept 手順 3 |
client で current Brain を識別 |
session.brain_id(自動)で識別 |
| §11 全体 | handover_list_brains ツールなし |
Brain レジストリ参照用ツールを追加 |
| §18 未決事項 | Brain identity は未扱い | Brain レジストリと clientInfo.name 取得方法を追記 |
今日(2026-06-10)の議論で確定した内容。
clientInfo.name → brain_id の自動設定work_session_start 時に MCP セッションの clientInfo.name を読み取り、WorkSessionState.brain_id に保存する。
Brain が手動で client を渡さなくても identity が確定する。
@dataclass
class WorkSessionState:
project_root: str | None = None
owner: str | None = None
repo: str | None = None
default_issue: int | None = None
brain_id: str | None = None # MCP clientInfo.name から自動設定
work_session_start 時に brain_id を {project_root}/session_logs/brain_registry.json に記録する(upsert)。
transfer 時に Brain A が Brain B の brain_id を参照するために使う。
{
"brains": {
"claude-code": { "last_seen": "2026-06-10T05:00:00Z" },
"openai-codex-cli": { "last_seen": "2026-06-10T04:50:00Z" }
}
}
source_brain = target_brain = session.brain_idtarget_brain == session.brain_id でマッチ → 自分の snapshot だけが返るany フォールバックは仕様書の通り維持するsource_brain = session.brain_id(自動)target_brain = Brain A が handover_list_brains() でレジストリを確認し、Brain B の brain_id を指定target_brain == session.brain_id でマッチBrain による target_brain の選択フロー(Brain 層の責務):
ユーザーが「〇〇に引き継いで」と指示した場合、Brain は以下のフローで target_brain を決定する:
handover_list_brains() でレジストリを取得handover_transfer を実行openai-codex-v2 か openai-codex-cli のどちらですか?」work_session_start を呼んでもらえますか?」Butler 側(MCP ツール層)に変更は不要。_lifecycle_candidates の 0件→need_input / 1件→自動 / 複数→提示 と同じ考え方を Brain 層で実現する。
_lifecycle_candidates の修正方針issue_id=None の時の動作を仕様書 §11.4 の意図に合わせる。
現状:
issue_id=None → project-level snapshot のみ
修正後:
issue_id=None かつ default_issue あり → default_issue スコープで検索
issue_id=None かつ default_issue なし → project-level にフォールバック
_lifecycle_candidates は issue_id を受け取るシグネチャのままで、呼び出し側(resume_handover)が default issue の解決を担う。
fuzzy candidates(曖昧マッチ)は採用しない。
source_brain=unknown が発生しなくなることで #59 の根本原因が消える。
#59 は #66 完了後に動作確認して Close する。
clientInfo.name を取得する方法調査済み — 方法確定
アクセスパス:
ctx.session.client_params.clientInfo.name
根拠:
mcp/server/session.py: ServerSession._client_params が InitializeRequest 受信時に設定される(line 165〜168)_client_params は types.InitializeRequestParams 型で .clientInfo: Implementation を持つImplementation は BaseMetadata を継承し name: str を持つContext.session は request_context.session(ServerSession)を返す(line 1298〜1300)実装方法: work_session_start に ctx: Context パラメータを追加するだけでよい。FastMCP が自動注入する。run_implement / run_apply_edits で同パターンの実績あり。client_params 読み取りは同期操作のみなので work_session_start は sync のままでよい。
def work_session_start(project_root: str, ctx: Context) -> dict[str, Any]:
brain_id: str | None = None
if ctx.session.client_params:
brain_id = ctx.session.client_params.clientInfo.name
return init_session(project_root, brain_id=brain_id)
確認済み(2026-06-10): Claude Code が送ってくる clientInfo.name の値は "claude-code"(ハイフン区切り、すべて小文字)。
_normalize_brain 通過後も "claude-code" のまま(ハイフンは許可文字)。
Codex の値は未確認。_normalize_brain を通すため "Claude Code" → "claude_code" のように正規化される(英数字 / ハイフン / アンダースコア以外を _ に変換)。
client パラメータの扱い調査済み — 候補A が自然
現行コード:
client: str | None = None を持つhandover_backend.py line 43: source_brain = _normalize_brain(client, default="unknown")target_brain マッチングにも使われるbrain_id 自動設定後の方針(候補A を採用):
client が明示された場合は client を優先(override)client=None の場合は session.brain_id を使うこれにより既存の client 手動指定のユースケースを壊さず、自動化の恩恵も受けられる。
brain_id = None(未取得)の場合の挙動調査済み — client=None 現行動作と同じにする
clientInfo.name が取得できなかった場合、brain_id = None のまま渡す。
呼び出し側では client = client or session.brain_id のようにフォールバックを組めばよい。
_normalize_brain(None, default="unknown") → "unknown"
_normalize_brain(None, default="any") → "any"
これは現行の client=None 時と同じ動作であり、既存テストへの影響がない。
brain_id = None の場合は実質「client が省略された場合」と同じになる(候補C 相当)。
handover_list_brains ツールの仕様調査済み — シンプルな仕様で確定できる
既存ツールのパターンに合わせた想定仕様:
# mcp_facade.py に追加
@server.tool(
name="handover_list_brains",
description=(
"接続実績のある Brain 一覧を返します。"
"handover_transfer の target_brain を確認するときに使います。"
),
)
def handover_list_brains() -> dict[str, Any]:
return list_brains(project_root=get_session().project_root)
# brain_registry.py
def list_brains(project_root: str) -> dict[str, Any]:
# brain_registry.json を読んで返す
# 返値: {"status": "ok", "data": {"brains": [{"brain_id": "...", "last_seen": "..."}, ...]}}
保存先: {project_root}/session_logs/brain_registry.json
推奨: このドキュメント確定後、実装前に仕様書を更新する
仕様書が古いまま実装すると次の Brain が仕様書を信頼できなくなる。
更新箇所は §6.変更対象ファイル に記載。
| ファイル | 変更内容 |
|---|---|
butler/mcp_facade.py |
work_session_start で clientInfo.name を取得し brain_id に設定。handover_list_brains ツールを追加 |
butler/work_session_state.py(または相当箇所) |
WorkSessionState に brain_id フィールドを追加 |
butler/brain_registry.py(新規) |
register_brain / list_brains |
butler/handover_backend.py |
_lifecycle_candidates のバグA修正。suspend/resume/transfer/accept で session.brain_id を自動利用 |
docs/capabilities/handover/仕様書.md |
§11.3〜11.6 の client パラメータ説明更新。Brain レジストリ・handover_list_brains を追記。§18 未決事項を更新 |
clientInfo.name を取得する方法を確認した(Claude Code: "claude-code")client パラメータの扱い方針を確定した(候補A: client or session.brain_id、後方互換)brain_id = None の場合の挙動を確定した(現行 client=None と同一動作)handover_list_brains ツールの仕様を確定した(brain_registry.json を返すシンプルな実装)