Skip to content

cross-refactoring: シンボルの列挙の書き方が違う同じ提案が統合されず、2 件目が「申告なし」で取り消しとして記録される #642

Closed as not planned

Description

@takemi-ohama

親: #726

何を見つけたか

cross-refactoring の候補づくり(proposals.build_candidates)は、提案をファイル(path)・シンボル(symbol)・兆候(smell)の 3 つが完全に一致するときだけ同一視して統合する。リファクタリング計画の「同じ変更か」の判定(commands/plan.py の _merge_duplicates)も、path + symbol が文字列として一致する組の中でしか問わない。シンボルの列挙の書き方が違うだけの同じ提案は統合されず、別々の改善項目として採られる。実装担当は片方を実装した時点でもう片方にやることが無くなり、2 件目はコミットが無いため not_done で見送られる。

develop(0447ac50)で、同じ path と兆候 duplication の提案を 3 者から渡して確かめた(agy / kiro は symbol='TOTAL_ID'、codex は symbol='TOTAL_ID, LIABILITY_KIND')。出力は候補の数と「(シンボル, 提案元, group_key)」の一覧である。

$ python3 -c '... proposals.build_candidates({"agy": [p("TOTAL_ID")], "kiro": [p("TOTAL_ID")], "codex": [p("TOTAL_ID, LIABILITY_KIND")]}) ...'
2 [('TOTAL_ID', ['agy', 'kiro'], ('personal/src/ingest.py', 'TOTAL_ID')), ('TOTAL_ID, LIABILITY_KIND', ['codex'], ('personal/src/ingest.py', 'TOTAL_ID, LIABILITY_KIND'))]

group_key が違うため、_merge_duplicates はこの 2 件を比べない。

最初の観測は takemi-ohama/ideabase PR #34(ndf 10.10.1)で、TOTAL_ID と LIABILITY_KIND の二重定義を models.py へ集める同じ改善が 2 項目(R3-002 と R3-003)になり、2 件目が取り消しとして記録された。

実害は差分の損失ではなく、記録と指標の汚れである。リファクタリング計画の見送った提案に not_done として並び、人が読むと「締め切りまでに終わらなかった」と読める。

どこで見つけたか

  • 統合の鍵: plugins/ndf/skills/cross-refactoring/scripts/refactor_lib/items.py の item_key(:26)と group_key(:40-42)。各値を文字列のまま比べる。proposals.py の _merge_by_key が item_key で統合する
  • 提案の正規化: proposals.py の _normalize_proposal(:48-)は symbol の前後の空白を落とすだけで、列挙を分解しない
  • 同じ変更の判定: commands/plan.py の _merge_duplicates(:140-171)。group_key ごとの組の中だけで問い、統合したものを duplicate で見送る
  • 見送りの判定: commands/implement.py。コミットの無い項目を not_done で見送る(結果ファイルの申告は使わず、git から取る)

直さないと何が起きるか

  • 同じ改善が 2 つの項目として想定最大時間の枠を消費し、別の提案が budget で押し出される
  • 2 件目は必ず not_done になり、リファクタリング計画と指標にコードの理由ではない見送りが残る

修正レイヤー

提案の対象(path と symbol)の解釈(#726)。シンボルの列挙を集合として読み、item_key と group_key をその集合から作れば、build_candidates の統合と _merge_duplicates の「同じ変更か」の問いが同じ形で直る。

この課題に個別に残るのは、統合が漏れたときの受け皿である。not_done のうち、同じファイル・同じ兆候の項目が実装済みのものは duplicate として見送る(implement.py の見送りの理由を分ける)。

由来

takemi-ohama/ideabase PR #34(振り返り https://github.com/takemi-ohama/ideabase/pull/34#issuecomment-5662176693)

やらないと判断した(2026-09-26)

  • 理由: 書き方の違う同じ提案が 2 項目になっても、差分は失われず、2 件目が not_done として記録に残るだけである。提案の対象の解釈を直す親の課題で同時に直る。
  • 実害: 見送りの一覧に理由の違う 1 件が残る記録の汚れ。予算の枠を 1 件分使う可能性はあるが観測は ideabase の 1 件だけ
  • 費用の見積り: 2〜3 ファイル(refactor_lib/items.py・commands/implement.py とテスト)
  • 再び考える条件: 同じ改善が 2 項目に割れて、別の提案が予算で押し出された記録が 1 件出たとき

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ndf-skillNDF の Skill 本体enhancementNew feature or requestpriority: low文書のみ・低頻度など、余力があれば対応するwontfixThis will not be worked on

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions