Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions docs/adr/adr-045-jj-workspace-parallel-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 結果から本採用 / 修正 / 却下を判定。

Expand Down Expand Up @@ -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 のため不要 — この非対称が初回検証で見落とされた |

## 検討した選択肢

Expand All @@ -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 運用時の追加設定」)
- **採用**

## 決定 (試験運用)
Expand Down Expand Up @@ -95,11 +96,68 @@ src の大規模分割 (メイン) と lint/facet/docs (改善) は編集領域

### マージ方法 (各 workspace で独立)

- 各 workspace で通常どおり `jj describe` → `jj bookmark create <name>` → `pnpm push` → `pnpm create-pr` → `pnpm merge-pr` を実行する。
- 各 workspace `jj describe` → `jj bookmark create <name>` → `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 は `<exe>` パス基準で解決されるため、`GIT_DIR` 設定下でもこの workspace に保たれる (並列 monitor の分離を壊さない)。

```sh
# Bash — $HOME=/c/Users/<user> (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\<user> (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` 自体が不要

## 影響

### 良い影響
Expand Down
3 changes: 3 additions & 0 deletions docs/todo-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mutex<()>>` は複製禁止だが通常 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<()>>` 等を複製すると各々独立した 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 への直接効果は小さい。

Expand Down
96 changes: 96 additions & 0 deletions docs/todo13.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mutex<()>>`) は複製禁止、通常 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<Mutex<()>>` 等の 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<Mutex<()>>` 等の 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 末尾を参照。)