Skip to content

refactor(PLAN65): cmd_scale の段階を前提の検査と段階の実行へ分ける (#192) - #232

Merged
takemi-ohama merged 8 commits into
release/v3.7.0from
feature/v3.7.0-scale-stages
Sep 22, 2026
Merged

takemi-ohama merged 8 commits into
release/v3.7.0from
feature/v3.7.0-scale-stages

Conversation

@takemi-ohama

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

Copy link
Copy Markdown
Contributor

Summary

設計文書 issues/PLAN65_scale-compose-path-design.md の「実装の分け方」の 2 本目(cmd_scale の段階を分ける)。
設計の決定 8 のとおり、_check_scale_request(前提の検査)と _run_scale_pipeline([1/5]〜[5/5])の 2 つを抽出した。
後処理(bao の token・./deploy・完了のログ)は cmd_scale の本体に残し、cmd_up(_run_pre_up_checks → _run_deploy_pipeline → 後処理)と対称にした。
本番の振る舞いは変えない構造変更である。 計画は issues/PLAN65_scale-compose-path-impl2.md。

Closes #192

変更点

  1. refactor(PLAN65): new_scale の 1 未満(error 1 行)・現在以下(warning + info 2 行)の判定を _check_scale_request(new_scale, current_scale) -> bool へ出した。文言と出し分けは変えない
  2. refactor(PLAN65): [1/5]〜[5/5](write_scale・ensure_volumes・ensure_network・_build_scaled_override・default_services・docker_compose(['up', '-d', '--no-recreate', *services])・wait_for_containers_ready)を
    _run_scale_pipeline(project_name, new_scale, current_scale, config, target, dev_service_name) -> Optional[Path] へ出した。
    起動が 0 以外なら Failed to start new containers を出して None、それ以外の失敗は DevbaseError / DockerError のまま伝播する(設計の契約の表どおり)
  3. テスト: tests/commands/test_container_scale_order.py に新設の 2 関数の契約のテストを 7 件追記した。既存のテストは 1 行も書き換えていない(586a1ae の時点で git diff eaa9e5d -- tests/commands/test_container_scale_order.py の削除行 0)
  4. style(PLAN65): 40 行へ合わせるために詰めた cmd_scale の空行・折り返し・コメントを抽出前の書式へ戻した(下の D-3)
  5. 構造改善(cross-refactoring)のテスト整備で cmd_scale の現状固定テストを 3 件足した(91ba4b4: 接続先の解決の失敗と ready 待ちの失敗、f2dc70a: project.yml に scale が無いときの既定台数)。scale_harness に本物の _resolve_docker_target を返す口と DockerError の import を足した(テスト本体の行は書き換えていない)。構造改善の提案は重要度 major 以上が 0 件で、本番コードへの変更は無い

採らなかった案(設計の決定 8 が退けたもの): 段階ごとに 5 関数へ分ける / _run_deploy_pipeline との統合。
1 本目の cross-refactoring で 3 者が挙げた extract_method は参考にとどめ、境界とシグネチャは設計に従った。

テスト駆動の証跡: 2 つの関数とも契約のテストを先に書き、変更前の実装で AttributeError: module 'devbase.commands.container' has no attribute '_check_scale_request'(/ _run_scale_pipeline)で落ちることを確かめてから抽出した。

D-3: cmd_scale の行数 — 数字は満たさない(意図は満たす)

D-3 の「40 行以下」は満たさない。数字のために読みやすさを犠牲にしない判断をした。
この条件の意図(設計の決定 8・#192)は「長い関数の段階に名前を付け、cmd_up と形を揃える」ことで、行数はその目安である。
一度は空行を削り 2 つの文を約 100 文字の 1 行へ詰めて 40 行に合わせたが、検査の持ち場で抽出前の書式へ戻した(b4c839a)。
40 行へ届かせるには、詰めるか、設計の決定 8 に無い 3 つ目の関数(後処理)を出すかのどちらかが要り、どちらも採らない。

要求の文書を書き足した: issues/PLAN65_scale-compose-path.md の D-3 の下に、この判断と数え方を追記した(条件の文言そのものは変えていない)。計画 issues/PLAN65_scale-compose-path-impl2.md の D-3 は未チェックにし、結果を記した。

要求の「86 行」は、def から最後の行まで(89 行)からシグネチャ 2 行とドキュメント文字列 1 行を除いた数(空行・コメントを含む)だった。

数え方(ast) 設計の時点 c3c0d3b 起点 eaa9e5d この Pull Request
def〜最後の行(end_lineno - lineno + 1) 89 92 54
要求の「86 行」と同じ数え方(シグネチャ・ドキュメント文字列を除く) 86 89 51
参考: 空行とコメントの行を除く 67 66 40

新設: _check_scale_request 16 行、_run_scale_pipeline 48 行。cmd_up は def〜最後の行で 72 行。

D-4: 段階の対応

grep -n "/5\]" lib/devbase/commands/container.py(コメントとドキュメント文字列を除くログの行はすべて _run_scale_pipeline(1615〜1662 行)の中):

1618:    """``[1/5]``〜``[5/5]`` の本体。生成した override compose のパスを返す。
1626:    logger.info("[1/5] Updating %s: scale=%d -> %d...",
1630:    logger.info("[2/5] Ensuring volumes exist for scale=%d...", new_scale)
1633:    logger.info("[2.5/5] Ensuring network exists...")
1636:    logger.info("[3/5] Generating scaled compose file...")
1642:    logger.info("[4/5] Starting new containers (%d..%d)...", current_scale + 1, new_scale)
1655:    logger.info("[5/5] Waiting for new containers to be ready...")
段階 cmd_up 側 cmd_scale 側(この Pull Request)
前提の検査 _run_pre_up_checks _check_group_consistency と _check_scale_request
[1/5]〜[5/5] _run_deploy_pipeline([1/6]〜[5/6]) _run_scale_pipeline
後処理 本体(./deploy → bao → …) 本体(bao → ./deploy → 完了のログ。順序は変えない)

段階の番号の文字列([2.5/5] を含む)は変えていない(決定 7)。

Test plan

release/v3.7.0 を base にした Pull Request では CI が動かない(#216)。以下はすべて手元(作業ツリー、macOS)で実行した。
実環境のプロジェクトでの devbase up / scale / login は実行していない。

  • 変更前の全件: uv run --locked pytest tests/ -q(eaa9e5d + 空コミット)→ 2889 passed、exit=0
  • 契約のテストを先に書いた時点: uv run --locked pytest tests/commands/test_container_scale_order.py -q → 新しいテストだけが AttributeError で失敗(4 failed / 15 passed、3 failed / 19 passed)
  • 1 つ目の抽出の後: uv run --locked pytest tests/ -q -k scale → 58 passed、exit=0(現状固定テストを含む)
  • 2 つ目の抽出の後: uv run --locked pytest tests/ -q -k scale → 61 passed、exit=0
  • 変更後の全件: uv run --locked pytest tests/ -q(586a1ae)→ 2896 passed、exit=0(足した 7 件の分だけ増えた。失敗 0)— C-3 / C-4 / E-1
  • 検査の持ち場の全件(f2dc70a、2026-09-22 23:40): uv run --locked pytest tests/ -q → 2900 passed、exit=0(2896 + 構造改善のテスト整備で足した 4 件〈parametrize 2 件を含む〉)
  • 同じ全件を Python 3.10 で(CI の行列の下限): uv run --locked --python 3.10 pytest tests/ -q → 2900 passed、exit=0
  • 限定: uv run --locked pytest tests/commands/test_container_scale_order.py tests/commands/test_container_up_order.py -q → 44 passed、exit=0(E-1: test_container_up_order.py は書き換えていない)
  • CI と同じ静的検査: python3 -m compileall -q lib bin → exit=0 / uvx ruff check --select=E9,F63,F7,F82 lib → exit=0
  • 構造改善(cross-refactoring、--severity-threshold major): テスト整備 1 ラウンドで 2 項目を適用し各ラウンドの全件テストが exit=0。構造改善の提案は 5 件すべて minor で採用 0 件(収束)。最終ゲートの全件テスト exit=0。改修計画: refactor(PLAN65): cmd_scale の段階を前提の検査と段階の実行へ分ける (#192) #232 (comment)
  • 実装レビュー(cross-review): round 1 agy / kiro、round 2 codex / kiro がいずれも APPROVE(3 者すべてが f2dc70a を見た)。round 1 の kiro のレビュー本文の minor(本文の D-3 の表の行数)はこの本文の更新で対応。未解決のスレッド 0
  • 結合(実環境の devbase scale): 実行していない。本番の系に対して devbase up / scale を走らせない方針のため。振る舞いは現状固定テスト(コマンド列・子プロセスの環境・失敗経路)で固定している
  • D-3: ast の行数 → cmd_scale は def〜最後の行で 54 行、要求の数え方で 51 行。40 行の数字は満たさない(理由は上の D-3)。段階のログ文字列はすべて _run_scale_pipeline にある
  • D-4: grep -n "/5\]" の出力(上)

🤖 Generated with Claude Code

takemi-ohama and others added 7 commits September 22, 2026 22:38
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
new_scale の 1 未満・現在以下の判定とログを関数へ移す(設計の決定 8)。
文言と出し分けは変えない。契約のテストを足し、既存の現状固定テストは書き換えない。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…192)

cmd_up の _run_deploy_pipeline と対称の段にする(設計の決定 8)。起動が 0 以外なら
Failed to start new containers を出して None を返し、ほかの失敗は伝播する。後処理
(bao の token・./deploy・完了のログ)は cmd_scale の本体に残す。段階の番号とログの
文言は変えない(決定 7)。cmd_scale の本体は 92 行から 40 行になる(D-3)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
40 行に届かせるために削った空行・1 行へ詰めた文・短くしたコメントを、抽出前の書式へ戻した。
振る舞いは変えない。D-3 の意図(段階の命名と cmd_up との形の一致)は満たし、行数は
要求の数え方で 51 行になる。数字のために読みやすさを犠牲にしない判断を要求の文書と計画に記した。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preserve configuration and subprocess side effects on target resolution and readiness failures.

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 #232

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

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

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

R1-001 — lib/devbase/commands/container.py#cmd_scale

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

なぜ: _resolve_docker_target が DevbaseError を送出したときに 'Scale failed' を出して 1 を返す try/except 経路が固定されていない。scale_harness は _resolve_docker_target を成功させるスタブに固定しており、この失敗分岐は既存テストで観測されていない。

手順: 1. scale_harness を使い、既存の設定と override の内容を保存し、./deploy を用意する。失敗箇所以外の外部依存は成功する疑似実装にする。
2. 接続先解決のケースでは実際の解決経路を通し、docker_context.resolve_target の依存境界で DevbaseError を発生させて cmd_scale(2) を実行する。現在の戻り値 1、設定と override の不変、Compose 起動要求なしを観測して固定する。
3. 別ケースでは wait_for_containers_ready の依存境界で DockerError を発生させて cmd_scale(2) を実行する。現在の戻り値 1、更新済み scale: 2 と生成済み override が残ること、Compose 起動要求があり停止・削除要求がないことを観測して固定する。
4. deploy は実際の実行経路を subprocess 境界まで通し、両ケースとも bash による deploy 実行要求がないことを固定する。内部ヘルパーの呼び出し回数やログの完全一致は検証しない。

R1-002 — lib/devbase/commands/container.py#cmd_scale

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

なぜ: 既存の公開入口テストは project.yml に scale: 1 を指定しており、config.scale が None のとき DEFAULT_SCALE(現状 2)を現在台数として扱う分岐を固定していない。既定値は増設の可否と deploy の対象番号の両方に影響する。

手順: 1. scale_harness を使い、project.yml から scale 行だけを除いた入力を用意する。外部依存は疑似実装とし、private 関数を直接呼ばない。
2. 独立したケースで cmd_scale(2) と cmd_scale(3) を実行し、現在の戻り値、設定ファイル、外部起動要求を観測する。
3. 2 のケースでは戻り値 1、設定ファイルの不変、Compose 起動要求なしを固定する。
4. 3 のケースでは ./deploy を用意して実際の deploy 実行経路を subprocess 境界まで通し、戻り値 0、保存された scale が 3、bash 子プロセスの DEVBASE_INSTANCE_INDEX が 3 のみであることを固定する。内部関数の呼び出し順や回数、表示文言は比較しない。

見送った項目

(なし)

Fix current behavior when config.scale is omitted in project.yml.
DEFAULT_SCALE is treated as current scale, rejecting scale 2 and deploying only instance 3 on scale 3.

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 | agy | APPROVE

設計決定8に基づく抽出、受け入れ条件D-3の行数判断と文書の整合性、および追加テストの妥当性を確認しました。修正を要する問題はありません。

@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

構造変更として設計(決定 8)どおり _check_scale_request / _run_scale_pipeline を抽出し、try 境界・ログ文言・後処理の順序を維持できている。追記した 7 件のテストも green(uv run --locked pytest tests/commands/test_container_scale_order.py -q → 26 passed)で、実装の細部より段階の契約(D-4)と失敗系の現状固定を検証しており妥当。既存コメント R1-001 / R1-002 の範囲は本 PR のテストが対応済みで重複指摘なし。

修正提案(本番の振る舞いには影響しない、文書の正確性のみ):

  • [minor / 正確性] PR 本文 D-3 表の行数が実測と不一致: PR 本文の D-3 表は cmd_scale の「def〜最後の行」列に 40 を記載しているが、実測は 54(ast の end_lineno - lineno + 1。def 1665〜end 1718)。この 54 は issues/PLAN65_scale-compose-path-impl2.md:48(「92 → 54」)や issues/PLAN65_scale-compose-path.md の「同じ数え方で 51 行」とも整合する。40 は本文の記述どおり「空行・コメントを除けば」の値であり、表の列見出し(def〜最後の行)とはずれている。修正: PR 本文 D-3 表の該当セルを 54 に直すか、列見出しを「空行・コメント除外」に改める。plan 側の文書(impl2.md / plan.md)は 54 / 51 で内部整合しているため変更不要。

@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

新たな修正指摘はありません(関連テスト 156 件成功、D-3 の行数と未達の記録を確認)。

@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

構造変更として妥当。_check_scale_request(16 行)/ _run_scale_pipeline(48 行)の抽出は本番の振る舞いを変えず、DockerError が DevbaseError のサブクラスであるため cmd_scale の except DevbaseError が従来どおり捕捉する点も保たれている。D-3 の行数(def〜最後の行で 54、要求の数え方で 51)は ast で実装と一致し、40 行を満たさない判断とその根拠が impl2.md / PLAN65_scale-compose-path.md / PR 本文で一貫している。追記された 7 件のテストは 26 件すべて緑で、順序固定テストの範囲を超えた過剰な実装依存はなく、名前・docstring と assert が一致している。修正を要する指摘は無い。

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