# fixtures.md — サンプルデータ仕様

DB接続前の開発は、すべて `lib/fixtures/` のサンプルデータで行います。
本書は「サンプルデータに**必ず含めるべきケース**」の一覧です。

正常系だけのきれいなデータで作ると、本番接続時に表示崩れ・判定ミスが一気に噴出します。
**異常系・境界値を先に仕込んでおくこと**が目的です。

---

## 1. 方式

```
lib/fixtures/          サンプルデータ（TypeScript、型付き）
  clean-logs.ts
  clean-task-reports.ts  ★清掃実績の新しい取得元（指示書AQ 決定2）。§2.6★
  task-status-rule.ts    ★`status` の分布と巡回列（依存の無い葉モジュール）。§2.6★
  prechecks.ts
  reports.ts
  ds-devices.ts
  facilities.ts
  schedules.ts
  inspections.ts
lib/db/
  index.ts             USE_FIXTURES で fixtures / prisma を切り替える
```

- 環境変数 `USE_FIXTURES=true` の間は、APIルートが fixtures を返す
- DB接続時は `USE_FIXTURES=false` にするだけ。**画面側のコードは一切変更しない**
- **判定ロジック（`lib/judge/`）はこの切替の外側に置く。** fixture開発中も本番同等のロジックが走る
- 元データはモック（`d-watch-mock.jsx`）の `LOG` / `PRECHK` / `DSA_INIT` / `RANK` / `ROBOTS` / `SCHED` / `DSDEV` / `INSPECTIONS` を移植し、本書のケースを追加する

### ダミー化のルール ★必須★

モックには**実在の店舗名・住所・電話番号**が入っています。移植時に必ず置換してください。

| 実データ | ダミー |
|---|---|
| ドン・キホーテ / MEGAドンキ | **バリューマート** / **メガバリューマート** |
| ユニー / アピタ | **フレッシュタウン** |
| PPIH | **ARH** |
| 住所 | 「東京都○○区1-2-3」形式の架空住所 |
| 電話番号 | `03-0000-0001` 形式（連番） |
| メール | `store001@example.com` |
| 担当者名 | 架空の氏名 |

シリアル番号（`8111R5628060011` 等）は形式が分かることに意味があるので、桁数・書式を保ったまま連番で構いません。

---

## 2. 清掃詳細ログ（clean-logs）

モックの `LOG` を移植し、以下を**追加**します。

### 2.1 判定の境界値 ★最重要★

| ケース | 完了率 | 期待される判定 | 表示（整数丸め） |
|---|---|---|---|
| ◎の下限すぐ上 | 90.1 | ◎ | 90% |
| ◎/○の境界 | 90.0 | ★◎（「90%**以上**」なので90.0は◎。D-139）★ | 90% |
| 丸めると90だが○ | 89.5 | ○ | 90% |
| ○の下限すぐ上 | 70.1 | ○ | 70% |
| ○/△の境界 | 70.0 | ★○（「70%**以上**」なので70.0は○。D-139）★ | 70% |
| △の下限すぐ上 | 10.1 | △ | 10% |
| △/×の境界 | 10.0 | △（「10%**未満**」が×なので10.0は△。★D-139 でも変わらない★） | 10% |
| ×の代表値 | 9.9 | × | 10% |
| ゼロ | 0 | × | 0% |
| 未実施 | null | × | `-` |

**この10ケースは判定ロジックのテストと同じ値を使うこと。** 画面表示とテストで挙動が一致していることを目視でも確認できます。

★**境界はすべて「上のランクに入る」**★（ユーザー指示 2026-09-16。**D-139**。旧 **D-020b** は破棄）。
★**× の境界だけは「10%未満」のまま**★なので、10.0 は △。

### 2.2 エスカレーション

| ケース | 連続日数 | 期待レベル | 適用ルール |
|---|---|---|---|
| 初回の出発失敗 | 1 | L1 | R1 |
| 完了率×（初回） | 1 | L1 | R2 |
| 2日連続△以下 | 2 | L2 | R3 |
| 3日連続 | 3 | L3 | R4 |
| 5日連続（長期化） | 5 | L3 | R4 |
| **スケジュール非存在日を挟む連続** | 実日数3・カレンダー日5 | L3 | R4 |

最後のケースが重要です。「月水金」スケジュールの施設で月・水・金と3回連続で失敗した場合、カレンダー上は5日間ですが**スケジュール存在日の連続は3日**なのでL3になります。この判定を間違えやすいので必ずデータに含めてください。

### 2.3 ステータスの組み合わせ

事前チェック1回目 / 2回目（★旧見出し 1h前ST / 30分前ST★。D-178）は以下を網羅:
- 待機中 → 待機中（正常）
- 充電中 → 待機中（1h前異常だが解消）
- ハンドルアップ → ハンドルアップ（未解消）
- 緊急停止 → 待機中（復旧）
- オフライン → オフライン
- セキュリティ有効（D-Sec契約施設のみ）
- ★事前チェックの予定が無い → `null` / `null`（画面は「-」、CSV は空のセル）★（D-171）: ★施設11 バリューマート川下店の履歴25行（6〜30日前）★。`lib/fixtures/prechecks.ts` に出てこない施設を選んだ。完了率・判定・行数は変えていない
- ★事前チェックの分数（★D-178★。列は分数ではなく順番で割り当てる。早い方が1回目）★:
  - ★既定は 60 / 30★（`PRECHECK_MINUTES_BEFORE`。`row()` の `status_1h_minutes` / `status_30m_minutes` を省略した行。status が null の回は分数も null）
  - ★60分前・10分前（設定に無い分数）★: 施設7 メガバリューマート緑野店・ロボット 8111R5628060100・昨日 01:30（待機中 / 待機中・60 / 10）
  - ★30分前の1回だけ★: 施設7・ロボット 8111R5628060177・昨日 02:00（1回目 待機中・30 / 2回目 `null`・`null`）
  - ★行は足していない★（既存の2行の分数だけを変えた。件数・id・完了率・判定は変わらない）。シードもこの分数で事前チェック行を作る

### 2.3b 結果待ち（★D-173★）

- ★施設11 バリューマート川下店・今日 03:30 の1行★が `result_pending: true`（`lib/fixtures/clean-logs.ts` の `pendingCases`）。`departed:false`・`task_status:null`・`completion_rate:null`・`×` のまま、事前チェックは無い（`null`。D-171）
- 画面: 詳細ログは出発「…」・清掃結果「結果待ち」・判定なし。ダッシュボードは「結果待ち 1」（成功率の分母に入れない）。連続日数・要レスキューには数えない（アラートは 2026-10-29 から出発失敗そのものを出さない）
- これに合わせて ★スケジュール id 34（施設11・毎日 03:30〜04:30）を足した★（曜日ごとの有効なスケジュール数 ＝ 今日の行数、の不変条件。D-050）。★今日の行は 10 → 11★、今日の `schedule_progress` は 11 / 9
- ★施設12 今日 03:30 は確定した出発失敗のまま★（§2.1 の境界値「未実施(null)」）

### 2.3c 予定外タスク（★指示書BG §2-1。ユーザー指示 2026-10-29★）

予定外タスク ＝ `clean_task_reports` のうち `clean_timetable_id` が 0 か NULL の行。`lib/fixtures/clean-logs.ts` の ★`unscheduledCleanLogs`★（2行）。

| 施設 | 開始（`started_at`） | 完了率 | 判定 | `status` | 連続 | 確かめること |
|---|---|---|---|---|---|---|
| 施設1 メガバリューマート中央本店 | 昨日 21:00 | 95.9 | ◎ | 4 | 0 | 表U1 U1-1（予定外の1行。同じ日に予定のある ◎ がある） |
| 施設11 バリューマート川下店 | 2日前 22:00 | 0（`clean_area` 0） | × | 6 | 1（L1） | 表U1 U1-2（× でも出発 ✓・未実施ではない）。施設11 は2〜5日前に予定が無いので、この日だけで連続1 |

- ★行は `unscheduledCleanLogItem()`（Prisma 版と同じ関数）で作る★。`id: null`・`row_key: "rp-<report_id>"`・`report_id` は19桁の文字列・事前チェック4列は `null`・`departed: true`・`result_pending: false`
- ★`cleanLogs`（予定のある行）には混ぜていない★ — 清掃結果（`clean-task-reports.ts`）・エリア（`areas.ts`）・シード（`scripts/seed-local-db.ts`）が予定の id を鍵に作られているため。★混ぜるのは `fixtureSource.cleanLogs()` だけ★（`lib/db/index.ts`。`scheduled_at` だけで安定に並べ直すので、予定のある行どうしの順序は変わらない）
- `cleanLogRunMeta()`（ダッシュボードの直近モードのメタ）には入れていない（予定外の行は予定の終了を持たない）
- 画面: 詳細ログの「予定」列は「予定外」（EN "Unscheduled"）、▼ を出さない（行を開かない）。CSV の「予定時刻」列も「予定外」
- ★既存の行・件数・id・判定は変えていない★（予定外の2行が増えるだけ。詳細ログの全件は 2 件増える）

### 2.4 データ量・表示崩れ

- **31日分のデータ**（日付フィルタの上限テスト用）
- **11件以上の△×行**（「もっと表示」の発火確認）
- **該当0件**（△×が1件もない日 = 全部◎の日）
- 施設名が長いケース（30文字程度）
- 1施設に複数ロボット（2台が別タスク = 2行になることの確認）

### 2.5 エリア別完了率（`GET /clean-logs/{id}/areas`）

`lib/fixtures/areas.ts`（`areaCompletions`）。★★1エリア = 1清掃ロボット★★（CLAUDE.md §8 /
TeamWorks 第8回 A9-2）。★1回の清掃の中を売り場・通路・レジ前に区切った内訳ではありません★。

★★日付と `clean_log_id` は `lib/fixtures/clean-logs.ts` の**既存の行から引いています**★★
（手書きの日付を作らない。既存ファイルは1行も変更していない）。
行の `completion_rate` は対応する清掃ログの完了率と同じ値です（一覧とモーダルで数字を食い違わせない）。
★★全体率（`overall_completion_rate`）は行の完了率（`runCompletionRate()`）の単純平均です★★（清掃1回＝1票。未実施の行は分母に入れない。
`lib/judge/kpi.ts` の `meanCleanRatio()`）。★指示書AQ §5-5 で面積ベース（Σclean_area ÷ Σtask_area）から変わり、D-155 / 指示書AS で平均する材料が `clean_ratio` 列から `runCompletionRate()` に変わりました★。
★行の完了率は面積から `runCompletionRate()` で決まります（D-155）★。面積は行の完了率から作ってあり（小数第6位）、
`runCompletionRate()` で元の完了率に戻ることを `lib/fixtures/__tests__/areas.test.ts` で確かめています。
★期間の合計どうし（Σclean_area ÷ Σtask_area）は全体率の分子・分母ではありません★。
★各行の `result_pending`（結果待ち。D-182）は対応する清掃ログの値を写しています★（新しい値は作っていない）。
施設11 の結果待ちの清掃ログに対応するエリアの行は無いので、★fixtures のエリアの行はすべて `result_pending: false`★です
（結果待ちのエリアを問題に数えないことは `lib/clean-logs/__tests__/areas.test.ts` の表6 で確かめています）。

| # | ケース | 施設 / 日 | 仕込んだ値 | 確認できること |
|---|---|---|---|---|
| 1 | 1エリアだけの施設 | 施設1 / 当日 | 90.1%（1000㎡） | ★1件でも正常（エラーにしない）★ / ◎の境界 |
| 2 | ちょうど90.0 | 施設2 / 当日 | 90.0%（1000㎡） | ★「90%以上」が◎なので ◎（D-139）★ |
| 3 | ちょうど70.0 | 施設5 / 当日 | 70.0%（1000㎡） | ★「70%以上」が○なので ○（D-139）★ |
| 4 | ちょうど10.0 | 施設8 / 当日 | 10.0%（1000㎡） | ★「10%未満」が×なので △★ |
| 5 | 0% | 施設10 / 当日 | 0%（1000㎡ / 0㎡） | ★「0%で実施した」は × だが**未実施ではない**★ |
| 6 | ★全エリア未実施★ | 施設12 / 当日 | `null`（面積0） | ★`completion_rate` は `null`（0% と混ぜない）★ / ★平均する行が0件なので全体率も `null`★ |
| 7 | ★3エリアの施設 + 担当面積が違う★ | 施設7 / 1日前 | 62%(1000㎡) / 94%(100㎡) / 88%(100㎡) | ★全体率は単純平均の 81.33%（○）★。旧定義の面積ベースだと 66.83…%（△）で**判定が割れる**ので、★面積ベースに戻っていないこと★を確認できる |
| 8 | ★★履歴の行が無いエリアと正常終了のエリアの混在★★ | 施設5 / 1日前 | 履歴なし（面積0） + 97%(1000㎡) | ★履歴なしは `completion_rate` が `null` で**単純平均の分母に入らない**★ → 全体 97.0%（◎） |
| 9 | 該当0件（全部◎○） | 施設1 / 1日前 | 92%（1000㎡） | ★`filter=problem` で `items` が**空配列**（404 にしない）★ |

- ★未実施・履歴なしの行は `task_area` / `clean_area` とも `0`★（ローカルDBのシードと同じ）。
  ★面積は全体率に効かない★ので、実DBで未実施行の `task_area` が非0でも全体率は変わらない（面積の表示にはDBの値をそのまま出す）
- ★`section_name` はシードと同じ `清掃エリア（ロボット107）` の形★。
  モックの「1F 生鮮エリア」のような名前は作りません（実装はDBの生値をそのまま出すため）
- ケース表の期待値は `lib/fixtures/__tests__/areas.test.ts` で固定しています

### 2.6 `clean_task_reports`（清掃実績の新しい取得元。★指示書AQ 決定2★）

`lib/fixtures/clean-task-reports.ts`（`cleanTaskReports`）。
期待値の固定は `lib/fixtures/__tests__/clean-task-reports.test.ts`（19ケース）。
ローカルDBへの投入は `scripts/seed-local-db.ts`（★同じ純粋関数を import している★）。

★**なぜ必要か**★: 指示書AQ 決定2 で ★清掃実績の取得元が `clean_status_histories` →
`clean_task_reports` に変わる★。★このテーブルはローカルDBにも fixtures にも1行も無かった★ため、
★切替後は全画面が0件になり、他担当が動作確認できない★（指示書AQ 落とし穴5）。
★`clean_status_histories` の投入は消していません★ — 切替の前後比較に使うためです。

★**これは API の形（`CleanLogItem`）ではなく DBテーブルの形です**★。
そのため型は `lib/types/` ではなく `lib/fixtures/clean-task-reports.ts` に置いています
（`CleanTaskReportFixture`）。

#### 実データの形（出所: ★ユーザー提供CSV `clean_task_reports.csv` 189行★。2026-09-30 受領）

| カラム | 実測 |
|---|---|
| `report_id` | ★19桁の数値文字列★（例 `1295486100215435264`）。`varchar(20)` |
| `clean_timetable_id` | ★CSV では189行すべて NULL★。★NULL と `0` は予定外のタスク★（NULL: ユーザー回答 2026-10-01、0: TeamWorks 回答 2026-10-27）。旧: 「今後 TeamWorks が埋める前提（決定5）」は 2026-10-01 に取り下げ |
| `status` | ★3:2件 / 4:161件 / 5:4件 / 6:22件★。値の意味は第10回 N-2 で受領（★6 は未受領★。D-155 項2） |
| `task_area` | ★清掃エリアごとにほぼ一定★（例: あるエリアは19行すべて `878.524992`） |
| `clean_area` | ★毎回変動し、68.3%（129/189行）で `task_area` を超える★ |
| `clean_ratio` | ★面積比とは一致しない★（例: 面積比130.0% のとき `clean_ratio` は 76.0） |
| `ended_at` | ★nullable★ |
| `clean_time` | 秒（★0 の行もある★） |

#### 仕込んだケース（境界値・異常系）

| # | ケース | 仕込み方 | 確認できること |
|---|---|---|---|
| 1 | ★未実施★ | ★行を作らない★（`completion_rate === null` の清掃） | ★指示書AQ 1-1「未実施 ＝ 対応する行が無い」★。`clean_task_reports` に `clean_error` 相当の列は無いので、★行の有無だけが根拠★ |
| 2 | 判定の境界値10ケース | ★`clean_area` を清掃ログの完了率から作る★（`clean_ratio` 列にも同じ値）。§2.1 の値（90.1 / 90.0 / 89.5 / 70.1 / 70.0 / 10.1 / 10.0 / 9.9 / 0、未実施は #1） | ★`runCompletionRate()`（面積から。D-155）でも §2.1 の判定が変わらないこと★。全行で元の完了率に戻ることをテストで固定 |
| 3 | ★`status` 4種★ | 3 / 4 / 5 / 6 を ★CSV実測の分布どおり★（189要素の巡回列）。★塊にせず散らしてある★ | ★画面・CSV で表示名（3 清掃キャンセル / 4 清掃正常終了 / 5 清掃異常終了）になり、6 は数字のまま出ること（D-155 項2）★。★`CLEAN_ERROR` に対応づけない★ |
| 4 | ★`ended_at` が NULL★ | 清掃 id が29の倍数の行 | ★順序キーが `ended_at ?? started_at` に倒れること（指示書AQ 1-4 / 落とし穴3）★。★非nullを型で要求している箇所は型ごと直す★ |
| 5 | ★`clean_timetable_id` が NULL★ | ★`history` ブロック（daysBack 6〜30）で id が37の倍数の行★ | ★落とし穴1★。★行が消えるのではなく「未実施」として静かに集計される（fail-silent）★。稼働率・出発成功率が落ちる |
| 6 | `clean_time` が 0 | ★`ended_at` が NULL の行★ | 実測に 0 の行がある。0除算・「0秒だから異常」といった扱いをしていないこと |
| 7 | ★完了率100の行は `task_area` を超える面積★ | 完了率100なら `task_area` × 1.00〜1.40（`pseudoRandom()`） | ★上限100の確認★。★ただし fixtures の清掃ログには完了率100が無い（最大 99.5）ので該当0件★（関数を直接テストで確かめる） |
| 8 | 清掃エリアごとに違う `task_area` | エリア（`section_id`）ごとに一定、600〜1000 m² で散らす（小数第6位まで） | ★既存シードの一律 1000 m² では出ない不具合★（総清掃面積の按分など） |
| 9 | ★`clean_timetable_id` が 0★（予定外のタスク。TeamWorks 回答 2026-10-27） | ★清掃ログに対応しない追加の1行★（`cleanTaskReportZeroTimetableRows`。`history` の実施済みの最初の行のロボットで、その3時間後に始めた清掃。完了率 55.5。`report_id` は清掃 id ＋ 900万） | ★総清掃面積（F-08）の面積の合計には入り、完了率・実施回数・重複の番人には入らない★（`lib/db/timetable-link.ts`）。★`cleanTaskReports`（清掃ログ由来の263行）には含めない★ — 既存の行と期待値を動かさないため。★fixtures 版の画面はこの配列を読まない★（DB 経由＝シードでだけ見える） |

★**#5 を `boundaryCases` / `escalationCases` / `statusCases` に当てていないのは意図**★です。
当てると §2.1 の境界値10ケースと §2.2 のエスカレーションが ★まるごと「未実施」になって
画面で確認できなくなる★ため、`history` ブロックだけを対象にしています
（この制約は `lib/fixtures/__tests__/clean-task-reports.test.ts` で固定済み）。

★★**#5 の行は、DB 経由と fixtures 経由で見え方が違います（既知の構造上の制約。意図した非対称）**★★

| 経路 | #5 の清掃の見え方 | なぜ |
|---|---|---|
| DB 経由（`USE_FIXTURES=false`） | ★出発失敗・`task_status` が `null`★ | `clean_timetable_id` が NULL の行は `IN` に一致しないので「対応する行が無い」＝未実施（指示書AQ 1-1）。★落とし穴1 そのもの★ |
| fixtures 経由（`cleanLogs`） | ★実施済み・`task_status` あり★ | `cleanLogs` の `departed` / `completion_rate` / `task_status` は `clean-logs.ts` の入力から決まり、`clean-task-reports.ts` の結果を見ない |

★fixtures 側を DB 経由に合わせないのは意図★です。#5 の対象（`cleanTaskReportNullTimetableLogIds`）は
★`completion_rate !== null` の行から作っている★ため、`cleanLogs` 側でその行を未実施にすると
★対象が0件になり、落とし穴1 を再現するケースそのものが消えます★。
この数行について2経路の画面表示が違うのは不具合ではありません（`lib/fixtures/clean-task-reports.ts` の
`cleanTaskReportNullTimetableLogIds` の docstring にも同じことを書いています）。

★`status` の規則（CSV実測の分布と189要素の巡回列）は `lib/fixtures/task-status-rule.ts` の1箇所だけ★で定義し、
`clean-task-reports.ts`（`status`）と `clean-logs.ts`（`task_status`）の両方がそこから import します。
★`clean-logs.ts` は `clean-task-reports.ts` を import できません★（あちらが読み込み時に `cleanLogs` / `history` を
読むため循環 import になる）。★2つの一致は `lib/fixtures/__tests__/areas.test.ts` で固定しています★。

#### 件数

| どこ | 行数 |
|---|---|
| `lib/fixtures/` | ★263行★（`cleanLogs` 280行のうち実施済み263行。未実施17行には行を作らない）＋ ★`clean_timetable_id` = 0 の1行★（#9。別の配列） |
| ローカルDB（本ループ + 施設7検証） | ★246行★（投入対象253行のうち実施済み240行 + 施設7検証6行）＋ ★`clean_timetable_id` = 0 の1行★（#9） |
| ローカルDB（画面確認用ボリューム） | 実行時に決まる（★シードの完了ログに出力される★） |

★`clean_status_histories` は未実施でも行を作る（`clean_error = 1`）ので、★2つのテーブルで
行数が違います。それが意図した状態★です（切替の前後比較の材料）。

★2026-10-02（D-155 / 指示書AS 段階6）★: 完了率が `clean_ratio` 列から ★1回ごとの面積比（`runCompletionRate()`）★ に
変わったため、`clean_area` を完了率と独立に作る方式（旧ケース #7「面積比と `clean_ratio` の乖離」。約7割が `task_area` 超え）をやめ、
★清掃ログの完了率から作る★ 方式に変えました（CSV の分布 68.3% の再現はやめた）。

#### 値の作り方

★すべて清掃 id から決まる純粋関数★（`Math.random()` を使わない＝冪等）。
★`scripts/seed-local-db.ts` は `lib/fixtures/clean-task-reports.ts` の関数をそのまま import します★ので、
★`USE_FIXTURES` の true/false で同じ値が出ます★（ズレたらどちらかのバグ）。
シードは投入後に ★status 4種 / `ended_at` NULL / `clean_timetable_id` NULL / `clean_timetable_id` 0 / `clean_time`=0 が
1件も無ければ例外で止まります★（fail-loud。ケースが欠けたまま他担当が動作確認に入らないため）。

---

## 3. アラート（prechecks）

| ケース | 内容 |
|---|---|
| 種別 | ★`precheck`（事前チェック異常）だけ★（ユーザー判断 2026-10-29「2で進めてください」。アラートは出発失敗を出さない）。旧 `launch_failed`（出発失敗）の id 4 / 8 は消した（id は振り直さない。4・8 は欠番）。出発失敗は §2 の清掃詳細ログ（施設12 今日 03:30 など）で見る |
| 分数の3通り | 60分前 / 30分前 / ★20分前（設定値以外の任意の分数。D-093 項2）★ |
| 3ステータスすべて | 未解消 / 対応中 / 解消（未解消4: id 1・2・3・9 / 対応中1: id 5 / 解消2: id 6・7） |
| 解消済み（時刻あり） | `responded_at` と `resolved_at` の両方が入っている |
| 対応中（解消前） | `responded_at` のみ、`resolved_at` は null |
| 未解消 | 両方 null |
| バッテリー低下 | `battery_level` が低い値（Pudu APIで取得可能と確認済み） |
| 該当0件 | 未解消が0件の日（サマリカードが0表示になる）。データは今日と昨日だけなので、2日前・3日前を指定する |
| ★本清掃は成功（`main_run_succeeded`）★ | ★true は id 6（出発成功・親の本清掃が ◎○）だけ★。id 7（完了率62% ＝ △）・ほかは false。★対応状況・件数はこの値で変わらない★（D-171。`lib/fixtures/prechecks.ts` の冒頭コメント） |

---

## 4. レポート（reports）

モックの `RANK` を移植し、以下を追加。

| ケース | 内容 |
|---|---|
| 3ステータス | 良好 / 注意 / 要改善 が最低1件ずつ |
| 前期比＋ | 改善している施設（▲表示） |
| 前期比− | 悪化している施設（▼表示） |
| 前期データなし | `prev` が null（新規施設。差分表示なし） |
| 11件以上 | 「全X施設を表示」の発火確認 |
| エラー種別（生値。D-077で変換しない方針に確定） | 実在する値が複数（`EmergencyStop` 等） / 未知の値 / 空文字 |
| 連続問題施設 | L1 / L2 / L3 が最低1件ずつ |
| 当期データ | 進行中の週・月・年（サマリテーブルに存在しない前提のケース） |
| 週次/月次/年次 | 3モードのデータ（粒度タブ切替の確認）。期間キーは実行日からの相対で6つ（今週 / 先週 / 今月 / 先月 / 今年 / 前年）。★年次は `month=0, week=0`★（§4.7。D-151 で今年・前年を追加） |

★確定期間のKPI（`kpiWeekly` / `kpiMonthly` / `kpiYearly`）は**作り値**です★（実データ由来ではない）。
当期（今週・今月・今年）は3つとも `kpiCurrentPeriod` を使い回しています。

---

## 5. D-Secログ（ds-devices）

| ケース | 内容 |
|---|---|
| 3状態すべて | 正常 / 通信断 / 異常 |
| 自動ソート確認 | 正常→異常→正常→通信断 の順で定義し、**表示時に異常→通信断→正常に並ぶ**ことを確認 |
| 備考あり/なし | `note` が空文字のケースを含む |
| 全部正常 | 異常0件のケース（サマリカードの表示確認） |
| 11件以上 | ページング確認用（12件） |
| 該当0件 | `q` に一致しない検索語（例: `q=ZZZ-NOMATCH`）を指定すると0件になる。専用のfixtureは用意していない |

※ **トークン期限（`te`）は移植しない。** 仕様から除外済みです。

---

## 6. D-Secアラート（ds-alerts）

取得元は `lib/fixtures/error-reports.ts` の `errorReportRows` から、Prisma 版と同じ純粋関数 `dsAlertsFromErrorRows()` で作ります（指示書AX D13。`lib/fixtures/ds-alerts.ts` は削除済み）。
対象は `error_type` が `D-S_CONTROL_FAILURE` の行だけ（★8行のうち出るのは7行★: `FX-ERR-10`・`12`〜`17`。★`FX-ERR-19` は清掃エリアが削除済みなので出ない — U8・表5 V6★）。`SECURITY_ON`・`NOT_STARTED` は含みません（エラー一覧 `/errors` には出ます）。

| ケース | 内容 |
|---|---|
| 3ステータス | 未解消 / 対応中 / 解消（`unresolved` / `in_progress` / `resolved`。エラー一覧と同じ語彙） |
| 備考あり/なし | `note` が空のケースと「回線業者に確認中」等あり |
| `errored_at` NULL | `FX-ERR-12`（検知時刻は `created_at`） |
| シリアルと施設の食い違い | `FX-ERR-16`（施設2の制御盤 SN・行の施設は3。U3） |
| 同時刻の並び | `FX-ERR-16` と `17`（検知時刻同値 → id 昇順で `16` が先） |
| `clean_timetable_id` NULL | `FX-ERR-12` |
| `security_off` | ★fixtures にも無い（D-180）★。API の `type=security_off` は200・0件（enum は互換のため残す） |
| 既定（`status=open`） | 5件（解消の `FX-ERR-14`・`15` 以外） |
| `status=all` | 7件 |
| `summary` | `{ unresolved: 3, in_progress: 2, resolved: 2 }`（7行に対して。`type` / `status` の絞り込みより前） |
| 該当0件 | ★「disconnected × done（旧 `status=done`）」で意図的に0件にするケースは無くなった★。0件を見たいときは分類（`customer_id` 等）で絞る |

★エラー一覧の上書き表（`fixture-error-report-overrides.ts`）を共有するので、`PATCH /errors/{error_id}/status` で書いた対応状況が D-Sec にも出ます（U4）。

※ モックの `DSA_INIT` には「トークン期限」種別が含まれています。**移植時に削除してください。**

---

## 7. 施設ロボットマスタ（facilities）

| ケース | 内容 |
|---|---|
| 契約フラグ4パターン | ★`varchar(10)`の10桁文字列（D-080）★。`0000000000`:未設定 / `1000000000`:D-Securityのみ / `0100000000`:D-Watchのみ / `1100000000`:両方 |
| ロボット0台 | 施設は登録済みだがロボット未設置 |
| ロボット複数台 | 2台、3台のケース（モックの立川店は3台） |
| メーカー・機種 | PUDU CC1 のみ（P1 の対象機種。CLAUDE.md §1）。未対応機種の表示確認は `lib/facilities/__tests__/query.test.ts` の合成データで担保する（指示書N / D-087） |
| 任意項目が空 | 電話・メール・担当者が null のケース |
| 複数都道府県 | 東京都 / 神奈川県 など |

契約フラグ4パターンは**メニュー表示制御のテストに必須**です（契約のないセクションはメニューごと非表示）。

---

## 8. スケジュール（schedules）

| ケース | 内容 |
|---|---|
| 曜日パターン | 毎日 / 月水金 / 平日のみ / 土日のみ |
| 有効・無効 | `active` の true / false 両方 |
| 日跨ぎ | 23:30開始〜00:30終了 |

無効スケジュールは「連続日数カウント」の対象外になるので、判定ロジックの確認にも使います。

**★D-050: 曜日パターンを持つ有効スケジュールは「補完ペア」にしてある★**

`schedule_progress.planned`（`lib/dashboard/aggregate.ts`）は施設を区別しない、
その日の曜日に該当する active なスケジュールの**総数**です。一方 `clean-logs.ts` の
当日分（daysBack=0）は曜日に関係なく毎日必ず11行になります（相対生成のため。★D-173 で結果待ちの1行（施設11）を足して 10 → 11★）。
この2つを常に一致させるため、平日のみ／土日のみ、月水金／日火木土 をそれぞれ
「全曜日を過不足なくカバーする補完ペア」にして、合計が常に11件になるようにしています
（★D-173 で「毎日」のスケジュール id 34（施設11・03:30〜04:30）を足して 10 → 11★。§2.3b）。
詳細な内訳は `lib/fixtures/schedules.ts` 冒頭のコメントを参照してください。

---

## 9. 点検記録（inspections）

| ケース | 内容 |
|---|---|
| 申し送りあり/なし | `notes` が空のケースと長文のケース |
| 写真全部あり | Before/After × 7項目 = 14枚 |
| 写真一部欠け | 一部項目のみ撮影（グリッドの欠けた表示確認） |
| 写真なし | 記録のみ登録されたケース |
| 同一施設の複数回 | 劣化傾向の比較表示用に、同じ施設で日付違いの記録 |

写真はダミー画像（単色＋項目名を書いたPNG等）で構いません。

---

## 10. 共通

すべての一覧APIで以下を確認できるようにします。

- **空配列**（該当0件のときの表示）
- **11件以上**（ページング / もっと表示の発火）
- **エラーレスポンス**（500、404 のときの画面表示）— fixture側にエラーを返すフラグを用意すると確認しやすい

---

## 11. DB接続への移行手順

1. `npx prisma db pull` で実スキーマを取り込み、テーブル定義書（28テーブル）と突き合わせる（**この確認だけは早めにやる**。カラム名の食い違いが後から見つかると全画面に波及するため）
2. カラム名・型の食い違いがあれば実装を止めて確認
3. `USE_FIXTURES=false` に切り替え、1画面ずつ実データで確認
4. **数値検証**: サマリテーブルの値と画面表示の一致、判定結果がfixture時と同じロジックで出ているかを確認
5. fixtures は**削除せず残す**（テスト用・デモ用・障害切り分け用に使う）

---

## 12. ダッシュボード（dashboard）

各値の定義は `docs/decisions.md` D-049 が正。集計は `lib/dashboard/aggregate.ts`（純粋関数）で、
期待値表は `lib/dashboard/__tests__/aggregate.test.ts` に固定してあります。

### 12.1 日付ごとに何が再現できるか（★D-050で確定★）

`clean-logs.ts` は実行日からの**相対生成**（`daysBack`。0 が当日）で、`schedules.ts` は
固定データです。`GET /api/dashboard?date=YYYY-MM-DD` の `date` を「今日から何日前か」で
指定することで、以下のケースを確認できます（`date` 省略時は当日＝daysBack=0）。
★`schedule_progress` は省略時も当日。`summary` と `problem_logs` は直近モード（D-159）★ — 下の表の
「当日（`date` 省略）」の行は ★スケジュール進捗（`planned` / `completed` / `data.anomaly`）だけ★ に当てはまる。
省略時の数値カードと清掃詳細ログ（コンパクト版）は §12.3。

| `date`（当日からの相対） | `planned` | 当日ログ行数 | `completed` | `data.anomaly` | 確認できること |
|---|---|---|---|---|---|
| 当日（daysBack=0、`date` 省略） | 11 | 11 | 9 | 出ない | **既定が正常であること。**曜日に関係なく常にこの値になる（`schedules.ts` の補完ペア設計。§8参照）。出発なしの2行は施設12 の出発失敗と施設11 の結果待ち（§2.3b） |
| 1日前（daysBack=1） | 11 | 11 | 7 | ★出ない★ | ★D-173 以前は「実績が予定を**上回る**乖離」（expected=10, actual=11）の再現用だった★。★planned が11になって一致し、★上回る側の乖離は fixtures で再現できなくなった★。★直さない（監督判断 2026-10-07）★: `data.anomaly` の判定は `logsOfDay.length !== planned`（`app/api/dashboard/route.ts`）で上回る・下回るが同じ分岐なので、下回る側（2〜5日前・6〜30日前）で同じ経路を確かめられる |
| 4日前（daysBack=4） | 11 | 1 | 1 | 出る（expected=11, actual=1） | 乖離（実績が予定を**大きく下回る**ケース。D-049が本来想定していた「収集処理が止まった」ケースに近い） |
| 2〜3日前・5日前 | 11 | 3 / 3 / 2 | 2 / 2 / 2 | 出る | 同様の乖離（不足側）の追加サンプル |
| 6〜30日前 | 11 | 10 | 変動 | ★出る（expected=11, actual=10）★ | 履歴データ（`history`）は1日10行の生成。★D-173 以前は planned と偶然一致して出なかった★。乖離確認には使わない |

★上の数字は `lib/fixtures` を import して `db.cleanLogs()`・`db.schedules()`・`computeScheduleProgress()` で数えたもの★
（当方の fixtures 由来。2026-10-07 11:3x JST。`completed` は出発した行の数なので相対生成の日によって変わりうる）。

`planned` はどの `date` を指定しても常に11（`schedules.ts` の設計上、曜日に関係なく
有効スケジュールの該当件数が11で一定のため。★D-173 で 10 → 11★）。乖離が起きるのは常に**清掃ログ側の行数**が
日によって変わるからで、これは意図的な相対生成データの性質です。

### 12.2 `planned = 0` の日は fixtures では再現しない（★意図的★）

上記の通り、既定（当日・曜日を問わず）を常に「正常」にするという要件（D-050）を満たすには、
`schedules.ts` の有効スケジュールの曜日該当件数を**全曜日で一定（11件。D-173 以前は10件）**にする必要があり、
その結果どの `date` を指定しても `planned` は0にならない設計になっています
（0になる曜日が1つでもあると、その曜日が「当日」に回ってきたときに既定が異常表示になってしまうため）。

そのため `planned = 0`（全スケジュールがその日該当なし → `rate` が `null`）のケースは、
**fixtures 経由では再現できません。** このケースは `computeScheduleProgress` 単体の
性質（0除算をしないこと）であり、`lib/dashboard/__tests__/aggregate.test.ts` の
「`planned = 0`（全施設が定休日）なら `rate` は `null`」テストで合成データ（fixtures非依存）
により直接検証済みです。画面から確認したい場合はこのテストを参照してください。

#### `/dashboard` の描画経路を直接確認したい場合（★手順・要注意★）

上の `aggregate.test.ts` は `computeScheduleProgress` という集計関数を直接呼んでいるだけで、
「`planned = 0` のレスポンスを `/dashboard` 画面がどう描画するか」（例:
「本日の清掃予定はありません」といった表示）は検証していません。画面の描画経路まで
目視で確認したい場合は、以下の手順で一時的に再現してください。

1. `lib/fixtures/schedules.ts` の全スケジュールの `active` を一時的にすべて `false` に変更する
   （曜日を問わず該当件数が0になるため `planned = 0` になる）
2. `npm run dev` で `/dashboard` を開き、表示を確認する
3. **確認が終わったら必ず `active` を元の値に戻す。**
   戻し忘れても、`lib/fixtures/__tests__/schedules.test.ts`（修正1で追加）が
   「有効スケジュールの該当件数が当日の清掃ログ行数と一致しない」として落ちるため、
   `npm test` を実行すれば戻し忘れにすぐ気付けます

`schedules.ts` の有効スケジュールの曜日該当件数を**全曜日で一定（11件）**に保つという制約
（12.2冒頭・D-050）がある限り、`planned = 0` を fixtures に**常設**することはできません。
常設すると、どこかの曜日で該当件数が0になってしまい、その曜日が「当日」に回ってきたときに
既定表示（`date` 省略・daysBack=0）が異常系になってしまうためです。そのため常設ではなく、
確認したいときだけ上記の手順で一時的に `active` を落とす運用にしています。

| ケース | 何を確認するか |
|---|---|
| `planned = 0` の日（全施設が定休日） | `rate` が `null` になる。**0除算で `NaN` / `Infinity` を出さないこと**。★fixturesでは再現不可。12.2参照。`aggregate.test.ts` で検証済み★ |
| `security_banner` が 0件 | バナーを出さない（空配列） |
| `security_banner` が 複数件 | 複数行の並び。`security_off` かつ未解消だけが出る。★fixtures・DB とも `security_off` が無いので出ない（D-180）。描画は `aggregate.test.ts` で検証★ |
| 当日の問題行が 0件 | コンパクト版清掃詳細ログが空のときの表示。★`date` 省略時は `problem_logs`（直近モード。§12.3）、`date` 指定時はその日の △×（D-159）★。旧: `/clean-logs?result=problem` を画面が別に呼んでいた |
| `success_rate = 100.0` | 全件 ◎○。丸め前の生値が返る（表示の丸めは画面側） |
| `success_rate = 0.0` | 全件 △× または未実施。`null`（分母0＝ログ0件）と区別できること |
| `planned` と清掃ログ行数が乖離する日 | `data.anomaly` が出る（収集処理の障害検知）。期待値と実測値だけを記録し、原因は断定しない。★再現手順は12.1参照★ |

**`failure_breakdown` は fixtures を用意しません。** M2 では返さない（D-048）。
M3 でエラーテーブルを扱うときに、型・DataSource メソッド・fixtures をまとめて作ります。

### 12.3 `date` 省略時（直近モード）に何が出るか（★D-159 / 指示書AV★）

`date` を省略すると（`?date=` の空文字も同じ）、`summary`（数値カード）と `problem_logs`（コンパクト版の表）は
★各清掃エリアの直近の清掃1件★ から作られます（規則は `lib/dashboard/latest-run.ts` の `pickLatestRunPerSection()`、
日数・分数は `lib/config` の `DASHBOARD_LATEST_RUN`）。

- 候補: 予定の開始 ≥ 現在 − 7日 ★かつ★ 予定の終了 ≤ 現在（★境界はどちらも含む★）。
  ★D-173 で「終了＋30分」（旧 `settleMinutes`）をやめた★ — 終わってからレポートが届くまでは、行の旗 `result_pending` で「結果待ち」と出す
- 清掃エリアごとに、候補のうち開始が一番新しい1件（同じなら id の大きいほう）。★出発失敗も選ぶ★（未実施に数える）。
  ★結果待ちの行も選ぶ★が、`summary.pending` に数え、★未実施にも成功率の分母にも入れない★。★`problem_logs` には出さない★（D-173）
- ★`schedule_progress`・要レスキュー・セキュリティバナーは当日のまま★（§12.1 の「当日」の行: 予定11・実施9）

★fixtures には予定の終了も清掃エリアも無い★ので、fixtures 版の `cleanLogRunMeta()` は
★終了＝予定の開始（`scheduled_at`）・清掃エリア＝ロボット（`serial_number`）★ とみなします（D10。★fixtures にデータは足さない★）。
つまり fixtures では「★開始時刻を過ぎた予定★」が「終わった予定」になります。
当日の行は 01:30〜03:30（最後は施設12 と施設11 の 03:30）なので、★JST 3:30 が境目★です（D-173 以前は 4:00）。
★fixtures の `result_pending` は時刻で決めず行に直書き★（施設11 の 03:30 だけが `true`。§2.3b）なので、時刻によって結果待ちが確定に変わることはありません。

★下の数字は `lib/fixtures` を import して `db.cleanLogs()`・`db.cleanLogRunMeta()`・`pickLatestRunPerSection()`・`computeSummary()` で数えたもの
（当方の fixtures 由来。2026-10-07 11:3x JST。★D-173 の後★）★。

**JST 3:30 以降**（3:30 ちょうどを含む。その日のうちは同じ値）:

| 項目 | 値 |
|---|---|
| 選ばれる行 | ★15行（ロボット15台）★ — 当日11行・前日4行（施設5 の2台目・施設7 の3台）。★施設11 は当日の結果待ちの行が選ばれる★（D-173 以前は6日前の行） |
| `summary` | ★成功8 / 失敗5 / 未実施1 / 結果待ち1 / 成功率 57.14…%（8 ÷ 14）★ |
| △×（`problem_logs`） | ★6行★ — 当日の5行（施設12 ×出発失敗・10 ×・9 ×・6 △・8 △）＋ 前日の施設7（△。`8111R5628060099`）。★施設11 の結果待ちは出ない★ |
| 表に出る先頭5件 | ★当日の5行: 施設 12・10・9・6・8★（データソースの順） |

- ★施設9 と施設6 は同じ 02:30★。fixtures の `cleanLogs`（`lib/fixtures/clean-logs.ts`）の並べ替えは `scheduled_at` しか見ないため、
  同じ時刻の行は ★実際には id の降順★（施設9 = id 大 → 施設6）で出ます。Prisma 版は id の昇順なので、DB では 6・9 の順になります。
  ★先頭5件の顔ぶれは変わりません★
- 6行目（前日の施設7）は表に出ません（画面は先頭5件）

**JST 3:30 より前**は、当日の行の一部が「終わっていない」扱いになり、そのロボットは前日以前の行が出ます。
★施設12（`8111R5628060166`）は7日以内に当日の行しか無いので消えます。それが正しい★。

| 現在（JST） | 選ばれる行 | `summary` | △× | 表に出る先頭5件 |
|---|---|---|---|---|
| 02:00 | 14行（当日5行） | 成功9 / 失敗3 / 未実施2 / 結果待ち0 / 成功率 64.28…% | 5行 | 施設 8・4・9・6・7 |
| 03:29 | 14行（当日9行） | 成功9 / 失敗5 / 未実施0 / 結果待ち0 / 成功率 64.28…% | 5行 | 施設 10・9・6・8・7 |

（02:00 は当日の 02:00 までの予定が終わった扱い。03:29 は 03:30 の施設12・施設11 だけが終わっていない扱い。
★D-173 以前の表（終了＋30分。02:00 は 01:30 だけ・03:59 が境目の直前）とは値が違う★）

## 13. エラー一覧（error-reports）（★D-160 / 指示書AW★）

`GET /errors`（`/errors` 画面）のサンプルデータは `lib/fixtures/error-reports.ts` の `errorReportRows`（20行）と
`errorReportTimetables`（予定5件）です。

- ★DB の行の形で持つ★（日時は `toDbJst()` を通した壁時計値の `Date`、id は `bigint`）。
  ★fixtures と Prisma は同じ純粋関数を通す★（`errorReportKeyFromRow()` → `filterSortErrorReportKeys()` → `errorReportItemFromRow()`）ので、
  `USE_FIXTURES` で件数・並びが変わりません
- ★20行のうち見える行は18行★ — ★既定（未解消・対応中）で15行（`FX-ERR-03` / `14` / `15` 以外）・「解消済みも表示」で18行★
- ★`FX-ERR-18`（削除済みの施設）・`19`（削除済みの清掃エリア）はエラー一覧にも D-Sec アラートにも出ない★（`isVisibleErrorRow()`。指示書AX U8・表5 V2/V6）。`PATCH /errors/{error_id}/status` は404
- ★fixtures に削除の概念があるのはこの2行（`FX-ERR-18`・`19`）だけ★ — 清掃ログ・予定・スケジュール・エリア・制御盤などの fixtures は `deleted_at` を持たない（D-182 で全画面に広げた「削除済みの施設・清掃エリアの行は出さない」は、Prisma 版では SQL の条件で外し、fixtures では外す対象が無い。★fixtures の件数は変わらない★）
- ★`robot_id` は読みません★（`select` で絞る。指示書AU 段階0-4）。fixtures にも値はありますが使いません —
  ★制御盤・セキュリティ・清掃開始失敗の行は `robot_id` が NULL★ という実データの形を示すためだけに持っています
- ★`error_level` の値（`CRITICAL` 以外）と、`SECURITY_ON` の `error_level` は実データ未受領のダミー★です。
  `NOT_STARTED`・`CRITICAL`・シリアル `---` の組はステージングの実測の形（D-158 2026-10-06 実測・先方投入）
- 施設名は `facilities` から、ロボットのシリアルは各施設の先頭のロボットから、制御盤のシリアルは `ds-devices` から id で引きます（★直書きしない・実在の店名を入れない★）
- ★予定は本表・履歴を分けません★（fixtures には2表の区別が無い）。`FX-ERR-06` の予定は「実DBでは履歴にだけある」想定です
- ★対応状況の更新（`PATCH /errors/{error_id}/status`）は `lib/db/fixture-error-report-overrides.ts` の上書き表★に入ります
  （fixtures は書き換えない。プロセスを再起動すると消える。アラートの上書き表とは状態を共有しない）
- 日付は実行日からの相対（`at()`）です

| id | 内容 | `status` | 紐づく予定 | 表1（指示書AW §3） |
|---|---|---|---|---|
| FX-ERR-01 | ロボットのエラー（`EmergencyStop` / `ERROR`）・施設1 | 0 未解消 | 本清掃 | E1 |
| FX-ERR-02 | ロボットのエラー（`LowBattery` / `WARNING`）・施設1 | 1 対応中（`responded_at` あり） | 事前チェック（30分前。`PRECHECK_MINUTES_BEFORE[1]`） | E2 |
| FX-ERR-03 | 解消済み（`HandleUp`）・施設3（`responded_at` / `resolved_at` / 備考あり） | 2 解消 | 本清掃 | E3（★既定では出ない★） |
| FX-ERR-04 | `clean_timetable_id` が NULL・施設2 | 0 | 予定なし | E4 |
| FX-ERR-05 | `clean_timetable_id` がどの予定にも無い id（9999）・施設2 | 0 | 予定不明 | E5 |
| FX-ERR-06 | 予定が履歴にだけある想定・施設4 | 0 | 本清掃 | E6 |
| FX-ERR-07 | `errored_at` が NULL（検知時刻は `created_at`）・施設4 | 0 | 本清掃 | E7 |
| FX-ERR-08 | ★`robot_id` NULL・シリアル `---`・`SECURITY_ON`★・施設5 | 0 | 本清掃 | E8 |
| FX-ERR-09 | 事前チェック（★10分前。設定に無い値★）・施設1 | 0 | 事前チェック（10分前） | E9 |
| FX-ERR-10 | ★`robot_id` NULL・シリアル = 制御盤の SN（`DS-001`）・`D-S_CONTROL_FAILURE`★・施設1 | 0 | 本清掃 | 制御盤のエラー（D-158 P-8 a）・指示書AX R1（D-Sec にも出る） |
| FX-ERR-11 | ★`robot_id` NULL・シリアル `---`・`NOT_STARTED`・`CRITICAL`★・施設5 | 0 | 本清掃 | 清掃開始失敗（ステージング実測の形） |
| FX-ERR-12 | ★`D-S_CONTROL_FAILURE`・施設11★・`errored_at` NULL・`clean_timetable_id` NULL | 0 | 予定なし | 指示書AX R4・R13（D-Sec にも出る） |
| FX-ERR-13 | ★`D-S_CONTROL_FAILURE`・施設5★・対応中・備考「回線業者に確認中」 | 1 | 本清掃 | 指示書AX R2（D-Sec にも出る） |
| FX-ERR-14 | ★`D-S_CONTROL_FAILURE`・施設4★・解消済み | 2 | 本清掃 | 指示書AX R3（★既定では出ない★。D-Sec は「全件」「解消済み」で出る） |
| FX-ERR-15 | ★`D-S_CONTROL_FAILURE`・施設10★・解消済み（2日前） | 2 | 本清掃 | 指示書AX R3（★既定では出ない★。D-Sec は「全件」「解消済み」で出る） |
| FX-ERR-16 | ★`D-S_CONTROL_FAILURE`・行は施設3・シリアルは施設2の制御盤★ | 0 | 本清掃 | 指示書AX R11（D-Sec にも出る） |
| FX-ERR-17 | ★`D-S_CONTROL_FAILURE`・施設8★・`FX-ERR-16` と同じ検知時刻・対応中 | 1 | 本清掃 | 指示書AX R12（D-Sec にも出る） |
| FX-ERR-18 | ロボットのエラー（`EmergencyStop`）・★削除済みのダミー施設（id 99）★ | 0 | 本清掃 | 表5 V2（★どこにも出ない★） |
| FX-ERR-19 | ★`D-S_CONTROL_FAILURE`・施設2・清掃エリア削除済み★ | 0 | 本清掃 | 表5 V6（★どこにも出ない★。施設2の `04`・`05` は出続ける — V9） |
| FX-ERR-20 | ★`clean_timetable_id` が 0★（予定外のタスク。TeamWorks 回答 2026-10-27）・`LostLocation`・施設6 | 0 | ★予定なし★（API の `clean_timetable_id` は null。★予定不明に化けないこと★） | E4 と同じ扱い |

★「解消済みも表示」の並び（検知時刻の降順。同値は id 昇順）★ — ★表の時刻から手で導いた順★:
`FX-ERR-07`（当日 06:40）→ `12`（02:55）→ `16`（01:30）→ `17`（01:30）→ `05`（前日 23:50）→ `11`（23:30）→
`01`（23:10）→ `13`（23:10）→ `08`（22:55）→ `09`（22:50）→ `10`（22:45）→ `02`（22:30）→ `04`（15:20）→
`20`（14:40）→ `14`（03:55）→ `03`（2日前 23:40）→ `15`（2日前 02:00）→ `06`（3日前 23:05）。
★`FX-ERR-18`・`19` は含まない★
（★当方の fixtures 由来。`lib/db/__tests__/fixture-error-reports.test.ts` が件数と降順を固定★）。

★既定（未解消・対応中）の並び★ — 上の並びから `FX-ERR-03`・`14`・`15`（解消）を除いたもの（15行）: `FX-ERR-07` → `12` → `16` → `17` → `05` → `11` → `01` → `13` → `08` → `09` → `10` → `02` → `04` → `20` → `06`。
（件数はテストで固定。並びは手で導いたもの）

### 13-1. エラー内容（`error_list_id` / `error_lists`）（★D-184 / 指示書BD §5-2★）

★選択肢★（`GET /error-lists`）は `lib/fixtures/error-lists.ts` の `errorLists`（★131行。スプレッドシート「PUDUエラー一覧_20261016」の「PUDUエラー 決定稿」シートを監督が抽出したもの★）です。
★全行 `inactive: 0`・`deleted_at: null`★（無効化・削除済みの選択肢は fixtures に無い。テストでは各テストの中で作る）。
★id 131 = 「OTHER ERROR / その他(該当なし)」★ — 備考必須（`ERROR_LIST_NOTE_REQUIRED_IDS`。`note_required: true`）。
★Pudu の `error_type` は入れない★（`error_lists` にその列が無い）。★実DBの `error_lists` と中身が一致するかは未確認★（ステージングに接続できない。指示書BD §8）。

★エラー一覧の行の `error_list_id`★ — ★大半は null（未選択）★。値があるのは次の5行だけです（`lib/db/__tests__/fixture-error-reports.test.ts` が固定）。

| id | `error_type` | `error_list_id` | `error_list` | 何を見るための行か |
|---|---|---|---|---|
| FX-ERR-01 | `EmergencyStop` | 40 | 40「非常停止スイッチが作動しました」 | 有効な id の名前解決 |
| FX-ERR-03 | `HandleUp`（解消済み） | 39 | 39「ハンドルが持ち上げられました」 | 解消済みの行でも値を持つ |
| FX-ERR-05 | `BrushError` | ★9999★ | ★null★（`error_lists` に無い。`error_list_id` は 9999 のまま） | 画面の「不明(9999)」 |
| FX-ERR-06 | `Offline` | 37 | 37「ロボットがオフラインです」 | 有効な id の名前解決 |
| FX-ERR-11 | ★`NOT_STARTED`★ | ★131★（備考あり） | 131「その他(該当なし)」・`note_required: true` | ★清掃開始失敗は選べる★・その他は備考必須 |

- ★`error_list_selectable`★ は `error_type` が `D-S_CONTROL_FAILURE`・`SECURITY_ON`（`ERROR_LIST_UNSELECTABLE_ERROR_TYPES`。完全一致）なら false — fixtures では `FX-ERR-08`（`SECURITY_ON`）と `D-S_CONTROL_FAILURE` の行（`10`・`12`〜`17`・`19`）。★これらの行には `error_list_id` を入れていない★
- ★名前解決は絞らない★（無効化・削除済みの選択肢でも解決する）。fixtures 版は全131行の索引、Prisma 版は行の id を集めて `error_lists` を `id IN` で1回引く（`lib/db/error-list-source.ts`）
- ★D-Sec の行（`D-S_CONTROL_FAILURE`）はローカルDBに投入される★（`scripts/seed-local-db.ts`）が、選べない種別なので `error_list_id` は入れない（null）
- ★`FX-ERR-11` に備考（ダミーの文）を足した★（指示書BD で追加。それまでは null）

#### ローカルDB（`dwatch_local`）に表と列を足す・入れ直す（★D-184 / 指示書BD §8★）

★`dwatch_local` には元々 `error_lists` も `robot_error_reports.error_list_id` も無い★（実DBモードでローカルを使うと読み取りが落ちる）。次の順で足す:

1. `npx tsx scripts/local-db/apply-error-lists.ts` — `scripts/local-db/20261027_error_lists.sql` を流す（表は `CREATE TABLE IF NOT EXISTS`、列は `information_schema` を見て無いときだけ `ADD COLUMN`。★何度流しても同じ★）。
   ★`DATABASE_URL` が localhost / 127.0.0.1 の `dwatch_local` でなければ接続前に止まる★（`lib/db/local-guard.ts`）。`.env.local` の `DATABASE_URL` がステージングを指している間は、実行するシェルでだけローカルに向けて渡す
2. `npm run seed:local` — `error_lists` を131行（`lib/fixtures/error-lists.ts`）で消して入れ直し、本清掃エラー（`RER-<id>-main`）の通番1・2・3 に `error_list_id` を入れる: ★38（有効）／131（その他・備考あり）／9999（`error_lists` に無い）★。残り（事前チェック行・D-Sec・決定3の2件・本清掃エラーの他の行）は null

★ステージング・本番には流さない★（スキーマは TeamWorks の領域。CLAUDE.md §7）。`prisma db push` / `migrate` の代わりでもある（指示書M 禁止事項3・4）。
★fixtures（`FX-ERR-*`）とローカルDB（`RER-*`）では `error_list_id` の入る行が違う★（ローカルDBは fixtures のロボットエラー行を投入しないため）
