PJR-QHKA 別リポジトリ構成(Detached Unit)のドキュメント化
1. 概要
SpecDojo の成果物をプロダクトと同じリポジトリに置かず、別リポジトリ(app1-specdojo/)で 運用する構成を docs-structure-guide.md へ追記する。採用条件、ディレクトリレイアウト、 result への app commit hash 記録によるトレーサビリティ方式を規定する。
現在のガイドは同一リポジトリ構成だけを前提としており、別リポジトリ構成の記述がない。
2. 方案の評価
2.1. 想定する構成
プロダクトと SpecDojo をリポジトリで分け、SpecDojo 側から開発を管理する。
app1/ プロダクト(別リポジトリ)
app1-specdojo/ SpecDojo(別リポジトリ)。npm i -D specdojo で CLI を入れる
app1-worktrees/ worktree の置き場動機はプロダクトリポジトリの見通しを良くすることである。SpecDojo は1つの登録簿項目につき start / wait / review / close の遷移 commit と統合 commit を生み、成果物の更新も加わる。 プロダクトの変更履歴を追う目的からは雑音になる。物量も大きく、本リポジトリでは kata だけで 301文書ある。
2.2. 成立を支える実装上の事実
| 要素 | 実装 |
|---|---|
| ルート決定 | .specdojo/ の位置から上方探索する。git には依存しない |
| パス解決 | specdojo.config.json が相対パスで宣言する |
| リポジトリ外拒否 | grade と register にはあるが、exec の実行系にはない |
| 親検証 | cwd を引数で受ける(invoke(definition, cwd)) |
| 親検証の内容 | npm run test:unit など。実行場所が変われば対象も変わる |
exec の実行系にリポジトリ外を拒否するガードはなく、親検証も cwd を受け取る設計である。 構成として原理的に不可能ではない。
2.3. 現状で未対応の点
一方、次は単一リポジトリを前提としており、多リポジトリでは追加の対応が要る。
| 論点 | 現状 |
|---|---|
| 編集対象の解決 | job の paths と task の targets は SpecDojo ルート相対 |
| worktree の隔離 | SpecDojo リポジトリの worktree を作る。プロダクト側は隔離外 |
| commit のスコープ | SpecDojo リポジトリに対して計算する。プロダクト側の commit 主体が未定義 |
| 親検証の実行場所 | SpecDojo ルートで実行する。プロダクトへ向ける設定がない |
worktree の置き場を分けても、隔離されるのは SpecDojo 側だけである。agent がプロダクトを 編集すると、その変更は隔離の外に出る。並行実行と失敗時のロールバックが効かない。
2.4. product docs の配置
プロダクトの docs をどちらへ置くかは、SpecDojo が管理するかどうかで決まる。本リポジトリの 実態は次のとおりである。
| 指標 | 実測 |
|---|---|
成果物カタログ(dct-*.yaml) | 28 件 |
うち docs/ja/product を指すもの | 21 件 |
カタログは base_path: /docs/ja/product/... の形で product docs を成果物として宣言する。 deliverable scaffold が雛形を生成し、exec が編集し、grade が評価する対象である。
つまり product docs は「プロダクトの付属文書」ではなく、SpecDojo が管理する成果物そのもので ある。app1/docs/ja/product へ置くと、SpecDojo の中核である catalog / exec / grade がリポジトリを またぐことになり、最も更新頻度の高い文書がすべて多リポジトリ対応を必要とする。
| 配置 | 評価 |
|---|---|
app1/docs/ja/product | 分離の利点が小さく、対応コストが大きい |
app1-specdojo/docs/ja/product | catalog / exec / grade が単一リポジトリで閉じる |
app1-specdojo/ 側を推す。プロダクトリポジトリの見通しを良くする目的にも合致する。文書を プロダクト側へ残すと、見通しの改善そのものが薄まる。
ただしトレードオフがある。プロダクトの実装と設計文書が別リポジトリになるため、実装変更と 文書更新が1つの commit にまとまらない。README や API リファレンスなど、プロダクト利用者が コードと同じ場所で読むべき文書は app1/ に残す判断があり得る。SpecDojo が成果物として管理する 文書と、プロダクトに付属する文書を分けて考える。
2.4.1. 再検討: src との同期という観点(2026-09-10)
2.4. は「SpecDojo が管理する成果物だから SpecDojo 側へ置く」という論理で判断した。管理主体を 基準にしている。src との同期が必要かという観点が欠けていた。実測を追加する。
src と docs/ja/product を同じ commit で変更した回数を直近 60 日で数えた。
| 区分 | 件数 |
|---|---|
| src と product を同時変更 | 24 |
| src のみ | 162 |
| product のみ | 128 |
同時変更の内容は実装と設計文書が一体のものである。
a8deb72c job のコマンド実行を決定論的にする → src/job.ts + sysd-job-execution.md
2a243d26 routine の action.kind を job へ統合 → src/routine.ts + 設計文書
2e9e24a5 実行の同一性を period から組み立て… → src + product docs実装を変えれば設計文書も変わる。PJR-GWY4 では src/job.ts と docs/ja/product/040-system-design/sysd-job-execution.md を同じ commit で変更した。
24 回の同時変更は、切れない結合の証拠である。別リポジトリにすると、この 24 回が 2 つの commit に分かれ原子性を失う。
| 状況 | 結果 |
|---|---|
| src を統合、docs の統合が失敗 | 実装と設計が食い違った状態で残る |
| 片方だけ revert | 整合が崩れる |
| 特定バージョンの設計を読む | 2 つのリポジトリの版を突き合わせる必要がある |
2.4.2. 量で判断すると見誤る
docs/ja/product の commit は直近 30 日で 88 件、全体 1007 件の 9% である。量だけ見れば分離の 影響は小さく見える。これは結合の質を見ていない。
一方 docs/ja/projects は 834 件で 83% を占め、src と連動しない。register の起票・遷移と exec の plan / result / evidence が積み上がるためである。commit 頻度の問題はこちらに起因する。
| 領域 | commit/30 日 | src との同期 |
|---|---|---|
docs/ja/projects | 834 | 不要 |
docs/ja/specdojo | 154 | 不要 |
docs/ja/product | 88 | 必要 |
src | 107 | — |
2.4.3. 境界の引き方
同期の観点を採ると、境界は「同期が必要か」で引かれる。
app1/ ← src + docs/ja/product(同期が必要な対)
app1-specdojo/ ← docs/ja/projects + docs/ja/specdojo(SpecDojo の運用記録)管理主体で引く 2.4. の案より、この方が結合の実態に合う。プロダクトリポジトリの commit 頻度を 抑える目的も、docs/ja/projects の 834 件を分離することで達成される。
2.4.4. 前提条件は変わらない
ただし 2.4. で挙げた障害は消えない。
| 課題 | 内容 |
|---|---|
| catalog の 78% が境界を越える | 27 件中 21 件が docs/ja/product を指す |
exec run --worktree | 二重 worktree が必須になる |
grade --target deliverable | 別リポジトリを読む必要がある |
2.5. の二重 worktree は将来構成として整理したもので、実装は未了である。この配置を採るなら 二重 worktree の実装が前提条件になる。
2.4.5. 判断の状態
同期の観点は 2.4. の判断を覆す根拠になりうる。ただし配置の変更は二重 worktree の実装を要し、 影響が大きい。本項では実測と論点の記録に留め、配置の確定は別途行う。
2.5. 二重 worktree による並行実行
docs とソースの両方を1つの登録簿項目で扱う場合、両リポジトリの worktree を app1-worktrees/ 配下へ作り、app1-specdojo/ の specdojo コマンドが両方を管理する構成が考えられる。
app1-worktrees/
<task-id>/
docs/ app1-specdojo の worktree
src/ app1 の worktree2.5.1. 実装が許す部分
ensureExecWorktree は repoRoot を引数で受け取る。特定のリポジトリに固定されていないため、 同じ関数で別リポジトリの worktree を作れる。
ensureExecWorktree({ repoRoot, worktreeBase, taskId, ... })worktreeBase はリポジトリ外であることを要求する。app1-worktrees/ は両リポジトリの外にあり、 この制約を満たす。branch 名は task ID から決まるため、両リポジトリで同じ名前を使える。
2.5.2. 単一リポジトリを前提とする部分
統合処理は repoRoot と worktree.path の2つだけを文脈として持ち、1組の対応関係を前提とする。
| 処理 | 現状 |
|---|---|
| commit 対象 | repoRoot に対して算出する |
| merge 先 | repoRoot で checkout 中のブランチ |
| 比較基準 | merge-base HEAD <rootHead> を repoRoot と worktree で取る |
| pipeline-state | task_id と run_id で1つ。リポジトリの区別を持たない |
二重 worktree にすると、これらがすべて2組必要になる。
2.5.3. 統合順序と部分状態
docs とソースの両方を扱う項目では、ソースを先に統合し、成功した場合だけ docs を統合する。 この順序により、起こり得る部分状態は1通りに定まる。
| 失敗箇所 | 結果 | 扱い |
|---|---|---|
| ソースの統合 | 両方とも未統合 | 清潔。項目全体を再実行する |
| docs の統合 | ソースのみ統合済み | docs の統合だけを再試行する |
任意の部分状態が生じるわけではないため、分散トランザクションのような一般的な回復手順は要らない。 回復に必要な仕組みは既にある。
- 統合段のみの再試行は
exec run --resumeが担う。executor と reporter が成功済みで統合だけが 失敗した run を、agent を起動せず統合から再開する。 - 統合済みブランチの再 merge は
isExecBranchMergedIntoCurrentが防ぐ。ソースを二重に統合しない。
ソースを先に統合する順序は、プロダクトの完全性を優先する点でも妥当である。SpecDojo 側の失敗が プロダクトへ波及しない。
2.5.4. 定義が必要な事項
部分状態そのものは扱えるが、次は運用上の定義を要する。
- 部分状態での item_status。ソース統合済みで docs 未統合の項目は
waitingとする。完了条件を 満たしておらず、再開で docs の統合だけを行うためである。reviewにすると実質完了として扱う ことになり、未統合の docs が追跡から外れる。 - 打ち切ったときの後始末。docs の統合が繰り返し失敗して項目を諦める場合、ソースは統合済みの まま残る。放置するか revert するかを決める必要がある。ただしこれは通常の merge 後の判断と同じ であり、二重 worktree に固有の問題ではない。
- 並行実行時の履歴順序。複数項目を並行実行すると、A のソース統合、B のソース統合、A の docs 統合という順序が起こり得る。各リポジトリ内では直列化されるため衝突しないが、A のソースと A の docs の間に B が入る状態は生じる。履歴を追うときの前提になる。
2.5.5. 実装が必要な事項
順序化で解ける課題とは別に、次は実装を要する。
- 親検証の割り当て。
test-unitはソース側、lint:mdとvalidate:schemaは docs 側で実行する 必要がある。現在の親検証はcwdを1つしか持たないため、検証 ID ごとに実行先を宣言する仕組みが 要る。 - agent の作業ディレクトリ。agent は両方を見る必要がある。
app1-worktrees/<task-id>/を cwd に すれば両方が相対パスで見えるが、pathsとtargetsの解決基準が「リポジトリルート相対」から 「タスク作業ディレクトリ相対」へ変わる。既存の単一リポジトリ実行と規則が分岐する。 - 失敗時の worktree 保持。現在は失敗した worktree を残して調査に使う。二重化した場合に どちらをどの条件で残すかを定める。
- 統合処理の複製。commit 対象の算出、merge 先の決定、pipeline-state の記録は
repoRootと worktree の1組を前提とする。2組を扱えるようにする。
2.5.6. 評価
構成として成立する。ensureExecWorktree が repoRoot を引数化しており、統合順序を定めれば 部分状態も1通りに収まる。回復に必要な統合段の再試行と再 merge の防止は既に実装済みである。
残るのは実装量と、上記の運用定義である。設計上の障害は見当たらない。
段階を分ける余地はある。docs のみを扱う項目(実装を伴わない要件・設計・計画の更新)であれば 統合先が1つで済み、二重 worktree の実装を待たずに別リポジトリ構成を試せる。
本項目は設計の整理までとするため、実装は範囲に含めない。ガイドには構成案、統合順序、部分状態の 扱い、未実装の事項を記載し、採用可否の判断材料を残す。
2.6. 複数のプロダクトリポジトリを管理する
1つの SpecDojo リポジトリが複数のプロダクトリポジトリを管理できる。
app1/ プロダクト
app1-marketing/ マーケティング
app1-specdojo/ SpecDojo(両方を管理)
docs/ja/projects/prj-app1/
docs/ja/projects/prj-marketing/2.6.1. 現状の設計で成立する
specdojo.config.json の projects は複数のプロジェクトを保持する。
export type SpecDojoConfig = {
version: 1;
current_project?: string;
projects: Record<string, SpecDojoProjectConfig>;
};各プロジェクトのパスは base_path を起点に組み立てられ、プロジェクトごとに独立する。すべての コマンドが --project <id> を受け取り、解決順序も定義済みである。
1. --project <id>
2. 環境変数 SPECDOJO_PROJECT
3. current_project
4. projects の先頭プロダクトごとにプロジェクトを分ければ、成果物、登録簿、実行記録がそれぞれ独立する。
2.6.2. 未対応の範囲
1つの登録簿項目が複数のプロダクトリポジトリを同時に変更する場合は成立しない。二重 worktree の 課題が組み合わせの分だけ増える。
| 論点 | プロダクト1つ | プロダクト複数 |
|---|---|---|
| worktree の隔離 | 2 組(docs と src) | 3 組以上 |
| 統合の順序 | src の次に docs | どの src を先にするか |
| 親検証 | 実行先が 2 つ | リポジトリごとに異なる |
| 部分状態 | 1 通り | 組み合わせの分だけ増える |
統合順序が定まらないと部分状態が一意にならない。2.5.3. 統合順序と部分状態 で示した回復手順が そのままでは使えない。
ただし実務上この状況は稀と考えられる。プロダクトとマーケティングの成果物を同一項目で変更する 場面は限定的で、項目をプロジェクト単位に分ければ回避できる。
対応は prj-0001:pjr-p7hy-multi-repo-single-item として分離する。
2.6. 適用範囲の整理
| 構成 | 評価 |
|---|---|
| 文書のみを扱う(自己完結) | 現状の実装で成立する |
| プロダクト駆動(本方案) | 設計上あり得るが、多リポジトリ対応の実装が要る |
| SpecDojo 自身の開発(本リポジトリ) | 成果物と実装が同じ変更で動くため同一リポジトリ |
本項目は設計の整理までとし、多リポジトリ対応の実装は範囲に含めない。ガイドには構成の紹介と あわせて、未対応の点と適用条件を明記する。
3. 完了条件
docs-structure-guide.mdに「別リポジトリ構成(Detached Unit)」の節がある。app1//app1-specdojo//app1-worktrees/のレイアウト例が記載されている。- product docs の配置判断と根拠が示されている。プロダクトに残す文書との切り分けも記載する。 ガイドの記述は SpecDojo 側へ置く前提だが、
再検討: src との同期という観点により見直しの 余地がある。配置を変更する場合はガイドも追従させる。 - 現状で未対応の点(編集対象の解決、worktree の隔離、commit のスコープ、親検証の実行場所)が 明記されている。
- result への app commit hash 記録によるトレーサビリティ方式と、片方向であること、記録時点の 定義が記載されている。
- 二重 worktree による並行実行の構成案が記載されている。ソースを先に統合する順序、生じ得る 部分状態、部分状態では
waitingとすることが示されている。 - 二重 worktree で実装を要する事項(親検証の割り当て、agent の作業ディレクトリ、失敗時の worktree 保持、統合処理の複製)が記載されている。
- 適用条件が明記されている。文書のみを扱う構成と、プロダクト駆動の構成を区別する。
npm run -s lint:mdが通る。
4. 作業内容
| No | 作業 | 担当 | 状態 | メモ |
|---|---|---|---|---|
| 1 | 構成の概要とディレクトリレイアウトを記述する | ARC | done | 3ディレクトリ構成 |
| 2 | product docs の配置判断と根拠を記述する | ARC | done | カタログ28件中21件が指す |
| 3 | 未対応の点を記述する | ARC | done | 隔離とcommitスコープ |
| 4 | トレーサビリティ方式と限界を記述する | ARC | done | 片方向、記録時点 |
| 5 | 二重 worktree の構成案と統合順序を記述する | ARC | done | 部分状態は waiting |
| 6 | 適用条件を記述する | ARC | done | 自己完結型と駆動型を区別 |
| 7 | lint を確認する | ARC | done | Markdown 検査実施 |
| 8 | 履歴改変に耐える主参照を規定する | ARC | done | item ID の Refs trailer |
| 9 | rebase / squash 後の確認手順を規定する | ARC | done | 統合先の Git 履歴を確認 |
| 10 | 既存 hash 記録の移行方針を規定する | ARC | done | PR と最終 hash を追記 |
5. 対応結果
specdojo:docs-structure-guide に「別リポジトリ構成(Detached Unit)」を追加した。
初回対応では、3ディレクトリの配置、プロダクト文書の切り分け、現行実装の境界、result から app commit への片方向トレーサビリティを規定した。
二重 worktree は将来構成として、app 側を先に統合する順序、部分状態での
waiting、採用前に必要な実装を明記した。多リポジトリ対応の実装自体は本項目の範囲外である。受け入れ時に orchestrator が
lint:mdとdocs:buildの通過を確認した。初回対応では、app commit hash を統合後に確定してから docs 側の result へ記録する手順まで 具体化した。この方式の欠陥と再開後の置き換えは
5.1.と5.2.に記録する。残課題は二重 worktree の実装(親検証の割り当て、agent の作業ディレクトリ、失敗時の worktree 保持、統合処理の複製)である。本項目は設計の整理とガイド追記までとする。
5.1. 再開: トレーサビリティ方式の欠陥
2026-09-08 に reopen した。10.5. result によるトレーサビリティ で規定した commit hash による 参照が、標準的な運用で目的を果たさない。
5.1.1. 欠陥の内容
ガイドは限界を認めている。
履歴改変によって hash が到達不能になる可能性と、参照の整合性を自動検証しない制約を
受け入れる必要があります。この判断が弱い。理由は3点ある。
squash merge は例外ではない。GitHub の PR で squash merge を選ぶ運用は広く使われる。稀な 履歴改変ではなく、標準的なワークフローで必ず新しい commit が生成される。
失敗が静かで回復できない。hash が変わるだけでなく、元の commit が到達不能になれば GC で 削除される。squash merge 後にブランチを削除する運用では参照先そのものが消え、git show は 失敗する。何を指していたかを知る手段が残らない。警告は出ず、追跡しようとした時に初めて分かる。
記録時点が squash と噛み合わない。ガイドは app 側の統合成功後、SpecDojo 側の統合前に記録 すると定める。PR ベースの squash では最終的な commit がその後に生成されるため、記録した hash は統合先ブランチに載る commit と一致しない。SpecDojo 側は統合済みであり、後から更新する運用は 成立しない。
5.1.2. 根本原因
ガイドは逆参照を意図的に排除している。
app1/ 側の commit message やファイルに SpecDojo result への逆参照を必須とせずこれは「アプリの commit 履歴を汚さない」という Detached Unit の目的から導かれた選択である。 目的としては正しいが、hash はリポジトリの履歴操作に従属する識別子であり、履歴と独立した 識別子を持たない限り履歴改変に耐えられない。
「汚さない」と「追跡できる」は hash だけでは両立しない。
5.1.3. 対処の方向
| 案 | 履歴改変への耐性 |
|---|---|
| 現状維持 | なし |
| commit message へ trailer | 高い |
| PR 番号・URL を記録 | 高い |
| app 側に tag を打つ | 中 |
commit message への trailer を主軸とし、PR 番号を併記する案を推す。
feat(auth): トークン失効処理を追加する
Refs: PJR-XXXX識別子が内容とともに移動するため、rebase では message が保持され、squash でも既定では各 commit の message が連結される。git log --grep で hash に依存せず追跡できる。Gerrit の Change-Id が同じ問題に対して採る方式である。汚染は commit message 1 行にとどまり、ファイルは 追加されない。
hash は記録時点のスナップショットとして残す価値はあるが、主たる参照にはできない。
5.1.4. 再開後の完了条件
- 履歴改変を経ても追跡できる識別子が主たる参照として規定されている。
- squash merge と rebase を経た場合に追跡できることを、手順として確認できる。
- 逆参照を必須としない現在の規定との関係が整理されている。アプリの履歴へ何をどこまで 書き込むかの判断根拠が示されている。
- 既に記録済みの hash がある場合の移行方針が示されている。
10.5.の暫定注意書きが解消されている。
5.2. 再開後の対応結果
- 主たる参照を commit hash から登録簿 item ID へ変更し、app の commit message に
Refs: PJR-XXXXtrailer を残す規則を設けた。PR 参照と40文字の最終 commit hash は、レビュー 経緯と確認時点を固定する補助情報として result 本文へ記録する。 - rebase 後は書き換え後の対象 commit、squash merge 後は最終 commit の message を確認する手順を 追加した。squash 時の自動的な message 連結には依存せず、統合先ブランチで
git log --grepを 実行して item ID を検索できることを SpecDojo 側の統合前に確認する。 - 「app 側への逆参照は不要」という旧規定を改め、app のファイルや状態遷移は増やさない一方、 commit message 1行の
Refs:trailer は必須とした。Detached Unit の履歴分離を維持しつつ、hash だけでは失われる追跡可能性を確保するためである。 - 既存 result の hash は削除せず、到達可能性と PR を調べて item ID、PR、最終 hash を追記する移行 方針を定めた。共有済み app 履歴の書き換えは禁止し、特定できない対応は推測せず未解決として 記録する。
- これにより
10.5.の暫定注意書きを解消し、再開後の完了条件を満たした。二重 worktree の実装は 引き続き本項目の範囲外である。
6. 関連ドキュメント
- specdojo:docs-structure-guide: 追記先のガイド。
- specdojo:exec-operation-guide: 親検証と編集対象の前提。
- [[specdojo:catalog-authoring-standard]]: 成果物カタログが
base_pathで成果物を宣言する規約。