worktree について
隔離されたGit worktreeの作成と引き渡しを管理するスキルで、doctrineコマンドを使ってワークスペースを作成、プロビジョニング、検証します。solo、worker、独立したcoordination worktreeの経路に対応し、それぞれ異なる隔離要件があります。
主な機能
- 01既存のlinked worktreeを検出して利用
- 02doctrine worktree forkで分岐ワークスペースを作成・設定
- 03solo、worker、coordinationの各経路に対応
- 04分岐前にソースツリーのクリーン状態を確認
- 05引き渡し前にプロジェクトのベースラインを検証
- 06設定時に許可リスト内の無視対象ファイルだけをコピー
活用シーン
- 01solo作業用の隔離ワークスペースを準備
- 02明示したベースコミットからworker用forkを作成
- 03dispatch coordination worktreeを作成または再開
- 04エージェントへの引き渡し前にforkを検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davidlee/ doctrine --skill worktreeインストール後、Claude Code で/worktreeを入力して読み込まれたか確認します。
同梱ファイルNOTICE.md
詳細
Worktree
分離された Git worktree のライフサイクルを管理します。既存の分離状態を検出し、doctrine worktree fork を使ってフォークを作成します(worktree の追加、プロビジョニング、可能な場合はフェーズへの紐付けまでを担う動詞です。すべて補償ロールバックに対応し、フォークは自身のツリー内の target/ にビルドします)。スポーンガードを実行し、引き継ぎ前にベースラインが正常であることを確認します。
開始時に宣言します:「worktree スキルを使って、分離されたワークスペースをセットアップします。」
これはサブスキルです。 /execute(mode=solo)は、オプトインされた分離のためにこのスキルを呼び出します。ディスパッチのスポーンスクリプトはハーネスに関係なく、すべてのワーカーに対して fork を呼び出します。/dispatch ルーターは、実行ごとの dispatch/<slice> ツリーに対して、調整パス(以下の worktree coordinate)を呼び出します。これは /route の遷移先ではありません。呼び出し元を通じてアクセスしてください。
モードの契約
このスキルはパラメーター化されているため、ファネルで再利用しても、solo のセマンティクスを引き継ぎません(分岐点)。
入力:
mode = solo | workerallow_work_in_place: bool—soloの場合のみ true- 要求されたブランチ/パス
base— フォークの作成元となる ref。soloの既定値はHEADです。workerでは明示的な base が必須です(オーケストレーターの調整用 HEADB)。 詳しくはlib:reference/dispatch-mechanics.md§ フォークの base は明示指定し、セッションの HEAD は使わない を参照してください。
動作:
solo— サンドボックスによって拒否された場合、work-in-place の段階にフォールバックしてもかまいません(フォークなし。許可されたトランクパスです)。worker— フォールバックしてはいけません。 実際のフォークがないワーカーは重大な失敗です。ファネルでは分離が必須です。fork --worker --base <B>は base を明示的に固定します。暗黙のセッション HEAD は使用しないでください。ワーカーの場合はBではなく、S.parent == Bが成立しなくなります。作成後、worker 側では制約付きの編集→検証→単一のSをフォークにコミットするループを実行します。詳しくはWorker モードを参照してください。
出力: { fork_path, branch, head_sha, provision_report, baseline_result }。
worker は、単一の差分 S をコミットした時点で { fork_branch, head_sha_after } を追加します。オーケストレーターは B..head_sha_after をインポートします。
調整モード — doctrine worktree coordinate(第 3 のパス)
上記の solo/worker はどちらも fork を経由します。ディスパッチ調整用 worktree は別の作成動詞であり、fork のモードではありません。/dispatch ルーターがバッチ 1 の前に、実行ごとに 1 回呼び出します。
doctrine worktree coordinate --slice <N> --dir <path>
これは、ファネル唯一の書き込み先である解決済みトランクから、独自の worktree に dispatch/<slice> を作成(または再開)します。重要なすべての点で fork とは異なります。
- オーケストレーター自身のツリーです。 調整用ツリーはオーケストレーターそのものです(worker モードは OFF。ワーカーであることはプロセスの属性であり、ディレクトリの属性ではありません。ワーカーの jail 内で
DOCTRINE_WORKERによって引き継がれます)。 オーケストレーター用の動詞クラスに属し、他の同クラスの動詞と同様、worker モードでは拒否されます。 - 作成または再開であり、二つ目のブランチは作りません。
dispatch/<slice>上にすでに存在する稼働中の worktree は拒否されます(coordination-live— 同じスライスの同時ディスパッチは正当ではありません)。ブランチが存在し、稼働中の worktree がない場合は再開(再接続)します。これにより、/handover後の新しいオーケストレーターも同じブランチを引き継げます(再開しても安定するため、実行ごとの識別子を使うと再開が壊れます)。 - コミット済みの
plan.tomlから、唯一のコピー処理を介してランタイムフェーズシートを再生成します(プロビジョニングに関する処理であり、調整層でのコピーはありません)。
これはワーカー差分のパスではありません。S もインポートもありません。ライフサイクルの大小関係は worktree-life < branch-life です。まとめの時点でディレクトリは削除されますが、dispatch/<slice> と、投影された review/phase refs は成果物として保持されます(/dispatch を参照)。以下の solo/worker の作成方法に変更はありません。
検出(作り直さず、適応する)
CWD がすでにリンクされたワークツリー内にある場合は、それに適応し、別のワークツリーを作成しないでください。
git rev-parse --git-dir # differs from common-dir ⇒ linked worktree
git rev-parse --git-common-dir
--git-dir ≠ --git-common-dir ⇒ すでにリンクされたワークツリー内にある → 作成をスキップします。
サブモジュールのガード。 サブモジュールでも同じ不等式が成立します。次の方法で区別します。
.gitの gitdir が…/worktrees/以下に解決される ⇒ ワークツリー、…/modules/以下に解決される ⇒ サブモジュール。またはgit rev-parse --show-superproject-working-treeの結果が空でない ⇒ サブモジュール。
サブモジュールは、求める分離状態ではありません。「まだフォークされていない」として扱います。
作成 — doctrine worktree fork(制御されたパス)
フォークディレクトリを選び、コミット前のガードを実行してから、動詞を呼び出します。
doctrine worktree fork --base <B> --branch <name> --dir <path> [--worker]
fork は、作成+プロビジョニング+(マーク付け)+出力を行う唯一の動詞であり、補償ロールバックによってアトミックに処理します(git worktree add の後に失敗すると、可能な範囲で worktree remove --force / branch -D / ディレクトリの削除を実行します。ロールバック自体が失敗した場合は、残ったものを明示して、ゼロ以外の終了コードで終了し、中途半端なロールバックを黙って残すことはありません)。次の処理を行います。
git worktree add -b <branch> <dir> <base>—<dir>/<branch>が存在する場合、または<base>がコミットでない場合は拒否します。<base>は明示的に指定してください。単独実行ならHEAD、ワーカーならBを指定します。この明示的なベース指定が、このパスを制御されたものにする理由です。- フォークをプロビジョニングします(唯一のコピー処理です。コピー時に調整/ランタイム層は除外します。プロビジョニングを参照)。
--workerを指定する場合、さらに--sliceと--phaseの両方、および<coord>/.worktrees/<name>ディレクトリが必要です。これらを指定すると、フォークをその(slice, phase)にバインドします。いずれかが欠けている場合、フォークは単に未バインドとなり、下流の動詞は推測で補わず、その状態を正直に報告します(単独実行では--workerを省略します)。- 人間向けのステータスは stderr に報告し、stdout は空のままにします(機械処理に適した状態です)。フォークは自身のツリー内にある
<dir>/targetにビルドします。環境変数の契約は出力されません。プラットフォームはビルド環境の提供から撤退したためです。
fork はオーケストレーター級であり、worker-mode 中は拒否されます(ワーカーはフォークしません。オーケストレーター/単独実行が worker-mode を OFF にして呼び出します)。
作成バックエンドの優先順位 — 順にフォールバックします。
- 既存の分離状態(上記の検出)→ 作成をスキップし、フォークを採用します。
- ハーネスネイティブの作成機能が存在し、作成のみ(自動コピーなし)で呼び出せる場合は使用します。これは機会があれば使うだけです。(Claude Code の
WorktreeCreateフックが該当しますが、ペイロードにベース/パス/タイプがないため doctrine から駆動できず、ディスパッチも使用しません。代わりに明示的にフォークします。)これを前提に設計してはいけません。依存しないでください。 doctrine worktree fork— 推奨され、テスト済みのデフォルトです。これを優先してください。- サンドボックスによる拒否時は、作業場所での作業(
soloのみ、allow_work_in_place=true)に切り替えます。フォークせず、トランクのパスを使います。workerは中止します。
ワークツリーのディレクトリ選択
fork を呼び出す前に、次の優先順位でフォークディレクトリを選びます
(superpowers:using-git-worktrees)。
- 既存のディレクトリを再利用する — 両方存在する場合は
worktreesより.worktreesを優先します。 - CLAUDE.md を確認する — 希望が記載されている場合は、確認せずにそれを使用します。
- ディレクトリが存在せず、希望も記載されていない場合は、ユーザーに尋ねます。
フォークのパスは <dir>/<branch> です。安全性: 作成前に、ディレクトリが Git の除外対象になっていることを確認する必要があります(git check-ignore -q .worktrees)。確認しないと、フォークの内容が追跡対象になります。除外されていない場合は、.gitignore に行を追加してから、先にコミットしてください。(プロジェクト外のグローバルディレクトリには、この確認は不要です。)
プロビジョニング
fork が自動的にプロビジョニングします。2つ目のコピー処理を実行してはいけません。 doctrine worktree provision <fork> は、単独で使う唯一のコピー処理として用意されています(fork をスキップした、まれな既存フォーク採用時に限り、ソースルートから実行してください。フォーク内からは実行しないでください)。.worktreeinclude を読み込み、層名を指定するパターンがあれば安全側に倒して失敗し、許可リストにある Git 除外対象のファイルのみをコピーします。広範な ** パターンが指定されていても、コピー時に調整/ランタイム層は除外されます。
正直な不変条件の説明(誇張しない)
コピー境界の保証が成り立つのは、プロビジョニングだけがコピーを行うためです。
doctrine worktree check-allowlistは静的な問題検出テストです。成功は、パターン名にそのティアが含まれていないことを示すだけで、網羅性を示すものではありません。実際の保証を担うのは、プロビジョニング内のselect_copiesです。- ハーネスが作成時に強制的にコピーし、作成のみで実行できない場合、doctrine ではそのコピーを防げません。保証は
check-allowlistのみとなり、プロジェクトは.worktreeincludeを正確に保つ必要があります。だからこそ、デフォルトでは(完全に制御可能な)fork動詞を使い、ハーネスネイティブの段階には決して依存しません。
ガード
スポーンの前後にこれらを実行します。ガードに失敗した場合は中止し、報告してください。独自の判断で先に進めてはいけません。
- スポーン前のコミット。
forkの前に、フォークにコミット済みの HEAD だけが反映されるよう、ソースツリーをクリーンにする必要があります。git status --porcelain -zを実行します。追跡対象のファイルに変更がある場合、または未追跡の非無視ファイルがある場合(フォークに暗黙のうちに含まれないため)は中止します。無視対象のファイルは問題ありません。プロビジョニングが許可リストの対象サブセットを処理します。 - ブランチポイントの確認。
forkは構成上、ワークツリーを<base>に配置します。並行処理拡張では、一括コミットの境界を確認します。doctrine worktree branch-point-check --base <B>は、調整用 HEAD がオーケストレーターのスポーン前のBと一致する場合に 0、それ以外の場合は 1 を返します(→ 再ディスパッチ)。これはインポート時にオーケストレーターが使うガードであり、ワーカーモードは OFF です。ワーカー側のガードではありません。これは HEAD が不動であることの比較であり、マージベースではありません。 - ベースラインの検証。 作成後、プロジェクトの再生成・検証コマンドをフォーク内で実行し、成功した場合にのみ引き継ぎを許可します(このリポジトリでは
doctrine check gate)。コマンドはプロジェクトが提供するものを使い、cargo …のようにハードコードしてはいけません。ビルドできないフォークはプロビジョニング時に修正し、決して引き渡してはいけません。
ワーカーモード(ファネルの半分)
mode=worker は、ディスパッチされたワーカーが fork --worker ワークツリー内で実行する経路です。作成、プロビジョニング、および上記のガードを先に変更せずに実行します。これはベースライン検証の成功後、単独での引き継ぎに代わって実行される処理です。ワーカーは制約付きの書き手です。インポート可能な差分を 1 つ作成して返し、調整/ランタイムのティアには触れません(フォークがすでにそのティアを含めないためです)。
lib:reference/dispatch-mechanics.md の § Worker identity は、スポーン時に設定されるプロセスの属性です。
lib:reference/dispatch-mechanics.md の §§ "The worker returns a working tree,
not a commit" と "Workers can silently discard their own work" を参照してください。
作業場所を変えずに作業する形へ劣化させてはいけません。 実際のフォークがないワーカーは必ず中止し、暗黙のうちに同じツリーを編集してはいけません(solo の段階 4 とは対照的です)。作成に失敗した場合は、報告して停止してください。
構造化されたレポート(オーケストレーターのコンテキストに保持し、doctrine のアーティファクトにはしない)を返します。変更内容、検証結果、記憶に値するメモ、フォークのブランチを記載します。ナレッジはフォークではなく、オーケストレーターが確認したコミットに追随します(以下の「trunk への記録」を参照)。
スカッシュによる孤児化の注意点(trunk への記録)
ワークツリーのブランチ内に記録されたメモリは、スカッシュマージによって孤児化します(内容は残りますが、Git の参照先は取り込まれないコミットを指すため、古さの検出が発生します)。永続的なメモリをフォーク後も残す必要がある場合は、ワークツリーのブランチ内ではなく、trunk に記録してください。
.worktreeinclude テンプレート(プロジェクト所有。インストールされません)
Doctrine には秘密情報やローカル固有のファイルがないため、デフォルトではコピーするものはありません。インストーラーも .worktreeinclude を同梱しません(ルートファイルをインストールすると、利用側プロジェクトのファイルを上書きしてしまいます)。Provision はファイルがなくても問題なく動作し(何もコピーせず、終了コード 0 を返します)。
gitignore 対象のローカルファイルをフォークに引き継ぐ必要があるプロジェクトは、リポジトリルートに .worktreeinclude を配置できます。空行、# コメント、リポジトリルートからの相対パス(リテラル)、単純な glob パターン(*、**、?)を指定できます。! による否定やアンカー指定はできません(パーサーが拒否します)。調整/ランタイム層は、いずれの場合も引き継がれません。
静的検証(完全性の確認ではなく、問題の兆候を検出するテスト)を実行します:doctrine worktree check-allowlist。
クイックリファレンス
| 状況 | 対応 |
|---|---|
--git-dir ≠ --git-common-dir で、サブモジュールではない | すでにフォーク済み → そのまま採用し、作成をスキップ |
サブモジュール(modules/ の gitdir/スーパープロジェクト) | 分離ではない → 未フォークとして扱う |
| ディスパッチ調整ツリー(実行ごと) | doctrine worktree coordinate --slice <N> --dir <path> — 解決済みの trunk を起点に作成または再開。fork モードではありません |
| デフォルトのフォーク(単独またはディスパッチワーカー) | doctrine worktree fork --base <B> --branch <name> --dir <path> [--worker] |
| ワーカーフォークのベース | --base <B> を明示。暗黙のセッション HEAD は使用しません(B ではありません) |
| Worktree ディレクトリが ignore 対象ではない | fork の前に .gitignore に追加し、コミット |
サンドボックスがフォークを拒否、mode=solo | 作業ツリー上で作業(フォークなし) |
サンドボックスがフォークを拒否、mode=worker | 中断(分離が必須) |
fork をスキップしたフォークを採用 | ソースルートから doctrine worktree provision <fork> を実行(唯一のコピー手段) |
| ツリーが dirty/未追跡かつ ignore 対象ではない | 中断。起動前にコミット |
| バッチコミットの境界 | doctrine worktree branch-point-check --base <B> → 1 の場合は再ディスパッチ |
| フォーク内でベースラインが失敗 | プロビジョニングで修正し、絶対に引き渡さない |
worker の開始 | export DOCTRINE_WORKER=1(自動で有効化。失敗しても処理を続行) |
worker の検証が成功 | マージではないコミット S を1つ作成し、{fork_branch, head_sha_after} を返す |
worker が2つ以上コミット/マージ/リベース済みフォーク | 契約違反 — オーケストレーターがインポート前に拒否 |
worker の検証が失敗 | 報告する。失敗した差分はコミットしない |
注意事項
絶対にしないこと:
fork/provision以外の方法で、gitignore 対象ファイルをフォークにコピーすること(唯一のコピー手段です。除外の保証はこれらに依存します)。forkの後に2つ目のコピーを行うこと、またはフォーク内からprovisionを実行すること。check-allowlistの成功が、許可リストの完全性を意味するかのように扱うこと(これは問題の兆候を検出するテストです)。workerが作業ツリー上での作業に切り替わることを許すこと。workerモードで、作成済みの.doctrine/ツリーに書き込むこと、export DOCTRINE_WORKER=1を省略すること、またはマージではないコミットSを2つ以上作成すること。- dirty なツリーからフォークすること、または失敗したベースラインを引き渡すこと。
--base <B>の代わりに、暗黙のセッション HEAD からworkerをフォークすること — ワーカーではセッション HEAD はBではありません。異なるベースから作成すると、S.parent == Bが成立しません。- このスキルのマスターを作成または編集すること。gitignore 対象の
.doctrine/skills/にインストールされたコピーは、再インストール時に上書きされます。
必ず行うこと:
- 作成前に検出し、既存の分離環境に合わせる。
fork動詞を優先し、ハーネスネイティブの段階は利用できる場合に限って使う。- 3つのガードをすべて実行し、引き渡し前にベースラインの成功を確認する。
- 永続的なメモリはフォークブランチではなく trunk に記録する。
結果
出力を報告します:fork_path、branch、head_sha、プロビジョニングレポート(コピーされたファイル/引き継がれなかったファイル)、およびベースラインの結果。フォークブランチを成果物として呼び出し元に返します。