SUZ-LAB

作り込んだ翌週に捨てる — 自前の通知機構を AIエージェント特化マルチプレクサ herdr へ全面移行した記録

前回 tmux で建てた「AI 開発コックピット」の本丸は、別セッションで止まった Claude に気づくための自前の要対応通知機構だった(claude-notify.sh + status.sh + hooks 4種)。その3日後、この自前資産を丸ごと捨て、AIエージェント特化マルチプレクサ herdr のネイティブなペイン状態検出(idle/working/done/blocked)へ載せ替えた。段階移行(PoC → labh オプトイン併存 → 既定切替 → tmux 完全撤去)で可逆性を保ちながら進め、GitHub タブと専用依存(gh-view/fzf)まで削り 3タブ Claude/File/Shell に絞った。「この環境で確実に動く薄い自前」から「成熟した専用ツールに素直に乗る」へ——前作の学びを自分で更新した判断と、pre-1.0 依存に乗るための備え、そして正直に残る未検証の穴まで、実際の PR と一次情報で残します。

なぜ、翌週に捨てたか

つい先日、ターミナル 1 枚に AI 開発コックピットを建てた回を書いた。SSH でログインしたら同じ作業場に着地し、Claude を何本も並行で回しながらファイルも PR も 1 枚の tmux に畳む——という常駐環境だ。あの回の本丸は見た目の 3 ウィンドウではなく、下段に仕込んだクロスセッション通知だった。別ウィンドウにいるあいだ、裏で止まった Claude に気づけない。だから Claude Code の hooks(Notification / Stop / UserPromptSubmit / SessionEnd)を ~/.claude-notify/<session_id>.<type> というマーカーに落とし、status-right で型別に数えて赤点滅/緑定常で光らせた。フックは主役の対話を妨げないよう全経路 exit 0、SGR の blink が緑背景ごと点滅する端末癖まで回避した——そこそこ作り込んだ

その3日後に、これを丸ごと捨てた。

きっかけは、AIエージェント特化のターミナルマルチプレクサ herdr を実機で触ったことだ。herdr はペイン単位でエージェントの状態(idle / working / done / blocked)をネイティブに検出する。前回わざわざフック4種とシェル2本で組んだ「誰が入力待ちか」が、ツールの標準機能として最初から乗っている。自前で持ち続ける理由が、その瞬間に消えた。

前作で私は「成熟した汎用ツールより、この環境で確実に動く薄い自前」と書いた(gh-dash をやめて gh + fzf にした判断だ)。今回はその逆を選んでいる。矛盾ではない。判断の軸は「環境で確実に動くか」で一貫していて、ツールの成熟度が上がった分だけ答えが反転しただけだ。作った翌週でも、捨てる理由が立てば捨てる。この記録は、その載せ替えの一部始終だ。

段階移行 — 可逆性を保ったまま既定を切り替える

「作った翌週に捨てる」ことのリスクは、勢いで壊すことだ。だから移行は段階に切って、各段で戻せるように進めた。

  • Phase 0(PoC・検証) … herdr v0.7.3 を devcontainer に実機導入し、idle → working → done実遷移を自分の目で確認した。移行の本丸=自前通知機構を herdr のネイティブ状態検出で置換できることを、ここで裏取りしてから先へ進めた(検証後はいったん完全撤去)。
  • Phase 1-2(オプトイン併存) … 当初は labh という別ランチャーで herdr をオプトイン追加し、既存の lab(tmux)と併存させた。herdr の claude 連携フックは herdr ペイン内(HERDR_ENV=1)でしか動かず、tmux 配下では即 exit 0 で抜けるので、両者は安全に同居できる。「気に入らなければ lab に戻ればいい」状態を挟んだ。
  • Phase 3(既定切替+撤去) … 手応えが出たので既定を切り替え、labh を廃止して lab 一本に戻した(lab-view-herdr.shlab-view.sh にリネーム、旧 tmux 版は削除)。ここまでを PR #115 に統合した。

トリガー式に後回しを管理する——lefthook 移行の回で身につけた「TODO は期日でなく着手条件で寝かせる」を、ここでも使った。Phase 0 の検証結果が Phase 3 への go サインそのものになった。

引き算の中身 — 移行の核は「削除」だった

この移行は機能追加の物語ではない。消したものの一覧が、そのまま設計だ。

  • 自前通知機構を撤去。 claude-notify.sh / claude-notify-status.sh を削除し、.claude/settings.json の hooks 4種(Notification / Stop / UserPromptSubmit / SessionEnd)を外した。要対応状態は herdr のサイドバー(agent_panel_sort=priority)が受け持つ。前作の本丸を、そっくりツールへ返した格好だ。
  • tmux 自体を撤去(PR #116)。 当初は手動フォールバックとして apt に tmux を残していたが、続けて削除した。herdr 未導入時の縮退は「素のログインシェルに着地」へ。二重起動ガードは $TMUX$HERDR_ENV に、要求ツールは tmux/lfherdr/claude に置き換えた。これで tmux は devcontainer から完全に消えた。
  • GitHub タブと専用依存を撤去(PR #117)。 前作で「gh-dash をやめて自前で組んだ」と胸を張った gh + fzf の PR ブラウザ(gh-view.sh / gh-view.lesskey / install_fzf)を、まるごと落とした。タブは Claude / File / GitHub / Shell の 4 枚から、Claude / File / Shell の 3 枚へ。gh CLI 自体は残置しているので、PR は Claude か Shell から叩けばよく、専用タブと fzf 依存を抱える割に合わなかった。作ったばかりの自前を捨てるのは二度目だが、判断の軸は同じだ。

herdr の設定(config.toml)と連携フックは、起動時に冪等生成する。Shift + ←/→ のタブ切替は前作からそのまま持ち込んだ。

自己修復するタブ — 消えても再ログインで戻る

前作の tmux コックピットには「再アタッチのたびに最新化する自己修復」を仕込んでいた。同じ思想を herdr でも実装したが、herdr はワークスペース状態をディスクに永続するぶん、壊れ方が具体的で、対応も具体的になった(PR #119 / #120、仕様は #121 で AGENTS.md に明文化)。

想定した壊れ方は2つ。

  • 状態A:タブそのものが消えた。 Claude タブ(Agent View)が無くなっている。→ 再ログインで作り直す。
  • 状態B:タブは在るが、中身が素シェルに戻った。 ラベルは Claude なのに Agent View でなく素のシェルが居座っている。→ これも検出して中身を張り直す。

再ログインでこの A / B を一括で自己修復し、Claude タブが消えても復活するようにした。「配置は永続する」ことは再接続で状態が飛ばない利点だが、裏返せば壊れた配置も永続してしまう。だから「毎回作り直す」ではなく「在るべき姿と照合して、欠けているものだけ張り直す」に寄せる必要があった。

稼働中インスタンスへ設定を波及させる

「設定は起動時に冪等生成」だけでは足りない場面が、移行後に出た。すでに立ち上がっているインスタンスに設定変更が届かないのだ。lf の設定を書き換えても、稼働中の lf は古いまま動き続ける。そこで lf -remote source で稼働中インスタンスへ設定を送り込み、起動時生成と稼働中反映の両方を押さえた(PR #122)。あわせて lf の配色の precedence について、ドキュメントの誤記(colors ファイルが最優先で shadowing は無い、が正)を訂正した(PR #123)。地味だが、「設定した=反映された」ではない、を踏んだ記録として残す。

pre-1.0 のツールに乗るということ

herdr は魅力的だが、pre-1.0・単独メンテ・AGPL だ。母艦の日常運用をここに預ける以上、いくつか腹をくくった。

  • バージョンを固定する。 pre-1.0 は設定 API を破壊的に変えうる。install_herdr(post-create)で HERDR_VERSION=0.7.3 にピンし、~/.local/bin へ冪等導入する。「勝手に上がって壊れる」を封じるのは、pre-1.0 に乗る最低条件だ。
  • 状態がディスクに永続することを理解して使う。 herdr はワークスペースのレイアウトをディスクに残す。server stop してもレイアウトは消えない。便利さと表裏の性質で、前述の自己修復もこの前提の上に成り立っている。
なぜ「薄い自前」を捨てて pre-1.0 に乗れたのか

前作の gh-dash 判断(成熟した汎用ツールを、環境のトークン権限問題で落とした)と、今回の herdr 判断(pre-1.0 の専用ツールに、自前資産ごと乗り換えた)は逆に見える。だが軸は同じ「この環境で確実に動くか」だ。gh-dash は Codespaces の GITHUB_TOKEN 権限に依存して固まった——環境で確実に動かなかった。herdr は実機の Phase 0 で idle → working → done を確認できた——この環境で確実に動いた。成熟度そのものではなく、目の前の環境での実動が判断基準だった。だから pre-1.0 でも、検証を通れば乗る。

検証 — 通ったところと、正直に残る穴

手を動かして確かめたのは次の通り。

  • ログイン → 着地。 SSH ログインで herdr の lab に自動アタッチし、Claude タブに着地。Shift + ←/→ で Claude → File → Shell を循環できること。
  • 自己修復。 タブが消えた状態A・素シェルに戻った状態B のどちらも、再ログインで復活すること。herdr 未導入時は素のログインシェルへ素直に縮退すること。
  • 稼働中反映。 lf の設定変更が lf -remote source で稼働中インスタンスにも効くこと。
  • 併存の安全性。 herdr の claude 連携フックが tmux 配下では即 exit 0 で抜け、旧環境と衝突しないこと(併存フェーズで確認)。
  • 触れたのは devcontainer 側のスクリプトと設定なので、アプリのビルドには影響しない。リポジトリ全体で pnpm typecheck / pnpm lint / pnpm format:check が通ること。

一方で、まだ実対話アタッチで確かめきれていない穴がある。ここは正直に残す。

  • blocked の実発火。 本リポジトリは bypassPermissions 運用のため、許可プロンプト由来の blocked が実際には出にくい可能性がある。ネイティブ状態検出の要である blocked を、実運用の遷移として見届けられていない。
  • iPad Magic Keyboard での Shift + 矢印 母艦は iPad + Blink から SSH で入ることが多い。tmux 時代から気にしていたこのキーの実配信・操作感を、herdr でまだ体感確認できていない。

前作が「検証」を丁寧に書いたぶん、続編で嘘をつかないことに価値がある。通したところと未確認を分けて書くのが、一次情報として残す作法だと考えている。

Copilot に守る側のコードを詰めさせる

移行の PR(#115)は、Copilot レビューを9 ラウンド回して固めた。ガードレールや起動スクリプトのような「守る側/土台のコード」ほど機械の目が効く——lefthook 回と同じ学びが、ここでも当たった。対応した主なものだけでも:HERDR_VERSION の env 化、空 ID ガード、json_field / workspace grep の空白許容、set -e 耐性2件、PATH 非依存の起動、grep -qwF 化、自己修復のリグレッション、File タブの lf ガード、cwd を repo ルートに固定、ws_id_by_label のラベル一致必須化。一方で tar.xz に関する指摘は、実証のうえ誤検知として却下した——機械の指摘も、鵜呑みにせず裏を取ってから容れる/退ける

学び

  • 「薄い自前 vs 専用ツール」の答えは、環境とツールの成熟度で反転する。 判断の軸(この環境で確実に動くか)を固定しておけば、翌週に逆の結論を出しても筋は通る。矛盾を恐れて過去の自作に縛られないほうが、良い作業場に着く。
  • 作った資産を捨てる勇気は、性質を言語化してあれば取れる。 前作で通知機構の狙い(割り込まない・依存を足さない・視界の端に常駐する)を言葉にしていたから、それを herdr のサイドバーが満たすと分かった瞬間、迷わず捨てられた。
  • 移行は段階に切ると、勢いで壊さずに済む。 PoC → オプトイン併存 → 既定切替 → 撤去。各段で戻せる状態を挟んだから、「作った翌週に捨てる」という速さと安全を両立できた。
  • pre-1.0 に乗るなら、ピンと状態永続の理解を込みで乗る。 バージョン固定は最低条件。ディスク永続は自己修復設計の前提。ツールの「らしさ」を理解して初めて、日常運用を預けられる。
  • 設定は「した」と「反映された」が別。 起動時の冪等生成に加えて、稼働中インスタンスへの波及(lf -remote source)まで見て、ようやく設定変更が閉じる。
#herdr#tmux#Claude Code#SSH#DevContainer#マルチエージェント#hooks#lf#AI

同じ柱のほかのログ

この柱の Roadmap を見る