# 指示書AQ — データ参照元の切替（サマリ3粒度 / clean_task_reports / 完了率）

**日付**: 2026-09-30 / **前提**: ユーザー指示 2026-09-30、CSV実測189行、本番DB実測

---

## 0. 何が変わるか（ユーザー確定事項）

| # | 決定 | 出所 |
|---|---|---|
| 1 | ★確定期間のサマリは `*_summaries` から取得する。★**週・月・年の3粒度**★が入る（週＝毎週日曜作成 / 月＝月初作成） | ユーザー指示 2026-09-30 |
| 2 | ★直近・昨日の清掃情報は `clean_task_reports` から取得する★ | 同上 |
| 3 | ★`clean_status_histories` は使わない（clean_task_reports に一本化）★ | 同上（選択肢の帰結を明示したうえでの選択） |
| 4 | ★清掃完了率は `clean_ratio` を採用する。面積比（Σclean_area÷Σtask_area）は廃止★ | 同上 |
| 5 | ★`clean_timetable_id` は今後 TeamWorks が埋める前提で実装する★ | 同上 |
| 6 | ★エラーは `robot_error_reports` に集約する。ただし実装は TeamWorks の入れ方を確認してから★ | 同上（本指示書の範囲外） |

### 決定4の根拠（実測。CLAUDE.md §6-6）

出所: ★ユーザー提供CSV `clean_task_reports.csv` 189行★（2026-09-30 受領。本番DBの `clean_task_reports` は0行なので、当DBからは取得できない）。

| 実測 | 値 |
|---|---|
| `clean_area > task_area` の行 | ★129 / 189（68.3%）★ |
| Σ`clean_area` ÷ Σ`task_area` | 90.2% |
| DBの `clean_ratio` の平均 | ★78.5%★ |
| 個別行の乖離例 | 面積比 13.8% ↔ `clean_ratio` 100.0% / 面積比 162.8% ↔ `clean_ratio` 61.0% |

★面積比と `clean_ratio` は相関しない★。面積比のままでは ★◎（90%以上）が常態化して判定が機能しない★。

---

## 1. 監督が決めたこと（エージェントは従うこと）

### 1-1. ★「未実施」の判定★

`clean_task_reports` に ★`clean_error` 相当の列が無い★。代わりに次を正とする。

```
未実施 ＝ 本清掃の clean_timetables 行に対応する clean_task_reports の行が無い
```

★これは推測ではなく、現行実装の第1分岐（`notExecutedFromHistory()` の `if (!history) return true`）と同じ規則★。
★「出発失敗」と「未実施」は同一扱いのまま★（ユーザー確認 2026-09-30）。

### 1-2. ★「清掃結果」列は `status` の生値を出す★

`clean_task_reports.status`（CSV実測値 ★3 / 4 / 5 / 6★、分布 3:2 / 4:161 / 5:4 / 6:22）は
★定義書に値の説明が無い★（`prisma/schema.prisma:520-521` が明記）。

★★推測で `CLEAN_ERROR` に対応づけないこと★★ — ★D-077 と同じ扱いにする（生値をそのまま表示）★。
`CLEAN_ERROR`（`lib/config/index.ts:231-300`）は ★削除しない★（`clean_statuses` 側で今後使う可能性があるため）。

### 1-3. ★面積は残す★

`clean_area` / `task_area` は ★総清掃面積（F-08）では引き続き使う★。
★使わなくなるのは「完了率の計算に使うこと」だけ★。

### 1-4. ★「最新1件」の順序キー★

`clean_task_reports.ended_at` は ★nullable★（`prisma/schema.prisma:533`）。
順序キーは ★`ended_at` があればそれ、無ければ `started_at`★ とし、★決める場所は1関数だけ★にする。

---

## 2. 担当割り（ファイル所有権。重複禁止）

### フェーズ1（並行可）

| 担当 | 所有ファイル | 内容 |
|---|---|---|
| ★A★ | `lib/format/period.ts` + `lib/format/__tests__/period.test.ts` | 年次対応。`periodRange` / `isCurrentPeriod` / `periodLabel` / `prevPeriodLabel` / `shiftPeriod` |
| ★B★ | `lib/types/index.ts` / `d-watch-api.yaml` / `lib/reports/query.ts` + そのテスト | `Period` に `yearly` を追加。`month=0` / `week=0` を年次で許可 |
| ★C★ | `lib/db/clean-log-mapping.ts` / `lib/format/clean-result.ts` + そのテスト | 未実施判定（1-1）と清掃結果の生値化（1-2）、順序キー（1-4） |
| ★D★ | `lib/db/report-mapping.ts` + そのテスト | `periodOfSummaryRow` が `month` を見るように。完了率を `clean_ratio` ベースに |
| ★E★ | `lib/db/consecutive-source.ts` / `lib/db/clean-area-source.ts` + そのテスト | 参照元の差し替え |
| ★F★ | `scripts/seed-local-db.ts` / `lib/fixtures/` | ★`clean_task_reports` のシードと fixtures を新設★（無いと切替後に全画面0件） |

### フェーズ2（フェーズ1完了後・単独）

| 担当 | 所有ファイル | 内容 |
|---|---|---|
| ★G★ | `lib/db/index.ts` | 8箇所の参照元置換 + 年次除外 filter 3箇所の削除 + `:2191` の fail-loud 見直し |

### フェーズ3（並行可）

| 担当 | 所有ファイル | 内容 |
|---|---|---|
| ★H★ | `components/features/reports/ReportsScreen.tsx` | 粒度タブを3つに |
| ★I★ | `lib/clean-logs/csv.ts` / `components/features/detail-log/CleanLogTable.tsx` | 清掃結果列の生値化 |
| ★J★ | `app/api/**/__tests__/` | モック形状の追従（176ケース） |

---

## 3. ★落とし穴（全担当が読むこと）★

| # | 罠 | なぜ危険か |
|---|---|---|
| 1 | ★`clean_timetable_id` が nullable★ | ★NULL は `IN` に一致しない★。行が消えるのではなく ★「未実施」として集計される★（fail-silent）。稼働率・出発成功率が0に落ちる |
| 2 | ★`periodOfSummaryRow()` が `week===0 → monthly` だけ見ている★ | 年次除外 filter を消すと ★年次行が「月次」として静かに混入★し、`customerSummariesByPeriodKey()` が重複検知で ★500★ |
| 3 | ★`ended_at` が nullable★ | `latestHistoryByTimetableId()` は非nullを型で要求している。型ごと直す |
| 4 | ★`status` の意味は未受領★ | 推測でマッピングしない（1-2） |
| 5 | ★`clean_task_reports` はローカルDB 0行★ | ★担当F が先にシードを入れないと、他担当は動作確認できない★ |
| 6 | ★年次は `month=0` かつ `week=0`★ | `Month` の `minimum: 1`（`d-watch-api.yaml:1491`）と `MONTH_MIN = 1`（`lib/reports/query.ts:57`）に当たる |

---

## 4. ★本指示書の範囲外★

- ★制御盤エラーの `robot_error_reports` への集約★ — `robot_id` が NOT NULL で FK があり、★値を捏造せずに入れる手が無い★。TeamWorks に入れ方を確認してから着手する（決定6）
- ★4カラムの UPDATE★ — 本番DBへの書き込み禁止が続いているため未着手のまま（`CLAUDE.md` §3）

---

## 5. ★追補（フェーズ1の完了報告を受けて監督が決めたこと。2026-09-30）★

| # | 決定 | 理由 |
|---|---|---|
| 5-1 | ★総清掃面積画面（F-08）の `completion_rate` も `clean_ratio` 由来にする★。★面積の合計（Σ`clean_area` / Σ`task_area`）は表示に残す★ | ★同じ「清掃完了率」が画面によって別の式になるのを防ぐ★。面積比のままだと、同じ施設が★レポート画面で78%・F-08で120%★と出うる（CSV実測: 面積比90.2% ↔ `clean_ratio` 平均78.5%）。複数行の畳み方は★担当Dが `lib/db/report-mapping.ts` に決めた関数を共用する★（2箇所で別々に畳まない） |
| 5-2 | ★API のフィールド `CleanLogItem.clean_error` を `task_status` に改名する★。★`d-watch-api.yaml` を先に直す★（`CLAUDE.md` §6-1） | ★中身が `clean_task_reports.status` に変わるのに名前が `clean_error` のままだと、読み手が旧コード表（0:正常 / 1:未実施 …）で解釈する★。担当Cが関数名を `formatCleanTaskStatus` に変えたのと同じ理屈（型が同じ `number \| null` なので、名前を変えないと直し忘れが型エラーにならない） |
| 5-3 | ★総清掃面積の集計は `lib/db/clean-area-source.ts` の `loadCleanAreaFacilityStats()` / `countDuplicatedTimetables()` を必ず経由する★。`lib/db/index.ts` に `groupBy` を直書きしない | ★重複検知の番人に `clean_timetable_id IS NOT NULL` の除外が入っている★。直書きすると除外が落ち、★NULL が1グループにまとまって毎回偽陽性★になる（担当E 申し送り） |
| 5-4 | ★シードは `dwatch_local` 以外に書けない★ことを確認済み（`lib/db/local-guard.ts:19` / `scripts/seed-local-db.ts:89`） | ★`.env.local` は本番DB（133.125.238.144）を指している★ため、実行前に必ずガードが効くことを監督が確認した |

### フェーズ2の担当（更新）

| 担当 | 所有ファイル | 内容 |
|---|---|---|
| ★G★ | `lib/db/index.ts` | 8経路の置換 + 年次除外3箇所の削除 + `:2191` の fail-loud 見直し + ★5-3 の関数経由化★ |
| ★K★ | `d-watch-api.yaml` / `lib/types/index.ts` | ★5-2 の改名★ + ★型の docstring に残る旧取得元（`clean_status_histories`）と面積比の定義（5-1）の書き換え★ |

### 5-5. ★完了率の定義は全画面で1つ★（§5-1 を拡張。担当K の指摘を受けて）

§5-1 は総清掃面積（F-08）だけを名指ししていたが、★理由（同じ「清掃完了率」が画面ごとに別の式になるのを防ぐ）は全画面に当てはまる★。次をすべて ★`clean_task_reports.clean_ratio` の単純平均（清掃1回＝1票。`lib/judge/kpi.ts` の `meanCleanRatio()`）★ に揃える。

| 画面 / フィールド | 旧 | 新 |
|---|---|---|
| レポートKPI `completion_rate` | 面積比 | ★単純平均★（担当D で済） |
| 総清掃面積 F-08 `completion_rate` | 面積比 | ★単純平均★ |
| ★エリア別完了率モーダル `overall_completion_rate`★ | 面積比 | ★単純平均★ |
| ★要レスキュー R-3・`RescueRequiredItem.completion_rate`★ | 面積加重 | ★単純平均★ |
| 清掃詳細ログ（1行＝1清掃） | `clean_ratio` | 変更なし（平均する対象が1件） |

★面積の合計フィールドは、表示用として残す（完了率の分子・分母ではない）★。

担当L の所有範囲に ★`lib/types/index.ts` の `CleanAreaFacilityStats` / `CleanAreaRow` / `CleanAreaTotal` / `AreaCompletionsResponse` / `RescueRequiredItem` と、`d-watch-api.yaml` の対応スキーマ★ を追加する（担当K は完了済みなので衝突しない）。
