何を見つけたか
外部プロセスの完了を待つループが、上限を持たない形で 4 箇所にある。
plugins/ndf/skills/external-ai/references/cli-codex.md:114
plugins/ndf/skills/external-ai/references/cli-codex.md:160
plugins/ndf/skills/external-ai/references/cli-agy.md:124
plugins/ndf/skills/qa-security-scan/03-report-template.md:128
codex と qa-security-scan の 3 箇所は until grep -q '^tokens used$' …; do sleep 30; done の形、
agy の 1 箇所は until ! kill -0 $PID 2>/dev/null; do sleep 30; done(プロセスの終了待ち)の形で、
いずれも待つ時間の上限を持たない。
$ grep -rn "until " plugins/ndf/skills/external-ai plugins/ndf/skills/qa-security-scan
plugins/ndf/skills/external-ai/references/cli-codex.md:111:until ! ps -p $PID; do sleep 30; done
plugins/ndf/skills/external-ai/references/cli-codex.md:114:until grep -q '^tokens used$' /tmp/codex-err.log 2>/dev/null; do
plugins/ndf/skills/external-ai/references/cli-codex.md:160:until grep -q '^tokens used$' /tmp/codex-err.log 2>/dev/null; do
plugins/ndf/skills/external-ai/references/cli-agy.md:124:until ! kill -0 $PID 2>/dev/null; do
plugins/ndf/skills/qa-security-scan/03-report-template.md:128:until grep -q '^tokens used$' /tmp/sec-scan-err.log 2>/dev/null; do
cli-codex.md:111 は「永久ループ化しうる」例として示された書き方で、手順ではない。
4 箇所のファイルを timeout / MAX_WAIT / deadline / SECONDS で探すと、当たりは cli-agy.md の
--print-timeout(agy 自身へ渡す引数、44 行ほか)だけで、待ちのループに上限を置く記述は無い。
agy が --print-timeout を過ぎても終了しなければ、cli-agy.md:124 の待ちは終わらない。
どこで見つけたか
上記の 3 ファイル。並行開発バッチ 07 の担当 A(#280)の設計工程で、完了待ちの仕組みを
持つ箇所を洗い出していて見つけた。
なぜこの変更の範囲外なのか
#280 の結論は「monitor.py を切り出さず、共通層へ移すだけにする」である。上限のない
待ちが残ることは、切り出しの要否とは別の課題である。 monitor.py の前提(識別子が整数、
プロセス番号の控え)とこの 4 箇所は噛み合うため、置き換えは可能だが、それは #280 の
受け入れ条件に含まれない。置き換え先は plugins/ndf/scripts/lib/monitor.py である
(cli-agy.md の「完了検知」も、cross-review は完了判定を monitor.py で行うと案内している)。
直さないと何が起きるか
外部の CLI が結果ファイルを書かずに終わったとき、または固まって終了しないとき、待ちが終わらない。
monitor.py が持つ早期エラーの検知・stall・hard timeout のいずれも働かないため、利用者が中断するまで進まない。
実行する側のツールの時間上限で途切れる場合は、完了を見ないまま先へ進む。 手順を書き写して
実行すると、待ちはホストのツールの上限で切られる(Claude Code の Bash は 1 回 600 秒まで、前景の
sleep は拒否される)。途切れた時点で外部の CLI が終わったかどうかが分からず、結果の回収へ進んでしまう。
関連
ホストのツールの上限(600 秒)を超える待ちは、区切って待つ形が使える。
plugins/ndf/skills/cross-review/scripts/bg-wait.sh は run で背景へ回し、wait を 124 が返る
あいだ別の呼び出しとして呼び直す(1 回の待ちは既定 540 秒、上限 540 秒)。cross-review の
骨組みはこの形で monitor.py --phase review を待っている(cross-review/SKILL.md:236-237)。
由来
issue #280
何を見つけたか
外部プロセスの完了を待つループが、上限を持たない形で 4 箇所にある。
codex と qa-security-scan の 3 箇所は
until grep -q '^tokens used$' …; do sleep 30; doneの形、agy の 1 箇所は
until ! kill -0 $PID 2>/dev/null; do sleep 30; done(プロセスの終了待ち)の形で、いずれも待つ時間の上限を持たない。
cli-codex.md:111は「永久ループ化しうる」例として示された書き方で、手順ではない。4 箇所のファイルを
timeout/MAX_WAIT/deadline/SECONDSで探すと、当たりはcli-agy.mdの--print-timeout(agy 自身へ渡す引数、44 行ほか)だけで、待ちのループに上限を置く記述は無い。agy が
--print-timeoutを過ぎても終了しなければ、cli-agy.md:124の待ちは終わらない。どこで見つけたか
上記の 3 ファイル。並行開発バッチ 07 の担当 A(#280)の設計工程で、完了待ちの仕組みを
持つ箇所を洗い出していて見つけた。
なぜこの変更の範囲外なのか
#280 の結論は「
monitor.pyを切り出さず、共通層へ移すだけにする」である。上限のない待ちが残ることは、切り出しの要否とは別の課題である。
monitor.pyの前提(識別子が整数、プロセス番号の控え)とこの 4 箇所は噛み合うため、置き換えは可能だが、それは #280 の
受け入れ条件に含まれない。置き換え先は
plugins/ndf/scripts/lib/monitor.pyである(
cli-agy.mdの「完了検知」も、cross-review は完了判定をmonitor.pyで行うと案内している)。直さないと何が起きるか
外部の CLI が結果ファイルを書かずに終わったとき、または固まって終了しないとき、待ちが終わらない。
monitor.pyが持つ早期エラーの検知・stall・hard timeout のいずれも働かないため、利用者が中断するまで進まない。実行する側のツールの時間上限で途切れる場合は、完了を見ないまま先へ進む。 手順を書き写して
実行すると、待ちはホストのツールの上限で切られる(Claude Code の Bash は 1 回 600 秒まで、前景の
sleepは拒否される)。途切れた時点で外部の CLI が終わったかどうかが分からず、結果の回収へ進んでしまう。関連
(
plugins/ndf/scripts/lib/limits.py)が持ち、レビューの工程は 1200 秒になった。担当の CLI の上限は、環境変数で解決した監視の上限 + 120 秒(既定 1320 秒)を導く。置き換え先の
monitor.pyは--phase <工程>で上限を表から引くため、待ちに上限を持たせるための前提は揃っている
ホストのツールの上限(600 秒)を超える待ちは、区切って待つ形が使える。
plugins/ndf/skills/cross-review/scripts/bg-wait.shはrunで背景へ回し、waitを 124 が返るあいだ別の呼び出しとして呼び直す(1 回の待ちは既定 540 秒、上限 540 秒)。cross-review の
骨組みはこの形で
monitor.py --phase reviewを待っている(cross-review/SKILL.md:236-237)。由来
issue #280