Skip to content

cross-refactoring: 実装担当が結果を残さないと同じ群が上限なしに開き直され、未検証のコミットが残る → 結果なしを取り込みの 1 か所で受けて取り消し、群が開いた回数と結末で開き直しを決める(#728 #647 #592 #553) - #796

Merged
takemi-ohama merged 20 commits into
developfrom
feat/issue-728-apply-intake
Sep 22, 2026

Conversation

@takemi-ohama

@takemi-ohama takemi-ohama commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

cross-refactoring: 実装担当が結果を残さないと同じ群が上限なしに開き直され、未検証のコミットが残る → 結果なしを取り込みの 1 か所で受けて取り消し、群が開いた回数と結末で開き直しを決める(#728 #647 #592 #553

Summary

収束リファクタリングの実装担当が結果ファイルを残さずに終わると、取り込みが下位の読み取りで
プロセスを終わらせていたため、同じ改善項目の集まりが上限なしに開き直され(Pull Request #757
29 回、rf646 で 3729 回)、担当が作ったコミットは検証を受けずに残っていた。

3 つの取り込み(適用・修正・最終ゲートの修正)が結果なしを 1 つの手順で受け、未検証の
コミットを取り消して結末を記録するようにした。集まりは開いた回数と前回の結末を持ち、担当を
替えて 1 回だけ開き直して終わる。項目の無い集まりは開かず、実行環境が帰属行を足した
コミットからも必須の記名が読める。

Closes #728
Closes #647
Closes #592
Closes #553

最終ゲートの修正で担当が結果を残さないと未検証のコミットが残る不具合(#674)も、同じ手順を通すことで直る。閉じる語は書かない(閉じるのは課題の棚卸に任せる)。

変えたこと

対象 変更
起動の結末の読み取り 状態と工程から結果ファイルの名前の幹を組み、共通層を呼んで値を返す包みにした。中断も画面への出力も行わない
取り込みの共通手順(新設) 範囲の確定・未検証のコミットの取り消し・叩き直しの判定・結果なしの一連を 1 つのモジュールへ集めた。3 つの取り込みが同じ手順を通る
取り消しの本体 事実の読み取りの層にあったものを取り込みの共通手順へ寄せ、適用・修正・最終ゲートの 3 か所が同じ実体を呼ぶ
集まりの進行 開き直しの判定と、輪番から担当を引く関数を新設した。集まりを開く側と適用の取り込みが同じ判定を読む
適用の取り込み 結果なしで取り消し・記録・担当の交代を行う。着手前のテストの確認を結果の読み取りより前へ移し、終了コードを 4 にした
修正の取り込み 結果を集まりの担当から読む。結果なしは修正ラウンドを 1 つ進め、起動し直せない結末では上限の値にする
最終ゲートの修正の取り込み 同じ手順を通し、結果なしは取り消して終了コード 2 で判定へ戻す
記名の読み取り メッセージを段落に分け、末尾から前へ 1 段落ずつ git の解析に掛ける。題名の段落は掛けない
無進捗の許容 起動の出力に足し、骨組みの 3 つの監視へ渡す。値はテストの制限時間 + 900 秒
雛形・手順書・説明文書 記名を最後の段落へ置く規約、進捗の記録の指示、結果なしのときの振る舞い、試行の上限、記名の 2 つの読み方

実測

見たもの
記名の解析に題名が要るか 要る。 記名だけの段落を単独で git interpret-trailers --parse へ渡すと出力が空になる。題名の行と空行を前に付けると 4 つとも返る(git 2.53.0)
散文と記名が混ざる段落 出力は空。題名を付けても同じ
参加者の決め方の変更(#727)の先後 着手の時点で開発版の起点に入っていない(git grep impl_assign は設計文書だけに当たる)。輪番から担当を引く関数は現行の割り当てを包む形で書いた
新しいテストが効いているか 試行の上限を 99 にすると 2 件、担当の交代を止めると 3 件が落ちる

構造の変更

適用の説明(docs/02-apply-and-review.md)が行数の上限(500 行)に達したため、改修計画の節を
報告の説明(docs/04-fix-and-report.md)へ移した。改修計画は報告の成果物であって適用の手順では
ないため、置き場所としてもそちらが合う。

やらないこと

Test plan

作業ツリーの fe174694 で実行した(2026-09-22 02:45〜02:48 UTC)。

  • uv run --with pytest pytest scripts/tests plugins/ndf -q4672 passed / exit=0
  • bash scripts/build-runtime-plugins.sh --checkexit=0
  • claude plugin validate .exit=0(未知フィールドの警告のみ。終了コードは変わらない)
  • python3 scripts/check-skill-frontmatter.pyexit=0
  • python3 scripts/check-doc-line-limit.py --root .exit=0
  • python3 plugins/ndf/scripts/instructions-check.py --root .exit=0
  • git grep -n revert_unverified_range -- plugins/ndf/skills/cross-refactoring/scripts → 0 件(AC50)
  • 継続的統合(gh pr checks 796)→ 15 件すべて pass
  • 受け入れ条件 AC1〜AC50 を確認済みにした(要求の文書のチェックリスト)

未検証の項目: 要求の文書の「手動確認」1 件。進捗の記録に作業段階が残るかと、結末の記録の
理由が監視の結果と一致するかは、この変更を載せた骨組みで収束リファクタリングを回した
ときにしか確かめられない。配布の後に確かめる(release-verification)。

既存の失敗: なし。範囲外と判断したもの: なし。

検査の工程

構造改善

この Pull Request が収束リファクタリングそのものを変えるため、進行に使ったのは開発版の
起点ブランチの骨組み
であって、この変更の版ではない。範囲は
plugins/ndf/skills/cross-refactoring/scripts と同 tests に絞った。

見たもの
ラウンド 6(テスト整備 2 / 構造改善 4)
終了理由 提案ラウンドの上限に到達
採用 29 件(適用 17 件 / 取り消し 12 件)
見送った提案 23 件
最終ゲート 全体のテスト exit=0

取り消しの単位は適用ラウンドであるため、担当が結末の記名を欠いた回と、どの改善項目にも
割り当てられないコミットを作った回は、その群を丸ごと戻している。戻した履歴は打ち消しの
コミットとして残る。改修計画と取り消しの内訳は次にある。

#796 (comment)

実装レビュー

レビュー担当は agy と kiro。1 ラウンドで新しい指摘が出なくなった。

指摘は 1 件で、構造改善で抽出した関数の戻り値注釈が使う名前が、同じファイルの取り込みに
入っていなかった(plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py)。
from __future__ import annotations があるため読み込みでは現れず、型注釈を解決した時点で
名前を引けない。kiro が指摘し、agy が独立に裏付けた。最終スイープで取り込みを直し、同じ形の
漏れが他に無いことを、範囲の 19 個のまとまり全件の型注釈の解決で確かめた(修正前 1 件 →
修正後 0 件)。未解決のレビュースレッドは 0 件。

文書の検査

実装計画(issues/issue-728-647-592-553-plan.md)に対する markdown-writing のセルフチェック。

# 検査 結果 扱い
1 識別子・略語の混入 19 行 すべてコードブロック(修正対象のディレクトリ図)とバッククォートの中。本文の主語・目的語には無い
2 検討痕跡・変更履歴 0 件
3 強い否定語 1 行(互換性の表の「確認できていない」) 着手前のテストの状態を指す事実の記述。書き換えない
4 過剰な装飾語 0 件
5 根拠の曖昧な断定 0 件
6 多義語(5 回以上) 0 語

🤖 Generated with Claude Code

https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB

takemi-ohama and others added 5 commits September 21, 2026 21:55
結末の読み取りを、状態と工程から結果ファイルの名前の幹を組んで共通層を呼ぶ包みへ
変える。中断も出力も行わないため、結果を残さなかった担当のコミットが取り消されない
まま残ることがなくなる。

範囲の確定・未検証のコミットの取り消し・結末の記録を新しいモジュールへ集め、3 つの
取り込み(適用・修正・最終ゲートの修正)が同じ手順を通るようにする。事実の読み取りの
層にあった取り消しは無くなる。

群の進行に開き直しの判定と輪番から担当を引く関数を足し、群を開く側と適用の取り込みの
両方が同じ判定を読む。項目の無い群と採用 0 件の群は開かずに取り消す。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB
取り込みの共通手順(範囲の確定・取り消し・記録)、適用の群の試行と担当の交代、
採用 0 件と項目の無い群、修正と最終ゲートの修正の結果なしを、それぞれ受け入れ条件の
単位で確かめる。

実装を壊して確かめた: 試行の上限を 99 にすると 2 件、担当の交代を止めると 3 件が
落ちる。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB
コミットのメッセージを段落に分け、末尾から前へ 1 段落ずつ git の解析へ掛けて記名を
読む。実行環境が帰属の段落を後ろへ足しても、必須の記名が読めるようになる。題名の
段落は解析に掛けない。

起動の出力に無進捗の許容(テストの制限時間 + 900 秒)を足し、骨組みの 3 つの監視へ
渡す。担当がテストを実行している間の無出力で打ち切られなくなる。3 つの雛形に進捗の
記録の指示と、必須の記名を最後の段落へ置く規約を書く。

手順書と説明文書を、結果なしのときの振る舞い・試行の上限・記名の 2 つの読み方に
合わせる。適用の説明が行数の上限に達したため、改修計画の節を報告の説明へ移した
(改修計画は報告の成果物であり、適用の手順ではない)。

実測: git 2.53.0 の `git interpret-trailers --parse` は、題名の行を補わないと何も
返さない。散文と記名が混ざる段落も返さない。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB
受け入れ条件 50 件を確認済みにする。設計文書の未確認の節に、輪番から担当を引く関数の
中身を現行の割り当てを包む形で書いたこと(参加者の決め方の変更は開発版の起点に
入っていなかった)と、適用の説明が行数の上限に達したことを残す。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB
適用試行の再実行、最終修正の範囲不明、作業ツリー基点の解決順を現状固定テストで保護する。

Item-Id: R1-001
Round: 1
Impl-Runtime: codex
Impl-Model: default
@takemi-ohama

takemi-ohama commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

改修計画 — devbasex/ai-plugins #796

/ndf:cross-refactoring が提案し、適用した改善項目の記録である。
理由と手順は提案の時点でしか残らないため、公開の直前に書き出している。

  • 対象範囲: plugins/ndf/skills/cross-refactoring/scripts, plugins/ndf/skills/cross-refactoring/tests
  • 着手前のテスト: uv run --with pytest pytest scripts/tests plugins/ndf -q

ラウンド 1(実装 codex / レビュー agy / kiro)

R1-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_merge_apply

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit codex / agy 採用 1

なぜ: 適用担当の結果なしが既に記録されている試行(叩き直し)において、already_closed ガードにより結果ファイルの再読み込みを行わず終了コード 2 で即座に復帰する分岐が未固定である

手順: 1. 現在の attempt と一致する apply の failed_attempts を持つ状態を作る
2. 結果の読み取りが行われたら失敗する観測用の代替を置く
3. cmd_merge_apply を再実行する
4. 結果を読まず終了コード 2 となり、状態と失敗試行の件数が変わらないことを比較する

R1-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_merge_apply

兆候・経路 手法・階層 重要度 提案元 状態 コミット
error unit codex / agy 採用 1

なぜ: 結果ファイルが無く、かつ起点から HEAD までの範囲を確定できない経路では、共通部品の返値までは固定されているが、公開コマンドが項目を blocked にして終了コード 4 で中断し、失敗試行を記録しない振る舞いは固定されていない

手順: 1. green の着手前テストと pending の適用群を持つ状態を作り、結果ファイルを置かない
2. コミット範囲の取得を確定不能にする
3. cmd_merge_apply を実行して終了コード 4 を観測する
4. 群の項目が blocked になり、failed_attempts が追加されず、適用範囲の取り消しも行われないことを比較する

R1-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py#cmd_merge_fix

兆候・経路 手法・階層 重要度 提案元 状態 コミット
error unit codex / agy 採用 1

なぜ: 修正結果が無く範囲も確定できない経路で、公開コマンドが修正ラウンドを 1 つ進めて終了コード 2 を返す振る舞いは未固定である。共通部品単体の範囲不明テストでは、この呼び出し側固有の進行を検出できない

手順: 1. 修正の起点と進行中の群を持つ状態を作り、修正結果ファイルを置かない
2. コミット範囲の取得を確定不能にする
3. cmd_merge_fix を実行して終了コード 2 を観測する
4. fix_rounds が 1 だけ増え、failed_attempts は追加されず、コミットの取り消しも行われないことを比較する

R1-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/gate.py#cmd_merge_final_fix

兆候・経路 手法・階層 重要度 提案元 状態 コミット
error unit codex / agy 採用 1

なぜ: 最終ゲートの修正結果が無く範囲も確定できない経路では、結果がある場合の範囲不明は固定済みだが、結果なしを閉じる途中で範囲不明となる別経路の終了状態が固定されていない

手順: 1. 修正担当・fix_rounds・fix_base_sha を持つ最終ゲート状態を作り、最終修正結果ファイルを置かない
2. コミット範囲の取得を確定不能にする
3. cmd_merge_final_fix を実行して終了コード 2 を観測する
4. fix_rounds と起点が変わらず、failed_attempts が追加されず、コミットの取り消しも行われないことを比較する

R1-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#default_worktree_base

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit kiro 採用 1

なぜ: 作業ディレクトリの親の解決には環境変数 NDF_WORKTREE_BASE を優先する分岐と、未設定時に /ndf-worktrees へ倒す分岐があるが、どちらも固定されていない。解決順は cross-review と揃えるという規約があり、構造改善で崩れやすい。

手順: 1. NDF_WORKTREE_BASE を一時パスへ設定し、戻り値が resolve() された そのパスであることを固定する(明示指定の分岐)
2. NDF_WORKTREE_BASE を未設定にし、戻り値が <tempfile.gettempdir()>/ndf-worktrees になることを固定する(フォールバックの分岐)

ラウンド 2(実装 agy / レビュー codex / kiro)

R2-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#group_reopening

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit agy / kiro 取り消し 1

なぜ: 4 本の分岐(empty / exhausted / resume / open)を返す純粋な判定関数だが、テストでは patch_lib で差し替えるだけ(test_apply_attempts.py 331 行)で、返り値そのものを固定した経路が 1 本も無い。開き直しの判定は適用ラウンドの中核なので、構造改善の前に現状の返り値を固定する。

手順: 1. items が空の group を渡し 'empty' が返ることを確かめる
2. failed_attempts に phase='apply' の記録を MAX_APPLY_ATTEMPTS 件持つ group で 'exhausted' が返ることを確かめる
3. attempt が失敗件数より大きい group(開いたまま閉じていない)で 'resume' が返ることを確かめる
4. attempt が失敗件数以下の group で 'open' が返ることを確かめる
5. 返り値の文字列だけを比較し、内部の数え方には触れない

R2-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py#_resolved_fix_thread_ids

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit agy 取り消し 1

なぜ: 修正結果の自己申告スレッドIDとGitHub API上の解決状態の積集合(claimed & actual)のみを採用する防御分岐である。GitHub APIの取得失敗時(None)や、自己申告が配列でない形式不良時に安全に空集合を返し、未解決指摘の不正な解決扱いを防ぐ振る舞いを単体テストで固定する。

手順: 1. payload の resolved_thread_ids が配列でない場合(文字列や数値等)に警告ログを出して空集合として扱うことを確認する。
2. resolved_threads_on_github が None を返したときに空集合を返すことを確認する。
3. 自己申告に含まれるがGitHub上で未解決のスレッドIDが除外され、両者で解決済みのIDのみが返ることを確認する。

R2-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#cmd_init

兆候・経路 手法・階層 重要度 提案元 状態 コミット
error unit codex 取り消し 1

なぜ: 通常の初期化と CLI 認証失敗は固定されているが、ホスト判定またはモデル指定の解析が失敗したとき、外部照会や作業ディレクトリ作成より前に中断する入力エラー経路は固定されていない

手順: 1. ホスト判定エラーとモデル指定エラーをそれぞれ返す入力を作る
2. cmd_init を呼ぶ
3. 中断コードを比較し、外部照会が呼ばれず作業ディレクトリも作られていないことを確認する

R2-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#revert_item_commits

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit codex 取り消し 1

なぜ: 未取り消しの項目を戻す正常経路と失敗経路は固定されているが、reverted が真の項目を再度渡したときに 0 を返し、Git の状態と項目を変えない冪等経路は固定されていない

手順: 1. reverted が真でコミットを持つ項目と、現在の HEAD を用意する
2. revert_item_commits を呼ぶ
3. 戻り値が 0 で、HEAD と項目の内容が呼び出し前から変わらないことを比較する

R2-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#scoped_item_ids

兆候・経路 手法・階層 重要度 提案元 状態 コミット
branch unit codex 採用 1

なぜ: apply_rounds を持たない旧形式と現在群が見つかる経路は周辺テストを通るが、apply_rounds があり指定された apply_round の群が存在しないときに entry 全体の items へ戻る分岐は固定されていない

手順: 1. items と apply_rounds を持ち、apply_round がどの群にも一致しない entry を作る
2. scoped_item_ids を呼ぶ
3. entry 全体の items が元の順序で返ることを比較する

ラウンド 3(実装 kiro / レビュー codex / agy)

R3-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/gate.py#cmd_final_gate

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major agy / kiro 取り消し 0

なぜ: 単独起動分岐(cross-review)、CIとローカルテストの排他判定、検査結果の記録、通過処理、修正上限到達時の終了処理、修正ラウンドの開始と永続化という複数の責務が 1 つの関数にフラットに同居している。結果分岐ごとの状態更新と環境値出力(emit)が入り組み、個別の制御フローを把握しづらい。

手順: 1. 通過時の状態更新と emit を _finish_passed(gate, path, state, detail) へ抽出する
2. 上限到達時の失敗確定と終了処理(status設定・emit・sys.exit(1))を _finish_failed(gate, path, state, limit, detail) へ抽出する
3. 修正ラウンド継続の準備処理(fix_rounds加算・fix_base_sha取得・impl決定・emit・sys.exit(2))を _start_fix_round(state, gate, path, detail) へ抽出する
4. cmd_final_gate を「状態読み込み → 単独起動チェック → ゲート実行と結果記録 → 各終了ヘルパーの呼び分け」というシンプルなディスパッチ関数へ整理する
5. tests/test_final_gate.py を実行し、各終了コードと出力される環境値が不変であることを確認する

R3-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_merge_test_judgements

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / agy 取り消し 0

なぜ: 現在の適用群の保留取得、担当者の判定結果ファイルの読み込みと正規化、判定の統合、問題発生時の項目ステータス更新と _apply_drop の実行、問題なし時の保留更新と報告が 1 つの関数に直列に記述されている。外部ファイル IO と状態変更・取り消し処理が分離されておらず、関数全体の把握が困難である。

手順: 1. 担当者の結果ファイルを読み込んで verdicts リストを抽出・正規化する処理を _load_test_judgement_verdicts(state, entry, group_no) へ抽出する
2. 判定問題(problem)がある場合の項目 abandoned 化・_apply_drop 呼び出し・保留解除処理を _reject_test_judgements(path, state, entry, group, problem) へ抽出する
3. cmd_merge_test_judgements を「保留確認 → 結果読み込み → 判定統合 → 却下または受理」という高レベルの流れに整理する
4. tests/test_assert_changes.py を実行し、結果欠落・不正JSON・振る舞い変更(changed)・undecidable・正常判定の各ケースで状態更新と終了コードが不変であることを確認する

R3-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#rounds_of_kind

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication minor codex / agy 取り消し 0

なぜ: setup.py の rounds_of_kind と report.py の _of_kind が、entry_kind に基づいて同一種類のラウンドをフィルタリングする全く同じ処理を別々に定義している。ラウンド種別の判定規則や取り出しロジックの正本が分散しており、修正時の二重管理の原因となる。

手順: 1. ラウンドの定義と操作を担う rounds.py に、rounds リストと kind を受け取ってフィルタする共通関数 rounds_of_kind を定義する
2. setup.py の rounds_of_kind および report.py の _of_kind を rounds.py の共通関数へ置き換える
3. 各モジュールで重複していた関数定義を削除し、import を更新する
4. tests/test_rounds.py, tests/test_test_rounds.py, tests/test_start_round_emits_runtimes.py を実行し、ラウンド種別ごとの上限判定や通し番号計算が不変であることを確認する

R3-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#merge_test_proposals

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication minor agy / kiro 取り消し 0

なぜ: merge_proposals と merge_test_proposals が「sources を回して正規化 → None を捨てる → _dedupe_key で鍵を作る → 既存なら merge* / 無ければ挿入」というマージ蓄積ループを同じ形で持つ。違うのは正規化関数と統合関数だけで、片方のループだけ直すと収集の挙動がずれる。

手順: 1. _accumulate_proposals(proposals, normalize_fn, merge_fn) -> dict[tuple[str, ...], dict[str, Any]] を抽出し、ループ処理本体を共通化する
2. merge_proposals から _accumulate_proposals(proposals, _normalize_proposal, _merge_one) を呼ぶように変更する
3. merge_test_proposals から _accumulate_proposals(proposals, _normalize_test_proposal, _merge_test_one) を呼ぶように変更する
4. tests/test_merge_proposals.py および tests/test_test_rounds.py を実行し、採用・見送りの結果が不変であることを確認する

R3-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#_normalize_proposal

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication extract_method minor agy / kiro 採用 1

なぜ: smell / technique / severity の 3 項目で、「語彙集合に含まれなければ警告を出力して unknown へ降格し、degraded フラグを立てる」同一構造のブロックが 3 回繰り返されている。同じ降格ルールが重複しており、警告文や降格処理の変更時に不整合が生じるリスクがある。

手順: 1. _degrade_if_unknown(value, allowed, source, label, path, symbol) -> tuple[str, bool] を抽出し、語彙検証・警告出力・unknownへの降格・degraded判定を共通化する
2. _normalize_proposal 内の smell, technique, severity の3ブロックをこの関数の呼び出しに置き換え、degraded フラグを集約する
3. tests/test_merge_proposals.py を実行し、語彙外値の降格と警告の振る舞いが不変であることを確認する

ラウンド 4(実装 claude / レビュー codex / kiro)

R4-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/verify.py#verify_apply_round

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / kiro 取り消し 1

なぜ: 1 関数(192-264 行)が、コミットの実在確認・現状固定テストの伴走確認・テスト期待値の変更検査・差分予算の検査・コミット粒度の検査という 5 段の独立した検証を直列に通しで行っている。段ごとに名前が付き(それぞれ別の理由で失敗理由を返す)、部分だけをテストできない。段を足すたびにこの関数が伸びる。

手順: 1. test_merge_apply.py、test_commit_granularity.py、test_commit_trailers_git.py、test_assert_changes.py の既存テストで各失敗理由と判定順を確認する
2. 全コミットの基本条件を検査する処理を名前付き関数へ抽出する
3. test_gap 項目の先頭コミット要件を検査する処理を名前付き関数へ抽出する
4. 見積と手法倍率から差分予算を検査する処理を名前付き関数へ抽出する
5. verify_apply_round は各検査を現在と同じ順で呼び、最初の失敗理由を返す流れだけにする
6. 対象テストと全体テストを実行する

R4-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/verify.py#merge_test_judgements

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication major kiro 採用 1

なぜ: merge_test_judgements(432 行目)と apply_judgements_to_group(491 行目)が、verdicts から answers 辞書を組む同一の内包表記(str(v.get("path")): str(v.get("verdict")) for v in verdicts if isinstance(v, dict) and v.get("path"))を持つ。AI エージェントの答えの読み方という同じ業務ルールに由来し、欠けた答えの扱いを変えるときは必ず両方を一緒に直す必要がある。片方だけ直すと段 2 の判定の解釈が食い違う。

手順: 1. verdicts を answers 辞書に変換する private 関数 _answers_by_path(verdicts) を verify.py に抽出する
2. merge_test_judgements の answers 生成をこの呼び出しに置き換える
3. apply_judgements_to_group の answers 生成を同じ呼び出しに置き換える
4. test_assert_changes.py の該当テストを実行し、通ることを確認する

R4-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_next_apply_round

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex 取り消し 1

なぜ: 次に開く群の探索、空・試行上限群の取り消し、群がない場合の終了、初回開始/未完試行/検証再開ごとの状態初期化、結果の出力が 1 関数に同居している。

手順: 1. test_apply_rounds.py と test_apply_attempts.py の既存テストで pending、applied、empty、exhausted、再開の各経路を確認する
2. 群を走査して開く対象と reopening 判定を返す処理を名前付き関数へ抽出する
3. 選ばれた群の初回開始/未完試行の再開/取り込み済み群の再開に応じて状態を整える処理を名前付き関数へ抽出する
4. cmd_next_apply_round は入出力、対象なしの終了、抽出関数の呼び出し、保存と emit の順序だけを表す形にする
5. 対象テストと全体テストを実行する

R4-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor.py#main

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex 取り消し 1

なぜ: 引数解析器の生成、4 種類のサブコマンド群の登録、個別オプションの登録、解析結果のディスパッチが 1 関数に連続しており、各コマンド群の構成意図を独立して確認しにくい。

手順: 1. 既存の CLI テストに各サブコマンド群の解析結果とディスパッチ先を固定するパラメータ化テストを追加する
2. 共通の id 引数だけを持つサブコマンド群の登録を名前付き関数へ抽出する
3. round 引数を持つ群と dry-run を持つ群の登録をそれぞれ名前付き関数へ抽出する
4. init と report の登録も個別関数へ抽出し、main は parser の生成、登録関数の呼び出し、parse_args、ディスパッチだけにする
5. 追加した固定テストと既存の CLI テストを実行する

R4-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#commit_test_changes

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication minor kiro 取り消し 1

なぜ: commit_files(161 行目)と commit_test_changes(167 行目)が、いずれも git_out(work, ["show", "--name-only", "--format=", sha]) を独立に実行し、出力を行ごとに分割して触れたファイル一覧を得ている。コミットが触ったファイルの列挙という同じ判断が 2 箇所にあり、列挙の仕方(git の引数・空行の除外)を変えるとき片方だけ直されうる。commit_test_changes は得た一覧を commit_files 経由で取れる。

手順: 1. commit_test_changes 内の git show --name-only 実行を commit_files(work, sha) の呼び出しへ置き換える
2. 得た一覧を _is_test_path でフィルタして従来どおり before/after を組む
3. test_git_facts.py の commit_files / commit_test_changes のテストを実行し、通ることを確認する

ラウンド 5(実装 codex / レビュー agy / kiro)

R5-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py#cmd_abandon_items

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / agy / kiro 採用 1

なぜ: cmd_abandon_items は未完了取り消しの再実行、処理済み群の早期復帰、取り消し対象が無い場合の早期分岐、dry-run 分岐、取り消しの実行 (run_drop)、見送り項目 (deferred_items) への登録ループ、群およびラウンドの状態確定と保存・プッシュという多数の段階を 1 関数に通しで記述しており、制御の流れが長くなっている。特に対象項目を走査して status を abandoned に更新し deferred_items に追記する処理は明確な単一責務を持ち、独立した補助関数へ抽出することで見通しと保守性が向上する。

手順: 1. converge.py 内に取り消し対象を走査して item の status を abandoned に更新し、未登録の項目を deferred_items に追加する補助関数 _record_deferred_abandoned_items(state: dict[str, Any], targets: list[str]) を抽出する。
2. cmd_abandon_items 内のループ処理を抽出した補助関数の呼び出しに置き換える。
3. cmd_abandon_items を「未完了の取り消し再開またはプッシュ → 処理済み・対象なしの早期判定 → dry-run 分岐 → 取り消し実行 → 見送り記録 → 状態確定とプッシュ」という高水準のオーケストレーション手順に整理する。
4. tests/test_abandon_items.py および関連テストを実行し、見送り記録の追記・冪等性・状態更新・プッシュの順序が変わらないことを確認する。

R5-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_merge_apply

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / agy 採用 1

なぜ: cmd_merge_apply は作業ツリー残滓の破棄、未完了適用の再開、処理済み判定による早期終了、着手前テスト状態 (baseline) の検証、適用スコープ生成と結果ファイル読み込み、コミット所有権の検証、検証の実行 (_verify_apply_group)、検証結果に応じた dry-run・取り消し・公開処理という多段階の処理を 1 つの関数で直列に管理している。処理済み判定や事前ゲート、結果の確定処理を整理・補助関数へ抽出することで、cmd_merge_apply が全体の実行パイプラインの進行制御に集中できるようにする。

手順: 1. 処理済みレコードの判定と再実行制御(採用 0 件時の取り消し状態化を含む)を補助関数へ抽出する。
2. 着手前テスト結果 (baseline) の検証とブロック判定を補助関数へ抽出する。
3. 検証結果を受けた状態の更新・取り消し・プッシュの反映処理を補助関数へ抽出する。
4. cmd_merge_apply は各フェーズを順に呼び出すオーケストレーターとし、既存の終了コードと保存・プッシュの順序を完全に維持する。
5. tests/test_merge_apply.py および tests/test_apply_attempts.py を実行し、既存の振る舞いと終了コードが変わらないことを確認する。

R5-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#_normalize_test_proposal

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication minor agy / kiro 採用 1

なぜ: _normalize_test_proposal は case と level の 2 つのフィールドに対して「語彙集合に含まれているか検証し、語彙外であれば警告ログを出力して unknown へ降格する」という同一パターンの判定処理をインラインで重複して保持している(if case not in TEST_CASES: ... と if level not in TEST_LEVELS: ...)。同ファイルの _normalize_proposal ではすでに _degrade_if_unknown による共通化が行われているが、テスト提案側には同型の重複が残っている。この降格処理を集約することで、テスト提案の正規化ルールを統一し変更漏れを防ぐ。

手順: 1. 語彙外の値を警告して unknown へ降格する処理を 1 つのヘルパー(例: _degrade_test_value(value, allowed, source, label, target) -> str)へ抽出する。テストの提案は重要度を持たないため degraded フラグは返さず、降格後の値だけを返す
2. _normalize_test_proposal の case ブロックをこのヘルパーの呼び出しへ置き換える
3. _normalize_test_proposal の level ブロックを同じヘルパーの呼び出しへ置き換える
4. tests/test_test_rounds.py を実行し、語彙内・語彙外(unknown への降格と対象外判定)の振る舞いが不変であることを確認する

R5-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#check_run_result

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method split_into_pipeline major codex 採用 1

なぜ: GitHub API の取得、JSON 構文解析、応答形状の検証、名前による選別、status と conclusion の集約という直列の段階が 1 関数に入り、外部応答の欠損・不正 JSON・未完了・失敗・全成功の境界を個別に固定する直接テストが見当たらない。

手順: 1. 先に gh api を差し替え、不正 JSON、check_runs 欠損、対象名なし、未完了、失敗、全成功を check_run_result の公開入出力で固定する現状固定テストを追加する
2. API 出力から check_runs のリストを検証して返す段階を関数へ抽出する
3. 名前が一致する run の選別を独立した段階へ抽出する
4. matched runs を pending・失敗結論・success の順で集約する段階を抽出する
5. check_run_result を取得から集約まで値を渡すパイプラインにし、None と各文字列の既存契約を維持する
6. 追加した現状固定テストと test_git_facts.py を実行する

ラウンド 6(実装 agy / レビュー codex / kiro)

R6-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#drop_items

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / kiro 検証中 1

なぜ: 取り消し対象の解決・範囲の確定と旧版フォールバック・積み直し計画・dry_run 分岐・実行・結果記録の 6 段が 1 関数に同居し、旧版フォールバック(apply_base_sha を記録していない状態ファイルで項目コミットだけを戻す)だけで独立した分岐と反復を抱える

手順: 1. commits_in_range が None のときの旧版フォールバック(pending を revert_item_commits で新しい順に戻して mode=item を返す)を _drop_legacy_by_item(state, pending, dry_run) として抽出
2. drop_items 本体は「pending 解決 → 範囲確定 → (旧版なら _drop_legacy_by_item) → plan → dry_run 分岐 → execute → record」の骨格だけにする
3. tests/test_drop_items_git.py と tests/test_abandon_items.py を実行し、item / round / skip の各 mode と旧版経路の振る舞いが不変であることを確認

R6-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/report.py#cmd_report

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex / kiro 検証中 1

なぜ: 1 つの関数が見出し行・改修計画・着手前テスト・最終ゲート・ラウンド表・改善項目表・見送り・指標・run_metrics 行を通しで print しており、段階が 8 つ以上並ぶ。各段は名前を付けられる独立した出力単位で、部分だけをテストできない

手順: 1. 実行メタ情報(repo/host/scope/final/plan/test_rounds_final/baseline/final_gate)を組み立てて出す部分を _print_header(state) として抽出
2. 見送り節(件数と改修計画参照)を _print_deferred(state) として抽出
3. 指標節(args.metrics 分岐配下)を _print_metrics(state) として抽出
4. run_metrics.report_line の 1 行を _print_run_metrics(path, state) として抽出
5. cmd_report は path/state の取得と抽出した各関数の呼び出しだけに縮める
6. tests/test_run_metrics_summary.py などの既存出力検証を実行して文言・順序が不変であることを確認

R6-003 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py#cmd_merge_fix

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex 検証中 1

なぜ: 状態準備、結果ファイルの取得と再実行判定、Git範囲の解決、コミット検証、状態更新、push、通知まで複数段階を通しで制御している。各段階には既に補助関数がある一方、入口関数には段階間の組み立てと副作用が残っている。

手順: 1. 結果取得から重複取り込み判定までを抽出する
2. HEAD・baseline・ordered_range を組み立てて検証と確定を行う処理を抽出する
3. merged_keys・回数・所要時間の記録から保存・push・通知までを抽出する
4. 既存の修正取り込みテストで結果なし、重複実行、未割当コミット、正常取り込みの終了コードと状態を確認する

R6-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#_verify_apply_group

兆候・経路 手法・階層 重要度 提案元 状態 コミット
long_method extract_method major codex 検証中 1

なぜ: 申告欠落の検出、コミット事実の収集、適用可否の判定、保留中のテスト判断の記録、項目状態と進捗の更新、永続化と結果通知が1関数に直列で同居している。判定材料の組み立てと判定後の副作用を個別に確認しにくい。

手順: 1. 申告から missing・shas・facts を組み立てる処理を名前付き関数へ抽出する
2. missing と verify_apply_round から problem を決める処理を抽出する
3. 保留判断・項目状態・進捗・保存・通知を行う処理を抽出する
4. 既存の適用結果検証テストで欠落・成功・失敗・dry-run の出力と状態が不変であることを確認する

R6-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#_normalize_test_proposal

兆候・経路 手法・階層 重要度 提案元 状態 コミット
duplication consolidate_duplication minor kiro 検証中 1

なぜ: _degrade_test_value は _degrade_if_unknown とほぼ同一の降格ロジック(allowed 判定・同じ警告文の骨格・unknown 返却)を持ち、差は警告文の位置表記(path#symbol か target か)だけである。語彙外降格という同じ業務ルールに由来し、変わるときは必ず一緒に変わる。降格の 2 系統が別々に古くなる

手順: 1. _degrade_if_unknown を位置表記を引数(location: str)で受ける形へ一般化し、構造改善側は f"{path}#{symbol}"、テスト側は target を渡す
2. _degrade_test_value を廃し、_normalize_test_proposal から一般化後の _degrade_if_unknown を呼ぶ(degraded フラグはテスト側では捨てる)
3. tests/test_merge_proposals.py・tests/test_merge_apply.py の語彙外降格の検証を実行し、警告文と unknown への降格が両系統で不変であることを確認

見送った項目

ラウンド 対象 兆候・経路 理由
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#git_out error 1 ラウンドの採用上限 5 件を超えた
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#tmp_dir_for branch 1 ラウンドの採用上限 5 件を超えた
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#current_group boundary 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/intake.py#discard_unverified branch 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/plan.py#publish_plan_comment branch 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#attempt_of boundary 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#deferred_record branch 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#group_reopening branch コミット d827efb にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py#_resolved_fix_thread_ids branch コミット d827efb にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#cmd_init error コミット d827efb にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#revert_item_commits branch コミット d827efb にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#cmd_start_round long_method 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/gate.py#cmd_merge_final_fix long_method 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/scope.py#scope_problem long_method 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/gate.py#cmd_final_gate long_method どの改善項目にも割り当てられていないコミットが 1 件(96fe22f)。検証を回避した変更や、状態と実差分の食い違いを Pull Request に残さないため、この適用ラウンドを取り消します
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_merge_test_judgements long_method どの改善項目にも割り当てられていないコミットが 1 件(96fe22f)。検証を回避した変更や、状態と実差分の食い違いを Pull Request に残さないため、この適用ラウンドを取り消します
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#rounds_of_kind duplication どの改善項目にも割り当てられていないコミットが 1 件(96fe22f)。検証を回避した変更や、状態と実差分の食い違いを Pull Request に残さないため、この適用ラウンドを取り消します
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#merge_test_proposals duplication どの改善項目にも割り当てられていないコミットが 1 件(96fe22f)。検証を回避した変更や、状態と実差分の食い違いを Pull Request に残さないため、この適用ラウンドを取り消します
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/verify.py#verify_apply_round long_method コミット bcca202 にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#cmd_next_apply_round long_method コミット bcca202 にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
4 plugins/ndf/skills/cross-refactoring/scripts/refactor.py#main long_method コミット bcca202 にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#commit_test_changes duplication コミット bcca202 にトレーラーが欠けています: Item-Id, Round, Impl-Runtime, Impl-Model
6 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/report.py#cmd_advance long_method 1 ラウンドの採用上限 5 件を超えた

takemi-ohama and others added 13 commits September 21, 2026 22:45
結果ファイルが無く、かつ起点から HEAD までの範囲を確定できない経路において、
cmd_merge_apply が項目を blocked にして終了コード 4 で中断し、失敗試行の記録や
コミット取り消しを行わない振る舞いを現状固定テストで保護する。

Item-Id: R1-002
Round: 1
Impl-Runtime: agy
Impl-Model: default
…ng/scripts/refactor_lib/commands/converge.py#cmd_merge_fix

修正結果が無く範囲も確定できないとき、公開コマンドが修正ラウンドを 1 つ進めて
終了コード 2 を返す振る舞いを固定する。共通部品単体の範囲不明テストでは検出
できない、呼び出し側固有の進行(fix_rounds を進め、failed_attempts を足さず、
取り消しも行わない)を観測する。

Item-Id: R1-003
Round: 1
Impl-Runtime: kiro
Impl-Model: default
適用ラウンド 1(提案ラウンド 2)のテスト整備 4 件。対象のコードは変更していない。

- R2-001 rounds.py#group_reopening — empty / exhausted / resume / open の
  4 分岐の返り値そのものを固定する(これまでは差し替えるだけだった)
- R2-002 commands/converge.py#_resolved_fix_thread_ids — 配列でない自己申告・
  GitHub 側が取得できない(None)・積集合だけを採る経路を単体で固定する
- R2-003 commands/setup.py#cmd_init — ホスト判定とモデル指定の誤りで、外部照会
  にも作業ディレクトリ作成にも進まないまま中断コード 4 で止まることを固定する
- R2-004 gitfacts.py#revert_item_commits — reverted が真の項目は 0 を返し、
  HEAD も項目も動かさない冪等経路を固定する

Item-Id: R2-001
Round: 2
Impl-Runtime: claude
Impl-Model: claude-opus-5[1m]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
現在の適用ラウンドに一致する群がない場合、entry 全体の items を元の順序で返す既存動作を固定する。

Item-Id: R2-005
Round: 2
Impl-Runtime: codex
Impl-Model: default
提案ラウンド3の適用ラウンド1として以下の構造改善を適用:
- R3-001: gate.py の cmd_final_gate から終了処理・修正準備処理を抽出
- R3-002: apply.py の cmd_merge_test_judgements から判定読み込みと却下処理を抽出
- R3-003: setup.py と report.py に重複していた rounds_of_kind を rounds.py へ集約
- R3-004: proposals.py の提案集約処理を _accumulate_proposals へ統合

Item-Id: R3-001
Item-Id: R3-002
Item-Id: R3-003
Item-Id: R3-004
Round: 3
Impl-Runtime: agy
Impl-Model: default
…ts/refactor_lib/proposals.py#_normalize_proposal

smell / technique / severity で 3 回繰り返していた「語彙外なら警告して unknown
へ降格し degraded を立てる」同一ブロックを `_degrade_if_unknown` へ抽出し、
降格ルールを 1 箇所へ集約した。振る舞いは不変。

Item-Id: R3-005
Round: 3
Impl-Runtime: kiro
Impl-Model: default
…es の統合

適用ラウンド 1 の 4 項目をまとめて適用した。振る舞いは変えていない。

- R4-001 verify.verify_apply_round: 5 段の検証を `_verify_all_commits` /
  `_verify_characterization_test` / `_verify_diff_budget` /
  `_verify_apply_commit_count` へ抽出し、本体は同じ順で呼んで最初の失敗理由を
  返す流れだけにした
- R4-003 commands/apply.cmd_next_apply_round: 群の走査と開き直しの判定を
  `_find_group_to_open` へ、開いた群の状態の初期化を `_begin_apply_group` へ抽出した
- R4-004 refactor.main: サブコマンドの登録を `_add_init_command` /
  `_add_id_commands` / `_add_round_commands` / `_add_dry_run_commands` /
  `_add_report_command` へ抽出した。解析とディスパッチの現状固定テスト
  (tests/test_cli_subcommands.py)を先に足してある
- R4-005 gitfacts.commit_test_changes: 触れたファイルの列挙を commit_files へ
  寄せ、git show --name-only の重複を無くした

Item-Id: R4-001
Round: 4
Impl-Runtime: claude
Impl-Model: default

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ing/scripts/refactor_lib/verify.py#merge_test_judgements

段2の判定結果をパス別の辞書へ変換する処理を共通化する。

Item-Id: R4-002
Round: 4
Impl-Runtime: codex
Impl-Model: default
…tfacts.py

提案ラウンド 5(適用ラウンド 1)の構造改善を適用:
- R5-001: converge.py の cmd_abandon_items から _record_deferred_abandoned_items を抽出
- R5-002: apply.py の cmd_merge_apply から事前検査・処理済み制御・反映処理を抽出してオーケストレーター化
- R5-003: proposals.py の _normalize_test_proposal における語彙外降格処理を _degrade_test_value へ集約
- R5-004: gitfacts.py の check_run_result をパイプライン関数群に分割し、現状固定テストを追加

Item-Id: R5-001
Round: 5
Impl-Runtime: agy
Impl-Model: default
適用ラウンド 6(提案ラウンド 6)の構造改善 5 件をまとめて適用する。振る舞いは変えない。

- R6-001 gitfacts.py#drop_items: 旧版フォールバックを _drop_legacy_by_item へ抽出
- R6-002 report.py#cmd_report: 見出し・見送り・指標・run_metrics を _print_* へ抽出
- R6-003 converge.py#cmd_merge_fix: 結果取得・範囲確定と検証・記録と公開を抽出
- R6-004 apply.py#_verify_apply_group: 事実収集・問題判定・記録の 3 段を抽出
- R6-005 proposals.py: _degrade_test_value を廃し _degrade_if_unknown を location 引数で一般化

Item-Id: R6-001
Round: 6
Impl-Runtime: kiro
Impl-Model: default

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | kiro | COMMENT

結末の読み取りの共通化(intake / read_launch_outcome)、取り消しの集約(discard_unverified への一本化)、開き直しの判定(group_reopening)、輪番からの担当引き当て(impl_for_seq / _switch_apply_impl)、記名の末尾段落読み(commit_trailers / _parse_trailer_paragraph)はいずれも設計・要求(issue-728-647-592-553)と整合しており、記名の読み取りは題名補完+末尾段落からの逆走査で帰属トレーラー付きコミットも正しく読めることを実測で確認した(772 passed)。

設計レベルで直すべき点は無い。以下 1 件のみ inline に minor を付けた。

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | agy | APPROVE

総評

  • 改修目的と設計の整合性:
    • 親課題 #728 および子課題 #647(無限試行)、#592(空群の無限ループ)、#553(トレーラー欠損による丸ごと取り消し)、#674(未検証コミットの残留)に対する抜本的修正が、確定設計(issues/issue-728-647-592-553-design.md)および要求仕様(issues/issue-728-647-592-553-requirements.md)に定められた受け入れ条件(AC1〜AC50)通りに実装されていることを確認しました。
    • 3 つの取り込み(merge-apply / merge-fix / merge-final-fix)が共有する「範囲確定 → 未検証コミット取り消し → 結末記録」の手順が refactor_lib/intake.py に美しく集約され、下位層の read_result がプロセスを終わらせず(LaunchOutcome を返却)上位で安全にハンドリングされる構成になっています。
    • 適用ラウンドの開き直し上限(2回)と担当交代、採用0件時の空群作成抑止、最終ゲート修正での未検証コミット取り消し、トレーラーの段落単位読み取り、構造改善(R1〜R6)の各変更について、振る舞いの破壊や境界条件の穴は見受けられません。
  • 検証実績:
    • cross-refactoring の全テスト(772 件)、およびリポジトリ全体のテストスイート(4,672 件)がすべて green で通過。
    • プラグインバリデーション(claude plugin validate .)、生成ファイルの同期検査(build-runtime-plugins.sh --check)、frontmatter 検査、ドキュメント 500 行上限検査もすべて合格。
  • 判定:
    • 指摘事項(Critical / Major / Minor)は 0 件です。APPROVE と判定します。

構造改善で抽出した `_fetch_fix_result` の戻り値注釈が `Optional[...]` を
使うが、取り込みが `from typing import Any` だけで `Optional` を欠いていた。
`from __future__ import annotations` があるため読み込み時には出ないが、
`typing.get_type_hints()` を通すと `NameError: name 'Optional' is not defined`
になる。

同じ形の漏れが他に無いことを、`scripts/` の 19 モジュールすべてを
`typing.get_type_hints()` へ通して確かめた(修正後 failures=0)。
`Optional` は rounds.py / verify.py / paths.py と同じ書き方に揃えた。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGCedPTy818Zw7VYdmE4GB
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

🔧 /ndf:fix サマリ(最終スイープ / Step 7.5)

対応件数: critical=0 / major=0 / minor=1 / nit=0(合計 1 件)
deferred: 0 件 / rejected: 0 件
commit: fe174694
未解決スレッド: 0 件(GraphQL の reviewThreadsisResolved == false で数え直して確認)

詳細

  • plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/converge.py:14 — 取り込みを from typing import Any, Optional へ直した(kiro の minor 指摘、agy が support で裏付け)。この PR の構造改善で抽出した _fetch_fix_result の戻り値注釈が Optional[...] を使う一方、取り込みに Optional が無く、typing.get_type_hints()NameError になっていた

同じ形の漏れの確認

指摘の 1 箇所を直すだけでなく、plugins/ndf/skills/cross-refactoring/scripts/ 配下の 19 モジュールすべてを取り込み、モジュール直下の関数・クラス・メソッドを typing.get_type_hints() へ通した。from __future__ import annotations があると読み込み時には出ない種類の欠落なので、実際に解決させて確かめている。

時点 結果
修正前 modules=19 failures=1converge._fetch_fix_result のみ)
修正後 modules=19 failures=0

書き方は | None ではなく Optional に揃えた。同じ木の rounds.py / verify.py / paths.py がすべて from typing import Any, Optional の形で、| None 形式はこの木に 1 件も無いため。

検証

uv run --with pytest pytest scripts/tests plugins/ndf -q
→ 4672 passed in 178.40s(終了コード 0)

CI は修正前の commit の時点で 15 チェックすべて pass。

@takemi-ohama
takemi-ohama marked this pull request as ready for review September 22, 2026 02:53
@takemi-ohama
takemi-ohama merged commit 24bf3a4 into develop Sep 22, 2026
15 checks passed
@takemi-ohama
takemi-ohama deleted the feat/issue-728-apply-intake branch September 22, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant