Skip to content

feat(plugin): 既存 plugins/ → repos/ マイグレーション (PLAN04 PR2)#30

Closed
takemi-ohama wants to merge 9 commits into
release/PLAN04from
feature/PLAN04-migration
Closed

feat(plugin): 既存 plugins/ → repos/ マイグレーション (PLAN04 PR2)#30
takemi-ohama wants to merge 9 commits into
release/PLAN04from
feature/PLAN04-migration

Conversation

@takemi-ohama
Copy link
Copy Markdown
Contributor

Summary

PR1 (#29) で repos/ 永続クローン方式に切り替えたが、PR1 以前に plugins/<name>/
ファイルコピーされた既存インストールは移行されない。本 PR でその移行ロジックを追加する。

実装内容 (予定)

  • lib/devbase/plugin/migrator.py (新規): plugins/ → repos/ 移行ロジック
    • legacy plugins/ インストールの検出
    • repos/ への永続クローン (未クローン時)
    • plugins/ とクローンの差分検出 → 差分ありは .bak 保全 + warning
    • InstalledPlugin.path の書き換え + sync_projects 再実行
    • --link が 0 件なら plugins/ を .gitkeep のみ残して削除
  • lib/devbase/commands/plugin.py + cli.py: devbase plugin migrate サブコマンド追加
  • tests/plugin/: マイグレーションテスト

Test plan

  • legacy plugins/ → repos/ 移行が正常完了
  • 移行後の projects/ シンボリックリンクが正しい
  • plugins/ にユーザー変更がある場合 → warning + .bak 保全
  • plugins/ にユーザー変更がない場合 → 削除
  • --link 残存時は plugins/ を維持 / 0 件なら .gitkeep 残して削除

takemi-ohama and others added 2 commits May 28, 2026 04:34
PLAN04 PR2。PR1 (#29) で repos/ 永続クローン方式に切り替えたが、PR1 以前に
plugins/<name>/ へファイルコピーされた既存インストールは移行されないため、その
移行ロジックを追加する。

- migrator.py (新規):
  - needs_migration / _is_legacy_plugin: legacy plugins/ インストールの検出
    (linked は --link 専用として除外)
  - _dirs_differ: コピーとクローンの差分検出 (内容変更・追加ファイルを保守的に差分扱い)
  - migrate: 未クローン repo の永続クローン作成、InstalledPlugin.path の repos/ 書き換え、
    差分なしは plugins/<name> 削除・差分ありは <name>.bak 保全、sync_projects 再実行、
    --link/.bak/skip が無ければ plugins/ を .gitkeep のみに正規化
- plugin migrate サブコマンド (cli.py / commands/plugin.py)
- install/update 初回実行時に _auto_migrate で自動移行

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 1 | gemini | REQUEST_CHANGES

マイグレーション時のデータ損失リスクを回避するため、比較ロジックの厳密化とバックアップの保護が必要です。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py Outdated
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 1 | codex | REQUEST_CHANGES

データ保持を目的にした migration で、symlink 差分の見落としと、失敗時に registry だけ先行更新される経路があります。どちらも旧 plugins/ copy の退避・削除前後の整合性を保つよう修正してください。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py Outdated
cross-review round 1 の major 指摘 4 件 (codex 2 / gemini 2) に対応。

- _dirs_differ: regular file のみ比較していたため legacy copy のみに存在する
  symlink / 空ディレクトリ / 型不一致を差分として検知できず、後続の
  shutil.rmtree で silently 削除される恐れがあった。全エントリを対象に
  型 + 内容 (file は byte, symlink は target) を比較するよう厳密化
- _unique_bak_path: 既存の <name>.bak を無条件に rmtree していたため、
  前回 migration で保全した未整理バックアップが消失する恐れがあった。
  存在時は .bak-2, .bak-3 ... と一意名に退避するよう変更
- migrate: filesystem の退避/削除が成功してから registry.add で
  plugins.yml を repos/ path に書き換えるよう順序を入れ替え。失敗時に
  registry だけ先行更新され retry も効かなくなる partial state を防止
- _cleanup_plugins_dir: .bak-N 形式も保全 .bak として検知するよう調整
- 上記挙動を網羅するテスト 6 件追加

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 1 fix 対応サマリ

commit: 41cea7f

重要度 件数
critical 0
major 2 (実 thread 4 件 / うち 2 件は codex・gemini の重複指摘)
minor 0
nit 0

対応した指摘 (全 4 thread / 独自再判定の結果すべて major で確定)

  1. symlink / 空ディレクトリ / 型不一致の差分検知漏れ (codex#55 + gemini#54, 重複)
    _dirs_differ を全エントリ走査に変更。_entry_kind で symlink/dir/file/other を分類し、type + 内容 (file は byte, symlink は target) を比較。legacy copy のみに存在する symlink・空ディレクトリ・型不一致も差分として検知し .bak 保全に倒れる。

  2. 既存 .bak の無条件削除 (gemini#219)
    _unique_bak_path() を新設。<name>.bak 存在時は .bak-2, .bak-3 … と一意名に退避。前回保全分の消失を防止。_cleanup_plugins_dir の保全検知も .bak-N 対応。

  3. registry 先行更新による partial state (codex#205)
    registry.add を filesystem の退避/削除が成功した後に移動。失敗時は registry が plugins/ path のまま残り次回 retry される。

テスト

migrator テスト 6 件追加 (symlink 追加 / 空ディレクトリ / symlink target 変更 / 型不一致 / 一致 symlink / 既存 .bak 非上書き)。全 249 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

deferred / rejected

  • deferred: 0 件
  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず、checks は reported なし (NONE)。ローカルで CI 相当 (compileall + ruff E9,F63,F7,F82 + pytest) を実行し全 pass を確認済み。

@reviewers 再レビューをお願いします。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 2 | gemini | REQUEST_CHANGES

今回の修正で、migration 時の filesystem 操作の安全性や順序の整合性が向上しました。一方で、差分検知ロジックの厳格さによる UX への影響や、状態報告の不正確な箇所が見受けられます。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py
Comment thread lib/devbase/plugin/migrator.py Outdated
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 2 | codex | REQUEST_CHANGES

移行処理で再試行不能な状態を残すパスと、通常の upstream 追加をローカル変更扱いする差分判定は修正が必要です。新規 devbase plugin migrate コマンドの CLI リファレンスも同じPRで追加してください。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py
cross-review round 2 の指摘対応。

- _dirs_differ: upstream 専用追加 (clone にのみ存在) を差分扱いしないよう
  変更。コピー側にのみ存在するエントリ・共通エントリの型/target/内容差分の
  みを preserved 判定に使い、通常の upstream 更新で不要な .bak 退避が発生
  しないようにした (codex#91 / gemini#91 重複指摘)。
- _files_equal: read_bytes() の全読み込みを 64KB チャンクのストリーム比較に
  置き換え、巨大ファイルでのメモリ枯渇リスクを排除 (gemini#105)。
- _ensure_repo_cloned: 前回 clone 失敗で残った partial dir (.git 無し /
  registry.yml 不正) を検知して削除・再 clone するよう修正。無限に
  parse 失敗を繰り返す経路を解消 (codex#132)。
- _cleanup_plugins_dir: .gitkeep でも .bak でもない想定外エントリが残る場合
  は cleaned=True と報告せず False を返すよう修正 (gemini#176)。
- docs: devbase plugin migrate の CLI リファレンスを追加 (codex review body)。

テスト 4 件追加 (upstream 専用追加は差分なし / 同サイズ内容差 / partial
clone 再 clone / cleanup の想定外エントリ保持)。全 252 件 pass。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 2 fix 対応サマリ

commit: 98cfb65

重要度 (独自再判定) 件数
critical 0
major 3
minor 1
nit 0

(実 thread 5 件 / うち line 91 は codex・gemini の重複指摘で実体 1 件)

対応した指摘

  1. 差分判定が upstream 専用追加まで preserved 扱い (gemini#91 major + codex#91 minor, 重複 → major で確定)
    _dirs_differ を「コピー削除でデータ損失が起きるか」の判定に再定義。判定材料を copy_entries - repo_entries (コピー側にのみ存在するユーザー追加) と共通エントリの型/symlink target/内容差分に限定。upstream (clone) 側にのみ追加されたファイルは差分扱いしないため、通常の upstream 更新で不要な .bak 退避・手動 reconcile が発生しなくなった。round 1 の test_extra_file_in_b_differs を新挙動 (test_upstream_only_addition_does_not_differ) に置換。

  2. partial clone の復旧不能経路 (codex#132 major)
    _ensure_repo_cloned に復旧処理を追加。.git を持たない既存 dir は rmtree して再 clone / git_clone 例外時は partial dir を削除して re-raise / parse_registry_yml 失敗時も clone_dir を削除。前回失敗で残った partial dir を再利用し続けて parse 失敗を無限ループする経路を解消。

  3. cleanup が想定外エントリ残存でも cleaned 報告 (gemini#176 major)
    _cleanup_plugins_dir.gitkeep でも .bak でもない想定外エントリが残る場合は log を出して False (uncleaned) を返すよう修正。

  4. 巨大ファイルのメモリ枯渇リスク (gemini#105 minor)
    read_bytes() の全読み込みを _files_equal() の 64KB チャンクストリーム比較に置換。サイズ比較で early short-circuit。低行数で robustness 改善のため本ラウンドで対応。

ドキュメント

  • codex review body の指摘に応じ、devbase plugin migrate の CLI リファレンス (docs/user/cli-reference.md) を追加。

テスト

migrator テスト 4 件追加 (upstream 専用追加は差分なし / 同サイズ内容差 / partial clone 再 clone / cleanup の想定外エントリ保持)。既存の upstream-only 差分テストを新挙動に更新。全 252 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

deferred / rejected

  • deferred: 0 件
  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず checks reported なし (NONE)。ローカルで CI 相当 (pytest 252 件 + ruff E9,F63,F7,F82 + compileall) を実行し全 pass を確認済み。

@reviewers 再レビューをお願いします。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 3 | gemini | REQUEST_CHANGES

今回の変更により、旧形式のプラグイン管理から repos/ ベースへの移行が自動化されます。全体的に良くテストされていますが、移行処理の正確性とパフォーマンスの観点から以下の点を修正してください。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/installer.py
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 3 | codex | REQUEST_CHANGES

移行時の保全判定と clone 健全性確認に、ユーザー変更を失うケースと復旧不能な repos/ path を登録するケースが残っています。下記を修正してください。

Comment thread lib/devbase/plugin/migrator.py
Comment thread lib/devbase/plugin/migrator.py
cross-review round 3 の指摘に対応:

- _cleanup_plugins_dir の `.bak` 判定を `'.bak' in name` から
  `<name>.bak[-N]` 末尾一致 (_is_bak_name) に修正。my.bakery 等の誤マッチを排除
- migrate ループ内の per-plugin `registry.add` を loop 末尾の単一
  `registry.add_many` に集約し plugins.yml の保存頻発を解消
  (各 plugin の fs 移動と entry 構築は同一 try 内のため失敗時の retry 性は維持)
- _ensure_repo_cloned で local_path 設定済みでも .git/registry.yml を
  検証 (_clone_is_healthy)。壊れた既存 dir は除去して再 clone
- _files_equal で S_IMODE を比較。旧コピーの実行ビット変更を差分扱いし保全
- _auto_migrate の preserved/skipped 再通知を loud な per-plugin WARNING から
  簡潔な INFO ヒント 1 行に抑制 (詳細は devbase plugin migrate 側で出力)

migrator テスト 12 件追加 (.bak 末尾判定 / clone 健全性 / 実行ビット差分 /
batched save / broken local_path 再 clone / 警告抑制)。全 264 件 pass。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 3 fix 対応サマリ

commit: 04f0d02

重要度 (独自再判定) 件数
critical 0
major 4
minor 1
nit 0

(対象 thread 5 件 / すべて妥当な指摘で修正対応)

対応した指摘

  1. .bak 判定が部分一致で誤マッチ (major / 正確性, migrator.py:211)
    _cleanup_plugins_dir'.bak' in e.name を、_unique_bak_path の命名規約 (<name>.bak / <name>.bak-N) に末尾一致する _is_bak_name (re.compile(r'\.bak(-\d+)?$')) に修正。my.bakery / notes.bak.txt 等の誤マッチを排除し、想定外エントリは leftover として uncleaned 報告。

  2. migrate ループ内の registry.add で plugins.yml 保存が頻発 (major / 効率性, migrator.py:311)
    per-plugin の registry.add を廃止し、path rewrite を pending に蓄積してループ末尾の registry.add_many(pending) の 1 回保存に集約 (新設 add_many は load/save を 1 回のみ実行)。各 plugin の fs 移動と entry 構築は同一 try 内のため、round 1 (codex#205) で対応した「fs 移動成功後に registry 更新」の retry 性は維持。

  3. repo.local_path 既存 dir の健全性未検証 (major / 復旧性, migrator.py:142)
    _ensure_repo_clonedlocal_path 設定済みでも _clone_is_healthy (.git + registry.yml 検証) を通してから返すよう変更。壊れた既存 dir は除去して再 clone。pull/update 不能なツリーを plugin path に登録する経路を排除。

  4. 通常ファイル比較が mode を無視 (major / データ保全, migrator.py:123)
    _files_equalstat.S_IMODE のパーミッションビット比較を追加。旧コピーで付け外しされた実行ビット等の mode 変更を差分として検知し .bak 保全に倒れる。

  5. _auto_migrate の警告が install/update 毎に再通知 (minor / UX, installer.py:94)
    preserved/skipped の loud な per-plugin WARNING を廃止し、devbase plugin migrate を案内する簡潔な INFO ヒント 1 行に置換。完全な per-plugin 詳細は明示コマンド側で WARNING 出力されるため情報は失われない。

テスト

migrator テスト 12 件追加 (.bak 末尾判定 3 件 / clone 健全性 3 件 / 実行ビット差分 / batched save / broken local_path 再 clone / 警告抑制 / .bak lookalike / 数字付き .bak)。全 264 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

deferred / rejected

  • deferred: 0 件
  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず checks reported なし (NONE)。ローカルで CI 相当 (pytest 264 件 + ruff E9,F63,F7,F82 + compileall) を実行し全 pass を確認済み。

@reviewers 再レビューをお願いします。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 4 | gemini | REQUEST_CHANGES

全体として移行ロジックの堅牢性が向上していますが、registry 保存時のデータ整合性やパーミッション比較の感度について改善の余地があります。

Comment thread lib/devbase/plugin/registry.py Outdated
Comment thread lib/devbase/plugin/migrator.py
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 4 | codex | REQUEST_CHANGES

既存の repos/ クローンを修復する経路で、ローカル変更を含む可能性がある作業ツリーを無条件削除しないようにしてください。

Comment thread lib/devbase/plugin/migrator.py Outdated
…PR2 round4)

- registry.add_many: 引数内で名前が重複する場合 last-wins で一意化してから
  反映し、plugins.yml に矛盾エントリが残らないようにした
- _files_equal: 全権限ビット比較を exec ビット (+x) 限定に変更し、umask /
  group 設定差による誤った .bak 退避を防止
- _ensure_repo_cloned: local_path 記録済みだが unhealthy な既存 dir でも
  .git があれば未コミット/未 push のローカル変更を失わないよう rmtree せず
  PluginError を送出し、.git 欠落 (真に壊れている) 場合のみ再 clone する

test: add_many 重複排除 / exec ビット限定 / .git 付き clone 保全の 6 件を追加

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 4 fix 対応サマリ

commit: bd2afd7

重要度 (独自再判定) 件数
critical 0
major 2
minor 1
nit 0

(対象 thread 3 件 / すべて round 3 修正への追従指摘で、いずれも妥当と判定し修正対応)

対応した指摘

  1. add_many の引数内で名前が重複すると registry が不整合 (major / 整合性, registry.py:83)
    add_many に引数 plugins 内の名前重複を排除する処理を追加。{p.name: p for p in plugins}last-wins に一意化 (dict の挿入順保持で最後の要素が残る) してから既存エントリ除外・extend を行う。同名を複数渡しても plugins.yml に矛盾エントリが残らない。

  2. _files_equal の全権限ビット比較が umask 差で誤検知 (minor / 正確性, migrator.py:81)
    権限比較を stat.S_IMODE (全権限ビット) から 実行ビット限定 (_EXEC_BITS = S_IXUSR | S_IXGRP | S_IXOTH) に変更。umask / group 設定差による read/write ビット差では誤って差分扱いされず、機能的に意味のある exec ビット (+x) の差のみ検知して .bak 保全に倒れる。

  3. _clone_is_healthy 起点で健全な .git 付き clone を無条件 rmtree (major / データ保全, migrator.py:184)
    _ensure_repo_cloned の既存 dir 削除を .git の有無で分岐.git 無し (真に壊れた pull 不能ツリー) のみ rmtree して再 clone、.git 有り (registry.yml 欠落だが未コミット/未 push のローカル変更を抱えうる) は削除せず PluginError を送出してユーザーに復元 (git checkout -- registry.yml) / 手動削除を促す。registry.yml 欠落だけで健全 clone を破壊しユーザー変更を失う経路を排除。例外時は当該プラグイン skipped + registry は plugins/ path のまま (次回 retry)。

テスト

migrator/registry テスト 6 件追加 (add_many 重複 last-wins / 既存置換 1 行 / rw 権限差は差分なし / exec 差は差分あり / .git 付き clone 保全 + skipped / .git 欠落で再 clone)。round 3 で追加した既存テストはすべて green のまま。全 270 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

deferred / rejected

  • deferred: 0 件
  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず checks reported なし (NONE)。ローカルで CI 相当 (pytest 270 件 + ruff E9,F63,F7,F82 + compileall) を実行し全 pass を確認済み。

@reviewers 再レビューをお願いします。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 5 | codex | REQUEST_CHANGES

移行処理に、失敗時の整合性崩れと既存 clone 削除のデータ保全リスクが残っています。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py Outdated
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 5 | gemini | APPROVE

PLAN04 (repos/ ベースのプラグイン管理) への移行ロジックおよび、既存の plugins/ からの自動移行 (auto-migration) の実装を確認しました。移行時の不整合を防ぐための registry.add_many での重複排除や、umask 等の影響を排除した _files_equal (exec bit 限定比較) など、エッジケースへの配慮が行き届いており、テストも非常に網羅的です。

インラインで指摘した点は、手動でファイルを壊した場合などの極めて限定的な状況に関するものであり、全体として品質は非常に高いと判断します。

また、PR外の既存コードではありますが、lib/devbase/plugin/registry.py_save メソッド (L49付近) において、tmp_path が未定義の状態で locals() を参照する可能性があります(mkstemp 自体が失敗した場合)。動作上は locals() チェックでガードされていますが、try の前に tmp_path = None と初期化しておく方がより安全で明快です。

Comment thread lib/devbase/plugin/migrator.py Outdated
Comment thread lib/devbase/plugin/migrator.py
…検知 (PR2 round5)

cross-review round 5 で指摘された 4 件に対応:

- derived clone 経路 (.git 保護): repo.local_path 未設定でも repos/<derived>
  に .git 付き既存 clone があり registry.yml だけ欠ける場合、無条件 rmtree で
  未コミット/未 push のローカル変更を失っていた。_reclaim_or_protect_existing
  を新設し local_path 経路と同じく .git 有りは削除せず PluginError で復旧案内
  する (freshly clone した分のみ破棄)。
- registry 先行永続化: 旧 plugins/ コピーの削除/.bak 退避 → add_many の順序を
  逆転し、検証済み path rewrite を破壊的 fs 操作の前に 1 回保存する二相構成へ。
  保存失敗時はコピー無傷で abort (次回 retry 可能)、phase2 の retire 失敗は
  registry が既に有効な repos/ clone を指すため lingering copy として
  _cleanup_plugins_dir が surface する (silent data loss を排除)。
- clone_dir がファイル/symlink で squat: clone_dir.is_dir() のみでは git_clone
  が失敗するため、ファイル/symlink は unlink して再 clone (git tree を持たない
  ため損失なし)。
- _entry_kind == 'other' (socket/pipe/device): 内容比較できず identical を
  証明できないため diverged 扱いとし .bak 保全に倒す。

migrator テスト 5 件追加 (derived .git 保護 / clone_dir ファイル squat /
registry 保存失敗でコピー無傷 / retire は保存後 / fifo は差分扱い)。
全 275 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 5 fix 対応サマリ

commit: f1854aa

重要度 (独自再判定) 件数
critical 0
major 3
minor 1
nit 0

(対象 thread 4 件 / すべて round 3/4 修正への追従ハードニングで、いずれも妥当と判定し修正対応)

対応した指摘

  1. derived clone 経路で .git 付き既存 clone を無条件削除 (major / データ保全, migrator.py:228)
    repo.local_path 未設定の旧登録でも repos/<derived>.git 付き clone があり registry.yml だけ欠ける場合、round 4 で守った local_path 経路と異なり無条件 rmtree で未コミット/未 push 変更を失っていた。(re-)clone 前の既存 dir 処理を新設 _reclaim_or_protect_existing() に共通化し、.git 有りは削除せず PluginError で復旧案内 (git checkout -- registry.yml / 手動削除)。parse_registry_yml 失敗時の破棄も 今回 clone した分のみ に限定 (freshly_cloned フラグ)、既存の .git 付き dir は同じ復旧エラーで保護。

  2. 旧コピー削除/.bak 退避の後に一括保存が失敗すると registry だけ旧パス (major / 整合性, migrator.py:387)
    round 3 で導入した一括保存 (add_many) が fs retire の だったため、保存失敗時にコピーは消えたのに plugins.yml は旧パスのままという非アトミックな窓があった。migrate()二相構成へ: Phase1 で検証 + 処遇決定 (破壊的 fs なし) → 検証済み path rewrite を 1 回保存 → Phase2 で旧コピーを削除/rename。保存失敗時はコピー無傷で abort (次回 retry 可能)。検証は commit 前に完了するため round 1 の retry 性も維持。Phase2 retire 失敗は registry が有効な repos/ clone を指すため lingering copy として _cleanup_plugins_dir が surface (silent data loss なし)。

  3. clone_dir.is_dir() チェックのみでファイル/symlink squat を見落とし (major / 堅牢性, migrator.py:185)
    repos/<DIRNAME> がディレクトリでなくファイル/symlink として存在すると後続 git_clone が失敗していた。_reclaim_or_protect_existing() で symlink (先に判定) / 通常ファイルは unlink() して再 clone (git tree を持たず損失なし)。

  4. _entry_kind'other' を返すと内容比較されず「差分なし」判定 (minor / 正確性, migrator.py:143)
    socket / pipe / device 等は内容比較で identical を証明できないため diverged 扱いとし .bak 保全に倒す。inspect できなかったデータの削除リスクを排除。

テスト

migrator テスト 5 件追加 (derived .git 保護 → skipped / clone_dir ファイル squat → 再 clone / registry 保存失敗でコピー無傷 / retire は保存後にのみ実行 / fifo は差分扱い)。round 3/4 で追加した既存テストはすべて green のまま。全 275 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

deferred / rejected

  • deferred: 0 件
  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず checks reported なし (NONE)。ローカルで CI 相当 (pytest 275 件 + ruff E9,F63,F7,F82 + compileall) を実行し全 pass を確認済み。

@reviewers 再レビューをお願いします。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 6 | gemini | APPROVE

PR #30 の実装を詳細に確認しました。全体として、非常に堅牢で配慮の行き届いた設計(二相コミット形式の migration、.git ディレクトリの保護、SSH/HTTPS URL の重複検知など)であり、実環境でのデータ安全性が高く評価できます。

設計レベルでの重大な懸念事項は見当たりませんでした。以下に、パフォーマンスとエッジケースの正確性に関する軽微な改善提案を数点送ります。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 6 | codex | REQUEST_CHANGES

正常な既存 repos/ clone を再利用できず migration が skip される経路が残っています。

Comment thread lib/devbase/plugin/migrator.py
Comment thread lib/devbase/plugin/migrator.py
Comment thread lib/devbase/plugin/updater.py
round 5 で derived 経路 (local_path 未設定) の `.git` 付き既存 dir を
無条件に保護 (PluginError) していたが過剰だった。`repos/<derived>` に
.git + registry.yml が両方そろった健全 clone が残っている場合は
PluginError で migration を skip せず、そのまま reuse して local_path を
永続化するよう修正。

- 健全 clone (.git + registry.yml) → reuse + local_path 永続化
- .git ありだが unhealthy (registry.yml 欠落) → 従来どおり保護 (PluginError)
- .git 無し / file・symlink squat → reclaim して再 clone

local_path 経路と derived 経路で挙動を揃え、fresh clone 後と healthy reuse
後の local_path 永続化を `_persist_repo_local_path` に共通化した。

健全 derived clone が reuse され migration が skip されないことを検証する
テスト `test_derived_path_with_healthy_clone_is_reused` を追加。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review round 6 fix 対応サマリ

commit: 71d56d2

重要度 (独自再判定) 件数
critical 0
major 1
minor 1 (deferred)
nit 0

(OPEN thread 3 件 / 内訳: codex 1 件 修正, gemini 2 件 = deferred 1 + praise 1)

対応した指摘

  1. derived clone 経路で健全 clone を再利用できず migration が skip (major / 正確性, migrator.py:245, codex)
    round 5 で derived 経路 (local_path 未設定) の .git 付き既存 dir を無条件に保護 (PluginError) していたが過剰だった。repos/<derived>.git + registry.yml が両方そろった健全 clone が残っている場合、保護で skip せず reuse すべき。_ensure_repo_cloned の derived 経路に _clone_is_healthy() 判定を先に入れ、local_path 経路と挙動を揃えた 3 分岐 に整理:

    • 健全 clone (.git + registry.yml) → reuse + local_path 永続化 (skip しない)
    • .git ありだが unhealthy (registry.yml 欠落) → 従来どおり 保護 (PluginError)
    • .git 無し / file・symlink squat → 削除して再 clone

    fresh clone 後と healthy reuse 後の local_path 永続化を _persist_repo_local_path() に共通化。

deferred (1 件)

  1. _ensure_repo_clonedadd_repository がループ毎に保存 (minor / 効率, migrator.py:424, gemini, 非ブロッキング)
    plugin path rewrite は既に add_many(pending) で 1 回保存に集約済み。残る repo 保存は リポジトリ数ぶん (多くの環境で 1〜2 件) のみ。これを batch 化すると registry に新規メソッドを追加し round 5 で硬化した二相構成 (検証→永続化→retire) に通す必要があり、複雑化と二相アトミシティ毀損のリスクに見合わない。指摘どおり現状のリポジトリ数で実用上の問題はないため deferred (Resolve せず)。将来ボトルネック化した場合に別 issue で検討。

no-action (1 件)

  1. update/install 時の _auto_migrate 挟み込みへの肯定的コメント (minor / ユーザビリティ, updater.py:193, gemini, praise)
    修正アクション不要。本設計を維持し reply の上 Resolve。

テスト

migrator テスト 1 件追加 (test_derived_path_with_healthy_clone_is_reused: 健全 derived clone が reuse され migration が skip されないこと / 既存 .git 配下の sentinel が破壊されないこと / local_path が永続化されることを検証)。round 3/4/5 で追加した既存テストはすべて green。全 276 件 pass。ruff (E9,F63,F7,F82) / compileall pass。

rejected

  • rejected: 0 件

CI

本 PR は base が release/PLAN04 のため ci.yml (pull_request: branches: [main]) は trigger されず checks reported なし (NONE)。ローカルで CI 相当 (pytest 276 件 + ruff E9,F63,F7,F82 + compileall) を実行し全 pass を確認済み。

@reviewers gemini は round 6 で APPROVE 済み、codex の major 1 件を本コミットで修正しました。再レビューをお願いします。

_ensure_repo_cloned が clone のたびに add_repository で plugins.yml を
保存していたため、多数リポジトリ移行時に保存回数が repo 数に比例していた。
clone 済み repo 行を pending_repos に貯め、path rewrite と合わせて Phase2
(破壊的 cleanup) の直前に save_migration で 1 回だけ保存するよう変更。

二相アトミシティは維持: 旧 copy 削除より前に registry が必ず flush 済みで
あること (clone を指す local_path / plugin path の両方) を不変条件として
保持。save_migration は repos + plugins を単一 load+save で upsert する。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

cross-review deferred minor 追対応 (round 7)

round 6 で deferred としていた minor 指摘 1 件 (thread PRRT_kwDOSHX-a86FY4NO) に対応しました。

指摘: migrate() は path rewrite を add_many で一括保存する一方、_ensure_repo_cloned 内の add_repository が clone ループ内で毎回 plugins.yml を保存するため、多数 repo 移行時に保存回数が repo 数に比例していた。

対応: clone 済み repo 行を pending_repos に蓄積し、path rewrite と合わせて Phase2 (破壊的 cleanup) 直前に PluginRegistry.save_migration(repos, plugins) で 1 回だけ保存。保存回数を O(1) に削減。

  • lib/devbase/plugin/registry.py: save_migration 追加 + _apply_repositories/_apply_plugins で upsert ロジックを共通化 (add_many/add_repository の挙動は不変)
  • lib/devbase/plugin/migrator.py: _persist_repo_local_path_build_persisted_repo (保存しない) にし、migrate() が単一 save_migration で flush

二相アトミシティ維持: save_migration は repos + plugins を単一 load+save で upsert し、旧 copy retire (Phase2) のに必ず flush される。「clone を指す registry (local_path / plugin path) が破壊的操作前に必ず永続化済み」の不変条件を保持。save 失敗時は copy 無傷で abort し再試行可能。

テスト: 多数 repo の fresh clone でも _save 1 回 + local_path/plugin path 永続化 + copy retire を検証、save_migration 単体テストを追加。既存のアトミシティ検証テスト (save-before-retire) は save_migration を patch するよう更新。pytest 全 280 件 green / compileall / ruff (E9,F63,F7,F82) clean。

commit: 0c7549b

@takemi-ohama takemi-ohama marked this pull request as ready for review May 28, 2026 15:22
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

ℹ️ レビューコメント履歴整理のため本 PR を一度 close し、同じブランチ feature/PLAN04-migration で新 PR を作り直します。ブランチの内容・base は変えません。

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