Skip to content

refactor(PLAN66): スナップショットの名前の検証を utils/names の述語へ寄せる (#203) - #235

Merged
takemi-ohama merged 6 commits into
release/v3.7.0from
feature/v3.7.0-snapshot-name
Sep 22, 2026
Merged

takemi-ohama merged 6 commits into
release/v3.7.0from
feature/v3.7.0-snapshot-name

Conversation

@takemi-ohama

@takemi-ohama takemi-ohama commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

設計文書 issues/PLAN66_project-name-validation-design.md の「実装の分け方」の 2 本目(スナップショットの寄せ。F3・決定 5・6)。
計画は issues/PLAN66_project-name-validation-impl2.md。

参照:

Closes #203

変更点

  1. docs(PLAN66): 実装 2 本目の計画を置いた
  2. refactor(PLAN66): lib/devbase/snapshot/manager.py の _VALID_NAME_RE を消し、_validate_name が utils/names.is_single_segment_name を呼ぶ(決定 5)
    • 例外の型(SnapshotError)・文言(「英数字・ハイフン・アンダースコア・ドットのみ使用可能、先頭は英数字」)・_safe_snap_dir の封じ込め(resolve() + startswith)は変えていない
    • not name のガードは述語が空を弾くため外した(呼び出し元はいずれも文字列を渡す。create は None を時刻の名前へ置き換えてから呼ぶ)
    • 受け付ける名前が狭まるのは 末尾の改行を持つ名前(abc\n)だけ。文字集合は同じ([A-Za-z0-9][A-Za-z0-9._-]*)で、re.match + $ が fullmatch に変わった差だけ
  3. tests/snapshot/test_manager_name.py(新設): _validate_name の受理 4 件(ok-name・a.b・A_b・0abc)と拒否 8 件(_foo・.x・-x・空・..・café・a/b・abc\n)を固定(決定 6。受け入れ条件 11 の列挙と同じ集合)。拒否は文言 無効なスナップショット名 も見る
  4. docs(PLAN66): 確定仕様 docs/specifications/cli-argument-resolution.md の「運用」の 2 つ目の箇条書きだけを書き換え(寄せていないのは env/bundle.py と env/secret_store.py の 2 つ、スナップショットは述語を共有、その理由と固定テストの在り処)。CHANGELOG.md の [Unreleased] に ### Changed を足した(1 本目の Added の行は変えていない)
  5. test(構造改善で足した 2 件。9c65ae1 / e32c639): 公開の入口(create / delete)から不正な名前が拒否され、subprocess.run / shutil.rmtree へ進まないことを固定した(tests/snapshot/test_manager_name.py)

採らなかった案(文言を NAME_FORM_HINT へ寄せる・逆向きに寄せる)は実装していない。

空の名前の検査を外したことの確認(決定 5 との整合)

決定 5 は「例外の型・文言・_safe_snap_dir の封じ込めを変えない」と決めている。外した not name のガードが利用者向けの文言を変えていないことを確かめた。

  • 変更前(1111926:lib/devbase/snapshot/manager.py)の _validate_name は if not name or not _VALID_NAME_RE.match(name): という 1 つの if で、空の名前も正規表現に外れた名前も 同じ raise・同じ文言(無効なスナップショット名: '<名前>' (英数字・…))を出していた。空の名前だけの別の文言は存在しない

  • 変更後も空の名前は述語(fullmatch)が弾き、同じ文言が出る。隔離した DEVBASE_ROOT / HOME での実行:

    $ ./bin/devbase snapshot create --name ''     # exit=1
    Error: スナップショット操作に失敗: 無効なスナップショット名: '' (英数字・ハイフン・アンダースコア・ドットのみ使用可能、先頭は英数字)
  • したがって削除を維持する(文言が同じであることを確かめたため)。戻すと同じ文言を出す分岐が 2 つになり、述語と重なる

受け付ける名前が狭まるのが末尾の改行 1 ケースだけであることも、隔離した環境で確かめた(下の Test plan)。

Test plan

release/v3.7.0 を base にした PR では CI が動かないため(#216)、手元で実行した。CI が行う検査(compileall / ruff)も手元で実行した。

  • 受け入れ条件 10・11: テストを先に書き、変更前は abc\n の 1 件だけが落ちることを確かめた
    • uv run --locked pytest tests/snapshot/test_manager_name.py -q → 1 failed, 11 passed(FAILED ...test_validate_name_rejects[abc\n] / DID NOT RAISE)
    • 変更後 uv run --locked pytest tests/snapshot/ -q → exit=0、87 passed in 5.00s(2026-09-23 02:49)
  • 受け入れ条件 12: grep -n "_VALID_NAME_RE" lib/devbase/snapshot/manager.py → 0 件(exit=1)
  • 受け入れ条件 17: uv run --locked pytest tests/ -q → exit=0、2936 passed in 143.10s(2026-09-23 01:30。head e32c639)
  • CI 相当の検査: uv run --locked python -m compileall -q lib bin → exit=0 / uvx ruff check --select=E9,F63,F7,F82 lib → exit=0(All checks passed!)
  • 受け入れ条件 13 の 2 つ目・3 つ目・14 の Changed: 差分で確認(docs/specifications/cli-argument-resolution.md・CHANGELOG.md)
  • 端から端まで(隔離した DEVBASE_ROOT と HOME。本番の系には触れていない)
    • ./bin/devbase snapshot create --name '_foo' → exit=1、無効なスナップショット名: '_foo' (…)
    • ./bin/devbase snapshot create --name $'abc\n' → exit=1、同じ文言(この 1 ケースだけが新たに弾かれる)
    • ./bin/devbase snapshot delete _foo → exit=1、同じ文言
    • ./bin/devbase snapshot create --name '' → exit=1、同じ文言(上の節)
    • 隔離した DEVBASE_ROOT の下にはディレクトリが作られない(find で確認)
  • 実環境のスナップショットに対する作成・復元・削除は実行していない

検査の工程

  • 構造改善(/ndf:cross-refactoring。範囲は snapshot/manager.py と tests/snapshot/test_manager_name.py、--severity-threshold major): テスト整備で 2 件を採用・適用(上の変更点 5)。構造改善の提案は 5 件とも PR の差分(_validate_name とそのテスト)の外(restore の分割・アーカイブ名の定数化など)を指したため採らなかった。最終ゲートは passed
  • 実装レビュー(/ndf:cross-review): 収束(approved)。round 1 = agy + kiro、round 2 = codex + kiro。3 者とも同じ head(e32c639)を見て指摘 0 件。未解決スレッド 0 件(GitHub 側で確認)

🤖 Generated with Claude Code

takemi-ohama and others added 5 commits September 23, 2026 00:49
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_VALID_NAME_RE を消し、_validate_name が is_single_segment_name を呼ぶ(決定 5)。
例外の型・文言・_safe_snap_dir の封じ込めは変えない。狭まるのは末尾の改行を持つ名前だけ。
tests/snapshot/test_manager_name.py で受理 4 件・拒否 8 件を固定する(決定 6)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fix current public-entry behavior before directory creation or Docker execution.

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

takemi-ohama commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

改修計画 — devbasex/devbase #235

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

  • 対象範囲: lib/devbase/snapshot/manager.py, tests/snapshot/test_manager_name.py
  • 着手前のテスト: uv run --locked pytest tests/snapshot -q

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

R1-001 — lib/devbase/snapshot/manager.py#SnapshotManager.create

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

なぜ: create は最初に _safe_snap_dir(name) -> _validate_name を通すが、不正な名前が公開入口 create から拒否されることは固定されていない。既存テストは private な _validate_name を直接呼ぶだけで、公開境界での拒否 (mkdir/Docker へ進む前に SnapshotError) を固定していない。

手順: 1. SnapshotManager(tmp_path) を作る
2. is_single_segment_name が弾く名前 (例 '../evil') を create(name=...) に渡す
3. SnapshotError が上がり、backups/ 配下にディレクトリが作られていない (Docker へ進んでいない) ことを比較する

R1-002 — lib/devbase/snapshot/manager.py#SnapshotManager.delete

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

なぜ: delete も先頭で _safe_snap_dir(name) -> _validate_name を通すが、不正な名前を公開入口 delete から拒否することは固定されていない。存在しない名前の 'not found' 経路とは別に、名前が不正な段階で shutil.rmtree へ進まず止まることを公開境界で固定できる。

手順: 1. SnapshotManager(tmp_path) を作る
2. is_single_segment_name が弾く名前 (例 '_foo') を delete(name=...) に渡す
3. SnapshotError が上がることを比較する (rmtree へ進まないので backups/ 配下は変化しない)

見送った項目

(なし)

Fix current public-entry behavior before directory deletion or rmtree execution.

Item-Id: R1-002
Round: 1
Impl-Runtime: agy
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 | APPROVE

_VALID_NAME_RE を utils/names.is_single_segment_name へ寄せる変更は妥当。文字集合が同一で re.match→fullmatch により末尾改行のみ拒否が狭まる点は CHANGELOG と仕様書に正確に反映され、受理4件・拒否8件と公開入口(create/delete)の副作用抑止テストで固定されている(uv run --locked pytest tests/snapshot/test_manager_name.py → 14 passed を確認)。path-traversal 防止はむしろ強化される方向で、正確性・セキュリティ・保守性の観点で修正を要する箇所は見当たらない。

@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

スナップショット名の検証共通化(PLAN66)、受理・拒否境界および副作用抑止のテスト、仕様書・CHANGELOG の整合性を確認しました。修正を要する指摘はありません。

@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 2 | codex | APPROVE

修正を要する新規指摘はありません。関連テスト 107 件が成功しました。

@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 2 | kiro | APPROVE

スナップショット名検証を utils/names.is_single_segment_name へ寄せる焦点の合った変更で、修正提案なし。旧 ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ (.match) → 新 fullmatch([A-Za-z0-9][A-Za-z0-9._-]*) の差は末尾改行の拒否のみで、文字集合は同一。受理/拒否と公開入口の副作用非到達がテストで固定され (tests/snapshot/test_manager_name.py)、re import は他正規表現で継続利用。CHANGELOG / 仕様の記述も変更範囲と一致。

@takemi-ohama
takemi-ohama marked this pull request as ready for review September 22, 2026 17:51
@takemi-ohama
takemi-ohama merged commit 9879093 into release/v3.7.0 Sep 22, 2026
@takemi-ohama
takemi-ohama deleted the feature/v3.7.0-snapshot-name branch September 22, 2026 20:38
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