Skip to content

Factory の開発ループ: 再開したジョブへ変えた予算を届ける #2405

Open

Description

@takemi-ohama

依頼(原文)

何をするか

Factory の開発ループで、承認待ち・利用上限待ちの間に画面で予算を変えても、再開したジョブ(START=resume)は古い予算のまま進む。再開したジョブへ変えた予算を届ける。

経緯

決めること

  • ランナーが再開の前に NDF の loop start --budget-* を打ち直すか、再開の指示へ予算を足すか

受け入れ条件

  • 承認待ちの間に予算を変えたループを再開すると、NDF の止まる条件の判定が新しい予算で行われる(テストで確かめる)

(書き手: 起票者、日付: 2026-11-02。スプリント lfactory2-3 の検査で後へ回した指摘を、AI が起票の記録として書いた本文である。利用者の指示の原文は含まない)

起票者の推定

依頼の全文が起票者の文であるため、題と本文の条件をすべてここに並べる。承認ゲート 1 で利用者が確かめるまで、利用者の決定として扱わない。

# 条件 推した出所 この要求での扱い
S1 再開したジョブ(START=resume)へ、画面で変えた予算を届ける PR 2397 のクロスレビューの指摘( #2397 )。factory/api/src/runs/control.ts の実行ジョブの指示の組み立てが、予算を START=loop のときだけ載せている 受け入れ条件 1・2
S2 対象の場面は「承認待ち」と「利用上限待ち」の間の変更 起票者の判断。再開のジョブを作る経路が、承認の答えによる再開とリセット時刻の後の再開の 2 つであること 受け入れ条件 1・2。周回の間の変更は既に届くため含まない(前提 1)
S3 今の NDF には再開したループへ予算を渡す口が無く、上書きできるのはループを始めるときの --budget-* だけ #2347 の設計の決定 14 前提 2 で事実を補う(既にあるループへの loop start --budget-* も予算を上書きするが、ほかの処理を伴う)
S4 届け方は「ランナーが再開の前に loop start --budget-* を打ち直す」か「再開の指示へ予算を足す」かの 2 つから選ぶ 起票者の判断 未決 1(設計で決める。選択肢を 2 つに限らない)
S5 新しい予算で止まる条件の判定が行われることを、テストで確かめる 起票者の判断 受け入れ条件 3・4

目的

  • Factory の利用者が、承認待ちや利用上限待ちで止まっている開発ループの予算を画面で変えたとき、ループが再開した後に NDF がその予算で止まるかを判定する状態にする
  • 画面に出る「予算の残り」と、NDF が止まる判定に使う予算を食い違わせない

前提

  • 前提 1: 周回の間(between_laps)に変えた予算は、次の周回のジョブ(START=loop)の指示の LOOP_BUDGET から --budget-* として NDF へ渡っており、この課題では変えない。根拠: factory/runner/src/runtime.ts の loopPrompt が LOOP_BUDGET を --budget-<単位> <値> へ並べ、factory/runner/src/loop.test.ts の「--budget-laps 3 --budget-usd 60」の例がそれを確かめている
  • 前提 2: NDF のループの記録(.ndf/loop/<名>/loop.json)は既にあるループにも loop start <名> --budget-* で予算を上書きでき、変わったときだけ予算の履歴(budget_history)に 1 行足す(plugins/ndf/scripts/lib/sprint_loop.py の apply_budget)。ただし既にあるループへの loop start は、止まった記録を解く手続き(承認ゲート 2 の答えの処理・周回の締め・予算超過の判定し直し)も同時に行う(同じファイルの cmd_start・resume_stopped)
  • 前提 3: NDF の止まる条件の判定は、周回を締めるときにループの記録の予算と締めた周回の消費の和を比べる(plugins/ndf/scripts/lib/sprint_loop_record.py の予算の判定)。したがって conductor を起こす前にループの記録の予算が新しい値になっていれば、その後の判定は新しい予算で行われる
  • 前提 4: 実行ジョブの指示の契約(factory/contracts/schemas/run-job-instruction.schema.json)は、ループの実行の resume に LOOP_BUDGET を載せることを既に許している。契約の形は変えない
  • 前提 5: 予算をループの記録へ反映できなかったときは、conductor を起こさずにジョブを失敗で終える。古い予算のまま進めると、この課題が直す食い違いをそのまま残すためである。人の判断待ちへの答えを書けなかったとき(factory/runner/src/runtime.ts の answerLoop)と同じ扱いにする

対象範囲

含む:

  • 承認の答えによる再開(APPROVAL_REQUEST_ID のある START=resume)で、Factory のループの今の予算を NDF のループの記録へ反映すること
  • 利用上限の後の再開(APPROVAL_REQUEST_ID の無い START=resume)で、同じく反映すること
  • 反映を確かめる自動テスト(Factory のサービスとランナー、必要なら NDF のスクリプト)
  • 確定仕様(docs/specifications/ndf-factory-dev-loops.md ほか)の予算の変更の記述を、再開のジョブにも届くことに合わせる

含まない:

  • 動いているジョブ(running)の途中で予算を届けること。変えた予算は次に作るジョブ(再開か次の周回)で届く
  • 画面で予算の単位を空にした(制限を外した)ときに、NDF の予算を既定へ戻すこと。今の NDF の --budget-* は値の上書きだけを持ち、外す口が無い。周回のジョブでも同じ制約があり、別の課題で扱う
  • 止まった(stopped)・取り消した(cancelled)ループの再開。決定 14 のとおり終わりの状態であり、続きは新しいループで始める
  • 予算の変更の画面と API(updateBudget)の形の変更

ドメインイベント

# イベント 引き金 失敗したとき 順序の前提
E1 利用者がループの予算を変えた 画面か API の予算の変更(updateBudget) 終わりの状態のループは already_finished で断る(既存) ループが running・between_laps・awaiting_approval のいずれか
E2 サービスが再開のジョブを作った 承認の答え(requestResume)か、リセット時刻の経過(resumeAfterReset) 既存の断りの理由(sprint_run_busy など)で作らない E1 の後に作られたジョブに、E1 の予算が載る
E3 ランナーが保存した状態を復元した 再開のジョブの起動 既存のとおりジョブを失敗にする E2
E4 ランナーがループの記録の予算を新しい値にした E3 の完了 conductor を起こさずジョブを失敗で終える(前提 5) E3 の後、conductor の起動の前
E5 conductor が前回の続きから進めた E4 の完了(予算を宣言していないループでは E3 の完了) 既存のとおり E4
E6 NDF が周回を締め、止まる条件を新しい予算で判定した conductor が周回を締めるコマンドを打つ 既存のとおり E4

E4 と人の判断待ちへの答えの書き込み(既存)の順序は、どちらが先でも結果が同じになることを設計で確かめる(未決 2)。

用語

用語 意味
ループの予算 開発ループが使ってよい周回の数・費用(USD)・時間の上限。Factory のループの行と NDF のループの記録の両方が持つ
再開のジョブ START=resume の実行ジョブ。承認の答えによるものと、利用上限の後のものがある
ループの記録 NDF が開発ループごとに持つ .ndf/loop/<名>/loop.json。予算・予算の履歴・周回・止まった理由を持つ

受け入れ条件

  • 1. 承認待ち(awaiting_approval)のループの予算を変えた後に、承認の答えで作られた再開のジョブでは、conductor を起こす前に NDF のループの記録の予算が変えた値になっている(ランナーのテストで、記録の予算を読んで確かめる)
  • 2. 利用上限待ち(awaiting_reset)の実行のループの予算を変えた後に、リセット時刻の後に作られた再開のジョブでも、受け入れ条件 1 と同じになる
  • 3. 予算を下げて再開した場合、締めた周回の消費の和が新しい予算以上になった周回の締めで、NDF が budget_<単位> の止まる条件を記録する(例: 費用の予算を 60 から 40 へ下げ、消費の和 50 で締めると budget_usd で止まる)
  • 4. 予算を上げて再開した場合、古い予算なら止まっていた消費の和でも NDF は止まる条件を記録しない(例: 費用の予算を 40 から 100 へ上げ、消費の和 50 で締めると止まらない)
  • 5. 再開のジョブの指示に載る予算は、そのジョブを作った時点の Factory のループの予算である(サービスのテストで、requestResume と resumeAfterReset の両方の指示に LOOP_BUDGET が載ることを確かめる)
  • 6. 予算が変わっていないときは、予算の履歴に行を足さない。変わったときは 1 行だけ足す
  • 7. 予算を反映する操作は、ループの記録の予算と予算の履歴のほかを変えない。止まった記録の解除・周回の締め・承認ゲート 2 の答えの処理は、conductor が前回の続きから進めるときに既存のとおり 1 回だけ起きる
  • 8. 予算をどの単位も宣言していないループの再開では、ループの記録の予算を変えない
  • 9. ループに属さない実行の再開のジョブは、指示も振る舞いも変わらない(LOOP_BUDGET を載せない)
  • 10. ループの記録へ予算を反映できなかったとき、ランナーは conductor を起こさずにジョブを失敗で終え、理由をジョブの出力に残す

非機能の条件

大項目 条件
運用・保守性 予算の上書きの規則(単位ごとの上書きと予算の履歴)は NDF の 1 か所に置き、ランナーとサービスに同じ規則を写さない
移行性 既に動いているループと保存済みの状態は、そのまま次の再開のジョブで新しい規則に乗る。データの移行は要らない

影響

対象 影響
公開インタフェース 変わらない。実行ジョブの指示の契約は再開のジョブの LOOP_BUDGET を既に許している(前提 4)。NDF に予算だけを反映するコマンドを足すかは未決 1
データ スキーマの変更は無い。NDF のループの記録の予算の履歴に、再開のときの変更が 1 行ずつ足される
既存の振る舞い 再開のジョブで、conductor の起動の前に予算の反映が入る。周回のジョブとループに属さない実行は変わらない

検証手段

項目 手段
テスト(Factory) factory/ の既存のテストのコマンド(サービスは factory/api、ランナーは factory/runner のテスト)
テスト(NDF) uv run --frozen --project . --all-extras pytest plugins/ndf/scripts/tests -q -n 4(NDF のスクリプトを変えるとき)
静的解析・型検査 factory/ の既存の型検査とリンタ
手動確認 リリース後テストで、Factory のループを承認待ちで止めて予算を変え、答えで再開した後のループの記録の予算と予算の履歴を見る

前提とする取り決め

項目 参照先 / 決めたこと
プロジェクト構造 AGENTS.md の「マーケットプレイスの構造」。plugins/ と .claude-plugin/marketplace.json から factory/ を参照しない(scripts/check-service-boundary.py)。ランナーは NDF のスクリプトを、導入した版の取得元の写しから呼ぶ(既存の answerLoop と同じ)
コーディング規約 AGENTS.md の「最小限のコード実装」。.md の文言を照合するテストを書かない
テスト戦略 指示の組み立てはサービスの単体テスト、反映の順序と失敗の扱いはランナーの単体テスト、予算の判定は NDF の既存のテスト(plugins/ndf/scripts/tests/test_sprint_loop.py)で担保する

境界

区分 内容
常に行う 既存のテストの実行、確定仕様の予算の変更の記述を実装に合わせる
確認してから行う NDF のスクリプトへ新しい副命令を足すこと(公開インタフェースの追加。承認ゲート 1 で見る)
行わない 予算の単位を外す口の追加、動いているジョブへの予算の割り込み、止まったループの再開

未決

項目 誰が決めるか 期限
1. 予算をループの記録へ反映する手段。候補は (a) ランナーが既存の loop start <名> --budget-* を打つ、(b) 再開の指示の文へ予算を足して conductor に打たせる、(c) NDF に予算だけを反映する副命令を足してランナーが打つ。(a) は止まった記録を解く手続きを conductor より先に起こすため、受け入れ条件 7 を満たせるかを確かめる。(b) は決まった手順を AI に任せる形になる 設計(承認ゲート 1 で利用者が見る) 設計 PR
2. 予算の反映と、人の判断待ちへの答えの書き込み(既存)の順序 設計 設計 PR

進行

モード: — / 進め方: auto

  • 要求と受け入れ条件 — 2026-11-04 08:06
  • 作業場所の用意
  • 設計 — 2026-11-04 08:09
  • 素材の収集と出典の確定
  • ドキュメント再構成 — 2026-11-04 08:15
  • ドキュメントレビュー
  • 計画
  • 実装
  • 構造改善
  • 実装レビュー
  • 完了判定
  • Pull Request — 2026-11-04 14:37
  • 確定仕様化 — 2026-11-04 14:30
  • 後片付け — 2026-11-04 15:02
  • 配布
  • 体裁レビュー
  • リリース後テスト — 2026-11-04 14:58
  • 振り返り — 2026-11-04 14:58
  • 棚卸し — 2026-11-04 15:02

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

  • 判定: unknown(承認ゲート 1)
  • 理由: 設計の中身は MVV に沿っている。再開のジョブへ変更後の予算を届ける。予算の上書きは既存の apply_budget の 1 か所に寄せる(Value 7)。決まった手順は conductor に任せず、ランナーが終了コードで成否を決める(Value 5)。どれも Mission と Vision の「人の判断が要る所だけで止まる」開発ループの正しさに寄与する/要求の検査(requirements-check)が終了コード 2 で止まった。対象の issues/issue-2405-requirements.md は PR に含まれているのに読めず、原因は確認されていない。要求と受け入れ条件が検査を通るかは材料から決められない。Value 1 は自動テストで守り、確かめていないものを済んだと扱わないことを求める。そのため、検査が止まったまま関門を自動で通すかは人へ戻す/決定 6 は、副命令が無い NDF(終了コード 2)では反映せずに続ける。版を固定した既存の実行の再開を止めない判断で、共通原則の「人を守る」と Value 1 に沿う。ただし argparse は値の誤りでも終了コード 2 を返すため、渡した予算の値の誤りも「副命令が無い」と読まれて素通りしうる。この点は設計の確認事項として残る/秘密・認証・利用者のデータ・戻せない操作・他のリポジトリへの公開・.ndf/ や CLAUDE.md の書き換えには当たらない(C1〜C8)。配布物の取得元と版の規則も変えない(P1)。API と画面の間で操作の差は生まない(スプリント Value 4)/決定 6 の確認コマンドは実行して確かめてある(Value 4)。未確認のまま残ること(実物の通し、古い版の実行の数)は表に明記してある(Value 8)。配布の後に確かめる項目は判定の対象にしない
  • 根拠の項目: Mission, Vision, Value 1, Value 4, Value 5, Value 7, Value 8, C1, C3, C4, C5, C7, P1, スプリント Value 4
  • 設計 Pull Request: Factory の開発ループ: 承認待ち・利用上限待ちの間に変えた予算で、再開した後の止まる判定を行う(#2405) #2478
  • 答え: approved
  • 答え方: python3 /persistent/ai/.claude/plugins/cache/ai-plugins/ndf/10.27.15/scripts/sprint-state.py loop answer lfactory3 2405 --approved|--rejected|--revise --note <指摘ファイル>

振り返り: #2490 (comment)

Activity

  1. added
    quality品質向上の Issue(棚卸しの品質の枠。.ndf/backlog.json の quality)
    on Nov 2, 2026
  2. added 2 commits that reference this issue on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    quality品質向上の Issue(棚卸しの品質の枠。.ndf/backlog.json の quality)

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions