From 3912bcf696b7290773eb2a4b5c616d68b13a7ad4 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 13:32:46 +0000 Subject: [PATCH 01/10] =?UTF-8?q?Docs:=20#542=20#786=20cross-review=20?= =?UTF-8?q?=E3=81=AE=E3=83=A9=E3=82=A6=E3=83=B3=E3=83=89=E3=82=92=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99=E8=A8=AD=E8=A8=88=EF=BC=88=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E3=83=BB=E8=A8=AD=E8=A8=88=E3=83=BB=E6=B1=BA=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E8=A8=98=E9=8C=B2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design-decisions.md | 128 ++++++++ issues/issue-542-786-design.md | 391 +++++++++++++++++++++++ issues/issue-542-786-requirements.md | 154 +++++++++ 3 files changed, 673 insertions(+) create mode 100644 issues/issue-542-786-design-decisions.md create mode 100644 issues/issue-542-786-design.md create mode 100644 issues/issue-542-786-requirements.md diff --git a/issues/issue-542-786-design-decisions.md b/issues/issue-542-786-design-decisions.md new file mode 100644 index 000000000..aee2154ef --- /dev/null +++ b/issues/issue-542-786-design-decisions.md @@ -0,0 +1,128 @@ +# #542 #786: cross-review のラウンドを減らす — 決定の記録 + +設計は [issue-542-786-design.md](issue-542-786-design.md)、要求は [issue-542-786-requirements.md](issue-542-786-requirements.md) にある。決定の根拠にした実測は末尾の「判断に使った実測」にまとめた。 + +## 決定 + +### 決定 1: cross-review の既定の母集合から agy を外し、claude / codex / kiro とホストにする + +agy が担当になると、テストを背景で起動して結果を残さずに終わることがある。起動し直しても残らなければ `final = error` で +収束が止まる(#786)。実運用は既に毎回 `--exclude agy` を渡しており、既定を運用に合わせる。形は cross-refactoring の +`DEFAULT_REFACTOR_RUNTIMES`(#664)にそろえる。「ホストを除いた既定の表」と、ホストを足す関数に分ける。ホストが agy の +ときは母集合に入る(#892 の「ホストも輪番に入る」を保つ)。 + +確定仕様 `cross-review-participants-and-seats.md` の「既定の母集合から agy を外さない。外すかは #786 が扱う」は、 +この決定で置き換わる。プロンプトの指示(決定 11)だけで直すことは採らなかった。担当が指示に従うかを確かめる手段が +無く、従わなかったときの損失(収束の停止)が大きいためである。 + +### 決定 2: 既定の母集合に無い者を外す指定は、止めずに無視して 1 行で知らせる + +今は母集合に無い者を `--exclude` に書くと `AssignmentError` で止まる。決定 1 の後は、今までの `--exclude agy` が +すべて止まる。`CLAUDE.md` の cross-review 節も利用者の手元の手順も、`--exclude agy` を書いている。そのため止めずに無視する。 +綴りの誤りは今どおり止める(外したつもりの者が残る誤りは、名前の綴りでしか起きないため)。 + +無視した名前を `excluded` に入れることは採らなかった。外した者と外す必要が無かった者が同じ記録になり、後から +母集合を読み違える。 + +### 決定 3: 1 ラウンド目から「見つけた指摘はすべて出す」を求める + +今のプロンプトには、見落としを残さないことを求める文が無い。`launch-reviewer.sh:166` の「全件」は、結果ファイルへ +全件を書く指示である。1 ラウンド目で出し切らない指摘は、修正の後のラウンドで「新しい指摘」として数えられ、 +ラウンドを 1 つ増やす。#542 は、出力が増えて agy の無進捗の打ち切りに当たることを懸念した。決定 1 で agy が既定から +外れるため、既定では当たらない。 + +出す対象は今のまま「修正アクションのある指摘」に限る。nit や良い点まで網羅させることは採らなかった。出力が増えても +ラウンドは減らない(nit は収束を止めない)。 + +### 決定 4: 2 ラウンド目以降は、前のラウンドからの変わったファイルの一覧と差分を読むコマンドを渡し、差分の本文は渡さない + +後のラウンドの指摘の多くは前の修正が作った穴である(#542 の PR #539、devbase PR #163)。担当に「どこが変わったか」と +「変わった節とそれを参照する節を先に見る」を渡す。1 つ後のラウンドで出るはずの穴を、同じラウンドで拾える。 +#542 の案のうち、出力を増やさずに効く「修正の後のラウンドへ観点を渡す」を採った。 + +差分の本文を埋め込むことは採らなかった。設計 PR の修正は 1 ラウンドで数百行になり、2 席 × ラウンド数だけプロンプトが +増える。担当は作業ツリーで `git diff` を実行できる。「指摘が採られたときに生じる経路も書け」を求めることも +採らなかった。修正の形を担当が予測する必要があり、当たらないことがある(#542)。 + +### 決定 5: 設計 PR 向けの観点テンプレートを足し、設計の成果物は増やさない + +設計 PR は今 `common` と `docs_only` にしか分類されない。設計の文書に特有の穴を見る観点が渡っていない。特有の穴は、 +3 文書の食い違い・状態の書き手の衝突・外部ツールの挙動の事実誤認の 3 つである。devbase PR #163 では事実誤認 1 件が 2 ラウンドを生んだ。振り返りは +「経路の一覧を表にしておけば 1 巡で出し切れた」と書いている。観点はレビューの側に置く。 + +`design` の成果物に「状態を書き換える経路の一覧」を足すことは採らなかった。対象になる設計を見分ける基準が要り、 +すべての設計の手間が増える。レビューの観点なら、状態を持たない設計では当たらずに済む。 + +見分けはファイル名の規約(`issues/` 配下の `-requirements.md` / `-design.md` / `-design-decisions.md`)で行う。 +ブランチ名(`design/`)で見分けることは採らなかった。観点を組むのは PR の変更ファイルからであり、ブランチ名は +状態ファイルの分類の入力に無い。 + +### 決定 6: 既存コメントの控えをラウンドの開始ごとに取り直し、失敗しても止めない + +控えは `init` の 1 回しか取っていない。次のラウンドの担当は、同じ実行の前のラウンドの指摘と「対応しました」の返信を +知らない。直った指摘の近くを別の言い方で再び指摘すると、振動の検知(同じ位置の重なり 0.5 以上)に当たる。 +#908 の実行 2 と #911 は振動で打ち切られた。 + +取り直しに失敗しても止めないのは、前の控えでも今と同じ条件でレビューできるためである。`init` の失敗は今どおり +止める(控えが無いと重複の検出が働かない)。控えを圧縮することは範囲外にした。取り直しで増えるのは同じ実行の +前のラウンドの行だけで、まず大きさを実装の PR で見る。 + +### 決定 7: 新しい指摘が minor だけのラウンドで収束し、minor は最終スイープで直す + +今の収束は「全員 pass か、新しい指摘が 0 件」で、minor だけの新しい指摘でも次のラウンドへ進む。次のラウンドは +その minor の修正が作った穴を探すことになり、ラウンドが続く。最終スイープ(Step 7.5)は既に、どの終わり方でも +残った minor と nit を直す。open のスレッドは 0 件になる。minor だけのラウンドの修正をこのスイープへ寄せれば、 +1 ラウンド分(2 席の起動・反証・判定・修正)が減る。 + +判定は証拠集約を通ったラウンドだけで行う。重要度は、反証と実行の検証を経た `review_findings` にしか無い。担当の +自己申告の重要度だけで収束させることは採らなかった。`_is_pass`(COMMENT で critical と major が 0 件なら pass)を +REQUEST_CHANGES にも広げることも採らなかった。担当の判定と新しさの判定が混ざり、前のラウンドから引き継いだ +major を見落とす。 + +### 決定 8: 母集合の全員が見るまで収束しない条件(#631)は採らない + +#631 は、1 ラウンドで収束すると母集合の 1 者が PR を一度も見ないことを問題にし、収束の条件へ網羅を足す提案である。 +ラウンドを増やす方向で、#542 と逆向きになる。費用は 1 ラウンドで収束した 37 件のうち 29 件にもう 1 ラウンドずつ +(約 4 時間)である。 + +#631 が根拠にした取りこぼし(devbase#171 と #191)は、round 1 が agy + kiro で codex が入らなかった形である。 +決定 1 の後の既定では、ホストが agy 以外なら round 1 は必ず codex + kiro になり、この形は起きない。見ないまま +収束し得るのは round 1 に座らない 1 者(ホストが claude なら claude)だけになる。 + +#631 の代案のうち「round 1 に codex を優先して入れる」は、決定 1 の固定順で満たされるため、`review_seats` を +変えずに済む。#631 の課題は、この決定を記録して閉じる(閉じるのは実装の後)。 + +### 決定 9: 結果を残さなかった担当を別のランタイムへ振り替えることは、この変更に含めない + +#786 の対処の候補のもう 1 つである。決定 1 で既定の担当から agy が外れ、残る担当の結果なしは手元の記録で +#819 の codex(利用上限)の 2 件だけである。利用上限は振り替えても同じ実行の中で再び当たり得るため、振り替えの +規則(いつ・誰へ・何回)は別に決める。置き場所の候補の #870(drive の pause と再開)は、本文でこれを扱っていない。 +範囲外の課題 #919 として起票した。 + +### 決定 10: 振動の検知・`max_rounds`・起動し直しの規則は変えない + +振動の閾値(0.5)と近傍の幅(3 行)には実測の根拠がまだ無い(`state.py` のコメント)。この変更は振動の原因の +1 つ(前のラウンドを知らない担当が同じ場所を再び指摘する)を決定 6 で減らす。閾値を同時に変えると、どちらが +効いたかを分けられない。`max_rounds` を下げてラウンドを切ることは、減らしたのではなく打ち切っただけになるため +採らなかった。 + +### 決定 11: レビュー担当にはテストも背景の処理も起動させず、適用担当にはテストを前景で待たせる + +レビューの実行の確かめは、進行側の `verify-findings` が行う。担当は確かめる手順を `suggested_check` に書く。 +担当がテストを走らせても判定には使われず、背景で起動すると結果を書かずに終わる経路になる(#786)。cross-refactoring の +適用担当は、ラウンドごとに 1 回テストを実行する設計である(`apply.md` の「適用ラウンドの単位で 1 回」)。そのため +実行を禁じず、前景で終わるまで待つことを求める。 + +cross-refactoring の既定の参加者からは、#664 で agy を外してある。この指示が効くのは、`--include agy` で戻したときと、 +他の担当が同じ形で終わるときである。 + +## 判断に使った実測 + +| 対象 | 値 | 出所 | +| --- | --- | --- | +| cross-review 88 件のラウンド数(2026-09-01〜09-15) | 1 ラウンド 42%(中央値 8.1 分)/ 2 ラウンド 28%(16.2 分)/ 3 以上 30%(50.2 分、時間の 70%) | #542(集計の方法は #662) | +| PR #539 の 10 巡の指摘 | 15 → 6 → 2 → 2 → 5 → 2 → 0 → 2 → 3 → 0。3 巡目以降の指摘は、分類した分がすべて前の修正が作った穴 | #542 | +| 直近の設計 PR の実行ごとの最大ラウンド | #908: 8 / 8 / 5、#843: 7(振動で打ち切り)、#911: 3(振動で打ち切り)、#794: 8、#890: 5 / 3、#902: 3 | 状態ファイルとレビューの見出し | +| 2 ラウンド目以降のプロンプト | 前のラウンドからの変更を渡していない。既存コメントの控えは `init` の時点のもので、#908 の実行 3 の round 5 のプロンプトに同じ実行の round 1〜4 の指摘が入っていない(プロンプト 61,962 バイトのうち控え 54,957 バイト) | `launch-reviewer.sh` と退避された作業ツリー | +| 担当の運用 | 2026-09-22 の #803 以降、ほぼすべての実行が `--exclude agy` で codex + kiro の 2 者 | 状態ファイルの `participants` | +| agy の結果なし | devbase v3.6.0 で 8 回(cross-review 5・cross-refactoring 3)。ai-plugins の手元の記録では NO_RESULT 1 件(#794)、STALLED 0 件 | #786、`monitor-outcomes.jsonl` | diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md new file mode 100644 index 000000000..c999078b9 --- /dev/null +++ b/issues/issue-542-786-design.md @@ -0,0 +1,391 @@ +# #542 #786: cross-review のラウンドを減らす — 設計 + +要求と受け入れ条件は [issue-542-786-requirements.md](issue-542-786-requirements.md) にある。決定の理由は +[issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある。この文書は「どう作るか」だけを扱う。 + +## 例: 同じ PR の 2 ラウンド目で担当が受け取るもの + +設計 PR(`issues/issue-900-design.md` を変える)の 1 ラウンド目で major 3 件・minor 2 件が出て、修正が +2 コミット入った後の 2 ラウンド目。`start-round` は次の 2 つを書く。 + +- 既存コメントの控え(取り直し): 1 ラウンド目の 5 件の指摘と、各件への「対応しました()」の返信の行 +- 前のラウンドからの変更: `cross-review-pr900-round2-changes.md` + +```markdown +## 前のラウンドからの変更 + +前のラウンド(round 1)の head `1a2b3c4` から今の head `5d6e7f8` までに、次のファイルが変わった。 + +- issues/issue-900-design.md +- issues/issue-900-requirements.md + +差分は作業ツリーで `git diff 1a2b3c4 5d6e7f8` を実行して読む。 +**変わった節と、それを参照する節・同じ契約を使う節を先に見る。** 修正が新しく作った経路(状態・分岐・引数)に +穴が無いかを確かめる。直った指摘を繰り返さない(既存コメントの控えに返信がある)。 +``` + +担当のプロンプトには、この節と、控えと、観点テンプレート(`common` / `docs_only` / `design`)が入る。2 ラウンド目で +新しい指摘が minor 1 件だけなら、`judge` は `NEW_FINDINGS=1` / `NEW_BLOCKING_FINDINGS=0` を出して収束する。 +minor 1 件は最終スイープが直す。 + +## 機能一覧 + +| # | 機能 | 誰が使うか | +| --- | --- | --- | +| F1 | 既定の母集合を claude / codex / kiro とホストにする | `init`(cross-review) | +| F2 | 既定の母集合に無い者を外す指定を、止めずに無視する | `init`(cross-review / cross-refactoring) | +| F3 | ラウンドの開始ごとに既存コメントの控えを取り直す | `start-round` | +| F4 | 前のラウンドからの変更の節を書く | `start-round` → `launch-reviewer.sh` | +| F5 | 出し切りの指示・テストと背景の処理を起動しない指示をプロンプトに入れる | `launch-reviewer.sh` | +| F6 | 設計 PR を分類し、設計向けの観点を渡す | `init`(観点の組み立て) | +| F7 | 新しい指摘が minor だけなら収束する | `judge` | +| F8 | minor だけの収束を報告に出す | `report` | +| F9 | 適用担当に、背景で起動したテストを残して終わらないよう求める | cross-refactoring の適用のプロンプト | + +## 構成要素 + +| 要素 | 責務 | 変更 | +| --- | --- | --- | +| `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す | +| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・judge・report | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。judge が止める指摘を数えて収束を判定し、ラウンドの記録に残す。`_resolve_reviewers` が無視した除外を 1 行で出す。report が minor だけの収束を出す | +| `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す | +| `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | +| `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | +| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節 | +| テスト | 下の「テスト設計」 | 既存の母集合のテストを直し、新しい分岐のテストを足す | + +変えないもの: + +| 対象 | 持つもの | +| --- | --- | +| `review_seats` | 輪番の式 | +| `_new_finding_count` | 新しい指摘の数え方 | +| `cmd_check_oscillation` | 振動の検知 | +| `_handle_no_result_round` | 結果が無い担当の起動し直し | +| `fix/scripts/fetch-pr-comments.sh` | 控えの形 | +| `refactor_pool` | cross-refactoring の母集合 | + +### 構成要素図 + +```mermaid +graph LR + subgraph lib["plugins/ndf/scripts/lib"] + A["assignment.py
DEFAULT_REVIEW_RUNTIMES
review_pool / resolve_participants"] + end + subgraph cr["cross-review/scripts"] + S["state.py
init / start-round / judge / report"] + L["launch-reviewer.sh"] + end + subgraph rf["cross-refactoring"] + SU["setup.py"] + AP["prompts/apply.md"] + end + F["fix/scripts/fetch-pr-comments.sh"] + G[("GitHub API")] + W[("作業ツリー
.cross_review/")] + S -->|母集合・参加者| A + SU -->|参加者| A + S -->|控えの取得| F + F --> G + S -->|控え・変更の節・state.json| W + L -->|読む| W +``` + +### システムの文脈と配置 + +変更はすべて利用者の手元で動くスクリプトの中に閉じる。外との境界は GitHub API(控えの取得。今は `init` の 1 回、 +変更後はラウンドごと)と、担当の CLI(codex / kiro / claude / agy。プロンプトのファイルを受け取る)の 2 つで、 +どちらの境界の形も変わらない。担当の CLI の数が既定で 1 つ減る(agy)。 + +```mermaid +graph LR + U["進行側(ホストの LLM)"] -->|state.py のコマンド| S["state.py"] + S -->|gh api| G[("GitHub")] + S -->|launch-reviewer.sh| C["担当の CLI
claude / codex / kiro
(agy は --include)"] + C -->|payload / result.json| S +``` + +### パッケージ・モジュール構成 + +```text +plugins/ndf/ +├── scripts/lib/assignment.py # 変える +├── scripts/tests/test_lib_assignment.py # 変える +└── skills/ + ├── cross-review/ + │ ├── SKILL.md # 変える + │ ├── docs/02-fix-and-rotation.md # 変える + │ ├── docs/05-pool-and-convergence.md # 変える + │ ├── docs/06-evidence.md # 変える + │ ├── scripts/state.py # 変える + │ ├── scripts/launch-reviewer.sh # 変える + │ └── tests/ # 変える・足す + └── cross-refactoring/ + ├── prompts/apply.md # 変える + ├── scripts/refactor_lib/commands/setup.py # 変える + └── tests/test_assignment.py # 変える +``` + +## 構造 + +変える型は `assignment.Participants`(参加者の解決の結果)の 1 つで、項目を 1 つ足す。 + +```mermaid +classDiagram + class Participants { + +list~str~ pool + +list~str~ included + +list~str~ excluded + +list~str~ ignored_exclude + +list~str~ available + +dict~str,str~ unavailable + +bool probe_skipped + +bool require_all + } + note for Participants "ignored_exclude を足す。既定は空。\n外した者(excluded)とは別に持つ" +``` + +## 入出力の契約 + +### 母集合と参加者(`assignment.py`) + +```python +DEFAULT_REVIEW_RUNTIMES = ("claude", "codex", "kiro") # ホストを除いた部分。agy は --include で足す + +def review_pool(host: str) -> list[str]: + # ホストになれない名前は今と同じく AssignmentError + return _in_fixed_order((*DEFAULT_REVIEW_RUNTIMES, host)) +``` + +| ホスト | `review_pool` | 引数なしの座席(round 1 / 2 / 3) | +| --- | --- | --- | +| claude / codex / kiro | `[claude, codex, kiro]` | codex+kiro / claude+kiro / claude+codex | +| agy | `[claude, codex, agy, kiro]` | codex+agy / agy+kiro / claude+kiro(今の既定と同じ輪番) | + +`resolve_participants` の除外の検査を次のように変える。 + +| 除外に書いた名前 | 今 | 変更後 | +| --- | --- | --- | +| 母集合か `--include` にある | 外す | 外す(同じ) | +| 綴りの正しいランタイム名で、母集合にも `--include` にも無い | `AssignmentError`(終了コード 1) | 外さずに無視し、`Participants.ignored_exclude` に固定順で残す | +| 綴りの誤り | `AssignmentError` | `AssignmentError`(同じ) | + +`Participants` に `ignored_exclude: list[str]`(既定は空)を足す。呼び出し側(cross-review の `_resolve_reviewers`、 +cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準エラーへ出して続ける。 + +```text +ℹ --exclude agy は既定の母集合に無いため無視しました(母集合: claude, codex, kiro) +``` + +状態ファイルの `participants` には `ignored_exclude` を書き足す(`excluded` には入れない。外した者と区別するため)。 + +### 既存コメントの控え(`state.py`) + +```python +def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | None: + """fetch-pr-comments.sh を呼び、成功なら path へ書いて None、失敗なら理由の文を返す。""" +``` + +| 呼ぶ場所 | 失敗したとき | +| --- | --- | +| `init` の新規開始(今の場所) | 今と同じく `die`(終了コード 1) | +| `start-round`(2 ラウンド目以降。ラウンドを開いた後、担当を起動する前) | 前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | + +1 ラウンド目は `init` が取った直後であるため取り直さない。控えの形(1 行 1 件、`[PR-COMMENT]` などの接頭辞)は変えない。 + +### 前のラウンドからの変更の節(`state.py start-round` → `launch-reviewer.sh`) + +`start-round` はラウンドを開いた後に `$TMP_DIR/cross-review-pr-round-changes.md` を扱う。 + +| 条件 | ファイル | +| --- | --- | +| 同じ PR の前のラウンドが無い(1 ラウンド目・PR の切り替え直後) | 書かない | +| 前のラウンドか今のラウンドの `head_sha` が無い | 書かない | +| 2 つの `head_sha` が同じ | 書かない | +| 違う | 書く(形は冒頭の例) | +| `git diff --name-only <前> <今>` が失敗した | 書かず、`⚠ 前のラウンドからの変更を取れませんでした` を出して続ける | + +- 変わったファイルは `git -C diff --name-only <前> <今>` の出力の順に並べ、50 件を上限にする。超えた分は + 「ほか N 件」の 1 行にする +- 差分の本文は書かない +- 同じ名前のファイルが前の起動で残っていれば、書かない場合も消す(古い節を次のプロンプトへ入れないため) + +`launch-reviewer.sh` は、このファイルが空でなければ中身を「既存コメントスナップショット」の節の直後へ入れる。 +無ければ何も入れない。 + +### プロンプトに足す指示(`launch-reviewer.sh`) + +| 置く節 | 足す指示 | +| --- | --- | +| `## 指摘に含めてはいけないもの` の直前 | 「見つけた指摘はこのラウンドですべて出す。次のラウンドへ回さない。重要度が minor のものも書く」 | +| `## 守るべきこと` | 「テストを実行しない。実行して確かめる手順は `suggested_check` に書く(進行側の `verify-findings` が実行する)」「背景で処理を起動しない。起動した処理の終わりを待たずに結果のファイルを書かないまま終わると、結果が無い担当として扱われる」 | + +### 設計向けの観点(`state.py`) + +分類の述語 `_is_design_doc_path(path)` は、次の 2 つを満たすパスで真を返す。 + +- `issues/` で始まる +- ファイル名が `-requirements.md` / `-design.md` / `-design-decisions.md` のどれかで終わる + +2 つの表へ 1 行ずつ足す。 + +| 表 | 足す行 | +| --- | --- | +| `PATH_CATEGORY_RULES` | `("design", _is_design_doc_path)` | +| `CATEGORY_TEMPLATES` | `"design": DESIGN_REVIEW_TEMPLATE` | +`docs_only` の判定は変えない +(設計 PR は `common` / `docs_only` / `design` の 3 つになる)。 + +```text +### 設計 PR +- 要求・設計・決定の記録の 3 文書で、受け入れ条件ごとに設計の要素とテスト設計の行があるか。決定で退けた案が他の節に残っていないか +- 状態(ファイル・環境変数・状態ファイルの項目・引数)ごとに、書き手と読み手を並べる。同じ状態を 2 つの経路が書く・読む側が書く側より先に動く・失敗した書き手の後に読む、の矛盾が無いか +- 外部コマンド・外部ツールの挙動(優先順位・終了コード・一致の範囲)を断定する記述に、実測の根拠(コマンドと出力)があるか +``` + +### judge の出力と状態ファイル + +`judge` の状態表示に 1 行を足す。今の行の形と順は変えない。 + +```text +REVIEWER_INTENTS='codex=REQUEST_CHANGES kiro=COMMENT' +NEW_FINDINGS=1 +NEW_BLOCKING_FINDINGS=0 +CARRIED_OVER_THREADS=0 +PENDING_POSTS=0 +``` + +| 値 | 条件 | +| --- | --- | +| 件数 | 証拠集約を通ったラウンドで、新しい指摘のうち重要度が critical か major のものの数 | +| `-` | 証拠集約を通っていない・指摘の記録を読めない(今の `NEW_FINDINGS=-` と同じ時と、証拠集約を通っていない時) | + +ラウンドの記録(`rounds[]` の要素)に 2 項目を足す。旧い状態ファイルに無くても読める。 + +| 項目 | 型 | 値 | +| --- | --- | --- | +| `new_blocking_findings` | int か null | 上の件数。`-` のときは null | +| `converged_by` | 文字列か無し | 収束したラウンドだけ。`all_pass`(全員 pass)/ `no_new_findings`(新しい指摘 0 件)/ `minor_only`(新しい指摘が minor だけ) | + +`report` は `converged_by = minor_only` のラウンドについて「round N は新しい指摘が minor だけで収束した(M 件を +最終スイープへ回した)」の 1 行を出す。M はそのラウンドの新しい指摘の件数(`NEW_FINDINGS`)。 + +## 処理の流れ + +### 1 ラウンド(変わる段を太字にする) + +図に含めないもの: cross-refactoring の `setup.py` と `prompts/apply.md`(cross-review のラウンドを通らない。 +変わるのは参加者の解決の 1 行の表示と、適用担当への指示の文だけである)。 + +```mermaid +sequenceDiagram + participant P as 進行側 + participant S as state.py + participant G as GitHub + participant L as launch-reviewer.sh + participant C as 担当 2 者 + P->>S: start-round + S->>S: 作業ツリーを head へ同期・ラウンドを開く + S->>G: **控えを取り直す(2 ラウンド目以降)** + S->>S: **変更の節のファイルを書く / 消す** + P->>L: 席ごとに起動 + L->>C: プロンプト(控え・**変更の節**・観点・**出し切りの指示**) + C-->>S: payload / result.json + P->>S: read-result・verify-findings・critique + P->>S: judge + S-->>P: **NEW_BLOCKING_FINDINGS** と終了コード + alt 0(収束。**minor だけを含む**) + P->>P: 最終スイープ(minor を直す)→ verify-sweep → report + else 2(止める指摘がある) + P->>S: check-oscillation → 修正 → merge-fix → 次のラウンド + end +``` + +### judge の収束の判定 + +```mermaid +graph TD + A["引き継いだ指摘がある"] -->|はい| X["続ける(2)"] + A -->|いいえ| B["全員 pass"] + B -->|はい| Y1["収束 all_pass"] + B -->|いいえ| C["新しい指摘を測れた"] + C -->|いいえ| X + C -->|はい| D["新しい指摘が 0 件"] + D -->|はい| Y2["収束 no_new_findings"] + D -->|いいえ| E["止める指摘を測れた(証拠集約を通った)"] + E -->|いいえ| X + E -->|はい| F["止める指摘が 0 件"] + F -->|はい| Y3["収束 minor_only"] + F -->|いいえ| X +``` + +収束した後の段(待ち行列が残れば 8、CI がコードの失敗なら 2、それ以外は `final = approved` で 0)は今と同じである。 + +### 止める指摘の数え方 + +`_new_finding_count` と同じ母集合と一致の判定を使う。違うのは、新しいと判定した指摘のうち重要度が critical か +major のものだけを数える点である。重要度は `review_findings` の要素の `severity` から読む(証拠集約を通った +ラウンドだけが持つ)。束ねられた指摘(`merged_into`)は数えず、代表の重要度(束ねたときに高い方へ揃えてある)を使う。 + +```python +def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: + """(止める指摘の件数, 測れたか)。証拠集約を通っていないラウンドは (0, False)。""" +``` + +## 非機能の実現方式 + +| 条件 | 実現方式 | +| --- | --- | +| 変更の節は 50 件で 6,000 バイト以下 | ファイル名だけを並べ、差分の本文を入れない。1 件 100 バイトとして 5,000 バイトと、定型の文 約 600 バイト | +| 控えの増分は同じ実行の前のラウンドの分だけ | 取り直しは `fetch-pr-comments.sh` の全件の取得で、増えるのは前の取得の後に投稿された行だけである | +| minor だけの収束を後から数えられる | ラウンドの記録の `converged_by` と `new_blocking_findings`、`report` の 1 行 | + +## 決定の記録 + +決定の理由と採らなかった案は [issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある。見出しだけを並べる。 + +- 決定 1: cross-review の既定の母集合から agy を外し、claude / codex / kiro とホストにする +- 決定 2: 既定の母集合に無い者を外す指定は、止めずに無視して 1 行で知らせる +- 決定 3: 1 ラウンド目から「見つけた指摘はすべて出す」を求める +- 決定 4: 2 ラウンド目以降は、前のラウンドからの変わったファイルの一覧と差分を読むコマンドを渡し、差分の本文は渡さない +- 決定 5: 設計 PR 向けの観点テンプレートを足し、設計の成果物は増やさない +- 決定 6: 既存コメントの控えをラウンドの開始ごとに取り直し、失敗しても止めない +- 決定 7: 新しい指摘が minor だけのラウンドで収束し、minor は最終スイープで直す +- 決定 8: 母集合の全員が見るまで収束しない条件(#631)は採らない +- 決定 9: 結果を残さなかった担当を別のランタイムへ振り替えることは、この変更に含めない +- 決定 10: 振動の検知・`max_rounds`・起動し直しの規則は変えない +- 決定 11: レビュー担当にはテストも背景の処理も起動させず、適用担当にはテストを前景で待たせる + +## テスト設計 + +`.md` の文言は固定しない(#885)。プロンプトは節の有無と、条件で入る・入らないの分岐を見る。 + +| 受け入れ条件 | 何で確かめるか | +| --- | --- | +| AC1 AC2 AC3 | `test_lib_assignment.py`: `review_pool(h)` をホスト 4 通りで比べる。`resolve_participants` + `review_seats` で host=claude の round 1〜3 を比べる | +| AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 1 | +| AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` が母集合の説明として 0 件 | +| AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | +| AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る | +| AC10 | 同上: 1 ラウンド目・同じ head・`head_sha` の無いラウンドでファイルが無く、前の起動の残りも消える。プロンプトに節が入らない | +| AC11 | 同上: 53 ファイルの差分で一覧が 50 件と「ほか 3 件」、ファイルの大きさが 6,000 バイト以下 | +| AC12 | 同上: `fetch-pr-comments.sh` を差し替えた偽物で、2 ラウンド目の `start-round` が呼び、控えが新しい中身になる。1 ラウンド目では呼ばない | +| AC13 | 同上: 偽物が失敗すると `start-round` が終了コード 0 で `⚠` の行を出し、控えは前の中身のまま | +| AC14 AC16 | `test_state_auto_review_templates.py`: `issues/issue-1-design.md` を含む変更が `common` / `docs_only` / `design` に、`issues/notes.md` と `docs/x-design.md` だけの変更が `design` を含まない | +| AC15 | 目で見る(テンプレートの文言) | +| AC17 AC18 AC19 AC20 | `test_state_judge_*`(新規 `test_state_judge_minor_only.py`): 証拠集約を通ったラウンドで新しい指摘が minor 2 件 → 終了コード 0・`NEW_BLOCKING_FINDINGS=0`・`converged_by=minor_only`。major 1 件と minor 1 件 → 2。証拠集約を通っていない → `NEW_BLOCKING_FINDINGS=-` で今の判定。引き継ぎがあれば 2 | +| AC21 | `report` のテスト: `converged_by=minor_only` のラウンドで 1 行が出る | +| AC22 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py`・`--only` のテストが変更なしで通る | +| AC23 | AC17 のテストで、母集合の 1 者が一度も座っていない状態でも収束する | +| AC24 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | + +## 未確認のまま残ること + +| 項目 | 内容 | +| --- | --- | +| ラウンドが実際に減るか | この変更の効果は、配布後の設計 PR のラウンド数で見る。比べる手段は #893 が作る。手元では退避された状態ファイル(`.git/ndf/worktree-trash/*/.cross_review/`)の `rounds` を数えて比べられる | +| 出し切りの指示で 1 ラウンド目の出力が増えるか | 増えると担当の所要が延びる。agy は既定から外れるため、無進捗の許容(480 秒)に当たる担当は既定では居ない。codex の無進捗の許容は 180 秒で、実装の PR の cross-review で所要を見る | +| minor だけの収束で見落とす穴 | 最終スイープの minor の修正は再レビューを通らない。今も最終スイープの修正は再レビューを通っていない(#908 の承認の依頼に同じ記載がある)ので、形としては同じ扱いになる。minor の修正が穴を作った例が出たら、決定 7 を見直す | +| 控えの大きさ | #908 の実行 3 で控えは 54,957 バイトだった。取り直しで同じ実行の指摘と返信の行が増える。実装の PR の cross-review で大きさを見る(圧縮は範囲外) | +| #892 の後の母集合で動いた実行が無い | claude が座席に入った実行は手元の記録にまだ無い。この変更の後の既定(claude / codex / kiro)で 2 ラウンド目に claude が座る | +| 設計 PR の見分けの取りこぼし | ファイル名の規約(`-requirements.md` / `-design.md` / `-design-decisions.md`)から外れた設計文書は `design` に分類されない。`issues/` 配下の既存の設計文書の名前は、実装の時点で `ls issues/*design*` で確かめる | diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md new file mode 100644 index 000000000..06cf2fc2e --- /dev/null +++ b/issues/issue-542-786-requirements.md @@ -0,0 +1,154 @@ +# #542 #786: cross-review のラウンドを減らす — 要求と受け入れ条件 + +設計は [issue-542-786-design.md](issue-542-786-design.md)、決定の理由は +[issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある。この文書は「何を満たすか」だけを扱う。 + +マイルストーンは「17 トークン消費の削減」(効果の順 2''')。版数は配布の工程が決める(`release`)。 +#631(収束の条件を厳しくする)は #542 と逆向きの提案であり、この設計の中で採否を決める(採らない。決定 8)。 + +## 例: 設計 Pull Request の 3 ラウンド目 + +ホストは claude、引数は付けない。 + +今(v10.17.3): + +1. 母集合は claude / codex / agy / kiro の 4 者で、round 1 の座席は codex と agy。agy はテストを背景で起動したまま結果を残さずに終わることがある(#786)。実運用では毎回 `--exclude agy` を手で渡している +2. round 1 の担当は、指摘を出し切る指示を受けていない。round 2 の担当は、round 1 の後にどの節が直ったかを知らされない。既存コメントの控えは init の時点のもので、round 1 の指摘とその対応が入っていない +3. round 3 で minor が 2 件だけ出ても、新しい指摘があるので round 4 へ進む。round 4 は round 3 の修正が作った穴を探す + +変更の後: + +1. 母集合は claude / codex / kiro の 3 者で、round 1 の座席は codex と kiro。agy は `--include agy` で戻す +2. round 1 の担当は「見つけた指摘はこのラウンドですべて出す」指示を受ける。round 2 の担当は、前のラウンドの head と今の head、その間に変わったファイルの一覧、「変わった節とそれを参照する節を先に見る」観点を受ける。既存コメントの控えはラウンドごとに取り直され、round 1 の指摘と「対応しました」の返信が入る。設計 PR には設計向けの観点も渡る +3. round 3 の新しい指摘が minor だけなら、そのラウンドで収束する。2 件は最終スイープが直す + +## 目的 + +- **3 ラウンド以上に進む実行を減らす。** ai-plugins の 2026-09-01〜09-15 の 88 件では、3 ラウンド以上の 30% が所要の 70% を使った(#542)。直近の設計 PR は 8 ラウンド × 3 回(#908)や振動の検知での打ち切り(#911 #843)に至っている +- **ラウンドが増える 2 つの原因に手を打つ。** 1 つは 1 ラウンド目で出し切っていないこと、もう 1 つは前の修正が作った穴を次のラウンドが後追いで見つけること(PR #539 の 3 巡目以降の指摘はすべて後者だった) +- **動かない担当で収束が止まるのをやめる**(#786)。手で `--exclude agy` を渡す運用を既定にする +- **ラウンドを増やす方向の提案(#631)とは方針を 1 つにする** + +## 対象範囲 + +含む: + +- cross-review のレビュー担当の既定の母集合から agy を外すこと(`--include agy` で戻す) +- レビューのプロンプトに足すもの: 出し切りの指示(全ラウンド)/ 前のラウンドからの変更と、変わった節を先に見る観点(2 ラウンド目以降)/ テストと背景の処理を起動しない指示 +- 既存コメントの控えをラウンドの開始ごとに取り直すこと +- 設計 PR を見分けて、設計向けの観点テンプレートを渡すこと +- 新しい指摘が minor だけのラウンドを収束とし、minor を最終スイープへ回すこと +- cross-refactoring の適用のプロンプトに、背景で起動したテストを待たずに終わらない指示を足すこと(#786 の cross-refactoring 側) +- 上の変更に合わせた文書(`SKILL.md`・`docs/`・確定仕様・`README`・`CLAUDE.md` の cross-review 節)の更新 + +含まない: + +- **#631 の収束の条件(母集合の全員が見るまで収束しない)。** 採らない(決定 8) +- **結果を残さなかった担当を別のランタイムへ振り替えること。** 範囲外の課題 #919 として起票した(決定 9)。置き場所の候補は #870 の drive だが、#870 の本文は扱っていない +- 振動の検知の閾値・`max_rounds`・起動し直しの規則の変更(決定 10) +- 設計の成果物(`design` の `deliverables.md`)へ「状態を書き換える経路の一覧」を足すこと。レビューの観点の側で扱う(決定 5) +- 既存コメントの控えを圧縮すること(大きさは非機能の条件で見張る) +- cross-refactoring の参加者の既定(#664 で agy は外してある) +- 効果の測定の仕組み(#893 が扱う) +- 設計の成果物のうち画面・永続データ・API。画面は持たない。状態ファイル(`state.json`)に足すのは、ラウンドと参加者の記録の項目だけである(旧い状態ファイルも読める)。呼び出しの約束はコマンドの引数と出力の行で、設計の入出力の契約の節で扱う + +## 受け入れ条件 + +### 母集合(#786) + +- [ ] AC1: ホストが claude で `--exclude` / `--include` を付けないとき、`init` の参加者は claude / codex / kiro の 3 者で、agy を含まない。座席は次の順になる + - round 1: codex と kiro + - round 2: claude と kiro + - round 3: claude と codex +- [ ] AC2: ホストが codex または kiro のときも、既定の参加者は claude / codex / kiro の 3 者である +- [ ] AC3: ホストが agy のとき、参加者は agy を含む 4 者である(ホストは常に母集合に入る。#892) +- [ ] AC4: `--include agy` を付けると agy が参加者に戻り、座席は今(v10.17.3)の既定と同じ輪番になる。`--exclude agy` を付けても(既定に無い者を外す指定でも)終了コード 0 で進む +- [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。母集合を 4 者と書いた記述は残らない + - `plugins/ndf/skills/cross-review/SKILL.md` と `docs/05-pool-and-convergence.md` + - 確定仕様 `docs/specifications/cross-review-participants-and-seats.md` + - `plugins/ndf/README.md` と `CLAUDE.md` の cross-review 節 + +### 担当が結果を残さずに終わらない(#786) + +- [ ] AC6: レビューのプロンプトの「守るべきこと」が、テストを実行しないこと・背景で処理を起動しないことを求め、実行の確かめは進行側(`verify-findings`)が行うと書く +- [ ] AC7: cross-refactoring の適用のプロンプトが、テストを前景で実行して終わるまで待ち、背景で起動した処理を残したまま終わらないことを求める + +### 出し切りと、前の修正の後の観点(#542) + +- [ ] AC8: どのラウンドのプロンプトも「見つけた指摘はこのラウンドですべて出す。次のラウンドへ回さない」の指示を持つ。出す対象(修正アクションのある指摘だけ)と、含めてはいけないもの(良い点・nit)は今のまま変わらない +- [ ] AC9: 同じ PR の 2 ラウンド目以降で、前のラウンドの head と今の head が違うとき、プロンプトは「前のラウンドからの変更」の節を持つ。節には 2 つの head の SHA・その間に変わったファイルの一覧・差分を読むコマンド・「変わった節と、それを参照する節・同じ契約を使う節を先に見る。修正が新しく作った経路(状態・分岐・引数)の穴を見る」の観点が入る +- [ ] AC10: 1 ラウンド目と、前のラウンドと head が同じラウンド(修正のコミットが無い)のプロンプトは「前のラウンドからの変更」の節を持たない +- [ ] AC11: 変わったファイルの一覧は 50 件を上限とし、超えた分は件数だけを書く。差分の本文はプロンプトへ入れない(担当が作業ツリーでコマンドを実行して読む) + +### 既存コメントの控え(#542) + +- [ ] AC12: ラウンドの開始(`start-round`)ごとに既存コメントの控えを取り直す。2 ラウンド目のプロンプトに、同じ実行の 1 ラウンド目で投稿された指摘と、その修正の返信が入る +- [ ] AC13: 取り直しに失敗したとき(`gh` の失敗など)、ラウンドは止まらず、前の控えのままプロンプトを作り、`⚠` で始まる 1 行で理由を出す + +### 設計向けの観点(#542) + +- [ ] AC14: 設計 PR(用語の表)は分類に `design` が加わる。`review_instructions` には設計向けの観点テンプレートが入る。`common` / `docs_only` の分類は今のまま残る +- [ ] AC15: 設計向けの観点テンプレートは次の 3 つを求める + - 要求・設計・決定の記録の 3 文書の間で、受け入れ条件・決定・テスト設計が対応しているか + - 状態(ファイル・環境変数・状態ファイルの項目)ごとに書き手と読み手を並べ、矛盾する経路が無いか + - 外部コマンドや外部ツールの挙動を断定する記述に、実測の根拠があるか +- [ ] AC16: 設計 PR でない PR は `design` に分類されない + +### 収束の判定(#542) + +- [ ] AC17: 次の 3 つがそろうと、`judge` は収束(終了コード 0、`final = approved`)と判定する。新しい minor の指摘は最終スイープ(Step 7.5)が直し、`verify-sweep` が open のスレッド 0 件を確かめる + - 証拠集約を通ったラウンドである + - 引き継いだ指摘が無い + - 新しい指摘が 1 件以上あり、そのすべてが minor である +- [ ] AC18: 新しい指摘に critical か major が 1 件でもあれば、今と同じく次のラウンドへ進む(終了コード 2) +- [ ] AC19: 証拠集約を通っていないラウンド(重要度を読めない)と、指摘の記録を読めないラウンドは、今の判定のまま変わらない +- [ ] AC20: `judge` の出力に、止める指摘の件数の行(`NEW_BLOCKING_FINDINGS=<件数>`、測れないときは `-`)が加わる。既存の行(`NEW_FINDINGS` など)の形は変わらない +- [ ] AC21: 最終の報告(`report`)に、minor だけで収束したラウンドであることと、最終スイープへ回した件数が出る + +### 変えないこと + +- [ ] AC22: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)・`--only` の扱いは変わらない +- [ ] AC23: 収束の判定は母集合の各担当がレビューしたかを見ない(#631 を採らない) +- [ ] AC24: 既存のテストがすべて通る(下の検証手段) + +## 非機能の条件 + +| 大項目 | 条件 | +| --- | --- | +| 性能・拡張性 | 「前のラウンドからの変更」の節は、変わったファイルが 50 件のとき 6,000 バイト以下に収まる(差分の本文を入れないため)。控えの取り直しで増えるのは、同じ実行の前のラウンドの指摘と返信の行だけである | +| 運用・保守性 | minor だけで収束したことは `judge` の出力と `report` に残り、後から数えられる(AC20 / AC21) | + +## 影響 + +| 対象 | 影響 | +| --- | --- | +| 公開インタフェース | `cross-review` の既定の参加者が変わる(agy が外れる)。引数は増えない。`--include agy` で今の既定へ戻せる | +| データ | 状態ファイルのラウンドの記録に、止める指摘の件数(`new_blocking_findings`)と収束の理由(`converged_by`)が、参加者の記録に無視した除外(`ignored_exclude`)が加わる。無い旧い状態ファイルは今の判定で読む | +| 既存の振る舞い | minor だけのラウンドで収束する / 2 ラウンド目以降のプロンプトが変わる / 控えがラウンドごとに新しくなる / 設計 PR に観点が 1 つ増える | + +## 検証手段 + +| 項目 | 手段 | +| --- | --- | +| テスト | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` | +| プラグインの検査 | `claude plugin validate .`(終了コード 0) | +| 手動確認 | 実装の Pull Request の cross-review を新しい既定で回し、プロンプトの控えに前のラウンドの指摘が入ること・2 ラウンド目の「前のラウンドからの変更」の節を、作業ツリーの `.cross_review/*-prompt.md` で目で見る | + +## 前提とする取り決め + +| 項目 | 参照先 / 決めたこと | +| --- | --- | +| ブランチと Pull Request | `AGENTS.md` の Git 運用ルール(`develop` 宛) | +| `.md` の文言のテスト | 書かない(#885 の方針)。プロンプトの文言は目で確かめ、テストは節の有無と条件の分岐を見る | +| 版 | マイルストーン 26 は 10.17.x の PATCH で出す | + +## 用語 + +| 用語 | 意味 | +| --- | --- | +| 母集合 | レビュー担当になり得るランタイムの集まり。`--exclude` / `--include` を当てる前の既定 | +| 座席 | 1 ラウンドでレビューする 2 者。母集合のうち使える者から輪番で選ぶ(`review_seats`) | +| 新しい指摘 | 直前のラウンドの指摘と一致しない、そのラウンドの指摘(`_new_finding_count`)。証拠集約を通ったラウンドでは数える 3 区分(`verified_blocking` / `needs_human_judgment` / `unrefuted`)だけ | +| 止める指摘 | 新しい指摘のうち重要度が critical か major のもの | +| 既存コメントの控え | PR の既存のインラインコメント・レビュー本文・PR コメントを 1 行ずつ並べたファイル(`cross-review-pr-existing-comments.txt`)。プロンプトへそのまま入る | +| 設計 PR | 変更ファイルに要求・設計・決定の記録の文書(`issues/` 配下の `*-requirements.md` / `*-design.md` / `*-design-decisions.md`)を含む Pull Request | From 1e32690c38f7b5f00ba83b49b7575605e57ca0b8 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 13:33:24 +0000 Subject: [PATCH 02/10] =?UTF-8?q?Docs:=20#542=20#786=20=E6=B1=BA=E5=AE=9A?= =?UTF-8?q?=E3=81=AE=E8=A8=98=E9=8C=B2=E3=81=AE=E8=A6=8B=E5=87=BA=E3=81=97?= =?UTF-8?q?=E3=82=92=E6=9C=AC=E6=96=87=E3=81=AE=E7=AA=81=E3=81=8D=E5=90=88?= =?UTF-8?q?=E3=82=8F=E3=81=9B=E3=81=AE=E5=BD=A2=E3=81=AB=E3=81=9D=E3=82=8D?= =?UTF-8?q?=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design-decisions.md | 2 +- issues/issue-542-786-design.md | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/issues/issue-542-786-design-decisions.md b/issues/issue-542-786-design-decisions.md index aee2154ef..c70bdbb68 100644 --- a/issues/issue-542-786-design-decisions.md +++ b/issues/issue-542-786-design-decisions.md @@ -2,7 +2,7 @@ 設計は [issue-542-786-design.md](issue-542-786-design.md)、要求は [issue-542-786-requirements.md](issue-542-786-requirements.md) にある。決定の根拠にした実測は末尾の「判断に使った実測」にまとめた。 -## 決定 +## 決定の記録 ### 決定 1: cross-review の既定の母集合から agy を外し、claude / codex / kiro とホストにする diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index c999078b9..05fc12483 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -342,19 +342,7 @@ def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: ## 決定の記録 -決定の理由と採らなかった案は [issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある。見出しだけを並べる。 - -- 決定 1: cross-review の既定の母集合から agy を外し、claude / codex / kiro とホストにする -- 決定 2: 既定の母集合に無い者を外す指定は、止めずに無視して 1 行で知らせる -- 決定 3: 1 ラウンド目から「見つけた指摘はすべて出す」を求める -- 決定 4: 2 ラウンド目以降は、前のラウンドからの変わったファイルの一覧と差分を読むコマンドを渡し、差分の本文は渡さない -- 決定 5: 設計 PR 向けの観点テンプレートを足し、設計の成果物は増やさない -- 決定 6: 既存コメントの控えをラウンドの開始ごとに取り直し、失敗しても止めない -- 決定 7: 新しい指摘が minor だけのラウンドで収束し、minor は最終スイープで直す -- 決定 8: 母集合の全員が見るまで収束しない条件(#631)は採らない -- 決定 9: 結果を残さなかった担当を別のランタイムへ振り替えることは、この変更に含めない -- 決定 10: 振動の検知・`max_rounds`・起動し直しの規則は変えない -- 決定 11: レビュー担当にはテストも背景の処理も起動させず、適用担当にはテストを前景で待たせる +決定の見出し・理由・採らなかった案は [issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある(11 件)。 ## テスト設計 From 76d7f962908e338f940d8a0eccd0b8d7a94906d3 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 13:40:15 +0000 Subject: [PATCH 03/10] =?UTF-8?q?Docs:=20#542=20minor=20=E3=81=A0=E3=81=91?= =?UTF-8?q?=E3=81=A7=E5=8F=8E=E6=9D=9F=E3=81=99=E3=82=8B=E6=A1=88=E3=82=92?= =?UTF-8?q?=E5=8F=96=E3=82=8A=E4=B8=8B=E3=81=92=E3=80=81=E5=8F=8E=E6=9D=9F?= =?UTF-8?q?=E3=81=AE=E5=88=A4=E5=AE=9A=E3=81=AF=E5=A4=89=E3=81=88=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=81=A8=E6=B1=BA=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 証拠集約は minor を数えない区分へ落とすため、新しい指摘が minor だけのラウンドは今も収束する。 決定 7 を「判定は変えない」に置き換え、judge の出力・状態ファイルの項目・テストの行を外した。 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design-decisions.md | 16 ++--- issues/issue-542-786-design.md | 91 ++++-------------------- issues/issue-542-786-requirements.md | 29 +++----- 3 files changed, 31 insertions(+), 105 deletions(-) diff --git a/issues/issue-542-786-design-decisions.md b/issues/issue-542-786-design-decisions.md index c70bdbb68..bd8a7baba 100644 --- a/issues/issue-542-786-design-decisions.md +++ b/issues/issue-542-786-design-decisions.md @@ -67,17 +67,15 @@ agy が担当になると、テストを背景で起動して結果を残さず 止める(控えが無いと重複の検出が働かない)。控えを圧縮することは範囲外にした。取り直しで増えるのは同じ実行の 前のラウンドの行だけで、まず大きさを実装の PR で見る。 -### 決定 7: 新しい指摘が minor だけのラウンドで収束し、minor は最終スイープで直す +### 決定 7: 収束の判定は変えない。新しい指摘が minor だけのラウンドは今も収束する -今の収束は「全員 pass か、新しい指摘が 0 件」で、minor だけの新しい指摘でも次のラウンドへ進む。次のラウンドは -その minor の修正が作った穴を探すことになり、ラウンドが続く。最終スイープ(Step 7.5)は既に、どの終わり方でも -残った minor と nit を直す。open のスレッドは 0 件になる。minor だけのラウンドの修正をこのスイープへ寄せれば、 -1 ラウンド分(2 席の起動・反証・判定・修正)が減る。 +証拠集約(#156、#732)は、再現していない minor を `insufficient_evidence`、再現した minor を `verified_non_blocking` に +分け、どちらも新しい指摘として数えない(`_classify_finding`)。そのため新しい指摘が minor だけのラウンドは、今も +`NEW_FINDINGS=0` で収束し、minor は最終スイープ(Step 7.5)が直す。ラウンドを延ばしているのは、前の修正が作った +major の穴である。この変更は判定ではなく、担当が受け取るもの(決定 3・4・5・6)でその穴を早く出させる。 -判定は証拠集約を通ったラウンドだけで行う。重要度は、反証と実行の検証を経た `review_findings` にしか無い。担当の -自己申告の重要度だけで収束させることは採らなかった。`_is_pass`(COMMENT で critical と major が 0 件なら pass)を -REQUEST_CHANGES にも広げることも採らなかった。担当の判定と新しさの判定が混ざり、前のラウンドから引き継いだ -major を見落とす。 +`_is_pass`(COMMENT で critical と major が 0 件なら pass)を REQUEST_CHANGES にも広げることは採らなかった。 +新しさの判定が既に minor を数えないため、広げても収束するラウンドは増えない。 ### 決定 8: 母集合の全員が見るまで収束しない条件(#631)は採らない diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index 05fc12483..8c27e4aee 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -24,9 +24,8 @@ 穴が無いかを確かめる。直った指摘を繰り返さない(既存コメントの控えに返信がある)。 ``` -担当のプロンプトには、この節と、控えと、観点テンプレート(`common` / `docs_only` / `design`)が入る。2 ラウンド目で -新しい指摘が minor 1 件だけなら、`judge` は `NEW_FINDINGS=1` / `NEW_BLOCKING_FINDINGS=0` を出して収束する。 -minor 1 件は最終スイープが直す。 +担当のプロンプトには、この節と、控えと、観点テンプレート(`common` / `docs_only` / `design`)が入る。担当は +修正が入った節から読み、その修正が作った穴を 2 ラウンド目のうちに出す。収束の判定は変えない。 ## 機能一覧 @@ -38,16 +37,14 @@ minor 1 件は最終スイープが直す。 | F4 | 前のラウンドからの変更の節を書く | `start-round` → `launch-reviewer.sh` | | F5 | 出し切りの指示・テストと背景の処理を起動しない指示をプロンプトに入れる | `launch-reviewer.sh` | | F6 | 設計 PR を分類し、設計向けの観点を渡す | `init`(観点の組み立て) | -| F7 | 新しい指摘が minor だけなら収束する | `judge` | -| F8 | minor だけの収束を報告に出す | `report` | -| F9 | 適用担当に、背景で起動したテストを残して終わらないよう求める | cross-refactoring の適用のプロンプト | +| F7 | 適用担当に、背景で起動したテストを残して終わらないよう求める | cross-refactoring の適用のプロンプト | ## 構成要素 | 要素 | 責務 | 変更 | | --- | --- | --- | | `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す | -| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・judge・report | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。judge が止める指摘を数えて収束を判定し、ラウンドの記録に残す。`_resolve_reviewers` が無視した除外を 1 行で出す。report が minor だけの収束を出す | +| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | @@ -59,7 +56,7 @@ minor 1 件は最終スイープが直す。 | 対象 | 持つもの | | --- | --- | | `review_seats` | 輪番の式 | -| `_new_finding_count` | 新しい指摘の数え方 | +| `_new_finding_count` / `_evaluate_convergence` | 新しい指摘の数え方と収束の判定 | | `cmd_check_oscillation` | 振動の検知 | | `_handle_no_result_round` | 結果が無い担当の起動し直し | | `fix/scripts/fetch-pr-comments.sh` | 控えの形 | @@ -73,7 +70,7 @@ graph LR A["assignment.py
DEFAULT_REVIEW_RUNTIMES
review_pool / resolve_participants"] end subgraph cr["cross-review/scripts"] - S["state.py
init / start-round / judge / report"] + S["state.py
init / start-round"] L["launch-reviewer.sh"] end subgraph rf["cross-refactoring"] @@ -243,32 +240,6 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No - 外部コマンド・外部ツールの挙動(優先順位・終了コード・一致の範囲)を断定する記述に、実測の根拠(コマンドと出力)があるか ``` -### judge の出力と状態ファイル - -`judge` の状態表示に 1 行を足す。今の行の形と順は変えない。 - -```text -REVIEWER_INTENTS='codex=REQUEST_CHANGES kiro=COMMENT' -NEW_FINDINGS=1 -NEW_BLOCKING_FINDINGS=0 -CARRIED_OVER_THREADS=0 -PENDING_POSTS=0 -``` - -| 値 | 条件 | -| --- | --- | -| 件数 | 証拠集約を通ったラウンドで、新しい指摘のうち重要度が critical か major のものの数 | -| `-` | 証拠集約を通っていない・指摘の記録を読めない(今の `NEW_FINDINGS=-` と同じ時と、証拠集約を通っていない時) | - -ラウンドの記録(`rounds[]` の要素)に 2 項目を足す。旧い状態ファイルに無くても読める。 - -| 項目 | 型 | 値 | -| --- | --- | --- | -| `new_blocking_findings` | int か null | 上の件数。`-` のときは null | -| `converged_by` | 文字列か無し | 収束したラウンドだけ。`all_pass`(全員 pass)/ `no_new_findings`(新しい指摘 0 件)/ `minor_only`(新しい指摘が minor だけ) | - -`report` は `converged_by = minor_only` のラウンドについて「round N は新しい指摘が minor だけで収束した(M 件を -最終スイープへ回した)」の 1 行を出す。M はそのラウンドの新しい指摘の件数(`NEW_FINDINGS`)。 ## 処理の流れ @@ -292,45 +263,14 @@ sequenceDiagram L->>C: プロンプト(控え・**変更の節**・観点・**出し切りの指示**) C-->>S: payload / result.json P->>S: read-result・verify-findings・critique - P->>S: judge - S-->>P: **NEW_BLOCKING_FINDINGS** と終了コード - alt 0(収束。**minor だけを含む**) - P->>P: 最終スイープ(minor を直す)→ verify-sweep → report - else 2(止める指摘がある) + P->>S: judge(変えない) + alt 0(収束) + P->>P: 最終スイープ → verify-sweep → report + else 2(新しい指摘がある) P->>S: check-oscillation → 修正 → merge-fix → 次のラウンド end ``` -### judge の収束の判定 - -```mermaid -graph TD - A["引き継いだ指摘がある"] -->|はい| X["続ける(2)"] - A -->|いいえ| B["全員 pass"] - B -->|はい| Y1["収束 all_pass"] - B -->|いいえ| C["新しい指摘を測れた"] - C -->|いいえ| X - C -->|はい| D["新しい指摘が 0 件"] - D -->|はい| Y2["収束 no_new_findings"] - D -->|いいえ| E["止める指摘を測れた(証拠集約を通った)"] - E -->|いいえ| X - E -->|はい| F["止める指摘が 0 件"] - F -->|はい| Y3["収束 minor_only"] - F -->|いいえ| X -``` - -収束した後の段(待ち行列が残れば 8、CI がコードの失敗なら 2、それ以外は `final = approved` で 0)は今と同じである。 - -### 止める指摘の数え方 - -`_new_finding_count` と同じ母集合と一致の判定を使う。違うのは、新しいと判定した指摘のうち重要度が critical か -major のものだけを数える点である。重要度は `review_findings` の要素の `severity` から読む(証拠集約を通った -ラウンドだけが持つ)。束ねられた指摘(`merged_into`)は数えず、代表の重要度(束ねたときに高い方へ揃えてある)を使う。 - -```python -def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: - """(止める指摘の件数, 測れたか)。証拠集約を通っていないラウンドは (0, False)。""" -``` ## 非機能の実現方式 @@ -338,7 +278,6 @@ def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: | --- | --- | | 変更の節は 50 件で 6,000 バイト以下 | ファイル名だけを並べ、差分の本文を入れない。1 件 100 バイトとして 5,000 バイトと、定型の文 約 600 バイト | | 控えの増分は同じ実行の前のラウンドの分だけ | 取り直しは `fetch-pr-comments.sh` の全件の取得で、増えるのは前の取得の後に投稿された行だけである | -| minor だけの収束を後から数えられる | ラウンドの記録の `converged_by` と `new_blocking_findings`、`report` の 1 行 | ## 決定の記録 @@ -361,11 +300,10 @@ def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: | AC13 | 同上: 偽物が失敗すると `start-round` が終了コード 0 で `⚠` の行を出し、控えは前の中身のまま | | AC14 AC16 | `test_state_auto_review_templates.py`: `issues/issue-1-design.md` を含む変更が `common` / `docs_only` / `design` に、`issues/notes.md` と `docs/x-design.md` だけの変更が `design` を含まない | | AC15 | 目で見る(テンプレートの文言) | -| AC17 AC18 AC19 AC20 | `test_state_judge_*`(新規 `test_state_judge_minor_only.py`): 証拠集約を通ったラウンドで新しい指摘が minor 2 件 → 終了コード 0・`NEW_BLOCKING_FINDINGS=0`・`converged_by=minor_only`。major 1 件と minor 1 件 → 2。証拠集約を通っていない → `NEW_BLOCKING_FINDINGS=-` で今の判定。引き継ぎがあれば 2 | -| AC21 | `report` のテスト: `converged_by=minor_only` のラウンドで 1 行が出る | -| AC22 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py`・`--only` のテストが変更なしで通る | -| AC23 | AC17 のテストで、母集合の 1 者が一度も座っていない状態でも収束する | -| AC24 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | +| AC17 | 既存の `test_classify_findings.py`(minor は数えない区分へ落ちる)と judge のテストが変更なしで通る | +| AC18 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py`・`--only` のテストが変更なしで通る | +| AC19 | `_evaluate_convergence` と `review_seats` を変えない(差分に現れない)ことを実装の PR で確かめる | +| AC20 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | ## 未確認のまま残ること @@ -373,7 +311,6 @@ def _new_blocking_count(st: dict[str, Any], pr: int) -> tuple[int, bool]: | --- | --- | | ラウンドが実際に減るか | この変更の効果は、配布後の設計 PR のラウンド数で見る。比べる手段は #893 が作る。手元では退避された状態ファイル(`.git/ndf/worktree-trash/*/.cross_review/`)の `rounds` を数えて比べられる | | 出し切りの指示で 1 ラウンド目の出力が増えるか | 増えると担当の所要が延びる。agy は既定から外れるため、無進捗の許容(480 秒)に当たる担当は既定では居ない。codex の無進捗の許容は 180 秒で、実装の PR の cross-review で所要を見る | -| minor だけの収束で見落とす穴 | 最終スイープの minor の修正は再レビューを通らない。今も最終スイープの修正は再レビューを通っていない(#908 の承認の依頼に同じ記載がある)ので、形としては同じ扱いになる。minor の修正が穴を作った例が出たら、決定 7 を見直す | | 控えの大きさ | #908 の実行 3 で控えは 54,957 バイトだった。取り直しで同じ実行の指摘と返信の行が増える。実装の PR の cross-review で大きさを見る(圧縮は範囲外) | | #892 の後の母集合で動いた実行が無い | claude が座席に入った実行は手元の記録にまだ無い。この変更の後の既定(claude / codex / kiro)で 2 ラウンド目に claude が座る | | 設計 PR の見分けの取りこぼし | ファイル名の規約(`-requirements.md` / `-design.md` / `-design-decisions.md`)から外れた設計文書は `design` に分類されない。`issues/` 配下の既存の設計文書の名前は、実装の時点で `ls issues/*design*` で確かめる | diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md index 06cf2fc2e..0149a3654 100644 --- a/issues/issue-542-786-requirements.md +++ b/issues/issue-542-786-requirements.md @@ -14,13 +14,13 @@ 1. 母集合は claude / codex / agy / kiro の 4 者で、round 1 の座席は codex と agy。agy はテストを背景で起動したまま結果を残さずに終わることがある(#786)。実運用では毎回 `--exclude agy` を手で渡している 2. round 1 の担当は、指摘を出し切る指示を受けていない。round 2 の担当は、round 1 の後にどの節が直ったかを知らされない。既存コメントの控えは init の時点のもので、round 1 の指摘とその対応が入っていない -3. round 3 で minor が 2 件だけ出ても、新しい指摘があるので round 4 へ進む。round 4 は round 3 の修正が作った穴を探す +3. round 2 の修正が major の穴を作ると、round 3 の担当はそれを知らずに全体を読み直し、見つけた穴で round 4 へ進む 変更の後: 1. 母集合は claude / codex / kiro の 3 者で、round 1 の座席は codex と kiro。agy は `--include agy` で戻す 2. round 1 の担当は「見つけた指摘はこのラウンドですべて出す」指示を受ける。round 2 の担当は、前のラウンドの head と今の head、その間に変わったファイルの一覧、「変わった節とそれを参照する節を先に見る」観点を受ける。既存コメントの控えはラウンドごとに取り直され、round 1 の指摘と「対応しました」の返信が入る。設計 PR には設計向けの観点も渡る -3. round 3 の新しい指摘が minor だけなら、そのラウンドで収束する。2 件は最終スイープが直す +3. round 3 の担当は round 2 の修正で変わった節から読むため、修正が作った穴を round 3 のうちに出す ## 目的 @@ -37,7 +37,6 @@ - レビューのプロンプトに足すもの: 出し切りの指示(全ラウンド)/ 前のラウンドからの変更と、変わった節を先に見る観点(2 ラウンド目以降)/ テストと背景の処理を起動しない指示 - 既存コメントの控えをラウンドの開始ごとに取り直すこと - 設計 PR を見分けて、設計向けの観点テンプレートを渡すこと -- 新しい指摘が minor だけのラウンドを収束とし、minor を最終スイープへ回すこと - cross-refactoring の適用のプロンプトに、背景で起動したテストを待たずに終わらない指示を足すこと(#786 の cross-refactoring 側) - 上の変更に合わせた文書(`SKILL.md`・`docs/`・確定仕様・`README`・`CLAUDE.md` の cross-review 節)の更新 @@ -45,12 +44,13 @@ - **#631 の収束の条件(母集合の全員が見るまで収束しない)。** 採らない(決定 8) - **結果を残さなかった担当を別のランタイムへ振り替えること。** 範囲外の課題 #919 として起票した(決定 9)。置き場所の候補は #870 の drive だが、#870 の本文は扱っていない +- 収束の判定の変更。minor は証拠集約で数えない区分へ落ちるため、新しい指摘が minor だけのラウンドは今も収束する(#732。決定 7) - 振動の検知の閾値・`max_rounds`・起動し直しの規則の変更(決定 10) - 設計の成果物(`design` の `deliverables.md`)へ「状態を書き換える経路の一覧」を足すこと。レビューの観点の側で扱う(決定 5) - 既存コメントの控えを圧縮すること(大きさは非機能の条件で見張る) - cross-refactoring の参加者の既定(#664 で agy は外してある) - 効果の測定の仕組み(#893 が扱う) -- 設計の成果物のうち画面・永続データ・API。画面は持たない。状態ファイル(`state.json`)に足すのは、ラウンドと参加者の記録の項目だけである(旧い状態ファイルも読める)。呼び出しの約束はコマンドの引数と出力の行で、設計の入出力の契約の節で扱う +- 設計の成果物のうち画面・永続データ・API。画面は持たない。状態ファイル(`state.json`)に足すのは、参加者の記録の 1 項目だけである(旧い状態ファイルも読める)。呼び出しの約束はコマンドの引数と出力の行で、設計の入出力の契約の節で扱う ## 受け入れ条件 @@ -96,35 +96,27 @@ ### 収束の判定(#542) -- [ ] AC17: 次の 3 つがそろうと、`judge` は収束(終了コード 0、`final = approved`)と判定する。新しい minor の指摘は最終スイープ(Step 7.5)が直し、`verify-sweep` が open のスレッド 0 件を確かめる - - 証拠集約を通ったラウンドである - - 引き継いだ指摘が無い - - 新しい指摘が 1 件以上あり、そのすべてが minor である -- [ ] AC18: 新しい指摘に critical か major が 1 件でもあれば、今と同じく次のラウンドへ進む(終了コード 2) -- [ ] AC19: 証拠集約を通っていないラウンド(重要度を読めない)と、指摘の記録を読めないラウンドは、今の判定のまま変わらない -- [ ] AC20: `judge` の出力に、止める指摘の件数の行(`NEW_BLOCKING_FINDINGS=<件数>`、測れないときは `-`)が加わる。既存の行(`NEW_FINDINGS` など)の形は変わらない -- [ ] AC21: 最終の報告(`report`)に、minor だけで収束したラウンドであることと、最終スイープへ回した件数が出る +- [ ] AC17: 収束の判定は変わらない。証拠集約を通ったラウンドで新しい指摘が minor だけなら、今と同じく新しい指摘 0 件として収束する ### 変えないこと -- [ ] AC22: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)・`--only` の扱いは変わらない -- [ ] AC23: 収束の判定は母集合の各担当がレビューしたかを見ない(#631 を採らない) -- [ ] AC24: 既存のテストがすべて通る(下の検証手段) +- [ ] AC18: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)・`--only` の扱いは変わらない +- [ ] AC19: 収束の判定は母集合の各担当がレビューしたかを見ない(#631 を採らない) +- [ ] AC20: 既存のテストがすべて通る(下の検証手段) ## 非機能の条件 | 大項目 | 条件 | | --- | --- | | 性能・拡張性 | 「前のラウンドからの変更」の節は、変わったファイルが 50 件のとき 6,000 バイト以下に収まる(差分の本文を入れないため)。控えの取り直しで増えるのは、同じ実行の前のラウンドの指摘と返信の行だけである | -| 運用・保守性 | minor だけで収束したことは `judge` の出力と `report` に残り、後から数えられる(AC20 / AC21) | ## 影響 | 対象 | 影響 | | --- | --- | | 公開インタフェース | `cross-review` の既定の参加者が変わる(agy が外れる)。引数は増えない。`--include agy` で今の既定へ戻せる | -| データ | 状態ファイルのラウンドの記録に、止める指摘の件数(`new_blocking_findings`)と収束の理由(`converged_by`)が、参加者の記録に無視した除外(`ignored_exclude`)が加わる。無い旧い状態ファイルは今の判定で読む | -| 既存の振る舞い | minor だけのラウンドで収束する / 2 ラウンド目以降のプロンプトが変わる / 控えがラウンドごとに新しくなる / 設計 PR に観点が 1 つ増える | +| データ | 状態ファイルの参加者の記録に、無視した除外(`ignored_exclude`)が加わる。無い旧い状態ファイルは今の判定で読む | +| 既存の振る舞い | 2 ラウンド目以降のプロンプトが変わる / 控えがラウンドごとに新しくなる / 設計 PR に観点が 1 つ増える | ## 検証手段 @@ -149,6 +141,5 @@ | 母集合 | レビュー担当になり得るランタイムの集まり。`--exclude` / `--include` を当てる前の既定 | | 座席 | 1 ラウンドでレビューする 2 者。母集合のうち使える者から輪番で選ぶ(`review_seats`) | | 新しい指摘 | 直前のラウンドの指摘と一致しない、そのラウンドの指摘(`_new_finding_count`)。証拠集約を通ったラウンドでは数える 3 区分(`verified_blocking` / `needs_human_judgment` / `unrefuted`)だけ | -| 止める指摘 | 新しい指摘のうち重要度が critical か major のもの | | 既存コメントの控え | PR の既存のインラインコメント・レビュー本文・PR コメントを 1 行ずつ並べたファイル(`cross-review-pr-existing-comments.txt`)。プロンプトへそのまま入る | | 設計 PR | 変更ファイルに要求・設計・決定の記録の文書(`issues/` 配下の `*-requirements.md` / `*-design.md` / `*-design-decisions.md`)を含む Pull Request | From c5f91e1d0eefdb54ad5763bddd7527812e532c44 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 13:47:30 +0000 Subject: [PATCH 04/10] =?UTF-8?q?Docs:=20#542=20#786=20=E9=99=A4=E5=A4=96?= =?UTF-8?q?=E3=81=AE=E7=84=A1=E8=A6=96=E3=81=AE=20cross-refactoring=20?= =?UTF-8?q?=E3=81=B8=E3=81=AE=E6=B3=A2=E5=8F=8A=E3=83=BB--only=20=E3=81=AE?= =?UTF-8?q?=E6=89=B1=E3=81=84=E3=83=BB=E6=8E=A7=E3=81=88=E3=81=AE=E5=8F=96?= =?UTF-8?q?=E3=82=8A=E7=9B=B4=E3=81=97=E3=81=AE=20PR=20=E3=81=A8=E6=99=82?= =?UTF-8?q?=E7=82=B9=E3=82=92=E6=B1=BA=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design-decisions.md | 12 +++++++- issues/issue-542-786-design.md | 37 ++++++++++++++++++------ issues/issue-542-786-requirements.md | 6 +++- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/issues/issue-542-786-design-decisions.md b/issues/issue-542-786-design-decisions.md index bd8a7baba..3192d8583 100644 --- a/issues/issue-542-786-design-decisions.md +++ b/issues/issue-542-786-design-decisions.md @@ -19,11 +19,14 @@ agy が担当になると、テストを背景で起動して結果を残さず 今は母集合に無い者を `--exclude` に書くと `AssignmentError` で止まる。決定 1 の後は、今までの `--exclude agy` が すべて止まる。`CLAUDE.md` の cross-review 節も利用者の手元の手順も、`--exclude agy` を書いている。そのため止めずに無視する。 -綴りの誤りは今どおり止める(外したつもりの者が残る誤りは、名前の綴りでしか起きないため)。 +綴りの誤りは今どおり引数の型が止める(終了コード 2。外したつもりの者が残る誤りは、名前の綴りでしか起きないため)。 無視した名前を `excluded` に入れることは採らなかった。外した者と外す必要が無かった者が同じ記録になり、後から 母集合を読み違える。 +判定は共通層(`assignment.resolve_participants`)に置くため、cross-refactoring の `--exclude agy` も同じく中断から +無視へ変わる。Skill ごとに扱いを分けることは採らなかった。同じ引数の意味が Skill で変わる。 + ### 決定 3: 1 ラウンド目から「見つけた指摘はすべて出す」を求める 今のプロンプトには、見落としを残さないことを求める文が無い。`launch-reviewer.sh:166` の「全件」は、結果ファイルへ @@ -114,6 +117,13 @@ major の穴である。この変更は判定ではなく、担当が受け取 cross-refactoring の既定の参加者からは、#664 で agy を外してある。この指示が効くのは、`--include agy` で戻したときと、 他の担当が同じ形で終わるときである。 +### 決定 12: `--only` で名指しした者は、既定の母集合に無くても参加者にする + +決定 1 の後、`--only agy` は「参加者のいずれかを指定してください」で止まるようになる。`--only` は 1 者を名指しする +指定で、名指しした時点で利用者の意図は明らかである。そのため母集合に無い者でも、足す者として扱う。 +`--exclude` にも書いた名前は、今どおり矛盾として止める。止めて `--include agy` を求めることは採らなかった。 +同じ意図を 2 つの引数で書かせることになる。 + ## 判断に使った実測 | 対象 | 値 | 出所 | diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index 8c27e4aee..69348e901 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -47,8 +47,9 @@ | `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | +| `plugins/ndf/skills/cross-refactoring/tests/test_init.py` | cross-refactoring の `init` のテスト | 「母集合に無い者は外せない」の中断の期待を、無視して続ける期待へ替える | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | -| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節 | +| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行) | | テスト | 下の「テスト設計」 | 既存の母集合のテストを直し、新しい分岐のテストを足す | 変えないもの: @@ -120,7 +121,8 @@ plugins/ndf/ └── cross-refactoring/ ├── prompts/apply.md # 変える ├── scripts/refactor_lib/commands/setup.py # 変える - └── tests/test_assignment.py # 変える + ├── docs/01-state-and-propose.md # 変える + └── tests/test_assignment.py / test_init.py # 変える ``` ## 構造 @@ -165,7 +167,7 @@ def review_pool(host: str) -> list[str]: | --- | --- | --- | | 母集合か `--include` にある | 外す | 外す(同じ) | | 綴りの正しいランタイム名で、母集合にも `--include` にも無い | `AssignmentError`(終了コード 1) | 外さずに無視し、`Participants.ignored_exclude` に固定順で残す | -| 綴りの誤り | `AssignmentError` | `AssignmentError`(同じ) | +| 綴りの誤り | 引数の型が弾く(終了コード 2。共通層まで届かない) | 同じ | `Participants` に `ignored_exclude: list[str]`(既定は空)を足す。呼び出し側(cross-review の `_resolve_reviewers`、 cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準エラーへ出して続ける。 @@ -176,6 +178,14 @@ cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準 状態ファイルの `participants` には `ignored_exclude` を書き足す(`excluded` には入れない。外した者と区別するため)。 +**`--only` で名指しした者は、既定の母集合に無くても参加者にする**(決定 12)。`resolve_participants` は、`only` が +母集合にも `--include` にも無く、`--exclude` にも無いとき、`only` を足す者として扱ってから今の検査を通す。 +`--only agy` は `--include agy` 無しで今と同じく agy 1 者で回る。`--only agy --exclude agy` は今どおり矛盾で止まる。 + +**この共有層の変更は cross-refactoring にも及ぶ。** cross-refactoring の `init` は、母集合に無い者の除外で今は +中断する(終了コード 4)。変更後は無視して `ℹ` の 1 行を出し、続ける。重なり(足す者と外す者に同じ名前)と +`none` と名前の混在は、今どおり終了コード 4 で中断する。 + ### 既存コメントの控え(`state.py`) ```python @@ -186,9 +196,16 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No | 呼ぶ場所 | 失敗したとき | | --- | --- | | `init` の新規開始(今の場所) | 今と同じく `die`(終了コード 1) | -| `start-round`(2 ラウンド目以降。ラウンドを開いた後、担当を起動する前) | 前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | +| `start-round`(状態ファイルの通しで 2 ラウンド目以降。ラウンドを開いた後、担当を起動する前) | 前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | + +| 項目 | 値 | +| --- | --- | +| 取り直す時点 | 状態ファイルの通しのラウンドが 2 以上のとき(`round_in_pr` ではない)。通しの 1 ラウンド目は `init` が取った直後であるため取り直さない | +| 取得する PR | `current_pr`(PR の巻き直しの後は新しい PR) | +| 書く先 | `$TMP_DIR/cross-review-pr-existing-comments.txt`(今の置き場所。`launch-reviewer.sh` が読む名前は変えない) | +| 巻き直しの直後 | 取り直す(新しい PR の既存コメントに置き換わる)。同じ PR の前のラウンドが無いため、変更の節は書かない | -1 ラウンド目は `init` が取った直後であるため取り直さない。控えの形(1 行 1 件、`[PR-COMMENT]` などの接頭辞)は変えない。 +控えの形(1 行 1 件、`[PR-COMMENT]` などの接頭辞)は変えない。 ### 前のラウンドからの変更の節(`state.py start-round` → `launch-reviewer.sh`) @@ -281,7 +298,7 @@ sequenceDiagram ## 決定の記録 -決定の見出し・理由・採らなかった案は [issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある(11 件)。 +決定の見出し・理由・採らなかった案は [issue-542-786-design-decisions.md](issue-542-786-design-decisions.md) にある(12 件)。 ## テスト設計 @@ -290,13 +307,15 @@ sequenceDiagram | 受け入れ条件 | 何で確かめるか | | --- | --- | | AC1 AC2 AC3 | `test_lib_assignment.py`: `review_pool(h)` をホスト 4 通りで比べる。`resolve_participants` + `review_seats` で host=claude の round 1〜3 を比べる | -| AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 1 | +| AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | +| AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`only="agy", exclude=["agy"]` は今どおり `AssignmentError` | +| AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4 | | AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` が母集合の説明として 0 件 | | AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | | AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る | | AC10 | 同上: 1 ラウンド目・同じ head・`head_sha` の無いラウンドでファイルが無く、前の起動の残りも消える。プロンプトに節が入らない | | AC11 | 同上: 53 ファイルの差分で一覧が 50 件と「ほか 3 件」、ファイルの大きさが 6,000 バイト以下 | -| AC12 | 同上: `fetch-pr-comments.sh` を差し替えた偽物で、2 ラウンド目の `start-round` が呼び、控えが新しい中身になる。1 ラウンド目では呼ばない | +| AC12 AC12b | 同上: `fetch-pr-comments.sh` を差し替えた偽物で、2 ラウンド目の `start-round` が呼び、控えが新しい中身になる。1 ラウンド目では呼ばない。`set-current-pr` の後の `start-round` は新しい PR の番号で呼ぶ | | AC13 | 同上: 偽物が失敗すると `start-round` が終了コード 0 で `⚠` の行を出し、控えは前の中身のまま | | AC14 AC16 | `test_state_auto_review_templates.py`: `issues/issue-1-design.md` を含む変更が `common` / `docs_only` / `design` に、`issues/notes.md` と `docs/x-design.md` だけの変更が `design` を含まない | | AC15 | 目で見る(テンプレートの文言) | @@ -310,7 +329,7 @@ sequenceDiagram | 項目 | 内容 | | --- | --- | | ラウンドが実際に減るか | この変更の効果は、配布後の設計 PR のラウンド数で見る。比べる手段は #893 が作る。手元では退避された状態ファイル(`.git/ndf/worktree-trash/*/.cross_review/`)の `rounds` を数えて比べられる | -| 出し切りの指示で 1 ラウンド目の出力が増えるか | 増えると担当の所要が延びる。agy は既定から外れるため、無進捗の許容(480 秒)に当たる担当は既定では居ない。codex の無進捗の許容は 180 秒で、実装の PR の cross-review で所要を見る | +| 出し切りの指示で 1 ラウンド目の出力が増えるか | 増えると担当の所要が延びる。既定の担当の無進捗の許容は codex 180 秒・kiro 480 秒・claude 900 秒で、実装の PR の cross-review で 3 者の所要と打ち切りの有無を見る | | 控えの大きさ | #908 の実行 3 で控えは 54,957 バイトだった。取り直しで同じ実行の指摘と返信の行が増える。実装の PR の cross-review で大きさを見る(圧縮は範囲外) | | #892 の後の母集合で動いた実行が無い | claude が座席に入った実行は手元の記録にまだ無い。この変更の後の既定(claude / codex / kiro)で 2 ラウンド目に claude が座る | | 設計 PR の見分けの取りこぼし | ファイル名の規約(`-requirements.md` / `-design.md` / `-design-decisions.md`)から外れた設計文書は `design` に分類されない。`issues/` 配下の既存の設計文書の名前は、実装の時点で `ls issues/*design*` で確かめる | diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md index 0149a3654..b29df2f8e 100644 --- a/issues/issue-542-786-requirements.md +++ b/issues/issue-542-786-requirements.md @@ -34,6 +34,7 @@ 含む: - cross-review のレビュー担当の既定の母集合から agy を外すこと(`--include agy` で戻す) +- 既定の母集合に無い者を外す指定を、止めずに無視すること(共通層のため cross-review と cross-refactoring の両方)と、`--only` で名指しした者を母集合に無くても参加者にすること - レビューのプロンプトに足すもの: 出し切りの指示(全ラウンド)/ 前のラウンドからの変更と、変わった節を先に見る観点(2 ラウンド目以降)/ テストと背景の処理を起動しない指示 - 既存コメントの控えをラウンドの開始ごとに取り直すこと - 設計 PR を見分けて、設計向けの観点テンプレートを渡すこと @@ -63,6 +64,8 @@ - [ ] AC2: ホストが codex または kiro のときも、既定の参加者は claude / codex / kiro の 3 者である - [ ] AC3: ホストが agy のとき、参加者は agy を含む 4 者である(ホストは常に母集合に入る。#892) - [ ] AC4: `--include agy` を付けると agy が参加者に戻り、座席は今(v10.17.3)の既定と同じ輪番になる。`--exclude agy` を付けても(既定に無い者を外す指定でも)終了コード 0 で進む +- [ ] AC4b: `--only agy` を `--include agy` 無しで渡すと、agy 1 者で回る(名指しした者は既定の母集合に無くても参加者になる)。`--only agy --exclude agy` は今どおり終了コード 1 で止まる +- [ ] AC4c: cross-refactoring の `init` に `--exclude agy` を渡すと(cross-refactoring の母集合に agy は無い)、中断せずに `ℹ` で始まる 1 行を出して続ける。足す者と外す者の重なりは今どおり終了コード 4 で止まる - [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。母集合を 4 者と書いた記述は残らない - `plugins/ndf/skills/cross-review/SKILL.md` と `docs/05-pool-and-convergence.md` - 確定仕様 `docs/specifications/cross-review-participants-and-seats.md` @@ -83,6 +86,7 @@ ### 既存コメントの控え(#542) - [ ] AC12: ラウンドの開始(`start-round`)ごとに既存コメントの控えを取り直す。2 ラウンド目のプロンプトに、同じ実行の 1 ラウンド目で投稿された指摘と、その修正の返信が入る +- [ ] AC12b: PR の巻き直しの後の最初のラウンドでは、新しい PR の既存コメントで控えを取り直す - [ ] AC13: 取り直しに失敗したとき(`gh` の失敗など)、ラウンドは止まらず、前の控えのままプロンプトを作り、`⚠` で始まる 1 行で理由を出す ### 設計向けの観点(#542) @@ -100,7 +104,7 @@ ### 変えないこと -- [ ] AC18: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)・`--only` の扱いは変わらない +- [ ] AC18: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)は変わらない。`--only` は AC4b の点だけが変わる - [ ] AC19: 収束の判定は母集合の各担当がレビューしたかを見ない(#631 を採らない) - [ ] AC20: 既存のテストがすべて通る(下の検証手段) From 809c249a9cc9a5bb6dcf3480c66c0749f9d44fd0 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 13:54:35 +0000 Subject: [PATCH 05/10] =?UTF-8?q?Docs:=20#542=20#786=20=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E6=8F=9B=E3=81=88=E3=82=8B=E6=97=A2=E5=AD=98=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=83=BB=E6=8E=A7=E3=81=88=E3=81=AE=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=A4=B1=E6=95=97=E3=83=BB=E4=B8=80=E8=A6=A7=E3=81=AE=E3=83=90?= =?UTF-8?q?=E3=82=A4=E3=83=88=E6=95=B0=E3=81=AE=E4=B8=8A=E9=99=90=E3=83=BB?= =?UTF-8?q?=E5=A5=91=E7=B4=84=E6=96=87=E6=9B=B8=E3=81=AE=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=AF=BE=E8=B1=A1=E3=82=92=E8=B6=B3=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design-decisions.md | 2 +- issues/issue-542-786-design.md | 33 +++++++++++++++++------- issues/issue-542-786-requirements.md | 9 ++++--- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/issues/issue-542-786-design-decisions.md b/issues/issue-542-786-design-decisions.md index 3192d8583..7026008dd 100644 --- a/issues/issue-542-786-design-decisions.md +++ b/issues/issue-542-786-design-decisions.md @@ -66,7 +66,7 @@ agy が担当になると、テストを背景で起動して結果を残さず 知らない。直った指摘の近くを別の言い方で再び指摘すると、振動の検知(同じ位置の重なり 0.5 以上)に当たる。 #908 の実行 2 と #911 は振動で打ち切られた。 -取り直しに失敗しても止めないのは、前の控えでも今と同じ条件でレビューできるためである。`init` の失敗は今どおり +取り直しに失敗しても止めないのは、前の控えでも今と同じ条件でレビューできるためである。3 つの取得元の一部だけが失敗したときも前の控えを残す。一部だけの控えで上書きすると、前のラウンドの指摘が重複の検出から消える。`init` の失敗は今どおり 止める(控えが無いと重複の検出が働かない)。控えを圧縮することは範囲外にした。取り直しで増えるのは同じ実行の 前のラウンドの行だけで、まず大きさを実装の PR で見る。 diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index 69348e901..78839f9ca 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -45,11 +45,12 @@ | --- | --- | --- | | `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す | | `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す | -| `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す | +| `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す。先頭のコメントの「母集合が 4 者」を「担当は 4 ランタイムのどれでもなりうる」へ直す(`launch-codex.sh`・`launch-agy.sh` の同じコメントも) | +| `plugins/ndf/skills/fix/scripts/fetch-pr-comments.sh` | 既存コメントの 3 つの取得元を 1 本で取る | 引数 `--strict` を足す。付けたときは 3 つのどれか 1 つでも失敗すれば終了コード 1(付けないときは今どおり 3 つとも失敗したときだけ 1) | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-refactoring/tests/test_init.py` | cross-refactoring の `init` のテスト | 「母集合に無い者は外せない」の中断の期待を、無視して続ける期待へ替える | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | -| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行) | +| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行と、203 行の「共通層が返す 7 項目」)。`cross-review/docs/04-contracts.md`(`participants` の 8 項目と一時ファイルの一覧)と確定仕様 `cross-review-participants-and-seats.md` の参加者の記録の表(8 項目)は、`ignored_exclude` と変更の節のファイルを足して 9 項目・一時ファイル 1 つ増へ直す | | テスト | 下の「テスト設計」 | 既存の母集合のテストを直し、新しい分岐のテストを足す | 変えないもの: @@ -60,7 +61,7 @@ | `_new_finding_count` / `_evaluate_convergence` | 新しい指摘の数え方と収束の判定 | | `cmd_check_oscillation` | 振動の検知 | | `_handle_no_result_round` | 結果が無い担当の起動し直し | -| `fix/scripts/fetch-pr-comments.sh` | 控えの形 | +| `fix/scripts/fetch-pr-comments.sh` の出力 | 控えの形(取得の失敗を返す引数だけを足す) | | `refactor_pool` | cross-refactoring の母集合 | ### 構成要素図 @@ -118,6 +119,7 @@ plugins/ndf/ │ ├── scripts/state.py # 変える │ ├── scripts/launch-reviewer.sh # 変える │ └── tests/ # 変える・足す + ├── fix/scripts/fetch-pr-comments.sh # 変える(--strict) └── cross-refactoring/ ├── prompts/apply.md # 変える ├── scripts/refactor_lib/commands/setup.py # 変える @@ -196,7 +198,7 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No | 呼ぶ場所 | 失敗したとき | | --- | --- | | `init` の新規開始(今の場所) | 今と同じく `die`(終了コード 1) | -| `start-round`(状態ファイルの通しで 2 ラウンド目以降。ラウンドを開いた後、担当を起動する前) | 前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | +| `start-round`(状態ファイルの通しで 2 ラウンド目以降。ラウンドを開いた後、担当を起動する前)。`--strict` を付けて呼び、出力を一時の名前のファイルへ書いてから、成功したときだけ控えへ改名する | 3 つの取得元のどれか 1 つでも失敗したら(終了コード 1)、前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | | 項目 | 値 | | --- | --- | @@ -219,8 +221,8 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No | 違う | 書く(形は冒頭の例) | | `git diff --name-only <前> <今>` が失敗した | 書かず、`⚠ 前のラウンドからの変更を取れませんでした` を出して続ける | -- 変わったファイルは `git -C diff --name-only <前> <今>` の出力の順に並べ、50 件を上限にする。超えた分は - 「ほか N 件」の 1 行にする +- 変わったファイルは `git -C diff --name-only <前> <今>` の出力の順に並べる。50 件に達するか、一覧の行の + バイト数の合計が次の 1 行で 5,000 を超えるところで打ち切り、残りは「ほか N 件」の 1 行にする - 差分の本文は書かない - 同じ名前のファイルが前の起動で残っていれば、書かない場合も消す(古い節を次のプロンプトへ入れないため) @@ -293,7 +295,7 @@ sequenceDiagram | 条件 | 実現方式 | | --- | --- | -| 変更の節は 50 件で 6,000 バイト以下 | ファイル名だけを並べ、差分の本文を入れない。1 件 100 バイトとして 5,000 バイトと、定型の文 約 600 バイト | +| 変更の節は 6,000 バイト以下 | ファイル名だけを並べ、差分の本文を入れない。一覧は 5,000 バイトで打ち切る(長いパスでも超えない)。定型の文は約 600 バイト、「ほか N 件」の行は 30 バイト以下 | | 控えの増分は同じ実行の前のラウンドの分だけ | 取り直しは `fetch-pr-comments.sh` の全件の取得で、増えるのは前の取得の後に投稿された行だけである | ## 決定の記録 @@ -310,17 +312,28 @@ sequenceDiagram | AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | | AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`only="agy", exclude=["agy"]` は今どおり `AssignmentError` | | AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4 | -| AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` が母集合の説明として 0 件 | +| AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` の当たりのうち、現行の説明は 0 件。出た版の変更点の記録(`plugins/ndf/README.md` の 10.17.3 の更新案内)は書き換えない | | AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | | AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る | | AC10 | 同上: 1 ラウンド目・同じ head・`head_sha` の無いラウンドでファイルが無く、前の起動の残りも消える。プロンプトに節が入らない | -| AC11 | 同上: 53 ファイルの差分で一覧が 50 件と「ほか 3 件」、ファイルの大きさが 6,000 バイト以下 | +| AC11 | 同上: 53 ファイルの差分で一覧が 50 件と「ほか 3 件」。名前が 200 バイトのファイル 40 件の差分で、一覧が 5,000 バイトで打ち切られ、ファイルの大きさが 6,000 バイト以下 | | AC12 AC12b | 同上: `fetch-pr-comments.sh` を差し替えた偽物で、2 ラウンド目の `start-round` が呼び、控えが新しい中身になる。1 ラウンド目では呼ばない。`set-current-pr` の後の `start-round` は新しい PR の番号で呼ぶ | +| AC12a | 同上: 偽物の `fetch-pr-comments.sh --strict` が 3 つのうち 1 つの失敗で 1 を返すと、控えが前の中身のまま残る。`fix` の側のテスト: `--strict` 無しでは今どおり 1 つの失敗で 0 | | AC13 | 同上: 偽物が失敗すると `start-round` が終了コード 0 で `⚠` の行を出し、控えは前の中身のまま | | AC14 AC16 | `test_state_auto_review_templates.py`: `issues/issue-1-design.md` を含む変更が `common` / `docs_only` / `design` に、`issues/notes.md` と `docs/x-design.md` だけの変更が `design` を含まない | | AC15 | 目で見る(テンプレートの文言) | | AC17 | 既存の `test_classify_findings.py`(minor は数えない区分へ落ちる)と judge のテストが変更なしで通る | -| AC18 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py`・`--only` のテストが変更なしで通る | +| AC18 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py` が変更なしで通る | + +**期待を書き換える既存のテスト**(決定 1・2・12 で振る舞いが変わるため): + +| テスト | 今の期待 | 変更後の期待 | +| --- | --- | --- | +| `cross-review/tests/test_state_review_pool.py` の `--only` のテスト(264 行・292 行付近) | 既定の母集合が 4 者 | 3 者(`--only agy` は AC4b) | +| 同じファイルの除外のテスト(422〜435 行付近) | `--exclude agy` で `excluded == ["agy"]` | `excluded == []`・`ignored_exclude == ["agy"]` | +| `cross-refactoring/tests/test_assignment.py`(56〜58 行) | `review_pool(host) == list(ALL_RUNTIMES)` | 既定の 3 者とホスト | +| `scripts/tests/test_lib_assignment.py`(66 行付近) | `review_pool` が 4 者 | 同上 | +| `cross-refactoring/tests/test_init.py`(285 行) | `--exclude agy` で中断(終了コード 4) | 続ける(AC4c) | | AC19 | `_evaluate_convergence` と `review_seats` を変えない(差分に現れない)ことを実装の PR で確かめる | | AC20 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md index b29df2f8e..cda151a61 100644 --- a/issues/issue-542-786-requirements.md +++ b/issues/issue-542-786-requirements.md @@ -66,7 +66,7 @@ - [ ] AC4: `--include agy` を付けると agy が参加者に戻り、座席は今(v10.17.3)の既定と同じ輪番になる。`--exclude agy` を付けても(既定に無い者を外す指定でも)終了コード 0 で進む - [ ] AC4b: `--only agy` を `--include agy` 無しで渡すと、agy 1 者で回る(名指しした者は既定の母集合に無くても参加者になる)。`--only agy --exclude agy` は今どおり終了コード 1 で止まる - [ ] AC4c: cross-refactoring の `init` に `--exclude agy` を渡すと(cross-refactoring の母集合に agy は無い)、中断せずに `ℹ` で始まる 1 行を出して続ける。足す者と外す者の重なりは今どおり終了コード 4 で止まる -- [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。母集合を 4 者と書いた記述は残らない +- [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。現行の説明として母集合を 4 者と書いた記述は残らない(出た版の変更点の記録は書き換えない) - `plugins/ndf/skills/cross-review/SKILL.md` と `docs/05-pool-and-convergence.md` - 確定仕様 `docs/specifications/cross-review-participants-and-seats.md` - `plugins/ndf/README.md` と `CLAUDE.md` の cross-review 節 @@ -81,11 +81,12 @@ - [ ] AC8: どのラウンドのプロンプトも「見つけた指摘はこのラウンドですべて出す。次のラウンドへ回さない」の指示を持つ。出す対象(修正アクションのある指摘だけ)と、含めてはいけないもの(良い点・nit)は今のまま変わらない - [ ] AC9: 同じ PR の 2 ラウンド目以降で、前のラウンドの head と今の head が違うとき、プロンプトは「前のラウンドからの変更」の節を持つ。節には 2 つの head の SHA・その間に変わったファイルの一覧・差分を読むコマンド・「変わった節と、それを参照する節・同じ契約を使う節を先に見る。修正が新しく作った経路(状態・分岐・引数)の穴を見る」の観点が入る - [ ] AC10: 1 ラウンド目と、前のラウンドと head が同じラウンド(修正のコミットが無い)のプロンプトは「前のラウンドからの変更」の節を持たない -- [ ] AC11: 変わったファイルの一覧は 50 件を上限とし、超えた分は件数だけを書く。差分の本文はプロンプトへ入れない(担当が作業ツリーでコマンドを実行して読む) +- [ ] AC11: 変わったファイルの一覧は 50 件か 5,000 バイトの先に当たる方で打ち切り、残りは件数だけを書く。差分の本文はプロンプトへ入れない(担当が作業ツリーでコマンドを実行して読む) ### 既存コメントの控え(#542) - [ ] AC12: ラウンドの開始(`start-round`)ごとに既存コメントの控えを取り直す。2 ラウンド目のプロンプトに、同じ実行の 1 ラウンド目で投稿された指摘と、その修正の返信が入る +- [ ] AC12a: 取り直しで 3 つの取得元(インラインコメント・レビュー本文・PR コメント)のどれか 1 つでも失敗したら、前の控えを残す(AC13 と同じ扱い) - [ ] AC12b: PR の巻き直しの後の最初のラウンドでは、新しい PR の既存コメントで控えを取り直す - [ ] AC13: 取り直しに失敗したとき(`gh` の失敗など)、ラウンドは止まらず、前の控えのままプロンプトを作り、`⚠` で始まる 1 行で理由を出す @@ -104,7 +105,7 @@ ### 変えないこと -- [ ] AC18: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)は変わらない。`--only` は AC4b の点だけが変わる +- [ ] AC18: 振動の検知の閾値(0.5)・`max_rounds`(12)・結果が無い担当の起動し直し(同じ席で 1 度だけ)は変わらない。これらのテストは変更なしで通る。`--only` は AC4b の点だけが変わる - [ ] AC19: 収束の判定は母集合の各担当がレビューしたかを見ない(#631 を採らない) - [ ] AC20: 既存のテストがすべて通る(下の検証手段) @@ -112,7 +113,7 @@ | 大項目 | 条件 | | --- | --- | -| 性能・拡張性 | 「前のラウンドからの変更」の節は、変わったファイルが 50 件のとき 6,000 バイト以下に収まる(差分の本文を入れないため)。控えの取り直しで増えるのは、同じ実行の前のラウンドの指摘と返信の行だけである | +| 性能・拡張性 | 「前のラウンドからの変更」の節は、ファイル名の長さと件数によらず 6,000 バイト以下に収まる(差分の本文を入れず、一覧を 5,000 バイトで打ち切るため)。控えの取り直しで増えるのは、同じ実行の前のラウンドの指摘と返信の行だけである | ## 影響 From 189d3d03a60cf2810d17aa5b70bd2cda7f0acde3 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 14:02:01 +0000 Subject: [PATCH 06/10] =?UTF-8?q?Docs:=20#542=20#786=20--only=20=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E7=AE=87=E6=89=80=E3=83=BB=E6=AF=94=E3=81=B9?= =?UTF-8?q?=E3=82=8B=E5=89=8D=E3=81=AE=E3=83=A9=E3=82=A6=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=81=AE=E9=81=B8=E3=81=B3=E6=96=B9=E3=83=BB=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=AE=E7=AF=80=E3=81=AE=E6=8C=BF=E5=85=A5=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E3=83=BB=E6=9B=B8=E3=81=8D=E6=8F=9B=E3=81=88=E3=82=8B=E6=97=A2?= =?UTF-8?q?=E5=AD=98=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=B6=B3=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index 78839f9ca..d6ac0e38d 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -43,14 +43,14 @@ | 要素 | 責務 | 変更 | | --- | --- | --- | -| `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す | +| `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す。`only` が母集合・`--include`・`--exclude` のどれにも無いときは足す者として扱う(決定 12)。`Participants.to_state()` は `ignored_exclude` を含む 8 項目を返す | | `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す。先頭のコメントの「母集合が 4 者」を「担当は 4 ランタイムのどれでもなりうる」へ直す(`launch-codex.sh`・`launch-agy.sh` の同じコメントも) | | `plugins/ndf/skills/fix/scripts/fetch-pr-comments.sh` | 既存コメントの 3 つの取得元を 1 本で取る | 引数 `--strict` を足す。付けたときは 3 つのどれか 1 つでも失敗すれば終了コード 1(付けないときは今どおり 3 つとも失敗したときだけ 1) | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | | `plugins/ndf/skills/cross-refactoring/tests/test_init.py` | cross-refactoring の `init` のテスト | 「母集合に無い者は外せない」の中断の期待を、無視して続ける期待へ替える | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | -| 文書 | 使い方と仕様 | `cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行と、203 行の「共通層が返す 7 項目」)。`cross-review/docs/04-contracts.md`(`participants` の 8 項目と一時ファイルの一覧)と確定仕様 `cross-review-participants-and-seats.md` の参加者の記録の表(8 項目)は、`ignored_exclude` と変更の節のファイルを足して 9 項目・一時ファイル 1 つ増へ直す | +| 文書 | 使い方と仕様 | `cross-review/docs/01-state-and-review.md`(Step 1 の `start-round` に、控えの取り直し・失敗しても続けること・変更の節のファイルを書く / 消すことを足す)・`cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行と、203 行の「共通層が返す 7 項目」)。`cross-review/docs/04-contracts.md`(`participants` の 8 項目と一時ファイルの一覧)と確定仕様 `cross-review-participants-and-seats.md` の参加者の記録の表(8 項目)は、`ignored_exclude` と変更の節のファイルを足して 9 項目・一時ファイル 1 つ増へ直す | | テスト | 下の「テスト設計」 | 既存の母集合のテストを直し、新しい分岐のテストを足す | 変えないもの: @@ -216,7 +216,7 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No | 条件 | ファイル | | --- | --- | | 同じ PR の前のラウンドが無い(1 ラウンド目・PR の切り替え直後) | 書かない | -| 前のラウンドか今のラウンドの `head_sha` が無い | 書かない | +| 比べる前のラウンド(今のラウンドより前で、`pr` が `current_pr` と同じもののうち最も新しいもの)か今のラウンドの `head_sha` が無い | 書かない | | 2 つの `head_sha` が同じ | 書かない | | 違う | 書く(形は冒頭の例) | | `git diff --name-only <前> <今>` が失敗した | 書かず、`⚠ 前のラウンドからの変更を取れませんでした` を出して続ける | @@ -226,7 +226,7 @@ def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | No - 差分の本文は書かない - 同じ名前のファイルが前の起動で残っていれば、書かない場合も消す(古い節を次のプロンプトへ入れないため) -`launch-reviewer.sh` は、このファイルが空でなければ中身を「既存コメントスナップショット」の節の直後へ入れる。 +`launch-reviewer.sh` は、このファイルが空でなければ中身を「既存コメントスナップショット」の節の後、追加レビュー観点(`$EXTRA_REVIEW_BLOCK`)の前へ入れる。 無ければ何も入れない。 ### プロンプトに足す指示(`launch-reviewer.sh`) @@ -334,6 +334,9 @@ sequenceDiagram | `cross-refactoring/tests/test_assignment.py`(56〜58 行) | `review_pool(host) == list(ALL_RUNTIMES)` | 既定の 3 者とホスト | | `scripts/tests/test_lib_assignment.py`(66 行付近) | `review_pool` が 4 者 | 同上 | | `cross-refactoring/tests/test_init.py`(285 行) | `--exclude agy` で中断(終了コード 4) | 続ける(AC4c) | +| `scripts/tests/test_lib_participants.py`(147 行付近) | 母集合に無い者の除外は例外 | 無視して `ignored_exclude` に残す | +| 同じファイル(193 行付近) | `to_state()` が 7 項目と完全一致 | `ignored_exclude` を含む 8 項目 | +| `cross-review/tests/test_state_resume_args.py`(195 行以降) | 再開の `--exclude agy` が `excluded` に残る | 既定の母集合では `ignored_exclude` に残り、`excluded` は空 | | AC19 | `_evaluate_convergence` と `review_seats` を変えない(差分に現れない)ことを実装の PR で確かめる | | AC20 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | From f700f5fcef2b1d8b2223318dcb5e51cf3ad27ff3 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 14:10:22 +0000 Subject: [PATCH 07/10] =?UTF-8?q?Docs:=20#542=20#786=20--only=20=E3=81=A7?= =?UTF-8?q?=E8=B6=B3=E3=81=97=E3=81=9F=E8=80=85=E3=81=AE=E8=A8=98=E9=8C=B2?= =?UTF-8?q?=E3=83=BB=E5=A0=B1=E5=91=8A=E3=81=AE=E9=99=A4=E5=A4=96=E3=81=AE?= =?UTF-8?q?=E8=A1=8C=E3=83=BB=E6=9B=B8=E3=81=8D=E6=8F=9B=E3=81=88=E3=82=8B?= =?UTF-8?q?=E6=97=A2=E5=AD=98=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE=E7=89=B9?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=B6=B3=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design.md | 15 ++++++++++----- issues/issue-542-786-requirements.md | 3 ++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index d6ac0e38d..423a613e7 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -44,7 +44,7 @@ | 要素 | 責務 | 変更 | | --- | --- | --- | | `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す。`only` が母集合・`--include`・`--exclude` のどれにも無いときは足す者として扱う(決定 12)。`Participants.to_state()` は `ignored_exclude` を含む 8 項目を返す | -| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す | +| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す。`report` の参加者の節に「--exclude で指定したが既定の母集合に無かった者」の 1 行を足し、`ignored_exclude` を出す | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す。先頭のコメントの「母集合が 4 者」を「担当は 4 ランタイムのどれでもなりうる」へ直す(`launch-codex.sh`・`launch-agy.sh` の同じコメントも) | | `plugins/ndf/skills/fix/scripts/fetch-pr-comments.sh` | 既存コメントの 3 つの取得元を 1 本で取る | 引数 `--strict` を足す。付けたときは 3 つのどれか 1 つでも失敗すれば終了コード 1(付けないときは今どおり 3 つとも失敗したときだけ 1) | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | @@ -183,6 +183,8 @@ cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準 **`--only` で名指しした者は、既定の母集合に無くても参加者にする**(決定 12)。`resolve_participants` は、`only` が 母集合にも `--include` にも無く、`--exclude` にも無いとき、`only` を足す者として扱ってから今の検査を通す。 `--only agy` は `--include agy` 無しで今と同じく agy 1 者で回る。`--only agy --exclude agy` は今どおり矛盾で止まる。 +足した名前は `participants.included` に書かない(記録は `only` だけが持つ)。再開で `--only none` を渡すと、 +既定の母集合へ戻り、agy は参加者から外れる。 **この共有層の変更は cross-refactoring にも及ぶ。** cross-refactoring の `init` は、母集合に無い者の除外で今は 中断する(終了コード 4)。変更後は無視して `ℹ` の 1 行を出し、続ける。重なり(足す者と外す者に同じ名前)と @@ -310,8 +312,9 @@ sequenceDiagram | --- | --- | | AC1 AC2 AC3 | `test_lib_assignment.py`: `review_pool(h)` をホスト 4 通りで比べる。`resolve_participants` + `review_seats` で host=claude の round 1〜3 を比べる | | AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | -| AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`only="agy", exclude=["agy"]` は今どおり `AssignmentError` | +| AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`included` は空。`only="agy", exclude=["agy"]` は今どおり `AssignmentError`。`test_state_resume_args.py`: `--only agy` で始めた実行を `--only none` で再開すると、参加者が既定の 3 者に戻る | | AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4 | +| AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え | | AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` の当たりのうち、現行の説明は 0 件。出た版の変更点の記録(`plugins/ndf/README.md` の 10.17.3 の更新案内)は書き換えない | | AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | | AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る | @@ -324,6 +327,8 @@ sequenceDiagram | AC15 | 目で見る(テンプレートの文言) | | AC17 | 既存の `test_classify_findings.py`(minor は数えない区分へ落ちる)と judge のテストが変更なしで通る | | AC18 | 既存の `test_state_check_oscillation.py`・`test_judge_no_result_reason.py` が変更なしで通る | +| AC19 | `_evaluate_convergence` と `review_seats` を変えない(差分に現れない)ことを実装の PR で確かめる | +| AC20 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | **期待を書き換える既存のテスト**(決定 1・2・12 で振る舞いが変わるため): @@ -331,14 +336,14 @@ sequenceDiagram | --- | --- | --- | | `cross-review/tests/test_state_review_pool.py` の `--only` のテスト(264 行・292 行付近) | 既定の母集合が 4 者 | 3 者(`--only agy` は AC4b) | | 同じファイルの除外のテスト(422〜435 行付近) | `--exclude agy` で `excluded == ["agy"]` | `excluded == []`・`ignored_exclude == ["agy"]` | +| 同じファイルの、`new_init` の既定の母集合を 4 者と期待する残りのテスト(400〜402・442・452・498〜500・507・552〜554・569 行付近) | 母集合・`available`・`calls` が agy を含む 4 者 | 既定の 3 者(agy を含む期待は `--include agy` を渡す形へ) | +| 同じファイルの `test_the_report_lists_who_took_part`(727 行付近) | 静的な参加者の記録から報告を作る | `init --exclude agy` で作った状態から報告を作り、「既定の母集合に無かった者: agy」の行が出る | | `cross-refactoring/tests/test_assignment.py`(56〜58 行) | `review_pool(host) == list(ALL_RUNTIMES)` | 既定の 3 者とホスト | | `scripts/tests/test_lib_assignment.py`(66 行付近) | `review_pool` が 4 者 | 同上 | | `cross-refactoring/tests/test_init.py`(285 行) | `--exclude agy` で中断(終了コード 4) | 続ける(AC4c) | | `scripts/tests/test_lib_participants.py`(147 行付近) | 母集合に無い者の除外は例外 | 無視して `ignored_exclude` に残す | | 同じファイル(193 行付近) | `to_state()` が 7 項目と完全一致 | `ignored_exclude` を含む 8 項目 | -| `cross-review/tests/test_state_resume_args.py`(195 行以降) | 再開の `--exclude agy` が `excluded` に残る | 既定の母集合では `ignored_exclude` に残り、`excluded` は空 | -| AC19 | `_evaluate_convergence` と `review_seats` を変えない(差分に現れない)ことを実装の PR で確かめる | -| AC20 | `uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4` が通る | +| `cross-review/tests/test_state_resume_args.py` の `test_exclude_reruns_the_probe_and_drops_the_name`・`test_the_participants_are_recorded_as_one_change`・`test_unpassed_arguments_come_from_the_state_file` | 再開の `--exclude agy` が `excluded` に残る | 既定の母集合では `ignored_exclude` に残り、`excluded` は空 | ## 未確認のまま残ること diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md index cda151a61..054258b35 100644 --- a/issues/issue-542-786-requirements.md +++ b/issues/issue-542-786-requirements.md @@ -64,8 +64,9 @@ - [ ] AC2: ホストが codex または kiro のときも、既定の参加者は claude / codex / kiro の 3 者である - [ ] AC3: ホストが agy のとき、参加者は agy を含む 4 者である(ホストは常に母集合に入る。#892) - [ ] AC4: `--include agy` を付けると agy が参加者に戻り、座席は今(v10.17.3)の既定と同じ輪番になる。`--exclude agy` を付けても(既定に無い者を外す指定でも)終了コード 0 で進む -- [ ] AC4b: `--only agy` を `--include agy` 無しで渡すと、agy 1 者で回る(名指しした者は既定の母集合に無くても参加者になる)。`--only agy --exclude agy` は今どおり終了コード 1 で止まる +- [ ] AC4b: `--only agy` を `--include agy` 無しで渡すと、agy 1 者で回る(名指しした者は既定の母集合に無くても参加者になる)。`--only agy --exclude agy` は今どおり終了コード 1 で止まる。再開で `--only none` を渡すと既定の 3 者に戻る - [ ] AC4c: cross-refactoring の `init` に `--exclude agy` を渡すと(cross-refactoring の母集合に agy は無い)、中断せずに `ℹ` で始まる 1 行を出して続ける。足す者と外す者の重なりは今どおり終了コード 4 で止まる +- [ ] AC4d: 完了の報告(`report`)に、`--exclude` で指定したが既定の母集合に無かった者が 1 行で出る - [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。現行の説明として母集合を 4 者と書いた記述は残らない(出た版の変更点の記録は書き換えない) - `plugins/ndf/skills/cross-review/SKILL.md` と `docs/05-pool-and-convergence.md` - 確定仕様 `docs/specifications/cross-review-participants-and-seats.md` From 02613a222d6c4996fe2121653cfa24463981b739 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 14:21:15 +0000 Subject: [PATCH 08/10] =?UTF-8?q?Docs:=20#542=20#786=20=E6=8E=A7=E3=81=88?= =?UTF-8?q?=E3=81=AE=E5=8F=96=E5=BE=97=E3=81=AE=E5=8E=B3=E3=81=97=E3=81=95?= =?UTF-8?q?=E3=82=92=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E5=85=83=E3=81=94?= =?UTF-8?q?=E3=81=A8=E3=81=AB=E5=88=86=E3=81=91=E3=80=81--only=20=E3=81=AE?= =?UTF-8?q?=E5=90=8D=E5=89=8D=E3=82=92=E8=B6=B3=E3=81=99=E5=89=8D=E3=81=AB?= =?UTF-8?q?=E7=A2=BA=E3=81=8B=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index 423a613e7..cea4fc776 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -180,7 +180,8 @@ cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準 状態ファイルの `participants` には `ignored_exclude` を書き足す(`excluded` には入れない。外した者と区別するため)。 -**`--only` で名指しした者は、既定の母集合に無くても参加者にする**(決定 12)。`resolve_participants` は、`only` が +**`--only` で名指しした者は、既定の母集合に無くても参加者にする**(決定 12)。`resolve_participants` は、まず `only` が +ランタイムの名前(`ALL_RUNTIMES`)かを確かめ、違えば今の綴りの検査と同じく `AssignmentError` にする。名前が正しく、 母集合にも `--include` にも無く、`--exclude` にも無いとき、`only` を足す者として扱ってから今の検査を通す。 `--only agy` は `--include agy` 無しで今と同じく agy 1 者で回る。`--only agy --exclude agy` は今どおり矛盾で止まる。 足した名前は `participants.included` に書かない(記録は `only` だけが持つ)。再開で `--only none` を渡すと、 @@ -193,14 +194,17 @@ cross-refactoring の `setup.py`)は、空でなければ次の 1 行を標準 ### 既存コメントの控え(`state.py`) ```python -def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path) -> str | None: - """fetch-pr-comments.sh を呼び、成功なら path へ書いて None、失敗なら理由の文を返す。""" +def _fetch_existing_comments(repo: str, pr: int, path: pathlib.Path, *, strict: bool) -> str | None: + """fetch-pr-comments.sh を呼び、成功なら path へ書いて None、失敗なら理由の文を返す。 + + strict=True なら --strict を付け、一時の名前へ書いてから成功したときだけ path へ改名する。 + """ ``` | 呼ぶ場所 | 失敗したとき | | --- | --- | -| `init` の新規開始(今の場所) | 今と同じく `die`(終了コード 1) | -| `start-round`(状態ファイルの通しで 2 ラウンド目以降。ラウンドを開いた後、担当を起動する前)。`--strict` を付けて呼び、出力を一時の名前のファイルへ書いてから、成功したときだけ控えへ改名する | 3 つの取得元のどれか 1 つでも失敗したら(終了コード 1)、前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | +| `init` の新規開始(今の場所)。`strict=False` で呼ぶ | 今と同じく、3 つとも失敗したときだけ `die`(終了コード 1)。1〜2 つの失敗は今どおり続ける | +| `start-round`(状態ファイルの通しで 2 ラウンド目以降。ラウンドを開いた後、担当を起動する前)。`strict=True` で呼ぶ(`--strict` を付け、出力を一時の名前のファイルへ書いてから、成功したときだけ控えへ改名する) | 3 つの取得元のどれか 1 つでも失敗したら(終了コード 1)、前の控えを残し、`⚠ 既存コメントの控えを取り直せませんでした(<理由の先頭 200 字>)。前の控えのまま進めます` を標準エラーへ出して続ける | | 項目 | 値 | | --- | --- | @@ -312,7 +316,7 @@ sequenceDiagram | --- | --- | | AC1 AC2 AC3 | `test_lib_assignment.py`: `review_pool(h)` をホスト 4 通りで比べる。`resolve_participants` + `review_seats` で host=claude の round 1〜3 を比べる | | AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | -| AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`included` は空。`only="agy", exclude=["agy"]` は今どおり `AssignmentError`。`test_state_resume_args.py`: `--only agy` で始めた実行を `--only none` で再開すると、参加者が既定の 3 者に戻る | +| AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`included` は空。`only="agy", exclude=["agy"]` は今どおり `AssignmentError`。`only="typo"` は `probe` を呼ぶ前に `AssignmentError`。`test_state_resume_args.py`: `--only agy` で始めた実行を `--only none` で再開すると、参加者が既定の 3 者に戻る | | AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4 | | AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え | | AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` の当たりのうち、現行の説明は 0 件。出た版の変更点の記録(`plugins/ndf/README.md` の 10.17.3 の更新案内)は書き換えない | @@ -321,7 +325,7 @@ sequenceDiagram | AC10 | 同上: 1 ラウンド目・同じ head・`head_sha` の無いラウンドでファイルが無く、前の起動の残りも消える。プロンプトに節が入らない | | AC11 | 同上: 53 ファイルの差分で一覧が 50 件と「ほか 3 件」。名前が 200 バイトのファイル 40 件の差分で、一覧が 5,000 バイトで打ち切られ、ファイルの大きさが 6,000 バイト以下 | | AC12 AC12b | 同上: `fetch-pr-comments.sh` を差し替えた偽物で、2 ラウンド目の `start-round` が呼び、控えが新しい中身になる。1 ラウンド目では呼ばない。`set-current-pr` の後の `start-round` は新しい PR の番号で呼ぶ | -| AC12a | 同上: 偽物の `fetch-pr-comments.sh --strict` が 3 つのうち 1 つの失敗で 1 を返すと、控えが前の中身のまま残る。`fix` の側のテスト: `--strict` 無しでは今どおり 1 つの失敗で 0 | +| AC12a | 同上: 偽物の `fetch-pr-comments.sh --strict` が 3 つのうち 1 つの失敗で 1 を返すと、控えが前の中身のまま残る。`init`(`strict=False`)では 1 つの失敗でも控えを書いて続け、3 つとも失敗すると終了コード 1。`fix` の側のテスト: `--strict` 無しでは今どおり 1 つの失敗で 0 | | AC13 | 同上: 偽物が失敗すると `start-round` が終了コード 0 で `⚠` の行を出し、控えは前の中身のまま | | AC14 AC16 | `test_state_auto_review_templates.py`: `issues/issue-1-design.md` を含む変更が `common` / `docs_only` / `design` に、`issues/notes.md` と `docs/x-design.md` だけの変更が `design` を含まない | | AC15 | 目で見る(テンプレートの文言) | From 19cfb1a825715891ca78f7c6fc3420c6327c5aa0 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 14:25:56 +0000 Subject: [PATCH 09/10] =?UTF-8?q?Docs:=20#542=20#786=20=E7=84=A1=E8=A6=96?= =?UTF-8?q?=E3=81=97=E3=81=9F=E9=99=A4=E5=A4=96=E3=82=92=E5=86=8D=E9=96=8B?= =?UTF-8?q?=E3=81=A7=E5=BC=95=E3=81=8D=E7=B6=99=E3=81=8E=E3=80=81cross-ref?= =?UTF-8?q?actoring=20=E3=81=AE=E5=A0=B1=E5=91=8A=E3=81=AB=E3=82=82?= =?UTF-8?q?=E5=87=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design.md | 7 ++++--- issues/issue-542-786-requirements.md | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index cea4fc776..b771f19cd 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -44,10 +44,11 @@ | 要素 | 責務 | 変更 | | --- | --- | --- | | `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す。`only` が母集合・`--include`・`--exclude` のどれにも無いときは足す者として扱う(決定 12)。`Participants.to_state()` は `ignored_exclude` を含む 8 項目を返す | -| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す。`report` の参加者の節に「--exclude で指定したが既定の母集合に無かった者」の 1 行を足し、`ignored_exclude` を出す | +| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す。`report` の参加者の節に「--exclude で指定したが既定の母集合に無かった者」の 1 行を足し、`ignored_exclude` を出す。再開の参加者の作り直しは、`--exclude` を渡さないとき `excluded` と `ignored_exclude` の両方を足し戻す(`--include` に同じ名前を渡したときは足し戻さず、新しい指定を優先する) | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す。先頭のコメントの「母集合が 4 者」を「担当は 4 ランタイムのどれでもなりうる」へ直す(`launch-codex.sh`・`launch-agy.sh` の同じコメントも) | | `plugins/ndf/skills/fix/scripts/fetch-pr-comments.sh` | 既存コメントの 3 つの取得元を 1 本で取る | 引数 `--strict` を足す。付けたときは 3 つのどれか 1 つでも失敗すれば終了コード 1(付けないときは今どおり 3 つとも失敗したときだけ 1) | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | +| `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/report.py` | cross-refactoring の完了報告 | cross-review の `report` と同じく「--exclude で指定したが既定の母集合に無かった者」の 1 行を足す | | `plugins/ndf/skills/cross-refactoring/tests/test_init.py` | cross-refactoring の `init` のテスト | 「母集合に無い者は外せない」の中断の期待を、無視して続ける期待へ替える | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | | 文書 | 使い方と仕様 | `cross-review/docs/01-state-and-review.md`(Step 1 の `start-round` に、控えの取り直し・失敗しても続けること・変更の節のファイルを書く / 消すことを足す)・`cross-review/SKILL.md`・`docs/02-fix-and-rotation.md`・`docs/05-pool-and-convergence.md`・`docs/06-evidence.md`・`docs/specifications/cross-review-participants-and-seats.md`・`plugins/ndf/README.md`・`CLAUDE.md` の cross-review 節。cross-refactoring の `docs/01-state-and-propose.md`(参加者の確定の段)と `docs/specifications/cross-refactoring-participants.md`(中断の表の「名前の矛盾」の行と、203 行の「共通層が返す 7 項目」)。`cross-review/docs/04-contracts.md`(`participants` の 8 項目と一時ファイルの一覧)と確定仕様 `cross-review-participants-and-seats.md` の参加者の記録の表(8 項目)は、`ignored_exclude` と変更の節のファイルを足して 9 項目・一時ファイル 1 つ増へ直す | @@ -317,8 +318,8 @@ sequenceDiagram | AC1 AC2 AC3 | `test_lib_assignment.py`: `review_pool(h)` をホスト 4 通りで比べる。`resolve_participants` + `review_seats` で host=claude の round 1〜3 を比べる | | AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | | AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`included` は空。`only="agy", exclude=["agy"]` は今どおり `AssignmentError`。`only="typo"` は `probe` を呼ぶ前に `AssignmentError`。`test_state_resume_args.py`: `--only agy` で始めた実行を `--only none` で再開すると、参加者が既定の 3 者に戻る | -| AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4 | -| AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え | +| AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4。報告のテスト: その状態から作った完了報告に「既定の母集合に無かった者: agy」の行が出る | +| AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え。`test_state_resume_args.py`: `--exclude agy` で始めた実行を `--include codex` だけで再開しても、`ignored_exclude == ["agy"]` が残り報告に出る | | AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` の当たりのうち、現行の説明は 0 件。出た版の変更点の記録(`plugins/ndf/README.md` の 10.17.3 の更新案内)は書き換えない | | AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | | AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る | diff --git a/issues/issue-542-786-requirements.md b/issues/issue-542-786-requirements.md index 054258b35..a824bc174 100644 --- a/issues/issue-542-786-requirements.md +++ b/issues/issue-542-786-requirements.md @@ -66,7 +66,7 @@ - [ ] AC4: `--include agy` を付けると agy が参加者に戻り、座席は今(v10.17.3)の既定と同じ輪番になる。`--exclude agy` を付けても(既定に無い者を外す指定でも)終了コード 0 で進む - [ ] AC4b: `--only agy` を `--include agy` 無しで渡すと、agy 1 者で回る(名指しした者は既定の母集合に無くても参加者になる)。`--only agy --exclude agy` は今どおり終了コード 1 で止まる。再開で `--only none` を渡すと既定の 3 者に戻る - [ ] AC4c: cross-refactoring の `init` に `--exclude agy` を渡すと(cross-refactoring の母集合に agy は無い)、中断せずに `ℹ` で始まる 1 行を出して続ける。足す者と外す者の重なりは今どおり終了コード 4 で止まる -- [ ] AC4d: 完了の報告(`report`)に、`--exclude` で指定したが既定の母集合に無かった者が 1 行で出る +- [ ] AC4d: cross-review と cross-refactoring の完了の報告に、`--exclude` で指定したが既定の母集合に無かった者が 1 行で出る。`--exclude` を渡さない再開の後も消えない - [ ] AC5: 次の文書が、既定の母集合を「claude / codex / kiro とホスト。agy は `--include agy` で戻す」と書く。現行の説明として母集合を 4 者と書いた記述は残らない(出た版の変更点の記録は書き換えない) - `plugins/ndf/skills/cross-review/SKILL.md` と `docs/05-pool-and-convergence.md` - 確定仕様 `docs/specifications/cross-review-participants-and-seats.md` From 1cfe7b0eaeafffe40aaf27c0ce974306c68840ba Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Wed, 23 Sep 2026 14:30:49 +0000 Subject: [PATCH 10/10] =?UTF-8?q?Docs:=20#542=20#786=20=E7=84=A1=E8=A6=96?= =?UTF-8?q?=E3=81=97=E3=81=9F=E9=99=A4=E5=A4=96=E3=81=AE=E5=86=8D=E9=96=8B?= =?UTF-8?q?=E3=81=A7=E3=81=AE=E5=BC=95=E3=81=8D=E7=B6=99=E3=81=8E=E3=82=92?= =?UTF-8?q?=20cross-refactoring=20=E3=81=AB=E3=82=82=E3=81=9D=E3=82=8D?= =?UTF-8?q?=E3=81=88=E3=80=81--include=20=E3=81=AE=E5=84=AA=E5=85=88?= =?UTF-8?q?=E3=82=92=E7=84=A1=E8=A6=96=E3=81=97=E3=81=9F=E9=99=A4=E5=A4=96?= =?UTF-8?q?=E3=81=A0=E3=81=91=E3=81=AB=E9=99=90=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy --- issues/issue-542-786-design.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/issues/issue-542-786-design.md b/issues/issue-542-786-design.md index b771f19cd..264c55078 100644 --- a/issues/issue-542-786-design.md +++ b/issues/issue-542-786-design.md @@ -44,10 +44,10 @@ | 要素 | 責務 | 変更 | | --- | --- | --- | | `plugins/ndf/scripts/lib/assignment.py` | 母集合と座席と参加者の解決 | 定数 `DEFAULT_REVIEW_RUNTIMES` を足し、`review_pool` をそれとホストから作る。`resolve_participants` は母集合に無い者の除外を無視し、`Participants.ignored_exclude` に残す。`only` が母集合・`--include`・`--exclude` のどれにも無いときは足す者として扱う(決定 12)。`Participants.to_state()` は `ignored_exclude` を含む 8 項目を返す | -| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す。`report` の参加者の節に「--exclude で指定したが既定の母集合に無かった者」の 1 行を足し、`ignored_exclude` を出す。再開の参加者の作り直しは、`--exclude` を渡さないとき `excluded` と `ignored_exclude` の両方を足し戻す(`--include` に同じ名前を渡したときは足し戻さず、新しい指定を優先する) | +| `plugins/ndf/skills/cross-review/scripts/state.py` | 状態ファイル・観点・参加者の解決・ラウンドの開始 | 控えの取得を関数 `_fetch_existing_comments` に分け、`start-round` からも呼ぶ。`start-round` が変更の節のファイルを書く。分類 `design` と `DESIGN_REVIEW_TEMPLATE` を足す。`_resolve_reviewers` が無視した除外を 1 行で出す。`report` の参加者の節に「--exclude で指定したが既定の母集合に無かった者」の 1 行を足し、`ignored_exclude` を出す。再開の参加者の作り直しは、`--exclude` を渡さないとき `excluded` と `ignored_exclude` の両方を足し戻す。`ignored_exclude` の名前を `--include` にも渡したときだけ、その名前を足し戻さない(新しい指定を優先する)。`excluded` の名前と `--include` の重なりは今どおり止める | | `plugins/ndf/skills/cross-review/scripts/launch-reviewer.sh` | レビューのプロンプトを組んで担当を起動する | 変更の節のファイルがあれば埋め込む。出し切りの指示と、テストと背景の処理を起動しない指示を足す。先頭のコメントの「母集合が 4 者」を「担当は 4 ランタイムのどれでもなりうる」へ直す(`launch-codex.sh`・`launch-agy.sh` の同じコメントも) | | `plugins/ndf/skills/fix/scripts/fetch-pr-comments.sh` | 既存コメントの 3 つの取得元を 1 本で取る | 引数 `--strict` を足す。付けたときは 3 つのどれか 1 つでも失敗すれば終了コード 1(付けないときは今どおり 3 つとも失敗したときだけ 1) | -| `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す | +| `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py` | cross-refactoring の参加者の解決 | 無視した除外を 1 行で出す。再開(`_resume`)の作り直しは state.py と同じ規則で `ignored_exclude` も足し戻す | | `plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/report.py` | cross-refactoring の完了報告 | cross-review の `report` と同じく「--exclude で指定したが既定の母集合に無かった者」の 1 行を足す | | `plugins/ndf/skills/cross-refactoring/tests/test_init.py` | cross-refactoring の `init` のテスト | 「母集合に無い者は外せない」の中断の期待を、無視して続ける期待へ替える | | `plugins/ndf/skills/cross-refactoring/prompts/apply.md` | 適用担当のプロンプト | テストを前景で終わるまで待つ指示を足す | @@ -319,7 +319,7 @@ sequenceDiagram | AC4 | 同上: `--include agy` の座席が今の既定の輪番と一致する。`--exclude agy` が例外を出さず `ignored_exclude == ["agy"]`、`available` に agy が無い。`test_state_review_pool.py`: `init --exclude agy` が終了コード 0 で `ℹ` の行を出し、状態ファイルに `ignored_exclude` が残る。綴りの誤りは今どおり 2 | | AC4b | `test_lib_assignment.py`: `only="agy"` で `include` 無しでも参加者が `[agy]` になり、`included` は空。`only="agy", exclude=["agy"]` は今どおり `AssignmentError`。`only="typo"` は `probe` を呼ぶ前に `AssignmentError`。`test_state_resume_args.py`: `--only agy` で始めた実行を `--only none` で再開すると、参加者が既定の 3 者に戻る | | AC4c | cross-refactoring の `test_init.py`: `{"exclude": [["agy"]]}` が中断せず `ℹ` の行を出す。重なりの指定は今どおり終了コード 4。報告のテスト: その状態から作った完了報告に「既定の母集合に無かった者: agy」の行が出る | -| AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え。`test_state_resume_args.py`: `--exclude agy` で始めた実行を `--include codex` だけで再開しても、`ignored_exclude == ["agy"]` が残り報告に出る | +| AC4d | 上の表の `test_the_report_lists_who_took_part` の書き換え。`test_state_resume_args.py`: `--exclude agy` で始めた実行を `--include codex` だけで再開しても、`ignored_exclude == ["agy"]` が残り報告に出る。`--exclude agy` で始めて `--include agy` で再開すると agy が参加者に戻る。`--exclude kiro` で始めて `--include kiro` で再開すると今どおり止まる。cross-refactoring の `test_init.py` の再開のテスト: `--exclude agy` で始めて `--include` だけで再開しても `ignored_exclude == ["agy"]` が残り、完了報告に出る | | AC5 | 目で見る。`grep -rn "4 者" plugins/ndf/skills/cross-review CLAUDE.md plugins/ndf/README.md docs/specifications/cross-review-participants-and-seats.md` の当たりのうち、現行の説明は 0 件。出た版の変更点の記録(`plugins/ndf/README.md` の 10.17.3 の更新案内)は書き換えない | | AC6 AC7 AC8 | 目で見る(文言)。`test_launch_reviewer_prompt_context.py` の既存の組み立てのテストが通る | | AC9 | `start-round` のテスト(新規 `test_state_round_changes.py`): 一時の git リポジトリで 2 つの head を作り、2 ラウンド目で変更の節のファイルが書かれ、2 つの SHA とファイル名が入る。`launch-reviewer.sh` の組み立てで、ファイルがあるときプロンプトに節が入る |