Skip to content

feat: tmux セッション整理コマンド tmux-first / tmux-clean を base イメージへ追加 - #99

Merged
takemi-ohama merged 8 commits into
mainfrom
feat/tmux-first
Aug 19, 2026
Merged

feat: tmux セッション整理コマンド tmux-first / tmux-clean を base イメージへ追加#99
takemi-ohama merged 8 commits into
mainfrom
feat/tmux-first

Conversation

@takemi-ohama

@takemi-ohamatakemi-ohama commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

概要

VS Code のウィンドウが異常終了 (クラッシュ / ホスト再起動 / 接続断) すると、VS Code サーバー側に pty が取り残され、tmux クライアントだけがセッションへ繋がったまま残ります。統合ターミナルの起動スクリプトはそのセッションを「使用中」と判定するため、<repo>-1 に戻れず <repo>-2, <repo>-3 … と新しいセッションが増え続けます。

これに対処する 2 つのコマンドを base イメージへ追加します。

コマンド役割エイリアス
tmux-first居座りクライアントを切断し、一番若い番号のセッションへ現在の端末を戻すtmux1
tmux-clean置き去りになったセッションを削除するtmuxc

関連 Issue

  • 該当なし

変更点

  • containers/base/Dockerfile: tmux-first / tmux-clean/usr/local/bin へ配置し、tmux1 / tmuxc エイリアスを ~/.bashrc へ追加
  • docs/user/environment-variables.md: 「tmux / screen 経由で使う場合」に対処手順を追記
  • CHANGELOG.md: [Unreleased] / Added に追記

tmux-first

項目内容
ベース名引数 > 現在のセッション名から末尾の -数字 を除去 > カレントディレクトリ名
対象<ベース名>-<数字> を数値昇順に並べた最小のもの
切断対象セッションに繋がっている「自分以外の」クライアント
切り替えtmux 内なら switch-client、tmux 外なら attach-session
  • ベース名の前方一致比較を awk で行うため、carmo-system-console-2 のようにハイフンを含む名前でも正しく分解します
  • 数値順ソートのため -10 より -1 が優先されます
  • 自分自身のクライアントは切断対象から除外し、特定できない場合は切断をスキップします
  • -n で切り替えずに対象と切断予定クライアントを確認できます

tmux-clean

既定では安全側に倒し、次のセッションは残します。

残す条件理由
keepertmux 内なら現在のセッション、tmux 外なら最小番号
アタッチ中誰かが使用中とみなす
シェル以外を実行中ビルドやサーバを実行中とみなす
  • -n で削除対象と除外理由を確認できます
  • -f で上記も含めてすべて削除します

動作確認

  • ローカルで ./bin/devbase --help が正常に動作する
  • 影響を受けるコマンドの主要シナリオを手動で確認した
  • 必要に応じてドキュメント (docs/, README.md) を更新した
  • CI が green である

1. スクリプト単体(tmux セッションと居座りクライアントを再現)

本番と同じ形(adminer-1 に居座りクライアント、adminer-2 に自分のクライアント)を作り、tmux-firsttmux-clean の順に実行:

before: /dev/pts/4 -> adminer-1 /dev/pts/5 -> adminer-2
log: tmux-first: 居座りクライアントを切断: /dev/pts/4 -> adminer-1
tmux-first: adminer-1 へ切り替えます
tmux-clean: base=adminer keeper=adminer-2
keep adminer-1 (アタッチ中: クライアント 1 個)
keep adminer-2 (keeper)
KILL adminer-3
after: /dev/pts/5 -> adminer-1

あわせて次を確認:

  • 多段ハイフンのベース名推定 (carmo-system-console-3 -> carmo-system-console)
  • 数値順ソート (-10 より -1 を優先)
  • すでに最若番の場合は切り替えず すでに <session> です を表示
  • 一致するセッションが無い場合はエラー終了 (exit 1) し既存セッション一覧を表示
  • tmux-clean の除外判定(keeper / アタッチ中 / 実行中の 3 種すべて)と -f での上書き
  • sh -n / bash -n の構文チェック

2. イメージへの組み込み(devbase-base:latest を親に、本 PR の Dockerfile ブロックをそのまま実ビルド)

V_USER = ubuntu ← USER 切り替え不要
V_FIRST = /usr/local/bin/tmux-first ← PATH で解決 (= 実行権限あり)
V_CLEAN = /usr/local/bin/tmux-clean ← PATH で解決 (= 実行権限あり)
V_ALIAS = alias tmux1='tmux-first' alias tmuxc='tmux-clean' ← ログイン+対話 bash で有効

補足

  • COPY --chmod とヒアドキュメントは BuildKit 専用ですが、既存の COPY --chmod=755 entrypoint.sh が同じ前提のため追加要件はありません
  • tmux は base の apt 行で導入済みのため、パッケージ追加はありません
  • COPYUSER に関係なく root 権限で書き込むため USER の切り替えは不要で、実行ユーザーは ubuntu のままです
  • tmux-clean の実装上の注意として、tmux の display-message -t は target-session ではなく target-pane を取り、また書式内のタブは _ に置換されるため、アタッチ数の取得には list-sessions -f のフィルタを使っています

🤖 Generated with Claude Code

https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK

takemi-ohamaand others added 2 commits August 19, 2026 10:51
VS Code のウィンドウが異常終了 (クラッシュ / ホスト再起動 / 接続断) すると、
VS Code サーバー側に pty が取り残され、tmux クライアントだけがセッションへ
繋がったまま残る。統合ターミナルの起動スクリプトはそのセッションを「使用中」と
判定するため、<repo>-1 に戻れず <repo>-2, <repo>-3 ... と新しいセッションが
増え続ける。
tmux-first は対象セッションに居座っている自分以外のクライアントを切断し、
一番若い番号のセッションへ現在の端末を切り替える。ベース名は現在のセッション名
から末尾の -数字 を除いて推定し、対象は数字の昇順で最小のものを選ぶ。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK
tmux-first で若い番号のセッションへ戻したあと、置き去りになった
<repo>-<数字> のセッションを削除するコマンドを追加する。
既定では安全側に倒し、keeper (tmux 内なら現在のセッション、外なら最小
番号)、アタッチ中のセッション、シェル以外を実行中のセッションは残す。
-n で削除対象と除外理由を確認でき、-f ですべて削除する。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK
@takemi-ohamatakemi-ohama changed the title feat: 若い番号の tmux セッションへ戻す tmux-first を base イメージへ追加feat: tmux セッション整理コマンド tmux-first / tmux-clean を base イメージへ追加Aug 19, 2026
Dockerfile 内にシェルスクリプトをベタ書きすると差分が読みづらく、
シンタックスハイライトや shellcheck も効かないため、
containers/base/tmux-first, tmux-clean として切り出して COPY する
(entrypoint.sh / dind と同じ方式)。スクリプト本文に変更はない。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | codex | REQUEST_CHANGES

既定動作で正常なクライアントまたはバックグラウンド処理を終了し得るため、安全な判定または明示的な破壊操作への分離が必要です。

Comment threadcontainers/base/tmux-first Outdated
Comment threadcontainers/base/tmux-clean Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | gemini | APPROVE

シェルスクリプトの堅牢性(文字列比較の安全性、パイプラインのエラーハンドリング、エッジケースへの配慮)や仕様の安全性(keeper の選定、dry-run の実装)が十分に担保されており、懸念点はありません。

レビュー指摘 (codex round 1) への対応。
- tmux-first: 対象セッションに接続しているというだけで切断せず、最終操作から
TMUX_FIRST_IDLE 秒 (既定 300) 以上経過したクライアントだけを居座りとみなす。
操作中のクライアントは残し、-f を付けたときのみ全件切断する。
dry-run では切断対象に加えて残すクライアントと経過秒数も表示する。
- tmux-clean: pane_current_command は foreground しか見えないため、
pane のシェル (pane_pid) に子プロセスが残っていれば実行中とみなして残す。
/proc/<pid>/task/*/children を優先し、無い環境では pgrep -P にフォールバック。
- CHANGELOG / docs へ上記の既定動作を反映。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

🔧 cross-review fix | round 1

対応件数: 2 件 (修正 2 / deferred 0 / rejected 0)

重要度件数内容
critical0-
major2tmux-first の無条件 detach / tmux-clean のバックグラウンドジョブ見落とし
minor0-
nit0-

修正内容

  1. tmux-first: 接続中だけを根拠に切断しない (containers/base/tmux-first)
    client_activity を見て、最終操作から TMUX_FIRST_IDLE 秒 (既定 300) 以上経過したクライアントだけを居座りとみなすように変更。操作中のクライアントは残し、経過秒数付きで警告表示します。全件切断は -f/--force に分離。dry-run に keep : 行を追加。

  2. tmux-clean: バックグラウンドジョブを検出 (containers/base/tmux-clean)
    pane_current_command は foreground しか示さないため、pane のシェル (pane_pid) に子プロセスが残っていれば「実行中」として通常モードでは削除しないように変更。/proc/<pid>/task/*/children を優先し、無い環境では pgrep -P にフォールバック。

あわせて CHANGELOG / docs/user/environment-variables.md の記述を新しい既定動作へ更新しました。

deferred / rejected

なし。

検証

  • sh -n containers/base/tmux-first / sh -n containers/base/tmux-clean : OK

  • ローカル tmux サーバーでの動作確認

    • sleep 600 & を実行中のセッションが tmux-clean の dry-run で keep される
    • 操作直後のクライアントは tmux-first 既定で keep、TMUX_FIRST_IDLE=0 / -f のときのみ detach
  • commit: 57965b9

  • CI: 修正前スナップショットは全 5 チェック SUCCESS (Python syntax check 3.10/3.11/3.12, ShellCheck, Ruff lint)。再実行の完了は待っていません。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | codex | REQUEST_CHANGES

TMUX_FIRST_IDLE の不正値で -f 相当の切断が起きないよう、破壊操作前の入力検証が必要です。

Comment threadcontainers/base/tmux-first Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | gemini | COMMENT

セッションやプロセスの依存関係を丁寧に辿り、使用中の端末やバックグラウンドジョブを保護する堅牢な実装でした。シェルスクリプトのベストプラクティス(set -eu の活用、安全な区切り文字の選択、/procpgrep の併用など)が適切に適用されており、非常に高品質です。1 点だけ、実行中の並行終了によるスクリプトの中断を防ぐための軽微な修正を提案します。

Comment threadcontainers/base/tmux-clean Outdated
- tmux-first: TMUX_FIRST_IDLE に負数や数値以外が入ると全クライアントが idle 条件を
満たし、-f なしで操作中の端末まで切断されるため、引数解釈後に 0 以上の整数か検証し
不正値では exit 2 で終了する
- tmux-clean: kill-session の直前にセッションが終了していた場合の競合エラーで set -e
により後続の削除処理が止まらないよう || true を付与
- CHANGELOG / docs/user/environment-variables.md に TMUX_FIRST_IDLE の制約を追記
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

cross-review round 2 — 修正サマリ

修正コミット: 83b1d73

#ファイル:行指摘(元)再判定対応
1containers/base/tmux-first:29TMUX_FIRST_IDLE が負数だと全クライアントが idle 条件を満たし -f なしで操作中の端末を切断major(据え置き)修正済み
2containers/base/tmux-clean:174kill-session の競合失敗で set -e により後続処理が中断minor(据え置き)修正済み

1. TMUX_FIRST_IDLE の入力検証 (major)

IDLE_MIN=${TMUX_FIRST_IDLE:-300} は検証なしで [ "$idle" -ge "$IDLE_MIN" ] に渡されていました。負数なら全クライアントが切断対象になり、round 1 で入れた安全策そのものを無効化します。数値以外なら set -e の下で test の構文エラー終了になります。

オプション解釈の直後に 0 以上の整数かを検証し、不正値では exit 2 するようにしました。-h/--help は不正値でも従来どおり表示できるよう、検証は引数解釈の後に置いています。

2. kill-session の競合エラー (minor)

セッション一覧の取得から kill-session までの間に対象が自然終了する競合は現実に起こり得ます。tmux-firstdetach-client と揃えて || true を付与しました。

ドキュメント整合

CHANGELOG.mddocs/user/environment-variables.mdTMUX_FIRST_IDLE は「0 以上の整数のみ有効、それ以外はエラー終了」を追記しました。

検証

  • sh -n containers/base/tmux-first / sh -n containers/base/tmux-clean — OK
  • TMUX_FIRST_IDLE=-5 / abc / 1.5 → exit 2、0 / 300 / 未設定 → 従来どおり継続、不正値 + -h → exit 0 で usage 表示、を実機確認
  • 本コミット前の CI は全 5 チェック SUCCESS(ShellCheck 含む)。本コミット分の CI 完了は待っていません

deferred / rejected: なし(未 Resolve の 2 件はいずれも修正のうえ Resolve 済み)

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 3 | codex | REQUEST_CHANGES

設定値検証と削除結果の報告について、2件の修正が必要です。

Comment threadcontainers/base/tmux-first Outdated
Comment threadcontainers/base/tmux-clean Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 3 | gemini | APPROVE

シェル非依存でのコマンド提供と、セッション削除競合時のログの正確性向上について、いくつかマイナーな修正提案をインラインに残しました。確認をお願いします。

Comment threadcontainers/base/Dockerfile Outdated
Comment threadcontainers/base/tmux-clean Outdated
- tmux-first: `${TMUX_FIRST_IDLE:-300}` を `${TMUX_FIRST_IDLE-300}` に変更。
明示的な空文字が既定値 300 に化けて検証を素通りしていたため、
「0 以上の整数以外はエラー終了」というドキュメントの契約と食い違っていた。
- tmux-clean: `kill-session || true` が競合以外の失敗も成功として KILL 表示・
KILLED 加算していた。削除成功時のみ加算し、対象消失は skip、それ以外の失敗は
標準エラーへ出力して失敗件数と終了ステータス 1 で通知する。
- Dockerfile: tmux1 / tmuxc を ~/.bashrc の alias ではなく /usr/local/bin の
symlink で提供する。alias は bash の対話シェルにしか効かず、zsh や
docker exec などの非対話実行では使えないため。
- CHANGELOG / docs も上記に合わせて更新。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

🔧 /ndf:fix サマリ (round 3)

対応件数: critical=0 / major=0 / minor=4 / nit=0 (合計 4 件、うち 1 件は codex/gemini の同一箇所重複指摘)
deferred: 0 件 / rejected: 0 件
commit: 732ed2d
CI (push 前スナップショット): SUCCESS — ShellCheck / Python syntax check (3.10, 3.11, 3.12) / Ruff lint

詳細

#指摘元箇所対応
1codexcontainers/base/tmux-first:32${TMUX_FIRST_IDLE:-300}${TMUX_FIRST_IDLE-300}。明示的な空文字が 300 に化けて検証を素通りしていた問題を修正
2codexcontainers/base/tmux-clean:176kill-session || true を廃止。成功時のみ KILLED 加算、対象消失は skip、それ以外の失敗は stderr + 失敗件数 + exit 1
3geminicontainers/base/tmux-clean:179#2 と同一箇所。1 回の修正で両方に対応
4geminicontainers/base/Dockerfile:179~/.bashrc の alias を /usr/local/bin の symlink (tmux1 / tmuxc) に置換。alias 行は削除済み

補足

  • docs: docs配下からprivateレジストリ参照を削除 #4 は提案どおり COPY の後に RUN ln を置きましたが、この RUNUSER ${USERNAME}(非 root)で実行され ubuntu:noble/usr/local/binroot:root 0755 のため、sudo ln -sf にしています(素の ln -s だとビルドが Permission denied で失敗します)。
  • 両スクリプトとも $0 によるディスパッチをしていないため、symlink 経由でも挙動は同一です。
  • CHANGELOG.md / docs/user/environment-variables.md の「エイリアス」記述と TMUX_FIRST_IDLE の説明も整合を取りました。

検証

  • sh -n containers/base/tmux-first containers/base/tmux-clean → OK
  • shellcheck -s sh(koalaman/shellcheck:stable)→ 指摘なし
  • TMUX_FIRST_IDLE="" / -5 → いずれも exit 2 でエラー終了、未設定時のみ既定 300
  • 実 tmux(隔離ソケット)で tmux-clean -n / -f の正常系: 削除 2 件 / 残す 1 件 / 失敗 0 件, rc=0
  • スタブ tmux で競合・失敗分岐: skip ztest-2 (既に終了していました) / セッションを削除できませんでした: ztest-3 / 削除 0 件 / 残す 1 件 / 失敗 1 件, rc=1(set -e でループが止まらないことも確認)

未 Resolve の指摘は 0 件です。@codex@gemini 再レビューをお願いします。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 4 | codex | REQUEST_CHANGES

呼び出し元クライアントの識別を安全に行えるよう修正が必要です。

Comment threadcontainers/base/tmux-first Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 4 | gemini | COMMENT

シェルスクリプトのパース処理における空白文字・メタ文字・globbing由来のエッジケースによる誤動作を防ぐため、より堅牢なパース方法への修正を提案しています。

Comment threadcontainers/base/tmux-clean Outdated
Comment threadcontainers/base/tmux-clean Outdated
Comment threadcontainers/base/tmux-first Outdated
レビュー round 4 の指摘対応。
- tmux-first: 実行元クライアントを「最終操作が SELF_FRESH(10) 秒以内」であることまで
確認できた場合のみ信頼し、確認できない場合は -f 付きでも切断せず切り替えのみ行う。
hook や send-keys 経由でキー入力を伴わずに起動された場合、従来は最終操作が最も新しい
だけの無関係なクライアントを実行元とみなし、実際の利用者の端末を切断していた。
client_name と client_activity は 1 回の display-message でまとめて取得し、
switch-client にも -c で同じクライアントを渡して判定と操作の食い違いをなくす。
- tmux-first / tmux-clean: `for e in $(tmux ...)` を while read + ヒアドキュメントに
置き換え、空白による単語分割とパス名展開 (glob) を排除。
- tmux-clean: `for s in $SESSIONS` も同様に while read へ。セッション名に空白が
含まれると別名として扱われ、削除対象を取り違えていた。
- tmux-clean: `-f "#{==:#{session_name},$s}"` はセッション名に `}` や `,` が含まれると
format 構文が壊れ、全セッションのアタッチ数を返して掃除が一切効かなくなるため、
`#{session_attached} #{session_name}` を awk で完全一致させる方式へ変更。
検証: sh -n / bash -n / dash -n、shellcheck (-s sh) クリーン。
実 tmux 3.7b (隔離ソケット) で、同一セッションへ 2 クライアント接続時の実行元保護、
別セッションからの ghost 切断 + 切り替え、非対話起動時の切断抑止、`we}ird` / `my proj`
/ `st*ar` / `com,ma` 各セッション名での keeper・アタッチ中・実行中判定を確認。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

🔧 /ndf:fix サマリ (round 4)

対応件数: critical=0 / major=1 / minor=3 (合計 4 件) — 未 Resolve の指摘 4 件すべて対応
deferred: 0 件 / rejected: 0 件 (指摘 1 は実装方法のみ変更して対応。理由はスレッド参照)
commit: 29b465c
CI (push 前スナップショット): SUCCESS (Python syntax 3.10/3.11/3.12, Ruff lint, ShellCheck)

詳細

#指摘対応
1[major] tmux-first:117 実行元 client の誤検出で自分自身を切断しうる✅ 別方式で対応
2[minor] tmux-clean:144#{==:...,$s} が名前中の } で壊れる✅ awk 完全一致へ
3[minor] tmux-clean:148for e in $(...) の単語分割 / glob✅ while read + heredoc
4[minor] tmux-first:134 同上✅ while read + heredoc

指摘 1 について

ご提案の tty × #{client_tty} 照合は採用していません。実 tmux 3.7b で検証した結果、pane 内の ttypane の pty(例 /dev/ttys013)を返し、client の tty(/dev/ttys016)とは別物のため常に不一致になり、この方式では ghost 切断機能が丸ごと無効化されるためです。

また display-message は、人がプロンプトへ打鍵して起動した場合は打鍵で client_activity が更新されるため必ず実行元自身を返すことも実測で確認しました(A/B 2 client を同一セッションへ繋ぎ A から打鍵 → CUR=A)。

実在したリスクは「キー入力を伴わない起動」(hook / send-keys / 自動化) のケースで、修正前コードで再現できました:

# 2 client を attach し 14 秒放置(全員 stale)→ send-keys で非対話起動
[修正前] 居座りクライアントを切断: /dev/ttys016 → 無関係な client を切断
[修正後] 実行元のクライアントを特定できないため切断は行いません → 両方生存

そこで 「実行元と断定するには最終操作が SELF_FRESH(10) 秒以内であること」 を要件に追加し、確認できない場合は -f 付きでも切断せず切り替えのみ行うようにしました。あわせて client_name / client_activity を 1 回の display-message でまとめて取得し、switch-client -c "$ME"判定した client と実際に切り替える client を固定しています。

ご指摘外で併せて修正

tmux-cleanfor s in $SESSIONS(メインループ・keeper 決定)も指摘 3/4 と同種の単語分割バグで、セッション名に空白が入ると破綻していました(my proj-1myproj-1 に分割され削除対象を取り違え、掃除が 0 件になる)。同様に while read へ変換しています。

検証

  • sh -n / bash -n / dash -n 全て OK
  • shellcheck -s sh(koalaman/shellcheck:stable)警告ゼロ
  • 実 tmux 3.7b(隔離ソケット -L 使用)での挙動確認
    • 同一セッションへ 2 client 接続 → 打鍵側から TMUX_FIRST_IDLE=0 tmux-first -f → 実行元生存 / 他方のみ切断
    • 別セッションから実行 → ghost 切断 + 自 client を最小番号セッションへ切り替え
    • 非対話起動(全 client stale)→ 切断せず切り替えのみ(修正前は誤切断を再現)
    • tmux-clean をセッション名 we}ird / my proj / st*ar / com,ma の 4 パターンで実行 → keeper・アタッチ中・実行中(前景コマンド / & バックグラウンドジョブ)判定・-n / -f すべて期待どおり
  • CHANGELOG.md / docs/user/environment-variables.md に「実行元を特定できない場合は切断しない」挙動を追記

@codex@gemini 再レビューをお願いします。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 5 | codex | REQUEST_CHANGES

実行元クライアントを特定できない場合の操作対象と、セッション状態を取得できない場合の削除判定を安全側に修正してください。

Comment threadcontainers/base/tmux-clean Outdated
Comment threadcontainers/base/tmux-first Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 5 | gemini | APPROVE

指摘事項はありません。

- tmux-clean: list-sessions / list-panes の失敗を `|| true` で空一覧に丸めていたため、
アタッチ数も実行中コマンドも不明なまま「アイドル」と判定して既定実行で削除していた。
取得できなかった場合は削除せず keep する (-f 指定時のみ従来どおり削除)。
走査中にセッションが消えていた場合は has-session で切り分けて skip する。
- tmux-first: ME_VERIFIED=0 のとき $ME は「直近に操作された別クライアント」を指し得るのに、
切断を止めたあとで switch-client -c "$ME" によりその別クライアントを切り替えていた。
未検証時は $ME を捨て、切断だけでなく切り替えも行わず手動コマンドを案内する。
- CHANGELOG / docs/user/environment-variables.md の記述を挙動に合わせる。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

/ndf:fix round 5 対応サマリ

修正コミット: f93c11a

round 5 の未 Resolve 指摘 2 件 (codex: REQUEST_CHANGES) をコードを読んで再判定し、いずれも major で妥当 と判断して修正しました。2 件とも round 4 (29b465c) の修正で新たに生じた論点です。gemini は APPROVE (0 件)。

#ファイル指摘再判定対応
1containers/base/tmux-clean:166list-panes の失敗を || true で空一覧にすると BUSY が空になり、実行中でも既定実行で削除されるmajor (妥当)修正 / Resolve
2containers/base/tmux-first:227ME_VERIFIED=0 でも ME が別クライアント名を保持し、switch-client -c "$ME" で他人の端末を切り替えるmajor (妥当)修正 / Resolve

1. tmux-clean を fail-closed 化

list-panes だけでなく、アタッチ数を取る list-sessions 側にも同じ fail-open (失敗時 ${ATT:-0} = 0 で「アタッチなし」扱い) があったため、両方を成否判定に変えました。

  • どちらかが失敗、または一覧に対象セッションが載っていない → STATE_OK=0
  • STATE_OK=0 のときは has-session で切り分け: 消えていれば skip、生きていて -f なしなら keep <name> (状態を取得できませんでした) + stderr 警告、-f ありなら従来どおり削除

2. tmux-first の未検証クライアント操作を停止

  • 検証できなかった場合は ME 自体を捨てる ([ "$ME_VERIFIED" = 1 ] || ME="")。以降「ME が非空 = 実行元と確定」という不変条件になります。
  • 代替動作: tmux 内で ME が空のときは切断だけでなく切り替えも行わず、手動コマンドを案内して exit 0-c を省いた switch-client も同じ「現在のクライアント」解決を使い他人の端末を掴み得るため、-c を外しても安全にならないという判断です (round 1 以降の「疑わしいときは他人の端末を触らない」方針を維持)。
  • -n の表示を self : (特定できません: 切断も切り替えも行いません) に変更。

検証

sh -n / shellcheck (koalaman/shellcheck:stable) はいずれもクリーン。加えて 実 tmux (3.7b) を隔離ソケット -L で立てて挙動確認しました。

tmux-clean (list-panes だけ失敗させるスタブ tmux 経由)

ケース結果
正常時 -nKILL base-2 / KILL base-3
list-panes 失敗 -nkeep (状態を取得できませんでした) ×2、削除対象 0 件
list-panes 失敗 実削除削除 0 件 / 残す 3 件、3 セッションとも残存
list-panes 失敗 + -f削除 2 件 (FORCE は従来どおり)
sleep 300 実行中keep base-2 (実行中: sleep) (回帰なし)

tmux-first (別 tmux サーバーの pane から本物のクライアントを base-2 へアタッチし、12 秒放置して ME_VERIFIED=0 を再現)

ケース結果
旧版 (29b465c) / 未検証「切断は行いません」と言いつつ base-1 へ切り替えます → クライアントが base-2base-1 に。指摘どおり再現
新版 / 未検証 本実行切り替えなし、クライアントは base-2 のまま、exit 0
新版 / 未検証 -nself : (特定できません: ...)detach : (なし)
新版 / 検証済み 本実行base-1 へ切り替えます → クライアント session=base-1 (正常系は回帰なし)

その他

  • CHANGELOG と docs/user/environment-variables.md の記述を新しい挙動へ更新
  • push 前の CI (ShellCheck / Ruff lint / Python syntax check ×3) は全て SUCCESS。今回のコミット分の再実行結果は待っていません
  • deferred / rejected はいずれも 0 件

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 6 | codex | APPROVE

修正が必要な指摘はありません。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 6 | gemini | APPROVE

設計・実装・ドキュメントにおいて修正を要する指摘事項はありません。

@takemi-ohama
takemi-ohama merged commit 29cbc09 into mainAug 19, 2026
5 checks passed
@takemi-ohama
takemi-ohama deleted the feat/tmux-first branch August 19, 2026 15:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@takemi-ohama