作り込んだ翌週に捨てる — 自前の通知機構を 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.sh→lab-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/lf→herdr/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 枚へ。ghCLI 自体は残置しているので、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)まで見て、ようやく設定変更が閉じる。




