Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
883ead3
Refactor: 起動の結末を値で受け、取り込みの取り消しを 1 か所へ寄せる(#728 #647 #592 #553)
takemi-ohama Sep 21, 2026
4a7bc4e
Test: 結果なしの取り込みと群の試行の受け入れ条件を固定する(#728 #647 #592 #553)
takemi-ohama Sep 21, 2026
9d8256d
Fix: 帰属行の後ろの記名を読み、無進捗の許容と手順書を実装に合わせる(#553 #728)
takemi-ohama Sep 21, 2026
f63f4e8
Docs: 受け入れ条件の確認結果と、実装で決まったことを記録する(#728 #647 #592 #553)
takemi-ohama Sep 21, 2026
a577efa
Test: cross-refactoring の未固定経路を追加
takemi-ohama Sep 21, 2026
352bd71
Test: 結果なし・範囲確定不能時の適用中断と項目ブロックを固定
takemi-ohama Sep 21, 2026
97510f8
Test: cmd_merge_fix の範囲不明経路を現状固定 — plugins/ndf/skills/cross-refactori…
takemi-ohama Sep 21, 2026
d827efb
Test: 開き直しの判定・解決申告の突き合わせ・入力エラー・取り消しの冪等を現状固定
takemi-ohama Sep 21, 2026
8aba78b
Revert "Test: 開き直しの判定・解決申告の突き合わせ・入力エラー・取り消しの冪等を現状固定"
takemi-ohama Sep 21, 2026
30f6196
Test: scoped_item_ids のフォールバックを現状固定
takemi-ohama Sep 21, 2026
96fe22f
Refactor: gate/apply/setup/proposalsのメソッド抽出と重複関数の統合
takemi-ohama Sep 21, 2026
c2e6b83
Revert "Refactor: gate/apply/setup/proposalsのメソッド抽出と重複関数の統合"
takemi-ohama Sep 21, 2026
422ff59
Refactor: extract_method — plugins/ndf/skills/cross-refactoring/scrip…
takemi-ohama Sep 21, 2026
bcca202
Refactor: extract_method — verify/apply/refactor.py の段の抽出と commit_fil…
takemi-ohama Sep 22, 2026
2b3d281
Revert "Refactor: extract_method — verify/apply/refactor.py の段の抽出と co…
takemi-ohama Sep 22, 2026
f33ecfd
Refactor: consolidate_duplication — plugins/ndf/skills/cross-refactor…
takemi-ohama Sep 22, 2026
6bcbd6b
Refactor: extract_method — converge.py / apply.py / proposals.py / gi…
takemi-ohama Sep 22, 2026
fd30c8a
Refactor: extract_method — cross-refactoring の長い関数の段の抽出
takemi-ohama Sep 22, 2026
fe17469
Fix: converge.py の Optional の取り込み漏れを直す
takemi-ohama Sep 22, 2026
52513b1
Merge remote-tracking branch 'origin/develop' into feat/issue-728-app…
takemi-ohama Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion issues/issue-728-647-592-553-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,12 @@ graph TD
| # | 項目 | 内容 | 決める時点 |
| --- | --- | --- | --- |
| 1 | G3 の実装の形 | `LaunchOutcome` の欄の名前は PR #781 の設計のとおりとしている。実装で変われば `read_result` の包みが吸収し、取り込みは変わらない | G3 の実装 Pull Request のマージ |
| 2 | G1 の先後 | `impl_for_seq` の中身が `assignment.assign` か `impl_assign(participants, seq)` かは、実装の着手時点の `develop` で決める | 実装の計画 |
| 2 | G1 の先後 | **決まった。** 実装の着手時点の開発版の起点に参加者の決め方の変更は入っていなかった(`git grep impl_assign` は設計文書だけに当たる)。輪番から担当を引く関数は現行の割り当て(`assignment.assign(seq, host)`)を包む形で書いた。後から入る側がその中だけを差し替える | 決定済み |
| 3 | 担当を替えた後の担当も結果を残さない割合 | 2 回目で救える群の数は測っていない。実行の要約の `apply_attempts` で数える | 配布後 |
| 4 | トレーラーの形でない署名を末尾に足すランタイム | codex / agy / kiro のコミットで帰属行の段落を見ていない。散文の段落を足す者が現れれば決定 13 は効かない | 次の実行の `failed` の理由を読む |
| 5 | 帰属行を同じ段落に続ける指示に claude が従うか | 決定 14 は補助で、従わなくても決定 13 で検証は通る | 実装後の最初の実行 |
| 6 | 担当が雛形の進捗マーカーに従うか | 従わなくても決定 15 の許容で打ち切られないのはテスト 1 回分まで | 実装後の最初の実行 |
| 8 | 適用の説明の行数 | 結果なしの節を足したことで行数の上限(500 行)に達したため、改修計画の節を報告の説明へ移した。次に節を足すときは分割が要る | 次に適用の説明を書き足すとき |
| 7 | 修正の担当が利用上限のとき、群の他の項目を救う手段 | 決定 10 は修正を見送りへ進める。担当を替えて修正を続ける形は、直しかけの文脈が要るため採らなかった。見送りが増えれば見直す | 配布後 |

## 申し送り(並行する設計との境界)
Expand Down
205 changes: 205 additions & 0 deletions issues/issue-728-647-592-553-plan.md

Large diffs are not rendered by default.

100 changes: 50 additions & 50 deletions issues/issue-728-647-592-553-requirements.md

Large diffs are not rendered by default.

19 changes: 12 additions & 7 deletions plugins/ndf/skills/cross-refactoring/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ allowed-tools:
| --- | --- | --- |
| テスト整備ラウンド | **足すべきテストを集める。** 3 者が提案し、採否を決める | `--max-test-rounds`(既定 2) |
| 提案ラウンド | **構造改善の提案を集める。** 3 者が提案し、採否を決める | `--max-outer-rounds`(既定 3) |
| 適用ラウンド | **同時に適用して検証する。** 書き換えるファイルが重ならない項目だけを含む。**上の 2 つのラウンドが共有する** | 別に置かない(`--max-items-per-round` が実質の上限 |
| 適用ラウンド | **同時に適用して検証する。** 書き換えるファイルが重ならない項目だけを含む。**上の 2 つのラウンドが共有する** | 同じ群を開き直すのは 2 回まで(引数を持たない固定値)。件数は `--max-items-per-round` が実質の上限 |
| 修正ラウンド | **検証の失敗を直す。上の 2 つのラウンドが共有する** | `--max-fix-rounds`(既定 3) |
| 改善項目 | 構造改善の提案の 1 件。`<ファイル>#<シンボル>` と兆候で識別する | — |
| テスト項目 | テスト整備の提案の 1 件。固定する入口(`target`)と経路の種類(`case`)で識別する | — |

**「バッチ」「パッチ」の語は使わない。** 読み手が別の意味で知っている語である。

**適用ラウンドに別の上限を置かない。** 採用件数の上限が既に群の数を切っている。
上限を 2 つ置くと、どちらで止まったのかを読み解く必要が出る。
**同じ群を開き直すのは 2 回までである。** 2 回目は別の担当が試す。2 回とも結果を
残さなければ、担当ではなく群の側を疑える。止まった理由は群の記録(取り消しの理由と
結末の記録)が持つ。

## 設計方針

Expand Down Expand Up @@ -302,6 +303,7 @@ while :; do
"$SCRIPTS/launch-cli.sh" "$a" "$PROPOSE_PHASE" "$ID" "$ROUND"
done
# 監視の上限は `--phase` の工程で上限の表(`lib/limits.py`)が決める。秒数を書かない。
# 無進捗の許容だけは `init` が出す(テスト 1 回分の無出力で打ち切らないため)。
# **結果ファイルの名前は種類で変えない**ので、監視の雛形と工程(`propose`)は
# テスト整備ラウンドでもそのまま使える。
"$LIB/monitor.py" "$ID" --agents "$RUNTIMES_CSV" --tmp-dir "$TMP_DIR" \
Expand All @@ -314,8 +316,9 @@ while :; do
rf_eval next-apply-round "$ID" "$ROUND" || break # 終了コード 1 = 群が尽きた
"$SCRIPTS/launch-cli.sh" "$IMPL" apply "$ID" "$ROUND"
"$LIB/monitor.py" "$ID" --agents "$IMPL" --tmp-dir "$TMP_DIR" \
--stem-template "{agent}-apply-r$ROUND" --phase apply
# 終了コード 2 = 適用が通らずこの群を取り消した。修正ラウンドは回さない
--stem-template "{agent}-apply-r$ROUND" --phase apply \
--stall-timeout "$IMPL_STALL_TIMEOUT"
# 終了コード 2 = この群を取り消した、または担当を替えて開き直す。修正ラウンドは回さない
rf merge-apply "$ID" "$ROUND" || continue

while :; do # 検証と修正の繰り返し
Expand All @@ -325,7 +328,8 @@ while :; do
fi
"$SCRIPTS/launch-cli.sh" "$IMPL" fix "$ID" "$ROUND"
"$LIB/monitor.py" "$ID" --agents "$IMPL" --tmp-dir "$TMP_DIR" \
--stem-template "{agent}-fix-r$ROUND" --phase fix
--stem-template "{agent}-fix-r$ROUND" --phase fix \
--stall-timeout "$IMPL_STALL_TIMEOUT"
rf merge-fix "$ID" "$ROUND"
done
# 次の群と、次のラウンドの提案に備えて読み取り用を同期する
Expand All @@ -346,7 +350,8 @@ while :; do
1) echo "⚠ 最終ゲートが通らないまま修正の上限に達しました" >&2; break ;;
2) "$SCRIPTS/launch-cli.sh" "$FINAL_FIX_IMPL" final-fix "$ID"
"$LIB/monitor.py" "$ID" --agents "$FINAL_FIX_IMPL" --tmp-dir "$TMP_DIR" \
--stem-template "{agent}-final-fix" --phase final-fix
--stem-template "{agent}-final-fix" --phase final-fix \
--stall-timeout "$IMPL_STALL_TIMEOUT"
rf merge-final-fix "$ID" ;;
*) exit $gate ;;
esac
Expand Down
82 changes: 41 additions & 41 deletions plugins/ndf/skills/cross-refactoring/docs/02-apply-and-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,36 @@
eval "$("$SCRIPTS/refactor.py" next-apply-round "$ID" "$ROUND")" # 1 = 群が尽きた
"$SCRIPTS/launch-cli.sh" "$IMPL" apply "$ID" "$ROUND"
"$LIB/monitor.py" "$ID" --agents "$IMPL" --tmp-dir "$TMP_DIR" \
--stem-template "{agent}-apply-r$ROUND" --phase apply
"$SCRIPTS/refactor.py" merge-apply "$ID" "$ROUND" # 2 = この群を取り消した / 4 = 中断
--stem-template "{agent}-apply-r$ROUND" --phase apply \
--stall-timeout "$IMPL_STALL_TIMEOUT"
"$SCRIPTS/refactor.py" merge-apply "$ID" "$ROUND" # 2 = 取り消した / 開き直す / 4 = 中断
```

終了コード 2 と 4 を**必ず区別する**。同じ扱いにすると、取り消しに失敗した状態を
「この群の失敗」として次の群へ進み、検証を通っていない変更が Pull Request に
残ったまま先へ進む(実測)。

**適用の単位は適用ラウンド(群)である。** 群の中の項目は書き換えるファイルが
重ならないので、**まとめて 1 コミット**にできる。群と群は同じファイルを直列に
書き換えるため順序に依存し、後続の群は先行の群を適用した後の作業ツリーを読む。
**無進捗の許容は `init` が出す**(`IMPL_STALL_TIMEOUT` = テストの制限時間 + 900 秒)。
担当はテストの実行中は何も出力しないため、制限時間そのままでは打ち切られる。

### 実装担当が結果を残さなかったとき

結果ファイルが無い・読めないまま終わることがある(無進捗の打ち切り・利用上限・起動の
直後に落ちた)。取り込みは、未検証のコミットを新しい順に取り消して起点を取り消し後の先端へ
進め、群の `failed_attempts[]` へ結末を 1 件記録し、終了コード 2 で終える。

| 何回目か | 何が起きるか |
| --- | --- |
| 1 回目 | その群で失敗した担当のどれとも違う、次の輪番の担当へ替えて開き直す |
| 2 回目 | 群を取り消し(`drop_reason: no_result`)、項目を見送りへ入れる。理由にはどの担当がどの理由で残さなかったかが並ぶ |
| 替える先が無い | 起動し直しの可否で決める。利用上限は待ちと相手の枠を使うだけなので 1 回目で取り消す |

**項目が 1 件も無い群は開かない。** 採用 0 件の提案ラウンドは群を作らず、残っている
項目なしの群は取り消し済み(`drop_reason: empty`)にして次へ進む。

**適用の単位は適用ラウンド(群)である。** 群の中の項目は書き換えるファイルが重ならない
ので、**まとめて 1 コミット**にできる。群と群は同じファイルを直列に書き換えるため順序に
依存し、後続の群は先行の群を適用した後の作業ツリーを読む。

実装担当を**1 つの群につき 1 回**起動し、その群の項目を優先度順に**直列適用**させる。
並列適用はしない(同一ブランチへの同時コミットは競合と取り消し単位の曖昧化を招く)。
Expand Down Expand Up @@ -208,38 +227,6 @@ fi
控えておき、最後に `git reset --soft` で 1 コミットへまとめる。控えた地点より前へ
戻すと他の群のコミットを巻き込むため、起点は群の着手前に固定する。

#### 改修計画は Pull Request のコメントに残す

**なぜ直すのか(理由)とどう直すのか(手順)は、提案の時点でしか残らない。**
状態ファイルには入っているが、そのディレクトリは差分から除外されるため、
Pull Request を読む側からは見えない。

**改修計画は実行の記録であって、リポジトリの知識ではない**(#436 決定 6)。既定の
置き場所は**対象の Pull Request のコメント 1 件**で、ラウンドが進むたびに**同じ
コメントを編集する**。

| 置き場所 | URL の安定 | 差分に混ざるか | 更新の手数 |
| --- | --- | --- | --- |
| **Pull Request のコメント 1 件**(既定) | **永続** | 混ざらない | 編集 1 回 |
| ファイル(`--plan-file`) | `<ref>` に依存。ブランチが消えると切れる | **混ざる** | コミットと push |

- 内容は**状態から決まる**。同じ状態からは同じ本文が出る
- **取り消した項目の内訳を持つのは改修計画だけである。** 他の文章は件数だけ述べる
- 本文の先頭に印(`<!-- cross-refactoring plan rf<ID> -->`)を置く。状態ファイルの
控えが失われても、印で同じコメントを引き当てられる。**引き当てられないと、
ラウンドのたびに新しいコメントが積まれる**
- **投稿に失敗しても進行は止めない。** 記録が残らないことと、変更が検証を通って
いないことは別である。失敗したことは出力に残る

**`--plan-file` は残す。** 明示したときだけファイルにする。この経路では公開を
生成物の同期と**同じコミット**に乗せる(分けると進行側のコミットが公開のたびに
2 つずつ積まれる)。空文字を渡すと記録しない。

**絶対パスと親へ抜ける経路は受け取った時点で拒む**(終了コード 4)。進行側は利用者の
リポジトリを触るため、作業ディレクトリの外へ書き出す余地を残さない。あわせて
`./issues/plan.md` のような表記も正規化する。git が返すパスと形が違うと、公開の
コミットメッセージが取り違えられる。

#### 範囲の指定は検証にも効かせる

`--scope` を必須にした目的は**提案の発散と変更の肥大を防ぐ**ことなので、指定を検証へ
Expand Down Expand Up @@ -357,15 +344,15 @@ Pull Request に残る。**都合の悪い変更を申告しないだけで検
| コマンド | 二重処理を防ぐ鍵 |
| --- | --- |
| `merge-proposals` | `proposal_keys` が既にあるか |
| `merge-apply` | `apply.merged_at` が既にあるか |
| `merge-apply` | `apply.merged_at` が既にあるか。結果を残さなかった試行は、群の `failed_attempts[]` に同じ工程と試行番号があるか |
| `merge-fix` | 試行番号(`verify-round` が進める)と結果ファイルの内容の組 |
| `abandon-items` | `abandoned` が既にあるか |

#### 結果ファイルの形が崩れていても落ちない

相手は LLM なので、`commits` が配列でない・要素が辞書でない・`sha` が文字列でない
といった崩れ方をする。結果ファイルを読む箇所は**型を確かめてから使い**、取り出せた
ものだけを扱う。落ちると進行が止まるだけで、何の検証にもならない
相手は LLM なので、`commits` が配列でない・要素が辞書でないといった崩れ方をする。
結果ファイルを読む箇所は**型を確かめてから使い**、取り出せたものだけを扱う。**JSON
オブジェクトとして読めない結果ファイルは結果なしと同じ扱いにする**(理由は `unparsable`)

**1 件の失敗でラウンドを止めない。** 失敗した項目だけを見送りにして、残りは採用する。
全件失敗のときだけ終了コード 2 を返し、次の提案ラウンドへ進む。
Expand Down Expand Up @@ -444,6 +431,19 @@ Impl-Model: gpt-5.5
自由文で「codex が実装」と書かせると集計に使えない。プロンプトに書くだけでは守られない
ので、`merge-apply` が有無を検証する。

**読み方は 2 つあり、見る範囲が違う。**

| 読み手 | 読み方 | 見る範囲 |
| --- | --- | --- |
| 人(集計) | `git log --format='%(trailers:key=Impl-Model,valueonly)'` | **最後の段落だけ** |
| 進行側(検証) | 末尾の段落から前へ 1 段落ずつ `git interpret-trailers --parse` に掛け、記名の段落と判定しなかったところで止める | 末尾から続く記名の段落すべて |

実行環境が `Co-Authored-By:` などの帰属行を別の段落として足しても、進行側はその段落を
飛ばして前まで読むため検証は通る。人の集計は最後の段落しか読まないので、**雛形では
帰属行を空行を挟まず同じ段落に続けるよう求めている**(従わなくても検証は通る)。
**1 段落目(題名)は判定に掛けない。** 掛けると `Round: 本文の題名` の形の題名を記名
として読む(実測)。散文と記名の形が混ざる段落は、git が記名の段落と判定しない。

`Impl-Model` には**実際に使ったモデル名**を書かせる。既定モデルで走った場合は
`default` として報告時に区別する。

Expand Down
Loading
Loading