同じ根本原因を持つ課題(子 issue)を、現れている場所ではなく根本原因の場所で直すための親 issue である(`issue-upkeep` の判定「ルートコーズ」)。再現手順と観測は各子 issue にある。 ## 修正レイヤー **cross-refactoring の提案の対象(ファイル・シンボル)の表し方の契約。** - 雛形(`plugins/ndf/skills/cross-refactoring/prompts/propose.md:60-85`)は `path` と `symbol` を別々の文字列で求める。`symbol` に複数のシンボルを並べる書き方も、`<ファイル>#<シンボル>` の 1 項目で書く形も、契約の外にある - `scripts/refactor_lib/proposals.py` の `_normalize_proposal`(`:48-59`)は、`path` か `symbol` が空なら警告だけを出して捨てる。捨てた件数は見送りに残らない - `symbol` は文字列のまま `refactor_lib/items.py` の `item_key`(`:26`、統合の鍵)と `group_key`(`:40`、候補の切り出しの組)になる。列挙の順序や書き方が違うだけで別の組になり、リファクタリング計画の「同じ変更か」(`commands/plan.py` の `_merge_duplicates`、`:141`)も同じ組の中でしか問わない ```console $ python3 -c 'from refactor_lib.proposals import build_candidates ok={"path":"a.py","symbol":"f, g","smell":"duplication","technique":"consolidate_duplication","severity":"major"} c,_=build_candidates({"y":[ok],"z":[dict(ok,symbol="g, f")]}); print(len(c),[i["symbol"] for i in c])' 2 ['f, g', 'g, f'] ``` (`plugins/ndf/skills/cross-refactoring/scripts` で実行。2026-09-26 の `develop`) ## 修正方針 統合(`consolidate_duplication`)。提案の対象を、1 つの入口が「パスとシンボルの集合」へ解釈する。統合の鍵・候補の組・見送りの除外は、その集合から作る。 ## 子 issue | 子 issue | 現象レイヤー | 観測 | | --- | --- | --- | | #684 | `_normalize_proposal` | `target` に行範囲を添えた提案が「path / symbol の無い提案」として捨てられ、見送りの一覧にも残らない | | #642 | `items.py` の `item_key` / `group_key` | シンボルの列挙の書き方が違う同じ提案が統合されず、別の項目として扱われる | ## 完了条件 - 提案の対象を 1 つの入口が解釈し、鍵と組もその結果から作る - 読めずに捨てた提案の件数が、集計と見送りの記録に残る - 各子 issue の再現手順を実行し、現象が出ないことを確かめる(子 issue はその時点の棚卸が「閉じてよい」で閉じる) ## やらないと判断した(2026-09-26) - 理由: 提案の対象の書き方の揺れで同じ提案が別の項目になるか、行範囲を添えた提案が捨てられる。項目の数は予算で絞られ、提案は毎回出し直されるので、失うのは一部の提案にとどまる。 - 実害: 揺れた提案 1 件あたり、重複した 1 項目か見送りに残らない 1 件。実害の件数は未測 - 費用の見積り: 3〜4 ファイル(proposals.py、items.py、commands/plan.py、テスト) - 再び考える条件: 同じ変更が 2 項目として実装され、取り消しか衝突の解消が要った記録が 1 件出たとき
Activity