Repository navigation
fix(cli): wait が Stop の届かないターンで --timeout まで空回りしないようにする - #3432
Merged
Merged
Conversation
## 23:41:35 のターン開始は何だったか(本番 logs/server.log.1、読むだけ)
[2026-10-07T23:40:24.052Z] agent-event-received {"worktreeId":"commandmate-issue-3420",...,"event":"user_prompt_submit"}
[2026-10-07T23:41:32.759Z] agent-event-stop-applied {"worktreeId":"commandmate-issue-3420",...}
[2026-10-07T23:41:35.219Z] ask-user-question-recorded {"worktreeId":"commandmate-issue-3420",...,"questionCount":1,"optionCounts":[3]}
[2026-10-07T23:41:35.219Z] agent-event-received {"worktreeId":"commandmate-issue-3420",...,"event":"pre_tool_use","detail":"AskUserQuestion"}
[2026-10-07T23:41:35.954Z] detection-divergence {"scraperStatus":"ready","scraperReason":"input_prompt","structuredStatus":"running","structuredEvent":"pre_tool_use","structuredEventAt":1791416495218}
[2026-10-07T23:41:46.036Z] detection-divergence-resolved {"durationMs":10082,"polls":2}
[2026-10-07T23:42:32.785Z] agent-event-received {...,"event":"notification","detail":"idle_prompt"}
→ turnStartedAt=1791416495218 は Stop の 3 秒後に届いた hook の
pre_tool_use(AskUserQuestion)。送信(user_prompt_submit / session-key-sender)は無く、
Auto-Yes の応答も 3420 には記録なし(auto-yes-poller は起動ログのみ)。画面は ready/input_prompt
のまま(ダイアログは描かれていない)。サーバーはこのターンを scraper_evidence で閉じたが、
その後 Stop は来なかった。
## なぜ 60 秒で打ち切られなかったか
出力は #1975 のゲート(promptLedgerStage、PENDING_PROMPT_HOLD_MS で上限あり)ではなく、
#1839 の unsettledTurnStage。#1975 の保持は turnStartedAt === null のときだけ働く。
今回は adoptTurnStart が閉じたターン(openedAt が wait 開始より後)を採用したため
turnStartedAt が立ち、turnSettled は lastStopEventAt(…492247) < turnStartedAt(…495218)
で永久に false。#1839 のゲートには上限が無かった。
## 直し方と根拠
unsettledTurnStage に releaseOrphanTurn を足す(関数の他の部分は変えない)。次をすべて満たす
ときだけ、ORPHAN_TURN_HOLD_MS(=PENDING_PROMPT_HOLD_MS, 60 秒)待ってから完了に進む
(basis=hook_stop。Note 行を stderr に出す)。
- サーバーがターン記録を出しており、採用したそのターン(openedAt === turnStartedAt)を
closedBy=scraper_evidence / stale で閉じている(サーバー自身が稼働中と見ていない)
- チャット台帳の最新の送信より lastStopEventAt が新しい(=最新の送信は Stop で答えられた)。
これが #1839 の 529(送信の後に Stop が無い)と今回(Stop の後に送信なしで開いたターン)を分ける。
台帳が読めない/送信が無い場合は従来どおり保持
- 画面が 60 秒変わらない(content の変化で計測をやり直す)。作業中・描画中は打ち切らない
--timeout / --stall-timeout は deadlineStage が先に判定するので、短ければそちらが優先(テストで固定)。
## テスト
tests/unit/cli/commands/wait-orphan-turn-3429.test.ts(新規)
- 陽性: 3420 の形で 30 秒時点は保持、60 秒経過後に exit 0 / basis=hook_stop
- --timeout 20 が先に効く
- 陰性: 最新の送信に Stop が無い(529)/ターンが開いたまま出力が続く/閉じたが画面が変わり続ける/
台帳に送信が無い → いずれも --timeout で 124
変異確認: release を無効化→陽性が落ちる、outstandingPrompt 判定を外す→529 陰性が落ちる、
画面変化のリセットを外す→画面変化の陰性が落ちる。
既存 #1839 / #1975 / #1930 / #3330 のテストは無変更で通過(tests/unit/cli 209 files / 3405 tests)。
npm run lint、npx tsc --noEmit 通過。
## 対になる場所
- 同じ保持の写し: promptLedgerStage(#1975、上限 60 秒あり)、selfResumeHoldStage(#2614、
上限 30 分あり)。どちらも上限があり変更不要。上限値は PENDING_PROMPT_HOLD_MS を再利用
- docs: docs/user-guide/cli-operations-guide.md の #1839「判定ルール」に例外を追記。en 版の
同ガイドは存在しない(docs/en を確認、該当なし)
- 古い動きを固定したテスト: wait-upstream-fault-1839(source 能力を出さないので新経路に乗らない)、
wait-turn-record-1930「holds on a turn ... still open」(closedAt=null で新経路に乗らない)。変更せず通過
- サーバー側 TURN_ACTIVITY_EVENTS(src/lib/session/provisional-turn.ts)が Stop 後の
pre_tool_use を新しいターンとして開く点が根の一つだが、scope 外なので変更しない
本文に無い指摘: src/lib/session/provisional-turn.ts:66 Stop 直後に届いた送信なしの pre_tool_use
(AskUserQuestion)で新しいターンを開くため、CLI 側の待ちが Stop を失う
## wait.ts の他の「待ち続ける」ゲート
unclassifiedStage(UNCLASSIFIED_DWELL_MS 上限)、Auto-Yes 保持(autoYesGrace 上限)、
startingSince(サーバー側で上限)、self-resume(30 分上限)、#1975(60 秒上限)を確認。
上限が無かったのは #1839 の unsettledTurnStage のみで、本変更で対処。
--on-prompt human の保持は人の応答待ちという仕様なので対象外。
## 環境
tmux・サーバー・バックグラウンドプロセスは起動していない。$HOME 配下へ書き込みなし。
~/.commandmate と ~/.local/state の ls -la を前後で比較: ~/.commandmate/hooks の件数
(971→973) と ~/.local/state/commandagent の mtime、$HOME 直下の件数 (103→102) が変化。
いずれもこの作業の操作によるものではない(稼働中の本番サーバー・他エージェントによる変化)。
service.json は worktree に存在しない。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 tasks
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
2026-10-08 の orchestrate run で、
commandmatedev wait --verifyがワーカーの作業完了後も 3 時間空回りし、exit 124 になった(#3429)。原因(本番の
logs/server.log.1で特定): ワーカーの Stop(23:41:32.759Z)の 3 秒後に、送信を伴わない hook のpre_tool_use(AskUserQuestion)が届き、サーバーがそれを新しいターンの開始として記録した(turnStartedAt=1791416495218)。サーバーはそのターンをscraper_evidenceで閉じたが、Stop は来なかった。waitの #1839 の未完ターンのゲート(unsettledTurnStage)には上限が無く、lastStopEventAt < turnStartedAtのまま永久に保持していた(#1975 の 60 秒の保持はturnStartedAt === nullのときだけ働く)。変更(
src/cli/commands/wait.ts):unsettledTurnStageにreleaseOrphanTurnを追加。次の条件をすべて満たすときだけ、ORPHAN_TURN_HOLD_MS(60 秒)待ってから完了に進む(basis=hook_stop、stderr に Note 行):scraper_evidence/staleで閉じているlastStopEventAtが新しい(最新の送信は Stop で答えられた。feat(cli): send 後にエージェントのターンが成立していない(上流 5xx ループ)ことを wait / capture で検知可能にする — #523 の再起票(hooks 一次ソース版) #1839 の「送信の後に Stop が無い」とはここで分かれる)--timeout/--stall-timeoutが短ければ、従来どおりそちらが優先される。Refs #3429(develop 向けのため Closes は使わない)
範囲外(この PR では扱わない)
src/lib/session/provisional-turn.ts:66: Stop の直後に届いた、送信の無いpre_tool_useで新しいターンを開く(今回の根の一つ。サーバー側)。追跡 Issue にするかは run の完了報告で判断するTest plan
commandmatedev verify --task: work-evidence / scope / env-clean / lint / typecheck / unit(テスト全体)すべて PASS(exit 0)tests/unit/cli/commands/wait-orphan-turn-3429.test.ts: 陽性(今回の形で 60 秒後に exit 0)/--timeoutが先に効く/陰性 4 本(最新の送信に Stop が無い・出力が続く・画面が変わり続ける・台帳に送信が無い → 124)。変異を入れると対応するテストが落ちることも確認(ワーカー)it/describeの削除 0 件tests/unit/guardsとtests/unit/docs(43 ファイル / 670 件)合格、changelog-fragments.mjs checkexit 0🤖 Generated with Claude Code