work_publish がタスクブランチを自動マージするrun_implement 完了後、Butler は butler/task-{task_id} ブランチにコミットし、
next_required_action として work_publish(git push)を指示している。
設計上は「Brain が結果を確認し、OK なら Brain がマージする」フローを想定していたが、
タスクブランチのまま push しているためマージが行われていなかった。
work_publish がタスクブランチ上で呼ばれた場合に、自動的に base ブランチへのマージを行ってから push する。
Brain は「確認して work_publish」の1ステップで完結し、新たなツールを覚える必要がない。
run_implement
→ Butler が butler/task-{task_id} ブランチで実装・コミット
→ ok result (next_required_action: work_publish)
→ Brain が evidence (changed_files / verification) を確認
→ Brain が承認 → work_publish() を呼ぶ
├─ [タスクブランチ検知] base ブランチ(main 等)に --no-ff マージ
├─ タスクブランチを削除
└─ push
butler/task-* 以外のブランチ上での work_publish は従来通り(マージなしで push)。
butler/work_record_backend.pypublish_work() にタスクブランチ検知とマージ処理を追加する。
追加するヘルパー関数:
def _default_base_branch(project_root: str) -> str:
"""origin/HEAD → main → master の順でベースブランチを決定する。"""
...
def _merge_task_branch(project_root: str, task_branch: str, base_branch: str) -> tuple[bool, str]:
"""task_branch を base_branch に --no-ff マージし、task_branch を削除する。"""
...
publish_work() の変更箇所:
_has_changes() チェックの直後、version bump の前に以下を挿入:
_, branch_out, _ = _run_git(["branch", "--show-current"], root)
if branch_out.startswith("butler/task-"):
base = _default_base_branch(root)
ok, detail = _merge_task_branch(root, branch_out, base)
if not ok:
return {
"status": "error",
"error_code": "merge_failed",
"summary": f"タスクブランチのマージに失敗しました: {detail}",
"hint": "コンフリクトを手動で解消してから再度 work_publish を呼んでください",
}
_merge_task_branch() の処理フロー:
git checkout <base_branch>git merge --no-ff <task_branch> -m "merge: <task_branch>"git merge --abort して元のブランチに戻り (False, detail) を返すgit branch -d <task_branch> でタスクブランチを削除(True, "") を返すベースブランチの決定 (_default_base_branch):
git symbolic-ref refs/remotes/origin/HEAD → refs/remotes/origin/main のような形式から抽出main → master の順でローカルブランチ存在確認"main" にフォールバックbutler/mcp_facade.pywork_publish の description を更新して、タスクブランチ上での挙動を明記する。
@server.tool(
name="work_publish",
description=(
"記録済みの作業を共有先へ反映します。"
"butler/task-* ブランチ上で呼ぶと、base ブランチ(main 等)への自動マージを行ってから push します。"
"未記録の作業が残っている場合は実行せず、先に work_record を促します。"
),
)
サーバー説明文(_SYSTEM_PROMPT 相当)にも work_publish の挙動を補足する。
implement_maid.py / gemini_cli_implement_maid.pynext_required_action はすでに work_publish を指しているため変更不要。
ただし hint を以下に更新してタスクブランチでの自動マージを Brain に伝える:
evidence["next_required_action"] = {
"tool": "work_publish",
"reason": "implement completed — review evidence, then call work_publish to merge and push",
"hint": (
f"evidence を確認し、OK なら work_publish() を呼んでください。"
f"タスクブランチ({work_branch})は自動的に main にマージされてから push されます。"
),
}
tests/test_work_record_backend.py(または新規 tests/test_work_merge_in_publish.py)| テストケース | 検証内容 |
|---|---|
タスクブランチから publish_work() |
main にマージされ、タスクブランチが削除され、push が呼ばれる |
通常ブランチから publish_work() |
マージ処理をスキップして従来通り push |
| マージコンフリクト発生 | merge_failed エラーが返り、元ブランチに戻る |
origin/HEAD が取れない環境 |
main フォールバックでマージが成功する |
--no-ff を採用する理由タスクブランチの commit が merge commit として履歴に残り、どの task_id の実装かトレースできる。
--squash は将来オプションとして追加可能。
git merge --abort でタスクブランチの状態に戻す。
Brain には merge_failed エラーと手動解消の hint を返す。
push は一切行わない(中途半端な状態で push されない)。
マージ成功後は git branch -d(safe delete)で削除する。
remote のタスクブランチは push されていないため、remote 側の削除は不要。