diff --git a/docs/adr/adr-045-jj-workspace-parallel-sessions.md b/docs/adr/adr-045-jj-workspace-parallel-sessions.md index c8501cf8..c5a5ae95 100644 --- a/docs/adr/adr-045-jj-workspace-parallel-sessions.md +++ b/docs/adr/adr-045-jj-workspace-parallel-sessions.md @@ -2,7 +2,7 @@ ## ステータス -試験運用 (2026-06-29) +試験運用 (2026-06-29) / 改訂 (2026-06-30: 初 PR 運用ケースで判明した secondary workspace の `.git` 不在 → gh ベースコマンドの `GIT_DIR` 必須、および merge-pipeline の bookmark 誤検出を「§ PR 運用時の追加設定」として追記) > ADR-039 (Experimental feature 標準パターン) に準拠: config opt-in なし (本 ADR は workflow 運用ポリシーであり実装機構ではないため該当しない) / kill-switch = 本 ADR を supersede する後続 ADR で停止可能、運用上は単一 workspace へ戻すだけで無効化 / bounded lifetime = 採用判定 3 ヶ月 (2026-09-29) を目安に dogfood 結果から本採用 / 修正 / 却下を判定。 @@ -37,6 +37,7 @@ git には worktree (同一リポジトリを汚染せず複数 working tree で | `.claude/settings.local.json` | gitignore (PROJECT_DIR テンプレ生成) → 未コピー | `build:all` 内の `build:hooks-settings` がそのパス用に生成 | | `.claude/pr-monitor-state.json` / lock / `.session-id` / `feedback-reports/` | gitignore + per-checkout | **workspace ごとに独立 → 並列 monitor が state を衝突させない** | | `/target/` (Cargo workspace) | gitignore | workspace ごとに独立ビルド (disk コストのみ) | +| `.git` (colocated git ref) | **secondary workspace には存在しない** (`jj workspace add` は colocated 化しない、`.jj` のみ) | **gh ベースの `pnpm create-pr` / `pnpm merge-pr` / `cli-pr-monitor --monitor-only` は `GIT_DIR` 必須** (後述「§ PR 運用時の追加設定」)。`pnpm push` は `jj git push` backend のため不要 — この非対称が初回検証で見落とされた | ## 検討した選択肢 @@ -58,7 +59,7 @@ git には worktree (同一リポジトリを汚染せず複数 working tree で - 同一リポジトリを共有しつつ独立した `@` で並列作業 = git worktree 相当 - commit store / op log / bookmark を共有するため、別 clone より効率的かつ状態同期が容易 - state / lock / target は gitignore + per-checkout で自動的に workspace 分離される -- 初回 setup (`pnpm build:all` で exe / settings 用意) を払えば、各 workspace で `pnpm push` / `pnpm merge-pr` が通常通り機能 +- 初回 setup (`pnpm build:all` で exe / settings 用意) を払えば、各 workspace で `pnpm push` が通常通り機能 (`pnpm create-pr` / `pnpm merge-pr` は gh ベースのため `GIT_DIR` の追加設定が必要 — 後述「§ PR 運用時の追加設定」) - **採用** ## 決定 (試験運用) @@ -95,11 +96,68 @@ src の大規模分割 (メイン) と lint/facet/docs (改善) は編集領域 ### マージ方法 (各 workspace で独立) -- 各 workspace で通常どおり `jj describe` → `jj bookmark create ` → `pnpm push` → `pnpm create-pr` → `pnpm merge-pr` を実行する。 +- 各 workspace で `jj describe` → `jj bookmark create ` → `pnpm push` → `pnpm create-pr` → `pnpm merge-pr` を実行する。**ただし `pnpm create-pr` / `pnpm merge-pr` / `cli-pr-monitor --monitor-only` は secondary workspace では `GIT_DIR` 必須** (後述「§ PR 運用時の追加設定」)。 - `pnpm merge-pr` は `@-` の bookmark から PR を自動検出する (ADR-013) ため、各 workspace は自分の bookmark に対応する PR のみをマージする。 - マージ後の `jj new master@origin` 同期は実行した workspace のみに効く。もう片方は調整ポイント 2 の手順で master を取り込む。 - push は `cli-push-runner` (ADR-015)、merge は `cli-merge-pipeline` (ADR-013) を各 workspace の `.claude/*.exe` 経由で使う (= 初回 `pnpm build:all` が前提)。 +### PR 運用時の追加設定 (2026-06-30 追記 — 初 PR 運用ケースで判明) + +#### 問題: secondary workspace は `.git` を持たない + +`jj workspace add` で作った secondary workspace は **colocated 化されず `.git` を持たない** (`.jj` のみ)。メインリポジトリ (`default` workspace、`.jj/repo` が指す先) は colocated で `.git` を持つ。 + +このため、内部で `gh` (= git リポジトリコンテキスト必須) を呼ぶコマンドが secondary workspace で `fatal: not a git repository` で失敗する。**`pnpm push` は `jj git push` backend なので動くが、PR 操作は全滅する** — この非対称が初回検証 (push 中心) で見落とされた。 + +| コマンド | 内部実装 | secondary workspace 単独 | +|---|---|:---:| +| `pnpm push` / `jj` 系すべて | jj backend (`.jj/repo/store/git`) | ✅ 動く | +| `pnpm create-pr` / `pnpm merge-pr` | `gh pr create` / `gh pr merge` | ❌ 要 `GIT_DIR` | +| `cli-pr-monitor --monitor-only` | `gh api` / `gh pr checks` | ❌ 要 `GIT_DIR` | +| `gh api` / `gh pr checks` 直接呼び出し | gh | ❌ 要 `GIT_DIR` | + +#### 解決: `GIT_DIR` でメインリポジトリの `.git` を参照 + +gh ベースのコマンド実行時に環境変数 `GIT_DIR` でメインリポジトリ (`.jj/repo` が指す先、本プロジェクトでは `~/work/claude-code-hook-test`) の `.git` を指す。**jj は `GIT_DIR` を無視する** (独自に `.jj/repo` を解決) ため、`GIT_DIR` は gh だけを制御し jj 操作 (bookmark 自動補完 / push / rebase) には影響しない。monitor state は `` パス基準で解決されるため、`GIT_DIR` 設定下でもこの workspace に保たれる (並列 monitor の分離を壊さない)。 + +```sh +# Bash — $HOME=/c/Users/ (Unix 形式) なのでフォワードスラッシュ。フルパスは $HOME で隠蔽 +GIT_DIR="$HOME/work/claude-code-hook-test/.git" pnpm create-pr -- --title "..." --body-file __pr-body.md +GIT_DIR="$HOME/work/claude-code-hook-test/.git" pnpm merge-pr +GIT_DIR="$HOME/work/claude-code-hook-test/.git" .claude/cli-pr-monitor.exe --monitor-only +``` + +```powershell +# PowerShell — $HOME=C:\Users\ (Windows 形式) なのでバックスラッシュ +$env:GIT_DIR = "$HOME\work\claude-code-hook-test\.git" +pnpm create-pr -- --title "..." --body-file __pr-body.md +pnpm merge-pr +& .\.claude\cli-pr-monitor.exe --monitor-only +``` + +注意: +- **シングルクォート不可** — `GIT_DIR='$HOME\...'` は `$HOME` が展開されずリテラル文字列になり失敗する。必ずダブルクォートで囲む。 +- **`work/claude-code-hook-test` の構成は固定** — 他 PC で配置が違えば調整が必要 (`.jj/repo` からの動的導出は jj 内部の `.jj/` 基準相対パスが絡み fragile なため非採用)。 +- **Bash で exe を直接呼ぶ際はフォワードスラッシュ** (`.claude/cli-pr-monitor.exe`)。`.\.claude\...` (バックスラッシュ) は Bash がエスケープ解釈して `command not found` になる。 + +#### merge-pipeline / monitor の bookmark 誤検出に注意 + +`cli-merge-pipeline` / `cli-pr-monitor` は `lib-jj-helpers` の `BOOKMARK_SEARCH_REVSETS = ["@", "@-", "@--"]` を優先順に探索し、**最初に bookmark を持つ commit のものを PR として採用**する (trunk 系は除外)。 + +PR head 以外の non-trunk bookmark が `@` / `@-` / `@--` の近接 3 世代に存在すると、それを誤って PR 候補に選ぶ (初 PR 運用で、別タスクの todo commit に付けた bookmark を `@` に置いていたため merge-pipeline が PR head ではなくそちらを選び 2 回失敗した)。 + +- PR 操作 (push / create-pr / merge-pr) 時は、近接 3 世代に **対象 PR の bookmark だけ**が来るようにする +- 「PR の作業」と「別タスクのローカル commit」を同 workspace に同時に持つ場合、別タスクの bookmark は近接 revset から外す (PR head を `@` か `@-` に置く、または別タスク commit の bookmark を一時的に外す) +- これは「§ タスク領域の分割方針」がファイル衝突のみを論じ、**bookmark/revset の近接性衝突**を盲点としていた点の補足 + +#### 恒久対策の候補 (follow-up) + +`GIT_DIR` の手動前置は忘れると失敗するため、構造的解決を推奨 (投資対効果順): + +1. **cli-* exe 側で `GIT_DIR` 自動注入** — `cli-pr-monitor` / `cli-merge-pipeline` が `.git` 不在を検出したら `.jj/repo` からメインリポジトリを導出し、子プロセスの gh に `GIT_DIR` を自動設定する。ユーザーが `GIT_DIR` を意識せず `pnpm create-pr` がそのまま動く。最も投資対効果が高い +2. **direnv `.envrc`** — workspace ルートで `GIT_DIR` を自動エクスポート (direnv 導入が前提) +3. **workspace の colocated 化** — `jj workspace add` 後に当該 workspace を `.git` 付きにできるか jj の機能を調査。可能なら `GIT_DIR` 自体が不要 + ## 影響 ### 良い影響 diff --git a/docs/todo-summary.md b/docs/todo-summary.md index aea350e0..f96512ca 100644 --- a/docs/todo-summary.md +++ b/docs/todo-summary.md @@ -101,6 +101,9 @@ | 228 | 🔧 Tier 2 | **`rate_limit_signal::cr_clean` の regression test (PR #224 post-merge-feedback T2-1 採用)** | todo13.md | S | なし (Fix 3 で拡張した `unresolved_threads` / `new_comments` / `actionable_comments` 3 field の clean 判定の回帰防止、None/境界ケース網羅、silent-clean 誤認の保護) | | 231 | 💎 Tier 3 | **ADR-022 拡張 — pre-create cleanup flow 例 + agent fmt スコープ指針 (PR #224 post-merge-feedback T3-1 採用)** | todo13.md | S | なし (CodeRabbit が `create_fix_commit` の空 findings 設計を bug 誤判定=却下 CR#2、agent 無差別 fmt の 2 事象を ADR-022 責務分離で codify、doc-only) | | 232 | 🔧 Tier 2 | **post-merge-feedback / workflow agent の repo 作業ツリー書込禁止 + 検知安全網 (PR #224 セッション合意)** | todo13.md | S-M | なし (merge 時に analyze-session agent が repo root に throwaway script (parse_transcript.py) を残した、日常工程ゆえ累積リスク = コンテキスト汚染。(1) feedback facets に repo 書込禁止 + jq/scratch 使用を明記 (2) post_steps/Stop hook で root 新規 untracked を warning 検知 (3) gitignore は補助) | +| 233 | 🔧 Tier 2 | **post-pr-review (takt) の diff scope を PR 全体に修正 — `@` 限定による docs-only 誤判定解消 (PR #227 観測)** | todo13.md | M | なし (PR #227 で post-pr-review analyze が `@` コミット (docs のみ) の diff を見て PR を docs-only 誤判定し、CodeRabbit が PR 全体で出した finding (create_pr.rs:208) を ADR-035 docs-only filter で誤って適用外化。今回は finding も false positive (composition root) だったため実害なしだが有効 finding 見逃しリスク。根因は ADR-027 pre-push-review の `jj diff -r @` 由来 review-diff.txt 流用 or post-pr-review 独自 @ 限定 diff 生成の疑い。diff scope を PR 全体 (base..head) に修正 or 分類を CodeRabbit findings file path 基準に変更) | +| 234 | 💎 Tier 3 | **memory `feedback-di-over-ambient-global-tests` に serialization primitive 例外境界 + PR #227 具体例を追記 (PR #227 post-merge-feedback T3-1 採用)** | todo13.md | XS | なし (PR #227 が memory 原則「DI over ambient global」の 2 例目 = PR #224 env_override_lock も同根、Frequency Medium。(a) `PR_MONITOR_STATE_FILE_OVERRIDE` race → `state_path: &Path` DI 解消の具体例 (b) serialization primitive `OnceLock>` は複製禁止だが通常 test helper は複製推奨という例外境界、を追記。「DI over ambient global」と「helper 複製推奨」の見かけの矛盾を解消。順位 235 と相補) | +| 235 | 💎 Tier 3 | **ADR-022 に Serialization Primitive Single-Instance Rule の Appendix 追加 (PR #227 post-merge-feedback T3-2 採用)** | todo13.md | S | なし (PR #224 T2-2 共有 env_override_lock helper 抽出 + PR #227 で同根の serialization primitive 単一化問題 2 PR 観測 = Frequency Medium。`OnceLock>` 等を複製すると各々独立した Mutex になり競合排除が破壊される特殊ケースを ADR-022 Appendix で明文化、通常 helper 複製推奨 (DRY) との例外境界を codify。ADR-046 独立化 (feedback T3-3) との overlap は着手時判断、順位 234 と相補) | **戦略**: Tier 1 を 2〜3 セッションで片付け → Tier 2 で ADR-032 の前提 + rate-limit + convergence cost 削減を進める → Tier 3 で ADR-032 を land + ドキュメント整備。Tier 4-5 は cleanup / 外部展開で daily efficiency への直接効果は小さい。 diff --git a/docs/todo13.md b/docs/todo13.md index 12bd05bf..28137121 100644 --- a/docs/todo13.md +++ b/docs/todo13.md @@ -194,6 +194,102 @@ --- +### post-pr-review (takt) の diff scope を PR 全体に修正 — `@` コミット限定による docs-only 誤判定の解消 (PR #227 観測) + +> **動機**: PR #227 (cli-pr-monitor flaky 修正 2 件 + docs 整理、3 commit) の post-pr monitor で、takt `post-pr-review` の analyze が PR を **docs-only と誤判定**した。実際は spvtqwor (create_pr.rs の tempfile 化) / qzpwsyzr (state path DI) の Rust 変更を含むが、analyze が見た diff は `@` コミット (`docs/todo*.md` のみ) だった。その結果、CodeRabbit が PR 全体 (`create_pr.rs:208`) を見て出した finding を ADR-035 docs-only filter で「適用外」と**誤フィルタ**した。今回は finding 自体も false positive (composition root のため DI 不要) だったため実害はなかったが、**有効な finding を見逃すリスク**がある。 +> +> **本タスクの位置づけ**: PR #227 セッション観測 (2026-06-30)、ユーザー判断で todo 登録。CodeRabbit が PR 全体 (base..head) を見るのに対し takt の判定 diff が `@` 限定で、findings と local diff scope が構造的に不整合になる点が核心。 +> +> **参照**: PR #227、`.takt/review-comments.json` (findings = `create_pr.rs:208`)、push runner ログ `[diff] 実行: jj diff -r @` / `review-diff.txt (68 行)`、ADR-027 (push-time review は `@` の simplicity 限定、architectural review は post-PR CodeRabbit に委ねる)、ADR-035 (docs-only 評価ポリシー — classify の入力 diff scope を誤ると誤適用)、cli-pr-monitor の `post-pr-review` 起動箇所 (`stages/takt.rs` 周辺)。 +> +> **実行優先度**: 🔧 **Tier 2** — Effort M。診断 (diff scope の生成箇所特定) + 修正。実害は CodeRabbit がフル PR を見るため現状限定的だが、自動フィルタの信頼性に関わる。 + +#### 設計決定 (案) + +- **(A)** post-pr-review の analyze に渡す diff を PR 全体 (`master..@` または PR base..head) に変更する。`@` 限定の pre-push-review (ADR-027) とは射程が異なる (post-PR は PR 全体を評価すべき) ことを明示。 +- **(B)** または docs-only 分類を local diff でなく **CodeRabbit findings の file path 基準** に切り替える (findings が code file を指すなら docs-only にしない)。 +- pre-push-review (ADR-027 = `@` 限定 simplicity) と diff 生成を共有しているなら、post-pr-review 専用に分離する。 + +#### 作業計画 + +- [ ] post-pr-review が docs-only 判定に使う diff の生成箇所を特定 (`review-diff.txt` 流用 or 独自生成) +- [ ] diff scope を PR 全体に修正、or 分類基準を findings file path に変更 +- [ ] dogfood: code + docs 混在 PR で docs-only 誤判定しないことを確認 +- [ ] 本 entry 削除 + todo-summary.md 行削除 + +#### 完了基準 + +- code 変更を含む PR が post-pr-review で docs-only と誤判定されず、code file を指す CodeRabbit finding が ADR-035 filter で誤って適用外にされない。 + +#### 詰まっている箇所 + +- diff scope を PR 全体にする際、pre-push-review (ADR-027 = `@` 限定 simplicity) との設定/生成共有部分に影響しないか。post-pr-review 専用に diff 生成を分離する必要があるか。 + +--- + +### memory `feedback-di-over-ambient-global-tests` に serialization primitive 例外境界 + PR #227 具体例を追記 (PR #227 post-merge-feedback T3-1 採用) + +> **動機**: PR #227 (cli-pr-monitor 並列テスト flaky 修正) の post-merge-feedback で採用候補 (T3-1) として浮上。既存 memory `feedback-di-over-ambient-global-tests` の「DI over ambient global」原則が、直感的には「通常 test helper は複製推奨」原則と矛盾するように見える問題を解消する。PR #227 は本原則の 2 例目 (PR #224 の env_override_lock 関連も同根)。 +> +> **本タスクの位置づけ**: PR #227 post-merge-feedback Tier 3 #1 採用候補 (Severity Low / Frequency Medium / Effort XS / Adoption Risk None)。ユーザー承認で todo 登録 (2026-06-30)。 +> +> **参照**: `.claude/feedback-reports/227.md` Tier 3 #1、memory `~/.claude/projects/C--Users-owner-work-ccht-improve/memory/feedback-di-over-ambient-global-tests.md`、PR #227 (state path DI)、PR #224 (env_override_lock)。 +> +> **実行優先度**: 💎 **Tier 3** — Effort XS。memory への数行追記。 + +#### 設計決定 (案) + +- (a) `PR_MONITOR_STATE_FILE_OVERRIDE` race → `state_path: &Path` DI での解消を具体例として列挙。 +- (b) 「serialization primitive (`static LOCK: OnceLock>`) は複製禁止、通常 test helper は複製推奨」という例外境界を明示。 + +#### 作業計画 + +- [ ] memory `feedback-di-over-ambient-global-tests.md` に (a) 具体例 + (b) 例外境界を追記 +- [ ] 本 entry 削除 + todo-summary.md 行削除 + +#### 完了基準 + +- memory に PR #227 の DI 具体例と serialization primitive 例外境界が明記され、「DI over ambient global」と「test helper 複製推奨」の見かけの矛盾が解消される。 + +#### 詰まっている箇所 + +- 特になし (memory 編集のみ)。順位 235 (ADR-022 Appendix) と内容が相補的なので同時着手が効率的。 + +--- + +### ADR-022 に Serialization Primitive Single-Instance Rule の Appendix 追加 (PR #227 post-merge-feedback T3-2 採用) + +> **動機**: PR #227 と PR #224 T2-2 (共有 env_override_lock helper 抽出) で同根の serialization primitive 単一化問題が 2 PR 観測 (Frequency Medium)。`OnceLock>` 等の serialization primitive をプロセス内で複製すると各々が独立した Mutex になり競合排除機能が破壊される。通常の helper function 複製推奨 (DRY) との例外境界が ADR-022 に未明文化。 +> +> **本タスクの位置づけ**: PR #227 post-merge-feedback Tier 3 #2 採用候補 (Severity Low / Frequency Medium / Effort S / Adoption Risk None)。ユーザー承認で todo 登録 (2026-06-30)。 +> +> **参照**: `.claude/feedback-reports/227.md` Tier 3 #2、`docs/adr/adr-022-automation-responsibility-separation.md`、PR #224 T2-2 (env_override_lock)、PR #227 (state path DI)、順位 234 (memory 拡張、相補)。 +> +> **実行優先度**: 💎 **Tier 3** — Effort S。ADR-022 への Appendix 追加。 + +#### 設計決定 (案) + +- ADR-022 に Appendix「Serialization Primitive Single-Instance Rule」を追加: + - `OnceLock>` 等の serialization primitive はプロセス内で単一化必須。 + - 複製すると各々が独立した Mutex になり競合排除機能が破壊される特殊ケース。 + - 通常の helper function 複製推奨 (DRY) との例外境界を明文化。 + +#### 作業計画 + +- [ ] ADR-022 に Serialization Primitive Single-Instance Rule の Appendix 追加 +- [ ] 順位 234 (memory) と cross-reference +- [ ] 本 entry 削除 + todo-summary.md 行削除 + +#### 完了基準 + +- ADR-022 に serialization primitive 単一化原則が明文化され、PR #224/#227 で観測された複製による競合排除破壊が構造的に予防される。 + +#### 詰まっている箇所 + +- ADR-022 (自動化コンポーネントの責務分離) の主題と serialization primitive (test isolation) がやや別軸。Appendix として追加するか、ADR-046 (feedback-reports T3-3 様子見) として独立させるかは着手時判断。 + +--- + ## 既知課題 (記録のみ、本セッションで未対応) (現時点で本ファイルへの既知課題は無し。docs/todo10.md / todo9.md 末尾を参照。)