# 指示書L — マスタ画面3枚（F-08 / F-09 / F-10）と `inspections` の一時非表示（D-079 項3）

| 項目 | 内容 |
|---|---|
| 実行環境 | ★**Cursor 内の Claude が実装します**★（`.claude/commands/d-watch.md` の役割分担表を参照） → **実際にはユーザーの直接指示によりClaude Code（Sonnet 5。監督役＋opusサブエージェント委譲）が実行した（2026-09-08）** |
| 実行状態 | ★**完了（2026-09-08。D-084）**★。段階0〜11すべて実施。段階10（ブラウザ確認）はcurl代替のため一部未確認 |
| 実行順 | ★**指示書K / 指示書I とはコードのファイルが重なりません**★（K は `lib/reports/` `lib/theme-status.ts` `lib/judge/advice.ts` 系、I は `lib/auth/` `proxy.ts` 系）。★**ただし `d-watch-api.yaml` と `docs/decisions.md` / `docs/handoff.md` は3本とも触ります**★。★**同時に走らせないこと**★ |
| 取得する決定番号 | ★**着手直前に `grep -n "^### D-0" docs/decisions.md` で最終番号を確認すること**★。★★**本指示書の作成中（2026-09-08 18:11）に、別セッションが `D-083`（本指示書作成のための調査結果）を追記しました。したがって最初の空き番号は `D-084` です**★★。★指示書K（D-079 を予定→**すでに別作業で使用済み**）と指示書I が先に走るとさらに繰り上がります★。★**2026-09-07 に `D-068` の二重採番が実際に起きています。必ず自分で確認してください**★ |
| 前提 | ★**D-079 項3**★（`docs/decisions.md:2146` 以降。他メンバーとの合意。作業順序の1番目） / ★**D-032**★（`:198`。system の限定スコープ） / ★**D-031**★（`:194`。点検記録は P1 スコープ） / ★**D-053**★（`:330`。機能しないUIを出さない） / ★**D-060**★（`:403`。`end_time` は整形済み文字列） / ★**D-080**★（`:2202`。`contract` は10桁文字列） / ★**D-072 項7**★（`:1052`。点検記録は最後） |
| 段階数 | ★**12段階**★（段階0〜段階11） |
| 着手前の基準 | `npm test` が ★**22ファイル / 461テスト 全パス**★、`npx tsc --noEmit` エラー0、`npm run lint` 0（★2026-09-08 実測。Vitest 4.1.11★）。**着手直前に自分で実行して実測値を基準にすること** |
| 作成 | 2026-09-08（作成時点の決定事項最終番号 **D-082**） |

> 実行後はこの表と、文書末尾の「実行状態」表を更新してください。

> ★**この指示書を読んでいるあなたへ**★
> `.claude/commands/d-watch.md` に「実装は Cursor 側、Claude Code は指示書作成まで」とありますが、
> これは**2つの環境の役割分担**です。**Cursor の中で動いている Claude は実装担当**です。
> この指示書を渡されたなら、**調査や改訂ではなく実装を行ってください。**

---

## ★★着手前にユーザーへ確認すること（2件）★★

★**この2件はどちらも「YAML に無いものを画面に出すかどうか」という契約の話です。**★
★**先に決めないと、段階1（API契約の修正）の内容が変わります。実装を始める前に必ず確認してください。**★

### ★確認1 — `system` 画面のスコープ★

| # | 事実 |
|---|---|
| 1 | ★**D-032（`docs/decisions.md:198-201`）は「表示設定」と「運用設定」の2つを実装対象としている**★ |
| 2 | ★**しかし `d-watch-api.yaml` に「表示設定」「運用設定」のエンドポイントは1本も無い**★。あるのは `/settings/advice`（`:554-599`）だけで、★**これは実装済み**★（`app/api/settings/advice/route.ts`） |
| 3 | ★**「表示設定」（ダッシュボードの表示項目 ON/OFF。`d-watch-mock.jsx:1182-1217`）は、設定の永続化とダッシュボード側の読み取り対応の両方が必要**★で、★本作業（マスタ画面3枚）の範囲を超えます★ |

**★当方の推奨スコープ（この指示書はこれで書いています）★**

| # | 内容 |
|---|---|
| 1 | ★**「運用設定」タブのみ**★を作る |
| 2 | ★**読み取り専用**★。閾値（完了率 90/70/10、事前チェック 60分/30分、エスカレーション L2=2日/L3=3日）を ★**`lib/config/` から読んで表示する**★（★**ハードコード禁止**★。`CLAUDE.md` §7） |
| 3 | アドバイス文言の一覧を ★**`GET /api/settings/advice`（実装済み）**★ から取得して表示する |
| 4 | ★**画面に「編集は P1 後半で対応します」と明示する**★ |
| 5 | ★**「表示設定」タブは作らない**★ |

**★理由★**: ★**D-053（`docs/decisions.md:330-334`）が「機能しない絞り込みUIを画面に出さない」と定めています**★。
「固定の選択肢だけがあって何も起きないUIは、不具合と区別できない」というのがその理由です。
★動かないトグルやテキストボックスを並べるのは D-053 に抵触します。★
★**一方、「読み取り専用です」と明示した表示は抵触しません**★ — 何も起きないのではなく、何も起きないことが宣言されているためです。

**★この方針で問題ないかを着手前に確認してください。★**
★**編集まで作るなら、先に `d-watch-api.yaml` に「表示設定」「運用設定」のエンドポイントを追加する必要があります**★
（`CLAUDE.md` §6-1「API契約を変えたくなったら、先に `d-watch-api.yaml` を直す」）。その場合この指示書の段階1と段階6を書き直してください。

### ★確認2 — `facilities` の検索ボックスと API パラメータの食い違い（★調査で新たに判明★）★

★**モックの検索ボックスは1つですが、YAML のクエリパラメータは3つに分かれています。そのままでは繋がりません。**★

| 側 | 実際の姿 |
|---|---|
| ★モック（`d-watch-mock.jsx:977`）★ | ★**入力欄は1つ**★（placeholder = `施設名 / シリアル / 住所`）。`:998-1002` のフィルタは ★**施設名 / 都道府県 / 住所 / シリアル / メーカー / 機種 の6項目を OR で**★ 部分一致させている |
| ★YAML（`d-watch-api.yaml:739-753`）★ | ★**`facility_name` / `serial` / `address` の3パラメータ**★。一覧APIのパラメータは通常 ★**AND**★ で組む |

★**1つの入力欄の値を3つのパラメータに同時に入れると、AND になって「施設名にも住所にもシリアルにも同じ文字列が含まれる施設」しかヒットしません（＝ほぼ常に0件）。**★
★**これは「検索が壊れている」ように見える挙動です。**★

**★当方の推奨（この指示書はこれで書いています）★**: ★**`q` パラメータを `/facilities` に追加する**★

| # | 内容 |
|---|---|
| 1 | ★**`q` を追加**★ — ★施設名 / 都道府県 / 住所 / シリアル / メーカー / 機種 を **OR** で部分一致★（モックの挙動そのまま） |
| 2 | ★**既存の `facility_name` / `serial` / `address` は残す**★。3つは互いに **AND**、`q` はそれらと **AND**（`q` 内部だけが OR）。★画面は `q` だけを使う★ |
| 3 | ★**前例がある**★ — `/ds-devices` の `q`（`d-watch-api.yaml:613-616`「施設名 or D-S制御盤シリアル番号(部分一致)」）と `/inspections` の `q`（`:815-818`）。★**既存2本と同じ形にするので新しい概念は増えません**★ |

★**`q` を足す＝API契約の変更です。段階1（YAML を先に直す段階）で行ってください**★（`CLAUDE.md` §6-1）。
★**この方針で問題ないかを着手前に確認してください。**★
★もし「YAML を増やしたくない」なら、代案は「画面の入力欄を3つに分ける（モックから外れる）」ですが、`CLAUDE.md` §5 は「モックが画面レイアウトの正」としているため、★当方は推奨しません★。★どちらを採るにせよ、採った理由を段階11 で記録すること★。★`/schedules` も同じ問題を抱えています（`facility_name` / `serial` の2つ。`:773-781`）。同じ判断を適用すること★

---

## この指示書の位置づけ

★**D-079 項3（他メンバーとの合意。`docs/decisions.md:2146-2179`）で、作業順序の1番目に置かれたものです。**★

```
1. プレースホルダ画面（facilities / schedules / system）
   ★inspections はメニューから隠してください★
```

★**理由（メンバーの言）**★: メニューを開いて空画面が出るのはデモで印象を損なう。
★**デモの目標は9月中に PPIH 向け**★（同 `:2155`）。

### ★調査で判明したこと — これは「作る」ではなく「差し替える」作業です★

★**4画面はすでに `PlaceholderScreen` で、いずれも5行しかありません**★（実測）:

```
app/(dashboard)/facilities/page.tsx    → <PlaceholderScreen title="施設ロボットマスタ" />
app/(dashboard)/schedules/page.tsx     → <PlaceholderScreen title="スケジュール管理" />
app/(dashboard)/system/page.tsx        → <PlaceholderScreen title="システム管理" />
app/(dashboard)/inspections/page.tsx   → <PlaceholderScreen title="点検記録" />
```

したがって本作業は ★**「プレースホルダを作る」ではなく「プレースホルダを実装済み画面に置き換える」**★ です。

### ★材料はほぼ揃っています（実測）★

| 材料 | 状態 |
|---|---|
| API契約 | ★**`d-watch-api.yaml:731`（`/facilities`）/ `:768`（`/schedules`）に定義済み**★ |
| TS型 | ★**`lib/types/index.ts:317-322`（`RobotSummary`）/ `:324-347`（`Facility`）/ `:349-360`（`Schedule`）**★ |
| fixtures | ★**`lib/fixtures/facilities.ts` 12件 / `lib/fixtures/schedules.ts` 13件**★ |
| `db.facilities()` / `db.schedules()` | ★**`lib/db/index.ts:99-104` に fixtures 実装あり（全件返す）**★。インターフェース宣言は `:60-61` |
| `GET /api/settings/advice` | ★**実装済み**★（`app/api/settings/advice/route.ts`） |
| 閾値の設定値 | ★**`lib/config/index.ts` に全部ある**★（`GRADE_THRESHOLDS` / `ESCALATION_THRESHOLDS` / `PRECHECK_MINUTES_BEFORE`） |
| ★**不足しているもの**★ | ★**API ルート2本（`app/api/facilities/route.ts` / `app/api/schedules/route.ts`）と画面3枚**★ |

★**`app/api/` に `facilities` / `schedules` / `inspections` のルートは存在しません**★（実測。あるのは `clean-logs` / `dashboard` / `ds-alerts` / `ds-devices` / `prechecks` / `reports` / `settings` の7つ）。

---

## ★禁止事項（この指示書の全段階に効きます）★

| # | 禁止 | 理由 |
|---|---|---|
| 1 | ★**モックの実在の店舗名・住所・電話番号・氏名・メールをコピーすること**★ | `CLAUDE.md` §7 / D-043。★`d-watch-mock.jsx:54-63` の `ROBOTS` 9件は実在の値★（`MEGAドンキ渋谷本店` / `渋谷区宇田川町28-6` / `03-5428-4086` / `shibuya@donki.co.jp` 等）。★**1文字もコピーしないこと**★。★必ず `lib/fixtures/facilities.ts` を使う★（架空値に置換済み）。★**`d-watch-mock.jsx:81-88` の `INSPECTIONS` 7件にも実在の人名・店舗名が入っています**★（段階1-6 参照。★今回は使いませんが、目に入るので明記しておきます★） |
| 2 | ★**`lib/fixtures/schedules.ts` に `active: true` のスケジュールを追加すること**★ | ★**ダッシュボードの既定表示が異常系になります**★。詳細は ★段階3 の地雷警告★ を必ず読むこと |
| 3 | ★**既存の `active: true` スケジュールの削除・無効化・`weekdays` の書き換え**★ | 同上。特に補完ペア（id23↔id30 / id25↔id26） |
| 4 | ★**`id: 24` の削除**★ | ★**唯一の `active: false`**★（`lib/fixtures/schedules.ts:94-105`）。判定ロジックの確認に使う（同ファイル `:17-18` / `:46-47`） |
| 5 | ★**動かないUIを画面に出すこと**★ | D-053。★モックの `📥 インポート` / `+ 新規追加` ボタン（`d-watch-mock.jsx:978-979`）は `onClick` の無い飾り★。★運用設定の入力欄も同じ★ |
| 6 | ★**閾値（90 / 70 / 10、60分 / 30分、L2=2日 / L3=3日）をハードコードすること**★ | `CLAUDE.md` §7。★必ず `lib/config/` から読む★ |
| 7 | ★**`customers.contract` を参照すること**★ | `CLAUDE.md` §7 / D-010。★`facilities.contract` のみが正★ |
| 8 | ★**`contract` を数値として比較すること**★ | ★**D-080**★。★`varchar(10)` の文字列で桁位置がサービスに対応★。`Number("0000000000")` は例外を出さずに `0` になるので、★全施設が「契約なし」になってもエラーもテスト失敗も出ません★ |
| 9 | ★**`lib/db/` の `prismaSource` に手を付けること**★ | 実DB接続は別作業。`notImplemented` のままにする |
| 10 | ★**点検記録（`inspections`）の実装に手を出すこと**★ | ★**D-072 項7**★（`docs/decisions.md:1052-1058`）。★写真の置き場所・バケット・署名URLが未決★。先に作ると「写真が表示できない画面」ができる |
| 11 | ★**`git add -A` を使うこと**★ | 過去に**別作業の途中ファイルを巻き込む事故**が起きています。★**必ずファイルパスを明示**★ |
| 12 | ★**メインDBへの書き込み**★ | `CLAUDE.md` §7 / D-042。今回そもそも書き込みは発生しません |
| 13 | ★**`components/layout/PlaceholderScreen.tsx` を削除すること**★ | ★`inspections` がまだ使っています★（段階7 で `inspections` は残す） |

---

## 作業の順序（★崩さないこと★）

| 段階 | 内容 | コミット |
|---|---|---|
| 段階0 | 前提の確認（★読むだけ★） | **コミットなし** |
| 段階1 | ★**API契約を直す**★（`d-watch-api.yaml`）+ 型を足す | 段階1で1コミット |
| 段階2 | `GET /api/facilities` を作る（+ `lib/facilities/query.ts` + テスト） | 段階2で1コミット |
| 段階3 | `GET /api/schedules` を作る（+ `lib/schedules/query.ts` + テスト）★地雷あり★ | 段階3で1コミット |
| 段階4 | 施設ロボットマスタ画面（F-08） | 段階4で1コミット |
| 段階5 | スケジュール管理画面（F-09） | 段階5で1コミット |
| 段階6 | 運用設定画面（F-10、★読み取り専用★） | 段階6で1コミット |
| 段階7 | `inspections` をメニューから隠す | 段階7で1コミット |
| 段階8 | i18n | 段階8で1コミット |
| 段階9 | テスト（★段階ごとに実行済みのはず。ここは最終確認★） | コミットなし |
| 段階10 | ブラウザで動作確認 | コミットなし |
| 段階11 | 記録と push | 段階11で1コミット + push |

★**段階ごとに `npx tsc --noEmit` / `npm run lint` / `npm test` を実行し、通ってからコミットすること**★（D-050。`docs/decisions.md:308`）。

---

## 段階0 — 前提の確認（★読むだけ・コミットなし★）

### 読むもの

| 資料 | 何を読むか |
|---|---|
| `docs/spec.md:90-96` | ★**F-08 施設ロボットマスタ（参照のみ）**★。★`:95`「登録・更新はD-Security側の画面で行うため、D-WatchはGETのみ」★ |
| `docs/spec.md:98-101` | ★**F-09 スケジュール（参照のみ）**★。「Pudu Cloud由来。変更はPudu Cloud管理画面で行う」 |
| `docs/spec.md:103-109` | ★**F-10 システム管理（P1限定スコープ）**★。サブタブごとの扱いの表 |
| `docs/decisions.md:194-196`（**D-031**） | 点検記録は P1 スコープ。★**隠すのはデモまでの一時対応であって、スコープは変えない**★ |
| `docs/decisions.md:198-201`（**D-032**） | system の限定スコープ。★実装する = 表示設定 / 運用設定★、★実装しない = 通知設定 / 顧客管理 / グループ管理★、★スコープ外 = ユーザー管理 / 権限管理 / 監査ログ★ |
| `docs/decisions.md:330-334`（**D-053**） | ★機能しない絞り込みUIを画面に出さない★。「固定の選択肢だけがあって何も起きないUIは、不具合と区別できない」 |
| `docs/decisions.md:403-412`（**D-060**） | ★`Schedule.end_time` は「翌04:30」の整形済み文字列で返す★。`2830` → `"翌04:30"` |
| `docs/decisions.md:1052-1058`（**D-072 項7**） | ★点検記録は最後★。写真の置き場所・バケット・期限付き署名URLが未決 |
| `docs/decisions.md:2146-2179`（**D-079 項3**） | ★本作業の発端★。★`:2171`「`schedules` の fixtures 地雷は、プレースホルダ画面の指示書に必ず入れる」★／★`:2173`「`inspections` は『デモまで一時的に隠す』で正しい。D-031 のスコープは変えない」★ |
| `docs/decisions.md:2202-2245`（**D-080**） | ★`facilities.contract` は10桁文字列★。★数値比較禁止★ |
| `docs/fixtures.md:165-178`（§7） | 施設ロボットマスタの用意すべきケース |
| `docs/fixtures.md:180-198`（§8） | ★**スケジュールの補完ペア設計**★。段階3 の地雷の一次資料 |
| `docs/fixtures.md:235-268`（§12） | ★**ダッシュボードの日付ごとの期待値表**★。★当日は `planned=10 / completed=9 / anomaly 出ない`★ |
| `CLAUDE.md` §4.1 / §4.6 / §6-1 / §7 / §9.3 / §9.4 | 判定閾値 / 契約フラグ / YAMLが先 / 禁止事項 / API規約 / 判定ロジックのテスト |

### ★雛形として読むもの（実装の作法を写す先）★

| ファイル | 何の雛形か |
|---|---|
| ★`app/(dashboard)/alerts/page.tsx`（5行）★ | ★**ページの雛形**★。`"use client"` は付けず、Screen コンポーネントを1つ返すだけ |
| ★`components/features/alerts/AlertsScreen.tsx`（208行）★ | ★**画面の雛形**★。`"use client"` / `useTheme()` / `apiFetch` / loading・error・0件 の3分岐（`:148-153`）/ CSS Modules |
| `components/features/alerts/AlertsScreen.module.css` | ★CSS Modules の雛形★。★色は `var(--tx-d)` 等の CSS変数で書く★（`app/globals.css:1-40`）。★インラインの色が要るときだけ `THEME_TOKENS[theme]` を使う★（`AlertsScreen.tsx:48`） |
| ★`app/api/ds-devices/route.ts`★ | ★**APIルートの雛形**★。`withRequestId` / `parseXxxQuery` の422 / `logApiError` の500 / `meta` の組み立て |
| ★`lib/ds-devices/query.ts`★ | ★**クエリ純粋関数の雛形**★。`parseDsDeviceQuery` / `filterDsDevicesByQ`（`:58-68`。★大文字小文字を無視した部分一致★）/ `sortDsDevices` |
| ★`lib/ds-devices/__tests__/query.test.ts`★ | ★**テストの雛形**★。`row(overrides)` ヘルパ + `describe` ごとの分割 |
| `app/api/prechecks/route.ts` / `lib/prechecks/query.ts` | 同上のもう1例（`ds-devices` と見比べて共通の作法を掴む） |

### ★踏まないでほしい地雷（先に頭に入れること）★

| # | 内容 |
|---|---|
| 1 | ★**`lib/fixtures/schedules.ts` に `active: true` を足すとダッシュボードが壊れます**★。★段階3 の地雷警告を必ず読んでから触ること★ |
| 2 | ★**`cwd` のドライブレターは必ず大文字 `C:\`**★。小文字だと Vitest が全滅します（既知の罠） |
| 3 | ★**`--reporter=basic` は Vitest 4 で削除済み**★。使うとロードに失敗しても exit 0 になり、★**壊れていることに気づけません**★。素の `npm test` を使うこと |
| 4 | ★**lint は `npm run lint`（= `eslint`）**★。★`next lint` は Next 16 で削除済み★ |
| 5 | **決定番号は追記直前に最終番号を確認する**。★2026-09-07 に `D-068` の二重採番が実際に起きました★ |
| 6 | ★**`d-watch-mock.jsx:54-63` の `ROBOTS` は実在の店舗情報**★。★見た瞬間にコピーしたくなる形をしていますが、絶対に使わないこと★ |

### 段階0の完了条件

- 上の資料を読んだこと
- `git status` がクリーンであること
- ★**着手前にユーザーへ確認すること（2件）の回答を得たこと**★
- `npm test` / `npx tsc --noEmit` / `npm run lint` を実行し、★**実測値を控えたこと**★
  （作成時点の基準は **22ファイル / 461テスト / tsc 0 / lint 0**。古くなっている可能性があります）
- ★**コミットしない**★

---

## 段階1 — API契約を直す（`d-watch-api.yaml`）+ 型を足す

★**`CLAUDE.md` §6-1「API契約を変えたくなったら、先に `d-watch-api.yaml` を直す」に従い、この段階を最初に置きます。**★
実装だけ変えて YAML と乖離させないためです。

### 1-1. ★`Schedule` スキーマに `facility_id` が無い★

`d-watch-api.yaml:1361-1375`:

```yaml
    Schedule:                                                            # :1361
      type: object
      description: モックのSCHED(s/f/d/st/en/on)に対応
      properties:
        id: { type: integer, example: 21 }
        serial_number: { type: string, example: "8111R5628060011" }
        facility_name: { type: string, example: "バリューマート渋谷本店" }
        weekdays:
          type: string
          example: "1111111"
          description: 日月火水木金土(清掃する曜日=1)。link_schedules同様の7桁形式
        weekdays_label: { type: string, example: "毎日", description: 表示用ラベル(サーバ生成) }
        start_time: { type: string, example: "02:00" }
        end_time: { type: string, example: "03:00" }
        active: { type: boolean }
```

★**`facility_id` がありません。**★ しかし ★**`lib/types/index.ts:352` には `facility_id: number` があり、fixtures 13件すべてが値を持っています**★。
★**YAML に追加してください**★（`serial_number` の後、`facility_name` の前が自然）:

```yaml
        facility_id: { type: integer, example: 1 }
```

★**「型と fixtures にあるものが YAML に無い」は、YAML を正とすると実装のほうが違反している状態です。**★
★実装側を削るのではなく YAML に足すのは、`facility_id` が「施設別の絞り込み」に将来必要になるためです★
（`DsDevice`（`:1254-1258`）も同じ形で `facility_id` と `facility_name` の両方を持っています）。

### 1-2. ★`q` パラメータを追加する（★確認2 の回答が「推奨どおり」だった場合★）★

★**確認2 でユーザーが `q` の追加を承認した場合のみ実施してください。**★

**`/facilities`（`:739-753`）に追加**:

```yaml
        - name: q
          in: query
          schema: { type: string }
          description: 施設名 or 都道府県 or 住所 or シリアル番号 or メーカー or 機種(部分一致・大文字小文字を区別しない)
```

**`/schedules`（`:773-781`）に追加**:

```yaml
        - name: q
          in: query
          schema: { type: string }
          description: 施設名 or シリアル番号(部分一致・大文字小文字を区別しない)
```

★**既存の `facility_name` / `serial` / `address` は消さないこと**★。
★description に「画面の検索欄は `q` を使う。個別パラメータは互いに AND、`q` は個別パラメータと AND（`q` の内部だけが OR）」と明記すること★。
★**この AND / OR の関係を書いておかないと、次に触る人が必ず迷います。**★

### 1-3. ★`Facility` / `Schedule` / `Meta` を実装と突き合わせる★

★**`facility_id` 以外にも食い違いが無いか、必ず自分の目で突き合わせてください。**★

| YAML | TS | 突き合わせ結果（★作成時点の実測★） |
|---|---|---|
| `Facility`（`:1337-1359`） | `lib/types/index.ts:324-347` | ★**一致**★（`id` / `facility_name` / `prefecture` / `address` / `phone` / `manager_name` / `charger_name` / `email` / `monitoring_enabled` / `d_security_enabled` / `robots`） |
| `Facility.robots[]`（`:1352-1359`） | `RobotSummary`（`:317-322`） | ★**一致**★（`id` / `maker` / `model` / `serial_number`） |
| `Schedule`（`:1361-1375`） | `:349-360` | ★**`facility_id` だけ不足**★ → 1-1 で追加 |
| `Meta`（`:1004-1009`） | `lib/types/index.ts` の `Meta` | `total` / `page` / `per_page`。★一覧APIは必ずこれを返す★（`CLAUDE.md` §9.3） |

★**突き合わせて新たな食い違いを見つけたら、この表に追記したうえで直し、段階11 で記録すること。**★

### 1-4. ★`AdviceSetting` の TS 型が存在しない（★調査で判明★）★

★**`d-watch-api.yaml:1229-1252` に `AdviceSetting` スキーマがありますが、`lib/types/index.ts` に対応する型がありません**★（実測。`grep -n "AdviceSetting" lib/types/index.ts` が 0件）。
`app/api/settings/advice/route.ts` の `GET` は ★**インラインのオブジェクトリテラル**★ を返しています。

★**`CLAUDE.md` §9.3「APIレスポンスは必ず型を定義してから実装する」に従い、`lib/types/index.ts` に `AdviceSetting` を追加してください**★（段階6 の画面がこれを使います）。
★YAML の `:1229-1252` に合わせること★:

| フィールド | 型 |
|---|---|
| `rule` | `"R1" \| "R2" \| "R3" \| "R4" \| "B1" \| "B2" \| "B3"` |
| `phase` | `"A" \| "B"` |
| `level` | `1 \| 2 \| 3` |
| `icon` | `string` |
| `title` | `string` |
| `text` | `string` |
| `actions` | `string[]` |
| `condition` | `string`（★発火条件の説明。編集不可★） |
| `customized` | `boolean`（既定値から変更されているか） |

★**`AdviceRuleId` は `lib/config/advice-rules.ts:16` にすでにあります**★。★`lib/types/` から `lib/config/` を import するか、型を再定義するかは、既存の依存方向に合わせて判断してください★（★どちらにせよ2箇所に別々の union を書かないこと★）。

★**ルート側（`app/api/settings/advice/route.ts` の `GET`）を、この型を返すように注釈すること**★。★ロジックは変えないこと★ — 型注釈を足すだけです。

### 1-5. ★既知の不整合として記録するもの（★今回は直さない★）★

★**`InspectionDetail.photos` の項目名が YAML と TS で食い違っています。**★

| 側 | 実際の姿 |
|---|---|
| ★YAML（`d-watch-api.yaml:1402-1405`）★ | ★**`item_type`**★ / `enum: [brush, dust_box, filter, squeegee, suction_hose, waste_tank, brush_back]`（★英語 enum★） |
| ★TS（`lib/types/index.ts:364-377`）★ | ★**`item`**★ / `InspectionItemName = "ブラシ" \| "ダストボックス" \| ...`（★日本語リテラル★） |

★**キー名も値の形も違います。**★ ★**今回は `inspections` を実装しないので直しません**★（禁止事項10 / D-072 項7）。
★**段階11 で「既知の不整合として未解決のまま残っている」ことを `docs/decisions.md` に記録してください。**★
★点検記録に着手する人が、実装を始める前にこれを解決する必要があります。★

### 1-6. ★★`d-watch-api.yaml` に実在の人名が漏れている（★調査で判明。ついでに直すこと★）★★

★**`d-watch-api.yaml` の2箇所に、実在の人名がそのまま `example` として書かれています**★（実測）:

| 行 | 内容 |
|---|---|
| ★`d-watch-api.yaml:861`★ | `inspector_name: { type: string, maxLength: 100, example: "香原秀俊" }` |
| ★`d-watch-api.yaml:1383`★ | `inspector_name: { type: string, example: "香原秀俊" }` |

★出所は `d-watch-mock.jsx:81-88` の `INSPECTIONS` 7件★（★点検者名が全件この名前。実在の店舗名も入っています★）。
★**`CLAUDE.md` §7「モックに残っている実在の住所・電話番号をそのまま使うこと」の禁止に該当します**★（D-043 と同趣旨）。

★**この段階で `d-watch-api.yaml` を開いているので、ついでにダミー名へ置換してください**★（例: `"点検 太郎"`）。
★2行だけの変更で、`inspections` の実装には一切踏み込みません**★（禁止事項10 に抵触しません）。
★**`d-watch-mock.jsx` 側は書き換えないこと**★ — ★モックは「画面レイアウト・文言・遷移の正」として保存されている資料で、本作業の対象外です★。
★段階11 で「YAML の `inspector_name` の example をダミー名に置換した」ことを記録すること★。

### 段階1の完了条件

- ★**`d-watch-api.yaml` の `Schedule` に `facility_id` があること**★（`grep -n "facility_id" d-watch-api.yaml` で `Schedule` 配下にヒットすること）
- ★確認2 が承認された場合★: `/facilities` と `/schedules` に `q` があり、★AND / OR の関係が description に書かれていること★
- ★**`lib/types/index.ts` に `AdviceSetting` があること**★
- ★**`InspectionDetail.photos` の不整合を直していないこと**★（記録するだけ。★直すと段階11 の記録と矛盾します★）
- ★**`grep -n "香原" d-watch-api.yaml` が 0件であること**★（1-6）
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス（★この段階ではまだ全部通るはず★）
- ★**ここで1コミット**★（D-050）

---

## 段階2 — `GET /api/facilities` を作る

### 2-1. 作るもの

| ファイル | 内容 |
|---|---|
| ★新規★ `app/api/facilities/route.ts` | ルート本体。★雛形は `app/api/ds-devices/route.ts`★ |
| ★新規★ `lib/facilities/query.ts` | ★**フィルタ・ページングの純粋関数**★。★雛形は `lib/ds-devices/query.ts`★ |
| ★新規★ `lib/facilities/__tests__/query.test.ts` | テスト。★雛形は `lib/ds-devices/__tests__/query.test.ts`★ |

★**必ず `ds-devices` / `prechecks` の現物を開いて、エラー形式・`meta`・ログの作法を写してください。**★
★この指示書に書いてある形と現物が違ったら、★現物が正★です（この指示書のほうが古い可能性があります）。★

### 2-2. ★フィルタとページングは API ルート側で組む★

★**`db.facilities()`（`lib/db/index.ts:99-101`）は全件返すだけです。**★
これは意図した設計で、`lib/db/index.ts:50-54` にこう書かれています:

> データソースが実装すべきインターフェース。
> フィルタや集計はこの層では行わず、APIルート側で組み立てます
> （実DB接続時にクエリへ落とすときの差分を小さくするため）。

★**この方針に従ってください。`lib/db/` にフィルタを足さないこと。**★

★**注記（実測）**★: ★`prismaSource` 側（`lib/db/index.ts:153-164`）は `facilities` / `schedules` / `inspections` / `inspectionDetail` のいずれも `notImplemented()` です★。
★**したがって今回作る API ルート2本は `USE_FIXTURES` のときだけ動きます**★。★これは意図した状態です★（実DB接続は別作業。禁止事項9）。
★**`prismaSource` を実装しようとしないこと。段階11 で「実DB接続時には別途 `prismaSource` の実装が要る」ことを申し送りとして記録してください。**★

### 2-3. クエリパラメータ

| パラメータ | 挙動 |
|---|---|
| `q` | ★施設名 / 都道府県 / 住所 / シリアル / メーカー / 機種 を **OR** で部分一致★（★確認2 で承認された場合★） |
| `facility_name` | 施設名の部分一致 |
| `serial` | ★**`robots[].serial_number` を横断して部分一致**★（下記 2-4） |
| `address` | 住所の部分一致 |
| `page` / `per_page` | `PAGINATION`（`lib/config/index.ts`）の `defaultPerPage: 50` / `maxPerPage: 200`。★不正値でも422にせず既定値・上限へ丸める★（`parseDsDeviceQuery` と同じ挙動） |

★**大文字小文字を無視した部分一致にすること**★（モックの挙動。`d-watch-mock.jsx:998-1002` が `.toLowerCase()` している）。
★`filterDsDevicesByQ`（`lib/ds-devices/query.ts:59-68`）がそのまま雛形になります。★

### 2-4. ★`serial` は施設が持つロボットを横断する★

★**施設は0台〜3台のロボットを持ちます。**★ `serial` の部分一致は ★**どれか1台でも一致すればその施設をヒットさせる**★ 形にしてください。

```ts
// イメージ（実装は任せます）
items.filter((f) => f.robots.some((r) => r.serial_number.toLowerCase().includes(needle)))
```

★**`robots` が空配列の施設で落ちないこと**★ — `some()` は空配列に対して `false` を返すので自然に安全ですが、★テストで固定してください★（`lib/fixtures/facilities.ts` の ★id12（`フレッシュタウン湖畔店`）が `robots: []`★）。

### 2-5. `meta` を返す

★**一覧APIは必ず `meta: { total, page, per_page }` を返す**★（`CLAUDE.md` §9.3）。
★`total` は「フィルタ後・ページング前」の件数★（`app/api/ds-devices/route.ts` の `const total = sorted.length;` と同じ）。

★**`/facilities` に `summary` はありません**★（`d-watch-api.yaml:754-765` は `items` と `meta` だけ）。
★`SummaryListResponse` ではなく `items` + `meta` のレスポンス型を使うこと★（`lib/types/index.ts` の既存の一覧レスポンス型を確認してから決めること）。

### 2-6. 並び順

★**YAML にもモックにも並び順の指定がありません。**★
★**`id` の昇順で安定させてください**★（`sortDsDevices` が「安定化のためだけの規則」として `serial_number` 昇順にしているのと同じ趣旨）。
★**理由を関数の docstring に書くこと**★ — 「運用上の優先度を表すものではなく、画面の順序がリロードで変わらないようにするための規則」。

### 2-7. テスト（★期待値表を先に作ること★。`CLAUDE.md` §9.4）

| # | ケース | 期待 |
|---|---|---|
| 1 | ★0件★（空配列を渡す） | `[]` / `meta.total = 0`。★落ちない★ |
| 2 | ★1件★ | そのまま返る |
| 3 | ★部分一致（施設名）★ | `メガバリュー` で複数ヒット |
| 4 | ★大文字小文字違い★ | `8111r` で `8111R5628060011` にヒットする |
| 5 | ★該当なし★ | `[]` / `meta.total = 0` |
| 6 | ★ページング境界★ | `per_page=5` で `page=1` / `page=2` / `page=3`（★最終ページが端数になること★）/ 範囲外の `page=99` は `[]` だが ★`meta.total` は全件数のまま★ |
| 7 | ★ロボット0台の施設★ | `serial` 検索で落ちない・ヒットしない |
| 8 | ★複数台の施設★ | 3台のうち3台目のシリアルで検索してもヒットする（`lib/fixtures/facilities.ts` の ★id7 が3台★） |
| 9 | ★`q` の OR★ | 住所の文字列で検索して施設名が違う施設がヒットする |
| 10 | ★複数パラメータの AND★ | `facility_name` と `address` を同時に指定して、両方満たす施設だけが残る |
| 11 | ★`per_page` の丸め★ | `per_page=9999` → `200`（`maxPerPage`）/ `per_page=0` → `1` / `page=-1` → `1` |

★**テストは fixtures ではなく `row(overrides)` ヘルパで組んだ合成データで書くこと**★（`lib/ds-devices/__tests__/query.test.ts:11-21` の作法）。
★fixtures に依存すると、fixtures を1件足しただけでテストが落ちます。★

### 段階2の完了条件

- `app/api/facilities/route.ts` / `lib/facilities/query.ts` / `lib/facilities/__tests__/query.test.ts` が存在すること
- ★**`lib/db/index.ts` に変更が入っていないこと**★（`git diff --stat lib/db/` が空）
- ★**期待値表の11ケースがテストに入っていること**★
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス（★テスト数が増えていること★）
- ★**ここで1コミット**★

---

## 段階3 — `GET /api/schedules` を作る

### ★★★この段階には地雷があります。実装より先に 3-3 を読んでください★★★

### 3-1. 作るもの

| ファイル | 内容 |
|---|---|
| ★新規★ `app/api/schedules/route.ts` | 段階2 と同じ形 |
| ★新規★ `lib/schedules/query.ts` | フィルタ・ページングの純粋関数 |
| ★新規★ `lib/schedules/__tests__/query.test.ts` | テスト |

### 3-2. クエリパラメータ

| パラメータ | 挙動 |
|---|---|
| `q` | ★施設名 / シリアル を **OR** で部分一致★（★確認2 で承認された場合★） |
| `facility_name` | 施設名の部分一致 |
| `serial` | ★`serial_number` の部分一致★（★`Schedule` は1レコードに1シリアルなので、段階2 のような横断は不要★） |
| `page` / `per_page` | 段階2 と同じ |

並び順は ★`id` の昇順★（段階2 と同じ理由）。

### 3-3. ★★★地雷 — `lib/fixtures/schedules.ts` に `active: true` を追加しないこと★★★

★**この地雷は「エラーも出ず、テストも通るのに、ダッシュボードの既定表示が異常系になる」タイプです。**★
★**D-079 項3（`docs/decisions.md:2171`）が「この指示書に必ず入れる」と名指しした項目です。**★

#### 何が起きるのか

★**`lib/dashboard/aggregate.ts:93` の1行がダッシュボードの「清掃予定件数」です**★:

```ts
// lib/dashboard/aggregate.ts:89-93
const dow = weekdayIndex(date);
const planned =
  dow === null
    ? 0
    : schedules.filter((s) => s.active && s.weekdays[dow] === "1").length;   // :93
```

★**施設を区別しない、フラットな件数カウントです。**★ どの施設のログかは一切参照しません。

一方 `lib/fixtures/clean-logs.ts` の当日分は ★**曜日に関係なく毎日必ず10行**★ 生成されます（実行日からの相対生成）。
★**この2つを常に一致させるために、fixtures は「どの曜日でも `planned = 10` になる」よう設計されています。**★

| 構成 | 内訳 |
|---|---|
| 補完ペア① | ★id25（平日のみ `0111110`）＋ id26（土日のみ `1000001`）★ → 全曜日を過不足なくカバー → ★常に合計1件★ |
| 補完ペア② | ★id23（月水金 `0101010`）＋ id30（日火木土 `1010101`、id23 の反転）★ → ★常に合計1件★ |
| 毎日（`1111111`） | id21 / id22 / id27 / id28 / id29 / id31 / id32 / id33 の ★8件★ |
| ★合計★ | ★**1 + 1 + 8 = 10。どの曜日でも常に10件**★ |

#### 何をすると壊れるのか

| 操作 | 症状 |
|---|---|
| ★**`active: true` を1件追加する**★ | ★`planned` が **11** になる★。当日ログは10行のまま → 画面が ★**「9 / 11 完了」**★ と出る（`components/features/dashboard/ScheduleProgressCard.tsx:25-27` が `{completed} / {planned} 完了` を描画）。さらに ★`app/api/dashboard/route.ts:84-92` の `data.anomaly` warn が出続けます★ |
| ★**曜日パターン付きで `active: true` を追加する**★ | ★**その曜日に当たった日だけ症状が出ます**★。★一番タチが悪い★ — 実装した日には再現せず、数日後に「なぜか壊れている」状態で発見されます |
| ★**既存の `active: true` を削除・無効化する**★ | ★`planned` が **9** 以下になる★。同じく `anomaly` が出ます |
| ★**補完ペアの `weekdays` を書き換える**★ | ★曜日によって `planned` が変動します★。id23↔id30 / id25↔id26 の対応が崩れるため |
| ★**id24 を削除する**★ | ★`planned` は変わりません（`active: false` なので）が、★**唯一の無効スケジュール**★ が消えて判定ロジックの確認ケースが失われます★ |

#### ★安全な操作★

| 操作 | 可否 |
|---|---|
| ★**`active: false` の追加**★ | ★**何件でも安全**★。`s.active` が先に `false` になるため `planned` に寄与しません |
| フィールドの追加（`facility_id` など） | 安全 |
| `start_time` / `end_time` / `weekdays_label` の変更 | 安全（`planned` は `active` と `weekdays` しか見ていないため） |

#### ★番人★

★**`lib/fixtures/__tests__/schedules.test.ts`（59行）が番人です。**★

| 行 | 内容 |
|---|---|
| `:29` | `describe("fixtures の不変条件: schedules と clean-logs の当日行数（D-050）")` |
| `:36` | `test("前提: 当日分の清掃ログが1件以上存在する")` |
| `:41` | ★**7曜日分をループで生成するテスト**★ — 「◯曜日: 有効スケジュールの該当件数が当日の清掃ログ行数と一致する」 |
| ★合計★ | ★**8テスト**★（前提1 + 7曜日） |

★**`lib/fixtures/schedules.ts` を変更したら必ず `npm test` を実行し、この8テストが通ることを確認してください。**★

#### ★参照先の書き方に注意★

★**この設計を説明するときに「D-050」と書かないでください。**★

| 番号 | 実際の内容 |
|---|---|
| `docs/decisions.md:308` の **D-050** | ★**「検証（型チェック・lint・テスト）が通った時点で作業ブランチにコミットする」**★ — ★補完ペア設計とは無関係★ |
| `lib/fixtures/schedules.ts` のコメント内の「D-050」 | ★補完ペア設計を指している★（`:21` / `:83` / `:108` / `:121` / `:170` / `:184` / `:197` / `:209`） |

★**番号で参照すると必ず混乱します。**★
★**参照するなら `docs/fixtures.md` §8（`:180-198`）と §12（`:235-268`）を使ってください。**★
★既存コメント内の「D-050」表記は、今回の作業では書き換えないこと★（本作業の範囲外で、差分が増えるだけです）。

### 3-4. ★DB 側の注意（将来の `prismaSource` 実装者向け。★今回は実装しません★）★

★**今回 `prismaSource` には手を付けません（禁止事項9）**★が、
★**`lib/schedules/query.ts` の docstring に次の4点を書き残してください。**★
★実DB接続時に「型エラーもテスト失敗も出ないまま静かに壊れる」箇所だからです。★

| # | 内容 |
|---|---|
| 1 | ★**論理が反転している**★ — `prisma/schema.prisma:512` の `CleanSchedule.inactive`（`Int @default(0)`。★**0 = 有効**★）。API/TS は `active: boolean`。★**`active: Boolean(row.inactive)` と書くと全件反転します**★。正しくは `active: row.inactive === 0` |
| 2 | ★**カラム名が違う**★ — DB は `weeks`（`prisma/schema.prisma:504`、`VarChar(7)`）、API/TS は `weekdays` |
| 3 | ★**時刻は `HHMM` の4桁文字列**★（`prisma/schema.prisma:506` / `:509`）。★**`end_time` の「時」は 24〜29 を取りうる**★（`"2830"` = 翌日4:30）。★素朴な `HH:MM` パースは壊れます★。★API は D-060 に従い `"翌04:30"` の整形済み文字列で返す★ |
| 4 | ★**`weekdays_label` は DB に存在しません**★。★サーバ側で `weeks` から生成する必要があります★ |

### 3-5. テスト（★期待値表★）

| # | ケース | 期待 |
|---|---|---|
| 1 | ★0件★ | `[]` / `meta.total = 0` |
| 2 | ★1件★ | そのまま返る |
| 3 | ★部分一致（施設名）★ | ヒットする |
| 4 | ★大文字小文字違い★ | `8111r` で `8111R...` にヒット |
| 5 | ★該当なし★ | `[]` |
| 6 | ★ページング境界★ | 段階2 の #6 と同じ |
| 7 | ★`active: false` の行も返る★ | ★**一覧は有効・無効の両方を返す**★（画面が「有効」列で見せ分ける。★API 側でフィルタしないこと★） |
| 8 | ★`q` の OR★ | シリアルの一部で検索して施設名が違う行がヒット |
| 9 | ★`per_page` の丸め★ | 段階2 の #11 と同じ |

### 段階3の完了条件

- `app/api/schedules/route.ts` / `lib/schedules/query.ts` / `lib/schedules/__tests__/query.test.ts` が存在すること
- ★**`git diff --stat lib/fixtures/schedules.ts` が空であること**★（★この段階では fixtures を1文字も触っていないはず★）
- ★**`lib/fixtures/__tests__/schedules.test.ts` の8テストが全部通ること**★
- ★**3-4 の4点が `lib/schedules/query.ts` の docstring に書かれていること**★
- ★**期待値表の9ケースがテストに入っていること**★
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス（★テスト数が増えていること★）
- ★**ここで1コミット**★

---

## 段階4 — 施設ロボットマスタ画面（F-08）

### 4-1. 作るもの

| ファイル | 内容 |
|---|---|
| ★新規★ `components/features/facilities/FacilitiesScreen.tsx` | 画面本体 |
| ★新規★ `components/features/facilities/FacilitiesScreen.module.css` | スタイル |
| ★差し替え★ `app/(dashboard)/facilities/page.tsx` | `PlaceholderScreen` → `FacilitiesScreen` |

`page.tsx` は ★`app/(dashboard)/alerts/page.tsx` と同じ5行の形★ にしてください。

### 4-2. ★モックの現物を正とする★（`CLAUDE.md` §5）

★**`d-watch-mock.jsx:971-1035` を実際に開いて写してください。**★ 以下は要点です。

| 要素 | 内容 |
|---|---|
| h1 | ★**`施設ロボットマスタ`**★（`:975`）。★サブタイトルなし★（`AlertsScreen.tsx:98` のような `subtitle` は付けない） |
| 検索input | ★**1つ**★。placeholder = ★**`施設名 / シリアル / 住所`**★（`:977`） |
| 表の列（★10列・原文のまま★。`:986-995`） | ★`施設名` / `都道府県` / `住所` / `電話番号` / `責任者名` / `担当者名` / `メール` / `監視` / `D-Sec` / `ロボット（メーカー / 機種 / シリアル）`★ |
| `監視` 列（`:1013`） | ★true → `○`（緑 `c.ok`）/ false → `ー`（`c.txM`）★。中央寄せ・fontSize 16 |
| `D-Sec` 列（`:1014`） | ★true → `○`（★シアン `c.ac`★）/ false → `ー`（`c.txM`）★ |
| ロボット列（`:1015-1025`） | ★**1台1行**★。★メーカーをピル（`Pill`）★ / ★機種は通常テキスト★ / ★シリアルは monospace★ |
| フッタ（`:1032`） | ★**`全{n}施設`**★ |
| 横スクロール（`:983-984`） | ★**`minWidth: 1300` 相当の表を `overflow-x: auto` で包む**★ |
| 施設名 | `fontWeight: 600`（`:1006`） |
| メール | ★monospace・fontSize 11★（`:1012`） |
| 行 | `verticalAlign: top`（`:1005`。★ロボット複数台のとき先頭行に揃うようにするため★） |

★**色は CSS変数（`var(--ok)` / `var(--ac)` / `var(--tx-m)` 等）で書くこと**★（`app/globals.css:1-40`）。
★`Pill` は `color` を文字列で受け取る（`components/ui/Pill.tsx`）ので、メーカーのピルだけは `THEME_TOKENS[theme].pr` を渡してください★（`AlertsScreen.tsx:48, 166` が前例）。

### 4-3. ★★`📥 インポート` と `+ 新規追加` ボタンは実装しないこと★★

| 事実 | 出典 |
|---|---|
| ★**登録・更新は D-Security 側の画面で行うため、D-Watch は GET のみ**★ | ★`docs/spec.md:95`★ / `d-watch-api.yaml:736` |
| ★**モックのボタンには `onClick` がありません**★ | ★`d-watch-mock.jsx:978-979`★（★飾りです★） |
| ★**動かないボタンを出すのは D-053 に抵触します**★ | `docs/decisions.md:330-334` |

★**ボタン自体を出さないこと。**★ ★グレーアウトして置いておくのも駄目です★ — 「あるのに使えない」は「不具合と区別できない」の典型です。

### 4-4. ★★実在の個人情報を絶対に使わないこと★★

★**`d-watch-mock.jsx:54-63` の `ROBOTS` 9件は、実在の店舗名・住所・電話番号・メールです。**★

```
例: MEGAドンキ渋谷本店 / 渋谷区宇田川町28-6 / 03-5428-4086 / shibuya@donki.co.jp
```

★**1文字もコピーしないこと**★（`CLAUDE.md` §7 / D-043）。
★**必ず `lib/fixtures/facilities.ts` を使ってください**★ — こちらは架空値に置換済みです:

```
メガバリューマート中央本店 / 東京都○○区1-2-3 / 03-0000-0001 / store001@example.com
```

★**画面のプレビュー用のダミー、コメント内の例、テストの期待値、いずれにも使わないこと。**★

### 4-5. 0件時の表示

★**モックには0件の分岐がありません。**★
★`AlertsScreen.tsx:151-153` の作法に倣ってください★:

```tsx
{!loading && !error && items.length === 0 && (
  <div className={styles.empty}>{t("該当する施設はありません")}</div>
)}
```

★loading / error / 0件 / データあり の4分岐すべてを作ること★（`AlertsScreen.tsx:148-205` が全部の例です）。

### 4-6. データ取得

- ★`apiFetch`（`lib/api/client.ts`）を使う★。エラーは `ApiFetchError` の `serverMessage` を優先し、無ければ既定文言（`AlertsScreen.tsx:78-81`）
- ★`per_page` は `PAGINATION.maxPerPage`（200）を渡す★（`AlertsScreen.tsx:64` と同じ。★fixtures は12件なので1ページに収まります★）
- ★検索は `q` を送る★（★確認2 で承認された場合★）
- ★**フッタの「全{n}施設」は `meta.total` を使うこと**★（`items.length` ではありません。★ページングが入ったときに嘘の数字になります★）

### 段階4の完了条件

- `/facilities` が `PlaceholderScreen` でなくなっていること
- ★**`grep -rn "MEGAドンキ\|ドンキ\|donki.co.jp\|宇田川町\|03-5428" components/features/facilities/` が 0件**★
- ★**`grep -n "インポート\|新規追加" components/features/facilities/` が 0件**★
- 0件時の表示があること
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス
- ★**ここで1コミット**★

---

## 段階5 — スケジュール管理画面（F-09）

### 5-1. 作るもの

| ファイル | 内容 |
|---|---|
| ★新規★ `components/features/schedules/SchedulesScreen.tsx` | 画面本体 |
| ★新規★ `components/features/schedules/SchedulesScreen.module.css` | スタイル |
| ★差し替え★ `app/(dashboard)/schedules/page.tsx` | `PlaceholderScreen` → `SchedulesScreen` |

### 5-2. ★モックの現物★（`d-watch-mock.jsx:1037-1069`）

| 要素 | 内容 |
|---|---|
| h1 | ★**`スケジュール管理`**★（`:1041`） |
| ★注意★ | ★**ナビのラベルは `📅 スケジュール`（`components/layout/AppShell.tsx:22`）で、h1 と一致しません**★。★これはモックどおりです★（モックのナビも `📅 スケジュール`、h1 は `スケジュール管理`）。★揃えないこと★ |
| 検索input | placeholder = ★**`施設名 / シリアル`**★（`:1042`） |
| 表の列（★5列★。`:1047-1051`） | ★`シリアル` / `施設名` / `曜日` / `時間` / `有効`★ |
| `シリアル` | ★monospace・fontSize 12★（`:1057`） |
| `時間` | ★**`{start_time} - {end_time}`**★（`:1060`） |
| `有効` 列（`:1061`） | ★on → `●`（緑 `c.ok`）/ off → `○`（★赤 `c.ng`★）★。★fontSize 18・中央寄せ★ |
| 無効行 | ★**行全体 `opacity: 0.5`**★（`:1056`） |
| ★ボタン・行クリック★ | ★**無し。完全な参照専用**★ |
| ★フッタ★ | ★モックには無い★。★付けないこと★（施設マスタと違って `全{n}件` の表示はモックに存在しません） |

### 5-3. ★`曜日` は `weekdays_label` を出す★

★**`weekdays`（7桁の `"1111111"`）を画面で解釈しないこと。**★
★**`weekdays_label`（サーバ生成の表示用ラベル。`"毎日"` / `"月水金"` / `"平日のみ"` / `"土日のみ"` / `"日火木土"`）をそのまま出してください。**★

★理由★: `CLAUDE.md` §9.6「判定結果はAPIから受け取る。フロントで再計算しない」と同じ趣旨です。
★7桁の解釈ロジックが画面とサーバの2箇所にあると、曜日の起点（日曜始まり）がズレたときに片方だけ直る事故が起きます。★

### 5-4. ★`end_time` をパースしないこと★

★**`end_time` は「翌04:30」のような整形済み文字列で来ます**★（★**D-060**★。`docs/decisions.md:403-412`）。

| DB の値 | API が返す値 |
|---|---|
| `0300` | `"03:00"` |
| `2830` | ★**`"翌04:30"`**★ |

★**画面で時刻をパースして計算しないこと。**★ ★`{start_time} - {end_time}` と並べるだけです。★
★fixtures には日跨ぎのケースがあります★ — ★id27（`メガバリューマート港南総本店`）が `start_time: "23:30"` / `end_time: "00:30"`★（`lib/fixtures/schedules.ts:133-143`）。
★**この行が「23:30 - 00:30」と表示されて崩れないことを、段階10 で目視確認してください。**★

### 5-5. 0件時の表示

段階4 と同じ作法（`AlertsScreen.tsx:151-153`）。

### 段階5の完了条件

- `/schedules` が `PlaceholderScreen` でなくなっていること
- ★**`weekdays` の7桁を画面で解釈していないこと**★（`grep -n "weekdays\[" components/features/schedules/` が 0件）
- ★**`end_time` をパース・計算していないこと**★（`grep -n "split(\":\")\|parseInt\|Number(" components/features/schedules/` を見て、時刻の計算が無いこと）
- 無効行が `opacity: 0.5` になっていること
- 0件時の表示があること
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス
- ★**ここで1コミット**★

---

## 段階6 — 運用設定画面（F-10、★読み取り専用★）

★**着手前確認1 の結果に従ってください。以下は当方の推奨スコープ（運用設定のみ・読み取り専用）での手順です。**★

### 6-1. 作るもの

| ファイル | 内容 |
|---|---|
| ★新規★ `components/features/system/SystemScreen.tsx` | 画面本体 |
| ★新規★ `components/features/system/SystemScreen.module.css` | スタイル |
| ★差し替え★ `app/(dashboard)/system/page.tsx` | `PlaceholderScreen` → `SystemScreen` |

### 6-2. ★タブは「運用設定」1つだけ★

★**モックは9タブありますが（`d-watch-mock.jsx:1156-1166`）、D-032（`docs/decisions.md:198-201`）で実装対象は2つだけです。**★

| タブ | D-032 での扱い | 今回 |
|---|---|---|
| 表示設定 | ★実装する★ | ★**作らない**★（確認1。永続化とダッシュボード側の対応が本作業の範囲を超えるため。★P1 後半の宿題として段階11 で記録★） |
| ★**運用設定**★ | ★実装する★ | ★**作る（読み取り専用）**★ |
| 通知設定 / 顧客管理 / グループ管理 | ★**実装しない**★（D-Security側画面を流用） | 作らない |
| ユーザー管理 / 権限管理 / 監査ログ | ★**スコープ外**★（P2以降） | 作らない |

★**タブが1つしかないなら、タブUIそのものを出さないことも検討してください。**★
★1つしか選べないタブ列は「他にもあるはずなのに無い」という誤解を生みます。★
★当方の推奨は「タブUIを出さず、h1 `システム管理` の下にセクション見出し `運用設定` を置く」★ですが、★判断は実装者に委ねます。採った形を段階11 で記録すること★。

### 6-3. ★モックの既知のバグ — `ops` タブが二重定義されている★

★**モックの `system` にはバグがあります。実装前に知っておいてください。**★

| 箇所 | 内容 |
|---|---|
| `d-watch-mock.jsx:1158` | `{ id: "ops", label: t("運用設定") }` ★1回目★ |
| ★`d-watch-mock.jsx:1164`★ | ★**`{ id: "ops", label: t("運用設定") }` 2回目（同じ id）**★ |
| `d-watch-mock.jsx:1221` | `{sysTab === "ops" && (` ★本文1回目★ |
| ★`d-watch-mock.jsx:1628`★ | ★**`{sysTab === "ops" && (` 本文2回目**★ |

★**React は同じ条件の分岐を2つとも描画するので、モックでは運用設定のカードが縦に2つ並びます。**★
★**どちらかに寄せる必要があります。**★

★**当方の推奨は `:1628` 側です**★:

| 理由 | 内容 |
|---|---|
| 1 | ★**カード1枚に3セクションが統合されている**★（`:1630` が1枚のカード、その中に事前チェック / 判定閾値 / エスカレーション）。`:1221` 側はカードが分かれている |
| 2 | ★**× グレードに「上記未満」表記がある**★（`:1670`）。`:1221` 側にはこの配慮が無い |
| 3 | ★**「変更はDFAシステム管理者のみ」の注記がある**★（`:1633`）。★権限の話が入っている＝後から書かれたように見える★ |

★**ただし、どちらが正かはモックからは判断できません。**★
★**「推奨」であって「確定」ではないことを理解したうえで採用し、採った側を段階11 で記録してください。**★

### 6-4. ★★閾値をハードコードしないこと★★

★**`CLAUDE.md` §7 の禁止事項です。必ず `lib/config/` から読んでください。**★

★**`lib/config/index.ts` の実際のエクスポート名を自分で読んで確認すること。**★ ★作成時点の実測は次のとおりです★:

| 表示するもの | 読む先 | 作成時点の値 |
|---|---|---|
| 事前チェック 第1回 / 第2回 | ★`PRECHECK_MINUTES_BEFORE`★ | `[60, 30] as const` |
| ◎ の閾値 | ★`GRADE_THRESHOLDS.excellent`★ | `90` |
| ○ の閾値 | ★`GRADE_THRESHOLDS.good`★ | `70` |
| △ の閾値 | ★`GRADE_THRESHOLDS.poor`★ | `10` |
| Lv2 の連続日数 | ★`ESCALATION_THRESHOLDS.level2Days`★ | `2` |
| Lv3 の連続日数 | ★`ESCALATION_THRESHOLDS.level3Days`★ | `3` |

★**`PRECHECK_MINUTES_BEFORE` は配列です**★。★「第1回 = `[0]`、第2回 = `[1]`」と決め打ちせず、配列を `map` して「第{i+1}回チェック: {n}分前」と描くほうが安全です★（将来3回になっても壊れません）。

### 6-5. ★★閾値の文言 — モックの「以上」をそのまま写さないこと★★（★調査で判明★）

★**モックと確定仕様が食い違っています。**★

| 側 | ◎ | ○ | △ | × |
|---|---|---|---|---|
| ★モック（`d-watch-mock.jsx:1660-1663`）★ | ★90 **以上**★ | ★70 **以上**★ | 10 以上 | 上記未満 |
| ★`CLAUDE.md` §4.1（★確定仕様★）★ | ★**90% 超**★ | ★**70% 超**★ | ★10% 以上 70% 以下★ | 10% 未満、または未実施 |
| ★`lib/config/index.ts` のコメント（★実装★）★ | ★「この値を**超える**と ◎（90.0 は ○ になる）」★ | ★「この値を**超える**と ○（70.0 は △ になる）」★ | 「この値を**下回る**と ×（10.0 は △ のまま。D-020b）」 | — |

★**`CLAUDE.md` §5 の「モックが文言の正」より、§4.1 の確定仕様が上位です。**★
★**画面には「90% 超」「70% 超」「10% 以上」と書いてください。**★ ★モックの「以上」を写すと、90.0% が ◎ に見える誤った説明になります。★
★**この判断を段階11 で記録すること。**★

### 6-6. 表示内容（★すべて読み取り専用★）

| セクション | 内容 |
|---|---|
| ★事前チェック設定★ | 第1回 60分前 / 第2回 30分前（★`PRECHECK_MINUTES_BEFORE` から★）。説明「清掃開始予定時刻の何分前にロボット状態をチェックするか」 |
| ★完了率の判定閾値★ | ◎ 90% 超 / ○ 70% 超 / △ 10% 以上 / × 上記未満（★6-5 の文言★）。★`GradeBadge`（`components/ui/GradeBadge.tsx`）が使えるか確認すること★ |
| ★アドバイス エスカレーション閾値★ | Lv2 = 2日以上連続 / Lv3 = 3日以上連続。★**注記「『連続』はカレンダー日ではなくスケジュール存在日の連続」を必ず入れること**★（★**D-026**★。`docs/decisions.md:122`。`CLAUDE.md` §4.5 も同旨） |
| ★アドバイス文言の一覧★ | 下記 6-7 |

### 6-7. ★アドバイス文言の一覧★

★**`GET /api/settings/advice`（実装済み）から取得してください。**★
★レスポンスは `{ items: AdviceSetting[] }`★（段階1-4 で型を足しています）。7件（R1〜R4 / B1〜B3）が返ります。

| フィールド | 表示 |
|---|---|
| `rule` | ルールID（`R1` 等） |
| `icon` / `level` | アイコンとレベルバッジ（`level` は 1=注意 / 2=警告 / 3=重大。`lib/config/advice-rules.ts:22`） |
| `title` | 見出し |
| `text` | 推定原因 |
| `actions` | 推奨アクション（★配列なのでリストで出す★） |
| ★`condition`★ | ★**発火条件。「編集不可」であることを画面に明示すること**★（`d-watch-api.yaml:1246-1249`「発火条件の説明(編集不可)」/ `lib/config/advice-rules.ts:30`） |
| `customized` | 既定値から変更されているか。★「編集済み」バッジに使えます★ |

★**`GET` だけを使うこと。`PATCH`（`d-watch-api.yaml:572-599`）は今回使いません。**★
★**`lib/config/advice-rules.ts` を画面から直接 import しないこと**★ — ★API は既定値に運用設定の編集内容を重ねて返します★（`app/api/settings/advice/route.ts` の `loadAdviceRules()`）。★設定ファイルを直接読むと、編集済みの文言が画面に出なくなります。★

★**注意**★: ★指示書K の段階7-B が `lib/config/advice-rules.ts:111`（B2）と `:121`（B3）の `condition` 文字列を書き換えます★。
★この画面は API 経由で読むので影響を受けませんが、★指示書K を先に実行した場合は表示される文言が変わります★。★不具合ではありません。★

### 6-8. ★「編集は P1 後半で対応します」と明示する★

★**画面のどこか（見出しの近く、または各セクションの上）に明示してください。**★

> ★この画面は現在★**参照のみ**★です。設定値の編集は P1 後半で対応します。★

★**動かないトグル・入力欄・保存ボタンを置かないこと**★（D-053）。
★モックの `<input type="number">`（`:1643` / `:1648` / `:1668` / `:1684` / `:1689`）は ★`defaultValue` だけで `onChange` も保存先も無い飾り★ です。★値をテキストとして表示してください。★

### 段階6の完了条件

- `/system` が `PlaceholderScreen` でなくなっていること
- ★**`grep -nE "\b(90|70|10|60|30)\b" components/features/system/SystemScreen.tsx` に閾値の数値リテラルが無いこと**★（★すべて `lib/config/` 由来★。★CSS の px 値は対象外なので `.module.css` は除いて確認すること★）
- ★**「90% 超」「70% 超」と表示されていること**★（★「90% 以上」ではない★。6-5）
- ★**「『連続』はスケジュール存在日の連続」の注記があること**★（D-026）
- ★**`condition` が「編集不可」と明示されていること**★
- ★**「編集は P1 後半で対応します」が画面に出ていること**★
- ★**`<input>` / `<button>`（保存系）を1つも置いていないこと**★
- ★**「表示設定」タブを作っていないこと**★
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス
- ★**ここで1コミット**★

---

## 段階7 — `inspections` をメニューから隠す

### 7-1. 直すのは1行だけ

★**`components/layout/AppShell.tsx:12`**★:

```tsx
const MENU_MONITORING = [
  { href: "/detail-log", id: "detailLog", label: "📋 清掃詳細ログ" },
  { href: "/alerts", id: "alert", label: "🔔 アラート" },
  { href: "/reports", id: "report", label: "📈 レポート" },
  { href: "/inspections", id: "inspection", label: "🔧 点検記録" },   // ★:12 この行★
] as const;
```

### 7-2. ★削除ではなくコメントアウトし、理由をコメントで残すこと★

★**復活させるときに、何を復活させればよいかが1行で分かる形にしてください。**★

```tsx
  // ★デモまで一時的に隠す（D-079 項3）★
  //   D-031 の「点検記録は P1 スコープ」は変えていない。
  //   写真ストア（バケット・期限付き署名URL）が未着手のため画面が成立しないため（D-072 項7）。
  //   ★復活させるときはこの1行のコメントを外すだけでよい★
  //   （`app/(dashboard)/inspections/page.tsx` と `lib/i18n.ts:14` は残してある）。
  // { href: "/inspections", id: "inspection", label: "🔧 点検記録" },
```

### 7-3. ★残すもの★

| 対象 | 理由 |
|---|---|
| ★`app/(dashboard)/inspections/page.tsx`★ | ★**直URLでは到達可能なままにする。これは意図した状態です**★（メニューから消すだけで、スコープからは外していないため） |
| ★`lib/i18n.ts:14` の `"🔧 点検記録": "🔧 Inspections"`★ | ★復活させるときに要ります★ |
| ★`lib/i18n.ts:39` の `点検記録: "Inspection Records"`★ | ★同上★（`page.tsx` が `PlaceholderScreen title="点検記録"` で使っています） |
| ★`components/layout/PlaceholderScreen.tsx`★ | ★`inspections` がまだ使っています★（禁止事項13） |
| ★`lib/fixtures/inspections.ts` / `db.inspections()`★ | 触らない |

### 7-4. ★型エラーは出ません★

★**`MENU_MONITORING` は `as const` の配列リテラルです。**★
★この1行を消しても、要素数に依存した型は無く、`m.id` は `key` にしか使われていないため型エラーは出ません★（`AppShell.tsx` を読んで確認してください）。
★**逆に言えば、消したことをコンパイラは教えてくれません。段階10 の目視確認が唯一の検証です。**★

### 段階7の完了条件

- ★**`npm run dev` 相当の環境でメニューに「🔧 点検記録」が出ないこと**★（★段階10 で確認★）
- ★**`app/(dashboard)/inspections/page.tsx` が残っていること**★
- ★**`lib/i18n.ts:14` と `:39` が残っていること**★
- ★**コメントに「D-079 項3」「D-031 のスコープは変えない」「D-072 項7」の3つが書かれていること**★
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス
- ★**ここで1コミット**★

---

## 段階8 — i18n

### 8-1. ★新しく追加した文言をすべて `lib/i18n.ts` の `EN` に足す★

★**`lib/i18n.ts` はキーが日本語原文のフラット辞書1枚です。完全一致でしか引けません。**★
★キーに無い文言は原文（日本語）のまま出ます — エラーにはならないので、★足し忘れに気づけません★。

### 8-2. ★すでに定義済みのもの（★重複キーを作らないこと★）★

| 分類 | キー | 行 |
|---|---|---|
| メニューラベル | `🔧 点検記録` / `🏢 施設ロボットマスタ` / `📅 スケジュール` / `⚙ システム管理` | ★`:14` / `:17` / `:18` / `:19`★ |
| セクション見出し | `モニタリング` / `設定` | ★`:20` / `:21`★ |
| 画面タイトル | `施設ロボットマスタ` / `スケジュール管理` / `システム管理` / `点検記録` | ★`:36` / `:37` / `:38` / `:39`★ |
| その他 | `準備中` | ★`:50`★ |
| 表の列（★流用できるもの★） | `施設名` / `シリアル` / `機種` | ★`:22` / `:24` / `:23`★ |
| 数値混じり | `全{n}施設を表示 →` | ★`:95`★ |

★**上のキーを再定義しないこと。**★ ★オブジェクトリテラルの重複キーは後勝ちで上書きされ、lint が拾わない可能性があります。★
★**足す前に必ず `grep -n "キー名" lib/i18n.ts` で確認してください。**★

### 8-3. ★足す必要があるもの（★実測。着手時に再確認すること★）★

| 画面 | 足す必要があるキーの例 |
|---|---|
| F-08 | `都道府県` / `住所` / `電話番号` / `責任者名` / `担当者名` / `メール` / `監視` / `D-Sec` / `ロボット（メーカー / 機種 / シリアル）` / `該当する施設はありません` |
| F-09 | `曜日` / `時間` / `有効` / `該当するスケジュールはありません` |
| F-10 | `運用設定` / `事前チェック設定` / `完了率の判定閾値` / `アドバイス エスカレーション閾値` / `分前` / `日以上連続` / `編集不可` / `編集済み` / `推定原因` / `推奨アクション` / `発火条件` / `編集は P1 後半で対応します` ほか |

★**この表は網羅ではありません。**★ ★実装した文言を自分で洗い出してください。★
★**`t()` を通していない日本語リテラルが画面に残っていないかを、実装後に目で確認すること。**★

### 8-4. ★数値が混ざる文言はキーにできない★

★**`translate()` はキーの完全一致でしか引けません。**★ ★`全12施設` をキーにはできません。★

★**方式A（プレースホルダ + replace）を使ってください**★（`lib/i18n.ts:78-99` にこの2方式の説明とトレードオフが書いてあります。★方式A が推奨★）:

```tsx
t("全{n}施設").replace("{n}", String(meta.total))
```

★**`全{n}施設を表示 →`（`:95`）はすでにありますが、`全{n}施設` はありません（実測）。**★
★**追加が必要かを実装時に確認してください**★ — フッタの文言をどちらにするかで変わります（★モックのフッタは `全{n}施設`★）。

★**方式B（`t("全") + n + t("施設")`）は使わないこと**★ — ★`全` のような1文字キーは他画面の文言と衝突します★（`lib/i18n.ts:92-93` の警告）。

### 段階8の完了条件

- ★**`lib/i18n.ts` に重複キーが無いこと**★（`grep -o '^  "\?[^":]*"\?:' lib/i18n.ts | sort | uniq -d` が 0件、程度の確認でよい）
- ★**3画面で `t()` を通していない日本語リテラルが無いこと**★（★見出し・列名・空表示・注記すべて★）
- ★**数値混じりの文言が方式A になっていること**★
- `npx tsc --noEmit` エラー0 / `npm run lint` 0 / `npm test` 全パス
- ★**ここで1コミット**★

---

## 段階9 — テスト（最終確認）

★**段階ごとに実行しているはずなので、ここは最終確認です。**★

```bash
npx tsc --noEmit
npm run lint
npm test
```

### 見るべきテスト

| ファイル | 内容 |
|---|---|
| ★`lib/facilities/__tests__/query.test.ts`（★新規★）★ | 段階2 の期待値表11ケース |
| ★`lib/schedules/__tests__/query.test.ts`（★新規★）★ | 段階3 の期待値表9ケース |
| ★`lib/fixtures/__tests__/schedules.test.ts`★ | ★**7曜日分 + 前提1 = 8テスト**★。★地雷を踏んでいないことの検証★ |
| `lib/dashboard/__tests__/aggregate.test.ts` | `planned` の集計。★念のため★ |

### ★踏まないでほしい地雷★

| # | 内容 |
|---|---|
| 1 | ★**`cwd` のドライブレターは大文字 `C:\` でなければ全く動きません**★。`vitest run` の出力に `RUN v4.x C:/Users/...` と大文字で出ているか確認すること |
| 2 | ★**`--reporter=basic` は Vitest 4 で削除済みです**★。使うと ★**ロードに失敗しても exit 0 になり、壊れていることに気づけません**★。素の `npm test` を使うこと |
| 3 | ★**lint は `npm run lint`（= `eslint`）**★。★`next lint` は Next 16 で削除済み★ |
| 4 | ★**テスト数が減っていないこと**★。基準は作成時点で ★**22ファイル / 461テスト**★（★着手直前の実測値を基準にすること★）。★**段階2 と段階3 で増えるのが期待値**★ |

### 段階9の完了条件

- 上の3コマンドがすべて通ること
- ★**テスト数が着手前の実測値から増えていること**★（★新規2ファイル分★）
- ★**コミットしない**★（段階1〜8 で済んでいるはず。差分が出たならどの段階の漏れかを特定してその段階に足す）

---

## 段階10 — ブラウザで動作確認

★**`npm run dev` を直接叩かず、Browser pane の `preview_start` を使ってください。**★
（★`.claude/launch.json` に設定が無ければ作ること。`runtimeExecutable: "npm"` / `runtimeArgs: ["run", "dev"]` / `port: 3000`★）

### 確認する画面

`/facilities` / `/schedules` / `/system` / ★`/dashboard`（★地雷を踏んでいないかの確認★）★

### 確認項目

| # | 項目 |
|---|---|
| 1 | ★**3画面が実際に表示されること**★（★「準備中」が出ないこと★） |
| 2 | ★**検索が動くこと**★ — 施設名（`メガバリュー`）/ シリアル（`8111R`、★小文字 `8111r` でも★）/ 住所（`○○区`）の部分一致 |
| 3 | ★**`/facilities` のフッタが `全12施設` であること**★（fixtures 12件） |
| 4 | ★**ロボット3台の施設（`メガバリューマート緑野店`）が3行で出ること**★ / ★**ロボット0台の施設（`フレッシュタウン湖畔店`）で崩れないこと**★ |
| 5 | ★**`/schedules` で id27 が `23:30 - 00:30` と出ること**★（日跨ぎ） / ★**無効行（id24）が薄く表示されること**★ |
| 6 | ★**`/system` の閾値が 90 / 70 / 10 / 60 / 30 / 2 / 3 で出ること**★ / ★**アドバイス7件（R1〜R4 / B1〜B3）が出ること**★ |
| 7 | ★★**`/dashboard` が「9 / 10 完了」のままであること**★★ — ★**11 や 6 になっていたら段階3 の地雷を踏んでいます**★ |
| 8 | ★**メニューに「🔧 点検記録」が出ていないこと**★ |
| 9 | ★**`/inspections` に直URLで飛べること**★（「準備中」が出る。★これが意図した状態★） |
| 10 | ★**ダーク / ライト 両方で崩れないこと**★ |
| 11 | ★**JP / EN 切替で英語が出ること**★（★列名・見出し・空表示すべて★） |
| 12 | ★**コンソールにエラーが出ていないこと**★（`read_console_messages`） |
| 13 | ★★**`data.anomaly` の warn が出ていないこと**★★（`preview_logs`。★出たら地雷★） |
| 14 | ★**横スクロールが `/facilities` の表の中だけで起きること**★（★body 全体が横に伸びないこと★） |

### 段階10の完了条件

- 上の14項目すべてを確認したこと
- ★**#7 と #13 は特に重要**★。★どちらか一方でも異常なら、`lib/fixtures/schedules.ts` の差分を `git diff` で確認すること★
- ★**コミットしない**★

---

## 段階11 — 記録と push

### 11-1. `docs/decisions.md` に新しい決定を追記する

★**着手直前に `grep -n "^### D-0" docs/decisions.md` で最終番号を確認すること**★
（★**本指示書の作成時点（2026-09-08）の最終は `D-082`（`:2390`）**★。★最初の空き番号は `D-083`★。★ただし指示書K / 指示書I が先に走ると繰り上がります★）。

**記録すべきもの**:

| # | 内容 |
|---|---|
| 1 | 指示書L を実行した。★**3画面（F-08 / F-09 / F-10）を実装し `PlaceholderScreen` から差し替えた**★ |
| 2 | ★**`system` の実装スコープ（運用設定のみ・読み取り専用）と、そう決めた理由**★（★D-053 に抵触しないため / 表示設定は永続化とダッシュボード側の対応が要り本作業の範囲を超えるため★） |
| 3 | ★**「表示設定」タブを作らなかったこと**★。★**D-032 では実装対象なので、P1 後半に残る宿題である**★ことを明記する |
| 4 | ★**モックの `ops` タブ二重定義（`d-watch-mock.jsx:1158` / `:1164`、本文 `:1221` / `:1628`）のうちどちらを採ったか**★と、その理由 |
| 5 | ★**閾値の文言をモックの「以上」ではなく確定仕様の「超」にしたこと**★（★`CLAUDE.md` §4.1 が §5 の「モックが文言の正」より上位★。段階6-5） |
| 6 | ★**モックの `📥 インポート` / `+ 新規追加` ボタンを実装しなかったこと**★（★`docs/spec.md:95` で GET のみ / モックのボタンは `onClick` の無い飾り / D-053★） |
| 7 | ★**`inspections` を隠したのは一時対応で、D-031 の P1 スコープは変えていないこと**★。★復活手順（`AppShell.tsx:12` のコメントを外すだけ）★ |
| 8 | ★**`d-watch-api.yaml` の `Schedule` に `facility_id` を追加したこと**★（型と fixtures には既にあった） |
| 9 | ★**`q` パラメータを追加したかどうか（確認2 の結果）と、AND / OR の関係**★。★追加しなかった場合は、検索欄をどう扱ったか★ |
| 10 | ★**`lib/types/index.ts` に `AdviceSetting` を追加したこと**★（YAML にはあったが TS に無かった。`CLAUDE.md` §9.3） |
| 11 | ★**`InspectionDetail.photos` の `item_type`（YAML `:1402`）vs `item`（TS `:373`）の不整合が未解決で残っていること**★。★点検記録に着手する前に解決が必要★ |
| 12 | ★★**契約フラグ（`facilities.contract`）によるメニュー表示制御は未実装のままであること**★★。★`CLAUDE.md` §4.6 が「契約のないセクションはメニューごと非表示」と定めているが、★**メニューは施設横断のグローバルUIなので「どの施設の契約か」が決まらない**★。★**P1 では全メニューを表示したままにする**★。★この論点そのものを記録すること★（★P2 で顧客アカウントが入ったときに、顧客単位で判定できるようになる★） |
| 13 | ★**`lib/fixtures/schedules.ts` を一切変更していないこと**★と、★その理由（`planned` の補完ペア設計。D-079 項3 の申し送り）★ |
| 14 | ★**`d-watch-api.yaml:861` / `:1383` の `inspector_name` の example にあった実在の人名をダミー名に置換したこと**★（`CLAUDE.md` §7 / D-043。段階1-6） |
| 15 | ★**実DB接続時には `prismaSource`（`lib/db/index.ts:153-164`）の `facilities` / `schedules` の実装が別途必要であること**★。★`lib/schedules/query.ts` の docstring に書いた4つの罠（`inactive` の反転 / `weeks` vs `weekdays` / `HHMM` の24〜29 / `weekdays_label` はDBに無い）を申し送ること★ |
| 16 | `docs/architecture.md:66-70` の実装状況表を最新化したこと |
| 17 | 実測値（`npm test` のファイル数・テスト数 / `npx tsc --noEmit` / `npm run lint`） |

### 11-2. ★既存の誤記を直す★

| 箇所 | 内容 |
|---|---|
| ★`docs/architecture.md:66-70`★ | ★**画面の実装状況の表が古い**★（実測）。★`:66` `alerts/` / `:67` `reports/` / `:69` `ds-logs/` `ds-alerts/` が「プレースホルダ」と書かれているが、★**いずれも実装済み**★（D-069 ほか）。★`:70` の `facilities/` `schedules/` `system/` は本作業で実装済みになる★。★**実際にプレースホルダとして残るのは `:68` の `inspections/` だけ**★。★この5行をまとめて直すこと★ |
| ★`docs/fixtures.md:169`★ | 「契約フラグ4パターン \| ★**0:未設定 / 1:D-Securityのみ / 2:D-Watchのみ / 3:両方**★」 — ★**D-080 で訂正された古い表現**★。★10桁文字列（`0000000000` / `1000000000` / `0100000000` / `1100000000`）に書き換えること★ |

### 11-3. `docs/handoff.md` に追記する

- ★**末尾に追記を1つ追加**★（作成時点の最新は ★追記19★。★着手時に確認すること★）
- ★**冒頭の「最新の追記」ポインタ（`:6-8` 付近）も更新すること**★
- ★**次にやることを更新する**★ — ★`inspections` は D-072 項7 のとおり最後★
- ★**申し送りを書く**★:
  - ★「表示設定」タブが P1 後半の宿題として残っていること（D-032）★
  - ★契約フラグによるメニュー表示制御が未実装であること（`CLAUDE.md` §4.6。判定単位が決まらないため）★
  - ★`InspectionDetail.photos` の不整合★
  - ★`lib/schedules/query.ts` の docstring に書いた DB 側の4つの罠（`inactive` の反転 / `weeks` vs `weekdays` / `HHMM` の24〜29 / `weekdays_label` はDBに無い）★
  - ★`docs/handoff/analysis/prismaSource実装前チェックリスト.md` があれば、そちらにも4つの罠を足すこと★

### 11-4. コミットと push

★**`git add` は明示的にファイルパスを指定すること。`git add -A` は禁止**★
（**過去に別作業の途中ファイルを巻き込む事故が起きています**）。

```bash
git status                       # 何が変わったか目で確認する
git add docs/decisions.md docs/handoff.md docs/fixtures.md
git add "docs/handoff/指示書L_マスタ画面3枚とinspectionsの一時非表示.md"
git commit -m "docs: 指示書L完了(D-0XX)を記録する"
git push origin develop
```

- ★**`main` へは push しないこと**★。`CLAUDE.md` §6-5 のとおり
  **main へのマージは PR 経由で、もう1人のレビューを通してから**行います
- **`--force` は使わないこと**
- ★**403 が出たら、無理に回避せずユーザーへ報告してください**★
  （認証情報を画面に出すコマンドを実行しないこと。`git credential fill` は**禁止**）

**push 後に確認すること**:

```bash
git log --oneline -1 origin/develop   # ローカルの HEAD と同じコミットか
git status -sb                        # 1行目に ahead/behind が無いか
```

★**この2つを段階11 の記録に実測値として書き足すこと。**★

### 段階11の完了条件

- `docs/decisions.md` に新しい決定（★着手直前に確認した番号★）が追記されていること
- ★**記録すべきもの17項目がすべて書かれていること**★（★特に #3 / #11 / #12 の「宿題として残る」3件★）
- `docs/fixtures.md:169` の契約フラグ表現が10桁文字列に直っていること
- `docs/architecture.md:66-70` の実装状況表が直っていること
- `docs/handoff.md` の**末尾に追記**があり、**冒頭のポインタも更新**されていること
- ★**`origin/develop` へ push 済みで、`git status -sb` に ahead/behind が無いこと**★
- 文書末尾の「実行状態」表が埋まっていること

---

## ★やらないこと★

| 対象 | 理由 |
|---|---|
| ★**点検記録（`inspections`）の画面実装**★ | ★**D-072 項7**★。写真の置き場所・バケット・期限付き署名URLが未決（禁止事項10） |
| ★**`InspectionDetail.photos` の `item_type` / `item` 不整合の修正**★ | ★今回 `inspections` を実装しないため★。★記録だけする（段階1-5 / 段階11 #11）★ |
| ★**「表示設定」タブ**★ | 確認1。★永続化とダッシュボード側の対応が要り、本作業の範囲を超える★。★P1 後半の宿題として記録する★ |
| ★**運用設定の編集機能**★ | ★YAML にエンドポイントが無い★。作るなら ★先に `d-watch-api.yaml` を直す★（`CLAUDE.md` §6-1） |
| ★**`📥 インポート` / `+ 新規追加` ボタン**★ | `docs/spec.md:95` で GET のみ。★モックのボタンは飾り★。D-053 |
| ★**`lib/fixtures/schedules.ts` の変更**★ | ★段階3 の地雷★。★`active: true` の追加・既存の削除・`weekdays` の書き換えすべて禁止★ |
| ★**`lib/db/` の `prismaSource` の実装**★ | 実DB接続は別作業。`notImplemented` のまま（禁止事項9） |
| ★**`lib/db/` へのフィルタ処理の追加**★ | ★`lib/db/index.ts:50-54` の設計方針★。フィルタは API ルート側 |
| ★**契約フラグによるメニュー表示制御**★ | ★判定単位（どの施設の契約か）が決まらない★。★P1 では全メニューを表示したままにし、論点を記録する★（段階11 #12） |
| ★**`components/layout/PlaceholderScreen.tsx` の削除**★ | ★`inspections` がまだ使っています★ |
| ★**`app/(dashboard)/inspections/page.tsx` の削除**★ | ★直URLでの到達は意図した状態★ |
| ★**`lib/i18n.ts:14` / `:39` の点検記録エントリの削除**★ | ★復活させるときに要ります★ |
| ★**メインDBへの書き込み**★ | `CLAUDE.md` §7 / D-042 |
| ★**`customers.contract` の参照**★ | `CLAUDE.md` §7 / D-010。★`facilities.contract` のみが正★ |

---

## 完了条件（全体）

1. ★**着手前確認2件の回答を得たこと**★（system のスコープ / `q` パラメータ）
2. 段階0〜11 をすべて実施したこと
3. `npx tsc --noEmit` エラー0 / `npm run lint` 0
4. `npm test` 全パス。★**テスト数が着手前の実測値から増えていること**★（作成時点の基準: 22ファイル / 461テスト）
5. ★**`/facilities` `/schedules` `/system` が `PlaceholderScreen` でなくなっていること**★
   （`grep -rn "PlaceholderScreen" "app/(dashboard)/"` が ★`inspections` にしかヒットしないこと★）
6. ★**`app/api/facilities/route.ts` と `app/api/schedules/route.ts` が存在すること**★
7. ★**`lib/facilities/query.ts` と `lib/schedules/query.ts` が純粋関数で、テストがあること**★
8. ★**`git diff --stat lib/fixtures/schedules.ts` が空であること**★（★地雷を踏んでいない★）
9. ★**`/dashboard` が「9 / 10 完了」で、`data.anomaly` の warn が出ないこと**★
10. ★**メニューに「🔧 点検記録」が出ないこと**★。★`app/(dashboard)/inspections/page.tsx` は残っていること★
11. ★**`grep -rn "MEGAドンキ\|donki.co.jp\|宇田川町\|03-5428" components/ lib/facilities/ lib/schedules/` が 0件**★
12. ★**`components/features/system/SystemScreen.tsx` に閾値の数値リテラルが無いこと**★（`lib/config/` から読んでいる）
13. ★**`d-watch-api.yaml` の `Schedule` に `facility_id` があること**★
14. ★**`lib/types/index.ts` に `AdviceSetting` があること**★
15. ★**3画面でダーク/ライト・JP/EN が壊れていないこと**★（段階10 で目視）
16. ★**`grep -n "香原" d-watch-api.yaml` が 0件であること**★（段階1-6）
17. `docs/decisions.md` / `docs/handoff.md` / `docs/fixtures.md` / `docs/architecture.md` の更新
18. ★**`origin/develop` へ push 済み**★（★`main` へは push しない★)
19. **文書末尾の「実行状態」表を更新した**

---

## 実行状態

| 段階 | 状態 | コミット |
|---|---|---|
| ★**着手前の確認（system スコープ / `q` パラメータ）**★ | ★**済（両方とも指示書の推奨どおり採用）**★ | — |
| 段階0 前提の確認 | 済 | （コミットなし） |
| 段階1 API契約を直す + 型を足す | 済 | `a666dcf` |
| 段階2 `GET /api/facilities` | 済 | `6bba958` |
| 段階3 `GET /api/schedules`（★地雷あり★） | 済（地雷回避済み） | `6df83e9` |
| 段階4 施設ロボットマスタ画面（F-08） | 済 | `5d3fe60` |
| 段階5 スケジュール管理画面（F-09） | 済 | `896e428` |
| 段階6 運用設定画面（F-10・読み取り専用） | 済 | `d6741a3` |
| 段階7 `inspections` をメニューから隠す | 済 | `2f768ba` |
| 段階8 i18n | 済 | `6f1b509` |
| 段階9 テスト | 済（24ファイル/530テスト全パス） | （コミットなし） |
| 段階10 ブラウザで動作確認 | ★**一部実施**★（`curl`+APIレスポンスで代替。クライアント描画・テーマ・JP/EN切替・コンソールは未確認） | （コミットなし） |
| 段階11 記録と push | 済 | このコミット + push |

**実行日**: 2026-09-08
**取得した決定番号**: D-084
**着手前の実測値**: 22ファイル / 461テスト / `tsc` 0 / `lint` 0（Vitest 4.1.11）
**`system` の実装スコープ**: 運用設定のみ・読み取り専用（確認1。指示書の推奨どおり採用）
**`q` パラメータ**: `/facilities` と `/schedules` に追加（確認2。指示書の推奨どおり採用）
**完了後の実測値**: 24ファイル / 530テスト / `tsc` 0 / `lint` 0
**詳細**: `docs/decisions.md` D-084 / `docs/handoff.md` 追記20 を参照
</content>
</invoke>
