Skip to content

検査のプランの構造改善が既定の予算で下限に届かず、33 行の差分でも refactor が落ちて abort へ進む #2047

Open

Description

@takemi-ohama

何が起きたか

2026-10-17、PR #2043(#1873。11 ファイル・33 行)の検査のプラン(supervise.py new check --pr 2043)で、構造改善(refactor)のステップが終了コード 1 で落ち、プランは abort へ進んだ。

  • 出力: 「--budget-minutes を 39 以上にして 5 分以内に init を打ち直すと、着手前のテストを走らせ直さずに続けます(下限では 1 件だけが入ります)」
  • プランの refactor の cmd は --budget-minutes を持たず(.ndf/supervise.json に refactor_budget_minutes も無い)、timeout は 1980 秒だった
  • new check --refactor-budget-minutes 40 で書き直し、run --from refactor で再開した

問題

  • new check が書く既定の予算が、cross-refactoring の下限(着手前のテストの時間から求める値)を下回り、小さな差分でも必ず落ちる。下限は init が知っているのに、プランを書く時点で予算へ反映されていない
  • 落ちたときは abort へ進み、検査の記録も「落ちた」になる。案内の「5 分以内に打ち直す」は、無人のキューでは間に合わない

依頼(案)

  • new check / new sprint の検査のプランが、予算を下限以上にして書く(予算を省いたときは下限を使う)か、refactor の下限不足を abort ではなく予算を上げての打ち直しへつなぐ

関連

由来

#2008 の進行中、#1873 の検査で見つけた

再発(2026-10-17)

再発(スプリント a2 の検査 PR #2053): 既定の予算 30 分に対し下限 38 分。#2008 (comment)

再発(2026-10-25、#2089 のスプリント s2089)

  • スプリント PR スプリント s2089 #2103 の検査のプラン(6-check.json。雛形のまま --budget-minutes 30 相当)で refactor が 2 回落ちて abort へ進んだ(~/.local/state/ndf/sv/s2089/6-check-state/progress.jsonl)
    • 1 回目(08:52 JST): 着手前のテストが 180 秒(予算 30 分の 10%)で打ち切られた。履歴が無い範囲の初回で、次の上限は 540 秒の見込みと記録された
    • 2 回目(08:58 JST): 着手前のテストは約 335 秒で終わったが、「--budget-minutes を 43 以上に」で落ちた
    • conductor が --budget-minutes 60 に書き直して 3 回目(09:45 JST)で完了した(46 分・採用 14 件)
  • 着手前のテストの上限(予算の 10%)と予算の下限はどちらも予算から決まるため、既定の 30 分のままでは、全体テストが 3 分を超えるこのリポジトリで 2 段で止まる

再発(2026-10-29、#2087 のスプリント m2087)

  • スプリント PR スプリント m2087 #2152 の検査のプラン(/work/ai-plugins/.ndf/tmp/sprint-m2087b/4-check.json、既定の予算 30 分)で refactor が refactor.py init の終了コード 4 で落ち、abort へ進んだ(2 回目の実行。記録は /work/ai-plugins/.ndf/tmp/sprint-m2087b/4-check-state.run2)。着手前のテストは約 8 分(3 回目の実行で 495.5 秒)
  • --refactor-budget-minutes 90 でプランを書き直し、done-4c.json で流し直して通った
  • 振り返り: PR Release: ndf v10.24.0 #2160 のコメント

発生の記録(2026-10-31、開発ループ l20261031 の周回 2)

  • 検査のプラン(スプリント PR スプリント l20261031-2 #2250 、132 ファイル)の refactor が、着手前のテストの打ち切りで refactor.py init exit 4 → abort へ進んだ
  • 上限は 180 秒 → 540 秒 → 1,620 秒と、打ち切りのたびに 3 倍になった。.ndf/project.json の test_duration は 1,518 秒(CI の 4 ステップの合計)を持っていたが、初回の上限はこれを読んでいない
  • 手で測ると factory の vitest は 12 秒で、残りは pytest の全体(plugins/ndf/scripts/tests ほか)だった。2 回分の打ち切りで約 13 分を失った

発生の記録(2026-10-31、スプリント m26-1)

  • 検査のプラン(スプリント PR スプリント m26-1 #2268 )の refactor で、着手前のテストが上限 180 秒 → 540 秒で 2 回打ち切られた。3 回目(上限 1,620 秒)でテストは 652 秒で通ったが、予算 30 分に収まらず init が止まった
  • conductor が 6-check.json の refactor に --budget-minutes 75・timeout 4,800 秒を足して流し直して通った。同じ時の全体テスト(6 並列)は 620 秒
  • 初回の上限が実測(.ndf/project.json の test_duration、直前の全体テスト)を読まず、打ち切った回の値から 3 倍ずつしか上がらない、という前回と同じ形。5 回目の発生

追加の事例(2026-10-31)

スプリントの PR #2283 で、変更前のテストに約 9 分かかり、リファクタリングの工程の時間の枠を使い切った。提案は 1 件も入らなかった(重複として閉じた #2287 から写した)。全体テストを軽くする #2291 と合わせて直す。

依頼(原文)

  • new check が書く既定の予算が、cross-refactoring の下限(着手前のテストの時間から求める値)を下回り、小さな差分でも必ず落ちる。下限は init が知っているのに、プランを書く時点で予算へ反映されていない
  • 落ちたときは abort へ進み、検査の記録も「落ちた」になる。案内の「5 分以内に打ち直す」は、無人のキューでは間に合わない

(書き手: 起票者、日付: 2026-10-17。本文の「問題」の節)

  • new check / new sprint の検査のプランが、予算を下限以上にして書く(予算を省いたときは下限を使う)か、refactor の下限不足を abort ではなく予算を上げての打ち直しへつなぐ

(書き手: 起票者、日付: 2026-10-17。本文の「依頼(案)」の節)

  • 初回の上限が実測(.ndf/project.json の test_duration、直前の全体テスト)を読まず、打ち切った回の値から 3 倍ずつしか上がらない、という前回と同じ形。5 回目の発生

(書き手: 起票者、日付: 2026-10-31。本文の「発生の記録(2026-10-31、スプリント m26-1)」の節)

全体テストを軽くする #2291 と合わせて直す。

(書き手: 起票者、日付: 2026-10-31。本文の「追加の事例」の節。重複として閉じた #2287 から写した文)

利用者の指示の原文はこの課題に無い。マイルストーン「27 テストの軽量化とリファクタリングの時間、文書の読みやすさ」に入っていることだけが、利用者がこの課題を扱うと決めた記録である。

起票者の推定

# 条件 推した出所 この要求での扱い
S1 想定最大時間を省いたときは、着手前のテストの時間から求めた下限を想定最大時間として使う 本文の「依頼(案)」の前半 受け入れ条件 3・4
S2 下限に届かないときは abort ではなく、想定最大時間を上げて打ち直す 本文の「依頼(案)」の後半 採らない。想定最大時間を明示したときは、その想定最大時間を利用者の決めた上限として扱い、黙って上げない(Value 10)。代わりに abort せずにリファクタリングを飛ばしてコードレビューへ進む(受け入れ条件 5)
S3 着手前のテストの初回の上限は、.ndf/project.json の test_duration か直前の全体テストの実測を読んで決める 本文の「発生の記録(2026-10-31、開発ループ l20261031 の周回 2)」と「スプリント m26-1」の節 受け入れ条件 1・2
S4 全体テストを軽くする #2291 と合わせて直す 本文の「追加の事例」の節 前提 4・含まない。同じマイルストーンで進めるが、全体テストの速さはこの課題の受け入れ条件にしない

目的

  • 検査のプラン(supervise.py new check / new sprint / new close が書くプラン)のリファクタリングのステップが、想定最大時間の既定値(30 分)と着手前のテストの上限の決め方のせいで落ち、検査が abort へ進むことを無くす
  • 無人のキューで、人が想定最大時間を書き直して打ち直さなくても、検査がコードレビューまで進む状態にする

前提

  • 前提 1: 「想定最大時間を明示した」とは、--refactor-budget-minutes の引数か .ndf/supervise.json の refactor_budget_minutes で想定最大時間を書いたことを指す。どちらも無いときを「想定最大時間を省いた」と呼ぶ
  • 前提 2: 想定最大時間を省いたときに想定最大時間を下限まで上げることに、別の上限は設けない。費用の上限を持ちたい利用者は refactor_budget_minutes を書く(前提 1 の明示になり、受け入れ条件 5 の扱いになる)
  • 前提 3: 初回の上限に使う既知の所要は、手元の全体テストの直近の実測(whole_runs の記録)を先に採り、無ければ .ndf/project.json の test_duration を採る。どちらも無いときは今の値(想定最大時間の 10%)のままにする
  • 前提 4: 全体テストそのものを速くする作業は 全体テストが重すぎて工程の時間の枠に収まらない。手元の全体テストを 3 分以内にする #2291 が持つ。この課題は、テストが遅いままでも検査が止まらないことだけを受け入れ条件にする
  • 前提 5: この変更より前に書き出したプランのファイル(--budget-minutes を持たない refactor の cmd と、既定の想定最大時間から求めた timeout)は書き直さない。新しい振る舞いは、この変更の後に書いたプランから効く

対象範囲

含む:

  • 着手前のテストの初回の上限の決め方(既知の所要を読む)
  • 想定最大時間を省いたときの、リファクタリングの想定最大時間の決め方(下限を下回らない)
  • 想定最大時間を省いたときの、プランの refactor のステップの timeout(リファクタリングの実行中に切れない)
  • リファクタリングが時間の枠に収まらないときの、プランの進み方(abort ではなくコードレビューへ進む)と検査の記録
  • new check / new sprint / new close の 3 つの雛形(どれも同じリファクタリングのステップを書く)

含まない:

ドメインイベント

# イベント 引き金 失敗したとき 順序の前提
E1 検査のプランを書いた(refactor のステップの想定最大時間と timeout が決まった) supervise.py new check / new sprint / new close 引数・宣言の誤りで書かずに止まる(今のまま) —
E2 リファクタリングのステップを始めた プランの実行が assess の後に refactor へ進んだ — E1
E3 着手前のテストの上限を決めた refactor.py init 既知の所要が読めなければ想定最大時間の 10% を使う(前提 3) E2
E4 着手前のテストが終わった / 打ち切られた E3 の上限の中でテストが終わる / 上限に達する 打ち切り → E7(飛ばした) E3
E5 リファクタリングの想定最大時間が決まった E4 の実測 x から下限を求める — E4
E6 手順の枠が想定最大時間に収まった / 収まらなかった E5 収まらない(想定最大時間を明示し、下限に届かない) → E7(飛ばした) E5
E7 リファクタリングを終えた / 時間の枠に収まらず飛ばした E6 の後の改修、または E4・E6 の失敗の経路 ステップの timeout で切られる経路は起きてはいけない(受け入れ条件 4) E6 または E4
E8 検査がコードレビューへ進み、検査の記録にリファクタリングの結果を残した E7 — E7

用語

用語 意味
想定最大時間 用語集のとおり(--budget-minutes)。プランでは --refactor-budget-minutes か .ndf/supervise.json の refactor_budget_minutes で渡す。本文の「依頼(原文)」の「予算」はこの語を指す
想定最大時間の下限 着手前のテストの実測 x から求める、手順の枠が収まる想定最大時間の最小値(timeline.required_budget_minutes)。init が「--budget-minutes を N 以上に」と案内する N
着手前のテスト refactor.py init が改修の前に走らせるテスト(範囲テストか全体テスト)
着手前のテストの初回の上限 同じ範囲の打ち切りの記録が配分の履歴に無いときの、着手前のテストの上限(秒)

受け入れ条件

  • 1. 手元の全体テストの直近の実測 m(whole_runs の記録)があり、同じ範囲の配分の履歴が無いとき、着手前のテストの上限は 3·m 秒以上になる(例: m = 620 秒なら 1,860 秒以上。今は想定最大時間 30 分の 10% の 180 秒)
  • 2. m が無く .ndf/project.json の test_duration の所要 w があるとき、着手前のテストの上限は 3·w 秒以上になる。m も w も無いときは今と同じ想定最大時間の 10% になる
  • 3. 想定最大時間を省いたとき、着手前のテストの実測 x から求めた下限が既定の想定最大時間(30 分)を超えても、refactor.py init は「--budget-minutes を N 以上に」で止まらず(終了コード 4 を返さず)、想定最大時間を下限以上にして続ける。使った想定最大時間と、それを下限から決めたことが状態ファイルと標準出力に残る(例: x = 495 秒で、今は止まる入力が通る)
  • 4. 想定最大時間を省いて書いたプランの refactor のステップは、受け入れ条件 3 で上げた想定最大時間でリファクタリングが終わる前に、ステップの timeout で切られない
  • 5. 想定最大時間を明示し、下限がその想定最大時間を超えるとき、および着手前のテストが上限で打ち切られたとき、プランは abort へ進まずコードレビュー(review)へ進む。検査の記録とフェーズレポートには、リファクタリングを「時間の枠に収まらず飛ばした」ことと、理由(要る下限の分、または打ち切った上限の秒)が残り、「落ちた」とは書かれない
  • 6. 想定最大時間を明示し、下限がその想定最大時間以下のときの振る舞い(想定最大時間・上限・枠の配分)は今と変わらない
  • 7. 起動の失敗・最終ゲートの失敗など、時間の枠と関係のない理由でリファクタリングが落ちたときは、今と同じく abort へ進む(受け入れ条件 5 の経路に混ざらない)
  • 8. new check / new sprint / new close の 3 つの雛形のどれで書いたプランでも、受け入れ条件 3〜5 が同じに効く

非機能の条件

大項目 条件
性能・拡張性 受け入れ条件 1・2 の上限は、テストが固まったときに待つ時間の最大を延ばす。延びる分は既知の所要の 3 倍までにとどまる
運用・保守性 想定最大時間を下限から決めたこと(受け入れ条件 3)と、リファクタリングを飛ばしたこと(受け入れ条件 5)は、人が後から記録だけで経緯を追える
移行性 書き出し済みのプランは書き直さない(前提 5)。古いプランで止まったときは、今と同じく --refactor-budget-minutes を付けて書き直す

影響

対象 影響
公開インタフェース 変わる(互換あり)。refactor.py で --budget-minutes を省いたときの意味が「30 分固定」から「30 分と下限の大きい方」になる。明示したときの意味は変わらない。supervise.py new の引数は変わらない
データ 状態ファイルと配分の履歴に、想定最大時間の出所(明示 / 下限から決めた)を足す見込み。既存の行は読める形のまま
既存の振る舞い 着手前のテストの初回の上限、想定最大時間を省いたときの想定最大時間、時間の枠の不足でのリファクタリングの失敗の経路

検証手段

項目 手段
テスト uv run --frozen --project . --all-extras pytest plugins/ndf/skills/cross-refactoring/tests plugins/ndf/scripts/tests -q -n 4
静的解析・型検査 bash scripts/check-lint.sh
手動確認 次のスプリントの検査のプランを想定最大時間を省いて書き、リファクタリングが下限の想定最大時間で通るか、飛ばしてコードレビューへ進むかを、フェーズレポートと状態ファイルで見る(リリース後テストで確かめる)

前提とする取り決め

項目 参照先 / 決めたこと
プロジェクト構造 AGENTS.md の「マーケットプレイスの構造」。着手前のテストと想定最大時間は plugins/ndf/skills/cross-refactoring/scripts/ と plugins/ndf/scripts/lib/test_strategy.py、プランの雛形は plugins/ndf/scripts/supervise_lib/ が持つ。同じ役割の値を両方に写さない(Value 6)
コーディング規約 AGENTS.md の「最小限のコード実装」。bash scripts/check-lint.sh
テスト戦略 上限・下限・想定最大時間の決め方は単体テストで、プランの進み方(abort へ進まない)は supervise.py のプランを実行するテストで担保する。.md の文言は照合しない

境界

区分 内容
常に行う 範囲のテストと lint の実行、受け入れ条件 3・5 の入力(x = 495 秒・想定最大時間 30 分)を再現するテスト
確認してから行う 状態ファイル・配分の履歴の形を変えること(承認ゲート 1・2 で人が見る)
行わない テストの高速化、枠の配分の比率と既定の想定最大時間の変更、書き出し済みのプランの書き換え

未決

項目 誰が決めるか 期限
想定最大時間を省いたときのステップの timeout を、プランを書く時点の見込みで決めるか、実行の時点で延ばすか(受け入れ条件 4 の作り方) design 設計 PR
リファクタリングを飛ばしたことを、refactor.py の終了コードで分けるか、駆動の結果 JSON で分けるか(受け入れ条件 5・7 の作り方) design 設計 PR
想定最大時間を省いたときに上げる想定最大時間に上限を設けないこと(前提 2)でよいか 利用者(承認ゲート 1) 設計の承認

進行

モード: — / 進め方: auto

  • 要求と受け入れ条件 — 2026-10-31 13:19
  • 作業場所の用意 — 2026-10-31 14:19
  • 設計 — 2026-10-31 13:23
  • 素材の収集と出典の確定
  • ドキュメント再構成
  • ドキュメントレビュー
  • 計画 — 2026-10-31 14:19
  • 実装 — 2026-10-31 15:20
  • 構造改善 — 2026-10-31 17:29
  • 実装レビュー — 2026-10-31 17:33
  • 完了判定 — 2026-10-31 15:57
  • Pull Request — 2026-10-31 16:14
  • 確定仕様化
  • 後片付け
  • 配布 — 2026-10-31 17:44
  • 体裁レビュー
  • リリース後テスト
  • 振り返り
  • 棚卸し

人の判断待ち(開発ループ l20261031)

  • 判定: unknown(承認ゲート 1)
  • 理由: 設計文書の『未確認のまま残ること』が 2 点を承認ゲート 1 で利用者が決める事項としている。1 つは想定最大時間を省いたときに引き上げる幅へ上限を設けないこと(前提 2)、もう 1 つは受け入れ条件 6 の読み方(決定 6 で、根拠は『根拠なし』)である。人の判断を前提にした未決が残るため、MVV 判定だけで関門を通すと、方向を決めるのは人とする共通原則と、人は問い直さずに 1 回で決められるとする Vision に沿うかを決められない/Value 10(費用 > 速さ > 品質。品質を上げる工程は費用と時間の上限の中でだけ行う)に照らすと、決定 2 はリファクタリングの想定最大時間を上限なしに引き上げる。明示した値は守る(決定 3)ものの、既定の実行で費用の上限が無くなる点が Value 10 に反するかどうかは、前提 2 の利用者の判断に依存する/PR 本文のテストで requirements-check が exit 2 で止まり、追加したはずの issues/issue-2047-requirements.md を読めていない。Value 2 は承認資料の材料を正しく揃えることを、Value 10 はその材料の正しさを効率の順位の外に置くことを求める。受け入れ条件と設計の対応が検査で確かめられていない/それ以外の設計は MVV に沿う。s の出所を 1 か所にまとめる点と、時間の式を supervise へ写さない点は Value 6、着手前のテストの実測と打って確かめた値を根拠にする点は Value 3、延長の判断を子に任せ余裕の式を 1 か所に残す点は Value 4、新しい設定を足さず既存の設定を受ける点は Value 5、飛ばした理由を記録に残す点は Value 7 に沿う/変更の対象は NDF の配布物のコード・設計文書・用語集だけである。秘密・認証・利用者のデータ・戻せない操作・.ndf/ や CLAUDE.md の書き換え・配布の取得元や版の規則のどれにも当たらない(C1〜C8・P1 に該当なし)
  • 根拠の項目: Mission, Vision, Value 2, Value 3, Value 4, Value 5, Value 6, Value 7, Value 10, C1, C7, P1
  • 設計 Pull Request: 検査のプラン: 想定最大時間を省いてもリファクタリングが下限の時間で通り、収まらないときはコードレビューへ進む(#2047) #2301
  • 答え: approved
  • 答え方: python3 /work/ai-plugins/plugins/ndf/scripts/sprint-state.py loop answer l20261031 2047 --approved|--rejected

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions