# 指示書AL — 当期KPIを顧客で絞り、レポート画面を1本ずつ出す

**作成: 2026-09-16 / ブランチ: `develop` / 前提: 指示書AK 完了（D-138）**

---

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

> ```
> D-Watch の実装をお願いします。ブランチは develop です。
>
> ■ 最初に読むもの（この順で、全部）
>   1. CLAUDE.md（全文。特に §4.2 KPI計算式 / §4.7 集計期間 / §7 禁止事項 / §9 実装規約）
>   2. docs/handoff/指示書AL_当期KPIの絞り込みとレポート画面の粒度.md（全文）
>   3. docs/decisions.md の D-138（grep -nE "^### D-(0|1)[0-9][0-9]" docs/decisions.md で
>      位置を出してから読む。★番号順に並んでいません★）
>   4. lib/db/index.ts の reportKpis()（確定期間と当期の2本立てを、先に読んで理解すること）
>
> ■ やること
>   指示書AL の 段階1 から 段階5 までを、★番号順に★実行してください。
>
> ■ 段階ごとに必ずやること
>   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/db/prisma.ts
>   ※ lib/judge/ は ★段階4 でだけ★ 触ります（grade.ts の比較演算子）。
>     ★段階1〜3 では触らないでください★。lib/judge/advice.ts は段階4 でも触りません
>
> ■ 絶対にやらないこと
>   ・生SQL / メインDBのスキーマ変更
>   ・★閾値の「数値」を変えること★（段階4 で変えるのは★比較演算子だけ★。90 / 70 / 10 はそのまま）
>   ・閾値のハードコード（lib/config から読むこと）
>   ・新しい CSS に font-family を書くこと
>   ・★確定期間の経路（customer_summaries を読むところ）を変えること★
>     — 段階6（D-138）で査読を通った実装です。今回は当期だけを直します
>   ・仕様が不明なところを推測で実装すること
>
> ■ 動作確認
>   ★ローカルDB（dwatch_local）に 2グループ・3顧客・11施設 が入っています★
>   （前回の検証で再シード済み。npm run seed:local 相当で作り直せます）。
>   ★テストDB（133.125.238.144）には書き込まないでください★。
>   期待される結果は指示書AL の §7 に書いてあります。
>
> ■ 終わったら
>   ・段階ごとのコミットハッシュ
>   ・tsc / lint / test の結果（テスト本数も）
>   ・★§7 の動作確認を実際に行った結果★
>   ・指示書どおりにできなかったところと、その理由
>   ・判断に迷って自分で決めたところ（あれば全部）
> ```

---

## 0. なぜこの指示書があるか

★指示書AK の実装は、査読3本とも合格でした。★
★落とし穴14個すべて回避、テスト 86ファイル・2,013件 全パス、`tsc` / `lint` は 0。★

★★直すのは、私（監督）が指示書AK で漏らした1箇所と、その巻き添えで起きている1件です。★★

| # | 何を漏らしたか |
|---|---|
| ★1★ | ★段階4 で絞り込みを効かせるルートを列挙したとき、`/reports/kpi` を入れ忘れた★。段階6 で確定期間だけを扱い、★当期を誰にも割り当てなかった★ |
| ★2★ | ★段階6 で「顧客が定まらなければ422」と決めたが、★★画面がその422をどう扱うか★★を指定しなかった★ |

★★実装者に落ち度はありません。★★
★むしろ①は実装者が自分で見つけて、D-138 §2・`lib/types/index.ts`・`docs/handoff.md` の
3箇所に残課題として記録しています。★ ★隠していません。★

---

## 1. ★① 当期のKPIが顧客で絞られていない★

### 1-1. 何が起きているか（実測）

★ローカルDB（3顧客）での実測★です。

| | 顧客1 | 顧客2 | 顧客3 | ★存在しないID★ |
|---|---|---|---|---|
| ★確定週の完了率★ | 87.1 | 78.4 | 69.7 | ★空（正しい）★ |
| ★★当期の完了率★★ | ★★55.3★★ | ★★55.3★★ | ★★55.3★★ | ★★55.3（空にならない）★★ |

★どの顧客を選んでも同じ数字★です。★全顧客の合算★になっています。

SQLでの検算:

```
customer_id=1 → 57.613889
customer_id=2 → 52.407143
customer_id=3 → 46.44
★全社        → 55.272727★   ← API が返している値
```

### 1-2. ★前期比がもっと悪い★

```
当期   = 全社の数字          ← 絞れていない
前期   = 選んだ1社の数字      ← 絞れている
前期比 = この2つの差分        ← ★意味のない値★
```

### 1-3. ★同じ画面の中で矛盾します★

```
KPIカード      全社の数字
下の施設一覧    選んだ1社だけ   ← facility-performance は当期も絞れている
```

★例外もテスト失敗も出ません。★

### 1-4. ★fixtures では絶対に見えません★

```
USE_FIXTURES=true  当期・customer_id=2 → ★200・空★（正しい）
Prisma 経路        当期・customer_id=2 → ★全社の数字★
```

★`CLAUDE.md` §9.5 の「切替で挙動が変わらない」が、当期だけ崩れています。★
★fixtures で開発している限り、この不具合は一生見つかりません。★

---

## 2. 段階1 — 当期のKPIを顧客で絞る

### 2-1. 変える場所

| ファイル | 何を |
|---|---|
| `lib/db/index.ts` の `reportKpis()` 当期ブランチ | ★`cleanTimetable.findMany` の `where` に施設の条件を足す★ |
| `lib/reports/query.ts` の `toReportKpiDbQuery()` | ★`facility_ids` を `null` 固定にしない★ |
| `app/api/reports/kpi/route.ts` | ★施設IDを解決して渡す★ |

★★`resolveFacilityIdsByMasters()` を使うこと★★ — ★他の3本（facility-performance /
continuous-problems / error-breakdown）が既に使っている、同じ1本★です。
★新しい解決関数を作らないこと。★

### 2-2. ★確定期間の経路は触らないこと★

★段階6（D-138）で査読を通った実装です。★
★`customer_summaries` を `customer_id` で絞る部分は、1行も変えないこと。★

★今回足すのは「当期の詳細集計を施設で絞る」だけ★です。

### 2-3. ★★確定期間と当期で、同じ施設集合になること★★

★これが今回いちばん大事な点です。★

```
確定期間 → customer_summaries を customer_id で絞る    （顧客粒度）
当期     → clean_timetables を facility_id で絞る      （施設粒度）
```

★別の経路ですが、同じ顧客を指しているはずです。★

★★`target.customerId` から施設IDを解決すること★★ —
★リクエストの `customer_id` / `group_id` から**別々に**解決すると、ずれる余地が残ります。★

具体的には:

```
1) resolveTargetCustomer() で対象顧客を1社に決める（既存。変えない）
2) ★その顧客ID★ から施設IDを解決する
3) 確定期間はその顧客IDで、当期はその施設IDで絞る
```

★★`group_id` から直接施設を引かないこと★★ — ★傘下2社なら既に422で止まっている★ので、
★段階1 に到達した時点で顧客は1社に定まっています。★

### 2-4. ★既存テストの期待値を作り直すこと★

`app/api/reports/__tests__/route.test.ts` の「#2 当期」は、
★`customer_id` を付けずに施設1〜3を合算する前提★で書かれています。

★★いまの誤った挙動をテストが固定しているわけではありません★★ —
★`customer_id` 未指定で顧客1社なら、全施設が対象になるのが正しい★からです。

★ただし、絞り込みが入ると通らなくなる可能性があります。★
★落ちたら、期待値を作り直すこと。★ ★テストを消して逃げないこと。★

### 2-5. 期待値表

★ローカルDB（2グループ・3顧客・11施設）を前提★とします。

| # | 入力 | 期待 |
|---|---|---|
| ★1-1★ | 当期・`customer_id=1` | ★★顧客1の施設だけの数字★★（★全社の数字にならない★） |
| ★1-2★ | 当期・`customer_id=2` | ★1-1 と違う数字★ |
| ★1-3★ | 当期・`customer_id=9999` | ★★空（`value` が `null`）★★（★★数字が出たら不具合★★） |
| 1-4 | 当期・`group_id=2`（傘下1社） | その顧客の数字 |
| 1-5 | 当期・指定なし・顧客1社 | 全施設（＝その顧客の全施設） |
| 1-6 | 当期・指定なし・顧客2社以上 | ★422★（段階6 で既に止まる） |
| ★1-7★ | ★確定期間と当期の両方を含む期間★ | ★★両方が同じ顧客の数字であること★★ |
| ★1-8★ | ★前期比★ | ★当期・前期とも同じ顧客の数字から計算されていること★ |

★★1-3 が今回の要です★★ — ★存在しない顧客を指定して数字が出るなら、絞れていません。★

★1-7 と 1-8 は、実データ（ローカルDB）で確かめること★。★テストだけで済ませないこと。★

### 2-6. fixtures 側

★fixtures 経路は既に正しく動いています★（`customer_id=2` で空）。
★壊さないこと。★ ★Prisma 側と挙動が揃うことを確かめること。★

---

## 3. ★② レポート画面が、1本の失敗で4本とも消える★

### 3-1. 何が起きているか

`components/features/reports/ReportsScreen.tsx` は4本を `Promise.all` で取り、
★1本でも失敗すると画面全体をエラー表示にします★。

```
/reports/kpi                    ← 顧客が定まらないと422
/reports/facility-performance   ← 施設単位。合算に問題なし
/reports/continuous-problems    ← 同上
/reports/error-breakdown        ← 同上
```

★KPIだけが422になっても、残り3本は問題なく出せます。★
★いまは画面ごと「顧客またはグループを指定してください」の1行になります。★

### 3-2. ★fixtures で開発できなくなっています★

★fixtures が4顧客になった★ため、★`USE_FIXTURES=true` だと分類を選ぶまで
レポート画面が何も見られません★。

★実運用（顧客1社）では発火しません★が、★開発中の人が最初に踏みます★。

### 3-3. ★どう直すか（監督の決定）★

```
1本ずつ、成功したものは出す。失敗したブロックだけにメッセージを出す。
```

★`Promise.all` を `Promise.allSettled` に変える★のが素直です。

★★ただし、次の2つを守ること★★:

- ★どのブロックが失敗したのかが分かること★ — ★「読み込みに失敗しました」だけでは、
  どれが欠けているのか分かりません★
- ★★全部失敗したときは、今までどおり画面全体のエラーにすること★★ —
  ★4つとも空の枠が並ぶより、1行で「読み込めませんでした」のほうが分かります★

### 3-4. ★422 のときは「選んでください」と誘導すること★

★APIは候補を返しています★（`details.customer_ids`）。★画面がそれを捨てています。★

```
KPIのブロックだけに:
  「顧客を選んでください」
  ＋ ★選択肢が3つあることが分かる形★（件数を出す、屋号セレクトを強調する、など）
```

★具体的な見せ方は実装者判断★です。★ただし「文言を読ませるだけ」で終わらせないこと。★
★`details.customer_ids` を使うこと。★

### 3-5. 期待値表

| # | 状況 | 期待 |
|---|---|---|
| 2-1 | 4本すべて成功 | ★今までどおり4ブロック★ |
| ★2-2★ | ★KPIだけ422★ | ★★残り3ブロックが出る★★＋KPIのブロックに誘導 |
| 2-3 | KPIだけ500 | ★残り3ブロックが出る★＋KPIのブロックにエラー |
| ★2-4★ | ★4本とも失敗★ | ★★画面全体のエラー（今までどおり）★★ |
| 2-5 | 施設別だけ失敗 | 残り3ブロックが出る |

★★2-2 と 2-4 の両方をテストに入れること。★★

★画面のテストは走りません★（`vitest.config.ts` の `include` は `lib/**` と `app/**`）。
★「どのブロックを出すか」を決める部分を `lib/` 側の純粋関数に切り出すこと★ —
★指示書AJ で `lib/dashboard/rescue-visibility.ts` を切り出したのと同じ形★です。

---

## 4. 段階3 — 細かい直し4件

★どれも小さいですが、放っておくと次の人が誤読します。★

### 4-1. YAML の古い一文

`d-watch-api.yaml` の `GroupId` の description 末尾:

```
絞り込みの適用はエンドポイントごとに順次有効化中(指示書AK 段階4)。
```

★段階4 は完了しました。★ ★この一文は今は正しくありません。★ ★消すか、現状に合わせること。★

### 4-2. `docs/handoff.md` の欠け

指示書AK §12-2 の4項目のうち、★「点検記録の絞り込みは、画面が復活したときに足すこと」★が
残課題リストに入っていません（★本文に「対象外」と書かれているだけ★）。

★残課題として足すこと。★ ★画面が復活したときに思い出せる形にすること。★

### 4-3. 不要な顧客マスタの読み込み

`app/api/reports/kpi/route.ts` は、★`customer_id` が明示されていても顧客マスタを読んでいます★。

★`resolveTargetCustomer()` は `customerId !== null` のとき顧客リストを見ません★ので、
★この読み込みは不要★です。★毎リクエスト1クエリ増えています。★

★`customer_id` が明示されているときは読まないようにすること。★

### 4-4. `customers()` に並び順が無い

`lib/db/index.ts` の `customers()` に `orderBy` がありません。
★422 の `details.customer_ids` の並びがDB任せ★になっています。

★`id` の昇順にすること★（`/api/customers` の並びと揃える）。
★テストはスタブ順で通ってしまうので、実DBで確かめること。★

---

## 5. 段階4 — ★判定の境界を「含める」に変える★

### 5-1. ユーザー指示（原文）

> ◯、×は90%以上のように境はふくめてください

確認したところ:

> 10％未満を×としてください

★**確定:**★

```
◎  完了率 90%以上
○  完了率 70%以上
△  完了率 10%以上
×  完了率 10%未満、または未実施
```

★★すべての境界が「上のランクに入る」で揃います。★★

### 5-2. ★何が変わるか★

| 完了率 | 今 | ★変更後★ |
|---|---|---|
| 90.1 | ◎ | ◎ |
| ★90.0★ | ★○★ | ★★◎★★ |
| 89.9 | ○ | ○ |
| 70.1 | ○ | ○ |
| ★70.0★ | ★△★ | ★★○★★ |
| 69.9 | △ | △ |
| 10.1 | △ | △ |
| 10.0 | △ | ★△（変わらない）★ |
| 9.9 | × | × |
| null（未実施） | × | × |

★★変わるのは 90.0 と 70.0 の2つだけです。★★

### 5-3. ★これまでの決定を覆します★

`docs/decisions.md` の ★D-020b★ が、★逆の規則★を定めています。

> - **決定**: 完了率 90.0 は ○、70.0 は △、**10.0 は △**（×ではない）
> - **理由**: ◎は「90%超」、○は「70%超」と、★境界値そのものは下のランクに落ちる規則で統一されている★

★★D-020b の見出しに `[破棄]` の印と行き先を書くこと★★
（D-024 / D-067 / D-053 / D-133 / D-135 と同じ作法）。

★★`CLAUDE.md` §4.1 は「確定仕様」であり、その本文を書き換えます。★★
★これは軽い変更ではありません。★ ★ユーザーの明示指示（2026-09-16）に基づくことを、
記録に原文つきで残すこと。★

### 5-4. ★★見落としやすい連鎖 — 「問題あり」の判定★★

★ここが今回いちばん危ないところです。★

`lib/db/clean-log-mapping.ts:282`

```ts
return completionRate === null || completionRate <= GRADE_THRESHOLDS.good;
```

★これは「△以下＝問題あり」を完了率から直接判定しています。★
★70.0 が △ だったので `<=` が正しかった★のですが、
★★70.0 が ○ になるので `<` に変えないと、○ の日が「問題あり」のままになります。★★

★この関数が効いている先★:

```
連続問題日数 → L1 / L2 / L3 → R3 / R4 のアドバイス
              → 要レスキュー店舗の R-2（連続問題レベル3）
              → レポートの「連続問題施設」
```

★★直し忘れても例外は出ません。★★ ★完了率ちょうど70.0の日だけが、静かに問題日として数えられ続けます。★

★`lib/judge/grade.ts` の docstring にも、この関係が明記されています★:

> あちらは完了率（生値）を入力に取り `rate === null || rate <= GRADE_THRESHOLDS.good` で判定する。
> ★判定結果は本関数と常に一致する★（`judgeGrade()` の「○ = 70%超」より、△× ⟺ `null` または 70 以下）。
> ★入力が違うだけの同じ規則★なので、★★片方の閾値だけを動かさないこと★★。

★★この注記を書き換えること。★★ ★「70 以下」→「70 未満」、「○ = 70%超」→「○ = 70%以上」。★

### 5-5. 変える場所

| ファイル | 何を |
|---|---|
| ★`lib/judge/grade.ts`★ | `rate > excellent` → ★`>=`★ / `rate > good` → ★`>=`★ / ★`rate < poor` は**そのまま**★ |
| 同上 | ★docstring の表と、`isProblemGrade()` の長い注記★ |
| ★`lib/db/clean-log-mapping.ts`★ | ★`<= GRADE_THRESHOLDS.good` → `<`★（§5-4） |
| `lib/config/index.ts` | ★`GRADE_THRESHOLDS` のコメント★（「この値を**超える**と ◎」→「**以上**で ◎」）。★数値は変えない★ |
| ★`CLAUDE.md` §4.1★ | 判定基準の表 |
| ★`CLAUDE.md` §9.4★ | ★境界値のリスト★（「90.4 → 表示90だが判定は◎、89.5 → 表示90だが判定は○」はそのまま正しい） |
| `docs/fixtures.md` | ★境界値表★ |
| `docs/decisions.md` | ★D-020b に `[破棄]` の印★ |
| `d-watch-api.yaml` | ★grade の説明に「超」「以下」が書かれていないか確かめること★ |

★★`GRADE_THRESHOLDS` の数値（90 / 70 / 10）は変えないこと。★★ ★比較演算子だけです。★

### 5-6. ★確かめるが、勝手に変えないもの★

`lib/fixtures/clean-logs.ts:226`

```ts
rate === null || rate < GRADE_THRESHOLDS.good ? 1 : 0,
```

★これは fixtures のデータ生成で、既に `<` です。★
★「問題あり」と同じ概念なのか、別の概念なのかを確かめてください。★

- ★同じ概念なら★: 今回の変更後は `<` が正しいので、★そのままでよい★
- ★別の概念なら★: ★触らないこと★

★どちらか判断できなければ、手を止めて報告すること。★ ★推測で変えないこと。★

`lib/judge/advice.ts:66`

```ts
completionRate < GRADE_THRESHOLDS.poor
```

★R2「完了率×（10%未満）」の判定です。★ ★× の境界は変わらないので、`<` のままで正しい★です。
★触らないこと。★

### 5-7. 期待値表

★`CLAUDE.md` §9.4 が「境界値を必ず含める」と定めています。★ ★10個すべてテストに入れること。★

| # | 入力 | 期待 |
|---|---|---|
| 4-1 | 90.1 | ◎ |
| ★4-2★ | ★90.0★ | ★★◎★★（★今は○。ここが変わる★） |
| 4-3 | 89.9 | ○ |
| 4-4 | 70.1 | ○ |
| ★4-5★ | ★70.0★ | ★★○★★（★今は△。ここが変わる★） |
| 4-6 | 69.9 | △ |
| 4-7 | 10.1 | △ |
| 4-8 | ★10.0★ | ★△（変わらない）★ |
| 4-9 | 9.9 | × |
| 4-10 | null | × |

★「問題あり」の期待値表★（§5-4）:

| # | 完了率 | 期待 |
|---|---|---|
| ★4-11★ | ★70.0★ | ★★問題なし★★（★今は問題あり★） |
| 4-12 | 69.9 | 問題あり |
| 4-13 | 70.1 | 問題なし |
| 4-14 | null | 問題あり |

★★4-11 を必ず入れること。★★ ★ここが漏れると、連続日数が静かに1日多く数えられます。★

### 5-8. ★既存テストが落ちます★

★境界値を固定しているテストが複数あります。★ ★期待値を新しい規則に直すこと。★
★★テストを消して逃げないこと。★★

★落ちたテストの一覧を報告に含めること★ — ★想定外の場所が落ちたら、それは連鎖の見落としです。★

---

## 6. 段階5 — 記録

### 6-1. `docs/decisions.md`

★★採番の前に必ず実測すること★★:

```bash
grep -nE "^### D-(0|1)[0-9][0-9]" docs/decisions.md | tail -3
```

★番号順に並んでいません。★

書くこと:

- ★当期KPIを顧客で絞ったこと★と、★★確定期間と当期で同じ施設集合になるよう
  `target.customerId` から解決した理由★★
- ★D-138 に残課題として書かれていた項目が解決したこと★ —
  ★★D-138 側の残課題の記述にも「D-1xx で解決」と印を付けること★★
  （★D-053 / D-133 / D-135 と同じ作法★）
- ★レポート画面を1本ずつ出すようにした理由★（★fixtures で開発できなくなっていた★）
- ★全部失敗したときだけ画面全体のエラーにする、という切り分けの理由★

### 6-2. `docs/handoff.md` に追記

- ★`USE_FIXTURES` の true / false で、レポート画面の見え方が揃ったこと★
- ★★段階7 の画面（分類セレクト・カスケード）の目視確認が、まだ行われていないこと★★ —
  ★査読3本とも「ログインが要るので未確認」と報告しています★
- ★`group_id = 0` の顧客が、ローカルDBに投入できないままであること★（第9回 Q7-4 待ち）
- ★4画面は「1操作＝1リクエスト」であること★（★レポート画面だけが検索ボタンで確定★）—
  ★モックに検索ボタンが無いための判断で、記録済み★。★変えるなら別の判断が要る★

---

## 7. ★動作確認★

### 7-1. 前提

★ローカルDB（`dwatch_local`）に 2グループ・3顧客・11施設 が入っています。★
★作り直すなら `scripts/seed-local-db.ts`。★
★★テストDB（`133.125.238.144`）には書き込まないこと。★★

★`.env.local` はテストDBを向いています。★ ★書き換えないこと★ —
★環境変数を上書きして別に起動するか、ルートハンドラを直接呼ぶこと。★

### 7-2. 確かめること

```
/api/reports/kpi?period=monthly&year=2026&month=9&customer_id=1
/api/reports/kpi?period=monthly&year=2026&month=9&customer_id=2
/api/reports/kpi?period=monthly&year=2026&month=9&customer_id=9999
```

| # | 期待 |
|---|---|
| ★6-1★ | ★★当期の数字が、顧客1 と 顧客2 で違うこと★★ |
| ★6-2★ | ★★`customer_id=9999` で当期が空になること★★（★数字が出たら直っていない★） |
| 6-3 | 確定期間の数字が、指示書AK のときと変わっていないこと |
| ★6-4★ | ★前期比が、当期・前期とも同じ顧客から計算されていること★ |
| 6-5 | 指定なし・3顧客 → ★422（今までどおり）★ |

★★自分でSQLを書いて検算すること。★★ ★APIの出力をそのまま信じないこと。★

### 7-3. レポート画面

★ログインが要るので、画面の目視は難しいかもしれません。★
★その場合は、切り出した純粋関数のテストと、API の応答で確かめること。★
★★「目視できなかった」と正直に報告してよい。★★ ★推測で「動いているはず」と書かないこと。★

---

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

| # | 落とし穴 | どうなるか |
|---|---|---|
| ★1★ | ★当期を `group_id` から直接絞る★ | ★確定期間（顧客粒度）と母集団がずれる★。★段階1 に来た時点で顧客は1社に定まっている★ |
| ★2★ | ★確定期間の経路も一緒に直す★ | ★査読を通った実装を壊す。今回は当期だけ★ |
| ★3★ | ★`facility_ids` を `?.length` で分岐する★ | ★「0件ヒット」が「全件」に化ける。例外もテスト失敗も出ない★ |
| ★4★ | ★落ちたテストを消して逃げる★ | ★期待値を作り直すこと★ |
| ★5★ | ★`Promise.allSettled` にしたが、どれが失敗したか分からない★ | 欠けているブロックに気づけない |
| ★6★ | ★全部失敗しても空の枠を4つ並べる★ | 1行で「読み込めませんでした」のほうが分かる |
| ★7★ | ★`details.customer_ids` を捨てる★ | ★APIが用意した候補が活きない★ |
| ★8★ | ★D-138 の残課題に印を付けない★ | ★次の人が「まだ直っていない」と読む★ |
| ★9★ | ★画面に判定を書く★ | ★画面のテストは走らない★。`lib/` に切り出すこと |
| ★10★ | ★テストDBに書き込む★ | ★★読み取り専用★★ |
| ★★11★★ | ★★`judgeGrade()` だけ直して `isProblemCompletion()` を直し忘れる★★ | ★★完了率ちょうど70.0の日が「問題あり」のまま数えられ続ける★★。★連続日数 → L1〜L3 → R3/R4 → 要レスキューまで波及する★。★例外もテスト失敗も出ない★ |
| ★12★ | ★`rate < GRADE_THRESHOLDS.poor` も `<=` にする★ | ★★× の境界は変えません★★（「10%未満が×」でユーザー確定）。★10.0 が × に落ちる★ |
| ★13★ | ★`GRADE_THRESHOLDS` の数値を変える★ | ★変えるのは比較演算子だけ★。90 / 70 / 10 はそのまま |
| ★14★ | ★`lib/judge/advice.ts` を直す★ | ★R2 は「×（10%未満）」で、× の境界は変わらない★。★触らないこと★ |
| ★15★ | ★D-020b と `CLAUDE.md` §4.1 を直さない★ | ★★コードと確定仕様が食い違う★★。次の人がどちらを信じるか分からなくなる |
| ★16★ | ★`lib/fixtures/clean-logs.ts` を確かめずに変える★ | ★同じ概念か別概念かを先に判断すること★。★判断できなければ手を止めて報告★ |

---

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

| 項目 | なぜ |
|---|---|
| ★4画面の draft / 確定の分離★ | ★モックに検索ボタンが無いための判断で、記録済み★。★変えるなら別の判断★ |
| ★総清掃面積の新画面★ | ★次の指示書★ |
| ★複数顧客の合算★ | ★4指標のうち2つを合算する材料がDBに無い★（第9回 6章で照会中） |
| ★`group_id = 0` の投入★ | ★DDLの話。DFAは直せない★（第9回 Q7-4） |
| ★200件超の実行計画対策★ | 測れる見積もりが無い |
| ★点検記録の絞り込み★ | 画面自体が未実装 |
