Skip to content

feat(herdr): 安全な worktree 作成プラグインを追加 - #73

Merged
gotomts merged 1 commit into
mainfrom
feat/herdr-safe-worktree-plugin
Aug 28, 2026
Merged

feat(herdr): 安全な worktree 作成プラグインを追加#73
gotomts merged 1 commit into
mainfrom
feat/herdr-safe-worktree-plugin

Conversation

@gotomts

@gotomts gotomts commented Aug 28, 2026

Copy link
Copy Markdown
Owner

目的

Herdr 標準の herdr worktree create --base <ref><ref> をローカルで解決する。

herdr worktree create --branch feat/x --base main   # ローカルの main(数日前かもしれない)
herdr worktree create --branch feat/x --base HEAD   # 今チェックアウト中のコミット

どちらも成功して worktree ができるので、古い base で作ったことに気づくのはだいたい PR を出した後になる。この経路を塞ぐ Herdr ローカルプラグイン dotfiles.safe-worktree を追加する。

変更概要

プラグイン本体(herdr/plugins/safe-worktree/

  • allowlist: config/repos.json(追跡・公開リポジトリ用)と repos.local.json(マシンローカル・非公開リポジトリ用)を結合。未登録のリポジトリでは作成しない。突き合わせは scheme / userinfo / port / 末尾 .git を無視した ホスト/パス で行い、正規化に失敗した URL は不一致として扱う
  • base の解決: 既定ブランチを毎回 git ls-remote --symref で問い合わせ、fetch 後の SHA を base として渡す。上流が既定ブランチを付け替えても(v2main など)ローカル設定を触らずに追従する
  • base の受理条件: HEAD / ローカルブランチ名 / 相対指定は拒否。<remote>/<branch> と存在確認済み SHA だけを受け付ける
  • 既存ブランチ: ローカルにあれば新規作成を拒否し、--reuseworktree open に回す。リモートにだけ同名ブランチがある場合は --reuse でも拒否する(base から作ると同名で別履歴が 2 つできるため)。リモートへの問い合わせが失敗した場合は「存在しない」と読み替えず停止する
  • 作成後照合: 実 HEAD と渡した base を突き合わせてから成功を報告する。不一致でも worktree は消さない
  • 監査: worktree.created イベントで、プラグインを経由しない作成と想定外の base を記録・通知する。削除・自動修復は一切しない

配置(setup/herdr-sync.zsh, Tier 2)

herdr plugin link でリポジトリの作業ツリーを直接登録し、allowlist の SSOT をプラグイン設定ディレクトリへシンボリックリンクする。setup/migrate.zsh の Phase 3 と health check に組み込んだ。パス解決は setup/lib/herdr.zsh に集約し、配置する側と確認する側が同じ関数を引く。

herdr plugin link は渡されたパスをそのまま登録先として保存するため、link と設定配置は primary チェックアウト(~/.dotfiles)から実行したときだけ行う。使い捨ての worktree を登録すると、削除時にプラグイン本体と allowlist の symlink が同時に壊れる。

CI

.github/workflows/setup-check.yml の対象に herdr/** を追加し、プラグインの bats スイートも実行する。

検証

  • bats 184 件 全 pass(プラグイン 66 / herdr-sync 21 / 既存 setup 97、失敗 0)。実 herdr はスタブに差し替え、git はサンドボックス内の使い捨てリポジトリにのみ実行する

  • main が同じ workflow に追加した Claude Code hook の unit test(python3 claude/hooks/destructive-command-guard.test.py、10 件)も rebase 後にローカルで実行し pass を確認

  • 実機確認: allowlist 済みの非公開リポジトリに対し、リモート既定ブランチ(origin/v2)の最新 SHA 起点で linked worktree を作成 → 監査ログ plugin_okhead_sha == expected_sha)、pending マーカー残留 0 を確認。allowlist 外の拒否、base の許可/拒否、linked worktree からの呼び出し、プラグインを経由しない作成の監査もすべて実機で確認済み。検証用に作成した worktree とブランチは削除済み

  • migrate.zsh --dry-run: herdr-sync のみ [WOULD RUN]、他は全 SKIP を確認

  • 独立 read-only レビューdotfiles-swt-review): 最終 finding なし

  • rebase 済み: 公開後に main が 4 commit 進み機械的に競合したため origin/mainb2c752d)へ rebase した。競合は .github/workflows/setup-check.ymlAGENTS.md の 2 ファイルのみで、いずれも main 側の更新と safe-worktree の追記を両方保持する形で解消している。他の 14 ファイルは rebase 前後で blob 一致を確認済み

既知の制約

  • 非公開リポジトリは repos.json に書かない。 このリポジトリは公開されており、非公開リポジトリ名がそのまま外部に出る。マシンローカルの repos.local.json(プラグイン設定ディレクトリ配置・追跡外)に書く。リポジトリ内に誤って作られた場合の保険として .gitignore にも入れてある。追跡されないため別のマシンには複製されない
  • migrate.zsh --apply は merge 後に primary チェックアウトで実行する。 feature worktree から実行すると herdr-sync が manifest に success として記録され、プラグインの登録先が消える予定のパスに固定される
  • herdr::plugin_config_dirherdr plugin config-dir に問い合わせるのは呼び出し元自身のホームを対象にするときだけ。root で走る health check が元ユーザーのホームを検査する経路では既定パスの組み立てを使うため、Herdr がレイアウトを変えても追従しない(変わった場合は health check が先に落ちる)
  • 監査フックの base 妥当性判定は、ローカルに既知の refs/remotes/<remote>/HEAD に対して行う。イベントフックでネットワーク I/O をして待たせないため、「最後に fetch した時点のリモート像」に対する判定になる

🤖 Generated with Claude Code

Herdr 標準の `herdr worktree create --base <ref>` は ref をローカルで解決するため、
`--base main` は古いままのローカル main を、`--base HEAD` は現在のチェックアウトを
黙って使う。どちらも成功するので、古い base で作ったことに気づくのは PR を出した後
になりやすい。

ローカルプラグイン `dotfiles.safe-worktree` を追加し、この経路を塞ぐ。

- allowlist(`config/repos.json` + マシンローカルの `repos.local.json`)に無い
  リポジトリでは作成しない
- 既定ブランチは毎回 `git ls-remote --symref` で問い合わせ、fetch 後の SHA を base に
  渡す(上流の既定ブランチ付け替えに自動追従する)
- `HEAD` とローカルブランチ名は base として拒否し、`<remote>/<branch>` と存在確認済み
  SHA だけを受け付ける
- 既存ブランチでの新規作成は拒否し、再利用は `worktree open` に回す。リモートにだけ
  同名ブランチがある場合も拒否する
- 作成後に実 HEAD と突き合わせてから成功を報告する
- `worktree.created` イベントでプラグインを経由しない作成と想定外の base を監査する
  (記録と通知のみ。削除・自動修復はしない)

配置は Tier 2 の `setup/herdr-sync.zsh` が担当し、`setup/migrate.zsh` の Phase 3 と
health check に組み込む。`herdr plugin link` は渡されたパスをそのまま登録先にするため、
link と設定配置は primary チェックアウトから実行したときだけ行う。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gotomts
gotomts force-pushed the feat/herdr-safe-worktree-plugin branch from b435519 to 4a9a51a Compare August 28, 2026 06:59
@gotomts
gotomts merged commit 655b1dc into main Aug 28, 2026
1 check passed
@gotomts
gotomts deleted the feat/herdr-safe-worktree-plugin branch August 28, 2026 07:03
Sign up for free to 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