何を見つけたか
全体テストの時間は少数のテストに集中しており、その多くは固定の待ち(1 秒のポーリング・5 秒のロック上限)と、同じ実装への重複した競合試験で占められている。
計測は develop(25da1de1)、--durations=0 で順に実行。全 5337 件・198 秒のうち、**0.5 秒以上の 43 件(0.8%)で 106 秒(53%)**を占める。
原因
対象
所要
打ち切りが猶予を待ち切る
cross-refactoring/tests/test_git_facts.py 3 件
21 秒(#883 で本体を直す)
bg-wait.sh の sleep 1 ポーリング
cross-review/tests/test_bg_wait.py 21 件(うち 1 秒前後が 10 件、4 秒・3 秒・2 秒が各 1 件)
19 秒
同じ共通実装への競合試験の重複
worktree/tests/test_registry.py の test_many_at_once_never_share_the_critical_section(worktree / workflow × 並列 6 / 12 の 4 通り、各 4 秒)と test_six_registrations_at_once_all_survive、scripts/tests/test_lock_common.py::test_six_at_once_leave_one_owner
23 秒
ロック待ちの上限 5 秒を待ち切る
worktree/tests/test_testenv.py の test_test_respects_inuse_lock_and_releases_after_failure(5.0 秒)・test_up_does_not_start_when_the_registry_lock_is_held(4.8 秒)、scripts/tests/test_lock_common.py::test_the_timeout_override_reaches_only_the_stage_state(6.0 秒)、scripts/tests/test_token_guard.py::test_lock_held_passes(3.6 秒)
19 秒
同時記録の繰り返し
development-workflow/tests/test_stage_check.py::test_records_at_once_never_skip_a_stage(8 回試行)
2.8 秒
プロセスグループの打ち切り
cross-review/tests/test_launch_cli_process_group.py
5.6 秒
競合試験の重複について。 wt_lock_acquire(plugins/ndf/scripts/lib/worktree-common.sh:2366)と wf_lock_acquire(plugins/ndf/skills/development-workflow/scripts/lib/workflow-common.sh:649)はどちらも ndf_lock_acquire を 1 行で呼ぶだけである(#293 で共通化済み)。同じ実装の臨界区間を、2 つの入口 × 2 つの並列数で 4 回、さらに共通ファイルの側で 1 回試している。入口が共通実装へ届くことは test_lock_common.py::test_the_existing_names_take_and_release_the_lock が既に見ている。
追記: CI では同じテストを 2 回回している(2026-09-23)
release/** から出した Pull Request では、Python tests が push と pull_request の 2 契機で同時に起動する。 .github/workflows/pytest.yml の on: が pull_request:(絞り込みなし)と push: branches: [main, develop, "release/**"] の両方を持つためである。
実例
push
pull_request
PR #886 (release/v10.17.0)
run 35821421926(05:11:56 起動)
run 35821431945(05:12:05 起動)
PR #881 (release/v10.17.0-dev.1)
04:56:23 起動
04:56:41 起動
1 回の実行は直近 8 回で約 5〜6 分で、他の検査(4〜48 秒)より 1 桁長い。2 本は並んで走るため待ち時間は増えないが、runner の時間は 2 倍になる。
直し方の候補: push の branches から "release/**" を外す(Pull Request の側が必ず回るため)。main / develop はマージ後の確認として残す。push 側の絞り込みがマージの可否に関わらないことは冒頭のコメントのとおり。
直さないと何が起きるか
並列実行(#882 )にしても、1 worker に遅いテストが偏ると全体の終わりがその worker で決まる。順に回す環境(cross-refactoring の --baseline-test など)では、この 100 秒がそのまま毎回かかる。
直し方の候補
待つこと自体が検出の手段になっているテストは、待ちを削らずに上限を注入する。 検出力を落とさないことを優先する(#390 の階層 3)。
bg-wait.sh のポーリング間隔を環境変数で縮められるようにする(既定の 1 秒は変えない)。または間隔を 0.2 秒にする(背景の待ちは Bash ツールの 600 秒上限の中なので、利用者側の費用はほぼ変わらない)
競合試験は共通実装に対する 1 通り(並列 12・試行 3 など)へ寄せ、入口ごとの parametrize を外す。臨界区間の試験を test_registry.py と test_lock_common.py のどちらが持つかを決める
ロックの上限 5 秒を待つテストは、上限の値を注入できる入口がある場合はそれを使う。台帳の上限を注入できないのは mkdir による排他の実装が worktree-common.sh と workflow-common.sh の 2 箇所に載る #293 の決定(上書きは控えの側だけ)なので、変えるなら決定の記録を更新する。変えない場合は、上限を待ち切ることを見るテストを 1 件に絞る
test_records_at_once_never_skip_a_stage の試行回数は、完了判定の 60 回と別に置いた値なので、4 回程度へ減らせるかを確かめる
受け入れ条件
順に回した全体テストで、0.5 秒以上のテストの合計が 40 秒以下になる
上の各テストが見ている不具合(臨界区間の重なり・上限を越えた待ち・終了コードの取りこぼし)を、変更後も失敗として検出できる(本体へ不具合を入れて落ちることを 1 度確かめる)
追記: PR #891 で行ったことと残り(2026-09-23)
PR #891 (light)はテストと継続的統合の設定だけを直した。 本番のスクリプトの変更を要する残りは、この課題に残す。
候補
状態
2. 競合試験の重複
済み。test_registry.py の 1 通り(wt_lock_acquire・並列 12・試行 3)へ寄せ、test_lock_common.py の A7 を外した
4. test_records_at_once_never_skip_a_stage の試行
済み。8 → 4 回(あわせて test_lock_held_passes を 4 → 3 回)
追記の 2 重実行
済み。push の branches から release/** を外した
1. bg-wait.sh のポーリング間隔
残り(本番のスクリプトの変更。順で 20 秒)
3. ロックの上限 5 秒を待つテスト
残り(上限の注入は #293 の決定の更新が要る。順で約 19 秒)
test_git_facts.py の猶予待ち
#883
順に回した全体テストの 0.5 秒以上の合計は 106.5 秒 → 88 秒。受け入れ条件の 40 秒以下には、残りの 3 つが要る。
関連
進行
モード: light / 作業ツリー: .worktrees/test/issue-882-884-test-speedup
何を見つけたか
全体テストの時間は少数のテストに集中しており、その多くは固定の待ち(1 秒のポーリング・5 秒のロック上限)と、同じ実装への重複した競合試験で占められている。
計測は
develop(25da1de1)、--durations=0で順に実行。全 5337 件・198 秒のうち、**0.5 秒以上の 43 件(0.8%)で 106 秒(53%)**を占める。cross-refactoring/tests/test_git_facts.py3 件bg-wait.shのsleep 1ポーリングcross-review/tests/test_bg_wait.py21 件(うち 1 秒前後が 10 件、4 秒・3 秒・2 秒が各 1 件)worktree/tests/test_registry.pyのtest_many_at_once_never_share_the_critical_section(worktree / workflow × 並列 6 / 12 の 4 通り、各 4 秒)とtest_six_registrations_at_once_all_survive、scripts/tests/test_lock_common.py::test_six_at_once_leave_one_ownerworktree/tests/test_testenv.pyのtest_test_respects_inuse_lock_and_releases_after_failure(5.0 秒)・test_up_does_not_start_when_the_registry_lock_is_held(4.8 秒)、scripts/tests/test_lock_common.py::test_the_timeout_override_reaches_only_the_stage_state(6.0 秒)、scripts/tests/test_token_guard.py::test_lock_held_passes(3.6 秒)development-workflow/tests/test_stage_check.py::test_records_at_once_never_skip_a_stage(8 回試行)cross-review/tests/test_launch_cli_process_group.py競合試験の重複について。
wt_lock_acquire(plugins/ndf/scripts/lib/worktree-common.sh:2366)とwf_lock_acquire(plugins/ndf/skills/development-workflow/scripts/lib/workflow-common.sh:649)はどちらもndf_lock_acquireを 1 行で呼ぶだけである(#293 で共通化済み)。同じ実装の臨界区間を、2 つの入口 × 2 つの並列数で 4 回、さらに共通ファイルの側で 1 回試している。入口が共通実装へ届くことはtest_lock_common.py::test_the_existing_names_take_and_release_the_lockが既に見ている。追記: CI では同じテストを 2 回回している(2026-09-23)
release/**から出した Pull Request では、Python testsが push と pull_request の 2 契機で同時に起動する。.github/workflows/pytest.ymlのon:がpull_request:(絞り込みなし)とpush: branches: [main, develop, "release/**"]の両方を持つためである。release/v10.17.0)release/v10.17.0-dev.1)1 回の実行は直近 8 回で約 5〜6 分で、他の検査(4〜48 秒)より 1 桁長い。2 本は並んで走るため待ち時間は増えないが、runner の時間は 2 倍になる。
直し方の候補: push の
branchesから"release/**"を外す(Pull Request の側が必ず回るため)。main/developはマージ後の確認として残す。push 側の絞り込みがマージの可否に関わらないことは冒頭のコメントのとおり。直さないと何が起きるか
並列実行(#882)にしても、1 worker に遅いテストが偏ると全体の終わりがその worker で決まる。順に回す環境(
cross-refactoringの--baseline-testなど)では、この 100 秒がそのまま毎回かかる。直し方の候補
待つこと自体が検出の手段になっているテストは、待ちを削らずに上限を注入する。 検出力を落とさないことを優先する(#390 の階層 3)。
bg-wait.shのポーリング間隔を環境変数で縮められるようにする(既定の 1 秒は変えない)。または間隔を 0.2 秒にする(背景の待ちは Bash ツールの 600 秒上限の中なので、利用者側の費用はほぼ変わらない)test_registry.pyとtest_lock_common.pyのどちらが持つかを決めるtest_records_at_once_never_skip_a_stageの試行回数は、完了判定の 60 回と別に置いた値なので、4 回程度へ減らせるかを確かめる受け入れ条件
追記: PR #891 で行ったことと残り(2026-09-23)
PR #891(
light)はテストと継続的統合の設定だけを直した。 本番のスクリプトの変更を要する残りは、この課題に残す。test_registry.pyの 1 通り(wt_lock_acquire・並列 12・試行 3)へ寄せ、test_lock_common.pyの A7 を外したtest_records_at_once_never_skip_a_stageの試行test_lock_held_passesを 4 → 3 回)branchesからrelease/**を外したbg-wait.shのポーリング間隔test_git_facts.pyの猶予待ち順に回した全体テストの 0.5 秒以上の合計は 106.5 秒 → 88 秒。受け入れ条件の 40 秒以下には、残りの 3 つが要る。
関連
run_with_timeoutの猶予待ち進行
モード: light / 作業ツリー:
.worktrees/test/issue-882-884-test-speedup