# 依頼 v8：ロボットが運んだ皿を「卓に着いた時点」で届いたにする／往復の記録を直す（2026-10-24）

段階C（unity_stageC_gaps_v1.md）で見つかった Unity の作りの問題を2つ直す。エイイチと決めたこと。
ロボットの速さ・止まり方・よけ方・料理の割り当てなど、ほかの動きは変えない。RobotSpec・PeopleSpec・ServiceSpec・SimParams の値も変えない。
**v7b（claude_code_request_v7b_stageB_resume.md）はこの依頼に入れたので、v7b は送らない。**

## 1. ロボットが運んだ皿を、卓に着いた時点で「届いた」にする

いまの作り：
- ロボットは卓に着いた時点で `StopsCompleted = _stopIndex + 1` にしている（ServingRobotController.CheckArrival、Phase.Dwell に入るところ）
- ところが、皿を届いたにする `ServiceOps.DishArrived(d, true)` は、往復の最後に受け渡し口へ戻ったとき（ServiceOps.OnRobotRunDone）にしか呼ばれない
- 客が食べ始めるのは、最初の料理が全部届いたとき（GroupManager の AllFirstDishesArrived）なので、ロボットが戻るまで客は食べ始めない
- 段階Bの記録では、これで料理の待ちが1皿あたり1.0〜1.3分長く出ていた

直し方：
- ロボットが卓に着いた時点（Phase.Dwell に入るとき。受け渡しの25秒＝tableDwellSec の前）に、その止まる所に対応する皿を `DishArrived(d, true)` にする。サイトの試算と同じ（試算は卓に着いた時点で届いたにし、そのあと受け渡しの25秒）
  - 例：ServingRobotController に「止まる所に着いた」イベント（引数：ロボット・止まる所の番号）を足し、ServiceOps が受けて皿を届いたにする
  - 止まる所の番号と皿の対応を、StartRun に渡すときの並び（ServiceOps の stops を作るループ）で持っておく。いまのループは `SlotOf` が null の皿を飛ばすので、「止まる所の番号＝batch の番号」とは限らない。対応表で引くこと
- 往復の最後（OnRobotRunDone）では、もう届いた皿は何もしない。まだ届いていない皿（arrivedS < 0）だけを、いままでどおり受け渡し口の先頭へ戻す（打ち切り・取りやめ・動けなくなった・着かなかった）
- dishes.csv の carry_s（届いた − 最後に載せた）は、この直しで自然に「卓に着くまで」になる。列は変えない
- 食べ始める・会計に進むの決まり（GroupManager）は変えない。届く時刻が早くなるだけ

## 2. 往復の記録（trips.csv）の皿の欄が、次の往復の行に書かれている

原因：
- ExperimentRunner は、ロボットを出すときに `c.TripCompleted += OnRunDone` を先に足し、そのあと `ops.RegisterRobot(c)` で ServiceOps の OnRobotRunDone を足している
- そのため、往復が終わると ExperimentRunner.OnRunDone が先に呼ばれる。そこで `ops.LastBatchOf(r)` を読むが、ServiceOps がまだその往復の皿を `_lastBatch` に入れていないので、前の往復の皿が入る
- 段階Bの trips.csv では、dishes・dish_ready_s・dish_arrived_s・undelivered が、同じロボットの1つ前の往復のものになっている（1回目の往復は dishes=0）

直し方：
- 呼ばれる順番に頼らない形にする。例：ServiceOps が自分の片づけを終えてから出す「往復の記録を書いてよい」イベントを ExperimentRunner が受ける、またはロボットが往復を始めるときに皿の並びをその往復に持たせる
- trips.csv に列を足す（今ある列の並びは変えず、後ろに足す）
  - `run_start_s`：その往復を始めた時刻（ほかの _s 列と同じ時刻の基準）
  - `dish_ids`：その往復で載せた皿の番号（; 区切り。dishes.csv の dish_id と同じ）
  - `stop_arrived_s`：止まる所ごとに着いた時刻（; 区切り。着かなかった所は -1）
- groups.csv に列を足す（後ろに）：`eat_start_s`（食べ始めた時刻。食べ始めなかった組は -1）
- conditions.json の trips_csv_columns も新しい列に合わせる

## 3. 段階Bの押し直しを安全にする（v7b の中身を、版を入れて直したもの）

- 台帳と照らし合わせるキーを「店・ロボットの台数・乱数の種・Unity の版（unity_cs）」にする。出力フォルダ名（日付入り）では照らし合わせない
  - 同じ版・同じ条件・同じ種の行が台帳にあれば、日付が違っても回さない
  - 版が違えば回す（今回の直しのあと、同じ18回をもう一度回すため）
- 出力フォルダ名に版を入れる：`{日付}_stageB_cs{版}_{店}_r{台数}_s{種}`。書き出す先のフォルダが既にあるときは上書きしない。末尾に `_2`、`_3` … を付けた新しいフォルダに書く
- run_info.json の pending_changes が空になっていた（保留が2件あったはず）。保留の数を正しく書く。数え方が分からなければ「不明」と書く
- 台帳（unity_ledger_v1.csv）の列は変えない。既存の18行と、既存の出力フォルダは触らない

## 4. 確かめ（Check-in の前に）

1回だけ回す（③ fam_p1・ロボット2台・種0、ServiceSim / Site Run から）。回したあと、出力の CSV で次を確かめて報告に書く：

- dishes.csv：ロボットが運んだ皿すべてで、arrived_s が trips.csv のその往復の stop_arrived_s のどれかと一致し、その往復の終わり（run_start_s + total_s）より前
- trips.csv：どの行も、dish_ids の皿が dishes.csv で同じロボットが運んだ皿で、dish_ready_s がその皿の ready_s と一致する（1つ前の往復の皿が入っていない）。1行目の dishes が0でない
- groups.csv：eat_start_s が、その組の最初の料理が全部届いた時刻と一致する
- 受け渡し口に戻されて人が運んだ皿（requeues.csv）が、二重に「届いた」になっていない（dishes.csv に同じ dish_id が2行ない・robot_dishes＋staff_dishes＝皿の数）
- summary.json の dish_wait_parts.robot.carry_mean_min が、段階B の同じ条件（2.90分）より短くなっている

## 5. 報告

- 直したファイルと、変えた所の説明
- 4 の確かめの結果（数字つき）
- Check-in の番号
- 回した1回の出力フォルダ名

段階Bの18回は、私（Claude）が4の結果を見てから回してもらう。
