Skip to content

fix(session): Stop の直後に送信なしで届いた pre_tool_use で新しいターンを開かない - #3441

Merged
Kewton merged 3 commits into
developfrom
feature/3437-provisional-turn-after-stop
Oct 8, 2026
Merged

Kewton merged 3 commits into
developfrom
feature/3437-provisional-turn-after-stop

Conversation

@Kewton

@Kewton Kewton commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Summary

サーバーは TURN_ACTIVITY_EVENTS(user_prompt_submit / pre_tool_use / post_tool_use)でターンを開く。2026-10-07T23:41Z、Claude の Stop の 2.5 秒後に送信の無い pre_tool_use(AskUserQuestion) が届き、サーバーが新しいターンを開いたまま Stop が来なかった。wait はこのターンを 3 時間待った(wait 側は #3432 で上限を付けた)。この PR はサーバー側の根を直す。

原因(実測):

  • 本番 logs/server.log.1: 23:41:32.759Z Stop → 23:41:35.219Z pre_tool_use(AskUserQuestion)。user_prompt_submit は無い。以後 Stop は来ない
  • Claude の transcript には、セッション全体に AskUserQuestion の tool_use が 1 件も無く、subagents も無い。Stop hook は継続を止めていない。この pre_tool_use は、Stop で閉じた会話の続きではなく、会話記録に残らない経路から来た(送り主は hook の payload がログに残っておらず特定できない)
  • 全 4 ログで「Stop の後、送信なしで最初に来た pre/post_tool_use」は 31 件。Claude は本件の 1 件だけで、残り 30 件は Command Code(user_prompt_submit を持たず pre_tool_use でターンが始まるソース)

変更: 送信を報告するソース(supportedEvents に user_prompt_submit を宣言。実質 Claude)からの pre_tool_use は、直近のターンがエージェント自身の stop で閉じていれば新しいターンを開かない。ツール id ではなく、intake が source の capabilities から読んだ宣言(AgentEventRecord.promptOpensTurns)で判定する。

Refs #3437(develop 向けのため Closes は使わない)

範囲外(この PR では扱わない)

  • src/lib/hooks/agent-event-intake.ts:390 の recordQuestionIfAsked は、会話外の AskUserQuestion でも質問の記録を残す
  • hook の payload(session_id / agent_id 等)を intake がログに残さないため、本件の送り主を後から特定できない
    (追跡 Issue にするかは run の完了報告で判断する)

Test plan

  • commandmatedev verify --task: work-evidence / scope / env-clean / lint / typecheck / unit(テスト全体)すべて PASS(exit 0)
  • 新規 tests/unit/lib/session/agent-event-turn-after-stop-3437.test.ts(11 件。判定行を外すと陽性 2 件が落ちることを確認)
  • develop 取り込み後に npx tsc --noEmit、tests/unit/lib/session・tests/unit/session・guards・docs が合格。既存の it/describe の削除 0 件
  • CI

🤖 Generated with Claude Code

Kewton and others added 3 commits October 8, 2026 18:28
## 原因(実測)
- 本番 logs/server.log.1(worktree commandmate-issue-3420, instance claude):
  23:40:24.052Z user_prompt_submit → 23:41:32.759Z agent-event-stop-applied →
  23:41:35.219Z ask-user-question-recorded + agent-event-received pre_tool_use
  (detail AskUserQuestion)。user_prompt_submit は無い。23:41:35.954Z の
  detection-divergence は scraper=ready(input_prompt) / structured=running
  (hook_pre_tool_use)。23:42:32.785Z notification(idle_prompt)(Stop の 60 秒後
  =その間 composer は空いたまま)。以後 Stop は来ない。
- transcript 07a5ddd9-....jsonl: 23:41:32.216Z の最終 assistant text →
  23:41:32.760Z Stop hook_success → stop_hook_summary(preventedContinuation
  false) → turn_duration で終わり、その後の記録は last-prompt / cost-state のみ。
  AskUserQuestion の tool_use はセッション全体に 1 件も無く、subagents も無い。
  同時刻の他プロジェクトの transcript(CommandAgent-develop / issue-3429 /
  MyCodeBranchDesk)にも AskUserQuestion は無い。
- よって、この pre_tool_use は Stop が閉じた会話の続きではない(Stop hook は継続を
  止めておらず、画面にピッカーも出ていない)。Claude プロセス内の本会話以外の
  経路(会話記録に残らない内部の後処理エージェント等と推定。hook の payload は
  ログに残っておらず、送り主までは特定できない)から来た。サーバーはこれを
  「開いていないので開く」と読んで Stop の来ないターンを開いた。
- 全 4 ログで「Stop の後、送信なしで最初に来た pre/post_tool_use」を数えると 31 件。
  claude は本件の 1 件だけ(2.5 秒後)。残り 30 件はすべて command-code
  (user_prompt_submit を持たず pre_tool_use でターンが始まるソース)。

## 扱い(候補 a を絞って採用)
- 送信を報告するソース(supportedEvents に user_prompt_submit を宣言)からの
  pre_tool_use は、直近のターンがエージェント自身の stop で閉じていれば新しい
  ターンを開かない(ターン・表示とも不変)。宣言は intake が source の
  capabilities から読み、AgentEventRecord.promptOpensTurns で渡す(ツール id は
  見ない)。該当するのは実質 claude のみ(codex/gemini/copilot/opencode-v2 は
  pre_tool_use を出さない)。
- (b)(c) を採らない理由: (b) 開いたうえで閉じる条件を足すと、wait が採用する
  openedAt が先に動いてしまう。(c) 直前のターンに属させると閉じたターンが
  running に戻り、Stop が来ないので同じ問題になる。
- 壊さないもの: 通常のターン(送信→活動→Stop)は開いたターンを続けるので不変。
  post_tool_use は従来どおり開く(Stop hook が block して続いた場合も、claude は
  AskUserQuestion の post_tool_use でターンを開く)。stale / scraper_evidence /
  resync_idle / session_end / generation の後は開く(サーバー側の推定で閉じた
  ターンの続きを潰さない)。Command Code の pre_tool_use 始まり・antigravity の
  自己再開 post_tool_use(#2614)は user_prompt_submit を宣言しないので不変。
  時間窓は使わない(送信を報告するソースのターンは必ず送信で始まるため)。

## 対になる場所
- TURN_ACTIVITY_EVENTS(src/lib/session/provisional-turn.ts)と CLI の写し
  TURN_OPENING_EVENT_TYPES(src/cli/commands/wait.ts): 集合は変えないので両方
  不変。status-contract-1926 の一致はそのまま。
- src/lib/agent-health/hook-observation.ts の TURN_ACTIVITY_EVENTS の写し: 集合
  不変のため変更不要(範囲外)。
- CLI wait(adoptTurnStart): turn record の openedAt を読むので、ターンが開かな
  ければ閉じたターンのまま turnSettled になる。変更不要。
- 古い動き(Stop の後の pre_tool_use で開く)を claude で固定したテスト: 探した
  (tests/unit/session/turn-model.test.ts, agent-event-turn-join-3330,
  lib/session/agent-event-turn-3375, api/hooks-agent-event)が無かった。
  command-code の pre_tool_use で開くテストはそのまま通る。
- docs の ja/en: 該当記述なし。

## 本文に無い指摘
- src/lib/hooks/agent-event-intake.ts:390 recordQuestionIfAsked は同じ
  pre_tool_use の質問を記録する(ask-user-question-recorded)。本件のような
  会話外の AskUserQuestion でも質問レコードが残る。ターンとは別の状態なので
  今回は変えていない。
- hook の payload(session_id / agent_id 等)を intake がログに残さないため、
  本件の送り主を後から特定できない。

## 検証
- npx vitest run tests/unit/lib/session/agent-event-turn-after-stop-3437.test.ts:
  11 passed。判定行を外すと陽性 2 件が落ちることを確認(変異注入)。
- 関連(tests/unit/lib/session, tests/unit/session, tests/unit/hooks,
  tests/unit/lib/hooks, tests/unit/cli/commands, api/hooks-agent-event 等):
  421 files / 6491 tests passed。
- npm run lint / npx tsc --noEmit: exit 0。
- ~/.commandmate / ~/.local/state の ls -la を前後で比較: エントリの増減なし
  ($HOME と ~/.local/state/commandagent の mtime のみ変化。別プロセス由来)。
  ~/.commandmate/service.json は前後とも無い。tmux・サーバーは起動していない。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Kewton Kewton added the bug Something isn't working label Oct 8, 2026
@Kewton
Kewton merged commit 663a8af into develop Oct 8, 2026
3 of 15 checks passed
@Kewton
Kewton deleted the feature/3437-provisional-turn-after-stop branch October 8, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant