# feat #595 — ターミナル間の自動往復（PR 1: 1往復）

対象 issue: https://github.com/receptron/mulmoterminal/issues/595
先行: #566（受け渡し）/ #574（pull 方向）/ #582（codex のターン境界検知）

Phase 3 のうち **1往復ぶん**を実装する。往復上限・停止・レビュー/対話のモード切替は PR 2。

## 何をするか

セルの宛先メニューの `⇄` を押すと:

1. 自分の直前ターンを相手セルに送って**自動送信**
2. 相手のターン完了を待つ
3. 相手の返答を自分に送って自動送信
4. 自分のターン完了を待って終了

## 設計判断

### 完了判定は「送ったテキストが相手の prompt に現れたこと」

当初は活動フラグの `done` を待つつもりだったが、**ユーザーが見ているペインでは `Stop` が `waiting` を立てない**（`activityHookEffects` が意図的に抑制する）。実機で確認済み。`done` を待つと見ているセルで永久に待つ。

次に「(prompt, reply) の組が変化したか」で判定する案にしたが、**Codex レビューで競合を指摘された**: 相手が既にターン実行中なら、submit 後に最初に観測される変化は**無関係な進行中ターンの完了**かもしれず、誤った返答を戻してしまう。

最終形は**因果の相関**を取る: 送ったテキストは相手の次の prompt になるので、**その prompt に自分が送った内容が現れたターンだけ**を答えとして受け入れる。相関は末尾 160 文字で取る（先頭は毎回同じ枠組み文なので、前の往復のメッセージにも一致してしまう）。

これは同時に**二重送信ガード**でもある。自分の送信に対応するターンが記録されるまで次を送らないので、遅いエージェントには待つだけで、二度送ることがない。

### スロットではなくセッションを確認してから送る

`submit` は**スロットキー**（`cell-3`）宛てで、会話宛てではない。往復中にユーザーがそのセルで別セッションに切り替えると、返答が切り替え先の会話に撃ち込まれる。送信直前に**開始時のセッションをまだ動かしているか**を確認する。

### 承認待ちの検知は不要

ループは**ターン完了を観測してからしか送らない**。承認プロンプトで止まっているセッションはターンを完了しないので、送信対象にならない。待ってタイムアウトし、表面化するだけ＝安全側に倒れる。

### 戻し側は返答だけを送る

A が送ったテキストは B の `user_message` になる。そのまま戻すと **A は自分の文章を受け取る**（往復ごとに）。`/api/transcript/last-turn?as=reply` を追加し、戻し側は prompt ブロックを落として「相手からの返答」として枠づける。

### 自動送信は bracketed paste ＋ 遅延 CR

同一バーストの text+CR は claude の TUI がペースト扱いして CR を飲む。bracketed paste に CR を貼り付けても、ペーストが確定する前に CR が届いて落ちる。サーバの `attachDraftInjection` が既に踏んでいる地雷なので同じ形にする。

## ファイル

| ファイル | 役割 |
| --- | --- |
| `src/composables/exchangeRules.ts` | **純関数** — 新ターン検知（二重送信ガード）、待機判定、結果メッセージ |
| `src/composables/useCrossTalk.ts` | 1往復のオーケストレーション。fetch / submit / sleep / clock / 中断を DI |
| `useTerminalConnections.ts` | `pasteAndSubmit()` |
| `handoff-text.ts` / `session-routes.ts` | `HandoffShape` と `?as=reply` |
| `TerminalCell.vue` | 宛先メニューの `⇄`、実行中の停止ボタン |

## 敵対的レビュー観点（CLAUDE.md 必須）

| 失敗モード | 対策 | テスト |
| --- | --- | --- |
| 副作用の二重実行 | 自分の送信に対応するターンが現れるまで次を送らない | 壊すと赤くなることを確認済み |
| 待機中の中断 | 各 await の前後で中断確認。セル unmount でも停止 | 同上 |
| 過剰なマッチ | 完了判定を**送信テキストとの相関**に限定。「何か変化した」では受け入れない | 同上 |
| 宛先の取り違え | 送信直前にスロットが開始時のセッションを動かしているか確認 | 同上 |

## 非スコープ（PR 2）

- 往復上限（3往復）
- レビュー / 対話のモード切替と VERDICT 終了判定

## 既知の制約

- **起動直後のセルに送ると入力が失われる**。codex の TUI が立ち上がりきる前のペーストは捨てられる（実測で確認）。実運用では「直前ターンを持つセル」を相手に選ぶので通常は起きないが、一度も使っていないセルを相手にすると待ってタイムアウトする
