# feat: 会話の区切りでユーザー要求と達成状況のサマリーを出す (#942)

## 目的

ターミナルのセッションが**ユーザーに制御を返す区切り**（作業完了 / 質問して待つ）で、
会話全体を通じたユーザーの要求と、達成 / 未達成を短くまとめて出す。

グリッドで複数セルを見張っていると、戻ってきたときに「何を頼んだか」「どこまで終わったか」が
スクロールバックを遡らないと分からない。それを回答の末尾に常に置く。

## 決めたこと

| 論点 | 決定 | 理由 |
|---|---|---|
| 注入手段 | `claude --append-system-prompt`（**inline**） | `--append-system-prompt-file` はサンドボックス spawn（コンテナ）からホストのパスが読めない。`--settings` が既に inline JSON なのと同じ判断。1.5KB 程度なので、シードプロンプトを positional から追い出した tmux の引数長制限には遠く届かない |
| 適用範囲 | **全 claude セッションで既定 ON**、設定なし | issue で決定済み |
| 無効化のフックポイント | プロンプト本文を専用モジュールに切り出すところまで | 設定キーは要求されていないので作らない。切り出してあれば後から差し込める |
| 出力の言語 | ラベルも含めユーザーの言語に合わせる | 日本語で会話しているのに見出しだけ英語、を避ける |
| 機械可読マーカー | **今回は入れない**（`---` の区切り線のみ） | ロスターへの取り込みは別の話。半端なマーカーはターミナル上のノイズにしかならない。#942 の決めることに残す |
| codex | 変更なし | `--append-system-prompt` 相当のフラグが無い |

## プロンプトの設計

「変なタイミングで出さない」が一番効く要件なので、**出す条件より出さない条件を厚く**書く。

- **出す**: 作業を終えて締めるとき / 質問・判断待ちで止まるとき
- **出さない**: 作業継続中（ツール呼び出しが続く）、事実質問に 1〜2 行で答えただけ、確認、挨拶
- **迷ったら出さない**: 毎ターン出るサマリーはノイズで、ノイズは読まれなくなる

「ユーザーの要求」は**会話全体の standing goal**であって直近のメッセージではない。
最初の要求を述べ、以降のターンで詰めた内容は修飾として畳み込む。話題が本当に変わったときだけ
新しい要求に切り替える。

「達成した」は**検証したものだけ**。走らせたテスト、存在するファイル、読んだ出力。
意図や「動くはず」は達成に数えない。

## 実装

1. `server/agents/session-summary-prompt.ts` — プロンプト本文（新規）
2. `server/agents/claude-args.ts` — `buildClaudeArgs` に `--append-system-prompt` を追加
3. `test/server/agents/claude-args.spec.ts` — 既存の完全一致アサーションを更新 + 新規ケース
4. `test/server/agents/session-summary-prompt.spec.ts` — 本文が満たすべき条件を固定（新規）
5. README / docs の更新

`--add-dir` は可変長引数なので**必ず最後**という既存の制約は維持する。

## 検証（実際に `claude -p` に食わせて確認）

プロンプトは書いただけでは効いているか分からないので、実物で確かめた。

| ケース | 期待 | 結果 |
|---|---|---|
| 事実質問 1 問（`const` と `let` の違い） | 出さない | 出なかった |
| ファイルを 1 つ作る作業を完了 | 出す | **最初の版では出なかった** → プロンプト修正後は出た |

最初の版には「迷ったら出さない」を入れていた。ノイズ対策のつもりだったが、
**1 ファイル書いて終わった時点でも黙る**ようになってしまった。そこがまさに出してほしい瞬間なので、
除外条件を「作業継続中」「そもそも作業がない（知識で答えた質問・挨拶・相槌）」の**2 つに閉じ**、
「小さい作業でも出す。1 ファイルだったから省く、は理由にならない」を明記した。

修正後の実出力（sonnet、日本語の会話）:

```
---
**依頼内容**: probe-out.txt というファイルを作成し、中に "hello" と書く。
**実施内容**: .../probe-out.txt を作成し、"hello" を書き込み完了。
```

`---` の区切り、ユーザーの言語のラベル、報告することが無い「未実施」行の省略、いずれも意図どおり。

**haiku では修正後も出なかった**（同じプロンプト・同じ入力で sonnet は出す）。
指示追従の強さの問題で、mulmoterminal の既定は haiku ではないため許容する。

## プロンプトに `"` を書いてはいけない（Codex レビューで発覚）

上の修正で `"1 ファイルだったから" は理由にならない` と**引用符付き**で書いたところ、
Windows の argv 不変条件を壊した。

`.cmd` で入った claude は cmd.exe 経由で起動され、コマンドラインが cmd と子プロセスの CRT に
**二度**パースされる。両者の引用符の解釈が食い違うため、JSON ペイロードはファイル渡しにして
argv から引用符を一掃してある（#813）。`test/server/session/session-settings.spec.ts` の
「the argv a Windows spawn ends up with」がその不変条件を固定していて、そこに引っかかった。

直し方は 2 つあった。引用符ごと外して言い換えるか、活版クォート `“ ”` に替えるか。
**後者を採った** — パーサにとって意味を持たない文字なので不変条件は保たれ、強調の言い回しも
残る。散文に引用符を使う道を塞がずに済む。

Codex はもう一つ「win32 ではプロンプトをファイル経由で渡す」案も挙げていたが採らない。
サンドボックス spawn はホストのパスを読めないコンテナで動く、という `--settings` と同じ理由。

加えて **`session-summary-prompt.spec.ts` 側にも**「ASCII の `"` を含まない」テストを置いた。
spawn 全体の不変条件テストは正しく落ちるが argv を指すだけで「どの文が壊したか」は言わない。
文面を直す人は散文を編集しているだけのつもりで、遠く離れた spawn のテストを見に行かない。

**手順上の反省**: 初回検証のあとプロンプトを直した際、`test/server/agents/` だけ再実行して
フルテストを回さなかった。CLAUDE.md の「全部走らせる」を守っていれば push 前に捕まえられた。

