Skip to content

cross-refactoring の是正: 範囲のテストで群を検証し、構造改善を飛ばせる判定を足す(#880 #883 #494 #723) - #917

Draft
takemi-ohama wants to merge 14 commits into
developfrom
feat/issue-880-883-494-723-cross-refactoring-fixes
Draft

takemi-ohama wants to merge 14 commits into
developfrom
feat/issue-880-883-494-723-cross-refactoring-fixes

Conversation

@takemi-ohama

Copy link
Copy Markdown
Contributor

Summary

cross-refactoring を是正して、構造改善の工程にかかる費用を下げ、飛ばしてよい場合を判定できるようにする。この PR は設計のまとまり(2'')の PR 2 である(PR 1 は #885)。

課題 変えたこと
#880 init --round-test CMD を足した。群の検証と修正コミットの検証は範囲のテストで行い、全体テスト(--baseline-test)は init と最終ゲートの 2 回だけにする。単独起動でも、2 つのテストが違えば最終ゲートで全体テストを 1 回走らせる。範囲のテストが --scope のテストを覆わなければ止める。--round-test を省いて全体テストが範囲より広いときは 1 行の案内を出す。検証の記録と最終ゲートの記録に seconds を足した
#883 打ち切りの点検で親シェルを先に回収する。子が終わった時点で戻る(exec sleep 30、上限 1 秒で 6.06 秒かかっていたものが 1.20 秒になった)
#494 refactor.py assess --base <ref> を足した。終了コードは 0 = 通す / 3 = 飛ばしてよい / 2 = 判定できない。workflow-modes.md の「構造改善の退避先」では、飛ばす条件と退避する条件を分けて書いた。stage-notes.md には構造改善の順(assess → --round-test--baseline-test)を書いた
#723 テスト整備の提案のうち、target.md を指すものを見送る。適用ラウンドでは、追跡している .md を指すテストの追加を失敗にする(doc_wording_tests)。propose-tests.md の「守ること」に 1 項目を足した

構造改善: この PR は検査の持ち場で通す(AC20〜AC23。PR 1 を取り込んだ後に行う)。

設計と違えたところ・設計に無く決めたところ

  • 最終ゲートの修正コミットでは、範囲のテストを走らせない(AC2 の一部)。 今の実装では、この経路はテストを走らせていない。直後の final-gate が全体テストで 1 度だけ判定し、--ci-check の実行では手元のテストを走らせない(cross-refactoring の全体フローを変える(コミット粒度・レビューをテストへ・最終ゲートの省略・ラウンド上限) #436 の決定 11 の排他。固定しているテストは test_final_fix.py::test_the_commit_test_status_is_not_checked)。AC2 が守りたい「全体テストをコミットごとに走らせない」はこのままで満たす。範囲のテストを足すと決定 11 を覆すため、この PR では変えない。検査の持ち場で、AC2 の文言をこの形に揃えるかを判断する
  • 単独起動の最終ゲートで --ci-check があるときは、手元の全体テストの代わりに継続的統合で判定する(AC3 の「--ci-check があれば今のとおり継続的統合で代える」に合わせた)
  • gitfacts._is_test_path を公開名 is_test_path に改名した(scope.py から使うため。モジュールの境界の検査は、非公開名をモジュールの外から使うことを禁じている)
  • doc_wording_tests(ファイル, 文字列) の組を返す。失敗の理由に文字列を載せるため
  • assesstracked_markdowngit ... -z で読む。ASCII 以外を含むパスが引用符付きで出るのを避けるため

Closes #880
Closes #883
Closes #494
Closes #723

Test plan

  • AC24: uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest pytest . -q -n 4 → 5422 passed、exit=0
  • cross-refactoring: pytest plugins/ndf/skills/cross-refactoring/tests -q -n 4 → 854 passed(着手前は 798 件)
  • AC1〜AC7: test_init.pytest_verify_round.pytest_abandon_items.pytest_final_gate.pytest_scope_gate.py に足したテスト
  • AC9〜AC11: test_git_facts.py の打ち切りの 4 件。所要は 1.20 / 1.22 / 2.74 / 3.55 秒で、既存 3 件の合計は約 7.5 秒(10 秒未満)
  • AC12〜AC14: tests/test_assess.py の 8 件
  • AC17・AC18: test_test_rounds.py の 2 件と tests/test_doc_wording_tests.py の 8 件
  • AC8・AC15・AC16: stage-notes.mdworkflow-modes.mdpropose-tests.md の該当の節を読んで確かめる(文言を固定するテストは書かない)
  • python3 plugins/ndf/skills/cross-refactoring/scripts/refactor.py assess --base origin/develop判定: 通す / 94 行、exit=0
  • python3 scripts/check-skill-frontmatter.py exit=0 / python3 scripts/check-markdown-links.py exit=0 / claude plugin validate . exit=0 / python3 plugins/ndf/scripts/instructions-check.py --root . exit=0
  • AC20〜AC23: 検査の持ち場で、PR 1 を取り込んだ後にこの PR へ cross-refactoring --workflow-step を通して数える

🤖 Generated with Claude Code

https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy

takemi-ohama and others added 10 commits September 23, 2026 12:57
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
SIGTERM の後の点検で親シェルを先に回収する。回収しないとゾンビがグループに
残り、子がすべて終わっていても猶予の 5 秒を毎回待っていた。判定はグループの
存否のまま変えない(SIGTERM を無視する子には SIGKILL が届く)。
打ち切りの 3 件のテストの確認の待ちを 1.5 秒へ縮める(合計 約 7.5 秒)。

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
- refactor.py に副命令 assess(--base REF、--max-lines N(既定 10))を登録する
- gitfacts.py に production_code_changes を足す。git diff --numstat -z --no-renames
  <base>...HEAD を読み、CODE_EXTENSIONS に当たり _is_test_path が偽のファイルと
  変更行(追加 + 削除)を返す。rename は旧パスと新パスの両方で判定する
- CODE_EXTENSIONS は cross-refactoring に無かったため、設計の一覧どおりに gitfacts.py へ置く
- 終了コードは 0 = 通す / 3 = 飛ばしてよい / 2 = 起点を解けない。出力は 判定・理由・本番コード の 3 行
- tests/test_assess.py に一時リポジトリで AC12〜AC14 を確かめるテストを 8 件足す

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
- refactor.py init に --round-test CMD を足し、--baseline-test の help を「着手前と最終ゲートで実行する全体のテスト」へ直す
- init は状態へ round_test(command・status)を保存する。省くか全体テストと同じ文字列なら全体テストを 1 回だけ実行して結果を写し、違えば全体テストの後に 1 回実行して失敗(終了コード 5 を含む)で止める
- --scope の関門は --round-test があればその実行集合(scope.round_test_roots)で判定する。オプションの値・作業ディレクトリの根・ラッパーのスクリプトは起点に数えない
- --round-test が無く --baseline-test が範囲より広いとき、1 行の案内を出して続ける(scope.round_test_hint)
- verify-round と merge-fix の修正コミットの検証は scope.round_test_command(state) を実行する。round_test の無い状態ファイルは baseline_test を返す
- 最終ゲートは単独起動でも round_test が baseline_test と違えば先に全体テストを 1 回通す(--ci-check があれば継続的統合で代える)
- verifications と final_gate.checks の各件へ seconds(final_gate は command も)を足す
- scope から使うため gitfacts._is_test_path を公開名 is_test_path へ改名する

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
- cross-refactoring の SKILL.md: 引数の表に --round-test、検証の単位を範囲のテストへ、
  前提に assess(終了コード 3 なら起動しない)
- CLAUDE.md の cross-refactoring の節: 起動の例に --round-test、全体テストが 2 回に寄ること
- workflow-modes.md「構造改善の退避先」: 飛ばす(assess の終了コード 3)と refactoring 単独へ
  退避する(今の 3 条件)を分け、飛ばしたときの記録の残し方を書く
- stage-notes.md: 構造改善に入ったら assess → --round-test に範囲のテスト →
  --baseline-test に全体テスト、の順を書く

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
- merge_test_proposals: target の # より前が .md で終わる提案を「文書の文言を固定するテストは足さない」で見送る(F8・AC17)
- verify.py に doc_wording_tests を新設し、verify_apply_round の verify_test_changes の直後で呼ぶ(F9・AC18)
  - 追加行の文字列リテラルが、追跡している .md のパスか / で揃えた末尾に一致すれば当たり
  - テストのファイルの直下の定数と、同じディレクトリの補助モジュールから import した定数も追う(補助モジュールは群で触っていなければ git から読む)
- gitfacts.py に tracked_markdown(git ls-files -z '*.md')を足し、apply.py が群ごとに 1 回読んで渡す

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhcogXCb1x3eStK4VoDDCy
状態探索、適用群の絞り込み、起動失敗、壊れた提案結果、バイナリ差分の現状動作を固定する。

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

takemi-ohama commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

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

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

  • 対象範囲: plugins/ndf/skills/cross-refactoring/scripts, plugins/ndf/skills/cross-refactoring/tests
  • 着手前のテスト: uv run --project plugins/playwright-kit/skills/playwright-kit-ops --with pytest --with pytest-xdist pytest . -q -n 4 -p no:cacheprovider

ラウンド 1(実装 codex)

R1-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#load_state

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

なぜ: 既存の test_paths.py は指定先優先・現在地への退避・環境変数未設定の 3 つの成功経路だけを固定している。指定先にも現在地にも対象 ID が無いときに die で中断する経路(終了コード 4)が固定されていない

手順: 1. CROSS_REFACTORING_TMP_DIR を空の一時ディレクトリへ向け、現在地にも .cross_refactoring の対象状態を置かない
2. load_state を対象 ID で呼ぶ
3. SystemExit の終了コードと、状態を返さず対象 ID を示して停止したことを比較する

R1-002 — plugins/ndf/skills/cross-refactoring/scripts/launch-cli.sh#collect_round_materials

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

なぜ: apply / fix のプロンプトへ渡す項目(RF_ITEMS)を、ラウンドと適用群(apply_round)で絞る jq の分岐がどのテストからも観測されていない。launch-cli.sh を起動するテストは propose / propose-tests / judge-test-changes / final-fix のプロンプトと起動引数だけを見ており、群をまたいだ項目が混ざらないこと・apply_round が 0 のときはそのラウンドの全項目を渡すこと・別ラウンドの項目を渡さないことは固定されていない。launch-cli.sh は関数へ分けた直後で、構造改善の対象になりやすい

手順: 1. crossref_helpers.make_state で rounds に {round: 1, apply_round: 2} と {round: 2} を、items に R1-001(round 1, apply_round 1)・R1-002(round 1, apply_round 2)・R1-003(round 1, apply_round 未設定)・R2-001(round 2)を置いた状態を作る
2. 既存の test_excluded_items._prompt_with_deferred と同じく、PATH へ何もしない codex の代替を置き、launch-cli.sh codex apply 130 1 を起動する
3. 生成された codex-apply-r1-prompt.md に現れる項目 ID の集合を正規表現 R\d+-\d{3} で取り出し、現状の出力を期待値として記録する(R1-002 だけのはず)
4. 同じ手順を fix フェーズでも行い、同じ集合になることを比べる
5. rounds の apply_round を 0 にした状態でもう一度起動し、R1-001 / R1-002 / R1-003 が入り R2-001 が入らないことを現状の出力として記録する

R1-003 — plugins/ndf/skills/cross-refactoring/scripts/launch-cli.sh#require_round

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

なぜ: 起動の失敗経路のうち固定されているのは未知のフェーズ(test_removed_review_phase)と判定差分の欠落(test_assert_changes)だけである。ROUND を省いたときに rounds の長さで補う分岐、rounds が空で ROUND も無いときに止まる分岐、未知のランタイムで止まる分岐は、どのテストも通していない。止まる経路では CLI が起動されないことが振る舞いである

手順: 1. make_state で rounds を空にした状態を作り、PATH へ引数を書き出す codex の代替(test_launch_agy_phases の STUB と同じ形)を置く
2. launch-cli.sh codex apply 130 を ROUND なしで起動し、終了コードが 0 でないこと・代替の CLI が起動されず引数ファイルが作られないこと・プロンプトファイルが作られないことを確かめる
3. rounds に 2 件(round 1 と round 2)を置いた状態で同じく ROUND なしで起動し、生成される stem が codex-apply-r2 になることを現状の出力として記録する
4. launch-cli.sh unknown propose 130 1 を起動し、終了コードが 0 でないこと・プロンプトファイルが作られないことを確かめる(メッセージは完全一致させない)

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

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

なぜ: 提案結果が JSON オブジェクトでない経路は固定されているが、JSON として壊れた結果を 1 者が返した経路は固定されていない。壊れた 1 件で全体を止めず、その者を提案なしとして残りを統合する振る舞いが未固定である

手順: 1. 3 者分の提案結果のうち 1 者のファイルへ不正な JSON を書き、残り 2 者には同じ有効な提案を書く
2. cmd_merge_proposals を公開コマンド入口から実行する
3. 実行が継続し、壊れた結果の提案件数が 0、有効な提案が 1 件だけ採用された状態を比較する

R1-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/assess.py#cmd_assess

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

なぜ: 本番コード 0 行・10 行・11 行と rename は固定されているが、git numstat が追加行・削除行を '-' で返すバイナリのコード拡張子ファイルは固定されていない。整数変換で落ちず、現状どおり 0 行として判定する境界が未固定である

手順: 1. 実 git リポジトリへ NUL を含む .py ファイルを追加してコミットする
2. refactor.py assess を CLI 入口から起点指定付きで実行する
3. クラッシュせず終了コード 3 となり、その本番コード 1 ファイルを 0 行として数える現在の判定結果を比較する

ラウンド 2(実装 kiro)

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

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

なぜ: 着手前のテストと範囲のテストは、失敗(終了コード非 0)で止まる経路は固定されているが、打ち切り(run_with_timeout が timed_out=True を返す)で止まる経路は 2 つとも固定されていない(setup.py 630-634 と 662-663 が未到達)。打ち切りは失敗と別の分岐で、終了コードも着手前のテストは 1、範囲のテストは ABORT と異なる

手順: 1. test_init.py の test_calls と同じく patch_lib で run_with_timeout を差し替え、コマンドごとに (None, True) を返せる疑似実装にする
2. 着手前のテストだけ打ち切りにして run_init を呼び、SystemExit の終了コード(現状の値)と、状態ファイルが書かれていないことを比較する
3. 着手前のテストは (0, False)、--round-test に別のコマンドを渡してそれだけ打ち切りにして run_init を呼び、SystemExit の終了コードが refactor_abort() であることと、状態ファイルが書かれていないことを比較する
4. 出力は文言の完全一致を取らず、打ち切った秒数が含まれることだけを見る

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

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

なぜ: 適用の検証に落ちた群の取り消しで、積み直せず run_drop が mode=round を返したときの分岐(群の全項目を abandoned にし failure_reason を付け、apply.applied を空・apply.failed を群の全項目にし、apply_base_sha を取り直す)が固定されていない。gitfacts.drop_items の round 退避は test_drop_items_git.py が固定しているが、それを受けた cmd_merge_apply 側の状態の書き換えはどのテストも通っていない(カバレッジ上 apply.py 1008-1017 が未到達)

手順: 1. 既存の merge-apply のテストと同じ形で、群に 2 項目を持つ状態ファイルと、検証に落ちる適用結果(片方の項目だけ失敗)を用意する
2. patch_lib で run_drop を {"mode": "round"} を返す疑似実装に差し替え、git_out(rev-parse HEAD) は固定の SHA を返させる
3. cmd_merge_apply を実行する
4. 状態ファイルを読み直し、群の 2 項目がともに status=abandoned で failure_reason を持つこと、apply.applied が空で apply.failed が群の全項目であること、apply_base_sha が固定の SHA へ更新されたこと、deferred_items に 2 項目が載ったことを比較する
5. mode=item を返したとき(失敗した項目だけ abandoned)を対照として 1 件並べ、分岐の両側を固定する

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

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

なぜ: 最終ゲートの全体テストについて成功と非ゼロ終了は固定されているが、タイムアウトを失敗として記録し修正ラウンドへ進む経路は固定されていない

手順: 1. 全体テストの実行結果をタイムアウトへ差し替えて cmd_final_gate を実行する
2. 失敗の終了コード、最終ゲート記録の timed-out 相当の詳細、修正ラウンドへの遷移を比較する

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

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

なぜ: 完了報告の既存テストは、参加者の記録が unavailable を持ち resume_changes が空でない状態でしか通っていない。確認を飛ばした(probe_skipped=True)/ 確認で落ちた者なし、再開で変えた値なし、再開で参加者(available を持つ dict)を変えた記録、test_rounds_final がある、テスト整備の項目(case / level を持つ項目)が項目表に並ぶ、の各分岐が未到達(report.py 152, 184-187, 195, 207, 262)

手順: 1. test_rounds.py の報告用の状態ファイルを作る補助を使い、参加者の記録と resume_changes と items だけを変えた状態を分岐ごとに作る
2. parametrize で (a) unavailable 空・probe_skipped=True (b) unavailable 空・probe_skipped=False (c) resume_changes 空 (d) resume_changes に field=participants で from/to が available を持つ dict (e) test_rounds_final あり・kind がテストの項目あり を与える
3. cmd_report(metrics=False) を呼び、capsys の出力から該当する 1 行(「確認を通らなかった者」「再開で変えた値」「テスト整備の終わり方」の行、項目表のその項目の行)だけを取り出す
4. 取り出した行を現状の出力で固定する。行全体の完全一致は避け、値の部分(NDF_SKIP_AUTH_CHECK の有無・なし・available の並び・case と level の値)だけを比較する

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

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

なぜ: モデル文字列の解析と不一致判定は個別に固定されているが、CLI の stdout ログから得た実測モデルを適用ラウンドの状態へ反映する正常経路は固定されていない

手順: 1. 実測モデルを含む stdout ログと、指定モデルを持つラウンド状態を作る
2. record_observed_model を実行する
3. impl_model.observed に現状の実測値が保存されることを比較する

ラウンド 3(実装 claude)

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

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

なぜ: 1 関数に約 90 行・5 つの結末(単独=cross-review へ委譲 / 検査の実行と記録 / 合格かつ単独 / 合格 / 上限到達 / 落ちて修正へ)が同居する。結末ごとに『gate へ書く → save → info → emit → return か sys.exit』の 4〜5 行が繰り返され、判定の流れが結末の記録に埋もれている。

手順: 1. 結末の発火を helper へ抽出する: _emit_cross_review(path, state, gate)(cross-review へ委譲)、_record_gate_check(gate, mode, command, passed, detail, seconds)(checks への 1 件追記)
2. 合格・上限到達・修正継続の 3 結末を _gate_passed, _gate_limit_reached, _gate_failing として抽出し、それぞれ save・info・emit・終了コードを閉じ込める
3. cmd_final_gate 本体は『委譲するか → 検査を実行し記録 → 合否で分岐して結末 helper を呼ぶ』の骨格だけを残す
4. tests/test_final_gate.py(21 件)を実行し、5 つの結末すべてが従来どおりの FINAL_GATE 値・終了コードを返すことを確認する

R3-002 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/verify.py#verify_apply_round

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

なぜ: コミット存在確認、各コミットの基礎検査、test_gap、テスト変更、文言固定テスト、差分予算、粒度を順番に検査する一方、各検査の呼び出しと早期終了が手書きで並び、検査順序という規則が関数本体の制御構文に埋もれている

手順: 1. facts 全体から基礎違反を返す段を抽出する
2. test_gap とテスト変更を扱うテスト保護段を抽出する
3. 文言固定、差分予算、コミット粒度を扱う差分制約段を抽出する
4. verify_apply_round は各段へ同じコンテキストを順に渡し、最初の問題を返すパイプラインにする
5. test_merge_apply.py、test_doc_wording_tests.py、test_commit_granularity.py の既存検査順とエラー文を固定したまま実行する

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

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

なぜ: ホスト・モデルの検証、PR と worktree の準備、scope 関門、再開判定、参加者確定、2 種類のテスト実行、状態保存と出力が 1 関数に直列で同居しており、各段階の入力と終了条件を個別に追いにくい

手順: 1. ホスト・モデル・include/exclude の解決を独立した入力解決段へ抽出する
2. PR コンテキスト、worktree、scope 関門、既存状態の再開判定を準備段へ抽出する
3. 参加者確定と baseline/round test 実行を検証段へ抽出する
4. InitialContext の生成から保存・出力までを初期状態確定段として残し、各段の出力を次段へ明示的に渡す
5. test_init.py の既存正常系・再開・失敗経路を段階ごとに実行する

R3-004 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#_record_drop_result

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

なぜ: _record_drop_result は引数が 9 個あり、そのうち owner / keep_ids / replay / ordered / pending の 5 個は _drop_replay_plan と drop_items が組み立てた「取り消しと積み直しの計画」をばらして運んでいるだけである。同じ計画を _dry_run_drop_plan と _execute_drop_replay も一部ずつ受け取っている。さらに戻り値の形 {mode, dropped, reverted, replayed} が gitfacts.py の 5 箇所(681 / 739 / 755 / 784 行と entry.drops の 732 行)で別々に辞書リテラルとして書かれていて、キーの追加や改名で食い違う

手順: 1. drop_items の dry-run 経路(_dry_run_drop_plan を通る)と、apply_base_sha が無い旧版の経路(_drop_legacy_by_item)を現状固定テストで tests/test_drop_items_git.py に固定する(戻り値の 4 キーと、dry-run では git の履歴と state が変わらないこと)
2. gitfacts.py にフィールド pending / ordered / owner / keep_ids / replay を持つ dataclass _DropPlan を導入する
3. _drop_replay_plan が (owner, keep_ids, replay) のタプルの代わりに _DropPlan を返すようにし、drop_items で受け取る
4. _dry_run_drop_plan(plan) / _execute_drop_replay(work, plan, head) / _record_drop_result(state, entry, plan, mapping, mode) へ引数を置き換える
5. 戻り値の辞書を作る _drop_summary(mode, dropped, reverted, replayed) を抽出し、5 箇所の辞書リテラルをこれの呼び出しへ置き換える
6. 既存の test_drop_items_git.py / test_abandon_items.py / test_final_gate.py と 1 で足した固定テストを実行する

R3-005 — plugins/ndf/skills/cross-refactoring/scripts/refactor.py#main

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

なぜ: 153 行の入口で init 固有引数、単純な id コマンド、round 付きコマンド、dry-run 付きコマンド、assess、report の各パーサー構築と dispatch を一括しており、コマンド群ごとの契約変更が同じ関数へ集中している

手順: 1. init サブパーサーの構築を add_init_parser として抽出する
2. 共通形を持つ id-only、round、dry-run コマンド群の登録をそれぞれ名前付き関数へ抽出する
3. assess と report の登録をそれぞれ抽出する
4. main には ArgumentParser の生成、各登録関数の呼び出し、parse_args、dispatch だけを残す
5. test_init.py の引数解析テストと CLI 関連テストを実行する

ラウンド 4(実装 codex)

R4-001 — plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#cmd_start_round

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

なぜ: 78 行の 1 関数に、終了判定・新しいラウンドの記録の雛形(13 鍵の辞書リテラル)・種類ごとの表示名と上限の選択・emit が同居している。雛形の組み立ては判断を含まない値の定義で、本体の流れを読む妨げになっている

手順: 1. 新しいラウンドの辞書リテラル(round〜reviews)を _new_round_entry(state, round_no, kind) として抽出し、impl_for_seq の呼び出しもその中へ移す
2. kind が TEST かどうかで label と limit を決める分岐を _round_label_and_limit(state, kind) として抽出し、(label, limit) を返す
3. cmd_start_round は 2 つを呼ぶ形に書き換える。鍵・値・保存の順序は変えない
4. tests/test_rounds.py・test_test_rounds.py・test_start_round_emits_runtimes.py を実行して不変を確かめる

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

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

なぜ: apply.py の _defer_abandoned_items と、item_id の既登録判定、abandoned 状態の扱い、failure_reason の既定化、deferred_record の追記という同じ業務判断を別々に保持しており、見送り記録の規則変更が片方だけに入る恐れがある。

手順: 1. 2 関数が必要とする差分である対象 item_id の選別条件を明示する
2. abandoned 項目を未登録時だけ deferred_record として追記する共通ヘルパーを、見送り記録を所有する層へ置く
3. _record_deferred_abandoned_items は status と failure_reason の設定後に共通ヘルパーを呼ぶ
4. _defer_abandoned_items は既に abandoned の項目を選んで同じ共通ヘルパーを呼ぶ
5. test_abandon_items.py と test_excluded_items.py で重複防止と failure_reason が不変であることを確認する

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

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

なぜ: 中断処理の再開、冪等ガード、対象なし、dry-run、取り消し実行、状態更新、push を直列に扱い、各終了経路の副作用を読み分ける負荷が高い。

手順: 1. pending_drop の再実行と pending_push の送信を行う前処理をヘルパーへ抽出する
2. 対象なしと dry-run の早期終了判定をヘルパーへ抽出する
3. 取り消し後の group・entry・state の更新と push を行う完了処理をヘルパーへ抽出する
4. cmd_abandon_items は各段階の呼び出し順だけを表す形にし、test_abandon_items.py の既存経路を実行する

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

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

なぜ: 再実行時の既存結果表示、種類別の統合戦略選択、状態更新、統計表示、採用 0 件の終了制御が同居し、テスト提案と構造改善提案の分岐が処理全体へ広がっている。

手順: 1. 統合済みラウンドの表示と終了判定をヘルパーへ抽出する
2. entry kind に応じて merge_test_proposals または merge_proposals を呼ぶ処理をヘルパーへ抽出する
3. 統合後の一覧表示と採用 0 件の終了制御をヘルパーへ抽出する
4. cmd_merge_proposals は load、既処理判定、統合、状態更新、結果通知の段階だけを残し、test_merge_proposals.py と test_test_rounds.py を実行する

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

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

なぜ: 取り消し結果の解釈、全件見送りへの状態変換、群の状態決定、次 phase の決定、対象外記録、保存用印の更新、push が 1 関数に連続し、再開契約の境界が埋もれている。

手順: 1. run_drop の結果から item・entry・group の採用状態を更新する部分をヘルパーへ抽出する
2. apply_base_sha と group status と次 phase を確定する部分をヘルパーへ抽出する
3. deferred 記録から pending_drop 解除、merged_at 設定、push までの永続化段階を名前付きヘルパーへ抽出する
4. _apply_drop は取り消し、状態反映、永続化の 3 段だけを残し、test_abandon_items.py の round/item 両モードと再開テストを実行する

見送った項目

ラウンド 対象 兆候・経路 理由
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/assess.py#cmd_assess branch 1 ラウンドの採用上限 5 件を超えた
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#git_out branch 1 ラウンドの採用上限 5 件を超えた
1 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/scope.py#round_test_command branch 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/rounds.py#current_group branch 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 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/scope.py#round_test_hint boundary 1 ラウンドの採用上限 5 件を超えた
2 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/vocabulary.py#vocabulary error 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/tests/conftest.py#patch_lib mock_targets_implementation_detail 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#_revert_unverified_apply_round duplication 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/gitfacts.py#commit_diff_lines duplication 1 ラウンドの採用上限 5 件を超えた
3 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/paths.py#git_out duplication 1 ラウンドの採用上限 5 件を超えた
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/plan.py#publish_plan_comment duplication 1 ラウンドの採用上限 5 件を超えた
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/measure.py#_phase_of long_method 1 ラウンドの採用上限 5 件を超えた
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/setup.py#_resume long_parameter_list 1 ラウンドの採用上限 5 件を超えた
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/proposals.py#_normalize_proposal duplication 1 ラウンドの採用上限 5 件を超えた
4 plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/commands/apply.py#_drop_group_without_result duplication 1 ラウンドの採用上限 5 件を超えた

takemi-ohama and others added 4 commits September 23, 2026 15:04
打ち切り・取り消し(mode=round)・最終ゲートの打ち切り・完了報告の分岐・
実測モデルの反映という、これまでどのテストも通っていなかった 5 つの経路を
現状固定テストで固定する。対象のコードは変更しない。

Item-Id: R2-001
Round: 2
Impl-Runtime: kiro
Impl-Model: default
…ts/refactor_lib/commands/gate.py#cmd_final_gate

提案ラウンド 3・適用ラウンド 1 の 5 項目をまとめて適用する(振る舞いは変えない)。

- R3-001 gate.py#cmd_final_gate: 結末ごとの記録を _emit_cross_review / _record_gate_check /
  _gate_passed / _gate_limit_reached / _gate_failing へ抽出し、本体を判定の骨格だけにする
- R3-002 verify.py#verify_apply_round: 基礎検査・テスト保護・差分制約の 3 段へ分け、
  _APPLY_ROUND_STAGES を順に通して最初の問題を返すパイプラインにする
- R3-003 setup.py#cmd_init: 入力解決・準備・再開判定・検証・初期状態の保存の段へ分ける。
  _emit_init は cmd_init から直接呼ぶ(check-skill-shell-vars.py がヘルパーを 1 段しかたどらないため)
- R3-004 gitfacts.py#_record_drop_result: 取り消しと積み直しの計画を _DropPlan にまとめ、
  結果の辞書を _drop_summary へ寄せる。dry-run と旧版の状態ファイルの経路を現状固定テストで固定
- R3-005 refactor.py#main: 副コマンド群ごとのパーサー登録を add_* 関数へ抽出する

Item-Id: R3-001
Round: 3
Impl-Runtime: claude
Impl-Model: default
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ラウンド生成と表示条件、提案統合の各段階を抽出し、取り消した項目の見送り記録を共通ヘルパーへ集約する。

Item-Id: R4-001
Round: 4
Impl-Runtime: codex
Impl-Model: default
…apply_drop

cmd_abandon_items をやり残しの後片づけ・対象なし処理・完了処理の 3 つの
ヘルパーへ抽出し、本体は各段階の呼び出し順だけを表す形にした。_apply_drop を
取り消しと採用状態の反映・状態確定・永続化の 3 段のヘルパーへ抽出した。
振る舞いは不変で、既存テストが通る。

Item-Id: R4-003
Round: 4
Impl-Runtime: kiro
Impl-Model: default

This branch has not been deployed

No deployments
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