Repository navigation
スプリント m8c - #481
スプリント m8c#481
Conversation
世代の snar を写した仮の snar と仮の名前の差分 (incr.tar.zst.new) へ控え、 tar の 2 以上と zstd の失敗を検査してから、差分 → snar の順に改名で置く。 写し・控え・改名のどこで失敗しても仮のファイルを消して SnapshotError を返す。 差分の作成は snapshot.snar.bak を作らない。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#466 を実装する(スプリント m8c)
pyright・typescript-language-server・typescript 5・intelephense を root で /opt/language-servers へ入れ、3 つのコマンドだけを PATH に出す。base の tsc と npm のグローバル領域は変えない。版の固定と 3 つのイメージの一致を静的な検査で縛る。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#236 を実装する(スプリント m8c)
復元前バックアップの名前 pre-restore-<時刻> が既にある世代(ディレクトリ・リンク・ snapshot.yml のエントリ)と重なったら、-2・-3 … を付けた名前のディレクトリを mkdir で 排他的に作る。復元元が同じ秒の pre-restore-* でも展開の前に置き換わらず、同じ秒の 2 回の復元で控えが 2 つ残る。設計 F3 を縛っていたテストを新しい振る舞いへ書き換えた。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#473 を実装する(スプリント m8c)
#236 マージ前の手動確認(conductor、2026-10-13)端末: macOS arm64、Docker Desktop の containerd のイメージストア。
|
…iscard_incremental_temps 仮のファイルを消す 2 つの関数 (_discard_incremental_temps・_discard_rebuild_temps) について、 仮の名前のファイルだけを消すこと、消せないときは警告して続けること、控えの失敗の経路で 消せなくても元の例外が出ることを固定する。 Item-Id: I-002 Impl-Runtime: claude Impl-Model: default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nager._create_incremental 差分を積めるかの確認と 3 種類のエラーを _reject_blocked_incremental へ、 meta.yml の記録を _record_incremental へ抜き出した。_create_incremental は 拒否の確認 → 除外 → 仮のファイルの掃除 → full への切替 → 控えて置く → 記録 の並びだけになる。文言と順序は変えていない。 Item-Id: I-001 Impl-Runtime: claude Impl-Model: claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…napshotManager._discard_incremental_temps 仮のファイルを消す手順(missing_ok で消し、OSError は警告して続ける)を _discard_temps へまとめ、_discard_rebuild_temps と _discard_incremental_temps は 消す名前の組と警告の前置きを渡すだけにした。警告の文言は前と同じ。 Item-Id: I-002 Impl-Runtime: claude Impl-Model: claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
改修計画 — devbasex/devbase #481
改善項目I-001 —
|
| 兆候 | 手法 | 重要度 | 等級 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|---|
| long_method | extract_method | major | high | claude / codex / kiro | 採用 | 1 |
なぜ: 差分を積めるかの検証と3種類のエラー文、除外選択、snar の有無による full への切替、仮ファイルへの控えと設置、メタデータ更新が1メソッドにある。特に冒頭の検証は後続のファイル操作とは独立した段階であり、抽出すると書き込みの順序を読み取りやすくなる。test_exclude_generations.py は最初の拒否理由と変更されない世代を、test_incremental_failure.py は失敗時のバイト列を確認している。
手順: 1. 先頭の _incremental_block_reason の呼び出しと 3 本の if/raise を _reject_blocked_incremental(name, snap_dir) -> None として抽出する(文言と順序はそのまま)
2. 末尾の meta.yml の更新(type・files・incremental_count)を _record_incremental(snap_dir, incr_name, next_num) として抽出する
3. _create_incremental は「拒否の確認 → excludes → 仮のファイルの掃除 → snar が無ければ full → 控えて置く → 記録」の並びだけにする
4. tests/snapshot/test_incremental_failure.py・test_auto_snapshot*.py・test_exclude_generations.py・test_manager_volumes.py を打って通ることを確かめる
I-002 — lib/devbase/snapshot/manager.py#SnapshotManager._discard_incremental_temps
| 兆候 | 手法 | 重要度 | 等級 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|---|
| duplication | consolidate_duplication | minor | medium | claude / codex | 採用 | 2 |
なぜ: この PR で足した _discard_incremental_temps は _discard_rebuild_temps と同じ手順(仮の名前の組を unlink(missing_ok=True) し、OSError は警告して続ける)を写したもので、違うのは消す名前の組と警告の文言だけ。消し方の規則(警告して続ける・missing_ok)を変えるときに 2 か所を揃えて直す必要がある
手順: 1. 静的メソッド _discard_temps(snap_dir, names, what) を足し、for/try/unlink/警告の本体をそこへ移す(警告は "%sの仮のファイル %s を消せませんでした: %s" に what を渡し、今の 2 つの文言をそのまま出す)
2. _discard_rebuild_temps の本体を _discard_temps(snap_dir, (FULL_ARCHIVE_TMP, SNAR_FILE_TMP, META_FILE_TMP), '作り直し') の 1 行にする
3. _discard_incremental_temps の本体を _discard_temps(snap_dir, (INCR_ARCHIVE_TMP, SNAR_FILE_TMP), '差分の作成') の 1 行にする
4. tests/snapshot/test_rebuild_generation.py と tests/snapshot/test_incremental_failure.py を打ち、警告の文言を見るテストも含めて通ることを確かめる
I-003 — tests/snapshot/fake_tar.py#write_backup_outputs
| 兆候 | 手法 | 重要度 | 等級 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|---|
| duplication | extract_test_helper | minor | medium | codex | 取り消し | 0 |
なぜ: fake_tar.py と test_incremental_failure.py が、控えのコマンドからアーカイブ名と snar 名を同じ正規表現で抜き出してファイルを書く知識を重複して持つ。差分失敗のテストで必要なのは回ごとに違う内容であり、別のコマンド解析ではない。
手順: 1. write_backup_outputs にアーカイブと snar の内容をキーワード引数で渡せるようにし、既定値 archive/snar を保つ。
2. test_incremental_failure.py の FakeManager は archive-N/snar-N を指定して呼び、重複した2正規表現と書き込みを除く。終了コードを差し替える正規表現は残す。
3. テストの期待バイト列を変更せず、tests/snapshot を実行する。
取り消した理由: テストの期待する振る舞いが変わっています(tests/snapshot/fake_tar.py)。構造改善では期待出力を変えません。振る舞いの変更は別の変更に分けてください
I-004 — tests/snapshot/test_backup_real_tar.py#LocalTarManager
| 兆候 | 手法 | 重要度 | 等級 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|---|
| divergent_change | extract_test_helper | minor | medium | codex | 取り消し | 1 |
なぜ: test_incremental_failure.py が test_backup_real_tar.py から LocalTarManager・_has_gnu_tar・_members を import している。実の tar を動かす共通基盤とフルバックアップのテストケースが同じモジュールにあり、共通部の利用だけで他のテスト定義とモジュール直下の pytestmark 判定まで読み込む依存になっている。
手順: 1. LocalTarManager、必要な MOUNT_RE、GNU tar 判定、アーカイブ一覧取得を tests/snapshot の非 test 補助モジュールへ移す。
2. test_backup_real_tar.py と test_incremental_failure.py が直接その補助モジュールを import するようにする。テストケースと skip marker は元の各ファイルに残す。
3. シェルのコマンド、例外変換、パス置換を変えず、元のテストファイルからの再公開を残さない。
4. 両テストを実行する。GNU tar/zstd が無く skip になる場合は、実行可能な環境での確認が残ることを記録する。
レビューで確かめるテストの差分: tests/snapshot/real_tar.py, tests/snapshot/test_backup_real_tar.py, tests/snapshot/test_incremental_failure.py(機械で期待値が変わったか決まらなかった)
取り消した理由: 実装の取り込み の取り消しで 8a6f773da870 の積み直しが衝突したため外した
I-005 — tests/snapshot/test_incremental_failure.py#_snapshot
| 兆候 | 手法 | 重要度 | 等級 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|---|
| duplication | extract_test_helper | minor | medium | claude | 取り消し | 1 |
なぜ: _snapshot(世代の全ファイルのバイト列の写し)と _meta(meta.yml の読み込み)が test_rebuild_generation.py と 1 字違わず同じ。どちらも「世代が 1 バイトも変わっていない」を判定する要の道具で、判定の仕方を変えるときに揃わなくなる
手順: 1. tests/snapshot/fake_tar.py(世代を差し替えて試す道具の置き場)に generation_bytes(snap_dir) と generation_meta(snap_dir) を足す
2. test_incremental_failure.py と test_rebuild_generation.py の _snapshot・_meta を消し、import した関数へ呼び出しを置き換える(各ファイルで十数か所の改名)
3. tests/snapshot を打って通ることを確かめる
レビューで確かめるテストの差分: tests/snapshot/fake_tar.py, tests/snapshot/test_incremental_failure.py, tests/snapshot/test_rebuild_generation.py(機械で期待値が変わったか決まらなかった)
取り消した理由: 実装の取り込み の取り消しで d87161e04638 の積み直しが衝突したため外した
指標の測定
- 状態: written / 言語とツールの対応: 既定(.ndf/code-metrics.json は無い) / 所要: 8.3 秒(使えた時間 90 秒 / 上限 90 秒)
- 指標のファイル: /var/folders/qz/qxt0p_y15xv5jg4x77zmtxj80000gp/T/ndf-worktrees/devbasex--devbase/rf481/work/.cross_refactoring/code-metrics-rf481.md
| 言語 | ツール | 版 | ランナー | ファイル | 秒 | 結果 |
|---|---|---|---|---|---|---|
| python | ruff-complexipy | ruff 0.16.9 / complexipy 8.0.1 | uvx | 209 | 0.9 | 測った |
| 重複: python | symilar | pylint 4.0.9 | uvx | 209 | 7.0 | 測った(3 箇所) |
時間の上限
| 値 | 中身 |
|---|---|
| 提案の枠の終わり(監視の上限 = 残り + 余裕) | 2026-10-13T03:15:50+09:00 |
| 改修計画の枠の終わり(同上) | 2026-10-13T03:18:50+09:00 |
| テストの追加の終わり = 実装の終わり − Σ(採っていて未検証の項目の実装の見積り) | 2026-10-13T03:17:48+09:00 |
| 実装の終わり = 最終ゲートの修正の打ち切り − バッファ − Σ(採っていて未検証の項目の検証の見積り) | 2026-10-13T03:21:03+09:00 |
| 直しの試行の打ち切り | 2026-10-13T03:28:59+09:00 |
| 最終ゲートの修正の打ち切り(想定最大時間の終わり) | 2026-10-13T03:37:10+09:00 |
| 打ち切りの後の取り消し(案 A)の締め切り = 最終ゲートの修正の打ち切り + 0.20·B(工程の 1 つとして起動したとき) | 2026-10-13T03:43:10+09:00 |
| 最終ゲートの修正の 1 回目に必ず渡す長さ(秒。予備時間の final_fix) | 330 |
| 指標の測定の上限(秒。提案の枠の中から割く) | 90 |
| 着手前のテスト 1 回の上限(秒) | 407 |
| 範囲テスト 1 回の上限(秒) | 482 |
| 手元の全体テスト 1 回の上限(秒) | 407 |
| CI の待ちの上限(秒) | 336 |
| 余裕(秒。手順の上限と CLI の上限に足す) | 90 |
入力: 予算 30 分 / 全体テストの所要 w 135.5(ndf-record) / 着手前の実測 x 160.6 / CI の壁時計 c 112.0 / 範囲テストの所要 s None(—) / 戦略 local-full
無音の打ち切りは手順の監視の上限と同じ値である。項目ごとの期限は持たない(実装を止めるのは実装の終わりだけ)。
見送った提案
| 対象 | 兆候 | 理由 | 根拠(MVV) | 補足 |
|---|---|---|---|---|
lib/devbase/snapshot/manager.py#SnapshotManager._claim_pre_restore_dir |
duplication | budget | Value 2 | 見積り 1.1 分が残りに入らない |
lib/devbase/snapshot/manager.py#SnapshotManager._discard_rebuild_temps / _discard_incremental_temps |
duplication | budget | Value 2 | 見積り 1.8 分が残りに入らない |
lib/devbase/snapshot/manager.py#SnapshotManager._create_incremental |
conditional_chain | budget | Value 2 / Value 3 | 見積り 1.4 分が残りに入らない |
lib/devbase/snapshot/manager.py#SnapshotManager.auto_snapshot_target |
long_method | budget | Value 2 | 見積り 1.5 分が残りに入らない |
tests/snapshot/test_incremental_failure.py#FakeManager._run_docker_tar |
duplication | budget | Vision | 見積り 1.0 分が残りに入らない |
tests/snapshot/test_rebuild_generation.py#fixed_now |
duplication | budget | Vision | 見積り 1.0 分が残りに入らない |
tests/volume/test_compose_dev_environment.py#project |
duplication | budget | Vision / Value 3 | 見積り 1.0 分が残りに入らない |
lib/devbase/snapshot/manager.py#SnapshotManager._extract_archive |
data_clump | budget | Value 2 / Value 3 | 見積り 1.3 分が残りに入らない |
lib/devbase/snapshot/manager.py#SnapshotManager._extract_archive / _apply_incrementals / restore |
data_clump | budget | Value 2 | 見積り 1.3 分が残りに入らない |
tests/containers/test_base_shellcheck_ci.py#_shellcheck_commands |
deep_nesting | budget | Vision | 見積り 1.5 分が残りに入らない |
tests/containers/test_base_dockerfile_fonts.py#_run_blocks |
duplication | budget | Value 1 | 見積り 1.7 分が残りに入らない |
tests/containers/test_base_image_font_matching.py#_docker_unavailable |
duplication | budget | Vision | 見積り 1.7 分が残りに入らない |
tests/cli/zsh_completion_harness.py#run_completions |
long_method | budget | Mission / Vision | 見積り 2.2 分が残りに入らない |
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | codex | REQUEST_CHANGES
差分の設置中断後に古い snar から再開する経路について、データ欠落を防ぐ修正が1件必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | kiro | APPROVE
差分の作成の失敗時の原子性(#466)・復元前バックアップの通し番号化(#473)・言語サーバの派生イメージへの追加(#236)を確認した。実装(_create_incremental / _install_incremental / _reject_blocked_incremental / _record_incremental / _discard_temps / _backup_before_restore / _claim_pre_restore_dir)は仕様(snapshot-series.md / snapshot-exclude.md)と一致し、改名の順(差分→snar)が『snar が進み差分が無い』状態を防ぐ不変条件を満たす。_claim_pre_restore_dir は排他的 mkdir で同秒の競合を避けている。テストは FixedDatetime で決定的、失敗系・境界を網羅。CHANGELOG は利用者影響と移行手順を記載。critical / major の基準(利用者経路の破壊・秘密/データ/不可逆・設計文書の実装齟齬)に当たる指摘は見つからなかった。
差分を置いた後に snapshot.snar の改名で止まると、次の差分は古い snar から 控えて前の差分の分を重ねて含んでいた。ディレクトリを A→B(止まった差分)、 B→C(次の差分)と改名すると次の差分が A→C を持ち、A の無い復元で失敗して C の中身が欠けていた。 置く間は snar を組の名前 snapshot.snar.pending-NNN へ移し、次の差分の作成が 先に組を揃える(差分が置かれていれば snar を進め、無ければ組の snar を消す。 どちらもできなければ止める)。作り直しは組の snar も消す。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔧 /ndf:fix サマリ | round 1 | commit f509cf5対応件数: critical=0 / major=1 / minor=0(合計 1 件) |
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | claude | APPROVE
round 1 の major(差分を置いた後に snar の改名で止まると、古い snar から重ねて控える)への f509cf5 の修正を確かめた。組の snar(snapshot.snar.pending-NNN)を経由して置くことで、どの時点で止まっても次の差分の作成が先に組を揃えるか、組の snar を消す。揃えられなければ止める。作り直し(_install_rebuilt_full)も組の snar を消す。仕様(snapshot-series.md)と実装は一致している。critical / major に当たる新しい指摘は無い。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | kiro | APPROVE
round 1 で指摘された中断後の差分の二重適用(takemi-ohama / codex)への修正 f509cf5 を検証した。pending-NNN 方式と _complete_pending_incremental により、差分→snar の改名の各中断点で (a) 打つ前のまま・(b) 差分あり snar は旧・組の pending が残る・(c) 進んだ、のいずれかに収束し、次の差分の作成が先に組を揃えてから控えるため『古い snar から二重に重ねて復元で欠落』は起きない。restore / _apply_incrementals は meta.yml['files'] ではなく incr-*.tar.zst の glob で適用し、incremental_count は glob 由来の next_num で自己修復するため、中断で meta.yml が差分を数え損ねても復元のデータ欠落・本数のずれにはつながらない。INCR_ARCHIVE_TMP / SNAR_PENDING_GLOB は INCR_ARCHIVE_GLOB・is_archive_file・_has_previous_files に当たらず、作り直し経路(_install_rebuilt_full)も pending を掃除する。仕様(snapshot-series.md の『差分の作成』『エラー処理』『運用』)は実装と一致し、設計文書と実装の食い違いは無い。#236 の Dockerfile と #473 の復元前バックアップは round 1 で確認済みで新たな変更は無い。指摘の基準(利用者経路の破壊・秘密/データ/不可逆・設計文書の実装齟齬)に当たる critical / major は見つからなかった。
🔧 /ndf:fix サマリ | commit (なし)対応件数: critical=0 / major=0 / minor=0(合計 0 件) |
リリース後テスト(スプリント m8c、2026-10-13)対象の版: main 打ち方の共通事項:
合否: 32 の受け入れ条件のすべてが合格(#466 12、#473 7、#236 13。#236 の 8〜10 は (b) を選んだため当たらない)。保留 2 行(#236 の amd64 での 2〜5、利用者のコンテナの中での公式 LSP プラグインの診断) 片付け: 作業用のボリューム |
振り返り(2026-10-13)対象: #466 / #473 / #236 / PR #481(確定仕様化 #482) スプリント m8c(マイルストーン 04、mode standard、pace normal)。#466 は差分の作成が失敗・中断したときに 何が起きたか
context window の大きさ
目印の付いた行は無い。プランで流した担当( 用語集の変化
承認ゲートの差し戻し関門 1: 0 件・関門 2: 0 件( MVV を見直すか版 1。覆し 0 件(閾値 3)・判定できない 0 件(閾値 5)・流出不具合 0 件(閾値 3)。兆候が無いため問わない(2026-10-13 の利用者の指示)。 次に変えること
途中で起票した課題無し( |
スプリント m8c の課題を main へ取り込む。
利用者向けの変化
snapshot.snarと差分の列は作成を始める前の状態のまま残ります。(#466 を実装する(スプリント m8c) #478)backup_commandに渡します。除外の仕様にもこのことを書いています。(#466 を実装する(スプリント m8c) #478)移行の手順
課題と設計
コミット
変更の統計
テスト(supervise.py の run のステップ)
集めた実装の PR
閉じる課題
Closes #236
Closes #466
Closes #473
手動確認
devbase-snapshot:latestのdocker run --rmと作業用のディレクトリで、実装の控えのコマンドに失敗を起こして結果を PR に残す(担当が打てる。利用者のボリュームとdevbase upは使わない)devbase-base:latestの上に建て、受け入れ条件 2〜5・7・8・9・12 をdocker run --rmで確かめる。devbase upは打たない(作業用のプロジェクトでも実のボリュームが付く。AGENTS.md の落とし穴)。base は建てない。非対話の担当はubuntu・composer:latestなどの取得がキーチェーンで止まり得るため、建てられなかったら「判断が要る」で返す(AGENTS.md の落とし穴).pyを編集し、公式 LSP プラグインの診断が届くことを見る(リリース後テスト)モード: standard / 通した工程: 実装 → Pull Request
🤖 Generated with Claude Code
レビューの修正で設計と違えたところ(conductor 追記)
snapshot.snar.pending-NNNへ移し、次の差分の作成が先に組を揃える(差分が置かれていれば snar を進め、無ければ組の snar を消す。どちらもできなければ止める)形に変えた。作り直しは組の snar も消す。snapshot-series.mdも同じコミットで直した