swain-teardown について
swain-teardownは、セッション終了時にリポジトリとワークツリーを確認し、未完了の完了手順を補い、引き継ぎとクリーンアップを調整するスキルです。未コミット変更、未マージブランチ、ワークツリー削除に安全確認を設けています。
主な機能
- 01孤立したワークツリーを確認
- 02未コミット変更を提示
- 03不足している完了手順を実行
- 04チケット同期を促す
- 05セッション引き継ぎ要約を作成
活用シーン
- 01セッション終了前にリポジトリ状態を確認
- 02セッションのブックマークがないワークツリーを検出
- 03引き継ぎ前にテストや振り返りを補完
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cristoslc/ swain --skill swain-teardownインストール後、Claude Code で/swain-teardownを入力して読み込まれたか確認します。
詳細
セッションの終了処理
セッションを終了するための単一のエントリーポイントです。ダイジェスト、ふりかえり、マージ、クリーンアップ、クローズ、コミットのシャットダウンシーケンス全体を実行します。以前の swain-session close ハンドラーを置き換えます(ADR-023)。
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
bash "$REPO_ROOT/.agents/bin/swain-session-check.sh" 2>/dev/null
session-chain が渡されておらず、JSON 出力の "status" が "active" 以外の場合は、オペレーターに「終了処理を行うアクティブなセッションはありません。」と伝えます。コード 0 で正常終了します。
このスキルの内容
セッションの終了前に、セッションの終了処理で一連の衛生チェックを実行します。
- 孤立したワークツリーの検出 — 対応するセッションブックマークがないワークツリーを見つけます(SPEC-233:安全チェックを伴う対処が可能)
- Git のダーティ状態チェック — オペレーターが離れる前に、未コミットの変更を明らかにします
- 完了パイプラインのチェック — BDD、スモークテスト、ふりかえりが実行されたことを確認し、未実施の手順を実行します(SPEC-258)
- チケット同期の確認 — 実施内容とチケットが一致しているか、オペレーターに確認を促します(SPEC-234:セッションがない場合は状態の低下を記録します)
- 引き継ぎの概要 — セッションの概要を SESSION-ROADMAP.md に追記します
ふりかえりについて: SPEC レベルのふりかえりは、現在、完了パイプラインの一部として実行されます(ステップ 3/swain-do ステップ 2d)。EPIC レベルのふりかえりは、swain-session の close ハンドラーによって実行されます。終了処理では、パイプラインのふりかえりステップが実施されなかった場合に限り、補完としてふりかえりを実行します。
ステップ 0 — 現在のロックファイルの要求を解除する(SPEC-247)
現在のセッションがアクティブなロックファイルのあるワークツリーで実行されている場合は、その要求を解除します。これを最初に実行することで、後続の孤立ワークツリー検出で現在のワークツリーがアクティブとして誤って検出されるのを防ぎます。
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
LOCKFILE_SCRIPT="$REPO_ROOT/.agents/bin/swain-lockfile.sh"
GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null)
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
if [ "$GIT_COMMON" != "$GIT_DIR" ] && [ -f "$LOCKFILE_SCRIPT" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
bash "$LOCKFILE_SCRIPT" release "$BRANCH" 2>/dev/null || true
fi
ステップ 1 — 孤立ワークツリーのチェック(SPEC-233、SPEC-247)
このステップでは、session.json の worktrees 配列からワークツリーのブックマークを読み取り、ロックファイルの状態と照合します(SPEC-247)。孤立ワークツリーは、安全チェックを行ったうえで、オペレーターの確認後に削除する候補として提示されます。このステップは --session-chain の有無にかかわらず、必ず実行されます。
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SESSION_FILE="$REPO_ROOT/.agents/session.json"
LOCKFILE_SCRIPT="$REPO_ROOT/.agents/bin/swain-lockfile.sh"
# Build a list of bookmarked worktree paths from session.json
bookmarked_wts=$(jq -r '(.worktrees // [])[] | .path' "$SESSION_FILE" 2>/dev/null)
orphan_candidates=()
orphan_results=()
ready_candidates=()
# Get all physical worktrees
git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
wt_path="${line#worktree }"
wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"
# Skip trunk (protected)
if [ "$wt_path" = "$REPO_ROOT" ]; then
echo "protected: $wt_path (trunk)"
continue
fi
# Check lockfile state (SPEC-247)
if [ -f "$LOCKFILE_SCRIPT" ]; then
lock_branch="${wt_branch##*/}" # strip refs/heads/ prefix
lockfile="$REPO_ROOT/.agents/worktrees/${lock_branch}.lock"
if [ -f "$lockfile" ]; then
# Check if ready_for_cleanup — safe to remove without confirmation
if grep -q 'ready_for_cleanup=true' "$lockfile" 2>/dev/null; then
echo "ready: $wt_path ($wt_branch) — marked ready_for_cleanup"
ready_candidates+=("$wt_path|$wt_branch|$lockfile")
continue
fi
# Check if actively claimed by another session
if ! bash "$LOCKFILE_SCRIPT" is-stale "$lock_branch" >/dev/null 2>&1; then
echo "claimed: $wt_path ($wt_branch) — active lockfile, skip"
continue
fi
fi
fi
# Check if bookmarked in session.json
is_orphan=true
reason=""
if echo "$bookmarked_wts" | grep -qF "$wt_path"; then
is_orphan=false
echo "linked: $wt_path ($wt_branch)"
fi
if [ "$is_orphan" = true ]; then
# Safety checks per SPEC-233 AC1-AC3
if [ "$wt_path" = "$(pwd)" ]; then
reason="current directory"
elif [ -n "$(git -C "$wt_path" status --porcelain 2>/dev/null)" ]; then
reason="uncommitted changes"
elif ! git merge-base --is-ancestor "$wt_branch" trunk 2>/dev/null; then
reason="branch not fully merged"
else
reason="safe to remove"
orphan_candidates+=("$wt_path|$wt_branch")
fi
echo "orphan: $wt_path ($wt_branch) — $reason"
orphan_results+=("$wt_path|$wt_branch|$reason")
fi
done
孤立ワークツリーが見つかった場合は、「次のワークツリーにはアクティブなセッションブックマークがありません。削除を検討してください。」と表示します。
ready_for_cleanup のワークツリーを自動削除する(SPEC-247)
ready_for_cleanup とマークされたワークツリーはマージとプッシュが完了しているため、オペレーターの確認なしで安全に削除できます。最初に session.json をアーカイブします(SPEC-248)。
ARCHIVE_SCRIPT="$REPO_ROOT/.agents/bin/swain-session-archive.sh"
for candidate in "${ready_candidates[@]}"; do
IFS='|' read -r wt_path wt_branch lockfile <<< "$candidate"
# Archive session.json before deletion
if [ -f "$ARCHIVE_SCRIPT" ]; then
bash "$ARCHIVE_SCRIPT" save "$wt_path" 2>/dev/null || true
fi
if git worktree remove "$wt_path" 2>/dev/null; then
rm -f "$lockfile" # Clean up lockfile (SPEC-247 AC3)
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path" 2>/dev/null || true
echo "Auto-removed (ready_for_cleanup): $wt_path"
fi
done
安全な孤立ワークツリーをオペレーターに確認する
reason="safe to remove" の孤立ワークツリーのみを削除候補として提示します。安全な孤立ワークツリーごとに、次の操作を行います。
removed_worktrees=()
for candidate in "${orphan_candidates[@]}"; do
wt_path="${candidate%%|*}"
wt_branch="${candidate##*|}"
echo "> Remove orphan worktree $wt_path ($wt_branch)? [y/N]"
read -r response
if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
# Archive session.json before deletion (SPEC-248)
if [ -f "$ARCHIVE_SCRIPT" ]; then
bash "$ARCHIVE_SCRIPT" save "$wt_path" 2>/dev/null || true
fi
if git worktree remove "$wt_path" 2>/dev/null; then
removed_worktrees+=("$wt_path|$wt_branch")
# Clean up lockfile if present (SPEC-247 AC3)
lock_branch="${wt_branch##*/}"
rm -f "$REPO_ROOT/.agents/worktrees/${lock_branch}.lock" 2>/dev/null || true
# Clear the bookmark from session.json
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path"
echo "Removed: $wt_path"
else
echo "ERROR: Failed to remove $wt_path — skipping bookmark cleanup"
fi
fi
done
安全規則(SPEC-233 AC1-AC3、AC7)
- 現在のディレクトリ: 削除候補として提示しません。
reason="current directory"としてフラグを設定します。 - 未コミットの変更: 削除候補として提示しません。
reason="uncommitted changes"としてフラグを設定します。 - 未マージのブランチ: 削除候補として提示しません。
reason="branch not fully merged"としてフラグを設定します。 - トランク: 削除候補として提示しません。必ず
protected: <repo_root> (trunk)と表示します。 - 確認が必須: 必ずオペレーターに確認します。自動削除はしません。
ステップ 2 — Git のダーティ状態チェック
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$REPO_ROOT"
git_status=$(git status --porcelain 2>/dev/null)
if [ -n "$git_status" ]; then
echo "WARN: Dirty git working tree — uncommitted changes detected."
echo "Changes:"
echo "$git_status" | head -20
else
echo "OK: Working tree is clean."
fi
確認結果と、実行可能な推奨事項を報告します。
ステップ 3 — 完了パイプラインのチェック(SPEC-258)
チケット同期に進む前に、このワークツリーで完了した作業について、完了パイプラインが実行されたことを確認します。未実施のステップがあれば、ここで実行します。
3a — パイプラインの状態を検出する
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
STATE_FILE="$REPO_ROOT/.agents/completion-state.json"
SWAIN_TEST="$REPO_ROOT/.agents/bin/swain-test.sh"
completion-state.json が存在する場合: 内容を読み取り、未完了のステップを確認します。
PENDING_STEPS=$(jq -r '.steps | to_entries[] | select(.value.status == "pending" or .value.status == "failed") | .key' "$STATE_FILE" 2>/dev/null)
SPEC_ID=$(jq -r '.spec_id // empty' "$STATE_FILE" 2>/dev/null)
PENDING_STEPSが空の場合 → すべてのステップが成功したか、スキップされています。「完了パイプライン:すべてのステップが完了しました。」と表示して続行します。PENDING_STEPSが空でない場合 → 対応が必要なステップを表示して実行します(3b を参照)。
completion-state.json が存在しない場合: ワークツリーに完了済みタスクがあるか確認します。
export PATH="$REPO_ROOT/.agents/bin:$PATH"
CLOSED_COUNT=$(ticket-query '.status == "closed"' 2>/dev/null | wc -l | tr -d ' ')
CLOSED_COUNT > 0の場合 → 作業は行われましたが、パイプラインは開始されていません。すべてのステップをpendingに設定して状態ファイルを作成し、実行します。# Try to find the SPEC ID from the plan epic EPIC_ID=$(ticket-query '.type == "epic"' 2>/dev/null | head -1 | jq -r '.id // empty') SPEC_ID=$(tk show "$EPIC_ID" 2>/dev/null | grep -i 'external_ref' | awk '{print $NF}') mkdir -p "$REPO_ROOT/.agents" jq -n --arg spec "${SPEC_ID:-unknown}" --arg branch "$(git branch --show-current)" \ '{spec_id: $spec, branch: $branch, pipeline_started: (now | todate), steps: {bdd_tests: {status: "pending", timestamp: null, detail: null}, smoke_test: {status: "pending", timestamp: null, detail: null}, retro: {status: "pending", timestamp: null, detail: null}}}' \ > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"CLOSED_COUNT == 0の場合 → 完了した作業はありません。このステップを何も表示せずにスキップします。
3b — 不足しているステップを実行する
パイプラインの順序(bdd_tests → smoke_test → retro)に沿って、各ステップの状態を確認します。passed または skipped のステップはスキップします。pending または failed のステップを実行します。
bdd_tests:
BDD_STATUS=$(jq -r '.steps.bdd_tests.status' "$STATE_FILE")
BDD_STATUS が pending または failed の場合:
-
swain-test.shが存在する場合:BDD_OUTPUT=$(bash "$SWAIN_TEST" --artifacts "$SPEC_ID" 2>&1) BDD_EXIT=$?- 終了コード 0 → 出力の先頭 5 行を詳細として
passedに更新します。 - 0 以外 → 末尾 10 行を詳細として
failedに更新します。次のように尋ねます:"BDD failed. retry, skip, or abort?"- retry → このステップを再実行します
- skip → 詳細を "operator skipped during teardown" として
skippedに設定します - abort → ティアダウン全体を停止し、同期に進みません
- 終了コード 0 → 出力の先頭 5 行を詳細として
-
swain-test.shが存在しない場合: 詳細を "swain-test.sh not available" としてskippedに設定します。警告を表示します。
すべての状態書き込みには、DESIGN-018 のアトミックな jq 更新パターンを使用します:
jq --arg step "$STEP" --arg status "$STATUS" --arg detail "$DETAIL" \
'.steps[$step].status = $status | .steps[$step].timestamp = (now | todate) | .steps[$step].detail = $detail' \
"$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
smoke_test:
pending または failed の場合:
-
swain-test.shが存在する場合: swain-test の出力から## SMOKEセクションを抽出し、オペレーターに提示します。次のように尋ねます:"Did the smoke test pass? (yes / no / skip)"- yes → 詳細を "operator confirmed during teardown" として
passedに設定します - no →
failedに設定します。次のように尋ねます:"retry, skip, or abort?" - skip → 詳細を "operator skipped during teardown" として
skippedに設定します
- yes → 詳細を "operator confirmed during teardown" として
-
swain-test.shが存在しない場合: 詳細を "swain-test.sh not available" としてskippedに設定します。
retro:
pending または failed の場合:
Skill ツールを使用して swain-retro を呼び出します:
Skill ツールを使用します:
swain-retroを次の args で呼び出します:"Teardown catch-up — run retro for before sync."
- 成功時 → 詳細を "retro captured during teardown" として
passedに設定します - 失敗時 → 次のように尋ねます:"Retro failed. retry or abort?"(retro はスキップできません)
3c — 強制バイパス
オペレーターが --force を付けてティアダウンを呼び出した場合(呼び出し引数に "force" または "--force" が含まれていることで判定):
# Mark all pending steps as skipped
for step in bdd_tests smoke_test retro; do
STEP_STATUS=$(jq -r ".steps.$step.status" "$STATE_FILE")
if [ "$STEP_STATUS" = "pending" ] || [ "$STEP_STATUS" = "failed" ]; then
jq --arg step "$step" \
'.steps[$step].status = "skipped" | .steps[$step].timestamp = (now | todate) | .steps[$step].detail = "operator forced bypass"' \
"$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
fi
done
次のように表示します:"Force bypass — all pending pipeline steps marked as skipped." ステップ 4 に進みます。
3d — パイプラインゲート
すべてのステップを実行またはスキップした後、次を確認します:
REMAINING=$(jq -r '.steps | to_entries[] | select(.value.status == "pending" or .value.status == "failed") | .key' "$STATE_FILE")
- 空の場合 → ステップ 4 に進みます
- 空でない場合 → ティアダウンはブロックされています。残っているステップを表示して停止します。
ステップ 4 — チケット同期の確認(SPEC-234 AC3、ステップ 3 から番号を変更)
このステップは必ず実行します。アクティブなセッションがない場合は、状態が不完全であることを記録します:
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
session_check=$(bash "$REPO_ROOT/.agents/bin/swain-session-check.sh" 2>/dev/null)
session_status=$(echo "$session_check" | jq -r '.status // "none"')
session_status が "active" でない場合:
--session-chainが渡されている場合は、そのまま続行します(呼び出し元がすでに確認済みです)。- それ以外の場合は、オペレーターに次のように伝えます:"No active session. Run
/swain-initto start one, or proceed with cleanup only?" 続行する場合は、ステップ 1~2(ダイジェスト/retro。セッション状態が必要です)をスキップし、ステップ 3(マージ)に進みます。
ステップ 1 — セッションダイジェスト
セッションがまだアクティブなうちに、セッションダイジェストを生成します。
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SESSION_ID="$(jq -r '.session_id // empty' "$REPO_ROOT/.agents/session-state.json" 2>/dev/null)"
if [ -n "$SESSION_ID" ]; then
bash "$REPO_ROOT/.agents/bin/swain-session-digest.sh" \
--session-id "$SESSION_ID" \
--output "$REPO_ROOT/.agents/session-log.jsonl" 2>/dev/null
fi
ダイジェストスクリプトが見つからないか、実行に失敗した場合は、警告を記録して続行します。ダイジェストは有用ですが、必須ではありません。
ステップ 2 — Retro(セッションはアクティブのまま)
重要: retro の実行時には、セッション状態を読み取れるよう、セッションがまだアクティブである必要があります。このステップの前にセッションを閉じないでください。
swain-retro を呼び出します:
Skill("swain-retro", "Session is closing. Run retro to capture learnings before session state is cleared.")
swain-retro が利用できない場合は、"Retro skill not found — skipping" を記録して続行します。スキルが見つからないことを理由に、シャットダウンシーケンスを失敗させてはいけません。
ステップ 3 — ワークツリーのブランチをマージする
trunk を除く各アクティブなワークツリーについて、そのブランチを trunk にマージするかどうかを確認します。これは以前のティアダウンで欠けていたステップです。これがなければブランチがマージされないため、ワークツリーを「安全に削除できる」状態にできません。
3.1 — ワークツリーを列挙する
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")
git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
wt_path="${line#worktree }"
# Skip trunk
[ "$wt_path" = "$REPO_ROOT" ] && continue
wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"
echo "$wt_path|$wt_branch"
done
3.2 — 各ワークツリーをトリアージする
各ワークツリーの状態を分類します。
| 状態 | 条件 | アクション |
|---|---|---|
| すでにマージ済み | git merge-base --is-ancestor "$wt_branch" "$TRUNK" | マージをスキップし、安全にクリーンアップできるものとしてマーク |
| クリーン、未マージ | 未コミットの変更がなく、ブランチが未マージ | マージを提案 |
| 変更あり | git -C "$wt_path" status --porcelain に出力がある | オペレーターに警告し、先にコミットするよう提案 |
| 現在のディレクトリ | "$wt_path" = "$(pwd)" | 先に ExitWorktree で退出する必要があります |
3.3 — 未マージのブランチをマージする
各「クリーン、未マージ」のワークツリーについて、オペレーターに確認します。
ワークツリー
{path}(ブランチ{branch})には未マージの変更があります。次のオプションがあります。
- マージ — 今すぐブランチを {trunk} にマージする
- PR — ブランチをプッシュしてプルリクエストを作成する
- スキップ — ブランチをそのまま残す(ワークツリーは保持されます)
マージする場合:
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")
# Link safety — scan for worktree-specific path links before merging
DETECT_SCRIPT="$REPO_ROOT/.agents/bin/detect-worktree-links.sh"
RESOLVE_SCRIPT="$REPO_ROOT/.agents/bin/resolve-worktree-links.sh"
if [ -x "$DETECT_SCRIPT" ]; then
MERGE_BASE=$(git -C "$wt_path" merge-base HEAD "origin/$TRUNK" 2>/dev/null || true)
CHANGED_FILES=$([ -n "$MERGE_BASE" ] && git -C "$wt_path" diff --name-only "$MERGE_BASE" HEAD 2>/dev/null || true)
if [ -n "$CHANGED_FILES" ]; then
if ! echo "$CHANGED_FILES" | xargs "$DETECT_SCRIPT" --repo-root "$REPO_ROOT" > /dev/null 2>&1; then
echo "[link-safety] Found suspicious links in $wt_branch. Resolving..."
echo "$CHANGED_FILES" | xargs "$RESOLVE_SCRIPT" --repo-root "$REPO_ROOT" 2>/dev/null || true
fi
fi
fi
# Merge into trunk from the main worktree
cd "$REPO_ROOT"
git fetch origin 2>/dev/null
git merge "$wt_branch" --no-edit
マージで競合が発生した場合は、オペレーターに報告し、このワークツリーをスキップします。強制的に競合を解決してはいけません。
PR の場合:
cd "$wt_path"
git push -u origin "$wt_branch" 2>/dev/null
SUBJECT=$(git log -1 --pretty=format:'%s')
gh pr create --base "$TRUNK" --head "$wt_branch" --title "$SUBJECT" --body "Teardown PR — branch from session teardown."
PR の URL を報告します。
スキップする場合: ワークツリーを保持対象としてマークします。ステップ 4 では削除されません。
3.4 — 「現在のディレクトリ」のワークツリーを処理する
オペレーターが現在ワークツリー内にいる場合は、最後に処理します。他のすべてのワークツリーを処理した後:
- このブランチもマージするか確認します(3.3 と同じオプション)。
- マージまたは PR の場合は、未コミットの変更を先にコミットしてから、
ExitWorktreeを呼び出して trunk に戻ります。 - スキップする場合は、
action: "keep"を指定してExitWorktreeを呼び出し、ブランチを保持します。
ステップ 4 — ワークツリーのクリーンアップ
マージ後、ブランチがマージ済みになったワークツリーをクリーンアップします。
4.1 — 再列挙してマージ状態を確認する
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")
git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
wt_path="${line#worktree }"
[ "$wt_path" = "$REPO_ROOT" ] && continue
wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"
# Safety checks
if [ "$wt_path" = "$(pwd)" ]; then
echo "SKIP: $wt_path — current directory"
continue
fi
if [ -n "$(git -C "$wt_path" status --porcelain 2>/dev/null)" ]; then
echo "SKIP: $wt_path — uncommitted changes"
continue
fi
if ! git merge-base --is-ancestor "$wt_branch" "$TRUNK" 2>/dev/null; then
echo "SKIP: $wt_path — branch not merged"
continue
fi
echo "SAFE: $wt_path|$wt_branch"
done
4.2 — 安全なワークツリーを削除する
SAFE な各ワークツリーについて、確認を求めずに削除します。ブランチはマージ済みで、未コミットの変更はなく、現在のディレクトリでもありません。オペレーターはステップ 3 ですでにマージを承認しています。
git worktree remove "$wt_path" 2>/dev/null && echo "Removed: $wt_path ($wt_branch)"
# Clear bookmark
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path" 2>/dev/null
削除に失敗した場合は報告し、続行します。
4.3 — 古いワークツリー参照を整理する
git worktree prune 2>/dev/null
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree prune 2>/dev/null
ステップ 5 — セッション状態を閉じる
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
WALKAWAY_NOTE="${WALKAWAY:-Session closed via teardown}"
bash "$REPO_ROOT/.agents/bin/swain-session-state.sh" close \
--walkaway "$WALKAWAY_NOTE" \
--session-roadmap "$(pwd)/SESSION-ROADMAP.md" 2>/dev/null
ウォークアウェイノートには、達成した内容をまとめます。セッションでの作業(マージ済みブランチ、完了したタスク、レトロの出力)から推測するか、オペレーターに一言でまとめてもらいます。
ステップ 6 — 引き継ぎをコミットする
終了処理中に変更されたセッション成果物をステージングしてコミットします。
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
# Stage session artifacts
git add SESSION-ROADMAP.md 2>/dev/null
git add docs/swain-retro/ 2>/dev/null
git add .agents/session-state.json 2>/dev/null
git add .agents/session-log.jsonl 2>/dev/null
# Check if there's anything to commit
# Resolve model name from the system prompt. When subagents ran under a
# different model, include one Co-Authored-By trailer per model (see
# AGENTS.md Model attribution).
if [ -n "$(git diff --cached --name-only 2>/dev/null)" ]; then
git commit -m "chore: session teardown handoff
Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"
fi
概要レポート
すべてのステップの完了後、概要を表示します。
=== Session Teardown Complete ===
Retro: {fired / skipped / not available}
Branches: {N merged, M PRs created, K preserved}
Worktrees: {N removed, M preserved}
Session state: closed
Handoff commit: {hash or "nothing to commit"}
重大なエラーが発生しない限り、コード 0 で終了します。
エラー処理
| 状況 | 対応 |
|---|---|
| アクティブなセッションがない(--session-chain を指定していない場合) | クリーンアップのみのモード(ステップ 3~4)を提案し、ダイジェスト/レトロをスキップ |
| ダイジェストスクリプトが見つからない | 警告して続行 |
| レトロスキルが見つからない | 警告して続行 |
| マージ競合 | 競合しているファイルを報告し、そのワークツリーをスキップ |
| ワークツリーの削除に失敗 | 報告して続行 |
| セッション状態スクリプトが見つからない | 警告して終了処理をスキップ |
| ステップ 6 でコミットするものがない | 「クリーン」と報告して終了 |
| Git を利用できない | 致命的エラー — 続行できません |
統合
swain-init からの呼び出し(意思決定予算)
セッション中に意思決定予算に達すると、swain-init は終了処理を呼び出せます。
Skill("swain-teardown", "Decision budget reached. Run teardown sequence.")
meta-router からの呼び出し
swain meta-router は、「done」、「wrap up」、「close session」、「teardown」をこのスキルにルーティングします。
単独での呼び出し
オペレーターはいつでも /swain-teardown を実行できます。アクティブなセッションがない場合は、クリーンアップのみのモードが提示されます。