Skip to content

スプリント m26-3 - #2348

Merged
takemi-ohama merged 33 commits into
developfrom
sprint/m26-3
Nov 1, 2026
Merged

takemi-ohama merged 33 commits into
developfrom
sprint/m26-3

Conversation

@takemi-ohama

Copy link
Copy Markdown
Contributor

スプリント m26-3 の課題を develop へ取り込む。

利用者向けの変化

移行の手順

  • 無し

課題と設計

コミット

変更の統計

 .../scripts/tests/test_bump_keeps_instructions.py  |   5 +-
 plugins/ndf/scripts/tests/test_record_find.py      | 174 +++++++++++
 plugins/ndf/scripts/tests/test_release_steps.py    |  66 +++-
 plugins/ndf/scripts/tests/test_release_version.py  | 333 +++++++++++++++++++++
 plugins/ndf/scripts/tests/test_sprint_loop.py      |  42 +++
 plugins/ndf/scripts/tests/test_step_scripts.py     |   4 +-
 plugins/ndf/scripts/tests/test_supervise.py        |   3 +-
 .../ndf/scripts/tests/test_supervise_close_auto.py |   5 +-
 plugins/ndf/scripts/tests/test_supervise_new.py    |  41 ++-
 plugins/ndf/scripts/tests/test_supervise_pace.py   |   1 +
 .../scripts/tests/test_supervise_pace_verify.py    |  16 +-
 .../skills/development-workflow/references/pace.md |  38 ++-
 .../development-workflow/references/waiting.md     |   5 +-
 plugins/ndf/skills/progress-tracking/SKILL.md      |   3 +-
 40 files changed, 1559 insertions(+), 147 deletions(-)

テスト(supervise.py の run のステップ)

ステップ exit 最後の行
collect 0

集めた実装の PR

閉じる課題

Closes #2044
Closes #2121
Closes #2164
Closes #2230

手動確認

モード: standard / 通した工程: 実装 → Pull Request

🤖 Generated with Claude Code

takemi-ohama and others added 30 commits November 1, 2026 03:16
- sprint_close_lib/record_find.py を新設。まとめの課題を閉じる PR とそれを参照する PR から、
  段階が本番か配布なしで、スプリント: が閉じる PR を含むリリース記録の PR を 1 本決める。
  2 つ以上のスプリントにあれば特定できないとして採らない
- queue_fill が {queue_record} を埋め、見つからなければ計画を書き換えずに流さない(unfilled)。
  plan.QUEUE_NAMED に {queue_record} を足し、run でも残れば止まる
- sprint_routes.record_pr_ref は unknown を組まず、merge・手で届ける・検証の環境だけ・
  手動反映の本番系と開発ループのまとめで {queue_record} を返す。雛形と昇格は今のまま
- 開発ループの承認ゲート 2 の後のまとめへ values.pr を渡さない。差し込みを埋められずに
  流さなかった計画のある周回は失敗で締める
- dist_record.released を足し、sprint-close.py と共有する(振る舞いは変えない)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2230 を実装する(スプリント m26-3)
版の判定を lib/release_version.py に置き、bump は書き換える前に origin のベースブランチの版と
タグを読む。ベースブランチの版以下・既にタグのある正式版は終了コード 3、読めなければ 2 で止める。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
new sprint / new close の --version・--prod を任意にし、--level を足す。雛形は版が無ければ
{release_version}・{release_base} を書き、プランへ「版」の欄を置く。bump は 2・3 で judge を通らずに止める。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
開発版は origin から次に空いている版を決め、記録の版が自分の配布の前に別のスプリントに出ていれば
決め直す。本番は開発版の記録の base で出し、タグがあれば止める。埋められなければ項目の version に理由を載せる。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bump は自分の作業場所の位置を渡す。キューの差し込みを通すテストは差し込みを差し替え、
origin を読む bump のテストは bare の origin を用意する。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2164 を実装する(スプリント m26-3)
…_sprint

manifest のステージ配列から queue と pipeline の文字列を組む部分を sprint_pipeline として抽出した。
手で行うステージの有無による 2 つの経路の文字列を現状固定テストで固定した。

Item-Id: I-001
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d_find.py#find_record

閉じる PR の収集(_closers_of)・候補の展開(_candidates)・記録の読み取りと振り分け(_released_records)を
それぞれ抽出し、find_record には順に呼ぶ流れと RecordHit の組み立てだけを残した。

Item-Id: I-002
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/release_templates.py#release_version_of

開発版から接尾辞を外す正規表現が release_version_of と sprint.prod_version に別々に書かれていたため、
release_templates に strip_suffix を 1 つ置いて両方から呼ぶ形にした。versions.release_base(semver)は
形の違う版で例外を投げ、ビルドメタデータも落とすため振る舞いが変わるので使わない。
sprint.py で使われなくなった re の import を消した。

Item-Id: I-003
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
抽出した sprint_pipeline でファイルが 500 行を超えたため、wait_lap と同じ sprint_loop_gate.py へ移し、sprint_loop.py からは読み込んで使う(sl.sprint_pipeline の参照は変わらない)。

Item-Id: I-001
Impl-Runtime: claude
Impl-Model: default
…ind_record

抽出した _candidates が backlog-refinement の upkeep_rank.py の同名関数と衝突したため、_record_candidates へ改名した。

Item-Id: I-002
Impl-Runtime: claude
Impl-Model: default
Item-Id: I-004
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e2_answer

承認と差し戻しの時系列から最新の答えを選ぶ部分を latest_gate2_answer(gates, rejections, since) として
抽出した。gate2_answer には状態ファイルと差し戻しログを読み、同じ周回・関門 2 の差し戻しに絞る部分だけを残す。
fresh と EPOCH の規則、同じ時刻は承認を採る順序、JsonReadError を空とする扱いはそのまま。

Item-Id: I-004
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng_blocker

kept_all を段階的に上書きする if/elif 連鎖を、配布の段階から閉じられない理由を返す _stage_blocker と、
リリース後テストの記録から (理由, verdicts) を返す _verify_verdicts に分けた。_closing_blocker は
_stage_blocker → (理由が無く、リリース後テストを見るとき) _verify_verdicts の 2 段だけにした。

Item-Id: I-005
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ue_fill.py#_fill_dev

記録がある開発版の確かめ直し(出ているか → 自分の配布か → 明示か → 決め直し)を
_recheck_dev へ抽出し、早期 return で入れ子を平らにした。_fill_dev は記録の有無で
2 つの経路を選ぶだけになる。振る舞いは変えない。

Item-Id: I-006
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
タグの無い正式版がベースブランチの版以下のときに終了コード 3 で止め、次に空いている版を
ベースブランチの次のパッチとする経路を固定する(既存テストが通っていなかった経路)。

Item-Id: I-007
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on.py#bump_check

old == new のときの打ち直しの判定を _rerun へ、止めるときの既に出ている版と次に空いている
版の組み立てを _taken_stop へ抽出した。bump_check は形の検査 → 観測 → 打ち直し →
書き換えてよいかの判定 → 止める理由の直線になる。振る舞いは変えない。

Item-Id: I-007
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_sprint

まとめの queue のコマンドの組み立て(gate2_pipeline・sprint_pipeline・continuation の 3 か所)を
sprint_loop_gate.queue_command へ抽出し、--max 3 を QUEUE_MAX にまとめた。
止まった記録を stops へ移して running に戻す 2 行(resume_stopped と cmd_sprint)を
resume_record へ抽出した。出力の文字列は変わらない。

Item-Id: I-008
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elease

開発版の PR の使い回し・作成とマージを _merge_release_pr へ、本番の PR の作成と承認した先端を
指定したマージの再試行(HEAD_MOVED_RETRIES・承認ゲートでの停止)を _merge_prod_pr へ抽出した。
cmd_release は前提検査・push・各段階の呼び出し・タグ公開・結果の組み立てを担う。
副作用の順序と出力は変わらない。

Item-Id: I-009
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
抽出でファイルが 1266 行になり、構造検査の例外リストの上限(1252 行)を超えた。振る舞いを変えずに
行数を詰めた: 本番の PR の題・本文を変数へ置いて create_pr の呼び出しを 1 行にし、抽出先の docstring を
短くし、ver の一時変数と cmd_release の空行を外し、マージコミットの補完を `or` にした(git は
merge が空のときだけ呼ばれる点は同じ)。1251 行。

Item-Id: I-009
Impl-Runtime: claude
Impl-Model: default
…#cmd_queue

then からステージの並びとすべてのプランを作る部分を _normalize_stages へ、
宣言の読み取り・done の初期化・知らせ始める位置と前の一覧の読み取りを
_prepare_queue へ抜き出した。cmd_queue は正規化 → 準備 → 起動 → 待ちの流れになる。
durable の import は本体に残す。

Item-Id: I-010
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item-Id: I-011
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…loop.py#resume_stopped

止まった理由ごとの再開の振る舞いを if-elif の連鎖から、理由ごとの手続き(_resume_mvv_changed /
_resume_user_stop / _resume_budget / _resume_gate2 / _resume_refused)と対応表へ移した。
手続きは (止まったままの結果 or None, 次のコマンド) を返す共通の形にそろえ、budget_* は接頭辞で引く。
resume_stopped は対応表を引いて手続きを呼び、再開の共通の後処理だけを持つ。振る舞いは変えない。

Item-Id: I-011
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
対応表への置き換えで sprint_loop.py が 541 行になり、構造検査(500 行)を超えた。
承認ゲート 2 の答えの読み取りと承認後の続き(EPOCH / gate2_answer / latest_gate2_answer /
gate2_pipeline)を新しいモジュール sprint_loop_gate2.py へ移し、sprint_loop から読み込む。
再開の手続きは sprint_loop の名前空間の gate2_answer / gate2_pipeline を引くままで、振る舞いは変えない。
使われなくなった import を外した。

Item-Id: I-011
Impl-Runtime: claude
Impl-Model: default
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

改修計画 — devbasex/ai-plugins #2348

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

  • 件数: 採用 11・未確認 0・取り消し 0・見送り 13(うち時間 13)(最終ゲート: passed)
  • 公開: 54f9288 を push した
  • 書き直した時刻: 2026-11-01T06:24:39
  • 対象範囲: docs/specifications/ndf-production-release-record.md, docs/specifications/ndf-sprint-misjudgments-unknown-record-and-manual-none.md, docs/specifications/ndf-workflow-development-loop.md, plugins/ndf/scripts/lib/dist_record.py, plugins/ndf/scripts/lib/release_version.py, plugins/ndf/scripts/lib/sprint_loop.py, plugins/ndf/scripts/lib/sprint_loop_gate.py, plugins/ndf/scripts/release-steps.py, plugins/ndf/scripts/sprint-close.py, plugins/ndf/scripts/sprint_close_lib/record_find.py, plugins/ndf/scripts/supervise_lib/decl.py, plugins/ndf/scripts/supervise_lib/new_args.py, plugins/ndf/scripts/supervise_lib/plan.py, plugins/ndf/scripts/supervise_lib/queue.py, plugins/ndf/scripts/supervise_lib/queue_fill.py, plugins/ndf/scripts/supervise_lib/release_templates.py, plugins/ndf/scripts/supervise_lib/sprint.py, plugins/ndf/scripts/supervise_lib/sprint_routes.py, plugins/ndf/scripts/supervise_lib/sprint_waves.py, plugins/ndf/skills/development-workflow/references/pace.md, plugins/ndf/skills/development-workflow/references/waiting.md, plugins/ndf/skills/progress-tracking/SKILL.md, tests, plugins/ndf/scripts/tests, plugins/ndf/skills/development-workflow/tests
  • テストの戦略: local-scoped-ci-whole(根拠 derived:test_duration)
  • 範囲テストの雛形: uv run --frozen --project . --all-extras pytest {paths} -q -n 4 -o junit_family=xunit1 --junitxml=.ndf/tmp/junit.xml / npm --prefix factory ci --no-audit --no-fund --silent && factory/node_modules/.bin/vitest run --root factory {paths} / python3 scripts/check-script-structure.py {paths}(静的解析) / bash scripts/check-lint.sh -- {paths}(静的解析)
  • 最終ゲートへ寄せた危険フラグ: D1, D3, D4, D5(項目 I-003, I-001, I-004, I-007, I-009, I-008, I-010, I-011)
  • 着手前のテスト: uv run --frozen --project . --all-extras pytest plugins/ndf/scripts/tests plugins/ndf/skills/development-workflow/tests tests -q -n 4 -o junit_family=xunit1 --junitxml=.ndf/tmp/junit.xml && python3 scripts/check-script-structure.py && bash scripts/check-lint.sh(scope)
  • 着手前のテストの結果: 通過(475.8 秒 / HEAD f9cb799)
  • 既存失敗の件数: 0
  • 想定最大時間: 75 分 / 改修計画の時点で使えた時間: 42.6 分
  • 実装担当: claude

改善項目

I-001 — plugins/ndf/scripts/lib/sprint_loop.py#cmd_sprint

兆候 手法 重要度 等級 提案元 状態 コミット
long_method extract_method major high claude / codex / kiro 採用 2

なぜ: 1 メソッドに (1) 周回の前提チェック・(2) sprint-state init の実行と MVV ゲートの写し・(3) new sprint の実行・(4) まとめの組み立て・(5) manifest からの pipeline 文字列の組み立て、が 67 行・認知的複雑度 22・分岐 10 で同居する。特に手で行うステージ有無で pipeline を分ける末尾のブロックは独立した段階として名前が付く。

手順: 1. test_sprint_loop.py のスプリント生成拒否、manual の後まで close を待つ経路、引継ぎの next のテストを確認する。
2. manifest のステージ配列・close のプラン・done・rec を入力とし、queue と pipeline を返す関数を同じファイル内に抽出する。最初の command、後続の command/manual、関門を除外する判定をそのまま移す。
3. cmd_sprint には外部実行、lap の更新、記録保存と結果出力を残し、抽出関数への値の受け渡しを明示する。
4. manual が無い場合と manual/別 command がある場合の文字列を現状固定し、close が案内される順序と既存 metrics が変わらないことを確認する。

レビューで確かめるテストの差分: plugins/ndf/scripts/tests/test_sprint_loop.py(機械で期待値が変わったか決まらなかった)

I-002 — plugins/ndf/scripts/sprint_close_lib/record_find.py#find_record

兆候 手法 重要度 等級 提案元 状態 コミット
long_method extract_method major high claude / codex / kiro 採用 2

なぜ: 1 関数に (1) まとめ課題ごとの閉じる PR の収集・(2) 候補 PR の展開・(3) 候補ごとの記録の読み取りと段階/スプリントによる絞り込み・(4) 曖昧判定と最終決定、の 4 段階が通しで同居し、認知的複雑度 30(本体の最大)・分岐 13・ネストした for の中に try が入る。段階に名前が付き、前段の出力が次段の入力になる直列構造なので連鎖へ分解できる。

手順: 1. 課題ごとの閉じる PR の集合を作る部分を _closers_of(root, repo, issues) -> dict[int, set[int]] として抽出
2. 閉じる PR と、それを参照する PR の和を作る部分を _candidates(root, repo, closers) -> set[int] として抽出
3. 候補ごとに parse_record して kept / pre に振り分ける部分を _released_records(root, repo, cands, closers_of) -> (kept, pre) として抽出(StepError → _Unreadable の変換もここへ)
4. find_record は try の中で 1〜3 を順に呼び、後半の RecordHit の組み立てだけを残す
5. test_record_find.py を流して結果が変わらないことを確かめる

I-003 — plugins/ndf/scripts/supervise_lib/release_templates.py#release_version_of

兆候 手法 重要度 等級 提案元 状態 コミット
duplication consolidate_duplication major high claude 採用 1

なぜ: 開発版から接尾辞を外す規則 re.sub(r"-.*$", "", v) が release_templates.release_version_of と supervise_lib/sprint.py の prod_version に別々に書かれている(範囲外の sprint_loop_record.prod_of にも 3 つ目の写しがあり、docstring が「同じ規則」と互いを指している)。この PR で release_version.py は versions.release_base(semver)を版の規則の 1 か所と定めたのに、雛形の側だけ正規表現の写しが残る

手順: 1. sprint.py の prod_version を、版があれば versions.release_base(version) を返す形へ置き換える(呼ぶ前に deps.require("versions") が済んでいるかを確かめる。済んでいなければ supervise_lib 側に 1 つだけ接尾辞を外す関数を置き、両方から呼ぶ)
2. release_templates.release_version_of の re.sub を同じ関数の呼び出しへ置き換える
3. sprint.py・release_templates.py で使われなくなった re の import を消す
4. test_supervise*.py・test_release_steps.py を流す

I-004 — plugins/ndf/scripts/lib/sprint_loop.py#gate2_answer

兆候 手法 重要度 等級 提案元 状態 コミット
hidden_dependency extract_method major medium codex / kiro 採用 2

なぜ: 引数は lap と since だが、実際には lap 内のパスとグローバルな差し戻しログを読み、時刻変換・新旧判定・承認と差し戻しの優先をまとめて決める。既存テストは停止前の承認を除く経路を通す一方、両方の記録の時系列の組合せを直接固定していない。

手順: 1. 変更前に現状固定テストを追加し、承認と差し戻しの前後関係、同時刻時の選択、since と同時刻、時刻なし、MVV による承認の除外を現在の出力で記録する。仕様を新しく定義し直さない。
2. state と差し戻しログを読む部分を gate2_answer に残し、gates・rejections・since を明示して受け取る純粋な選択関数を同じファイル内に抽出する。fresh と EPOCH の既存規則をそのまま移す。
3. gate2_answer の公開シグネチャと JsonReadError 時の空配列扱いは維持し、抽出関数を呼ぶ。
4. 純粋な関数の固定テストと、cmd_start 経由のゲート 2 再開テストを両方実行する。

I-005 — plugins/ndf/scripts/sprint-close.py#_closing_blocker

兆候 手法 重要度 等級 提案元 状態 コミット
conditional_chain extract_method minor medium claude / kiro 採用 1

なぜ: 閉じてよいかの判定が kept_all を段階的に上書きする if/elif 連鎖(record_pr 0 / unknown / 記録なし / 本番前)と、その後の verify ブロックの判定が 1 関数に積み重なる(認知的複雑度 22・分岐 9・ネストが深い)。kept_all を途中で書き換える形は読む順でしか追えない。

手順: 1. record_pr と stage から閉じられない理由(kept_all)だけを決める _stage_blocker(a, rec) を抽出し、理由 or None を返す
2. verify ブロックから verdicts を決める部分を _verify_verdicts(a, rec, record_repo) として抽出
3. _closing_blocker 本体は _stage_blocker → (理由が無ければ) _verify_verdicts の 2 段にし、kept_all の逐次上書きをやめる

I-006 — plugins/ndf/scripts/supervise_lib/queue_fill.py#_fill_dev

兆候 手法 重要度 等級 提案元 状態 コミット
deep_nesting flatten_conditional minor medium claude / kiro 採用 1

なぜ: 開発版の版を決める処理が rec の有無・is_taken・own の 3 段の入れ子で、正常系(ensure_filled)が末尾に埋もれる(認知的複雑度 21・分岐 8・return 5)。例外的な状態(読めない・既出・自分の配布でない)を先に判定して抜ける形にできる。

手順: 1. 記録がある側を _recheck_dev(f, obs, rec) -> (rec, stopped) として抽出し、早期 return でネストを平らにする(is_taken でなければ rec をそのまま返す → own が None なら unreadable で止める → own なら rec を返す → explicit なら taken で止める → redecide)
2. _fill_dev は記録の有無で 2 つの経路を選ぶだけにする
3. test_release_version.py と queue の版の差し込みを通すテスト(fill_release_version を使う 3 本)を流す

I-007 — plugins/ndf/scripts/lib/release_version.py#bump_check

兆候 手法 重要度 等級 提案元 状態 コミット
conditional_chain flatten_conditional minor medium claude / kiro 採用 2

なぜ: bump_check は old == new と cmp の組み合わせを 2 回に分けて判定し、後半は is_taken・prerelease・cmp で次の版と既存の版を別々の三項・if 連鎖で決める。打ち直しの判定(old == new の場合)と、止める理由の組み立てが 1 関数に混ざる

手順: 1. old == new のときの打ち直しの判定(cmp < 0 / cmp == 0 と own_released)を _rerun(repo, new, base, obs, cmp, metrics) -> (already, stop) | None として抽出
2. 止めるときの existing と nxt を決める部分を _taken_stop(plugin, new, obs, cmp, metrics) -> BumpStop として抽出
3. bump_check は形の検査 → 観測 → _rerun → 書き換えてよいかの判定 → _taken_stop の直線にする
4. test_release_steps.py と test_bump_keeps_instructions.py の bump の経路を流す(bump_check を直接呼ぶテストが無いため、終了コード 2 / 3 と打ち直しの 3 経路を通すテストがあるかを先に確かめ、欠ければ現状固定テストを足す)

レビューで確かめるテストの差分: plugins/ndf/scripts/tests/test_release_steps.py(機械で期待値が変わったか決まらなかった)

I-008 — plugins/ndf/scripts/lib/sprint_loop.py#cmd_sprint

兆候 手法 重要度 等級 提案元 状態 コミット
duplication extract_method major medium claude 採用 1

なぜ: まとめの queue のコマンド「python3 SUPERVISE queue <ステージ> --max 3 [--then ...] --done 」の組み立てが cmd_sprint・gate2_pipeline・sprint_loop_gate.py(342 行)の 3 か所に写されている。--max 3 の値も 3 か所に散る。止まった記録を stops へ移して running に戻す 2 行(resumed_at)も resume_stopped と cmd_sprint に重複する

手順: 1. sprint_loop_gate.py に queue_command(stages: list[list[str]], done: Path) -> str を抽出し、--max 3 を名前付きの定数にする
2. sprint_loop.gate2_pipeline と cmd_sprint の手で行うステージがある側の組み立てをこの関数の呼び出しへ置き換える
3. sprint_loop_gate.py の 342 行の組み立ても同じ関数へ置き換える
4. 止まった記録を解く 2 行を resume_record(env, rec) として抽出し、resume_stopped と cmd_sprint から呼ぶ
5. test_sprint_loop.py を流す(出力の pipeline 文字列が一致することで固定される)

I-009 — plugins/ndf/scripts/release-steps.py#cmd_release

兆候 手法 重要度 等級 提案元 状態 コミット
long_method extract_method major medium codex 採用 2

なぜ: 配布前提の検査、開発版 PR の再利用とマージ、本番の承認コミット照合と先端移動時の再試行、タグ公開、結果の整形まで 1 関数が担う。承認に関わる再試行の境界が PR 作成や結果集計の中に埋もれ、変更時に読む範囲が広い。

手順: 1. test_release_steps.py と test_release_approved_commit.py を実行し、PR 再利用・承認の外の変更・先端移動・承認情報なしでの停止を固定する。
2. 開発版 PR の再利用または作成とマージを、PR 番号とマージコミットを返す関数へ抽出する。前提検査と push の順序は保つ。
3. 本番 PR の作成と承認した先端を指定したマージ再試行を、必要な承認情報を明示して受け取る関数へ抽出する。再照合、HEAD_MOVED_RETRIES、Gate の終了コードと metrics の更新時点を維持する。
4. cmd_release は各段階の呼び出し・dev の結果出力・タグ公開・最終結果の組み立てを担う。抽出先は同じ対象ファイル内とし、既存の _publish_tag_and_release を使う。
5. 外部コマンドを新規に実行せず、既存テストの git/gh の代替で副作用の順序と公開結果を確認する。

I-010 — plugins/ndf/scripts/supervise_lib/queue.py#cmd_queue

兆候 手法 重要度 等級 提案元 状態 コミット
long_method extract_method major medium kiro 採用 1

なぜ: 1 関数に (1) then/stages の正規化・(2) 宣言の読み取りと done の初期化・(3) 耐久ワークフローの起動と resolve・(4) wait ループと attention の通知、が 57 行・認知的複雑度 22・引数 7 で同居する。stages の正規化と起動準備は durable を import する前の純粋なファイル処理で、分けると単体で試せる。

手順: 1. then から then_stages/stages/all_plans を作る部分を _normalize_stages(plans, then) として抽出
2. 宣言の読み取り・done の初期化・listing の判定を _prepare_queue(all_plans, plans, done) として抽出し、(decl, done_path, seen, listing) か _not_started を返す
3. cmd_queue 本体は正規化 → 準備 → launch_queue → wait ループに整理(durable の import は本体に残す)

I-011 — plugins/ndf/scripts/lib/sprint_loop.py#resume_stopped

兆候 手法 重要度 等級 提案元 状態 コミット
conditional_chain replace_with_lookup_table major low kiro 採用 3

なぜ: 止まった理由(stop['condition'])ごとの再開の振る舞いが、mvv_changed / user_stop / budget_* / gate2_returned / refused(+CONTINUATION_STEP) / refused の長い if-elif 連鎖で 1 関数に散らばる(認知的複雑度 22・分岐 11・return 5)。種類ごとの早期 return と nxt の組み立てが混在し、新しい止まり方を増やすたびにこの連鎖を直す。

手順: 1. 各分岐の本体を condition ごとのハンドラ関数(_resume_mvv_changed / _resume_user_stop / _resume_budget / _resume_gate2 / resume_refused)へ抽出し、(停止結果 or None, nxt) を返す共通の形にそろえる
2. condition のプレフィックス照合(budget
)を含めてディスパッチする小さな対応表を作る
3. resume_stopped 本体は対応表を引いてハンドラを呼び、早期 return か stops への移動+write_record+handoff の共通後処理だけを残す

指標の測定

  • 状態: written / 言語とツールの対応: 既定(.ndf/code-metrics.json は無い) / 所要: 24.9 秒(使えた時間 225 秒 / 上限 225 秒)
  • 指標のファイル: /tmp/ndf-worktrees/devbasex--ai-plugins/rf2348/work/.cross_refactoring/code-metrics-rf2348.md
言語 ツール 版 ランナー ファイル 秒 結果
python ruff-complexipy ruff 0.16.9 / complexipy 8.0.1 uvx 234 0.8 測った
shell — — — 10 — unsupported_language
重複: python symilar pylint 4.0.9 uvx 234 21.9 測った(2 箇所)
重複: shell jscpd jscpd 4.3.0 npx 10 1.2 測った(0 箇所)

時間の上限

値 中身
提案の枠の終わり(監視の上限 = 残り + 余裕) 2026-11-01T05:46:17+00:00
改修計画の枠の終わり(同上) 2026-11-01T05:53:47+00:00
テストの追加の終わり = 実装の終わり − Σ(採っていて未検証の項目の実装の見積り) 2026-11-01T06:09:00+00:00
実装の終わり = 最終ゲートの修正の打ち切り − バッファ − Σ(採っていて未検証の項目の検証の見積り) 2026-11-01T06:11:32+00:00
直しの試行の打ち切り 2026-11-01T06:21:13+00:00
想定最大時間の終わり H(子プロセスはすべてここまでに止まる) 2026-11-01T06:38:22+00:00
確かめの開始の締め切り = H − 確かめの見込み(最終ゲート修正と打ち切りの後の取り消しもここまで) 2026-11-01T06:23:00+00:00
確かめの見込み(秒。最終ゲートの確かめ 1 回。バッファの最終ゲートの全体テスト) 922
最終ゲート修正 1 回の見込み(秒。バッファの final_fix。起動は今 + この値が確かめの開始の締め切り以前のときだけ) 107
指標の測定の上限(秒。提案の枠の中から割く) 225
着手前のテスト 1 回の上限(秒) 3708
範囲テスト 1 回の上限(秒) 1428
手元の全体テスト 1 回の上限(秒) 3708
CI の待ちの上限(秒) 2766
余裕(秒。手順の上限と CLI の上限に足す) 225

入力: 予算 75 分 / 全体テストの所要 w 1236.0(ci-steps) / 着手前の実測 x 475.8 / CI の壁時計 c 922.0 / 着手前のテストの所要の見込み s 1236.0(whole) / 戦略 local-scoped-ci-whole

無音の打ち切りは手順の監視の上限と同じ値である。項目ごとの期限は持たない(実装を止めるのは実装の終わりだけ)。

見送った提案

対象 兆候 理由 根拠(MVV) 補足
plugins/ndf/scripts/lib/sprint_loop.py#gate2_answer long_method duplicate Value 6 C-007 と同じ変更
plugins/ndf/scripts/lib/release_version.py#bump_check long_method duplicate Value 6 C-016 と同じ変更
plugins/ndf/scripts/lib/sprint_loop_gate.py#continuation duplication budget Value 6 / Value 7 / Value 4 見積り 16.4 分が残りに入らない
plugins/ndf/scripts/supervise_lib/sprint_waves.py#plan_sprint_release duplication budget Value 6 / Value 5 見積り 16.4 分が残りに入らない
plugins/ndf/scripts/supervise_lib/release_templates.py#release_names duplication budget Value 6 / Value 7 見積り 16.4 分が残りに入らない
plugins/ndf/scripts/lib/release_version.py#observe long_method budget Value 10 / Value 4 見積り 8.8 分が残りに入らない
plugins/ndf/scripts/supervise_lib/queue.py#fill_stage long_method budget Value 10 / Value 4 見積り 8.8 分が残りに入らない
plugins/ndf/scripts/supervise_lib/sprint_routes.py#manual_production_waves duplication budget Value 10 / Value 6 見積り 8.8 分が残りに入らない
plugins/ndf/scripts/tests/test_supervise_design_match.py#pr_repo duplication budget Value 10 / Value 6 見積り 9.1 分が残りに入らない
plugins/ndf/scripts/tests/test_supervise_new.py#test_supervise_new.py duplication budget Value 10 / Value 6 見積り 9.1 分が残りに入らない
plugins/ndf/scripts/sprint_close_lib/record_find.py#RecordHit.reason conditional_chain budget Value 10 / Value 6 見積り 9.2 分が残りに入らない
plugins/ndf/scripts/lib/sprint_loop.py#cmd_sprint magic_value budget Value 8 / Value 10 / Value 6 見積り 9.9 分が残りに入らない
plugins/ndf/scripts/lib/dist_record.py#released magic_value budget Value 8 / Value 10 / Value 6 / Value 7 見積り 9.9 分が残りに入らない
plugins/ndf/scripts/supervise_lib/release_templates.py#_prepare_steps long_parameter_list budget Value 10 / Value 6 見積り 11.9 分が残りに入らない
plugins/ndf/scripts/release-steps.py#base_version duplication budget Value 6 / Value 10 見積り 16.4 分が残りに入らない

@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 | codex | REQUEST_CHANGES

配布不要の条件判定より先に版を検査して後続を止める経路を修正してください。

@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

41 ファイル(#2089 系の extract_method/consolidate を中心とした refactoring と、#2164 の版の fill 時決定・#2230 の記録 PR 探索の 2 機能)を全件読んだ。gate2_answer/latest_gate2_answer・resume_stopped のハンドラ分割・_closing_blocker/_stage_blocker/_verify_verdicts・release-steps の _merge_release_pr/_merge_prod_pr 抽出・cmd_queue の _normalize_stages/_prepare_queue 分割・queue_command への --max 3 の一本化は、base との差分を追った範囲でいずれも挙動が保たれている。新機能で増える git fetch / gh 呼び出し(bump の observe、fill_release_version、find_record)は #2164・#2230 の設計どおりで、plan を atomic に埋め戻すため queue 打ち直しでも二重に呼ばれず、test で境界・失敗系・落下後の再開まで固定されている。docs(3 spec)・pace.md・waiting.md・SKILL.md の記述はコード(has_record・record_pr_ref の分岐順・{queue_record} の release-verify 配置・unfilled を失敗に数える規則)と一致している。critical/major の基準(通常経路の誤動作・秘密/認可/データ/不可逆・呼び出し回数の意図せぬ増加・設計と実装の食い違い)に当たる指摘は、今回の範囲では見つからなかった。

「版」と「実行の条件」を持つ配布のプランは、queue の差し込みで版を照会する前に条件を打つ。
配布不要(skip_code)なら版を照会・差し込みせずに流し、engine が条件を打ち直して完了で終える。
明示の版が既出でも unfilled で止まらず、まとめ・課題のクローズへ進む。
条件の実行は engine と queue で共有する run_condition(steps.py)へ寄せた。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

🔧 /ndf:fix サマリ | round 1 | commit 62f27fe

対応件数: critical=0 / major=1 / minor=0(合計 1 件)
決着: 1 件 / 見送り: 0 件 / 却下: 0 件
CI: SUCCESS

@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 | claude | REQUEST_CHANGES

開発ループのまとめが、周回のスプリントで出し終えた版を明示の版として渡し続けるため、最終の検査で変更が出た周回は #2164 の既出の検査で必ず止まる。1 件を直してください。

@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

43 ファイル全件を base...head の差分で読んだ。#2089 のリファクタリング(sprint_loop の resume ハンドラ分割と gate2 を sprint_loop_gate2 へ抽出・engine/queue の run_condition と _normalize_stages/_prepare_queue 抽出・release-steps の _merge_release_pr/_merge_prod_pr・sprint-close の _stage_blocker/_verify_verdicts・queue_command への --max 一本化)と、2 機能(#2164 の版の fill 時決定 = release_version.py・queue_fill.fill_release_version、#2230 の記録 PR 探索 = sprint_close_lib/record_find.py・{queue_record})を追った。round 1 の codex の major(配布不要の条件判定を版の検査より先に行う)は commit 62f27fe の queue.fill_stage の _release_skipped_by_condition で対応済みで、test_fill_stage_asks_the_condition_before_the_version が code=0/3 の両経路を固定している。重点基準(通常経路の誤動作・秘密/認可/データ/不可逆・ネットワーク/CI/AI 起動の回数増・設計文書と実装の食い違い)に当たる critical/major は、この範囲では見つからなかった。調べた候補はいずれも設計どおりで棄却した: (1) 配布プランの実行の条件が fill_stage と engine の 2 回走るが check-trigger.py changed は通信せず git 履歴のみ読む読み取り専用で副作用なし・skip 最適化のため構造上避けにくい。(2) dev プランの流し直しごとの git fetch/own_released は版の横取りを再検査する意図的な設計(#2164)で、ensure_filled が冪等。(3) record_pr_ref が STAGE_NONE→pace:auto→テンプレの順へ替わり auto のまとめが {queue_record} を使うのは #2230 決定 5 のとおりで test で固定。(4) {release_version}/{release_base} は leftover_placeholders の網に無いが、この語の産出元 release_names/version_field は常に「版」欄も置くため実経路で未解決のまま流れない。closing_refs の (repo, n) 照合・released の段階判定・gate2_answer の現状固定テストも実装と一致。

最終の検査で変更があると、出し終えた版が既出として止まり、まとめと周回が失敗で締まっていた。
まとめの配布の版は流す時点に次に空いている版で決める。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

🔧 /ndf:fix サマリ | commit b88f88f

対応件数: critical=0 / major=0 / minor=0(合計 0 件)
決着: 1 件 / 見送り: 0 件 / 却下: 0 件
CI: NONE

@takemi-ohama
takemi-ohama marked this pull request as ready for review November 1, 2026 07:23
@takemi-ohama
takemi-ohama merged commit e4d1a1d into develop Nov 1, 2026
23 checks passed
@takemi-ohama
takemi-ohama deleted the sprint/m26-3 branch November 1, 2026 07:24
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

MVV 判定(関門 2(本番への配布の承認))

  • 判定: follow(関門を省いた。利用者が承認した MVV を事前の許可として扱う)
  • 根拠: Mission / Vision / Value 1 / Value 2 / Value 4 / Value 6 / Value 10 / C5 / C7 / P1(プロジェクト MVV: approved・版 3)
  • 時刻: 2026-11-01T07:27:18+00:00
  • ログ: /home/ubuntu/.local/state/ndf/mvv-gate.jsonl の 2026-11-01T07:27:18+00:00 の行

理由

  • Mission と Vision: スプリントの配布の版をプランを流す時点に決めるので、並行するスプリントが先に版を出しても conductor がプランを作り直さずに済む。まとめの記録 PR も流す時点に探すので、人の手当てが要る所を減らす変更であり、工程を人の判断が要る所だけで止める方向に沿う。
  • Value 1 と Value 10: 開発版 10.27.6-dev.1 を Claude Code と Codex の 2 経路で導入し、版と中身の一致を確かめた。CI は 23/23 で合格し、cross-review は 2 ラウンドで見送った指摘は 0 件。設計で未確認の点は、実装のテストかリリース後テストへ回す形で一覧になっており、配布の後でしか取れない実測は判定の対象外として扱った。
  • Value 2: 承認資料に、決めたこと・未検証のまま残ること・戻し方がそろっている。足りない材料(PR の本文の未検証・残る危険)も「無い材料」として明示されている。
  • Value 4 と Value 6: 版を決める処理を release_version に、記録の PR を探す処理を record_find に寄せた。判断の要らない手順をスクリプトに集め、共通の層で直している。
  • 必ず承認が要る操作: 指示書(AGENTS.md / CLAUDE.md)と .ndf/ の変更は無く、C7 に当たらない。配布は同じリポジトリの main とタグ、GitHub Release であり、C5(他のリポジトリへの公開)に当たらない。版の上げ幅(PATCH の系列)と取得元・既定ブランチは変えておらず、版を決める時点と止まる条件を変えるだけなので、P1 にも当たらないと判断した。

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