# 指示書AO — ダッシュボードと詳細ログの列を直す

**作成: 2026-09-17 / ブランチ: `develop`（HEAD `1726817`）/ 前提: D-143 §3（UI差分 第3章の判断）**

---

## ★Cursor への指示文（ここから下をそのまま貼ってください）★

> ```
> D-Watch の実装をお願いします。ブランチは develop です。
>
> ■ 最初に読むもの（この順で、全部）
>   1. CLAUDE.md（全文。特に §4.3 端数処理 / §4.8 エラー種別は変換しない /
>      §5 モックが正 / §7 禁止事項 / §9 実装規約）
>   2. docs/handoff/指示書AO_ダッシュボードと詳細ログの列を直す.md（全文）
>   3. components/features/detail-log/CleanLogTable.tsx（★今回の主役。1本で2画面を兼ねている★）
>   4. lib/format/raw-label.ts（★段階1で作る関数の手本。置き場所も作法もこれに倣う★）
>   5. lib/clean-logs/csv.ts と lib/clean-logs/__tests__/csv.test.ts
>      （★テストが列を**位置**で見ている。段階3の前に必ず読む★）
>   6. components/features/detail-log/DetailLogScreen.tsx の toggleRow() / openAreas() /
>      AreaModal の使い方（★段階5でダッシュボードへ移植する★）
>
> ■ やること
>   指示書AO の 段階1 から 段階7 までを、★番号順に★実行してください。
>   ★段階1（清掃結果ラベルの純粋関数）は「期待値表をテストに落としてから実装する」
>     順序を崩さないでください★（CLAUDE.md §6-2 / §9.4）。
>   ★段階の順序には依存関係があります★ — 列を足してから行展開を有効にします
>     （colSpan の値が変わるため。逆順だと展開行が詰まって壊れます）。
>
> ■ 段階ごとに必ずやること
>   1. npx tsc --noEmit    → 0 であること
>   2. npm run lint        → 0 であること
>   3. npm test            → 全パス
>   4. コミットする（★段階ごとに1コミット★。まとめない）
>
> ■ コミットの決まり
>   ・git add -A は使わないでください。必ずファイルパスを明示してください
>   ・コミットメッセージは日本語。「なぜそうしたか」を1〜2行入れてください
>   ・push はしないでください
>
> ■ 絶対に触らないファイル
>   README.md / docs/code-map.md /
>   docs/handoff/管理画面_設計検討_20260910.md / .claude/worktrees/** /
>   d-watch-mock.jsx / lib/judge/** / lib/db/** / prisma/**
>
> ■ 絶対にやらないこと
>   ・★clean_error を7分類に変換すること★（D-077 / CLAUDE.md §4.8）
>   ・★コード値（0/1/2/3/4）をコードに直書きすること★ — lib/config の CLEAN_ERROR を使う
>   ・★未知のコードを「その他」「不明」に潰すこと★（D-089 項3 / D-104 項2）
>   ・表示用に丸めた値で判定すること（CLAUDE.md §4.3）
>   ・components/ に判定・整形ロジックを書くこと（★テストが走りません★。落とし穴1）
>   ・生SQL（$queryRaw / $executeRaw / *Unsafe / Prisma.raw）— ESLint が落とします
>   ・メインDBのスキーマ変更・マイグレーション作成
>   ・新しい CSS に font-family を書くこと
>   ・仕様が不明なところを推測で実装すること
>
> ■ 動作確認
>   ★dev server はポート3000です★。ログインが要るので目視が難しければ
>   「できなかった」と正直に書いてください。★推測で「動いているはず」と書かないこと★。
>
> ■ 終わったら
>   ・段階ごとのコミットハッシュ
>   ・tsc / lint / test の結果（★テスト本数も★。着手前は 96ファイル / 2,141テスト）
>   ・指示書どおりにできなかったところと、その理由
>   ・判断に迷って自分で決めたところ（あれば全部）
> ```

---

## 0. この指示書の位置づけ

`docs/handoff/UI差分第3章_調査_20260916.md`（937行）で洗い出した8件のうち、
★ユーザーが 2026-09-16 に「直す」と決めた4件★を実装します。

| 第3章 # | 件名 | ユーザー判断 | 本指示書 |
|---|---|---|---|
| #2 | 死んだ「判定 ?」ボタン | ★直す★ | ★段階6★ |
| #3 | ダッシュボードの清掃ログ表が4列 | ★★「出発」列だけ足す★★ | ★段階4★ |
| #6 | コンパクト表の行展開が無効 | ★直す★ | ★段階5★ |
| — | 清掃結果（`clean_error`）が画面にもCSVにも出ていない | ★出す★ | ★段階1〜3★ |
| #1 | 「未対応」の対訳キー衝突 | ★★直さない★★ | §2 |
| #4 | サイドバーの未対応件数バッジ | ★★足さない★★ | §2 |
| #5 | D-Secアラート「通信断」の色 | ★★アンバー維持★★ | §2 |
| #7 | D-Secアラートの期間フィルタ | ★★今は入れない★★ | §2 |
| #8 | バッテリー残量行 | ★意図した仕様★ | §2 |

★第3章の調査は「日付・1h前ST・30分前ST・出発の4列を戻す」を提案しましたが、
ユーザーは★★「出発」1列だけ★★と決めました。★提案のほうを実装しないこと★。★

### 0-1. ★D-107 の申し送りが片付きます★

`lib/types/index.ts:238` に、こう書いてあります:

> ★★画面・CSV には出していない★★ — 2/3/4 の出し方が未決（D-107 の申し送り 項1）。

★本指示書でこの申し送りが片付きます★（段階2・段階3）。
★★型のコメントと `lib/config/index.ts:248-249` のコメントを、実装に合わせて直すこと★★ —
直さないと、次の担当者が「まだ出していない」と読んで二重に実装します。

---

## 1. ★先に知っておくこと（実測）★

★どれも 2026-09-17 に自分で開いて確かめた事実です。★
★★行番号は着手時にズレている可能性があります。必ず自分で開いて確認してください。★★

### 1-1. ★表のコンポーネントは1本だけ★

```
components/features/detail-log/CleanLogTable.tsx   ← ★これ1本★
  ├─ components/features/dashboard/DashboardScreen.tsx:152   compact={true}
  └─ components/features/detail-log/DetailLogScreen.tsx:495   compact 無し
```

★★新規コンポーネントを作らないこと★★ — `CleanLogTable.tsx:26-27` に
「表を分けると修正が2箇所に散るため、新規コンポーネントは作らずこのテーブルの prop で分岐する」
と明記されています。★この判断はそのまま維持します★。

### 1-2. ★現在の列★

| # | 列 | compact（ダッシュボード） | 非compact（詳細ログ） |
|---|---|---|---|
| 1 | 施設名 | ★出す★ | 出す |
| 2 | 機種 | 落とす | 出す |
| 3 | シリアル | 落とす | 出す |
| 4 | 日付 | 落とす | 出す |
| 5 | 予定 | ★出す★ | 出す |
| 6 | 1h前ST | 落とす | 出す |
| 7 | 30分前ST | 落とす | 出す |
| 8 | 出発 | ★★落とす → 段階4で出す★★ | 出す |
| 9 | 完了率 | ★出す★ | 出す |
| 10 | 判定 | ★出す★ | 出す |
| — | ★清掃結果★ | ★★足さない★★ | ★★段階2で足す★★ |

```
着手前:  compact = 4列 / 非compact = 10列
段階2後: compact = 4列 / 非compact = 11列
段階4後: compact = 5列 / 非compact = 11列   ← ★最終形★
```

### 1-3. ★`colSpan` が2箇所ある★

```
CleanLogTable.tsx:156   空状態の行        colSpan={compact ? 4 : 10}
CleanLogTable.tsx:205   アドバイス展開行  colSpan={compact ? 4 : 10}
```

★★列を足したら両方直すこと★★。★片方だけ直しても tsc も lint もテストも通ります★
（画面のテストが存在しないため）。★見た目が詰まるだけなので、目視でしか気づけません★。

最終的に★両方とも `colSpan={compact ? 5 : 11}`★になります。

### 1-4. ★行展開の無効化は `:162-163`★

```tsx
// CleanLogTable.tsx:162-163
const hasAdvice = !compact && (r.grade === "△" || r.grade === "×");
const isExpanded = !compact && expandedId === r.id;
```

根拠として `CleanLogTable.tsx:23-27` の docstring がこう書いています:

> ダッシュボードではアドバイスを一切出さない規約のため、compact 時は行クリック・
> アドバイス展開も無効化する。

★★この「規約」はどの資料にも存在しません★★。

```
$ grep -n "アドバイスを一切\|ダッシュボードではアドバイス" \
    docs/decisions.md docs/spec.md docs/handoff.md
（0件）
```

`git blame` では `47f2df4`（2026-08-30「F-01 ダッシュボード画面を実装」）の初回実装で
書かれたもので、★実装者の自己申告★です。★段階5でこの2文を消してください★。

★★消すだけにして、別の「規約」を書かないこと★★ — 出所不明の根拠を足したのが
この事故の原因です。★消す代わりに「ユーザー判断 2026-09-16（D-143 §3 #6）」と書きます★。

### 1-5. ★`clean_error` は API契約にも型にも既にある★

```
d-watch-api.yaml:1621-1632   clean_error（integer / nullable / 0〜4 の説明つき）
lib/types/index.ts:240       clean_error: number | null;
```

★★つまり YAML のスキーマ変更は要りません★★（`CLAUDE.md` §6-1 が要求する
「API契約を先に直す」は、今回は該当しません）。★着手時に自分で確認すること★。

★ただし `d-watch-api.yaml:1632` の説明文★:

> 2/3/4 は未実施とは違う扱いで departed=true(画面への出し方は未決)

★この「未決」だけが古くなります★。★段階2で「詳細ログの『清掃結果』列とCSVに生値のラベルで出す」に
書き換えること★。★スキーマは1文字も変えません★（説明文だけ）。

### 1-6. ★ラベル関数は既にある★

```
lib/config/index.ts:262   CLEAN_ERROR            0〜4 のコード（★直書き禁止★）
lib/config/index.ts:285   CLEAN_ERROR_LABELS     コード → ラベル（★定義書の原文ママ★）
lib/config/index.ts:301   cleanErrorLabel(code)  無いコードは String(code) を返す
```

| コード | ラベル |
|---|---|
| 0 | `正常` |
| 1 | `異常(清掃未実施)` |
| 2 | `異常(セキュリティ有効)` |
| 3 | `異常(D-S制御盤異常)` |
| 4 | `異常(清掃ロボット異常)` |
| ★5〜255★ | ★★`String(code)` ＝ 生値がそのまま出る。それが正しい挙動★★（D-077） |

★★括弧は半角のまま。全角に直さないこと★★（`lib/config/index.ts:277-278`。
定義書の原文が半角）。★言い換え・要約もしないこと★。

★★`cleanErrorLabel()` は `null` を受け取れません★★（引数が `number`）。
★だから段階1で `null` を扱う関数を1つ足します★。

### 1-7. ★CSVの列定義★

```
lib/clean-logs/csv.ts:62-73    CLEAN_LOG_CSV_HEADER（10要素）
lib/clean-logs/csv.ts:85-102   buildCleanLogCsv()。★行の組み立ては :90-99 の10セル★
```

★全列が例外なく `csvCell()` を通っています★（D-125）。
★`csv.ts:78-83` のコメントに「列の追加は『`csvCell()` で包む』だけで済む」とあります★。
★★その約束を破らないこと★★。

### 1-8. ★テストの現況（着手前の実測。2026-09-17）★

```
$ npm test
 Test Files  96 passed (96)
      Tests  2141 passed (2141)
```

★この数字を基準にしてください★。

---

## 2. ★★やらないこと（ユーザーが「やらない」と決めたもの）★★

★★以下は「やり忘れ」ではありません。ユーザーが 2026-09-16 に判断した結果です（D-143 §3）。
親切心で直さないでください。★★

| 第3章 # | 件名 | ★理由★ |
|---|---|---|
| ★#1★ | ★「未対応」の対訳キー衝突★（`lib/i18n.ts:160` の `未対応: "Unsupported"`。★英語で「非対応」の意味になる★） | ★★英語を配布するか未定★★のためユーザーが「そのまま」と判断。★★★英語配布を決めた時点で必ず直す必要があります★★★ — ★D-Secアラートの「未対応」（人が対応していない）が `Unsupported`（機器が非対応）と出続けます★。★`要対応` にも同型の地雷があります★（今は辞書に無く和文のままですが、素直に対訳を足した瞬間に同じ事故になります） |
| #4 | サイドバーの未対応件数バッジ | ★PATCH（対応状況更新）が未実装・DB更新用アカウントも未受領★。ユーザー判断で保留 |
| #5 | D-Secアラート「通信断」の色を赤に戻す | ★★アンバー維持で確定★★ |
| #7 | D-Secアラートに期間フィルタを足す | ★今は入れない★。入れるなら `d-watch-api.yaml` の変更から |
| #8 | バッテリー残量行を消す | ★意図した仕様★（D-105 項5）。★★空欄のまま残すのが正★★。★行ごと消さないこと★ |

★★#1 について、指示書に書いておく注記★★:

> 英語表示を配布すると決めた時点で、`lib/i18n.ts` の `未対応` キーは必ず直す必要があります。
> 直し方は「キーを日本語原文のまま使う方式」自体を変えるか（1階層辞書をやめる）、
> 呼び出し側で別の日本語（例: `未対応（対応状況）`）に分けるかの2択で、
> ★どちらも `lib/i18n.ts` の作法を変える話なので、単独の指示書が要ります★。
> ★本指示書では触りません。★

---

## 3. ★触ってはいけないもの★

| 対象 | 理由 |
|---|---|
| `README.md` / `docs/code-map.md` / `docs/handoff/管理画面_設計検討_20260910.md` / `.claude/worktrees/**` | ★別作業★ |
| ★`d-watch-mock.jsx`★ | ★★モックは原型として保存する★★（`CLAUDE.md` §7）。★1文字も書き換えない★ |
| ★`lib/judge/**`★ | ★判定ロジックは変えません★ |
| ★`lib/db/**` / `prisma/**`★ | ★今回はDB側を1行も変えません★（`clean_error` は既にAPIまで来ています） |
| ★`d-watch-api.yaml` のスキーマ★ | ★変更不要★（§1-5）。★★説明文1箇所だけ★★直します |
| ★テストDB（`133.125.238.144`）への書き込み★ | ★★読み取り専用★★ |

★★禁止★★: `git add -A` / 生SQL / スキーマ変更 / ★コード値の直書き★ /
★`components/` への判定・整形ロジック★ / ★新しいCSSへの `font-family`★ / 推測での実装

---

## 4. 段階1 — ★清掃結果ラベルの整形を純粋関数にする★（★テストが先★）

### 4-1. 置き場所

★新規 `lib/format/clean-result.ts`★ と `lib/format/__tests__/clean-result.test.ts`。

★手本は `lib/format/raw-label.ts`★（27行。★D-077 を維持したまま「空である」事実だけを
差し替える純粋関数★）。★docstring の書き方もこれに倣ってください★。

★★`components/` に書かないこと★★ — `vitest.config.ts:14` の `include` は
`["lib/**/*.test.ts", "app/**/*.test.ts"]` で、★`components/**` は走りません★（落とし穴1）。

★`lib/judge/` にも置かないこと★ — あちらは◎○△×等の★評価★の置き場所で、
こちらは★表示整形★です（`lib/format/raw-label.ts:15-16` に同じ切り分けが書いてあります）。

### 4-2. 何をする関数か

```ts
export function formatCleanResult(cleanError: number | null): string
```

```
null   → 「出発失敗」
0〜4   → CLEAN_ERROR_LABELS のラベル（★定義書の原文ママ★）
5以上  → String(code)（★生値。D-077★）
```

★★`null` を「出発失敗」と表示する根拠★★（★コメントに書くこと★）:

- `d-watch-api.yaml:1629-1630` — 「null は『履歴の行が無い(＝清掃結果が記録されていない)』
  ことを表し、★出発失敗として扱う★(D-107 設計3-C)」
- `lib/types/index.ts:219-221` — `clean_error === 1` **または** `null` のとき `departed === false`
- ★ユーザー確定（2026-09-16）★: ★「`null` は『出発失敗』と表示する」★

★★「(値なし)」にしないこと★★ — `formatRawLabel()` の `EMPTY_VALUE_LABEL` は
「DBのカラムが空」という事実だけを述べる文言で、★ここは「履歴の行が無い＝出発していない」という
**意味が確定している** 場面★です。★ユーザーが文言を指定しています★。

★★文言は関数と同じファイルの定数に出すこと★★（`EMPTY_VALUE_LABEL` と同じ形）:

```ts
/** `clean_error` が null（履歴の行が無い）のときの表示。★ユーザー確定 2026-09-16★ */
export const CLEAN_RESULT_NOT_DEPARTED_LABEL = "出発失敗";
```

★画面とCSVの2箇所から使うので、文字列を2回書かないためです★。

### 4-3. ★期待値表（★先にテストへ★）★

| # | 入力 | 期待 | なぜ入れるか |
|---|---|---|---|
| ★1-1★ | `null` | ★`"出発失敗"`★ | ★ユーザー確定の本体★ |
| 1-2 | `CLEAN_ERROR.normal`（0） | `"正常"` | |
| ★1-3★ | `CLEAN_ERROR.notExecuted`（1） | ★`"異常(清掃未実施)"`★ | ★★`null` と別物であること★★。★同じ「出発失敗」に潰さない★ |
| 1-4 | `CLEAN_ERROR.securityEnabled`（2） | `"異常(セキュリティ有効)"` | ★D-107 申し送りの本題★ |
| 1-5 | `CLEAN_ERROR.controlBoardError`（3） | `"異常(D-S制御盤異常)"` | 同上 |
| 1-6 | `CLEAN_ERROR.robotError`（4） | `"異常(清掃ロボット異常)"` | 同上 |
| ★1-7★ | `5` | ★`"5"`★ | ★★未知のコードは生値★★（D-077） |
| ★1-8★ | `255` | ★`"255"`★ | ★`unsigned tinyint` の上限★ |
| ★1-9★ | `255` | ★★「その他」「不明」を**含まない**★★ | ★D-089 項3 / D-104 項2★。★`clean-error.test.ts:60-65` に同じ形のテストがあるので倣う★ |
| ★1-10★ | `0` | ★`"出発失敗"` ではない★ | ★`null` と `0` の取り違え（`??` と `\|\|` の事故）を止める★ |
| 1-11 | 全コード | ★`CLEAN_ERROR_LABELS` と1文字も違わない★ | ★ラベルを画面側で言い換えていないことの担保★ |

★★1-10 を必ず入れること★★ — `clean_error ?? ...` を `clean_error || ...` と書くと
★`0`（正常）が「出発失敗」に化けます★。★例外もtscエラーも出ません★。

★コード値を直書きせず `CLEAN_ERROR` を使うこと★（テスト側も。
`lib/db/__tests__/clean-log-mapping.test.ts:285` に同じ趣旨のテストがあります）。

### 4-4. 完了条件

- [ ] `lib/format/clean-result.ts` と `lib/format/__tests__/clean-result.test.ts` がある
- [ ] ★テストを先にコミットしていなくてよいが、**実装より先に書く**★
- [ ] `npx tsc --noEmit` → ★0★
- [ ] `npm run lint` → ★0★
- [ ] `npm test` → 全パス。★★97ファイル（+1）/ 2,150前後（+8〜12）★★
- [ ] ★`CLEAN_ERROR` を import していること★（`0`〜`4` の直書きが無いこと）

**コミットメッセージ案**

```
feat(format): 清掃結果ラベルの整形を純粋関数にする（null は「出発失敗」）

clean_error を画面とCSVの2箇所で使うため、null の扱いを1箇所に閉じた。
cleanErrorLabel() は null を受け取れず、?? と || の取り違えで 0（正常）が
「出発失敗」に化ける事故が静かに起きるため、期待値表で固定した（指示書AO 段階1）。
```

---

## 5. 段階2 — ★詳細ログの表に「清掃結果」列を足す★

### 5-1. 触るファイル

| ファイル | やること |
|---|---|
| `components/features/detail-log/CleanLogTable.tsx` | ★`!compact` 付きの `<th>` / `<td>` を1組追加★・★`colSpan` を2箇所直す★ |
| `lib/i18n.ts` | ★`清掃結果` の対訳を追加★ |
| `lib/types/index.ts` | ★`clean_error` の docstring から「画面・CSVには出していない」を消す★ |
| `lib/config/index.ts` | ★`CLEAN_ERROR` の docstring から「画面への出し方は未決」を消す★ |
| `d-watch-api.yaml` | ★`clean_error` の **description 1行だけ**★（★スキーマは変えない★） |

### 5-2. 列の位置

★★「出発」列（`:102-106` の `<th>` / `:188-192` の `<td>`）の直後、「完了率」の直前★★。

★理由★: ★`出発`（✓/✗）も `清掃結果` も出所は同じ `clean_status_histories.clean_error` です★
（`d-watch-api.yaml:1617-1620`）。★隣に並べると、✗ の理由がその場で読めます★。
★離すと、2列の関係が読み取れません★。

```
… 30分前ST │ 出発 │ ★清掃結果★ │ 完了率 │ 判定
```

### 5-3. 実装

```tsx
{!compact && (
  <th className={styles.th} style={{ textAlign: "left" }}>
    {t("清掃結果")}
  </th>
)}
```

```tsx
{!compact && <td className={styles.td}>{formatCleanResult(r.clean_error)}</td>}
```

★★セルの値を `t()` に通さないこと★★ — ★理由をコメントに書くこと★:

- `CLEAN_ERROR_LABELS` は★定義書の原文★です（`lib/config/index.ts:272-278`）。
  ★対訳を当てると「何が原文だったか」が分からなくなります★
- ★未知コードは `String(code)`★ で、そもそも訳せません
- ★既存の `status_1h` / `status_30m`（`Pill`）も `t()` を通していません★。★同じ扱いに揃えます★
- ★見出し（`清掃結果`）だけ `t()` を通します★

### 5-4. ★`colSpan` を直す★

```
:156   colSpan={compact ? 4 : 10}  →  colSpan={compact ? 4 : ★11★}
:205   colSpan={compact ? 4 : 10}  →  colSpan={compact ? 4 : ★11★}
```

★★2箇所とも★★。★段階4で compact 側も 5 に変わります★。

### 5-5. ★i18n★

`lib/i18n.ts` の `EN` に足します。★`施設名` 〜 `判定` が並んでいる塊（`:23-31`）の
`出発` と `完了率` の間★に置いてください（★画面の列順と辞書の並びを揃えるため★）。

```ts
清掃結果: "Clean Result",
```

★★衝突の確認（★必ずやること★）★★:

```bash
grep -n "清掃結果" lib/i18n.ts
```

★着手前の実測では 0件★です。★1件でも出たら止まって報告してください★ —
`lib/i18n.ts` は★キーが日本語原文の1階層辞書★なので、
★同じ日本語を別の意味で使うと必ず衝突します★（`未対応` → `Unsupported` の事故が実在。§2 #1）。

★`清掃結果` は `docs/spec.md` や `app/api/dashboard/route.ts` のコメントでは
「昨夜の清掃結果サマリ」という別の意味で使われています★が、
★★`t()` のキーとしては未使用なので衝突しません★★。
★ただし将来「清掃結果サマリー」を `t()` に通す人が出たら、そちらは別キーになります★
（`DashboardScreen.tsx:81` の `昨夜の清掃結果サマリー — {today}` は今も `t()` を通していません）。

### 5-6. ★古くなるコメントを直す★

★これを忘れると、次の担当者が「まだ出していない」と読んで二重に実装します。★

| 場所 | 現在 | 直す |
|---|---|---|
| `lib/types/index.ts:238` | ★「★★画面・CSV には出していない★★ — 2/3/4 の出し方が未決（D-107 の申し送り 項1）。」★ | ★「詳細ログの『清掃結果』列とCSVに `formatCleanResult()` で出す（指示書AO / D-1xx）」★ |
| `lib/config/index.ts:248-249` | ★「★★どの画面にどう出すかは**未決**★★（D-107 の申し送り）。現状は API まで生値を運ぶだけで、画面・CSV には出していない。」★ | 同上 |
| `d-watch-api.yaml:1632` | 「2/3/4 は未実施とは違う扱いで departed=true(画面への出し方は未決)」 | 「2/3/4 は未実施とは違う扱いで departed=true。詳細ログの『清掃結果』列とCSVに生値のラベルで出す(指示書AO)」 |

★★`D-1xx` は段階7で採番した実際の番号に置き換えること★★（先に書かない）。

### 5-7. 完了条件

- [ ] ★詳細ログの表が11列★（数えること）
- [ ] ★ダッシュボードは4列のまま★（★この段階では変わらない★）
- [ ] ★`colSpan` が2箇所とも `compact ? 4 : 11`★
- [ ] `grep -n "清掃結果" lib/i18n.ts` → ★1件（追加した行だけ）★
- [ ] ★`lib/types/index.ts` / `lib/config/index.ts` / `d-watch-api.yaml` のコメントが実装と一致★
- [ ] ★`d-watch-api.yaml` の**スキーマ**（型・nullable・enum）が1文字も変わっていないこと★
- [ ] `npx tsc --noEmit` → 0 / `npm run lint` → 0 / `npm test` → 全パス（★本数は段階1のまま★）

★★テストは増えません★★ — ★画面のテストは存在しません★（`vitest.config.ts:14`）。
★「増えないこと」自体が正しい★ので、増やそうとして `components/` にテストを置かないでください。

**コミットメッセージ案**

```
feat(detail-log): 清掃詳細ログに「清掃結果」列を足す（10列→11列）

clean_error は API まで来ていたが画面に出しておらず、✗（出発失敗）の理由が
読めなかった。出発列の隣に置いて、同じ clean_error 由来であることを見せる。
D-107 の申し送り 項1（2/3/4 の出し方が未決）はこれで片付く（指示書AO 段階2）。
```

---

## 6. 段階3 — ★CSVに「清掃結果」列を足す★

### 6-1. 触るファイル

| ファイル | やること |
|---|---|
| `lib/clean-logs/csv.ts` | ★ヘッダ（`:62-73`）と行（`:90-99`）の**両方**★ |
| `lib/clean-logs/__tests__/csv.test.ts` | ★★位置で見ているテストの添字を直す★★ + ★新しい列のテストを足す★ |

### 6-2. 列の位置

★★画面と同じ位置★★ — `出発` と `完了率` の間。

★理由★: `csv.ts:61` のコメントが★「ヘッダ行の見出し（モックの列順に合わせる）」★です。
★画面とCSVで列順が違うと、突き合わせる人が毎回並べ替えることになります★。

```ts
export const CLEAN_LOG_CSV_HEADER = [
  "施設名", "機種", "シリアル", "日付", "予定時刻",
  "1h前ST", "30分前ST", "出発", ★"清掃結果"★, "完了率", "判定",
] as const;
```

```ts
csvCell(r.departed ? "出発" : "未出発"),
★csvCell(formatCleanResult(r.clean_error)),★
csvCell(displayRate(r.completion_rate)),
```

★★必ず `csvCell()` を通すこと★★（D-125。`csv.ts:78-83`）。
★「ラベルは固定文言だから安全」という判断をしないこと★ — ★未知コードは `String(code)` で、
`-` 始まりにはなりませんが、★列ごとに安全性を判断させないのがこの設計の要点★です。

### 6-3. ★★既存テストが**位置**で列を見ています（最大の罠）★★

```
lib/clean-logs/__tests__/csv.test.ts:112   cells(row)[8]   完了率
                                    :126   cells(base)[8]  完了率
                                    :131   cells(base)[7]  出発
                                    :132   cells(base)[9]  判定
                                    :134   cells(...)[7]   出発
                                    :152   c[0] c[1] c[2] c[5] c[6] ★c[8]★
```

★`清掃結果` を index 8 に挿入すると★:

| 旧 | 新 | 列 |
|---|---|---|
| `[7]` | `[7]` | 出発（★変わらない★） |
| — | ★`[8]`★ | ★清掃結果（新規）★ |
| `[8]` | ★`[9]`★ | ★完了率★ |
| `[9]` | ★`[10]`★ | ★判定★ |

★★`[8]` → `[9]` / `[9]` → `[10]` を、★5箇所★直すこと★★。

★★直し忘れると「テストが落ちて気づく」ので、そこは安全です★★。
★危険なのは逆で、「落ちたから添字を合わせる」だけで済ませて、
★新しい列そのもののテストを書かないこと★です。

★`:164` の `expect(cells(base)).toHaveLength(CLEAN_LOG_CSV_HEADER.length)` は
★定数を見ているので自動で追随します★（直す必要なし）。

### 6-4. ★足すテスト★

| # | 入力 | 期待 |
|---|---|---|
| ★3-1★ | `clean_error: 0` | ★`cells(base)[8] === "正常"`★ |
| ★3-2★ | `clean_error: null` | ★`"出発失敗"`★ |
| ★3-3★ | `clean_error: 2` | ★`"異常(セキュリティ有効)"`★（★括弧は半角★） |
| ★3-4★ | `clean_error: 5` | ★`"5"`★（★生値★） |
| ★3-5★ | ★ヘッダの8番目★ | ★`CLEAN_LOG_CSV_HEADER[8] === "清掃結果"`★ |

★★3-3 を必ず入れること★★ — ★`異常(セキュリティ有効)` は `(` `)` を含みますが、
`csvCell()` の引用判定は `/[",\n\r]/` なので★引用されません★。
★「引用されないのが正しい」ことを固定します★（後から誰かが「括弧も引用しよう」として
既存CSVの互換を壊すのを止めるため）。

★`:139-157` の「★どの列に危険な値が来ても素通ししない★」テストに
★清掃結果の列は追加できません★（値が `clean_error` から機械的に決まり、
★危険な文字列を作れない★ため）。★その旨を1行コメントで書いてください★ —
書かないと「この列だけ漏れている」と読まれます。

### 6-5. 完了条件

- [ ] ★`CLEAN_LOG_CSV_HEADER` が11要素★
- [ ] ★行の組み立ても11セル★（★`csvCell()` を11回通る★）
- [ ] ★添字を直した5箇所がすべて通る★
- [ ] ★新テスト5本★
- [ ] `npx tsc --noEmit` → 0 / `npm run lint` → 0 / `npm test` → 全パス。
      ★★97ファイル / 2,155前後（段階1から +5）★★
- [ ] ★CSVの列順が画面の列順と一致していること★（★目で並べて確かめる★）

**コミットメッセージ案**

```
feat(csv): 清掃詳細ログのCSVに「清掃結果」列を足す（画面と同じ位置）

画面（段階2）とCSVで列順が違うと突き合わせのたびに並べ替えになるため、
出発と完了率の間に入れた。既存テストが列を位置で見ているので添字を直し、
新しい列が csvCell() を通っていることを固定した（指示書AO 段階3 / D-125）。
```

---

## 7. 段階4 — ★ダッシュボードに「出発」列を足す★（4列 → 5列）

### 7-1. 触るファイル

`components/features/detail-log/CleanLogTable.tsx` ★のみ★。

### 7-2. やること

`:102-106` の `<th>` と `:188-192` の `<td>` から★`!compact &&` を外す★だけです。

```tsx
// before
{!compact && (
  <th className={styles.th} style={{ textAlign: "center" }}>{t("出発")}</th>
)}
// after
<th className={styles.th} style={{ textAlign: "center" }}>{t("出発")}</th>
```

★★ソース上の位置を動かさないこと★★ — `出発` の `<th>` は `30分前ST` と `完了率` の間にあり、
★`!compact` を外すだけで compact 時の列順が「施設名 / 予定 / 出発 / 完了率 / 判定」になります★。
★並べ替える必要はありません★。

### 7-3. ★`colSpan` を直す★（★2箇所★）

```
:156   colSpan={compact ? 4 : 11}  →  colSpan={compact ? ★5★ : 11}
:205   colSpan={compact ? 4 : 11}  →  colSpan={compact ? ★5★ : 11}
```

★★これが最終形です★★。

### 7-4. ★docstring を直す★

`CleanLogTable.tsx:24` の「★4列表示にする（施設名/予定時刻/完了率/判定のみ）★」が
★事実と違うようになります★。

```
★5列表示にする（施設名 / 予定 / 出発 / 完了率 / 判定）★
```

★★「機種・シリアル・日付・1h前ST・30分前ST を落とす」と**落とすほうを書く**こと★★ —
★出す列を並べると、列を足すたびに書き換えが要ります★。

★★「ユーザー判断（2026-09-16。D-143 §3 #3）で、足すのは『出発』1列だけと決めた」と
書き添えること★★ — ★書かないと、次に `docs/spec.md:32`（「機種・シリアル列を省略」＝8列）と
突き合わせた人が「日付・1h前ST・30分前ST も戻し忘れている」と判断して足します★。

### 7-5. ★i18n・CSS は変更不要★（★実測★）

- `出発` は★対訳済み★（`lib/i18n.ts:29` の `出発: "Dep."`）
- `CleanLogTable.module.css:1-5` が `.table { min-width: 1000px }`、
  `DashboardScreen.module.css:75-77` が `.tableWrap { overflow-x: auto }`。
  ★★横スクロールで収まるので CSS 変更は不要★★
- ★★新しいCSSを書かないこと★★（書くなら `font-family` を入れない。`CLAUDE.md` §0-3）

### 7-6. 完了条件

- [ ] ★ダッシュボードの表が5列★（施設名 / 予定 / ★出発★ / 完了率 / 判定）
- [ ] ★詳細ログは11列のまま★
- [ ] ★`colSpan` が2箇所とも `compact ? 5 : 11`★
- [ ] ★docstring が5列と書いてあり、ユーザー判断の出所が書いてある★
- [ ] ★CSS を1行も足していないこと★
- [ ] `npx tsc --noEmit` → 0 / `npm run lint` → 0 / `npm test` → 全パス（★本数は段階3のまま★）

**コミットメッセージ案**

```
feat(dashboard): ダッシュボードの清掃ログ表に「出発」列を足す（4列→5列）

△×の行しか出ない表で、出発したのかどうかが読めなかった。
日付・1h前ST・30分前ST は足さない（ユーザー判断 2026-09-16 / D-143 §3 #3）。
colSpan は空状態と展開行の2箇所とも追随させた（指示書AO 段階4）。
```

---

## 8. 段階5 — ★コンパクト表の行展開を有効にする★

### 8-1. ★なぜ段階4より後なのか★

★`colSpan` が `compact ? 5 : 11` になっている必要があります★。
★段階4より先にやると、展開したアドバイス行が4列ぶんしか広がらず、右端が詰まります★。
★tsc も lint もテストも通るので、目視でしか気づけません★。

### 8-2. 触るファイル

| ファイル | やること |
|---|---|
| `components/features/detail-log/CleanLogTable.tsx` | ★`:162-163` の `!compact &&` を外す★ / ★`:23-27` の存在しない「規約」のコメントを消す★ |
| `components/features/dashboard/DashboardScreen.tsx` | ★state とアドバイス取得を足す★ / ★★エリアモーダルを足す★★（§8-4） |

### 8-3. `CleanLogTable.tsx`

```tsx
// before
const hasAdvice = !compact && (r.grade === "△" || r.grade === "×");
const isExpanded = !compact && expandedId === r.id;
// after
const hasAdvice = r.grade === "△" || r.grade === "×";
const isExpanded = expandedId === r.id;
```

★★モックと同じ形になります★★（`d-watch-mock.jsx:311` は
`var hasAdvice = r.g === "△" || r.g === "×";` で `compact` を見ていません）。

★docstring（`:23-27`）から★★この2文を消すこと★★:

> ダッシュボードではアドバイスを一切出さない規約のため、compact 時は行クリック・
> アドバイス展開も無効化する。

★★代わりに書くこと★★:

```
★compact でも行展開する★（モック `d-watch-mock.jsx:311` は compact を見ていない）。
★かつて「ダッシュボードではアドバイスを一切出さない規約」と書かれていたが、
  ★★その「規約」はどの資料にも存在しなかった★★（grep 0件。実装者の自己申告）。
  ユーザー判断で展開を有効にした（2026-09-16 / D-143 §3 #6 / 指示書AO 段階5）。
```

★★「規約が無かった」という事実を残すこと★★ — ★消すだけだと、また誰かが同じ
思い込みを書き足します★。

### 8-4. ★★`DashboardScreen.tsx`（ここがいちばん量が多い）★★

`DetailLogScreen.tsx` から★そのまま移植できます★。

| 移す先 | 元 |
|---|---|
| `expandedId` / `adviceById` / `adviceLoadingId` の state | `DetailLogScreen.tsx:84-88` |
| `toggleRow()` | `DetailLogScreen.tsx:214-246` |
| `openAreas()` | `DetailLogScreen.tsx:257-263` |
| `areaTarget` / `areaFilter` / `areaData` / `areaLoading` / `areaError` | `DetailLogScreen.tsx:90-95` |
| エリア取得の `useEffect` | `DetailLogScreen.tsx:265-297` |
| `<AreaModal ... />` | `DetailLogScreen.tsx:513-524` |
| `AreaTarget` 型 | `DetailLogScreen.tsx:31-40` |

そして `CleanLogTable` への受け渡しを差し替えます（`DashboardScreen.tsx:152-162`）:

```tsx
expandedId={expandedId}          // was null
adviceById={adviceById}          // was {}
adviceLoadingId={adviceLoadingId}// was null
onToggleRow={toggleRow}          // was () => {}
onOpenAreas={openAreas}          // was () => {}
```

★★`onOpenAreas` を `() => {}` のまま残さないこと（★見落としやすい★）★★

★アドバイスが1件以上あると、展開部に★「エリア別詳細を見る →」ボタン★が出ます★
（`CleanLogTable.tsx:235-244`）。★★`onOpenAreas` が空関数のままだと、
このボタンは押しても何も起きません★★ — ★今回直している「死んだ『判定 ?』ボタン」と
**まったく同じ不具合**を、その隣に作ることになります★。

★モックでは compact でもこのボタンからモーダルが開きます★
（`d-watch-mock.jsx:358` は `LogTable` の中にあり、`compact` で分岐していません）。
★`CLAUDE.md` §5 でモックが★遷移の正★です★。
★したがって `AreaModal` をダッシュボードにも置きます★。

★★`/detail-log` へ遷移させて代用しないこと★★ — ★モックはモーダルを開きます★。
★遷移にすると、開いていた行の情報が失われます★。

### 8-5. ★AreaModal の移植で気をつけること★

- ★`AreaModal` は `components/features/detail-log/` にあります★。
  ★ダッシュボードから import して構いません★（`CleanLogTable` と同じ扱い。★複製しないこと★）
- ★取得は `openAreas()` の中でやらないこと★ — `DetailLogScreen.tsx:248-255` のコメントのとおり、
  ★`filter` の切り替えでも取り直す必要がある★ので `useEffect` に寄せてあります。
  ★2本に分けないこと★
- ★`cancelled` フラグを落とさないこと★（`DetailLogScreen.tsx:269`）— ★古い応答が後から
  届いて新しい表示を上書きします★
- ★`toggleRow()` の `catch` 節をそのまま持ってくること★（`:221-243`）—
  ★取得に失敗しても行は開き、一覧が持っている値で最小限を埋めます★。
  ★★`row.consecutive_problem_days_capped ?? false` の `??` を `||` にしないこと★★

### 8-6. ★i18n は今回変えない★

展開部の見出し（`CleanLogTable.tsx:37-51` の「🔴 レベル3 — DFA介入を検討」等）は
★`t()` を通していません★。★EN では和文のまま出ます★。

★★これは詳細ログでも同じ既存の抜けなので、本段階では直しません★★
（§2 #1 と同じ理由 — ★英語を配布するか未定★）。★ダッシュボードでも和文が出ることを
報告に1行書いてください★。

### 8-7. 完了条件

- [ ] ★ダッシュボードの △× 行をクリックすると展開する★
- [ ] ★展開行が5列ぶんの幅いっぱいに広がる★（★詰まっていないこと★）
- [ ] ★`▼` / `▲` が判定列に出る★
- [ ] ★「エリア別詳細を見る →」を押すとモーダルが開く★（★★空関数のままになっていないこと★★）
- [ ] ★モーダルの絞り込み（問題エリアのみ / 全件）を切り替えると取り直す★
- [ ] ★`CleanLogTable.tsx` に「規約」という語が残っていないこと★
      （`grep -n "規約" components/features/detail-log/CleanLogTable.tsx` → ★0件★）
- [ ] ★詳細ログ側の挙動が変わっていないこと★（★同じ表を共有しているため★）
- [ ] `npx tsc --noEmit` → 0 / `npm run lint` → 0 / `npm test` → 全パス（★本数は段階3のまま★）

**コミットメッセージ案**

```
feat(dashboard): コンパクト表の行展開アドバイスを有効にする

根拠とされていた「ダッシュボードではアドバイスを一切出さない規約」は
どの資料にも存在せず（grep 0件）、初回実装の自己申告だった。
モックは compact でも展開する。エリア別詳細のモーダルも合わせて移植した
（押しても何も起きないボタンを新たに作らないため）。
ユーザー判断 2026-09-16 / D-143 §3 #6 / 指示書AO 段階5。
```

---

## 9. 段階6 — ★「判定 ?」ボタンを機能させる★

### 9-1. 現状

ボタンは★compact でも表示されています★（`CleanLogTable.tsx:110-120`）。
凡例本体（`:123-152`）は `showGradeInfo` でしか開きません。
ダッシュボードは★`showGradeInfo={false}` / `onToggleGradeInfo={() => {}}`★を渡しています
（`DashboardScreen.tsx:158-159`）。

★★押しても何も起きないのは仕様上の制約ではなく、状態を持たせていないだけです★★。

### 9-2. やること（★`useState` 1つ★）

`components/features/dashboard/DashboardScreen.tsx` ★のみ★:

```tsx
const [showGradeInfo, setShowGradeInfo] = useState(false);
...
showGradeInfo={showGradeInfo}
onToggleGradeInfo={() => setShowGradeInfo((v) => !v)}
```

★`CleanLogTable.tsx` は変更不要★です。

### 9-3. ★D-053 との整合★（★聞かれる前に書いておきます★）

D-053 は★「固定の選択肢だけがあって何も起きないUIは、不具合と区別できない」★ため
絞り込み `<select>` を★消し★ました。

★本件は前提が違います★ — ★D-053 が「消す」を選んだのは、機能させるためのデータ
（マスタ）が存在しなかったから★です。★こちらはデータも凡例本体も既にあり、
必要なのは `useState` 1つ★です。

同じ趣旨が `lib/i18n.ts:320-321` にも明文化されています:

> ★★`disabled` だけにして理由を出さないことはしない★★ —
> 押せないボタンは、理由が書いていないと不具合と区別がつかない（D-053 と同じ理由）

★★したがって D-053 と整合するのは「機能させる」です★★。
★「ボタンを消す」を選ばないこと★ — ★モックには `?` があり、押すと開きます★
（`d-watch-mock.jsx:301`）。★消すと `CLAUDE.md` §5 に反します★。

### 9-4. ★★注記: EN表示で和文の塊が1つ増えます★★

凡例の文言は★`t()` を通していません★:

```
CleanLogTable.tsx:126   判定基準（タスク完了率）:
CleanLogTable.tsx:142   （閾値はシステム設定で変更可能）
```

★開けるようにすると、EN表示で和文の塊が新たに1つ増えます★。

★★これは既存の抜けであって本段階が作る問題ではありません★★
（★同じ凡例は詳細ログでも開きます★）。★§2 #1 と同じ理由で今回は直しません★。
★報告に1行書いてください★。

★★もし直すなら★★: 辞書に足すのは★2キーだけ★です。
★ただし `◎ {GRADE_THRESHOLDS.excellent}%以上` のような★数値混じりの行★は、
`lib/i18n.ts` の★方式A（プレースホルダ + replace）★に乗せる必要があります
（`"{n}分前"` などが前例）。★1文字キー（`%以上` 等）にしないこと★。
★これは本指示書の範囲外です★。

### 9-5. 完了条件

- [ ] ★ダッシュボードで `?` を押すと凡例が開く★
- [ ] ★もう一度押す / `✕` を押すと閉じる★
- [ ] ★詳細ログ側の挙動が変わっていないこと★
- [ ] ★`CleanLogTable.tsx` を変更していないこと★
- [ ] `npx tsc --noEmit` → 0 / `npm run lint` → 0 / `npm test` → 全パス（★本数は段階3のまま★）

**コミットメッセージ案**

```
fix(dashboard): 「判定 ?」ボタンを機能させる（useState 1つ）

凡例本体もデータも既にあり、状態を渡していないだけだった。
D-053（何も起きないUIは消す）は「機能させるデータが無い」場合の話で、
本件は前提が違う。モックには ? があり押すと開く（CLAUDE.md §5）。
ユーザー判断 2026-09-16 / D-143 §3 #2 / 指示書AO 段階6。
```

---

## 10. 段階7 — ★記録★

### 10-1. ★★採番の前に必ず実測すること★★

```bash
grep -n "^### D-" docs/decisions.md | tail -3
```

★★`^### D-0` では3桁台を拾えません★★（★二重採番の事故歴があります★）。

★着手前の実測では最終番号は `D-143`（`docs/decisions.md:11255`）★です。
★★着手時点で増えている可能性があるので、必ず自分で確認してください★★。

★D-143 §3 の表と同じ形式で、採番の根拠を本文の冒頭に1行残すこと★:

> ★**採番についての注記**★: `grep -n "^### D-" docs/decisions.md | tail -3` で最終番号が
> ★`D-1xx`★ であることを実測してから `D-1yy` を採った。

### 10-2. `docs/decisions.md` に書くこと

- ★D-143 §3 の「未回答」3件（#2 / #3 / #6）が、ユーザー判断で確定したこと★
  （★#3 は「8列に戻す」ではなく★★「出発1列だけ」★★。★調査の提案と違う★点を明記）
- ★`clean_error` を画面とCSVに出したこと★と、★★D-107 の申し送り 項1 が片付いたこと★★
- ★`null` を「出発失敗」と表示すると決めたこと★（★ユーザー原文を転記★）と、
  ★`clean_error = 1`（異常(清掃未実施)）とは**別の表示**にしたこと★
- ★★未知コード（5〜255）は `String(code)` のまま画面に出ること★★
  （D-077。★「その他」「不明」に潰さない★）
- ★存在しない「規約」を根拠にコンパクト表の行展開が無効化されていたこと★と、
  ★★その記述を消したこと★★（★同じ思い込みが再発しないように、事実として残す★）
- ★エリアモーダルをダッシュボードにも置いた理由★
  （★空関数のままだと「押しても何も起きないボタン」を新たに作ることになる★）
- ★CSVの列順を画面と揃えた理由★と、★★テストが列を位置で見ているので添字を直したこと★★
- ★★やらないと決めた5件（§2）とその理由★★ — ★次の担当者が「作り忘れ」と誤認しないため★
- ★★英語配布を決めた時点で `未対応` の対訳を必ず直す必要があること★★

### 10-3. `docs/handoff.md` に追記すること

- ★★`lib/i18n.ts` の `未対応` → `Unsupported` は直していない★★（D-143 §3 #1）。
  ★英語配布を決めた時点で必ず直す★。★`要対応` にも同型の地雷がある★
- ★凡例（`判定基準（タスク完了率）:` / `（閾値はシステム設定で変更可能）`）と
  アドバイス展開部の見出しが `t()` を通っていない★ — ★EN で和文が出る★。
  ★段階6でダッシュボードでも出るようになった★
- ★`1h前ST` / `30分前ST` はダッシュボードには出していない★（ユーザー判断）
- ★サイドバーの未対応件数バッジは足さない★（PATCH 未実装・DB更新用アカウント未受領）
- ★D-Secアラートの期間フィルタは入れない★（入れるなら `d-watch-api.yaml` の変更から）

### 10-4. 完了条件

- [ ] ★採番を実測してから採ったことが本文に書いてある★
- [ ] ★`lib/types/index.ts` / `lib/config/index.ts` / `d-watch-api.yaml` に書いた `D-1xx` が
      実際の番号に置き換わっている★
- [ ] `docs/handoff.md` に5件の申し送りが入っている
- [ ] ★`git add` にファイルパスを明示している★（★`git add -A` を使っていない★）

**コミットメッセージ案**

```
docs: ダッシュボードと詳細ログの列修正を記録する（D-1xx）

やったこと4件とやらないと決めた5件の両方を残す。存在しない「規約」が
根拠になっていた経緯と、英語配布時に「未対応」の対訳を直す必要があることを
申し送りに入れた（指示書AO 段階7）。
```

---

## 11. ★実装者へ — 特に踏みやすい落とし穴★

| # | 落とし穴 | どうなるか |
|---|---|---|
| ★1★ | ★判定・整形を `components/` に書く★ | ★★テストが走らない★★（`vitest.config.ts:14` の `include` は `lib/**` と `app/**` のみ）。★落ちないのでゼロ件成功と区別がつかない★ |
| ★2★ | ★`colSpan` の片方だけ直す★ | ★tsc も lint もテストも通る★。★空状態か展開行のどちらかが詰まる。目視でしか気づけない★ |
| ★3★ | ★段階5 を段階4 より先にやる★ | ★展開行が4列ぶんしか広がらない★。★同じく気づけない★ |
| ★4★ | ★`clean_error ?? x` を `\|\| x` と書く★ | ★★`0`（正常）が「出発失敗」に化ける★★。★例外も tsc エラーも出ない★（段階1 期待値表 1-10） |
| ★5★ | ★`clean_error === 1` と `null` を同じ表示に潰す★ | ★「履歴が無い」と「未実施として記録された」の区別が消える★（期待値表 1-3） |
| ★6★ | ★未知コードを「その他」「不明」にする★ | ★D-089 項3 / D-104 項2 に反する★。★破棄済み D-067 の方針と紛らわしい★。★`String(code)` が正★ |
| ★7★ | ★ラベルの半角括弧を全角に直す★ | ★定義書の原文が半角★（`lib/config/index.ts:277-278`）。★実データと突き合わせたときに原文が分からなくなる★ |
| ★8★ | ★CSVテストの添字を直し忘れる★ | ★テストが落ちるので**そこは安全**★。★危険なのは「落ちたから添字を合わせる」だけで新しい列のテストを書かないこと★ |
| ★9★ | ★CSVで `csvCell()` を通さない★ | ★D-125 の関門が1つ増える★。★`csv.ts:78-83` の約束が崩れる★ |
| ★10★ | ★CSVの列順を画面と変える★ | ★突き合わせのたびに並べ替えになる★ |
| ★11★ | ★`onOpenAreas` を `() => {}` のまま残す★ | ★★今直している「死んだボタン」と同じものを隣に作る★★（§8-4） |
| ★12★ | ★`?.length` / `\|\|` でモーダルの応答を分岐する★ | — ★`cancelled` フラグを落とすと古い応答が新しい表示を上書きする★ |
| ★13★ | ★`t()` に `清掃結果` と別の意味の日本語を足す★ | ★★`lib/i18n.ts` はキーが日本語原文の1階層辞書★★。★同じ日本語を別の意味で使うと必ず衝突する★（`未対応` → `Unsupported` の事故が実在）。★足す前に必ず grep する★ |
| ★14★ | ★セルの値を `t()` に通す★ | ★`CLEAN_ERROR_LABELS` は定義書の原文★。★訳すと何が原文か分からなくなる★。★未知コードはそもそも訳せない★ |
| ★15★ | ★「規約」のコメントを消して別の根拠を書く★ | ★出所不明の根拠を足したのがこの事故の原因★。★「規約は存在しなかった」という事実を残す★ |
| ★16★ | ★モック（`d-watch-mock.jsx`）を直す★ | ★★原型として保存する★★（`CLAUDE.md` §7）。★1文字も書き換えない★ |
| ★17★ | ★`d-watch-api.yaml` のスキーマを変える★ | ★変更不要★（§1-5）。★直すのは説明文1行だけ★ |
| ★18★ | ★新しいCSSに `font-family` を書く★ | ★漢字が中華字形になる★（`CLAUDE.md` §0-3）。★そもそも今回CSSは要らない★ |
| ★19★ | ★`git add -A`★ | ★★禁止★★（`CLAUDE.md` §7。★別作業の途中ファイルを巻き込む事故歴あり★） |
| ★20★ | ★決定番号を先に決める★ | ★★二重採番の事故歴あり★★。★着手直前に `grep -n "^### D-" \| tail -3` で実測する★ |

---

## 12. ★やらないこと（この指示書の範囲外）★

| 項目 | なぜ |
|---|---|
| ★`未対応` の対訳キー衝突を直す★ | ★英語を配布するか未定★（§2 #1 / D-143）。★★英語配布を決めた時点で必ず直す★★ |
| ★サイドバーの未対応件数バッジ★ | ★PATCH 未実装・DB更新用アカウント未受領★（D-143） |
| ★D-Secアラート「通信断」の色★ | ★アンバー維持で確定★（D-143） |
| ★D-Secアラートの期間フィルタ★ | ★今は入れない★（D-143）。★入れるなら `d-watch-api.yaml` の変更から★ |
| ★アラート画面のバッテリー残量行★ | ★意図した仕様★（D-105 項5）。★空欄のまま残すのが正★ |
| ★ダッシュボードに日付 / 1h前ST / 30分前ST を足す★ | ★★ユーザーが「出発だけ」と決めた★★（D-143 §3 #3 の追加判断） |
| ★凡例・アドバイス見出しの対訳★ | ★既存の抜け★。★英語配布の判断とセットで別途★ |
| ★`1h前ST` / `30分前ST` の対訳★ | ★モックも実装も `t()` を通していない生文字列★。★詳細ログでも同じなので今回は変えない★ |
| ★ダッシュボードの清掃ログをCSVに出す★ | ★ユーザーが求めていない★（CSVは詳細ログのみ） |
| ★`clean_error` による絞り込み★ | ★API契約に無い★。★要るなら `d-watch-api.yaml` の変更から★ |
| ★画面のテストを書けるようにする★ | ★`vitest.config.ts` の `include` を広げる話★。★別課題★ |
| ★スマートフォン幅のレイアウト★ | 別課題 |
